fetch: fetch objects by their exact SHA-1 object names
Teach "git fetch" to accept an exact SHA-1 object name the user may obtain out of band on the LHS of a pathspec, and send it on a "want" message when the server side advertises the allow-tip-sha1-in-want capability. Signed-off-by: Junio C Hamano <gitster@pobox.com>
Junio C Hamano committed
Jan 29, 2013 at 14:02 UTC
6e7b66eebd18c11f58a9790b8f071618a1bb5b2c
4 files changed
+67
-2
fetch-pack.c
+21
-1
@@ -36,7 +36,7 @@ static int marked;
36
#define MAX_IN_VAIN 256
37
38
static struct commit_list *rev_list;
39
-static int non_common_revs, multi_ack, use_sideband;
39
+static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
40
41
static void rev_list_push(struct commit *commit, int mark)
42
{
@@ -563,6 +563,21 @@ static void filter_refs(struct fetch_pack_args *args,
563
}
564
}
565
566
+ /* Append unmatched requests to the list */
567
+ if (allow_tip_sha1_in_want) {
568
+ for (i = 0; i < nr_sought; i++) {
569
+ ref = sought[i];
570
+ if (ref->matched)
571
+ continue;
572
+ if (get_sha1_hex(ref->name, ref->old_sha1))
573
+ continue;
574
+
575
+ ref->matched = 1;
576
+ *newtail = ref;
577
+ ref->next = NULL;
578
+ newtail = &ref->next;
579
+ }
580
+ }
581
*refs = newlist;
582
}
583
@@ -803,6 +818,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
818
fprintf(stderr, "Server supports side-band\n");
819
use_sideband = 1;
820
}
821
+ if (server_supports("allow-tip-sha1-in-want")) {
822
+ if (args->verbose)
823
+ fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
824
+ allow_tip_sha1_in_want = 1;
825
+ }
826
if (!server_supports("thin-pack"))
827
args->use_thin_pack = 0;
828
if (!server_supports("no-progress"))
remote.c
+11
-1
@@ -15,6 +15,7 @@ static struct refspec s_tag_refspec = {
15
0,
16
1,
17
0,
18
+ 0,
19
"refs/tags/*",
20
"refs/tags/*"
21
};
@@ -565,9 +566,13 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
566
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
567
568
if (fetch) {
569
+ unsigned char unused[40];
570
+
571
/* LHS */
572
if (!*rs[i].src)
573
; /* empty is ok; it means "HEAD" */
574
+ else if (llen == 40 && !get_sha1_hex(rs[i].src, unused))
575
+ rs[i].exact_sha1 = 1; /* ok */
576
else if (!check_refname_format(rs[i].src, flags))
577
; /* valid looking ref is ok */
578
else
@@ -1495,7 +1500,12 @@ int get_fetch_map(const struct ref *remote_refs,
1500
} else {
1501
const char *name = refspec->src[0] ? refspec->src : "HEAD";
1502
1498
- ref_map = get_remote_ref(remote_refs, name);
1503
+ if (refspec->exact_sha1) {
1504
+ ref_map = alloc_ref(name);
1505
+ get_sha1_hex(name, ref_map->old_sha1);
1506
+ } else {
1507
+ ref_map = get_remote_ref(remote_refs, name);
1508
+ }
1509
if (!missing_ok && !ref_map)
1510
die("Couldn't find remote ref %s", name);
1511
if (ref_map) {
remote.h
+1
@@ -62,6 +62,7 @@ struct refspec {
62
unsigned force : 1;
63
unsigned pattern : 1;
64
unsigned matching : 1;
65
+ unsigned exact_sha1 : 1;
66
67
char *src;
68
char *dst;
t/t5516-fetch-push.sh
+34
@@ -1064,4 +1064,38 @@ do
1064
'
1065
done
1066
1067
+test_expect_success 'fetch exact SHA1' '
1068
+ mk_test heads/master hidden/one &&
1069
+ git push testrepo master:refs/hidden/one &&
1070
+ (
1071
+ cd testrepo &&
1072
+ git config transfer.hiderefs refs/hidden
1073
+ ) &&
1074
+ check_push_result $the_commit hidden/one &&
1075
+
1076
+ mk_child child &&
1077
+ (
1078
+ cd child &&
1079
+
1080
+ # make sure $the_commit does not exist here
1081
+ git repack -a -d &&
1082
+ git prune &&
1083
+ test_must_fail git cat-file -t $the_commit &&
1084
+
1085
+ # fetching the hidden object should fail by default
1086
+ test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy &&
1087
+ test_must_fail git rev-parse --verify refs/heads/copy &&
1088
+
1089
+ # the server side can allow it to succeed
1090
+ (
1091
+ cd ../testrepo &&
1092
+ git config uploadpack.allowtipsha1inwant true
1093
+ ) &&
1094
+
1095
+ git fetch -v ../testrepo $the_commit:refs/heads/copy &&
1096
+ result=$(git rev-parse --verify refs/heads/copy) &&
1097
+ test "$the_commit" = "$result"
1098
+ )
1099
+'
1100
+
1101
test_done