clone: partial clone

Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonathan Tan committed Dec 8, 2017 at 15:58 UTC 548719fbdc4ccd5d678afafbb5e3b5c8dac28489
2 files changed +69 -2
builtin/clone.c
+20 -2
@@ -26,6 +26,7 @@
26 #include "run-command.h"
27 #include "connected.h"
28 #include "packfile.h"
29 +#include "list-objects-filter-options.h"
30
31 /*
32 * Overall FIXMEs:
@@ -60,6 +61,7 @@ static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
61 static int option_dissociate;
62 static int max_jobs = -1;
63 static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
64 +static struct list_objects_filter_options filter_options;
65
66 static int recurse_submodules_cb(const struct option *opt,
67 const char *arg, int unset)
@@ -135,6 +137,7 @@ static struct option builtin_clone_options[] = {
137 TRANSPORT_FAMILY_IPV4),
138 OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
139 TRANSPORT_FAMILY_IPV6),
140 + OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
141 OPT_END()
142 };
143
@@ -886,6 +889,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
889 struct refspec *refspec;
890 const char *fetch_pattern;
891
892 + fetch_if_missing = 0;
893 +
894 packet_trace_identity("clone");
895 argc = parse_options(argc, argv, prefix, builtin_clone_options,
896 builtin_clone_usage, 0);
@@ -1073,6 +1078,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
1078 warning(_("--shallow-since is ignored in local clones; use file:// instead."));
1079 if (option_not.nr)
1080 warning(_("--shallow-exclude is ignored in local clones; use file:// instead."));
1081 + if (filter_options.choice)
1082 + warning(_("--filter is ignored in local clones; use file:// instead."));
1083 if (!access(mkpath("%s/shallow", path), F_OK)) {
1084 if (option_local > 0)
1085 warning(_("source repository is shallow, ignoring --local"));
@@ -1104,7 +1111,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
1111 transport_set_option(transport, TRANS_OPT_UPLOADPACK,
1112 option_upload_pack);
1113
1107 - if (transport->smart_options && !deepen)
1114 + if (filter_options.choice) {
1115 + transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
1116 + filter_options.filter_spec);
1117 + transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
1118 + }
1119 +
1120 + if (transport->smart_options && !deepen && !filter_options.choice)
1121 transport->smart_options->check_self_contained_and_connected = 1;
1122
1123 refs = transport_get_remote_refs(transport);
@@ -1164,13 +1177,17 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
1177 write_refspec_config(src_ref_prefix, our_head_points_at,
1178 remote_head_points_at, &branch_top);
1179
1180 + if (filter_options.choice)
1181 + partial_clone_register("origin", &filter_options);
1182 +
1183 if (is_local)
1184 clone_local(path, git_dir);
1185 else if (refs && complete_refs_before_fetch)
1186 transport_fetch_refs(transport, mapped_refs);
1187
1188 update_remote_refs(refs, mapped_refs, remote_head_points_at,
1173 - branch_top.buf, reflog_msg.buf, transport, !is_local);
1189 + branch_top.buf, reflog_msg.buf, transport,
1190 + !is_local && !filter_options.choice);
1191
1192 update_head(our_head_points_at, remote_head, reflog_msg.buf);
1193
@@ -1191,6 +1208,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
1208 }
1209
1210 junk_mode = JUNK_LEAVE_REPO;
1211 + fetch_if_missing = 1;
1212 err = checkout(submodule_progress);
1213
1214 strbuf_release(&reflog_msg);
t/t5601-clone.sh
+49
@@ -571,4 +571,53 @@ test_expect_success 'GIT_TRACE_PACKFILE produces a usable pack' '
571 git -C replay.git index-pack -v --stdin <tmp.pack
572 '
573
574 +partial_clone () {
575 + SERVER="$1" &&
576 + URL="$2" &&
577 +
578 + rm -rf "$SERVER" client &&
579 + test_create_repo "$SERVER" &&
580 + test_commit -C "$SERVER" one &&
581 + HASH1=$(git hash-object "$SERVER/one.t") &&
582 + git -C "$SERVER" revert HEAD &&
583 + test_commit -C "$SERVER" two &&
584 + HASH2=$(git hash-object "$SERVER/two.t") &&
585 + test_config -C "$SERVER" uploadpack.allowfilter 1 &&
586 + test_config -C "$SERVER" uploadpack.allowanysha1inwant 1 &&
587 +
588 + git clone --filter=blob:limit=0 "$URL" client &&
589 +
590 + git -C client fsck &&
591 +
592 + # Ensure that unneeded blobs are not inadvertently fetched.
593 + test_config -C client extensions.partialclone "not a remote" &&
594 + test_must_fail git -C client cat-file -e "$HASH1" &&
595 +
596 + # But this blob was fetched, because clone performs an initial checkout
597 + git -C client cat-file -e "$HASH2"
598 +}
599 +
600 +test_expect_success 'partial clone' '
601 + partial_clone server "file://$(pwd)/server"
602 +'
603 +
604 +test_expect_success 'partial clone: warn if server does not support object filtering' '
605 + rm -rf server client &&
606 + test_create_repo server &&
607 + test_commit -C server one &&
608 +
609 + git clone --filter=blob:limit=0 "file://$(pwd)/server" client 2> err &&
610 +
611 + test_i18ngrep "filtering not recognized by server" err
612 +'
613 +
614 +. "$TEST_DIRECTORY"/lib-httpd.sh
615 +start_httpd
616 +
617 +test_expect_success 'partial clone using HTTP' '
618 + partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
619 +'
620 +
621 +stop_httpd
622 +
623 test_done