From b5f62ebea55a07dbd9f68f33f5efbd0437946220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:43 +0700 Subject: remote-curl.c: convert fetch_git() to use argv_array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/remote-curl.c b/remote-curl.c index c704857..fd030c1 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -725,38 +725,28 @@ static int fetch_git(struct discovery *heads, { struct rpc_state rpc; struct strbuf preamble = STRBUF_INIT; - char *depth_arg = NULL; - int argc = 0, i, err; - const char *argv[17]; - - argv[argc++] = "fetch-pack"; - argv[argc++] = "--stateless-rpc"; - argv[argc++] = "--stdin"; - argv[argc++] = "--lock-pack"; + int i, err; + struct argv_array args = ARGV_ARRAY_INIT; + + argv_array_pushl(&args, "fetch-pack", "--stateless-rpc", + "--stdin", "--lock-pack", NULL); if (options.followtags) - argv[argc++] = "--include-tag"; + argv_array_push(&args, "--include-tag"); if (options.thin) - argv[argc++] = "--thin"; - if (options.verbosity >= 3) { - argv[argc++] = "-v"; - argv[argc++] = "-v"; - } + argv_array_push(&args, "--thin"); + if (options.verbosity >= 3) + argv_array_pushl(&args, "-v", "-v", NULL); if (options.check_self_contained_and_connected) - argv[argc++] = "--check-self-contained-and-connected"; + argv_array_push(&args, "--check-self-contained-and-connected"); if (options.cloning) - argv[argc++] = "--cloning"; + argv_array_push(&args, "--cloning"); if (options.update_shallow) - argv[argc++] = "--update-shallow"; + argv_array_push(&args, "--update-shallow"); if (!options.progress) - argv[argc++] = "--no-progress"; - if (options.depth) { - struct strbuf buf = STRBUF_INIT; - strbuf_addf(&buf, "--depth=%lu", options.depth); - depth_arg = strbuf_detach(&buf, NULL); - argv[argc++] = depth_arg; - } - argv[argc++] = url.buf; - argv[argc++] = NULL; + argv_array_push(&args, "--no-progress"); + if (options.depth) + argv_array_pushf(&args, "--depth=%lu", options.depth); + argv_array_push(&args, url.buf); for (i = 0; i < nr_heads; i++) { struct ref *ref = to_fetch[i]; @@ -769,7 +759,7 @@ static int fetch_git(struct discovery *heads, memset(&rpc, 0, sizeof(rpc)); rpc.service_name = "git-upload-pack", - rpc.argv = argv; + rpc.argv = args.argv; rpc.stdin_preamble = &preamble; rpc.gzip_request = 1; @@ -778,7 +768,7 @@ static int fetch_git(struct discovery *heads, write_or_die(1, rpc.result.buf, rpc.result.len); strbuf_release(&rpc.result); strbuf_release(&preamble); - free(depth_arg); + argv_array_clear(&args); return err; } -- cgit v0.10.2-6-g49f6 From 9318c5dd140b55c0384843c57b4b24a378cd5068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:44 +0700 Subject: transport-helper.c: refactor set_helper_option() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For now we can handle two types, string and boolean, in set_helper_option(). Later on we'll add string_list support, which does not fit well. The new function strbuf_set_helper_option() can be reused for a separate function that handles string-list. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/transport-helper.c b/transport-helper.c index a6bff8b..27a34e9 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -260,6 +260,28 @@ static const char *boolean_options[] = { TRANS_OPT_FOLLOWTAGS, }; +static int strbuf_set_helper_option(struct helper_data *data, + struct strbuf *buf) +{ + int ret; + + sendline(data, buf); + if (recvline(data, buf)) + exit(128); + + if (!strcmp(buf->buf, "ok")) + ret = 0; + else if (starts_with(buf->buf, "error")) + ret = -1; + else if (!strcmp(buf->buf, "unsupported")) + ret = 1; + else { + warning("%s unexpectedly said: '%s'", data->name, buf->buf); + ret = 1; + } + return ret; +} + static int set_helper_option(struct transport *transport, const char *name, const char *value) { @@ -291,20 +313,7 @@ static int set_helper_option(struct transport *transport, quote_c_style(value, &buf, NULL, 0); strbuf_addch(&buf, '\n'); - sendline(data, &buf); - if (recvline(data, &buf)) - exit(128); - - if (!strcmp(buf.buf, "ok")) - ret = 0; - else if (starts_with(buf.buf, "error")) { - ret = -1; - } else if (!strcmp(buf.buf, "unsupported")) - ret = 1; - else { - warning("%s unexpectedly said: '%s'", data->name, buf.buf); - ret = 1; - } + ret = strbuf_set_helper_option(data, &buf); strbuf_release(&buf); return ret; } -- cgit v0.10.2-6-g49f6 From e8e44de7874f6ea7cdf35b526f790cc7fa8f34e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:45 +0700 Subject: upload-pack: move shallow deepen code out of receive_needs() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a prep step for further refactoring. Besides reindentation and s/shallows\./shallows->/g, no other changes are expected. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/upload-pack.c b/upload-pack.c index b3f6653..97ed620 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -538,6 +538,55 @@ error: } } +static void deepen(int depth, const struct object_array *shallows) +{ + struct commit_list *result = NULL, *backup = NULL; + int i; + if (depth == INFINITE_DEPTH && !is_repository_shallow()) + for (i = 0; i < shallows->nr; i++) { + struct object *object = shallows->objects[i].item; + object->flags |= NOT_SHALLOW; + } + else + backup = result = + get_shallow_commits(&want_obj, depth, + SHALLOW, NOT_SHALLOW); + while (result) { + struct object *object = &result->item->object; + if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { + packet_write(1, "shallow %s", + oid_to_hex(&object->oid)); + register_shallow(object->oid.hash); + shallow_nr++; + } + result = result->next; + } + free_commit_list(backup); + for (i = 0; i < shallows->nr; i++) { + struct object *object = shallows->objects[i].item; + if (object->flags & NOT_SHALLOW) { + struct commit_list *parents; + packet_write(1, "unshallow %s", + oid_to_hex(&object->oid)); + object->flags &= ~CLIENT_SHALLOW; + /* make sure the real parents are parsed */ + unregister_shallow(object->oid.hash); + object->parsed = 0; + parse_commit_or_die((struct commit *)object); + parents = ((struct commit *)object)->parents; + while (parents) { + add_object_array(&parents->item->object, + NULL, &want_obj); + parents = parents->next; + } + add_object_array(object, NULL, &extra_edge_obj); + } + /* make sure commit traversal conforms to client */ + register_shallow(object->oid.hash); + } + packet_flush(1); +} + static void receive_needs(void) { struct object_array shallows = OBJECT_ARRAY_INIT; @@ -630,53 +679,9 @@ static void receive_needs(void) if (depth == 0 && shallows.nr == 0) return; - if (depth > 0) { - struct commit_list *result = NULL, *backup = NULL; - int i; - if (depth == INFINITE_DEPTH && !is_repository_shallow()) - for (i = 0; i < shallows.nr; i++) { - struct object *object = shallows.objects[i].item; - object->flags |= NOT_SHALLOW; - } - else - backup = result = - get_shallow_commits(&want_obj, depth, - SHALLOW, NOT_SHALLOW); - while (result) { - struct object *object = &result->item->object; - if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { - packet_write(1, "shallow %s", - oid_to_hex(&object->oid)); - register_shallow(object->oid.hash); - shallow_nr++; - } - result = result->next; - } - free_commit_list(backup); - for (i = 0; i < shallows.nr; i++) { - struct object *object = shallows.objects[i].item; - if (object->flags & NOT_SHALLOW) { - struct commit_list *parents; - packet_write(1, "unshallow %s", - oid_to_hex(&object->oid)); - object->flags &= ~CLIENT_SHALLOW; - /* make sure the real parents are parsed */ - unregister_shallow(object->oid.hash); - object->parsed = 0; - parse_commit_or_die((struct commit *)object); - parents = ((struct commit *)object)->parents; - while (parents) { - add_object_array(&parents->item->object, - NULL, &want_obj); - parents = parents->next; - } - add_object_array(object, NULL, &extra_edge_obj); - } - /* make sure commit traversal conforms to client */ - register_shallow(object->oid.hash); - } - packet_flush(1); - } else + if (depth > 0) + deepen(depth, &shallows); + else if (shallows.nr > 0) { int i; for (i = 0; i < shallows.nr; i++) -- cgit v0.10.2-6-g49f6 From 5c24cdea1e33c72bfed4af25a363eb5ceae11199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:46 +0700 Subject: upload-pack: move "shallow" sending code out of deepen() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/upload-pack.c b/upload-pack.c index 97ed620..0eb9a0b 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -538,6 +538,20 @@ error: } } +static void send_shallow(struct commit_list *result) +{ + while (result) { + struct object *object = &result->item->object; + if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { + packet_write(1, "shallow %s", + oid_to_hex(&object->oid)); + register_shallow(object->oid.hash); + shallow_nr++; + } + result = result->next; + } +} + static void deepen(int depth, const struct object_array *shallows) { struct commit_list *result = NULL, *backup = NULL; @@ -551,16 +565,7 @@ static void deepen(int depth, const struct object_array *shallows) backup = result = get_shallow_commits(&want_obj, depth, SHALLOW, NOT_SHALLOW); - while (result) { - struct object *object = &result->item->object; - if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { - packet_write(1, "shallow %s", - oid_to_hex(&object->oid)); - register_shallow(object->oid.hash); - shallow_nr++; - } - result = result->next; - } + send_shallow(result); free_commit_list(backup); for (i = 0; i < shallows->nr; i++) { struct object *object = shallows->objects[i].item; -- cgit v0.10.2-6-g49f6 From ef635b9056e23c06b4b5bcb4de294c1f6d9dda24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:47 +0700 Subject: upload-pack: remove unused variable "backup" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the last patch, "result" and "backup" are the same. "result" used to move, but the movement is now contained in send_shallow(). Delete this redundant variable. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/upload-pack.c b/upload-pack.c index 0eb9a0b..ee5d20b 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -554,7 +554,7 @@ static void send_shallow(struct commit_list *result) static void deepen(int depth, const struct object_array *shallows) { - struct commit_list *result = NULL, *backup = NULL; + struct commit_list *result = NULL; int i; if (depth == INFINITE_DEPTH && !is_repository_shallow()) for (i = 0; i < shallows->nr; i++) { @@ -562,11 +562,10 @@ static void deepen(int depth, const struct object_array *shallows) object->flags |= NOT_SHALLOW; } else - backup = result = - get_shallow_commits(&want_obj, depth, - SHALLOW, NOT_SHALLOW); + result = get_shallow_commits(&want_obj, depth, + SHALLOW, NOT_SHALLOW); send_shallow(result); - free_commit_list(backup); + free_commit_list(result); for (i = 0; i < shallows->nr; i++) { struct object *object = shallows->objects[i].item; if (object->flags & NOT_SHALLOW) { -- cgit v0.10.2-6-g49f6 From 873700c92e01dbd5ccbe5d36878bb96d1bef3c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:48 +0700 Subject: upload-pack: move "unshallow" sending code out of deepen() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also add some more comments in this code because it takes too long to understand what it does (to me, who should be familiar enough to understand this code well!) Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/upload-pack.c b/upload-pack.c index ee5d20b..73a8b28 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -552,20 +552,10 @@ static void send_shallow(struct commit_list *result) } } -static void deepen(int depth, const struct object_array *shallows) +static void send_unshallow(const struct object_array *shallows) { - struct commit_list *result = NULL; int i; - if (depth == INFINITE_DEPTH && !is_repository_shallow()) - for (i = 0; i < shallows->nr; i++) { - struct object *object = shallows->objects[i].item; - object->flags |= NOT_SHALLOW; - } - else - result = get_shallow_commits(&want_obj, depth, - SHALLOW, NOT_SHALLOW); - send_shallow(result); - free_commit_list(result); + for (i = 0; i < shallows->nr; i++) { struct object *object = shallows->objects[i].item; if (object->flags & NOT_SHALLOW) { @@ -573,7 +563,13 @@ static void deepen(int depth, const struct object_array *shallows) packet_write(1, "unshallow %s", oid_to_hex(&object->oid)); object->flags &= ~CLIENT_SHALLOW; - /* make sure the real parents are parsed */ + /* + * We want to _register_ "object" as shallow, but we + * also need to traverse object's parents to deepen a + * shallow clone. Unregister it for now so we can + * parse and add the parents to the want list, then + * re-register it. + */ unregister_shallow(object->oid.hash); object->parsed = 0; parse_commit_or_die((struct commit *)object); @@ -588,6 +584,27 @@ static void deepen(int depth, const struct object_array *shallows) /* make sure commit traversal conforms to client */ register_shallow(object->oid.hash); } +} + +static void deepen(int depth, const struct object_array *shallows) +{ + if (depth == INFINITE_DEPTH && !is_repository_shallow()) { + int i; + + for (i = 0; i < shallows->nr; i++) { + struct object *object = shallows->objects[i].item; + object->flags |= NOT_SHALLOW; + } + } else { + struct commit_list *result; + + result = get_shallow_commits(&want_obj, depth, + SHALLOW, NOT_SHALLOW); + send_shallow(result); + free_commit_list(result); + } + + send_unshallow(shallows); packet_flush(1); } -- cgit v0.10.2-6-g49f6 From 8bf3b75841ead5a796b62a54534b5a203adffae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:49 +0700 Subject: upload-pack: use skip_prefix() instead of starts_with() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/upload-pack.c b/upload-pack.c index 73a8b28..fa7ce09 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -276,7 +276,7 @@ static void create_pack_file(void) die("git upload-pack: %s", abort_msg); } -static int got_sha1(char *hex, unsigned char *sha1) +static int got_sha1(const char *hex, unsigned char *sha1) { struct object *o; int we_knew_they_have = 0; @@ -382,6 +382,8 @@ static int get_common_commits(void) for (;;) { char *line = packet_read_line(0, NULL); + const char *arg; + reset_timeout(); if (!line) { @@ -403,8 +405,8 @@ static int get_common_commits(void) got_other = 0; continue; } - if (starts_with(line, "have ")) { - switch (got_sha1(line+5, sha1)) { + if (skip_prefix(line, "have ", &arg)) { + switch (got_sha1(arg, sha1)) { case -1: /* they have what we do not */ got_other = 1; if (multi_ack && ok_to_give_up()) { @@ -620,14 +622,16 @@ static void receive_needs(void) const char *features; unsigned char sha1_buf[20]; char *line = packet_read_line(0, NULL); + const char *arg; + reset_timeout(); if (!line) break; - if (starts_with(line, "shallow ")) { + if (skip_prefix(line, "shallow ", &arg)) { unsigned char sha1[20]; struct object *object; - if (get_sha1_hex(line + 8, sha1)) + if (get_sha1_hex(arg, sha1)) die("invalid shallow line: %s", line); object = parse_object(sha1); if (!object) @@ -640,19 +644,19 @@ static void receive_needs(void) } continue; } - if (starts_with(line, "deepen ")) { + if (skip_prefix(line, "deepen ", &arg)) { char *end; - depth = strtol(line + 7, &end, 0); - if (end == line + 7 || depth <= 0) + depth = strtol(arg, &end, 0); + if (end == arg || depth <= 0) die("Invalid deepen: %s", line); continue; } - if (!starts_with(line, "want ") || - get_sha1_hex(line+5, sha1_buf)) + if (!skip_prefix(line, "want ", &arg) || + get_sha1_hex(arg, sha1_buf)) die("git upload-pack: protocol error, " "expected to get sha, not '%s'", line); - features = line + 45; + features = arg + 40; if (parse_feature_request(features, "multi_ack_detailed")) multi_ack = 2; @@ -859,7 +863,7 @@ int main(int argc, char **argv) check_replace_refs = 0; for (i = 1; i < argc; i++) { - char *arg = argv[i]; + const char *arg = argv[i]; if (arg[0] != '-') break; @@ -875,8 +879,8 @@ int main(int argc, char **argv) strict = 1; continue; } - if (starts_with(arg, "--timeout=")) { - timeout = atoi(arg+10); + if (skip_prefix(arg, "--timeout=", &arg)) { + timeout = atoi(arg); daemon_mode = 1; continue; } -- cgit v0.10.2-6-g49f6 From 6e414e30fd12460d7a7ba3ce67d20aa82a41a737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:50 +0700 Subject: upload-pack: tighten number parsing at "deepen" lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/upload-pack.c b/upload-pack.c index fa7ce09..8f4d7f4 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -645,9 +645,9 @@ static void receive_needs(void) continue; } if (skip_prefix(line, "deepen ", &arg)) { - char *end; + char *end = NULL; depth = strtol(arg, &end, 0); - if (end == arg || depth <= 0) + if (!end || *end || depth <= 0) die("Invalid deepen: %s", line); continue; } -- cgit v0.10.2-6-g49f6 From 7fcbd37f9c1b7413b408d0223344d070613777ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:51 +0700 Subject: upload-pack: make check_non_tip() clean things up on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On error check_non_tip() will die and not closing file descriptors is no big deal. The next patch will split the majority of this function out for reuse in other cases, where die() may not be the only outcome. Same story for popping SIGPIPE out of the signal chain. So let's make sure we clean things up properly first. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/upload-pack.c b/upload-pack.c index 8f4d7f4..7ce97ec 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -475,16 +475,16 @@ static void check_non_tip(void) cmd.in = -1; cmd.out = -1; - if (start_command(&cmd)) - goto error; - /* - * If rev-list --stdin encounters an unknown commit, it - * terminates, which will cause SIGPIPE in the write loop + * If the next rev-list --stdin encounters an unknown commit, + * it terminates, which will cause SIGPIPE in the write loop * below. */ sigchain_push(SIGPIPE, SIG_IGN); + if (start_command(&cmd)) + goto error; + namebuf[0] = '^'; namebuf[41] = '\n'; for (i = get_max_object_index(); 0 < i; ) { @@ -507,8 +507,7 @@ static void check_non_tip(void) goto error; } close(cmd.in); - - sigchain_pop(SIGPIPE); + cmd.in = -1; /* * The commits out of the rev-list are not ancestors of @@ -518,6 +517,7 @@ static void check_non_tip(void) if (i) goto error; close(cmd.out); + cmd.out = -1; /* * rev-list may have died by encountering a bad commit @@ -527,10 +527,19 @@ static void check_non_tip(void) if (finish_command(&cmd)) goto error; + sigchain_pop(SIGPIPE); + /* All the non-tip ones are ancestors of what we advertised */ return; error: + sigchain_pop(SIGPIPE); + + if (cmd.in >= 0) + close(cmd.in); + if (cmd.out >= 0) + close(cmd.out); + /* Pick one of them (we know there at least is one) */ for (i = 0; i < want_obj.nr; i++) { o = want_obj.objects[i].item; -- cgit v0.10.2-6-g49f6 From 3f0f6624f554a663ca0c83fd80f769dc9cef93e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:52 +0700 Subject: upload-pack: move rev-list code out of check_non_tip() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/upload-pack.c b/upload-pack.c index 7ce97ec..93c0522 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -451,7 +451,7 @@ static int is_our_ref(struct object *o) return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF); } -static void check_non_tip(void) +static int has_unreachable(struct object_array *src) { static const char *argv[] = { "rev-list", "--stdin", NULL, @@ -461,14 +461,6 @@ static void check_non_tip(void) char namebuf[42]; /* ^ + SHA-1 + LF */ int i; - /* - * In the normal in-process case without - * uploadpack.allowReachableSHA1InWant, - * non-tip requests can never happen. - */ - if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1)) - goto error; - cmd.argv = argv; cmd.git_cmd = 1; cmd.no_stderr = 1; @@ -498,8 +490,8 @@ static void check_non_tip(void) goto error; } namebuf[40] = '\n'; - for (i = 0; i < want_obj.nr; i++) { - o = want_obj.objects[i].item; + for (i = 0; i < src->nr; i++) { + o = src->objects[i].item; if (is_our_ref(o)) continue; memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ); @@ -530,7 +522,7 @@ static void check_non_tip(void) sigchain_pop(SIGPIPE); /* All the non-tip ones are ancestors of what we advertised */ - return; + return 0; error: sigchain_pop(SIGPIPE); @@ -539,10 +531,28 @@ error: close(cmd.in); if (cmd.out >= 0) close(cmd.out); + return 1; +} +static void check_non_tip(void) +{ + int i; + + /* + * In the normal in-process case without + * uploadpack.allowReachableSHA1InWant, + * non-tip requests can never happen. + */ + if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1)) + goto error; + if (!has_unreachable(&want_obj)) + /* All the non-tip ones are ancestors of what we advertised */ + return; + +error: /* Pick one of them (we know there at least is one) */ for (i = 0; i < want_obj.nr; i++) { - o = want_obj.objects[i].item; + struct object *o = want_obj.objects[i].item; if (!is_our_ref(o)) die("git upload-pack: not our ref %s", oid_to_hex(&o->oid)); -- cgit v0.10.2-6-g49f6 From 45a3e52641fd58c2ebaba2fd2a03308fd6ab8a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:53 +0700 Subject: fetch-pack: use skip_prefix() instead of starts_with() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 9b2a514..8332d3d 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -59,12 +59,12 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) for (i = 1; i < argc && *argv[i] == '-'; i++) { const char *arg = argv[i]; - if (starts_with(arg, "--upload-pack=")) { - args.uploadpack = arg + 14; + if (skip_prefix(arg, "--upload-pack=", &arg)) { + args.uploadpack = arg; continue; } - if (starts_with(arg, "--exec=")) { - args.uploadpack = arg + 7; + if (skip_prefix(arg, "--exec=", &arg)) { + args.uploadpack = arg; continue; } if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) { @@ -100,8 +100,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) args.verbose = 1; continue; } - if (starts_with(arg, "--depth=")) { - args.depth = strtol(arg + 8, NULL, 0); + if (skip_prefix(arg, "--depth=", &arg)) { + args.depth = strtol(arg, NULL, 0); continue; } if (!strcmp("--no-progress", arg)) { -- cgit v0.10.2-6-g49f6 From 0d789a5bc1aeef2d6d0d3120efc4b85766a0a327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:54 +0700 Subject: fetch-pack: use a common function for verbose printing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reduces the number of "if (verbose)" which makes it a bit easier to read imo. It also makes it easier to redirect all these printouts, to a file for example. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/fetch-pack.c b/fetch-pack.c index 01e34b6..4020744 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -50,6 +50,21 @@ static int non_common_revs, multi_ack, use_sideband; #define ALLOW_REACHABLE_SHA1 02 static unsigned int allow_unadvertised_object_request; +__attribute__((format (printf, 2, 3))) +static inline void print_verbose(const struct fetch_pack_args *args, + const char *fmt, ...) +{ + va_list params; + + if (!args->verbose) + return; + + va_start(params, fmt); + vfprintf(stderr, fmt, params); + va_end(params); + fputc('\n', stderr); +} + static void rev_list_push(struct commit *commit, int mark) { if (!(commit->object.flags & mark)) { @@ -375,8 +390,7 @@ static int find_common(struct fetch_pack_args *args, retval = -1; while ((sha1 = get_rev())) { packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1)); - if (args->verbose) - fprintf(stderr, "have %s\n", sha1_to_hex(sha1)); + print_verbose(args, "have %s", sha1_to_hex(sha1)); in_vain++; if (flush_at <= ++count) { int ack; @@ -397,9 +411,9 @@ static int find_common(struct fetch_pack_args *args, consume_shallow_list(args, fd[0]); do { ack = get_ack(fd[0], result_sha1); - if (args->verbose && ack) - fprintf(stderr, "got ack %d %s\n", ack, - sha1_to_hex(result_sha1)); + if (ack) + print_verbose(args, "got ack %d %s", ack, + sha1_to_hex(result_sha1)); switch (ack) { case ACK: flushes = 0; @@ -438,8 +452,7 @@ static int find_common(struct fetch_pack_args *args, } while (ack); flushes--; if (got_continue && MAX_IN_VAIN < in_vain) { - if (args->verbose) - fprintf(stderr, "giving up\n"); + print_verbose(args, "giving up"); break; /* give up */ } } @@ -449,8 +462,7 @@ done: packet_buf_write(&req_buf, "done\n"); send_request(args, fd[1], &req_buf); } - if (args->verbose) - fprintf(stderr, "done\n"); + print_verbose(args, "done"); if (retval != 0) { multi_ack = 0; flushes++; @@ -462,9 +474,8 @@ done: while (flushes || multi_ack) { int ack = get_ack(fd[0], result_sha1); if (ack) { - if (args->verbose) - fprintf(stderr, "got ack (%d) %s\n", ack, - sha1_to_hex(result_sha1)); + print_verbose(args, "got ack (%d) %s", ack, + sha1_to_hex(result_sha1)); if (ack == ACK) return 0; multi_ack = 1; @@ -509,9 +520,8 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args, unsigned long cutoff) { while (complete && cutoff <= complete->item->date) { - if (args->verbose) - fprintf(stderr, "Marking %s as complete\n", - oid_to_hex(&complete->item->object.oid)); + print_verbose(args, "Marking %s as complete", + oid_to_hex(&complete->item->object.oid)); pop_most_recent_commit(&complete, COMPLETE); } } @@ -652,18 +662,12 @@ static int everything_local(struct fetch_pack_args *args, o = lookup_object(remote); if (!o || !(o->flags & COMPLETE)) { retval = 0; - if (!args->verbose) - continue; - fprintf(stderr, - "want %s (%s)\n", sha1_to_hex(remote), - ref->name); + print_verbose(args, "want %s (%s)", sha1_to_hex(remote), + ref->name); continue; } - if (!args->verbose) - continue; - fprintf(stderr, - "already have %s (%s)\n", sha1_to_hex(remote), - ref->name); + print_verbose(args, "already have %s (%s)", sha1_to_hex(remote), + ref->name); } return retval; } @@ -810,39 +814,32 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow")) die("Server does not support shallow clients"); if (server_supports("multi_ack_detailed")) { - if (args->verbose) - fprintf(stderr, "Server supports multi_ack_detailed\n"); + print_verbose(args, "Server supports multi_ack_detailed"); multi_ack = 2; if (server_supports("no-done")) { - if (args->verbose) - fprintf(stderr, "Server supports no-done\n"); + print_verbose(args, "Server supports no-done"); if (args->stateless_rpc) no_done = 1; } } else if (server_supports("multi_ack")) { - if (args->verbose) - fprintf(stderr, "Server supports multi_ack\n"); + print_verbose(args, "Server supports multi_ack"); multi_ack = 1; } if (server_supports("side-band-64k")) { - if (args->verbose) - fprintf(stderr, "Server supports side-band-64k\n"); + print_verbose(args, "Server supports side-band-64k"); use_sideband = 2; } else if (server_supports("side-band")) { - if (args->verbose) - fprintf(stderr, "Server supports side-band\n"); + print_verbose(args, "Server supports side-band"); use_sideband = 1; } if (server_supports("allow-tip-sha1-in-want")) { - if (args->verbose) - fprintf(stderr, "Server supports allow-tip-sha1-in-want\n"); + print_verbose(args, "Server supports allow-tip-sha1-in-want"); allow_unadvertised_object_request |= ALLOW_TIP_SHA1; } if (server_supports("allow-reachable-sha1-in-want")) { - if (args->verbose) - fprintf(stderr, "Server supports allow-reachable-sha1-in-want\n"); + print_verbose(args, "Server supports allow-reachable-sha1-in-want"); allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1; } if (!server_supports("thin-pack")) @@ -851,17 +848,16 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, args->no_progress = 0; if (!server_supports("include-tag")) args->include_tag = 0; - if (server_supports("ofs-delta")) { - if (args->verbose) - fprintf(stderr, "Server supports ofs-delta\n"); - } else + if (server_supports("ofs-delta")) + print_verbose(args, "Server supports ofs-delta"); + else prefer_ofs_delta = 0; if ((agent_feature = server_feature_value("agent", &agent_len))) { agent_supported = 1; - if (args->verbose && agent_len) - fprintf(stderr, "Server version is %.*s\n", - agent_len, agent_feature); + if (agent_len) + print_verbose(args, "Server version is %.*s", + agent_len, agent_feature); } if (everything_local(args, &ref, sought, nr_sought)) { -- cgit v0.10.2-6-g49f6 From 1dd73e20d72921fe81a881a9f6cff6d02eca8146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:55 +0700 Subject: fetch-pack.c: mark strings for translating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/fetch-pack.c b/fetch-pack.c index 4020744..08caf1d 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -208,7 +208,7 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd) continue; if (starts_with(line, "unshallow ")) continue; - die("git fetch-pack: expected shallow list"); + die(_("git fetch-pack: expected shallow list")); } } } @@ -220,7 +220,7 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1) const char *arg; if (!len) - die("git fetch-pack: expected ACK/NAK, got EOF"); + die(_("git fetch-pack: expected ACK/NAK, got EOF")); if (!strcmp(line, "NAK")) return NAK; if (skip_prefix(line, "ACK ", &arg)) { @@ -238,7 +238,7 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1) return ACK; } } - die("git fetch_pack: expected ACK/NAK, got '%s'", line); + die(_("git fetch_pack: expected ACK/NAK, got '%s'"), line); } static void send_request(struct fetch_pack_args *args, @@ -285,7 +285,7 @@ static int find_common(struct fetch_pack_args *args, size_t state_len = 0; if (args->stateless_rpc && multi_ack == 1) - die("--stateless-rpc requires multi_ack_detailed"); + die(_("--stateless-rpc requires multi_ack_detailed")); if (marked) for_each_ref(clear_marks, NULL); marked = 1; @@ -357,23 +357,23 @@ static int find_common(struct fetch_pack_args *args, while ((line = packet_read_line(fd[0], NULL))) { if (skip_prefix(line, "shallow ", &arg)) { if (get_sha1_hex(arg, sha1)) - die("invalid shallow line: %s", line); + die(_("invalid shallow line: %s"), line); register_shallow(sha1); continue; } if (skip_prefix(line, "unshallow ", &arg)) { if (get_sha1_hex(arg, sha1)) - die("invalid unshallow line: %s", line); + die(_("invalid unshallow line: %s"), line); if (!lookup_object(sha1)) - die("object not found: %s", line); + die(_("object not found: %s"), line); /* make sure that it is parsed as shallow */ if (!parse_object(sha1)) - die("error in object: %s", line); + die(_("error in object: %s"), line); if (unregister_shallow(sha1)) - die("no shallow found: %s", line); + die(_("no shallow found: %s"), line); continue; } - die("expected shallow/unshallow, got %s", line); + die(_("expected shallow/unshallow, got %s"), line); } } else if (!args->stateless_rpc) send_request(args, fd[1], &req_buf); @@ -412,8 +412,8 @@ static int find_common(struct fetch_pack_args *args, do { ack = get_ack(fd[0], result_sha1); if (ack) - print_verbose(args, "got ack %d %s", ack, - sha1_to_hex(result_sha1)); + print_verbose(args, _("got %s %d %s"), "ack", + ack, sha1_to_hex(result_sha1)); switch (ack) { case ACK: flushes = 0; @@ -426,7 +426,7 @@ static int find_common(struct fetch_pack_args *args, struct commit *commit = lookup_commit(result_sha1); if (!commit) - die("invalid commit %s", sha1_to_hex(result_sha1)); + die(_("invalid commit %s"), sha1_to_hex(result_sha1)); if (args->stateless_rpc && ack == ACK_common && !(commit->object.flags & COMMON)) { @@ -452,7 +452,7 @@ static int find_common(struct fetch_pack_args *args, } while (ack); flushes--; if (got_continue && MAX_IN_VAIN < in_vain) { - print_verbose(args, "giving up"); + print_verbose(args, _("giving up")); break; /* give up */ } } @@ -462,7 +462,7 @@ done: packet_buf_write(&req_buf, "done\n"); send_request(args, fd[1], &req_buf); } - print_verbose(args, "done"); + print_verbose(args, _("done")); if (retval != 0) { multi_ack = 0; flushes++; @@ -474,8 +474,8 @@ done: while (flushes || multi_ack) { int ack = get_ack(fd[0], result_sha1); if (ack) { - print_verbose(args, "got ack (%d) %s", ack, - sha1_to_hex(result_sha1)); + print_verbose(args, _("got %s (%d) %s"), "ack", + ack, sha1_to_hex(result_sha1)); if (ack == ACK) return 0; multi_ack = 1; @@ -520,7 +520,7 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args, unsigned long cutoff) { while (complete && cutoff <= complete->item->date) { - print_verbose(args, "Marking %s as complete", + print_verbose(args, _("Marking %s as complete"), oid_to_hex(&complete->item->object.oid)); pop_most_recent_commit(&complete, COMPLETE); } @@ -666,7 +666,7 @@ static int everything_local(struct fetch_pack_args *args, ref->name); continue; } - print_verbose(args, "already have %s (%s)", sha1_to_hex(remote), + print_verbose(args, _("already have %s (%s)"), sha1_to_hex(remote), ref->name); } return retval; @@ -702,8 +702,7 @@ static int get_pack(struct fetch_pack_args *args, demux.data = xd; demux.out = -1; if (start_async(&demux)) - die("fetch-pack: unable to fork off sideband" - " demultiplexer"); + die(_("fetch-pack: unable to fork off sideband demultiplexer")); } else demux.out = xd[0]; @@ -711,7 +710,7 @@ static int get_pack(struct fetch_pack_args *args, if (!args->keep_pack && unpack_limit) { if (read_pack_header(demux.out, &header)) - die("protocol error: bad pack header"); + die(_("protocol error: bad pack header")); pass_header = 1; if (ntohl(header.hdr_entries) < unpack_limit) do_keep = 0; @@ -767,7 +766,7 @@ static int get_pack(struct fetch_pack_args *args, cmd.in = demux.out; cmd.git_cmd = 1; if (start_command(&cmd)) - die("fetch-pack: unable to fork off %s", cmd_name); + die(_("fetch-pack: unable to fork off %s"), cmd_name); if (do_keep && pack_lockfile) { *pack_lockfile = index_pack_lockfile(cmd.out); close(cmd.out); @@ -783,9 +782,9 @@ static int get_pack(struct fetch_pack_args *args, args->check_self_contained_and_connected && ret == 0; else - die("%s failed", cmd_name); + die(_("%s failed"), cmd_name); if (use_sideband && finish_async(&demux)) - die("error in sideband demultiplexer"); + die(_("error in sideband demultiplexer")); return 0; } @@ -812,34 +811,34 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name); if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow")) - die("Server does not support shallow clients"); + die(_("Server does not support shallow clients")); if (server_supports("multi_ack_detailed")) { - print_verbose(args, "Server supports multi_ack_detailed"); + print_verbose(args, _("Server supports multi_ack_detailed")); multi_ack = 2; if (server_supports("no-done")) { - print_verbose(args, "Server supports no-done"); + print_verbose(args, _("Server supports no-done")); if (args->stateless_rpc) no_done = 1; } } else if (server_supports("multi_ack")) { - print_verbose(args, "Server supports multi_ack"); + print_verbose(args, _("Server supports multi_ack")); multi_ack = 1; } if (server_supports("side-band-64k")) { - print_verbose(args, "Server supports side-band-64k"); + print_verbose(args, _("Server supports side-band-64k")); use_sideband = 2; } else if (server_supports("side-band")) { - print_verbose(args, "Server supports side-band"); + print_verbose(args, _("Server supports side-band")); use_sideband = 1; } if (server_supports("allow-tip-sha1-in-want")) { - print_verbose(args, "Server supports allow-tip-sha1-in-want"); + print_verbose(args, _("Server supports allow-tip-sha1-in-want")); allow_unadvertised_object_request |= ALLOW_TIP_SHA1; } if (server_supports("allow-reachable-sha1-in-want")) { - print_verbose(args, "Server supports allow-reachable-sha1-in-want"); + print_verbose(args, _("Server supports allow-reachable-sha1-in-want")); allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1; } if (!server_supports("thin-pack")) @@ -849,14 +848,14 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, if (!server_supports("include-tag")) args->include_tag = 0; if (server_supports("ofs-delta")) - print_verbose(args, "Server supports ofs-delta"); + print_verbose(args, _("Server supports ofs-delta")); else prefer_ofs_delta = 0; if ((agent_feature = server_feature_value("agent", &agent_len))) { agent_supported = 1; if (agent_len) - print_verbose(args, "Server version is %.*s", + print_verbose(args, _("Server version is %.*s"), agent_len, agent_feature); } @@ -869,7 +868,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, /* When cloning, it is not unusual to have * no common commit. */ - warning("no common commits"); + warning(_("no common commits")); if (args->stateless_rpc) packet_flush(fd[1]); @@ -881,7 +880,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, else alternate_shallow_file = NULL; if (get_pack(args, fd, pack_lockfile)) - die("git fetch-pack: fetch failed."); + die(_("git fetch-pack: fetch failed.")); all_done: return ref; @@ -1043,7 +1042,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args, if (!ref) { packet_flush(fd[1]); - die("no matching remote head"); + die(_("no matching remote head")); } prepare_shallow_info(&si, shallow); ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, -- cgit v0.10.2-6-g49f6 From 79891cb90a4e5b2680308ba2b757a218b9aaa14c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:56 +0700 Subject: fetch-pack: use a separate flag for fetch in deepening mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shallow repo could be deepened or shortened when then user gives --depth. But in future that won't be the only way to deepen/shorten a repo. Stop relying on args->depth in this mode. Future deepening methods can simply set this flag on instead of updating all these if expressions. The new name "deepen" was chosen after the command to define shallow boundary in pack protocol. New commands also follow this tradition. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/fetch-pack.c b/fetch-pack.c index 08caf1d..a14d24a 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -197,7 +197,7 @@ enum ack_type { static void consume_shallow_list(struct fetch_pack_args *args, int fd) { - if (args->stateless_rpc && args->depth > 0) { + if (args->stateless_rpc && args->deepen) { /* If we sent a depth we will get back "duplicate" * shallow and unshallow commands every time there * is a block of have lines exchanged. @@ -348,7 +348,7 @@ static int find_common(struct fetch_pack_args *args, packet_buf_flush(&req_buf); state_len = req_buf.len; - if (args->depth > 0) { + if (args->deepen) { char *line; const char *arg; unsigned char sha1[20]; @@ -557,7 +557,7 @@ static void filter_refs(struct fetch_pack_args *args, } if (!keep && args->fetch_all && - (!args->depth || !starts_with(ref->name, "refs/tags/"))) + (!args->deepen || !starts_with(ref->name, "refs/tags/"))) keep = 1; if (keep) { @@ -627,7 +627,7 @@ static int everything_local(struct fetch_pack_args *args, } } - if (!args->depth) { + if (!args->deepen) { for_each_ref(mark_complete_oid, NULL); for_each_alternate_ref(mark_alternate_complete, NULL); commit_list_sort_by_date(&complete); @@ -812,6 +812,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow")) die(_("Server does not support shallow clients")); + if (args->depth > 0) + args->deepen = 1; if (server_supports("multi_ack_detailed")) { print_verbose(args, _("Server supports multi_ack_detailed")); multi_ack = 2; @@ -872,7 +874,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, if (args->stateless_rpc) packet_flush(fd[1]); - if (args->depth > 0) + if (args->deepen) setup_alternate_shallow(&shallow_lock, &alternate_shallow_file, NULL); else if (si->nr_ours || si->nr_theirs) @@ -939,7 +941,7 @@ static void update_shallow(struct fetch_pack_args *args, int *status; int i; - if (args->depth > 0 && alternate_shallow_file) { + if (args->deepen && alternate_shallow_file) { if (*alternate_shallow_file == '\0') { /* --unshallow */ unlink_or_warn(git_path_shallow()); rollback_lock_file(&shallow_lock); diff --git a/fetch-pack.h b/fetch-pack.h index bb7fd76..4d0adb0 100644 --- a/fetch-pack.h +++ b/fetch-pack.h @@ -25,6 +25,7 @@ struct fetch_pack_args { unsigned self_contained_and_connected:1; unsigned cloning:1; unsigned update_shallow:1; + unsigned deepen:1; }; /* -- cgit v0.10.2-6-g49f6 From 3d9ff4d736475bc74ed249c579b3ad7322e22b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:57 +0700 Subject: shallow.c: implement a generic shallow boundary finder based on rev-list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of a custom commit walker like get_shallow_commits(), this new function uses rev-list to mark NOT_SHALLOW to all reachable commits, except borders. The definition of reachable is to be defined by the protocol later. This makes it more flexible to define shallow boundary. The way we find border is paint all reachable commits NOT_SHALLOW. Any of them that "touches" commits without NOT_SHALLOW flag are considered shallow (e.g. zero parents via grafting mechanism). Shallow commits and their true parents are all marked SHALLOW. Then NOT_SHALLOW is removed from shallow commits at the end. There is an interesting observation. With a generic walker, we can produce all kinds of shallow cutting. In the following graph, every commit but "x" is reachable. "b" is a parent of "a". x -- a -- o / / x -- c -- b -- o After this function is run, "a" and "c" are both considered shallow commits. After grafting occurs at the client side, what we see is a -- o / c -- b -- o Notice that because of grafting, "a" has zero parents, so "b" is no longer a parent of "a". This is unfortunate and may be solved in two ways. The first is change the way shallow grafting works and keep "a -- b" connection if "b" exists and always ends at shallow commits (iow, no loose ends). This is hard to detect, or at least not cheap to do. The second way is mark one "x" as shallow commit instead of "a" and produce this graph at client side: x -- a -- o / / c -- b -- o More commits, but simpler grafting rules. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/commit.h b/commit.h index 5d58be0..b717be1 100644 --- a/commit.h +++ b/commit.h @@ -258,6 +258,8 @@ extern int for_each_commit_graft(each_commit_graft_fn, void *); extern int is_repository_shallow(void); extern struct commit_list *get_shallow_commits(struct object_array *heads, int depth, int shallow_flag, int not_shallow_flag); +extern struct commit_list *get_shallow_commits_by_rev_list( + int ac, const char **av, int shallow_flag, int not_shallow_flag); extern void set_alternate_shallow_file(const char *path, int override); extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol, const struct sha1_array *extra); diff --git a/shallow.c b/shallow.c index 60f1505..40c2485 100644 --- a/shallow.c +++ b/shallow.c @@ -10,6 +10,8 @@ #include "diff.h" #include "revision.h" #include "commit-slab.h" +#include "revision.h" +#include "list-objects.h" static int is_shallow = -1; static struct stat_validity shallow_stat; @@ -137,6 +139,82 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, return result; } +static void show_commit(struct commit *commit, void *data) +{ + commit_list_insert(commit, data); +} + +/* + * Given rev-list arguments, run rev-list. All reachable commits + * except border ones are marked with not_shallow_flag. Border commits + * are marked with shallow_flag. The list of border/shallow commits + * are also returned. + */ +struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av, + int shallow_flag, + int not_shallow_flag) +{ + struct commit_list *result = NULL, *p; + struct commit_list *not_shallow_list = NULL; + struct rev_info revs; + int both_flags = shallow_flag | not_shallow_flag; + + /* + * SHALLOW (excluded) and NOT_SHALLOW (included) should not be + * set at this point. But better be safe than sorry. + */ + clear_object_flags(both_flags); + + is_repository_shallow(); /* make sure shallows are read */ + + init_revisions(&revs, NULL); + save_commit_buffer = 0; + setup_revisions(ac, av, &revs, NULL); + + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); + traverse_commit_list(&revs, show_commit, NULL, ¬_shallow_list); + + /* Mark all reachable commits as NOT_SHALLOW */ + for (p = not_shallow_list; p; p = p->next) + p->item->object.flags |= not_shallow_flag; + + /* + * mark border commits SHALLOW + NOT_SHALLOW. + * We cannot clear NOT_SHALLOW right now. Imagine border + * commit A is processed first, then commit B, whose parent is + * A, later. If NOT_SHALLOW on A is cleared at step 1, B + * itself is considered border at step 2, which is incorrect. + */ + for (p = not_shallow_list; p; p = p->next) { + struct commit *c = p->item; + struct commit_list *parent; + + if (parse_commit(c)) + die("unable to parse commit %s", + oid_to_hex(&c->object.oid)); + + for (parent = c->parents; parent; parent = parent->next) + if (!(parent->item->object.flags & not_shallow_flag)) { + c->object.flags |= shallow_flag; + commit_list_insert(c, &result); + break; + } + } + free_commit_list(not_shallow_list); + + /* + * Now we can clean up NOT_SHALLOW on border commits. Having + * both flags set can confuse the caller. + */ + for (p = result; p; p = p->next) { + struct object *o = &p->item->object; + if ((o->flags & both_flags) == both_flags) + o->flags &= ~not_shallow_flag; + } + return result; +} + static void check_shallow_file_for_update(void) { if (is_shallow == -1) -- cgit v0.10.2-6-g49f6 From 569e554be9cb88047d9f2752750e0c260241f446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:58 +0700 Subject: upload-pack: add deepen-since to cut shallow repos based on time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should allow the user to say "create a shallow clone containing the work from last year" (once the client side is fixed up, of course). In theory deepen-since and deepen (aka --depth) can be used together to draw the shallow boundary (whether it's intersection or union is up to discussion, but if rev-list is used, it's likely intersection). However, because deepen goes with a custom commit walker, we can't mix the two yet. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt index c6977bb..9251df1 100644 --- a/Documentation/technical/pack-protocol.txt +++ b/Documentation/technical/pack-protocol.txt @@ -219,7 +219,8 @@ out of what the server said it could do with the first 'want' line. shallow-line = PKT-LINE("shallow" SP obj-id) - depth-request = PKT-LINE("deepen" SP depth) + depth-request = PKT-LINE("deepen" SP depth) / + PKT-LINE("deepen-since" SP timestamp) first-want = PKT-LINE("want" SP obj-id SP capability-list) additional-want = PKT-LINE("want" SP obj-id) diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt index eaab6b4..f08cc4e 100644 --- a/Documentation/technical/protocol-capabilities.txt +++ b/Documentation/technical/protocol-capabilities.txt @@ -179,6 +179,15 @@ This capability adds "deepen", "shallow" and "unshallow" commands to the fetch-pack/upload-pack protocol so clients can request shallow clones. +deepen-since +------------ + +This capability adds "deepen-since" command to fetch-pack/upload-pack +protocol so the client can request shallow clones that are cut at a +specific time, instead of depth. Internally it's equivalent of doing +"rev-list --max-age=" on the server side. "deepen-since" +cannot be used with "deepen". + no-progress ----------- diff --git a/upload-pack.c b/upload-pack.c index 93c0522..5269461 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -14,6 +14,7 @@ #include "sigchain.h" #include "version.h" #include "string-list.h" +#include "argv-array.h" static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=] "; @@ -629,11 +630,25 @@ static void deepen(int depth, const struct object_array *shallows) packet_flush(1); } +static void deepen_by_rev_list(int ac, const char **av, + struct object_array *shallows) +{ + struct commit_list *result; + + result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW); + send_shallow(result); + free_commit_list(result); + send_unshallow(shallows); + packet_flush(1); +} + static void receive_needs(void) { struct object_array shallows = OBJECT_ARRAY_INIT; int depth = 0; int has_non_tip = 0; + unsigned long deepen_since = 0; + int deepen_rev_list = 0; shallow_nr = 0; for (;;) { @@ -670,6 +685,16 @@ static void receive_needs(void) die("Invalid deepen: %s", line); continue; } + if (skip_prefix(line, "deepen-since ", &arg)) { + char *end = NULL; + deepen_since = strtoul(arg, &end, 0); + if (!end || *end || !deepen_since || + /* revisions.c's max_age -1 is special */ + deepen_since == -1) + die("Invalid deepen-since: %s", line); + deepen_rev_list = 1; + continue; + } if (!skip_prefix(line, "want ", &arg) || get_sha1_hex(arg, sha1_buf)) die("git upload-pack: protocol error, " @@ -721,10 +746,26 @@ static void receive_needs(void) if (!use_sideband && daemon_mode) no_progress = 1; - if (depth == 0 && shallows.nr == 0) + if (depth == 0 && !deepen_rev_list && shallows.nr == 0) return; + if (depth > 0 && deepen_rev_list) + die("git upload-pack: deepen and deepen-since cannot be used together"); if (depth > 0) deepen(depth, &shallows); + else if (deepen_rev_list) { + struct argv_array av = ARGV_ARRAY_INIT; + int i; + + argv_array_push(&av, "rev-list"); + if (deepen_since) + argv_array_pushf(&av, "--max-age=%lu", deepen_since); + for (i = 0; i < want_obj.nr; i++) { + struct object *o = want_obj.objects[i].item; + argv_array_push(&av, oid_to_hex(&o->oid)); + } + deepen_by_rev_list(av.argc, av.argv, &shallows); + argv_array_clear(&av); + } else if (shallows.nr > 0) { int i; @@ -773,7 +814,7 @@ static int send_ref(const char *refname, const struct object_id *oid, int flag, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band" - " side-band-64k ofs-delta shallow no-progress" + " side-band-64k ofs-delta shallow deepen-since no-progress" " include-tag multi_ack_detailed"; const char *refname_nons = strip_namespace(refname); struct object_id peeled; -- cgit v0.10.2-6-g49f6 From 508ea88226bbdd25ac0aac4f9c8a65a2c3b5be5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:53:59 +0700 Subject: fetch: define shallow boundary with --shallow-since MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 952dfdf..8738d3d 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -14,6 +14,10 @@ linkgit:git-clone[1]), deepen or shorten the history to the specified number of commits. Tags for the deepened commits are not fetched. +--shallow-since=:: + Deepen or shorten the history of a shallow repository to + include all reachable commits after . + --unshallow:: If the source repository is complete, convert a shallow repository to a complete one, removing all the limitations diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt index 8680f45..99e6257 100644 --- a/Documentation/git-fetch-pack.txt +++ b/Documentation/git-fetch-pack.txt @@ -87,6 +87,10 @@ be in a separate packet, and the list must end with a flush packet. 'git-upload-pack' treats the special depth 2147483647 as infinite even if there is an ancestor-chain that long. +--shallow-since=:: + Deepen or shorten the history of a shallow'repository to + include all reachable commits after . + --no-progress:: Do not show the progress. diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt index 78e0b27..9971d9a 100644 --- a/Documentation/gitremote-helpers.txt +++ b/Documentation/gitremote-helpers.txt @@ -415,6 +415,9 @@ set by Git if the remote helper has the 'option' capability. 'option depth' :: Deepens the history of a shallow repository. +'option deepen-since :: + Deepens the history of a shallow repository based on time. + 'option followtags' {'true'|'false'}:: If enabled the helper should automatically fetch annotated tag objects if the object the tag points at was transferred diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 8332d3d..0402e27 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -104,6 +104,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) args.depth = strtol(arg, NULL, 0); continue; } + if (skip_prefix(arg, "--shallow-since=", &arg)) { + args.deepen_since = xstrdup(arg); + continue; + } if (!strcmp("--no-progress", arg)) { args.no_progress = 1; continue; diff --git a/builtin/fetch.c b/builtin/fetch.c index 8e74213..283aa95 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -36,9 +36,10 @@ static int prune = -1; /* unspecified */ static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity; static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT; -static int tags = TAGS_DEFAULT, unshallow, update_shallow; +static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen; static int max_children = 1; static const char *depth; +static const char *deepen_since; static const char *upload_pack; static struct strbuf default_rla = STRBUF_INIT; static struct transport *gtransport; @@ -115,6 +116,8 @@ static struct option builtin_fetch_options[] = { OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), OPT_STRING(0, "depth", &depth, N_("depth"), N_("deepen history of shallow clone")), + OPT_STRING(0, "shallow-since", &deepen_since, N_("time"), + N_("deepen history of shallow repository based on time")), { OPTION_SET_INT, 0, "unshallow", &unshallow, NULL, N_("convert to a complete repository"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 }, @@ -754,7 +757,7 @@ static int quickfetch(struct ref *ref_map) * really need to perform. Claiming failure now will ensure * we perform the network exchange to deepen our history. */ - if (depth) + if (deepen) return -1; return check_everything_connected(iterate_ref_map, 1, &rm); } @@ -859,7 +862,7 @@ static void set_option(struct transport *transport, const char *name, const char name, transport->url); } -static struct transport *prepare_transport(struct remote *remote) +static struct transport *prepare_transport(struct remote *remote, int deepen) { struct transport *transport; transport = transport_get(remote, NULL); @@ -870,6 +873,8 @@ static struct transport *prepare_transport(struct remote *remote) set_option(transport, TRANS_OPT_KEEP, "yes"); if (depth) set_option(transport, TRANS_OPT_DEPTH, depth); + if (deepen && deepen_since) + set_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since); if (update_shallow) set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes"); return transport; @@ -877,8 +882,18 @@ static struct transport *prepare_transport(struct remote *remote) static void backfill_tags(struct transport *transport, struct ref *ref_map) { - if (transport->cannot_reuse) { - gsecondary = prepare_transport(transport->remote); + int cannot_reuse; + + /* + * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it + * when remote helper is used (setting it to an empty string + * is not unsetting). We could extend the remote helper + * protocol for that, but for now, just force a new connection + * without deepen-since. + */ + cannot_reuse = transport->cannot_reuse || deepen_since; + if (cannot_reuse) { + gsecondary = prepare_transport(transport->remote, 0); transport = gsecondary; } @@ -1095,7 +1110,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv) die(_("No remote repository specified. Please, specify either a URL or a\n" "remote name from which new revisions should be fetched.")); - gtransport = prepare_transport(remote); + gtransport = prepare_transport(remote, 1); if (prune < 0) { /* no command line request */ @@ -1167,6 +1182,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) /* no need to be strict, transport_set_option() will validate it again */ if (depth && atoi(depth) < 1) die(_("depth %s is not a positive number"), depth); + if (depth || deepen_since) + deepen = 1; if (recurse_submodules != RECURSE_SUBMODULES_OFF) { if (recurse_submodules_default) { diff --git a/fetch-pack.c b/fetch-pack.c index a14d24a..a2f25c1 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -21,6 +21,7 @@ static int fetch_unpack_limit = -1; static int unpack_limit = 100; static int prefer_ofs_delta = 1; static int no_done; +static int deepen_since_ok; static int fetch_fsck_objects = -1; static int transfer_fsck_objects = -1; static int agent_supported; @@ -326,6 +327,7 @@ static int find_common(struct fetch_pack_args *args, if (args->no_progress) strbuf_addstr(&c, " no-progress"); if (args->include_tag) strbuf_addstr(&c, " include-tag"); if (prefer_ofs_delta) strbuf_addstr(&c, " ofs-delta"); + if (deepen_since_ok) strbuf_addstr(&c, " deepen-since"); if (agent_supported) strbuf_addf(&c, " agent=%s", git_user_agent_sanitized()); packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf); @@ -345,6 +347,10 @@ static int find_common(struct fetch_pack_args *args, write_shallow_commits(&req_buf, 1, NULL); if (args->depth > 0) packet_buf_write(&req_buf, "deepen %d", args->depth); + if (args->deepen_since) { + unsigned long max_age = approxidate(args->deepen_since); + packet_buf_write(&req_buf, "deepen-since %lu", max_age); + } packet_buf_flush(&req_buf); state_len = req_buf.len; @@ -812,7 +818,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow")) die(_("Server does not support shallow clients")); - if (args->depth > 0) + if (args->depth > 0 || args->deepen_since) args->deepen = 1; if (server_supports("multi_ack_detailed")) { print_verbose(args, _("Server supports multi_ack_detailed")); @@ -860,6 +866,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, print_verbose(args, _("Server version is %.*s"), agent_len, agent_feature); } + if (server_supports("deepen-since")) + deepen_since_ok = 1; + else if (args->deepen_since) + die(_("Server does not support --shallow-since")); if (everything_local(args, &ref, sought, nr_sought)) { packet_flush(fd[1]); diff --git a/fetch-pack.h b/fetch-pack.h index 4d0adb0..f7eadb2 100644 --- a/fetch-pack.h +++ b/fetch-pack.h @@ -10,6 +10,7 @@ struct fetch_pack_args { const char *uploadpack; int unpacklimit; int depth; + const char *deepen_since; unsigned quiet:1; unsigned keep_pack:1; unsigned lock_pack:1; diff --git a/remote-curl.c b/remote-curl.c index fd030c1..5876f24 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -20,6 +20,7 @@ static struct strbuf url = STRBUF_INIT; struct options { int verbosity; unsigned long depth; + char *deepen_since; unsigned progress : 1, check_self_contained_and_connected : 1, cloning : 1, @@ -60,6 +61,10 @@ static int set_option(const char *name, const char *value) options.depth = v; return 0; } + else if (!strcmp(name, "deepen-since")) { + options.deepen_since = xstrdup(value); + return 0; + } else if (!strcmp(name, "followtags")) { if (!strcmp(value, "true")) options.followtags = 1; @@ -699,8 +704,8 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch) char **targets = xmalloc(nr_heads * sizeof(char*)); int ret, i; - if (options.depth) - die("dumb http transport does not support --depth"); + if (options.depth || options.deepen_since) + die("dumb http transport does not support shallow capabilities"); for (i = 0; i < nr_heads; i++) targets[i] = xstrdup(oid_to_hex(&to_fetch[i]->old_oid)); @@ -746,6 +751,8 @@ static int fetch_git(struct discovery *heads, argv_array_push(&args, "--no-progress"); if (options.depth) argv_array_pushf(&args, "--depth=%lu", options.depth); + if (options.deepen_since) + argv_array_pushf(&args, "--shallow-since=%s", options.deepen_since); argv_array_push(&args, url.buf); for (i = 0; i < nr_heads; i++) { diff --git a/transport.c b/transport.c index c92f8ae..f04a302 100644 --- a/transport.c +++ b/transport.c @@ -151,6 +151,9 @@ static int set_git_option(struct git_transport_options *opts, die("transport: invalid depth option '%s'", value); } return 0; + } else if (!strcmp(name, TRANS_OPT_DEEPEN_SINCE)) { + opts->deepen_since = value; + return 0; } return 1; } @@ -205,6 +208,7 @@ static int fetch_refs_via_pack(struct transport *transport, args.quiet = (transport->verbose < 0); args.no_progress = !transport->progress; args.depth = data->options.depth; + args.deepen_since = data->options.deepen_since; args.check_self_contained_and_connected = data->options.check_self_contained_and_connected; args.cloning = transport->cloning; diff --git a/transport.h b/transport.h index 8ebaaf2..9c10a44 100644 --- a/transport.h +++ b/transport.h @@ -13,6 +13,7 @@ struct git_transport_options { unsigned self_contained_and_connected : 1; unsigned update_shallow : 1; int depth; + const char *deepen_since; const char *uploadpack; const char *receivepack; struct push_cas_option *cas; @@ -171,6 +172,9 @@ int transport_restrict_protocols(void); /* Limit the depth of the fetch if not null */ #define TRANS_OPT_DEPTH "depth" +/* Limit the depth of the fetch based on time if not null */ +#define TRANS_OPT_DEEPEN_SINCE "deepen-since" + /* Aggressively fetch annotated tags if possible */ #define TRANS_OPT_FOLLOWTAGS "followtags" -- cgit v0.10.2-6-g49f6 From 994c2aaf31e1f8e8e69f86324e585620fee68c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:54:00 +0700 Subject: clone: define shallow clone boundary based on time with --shallow-since MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index b7c467a..a410409 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -193,6 +193,9 @@ objects from the source repository into a pack in the cloned repository. `--no-single-branch` is given to fetch the histories near the tips of all branches. +--shallow-since=:: + Create a shallow clone with a history after the specified time. + --[no-]single-branch:: Clone only the history leading to the tip of a single branch, either specified by the `--branch` option or the primary diff --git a/builtin/clone.c b/builtin/clone.c index bcba080..dc2ef4f 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -40,7 +40,8 @@ static const char * const builtin_clone_usage[] = { static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1; static int option_local = -1, option_no_hardlinks, option_shared, option_recursive; -static char *option_template, *option_depth; +static int deepen; +static char *option_template, *option_depth, *option_since; static char *option_origin = NULL; static char *option_branch = NULL; static const char *real_git_dir; @@ -86,6 +87,8 @@ static struct option builtin_clone_options[] = { N_("path to git-upload-pack on the remote")), OPT_STRING(0, "depth", &option_depth, N_("depth"), N_("create a shallow clone of that depth")), + OPT_STRING(0, "shallow-since", &option_since, N_("time"), + N_("create a shallow clone since a specific time")), OPT_BOOL(0, "single-branch", &option_single_branch, N_("clone only one branch, HEAD or --branch")), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), @@ -849,8 +852,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) usage_msg_opt(_("You must specify a repository to clone."), builtin_clone_usage, builtin_clone_options); + if (option_depth || option_since) + deepen = 1; if (option_single_branch == -1) - option_single_branch = option_depth ? 1 : 0; + option_single_branch = deepen ? 1 : 0; if (option_mirror) option_bare = 1; @@ -976,6 +981,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (is_local) { if (option_depth) warning(_("--depth is ignored in local clones; use file:// instead.")); + if (option_since) + warning(_("--shallow-since is ignored in local clones; use file:// instead.")); if (!access(mkpath("%s/shallow", path), F_OK)) { if (option_local > 0) warning(_("source repository is shallow, ignoring --local")); @@ -994,6 +1001,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_depth) transport_set_option(transport, TRANS_OPT_DEPTH, option_depth); + if (option_since) + transport_set_option(transport, TRANS_OPT_DEEPEN_SINCE, + option_since); if (option_single_branch) transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); @@ -1001,7 +1011,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) transport_set_option(transport, TRANS_OPT_UPLOADPACK, option_upload_pack); - if (transport->smart_options && !option_depth) + if (transport->smart_options && !deepen) transport->smart_options->check_self_contained_and_connected = 1; refs = transport_get_remote_refs(transport); -- cgit v0.10.2-6-g49f6 From 6d43a0cefd769c650bd114f41434ef5a7aa998e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:54:01 +0700 Subject: t5500, t5539: tests for shallow depth since a specific date MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index e5f83bf..26f050d 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -637,4 +637,28 @@ test_expect_success MINGW 'fetch-pack --diag-url c:repo' ' check_prot_path c:repo file c:repo ' +test_expect_success 'clone shallow since ...' ' + test_create_repo shallow-since && + ( + cd shallow-since && + GIT_COMMITTER_DATE="100000000 +0700" git commit --allow-empty -m one && + GIT_COMMITTER_DATE="200000000 +0700" git commit --allow-empty -m two && + GIT_COMMITTER_DATE="300000000 +0700" git commit --allow-empty -m three && + git clone --shallow-since "300000000 +0700" "file://$(pwd)/." ../shallow11 && + git -C ../shallow11 log --pretty=tformat:%s HEAD >actual && + echo three >expected && + test_cmp expected actual + ) +' + +test_expect_success 'fetch shallow since ...' ' + git -C shallow11 fetch --shallow-since "200000000 +0700" origin && + git -C shallow11 log --pretty=tformat:%s origin/master >actual && + cat >expected <<-\EOF && + three + two + EOF + test_cmp expected actual +' + test_done diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh index 37a4335..704753c 100755 --- a/t/t5539-fetch-http-shallow.sh +++ b/t/t5539-fetch-http-shallow.sh @@ -73,5 +73,30 @@ test_expect_success 'no shallow lines after receiving ACK ready' ' ) ' +test_expect_success 'clone shallow since ...' ' + test_create_repo shallow-since && + ( + cd shallow-since && + GIT_COMMITTER_DATE="100000000 +0700" git commit --allow-empty -m one && + GIT_COMMITTER_DATE="200000000 +0700" git commit --allow-empty -m two && + GIT_COMMITTER_DATE="300000000 +0700" git commit --allow-empty -m three && + mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-since.git" && + git clone --shallow-since "300000000 +0700" $HTTPD_URL/smart/shallow-since.git ../shallow11 && + git -C ../shallow11 log --pretty=tformat:%s HEAD >actual && + echo three >expected && + test_cmp expected actual + ) +' + +test_expect_success 'fetch shallow since ...' ' + git -C shallow11 fetch --shallow-since "200000000 +0700" origin && + git -C shallow11 log --pretty=tformat:%s origin/master >actual && + cat >expected <<-\EOF && + three + two + EOF + test_cmp expected actual +' + stop_httpd test_done -- cgit v0.10.2-6-g49f6 From 41da7111f2c825ee0dcd319ec246a84b5ca0fd21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:54:02 +0700 Subject: refs: add expand_ref() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is basically dwim_ref() without @{} support. To be used on the server side where we want to expand abbreviated to full ref names and nothing else. The first user is "git clone/fetch --shallow-exclude". Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index e2d34b2..842e4d8 100644 --- a/refs.c +++ b/refs.c @@ -392,6 +392,13 @@ static char *substitute_branch_name(const char **string, int *len) int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) { char *last_branch = substitute_branch_name(&str, &len); + int refs_found = expand_ref(str, len, sha1, ref); + free(last_branch); + return refs_found; +} + +int expand_ref(const char *str, int len, unsigned char *sha1, char **ref) +{ const char **p, *r; int refs_found = 0; @@ -417,7 +424,6 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) warning("ignoring broken ref %s.", fullref); } } - free(last_branch); return refs_found; } diff --git a/refs.h b/refs.h index 3c3da29..31a2fa6 100644 --- a/refs.h +++ b/refs.h @@ -90,6 +90,7 @@ extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned c */ extern int refname_match(const char *abbrev_name, const char *full_name); +extern int expand_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); -- cgit v0.10.2-6-g49f6 From 269a7a831636b7c7a453f6621fc8b440ff28a408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:54:03 +0700 Subject: upload-pack: support define shallow boundary by excluding revisions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should allow the user to say "create a shallow clone of this branch after version ". Short refs are accepted and expanded at the server side with expand_ref() because we cannot expand (unknown) refs from the client side. Like deepen-since, deepen-not cannot be used with deepen. But deepen-not can be mixed with deepen-since. The result is exactly how you do the command "git rev-list --since=... --not ref". Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt index 9251df1..dee33a6 100644 --- a/Documentation/technical/pack-protocol.txt +++ b/Documentation/technical/pack-protocol.txt @@ -220,7 +220,8 @@ out of what the server said it could do with the first 'want' line. shallow-line = PKT-LINE("shallow" SP obj-id) depth-request = PKT-LINE("deepen" SP depth) / - PKT-LINE("deepen-since" SP timestamp) + PKT-LINE("deepen-since" SP timestamp) / + PKT-LINE("deepen-not" SP ref) first-want = PKT-LINE("want" SP obj-id SP capability-list) additional-want = PKT-LINE("want" SP obj-id) diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt index f08cc4e..0e6b57d 100644 --- a/Documentation/technical/protocol-capabilities.txt +++ b/Documentation/technical/protocol-capabilities.txt @@ -188,6 +188,15 @@ specific time, instead of depth. Internally it's equivalent of doing "rev-list --max-age=" on the server side. "deepen-since" cannot be used with "deepen". +deepen-not +---------- + +This capability adds "deepen-not" command to fetch-pack/upload-pack +protocol so the client can request shallow clones that are cut at a +specific revision, instead of depth. Internally it's equivalent of +doing "rev-list --not " on the server side. "deepen-not" +cannot be used with "deepen", but can be used with "deepen-since". + no-progress ----------- diff --git a/upload-pack.c b/upload-pack.c index 5269461..acc6d97 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -645,6 +645,7 @@ static void deepen_by_rev_list(int ac, const char **av, static void receive_needs(void) { struct object_array shallows = OBJECT_ARRAY_INIT; + struct string_list deepen_not = STRING_LIST_INIT_DUP; int depth = 0; int has_non_tip = 0; unsigned long deepen_since = 0; @@ -695,6 +696,16 @@ static void receive_needs(void) deepen_rev_list = 1; continue; } + if (skip_prefix(line, "deepen-not ", &arg)) { + char *ref = NULL; + unsigned char sha1[20]; + if (expand_ref(arg, strlen(arg), sha1, &ref) != 1) + die("git upload-pack: ambiguous deepen-not: %s", line); + string_list_append(&deepen_not, ref); + free(ref); + deepen_rev_list = 1; + continue; + } if (!skip_prefix(line, "want ", &arg) || get_sha1_hex(arg, sha1_buf)) die("git upload-pack: protocol error, " @@ -749,7 +760,7 @@ static void receive_needs(void) if (depth == 0 && !deepen_rev_list && shallows.nr == 0) return; if (depth > 0 && deepen_rev_list) - die("git upload-pack: deepen and deepen-since cannot be used together"); + die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together"); if (depth > 0) deepen(depth, &shallows); else if (deepen_rev_list) { @@ -759,6 +770,14 @@ static void receive_needs(void) argv_array_push(&av, "rev-list"); if (deepen_since) argv_array_pushf(&av, "--max-age=%lu", deepen_since); + if (deepen_not.nr) { + argv_array_push(&av, "--not"); + for (i = 0; i < deepen_not.nr; i++) { + struct string_list_item *s = deepen_not.items + i; + argv_array_push(&av, s->string); + } + argv_array_push(&av, "--not"); + } for (i = 0; i < want_obj.nr; i++) { struct object *o = want_obj.objects[i].item; argv_array_push(&av, oid_to_hex(&o->oid)); @@ -814,7 +833,7 @@ static int send_ref(const char *refname, const struct object_id *oid, int flag, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band" - " side-band-64k ofs-delta shallow deepen-since no-progress" + " side-band-64k ofs-delta shallow deepen-since deepen-not no-progress" " include-tag multi_ack_detailed"; const char *refname_nons = strip_namespace(refname); struct object_id peeled; -- cgit v0.10.2-6-g49f6 From a45a260086b395729e3c26c9680602e1352184b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:54:04 +0700 Subject: fetch: define shallow boundary with --shallow-exclude MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 8738d3d..7aa1285 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -18,6 +18,11 @@ Deepen or shorten the history of a shallow repository to include all reachable commits after . +--shallow-exclude=:: + Deepen or shorten the history of a shallow repository to + exclude commits reachable from a specified remote branch or tag. + This option can be specified multiple times. + --unshallow:: If the source repository is complete, convert a shallow repository to a complete one, removing all the limitations diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt index 99e6257..4d15b04 100644 --- a/Documentation/git-fetch-pack.txt +++ b/Documentation/git-fetch-pack.txt @@ -91,6 +91,11 @@ be in a separate packet, and the list must end with a flush packet. Deepen or shorten the history of a shallow'repository to include all reachable commits after . +--shallow-exclude=:: + Deepen or shorten the history of a shallow repository to + exclude commits reachable from a specified remote branch or tag. + This option can be specified multiple times. + --no-progress:: Do not show the progress. diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt index 9971d9a..75bb638 100644 --- a/Documentation/gitremote-helpers.txt +++ b/Documentation/gitremote-helpers.txt @@ -418,6 +418,10 @@ set by Git if the remote helper has the 'option' capability. 'option deepen-since :: Deepens the history of a shallow repository based on time. +'option deepen-not :: + Deepens the history of a shallow repository excluding ref. + Multiple options add up. + 'option followtags' {'true'|'false'}:: If enabled the helper should automatically fetch annotated tag objects if the object the tag points at was transferred diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 0402e27..07570be 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -50,6 +50,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) struct child_process *conn; struct fetch_pack_args args; struct sha1_array shallow = SHA1_ARRAY_INIT; + struct string_list deepen_not = STRING_LIST_INIT_DUP; packet_trace_identity("fetch-pack"); @@ -108,6 +109,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) args.deepen_since = xstrdup(arg); continue; } + if (skip_prefix(arg, "--shallow-exclude=", &arg)) { + string_list_append(&deepen_not, arg); + continue; + } if (!strcmp("--no-progress", arg)) { args.no_progress = 1; continue; @@ -135,6 +140,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) } usage(fetch_pack_usage); } + if (deepen_not.nr) + args.deepen_not = &deepen_not; if (i < argc) dest = argv[i++]; diff --git a/builtin/fetch.c b/builtin/fetch.c index 283aa95..147504d 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -41,6 +41,7 @@ static int max_children = 1; static const char *depth; static const char *deepen_since; static const char *upload_pack; +static struct string_list deepen_not = STRING_LIST_INIT_NODUP; static struct strbuf default_rla = STRBUF_INIT; static struct transport *gtransport; static struct transport *gsecondary; @@ -118,6 +119,8 @@ static struct option builtin_fetch_options[] = { N_("deepen history of shallow clone")), OPT_STRING(0, "shallow-since", &deepen_since, N_("time"), N_("deepen history of shallow repository based on time")), + OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"), + N_("deepen history of shallow clone by excluding rev")), { OPTION_SET_INT, 0, "unshallow", &unshallow, NULL, N_("convert to a complete repository"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 }, @@ -875,6 +878,9 @@ static struct transport *prepare_transport(struct remote *remote, int deepen) set_option(transport, TRANS_OPT_DEPTH, depth); if (deepen && deepen_since) set_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since); + if (deepen && deepen_not.nr) + set_option(transport, TRANS_OPT_DEEPEN_NOT, + (const char *)&deepen_not); if (update_shallow) set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes"); return transport; @@ -889,9 +895,10 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map) * when remote helper is used (setting it to an empty string * is not unsetting). We could extend the remote helper * protocol for that, but for now, just force a new connection - * without deepen-since. + * without deepen-since. Similar story for deepen-not. */ - cannot_reuse = transport->cannot_reuse || deepen_since; + cannot_reuse = transport->cannot_reuse || + deepen_since || deepen_not.nr; if (cannot_reuse) { gsecondary = prepare_transport(transport->remote, 0); transport = gsecondary; @@ -1182,7 +1189,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) /* no need to be strict, transport_set_option() will validate it again */ if (depth && atoi(depth) < 1) die(_("depth %s is not a positive number"), depth); - if (depth || deepen_since) + if (depth || deepen_since || deepen_not.nr) deepen = 1; if (recurse_submodules != RECURSE_SUBMODULES_OFF) { diff --git a/fetch-pack.c b/fetch-pack.c index a2f25c1..ad7d00f 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -22,6 +22,7 @@ static int unpack_limit = 100; static int prefer_ofs_delta = 1; static int no_done; static int deepen_since_ok; +static int deepen_not_ok; static int fetch_fsck_objects = -1; static int transfer_fsck_objects = -1; static int agent_supported; @@ -328,6 +329,7 @@ static int find_common(struct fetch_pack_args *args, if (args->include_tag) strbuf_addstr(&c, " include-tag"); if (prefer_ofs_delta) strbuf_addstr(&c, " ofs-delta"); if (deepen_since_ok) strbuf_addstr(&c, " deepen-since"); + if (deepen_not_ok) strbuf_addstr(&c, " deepen-not"); if (agent_supported) strbuf_addf(&c, " agent=%s", git_user_agent_sanitized()); packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf); @@ -351,6 +353,13 @@ static int find_common(struct fetch_pack_args *args, unsigned long max_age = approxidate(args->deepen_since); packet_buf_write(&req_buf, "deepen-since %lu", max_age); } + if (args->deepen_not) { + int i; + for (i = 0; i < args->deepen_not->nr; i++) { + struct string_list_item *s = args->deepen_not->items + i; + packet_buf_write(&req_buf, "deepen-not %s", s->string); + } + } packet_buf_flush(&req_buf); state_len = req_buf.len; @@ -818,7 +827,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow")) die(_("Server does not support shallow clients")); - if (args->depth > 0 || args->deepen_since) + if (args->depth > 0 || args->deepen_since || args->deepen_not) args->deepen = 1; if (server_supports("multi_ack_detailed")) { print_verbose(args, _("Server supports multi_ack_detailed")); @@ -870,6 +879,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, deepen_since_ok = 1; else if (args->deepen_since) die(_("Server does not support --shallow-since")); + if (server_supports("deepen-not")) + deepen_not_ok = 1; + else if (args->deepen_not) + die(_("Server does not support --shallow-exclude")); if (everything_local(args, &ref, sought, nr_sought)) { packet_flush(fd[1]); diff --git a/fetch-pack.h b/fetch-pack.h index f7eadb2..144301f 100644 --- a/fetch-pack.h +++ b/fetch-pack.h @@ -11,6 +11,7 @@ struct fetch_pack_args { int unpacklimit; int depth; const char *deepen_since; + const struct string_list *deepen_not; unsigned quiet:1; unsigned keep_pack:1; unsigned lock_pack:1; diff --git a/remote-curl.c b/remote-curl.c index 5876f24..1406e6a 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -21,6 +21,7 @@ struct options { int verbosity; unsigned long depth; char *deepen_since; + struct string_list deepen_not; unsigned progress : 1, check_self_contained_and_connected : 1, cloning : 1, @@ -65,6 +66,10 @@ static int set_option(const char *name, const char *value) options.deepen_since = xstrdup(value); return 0; } + else if (!strcmp(name, "deepen-not")) { + string_list_append(&options.deepen_not, value); + return 0; + } else if (!strcmp(name, "followtags")) { if (!strcmp(value, "true")) options.followtags = 1; @@ -753,6 +758,9 @@ static int fetch_git(struct discovery *heads, argv_array_pushf(&args, "--depth=%lu", options.depth); if (options.deepen_since) argv_array_pushf(&args, "--shallow-since=%s", options.deepen_since); + for (i = 0; i < options.deepen_not.nr; i++) + argv_array_pushf(&args, "--shallow-exclude=%s", + options.deepen_not.items[i].string); argv_array_push(&args, url.buf); for (i = 0; i < nr_heads; i++) { @@ -973,6 +981,7 @@ int main(int argc, const char **argv) options.verbosity = 1; options.progress = !!isatty(2); options.thin = 1; + string_list_init(&options.deepen_not, 1); remote = remote_get(argv[1]); diff --git a/transport-helper.c b/transport-helper.c index 27a34e9..cc1a396 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -282,6 +282,26 @@ static int strbuf_set_helper_option(struct helper_data *data, return ret; } +static int string_list_set_helper_option(struct helper_data *data, + const char *name, + struct string_list *list) +{ + struct strbuf buf = STRBUF_INIT; + int i, ret = 0; + + for (i = 0; i < list->nr; i++) { + strbuf_addf(&buf, "option %s ", name); + quote_c_style(list->items[i].string, &buf, NULL, 0); + strbuf_addch(&buf, '\n'); + + if ((ret = strbuf_set_helper_option(data, &buf))) + break; + strbuf_reset(&buf); + } + strbuf_release(&buf); + return ret; +} + static int set_helper_option(struct transport *transport, const char *name, const char *value) { @@ -294,6 +314,10 @@ static int set_helper_option(struct transport *transport, if (!data->option) return 1; + if (!strcmp(name, "deepen-not")) + return string_list_set_helper_option(data, name, + (struct string_list *)value); + for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) { if (!strcmp(name, unsupported_options[i])) return 1; diff --git a/transport.c b/transport.c index f04a302..3e6f3aa 100644 --- a/transport.c +++ b/transport.c @@ -154,6 +154,9 @@ static int set_git_option(struct git_transport_options *opts, } else if (!strcmp(name, TRANS_OPT_DEEPEN_SINCE)) { opts->deepen_since = value; return 0; + } else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) { + opts->deepen_not = (const struct string_list *)value; + return 0; } return 1; } @@ -209,6 +212,7 @@ static int fetch_refs_via_pack(struct transport *transport, args.no_progress = !transport->progress; args.depth = data->options.depth; args.deepen_since = data->options.deepen_since; + args.deepen_not = data->options.deepen_not; args.check_self_contained_and_connected = data->options.check_self_contained_and_connected; args.cloning = transport->cloning; diff --git a/transport.h b/transport.h index 9c10a44..ab61932 100644 --- a/transport.h +++ b/transport.h @@ -5,6 +5,8 @@ #include "run-command.h" #include "remote.h" +struct string_list; + struct git_transport_options { unsigned thin : 1; unsigned keep : 1; @@ -14,6 +16,7 @@ struct git_transport_options { unsigned update_shallow : 1; int depth; const char *deepen_since; + const struct string_list *deepen_not; const char *uploadpack; const char *receivepack; struct push_cas_option *cas; @@ -175,6 +178,9 @@ int transport_restrict_protocols(void); /* Limit the depth of the fetch based on time if not null */ #define TRANS_OPT_DEEPEN_SINCE "deepen-since" +/* Limit the depth of the fetch based on revs if not null */ +#define TRANS_OPT_DEEPEN_NOT "deepen-not" + /* Aggressively fetch annotated tags if possible */ #define TRANS_OPT_FOLLOWTAGS "followtags" -- cgit v0.10.2-6-g49f6 From 859e5df916cc3f3cba920c527f485ffaf6d7efa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:54:05 +0700 Subject: clone: define shallow clone boundary with --shallow-exclude MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index a410409..5049663 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -196,6 +196,11 @@ objects from the source repository into a pack in the cloned repository. --shallow-since=:: Create a shallow clone with a history after the specified time. +--shallow-exclude=:: + Create a shallow clone with a history, excluding commits + reachable from a specified remote branch or tag. This option + can be specified multiple times. + --[no-]single-branch:: Clone only the history leading to the tip of a single branch, either specified by the `--branch` option or the primary diff --git a/builtin/clone.c b/builtin/clone.c index dc2ef4f..3849231 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -44,6 +44,7 @@ static int deepen; static char *option_template, *option_depth, *option_since; static char *option_origin = NULL; static char *option_branch = NULL; +static struct string_list option_not = STRING_LIST_INIT_NODUP; static const char *real_git_dir; static char *option_upload_pack = "git-upload-pack"; static int option_verbosity; @@ -89,6 +90,8 @@ static struct option builtin_clone_options[] = { N_("create a shallow clone of that depth")), OPT_STRING(0, "shallow-since", &option_since, N_("time"), N_("create a shallow clone since a specific time")), + OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("revision"), + N_("deepen history of shallow clone by excluding rev")), OPT_BOOL(0, "single-branch", &option_single_branch, N_("clone only one branch, HEAD or --branch")), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), @@ -852,7 +855,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) usage_msg_opt(_("You must specify a repository to clone."), builtin_clone_usage, builtin_clone_options); - if (option_depth || option_since) + if (option_depth || option_since || option_not.nr) deepen = 1; if (option_single_branch == -1) option_single_branch = deepen ? 1 : 0; @@ -983,6 +986,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) warning(_("--depth is ignored in local clones; use file:// instead.")); if (option_since) warning(_("--shallow-since is ignored in local clones; use file:// instead.")); + if (option_not.nr) + warning(_("--shallow-exclude is ignored in local clones; use file:// instead.")); if (!access(mkpath("%s/shallow", path), F_OK)) { if (option_local > 0) warning(_("source repository is shallow, ignoring --local")); @@ -1004,6 +1009,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_since) transport_set_option(transport, TRANS_OPT_DEEPEN_SINCE, option_since); + if (option_not.nr) + transport_set_option(transport, TRANS_OPT_DEEPEN_NOT, + (const char *)&option_not); if (option_single_branch) transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); -- cgit v0.10.2-6-g49f6 From cdc37277f904ff5c832f04db9e799bccd9672ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:54:06 +0700 Subject: t5500, t5539: tests for shallow depth excluding a ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 26f050d..145b370 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -661,4 +661,25 @@ test_expect_success 'fetch shallow since ...' ' test_cmp expected actual ' +test_expect_success 'shallow clone exclude tag two' ' + test_create_repo shallow-exclude && + ( + cd shallow-exclude && + test_commit one && + test_commit two && + test_commit three && + git clone --shallow-exclude two "file://$(pwd)/." ../shallow12 && + git -C ../shallow12 log --pretty=tformat:%s HEAD >actual && + echo three >expected && + test_cmp expected actual + ) +' + +test_expect_success 'fetch exclude tag one' ' + git -C shallow12 fetch --shallow-exclude one origin && + git -C shallow12 log --pretty=tformat:%s origin/master >actual && + test_write_lines three two >expected && + test_cmp expected actual +' + test_done diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh index 704753c..8e38c1b 100755 --- a/t/t5539-fetch-http-shallow.sh +++ b/t/t5539-fetch-http-shallow.sh @@ -98,5 +98,27 @@ test_expect_success 'fetch shallow since ...' ' test_cmp expected actual ' +test_expect_success 'shallow clone exclude tag two' ' + test_create_repo shallow-exclude && + ( + cd shallow-exclude && + test_commit one && + test_commit two && + test_commit three && + mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-exclude.git" && + git clone --shallow-exclude two $HTTPD_URL/smart/shallow-exclude.git ../shallow12 && + git -C ../shallow12 log --pretty=tformat:%s HEAD >actual && + echo three >expected && + test_cmp expected actual + ) +' + +test_expect_success 'fetch exclude tag one' ' + git -C shallow12 fetch --shallow-exclude one origin && + git -C shallow12 log --pretty=tformat:%s origin/master >actual && + test_write_lines three two >expected && + test_cmp expected actual +' + stop_httpd test_done -- cgit v0.10.2-6-g49f6 From 2997178ee63c76a4c449f35f299e20b32956795a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:54:07 +0700 Subject: upload-pack: split check_unreachable() in two, prep for get_reachable_list() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/upload-pack.c b/upload-pack.c index acc6d97..adb8e33 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -452,21 +452,24 @@ static int is_our_ref(struct object *o) return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF); } -static int has_unreachable(struct object_array *src) +/* + * on successful case, it's up to the caller to close cmd->out + */ +static int do_reachable_revlist(struct child_process *cmd, + struct object_array *src) { static const char *argv[] = { "rev-list", "--stdin", NULL, }; - static struct child_process cmd = CHILD_PROCESS_INIT; struct object *o; char namebuf[42]; /* ^ + SHA-1 + LF */ int i; - cmd.argv = argv; - cmd.git_cmd = 1; - cmd.no_stderr = 1; - cmd.in = -1; - cmd.out = -1; + cmd->argv = argv; + cmd->git_cmd = 1; + cmd->no_stderr = 1; + cmd->in = -1; + cmd->out = -1; /* * If the next rev-list --stdin encounters an unknown commit, @@ -475,7 +478,7 @@ static int has_unreachable(struct object_array *src) */ sigchain_push(SIGPIPE, SIG_IGN); - if (start_command(&cmd)) + if (start_command(cmd)) goto error; namebuf[0] = '^'; @@ -487,7 +490,7 @@ static int has_unreachable(struct object_array *src) if (!is_our_ref(o)) continue; memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ); - if (write_in_full(cmd.in, namebuf, 42) < 0) + if (write_in_full(cmd->in, namebuf, 42) < 0) goto error; } namebuf[40] = '\n'; @@ -496,17 +499,39 @@ static int has_unreachable(struct object_array *src) if (is_our_ref(o)) continue; memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ); - if (write_in_full(cmd.in, namebuf, 41) < 0) + if (write_in_full(cmd->in, namebuf, 41) < 0) goto error; } - close(cmd.in); - cmd.in = -1; + close(cmd->in); + cmd->in = -1; + sigchain_pop(SIGPIPE); + + return 0; + +error: + sigchain_pop(SIGPIPE); + + if (cmd->in >= 0) + close(cmd->in); + if (cmd->out >= 0) + close(cmd->out); + return -1; +} + +static int has_unreachable(struct object_array *src) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + char buf[1]; + int i; + + if (do_reachable_revlist(&cmd, src) < 0) + return 1; /* * The commits out of the rev-list are not ancestors of * our ref. */ - i = read_in_full(cmd.out, namebuf, 1); + i = read_in_full(cmd.out, buf, 1); if (i) goto error; close(cmd.out); @@ -520,16 +545,11 @@ static int has_unreachable(struct object_array *src) if (finish_command(&cmd)) goto error; - sigchain_pop(SIGPIPE); - /* All the non-tip ones are ancestors of what we advertised */ return 0; error: sigchain_pop(SIGPIPE); - - if (cmd.in >= 0) - close(cmd.in); if (cmd.out >= 0) close(cmd.out); return 1; -- cgit v0.10.2-6-g49f6 From 079aa97e24daff1329f041e00ba621b0afd59163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:54:08 +0700 Subject: upload-pack: add get_reachable_list() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/object.h b/object.h index f8b6442..614a006 100644 --- a/object.h +++ b/object.h @@ -31,7 +31,7 @@ struct object_array { * revision.h: 0---------10 26 * fetch-pack.c: 0---4 * walker.c: 0-2 - * upload-pack.c: 11----------------19 + * upload-pack.c: 4 11----------------19 * builtin/blame.c: 12-13 * bisect.c: 16 * bundle.c: 16 diff --git a/upload-pack.c b/upload-pack.c index adb8e33..3227df8 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -456,7 +456,8 @@ static int is_our_ref(struct object *o) * on successful case, it's up to the caller to close cmd->out */ static int do_reachable_revlist(struct child_process *cmd, - struct object_array *src) + struct object_array *src, + struct object_array *reachable) { static const char *argv[] = { "rev-list", "--stdin", NULL, @@ -487,6 +488,8 @@ static int do_reachable_revlist(struct child_process *cmd, o = get_indexed_object(--i); if (!o) continue; + if (reachable && o->type == OBJ_COMMIT) + o->flags &= ~TMP_MARK; if (!is_our_ref(o)) continue; memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ); @@ -496,8 +499,13 @@ static int do_reachable_revlist(struct child_process *cmd, namebuf[40] = '\n'; for (i = 0; i < src->nr; i++) { o = src->objects[i].item; - if (is_our_ref(o)) + if (is_our_ref(o)) { + if (reachable) + add_object_array(o, NULL, reachable); continue; + } + if (reachable && o->type == OBJ_COMMIT) + o->flags |= TMP_MARK; memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ); if (write_in_full(cmd->in, namebuf, 41) < 0) goto error; @@ -518,13 +526,51 @@ error: return -1; } +static int get_reachable_list(struct object_array *src, + struct object_array *reachable) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + int i; + struct object *o; + char namebuf[42]; /* ^ + SHA-1 + LF */ + + if (do_reachable_revlist(&cmd, src, reachable) < 0) + return -1; + + while ((i = read_in_full(cmd.out, namebuf, 41)) == 41) { + struct object_id sha1; + + if (namebuf[40] != '\n' || get_oid_hex(namebuf, &sha1)) + break; + + o = lookup_object(sha1.hash); + if (o && o->type == OBJ_COMMIT) { + o->flags &= ~TMP_MARK; + } + } + for (i = get_max_object_index(); 0 < i; i--) { + o = get_indexed_object(i - 1); + if (o && o->type == OBJ_COMMIT && + (o->flags & TMP_MARK)) { + add_object_array(o, NULL, reachable); + o->flags &= ~TMP_MARK; + } + } + close(cmd.out); + + if (finish_command(&cmd)) + return -1; + + return 0; +} + static int has_unreachable(struct object_array *src) { struct child_process cmd = CHILD_PROCESS_INIT; char buf[1]; int i; - if (do_reachable_revlist(&cmd, src) < 0) + if (do_reachable_revlist(&cmd, src, NULL) < 0) return 1; /* -- cgit v0.10.2-6-g49f6 From cccf74e2da85808478c784e403a69bbfe2b9f518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 12 Jun 2016 17:54:09 +0700 Subject: fetch, upload-pack: --deepen=N extends shallow boundary by N commits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In git-fetch, --depth argument is always relative with the latest remote refs. This makes it a bit difficult to cover this use case, where the user wants to make the shallow history, say 3 levels deeper. It would work if remote refs have not moved yet, but nobody can guarantee that, especially when that use case is performed a couple months after the last clone or "git fetch --depth". Also, modifying shallow boundary using --depth does not work well with clones created by --since or --not. This patch fixes that. A new argument --deepen= will add more (*) parent commits to the current history regardless of where remote refs are. Have/Want negotiation is still respected. So if remote refs move, the server will send two chunks: one between "have" and "want" and another to extend shallow history. In theory, the client could send no "want"s in order to get the second chunk only. But the protocol does not allow that. Either you send no want lines, which means ls-remote; or you have to send at least one want line that carries deep-relative to the server.. The main work was done by Dongcan Jiang. I fixed it up here and there. And of course all the bugs belong to me. (*) We could even support --deepen= where is negative. In that case we can cut some history from the shallow clone. This operation (and --depth=) does not require interaction with remote side (and more complicated to implement as a result). Helped-by: Duy Nguyen Helped-by: Eric Sunshine Helped-by: Junio C Hamano Signed-off-by: Dongcan Jiang Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 7aa1285..3b91f15 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -14,6 +14,11 @@ linkgit:git-clone[1]), deepen or shorten the history to the specified number of commits. Tags for the deepened commits are not fetched. +--deepen=:: + Similar to --depth, except it specifies the number of commits + from the current shallow boundary instead of from the tip of + each remote branch history. + --shallow-since=:: Deepen or shorten the history of a shallow repository to include all reachable commits after . diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt index 4d15b04..c20958f 100644 --- a/Documentation/git-fetch-pack.txt +++ b/Documentation/git-fetch-pack.txt @@ -96,6 +96,11 @@ be in a separate packet, and the list must end with a flush packet. exclude commits reachable from a specified remote branch or tag. This option can be specified multiple times. +--deepen-relative:: + Argument --depth specifies the number of commits from the + current shallow boundary instead of from the tip of each + remote branch history. + --no-progress:: Do not show the progress. diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt index 75bb638..6fca268 100644 --- a/Documentation/gitremote-helpers.txt +++ b/Documentation/gitremote-helpers.txt @@ -422,6 +422,10 @@ set by Git if the remote helper has the 'option' capability. Deepens the history of a shallow repository excluding ref. Multiple options add up. +'option deepen-relative {'true'|'false'}:: + Deepens the history of a shallow repository relative to + current boundary. Only valid when used with "option depth". + 'option followtags' {'true'|'false'}:: If enabled the helper should automatically fetch annotated tag objects if the object the tag points at was transferred diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt index 0e6b57d..4fd6dcc 100644 --- a/Documentation/technical/protocol-capabilities.txt +++ b/Documentation/technical/protocol-capabilities.txt @@ -197,6 +197,13 @@ specific revision, instead of depth. Internally it's equivalent of doing "rev-list --not " on the server side. "deepen-not" cannot be used with "deepen", but can be used with "deepen-since". +deepen-relative +--------------- + +If this capability is requested by the client, the semantics of +"deepen" command is changed. The "depth" argument is the depth from +the current shallow boundary, instead of the depth from remote refs. + no-progress ----------- diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 07570be..8265348 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -113,6 +113,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) string_list_append(&deepen_not, arg); continue; } + if (!strcmp(arg, "--deepen-relative")) { + args.deepen_relative = 1; + continue; + } if (!strcmp("--no-progress", arg)) { args.no_progress = 1; continue; diff --git a/builtin/fetch.c b/builtin/fetch.c index 147504d..7b0ea1c 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -34,7 +34,7 @@ static int fetch_prune_config = -1; /* unspecified */ static int prune = -1; /* unspecified */ #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */ -static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity; +static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative; static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen; static int max_children = 1; @@ -121,6 +121,8 @@ static struct option builtin_fetch_options[] = { N_("deepen history of shallow repository based on time")), OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"), N_("deepen history of shallow clone by excluding rev")), + OPT_INTEGER(0, "deepen", &deepen_relative, + N_("deepen history of shallow clone")), { OPTION_SET_INT, 0, "unshallow", &unshallow, NULL, N_("convert to a complete repository"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 }, @@ -881,6 +883,8 @@ static struct transport *prepare_transport(struct remote *remote, int deepen) if (deepen && deepen_not.nr) set_option(transport, TRANS_OPT_DEEPEN_NOT, (const char *)&deepen_not); + if (deepen_relative) + set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes"); if (update_shallow) set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes"); return transport; @@ -906,6 +910,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map) transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); transport_set_option(transport, TRANS_OPT_DEPTH, "0"); + transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL); fetch_refs(transport, ref_map); if (gsecondary) { @@ -1177,6 +1182,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_fetch_options, builtin_fetch_usage, 0); + if (deepen_relative) { + if (deepen_relative < 0) + die(_("Negative depth in --deepen is not supported")); + if (depth) + die(_("--deepen and --depth are mutually exclusive")); + depth = xstrfmt("%d", deepen_relative); + } if (unshallow) { if (depth) die(_("--depth and --unshallow cannot be used together")); diff --git a/fetch-pack.c b/fetch-pack.c index ad7d00f..e2a235f 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -324,6 +324,7 @@ static int find_common(struct fetch_pack_args *args, if (no_done) strbuf_addstr(&c, " no-done"); if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k"); if (use_sideband == 1) strbuf_addstr(&c, " side-band"); + if (args->deepen_relative) strbuf_addstr(&c, " deepen-relative"); if (args->use_thin_pack) strbuf_addstr(&c, " thin-pack"); if (args->no_progress) strbuf_addstr(&c, " no-progress"); if (args->include_tag) strbuf_addstr(&c, " include-tag"); @@ -883,6 +884,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, deepen_not_ok = 1; else if (args->deepen_not) die(_("Server does not support --shallow-exclude")); + if (!server_supports("deepen-relative") && args->deepen_relative) + die(_("Server does not support --deepen")); if (everything_local(args, &ref, sought, nr_sought)) { packet_flush(fd[1]); diff --git a/fetch-pack.h b/fetch-pack.h index 144301f..c912e3d 100644 --- a/fetch-pack.h +++ b/fetch-pack.h @@ -12,6 +12,7 @@ struct fetch_pack_args { int depth; const char *deepen_since; const struct string_list *deepen_not; + unsigned deepen_relative:1; unsigned quiet:1; unsigned keep_pack:1; unsigned lock_pack:1; diff --git a/remote-curl.c b/remote-curl.c index 1406e6a..d56412d 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -30,7 +30,8 @@ struct options { dry_run : 1, thin : 1, /* One of the SEND_PACK_PUSH_CERT_* constants. */ - push_cert : 2; + push_cert : 2, + deepen_relative : 1; }; static struct options options; static struct string_list cas_options = STRING_LIST_INIT_DUP; @@ -70,6 +71,15 @@ static int set_option(const char *name, const char *value) string_list_append(&options.deepen_not, value); return 0; } + else if (!strcmp(name, "deepen-relative")) { + if (!strcmp(value, "true")) + options.deepen_relative = 1; + else if (!strcmp(value, "false")) + options.deepen_relative = 0; + else + return -1; + return 0; + } else if (!strcmp(name, "followtags")) { if (!strcmp(value, "true")) options.followtags = 1; @@ -761,6 +771,8 @@ static int fetch_git(struct discovery *heads, for (i = 0; i < options.deepen_not.nr; i++) argv_array_pushf(&args, "--shallow-exclude=%s", options.deepen_not.items[i].string); + if (options.deepen_relative && options.depth) + argv_array_push(&args, "--deepen-relative"); argv_array_push(&args, url.buf); for (i = 0; i < nr_heads; i++) { diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 145b370..a908036 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -682,4 +682,27 @@ test_expect_success 'fetch exclude tag one' ' test_cmp expected actual ' +test_expect_success 'fetching deepen' ' + test_create_repo shallow-deepen && + ( + cd shallow-deepen && + test_commit one && + test_commit two && + test_commit three && + git clone --depth 1 "file://$(pwd)/." deepen && + test_commit four && + git -C deepen log --pretty=tformat:%s master >actual && + echo three >expected && + test_cmp expected actual && + git -C deepen fetch --deepen=1 && + git -C deepen log --pretty=tformat:%s origin/master >actual && + cat >expected <<-\EOF && + four + three + two + EOF + test_cmp expected actual + ) +' + test_done diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh index 8e38c1b..5fbf67c 100755 --- a/t/t5539-fetch-http-shallow.sh +++ b/t/t5539-fetch-http-shallow.sh @@ -120,5 +120,31 @@ test_expect_success 'fetch exclude tag one' ' test_cmp expected actual ' +test_expect_success 'fetching deepen' ' + test_create_repo shallow-deepen && + ( + cd shallow-deepen && + test_commit one && + test_commit two && + test_commit three && + mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" && + git clone --depth 1 $HTTPD_URL/smart/shallow-deepen.git deepen && + mv "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" .git && + test_commit four && + git -C deepen log --pretty=tformat:%s master >actual && + echo three >expected && + test_cmp expected actual && + mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" && + git -C deepen fetch --deepen=1 && + git -C deepen log --pretty=tformat:%s origin/master >actual && + cat >expected <<-\EOF && + four + three + two + EOF + test_cmp expected actual + ) +' + stop_httpd test_done diff --git a/transport-helper.c b/transport-helper.c index cc1a396..a5cdd77 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -258,6 +258,7 @@ static const char *boolean_options[] = { TRANS_OPT_THIN, TRANS_OPT_KEEP, TRANS_OPT_FOLLOWTAGS, + TRANS_OPT_DEEPEN_RELATIVE }; static int strbuf_set_helper_option(struct helper_data *data, diff --git a/transport.c b/transport.c index 3e6f3aa..3e76a9a 100644 --- a/transport.c +++ b/transport.c @@ -157,6 +157,9 @@ static int set_git_option(struct git_transport_options *opts, } else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) { opts->deepen_not = (const struct string_list *)value; return 0; + } else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) { + opts->deepen_relative = !!value; + return 0; } return 1; } @@ -213,6 +216,7 @@ static int fetch_refs_via_pack(struct transport *transport, args.depth = data->options.depth; args.deepen_since = data->options.deepen_since; args.deepen_not = data->options.deepen_not; + args.deepen_relative = data->options.deepen_relative; args.check_self_contained_and_connected = data->options.check_self_contained_and_connected; args.cloning = transport->cloning; diff --git a/transport.h b/transport.h index ab61932..bdc3518 100644 --- a/transport.h +++ b/transport.h @@ -14,6 +14,7 @@ struct git_transport_options { unsigned check_self_contained_and_connected : 1; unsigned self_contained_and_connected : 1; unsigned update_shallow : 1; + unsigned deepen_relative : 1; int depth; const char *deepen_since; const struct string_list *deepen_not; @@ -181,6 +182,9 @@ int transport_restrict_protocols(void); /* Limit the depth of the fetch based on revs if not null */ #define TRANS_OPT_DEEPEN_NOT "deepen-not" +/* Limit the deepen of the fetch if not null */ +#define TRANS_OPT_DEEPEN_RELATIVE "deepen-relative" + /* Aggressively fetch annotated tags if possible */ #define TRANS_OPT_FOLLOWTAGS "followtags" diff --git a/upload-pack.c b/upload-pack.c index 3227df8..e40d15a 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -32,6 +32,7 @@ static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=< static unsigned long oldest_have; +static int deepen_relative; static int multi_ack; static int no_done; static int use_thin_pack, use_ofs_delta, use_include_tag; @@ -674,7 +675,8 @@ static void send_unshallow(const struct object_array *shallows) } } -static void deepen(int depth, const struct object_array *shallows) +static void deepen(int depth, int deepen_relative, + struct object_array *shallows) { if (depth == INFINITE_DEPTH && !is_repository_shallow()) { int i; @@ -683,6 +685,17 @@ static void deepen(int depth, const struct object_array *shallows) struct object *object = shallows->objects[i].item; object->flags |= NOT_SHALLOW; } + } else if (deepen_relative) { + struct object_array reachable_shallows = OBJECT_ARRAY_INIT; + struct commit_list *result; + + get_reachable_list(shallows, &reachable_shallows); + result = get_shallow_commits(&reachable_shallows, + depth + 1, + SHALLOW, NOT_SHALLOW); + send_shallow(result); + free_commit_list(result); + object_array_clear(&reachable_shallows); } else { struct commit_list *result; @@ -779,6 +792,8 @@ static void receive_needs(void) features = arg + 40; + if (parse_feature_request(features, "deepen-relative")) + deepen_relative = 1; if (parse_feature_request(features, "multi_ack_detailed")) multi_ack = 2; else if (parse_feature_request(features, "multi_ack")) @@ -828,7 +843,7 @@ static void receive_needs(void) if (depth > 0 && deepen_rev_list) die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together"); if (depth > 0) - deepen(depth, &shallows); + deepen(depth, deepen_relative, &shallows); else if (deepen_rev_list) { struct argv_array av = ARGV_ARRAY_INIT; int i; @@ -899,8 +914,8 @@ static int send_ref(const char *refname, const struct object_id *oid, int flag, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band" - " side-band-64k ofs-delta shallow deepen-since deepen-not no-progress" - " include-tag multi_ack_detailed"; + " side-band-64k ofs-delta shallow deepen-since deepen-not" + " deepen-relative no-progress include-tag multi_ack_detailed"; const char *refname_nons = strip_namespace(refname); struct object_id peeled; -- cgit v0.10.2-6-g49f6