Skip to content

Commit 54db5c0

Browse files
committed
Merge branch 'jt/partial-clone-proto-v2'
Transfer protocol v2 learned to support the partial clone. * jt/partial-clone-proto-v2: {fetch,upload}-pack: support filter in protocol v2 upload-pack: read config when serving protocol v2 upload-pack: fix error message typo
2 parents 42c8ce1 + ba95710 commit 54db5c0

File tree

5 files changed

+171
-6
lines changed

5 files changed

+171
-6
lines changed

Documentation/technical/protocol-v2.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,15 @@ included in the clients request as well as the potential addition of the
290290
Cannot be used with "deepen", but can be used with
291291
"deepen-since".
292292

293+
If the 'filter' feature is advertised, the following argument can be
294+
included in the client's request:
295+
296+
filter <filter-spec>
297+
Request that various objects from the packfile be omitted
298+
using one of several filtering techniques. These are intended
299+
for use with partial clone and partial fetch operations. See
300+
`rev-list` for possible "filter-spec" values.
301+
293302
The response of `fetch` is broken into a number of sections separated by
294303
delimiter packets (0001), with each section beginning with its section
295304
header.

fetch-pack.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,14 +1198,29 @@ static int send_fetch_request(int fd_out, const struct fetch_pack_args *args,
11981198
else if (is_repository_shallow() || args->deepen)
11991199
die(_("Server does not support shallow requests"));
12001200

1201+
/* Add filter */
1202+
if (server_supports_feature("fetch", "filter", 0) &&
1203+
args->filter_options.choice) {
1204+
print_verbose(args, _("Server supports filter"));
1205+
packet_buf_write(&req_buf, "filter %s",
1206+
args->filter_options.filter_spec);
1207+
} else if (args->filter_options.choice) {
1208+
warning("filtering not recognized by server, ignoring");
1209+
}
1210+
12011211
/* add wants */
12021212
add_wants(wants, &req_buf);
12031213

1204-
/* Add all of the common commits we've found in previous rounds */
1205-
add_common(&req_buf, common);
1214+
if (args->no_dependents) {
1215+
packet_buf_write(&req_buf, "done");
1216+
ret = 1;
1217+
} else {
1218+
/* Add all of the common commits we've found in previous rounds */
1219+
add_common(&req_buf, common);
12061220

1207-
/* Add initial haves */
1208-
ret = add_haves(&req_buf, haves_to_send, in_vain);
1221+
/* Add initial haves */
1222+
ret = add_haves(&req_buf, haves_to_send, in_vain);
1223+
}
12091224

12101225
/* Send request */
12111226
packet_buf_flush(&req_buf);

t/t5701-git-serve.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,18 @@ test_expect_success 'sending server-options' '
194194
test_cmp actual expect
195195
'
196196

197+
test_expect_success 'unexpected lines are not allowed in fetch request' '
198+
git init server &&
199+
200+
test-pkt-line pack >in <<-EOF &&
201+
command=fetch
202+
0001
203+
this-is-not-a-command
204+
0000
205+
EOF
206+
207+
test_must_fail git -C server serve --stateless-rpc <in >/dev/null 2>err &&
208+
grep "unexpected line: .this-is-not-a-command." err
209+
'
210+
197211
test_done

t/t5702-protocol-v2.sh

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,118 @@ test_expect_success 'server-options are sent when fetching' '
233233
grep "server-option=world" log
234234
'
235235

236+
test_expect_success 'upload-pack respects config using protocol v2' '
237+
git init server &&
238+
write_script server/.git/hook <<-\EOF &&
239+
touch hookout
240+
"$@"
241+
EOF
242+
test_commit -C server one &&
243+
244+
test_config_global uploadpack.packobjectshook ./hook &&
245+
test_path_is_missing server/.git/hookout &&
246+
git -c protocol.version=2 clone "file://$(pwd)/server" client &&
247+
test_path_is_file server/.git/hookout
248+
'
249+
250+
test_expect_success 'setup filter tests' '
251+
rm -rf server client &&
252+
git init server &&
253+
254+
# 1 commit to create a file, and 1 commit to modify it
255+
test_commit -C server message1 a.txt &&
256+
test_commit -C server message2 a.txt &&
257+
git -C server config protocol.version 2 &&
258+
git -C server config uploadpack.allowfilter 1 &&
259+
git -C server config uploadpack.allowanysha1inwant 1 &&
260+
git -C server config protocol.version 2
261+
'
262+
263+
test_expect_success 'partial clone' '
264+
GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \
265+
clone --filter=blob:none "file://$(pwd)/server" client &&
266+
grep "version 2" trace &&
267+
268+
# Ensure that the old version of the file is missing
269+
git -C client rev-list master --quiet --objects --missing=print \
270+
>observed.oids &&
271+
grep "$(git -C server rev-parse message1:a.txt)" observed.oids &&
272+
273+
# Ensure that client passes fsck
274+
git -C client fsck
275+
'
276+
277+
test_expect_success 'dynamically fetch missing object' '
278+
rm "$(pwd)/trace" &&
279+
GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
280+
cat-file -p $(git -C server rev-parse message1:a.txt) &&
281+
grep "version 2" trace
282+
'
283+
284+
test_expect_success 'partial fetch' '
285+
rm -rf client "$(pwd)/trace" &&
286+
git init client &&
287+
SERVER="file://$(pwd)/server" &&
288+
test_config -C client extensions.partialClone "$SERVER" &&
289+
290+
GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
291+
fetch --filter=blob:none "$SERVER" master:refs/heads/other &&
292+
grep "version 2" trace &&
293+
294+
# Ensure that the old version of the file is missing
295+
git -C client rev-list other --quiet --objects --missing=print \
296+
>observed.oids &&
297+
grep "$(git -C server rev-parse message1:a.txt)" observed.oids &&
298+
299+
# Ensure that client passes fsck
300+
git -C client fsck
301+
'
302+
303+
test_expect_success 'do not advertise filter if not configured to do so' '
304+
SERVER="file://$(pwd)/server" &&
305+
306+
rm "$(pwd)/trace" &&
307+
git -C server config uploadpack.allowfilter 1 &&
308+
GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \
309+
ls-remote "$SERVER" &&
310+
grep "fetch=.*filter" trace &&
311+
312+
rm "$(pwd)/trace" &&
313+
git -C server config uploadpack.allowfilter 0 &&
314+
GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \
315+
ls-remote "$SERVER" &&
316+
grep "fetch=" trace >fetch_capabilities &&
317+
! grep filter fetch_capabilities
318+
'
319+
320+
test_expect_success 'partial clone warns if filter is not advertised' '
321+
rm -rf client &&
322+
git -C server config uploadpack.allowfilter 0 &&
323+
git -c protocol.version=2 \
324+
clone --filter=blob:none "file://$(pwd)/server" client 2>err &&
325+
test_i18ngrep "filtering not recognized by server, ignoring" err
326+
'
327+
328+
test_expect_success 'even with handcrafted request, filter does not work if not advertised' '
329+
git -C server config uploadpack.allowfilter 0 &&
330+
331+
# Custom request that tries to filter even though it is not advertised.
332+
test-pkt-line pack >in <<-EOF &&
333+
command=fetch
334+
0001
335+
want $(git -C server rev-parse master)
336+
filter blob:none
337+
0000
338+
EOF
339+
340+
test_must_fail git -C server serve --stateless-rpc <in >/dev/null 2>err &&
341+
grep "unexpected line: .filter blob:none." err &&
342+
343+
# Exercise to ensure that if advertised, filter works
344+
git -C server config uploadpack.allowfilter 1 &&
345+
git -C server serve --stateless-rpc <in >/dev/null
346+
'
347+
236348
# Test protocol v2 with 'http://' transport
237349
#
238350
. "$TEST_DIRECTORY"/lib-httpd.sh

upload-pack.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,7 @@ static void process_args(struct packet_reader *request,
12051205
{
12061206
while (packet_reader_read(request) != PACKET_READ_FLUSH) {
12071207
const char *arg = request->line;
1208+
const char *p;
12081209

12091210
/* process want */
12101211
if (parse_want(arg))
@@ -1251,8 +1252,13 @@ static void process_args(struct packet_reader *request,
12511252
continue;
12521253
}
12531254

1255+
if (allow_filter && skip_prefix(arg, "filter ", &p)) {
1256+
parse_list_objects_filter(&filter_options, p);
1257+
continue;
1258+
}
1259+
12541260
/* ignore unknown lines maybe? */
1255-
die("unexpect line: '%s'", arg);
1261+
die("unexpected line: '%s'", arg);
12561262
}
12571263
}
12581264

@@ -1376,6 +1382,8 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
13761382
enum fetch_state state = FETCH_PROCESS_ARGS;
13771383
struct upload_pack_data data;
13781384

1385+
git_config(upload_pack_config, NULL);
1386+
13791387
upload_pack_data_init(&data);
13801388
use_sideband = LARGE_PACKET_MAX;
13811389

@@ -1428,7 +1436,14 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
14281436
int upload_pack_advertise(struct repository *r,
14291437
struct strbuf *value)
14301438
{
1431-
if (value)
1439+
if (value) {
1440+
int allow_filter_value;
14321441
strbuf_addstr(value, "shallow");
1442+
if (!repo_config_get_bool(the_repository,
1443+
"uploadpack.allowfilter",
1444+
&allow_filter_value) &&
1445+
allow_filter_value)
1446+
strbuf_addstr(value, " filter");
1447+
}
14331448
return 1;
14341449
}

0 commit comments

Comments
 (0)