fetch-pack: always allow fetching of literal SHA1s

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. Helped-by: Jeff King <peff@peff.net> Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> Reviewed-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonathan Tan committed May 15, 2017 at 10:32 UTC fdb69d33c43eb56a564a3e1c805dea4b29162667
2 files changed +73 -2
fetch-pack.c
+38 -2
@@ -15,6 +15,7 @@
15 #include "version.h"
16 #include "prio-queue.h"
17 #include "sha1-array.h"
18 +#include "oidset.h"
19
20 static int transfer_unpack_limit = -1;
21 static int fetch_unpack_limit = -1;
@@ -592,13 +593,38 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args,
593 }
594 }
595
596 +static void add_refs_to_oidset(struct oidset *oids, struct ref *refs)
597 +{
598 + for (; refs; refs = refs->next)
599 + oidset_insert(oids, &refs->old_oid);
600 +}
601 +
602 +static int tip_oids_contain(struct oidset *tip_oids,
603 + struct ref *unmatched, struct ref *newlist,
604 + const struct object_id *id)
605 +{
606 + /*
607 + * Note that this only looks at the ref lists the first time it's
608 + * called. This works out in filter_refs() because even though it may
609 + * add to "newlist" between calls, the additions will always be for
610 + * oids that are already in the set.
611 + */
612 + if (!tip_oids->map.tablesize) {
613 + add_refs_to_oidset(tip_oids, unmatched);
614 + add_refs_to_oidset(tip_oids, newlist);
615 + }
616 + return oidset_contains(tip_oids, id);
617 +}
618 +
619 static void filter_refs(struct fetch_pack_args *args,
620 struct ref **refs,
621 struct ref **sought, int nr_sought)
622 {
623 struct ref *newlist = NULL;
624 struct ref **newtail = &newlist;
625 + struct ref *unmatched = NULL;
626 struct ref *ref, *next;
627 + struct oidset tip_oids = OIDSET_INIT;
628 int i;
629
630 i = 0;
@@ -631,7 +657,8 @@ static void filter_refs(struct fetch_pack_args *args,
657 ref->next = NULL;
658 newtail = &ref->next;
659 } else {
634 - free(ref);
660 + ref->next = unmatched;
661 + unmatched = ref;
662 }
663 }
664
@@ -648,7 +675,9 @@ static void filter_refs(struct fetch_pack_args *args,
675 continue;
676
677 if ((allow_unadvertised_object_request &
651 - (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
678 + (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1)) ||
679 + tip_oids_contain(&tip_oids, unmatched, newlist,
680 + &ref->old_oid)) {
681 ref->match_status = REF_MATCHED;
682 *newtail = copy_ref(ref);
683 newtail = &(*newtail)->next;
@@ -656,6 +685,13 @@ static void filter_refs(struct fetch_pack_args *args,
685 ref->match_status = REF_UNADVERTISED_NOT_ALLOWED;
686 }
687 }
688 +
689 + oidset_clear(&tip_oids);
690 + for (ref = unmatched; ref; ref = next) {
691 + next = ref->next;
692 + free(ref);
693 + }
694 +
695 *refs = newlist;
696 }
697
t/t5500-fetch-pack.sh
+35
@@ -547,6 +547,41 @@ test_expect_success 'fetch-pack can fetch a raw sha1' '
547 git fetch-pack hidden $(git -C hidden rev-parse refs/hidden/one)
548 '
549
550 +test_expect_success 'fetch-pack can fetch a raw sha1 that is advertised as a ref' '
551 + rm -rf server client &&
552 + git init server &&
553 + test_commit -C server 1 &&
554 +
555 + git init client &&
556 + git -C client fetch-pack ../server \
557 + $(git -C server rev-parse refs/heads/master)
558 +'
559 +
560 +test_expect_success 'fetch-pack can fetch a raw sha1 overlapping a named ref' '
561 + rm -rf server client &&
562 + git init server &&
563 + test_commit -C server 1 &&
564 + test_commit -C server 2 &&
565 +
566 + git init client &&
567 + git -C client fetch-pack ../server \
568 + $(git -C server rev-parse refs/tags/1) refs/tags/1
569 +'
570 +
571 +test_expect_success 'fetch-pack cannot fetch a raw sha1 that is not advertised as a ref' '
572 + rm -rf server &&
573 +
574 + git init server &&
575 + test_commit -C server 5 &&
576 + git -C server tag -d 5 &&
577 + test_commit -C server 6 &&
578 +
579 + git init client &&
580 + test_must_fail git -C client fetch-pack ../server \
581 + $(git -C server rev-parse refs/heads/master^) 2>err &&
582 + test_i18ngrep "Server does not allow request for unadvertised object" err
583 +'
584 +
585 check_prot_path () {
586 cat >expected <<-EOF &&
587 Diag: url=$1