fetch: support filters

Teach fetch to support filters. This is only allowed for the remote configured in extensions.partialcloneremote. Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff Hostetler committed Dec 8, 2017 at 15:58 UTC acb0c57260aa78fc99939de2a27c48b5a3fb4f21
4 files changed +65 -2
builtin/fetch.c
+21 -2
@@ -18,6 +18,7 @@
18 #include "argv-array.h"
19 #include "utf8.h"
20 #include "packfile.h"
21 +#include "list-objects-filter-options.h"
22
23 static const char * const builtin_fetch_usage[] = {
24 N_("git fetch [<options>] [<repository> [<refspec>...]]"),
@@ -55,6 +56,7 @@ static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
56 static int shown_url = 0;
57 static int refmap_alloc, refmap_nr;
58 static const char **refmap_array;
59 +static struct list_objects_filter_options filter_options;
60
61 static int git_fetch_config(const char *k, const char *v, void *cb)
62 {
@@ -160,6 +162,7 @@ static struct option builtin_fetch_options[] = {
162 TRANSPORT_FAMILY_IPV4),
163 OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
164 TRANSPORT_FAMILY_IPV6),
165 + OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
166 OPT_END()
167 };
168
@@ -1044,6 +1047,11 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
1047 set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
1048 if (update_shallow)
1049 set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
1050 + if (filter_options.choice) {
1051 + set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
1052 + filter_options.filter_spec);
1053 + set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
1054 + }
1055 return transport;
1056 }
1057
@@ -1328,6 +1336,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
1336
1337 packet_trace_identity("fetch");
1338
1339 + fetch_if_missing = 0;
1340 +
1341 /* Record the command line for the reflog */
1342 strbuf_addstr(&default_rla, "fetch");
1343 for (i = 1; i < argc; i++)
@@ -1361,6 +1371,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
1371 if (depth || deepen_since || deepen_not.nr)
1372 deepen = 1;
1373
1374 + if (filter_options.choice && !repository_format_partial_clone)
1375 + die("--filter can only be used when extensions.partialClone is set");
1376 +
1377 if (all) {
1378 if (argc == 1)
1379 die(_("fetch --all does not take a repository argument"));
@@ -1390,10 +1403,16 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
1403 }
1404 }
1405
1393 - if (remote)
1406 + if (remote) {
1407 + if (filter_options.choice &&
1408 + strcmp(remote->name, repository_format_partial_clone))
1409 + die(_("--filter can only be used with the remote configured in core.partialClone"));
1410 result = fetch_one(remote, argc, argv);
1395 - else
1411 + } else {
1412 + if (filter_options.choice)
1413 + die(_("--filter can only be used with the remote configured in core.partialClone"));
1414 result = fetch_multiple(&list);
1415 + }
1416
1417 if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
1418 struct argv_array options = ARGV_ARRAY_INIT;
connected.c
+2
@@ -56,6 +56,8 @@ int check_connected(sha1_iterate_fn fn, void *cb_data,
56 argv_array_push(&rev_list.args,"rev-list");
57 argv_array_push(&rev_list.args, "--objects");
58 argv_array_push(&rev_list.args, "--stdin");
59 + if (repository_format_partial_clone)
60 + argv_array_push(&rev_list.args, "--exclude-promisor-objects");
61 argv_array_push(&rev_list.args, "--not");
62 argv_array_push(&rev_list.args, "--all");
63 argv_array_push(&rev_list.args, "--quiet");
remote-curl.c
+6
@@ -24,6 +24,7 @@ struct options {
24 char *deepen_since;
25 struct string_list deepen_not;
26 struct string_list push_options;
27 + char *filter;
28 unsigned progress : 1,
29 check_self_contained_and_connected : 1,
30 cloning : 1,
@@ -165,6 +166,9 @@ static int set_option(const char *name, const char *value)
166 } else if (!strcmp(name, "no-dependents")) {
167 options.no_dependents = 1;
168 return 0;
169 + } else if (!strcmp(name, "filter")) {
170 + options.filter = xstrdup(value);;
171 + return 0;
172 } else {
173 return 1 /* unsupported */;
174 }
@@ -834,6 +838,8 @@ static int fetch_git(struct discovery *heads,
838 argv_array_push(&args, "--from-promisor");
839 if (options.no_dependents)
840 argv_array_push(&args, "--no-dependents");
841 + if (options.filter)
842 + argv_array_pushf(&args, "--filter=%s", options.filter);
843 argv_array_push(&args, url.buf);
844
845 for (i = 0; i < nr_heads; i++) {
t/t5500-fetch-pack.sh
+36
@@ -782,4 +782,40 @@ test_expect_success 'filtering by size has no effect if support for it is not ad
782 test_i18ngrep "filtering not recognized by server" err
783 '
784
785 +fetch_filter_blob_limit_zero () {
786 + SERVER="$1"
787 + URL="$2"
788 +
789 + rm -rf "$SERVER" client &&
790 + test_create_repo "$SERVER" &&
791 + test_commit -C "$SERVER" one &&
792 + test_config -C "$SERVER" uploadpack.allowfilter 1 &&
793 +
794 + git clone "$URL" client &&
795 + test_config -C client extensions.partialclone origin &&
796 +
797 + test_commit -C "$SERVER" two &&
798 +
799 + git -C client fetch --filter=blob:limit=0 origin HEAD:somewhere &&
800 +
801 + # Ensure that commit is fetched, but blob is not
802 + test_config -C client extensions.partialclone "arbitrary string" &&
803 + git -C client cat-file -e $(git -C "$SERVER" rev-parse two) &&
804 + test_must_fail git -C client cat-file -e $(git hash-object "$SERVER/two.t")
805 +}
806 +
807 +test_expect_success 'fetch with --filter=blob:limit=0' '
808 + fetch_filter_blob_limit_zero server server
809 +'
810 +
811 +. "$TEST_DIRECTORY"/lib-httpd.sh
812 +start_httpd
813 +
814 +test_expect_success 'fetch with --filter=blob:limit=0 and HTTP' '
815 + fetch_filter_blob_limit_zero "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
816 +'
817 +
818 +stop_httpd
819 +
820 +
821 test_done