Skip to content

Commit 68ee628

Browse files
morotengitster
authored andcommitted
upload-pack: optionally allow fetching reachable sha1
With uploadpack.allowReachableSHA1InWant configuration option set on the server side, "git fetch" can make a request with a "want" line that names an object that has not been advertised (likely to have been obtained out of band or from a submodule pointer). Only objects reachable from the branch tips, i.e. the union of advertised branches and branches hidden by transfer.hideRefs, will be processed. Note that there is an associated cost of having to walk back the history to check the reachability. This feature can be used when obtaining the content of a certain commit, for which the sha1 is known, without the need of cloning the whole repository, especially if a shallow fetch is used. Useful cases are e.g. repositories containing large files in the history, fetching only the needed data for a submodule checkout, when sharing a sha1 without telling which exact branch it belongs to and in Gerrit, if you think in terms of commits instead of change numbers. (The Gerrit case has already been solved through allowTipSHA1InWant as every Gerrit change has a ref.) Signed-off-by: Fredrik Medley <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7199c09 commit 68ee628

File tree

6 files changed

+97
-6
lines changed

6 files changed

+97
-6
lines changed

Documentation/config.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2538,6 +2538,12 @@ uploadpack.allowTipSHA1InWant::
25382538
of a hidden ref (by default, such a request is rejected).
25392539
see also `uploadpack.hideRefs`.
25402540

2541+
uploadpack.allowReachableSHA1InWant::
2542+
Allow `upload-pack` to accept a fetch request that asks for an
2543+
object that is reachable from any ref tip. However, note that
2544+
calculating object reachability is computationally expensive.
2545+
Defaults to `false`.
2546+
25412547
uploadpack.keepAlive::
25422548
When `upload-pack` has started `pack-objects`, there may be a
25432549
quiet period while `pack-objects` prepares the pack. Normally

Documentation/technical/http-protocol.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,8 @@ Servers SHOULD support all capabilities defined here.
319319
Clients MUST send at least one "want" command in the request body.
320320
Clients MUST NOT reference an id in a "want" command which did not
321321
appear in the response obtained through ref discovery unless the
322-
server advertises capability `allow-tip-sha1-in-want`.
322+
server advertises capability `allow-tip-sha1-in-want` or
323+
`allow-reachable-sha1-in-want`.
323324

324325
compute_request = want_list
325326
have_list

Documentation/technical/protocol-capabilities.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,13 @@ If the upload-pack server advertises this capability, fetch-pack may
260260
send "want" lines with SHA-1s that exist at the server but are not
261261
advertised by upload-pack.
262262

263+
allow-reachable-sha1-in-want
264+
----------------------------
265+
266+
If the upload-pack server advertises this capability, fetch-pack may
267+
send "want" lines with SHA-1s that exist at the server but are not
268+
advertised by upload-pack.
269+
263270
push-cert=<nonce>
264271
-----------------
265272

fetch-pack.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ static struct prio_queue rev_list = { compare_commits_by_commit_date };
4646
static int non_common_revs, multi_ack, use_sideband;
4747
/* Allow specifying sha1 if it is a ref tip. */
4848
#define ALLOW_TIP_SHA1 01
49+
/* Allow request of a sha1 if it is reachable from a ref (possibly hidden ref). */
50+
#define ALLOW_REACHABLE_SHA1 02
4951
static unsigned int allow_unadvertised_object_request;
5052

5153
static void rev_list_push(struct commit *commit, int mark)
@@ -545,7 +547,8 @@ static void filter_refs(struct fetch_pack_args *args,
545547
}
546548

547549
/* Append unmatched requests to the list */
548-
if ((allow_unadvertised_object_request & ALLOW_TIP_SHA1)) {
550+
if ((allow_unadvertised_object_request &
551+
(ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
549552
for (i = 0; i < nr_sought; i++) {
550553
unsigned char sha1[20];
551554

@@ -826,6 +829,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
826829
fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
827830
allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
828831
}
832+
if (server_supports("allow-reachable-sha1-in-want")) {
833+
if (args->verbose)
834+
fprintf(stderr, "Server supports allow-reachable-sha1-in-want\n");
835+
allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
836+
}
829837
if (!server_supports("thin-pack"))
830838
args->use_thin_pack = 0;
831839
if (!server_supports("no-progress"))

t/t5516-fetch-push.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,61 @@ test_expect_success 'fetch exact SHA1' '
11201120
)
11211121
'
11221122

1123+
for configallowtipsha1inwant in true false
1124+
do
1125+
test_expect_success "shallow fetch reachable SHA1 (but not a ref), allowtipsha1inwant=$configallowtipsha1inwant" '
1126+
mk_empty testrepo &&
1127+
(
1128+
cd testrepo &&
1129+
git config uploadpack.allowtipsha1inwant $configallowtipsha1inwant &&
1130+
git commit --allow-empty -m foo &&
1131+
git commit --allow-empty -m bar
1132+
) &&
1133+
SHA1=$(git --git-dir=testrepo/.git rev-parse HEAD^) &&
1134+
mk_empty shallow &&
1135+
(
1136+
cd shallow &&
1137+
test_must_fail git fetch --depth=1 ../testrepo/.git $SHA1 &&
1138+
git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
1139+
git fetch --depth=1 ../testrepo/.git $SHA1 &&
1140+
git cat-file commit $SHA1
1141+
)
1142+
'
1143+
1144+
test_expect_success "deny fetch unreachable SHA1, allowtipsha1inwant=$configallowtipsha1inwant" '
1145+
mk_empty testrepo &&
1146+
(
1147+
cd testrepo &&
1148+
git config uploadpack.allowtipsha1inwant $configallowtipsha1inwant &&
1149+
git commit --allow-empty -m foo &&
1150+
git commit --allow-empty -m bar &&
1151+
git commit --allow-empty -m xyz
1152+
) &&
1153+
SHA1_1=$(git --git-dir=testrepo/.git rev-parse HEAD^^) &&
1154+
SHA1_2=$(git --git-dir=testrepo/.git rev-parse HEAD^) &&
1155+
SHA1_3=$(git --git-dir=testrepo/.git rev-parse HEAD) &&
1156+
(
1157+
cd testrepo &&
1158+
git reset --hard $SHA1_2 &&
1159+
git cat-file commit $SHA1_1 &&
1160+
git cat-file commit $SHA1_3
1161+
) &&
1162+
mk_empty shallow &&
1163+
(
1164+
cd shallow &&
1165+
test_must_fail git fetch ../testrepo/.git $SHA1_3 &&
1166+
test_must_fail git fetch ../testrepo/.git $SHA1_1 &&
1167+
git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
1168+
git fetch ../testrepo/.git $SHA1_1 &&
1169+
git cat-file commit $SHA1_1 &&
1170+
test_must_fail git cat-file commit $SHA1_2 &&
1171+
git fetch ../testrepo/.git $SHA1_2 &&
1172+
git cat-file commit $SHA1_2 &&
1173+
test_must_fail git fetch ../testrepo/.git $SHA1_3
1174+
)
1175+
'
1176+
done
1177+
11231178
test_expect_success 'fetch follows tags by default' '
11241179
mk_test testrepo heads/master &&
11251180
rm -fr src dst &&

upload-pack.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ static int use_thin_pack, use_ofs_delta, use_include_tag;
3737
static int no_progress, daemon_mode;
3838
/* Allow specifying sha1 if it is a ref tip. */
3939
#define ALLOW_TIP_SHA1 01
40+
/* Allow request of a sha1 if it is reachable from a ref (possibly hidden ref). */
41+
#define ALLOW_REACHABLE_SHA1 02
4042
static unsigned int allow_unadvertised_object_request;
4143
static int shallow_nr;
4244
static struct object_array have_obj;
@@ -444,7 +446,8 @@ static int get_common_commits(void)
444446

445447
static int is_our_ref(struct object *o)
446448
{
447-
int allow_hidden_ref = (allow_unadvertised_object_request & ALLOW_TIP_SHA1);
449+
int allow_hidden_ref = (allow_unadvertised_object_request &
450+
(ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
448451
return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
449452
}
450453

@@ -458,8 +461,12 @@ static void check_non_tip(void)
458461
char namebuf[42]; /* ^ + SHA-1 + LF */
459462
int i;
460463

461-
/* In the normal in-process case non-tip request can never happen */
462-
if (!stateless_rpc)
464+
/*
465+
* In the normal in-process case without
466+
* uploadpack.allowReachableSHA1InWant,
467+
* non-tip requests can never happen.
468+
*/
469+
if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))
463470
goto error;
464471

465472
cmd.argv = argv;
@@ -726,11 +733,13 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
726733
struct strbuf symref_info = STRBUF_INIT;
727734

728735
format_symref_info(&symref_info, cb_data);
729-
packet_write(1, "%s %s%c%s%s%s%s agent=%s\n",
736+
packet_write(1, "%s %s%c%s%s%s%s%s agent=%s\n",
730737
sha1_to_hex(sha1), refname_nons,
731738
0, capabilities,
732739
(allow_unadvertised_object_request & ALLOW_TIP_SHA1) ?
733740
" allow-tip-sha1-in-want" : "",
741+
(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1) ?
742+
" allow-reachable-sha1-in-want" : "",
734743
stateless_rpc ? " no-done" : "",
735744
symref_info.buf,
736745
git_user_agent_sanitized());
@@ -795,6 +804,11 @@ static int upload_pack_config(const char *var, const char *value, void *unused)
795804
allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
796805
else
797806
allow_unadvertised_object_request &= ~ALLOW_TIP_SHA1;
807+
} else if (!strcmp("uploadpack.allowreachablesha1inwant", var)) {
808+
if (git_config_bool(var, value))
809+
allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
810+
else
811+
allow_unadvertised_object_request &= ~ALLOW_REACHABLE_SHA1;
798812
} else if (!strcmp("uploadpack.keepalive", var)) {
799813
keepalive = git_config_int(var, value);
800814
if (!keepalive)

0 commit comments

Comments
 (0)