aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2013-01-29 14:02:15 -0800
committerJunio C Hamano <gitster@pobox.com>2013-02-07 14:07:53 -0800
commit6e7b66eebd18c11f58a9790b8f071618a1bb5b2c (patch)
tree741aa5bec82254920bf24bdee1678b7039c36470
parent390eb36b0a9cbaa5051040c762b986ddd4b3848f (diff)
downloadgit-6e7b66eebd18c11f58a9790b8f071618a1bb5b2c.tar.gz
git-6e7b66eebd18c11f58a9790b8f071618a1bb5b2c.tar.xz
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>
-rw-r--r--fetch-pack.c22
-rw-r--r--remote.c12
-rw-r--r--remote.h1
-rwxr-xr-xt/t5516-fetch-push.sh34
4 files changed, 67 insertions, 2 deletions
diff --git a/fetch-pack.c b/fetch-pack.c
index 915c0b788..70db64651 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -36,7 +36,7 @@ static int marked;
#define MAX_IN_VAIN 256
static struct commit_list *rev_list;
-static int non_common_revs, multi_ack, use_sideband;
+static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
static void rev_list_push(struct commit *commit, int mark)
{
@@ -563,6 +563,21 @@ static void filter_refs(struct fetch_pack_args *args,
}
}
+ /* Append unmatched requests to the list */
+ if (allow_tip_sha1_in_want) {
+ for (i = 0; i < nr_sought; i++) {
+ ref = sought[i];
+ if (ref->matched)
+ continue;
+ if (get_sha1_hex(ref->name, ref->old_sha1))
+ continue;
+
+ ref->matched = 1;
+ *newtail = ref;
+ ref->next = NULL;
+ newtail = &ref->next;
+ }
+ }
*refs = newlist;
}
@@ -803,6 +818,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
fprintf(stderr, "Server supports side-band\n");
use_sideband = 1;
}
+ if (server_supports("allow-tip-sha1-in-want")) {
+ if (args->verbose)
+ fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
+ allow_tip_sha1_in_want = 1;
+ }
if (!server_supports("thin-pack"))
args->use_thin_pack = 0;
if (!server_supports("no-progress"))
diff --git a/remote.c b/remote.c
index 1b7828d19..1118d058d 100644
--- a/remote.c
+++ b/remote.c
@@ -15,6 +15,7 @@ static struct refspec s_tag_refspec = {
0,
1,
0,
+ 0,
"refs/tags/*",
"refs/tags/*"
};
@@ -565,9 +566,13 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
if (fetch) {
+ unsigned char unused[40];
+
/* LHS */
if (!*rs[i].src)
; /* empty is ok; it means "HEAD" */
+ else if (llen == 40 && !get_sha1_hex(rs[i].src, unused))
+ rs[i].exact_sha1 = 1; /* ok */
else if (!check_refname_format(rs[i].src, flags))
; /* valid looking ref is ok */
else
@@ -1495,7 +1500,12 @@ int get_fetch_map(const struct ref *remote_refs,
} else {
const char *name = refspec->src[0] ? refspec->src : "HEAD";
- ref_map = get_remote_ref(remote_refs, name);
+ if (refspec->exact_sha1) {
+ ref_map = alloc_ref(name);
+ get_sha1_hex(name, ref_map->old_sha1);
+ } else {
+ ref_map = get_remote_ref(remote_refs, name);
+ }
if (!missing_ok && !ref_map)
die("Couldn't find remote ref %s", name);
if (ref_map) {
diff --git a/remote.h b/remote.h
index 251d8fd96..f7b08f15d 100644
--- a/remote.h
+++ b/remote.h
@@ -62,6 +62,7 @@ struct refspec {
unsigned force : 1;
unsigned pattern : 1;
unsigned matching : 1;
+ unsigned exact_sha1 : 1;
char *src;
char *dst;
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 2b26ecebf..dfe0f1d03 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -1064,4 +1064,38 @@ do
'
done
+test_expect_success 'fetch exact SHA1' '
+ mk_test heads/master hidden/one &&
+ git push testrepo master:refs/hidden/one &&
+ (
+ cd testrepo &&
+ git config transfer.hiderefs refs/hidden
+ ) &&
+ check_push_result $the_commit hidden/one &&
+
+ mk_child child &&
+ (
+ cd child &&
+
+ # make sure $the_commit does not exist here
+ git repack -a -d &&
+ git prune &&
+ test_must_fail git cat-file -t $the_commit &&
+
+ # fetching the hidden object should fail by default
+ test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy &&
+ test_must_fail git rev-parse --verify refs/heads/copy &&
+
+ # the server side can allow it to succeed
+ (
+ cd ../testrepo &&
+ git config uploadpack.allowtipsha1inwant true
+ ) &&
+
+ git fetch -v ../testrepo $the_commit:refs/heads/copy &&
+ result=$(git rev-parse --verify refs/heads/copy) &&
+ test "$the_commit" = "$result"
+ )
+'
+
test_done