fetch-pack, when fetching a literal SHA-1 from a server that is not configured with uploadpack.allowtipsha1inwant (or similar), always returns an error message of the form "Server does not allow request for unadvertised object %s". However, it is sometimes the case that such object is advertised. This situation would occur, for example, if a user or a script was provided a SHA-1 instead of a branch or tag name for fetching, and wanted to invoke "git fetch" or "git fetch-pack" using that SHA-1. Teach fetch-pack to also check the SHA-1s of the refs in the received ref advertisement if a literal SHA-1 was given by the user. Signed-off-by: Jonathan Tan <jonathantanmy@xxxxxxxxxx> --- Thanks, peff, for your suggestions. Changes from v1: - updated commit message to explain a bit of the context - use parse_oid_hex instead of get_oid_hex to avoid the constant 40 peff also suggested a possible optimization in computing is_literal_sha1 outside the loop. After some thought, I don't think that it takes that much time in the general case where the ref name starts with "refs/", because parse_oid_hex will see the "r" (which is not in "[0-9a-f]") and immediately return. So I left it as-is. fetch-pack.c | 31 +++++++++++++++++-------------- t/t5500-fetch-pack.sh | 6 ++++++ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/fetch-pack.c b/fetch-pack.c index afb8b0502..d4b57b9ba 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -592,6 +592,15 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args, } } +static int is_literal_sha1(const struct ref *ref) +{ + struct object_id oid; + const char *end; + return !parse_oid_hex(ref->name, &oid, &end) && + !*end && + !oidcmp(&oid, &ref->old_oid); +} + static void filter_refs(struct fetch_pack_args *args, struct ref **refs, struct ref **sought, int nr_sought) @@ -601,7 +610,6 @@ static void filter_refs(struct fetch_pack_args *args, struct ref *ref, *next; int i; - i = 0; for (ref = *refs; ref; ref = next) { int keep = 0; next = ref->next; @@ -610,15 +618,14 @@ static void filter_refs(struct fetch_pack_args *args, check_refname_format(ref->name, 0)) ; /* trash */ else { - while (i < nr_sought) { - int cmp = strcmp(ref->name, sought[i]->name); - if (cmp < 0) - break; /* definitely do not have it */ - else if (cmp == 0) { - keep = 1; /* definitely have it */ - sought[i]->match_status = REF_MATCHED; + for (i = 0; i < nr_sought; i++) { + struct ref *s = sought[i]; + if (!strcmp(ref->name, s->name) || + (is_literal_sha1(s) && + !oidcmp(&ref->old_oid, &s->old_oid))) { + keep = 1; + s->match_status = REF_MATCHED; } - i++; } } @@ -637,14 +644,10 @@ static void filter_refs(struct fetch_pack_args *args, /* Append unmatched requests to the list */ for (i = 0; i < nr_sought; i++) { - unsigned char sha1[20]; - ref = sought[i]; if (ref->match_status != REF_NOT_MATCHED) continue; - if (get_sha1_hex(ref->name, sha1) || - ref->name[40] != '\0' || - hashcmp(sha1, ref->old_oid.hash)) + if (!is_literal_sha1(ref)) continue; if ((allow_unadvertised_object_request & diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index b5865b385..d87983bc2 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -547,6 +547,12 @@ test_expect_success 'fetch-pack can fetch a raw sha1' ' git fetch-pack hidden $(git -C hidden rev-parse refs/hidden/one) ' +test_expect_success 'fetch-pack can fetch a raw sha1 that is advertised as a ref' ' + git init server && + test_commit -C server 4 && + git fetch-pack server $(git -C server rev-parse refs/heads/master) +' + check_prot_path () { cat >expected <<-EOF && Diag: url=$1 -- 2.13.0.rc2.291.g57267f2277-goog