{"thread":{"id":"42016","subject":"[PATCH 00/26] nd/shallow-deepen updates","startedAt":"2016-04-13T12:54:44Z","lastAt":"2016-06-16T02:19:50Z","messageCount":40,"participants":["Nguyễn Thái Ngọc Duy","Junio C Hamano","Eric Sunshine","Duy Nguyen"],"isPatch":true,"patchVersion":1,"patchTotal":26},"messages":[{"id":"283317","messageId":"1460552110-5554-1-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":null,"subject":"[PATCH 00/26] nd/shallow-deepen updates","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:44Z","receivedAt":"2016-04-13T12:54:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Compared to 'pu'\n\n14/26 has a fix from Jeff, about incorrectly sharing variable for\niteration. Interdiff\n\n--- a/shallow.c\n+++ b/shallow.c\n@@ -188,13 +188,14 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n \t */\n \tfor (p = not_shallow_list; p; p = p->next) {\n \t\tstruct commit *c = p->item;\n+\t\tstruct commit_list *parent;\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\tfor (parent = c->parents; parent; parent = parent->next)\n+\t\t\tif (!(parent->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\n21/26 adds \"static\" (from Ramsay Jones)\n\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -41,7 +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 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\nAnd 12/26 is a new one.\n\n  [01/26] remote-curl.c: convert fetch_git() to use argv_array\n  [02/26] transport-helper.c: refactor set_helper_option()\n  [03/26] upload-pack: move shallow deepen code out of receive_needs()\n  [04/26] upload-pack: move \"shallow\" sending code out of deepen()\n  [05/26] upload-pack: remove unused variable \"backup\"\n  [06/26] upload-pack: move \"unshallow\" sending code out of deepen()\n  [07/26] upload-pack: use skip_prefix() instead of starts_with()\n  [08/26] upload-pack: tighten number parsing at \"deepen\" lines\n  [09/26] upload-pack: move rev-list code out of check_non_tip()\n  [10/26] fetch-pack: use skip_prefix() instead of starts_with()\n  [11/26] fetch-pack: use a common function for verbose printing\n  [12/26] fetch-pack.c: mark strings for translating\n  [13/26] fetch-pack: use a separate flag for fetch in deepening mode\n  [14/26] shallow.c: implement a generic shallow boundary finder based on rev-list\n  [15/26] upload-pack: add deepen-since to cut shallow repos based on time\n  [16/26] fetch: define shallow boundary with --shallow-since\n  [17/26] clone: define shallow clone boundary based on time with --shallow-since\n  [18/26] t5500, t5539: tests for shallow depth since a specific date\n  [19/26] refs: add expand_ref()\n  [20/26] upload-pack: support define shallow boundary by excluding revisions\n  [21/26] fetch: define shallow boundary with --shallow-exclude\n  [22/26] clone: define shallow clone boundary with --shallow-exclude\n  [23/26] t5500, t5539: tests for shallow depth excluding a ref\n  [24/26] upload-pack: split check_unreachable() in two, prep for get_reachable_list()\n  [25/26] upload-pack: add get_reachable_list()\n  [26/26] fetch, upload-pack: --deepen=N extends shallow boundary by N commits\n\nTotal 23 files changed, 879 insertions(+), 221 deletions(-)\n"},{"id":"283318","messageId":"1460552110-5554-2-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 01/26] remote-curl.c: convert fetch_git() to use argv_array","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:45Z","receivedAt":"2016-04-13T12:54: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 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.8.0.rc0.210.gd302cd2\n"},{"id":"283319","messageId":"1460552110-5554-3-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 02/26] transport-helper.c: refactor set_helper_option()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:46Z","receivedAt":"2016-04-13T12:54:46Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283320","messageId":"1460552110-5554-4-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 03/26] upload-pack: move shallow deepen code out of receive_needs()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:47Z","receivedAt":"2016-04-13T12:54:47Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283326","messageId":"1460552110-5554-5-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 04/26] upload-pack: move \"shallow\" sending code out of deepen()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:48Z","receivedAt":"2016-04-13T12:54: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 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.8.0.rc0.210.gd302cd2\n"},{"id":"283321","messageId":"1460552110-5554-6-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 05/26] upload-pack: remove unused variable \"backup\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:49Z","receivedAt":"2016-04-13T12:54:49Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283322","messageId":"1460552110-5554-7-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 06/26] upload-pack: move \"unshallow\" sending code out of deepen()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:50Z","receivedAt":"2016-04-13T12:54:50Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283323","messageId":"1460552110-5554-8-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 07/26] upload-pack: use skip_prefix() instead of starts_with()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:51Z","receivedAt":"2016-04-13T12:54:51Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283324","messageId":"1460552110-5554-9-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 08/26] upload-pack: tighten number parsing at \"deepen\" lines","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:52Z","receivedAt":"2016-04-13T12:54:52Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283328","messageId":"1460552110-5554-10-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 09/26] 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-04-13T12:54:53Z","receivedAt":"2016-04-13T12:54: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 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.8.0.rc0.210.gd302cd2\n"},{"id":"283325","messageId":"1460552110-5554-11-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 10/26] fetch-pack: use skip_prefix() instead of starts_with()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:54Z","receivedAt":"2016-04-13T12:54: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 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.8.0.rc0.210.gd302cd2\n"},{"id":"283327","messageId":"1460552110-5554-12-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 11/26] fetch-pack: use a common function for verbose printing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:55Z","receivedAt":"2016-04-13T12:54:55Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283329","messageId":"1460552110-5554-13-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 12/26] fetch-pack.c: mark strings for translating","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:54:56Z","receivedAt":"2016-04-13T12:54:56Z","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 fetch-pack.c | 75 ++++++++++++++++++++++++++++++------------------------------\n 1 file changed, 37 insertions(+), 38 deletions(-)\n\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 4020744..08caf1d 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -208,7 +208,7 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)\n \t\t\t\tcontinue;\n \t\t\tif (starts_with(line, \"unshallow \"))\n \t\t\t\tcontinue;\n-\t\t\tdie(\"git fetch-pack: expected shallow list\");\n+\t\t\tdie(_(\"git fetch-pack: expected shallow list\"));\n \t\t}\n \t}\n }\n@@ -220,7 +220,7 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)\n \tconst char *arg;\n \n \tif (!len)\n-\t\tdie(\"git fetch-pack: expected ACK/NAK, got EOF\");\n+\t\tdie(_(\"git fetch-pack: expected ACK/NAK, got EOF\"));\n \tif (!strcmp(line, \"NAK\"))\n \t\treturn NAK;\n \tif (skip_prefix(line, \"ACK \", &arg)) {\n@@ -238,7 +238,7 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)\n \t\t\treturn ACK;\n \t\t}\n \t}\n-\tdie(\"git fetch_pack: expected ACK/NAK, got '%s'\", line);\n+\tdie(_(\"git fetch_pack: expected ACK/NAK, got '%s'\"), line);\n }\n \n static void send_request(struct fetch_pack_args *args,\n@@ -285,7 +285,7 @@ static int find_common(struct fetch_pack_args *args,\n \tsize_t state_len = 0;\n \n \tif (args->stateless_rpc && multi_ack == 1)\n-\t\tdie(\"--stateless-rpc requires multi_ack_detailed\");\n+\t\tdie(_(\"--stateless-rpc requires multi_ack_detailed\"));\n \tif (marked)\n \t\tfor_each_ref(clear_marks, NULL);\n \tmarked = 1;\n@@ -357,23 +357,23 @@ static int find_common(struct fetch_pack_args *args,\n \t\twhile ((line = packet_read_line(fd[0], NULL))) {\n \t\t\tif (skip_prefix(line, \"shallow \", &arg)) {\n \t\t\t\tif (get_sha1_hex(arg, sha1))\n-\t\t\t\t\tdie(\"invalid shallow line: %s\", line);\n+\t\t\t\t\tdie(_(\"invalid shallow line: %s\"), line);\n \t\t\t\tregister_shallow(sha1);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (skip_prefix(line, \"unshallow \", &arg)) {\n \t\t\t\tif (get_sha1_hex(arg, sha1))\n-\t\t\t\t\tdie(\"invalid unshallow line: %s\", line);\n+\t\t\t\t\tdie(_(\"invalid unshallow line: %s\"), line);\n \t\t\t\tif (!lookup_object(sha1))\n-\t\t\t\t\tdie(\"object not found: %s\", line);\n+\t\t\t\t\tdie(_(\"object not found: %s\"), line);\n \t\t\t\t/* make sure that it is parsed as shallow */\n \t\t\t\tif (!parse_object(sha1))\n-\t\t\t\t\tdie(\"error in object: %s\", line);\n+\t\t\t\t\tdie(_(\"error in object: %s\"), line);\n \t\t\t\tif (unregister_shallow(sha1))\n-\t\t\t\t\tdie(\"no shallow found: %s\", line);\n+\t\t\t\t\tdie(_(\"no shallow found: %s\"), line);\n \t\t\t\tcontinue;\n \t\t\t}\n-\t\t\tdie(\"expected shallow/unshallow, got %s\", line);\n+\t\t\tdie(_(\"expected shallow/unshallow, got %s\"), line);\n \t\t}\n \t} else if (!args->stateless_rpc)\n \t\tsend_request(args, fd[1], &req_buf);\n@@ -412,8 +412,8 @@ static int find_common(struct fetch_pack_args *args,\n \t\t\tdo {\n \t\t\t\tack = get_ack(fd[0], 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\t\tprint_verbose(args, _(\"got %s %d %s\"), \"ack\",\n+\t\t\t\t\t\t      ack, 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@@ -426,7 +426,7 @@ static int find_common(struct fetch_pack_args *args,\n \t\t\t\t\tstruct commit *commit =\n \t\t\t\t\t\tlookup_commit(result_sha1);\n \t\t\t\t\tif (!commit)\n-\t\t\t\t\t\tdie(\"invalid commit %s\", sha1_to_hex(result_sha1));\n+\t\t\t\t\t\tdie(_(\"invalid commit %s\"), sha1_to_hex(result_sha1));\n \t\t\t\t\tif (args->stateless_rpc\n \t\t\t\t\t && ack == ACK_common\n \t\t\t\t\t && !(commit->object.flags & COMMON)) {\n@@ -452,7 +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\tprint_verbose(args, \"giving up\");\n+\t\t\t\tprint_verbose(args, _(\"giving up\"));\n \t\t\t\tbreak; /* give up */\n \t\t\t}\n \t\t}\n@@ -462,7 +462,7 @@ done:\n \t\tpacket_buf_write(&req_buf, \"done\\n\");\n \t\tsend_request(args, fd[1], &req_buf);\n \t}\n-\tprint_verbose(args, \"done\");\n+\tprint_verbose(args, _(\"done\"));\n \tif (retval != 0) {\n \t\tmulti_ack = 0;\n \t\tflushes++;\n@@ -474,8 +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\tprint_verbose(args, \"got ack (%d) %s\", ack,\n-\t\t\t\t      sha1_to_hex(result_sha1));\n+\t\t\tprint_verbose(args, _(\"got %s (%d) %s\"), \"ack\",\n+\t\t\t\t      ack, sha1_to_hex(result_sha1));\n \t\t\tif (ack == ACK)\n \t\t\t\treturn 0;\n \t\t\tmulti_ack = 1;\n@@ -520,7 +520,7 @@ 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\tprint_verbose(args, \"Marking %s as complete\",\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@@ -666,7 +666,7 @@ static int everything_local(struct fetch_pack_args *args,\n \t\t\t\t      ref->name);\n \t\t\tcontinue;\n \t\t}\n-\t\tprint_verbose(args, \"already have %s (%s)\", sha1_to_hex(remote),\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@@ -702,8 +702,7 @@ static int get_pack(struct fetch_pack_args *args,\n \t\tdemux.data = xd;\n \t\tdemux.out = -1;\n \t\tif (start_async(&demux))\n-\t\t\tdie(\"fetch-pack: unable to fork off sideband\"\n-\t\t\t    \" demultiplexer\");\n+\t\t\tdie(_(\"fetch-pack: unable to fork off sideband demultiplexer\"));\n \t}\n \telse\n \t\tdemux.out = xd[0];\n@@ -711,7 +710,7 @@ static int get_pack(struct fetch_pack_args *args,\n \tif (!args->keep_pack && unpack_limit) {\n \n \t\tif (read_pack_header(demux.out, &header))\n-\t\t\tdie(\"protocol error: bad pack header\");\n+\t\t\tdie(_(\"protocol error: bad pack header\"));\n \t\tpass_header = 1;\n \t\tif (ntohl(header.hdr_entries) < unpack_limit)\n \t\t\tdo_keep = 0;\n@@ -767,7 +766,7 @@ static int get_pack(struct fetch_pack_args *args,\n \tcmd.in = demux.out;\n \tcmd.git_cmd = 1;\n \tif (start_command(&cmd))\n-\t\tdie(\"fetch-pack: unable to fork off %s\", cmd_name);\n+\t\tdie(_(\"fetch-pack: unable to fork off %s\"), cmd_name);\n \tif (do_keep && pack_lockfile) {\n \t\t*pack_lockfile = index_pack_lockfile(cmd.out);\n \t\tclose(cmd.out);\n@@ -783,9 +782,9 @@ static int get_pack(struct fetch_pack_args *args,\n \t\t\targs->check_self_contained_and_connected &&\n \t\t\tret == 0;\n \telse\n-\t\tdie(\"%s failed\", cmd_name);\n+\t\tdie(_(\"%s failed\"), cmd_name);\n \tif (use_sideband && finish_async(&demux))\n-\t\tdie(\"error in sideband demultiplexer\");\n+\t\tdie(_(\"error in sideband demultiplexer\"));\n \treturn 0;\n }\n \n@@ -812,34 +811,34 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \tqsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name);\n \n \tif ((args->depth > 0 || is_repository_shallow()) && !server_supports(\"shallow\"))\n-\t\tdie(\"Server does not support shallow clients\");\n+\t\tdie(_(\"Server does not support shallow clients\"));\n \tif (server_supports(\"multi_ack_detailed\")) {\n-\t\tprint_verbose(args, \"Server supports multi_ack_detailed\");\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\tprint_verbose(args, \"Server supports no-done\");\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\tprint_verbose(args, \"Server supports multi_ack\");\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\tprint_verbose(args, \"Server supports side-band-64k\");\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\tprint_verbose(args, \"Server supports side-band\");\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\tprint_verbose(args, \"Server supports allow-tip-sha1-in-want\");\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\tprint_verbose(args, \"Server supports allow-reachable-sha1-in-want\");\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@@ -849,14 +848,14 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \tif (!server_supports(\"include-tag\"))\n \t\targs->include_tag = 0;\n \tif (server_supports(\"ofs-delta\"))\n-\t\tprint_verbose(args, \"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 (agent_len)\n-\t\t\tprint_verbose(args, \"Server version is %.*s\",\n+\t\t\tprint_verbose(args, _(\"Server version is %.*s\"),\n \t\t\t\t      agent_len, agent_feature);\n \t}\n \n@@ -869,7 +868,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \t\t\t/* When cloning, it is not unusual to have\n \t\t\t * no common commit.\n \t\t\t */\n-\t\t\twarning(\"no common commits\");\n+\t\t\twarning(_(\"no common commits\"));\n \n \tif (args->stateless_rpc)\n \t\tpacket_flush(fd[1]);\n@@ -881,7 +880,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \telse\n \t\talternate_shallow_file = NULL;\n \tif (get_pack(args, fd, pack_lockfile))\n-\t\tdie(\"git fetch-pack: fetch failed.\");\n+\t\tdie(_(\"git fetch-pack: fetch failed.\"));\n \n  all_done:\n \treturn ref;\n@@ -1043,7 +1042,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,\n \n \tif (!ref) {\n \t\tpacket_flush(fd[1]);\n-\t\tdie(\"no matching remote head\");\n+\t\tdie(_(\"no matching remote head\"));\n \t}\n \tprepare_shallow_info(&si, shallow);\n \tref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,\n-- \n2.8.0.rc0.210.gd302cd2\n"},{"id":"283330","messageId":"1460552110-5554-14-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 13/26] 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-04-13T12:54:57Z","receivedAt":"2016-04-13T12:54:57Z","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 08caf1d..a14d24a 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@@ -812,6 +812,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@@ -872,7 +874,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@@ -939,7 +941,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.8.0.rc0.210.gd302cd2\n"},{"id":"283331","messageId":"1460552110-5554-15-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 14/26] 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-04-13T12:54:58Z","receivedAt":"2016-04-13T12:54:58Z","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 | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 80 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..40c2485 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,82 @@ 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+\t\tstruct commit_list *parent;\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 (parent = c->parents; parent; parent = parent->next)\n+\t\t\tif (!(parent->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.8.0.rc0.210.gd302cd2\n"},{"id":"283334","messageId":"1460552110-5554-16-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 15/26] 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-04-13T12:54:59Z","receivedAt":"2016-04-13T12:54:59Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283332","messageId":"1460552110-5554-17-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 16/26] fetch: define shallow boundary with --shallow-since","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:55:00Z","receivedAt":"2016-04-13T12:55: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 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 a14d24a..a2f25c1 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@@ -812,7 +818,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@@ -860,6 +866,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.8.0.rc0.210.gd302cd2\n"},{"id":"283333","messageId":"1460552110-5554-18-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 17/26] 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-04-13T12:55:01Z","receivedAt":"2016-04-13T12:55: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 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.8.0.rc0.210.gd302cd2\n"},{"id":"283336","messageId":"1460552110-5554-19-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 18/26] t5500, t5539: tests for shallow depth since a specific date","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:55:02Z","receivedAt":"2016-04-13T12:55: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 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.8.0.rc0.210.gd302cd2\n"},{"id":"283335","messageId":"1460552110-5554-20-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 19/26] refs: add expand_ref()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:55:03Z","receivedAt":"2016-04-13T12:55:03Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283339","messageId":"1460552110-5554-21-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 20/26] upload-pack: support define shallow boundary by excluding revisions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:55:04Z","receivedAt":"2016-04-13T12:55:04Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283337","messageId":"1460552110-5554-22-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 21/26] fetch: define shallow boundary with --shallow-exclude","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:55:05Z","receivedAt":"2016-04-13T12:55:05Z","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..154b9ad 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+static 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 a2f25c1..ad7d00f 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@@ -818,7 +827,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@@ -870,6 +879,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.8.0.rc0.210.gd302cd2\n"},{"id":"283338","messageId":"1460552110-5554-23-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 22/26] clone: define shallow clone boundary with --shallow-exclude","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:55:06Z","receivedAt":"2016-04-13T12:55:06Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283340","messageId":"1460552110-5554-24-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 23/26] t5500, t5539: tests for shallow depth excluding a ref","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:55:07Z","receivedAt":"2016-04-13T12:55:07Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283341","messageId":"1460552110-5554-25-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 24/26] 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-04-13T12:55:08Z","receivedAt":"2016-04-13T12:55:08Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283342","messageId":"1460552110-5554-26-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 25/26] upload-pack: add get_reachable_list()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-04-13T12:55:09Z","receivedAt":"2016-04-13T12:55:09Z","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.8.0.rc0.210.gd302cd2\n"},{"id":"283343","messageId":"1460552110-5554-27-git-send-email-pclouds@gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 26/26] 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-04-13T12:55:10Z","receivedAt":"2016-04-13T12:55:10Z","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 154b9ad..68b44ba 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 ad7d00f..e2a235f 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@@ -883,6 +884,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.8.0.rc0.210.gd302cd2\n"},{"id":"283454","messageId":"xmqqbn5ci2z5.fsf@gitster.mtv.corp.google.com","threadId":"42016","inReplyTo":"1460552110-5554-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 00/26] nd/shallow-deepen updates","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-14T16:12:30Z","receivedAt":"2016-04-14T16:12:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thanks, picked up and re-queued.\n"},{"id":"287484","messageId":"CAPig+cQjteE3vgT=UoOne1PX7zmE5JhkqpXUbkCwheRvzkUJgw@mail.gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 01/26] remote-curl.c: convert fetch_git() to use argv_array","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-05-25T06:41:45Z","receivedAt":"2016-05-25T06:41:45Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2016 at 8:54 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/remote-curl.c b/remote-curl.c\n> @@ -726,37 +726,30 @@ static int fetch_git(struct discovery *heads,\n>         char *depth_arg = NULL;\n> -       int argc = 0, i, err;\n> -       const char *argv[17];\n> +       int i, err;\n> +       struct argv_array args = ARGV_ARRAY_INIT;\n> [...]\n>         if (options.verbosity >= 3) {\n> -               argv[argc++] = \"-v\";\n> -               argv[argc++] = \"-v\";\n> +               argv_array_push(&args, \"-v\");\n> +               argv_array_push(&args, \"-v\");\n\nA bit more natural might have been:\n\n    argv_array_pushl(&args, \"-v\", \"-v\", NULL);\n\nthough the diff becomes noisier when the braces get dropped. Not worth\na re-roll, of course.\n\n>         }\n> [...]\n> -       if (options.depth) {\n> -               struct strbuf buf = STRBUF_INIT;\n> -               strbuf_addf(&buf, \"--depth=%lu\", options.depth);\n> -               depth_arg = strbuf_detach(&buf, NULL);\n> -               argv[argc++] = depth_arg;\n> -       }\n> -       argv[argc++] = url.buf;\n> -       argv[argc++] = NULL;\n> +               argv_array_push(&args, \"--no-progress\");\n> +       if (options.depth)\n> +               argv_array_pushf(&args, \"--depth=%lu\", options.depth);\n\n'depth_arg' becomes unused with this change and can be retired...\n\n> +       argv_array_push(&args, url.buf);\n> @@ -779,6 +772,7 @@ static int fetch_git(struct discovery *heads,\n>         strbuf_release(&rpc.result);\n>         strbuf_release(&preamble);\n>         free(depth_arg);\n\n...along with this unnecessary free().\n\n> +       argv_array_clear(&args);\n>         return err;\n>  }\n"},{"id":"287485","messageId":"CAPig+cR--ZK1eGfOKfE-PhKha80EqwVi-AH90GxOQt4vka3=tg@mail.gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 02/26] transport-helper.c: refactor set_helper_option()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-05-25T06:47:56Z","receivedAt":"2016-05-25T06:47:56Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2016 at 8:54 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> For now we can handle two types, string and boolean, in\n> set_helper_option(). Later on we'll add string_list support, which does\n> not fit well. The new function strbuf_set_helper_option() can be reused\n> for a separate function that handles string-list.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/transport-helper.c b/transport-helper.c\n> @@ -260,6 +260,28 @@ static const char *boolean_options[] = {\n> +static int strbuf_set_helper_option(struct helper_data *data,\n> +                                   struct strbuf *buf)\n> +{\n> +       int ret;\n> +\n> +       sendline(data, buf);\n> +       if (recvline(data, buf))\n> +               exit(128);\n> +\n> +       if (!strcmp(buf->buf, \"ok\"))\n> +               ret = 0;\n> +       else if (starts_with(buf->buf, \"error\")) {\n> +               ret = -1;\n> +       } else if (!strcmp(buf->buf, \"unsupported\"))\n\nYou could use this opportunity to drop the unnecessary braces. (True,\ndoing so makes it something other than pure code movement, but it's\nminor and probably a worthy cleanup.)\n\n> +               ret = 1;\n> +       else {\n> +               warning(\"%s unexpectedly said: '%s'\", data->name, buf->buf);\n> +               ret = 1;\n> +       }\n> +       return ret;\n> +}\n> +\n>  static int set_helper_option(struct transport *transport,\n>                           const char *name, const char *value)\n>  {\n> @@ -291,20 +313,7 @@ static int set_helper_option(struct transport *transport,\n>                 quote_c_style(value, &buf, NULL, 0);\n>         strbuf_addch(&buf, '\\n');\n>\n> -       sendline(data, &buf);\n> -       if (recvline(data, &buf))\n> -               exit(128);\n> -\n> -       if (!strcmp(buf.buf, \"ok\"))\n> -               ret = 0;\n> -       else if (starts_with(buf.buf, \"error\")) {\n> -               ret = -1;\n> -       } else if (!strcmp(buf.buf, \"unsupported\"))\n> -               ret = 1;\n> -       else {\n> -               warning(\"%s unexpectedly said: '%s'\", data->name, buf.buf);\n> -               ret = 1;\n> -       }\n> +       ret = strbuf_set_helper_option(data, &buf);\n>         strbuf_release(&buf);\n>         return ret;\n>  }\n"},{"id":"287486","messageId":"CAPig+cTg-oTMiRBbcTpRdH5F8z=o_iQmndnCC-uZu6Sw93X+wA@mail.gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 07/26] upload-pack: use skip_prefix() instead of starts_with()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-05-25T07:02:39Z","receivedAt":"2016-05-25T07:02:39Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2016 at 8:54 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> upload-pack: use skip_prefix() instead of starts_with()\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/upload-pack.c b/upload-pack.c\n> @@ -403,8 +405,8 @@ static int get_common_commits(void)\n> -               if (starts_with(line, \"have \")) {\n> -                       switch (got_sha1(line+5, sha1)) {\n> +               if (skip_prefix(line, \"have \", &arg)) {\n> +                       switch (got_sha1(arg, sha1)) {\n\nThere's one more instance of starts_with() in upload-pack.c:main()\nwhich could be given the same treatment:\n\n    if (starts_with(arg, \"--timeout=\")) {\n        timeout = atoi(arg+10);\n        daemon_mode = 1;\n        continue;\n    }\n\nWas its omission an oversight or intentional since it's not related to\nthe others?\n"},{"id":"287487","messageId":"CAPig+cRM_6Yj0=ZOwf8xq9fipCVOFVVLeh5yOpqTxrq4r314iw@mail.gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-10-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 09/26] upload-pack: move rev-list code out of check_non_tip()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-05-25T07:36:49Z","receivedAt":"2016-05-25T07:36:49Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2016 at 8:54 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/upload-pack.c b/upload-pack.c\n> @@ -451,7 +451,7 @@ static int is_our_ref(struct object *o)\n> -static void check_non_tip(void)\n> +static int check_unreachable(struct object_array *src)\n> @@ -461,14 +461,6 @@ static void check_non_tip(void)\n> -       /*\n> -        * In the normal in-process case without\n> -        * uploadpack.allowReachableSHA1InWant,\n> -        * non-tip requests can never happen.\n> -        */\n> -       if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))\n> -               goto error;\n> -\n> @@ -476,7 +468,7 @@ static void check_non_tip(void)\n>         if (start_command(&cmd))\n> -               goto error;\n> +               return 0;\n> @@ -495,16 +487,16 @@ static void check_non_tip(void)\n>                 if (write_in_full(cmd.in, namebuf, 42) < 0)\n> -                       goto error;\n> +                       return 0;\n> [...]\n> +       for (i = 0; i < src->nr; i++) {\n> +               o = src->objects[i].item;\n>                 if (is_our_ref(o))\n>                         continue;\n>                 memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n>                 if (write_in_full(cmd.in, namebuf, 41) < 0)\n> -                       goto error;\n> +                       return 0;\n>         }\n>         close(cmd.in);\n\nIt's a little bit ugly how this close() becomes disconnected after the\nrefactoring. In the original code, due to the consistent application\nof 'goto error', it's reasonably obvious that skipping the close() is\nnot harmful since the error case die()'s unconditionally (according to\nthe comment). However, after the refactoring, the function simply\nreturns without invoking close(), so there's a disconnect, and it's\nnot obvious without looking at the caller that the program will die().\n\nAlso, if this function later gets a new caller, is that caller going\nto need intimate knowledge about this potential descriptor leak?\n\n> @@ -516,7 +508,7 @@ static void check_non_tip(void)\n>          */\n>         i = read_in_full(cmd.out, namebuf, 1);\n>         if (i)\n> -               goto error;\n> +               return 0;\n>         close(cmd.out);\n\nSame observation.\n\nIt might be clearer if you retained the 'error' label and used it to\nensure that the descriptors get closed:\n\n    cmd.in = -1;\n    cmd.out = -1;\n    ...\n    if (...)\n        goto error;\n    ...\n    /* All the non-tip ... */\n    return 1;\n\nerror:\n    if (cmd.in >= 0)\n        close(cmd.in);\n    if (cmd.out >= 0)\n        close(cmd.out);\n    return 0;\n\n>         /*\n> @@ -525,15 +517,29 @@ static void check_non_tip(void)\n>          * even when it showed no commit.\n>          */\n>         if (finish_command(&cmd))\n> -               goto error;\n> +               return 0;\n\nHere too. Does 'cmd' need to be cleaned up if the function bails\nbefore finish_command()?\n\n>         /* All the non-tip ones are ancestors of what we advertised */\n> -       return;\n> +       return 1;\n> +}\n> +\n> +static void check_non_tip(void)\n> +{\n> +       int i;\n> +\n> +       /*\n> +        * In the normal in-process case without\n> +        * uploadpack.allowReachableSHA1InWant,\n> +        * non-tip requests can never happen.\n> +        */\n> +       if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))\n> +               ;               /* error */\n> +       else if (check_unreachable(&want_obj))\n> +               return;\n\nWith the loss of the 'error' label (below), this logic becomes a bit\nmore difficult to follow. I wonder if it would help to invert the\nsense of the conditional...\n\n    if (stateless_rpc || ...)\n        if (check_unreachable(...))\n            return;\n\nOr, perhaps, retain the 'error' label:\n\n    if (!stateless_rpc && ...)\n        goto error:\n    if (check_unreachable(...))\n        return;\n\nerror:\n    /* Pick one ... */\n\nI think I might find the latter a bit clearer, but it's highly subjective.\n\n> -error:\n>         /* Pick one of them (we know there at least is one) */\n>         for (i = 0; i < want_obj.nr; i++) {\n> -               o = want_obj.objects[i].item;\n> +               struct object *o = want_obj.objects[i].item;\n>                 if (!is_our_ref(o))\n>                         die(\"git upload-pack: not our ref %s\",\n>                             oid_to_hex(&o->oid));\n"},{"id":"287646","messageId":"CAPig+cQ3SEZ6WCpHvq-HukgjauYtSqrz5Z2fwkYxoptnKtYLfg@mail.gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-15-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 14/26] shallow.c: implement a generic shallow boundary finder based on rev-list","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-05-27T04:00:03Z","receivedAt":"2016-05-27T04:00:03Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2016 at 8:54 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Instead of a custom commit walker like get_shallow_commits(), this new\n> function uses rev-list to mark NOT_SHALLOW to all reachable commits,\n> except borders. The definition of reachable is to be defined by the\n> protocol later. This makes it more flexible to define shallow boundary.\n>\n> The way we find find border is paint all reachable commits NOT_SHALLOW.\n\nGrammo: \"find find\"\n\n> Any of them that \"touches\" commits without NOT_SHALLOW flag are\n> considered shallow (e.g. zero parents via grafting mechanism). Shallow\n> commits and their true parents are all marked SHALLOW. Then NOT_SHALLOW\n> is removed from shallow commits at the end.\n>\n> There is an interesting observation. With a generic walker, we can\n> produce all kinds of shallow cutting. In the following graph, every\n> commit but \"x\" is reachable. \"b\" is a parent of \"a\".\n>\n>            x -- a -- o\n>           /    /\n>     x -- c -- b -- o\n>\n> After this function is run, \"a\" and \"c\" are both considered shallow\n> commits. After grafting occurs at the client side, what we see is\n>\n>                 a -- o\n>                     /\n>          c -- b -- o\n>\n> Notice that because of grafting, \"a\" has zero parents, so \"b\" is no\n> longer a parent of \"a\".\n>\n> This is unfortunate and may be solved in two ways. The first is change\n> the way shallow grafting works and keep \"a -- b\" connection if \"b\"\n> exists and always ends at shallow commits (iow, no loose ends). This is\n> hard to detect, or at least not cheap to do.\n>\n> The second way is mark one \"x\" as shallow commit instead of \"a\" and\n> produce this graph at client side:\n>\n>            x -- a -- o\n>                /    /\n>          c -- b -- o\n>\n> More commits, but simpler grafting rules.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n"},{"id":"288381","messageId":"CAPig+cS8CrnF8N8SfPRtnug8r6O70M1qhnjaZTQtbgGNxwN4Ag@mail.gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-19-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 18/26] t5500, t5539: tests for shallow depth since a specific date","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-06-05T04:43:31Z","receivedAt":"2016-06-05T04:43:31Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2016 at 8:55 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh\n> @@ -73,5 +73,31 @@ test_expect_success 'no shallow lines after receiving ACK ready' '\n>         )\n>  '\n>\n> +test_expect_success 'clone shallow since ...' '\n> +       test_create_repo shallow-since &&\n> +       (\n> +       cd shallow-since &&\n> +       GIT_COMMITTER_DATE=\"100000000 +0700\" git commit --allow-empty -m one &&\n> +       GIT_COMMITTER_DATE=\"200000000 +0700\" git commit --allow-empty -m two &&\n> +       GIT_COMMITTER_DATE=\"300000000 +0700\" git commit --allow-empty -m three &&\n> +       mv .git \"$HTTPD_DOCUMENT_ROOT_PATH/shallow-since.git\" &&\n> +       git clone --shallow-since \"300000000 +0700\" $HTTPD_URL/smart/shallow-since.git ../shallow11 &&\n> +       git -C ../shallow11 log --pretty=tformat:%s HEAD >actual &&\n> +       echo three >expected &&\n> +       test_cmp expected actual\n> +       )\n> +'\n> +\n> +test_expect_success 'fetch shallow since ...' '\n> +       git -C shallow11 fetch --shallow-since \"200000000 +0700\" origin &&\n> +       git -C shallow11 log --pretty=tformat:%s origin/master >actual &&\n> +       cat >expected <<-\\EOF &&\n> +       three\n> +       two\n> +       EOF\n> +       test_cmp expected actual\n> +'\n> +\n> +\n\nStyle: extra blank line (not worth a re-roll, of course)\n\n>  stop_httpd\n>  test_done\n> --\n> 2.8.0.rc0.210.gd302cd2\n"},{"id":"288387","messageId":"CAPig+cR--0YV7Loh7fVZb4LA9oYPwNT2+QpL1tzSr-1dZu+mhw@mail.gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-22-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 21/26] fetch: define shallow boundary with --shallow-exclude","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-06-05T05:03:57Z","receivedAt":"2016-06-05T05:03:57Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2016 at 8:55 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/builtin/fetch.c b/builtin/fetch.c\n> @@ -41,6 +41,7 @@ static int max_children = 1;\n> +static struct string_list deepen_not = STRING_LIST_INIT_NODUP;\n> @@ -50,6 +51,13 @@ static int shown_url = 0;\n> +static int option_parse_deepen_not(const struct option *opt,\n> +                                  const char *arg, int unset)\n> +{\n> +       string_list_append(&deepen_not, arg);\n> +       return 0;\n> +}\n> +\n> @@ -118,6 +126,9 @@ static struct option builtin_fetch_options[] = {\n>                    N_(\"deepen history of shallow clone\")),\n>         OPT_STRING(0, \"shallow-since\", &deepen_since, N_(\"time\"),\n>                    N_(\"deepen history of shallow repository based on time\")),\n> +       { OPTION_CALLBACK, 0, \"shallow-exclude\", NULL, N_(\"revision\"),\n> +                   N_(\"deepen history of shallow clone by excluding rev\"),\n> +                   PARSE_OPT_NONEG, option_parse_deepen_not },\n\nIs this a candidate for OPT_STRING_LIST()?\n\n>         { OPTION_SET_INT, 0, \"unshallow\", &unshallow, NULL,\n>                    N_(\"convert to a complete repository\"),\n>                    PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },\n"},{"id":"288386","messageId":"CAPig+cTrC_Y9zYVqy_gDp+HgJ8SFUUV45y83er_V-KkHmrYv7w@mail.gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-23-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 22/26] clone: define shallow clone boundary with --shallow-exclude","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-06-05T05:05:48Z","receivedAt":"2016-06-05T05:05:48Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2016 at 8:55 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> @@ -44,6 +44,7 @@ static int deepen;\n> +static struct string_list option_not = STRING_LIST_INIT_NODUP;\n> @@ -52,6 +53,13 @@ static struct string_list option_config;\n> +static int option_parse_deepen_not(const struct option *opt,\n> +                                  const char *arg, int unset)\n> +{\n> +       string_list_append(&option_not, arg);\n> +       return 0;\n> +}\n> +\n> @@ -89,6 +97,9 @@ static struct option builtin_clone_options[] = {\n>                     N_(\"create a shallow clone of that depth\")),\n>         OPT_STRING(0, \"shallow-since\", &option_since, N_(\"time\"),\n>                     N_(\"create a shallow clone since a specific time\")),\n> +       { OPTION_CALLBACK, 0, \"shallow-exclude\", NULL, N_(\"revision\"),\n> +                   N_(\"deepen history of shallow clone by excluding rev\"),\n> +                   PARSE_OPT_NONEG, option_parse_deepen_not },\n\nOPT_STRING_LIST()?\n\n>         OPT_BOOL(0, \"single-branch\", &option_single_branch,\n>                     N_(\"clone only one branch, HEAD or --branch\")),\n>         OPT_STRING(0, \"separate-git-dir\", &real_git_dir, N_(\"gitdir\"),\n"},{"id":"288388","messageId":"CAPig+cSFk65f831Z+U6gwdW2Brs4JOtxj2b9toOKrJAfDZX9iA@mail.gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-24-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 23/26] t5500, t5539: tests for shallow depth excluding a ref","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-06-05T05:09:44Z","receivedAt":"2016-06-05T05:09:44Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2016 at 8:55 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\n> @@ -661,4 +661,26 @@ test_expect_success 'fetch shallow since ...' '\n> +test_expect_success 'fetch exclude tag one' '\n> +       git -C shallow12 fetch --shallow-exclude one origin &&\n> +       git -C shallow12 log --pretty=tformat:%s origin/master >actual &&\n> +       echo three >expected &&\n> +       echo two  >>expected &&\n\nPerhaps:\n\n    echo >expected <<-\\EOF &&\n    three\n    two\n    EOF\n\nor:\n\n    test_write_lines three two >expected &&\n\nbut not worth a re-roll.\n\n> +       test_cmp expected actual\n> +'\n> diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh\n> @@ -98,6 +98,28 @@ test_expect_success 'fetch shallow since ...' '\n> +test_expect_success 'fetch exclude tag one' '\n> +       git -C shallow12 fetch --shallow-exclude one origin &&\n> +       git -C shallow12 log --pretty=tformat:%s origin/master >actual &&\n> +       echo three >expected &&\n> +       echo two  >>expected &&\n\nLikewise.\n\n> +       test_cmp expected actual\n> +'\n"},{"id":"288400","messageId":"CAPig+cTyzA1HFt=K14UBPsKhWhHmy0WX8=tru7HQmP3m+GpGgQ@mail.gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-25-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 24/26] upload-pack: split check_unreachable() in two, prep for get_reachable_list()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-06-05T19:43:16Z","receivedAt":"2016-06-05T19:43:16Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2016 at 8:55 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/upload-pack.c b/upload-pack.c\n> @@ -452,24 +452,24 @@ static int is_our_ref(struct object *o)\n> -static int check_unreachable(struct object_array *src)\n> +static int do_reachable_revlist(struct child_process *cmd,\n> +                               struct object_array *src)\n> @@ -487,8 +487,8 @@ static int check_unreachable(struct object_array *src)\n>                 if (!is_our_ref(o))\n>                         continue;\n>                 memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n> -               if (write_in_full(cmd.in, namebuf, 42) < 0)\n> -                       return 0;\n> +               if (write_in_full(cmd->in, namebuf, 42) < 0)\n> +                       return -1;\n>         }\n>         namebuf[40] = '\\n';\n>         for (i = 0; i < src->nr; i++) {\n> @@ -496,18 +496,29 @@ static int check_unreachable(struct object_array *src)\n>                 if (is_our_ref(o))\n>                         continue;\n>                 memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n> -               if (write_in_full(cmd.in, namebuf, 41) < 0)\n> -                       return 0;\n> +               if (write_in_full(cmd->in, namebuf, 41) < 0)\n> +                       return -1;\n>         }\n> -       close(cmd.in);\n> +       close(cmd->in);\n>\n>         sigchain_pop(SIGPIPE);\n\nNot a new issue, but does it matter that there are early returns\nbefore sigchain_pop()?\n\n> +       return 0;\n> +}\n> +\n> +static int check_unreachable(struct object_array *src)\n> +{\n> +       struct child_process cmd = CHILD_PROCESS_INIT;\n> +       int i, ret = do_reachable_revlist(&cmd, src);\n> +       char buf[1];\n> +\n> +       if (ret < 0)\n> +               return 0;\n\nNit: It might be a bit easier to see what this conditional is checking\nif it is closer to the code which sets 'ret'. Perhaps either:\n\n    char buf[1];\n    int i, ret = ...;\n\n    if (ret < 0)\n\nor:\n\n    char buf[1];\n    int i, ret;\n\n    ret = ...;\n    if (ret < 0)\n\n>         /*\n>          * The commits out of the rev-list are not ancestors of\n>          * our ref.\n>          */\n> -       i = read_in_full(cmd.out, namebuf, 1);\n> +       i = read_in_full(cmd.out, buf, 1);\n\ns/1/sizeof(buf)/ perhaps or does that make the intent less clear?\n\n>         if (i)\n>                 return 0;\n>         close(cmd.out);\n"},{"id":"288401","messageId":"CAPig+cS8ON9QtOWJ_eFtRmD7NUKjE8UnMc8GFgfr4HRKAWiq2A@mail.gmail.com","threadId":"42016","inReplyTo":"1460552110-5554-26-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 25/26] upload-pack: add get_reachable_list()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-06-05T19:51:17Z","receivedAt":"2016-06-05T19:51:17Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2016 at 8:55 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/upload-pack.c b/upload-pack.c\n> @@ -505,10 +513,48 @@ static int do_reachable_revlist(struct child_process *cmd,\n> +static int get_reachable_list(struct object_array *src,\n> +                             struct object_array *reachable)\n> +{\n> +       struct child_process cmd = CHILD_PROCESS_INIT;\n> +       int i, ret = do_reachable_revlist(&cmd, src, reachable);\n> +       struct object *o;\n> +       char namebuf[42]; /* ^ + SHA-1 + LF */\n> +\n> +       if (ret < 0)\n> +               return -1;\n\nSame comment as previous patch: It's a bit difficult to see what this\nconditional is checking since it is so far removed (textually) from\nthe assignment. Perhaps:\n\n    ret = do_reachable_revlist(...);\n    if (ret < 0)\n\nwould make it clearer.\n\n> +       while ((i = read_in_full(cmd.out, namebuf, 41)) == 41) {\n> +               struct object_id sha1;\n> +\n> +               if (namebuf[40] != '\\n' || get_oid_hex(namebuf, &sha1))\n> +                       break;\n> +\n> +               o = lookup_object(sha1.hash);\n> +               if (o && o->type == OBJ_COMMIT) {\n> +                       o->flags &= ~TMP_MARK;\n> +               }\n> +       }\n> +       for (i = get_max_object_index(); 0 < i; i--) {\n> +               o = get_indexed_object(i - 1);\n> +               if (o && o->type == OBJ_COMMIT &&\n> +                   (o->flags & TMP_MARK)) {\n> +                       add_object_array(o, NULL, reachable);\n> +                               o->flags &= ~TMP_MARK;\n> +               }\n> +       }\n> +       close(cmd.out);\n> +\n> +       if (finish_command(&cmd))\n> +               return -1;\n> +\n> +       return 0;\n> +}\n"},{"id":"288893","messageId":"CACsJy8BTWu-meKQcB8Xqa5bniM1z3LNP6eEJe_mmiuPTD5U9_A@mail.gmail.com","threadId":"42016","inReplyTo":"CAPig+cTyzA1HFt=K14UBPsKhWhHmy0WX8=tru7HQmP3m+GpGgQ@mail.gmail.com","subject":"Re: [PATCH 24/26] upload-pack: split check_unreachable() in two, prep for get_reachable_list()","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-10T12:14:18Z","receivedAt":"2016-06-16T02:19:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Jun 6, 2016 at 2:43 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n>>         /*\n>>          * The commits out of the rev-list are not ancestors of\n>>          * our ref.\n>>          */\n>> -       i = read_in_full(cmd.out, namebuf, 1);\n>> +       i = read_in_full(cmd.out, buf, 1);\n>\n> s/1/sizeof(buf)/ perhaps or does that make the intent less clear?\n\nYeah I think so. \"1\" makes it obvious that we do not care about the\nactual content (or at least raise some eyebrows and force the reader\nto understand this entire function). sizeof(buf), not so much.\n-- \nDuy\n"}]}