{"thread":{"id":"41483","subject":"[PATCH v3 01/25] remote-curl.c: convert fetch_git() to use argv_array","startedAt":"2016-02-23T13:44:38Z","lastAt":"2016-02-23T13:45:03Z","messageCount":26,"participants":["Nguyễn Thái Ngọc Duy"],"isPatch":true,"patchVersion":3,"patchTotal":25},"messages":[{"id":"279004","messageId":"1456235103-26317-1-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":null,"subject":"[PATCH v3 00/25] More flexibility in making shallow clones","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:38Z","receivedAt":"2016-02-23T13:44:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Changes since v2 [1]\n\n - PATCH v2 03/25, transport-helper.c: do not send null option to\n   remote, is dropped. It's just a hack. Even if we want to set NULL\n   to a transport option to say \"reset to defaults\", we need to tell\n   remote helper about it, not silently ignore it. 15/25 solves it\n   without the old patch.\n\n - get_shallow_commits_by_rev_list() avoids going through all in-core\n   objects in 13/25\n\n - split check_unreachable()'s double usage, 23/25 (new) and\n   24/25. 23/25 actually makes interdiff a bit messy to read.\n\n - move --not arguments before revs in 19/25\n\n - some more small changes for other comments, see interdiff.\n\n[1] http://thread.gmane.org/gmane.comp.version-control.git/285414\n\nInterdiff\n\ndiff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c\nindex 67a13af..8265348 100644\n--- a/builtin/fetch-pack.c\n+++ b/builtin/fetch-pack.c\n@@ -50,6 +50,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \tstruct child_process *conn;\n \tstruct fetch_pack_args args;\n \tstruct sha1_array shallow = SHA1_ARRAY_INIT;\n+\tstruct string_list deepen_not = STRING_LIST_INIT_DUP;\n \n \tpacket_trace_identity(\"fetch-pack\");\n \n@@ -58,14 +59,13 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \n \tfor (i = 1; i < argc && *argv[i] == '-'; i++) {\n \t\tconst char *arg = argv[i];\n-\t\tconst char *value;\n \n-\t\tif (skip_prefix(arg, \"--upload-pack=\", &value)) {\n-\t\t\targs.uploadpack = value;\n+\t\tif (skip_prefix(arg, \"--upload-pack=\", &arg)) {\n+\t\t\targs.uploadpack = arg;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (skip_prefix(arg, \"--exec=\", &value)) {\n-\t\t\targs.uploadpack = value;\n+\t\tif (skip_prefix(arg, \"--exec=\", &arg)) {\n+\t\t\targs.uploadpack = arg;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(\"--quiet\", arg) || !strcmp(\"-q\", arg)) {\n@@ -101,22 +101,16 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \t\t\targs.verbose = 1;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (skip_prefix(arg, \"--depth=\", &value)) {\n-\t\t\targs.depth = strtol(value, NULL, 0);\n+\t\tif (skip_prefix(arg, \"--depth=\", &arg)) {\n+\t\t\targs.depth = strtol(arg, NULL, 0);\n \t\t\tcontinue;\n \t\t}\n-\t\tif (skip_prefix(arg, \"--shallow-since=\", &value)) {\n-\t\t\targs.deepen_since = xstrdup(value);\n+\t\tif (skip_prefix(arg, \"--shallow-since=\", &arg)) {\n+\t\t\targs.deepen_since = xstrdup(arg);\n \t\t\tcontinue;\n \t\t}\n-\t\tif (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n-\t\t\tstatic struct string_list *deepen_not;\n-\t\t\tif (!deepen_not) {\n-\t\t\t\tdeepen_not = xmalloc(sizeof(*deepen_not));\n-\t\t\t\tstring_list_init(deepen_not, 1);\n-\t\t\t\targs.deepen_not = deepen_not;\n-\t\t\t}\n-\t\t\tstring_list_append(deepen_not, value);\n+\t\tif (skip_prefix(arg, \"--shallow-exclude=\", &arg)) {\n+\t\t\tstring_list_append(&deepen_not, arg);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"--deepen-relative\")) {\n@@ -150,6 +144,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \t\t}\n \t\tusage(fetch_pack_usage);\n \t}\n+\tif (deepen_not.nr)\n+\t\targs.deepen_not = &deepen_not;\n \n \tif (i < argc)\n \t\tdest = argv[i++];\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex c5f0e9e..808c11b 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -875,7 +875,7 @@ static void set_option(struct transport *transport, const char *name, const char\n \t\t\tname, transport->url);\n }\n \n-static struct transport *prepare_transport(struct remote *remote)\n+static struct transport *prepare_transport(struct remote *remote, int deepen)\n {\n \tstruct transport *transport;\n \ttransport = transport_get(remote, NULL);\n@@ -886,9 +886,9 @@ static struct transport *prepare_transport(struct remote *remote)\n \t\tset_option(transport, TRANS_OPT_KEEP, \"yes\");\n \tif (depth)\n \t\tset_option(transport, TRANS_OPT_DEPTH, depth);\n-\tif (deepen_since)\n+\tif (deepen && deepen_since)\n \t\tset_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since);\n-\tif (deepen_not.nr)\n+\tif (deepen && deepen_not.nr)\n \t\tset_option(transport, TRANS_OPT_DEEPEN_NOT,\n \t\t\t   (const char *)&deepen_not);\n \tif (deepen_relative)\n@@ -900,15 +900,24 @@ static struct transport *prepare_transport(struct remote *remote)\n \n static void backfill_tags(struct transport *transport, struct ref *ref_map)\n {\n-\tif (transport->cannot_reuse) {\n-\t\tgsecondary = prepare_transport(transport->remote);\n+\tint cannot_reuse;\n+\n+\t/*\n+\t * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it\n+\t * when remote helper is used (setting it to an empty string\n+\t * is not unsetting). We could extend the remote helper\n+\t * protocol for that, but for now, just force a new connection\n+\t * without deepen-since. Similar story for deepen-not.\n+\t */\n+\tcannot_reuse = transport->cannot_reuse ||\n+\t\tdeepen_since || deepen_not.nr;\n+\tif (cannot_reuse) {\n+\t\tgsecondary = prepare_transport(transport->remote, 0);\n \t\ttransport = gsecondary;\n \t}\n \n \ttransport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);\n \ttransport_set_option(transport, TRANS_OPT_DEPTH, \"0\");\n-\ttransport_set_option(transport, TRANS_OPT_DEEPEN_SINCE, NULL);\n-\ttransport_set_option(transport, TRANS_OPT_DEEPEN_NOT, NULL);\n \ttransport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);\n \tfetch_refs(transport, ref_map);\n \n@@ -1121,7 +1130,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\tdie(_(\"No remote repository specified.  Please, specify either a URL or a\\n\"\n \t\t    \"remote name from which new revisions should be fetched.\"));\n \n-\tgtransport = prepare_transport(remote);\n+\tgtransport = prepare_transport(remote, 1);\n \n \tif (prune < 0) {\n \t\t/* no command line request */\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 69a99e5..ace9217 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -857,7 +857,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \t\tallow_unadvertised_object_request |= ALLOW_TIP_SHA1;\n \t}\n \tif (server_supports(\"allow-reachable-sha1-in-want\")) {\n-\t\tprint_verbose(args, \"Server supports allow-reachable-sha1-in-want\\n\");\n+\t\tprint_verbose(args, \"Server supports allow-reachable-sha1-in-want\");\n \t\tallow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;\n \t}\n \tif (!server_supports(\"thin-pack\"))\ndiff --git a/remote-curl.c b/remote-curl.c\nindex ea06c5d..3f1a8f5 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -750,8 +750,7 @@ static int fetch_git(struct discovery *heads,\n \tstruct argv_array args = ARGV_ARRAY_INIT;\n \n \targv_array_pushl(&args, \"fetch-pack\", \"--stateless-rpc\",\n-\t\t\t \"--stdin\", \"--lock-pack\",\n-\t\t\t NULL);\n+\t\t\t \"--stdin\", \"--lock-pack\", NULL);\n \tif (options.followtags)\n \t\targv_array_push(&args, \"--include-tag\");\n \tif (options.thin)\ndiff --git a/shallow.c b/shallow.c\nindex f5d5c1d..5dca743 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -141,7 +141,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,\n \n static void show_commit(struct commit *commit, void *data)\n {\n-\tcommit->object.flags |= *(int *)data;\n+\tcommit_list_insert(commit, data);\n }\n \n /*\n@@ -155,20 +155,15 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n \t\t\t\t\t\t    int not_shallow_flag)\n {\n \tstruct commit_list *result = NULL, *p;\n+\tstruct commit_list *not_shallow_list = NULL;\n \tstruct rev_info revs;\n-\tunsigned int i, nr;\n+\tint both_flags = shallow_flag | not_shallow_flag;\n \n \t/*\n \t * SHALLOW (excluded) and NOT_SHALLOW (included) should not be\n \t * set at this point. But better be safe than sorry.\n \t */\n-\tnr = get_max_object_index();\n-\tfor (i = 0; i < nr; i++) {\n-\t\tstruct object *o = get_indexed_object(i);\n-\t\tif (!o || o->type != OBJ_COMMIT)\n-\t\t\tcontinue;\n-\t\to->flags &= ~(shallow_flag | not_shallow_flag);\n-\t}\n+\tclear_object_flags(both_flags);\n \n \tis_repository_shallow(); /* make sure shallows are read */\n \n@@ -176,10 +171,13 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n \tsave_commit_buffer = 0;\n \tsetup_revisions(ac, av, &revs, NULL);\n \n-\t/* Mark all reachable commits as NOT_SHALLOW */\n \tif (prepare_revision_walk(&revs))\n \t\tdie(\"revision walk setup failed\");\n-\ttraverse_commit_list(&revs, show_commit, NULL, &not_shallow_flag);\n+\ttraverse_commit_list(&revs, show_commit, NULL, &not_shallow_list);\n+\n+\t/* Mark all reachable commits as NOT_SHALLOW */\n+\tfor (p = not_shallow_list; p; p = p->next)\n+\t\tp->item->object.flags |= not_shallow_flag;\n \n \t/*\n \t * mark border commits SHALLOW + NOT_SHALLOW.\n@@ -188,14 +186,8 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n \t * A, later. If NOT_SHALLOW on A is cleared at step 1, B\n \t * itself is considered border at step 2, which is incorrect.\n \t */\n-\tnr = get_max_object_index();\n-\tfor (i = 0; i < nr; i++) {\n-\t\tstruct object *o = get_indexed_object(i);\n-\t\tstruct commit *c = (struct commit *)o;\n-\n-\t\tif (!o || o->type != OBJ_COMMIT ||\n-\t\t    !(o->flags & not_shallow_flag))\n-\t\t\tcontinue;\n+\tfor (p = not_shallow_list; p; p = p->next) {\n+\t\tstruct commit *c = p->item;\n \n \t\tif (parse_commit(c))\n \t\t\tdie(\"unable to parse commit %s\",\n@@ -203,21 +195,21 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n \n \t\tfor (p = c->parents; p; p = p->next)\n \t\t\tif (!(p->item->object.flags & not_shallow_flag)) {\n-\t\t\t\to->flags |= shallow_flag;\n+\t\t\t\tc->object.flags |= shallow_flag;\n \t\t\t\tcommit_list_insert(c, &result);\n \t\t\t\tbreak;\n \t\t\t}\n \t}\n+\tfree_commit_list(not_shallow_list);\n \n \t/*\n \t * Now we can clean up NOT_SHALLOW on border commits. Having\n \t * both flags set can confuse the caller.\n \t */\n \tfor (p = result; p; p = p->next) {\n-\t\tstruct object *ro = &p->item->object;\n-\t\tif ((ro->flags & not_shallow_flag) &&\n-\t\t    (ro->flags & shallow_flag))\n-\t\t\tro->flags &= ~not_shallow_flag;\n+\t\tstruct object *o = &p->item->object;\n+\t\tif ((o->flags & both_flags) == both_flags)\n+\t\t\to->flags &= ~not_shallow_flag;\n \t}\n \treturn result;\n }\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex ed8d18c..f512098 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -654,8 +654,10 @@ test_expect_success 'clone shallow since ...' '\n test_expect_success 'fetch shallow since ...' '\n \tgit -C shallow11 fetch --shallow-since \"200000000 +0700\" origin &&\n \tgit -C shallow11 log --pretty=tformat:%s origin/master >actual &&\n-\techo three >expected &&\n-\techo two  >>expected &&\n+\tcat >expected <<-\\EOF &&\n+\tthree\n+\ttwo\n+\tEOF\n \ttest_cmp expected actual\n '\n \ndiff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh\nindex 06389eb..25f8968 100755\n--- a/t/t5539-fetch-http-shallow.sh\n+++ b/t/t5539-fetch-http-shallow.sh\n@@ -91,8 +91,10 @@ test_expect_success 'clone shallow since ...' '\n test_expect_success 'fetch shallow since ...' '\n \tgit -C shallow11 fetch --shallow-since \"200000000 +0700\" origin &&\n \tgit -C shallow11 log --pretty=tformat:%s origin/master >actual &&\n-\techo three >expected &&\n-\techo two  >>expected &&\n+\tcat >expected <<-\\EOF &&\n+\tthree\n+\ttwo\n+\tEOF\n \ttest_cmp expected actual\n '\n \ndiff --git a/transport-helper.c b/transport-helper.c\nindex 3c1ae6f..b894b60 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -290,9 +290,6 @@ static int string_list_set_helper_option(struct helper_data *data,\n \tstruct strbuf buf = STRBUF_INIT;\n \tint i, ret = 0;\n \n-\tif (!list)\n-\t\treturn 0;\n-\n \tfor (i = 0; i < list->nr; i++) {\n \t\tstrbuf_addf(&buf, \"option %s \", name);\n \t\tquote_c_style(list->items[i].string, &buf, NULL, 0);\n@@ -337,12 +334,8 @@ static int set_helper_option(struct transport *transport,\n \tstrbuf_addf(&buf, \"option %s \", name);\n \tif (is_bool)\n \t\tstrbuf_addstr(&buf, value ? \"true\" : \"false\");\n-\telse if (value)\n+\telse\n \t\tquote_c_style(value, &buf, NULL, 0);\n-\telse {\n-\t\tstrbuf_release(&buf);\n-\t\treturn 0;\n-\t}\n \tstrbuf_addch(&buf, '\\n');\n \n \tret = strbuf_set_helper_option(data, &buf);\ndiff --git a/transport.c b/transport.c\nindex 2f3823a..f9c9e67 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -482,6 +482,7 @@ static int set_git_option(struct git_transport_options *opts,\n \t\treturn 0;\n \t} else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) {\n \t\topts->deepen_not = (const struct string_list *)value;\n+\t\treturn 0;\n \t} else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {\n \t\topts->deepen_relative = !!value;\n \t\treturn 0;\ndiff --git a/upload-pack.c b/upload-pack.c\nindex f05971e..18b914a 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -452,33 +452,26 @@ static int is_our_ref(struct object *o)\n \t\t\t(ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));\n \treturn o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);\n }\n-/*\n- * If reachable is NULL, return 1 if there is no unreachable object,\n- * zero otherwise.\n- *\n- * If reachable is not NULL, it's filled with reachable objects.\n- * Return value is irrelevant. The caller has to compare reachable and\n- * src to find out if there's any unreachable object.\n- */\n-static int check_unreachable(struct object_array *reachable,\n-\t\t\t     struct object_array *src)\n+\n+static int do_reachable_revlist(struct child_process *cmd,\n+\t\t\t\tstruct object_array *src,\n+\t\t\t\tstruct object_array *reachable)\n {\n \tstatic const char *argv[] = {\n \t\t\"rev-list\", \"--stdin\", NULL,\n \t};\n-\tstatic struct child_process cmd = CHILD_PROCESS_INIT;\n \tstruct object *o;\n \tchar namebuf[42]; /* ^ + SHA-1 + LF */\n \tint i;\n \n-\tcmd.argv = argv;\n-\tcmd.git_cmd = 1;\n-\tcmd.no_stderr = 1;\n-\tcmd.in = -1;\n-\tcmd.out = -1;\n+\tcmd->argv = argv;\n+\tcmd->git_cmd = 1;\n+\tcmd->no_stderr = 1;\n+\tcmd->in = -1;\n+\tcmd->out = -1;\n \n-\tif (start_command(&cmd))\n-\t\treturn 0;\n+\tif (start_command(cmd))\n+\t\treturn -1;\n \n \t/*\n \t * If rev-list --stdin encounters an unknown commit, it\n@@ -498,8 +491,8 @@ static int check_unreachable(struct object_array *reachable,\n \t\tif (!is_our_ref(o))\n \t\t\tcontinue;\n \t\tmemcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n-\t\tif (write_in_full(cmd.in, namebuf, 42) < 0)\n-\t\t\treturn 0;\n+\t\tif (write_in_full(cmd->in, namebuf, 42) < 0)\n+\t\t\treturn -1;\n \t}\n \tnamebuf[40] = '\\n';\n \tfor (i = 0; i < src->nr; i++) {\n@@ -512,22 +505,26 @@ static int check_unreachable(struct object_array *reachable,\n \t\tif (reachable && o->type == OBJ_COMMIT)\n \t\t\to->flags |= TMP_MARK;\n \t\tmemcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n-\t\tif (write_in_full(cmd.in, namebuf, 41) < 0)\n-\t\t\treturn 0;\n+\t\tif (write_in_full(cmd->in, namebuf, 41) < 0)\n+\t\t\treturn -1;\n \t}\n-\tclose(cmd.in);\n+\tclose(cmd->in);\n \n \tsigchain_pop(SIGPIPE);\n-\n-\t/*\n-\t * The commits out of the rev-list are not ancestors of\n-\t * our ref.\n-\t */\n-\tif (!reachable) {\n-\t\ti = read_in_full(cmd.out, namebuf, 1);\n-\t\tif (i)\n \treturn 0;\n-\t} else {\n+}\n+\n+static int get_reachable_list(struct object_array *src,\n+\t\t\t      struct object_array *reachable)\n+{\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\tint i, ret = do_reachable_revlist(&cmd, src, reachable);\n+\tstruct object *o;\n+\tchar namebuf[42]; /* ^ + SHA-1 + LF */\n+\n+\tif (ret < 0)\n+\t\treturn -1;\n+\n \twhile ((i = read_in_full(cmd.out, namebuf, 41)) == 41) {\n \t\tstruct object_id sha1;\n \n@@ -539,15 +536,38 @@ static int check_unreachable(struct object_array *reachable,\n \t\t\to->flags &= ~TMP_MARK;\n \t\t}\n \t}\n-\t\tfor (i = get_max_object_index(); 0 < i; ) {\n-\t\t\to = get_indexed_object(--i);\n+\tfor (i = get_max_object_index(); 0 < i; i--) {\n+\t\to = get_indexed_object(i - 1);\n \t\tif (o && o->type == OBJ_COMMIT &&\n \t\t    (o->flags & TMP_MARK)) {\n \t\t\tadd_object_array(o, NULL, reachable);\n \t\t\t\to->flags &= ~TMP_MARK;\n \t\t}\n \t}\n-\t}\n+\tclose(cmd.out);\n+\n+\tif (finish_command(&cmd))\n+\t\treturn -1;\n+\n+\treturn 0;\n+}\n+\n+static int check_unreachable(struct object_array *src)\n+{\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\tint i, ret = do_reachable_revlist(&cmd, src, NULL);\n+\tchar buf[1];\n+\n+\tif (ret < 0)\n+\t\treturn 0;\n+\n+\t/*\n+\t * The commits out of the rev-list are not ancestors of\n+\t * our ref.\n+\t */\n+\ti = read_in_full(cmd.out, buf, 1);\n+\tif (i)\n+\t\treturn 0;\n \tclose(cmd.out);\n \n \t/*\n@@ -573,7 +593,7 @@ static void check_non_tip(void)\n \t */\n \tif (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))\n \t\t;\t\t/* error */\n-\telse if (check_unreachable(NULL, &want_obj))\n+\telse if (check_unreachable(&want_obj))\n \t\treturn;\n \n \t/* Pick one of them (we know there at least is one) */\n@@ -645,7 +665,7 @@ static void deepen(int depth, int deepen_relative,\n \t\t}\n \telse if (deepen_relative) {\n \t\tstruct object_array reachable_shallows = OBJECT_ARRAY_INIT;\n-\t\t(void)check_unreachable(&reachable_shallows, shallows);\n+\t\tget_reachable_list(shallows, &reachable_shallows);\n \t\tresult = get_shallow_commits(&reachable_shallows,\n \t\t\t\t\t     depth + 1,\n \t\t\t\t\t     SHALLOW, NOT_SHALLOW);\n@@ -729,7 +749,7 @@ static void receive_needs(void)\n \t\t\tchar *ref = NULL;\n \t\t\tunsigned char sha1[20];\n \t\t\tif (expand_ref(arg, strlen(arg), sha1, &ref) != 1)\n-\t\t\t\tdie(\"Ambiguous deepen-not: %s\", line);\n+\t\t\t\tdie(\"git upload-pack: ambiguous deepen-not: %s\", line);\n \t\t\tstring_list_append(&deepen_not, ref);\n \t\t\tfree(ref);\n \t\t\tdeepen_rev_list = 1;\n@@ -791,7 +811,7 @@ static void receive_needs(void)\n \tif (depth == 0 && !deepen_rev_list && shallows.nr == 0)\n \t\treturn;\n \tif (depth > 0 && deepen_rev_list)\n-\t\tdie(\"--depth and --shallow-since (or --shallow-exclude) cannot be used together\");\n+\t\tdie(\"git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together\");\n \tif (depth > 0)\n \t\tdeepen(depth, deepen_relative, &shallows);\n \telse if (deepen_rev_list) {\n@@ -801,16 +821,17 @@ static void receive_needs(void)\n \t\targv_array_push(&av, \"rev-list\");\n \t\tif (deepen_since)\n \t\t\targv_array_pushf(&av, \"--max-age=%lu\", deepen_since);\n-\t\tfor (i = 0; i < want_obj.nr; i++) {\n-\t\t\tstruct object *o = want_obj.objects[i].item;\n-\t\t\targv_array_push(&av, oid_to_hex(&o->oid));\n-\t\t}\n \t\tif (deepen_not.nr) {\n \t\t\targv_array_push(&av, \"--not\");\n \t\t\tfor (i = 0; i < deepen_not.nr; i++) {\n \t\t\t\tstruct string_list_item *s = deepen_not.items + i;\n \t\t\t\targv_array_push(&av, s->string);\n \t\t\t}\n+\t\t\targv_array_push(&av, \"--not\");\n+\t\t}\n+\t\tfor (i = 0; i < want_obj.nr; i++) {\n+\t\t\tstruct object *o = want_obj.objects[i].item;\n+\t\t\targv_array_push(&av, oid_to_hex(&o->oid));\n \t\t}\n \t\tdeepen_by_rev_list(av.argc, av.argv, &shallows);\n \t\targv_array_clear(&av);\n\n\nNguyễn Thái Ngọc Duy (25):\n  remote-curl.c: convert fetch_git() to use argv_array\n  transport-helper.c: refactor set_helper_option()\n  upload-pack: move shallow deepen code out of receive_needs()\n  upload-pack: move \"shallow\" sending code out of deepen()\n  upload-pack: remove unused variable \"backup\"\n  upload-pack: move \"unshallow\" sending code out of deepen()\n  upload-pack: use skip_prefix() instead of starts_with()\n  upload-pack: tighten number parsing at \"deepen\" lines\n  upload-pack: move rev-list code out of check_non_tip()\n  fetch-pack: use skip_prefix() instead of starts_with()\n  fetch-pack: use a common function for verbose printing\n  fetch-pack: use a separate flag for fetch in deepening mode\n  shallow.c: implement a generic shallow boundary finder based on\n    rev-list\n  upload-pack: add deepen-since to cut shallow repos based on time\n  fetch: define shallow boundary with --shallow-since\n  clone: define shallow clone boundary based on time with\n    --shallow-since\n  t5500, t5539: tests for shallow depth since a specific date\n  refs: add expand_ref()\n  upload-pack: support define shallow boundary by excluding revisions\n  fetch: define shallow boundary with --shallow-exclude\n  clone: define shallow clone boundary with --shallow-exclude\n  t5500, t5539: tests for shallow depth excluding a ref\n  upload-pack: split check_unreachable() in two, prep for\n    get_reachable_list()\n  upload-pack: add get_reachable_list()\n  fetch, upload-pack: --deepen=N extends shallow boundary by N commits\n\n Documentation/fetch-options.txt                   |  14 +\n Documentation/git-clone.txt                       |   8 +\n Documentation/git-fetch-pack.txt                  |  14 +\n Documentation/gitremote-helpers.txt               |  11 +\n Documentation/technical/pack-protocol.txt         |   4 +-\n Documentation/technical/protocol-capabilities.txt |  25 ++\n builtin/clone.c                                   |  32 +-\n builtin/fetch-pack.c                              |  27 +-\n builtin/fetch.c                                   |  58 +++-\n commit.h                                          |   2 +\n fetch-pack.c                                      | 128 ++++----\n fetch-pack.h                                      |   4 +\n object.h                                          |   2 +-\n refs.c                                            |   8 +-\n refs.h                                            |   1 +\n remote-curl.c                                     |  74 +++--\n shallow.c                                         |  77 +++++\n t/t5500-fetch-pack.sh                             |  69 +++++\n t/t5539-fetch-http-shallow.sh                     |  74 +++++\n transport-helper.c                                |  62 +++-\n transport.c                                       |  12 +\n transport.h                                       |  14 +\n upload-pack.c                                     | 338 ++++++++++++++++------\n 23 files changed, 858 insertions(+), 200 deletions(-)\n\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279003","messageId":"1456235103-26317-2-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 01/25] remote-curl.c: convert fetch_git() to use argv_array","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:39Z","receivedAt":"2016-02-23T13:44:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n remote-curl.c | 40 +++++++++++++++++-----------------------\n 1 file changed, 17 insertions(+), 23 deletions(-)\n\ndiff --git a/remote-curl.c b/remote-curl.c\nindex c704857..4289c20 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -726,37 +726,30 @@ static int fetch_git(struct discovery *heads,\n \tstruct rpc_state rpc;\n \tstruct strbuf preamble = STRBUF_INIT;\n \tchar *depth_arg = NULL;\n-\tint argc = 0, i, err;\n-\tconst char *argv[17];\n+\tint i, err;\n+\tstruct argv_array args = ARGV_ARRAY_INIT;\n \n-\targv[argc++] = \"fetch-pack\";\n-\targv[argc++] = \"--stateless-rpc\";\n-\targv[argc++] = \"--stdin\";\n-\targv[argc++] = \"--lock-pack\";\n+\targv_array_pushl(&args, \"fetch-pack\", \"--stateless-rpc\",\n+\t\t\t \"--stdin\", \"--lock-pack\", NULL);\n \tif (options.followtags)\n-\t\targv[argc++] = \"--include-tag\";\n+\t\targv_array_push(&args, \"--include-tag\");\n \tif (options.thin)\n-\t\targv[argc++] = \"--thin\";\n+\t\targv_array_push(&args, \"--thin\");\n \tif (options.verbosity >= 3) {\n-\t\targv[argc++] = \"-v\";\n-\t\targv[argc++] = \"-v\";\n+\t\targv_array_push(&args, \"-v\");\n+\t\targv_array_push(&args, \"-v\");\n \t}\n \tif (options.check_self_contained_and_connected)\n-\t\targv[argc++] = \"--check-self-contained-and-connected\";\n+\t\targv_array_push(&args, \"--check-self-contained-and-connected\");\n \tif (options.cloning)\n-\t\targv[argc++] = \"--cloning\";\n+\t\targv_array_push(&args, \"--cloning\");\n \tif (options.update_shallow)\n-\t\targv[argc++] = \"--update-shallow\";\n+\t\targv_array_push(&args, \"--update-shallow\");\n \tif (!options.progress)\n-\t\targv[argc++] = \"--no-progress\";\n-\tif (options.depth) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstrbuf_addf(&buf, \"--depth=%lu\", options.depth);\n-\t\tdepth_arg = strbuf_detach(&buf, NULL);\n-\t\targv[argc++] = depth_arg;\n-\t}\n-\targv[argc++] = url.buf;\n-\targv[argc++] = NULL;\n+\t\targv_array_push(&args, \"--no-progress\");\n+\tif (options.depth)\n+\t\targv_array_pushf(&args, \"--depth=%lu\", options.depth);\n+\targv_array_push(&args, url.buf);\n \n \tfor (i = 0; i < nr_heads; i++) {\n \t\tstruct ref *ref = to_fetch[i];\n@@ -769,7 +762,7 @@ static int fetch_git(struct discovery *heads,\n \n \tmemset(&rpc, 0, sizeof(rpc));\n \trpc.service_name = \"git-upload-pack\",\n-\trpc.argv = argv;\n+\trpc.argv = args.argv;\n \trpc.stdin_preamble = &preamble;\n \trpc.gzip_request = 1;\n \n@@ -779,6 +772,7 @@ static int fetch_git(struct discovery *heads,\n \tstrbuf_release(&rpc.result);\n \tstrbuf_release(&preamble);\n \tfree(depth_arg);\n+\targv_array_clear(&args);\n \treturn err;\n }\n \n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279005","messageId":"1456235103-26317-3-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 02/25] transport-helper.c: refactor set_helper_option()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:40Z","receivedAt":"2016-02-23T13:44:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"For now we can handle two types, string and boolean, in\nset_helper_option(). Later on we'll add string_list support, which does\nnot fit well. The new function strbuf_set_helper_option() can be reused\nfor a separate function that handles string-list.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n transport-helper.c | 37 +++++++++++++++++++++++--------------\n 1 file changed, 23 insertions(+), 14 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex a6bff8b..35023da 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -260,6 +260,28 @@ static const char *boolean_options[] = {\n \tTRANS_OPT_FOLLOWTAGS,\n \t};\n \n+static int strbuf_set_helper_option(struct helper_data *data,\n+\t\t\t\t    struct strbuf *buf)\n+{\n+\tint ret;\n+\n+\tsendline(data, buf);\n+\tif (recvline(data, buf))\n+\t\texit(128);\n+\n+\tif (!strcmp(buf->buf, \"ok\"))\n+\t\tret = 0;\n+\telse if (starts_with(buf->buf, \"error\")) {\n+\t\tret = -1;\n+\t} else if (!strcmp(buf->buf, \"unsupported\"))\n+\t\tret = 1;\n+\telse {\n+\t\twarning(\"%s unexpectedly said: '%s'\", data->name, buf->buf);\n+\t\tret = 1;\n+\t}\n+\treturn ret;\n+}\n+\n static int set_helper_option(struct transport *transport,\n \t\t\t  const char *name, const char *value)\n {\n@@ -291,20 +313,7 @@ static int set_helper_option(struct transport *transport,\n \t\tquote_c_style(value, &buf, NULL, 0);\n \tstrbuf_addch(&buf, '\\n');\n \n-\tsendline(data, &buf);\n-\tif (recvline(data, &buf))\n-\t\texit(128);\n-\n-\tif (!strcmp(buf.buf, \"ok\"))\n-\t\tret = 0;\n-\telse if (starts_with(buf.buf, \"error\")) {\n-\t\tret = -1;\n-\t} else if (!strcmp(buf.buf, \"unsupported\"))\n-\t\tret = 1;\n-\telse {\n-\t\twarning(\"%s unexpectedly said: '%s'\", data->name, buf.buf);\n-\t\tret = 1;\n-\t}\n+\tret = strbuf_set_helper_option(data, &buf);\n \tstrbuf_release(&buf);\n \treturn ret;\n }\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279006","messageId":"1456235103-26317-4-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 03/25] upload-pack: move shallow deepen code out of receive_needs()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:41Z","receivedAt":"2016-02-23T13:44:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a prep step for further refactoring. Besides reindentation and\ns/shallows\\./shallows->/g, no other changes are expected.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 99 +++++++++++++++++++++++++++++++----------------------------\n 1 file changed, 52 insertions(+), 47 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex b3f6653..97ed620 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -538,6 +538,55 @@ error:\n \t}\n }\n \n+static void deepen(int depth, const struct object_array *shallows)\n+{\n+\tstruct commit_list *result = NULL, *backup = NULL;\n+\tint i;\n+\tif (depth == INFINITE_DEPTH && !is_repository_shallow())\n+\t\tfor (i = 0; i < shallows->nr; i++) {\n+\t\t\tstruct object *object = shallows->objects[i].item;\n+\t\t\tobject->flags |= NOT_SHALLOW;\n+\t\t}\n+\telse\n+\t\tbackup = result =\n+\t\t\tget_shallow_commits(&want_obj, depth,\n+\t\t\t\t\t    SHALLOW, NOT_SHALLOW);\n+\twhile (result) {\n+\t\tstruct object *object = &result->item->object;\n+\t\tif (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {\n+\t\t\tpacket_write(1, \"shallow %s\",\n+\t\t\t\t     oid_to_hex(&object->oid));\n+\t\t\tregister_shallow(object->oid.hash);\n+\t\t\tshallow_nr++;\n+\t\t}\n+\t\tresult = result->next;\n+\t}\n+\tfree_commit_list(backup);\n+\tfor (i = 0; i < shallows->nr; i++) {\n+\t\tstruct object *object = shallows->objects[i].item;\n+\t\tif (object->flags & NOT_SHALLOW) {\n+\t\t\tstruct commit_list *parents;\n+\t\t\tpacket_write(1, \"unshallow %s\",\n+\t\t\t\t     oid_to_hex(&object->oid));\n+\t\t\tobject->flags &= ~CLIENT_SHALLOW;\n+\t\t\t/* make sure the real parents are parsed */\n+\t\t\tunregister_shallow(object->oid.hash);\n+\t\t\tobject->parsed = 0;\n+\t\t\tparse_commit_or_die((struct commit *)object);\n+\t\t\tparents = ((struct commit *)object)->parents;\n+\t\t\twhile (parents) {\n+\t\t\t\tadd_object_array(&parents->item->object,\n+\t\t\t\t\t\t NULL, &want_obj);\n+\t\t\t\tparents = parents->next;\n+\t\t\t}\n+\t\t\tadd_object_array(object, NULL, &extra_edge_obj);\n+\t\t}\n+\t\t/* make sure commit traversal conforms to client */\n+\t\tregister_shallow(object->oid.hash);\n+\t}\n+\tpacket_flush(1);\n+}\n+\n static void receive_needs(void)\n {\n \tstruct object_array shallows = OBJECT_ARRAY_INIT;\n@@ -630,53 +679,9 @@ static void receive_needs(void)\n \n \tif (depth == 0 && shallows.nr == 0)\n \t\treturn;\n-\tif (depth > 0) {\n-\t\tstruct commit_list *result = NULL, *backup = NULL;\n-\t\tint i;\n-\t\tif (depth == INFINITE_DEPTH && !is_repository_shallow())\n-\t\t\tfor (i = 0; i < shallows.nr; i++) {\n-\t\t\t\tstruct object *object = shallows.objects[i].item;\n-\t\t\t\tobject->flags |= NOT_SHALLOW;\n-\t\t\t}\n-\t\telse\n-\t\t\tbackup = result =\n-\t\t\t\tget_shallow_commits(&want_obj, depth,\n-\t\t\t\t\t\t    SHALLOW, NOT_SHALLOW);\n-\t\twhile (result) {\n-\t\t\tstruct object *object = &result->item->object;\n-\t\t\tif (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {\n-\t\t\t\tpacket_write(1, \"shallow %s\",\n-\t\t\t\t\t\toid_to_hex(&object->oid));\n-\t\t\t\tregister_shallow(object->oid.hash);\n-\t\t\t\tshallow_nr++;\n-\t\t\t}\n-\t\t\tresult = result->next;\n-\t\t}\n-\t\tfree_commit_list(backup);\n-\t\tfor (i = 0; i < shallows.nr; i++) {\n-\t\t\tstruct object *object = shallows.objects[i].item;\n-\t\t\tif (object->flags & NOT_SHALLOW) {\n-\t\t\t\tstruct commit_list *parents;\n-\t\t\t\tpacket_write(1, \"unshallow %s\",\n-\t\t\t\t\toid_to_hex(&object->oid));\n-\t\t\t\tobject->flags &= ~CLIENT_SHALLOW;\n-\t\t\t\t/* make sure the real parents are parsed */\n-\t\t\t\tunregister_shallow(object->oid.hash);\n-\t\t\t\tobject->parsed = 0;\n-\t\t\t\tparse_commit_or_die((struct commit *)object);\n-\t\t\t\tparents = ((struct commit *)object)->parents;\n-\t\t\t\twhile (parents) {\n-\t\t\t\t\tadd_object_array(&parents->item->object,\n-\t\t\t\t\t\t\tNULL, &want_obj);\n-\t\t\t\t\tparents = parents->next;\n-\t\t\t\t}\n-\t\t\t\tadd_object_array(object, NULL, &extra_edge_obj);\n-\t\t\t}\n-\t\t\t/* make sure commit traversal conforms to client */\n-\t\t\tregister_shallow(object->oid.hash);\n-\t\t}\n-\t\tpacket_flush(1);\n-\t} else\n+\tif (depth > 0)\n+\t\tdeepen(depth, &shallows);\n+\telse\n \t\tif (shallows.nr > 0) {\n \t\t\tint i;\n \t\t\tfor (i = 0; i < shallows.nr; i++)\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279007","messageId":"1456235103-26317-5-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 04/25] upload-pack: move \"shallow\" sending code out of deepen()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:42Z","receivedAt":"2016-02-23T13:44:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 25 +++++++++++++++----------\n 1 file changed, 15 insertions(+), 10 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 97ed620..0eb9a0b 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -538,6 +538,20 @@ error:\n \t}\n }\n \n+static void send_shallow(struct commit_list *result)\n+{\n+\twhile (result) {\n+\t\tstruct object *object = &result->item->object;\n+\t\tif (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {\n+\t\t\tpacket_write(1, \"shallow %s\",\n+\t\t\t\t     oid_to_hex(&object->oid));\n+\t\t\tregister_shallow(object->oid.hash);\n+\t\t\tshallow_nr++;\n+\t\t}\n+\t\tresult = result->next;\n+\t}\n+}\n+\n static void deepen(int depth, const struct object_array *shallows)\n {\n \tstruct commit_list *result = NULL, *backup = NULL;\n@@ -551,16 +565,7 @@ static void deepen(int depth, const struct object_array *shallows)\n \t\tbackup = result =\n \t\t\tget_shallow_commits(&want_obj, depth,\n \t\t\t\t\t    SHALLOW, NOT_SHALLOW);\n-\twhile (result) {\n-\t\tstruct object *object = &result->item->object;\n-\t\tif (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {\n-\t\t\tpacket_write(1, \"shallow %s\",\n-\t\t\t\t     oid_to_hex(&object->oid));\n-\t\t\tregister_shallow(object->oid.hash);\n-\t\t\tshallow_nr++;\n-\t\t}\n-\t\tresult = result->next;\n-\t}\n+\tsend_shallow(result);\n \tfree_commit_list(backup);\n \tfor (i = 0; i < shallows->nr; i++) {\n \t\tstruct object *object = shallows->objects[i].item;\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279008","messageId":"1456235103-26317-6-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 05/25] upload-pack: remove unused variable \"backup\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:43Z","receivedAt":"2016-02-23T13:44:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"After the last patch, \"result\" and \"backup\" are the same. \"result\" used\nto move, but the movement is now contained in send_shallow(). Delete\nthis redundant variable.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 9 ++++-----\n 1 file changed, 4 insertions(+), 5 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 0eb9a0b..ee5d20b 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -554,7 +554,7 @@ static void send_shallow(struct commit_list *result)\n \n static void deepen(int depth, const struct object_array *shallows)\n {\n-\tstruct commit_list *result = NULL, *backup = NULL;\n+\tstruct commit_list *result = NULL;\n \tint i;\n \tif (depth == INFINITE_DEPTH && !is_repository_shallow())\n \t\tfor (i = 0; i < shallows->nr; i++) {\n@@ -562,11 +562,10 @@ static void deepen(int depth, const struct object_array *shallows)\n \t\t\tobject->flags |= NOT_SHALLOW;\n \t\t}\n \telse\n-\t\tbackup = result =\n-\t\t\tget_shallow_commits(&want_obj, depth,\n-\t\t\t\t\t    SHALLOW, NOT_SHALLOW);\n+\t\tresult = get_shallow_commits(&want_obj, depth,\n+\t\t\t\t\t     SHALLOW, NOT_SHALLOW);\n \tsend_shallow(result);\n-\tfree_commit_list(backup);\n+\tfree_commit_list(result);\n \tfor (i = 0; i < shallows->nr; i++) {\n \t\tstruct object *object = shallows->objects[i].item;\n \t\tif (object->flags & NOT_SHALLOW) {\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279028","messageId":"1456235103-26317-7-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 06/25] upload-pack: move \"unshallow\" sending code out of deepen()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:44Z","receivedAt":"2016-02-23T13:44:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Also add some more comments in this code because it takes too long to\nunderstand what it does (to me, who should be familiar enough to\nunderstand this code well!)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 39 ++++++++++++++++++++++++++-------------\n 1 file changed, 26 insertions(+), 13 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex ee5d20b..bfb7985 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -552,20 +552,10 @@ static void send_shallow(struct commit_list *result)\n \t}\n }\n \n-static void deepen(int depth, const struct object_array *shallows)\n+static void send_unshallow(const struct object_array *shallows)\n {\n-\tstruct commit_list *result = NULL;\n \tint i;\n-\tif (depth == INFINITE_DEPTH && !is_repository_shallow())\n-\t\tfor (i = 0; i < shallows->nr; i++) {\n-\t\t\tstruct object *object = shallows->objects[i].item;\n-\t\t\tobject->flags |= NOT_SHALLOW;\n-\t\t}\n-\telse\n-\t\tresult = get_shallow_commits(&want_obj, depth,\n-\t\t\t\t\t     SHALLOW, NOT_SHALLOW);\n-\tsend_shallow(result);\n-\tfree_commit_list(result);\n+\n \tfor (i = 0; i < shallows->nr; i++) {\n \t\tstruct object *object = shallows->objects[i].item;\n \t\tif (object->flags & NOT_SHALLOW) {\n@@ -573,7 +563,13 @@ static void deepen(int depth, const struct object_array *shallows)\n \t\t\tpacket_write(1, \"unshallow %s\",\n \t\t\t\t     oid_to_hex(&object->oid));\n \t\t\tobject->flags &= ~CLIENT_SHALLOW;\n-\t\t\t/* make sure the real parents are parsed */\n+\t\t\t/*\n+\t\t\t * We want to _register_ \"object\" as shallow, but we\n+\t\t\t * also need to traverse object's parents to deepen a\n+\t\t\t * shallow clone. Unregister it for now so we can\n+\t\t\t * parse and add the parents to the want list, then\n+\t\t\t * re-register it.\n+\t\t\t */\n \t\t\tunregister_shallow(object->oid.hash);\n \t\t\tobject->parsed = 0;\n \t\t\tparse_commit_or_die((struct commit *)object);\n@@ -588,6 +584,23 @@ static void deepen(int depth, const struct object_array *shallows)\n \t\t/* make sure commit traversal conforms to client */\n \t\tregister_shallow(object->oid.hash);\n \t}\n+}\n+\n+static void deepen(int depth, const struct object_array *shallows)\n+{\n+\tstruct commit_list *result = NULL;\n+\tint i;\n+\tif (depth == INFINITE_DEPTH && !is_repository_shallow())\n+\t\tfor (i = 0; i < shallows->nr; i++) {\n+\t\t\tstruct object *object = shallows->objects[i].item;\n+\t\t\tobject->flags |= NOT_SHALLOW;\n+\t\t}\n+\telse\n+\t\tresult = get_shallow_commits(&want_obj, depth,\n+\t\t\t\t\t     SHALLOW, NOT_SHALLOW);\n+\tsend_shallow(result);\n+\tfree_commit_list(result);\n+\tsend_unshallow(shallows);\n \tpacket_flush(1);\n }\n \n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279022","messageId":"1456235103-26317-8-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 07/25] upload-pack: use skip_prefix() instead of starts_with()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:45Z","receivedAt":"2016-02-23T13:44:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 26 +++++++++++++++-----------\n 1 file changed, 15 insertions(+), 11 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex bfb7985..257ad48 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -276,7 +276,7 @@ static void create_pack_file(void)\n \tdie(\"git upload-pack: %s\", abort_msg);\n }\n \n-static int got_sha1(char *hex, unsigned char *sha1)\n+static int got_sha1(const char *hex, unsigned char *sha1)\n {\n \tstruct object *o;\n \tint we_knew_they_have = 0;\n@@ -382,6 +382,8 @@ static int get_common_commits(void)\n \n \tfor (;;) {\n \t\tchar *line = packet_read_line(0, NULL);\n+\t\tconst char *arg;\n+\n \t\treset_timeout();\n \n \t\tif (!line) {\n@@ -403,8 +405,8 @@ static int get_common_commits(void)\n \t\t\tgot_other = 0;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (starts_with(line, \"have \")) {\n-\t\t\tswitch (got_sha1(line+5, sha1)) {\n+\t\tif (skip_prefix(line, \"have \", &arg)) {\n+\t\t\tswitch (got_sha1(arg, sha1)) {\n \t\t\tcase -1: /* they have what we do not */\n \t\t\t\tgot_other = 1;\n \t\t\t\tif (multi_ack && ok_to_give_up()) {\n@@ -616,14 +618,16 @@ static void receive_needs(void)\n \t\tconst char *features;\n \t\tunsigned char sha1_buf[20];\n \t\tchar *line = packet_read_line(0, NULL);\n+\t\tconst char *arg;\n+\n \t\treset_timeout();\n \t\tif (!line)\n \t\t\tbreak;\n \n-\t\tif (starts_with(line, \"shallow \")) {\n+\t\tif (skip_prefix(line, \"shallow \", &arg)) {\n \t\t\tunsigned char sha1[20];\n \t\t\tstruct object *object;\n-\t\t\tif (get_sha1_hex(line + 8, sha1))\n+\t\t\tif (get_sha1_hex(arg, sha1))\n \t\t\t\tdie(\"invalid shallow line: %s\", line);\n \t\t\tobject = parse_object(sha1);\n \t\t\tif (!object)\n@@ -636,19 +640,19 @@ static void receive_needs(void)\n \t\t\t}\n \t\t\tcontinue;\n \t\t}\n-\t\tif (starts_with(line, \"deepen \")) {\n+\t\tif (skip_prefix(line, \"deepen \", &arg)) {\n \t\t\tchar *end;\n-\t\t\tdepth = strtol(line + 7, &end, 0);\n-\t\t\tif (end == line + 7 || depth <= 0)\n+\t\t\tdepth = strtol(arg, &end, 0);\n+\t\t\tif (end == arg || depth <= 0)\n \t\t\t\tdie(\"Invalid deepen: %s\", line);\n \t\t\tcontinue;\n \t\t}\n-\t\tif (!starts_with(line, \"want \") ||\n-\t\t    get_sha1_hex(line+5, sha1_buf))\n+\t\tif (!skip_prefix(line, \"want \", &arg) ||\n+\t\t    get_sha1_hex(arg, sha1_buf))\n \t\t\tdie(\"git upload-pack: protocol error, \"\n \t\t\t    \"expected to get sha, not '%s'\", line);\n \n-\t\tfeatures = line + 45;\n+\t\tfeatures = arg + 40;\n \n \t\tif (parse_feature_request(features, \"multi_ack_detailed\"))\n \t\t\tmulti_ack = 2;\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279009","messageId":"1456235103-26317-9-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 08/25] upload-pack: tighten number parsing at \"deepen\" lines","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:46Z","receivedAt":"2016-02-23T13:44:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 257ad48..9f14933 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -641,9 +641,9 @@ static void receive_needs(void)\n \t\t\tcontinue;\n \t\t}\n \t\tif (skip_prefix(line, \"deepen \", &arg)) {\n-\t\t\tchar *end;\n+\t\t\tchar *end = NULL;\n \t\t\tdepth = strtol(arg, &end, 0);\n-\t\t\tif (end == arg || depth <= 0)\n+\t\t\tif (!end || *end || depth <= 0)\n \t\t\t\tdie(\"Invalid deepen: %s\", line);\n \t\t\tcontinue;\n \t\t}\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279010","messageId":"1456235103-26317-10-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 09/25] upload-pack: move rev-list code out of check_non_tip()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:47Z","receivedAt":"2016-02-23T13:44:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 44 +++++++++++++++++++++++++-------------------\n 1 file changed, 25 insertions(+), 19 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 9f14933..c8dafe8 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -451,7 +451,7 @@ static int is_our_ref(struct object *o)\n \treturn o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);\n }\n \n-static void check_non_tip(void)\n+static int check_unreachable(struct object_array *src)\n {\n \tstatic const char *argv[] = {\n \t\t\"rev-list\", \"--stdin\", NULL,\n@@ -461,14 +461,6 @@ static void check_non_tip(void)\n \tchar namebuf[42]; /* ^ + SHA-1 + LF */\n \tint i;\n \n-\t/*\n-\t * In the normal in-process case without\n-\t * uploadpack.allowReachableSHA1InWant,\n-\t * non-tip requests can never happen.\n-\t */\n-\tif (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))\n-\t\tgoto error;\n-\n \tcmd.argv = argv;\n \tcmd.git_cmd = 1;\n \tcmd.no_stderr = 1;\n@@ -476,7 +468,7 @@ static void check_non_tip(void)\n \tcmd.out = -1;\n \n \tif (start_command(&cmd))\n-\t\tgoto error;\n+\t\treturn 0;\n \n \t/*\n \t * If rev-list --stdin encounters an unknown commit, it\n@@ -495,16 +487,16 @@ static void check_non_tip(void)\n \t\t\tcontinue;\n \t\tmemcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n \t\tif (write_in_full(cmd.in, namebuf, 42) < 0)\n-\t\t\tgoto error;\n+\t\t\treturn 0;\n \t}\n \tnamebuf[40] = '\\n';\n-\tfor (i = 0; i < want_obj.nr; i++) {\n-\t\to = want_obj.objects[i].item;\n+\tfor (i = 0; i < src->nr; i++) {\n+\t\to = src->objects[i].item;\n \t\tif (is_our_ref(o))\n \t\t\tcontinue;\n \t\tmemcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n \t\tif (write_in_full(cmd.in, namebuf, 41) < 0)\n-\t\t\tgoto error;\n+\t\t\treturn 0;\n \t}\n \tclose(cmd.in);\n \n@@ -516,7 +508,7 @@ static void check_non_tip(void)\n \t */\n \ti = read_in_full(cmd.out, namebuf, 1);\n \tif (i)\n-\t\tgoto error;\n+\t\treturn 0;\n \tclose(cmd.out);\n \n \t/*\n@@ -525,15 +517,29 @@ static void check_non_tip(void)\n \t * even when it showed no commit.\n \t */\n \tif (finish_command(&cmd))\n-\t\tgoto error;\n+\t\treturn 0;\n \n \t/* All the non-tip ones are ancestors of what we advertised */\n-\treturn;\n+\treturn 1;\n+}\n+\n+static void check_non_tip(void)\n+{\n+\tint i;\n+\n+\t/*\n+\t * In the normal in-process case without\n+\t * uploadpack.allowReachableSHA1InWant,\n+\t * non-tip requests can never happen.\n+\t */\n+\tif (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))\n+\t\t;\t\t/* error */\n+\telse if (check_unreachable(&want_obj))\n+\t\treturn;\n \n-error:\n \t/* Pick one of them (we know there at least is one) */\n \tfor (i = 0; i < want_obj.nr; i++) {\n-\t\to = want_obj.objects[i].item;\n+\t\tstruct object *o = want_obj.objects[i].item;\n \t\tif (!is_our_ref(o))\n \t\t\tdie(\"git upload-pack: not our ref %s\",\n \t\t\t    oid_to_hex(&o->oid));\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279011","messageId":"1456235103-26317-11-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 10/25] fetch-pack: use skip_prefix() instead of starts_with()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:48Z","receivedAt":"2016-02-23T13:44:48Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fetch-pack.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c\nindex 9b2a514..8332d3d 100644\n--- a/builtin/fetch-pack.c\n+++ b/builtin/fetch-pack.c\n@@ -59,12 +59,12 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \tfor (i = 1; i < argc && *argv[i] == '-'; i++) {\n \t\tconst char *arg = argv[i];\n \n-\t\tif (starts_with(arg, \"--upload-pack=\")) {\n-\t\t\targs.uploadpack = arg + 14;\n+\t\tif (skip_prefix(arg, \"--upload-pack=\", &arg)) {\n+\t\t\targs.uploadpack = arg;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (starts_with(arg, \"--exec=\")) {\n-\t\t\targs.uploadpack = arg + 7;\n+\t\tif (skip_prefix(arg, \"--exec=\", &arg)) {\n+\t\t\targs.uploadpack = arg;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(\"--quiet\", arg) || !strcmp(\"-q\", arg)) {\n@@ -100,8 +100,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \t\t\targs.verbose = 1;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (starts_with(arg, \"--depth=\")) {\n-\t\t\targs.depth = strtol(arg + 8, NULL, 0);\n+\t\tif (skip_prefix(arg, \"--depth=\", &arg)) {\n+\t\t\targs.depth = strtol(arg, NULL, 0);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(\"--no-progress\", arg)) {\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279012","messageId":"1456235103-26317-12-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 11/25] fetch-pack: use a common function for verbose printing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:49Z","receivedAt":"2016-02-23T13:44:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This reduces the number of \"if (verbose)\" which makes it a bit easier\nto read imo. It also makes it easier to redirect all these printouts,\nto a file for example.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n fetch-pack.c | 88 +++++++++++++++++++++++++++++-------------------------------\n 1 file changed, 42 insertions(+), 46 deletions(-)\n\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 01e34b6..4020744 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -50,6 +50,21 @@ static int non_common_revs, multi_ack, use_sideband;\n #define ALLOW_REACHABLE_SHA1\t02\n static unsigned int allow_unadvertised_object_request;\n \n+__attribute__((format (printf, 2, 3)))\n+static inline void print_verbose(const struct fetch_pack_args *args,\n+\t\t\t\t const char *fmt, ...)\n+{\n+\tva_list params;\n+\n+\tif (!args->verbose)\n+\t\treturn;\n+\n+\tva_start(params, fmt);\n+\tvfprintf(stderr, fmt, params);\n+\tva_end(params);\n+\tfputc('\\n', stderr);\n+}\n+\n static void rev_list_push(struct commit *commit, int mark)\n {\n \tif (!(commit->object.flags & mark)) {\n@@ -375,8 +390,7 @@ static int find_common(struct fetch_pack_args *args,\n \tretval = -1;\n \twhile ((sha1 = get_rev())) {\n \t\tpacket_buf_write(&req_buf, \"have %s\\n\", sha1_to_hex(sha1));\n-\t\tif (args->verbose)\n-\t\t\tfprintf(stderr, \"have %s\\n\", sha1_to_hex(sha1));\n+\t\tprint_verbose(args, \"have %s\", sha1_to_hex(sha1));\n \t\tin_vain++;\n \t\tif (flush_at <= ++count) {\n \t\t\tint ack;\n@@ -397,9 +411,9 @@ static int find_common(struct fetch_pack_args *args,\n \t\t\tconsume_shallow_list(args, fd[0]);\n \t\t\tdo {\n \t\t\t\tack = get_ack(fd[0], result_sha1);\n-\t\t\t\tif (args->verbose && ack)\n-\t\t\t\t\tfprintf(stderr, \"got ack %d %s\\n\", ack,\n-\t\t\t\t\t\t\tsha1_to_hex(result_sha1));\n+\t\t\t\tif (ack)\n+\t\t\t\t\tprint_verbose(args, \"got ack %d %s\", ack,\n+\t\t\t\t\t\t      sha1_to_hex(result_sha1));\n \t\t\t\tswitch (ack) {\n \t\t\t\tcase ACK:\n \t\t\t\t\tflushes = 0;\n@@ -438,8 +452,7 @@ static int find_common(struct fetch_pack_args *args,\n \t\t\t} while (ack);\n \t\t\tflushes--;\n \t\t\tif (got_continue && MAX_IN_VAIN < in_vain) {\n-\t\t\t\tif (args->verbose)\n-\t\t\t\t\tfprintf(stderr, \"giving up\\n\");\n+\t\t\t\tprint_verbose(args, \"giving up\");\n \t\t\t\tbreak; /* give up */\n \t\t\t}\n \t\t}\n@@ -449,8 +462,7 @@ done:\n \t\tpacket_buf_write(&req_buf, \"done\\n\");\n \t\tsend_request(args, fd[1], &req_buf);\n \t}\n-\tif (args->verbose)\n-\t\tfprintf(stderr, \"done\\n\");\n+\tprint_verbose(args, \"done\");\n \tif (retval != 0) {\n \t\tmulti_ack = 0;\n \t\tflushes++;\n@@ -462,9 +474,8 @@ done:\n \twhile (flushes || multi_ack) {\n \t\tint ack = get_ack(fd[0], result_sha1);\n \t\tif (ack) {\n-\t\t\tif (args->verbose)\n-\t\t\t\tfprintf(stderr, \"got ack (%d) %s\\n\", ack,\n-\t\t\t\t\tsha1_to_hex(result_sha1));\n+\t\t\tprint_verbose(args, \"got ack (%d) %s\", ack,\n+\t\t\t\t      sha1_to_hex(result_sha1));\n \t\t\tif (ack == ACK)\n \t\t\t\treturn 0;\n \t\t\tmulti_ack = 1;\n@@ -509,9 +520,8 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args,\n \t\t\t\t\t unsigned long cutoff)\n {\n \twhile (complete && cutoff <= complete->item->date) {\n-\t\tif (args->verbose)\n-\t\t\tfprintf(stderr, \"Marking %s as complete\\n\",\n-\t\t\t\toid_to_hex(&complete->item->object.oid));\n+\t\tprint_verbose(args, \"Marking %s as complete\",\n+\t\t\t      oid_to_hex(&complete->item->object.oid));\n \t\tpop_most_recent_commit(&complete, COMPLETE);\n \t}\n }\n@@ -652,18 +662,12 @@ static int everything_local(struct fetch_pack_args *args,\n \t\to = lookup_object(remote);\n \t\tif (!o || !(o->flags & COMPLETE)) {\n \t\t\tretval = 0;\n-\t\t\tif (!args->verbose)\n-\t\t\t\tcontinue;\n-\t\t\tfprintf(stderr,\n-\t\t\t\t\"want %s (%s)\\n\", sha1_to_hex(remote),\n-\t\t\t\tref->name);\n+\t\t\tprint_verbose(args, \"want %s (%s)\", sha1_to_hex(remote),\n+\t\t\t\t      ref->name);\n \t\t\tcontinue;\n \t\t}\n-\t\tif (!args->verbose)\n-\t\t\tcontinue;\n-\t\tfprintf(stderr,\n-\t\t\t\"already have %s (%s)\\n\", sha1_to_hex(remote),\n-\t\t\tref->name);\n+\t\tprint_verbose(args, \"already have %s (%s)\", sha1_to_hex(remote),\n+\t\t\t      ref->name);\n \t}\n \treturn retval;\n }\n@@ -810,39 +814,32 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \tif ((args->depth > 0 || is_repository_shallow()) && !server_supports(\"shallow\"))\n \t\tdie(\"Server does not support shallow clients\");\n \tif (server_supports(\"multi_ack_detailed\")) {\n-\t\tif (args->verbose)\n-\t\t\tfprintf(stderr, \"Server supports multi_ack_detailed\\n\");\n+\t\tprint_verbose(args, \"Server supports multi_ack_detailed\");\n \t\tmulti_ack = 2;\n \t\tif (server_supports(\"no-done\")) {\n-\t\t\tif (args->verbose)\n-\t\t\t\tfprintf(stderr, \"Server supports no-done\\n\");\n+\t\t\tprint_verbose(args, \"Server supports no-done\");\n \t\t\tif (args->stateless_rpc)\n \t\t\t\tno_done = 1;\n \t\t}\n \t}\n \telse if (server_supports(\"multi_ack\")) {\n-\t\tif (args->verbose)\n-\t\t\tfprintf(stderr, \"Server supports multi_ack\\n\");\n+\t\tprint_verbose(args, \"Server supports multi_ack\");\n \t\tmulti_ack = 1;\n \t}\n \tif (server_supports(\"side-band-64k\")) {\n-\t\tif (args->verbose)\n-\t\t\tfprintf(stderr, \"Server supports side-band-64k\\n\");\n+\t\tprint_verbose(args, \"Server supports side-band-64k\");\n \t\tuse_sideband = 2;\n \t}\n \telse if (server_supports(\"side-band\")) {\n-\t\tif (args->verbose)\n-\t\t\tfprintf(stderr, \"Server supports side-band\\n\");\n+\t\tprint_verbose(args, \"Server supports side-band\");\n \t\tuse_sideband = 1;\n \t}\n \tif (server_supports(\"allow-tip-sha1-in-want\")) {\n-\t\tif (args->verbose)\n-\t\t\tfprintf(stderr, \"Server supports allow-tip-sha1-in-want\\n\");\n+\t\tprint_verbose(args, \"Server supports allow-tip-sha1-in-want\");\n \t\tallow_unadvertised_object_request |= ALLOW_TIP_SHA1;\n \t}\n \tif (server_supports(\"allow-reachable-sha1-in-want\")) {\n-\t\tif (args->verbose)\n-\t\t\tfprintf(stderr, \"Server supports allow-reachable-sha1-in-want\\n\");\n+\t\tprint_verbose(args, \"Server supports allow-reachable-sha1-in-want\");\n \t\tallow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;\n \t}\n \tif (!server_supports(\"thin-pack\"))\n@@ -851,17 +848,16 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \t\targs->no_progress = 0;\n \tif (!server_supports(\"include-tag\"))\n \t\targs->include_tag = 0;\n-\tif (server_supports(\"ofs-delta\")) {\n-\t\tif (args->verbose)\n-\t\t\tfprintf(stderr, \"Server supports ofs-delta\\n\");\n-\t} else\n+\tif (server_supports(\"ofs-delta\"))\n+\t\tprint_verbose(args, \"Server supports ofs-delta\");\n+\telse\n \t\tprefer_ofs_delta = 0;\n \n \tif ((agent_feature = server_feature_value(\"agent\", &agent_len))) {\n \t\tagent_supported = 1;\n-\t\tif (args->verbose && agent_len)\n-\t\t\tfprintf(stderr, \"Server version is %.*s\\n\",\n-\t\t\t\tagent_len, agent_feature);\n+\t\tif (agent_len)\n+\t\t\tprint_verbose(args, \"Server version is %.*s\",\n+\t\t\t\t      agent_len, agent_feature);\n \t}\n \n \tif (everything_local(args, &ref, sought, nr_sought)) {\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279013","messageId":"1456235103-26317-13-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 12/25] fetch-pack: use a separate flag for fetch in deepening mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:50Z","receivedAt":"2016-02-23T13:44:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"The shallow repo could be deepened or shortened when then user gives\n--depth. But in future that won't be the only way to deepen/shorten a\nrepo. Stop relying on args->depth in this mode. Future deepening\nmethods can simply set this flag on instead of updating all these if\nexpressions.\n\nThe new name \"deepen\" was chosen after the command to define shallow\nboundary in pack protocol. New commands also follow this tradition.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n fetch-pack.c | 14 ++++++++------\n fetch-pack.h |  1 +\n 2 files changed, 9 insertions(+), 6 deletions(-)\n\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 4020744..8c37e5f 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -197,7 +197,7 @@ enum ack_type {\n \n static void consume_shallow_list(struct fetch_pack_args *args, int fd)\n {\n-\tif (args->stateless_rpc && args->depth > 0) {\n+\tif (args->stateless_rpc && args->deepen) {\n \t\t/* If we sent a depth we will get back \"duplicate\"\n \t\t * shallow and unshallow commands every time there\n \t\t * is a block of have lines exchanged.\n@@ -348,7 +348,7 @@ static int find_common(struct fetch_pack_args *args,\n \tpacket_buf_flush(&req_buf);\n \tstate_len = req_buf.len;\n \n-\tif (args->depth > 0) {\n+\tif (args->deepen) {\n \t\tchar *line;\n \t\tconst char *arg;\n \t\tunsigned char sha1[20];\n@@ -557,7 +557,7 @@ static void filter_refs(struct fetch_pack_args *args,\n \t\t}\n \n \t\tif (!keep && args->fetch_all &&\n-\t\t    (!args->depth || !starts_with(ref->name, \"refs/tags/\")))\n+\t\t    (!args->deepen || !starts_with(ref->name, \"refs/tags/\")))\n \t\t\tkeep = 1;\n \n \t\tif (keep) {\n@@ -627,7 +627,7 @@ static int everything_local(struct fetch_pack_args *args,\n \t\t}\n \t}\n \n-\tif (!args->depth) {\n+\tif (!args->deepen) {\n \t\tfor_each_ref(mark_complete_oid, NULL);\n \t\tfor_each_alternate_ref(mark_alternate_complete, NULL);\n \t\tcommit_list_sort_by_date(&complete);\n@@ -813,6 +813,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \n \tif ((args->depth > 0 || is_repository_shallow()) && !server_supports(\"shallow\"))\n \t\tdie(\"Server does not support shallow clients\");\n+\tif (args->depth > 0)\n+\t\targs->deepen = 1;\n \tif (server_supports(\"multi_ack_detailed\")) {\n \t\tprint_verbose(args, \"Server supports multi_ack_detailed\");\n \t\tmulti_ack = 2;\n@@ -873,7 +875,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \n \tif (args->stateless_rpc)\n \t\tpacket_flush(fd[1]);\n-\tif (args->depth > 0)\n+\tif (args->deepen)\n \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n \t\t\t\t\tNULL);\n \telse if (si->nr_ours || si->nr_theirs)\n@@ -940,7 +942,7 @@ static void update_shallow(struct fetch_pack_args *args,\n \tint *status;\n \tint i;\n \n-\tif (args->depth > 0 && alternate_shallow_file) {\n+\tif (args->deepen && alternate_shallow_file) {\n \t\tif (*alternate_shallow_file == '\\0') { /* --unshallow */\n \t\t\tunlink_or_warn(git_path_shallow());\n \t\t\trollback_lock_file(&shallow_lock);\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex bb7fd76..4d0adb0 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -25,6 +25,7 @@ struct fetch_pack_args {\n \tunsigned self_contained_and_connected:1;\n \tunsigned cloning:1;\n \tunsigned update_shallow:1;\n+\tunsigned deepen:1;\n };\n \n /*\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279015","messageId":"1456235103-26317-14-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 13/25] shallow.c: implement a generic shallow boundary finder based on rev-list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:51Z","receivedAt":"2016-02-23T13:44:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Instead of a custom commit walker like get_shallow_commits(), this new\nfunction uses rev-list to mark NOT_SHALLOW to all reachable commits,\nexcept borders. The definition of reachable is to be defined by the\nprotocol later. This makes it more flexible to define shallow boundary.\n\nThe way we find find border is paint all reachable commits NOT_SHALLOW.\nAny of them that \"touches\" commits without NOT_SHALLOW flag are\nconsidered shallow (e.g. zero parents via grafting mechanism). Shallow\ncommits and their true parents are all marked SHALLOW. Then NOT_SHALLOW\nis removed from shallow commits at the end.\n\nThere is an interesting observation. With a generic walker, we can\nproduce all kinds of shallow cutting. In the following graph, every\ncommit but \"x\" is reachable. \"b\" is a parent of \"a\".\n\n           x -- a -- o\n          /    /\n    x -- c -- b -- o\n\nAfter this function is run, \"a\" and \"c\" are both considered shallow\ncommits. After grafting occurs at the client side, what we see is\n\n                a -- o\n                    /\n         c -- b -- o\n\nNotice that because of grafting, \"a\" has zero parents, so \"b\" is no\nlonger a parent of \"a\".\n\nThis is unfortunate and may be solved in two ways. The first is change\nthe way shallow grafting works and keep \"a -- b\" connection if \"b\"\nexists and always ends at shallow commits (iow, no loose ends). This is\nhard to detect, or at least not cheap to do.\n\nThe second way is mark one \"x\" as shallow commit instead of \"a\" and\nproduce this graph at client side:\n\n           x -- a -- o\n               /    /\n         c -- b -- o\n\nMore commits, but simpler grafting rules.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n commit.h  |  2 ++\n shallow.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 79 insertions(+)\n\ndiff --git a/commit.h b/commit.h\nindex 5d58be0..b717be1 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -258,6 +258,8 @@ extern int for_each_commit_graft(each_commit_graft_fn, void *);\n extern int is_repository_shallow(void);\n extern struct commit_list *get_shallow_commits(struct object_array *heads,\n \t\tint depth, int shallow_flag, int not_shallow_flag);\n+extern struct commit_list *get_shallow_commits_by_rev_list(\n+\t\tint ac, const char **av, int shallow_flag, int not_shallow_flag);\n extern void set_alternate_shallow_file(const char *path, int override);\n extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,\n \t\t\t\t const struct sha1_array *extra);\ndiff --git a/shallow.c b/shallow.c\nindex 60f1505..5dca743 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -10,6 +10,8 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"commit-slab.h\"\n+#include \"revision.h\"\n+#include \"list-objects.h\"\n \n static int is_shallow = -1;\n static struct stat_validity shallow_stat;\n@@ -137,6 +139,81 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,\n \treturn result;\n }\n \n+static void show_commit(struct commit *commit, void *data)\n+{\n+\tcommit_list_insert(commit, data);\n+}\n+\n+/*\n+ * Given rev-list arguments, run rev-list. All reachable commits\n+ * except border ones are marked with not_shallow_flag. Border commits\n+ * are marked with shallow_flag. The list of border/shallow commits\n+ * are also returned.\n+ */\n+struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n+\t\t\t\t\t\t    int shallow_flag,\n+\t\t\t\t\t\t    int not_shallow_flag)\n+{\n+\tstruct commit_list *result = NULL, *p;\n+\tstruct commit_list *not_shallow_list = NULL;\n+\tstruct rev_info revs;\n+\tint both_flags = shallow_flag | not_shallow_flag;\n+\n+\t/*\n+\t * SHALLOW (excluded) and NOT_SHALLOW (included) should not be\n+\t * set at this point. But better be safe than sorry.\n+\t */\n+\tclear_object_flags(both_flags);\n+\n+\tis_repository_shallow(); /* make sure shallows are read */\n+\n+\tinit_revisions(&revs, NULL);\n+\tsave_commit_buffer = 0;\n+\tsetup_revisions(ac, av, &revs, NULL);\n+\n+\tif (prepare_revision_walk(&revs))\n+\t\tdie(\"revision walk setup failed\");\n+\ttraverse_commit_list(&revs, show_commit, NULL, &not_shallow_list);\n+\n+\t/* Mark all reachable commits as NOT_SHALLOW */\n+\tfor (p = not_shallow_list; p; p = p->next)\n+\t\tp->item->object.flags |= not_shallow_flag;\n+\n+\t/*\n+\t * mark border commits SHALLOW + NOT_SHALLOW.\n+\t * We cannot clear NOT_SHALLOW right now. Imagine border\n+\t * commit A is processed first, then commit B, whose parent is\n+\t * A, later. If NOT_SHALLOW on A is cleared at step 1, B\n+\t * itself is considered border at step 2, which is incorrect.\n+\t */\n+\tfor (p = not_shallow_list; p; p = p->next) {\n+\t\tstruct commit *c = p->item;\n+\n+\t\tif (parse_commit(c))\n+\t\t\tdie(\"unable to parse commit %s\",\n+\t\t\t    oid_to_hex(&c->object.oid));\n+\n+\t\tfor (p = c->parents; p; p = p->next)\n+\t\t\tif (!(p->item->object.flags & not_shallow_flag)) {\n+\t\t\t\tc->object.flags |= shallow_flag;\n+\t\t\t\tcommit_list_insert(c, &result);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t}\n+\tfree_commit_list(not_shallow_list);\n+\n+\t/*\n+\t * Now we can clean up NOT_SHALLOW on border commits. Having\n+\t * both flags set can confuse the caller.\n+\t */\n+\tfor (p = result; p; p = p->next) {\n+\t\tstruct object *o = &p->item->object;\n+\t\tif ((o->flags & both_flags) == both_flags)\n+\t\t\to->flags &= ~not_shallow_flag;\n+\t}\n+\treturn result;\n+}\n+\n static void check_shallow_file_for_update(void)\n {\n \tif (is_shallow == -1)\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279014","messageId":"1456235103-26317-15-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 14/25] upload-pack: add deepen-since to cut shallow repos based on time","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:52Z","receivedAt":"2016-02-23T13:44:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This should allow the user to say \"create a shallow clone containing the\nwork from last year\" (once the client side is fixed up, of course).\n\nIn theory deepen-since and deepen (aka --depth) can be used together to\ndraw the shallow boundary (whether it's intersection or union is up to\ndiscussion, but if rev-list is used, it's likely intersection). However,\nbecause deepen goes with a custom commit walker, we can't mix the two\nyet.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/technical/pack-protocol.txt         |  3 +-\n Documentation/technical/protocol-capabilities.txt |  9 +++++\n upload-pack.c                                     | 45 ++++++++++++++++++++++-\n 3 files changed, 54 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt\nindex c6977bb..9251df1 100644\n--- a/Documentation/technical/pack-protocol.txt\n+++ b/Documentation/technical/pack-protocol.txt\n@@ -219,7 +219,8 @@ out of what the server said it could do with the first 'want' line.\n \n   shallow-line      =  PKT-LINE(\"shallow\" SP obj-id)\n \n-  depth-request     =  PKT-LINE(\"deepen\" SP depth)\n+  depth-request     =  PKT-LINE(\"deepen\" SP depth) /\n+\t\t       PKT-LINE(\"deepen-since\" SP timestamp)\n \n   first-want        =  PKT-LINE(\"want\" SP obj-id SP capability-list)\n   additional-want   =  PKT-LINE(\"want\" SP obj-id)\ndiff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt\nindex eaab6b4..f08cc4e 100644\n--- a/Documentation/technical/protocol-capabilities.txt\n+++ b/Documentation/technical/protocol-capabilities.txt\n@@ -179,6 +179,15 @@ This capability adds \"deepen\", \"shallow\" and \"unshallow\" commands to\n the  fetch-pack/upload-pack protocol so clients can request shallow\n clones.\n \n+deepen-since\n+------------\n+\n+This capability adds \"deepen-since\" command to fetch-pack/upload-pack\n+protocol so the client can request shallow clones that are cut at a\n+specific time, instead of depth. Internally it's equivalent of doing\n+\"rev-list --max-age=<timestamp>\" on the server side. \"deepen-since\"\n+cannot be used with \"deepen\".\n+\n no-progress\n -----------\n \ndiff --git a/upload-pack.c b/upload-pack.c\nindex c8dafe8..58c0936 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -14,6 +14,7 @@\n #include \"sigchain.h\"\n #include \"version.h\"\n #include \"string-list.h\"\n+#include \"argv-array.h\"\n \n static const char upload_pack_usage[] = \"git upload-pack [--strict] [--timeout=<n>] <dir>\";\n \n@@ -612,11 +613,25 @@ static void deepen(int depth, const struct object_array *shallows)\n \tpacket_flush(1);\n }\n \n+static void deepen_by_rev_list(int ac, const char **av,\n+\t\t\t       struct object_array *shallows)\n+{\n+\tstruct commit_list *result;\n+\n+\tresult = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);\n+\tsend_shallow(result);\n+\tfree_commit_list(result);\n+\tsend_unshallow(shallows);\n+\tpacket_flush(1);\n+}\n+\n static void receive_needs(void)\n {\n \tstruct object_array shallows = OBJECT_ARRAY_INIT;\n \tint depth = 0;\n \tint has_non_tip = 0;\n+\tunsigned long deepen_since = 0;\n+\tint deepen_rev_list = 0;\n \n \tshallow_nr = 0;\n \tfor (;;) {\n@@ -653,6 +668,16 @@ static void receive_needs(void)\n \t\t\t\tdie(\"Invalid deepen: %s\", line);\n \t\t\tcontinue;\n \t\t}\n+\t\tif (skip_prefix(line, \"deepen-since \", &arg)) {\n+\t\t\tchar *end = NULL;\n+\t\t\tdeepen_since = strtoul(arg, &end, 0);\n+\t\t\tif (!end || *end || !deepen_since ||\n+\t\t\t    /* revisions.c's max_age -1 is special */\n+\t\t\t    deepen_since == -1)\n+\t\t\t\tdie(\"Invalid deepen-since: %s\", line);\n+\t\t\tdeepen_rev_list = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!skip_prefix(line, \"want \", &arg) ||\n \t\t    get_sha1_hex(arg, sha1_buf))\n \t\t\tdie(\"git upload-pack: protocol error, \"\n@@ -704,10 +729,26 @@ static void receive_needs(void)\n \tif (!use_sideband && daemon_mode)\n \t\tno_progress = 1;\n \n-\tif (depth == 0 && shallows.nr == 0)\n+\tif (depth == 0 && !deepen_rev_list && shallows.nr == 0)\n \t\treturn;\n+\tif (depth > 0 && deepen_rev_list)\n+\t\tdie(\"git upload-pack: deepen and deepen-since cannot be used together\");\n \tif (depth > 0)\n \t\tdeepen(depth, &shallows);\n+\telse if (deepen_rev_list) {\n+\t\tstruct argv_array av = ARGV_ARRAY_INIT;\n+\t\tint i;\n+\n+\t\targv_array_push(&av, \"rev-list\");\n+\t\tif (deepen_since)\n+\t\t\targv_array_pushf(&av, \"--max-age=%lu\", deepen_since);\n+\t\tfor (i = 0; i < want_obj.nr; i++) {\n+\t\t\tstruct object *o = want_obj.objects[i].item;\n+\t\t\targv_array_push(&av, oid_to_hex(&o->oid));\n+\t\t}\n+\t\tdeepen_by_rev_list(av.argc, av.argv, &shallows);\n+\t\targv_array_clear(&av);\n+\t}\n \telse\n \t\tif (shallows.nr > 0) {\n \t\t\tint i;\n@@ -756,7 +797,7 @@ static int send_ref(const char *refname, const struct object_id *oid,\n \t\t    int flag, void *cb_data)\n {\n \tstatic const char *capabilities = \"multi_ack thin-pack side-band\"\n-\t\t\" side-band-64k ofs-delta shallow no-progress\"\n+\t\t\" side-band-64k ofs-delta shallow deepen-since no-progress\"\n \t\t\" include-tag multi_ack_detailed\";\n \tconst char *refname_nons = strip_namespace(refname);\n \tstruct object_id peeled;\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279016","messageId":"1456235103-26317-16-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 15/25] fetch: define shallow boundary with --shallow-since","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:53Z","receivedAt":"2016-02-23T13:44:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/fetch-options.txt     |  4 ++++\n Documentation/git-fetch-pack.txt    |  4 ++++\n Documentation/gitremote-helpers.txt |  3 +++\n builtin/fetch-pack.c                |  4 ++++\n builtin/fetch.c                     | 29 +++++++++++++++++++++++------\n fetch-pack.c                        | 12 +++++++++++-\n fetch-pack.h                        |  1 +\n remote-curl.c                       | 11 +++++++++--\n transport.c                         |  4 ++++\n transport.h                         |  4 ++++\n 10 files changed, 67 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 952dfdf..8738d3d 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -14,6 +14,10 @@\n \tlinkgit:git-clone[1]), deepen or shorten the history to the specified\n \tnumber of commits. Tags for the deepened commits are not fetched.\n \n+--shallow-since=<date>::\n+\tDeepen or shorten the history of a shallow repository to\n+\tinclude all reachable commits after <date>.\n+\n --unshallow::\n \tIf the source repository is complete, convert a shallow\n \trepository to a complete one, removing all the limitations\ndiff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt\nindex 8680f45..99e6257 100644\n--- a/Documentation/git-fetch-pack.txt\n+++ b/Documentation/git-fetch-pack.txt\n@@ -87,6 +87,10 @@ be in a separate packet, and the list must end with a flush packet.\n \t'git-upload-pack' treats the special depth 2147483647 as\n \tinfinite even if there is an ancestor-chain that long.\n \n+--shallow-since=<date>::\n+\tDeepen or shorten the history of a shallow'repository to\n+\tinclude all reachable commits after <date>.\n+\n --no-progress::\n \tDo not show the progress.\n \ndiff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt\nindex 78e0b27..9971d9a 100644\n--- a/Documentation/gitremote-helpers.txt\n+++ b/Documentation/gitremote-helpers.txt\n@@ -415,6 +415,9 @@ set by Git if the remote helper has the 'option' capability.\n 'option depth' <depth>::\n \tDeepens the history of a shallow repository.\n \n+'option deepen-since <timestamp>::\n+\tDeepens the history of a shallow repository based on time.\n+\n 'option followtags' {'true'|'false'}::\n \tIf enabled the helper should automatically fetch annotated\n \ttag objects if the object the tag points at was transferred\ndiff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c\nindex 8332d3d..0402e27 100644\n--- a/builtin/fetch-pack.c\n+++ b/builtin/fetch-pack.c\n@@ -104,6 +104,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \t\t\targs.depth = strtol(arg, NULL, 0);\n \t\t\tcontinue;\n \t\t}\n+\t\tif (skip_prefix(arg, \"--shallow-since=\", &arg)) {\n+\t\t\targs.deepen_since = xstrdup(arg);\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(\"--no-progress\", arg)) {\n \t\t\targs.no_progress = 1;\n \t\t\tcontinue;\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 8e74213..283aa95 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -36,9 +36,10 @@ static int prune = -1; /* unspecified */\n \n static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;\n static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;\n-static int tags = TAGS_DEFAULT, unshallow, update_shallow;\n+static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n static int max_children = 1;\n static const char *depth;\n+static const char *deepen_since;\n static const char *upload_pack;\n static struct strbuf default_rla = STRBUF_INIT;\n static struct transport *gtransport;\n@@ -115,6 +116,8 @@ static struct option builtin_fetch_options[] = {\n \tOPT_BOOL(0, \"progress\", &progress, N_(\"force progress reporting\")),\n \tOPT_STRING(0, \"depth\", &depth, N_(\"depth\"),\n \t\t   N_(\"deepen history of shallow clone\")),\n+\tOPT_STRING(0, \"shallow-since\", &deepen_since, N_(\"time\"),\n+\t\t   N_(\"deepen history of shallow repository based on time\")),\n \t{ OPTION_SET_INT, 0, \"unshallow\", &unshallow, NULL,\n \t\t   N_(\"convert to a complete repository\"),\n \t\t   PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },\n@@ -754,7 +757,7 @@ static int quickfetch(struct ref *ref_map)\n \t * really need to perform.  Claiming failure now will ensure\n \t * we perform the network exchange to deepen our history.\n \t */\n-\tif (depth)\n+\tif (deepen)\n \t\treturn -1;\n \treturn check_everything_connected(iterate_ref_map, 1, &rm);\n }\n@@ -859,7 +862,7 @@ static void set_option(struct transport *transport, const char *name, const char\n \t\t\tname, transport->url);\n }\n \n-static struct transport *prepare_transport(struct remote *remote)\n+static struct transport *prepare_transport(struct remote *remote, int deepen)\n {\n \tstruct transport *transport;\n \ttransport = transport_get(remote, NULL);\n@@ -870,6 +873,8 @@ static struct transport *prepare_transport(struct remote *remote)\n \t\tset_option(transport, TRANS_OPT_KEEP, \"yes\");\n \tif (depth)\n \t\tset_option(transport, TRANS_OPT_DEPTH, depth);\n+\tif (deepen && deepen_since)\n+\t\tset_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since);\n \tif (update_shallow)\n \t\tset_option(transport, TRANS_OPT_UPDATE_SHALLOW, \"yes\");\n \treturn transport;\n@@ -877,8 +882,18 @@ static struct transport *prepare_transport(struct remote *remote)\n \n static void backfill_tags(struct transport *transport, struct ref *ref_map)\n {\n-\tif (transport->cannot_reuse) {\n-\t\tgsecondary = prepare_transport(transport->remote);\n+\tint cannot_reuse;\n+\n+\t/*\n+\t * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it\n+\t * when remote helper is used (setting it to an empty string\n+\t * is not unsetting). We could extend the remote helper\n+\t * protocol for that, but for now, just force a new connection\n+\t * without deepen-since.\n+\t */\n+\tcannot_reuse = transport->cannot_reuse || deepen_since;\n+\tif (cannot_reuse) {\n+\t\tgsecondary = prepare_transport(transport->remote, 0);\n \t\ttransport = gsecondary;\n \t}\n \n@@ -1095,7 +1110,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)\n \t\tdie(_(\"No remote repository specified.  Please, specify either a URL or a\\n\"\n \t\t    \"remote name from which new revisions should be fetched.\"));\n \n-\tgtransport = prepare_transport(remote);\n+\tgtransport = prepare_transport(remote, 1);\n \n \tif (prune < 0) {\n \t\t/* no command line request */\n@@ -1167,6 +1182,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t/* no need to be strict, transport_set_option() will validate it again */\n \tif (depth && atoi(depth) < 1)\n \t\tdie(_(\"depth %s is not a positive number\"), depth);\n+\tif (depth || deepen_since)\n+\t\tdeepen = 1;\n \n \tif (recurse_submodules != RECURSE_SUBMODULES_OFF) {\n \t\tif (recurse_submodules_default) {\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 8c37e5f..c57231b 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -21,6 +21,7 @@ static int fetch_unpack_limit = -1;\n static int unpack_limit = 100;\n static int prefer_ofs_delta = 1;\n static int no_done;\n+static int deepen_since_ok;\n static int fetch_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n static int agent_supported;\n@@ -326,6 +327,7 @@ static int find_common(struct fetch_pack_args *args,\n \t\t\tif (args->no_progress)   strbuf_addstr(&c, \" no-progress\");\n \t\t\tif (args->include_tag)   strbuf_addstr(&c, \" include-tag\");\n \t\t\tif (prefer_ofs_delta)   strbuf_addstr(&c, \" ofs-delta\");\n+\t\t\tif (deepen_since_ok)    strbuf_addstr(&c, \" deepen-since\");\n \t\t\tif (agent_supported)    strbuf_addf(&c, \" agent=%s\",\n \t\t\t\t\t\t\t    git_user_agent_sanitized());\n \t\t\tpacket_buf_write(&req_buf, \"want %s%s\\n\", remote_hex, c.buf);\n@@ -345,6 +347,10 @@ static int find_common(struct fetch_pack_args *args,\n \t\twrite_shallow_commits(&req_buf, 1, NULL);\n \tif (args->depth > 0)\n \t\tpacket_buf_write(&req_buf, \"deepen %d\", args->depth);\n+\tif (args->deepen_since) {\n+\t\tunsigned long max_age = approxidate(args->deepen_since);\n+\t\tpacket_buf_write(&req_buf, \"deepen-since %lu\", max_age);\n+\t}\n \tpacket_buf_flush(&req_buf);\n \tstate_len = req_buf.len;\n \n@@ -813,7 +819,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \n \tif ((args->depth > 0 || is_repository_shallow()) && !server_supports(\"shallow\"))\n \t\tdie(\"Server does not support shallow clients\");\n-\tif (args->depth > 0)\n+\tif (args->depth > 0 || args->deepen_since)\n \t\targs->deepen = 1;\n \tif (server_supports(\"multi_ack_detailed\")) {\n \t\tprint_verbose(args, \"Server supports multi_ack_detailed\");\n@@ -861,6 +867,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \t\t\tprint_verbose(args, \"Server version is %.*s\",\n \t\t\t\t      agent_len, agent_feature);\n \t}\n+\tif (server_supports(\"deepen-since\"))\n+\t\tdeepen_since_ok = 1;\n+\telse if (args->deepen_since)\n+\t\tdie(\"Server does not support --shallow-since\");\n \n \tif (everything_local(args, &ref, sought, nr_sought)) {\n \t\tpacket_flush(fd[1]);\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex 4d0adb0..f7eadb2 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -10,6 +10,7 @@ struct fetch_pack_args {\n \tconst char *uploadpack;\n \tint unpacklimit;\n \tint depth;\n+\tconst char *deepen_since;\n \tunsigned quiet:1;\n \tunsigned keep_pack:1;\n \tunsigned lock_pack:1;\ndiff --git a/remote-curl.c b/remote-curl.c\nindex 4289c20..ffa4faa 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -20,6 +20,7 @@ static struct strbuf url = STRBUF_INIT;\n struct options {\n \tint verbosity;\n \tunsigned long depth;\n+\tchar *deepen_since;\n \tunsigned progress : 1,\n \t\tcheck_self_contained_and_connected : 1,\n \t\tcloning : 1,\n@@ -60,6 +61,10 @@ static int set_option(const char *name, const char *value)\n \t\toptions.depth = v;\n \t\treturn 0;\n \t}\n+\telse if (!strcmp(name, \"deepen-since\")) {\n+\t\toptions.deepen_since = xstrdup(value);\n+\t\treturn 0;\n+\t}\n \telse if (!strcmp(name, \"followtags\")) {\n \t\tif (!strcmp(value, \"true\"))\n \t\t\toptions.followtags = 1;\n@@ -699,8 +704,8 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)\n \tchar **targets = xmalloc(nr_heads * sizeof(char*));\n \tint ret, i;\n \n-\tif (options.depth)\n-\t\tdie(\"dumb http transport does not support --depth\");\n+\tif (options.depth || options.deepen_since)\n+\t\tdie(\"dumb http transport does not support shallow capabilities\");\n \tfor (i = 0; i < nr_heads; i++)\n \t\ttargets[i] = xstrdup(oid_to_hex(&to_fetch[i]->old_oid));\n \n@@ -749,6 +754,8 @@ static int fetch_git(struct discovery *heads,\n \t\targv_array_push(&args, \"--no-progress\");\n \tif (options.depth)\n \t\targv_array_pushf(&args, \"--depth=%lu\", options.depth);\n+\tif (options.deepen_since)\n+\t\targv_array_pushf(&args, \"--shallow-since=%s\", options.deepen_since);\n \targv_array_push(&args, url.buf);\n \n \tfor (i = 0; i < nr_heads; i++) {\ndiff --git a/transport.c b/transport.c\nindex c92f8ae..f04a302 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -151,6 +151,9 @@ static int set_git_option(struct git_transport_options *opts,\n \t\t\t\tdie(\"transport: invalid depth option '%s'\", value);\n \t\t}\n \t\treturn 0;\n+\t} else if (!strcmp(name, TRANS_OPT_DEEPEN_SINCE)) {\n+\t\topts->deepen_since = value;\n+\t\treturn 0;\n \t}\n \treturn 1;\n }\n@@ -205,6 +208,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \targs.quiet = (transport->verbose < 0);\n \targs.no_progress = !transport->progress;\n \targs.depth = data->options.depth;\n+\targs.deepen_since = data->options.deepen_since;\n \targs.check_self_contained_and_connected =\n \t\tdata->options.check_self_contained_and_connected;\n \targs.cloning = transport->cloning;\ndiff --git a/transport.h b/transport.h\nindex 8ebaaf2..9c10a44 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -13,6 +13,7 @@ struct git_transport_options {\n \tunsigned self_contained_and_connected : 1;\n \tunsigned update_shallow : 1;\n \tint depth;\n+\tconst char *deepen_since;\n \tconst char *uploadpack;\n \tconst char *receivepack;\n \tstruct push_cas_option *cas;\n@@ -171,6 +172,9 @@ int transport_restrict_protocols(void);\n /* Limit the depth of the fetch if not null */\n #define TRANS_OPT_DEPTH \"depth\"\n \n+/* Limit the depth of the fetch based on time if not null */\n+#define TRANS_OPT_DEEPEN_SINCE \"deepen-since\"\n+\n /* Aggressively fetch annotated tags if possible */\n #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n \n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279017","messageId":"1456235103-26317-17-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 16/25] clone: define shallow clone boundary based on time with --shallow-since","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:54Z","receivedAt":"2016-02-23T13:44:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-clone.txt |  3 +++\n builtin/clone.c             | 16 +++++++++++++---\n 2 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex b7c467a..a410409 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -193,6 +193,9 @@ objects from the source repository into a pack in the cloned repository.\n \t`--no-single-branch` is given to fetch the histories near the\n \ttips of all branches.\n \n+--shallow-since=<date>::\n+\tCreate a shallow clone with a history after the specified time.\n+\n --[no-]single-branch::\n \tClone only the history leading to the tip of a single branch,\n \teither specified by the `--branch` option or the primary\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex bcba080..dc2ef4f 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -40,7 +40,8 @@ static const char * const builtin_clone_usage[] = {\n \n static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;\n static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;\n-static char *option_template, *option_depth;\n+static int deepen;\n+static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n static char *option_branch = NULL;\n static const char *real_git_dir;\n@@ -86,6 +87,8 @@ static struct option builtin_clone_options[] = {\n \t\t   N_(\"path to git-upload-pack on the remote\")),\n \tOPT_STRING(0, \"depth\", &option_depth, N_(\"depth\"),\n \t\t    N_(\"create a shallow clone of that depth\")),\n+\tOPT_STRING(0, \"shallow-since\", &option_since, N_(\"time\"),\n+\t\t    N_(\"create a shallow clone since a specific time\")),\n \tOPT_BOOL(0, \"single-branch\", &option_single_branch,\n \t\t    N_(\"clone only one branch, HEAD or --branch\")),\n \tOPT_STRING(0, \"separate-git-dir\", &real_git_dir, N_(\"gitdir\"),\n@@ -849,8 +852,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n \t\t\tbuiltin_clone_usage, builtin_clone_options);\n \n+\tif (option_depth || option_since)\n+\t\tdeepen = 1;\n \tif (option_single_branch == -1)\n-\t\toption_single_branch = option_depth ? 1 : 0;\n+\t\toption_single_branch = deepen ? 1 : 0;\n \n \tif (option_mirror)\n \t\toption_bare = 1;\n@@ -976,6 +981,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tif (is_local) {\n \t\tif (option_depth)\n \t\t\twarning(_(\"--depth is ignored in local clones; use file:// instead.\"));\n+\t\tif (option_since)\n+\t\t\twarning(_(\"--shallow-since is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n@@ -994,6 +1001,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tif (option_depth)\n \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n \t\t\t\t     option_depth);\n+\tif (option_since)\n+\t\ttransport_set_option(transport, TRANS_OPT_DEEPEN_SINCE,\n+\t\t\t\t     option_since);\n \tif (option_single_branch)\n \t\ttransport_set_option(transport, TRANS_OPT_FOLLOWTAGS, \"1\");\n \n@@ -1001,7 +1011,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\ttransport_set_option(transport, TRANS_OPT_UPLOADPACK,\n \t\t\t\t     option_upload_pack);\n \n-\tif (transport->smart_options && !option_depth)\n+\tif (transport->smart_options && !deepen)\n \t\ttransport->smart_options->check_self_contained_and_connected = 1;\n \n \trefs = transport_get_remote_refs(transport);\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279020","messageId":"1456235103-26317-18-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 17/25] t5500, t5539: tests for shallow depth since a specific date","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:55Z","receivedAt":"2016-02-23T13:44:55Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t5500-fetch-pack.sh         | 24 ++++++++++++++++++++++++\n t/t5539-fetch-http-shallow.sh | 26 ++++++++++++++++++++++++++\n 2 files changed, 50 insertions(+)\n\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex e5f83bf..26f050d 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -637,4 +637,28 @@ test_expect_success MINGW 'fetch-pack --diag-url c:repo' '\n \tcheck_prot_path c:repo file c:repo\n '\n \n+test_expect_success 'clone shallow since ...' '\n+\ttest_create_repo shallow-since &&\n+\t(\n+\tcd shallow-since &&\n+\tGIT_COMMITTER_DATE=\"100000000 +0700\" git commit --allow-empty -m one &&\n+\tGIT_COMMITTER_DATE=\"200000000 +0700\" git commit --allow-empty -m two &&\n+\tGIT_COMMITTER_DATE=\"300000000 +0700\" git commit --allow-empty -m three &&\n+\tgit clone --shallow-since \"300000000 +0700\" \"file://$(pwd)/.\" ../shallow11 &&\n+\tgit -C ../shallow11 log --pretty=tformat:%s HEAD >actual &&\n+\techo three >expected &&\n+\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'fetch shallow since ...' '\n+\tgit -C shallow11 fetch --shallow-since \"200000000 +0700\" origin &&\n+\tgit -C shallow11 log --pretty=tformat:%s origin/master >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tthree\n+\ttwo\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n test_done\ndiff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh\nindex 37a4335..6d77ca7 100755\n--- a/t/t5539-fetch-http-shallow.sh\n+++ b/t/t5539-fetch-http-shallow.sh\n@@ -73,5 +73,31 @@ test_expect_success 'no shallow lines after receiving ACK ready' '\n \t)\n '\n \n+test_expect_success 'clone shallow since ...' '\n+\ttest_create_repo shallow-since &&\n+\t(\n+\tcd shallow-since &&\n+\tGIT_COMMITTER_DATE=\"100000000 +0700\" git commit --allow-empty -m one &&\n+\tGIT_COMMITTER_DATE=\"200000000 +0700\" git commit --allow-empty -m two &&\n+\tGIT_COMMITTER_DATE=\"300000000 +0700\" git commit --allow-empty -m three &&\n+\tmv .git \"$HTTPD_DOCUMENT_ROOT_PATH/shallow-since.git\" &&\n+\tgit clone --shallow-since \"300000000 +0700\" $HTTPD_URL/smart/shallow-since.git ../shallow11 &&\n+\tgit -C ../shallow11 log --pretty=tformat:%s HEAD >actual &&\n+\techo three >expected &&\n+\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'fetch shallow since ...' '\n+\tgit -C shallow11 fetch --shallow-since \"200000000 +0700\" origin &&\n+\tgit -C shallow11 log --pretty=tformat:%s origin/master >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tthree\n+\ttwo\n+\tEOF\n+\ttest_cmp expected actual\n+'\n+\n+\n stop_httpd\n test_done\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279019","messageId":"1456235103-26317-19-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 18/25] refs: add expand_ref()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:56Z","receivedAt":"2016-02-23T13:44:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is basically dwim_ref() without @{} support. To be used on the\nserver side where we want to expand abbreviated to full ref names and\nnothing else. The first user is \"git clone/fetch --shallow-exclude\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 8 +++++++-\n refs.h | 1 +\n 2 files changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git a/refs.c b/refs.c\nindex e2d34b2..842e4d8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -392,6 +392,13 @@ static char *substitute_branch_name(const char **string, int *len)\n int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)\n {\n \tchar *last_branch = substitute_branch_name(&str, &len);\n+\tint   refs_found  = expand_ref(str, len, sha1, ref);\n+\tfree(last_branch);\n+\treturn refs_found;\n+}\n+\n+int expand_ref(const char *str, int len, unsigned char *sha1, char **ref)\n+{\n \tconst char **p, *r;\n \tint refs_found = 0;\n \n@@ -417,7 +424,6 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)\n \t\t\twarning(\"ignoring broken ref %s.\", fullref);\n \t\t}\n \t}\n-\tfree(last_branch);\n \treturn refs_found;\n }\n \ndiff --git a/refs.h b/refs.h\nindex 3c3da29..31a2fa6 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -90,6 +90,7 @@ extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned c\n  */\n extern int refname_match(const char *abbrev_name, const char *full_name);\n \n+extern int expand_ref(const char *str, int len, unsigned char *sha1, char **ref);\n extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);\n extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);\n \n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279018","messageId":"1456235103-26317-20-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 19/25] upload-pack: support define shallow boundary by excluding revisions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:57Z","receivedAt":"2016-02-23T13:44:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This should allow the user to say \"create a shallow clone of this branch\nafter version <some-tag>\".\n\nShort refs are accepted and expanded at the server side with expand_ref()\nbecause we cannot expand (unknown) refs from the client side.\n\nLike deepen-since, deepen-not cannot be used with deepen. But deepen-not\ncan be mixed with deepen-since. The result is exactly how you do the\ncommand \"git rev-list --since=... --not ref\".\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/technical/pack-protocol.txt         |  3 ++-\n Documentation/technical/protocol-capabilities.txt |  9 +++++++++\n upload-pack.c                                     | 23 +++++++++++++++++++++--\n 3 files changed, 32 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt\nindex 9251df1..dee33a6 100644\n--- a/Documentation/technical/pack-protocol.txt\n+++ b/Documentation/technical/pack-protocol.txt\n@@ -220,7 +220,8 @@ out of what the server said it could do with the first 'want' line.\n   shallow-line      =  PKT-LINE(\"shallow\" SP obj-id)\n \n   depth-request     =  PKT-LINE(\"deepen\" SP depth) /\n-\t\t       PKT-LINE(\"deepen-since\" SP timestamp)\n+\t\t       PKT-LINE(\"deepen-since\" SP timestamp) /\n+\t\t       PKT-LINE(\"deepen-not\" SP ref)\n \n   first-want        =  PKT-LINE(\"want\" SP obj-id SP capability-list)\n   additional-want   =  PKT-LINE(\"want\" SP obj-id)\ndiff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt\nindex f08cc4e..0e6b57d 100644\n--- a/Documentation/technical/protocol-capabilities.txt\n+++ b/Documentation/technical/protocol-capabilities.txt\n@@ -188,6 +188,15 @@ specific time, instead of depth. Internally it's equivalent of doing\n \"rev-list --max-age=<timestamp>\" on the server side. \"deepen-since\"\n cannot be used with \"deepen\".\n \n+deepen-not\n+----------\n+\n+This capability adds \"deepen-not\" command to fetch-pack/upload-pack\n+protocol so the client can request shallow clones that are cut at a\n+specific revision, instead of depth. Internally it's equivalent of\n+doing \"rev-list --not <rev>\" on the server side. \"deepen-not\"\n+cannot be used with \"deepen\", but can be used with \"deepen-since\".\n+\n no-progress\n -----------\n \ndiff --git a/upload-pack.c b/upload-pack.c\nindex 58c0936..95a0bfb 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -628,6 +628,7 @@ static void deepen_by_rev_list(int ac, const char **av,\n static void receive_needs(void)\n {\n \tstruct object_array shallows = OBJECT_ARRAY_INIT;\n+\tstruct string_list deepen_not = STRING_LIST_INIT_DUP;\n \tint depth = 0;\n \tint has_non_tip = 0;\n \tunsigned long deepen_since = 0;\n@@ -678,6 +679,16 @@ static void receive_needs(void)\n \t\t\tdeepen_rev_list = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (skip_prefix(line, \"deepen-not \", &arg)) {\n+\t\t\tchar *ref = NULL;\n+\t\t\tunsigned char sha1[20];\n+\t\t\tif (expand_ref(arg, strlen(arg), sha1, &ref) != 1)\n+\t\t\t\tdie(\"git upload-pack: ambiguous deepen-not: %s\", line);\n+\t\t\tstring_list_append(&deepen_not, ref);\n+\t\t\tfree(ref);\n+\t\t\tdeepen_rev_list = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!skip_prefix(line, \"want \", &arg) ||\n \t\t    get_sha1_hex(arg, sha1_buf))\n \t\t\tdie(\"git upload-pack: protocol error, \"\n@@ -732,7 +743,7 @@ static void receive_needs(void)\n \tif (depth == 0 && !deepen_rev_list && shallows.nr == 0)\n \t\treturn;\n \tif (depth > 0 && deepen_rev_list)\n-\t\tdie(\"git upload-pack: deepen and deepen-since cannot be used together\");\n+\t\tdie(\"git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together\");\n \tif (depth > 0)\n \t\tdeepen(depth, &shallows);\n \telse if (deepen_rev_list) {\n@@ -742,6 +753,14 @@ static void receive_needs(void)\n \t\targv_array_push(&av, \"rev-list\");\n \t\tif (deepen_since)\n \t\t\targv_array_pushf(&av, \"--max-age=%lu\", deepen_since);\n+\t\tif (deepen_not.nr) {\n+\t\t\targv_array_push(&av, \"--not\");\n+\t\t\tfor (i = 0; i < deepen_not.nr; i++) {\n+\t\t\t\tstruct string_list_item *s = deepen_not.items + i;\n+\t\t\t\targv_array_push(&av, s->string);\n+\t\t\t}\n+\t\t\targv_array_push(&av, \"--not\");\n+\t\t}\n \t\tfor (i = 0; i < want_obj.nr; i++) {\n \t\t\tstruct object *o = want_obj.objects[i].item;\n \t\t\targv_array_push(&av, oid_to_hex(&o->oid));\n@@ -797,7 +816,7 @@ static int send_ref(const char *refname, const struct object_id *oid,\n \t\t    int flag, void *cb_data)\n {\n \tstatic const char *capabilities = \"multi_ack thin-pack side-band\"\n-\t\t\" side-band-64k ofs-delta shallow deepen-since no-progress\"\n+\t\t\" side-band-64k ofs-delta shallow deepen-since deepen-not no-progress\"\n \t\t\" include-tag multi_ack_detailed\";\n \tconst char *refname_nons = strip_namespace(refname);\n \tstruct object_id peeled;\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279021","messageId":"1456235103-26317-21-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 20/25] fetch: define shallow boundary with --shallow-exclude","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:58Z","receivedAt":"2016-02-23T13:44:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/fetch-options.txt     |  5 +++++\n Documentation/git-fetch-pack.txt    |  5 +++++\n Documentation/gitremote-helpers.txt |  4 ++++\n builtin/fetch-pack.c                |  7 +++++++\n builtin/fetch.c                     | 21 ++++++++++++++++++---\n fetch-pack.c                        | 15 ++++++++++++++-\n fetch-pack.h                        |  1 +\n remote-curl.c                       |  9 +++++++++\n transport-helper.c                  | 24 ++++++++++++++++++++++++\n transport.c                         |  4 ++++\n transport.h                         |  6 ++++++\n 11 files changed, 97 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 8738d3d..7aa1285 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -18,6 +18,11 @@\n \tDeepen or shorten the history of a shallow repository to\n \tinclude all reachable commits after <date>.\n \n+--shallow-exclude=<revision>::\n+\tDeepen or shorten the history of a shallow repository to\n+\texclude commits reachable from a specified remote branch or tag.\n+\tThis option can be specified multiple times.\n+\n --unshallow::\n \tIf the source repository is complete, convert a shallow\n \trepository to a complete one, removing all the limitations\ndiff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt\nindex 99e6257..4d15b04 100644\n--- a/Documentation/git-fetch-pack.txt\n+++ b/Documentation/git-fetch-pack.txt\n@@ -91,6 +91,11 @@ be in a separate packet, and the list must end with a flush packet.\n \tDeepen or shorten the history of a shallow'repository to\n \tinclude all reachable commits after <date>.\n \n+--shallow-exclude=<revision>::\n+\tDeepen or shorten the history of a shallow repository to\n+\texclude commits reachable from a specified remote branch or tag.\n+\tThis option can be specified multiple times.\n+\n --no-progress::\n \tDo not show the progress.\n \ndiff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt\nindex 9971d9a..75bb638 100644\n--- a/Documentation/gitremote-helpers.txt\n+++ b/Documentation/gitremote-helpers.txt\n@@ -418,6 +418,10 @@ set by Git if the remote helper has the 'option' capability.\n 'option deepen-since <timestamp>::\n \tDeepens the history of a shallow repository based on time.\n \n+'option deepen-not <ref>::\n+\tDeepens the history of a shallow repository excluding ref.\n+\tMultiple options add up.\n+\n 'option followtags' {'true'|'false'}::\n \tIf enabled the helper should automatically fetch annotated\n \ttag objects if the object the tag points at was transferred\ndiff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c\nindex 0402e27..07570be 100644\n--- a/builtin/fetch-pack.c\n+++ b/builtin/fetch-pack.c\n@@ -50,6 +50,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \tstruct child_process *conn;\n \tstruct fetch_pack_args args;\n \tstruct sha1_array shallow = SHA1_ARRAY_INIT;\n+\tstruct string_list deepen_not = STRING_LIST_INIT_DUP;\n \n \tpacket_trace_identity(\"fetch-pack\");\n \n@@ -108,6 +109,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \t\t\targs.deepen_since = xstrdup(arg);\n \t\t\tcontinue;\n \t\t}\n+\t\tif (skip_prefix(arg, \"--shallow-exclude=\", &arg)) {\n+\t\t\tstring_list_append(&deepen_not, arg);\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(\"--no-progress\", arg)) {\n \t\t\targs.no_progress = 1;\n \t\t\tcontinue;\n@@ -135,6 +140,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \t\t}\n \t\tusage(fetch_pack_usage);\n \t}\n+\tif (deepen_not.nr)\n+\t\targs.deepen_not = &deepen_not;\n \n \tif (i < argc)\n \t\tdest = argv[i++];\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 283aa95..8d19bf6 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -41,6 +41,7 @@ static int max_children = 1;\n static const char *depth;\n static const char *deepen_since;\n static const char *upload_pack;\n+struct string_list deepen_not = STRING_LIST_INIT_NODUP;\n static struct strbuf default_rla = STRBUF_INIT;\n static struct transport *gtransport;\n static struct transport *gsecondary;\n@@ -50,6 +51,13 @@ static int shown_url = 0;\n static int refmap_alloc, refmap_nr;\n static const char **refmap_array;\n \n+static int option_parse_deepen_not(const struct option *opt,\n+\t\t\t\t   const char *arg, int unset)\n+{\n+\tstring_list_append(&deepen_not, arg);\n+\treturn 0;\n+}\n+\n static int option_parse_recurse_submodules(const struct option *opt,\n \t\t\t\t   const char *arg, int unset)\n {\n@@ -118,6 +126,9 @@ static struct option builtin_fetch_options[] = {\n \t\t   N_(\"deepen history of shallow clone\")),\n \tOPT_STRING(0, \"shallow-since\", &deepen_since, N_(\"time\"),\n \t\t   N_(\"deepen history of shallow repository based on time\")),\n+\t{ OPTION_CALLBACK, 0, \"shallow-exclude\", NULL, N_(\"revision\"),\n+\t\t    N_(\"deepen history of shallow clone by excluding rev\"),\n+\t\t    PARSE_OPT_NONEG, option_parse_deepen_not },\n \t{ OPTION_SET_INT, 0, \"unshallow\", &unshallow, NULL,\n \t\t   N_(\"convert to a complete repository\"),\n \t\t   PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },\n@@ -875,6 +886,9 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)\n \t\tset_option(transport, TRANS_OPT_DEPTH, depth);\n \tif (deepen && deepen_since)\n \t\tset_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since);\n+\tif (deepen && deepen_not.nr)\n+\t\tset_option(transport, TRANS_OPT_DEEPEN_NOT,\n+\t\t\t   (const char *)&deepen_not);\n \tif (update_shallow)\n \t\tset_option(transport, TRANS_OPT_UPDATE_SHALLOW, \"yes\");\n \treturn transport;\n@@ -889,9 +903,10 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)\n \t * when remote helper is used (setting it to an empty string\n \t * is not unsetting). We could extend the remote helper\n \t * protocol for that, but for now, just force a new connection\n-\t * without deepen-since.\n+\t * without deepen-since. Similar story for deepen-not.\n \t */\n-\tcannot_reuse = transport->cannot_reuse || deepen_since;\n+\tcannot_reuse = transport->cannot_reuse ||\n+\t\tdeepen_since || deepen_not.nr;\n \tif (cannot_reuse) {\n \t\tgsecondary = prepare_transport(transport->remote, 0);\n \t\ttransport = gsecondary;\n@@ -1182,7 +1197,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \t/* no need to be strict, transport_set_option() will validate it again */\n \tif (depth && atoi(depth) < 1)\n \t\tdie(_(\"depth %s is not a positive number\"), depth);\n-\tif (depth || deepen_since)\n+\tif (depth || deepen_since || deepen_not.nr)\n \t\tdeepen = 1;\n \n \tif (recurse_submodules != RECURSE_SUBMODULES_OFF) {\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex c57231b..a684160 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -22,6 +22,7 @@ static int unpack_limit = 100;\n static int prefer_ofs_delta = 1;\n static int no_done;\n static int deepen_since_ok;\n+static int deepen_not_ok;\n static int fetch_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n static int agent_supported;\n@@ -328,6 +329,7 @@ static int find_common(struct fetch_pack_args *args,\n \t\t\tif (args->include_tag)   strbuf_addstr(&c, \" include-tag\");\n \t\t\tif (prefer_ofs_delta)   strbuf_addstr(&c, \" ofs-delta\");\n \t\t\tif (deepen_since_ok)    strbuf_addstr(&c, \" deepen-since\");\n+\t\t\tif (deepen_not_ok)      strbuf_addstr(&c, \" deepen-not\");\n \t\t\tif (agent_supported)    strbuf_addf(&c, \" agent=%s\",\n \t\t\t\t\t\t\t    git_user_agent_sanitized());\n \t\t\tpacket_buf_write(&req_buf, \"want %s%s\\n\", remote_hex, c.buf);\n@@ -351,6 +353,13 @@ static int find_common(struct fetch_pack_args *args,\n \t\tunsigned long max_age = approxidate(args->deepen_since);\n \t\tpacket_buf_write(&req_buf, \"deepen-since %lu\", max_age);\n \t}\n+\tif (args->deepen_not) {\n+\t\tint i;\n+\t\tfor (i = 0; i < args->deepen_not->nr; i++) {\n+\t\t\tstruct string_list_item *s = args->deepen_not->items + i;\n+\t\t\tpacket_buf_write(&req_buf, \"deepen-not %s\", s->string);\n+\t\t}\n+\t}\n \tpacket_buf_flush(&req_buf);\n \tstate_len = req_buf.len;\n \n@@ -819,7 +828,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \n \tif ((args->depth > 0 || is_repository_shallow()) && !server_supports(\"shallow\"))\n \t\tdie(\"Server does not support shallow clients\");\n-\tif (args->depth > 0 || args->deepen_since)\n+\tif (args->depth > 0 || args->deepen_since || args->deepen_not)\n \t\targs->deepen = 1;\n \tif (server_supports(\"multi_ack_detailed\")) {\n \t\tprint_verbose(args, \"Server supports multi_ack_detailed\");\n@@ -871,6 +880,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \t\tdeepen_since_ok = 1;\n \telse if (args->deepen_since)\n \t\tdie(\"Server does not support --shallow-since\");\n+\tif (server_supports(\"deepen-not\"))\n+\t\tdeepen_not_ok = 1;\n+\telse if (args->deepen_not)\n+\t\tdie(\"Server does not support --shallow-exclude\");\n \n \tif (everything_local(args, &ref, sought, nr_sought)) {\n \t\tpacket_flush(fd[1]);\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex f7eadb2..144301f 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -11,6 +11,7 @@ struct fetch_pack_args {\n \tint unpacklimit;\n \tint depth;\n \tconst char *deepen_since;\n+\tconst struct string_list *deepen_not;\n \tunsigned quiet:1;\n \tunsigned keep_pack:1;\n \tunsigned lock_pack:1;\ndiff --git a/remote-curl.c b/remote-curl.c\nindex ffa4faa..71122ac 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -21,6 +21,7 @@ struct options {\n \tint verbosity;\n \tunsigned long depth;\n \tchar *deepen_since;\n+\tstruct string_list deepen_not;\n \tunsigned progress : 1,\n \t\tcheck_self_contained_and_connected : 1,\n \t\tcloning : 1,\n@@ -65,6 +66,10 @@ static int set_option(const char *name, const char *value)\n \t\toptions.deepen_since = xstrdup(value);\n \t\treturn 0;\n \t}\n+\telse if (!strcmp(name, \"deepen-not\")) {\n+\t\tstring_list_append(&options.deepen_not, value);\n+\t\treturn 0;\n+\t}\n \telse if (!strcmp(name, \"followtags\")) {\n \t\tif (!strcmp(value, \"true\"))\n \t\t\toptions.followtags = 1;\n@@ -756,6 +761,9 @@ static int fetch_git(struct discovery *heads,\n \t\targv_array_pushf(&args, \"--depth=%lu\", options.depth);\n \tif (options.deepen_since)\n \t\targv_array_pushf(&args, \"--shallow-since=%s\", options.deepen_since);\n+\tfor (i = 0; i < options.deepen_not.nr; i++)\n+\t\targv_array_pushf(&args, \"--shallow-exclude=%s\",\n+\t\t\t\t options.deepen_not.items[i].string);\n \targv_array_push(&args, url.buf);\n \n \tfor (i = 0; i < nr_heads; i++) {\n@@ -977,6 +985,7 @@ int main(int argc, const char **argv)\n \toptions.verbosity = 1;\n \toptions.progress = !!isatty(2);\n \toptions.thin = 1;\n+\tstring_list_init(&options.deepen_not, 1);\n \n \tremote = remote_get(argv[1]);\n \ndiff --git a/transport-helper.c b/transport-helper.c\nindex 35023da..d644568 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -282,6 +282,26 @@ static int strbuf_set_helper_option(struct helper_data *data,\n \treturn ret;\n }\n \n+static int string_list_set_helper_option(struct helper_data *data,\n+\t\t\t\t\t const char *name,\n+\t\t\t\t\t struct string_list *list)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint i, ret = 0;\n+\n+\tfor (i = 0; i < list->nr; i++) {\n+\t\tstrbuf_addf(&buf, \"option %s \", name);\n+\t\tquote_c_style(list->items[i].string, &buf, NULL, 0);\n+\t\tstrbuf_addch(&buf, '\\n');\n+\n+\t\tif ((ret = strbuf_set_helper_option(data, &buf)))\n+\t\t\tbreak;\n+\t\tstrbuf_reset(&buf);\n+\t}\n+\tstrbuf_release(&buf);\n+\treturn ret;\n+}\n+\n static int set_helper_option(struct transport *transport,\n \t\t\t  const char *name, const char *value)\n {\n@@ -294,6 +314,10 @@ static int set_helper_option(struct transport *transport,\n \tif (!data->option)\n \t\treturn 1;\n \n+\tif (!strcmp(name, \"deepen-not\"))\n+\t\treturn string_list_set_helper_option(data, name,\n+\t\t\t\t\t\t     (struct string_list *)value);\n+\n \tfor (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {\n \t\tif (!strcmp(name, unsupported_options[i]))\n \t\t\treturn 1;\ndiff --git a/transport.c b/transport.c\nindex f04a302..3e6f3aa 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -154,6 +154,9 @@ static int set_git_option(struct git_transport_options *opts,\n \t} else if (!strcmp(name, TRANS_OPT_DEEPEN_SINCE)) {\n \t\topts->deepen_since = value;\n \t\treturn 0;\n+\t} else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) {\n+\t\topts->deepen_not = (const struct string_list *)value;\n+\t\treturn 0;\n \t}\n \treturn 1;\n }\n@@ -209,6 +212,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \targs.no_progress = !transport->progress;\n \targs.depth = data->options.depth;\n \targs.deepen_since = data->options.deepen_since;\n+\targs.deepen_not = data->options.deepen_not;\n \targs.check_self_contained_and_connected =\n \t\tdata->options.check_self_contained_and_connected;\n \targs.cloning = transport->cloning;\ndiff --git a/transport.h b/transport.h\nindex 9c10a44..ab61932 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -5,6 +5,8 @@\n #include \"run-command.h\"\n #include \"remote.h\"\n \n+struct string_list;\n+\n struct git_transport_options {\n \tunsigned thin : 1;\n \tunsigned keep : 1;\n@@ -14,6 +16,7 @@ struct git_transport_options {\n \tunsigned update_shallow : 1;\n \tint depth;\n \tconst char *deepen_since;\n+\tconst struct string_list *deepen_not;\n \tconst char *uploadpack;\n \tconst char *receivepack;\n \tstruct push_cas_option *cas;\n@@ -175,6 +178,9 @@ int transport_restrict_protocols(void);\n /* Limit the depth of the fetch based on time if not null */\n #define TRANS_OPT_DEEPEN_SINCE \"deepen-since\"\n \n+/* Limit the depth of the fetch based on revs if not null */\n+#define TRANS_OPT_DEEPEN_NOT \"deepen-not\"\n+\n /* Aggressively fetch annotated tags if possible */\n #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n \n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279023","messageId":"1456235103-26317-22-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 21/25] clone: define shallow clone boundary with --shallow-exclude","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:44:59Z","receivedAt":"2016-02-23T13:44:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-clone.txt |  5 +++++\n builtin/clone.c             | 18 +++++++++++++++++-\n 2 files changed, 22 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex a410409..5049663 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -196,6 +196,11 @@ objects from the source repository into a pack in the cloned repository.\n --shallow-since=<date>::\n \tCreate a shallow clone with a history after the specified time.\n \n+--shallow-exclude=<revision>::\n+\tCreate a shallow clone with a history, excluding commits\n+\treachable from a specified remote branch or tag.  This option\n+\tcan be specified multiple times.\n+\n --[no-]single-branch::\n \tClone only the history leading to the tip of a single branch,\n \teither specified by the `--branch` option or the primary\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex dc2ef4f..5ccf6b7 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -44,6 +44,7 @@ static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n static char *option_branch = NULL;\n+static struct string_list option_not = STRING_LIST_INIT_NODUP;\n static const char *real_git_dir;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbosity;\n@@ -52,6 +53,13 @@ static struct string_list option_config;\n static struct string_list option_reference;\n static int option_dissociate;\n \n+static int option_parse_deepen_not(const struct option *opt,\n+\t\t\t\t   const char *arg, int unset)\n+{\n+\tstring_list_append(&option_not, arg);\n+\treturn 0;\n+}\n+\n static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n@@ -89,6 +97,9 @@ static struct option builtin_clone_options[] = {\n \t\t    N_(\"create a shallow clone of that depth\")),\n \tOPT_STRING(0, \"shallow-since\", &option_since, N_(\"time\"),\n \t\t    N_(\"create a shallow clone since a specific time\")),\n+\t{ OPTION_CALLBACK, 0, \"shallow-exclude\", NULL, N_(\"revision\"),\n+\t\t    N_(\"deepen history of shallow clone by excluding rev\"),\n+\t\t    PARSE_OPT_NONEG, option_parse_deepen_not },\n \tOPT_BOOL(0, \"single-branch\", &option_single_branch,\n \t\t    N_(\"clone only one branch, HEAD or --branch\")),\n \tOPT_STRING(0, \"separate-git-dir\", &real_git_dir, N_(\"gitdir\"),\n@@ -852,7 +863,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n \t\t\tbuiltin_clone_usage, builtin_clone_options);\n \n-\tif (option_depth || option_since)\n+\tif (option_depth || option_since || option_not.nr)\n \t\tdeepen = 1;\n \tif (option_single_branch == -1)\n \t\toption_single_branch = deepen ? 1 : 0;\n@@ -983,6 +994,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\twarning(_(\"--depth is ignored in local clones; use file:// instead.\"));\n \t\tif (option_since)\n \t\t\twarning(_(\"--shallow-since is ignored in local clones; use file:// instead.\"));\n+\t\tif (option_not.nr)\n+\t\t\twarning(_(\"--shallow-exclude is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n@@ -1004,6 +1017,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tif (option_since)\n \t\ttransport_set_option(transport, TRANS_OPT_DEEPEN_SINCE,\n \t\t\t\t     option_since);\n+\tif (option_not.nr)\n+\t\ttransport_set_option(transport, TRANS_OPT_DEEPEN_NOT,\n+\t\t\t\t     (const char *)&option_not);\n \tif (option_single_branch)\n \t\ttransport_set_option(transport, TRANS_OPT_FOLLOWTAGS, \"1\");\n \n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279025","messageId":"1456235103-26317-23-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 22/25] t5500, t5539: tests for shallow depth excluding a ref","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:45:00Z","receivedAt":"2016-02-23T13:45:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t5500-fetch-pack.sh         | 22 ++++++++++++++++++++++\n t/t5539-fetch-http-shallow.sh | 22 ++++++++++++++++++++++\n 2 files changed, 44 insertions(+)\n\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 26f050d..a3fe5ca 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -661,4 +661,26 @@ test_expect_success 'fetch shallow since ...' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'shallow clone exclude tag two' '\n+\ttest_create_repo shallow-exclude &&\n+\t(\n+\tcd shallow-exclude &&\n+\ttest_commit one &&\n+\ttest_commit two &&\n+\ttest_commit three &&\n+\tgit clone --shallow-exclude two \"file://$(pwd)/.\" ../shallow12 &&\n+\tgit -C ../shallow12 log --pretty=tformat:%s HEAD >actual &&\n+\techo three >expected &&\n+\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'fetch exclude tag one' '\n+\tgit -C shallow12 fetch --shallow-exclude one origin &&\n+\tgit -C shallow12 log --pretty=tformat:%s origin/master >actual &&\n+\techo three >expected &&\n+\techo two  >>expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\ndiff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh\nindex 6d77ca7..f71573d 100755\n--- a/t/t5539-fetch-http-shallow.sh\n+++ b/t/t5539-fetch-http-shallow.sh\n@@ -98,6 +98,28 @@ test_expect_success 'fetch shallow since ...' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'shallow clone exclude tag two' '\n+\ttest_create_repo shallow-exclude &&\n+\t(\n+\tcd shallow-exclude &&\n+\ttest_commit one &&\n+\ttest_commit two &&\n+\ttest_commit three &&\n+\tmv .git \"$HTTPD_DOCUMENT_ROOT_PATH/shallow-exclude.git\" &&\n+\tgit clone --shallow-exclude two $HTTPD_URL/smart/shallow-exclude.git ../shallow12 &&\n+\tgit -C ../shallow12 log --pretty=tformat:%s HEAD >actual &&\n+\techo three >expected &&\n+\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'fetch exclude tag one' '\n+\tgit -C shallow12 fetch --shallow-exclude one origin &&\n+\tgit -C shallow12 log --pretty=tformat:%s origin/master >actual &&\n+\techo three >expected &&\n+\techo two  >>expected &&\n+\ttest_cmp expected actual\n+'\n \n stop_httpd\n test_done\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279024","messageId":"1456235103-26317-24-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 23/25] upload-pack: split check_unreachable() in two, prep for get_reachable_list()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:45:01Z","receivedAt":"2016-02-23T13:45:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 41 ++++++++++++++++++++++++++---------------\n 1 file changed, 26 insertions(+), 15 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 95a0bfb..9e4a4fa 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -452,24 +452,24 @@ static int is_our_ref(struct object *o)\n \treturn o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);\n }\n \n-static int check_unreachable(struct object_array *src)\n+static int do_reachable_revlist(struct child_process *cmd,\n+\t\t\t\tstruct object_array *src)\n {\n \tstatic const char *argv[] = {\n \t\t\"rev-list\", \"--stdin\", NULL,\n \t};\n-\tstatic struct child_process cmd = CHILD_PROCESS_INIT;\n \tstruct object *o;\n \tchar namebuf[42]; /* ^ + SHA-1 + LF */\n \tint i;\n \n-\tcmd.argv = argv;\n-\tcmd.git_cmd = 1;\n-\tcmd.no_stderr = 1;\n-\tcmd.in = -1;\n-\tcmd.out = -1;\n+\tcmd->argv = argv;\n+\tcmd->git_cmd = 1;\n+\tcmd->no_stderr = 1;\n+\tcmd->in = -1;\n+\tcmd->out = -1;\n \n-\tif (start_command(&cmd))\n-\t\treturn 0;\n+\tif (start_command(cmd))\n+\t\treturn -1;\n \n \t/*\n \t * If rev-list --stdin encounters an unknown commit, it\n@@ -487,8 +487,8 @@ static int check_unreachable(struct object_array *src)\n \t\tif (!is_our_ref(o))\n \t\t\tcontinue;\n \t\tmemcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n-\t\tif (write_in_full(cmd.in, namebuf, 42) < 0)\n-\t\t\treturn 0;\n+\t\tif (write_in_full(cmd->in, namebuf, 42) < 0)\n+\t\t\treturn -1;\n \t}\n \tnamebuf[40] = '\\n';\n \tfor (i = 0; i < src->nr; i++) {\n@@ -496,18 +496,29 @@ static int check_unreachable(struct object_array *src)\n \t\tif (is_our_ref(o))\n \t\t\tcontinue;\n \t\tmemcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n-\t\tif (write_in_full(cmd.in, namebuf, 41) < 0)\n-\t\t\treturn 0;\n+\t\tif (write_in_full(cmd->in, namebuf, 41) < 0)\n+\t\t\treturn -1;\n \t}\n-\tclose(cmd.in);\n+\tclose(cmd->in);\n \n \tsigchain_pop(SIGPIPE);\n+\treturn 0;\n+}\n+\n+static int check_unreachable(struct object_array *src)\n+{\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\tint i, ret = do_reachable_revlist(&cmd, src);\n+\tchar buf[1];\n+\n+\tif (ret < 0)\n+\t\treturn 0;\n \n \t/*\n \t * The commits out of the rev-list are not ancestors of\n \t * our ref.\n \t */\n-\ti = read_in_full(cmd.out, namebuf, 1);\n+\ti = read_in_full(cmd.out, buf, 1);\n \tif (i)\n \t\treturn 0;\n \tclose(cmd.out);\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279026","messageId":"1456235103-26317-25-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 24/25] upload-pack: add get_reachable_list()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:45:02Z","receivedAt":"2016-02-23T13:45:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n object.h      |  2 +-\n upload-pack.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++---\n 2 files changed, 50 insertions(+), 4 deletions(-)\n\ndiff --git a/object.h b/object.h\nindex f8b6442..614a006 100644\n--- a/object.h\n+++ b/object.h\n@@ -31,7 +31,7 @@ struct object_array {\n  * revision.h:      0---------10                                26\n  * fetch-pack.c:    0---4\n  * walker.c:        0-2\n- * upload-pack.c:               11----------------19\n+ * upload-pack.c:       4       11----------------19\n  * builtin/blame.c:               12-13\n  * bisect.c:                               16\n  * bundle.c:                               16\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 9e4a4fa..9bd590c 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -453,7 +453,8 @@ static int is_our_ref(struct object *o)\n }\n \n static int do_reachable_revlist(struct child_process *cmd,\n-\t\t\t\tstruct object_array *src)\n+\t\t\t\tstruct object_array *src,\n+\t\t\t\tstruct object_array *reachable)\n {\n \tstatic const char *argv[] = {\n \t\t\"rev-list\", \"--stdin\", NULL,\n@@ -484,6 +485,8 @@ static int do_reachable_revlist(struct child_process *cmd,\n \t\to = get_indexed_object(--i);\n \t\tif (!o)\n \t\t\tcontinue;\n+\t\tif (reachable && o->type == OBJ_COMMIT)\n+\t\t\to->flags &= ~TMP_MARK;\n \t\tif (!is_our_ref(o))\n \t\t\tcontinue;\n \t\tmemcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n@@ -493,8 +496,13 @@ static int do_reachable_revlist(struct child_process *cmd,\n \tnamebuf[40] = '\\n';\n \tfor (i = 0; i < src->nr; i++) {\n \t\to = src->objects[i].item;\n-\t\tif (is_our_ref(o))\n+\t\tif (is_our_ref(o)) {\n+\t\t\tif (reachable)\n+\t\t\t\tadd_object_array(o, NULL, reachable);\n \t\t\tcontinue;\n+\t\t}\n+\t\tif (reachable && o->type == OBJ_COMMIT)\n+\t\t\to->flags |= TMP_MARK;\n \t\tmemcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n \t\tif (write_in_full(cmd->in, namebuf, 41) < 0)\n \t\t\treturn -1;\n@@ -505,10 +513,48 @@ static int do_reachable_revlist(struct child_process *cmd,\n \treturn 0;\n }\n \n+static int get_reachable_list(struct object_array *src,\n+\t\t\t      struct object_array *reachable)\n+{\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\tint i, ret = do_reachable_revlist(&cmd, src, reachable);\n+\tstruct object *o;\n+\tchar namebuf[42]; /* ^ + SHA-1 + LF */\n+\n+\tif (ret < 0)\n+\t\treturn -1;\n+\n+\twhile ((i = read_in_full(cmd.out, namebuf, 41)) == 41) {\n+\t\tstruct object_id sha1;\n+\n+\t\tif (namebuf[40] != '\\n' || get_oid_hex(namebuf, &sha1))\n+\t\t\tbreak;\n+\n+\t\to = lookup_object(sha1.hash);\n+\t\tif (o && o->type == OBJ_COMMIT) {\n+\t\t\to->flags &= ~TMP_MARK;\n+\t\t}\n+\t}\n+\tfor (i = get_max_object_index(); 0 < i; i--) {\n+\t\to = get_indexed_object(i - 1);\n+\t\tif (o && o->type == OBJ_COMMIT &&\n+\t\t    (o->flags & TMP_MARK)) {\n+\t\t\tadd_object_array(o, NULL, reachable);\n+\t\t\t\to->flags &= ~TMP_MARK;\n+\t\t}\n+\t}\n+\tclose(cmd.out);\n+\n+\tif (finish_command(&cmd))\n+\t\treturn -1;\n+\n+\treturn 0;\n+}\n+\n static int check_unreachable(struct object_array *src)\n {\n \tstruct child_process cmd = CHILD_PROCESS_INIT;\n-\tint i, ret = do_reachable_revlist(&cmd, src);\n+\tint i, ret = do_reachable_revlist(&cmd, src, NULL);\n \tchar buf[1];\n \n \tif (ret < 0)\n-- \n2.7.1.532.gd9e3aaa\n"},{"id":"279027","messageId":"1456235103-26317-26-git-send-email-pclouds@gmail.com","threadId":"41483","inReplyTo":"1456235103-26317-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v3 25/25] fetch, upload-pack: --deepen=N extends shallow boundary by N commits","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-23T13:45:03Z","receivedAt":"2016-02-23T13:45:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In git-fetch, --depth argument is always relative with the latest\nremote refs. This makes it a bit difficult to cover this use case,\nwhere the user wants to make the shallow history, say 3 levels\ndeeper. It would work if remote refs have not moved yet, but nobody\ncan guarantee that, especially when that use case is performed a\ncouple months after the last clone or \"git fetch --depth\". Also,\nmodifying shallow boundary using --depth does not work well with\nclones created by --since or --not.\n\nThis patch fixes that. A new argument --deepen=<N> will add <N> more (*)\nparent commits to the current history regardless of where remote refs\nare.\n\nHave/Want negotiation is still respected. So if remote refs move, the\nserver will send two chunks: one between \"have\" and \"want\" and another\nto extend shallow history. In theory, the client could send no \"want\"s\nin order to get the second chunk only. But the protocol does not allow\nthat. Either you send no want lines, which means ls-remote; or you\nhave to send at least one want line that carries deep-relative to the\nserver..\n\nThe main work was done by Dongcan Jiang. I fixed it up here and there.\nAnd of course all the bugs belong to me.\n\n(*) We could even support --deepen=<N> where <N> is negative. In that\ncase we can cut some history from the shallow clone. This operation\n(and --depth=<shorter depth>) does not require interaction with remote\nside (and more complicated to implement as a result).\n\nHelped-by: Duy Nguyen <pclouds@gmail.com>\nHelped-by: Eric Sunshine <sunshine@sunshineco.com>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Dongcan Jiang <dongcan.jiang@gmail.com>\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/fetch-options.txt                   |  5 +++++\n Documentation/git-fetch-pack.txt                  |  5 +++++\n Documentation/gitremote-helpers.txt               |  4 ++++\n Documentation/technical/protocol-capabilities.txt |  7 ++++++\n builtin/fetch-pack.c                              |  4 ++++\n builtin/fetch.c                                   | 14 +++++++++++-\n fetch-pack.c                                      |  3 +++\n fetch-pack.h                                      |  1 +\n remote-curl.c                                     | 14 +++++++++++-\n t/t5500-fetch-pack.sh                             | 23 ++++++++++++++++++++\n t/t5539-fetch-http-shallow.sh                     | 26 +++++++++++++++++++++++\n transport-helper.c                                |  1 +\n transport.c                                       |  4 ++++\n transport.h                                       |  4 ++++\n upload-pack.c                                     | 21 +++++++++++++-----\n 15 files changed, 129 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 7aa1285..3b91f15 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -14,6 +14,11 @@\n \tlinkgit:git-clone[1]), deepen or shorten the history to the specified\n \tnumber of commits. Tags for the deepened commits are not fetched.\n \n+--deepen=<depth>::\n+\tSimilar to --depth, except it specifies the number of commits\n+\tfrom the current shallow boundary instead of from the tip of\n+\teach remote branch history.\n+\n --shallow-since=<date>::\n \tDeepen or shorten the history of a shallow repository to\n \tinclude all reachable commits after <date>.\ndiff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt\nindex 4d15b04..c20958f 100644\n--- a/Documentation/git-fetch-pack.txt\n+++ b/Documentation/git-fetch-pack.txt\n@@ -96,6 +96,11 @@ be in a separate packet, and the list must end with a flush packet.\n \texclude commits reachable from a specified remote branch or tag.\n \tThis option can be specified multiple times.\n \n+--deepen-relative::\n+\tArgument --depth specifies the number of commits from the\n+\tcurrent shallow boundary instead of from the tip of each\n+\tremote branch history.\n+\n --no-progress::\n \tDo not show the progress.\n \ndiff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt\nindex 75bb638..6fca268 100644\n--- a/Documentation/gitremote-helpers.txt\n+++ b/Documentation/gitremote-helpers.txt\n@@ -422,6 +422,10 @@ set by Git if the remote helper has the 'option' capability.\n \tDeepens the history of a shallow repository excluding ref.\n \tMultiple options add up.\n \n+'option deepen-relative {'true'|'false'}::\n+\tDeepens the history of a shallow repository relative to\n+\tcurrent boundary. Only valid when used with \"option depth\".\n+\n 'option followtags' {'true'|'false'}::\n \tIf enabled the helper should automatically fetch annotated\n \ttag objects if the object the tag points at was transferred\ndiff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt\nindex 0e6b57d..4fd6dcc 100644\n--- a/Documentation/technical/protocol-capabilities.txt\n+++ b/Documentation/technical/protocol-capabilities.txt\n@@ -197,6 +197,13 @@ specific revision, instead of depth. Internally it's equivalent of\n doing \"rev-list --not <rev>\" on the server side. \"deepen-not\"\n cannot be used with \"deepen\", but can be used with \"deepen-since\".\n \n+deepen-relative\n+---------------\n+\n+If this capability is requested by the client, the semantics of\n+\"deepen\" command is changed. The \"depth\" argument is the depth from\n+the current shallow boundary, instead of the depth from remote refs.\n+\n no-progress\n -----------\n \ndiff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c\nindex 07570be..8265348 100644\n--- a/builtin/fetch-pack.c\n+++ b/builtin/fetch-pack.c\n@@ -113,6 +113,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \t\t\tstring_list_append(&deepen_not, arg);\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--deepen-relative\")) {\n+\t\t\targs.deepen_relative = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(\"--no-progress\", arg)) {\n \t\t\targs.no_progress = 1;\n \t\t\tcontinue;\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 8d19bf6..808c11b 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -34,7 +34,7 @@ static int fetch_prune_config = -1; /* unspecified */\n static int prune = -1; /* unspecified */\n #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */\n \n-static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;\n+static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;\n static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n static int max_children = 1;\n@@ -129,6 +129,8 @@ static struct option builtin_fetch_options[] = {\n \t{ OPTION_CALLBACK, 0, \"shallow-exclude\", NULL, N_(\"revision\"),\n \t\t    N_(\"deepen history of shallow clone by excluding rev\"),\n \t\t    PARSE_OPT_NONEG, option_parse_deepen_not },\n+\tOPT_INTEGER(0, \"deepen\", &deepen_relative,\n+\t\t    N_(\"deepen history of shallow clone\")),\n \t{ OPTION_SET_INT, 0, \"unshallow\", &unshallow, NULL,\n \t\t   N_(\"convert to a complete repository\"),\n \t\t   PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },\n@@ -889,6 +891,8 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)\n \tif (deepen && deepen_not.nr)\n \t\tset_option(transport, TRANS_OPT_DEEPEN_NOT,\n \t\t\t   (const char *)&deepen_not);\n+\tif (deepen_relative)\n+\t\tset_option(transport, TRANS_OPT_DEEPEN_RELATIVE, \"yes\");\n \tif (update_shallow)\n \t\tset_option(transport, TRANS_OPT_UPDATE_SHALLOW, \"yes\");\n \treturn transport;\n@@ -914,6 +918,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)\n \n \ttransport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);\n \ttransport_set_option(transport, TRANS_OPT_DEPTH, \"0\");\n+\ttransport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);\n \tfetch_refs(transport, ref_map);\n \n \tif (gsecondary) {\n@@ -1185,6 +1190,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix,\n \t\t\t     builtin_fetch_options, builtin_fetch_usage, 0);\n \n+\tif (deepen_relative) {\n+\t\tif (deepen_relative < 0)\n+\t\t\tdie(_(\"Negative depth in --deepen is not supported\"));\n+\t\tif (depth)\n+\t\t\tdie(_(\"--deepen and --depth are mutually exclusive\"));\n+\t\tdepth = xstrfmt(\"%d\", deepen_relative);\n+\t}\n \tif (unshallow) {\n \t\tif (depth)\n \t\t\tdie(_(\"--depth and --unshallow cannot be used together\"));\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex a684160..ace9217 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -324,6 +324,7 @@ static int find_common(struct fetch_pack_args *args,\n \t\t\tif (no_done)            strbuf_addstr(&c, \" no-done\");\n \t\t\tif (use_sideband == 2)  strbuf_addstr(&c, \" side-band-64k\");\n \t\t\tif (use_sideband == 1)  strbuf_addstr(&c, \" side-band\");\n+\t\t\tif (args->deepen_relative) strbuf_addstr(&c, \" deepen-relative\");\n \t\t\tif (args->use_thin_pack) strbuf_addstr(&c, \" thin-pack\");\n \t\t\tif (args->no_progress)   strbuf_addstr(&c, \" no-progress\");\n \t\t\tif (args->include_tag)   strbuf_addstr(&c, \" include-tag\");\n@@ -884,6 +885,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \t\tdeepen_not_ok = 1;\n \telse if (args->deepen_not)\n \t\tdie(\"Server does not support --shallow-exclude\");\n+\tif (!server_supports(\"deepen-relative\") && args->deepen_relative)\n+\t\tdie(\"Server does not support --deepen\");\n \n \tif (everything_local(args, &ref, sought, nr_sought)) {\n \t\tpacket_flush(fd[1]);\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex 144301f..c912e3d 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -12,6 +12,7 @@ struct fetch_pack_args {\n \tint depth;\n \tconst char *deepen_since;\n \tconst struct string_list *deepen_not;\n+\tunsigned deepen_relative:1;\n \tunsigned quiet:1;\n \tunsigned keep_pack:1;\n \tunsigned lock_pack:1;\ndiff --git a/remote-curl.c b/remote-curl.c\nindex 71122ac..3f1a8f5 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -30,7 +30,8 @@ struct options {\n \t\tdry_run : 1,\n \t\tthin : 1,\n \t\t/* One of the SEND_PACK_PUSH_CERT_* constants. */\n-\t\tpush_cert : 2;\n+\t\tpush_cert : 2,\n+\t\tdeepen_relative : 1;\n };\n static struct options options;\n static struct string_list cas_options = STRING_LIST_INIT_DUP;\n@@ -70,6 +71,15 @@ static int set_option(const char *name, const char *value)\n \t\tstring_list_append(&options.deepen_not, value);\n \t\treturn 0;\n \t}\n+\telse if (!strcmp(name, \"deepen-relative\")) {\n+\t\tif (!strcmp(value, \"true\"))\n+\t\t\toptions.deepen_relative = 1;\n+\t\telse if (!strcmp(value, \"false\"))\n+\t\t\toptions.deepen_relative = 0;\n+\t\telse\n+\t\t\treturn -1;\n+\t\treturn 0;\n+\t}\n \telse if (!strcmp(name, \"followtags\")) {\n \t\tif (!strcmp(value, \"true\"))\n \t\t\toptions.followtags = 1;\n@@ -764,6 +774,8 @@ static int fetch_git(struct discovery *heads,\n \tfor (i = 0; i < options.deepen_not.nr; i++)\n \t\targv_array_pushf(&args, \"--shallow-exclude=%s\",\n \t\t\t\t options.deepen_not.items[i].string);\n+\tif (options.deepen_relative && options.depth)\n+\t\targv_array_push(&args, \"--deepen-relative\");\n \targv_array_push(&args, url.buf);\n \n \tfor (i = 0; i < nr_heads; i++) {\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex a3fe5ca..f512098 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -683,4 +683,27 @@ test_expect_success 'fetch exclude tag one' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'fetching deepen' '\n+\ttest_create_repo shallow-deepen &&\n+\t(\n+\tcd shallow-deepen &&\n+\ttest_commit one &&\n+\ttest_commit two &&\n+\ttest_commit three &&\n+\tgit clone --depth 1 \"file://$(pwd)/.\" deepen &&\n+\ttest_commit four &&\n+\tgit -C deepen log --pretty=tformat:%s master >actual &&\n+\techo three >expected &&\n+\ttest_cmp expected actual &&\n+\tgit -C deepen fetch --deepen=1 &&\n+\tgit -C deepen log --pretty=tformat:%s origin/master >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tfour\n+\tthree\n+\ttwo\n+\tEOF\n+\ttest_cmp expected actual\n+\t)\n+'\n+\n test_done\ndiff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh\nindex f71573d..25f8968 100755\n--- a/t/t5539-fetch-http-shallow.sh\n+++ b/t/t5539-fetch-http-shallow.sh\n@@ -121,5 +121,31 @@ test_expect_success 'fetch exclude tag one' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'fetching deepen' '\n+\ttest_create_repo shallow-deepen &&\n+\t(\n+\tcd shallow-deepen &&\n+\ttest_commit one &&\n+\ttest_commit two &&\n+\ttest_commit three &&\n+\tmv .git \"$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git\" &&\n+\tgit clone --depth 1 $HTTPD_URL/smart/shallow-deepen.git deepen &&\n+\tmv \"$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git\" .git &&\n+\ttest_commit four &&\n+\tgit -C deepen log --pretty=tformat:%s master >actual &&\n+\techo three >expected &&\n+\ttest_cmp expected actual &&\n+\tmv .git \"$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git\" &&\n+\tgit -C deepen fetch --deepen=1 &&\n+\tgit -C deepen log --pretty=tformat:%s origin/master >actual &&\n+\tcat >expected <<-\\EOF &&\n+\tfour\n+\tthree\n+\ttwo\n+\tEOF\n+\ttest_cmp expected actual\n+\t)\n+'\n+\n stop_httpd\n test_done\ndiff --git a/transport-helper.c b/transport-helper.c\nindex d644568..b894b60 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -258,6 +258,7 @@ static const char *boolean_options[] = {\n \tTRANS_OPT_THIN,\n \tTRANS_OPT_KEEP,\n \tTRANS_OPT_FOLLOWTAGS,\n+\tTRANS_OPT_DEEPEN_RELATIVE\n \t};\n \n static int strbuf_set_helper_option(struct helper_data *data,\ndiff --git a/transport.c b/transport.c\nindex 3e6f3aa..3e76a9a 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -157,6 +157,9 @@ static int set_git_option(struct git_transport_options *opts,\n \t} else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) {\n \t\topts->deepen_not = (const struct string_list *)value;\n \t\treturn 0;\n+\t} else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {\n+\t\topts->deepen_relative = !!value;\n+\t\treturn 0;\n \t}\n \treturn 1;\n }\n@@ -213,6 +216,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \targs.depth = data->options.depth;\n \targs.deepen_since = data->options.deepen_since;\n \targs.deepen_not = data->options.deepen_not;\n+\targs.deepen_relative = data->options.deepen_relative;\n \targs.check_self_contained_and_connected =\n \t\tdata->options.check_self_contained_and_connected;\n \targs.cloning = transport->cloning;\ndiff --git a/transport.h b/transport.h\nindex ab61932..bdc3518 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -14,6 +14,7 @@ struct git_transport_options {\n \tunsigned check_self_contained_and_connected : 1;\n \tunsigned self_contained_and_connected : 1;\n \tunsigned update_shallow : 1;\n+\tunsigned deepen_relative : 1;\n \tint depth;\n \tconst char *deepen_since;\n \tconst struct string_list *deepen_not;\n@@ -181,6 +182,9 @@ int transport_restrict_protocols(void);\n /* Limit the depth of the fetch based on revs if not null */\n #define TRANS_OPT_DEEPEN_NOT \"deepen-not\"\n \n+/* Limit the deepen of the fetch if not null */\n+#define TRANS_OPT_DEEPEN_RELATIVE \"deepen-relative\"\n+\n /* Aggressively fetch annotated tags if possible */\n #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n \ndiff --git a/upload-pack.c b/upload-pack.c\nindex 9bd590c..18b914a 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -32,6 +32,7 @@ static const char upload_pack_usage[] = \"git upload-pack [--strict] [--timeout=<\n \n static unsigned long oldest_have;\n \n+static int deepen_relative;\n static int multi_ack;\n static int no_done;\n static int use_thin_pack, use_ofs_delta, use_include_tag;\n@@ -652,7 +653,8 @@ static void send_unshallow(const struct object_array *shallows)\n \t}\n }\n \n-static void deepen(int depth, const struct object_array *shallows)\n+static void deepen(int depth, int deepen_relative,\n+\t\t   struct object_array *shallows)\n {\n \tstruct commit_list *result = NULL;\n \tint i;\n@@ -661,7 +663,14 @@ static void deepen(int depth, const struct object_array *shallows)\n \t\t\tstruct object *object = shallows->objects[i].item;\n \t\t\tobject->flags |= NOT_SHALLOW;\n \t\t}\n-\telse\n+\telse if (deepen_relative) {\n+\t\tstruct object_array reachable_shallows = OBJECT_ARRAY_INIT;\n+\t\tget_reachable_list(shallows, &reachable_shallows);\n+\t\tresult = get_shallow_commits(&reachable_shallows,\n+\t\t\t\t\t     depth + 1,\n+\t\t\t\t\t     SHALLOW, NOT_SHALLOW);\n+\t\tobject_array_clear(&reachable_shallows);\n+\t} else\n \t\tresult = get_shallow_commits(&want_obj, depth,\n \t\t\t\t\t     SHALLOW, NOT_SHALLOW);\n \tsend_shallow(result);\n@@ -753,6 +762,8 @@ static void receive_needs(void)\n \n \t\tfeatures = arg + 40;\n \n+\t\tif (parse_feature_request(features, \"deepen-relative\"))\n+\t\t\tdeepen_relative = 1;\n \t\tif (parse_feature_request(features, \"multi_ack_detailed\"))\n \t\t\tmulti_ack = 2;\n \t\telse if (parse_feature_request(features, \"multi_ack\"))\n@@ -802,7 +813,7 @@ static void receive_needs(void)\n \tif (depth > 0 && deepen_rev_list)\n \t\tdie(\"git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together\");\n \tif (depth > 0)\n-\t\tdeepen(depth, &shallows);\n+\t\tdeepen(depth, deepen_relative, &shallows);\n \telse if (deepen_rev_list) {\n \t\tstruct argv_array av = ARGV_ARRAY_INIT;\n \t\tint i;\n@@ -873,8 +884,8 @@ static int send_ref(const char *refname, const struct object_id *oid,\n \t\t    int flag, void *cb_data)\n {\n \tstatic const char *capabilities = \"multi_ack thin-pack side-band\"\n-\t\t\" side-band-64k ofs-delta shallow deepen-since deepen-not no-progress\"\n-\t\t\" include-tag multi_ack_detailed\";\n+\t\t\" side-band-64k ofs-delta shallow deepen-since deepen-not\"\n+\t\t\" deepen-relative no-progress include-tag multi_ack_detailed\";\n \tconst char *refname_nons = strip_namespace(refname);\n \tstruct object_id peeled;\n \n-- \n2.7.1.532.gd9e3aaa\n"}]}