{"thread":{"id":"41326","subject":"[PATCH v2 00/25] More flexibility in making shallow clones","startedAt":"2016-02-04T09:03:36Z","lastAt":"2016-02-19T09:30:23Z","messageCount":63,"participants":["Nguyễn Thái Ngọc Duy","Junio C Hamano","Eric Sunshine","Duy Nguyen"],"isPatch":true,"patchVersion":2,"patchTotal":25},"messages":[{"id":"277381","messageId":"1454576641-29615-1-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":null,"subject":"[PATCH v2 00/25] More flexibility in making shallow clones","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:36Z","receivedAt":"2016-02-04T09:03:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This series brings three new options to shallow clone/fetch, to let\nyou specify cut point by time, or by excluding some refs, or to let\nyou extend shallow boundary by <N> commits.\n\nThe series is now complete. Changes since v1 [1]\n\n - smart http support\n - option names --not and --since are changed to --shallow-exclude\n   and --shallow-since\n - fix the last patch per Eric's comments (the tests were totally\n   broken but I didn't realize)\n\nThe meat starts since 14/25. Before that is just cleanups and stuff.\nHappy (shallowly) cloning!\n\n[1] http://thread.gmane.org/gmane.comp.version-control.git/283110\n\nNguyễn Thái Ngọc Duy (25):\n  remote-curl.c: convert fetch_git() to use argv_array\n  transport-helper.c: refactor set_helper_option()\n  transport-helper.c: do not send null option to remote helper\n  upload-pack: move shallow deepen code out of receive_needs()\n  upload-pack: move \"shallow\" sending code out of deepen()\n  upload-pack: remove unused variable \"backup\"\n  upload-pack: move \"unshallow\" sending code out of deepen()\n  upload-pack: use skip_prefix() instead of starts_with() when possible\n  upload-pack: tighten number parsing at \"deepen\" lines\n  upload-pack: move rev-list code out of check_non_tip()\n  fetch-pack: use skip_prefix() instead of starts_with() when possible\n  fetch-pack: use a common function for verbose printing\n  fetch-pack: use a separate flag for fetch in deepening mode\n  shallow.c: implement a generic shallow boundary finder based on rev-list\n  upload-pack: add deepen-since to cut shallow repos based on time\n  fetch: define shallow boundary with --shallow-since\n  clone: define shallow clone boundary based on time with --shallow-since\n  t5500, t5539: tests for shallow depth since a specific date\n  refs: add expand_ref()\n  upload-pack: support define shallow boundary by excluding revisions\n  fetch: define shallow boundary with --shallow-exclude\n  clone: define shallow clone boundary with --shallow-exclude\n  t5500, t5539: tests for shallow depth excluding a ref\n  upload-pack: make check_reachable_object() return unreachable list if asked\n  fetch, upload-pack: --deepen=N extends shallow boundary by N commits\n\n Documentation/fetch-options.txt                   |  14 +\n Documentation/git-clone.txt                       |   8 +\n Documentation/git-fetch-pack.txt                  |  14 +\n Documentation/gitremote-helpers.txt               |  11 +\n Documentation/technical/pack-protocol.txt         |   4 +-\n Documentation/technical/protocol-capabilities.txt |  25 ++\n builtin/clone.c                                   |  32 ++-\n builtin/fetch-pack.c                              |  31 ++-\n builtin/fetch.c                                   |  41 ++-\n commit.h                                          |   2 +\n fetch-pack.c                                      | 128 +++++----\n fetch-pack.h                                      |   4 +\n object.h                                          |   2 +-\n refs.c                                            |   8 +-\n refs.h                                            |   1 +\n remote-curl.c                                     |  75 ++++--\n shallow.c                                         |  85 ++++++\n t/t5500-fetch-pack.sh                             |  67 +++++\n t/t5539-fetch-http-shallow.sh                     |  72 ++++++\n transport-helper.c                                |  71 +++--\n transport.c                                       |  11 +\n transport.h                                       |  14 +\n upload-pack.c                                     | 301 ++++++++++++++++------\n 23 files changed, 832 insertions(+), 189 deletions(-)\n\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277382","messageId":"1454576641-29615-2-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 01/25] remote-curl.c: convert fetch_git() to use argv_array","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:37Z","receivedAt":"2016-02-04T09:03:37Z","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 | 41 ++++++++++++++++++-----------------------\n 1 file changed, 18 insertions(+), 23 deletions(-)\n\ndiff --git a/remote-curl.c b/remote-curl.c\nindex c704857..3ac5b6c 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -726,37 +726,31 @@ 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\",\n+\t\t\t 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 +763,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 +773,7 @@ static int fetch_git(struct discovery *heads,\n \tstrbuf_release(&rpc.result);\n \tstrbuf_release(&preamble);\n \tfree(depth_arg);\n+\targv_array_clear(&args);\n \treturn err;\n }\n \n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277383","messageId":"1454576641-29615-3-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 02/25] transport-helper.c: refactor set_helper_option()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:38Z","receivedAt":"2016-02-04T09:03:38Z","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 transport-helper.c | 37 +++++++++++++++++++++++--------------\n 1 file changed, 23 insertions(+), 14 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex a6bff8b..35023da 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -260,6 +260,28 @@ static const char *boolean_options[] = {\n \tTRANS_OPT_FOLLOWTAGS,\n \t};\n \n+static int strbuf_set_helper_option(struct helper_data *data,\n+\t\t\t\t    struct strbuf *buf)\n+{\n+\tint ret;\n+\n+\tsendline(data, buf);\n+\tif (recvline(data, buf))\n+\t\texit(128);\n+\n+\tif (!strcmp(buf->buf, \"ok\"))\n+\t\tret = 0;\n+\telse if (starts_with(buf->buf, \"error\")) {\n+\t\tret = -1;\n+\t} else if (!strcmp(buf->buf, \"unsupported\"))\n+\t\tret = 1;\n+\telse {\n+\t\twarning(\"%s unexpectedly said: '%s'\", data->name, buf->buf);\n+\t\tret = 1;\n+\t}\n+\treturn ret;\n+}\n+\n static int set_helper_option(struct transport *transport,\n \t\t\t  const char *name, const char *value)\n {\n@@ -291,20 +313,7 @@ static int set_helper_option(struct transport *transport,\n \t\tquote_c_style(value, &buf, NULL, 0);\n \tstrbuf_addch(&buf, '\\n');\n \n-\tsendline(data, &buf);\n-\tif (recvline(data, &buf))\n-\t\texit(128);\n-\n-\tif (!strcmp(buf.buf, \"ok\"))\n-\t\tret = 0;\n-\telse if (starts_with(buf.buf, \"error\")) {\n-\t\tret = -1;\n-\t} else if (!strcmp(buf.buf, \"unsupported\"))\n-\t\tret = 1;\n-\telse {\n-\t\twarning(\"%s unexpectedly said: '%s'\", data->name, buf.buf);\n-\t\tret = 1;\n-\t}\n+\tret = strbuf_set_helper_option(data, &buf);\n \tstrbuf_release(&buf);\n \treturn ret;\n }\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277384","messageId":"1454576641-29615-4-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 03/25] transport-helper.c: do not send null option to remote helper","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:39Z","receivedAt":"2016-02-04T09:03:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n transport-helper.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 35023da..2e78c4d 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -309,8 +309,12 @@ static int set_helper_option(struct transport *transport,\n \tstrbuf_addf(&buf, \"option %s \", name);\n \tif (is_bool)\n \t\tstrbuf_addstr(&buf, value ? \"true\" : \"false\");\n-\telse\n+\telse if (value)\n \t\tquote_c_style(value, &buf, NULL, 0);\n+\telse {\n+\t\tstrbuf_release(&buf);\n+\t\treturn 0;\n+\t}\n \tstrbuf_addch(&buf, '\\n');\n \n \tret = strbuf_set_helper_option(data, &buf);\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277385","messageId":"1454576641-29615-5-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 04/25] upload-pack: move shallow deepen code out of receive_needs()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:40Z","receivedAt":"2016-02-04T09:03:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is a prep step for further refactoring. Besides reindentation and\ns/shallows\\./shallows->/g, no other changes are expected.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 99 +++++++++++++++++++++++++++++++----------------------------\n 1 file changed, 52 insertions(+), 47 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex b3f6653..97ed620 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -538,6 +538,55 @@ error:\n \t}\n }\n \n+static void deepen(int depth, const struct object_array *shallows)\n+{\n+\tstruct commit_list *result = NULL, *backup = NULL;\n+\tint i;\n+\tif (depth == INFINITE_DEPTH && !is_repository_shallow())\n+\t\tfor (i = 0; i < shallows->nr; i++) {\n+\t\t\tstruct object *object = shallows->objects[i].item;\n+\t\t\tobject->flags |= NOT_SHALLOW;\n+\t\t}\n+\telse\n+\t\tbackup = result =\n+\t\t\tget_shallow_commits(&want_obj, depth,\n+\t\t\t\t\t    SHALLOW, NOT_SHALLOW);\n+\twhile (result) {\n+\t\tstruct object *object = &result->item->object;\n+\t\tif (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {\n+\t\t\tpacket_write(1, \"shallow %s\",\n+\t\t\t\t     oid_to_hex(&object->oid));\n+\t\t\tregister_shallow(object->oid.hash);\n+\t\t\tshallow_nr++;\n+\t\t}\n+\t\tresult = result->next;\n+\t}\n+\tfree_commit_list(backup);\n+\tfor (i = 0; i < shallows->nr; i++) {\n+\t\tstruct object *object = shallows->objects[i].item;\n+\t\tif (object->flags & NOT_SHALLOW) {\n+\t\t\tstruct commit_list *parents;\n+\t\t\tpacket_write(1, \"unshallow %s\",\n+\t\t\t\t     oid_to_hex(&object->oid));\n+\t\t\tobject->flags &= ~CLIENT_SHALLOW;\n+\t\t\t/* make sure the real parents are parsed */\n+\t\t\tunregister_shallow(object->oid.hash);\n+\t\t\tobject->parsed = 0;\n+\t\t\tparse_commit_or_die((struct commit *)object);\n+\t\t\tparents = ((struct commit *)object)->parents;\n+\t\t\twhile (parents) {\n+\t\t\t\tadd_object_array(&parents->item->object,\n+\t\t\t\t\t\t NULL, &want_obj);\n+\t\t\t\tparents = parents->next;\n+\t\t\t}\n+\t\t\tadd_object_array(object, NULL, &extra_edge_obj);\n+\t\t}\n+\t\t/* make sure commit traversal conforms to client */\n+\t\tregister_shallow(object->oid.hash);\n+\t}\n+\tpacket_flush(1);\n+}\n+\n static void receive_needs(void)\n {\n \tstruct object_array shallows = OBJECT_ARRAY_INIT;\n@@ -630,53 +679,9 @@ static void receive_needs(void)\n \n \tif (depth == 0 && shallows.nr == 0)\n \t\treturn;\n-\tif (depth > 0) {\n-\t\tstruct commit_list *result = NULL, *backup = NULL;\n-\t\tint i;\n-\t\tif (depth == INFINITE_DEPTH && !is_repository_shallow())\n-\t\t\tfor (i = 0; i < shallows.nr; i++) {\n-\t\t\t\tstruct object *object = shallows.objects[i].item;\n-\t\t\t\tobject->flags |= NOT_SHALLOW;\n-\t\t\t}\n-\t\telse\n-\t\t\tbackup = result =\n-\t\t\t\tget_shallow_commits(&want_obj, depth,\n-\t\t\t\t\t\t    SHALLOW, NOT_SHALLOW);\n-\t\twhile (result) {\n-\t\t\tstruct object *object = &result->item->object;\n-\t\t\tif (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {\n-\t\t\t\tpacket_write(1, \"shallow %s\",\n-\t\t\t\t\t\toid_to_hex(&object->oid));\n-\t\t\t\tregister_shallow(object->oid.hash);\n-\t\t\t\tshallow_nr++;\n-\t\t\t}\n-\t\t\tresult = result->next;\n-\t\t}\n-\t\tfree_commit_list(backup);\n-\t\tfor (i = 0; i < shallows.nr; i++) {\n-\t\t\tstruct object *object = shallows.objects[i].item;\n-\t\t\tif (object->flags & NOT_SHALLOW) {\n-\t\t\t\tstruct commit_list *parents;\n-\t\t\t\tpacket_write(1, \"unshallow %s\",\n-\t\t\t\t\toid_to_hex(&object->oid));\n-\t\t\t\tobject->flags &= ~CLIENT_SHALLOW;\n-\t\t\t\t/* make sure the real parents are parsed */\n-\t\t\t\tunregister_shallow(object->oid.hash);\n-\t\t\t\tobject->parsed = 0;\n-\t\t\t\tparse_commit_or_die((struct commit *)object);\n-\t\t\t\tparents = ((struct commit *)object)->parents;\n-\t\t\t\twhile (parents) {\n-\t\t\t\t\tadd_object_array(&parents->item->object,\n-\t\t\t\t\t\t\tNULL, &want_obj);\n-\t\t\t\t\tparents = parents->next;\n-\t\t\t\t}\n-\t\t\t\tadd_object_array(object, NULL, &extra_edge_obj);\n-\t\t\t}\n-\t\t\t/* make sure commit traversal conforms to client */\n-\t\t\tregister_shallow(object->oid.hash);\n-\t\t}\n-\t\tpacket_flush(1);\n-\t} else\n+\tif (depth > 0)\n+\t\tdeepen(depth, &shallows);\n+\telse\n \t\tif (shallows.nr > 0) {\n \t\t\tint i;\n \t\t\tfor (i = 0; i < shallows.nr; i++)\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277386","messageId":"1454576641-29615-6-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 05/25] upload-pack: move \"shallow\" sending code out of deepen()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:41Z","receivedAt":"2016-02-04T09:03:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 25 +++++++++++++++----------\n 1 file changed, 15 insertions(+), 10 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 97ed620..0eb9a0b 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -538,6 +538,20 @@ error:\n \t}\n }\n \n+static void send_shallow(struct commit_list *result)\n+{\n+\twhile (result) {\n+\t\tstruct object *object = &result->item->object;\n+\t\tif (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {\n+\t\t\tpacket_write(1, \"shallow %s\",\n+\t\t\t\t     oid_to_hex(&object->oid));\n+\t\t\tregister_shallow(object->oid.hash);\n+\t\t\tshallow_nr++;\n+\t\t}\n+\t\tresult = result->next;\n+\t}\n+}\n+\n static void deepen(int depth, const struct object_array *shallows)\n {\n \tstruct commit_list *result = NULL, *backup = NULL;\n@@ -551,16 +565,7 @@ static void deepen(int depth, const struct object_array *shallows)\n \t\tbackup = result =\n \t\t\tget_shallow_commits(&want_obj, depth,\n \t\t\t\t\t    SHALLOW, NOT_SHALLOW);\n-\twhile (result) {\n-\t\tstruct object *object = &result->item->object;\n-\t\tif (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {\n-\t\t\tpacket_write(1, \"shallow %s\",\n-\t\t\t\t     oid_to_hex(&object->oid));\n-\t\t\tregister_shallow(object->oid.hash);\n-\t\t\tshallow_nr++;\n-\t\t}\n-\t\tresult = result->next;\n-\t}\n+\tsend_shallow(result);\n \tfree_commit_list(backup);\n \tfor (i = 0; i < shallows->nr; i++) {\n \t\tstruct object *object = shallows->objects[i].item;\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277387","messageId":"1454576641-29615-7-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 06/25] upload-pack: remove unused variable \"backup\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:42Z","receivedAt":"2016-02-04T09:03:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"After the last patch, \"result\" and \"backup\" are the same. \"result\" used\nto move, but the movement is now contained in send_shallow(). Delete\nthis redundant variable.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 9 ++++-----\n 1 file changed, 4 insertions(+), 5 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 0eb9a0b..ee5d20b 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -554,7 +554,7 @@ static void send_shallow(struct commit_list *result)\n \n static void deepen(int depth, const struct object_array *shallows)\n {\n-\tstruct commit_list *result = NULL, *backup = NULL;\n+\tstruct commit_list *result = NULL;\n \tint i;\n \tif (depth == INFINITE_DEPTH && !is_repository_shallow())\n \t\tfor (i = 0; i < shallows->nr; i++) {\n@@ -562,11 +562,10 @@ static void deepen(int depth, const struct object_array *shallows)\n \t\t\tobject->flags |= NOT_SHALLOW;\n \t\t}\n \telse\n-\t\tbackup = result =\n-\t\t\tget_shallow_commits(&want_obj, depth,\n-\t\t\t\t\t    SHALLOW, NOT_SHALLOW);\n+\t\tresult = get_shallow_commits(&want_obj, depth,\n+\t\t\t\t\t     SHALLOW, NOT_SHALLOW);\n \tsend_shallow(result);\n-\tfree_commit_list(backup);\n+\tfree_commit_list(result);\n \tfor (i = 0; i < shallows->nr; i++) {\n \t\tstruct object *object = shallows->objects[i].item;\n \t\tif (object->flags & NOT_SHALLOW) {\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277388","messageId":"1454576641-29615-8-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 07/25] upload-pack: move \"unshallow\" sending code out of deepen()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:43Z","receivedAt":"2016-02-04T09:03:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Also add some more comments in this code because it takes too long to\nunderstand what it does (to me, who should be familiar enough to\nunderstand this code well!)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 39 ++++++++++++++++++++++++++-------------\n 1 file changed, 26 insertions(+), 13 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex ee5d20b..bfb7985 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -552,20 +552,10 @@ static void send_shallow(struct commit_list *result)\n \t}\n }\n \n-static void deepen(int depth, const struct object_array *shallows)\n+static void send_unshallow(const struct object_array *shallows)\n {\n-\tstruct commit_list *result = NULL;\n \tint i;\n-\tif (depth == INFINITE_DEPTH && !is_repository_shallow())\n-\t\tfor (i = 0; i < shallows->nr; i++) {\n-\t\t\tstruct object *object = shallows->objects[i].item;\n-\t\t\tobject->flags |= NOT_SHALLOW;\n-\t\t}\n-\telse\n-\t\tresult = get_shallow_commits(&want_obj, depth,\n-\t\t\t\t\t     SHALLOW, NOT_SHALLOW);\n-\tsend_shallow(result);\n-\tfree_commit_list(result);\n+\n \tfor (i = 0; i < shallows->nr; i++) {\n \t\tstruct object *object = shallows->objects[i].item;\n \t\tif (object->flags & NOT_SHALLOW) {\n@@ -573,7 +563,13 @@ static void deepen(int depth, const struct object_array *shallows)\n \t\t\tpacket_write(1, \"unshallow %s\",\n \t\t\t\t     oid_to_hex(&object->oid));\n \t\t\tobject->flags &= ~CLIENT_SHALLOW;\n-\t\t\t/* make sure the real parents are parsed */\n+\t\t\t/*\n+\t\t\t * We want to _register_ \"object\" as shallow, but we\n+\t\t\t * also need to traverse object's parents to deepen a\n+\t\t\t * shallow clone. Unregister it for now so we can\n+\t\t\t * parse and add the parents to the want list, then\n+\t\t\t * re-register it.\n+\t\t\t */\n \t\t\tunregister_shallow(object->oid.hash);\n \t\t\tobject->parsed = 0;\n \t\t\tparse_commit_or_die((struct commit *)object);\n@@ -588,6 +584,23 @@ static void deepen(int depth, const struct object_array *shallows)\n \t\t/* make sure commit traversal conforms to client */\n \t\tregister_shallow(object->oid.hash);\n \t}\n+}\n+\n+static void deepen(int depth, const struct object_array *shallows)\n+{\n+\tstruct commit_list *result = NULL;\n+\tint i;\n+\tif (depth == INFINITE_DEPTH && !is_repository_shallow())\n+\t\tfor (i = 0; i < shallows->nr; i++) {\n+\t\t\tstruct object *object = shallows->objects[i].item;\n+\t\t\tobject->flags |= NOT_SHALLOW;\n+\t\t}\n+\telse\n+\t\tresult = get_shallow_commits(&want_obj, depth,\n+\t\t\t\t\t     SHALLOW, NOT_SHALLOW);\n+\tsend_shallow(result);\n+\tfree_commit_list(result);\n+\tsend_unshallow(shallows);\n \tpacket_flush(1);\n }\n \n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277389","messageId":"1454576641-29615-9-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 08/25] upload-pack: use skip_prefix() instead of starts_with() when possible","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:44Z","receivedAt":"2016-02-04T09:03:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 26 +++++++++++++++-----------\n 1 file changed, 15 insertions(+), 11 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex bfb7985..257ad48 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -276,7 +276,7 @@ static void create_pack_file(void)\n \tdie(\"git upload-pack: %s\", abort_msg);\n }\n \n-static int got_sha1(char *hex, unsigned char *sha1)\n+static int got_sha1(const char *hex, unsigned char *sha1)\n {\n \tstruct object *o;\n \tint we_knew_they_have = 0;\n@@ -382,6 +382,8 @@ static int get_common_commits(void)\n \n \tfor (;;) {\n \t\tchar *line = packet_read_line(0, NULL);\n+\t\tconst char *arg;\n+\n \t\treset_timeout();\n \n \t\tif (!line) {\n@@ -403,8 +405,8 @@ static int get_common_commits(void)\n \t\t\tgot_other = 0;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (starts_with(line, \"have \")) {\n-\t\t\tswitch (got_sha1(line+5, sha1)) {\n+\t\tif (skip_prefix(line, \"have \", &arg)) {\n+\t\t\tswitch (got_sha1(arg, sha1)) {\n \t\t\tcase -1: /* they have what we do not */\n \t\t\t\tgot_other = 1;\n \t\t\t\tif (multi_ack && ok_to_give_up()) {\n@@ -616,14 +618,16 @@ static void receive_needs(void)\n \t\tconst char *features;\n \t\tunsigned char sha1_buf[20];\n \t\tchar *line = packet_read_line(0, NULL);\n+\t\tconst char *arg;\n+\n \t\treset_timeout();\n \t\tif (!line)\n \t\t\tbreak;\n \n-\t\tif (starts_with(line, \"shallow \")) {\n+\t\tif (skip_prefix(line, \"shallow \", &arg)) {\n \t\t\tunsigned char sha1[20];\n \t\t\tstruct object *object;\n-\t\t\tif (get_sha1_hex(line + 8, sha1))\n+\t\t\tif (get_sha1_hex(arg, sha1))\n \t\t\t\tdie(\"invalid shallow line: %s\", line);\n \t\t\tobject = parse_object(sha1);\n \t\t\tif (!object)\n@@ -636,19 +640,19 @@ static void receive_needs(void)\n \t\t\t}\n \t\t\tcontinue;\n \t\t}\n-\t\tif (starts_with(line, \"deepen \")) {\n+\t\tif (skip_prefix(line, \"deepen \", &arg)) {\n \t\t\tchar *end;\n-\t\t\tdepth = strtol(line + 7, &end, 0);\n-\t\t\tif (end == line + 7 || depth <= 0)\n+\t\t\tdepth = strtol(arg, &end, 0);\n+\t\t\tif (end == arg || depth <= 0)\n \t\t\t\tdie(\"Invalid deepen: %s\", line);\n \t\t\tcontinue;\n \t\t}\n-\t\tif (!starts_with(line, \"want \") ||\n-\t\t    get_sha1_hex(line+5, sha1_buf))\n+\t\tif (!skip_prefix(line, \"want \", &arg) ||\n+\t\t    get_sha1_hex(arg, sha1_buf))\n \t\t\tdie(\"git upload-pack: protocol error, \"\n \t\t\t    \"expected to get sha, not '%s'\", line);\n \n-\t\tfeatures = line + 45;\n+\t\tfeatures = arg + 40;\n \n \t\tif (parse_feature_request(features, \"multi_ack_detailed\"))\n \t\t\tmulti_ack = 2;\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277392","messageId":"1454576641-29615-10-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 09/25] upload-pack: tighten number parsing at \"deepen\" lines","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:45Z","receivedAt":"2016-02-04T09:03:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 257ad48..9f14933 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -641,9 +641,9 @@ static void receive_needs(void)\n \t\t\tcontinue;\n \t\t}\n \t\tif (skip_prefix(line, \"deepen \", &arg)) {\n-\t\t\tchar *end;\n+\t\t\tchar *end = NULL;\n \t\t\tdepth = strtol(arg, &end, 0);\n-\t\t\tif (end == arg || depth <= 0)\n+\t\t\tif (!end || *end || depth <= 0)\n \t\t\t\tdie(\"Invalid deepen: %s\", line);\n \t\t\tcontinue;\n \t\t}\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277391","messageId":"1454576641-29615-11-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 10/25] upload-pack: move rev-list code out of check_non_tip()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:46Z","receivedAt":"2016-02-04T09:03:46Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n upload-pack.c | 44 +++++++++++++++++++++++++-------------------\n 1 file changed, 25 insertions(+), 19 deletions(-)\n\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 9f14933..c8dafe8 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -451,7 +451,7 @@ static int is_our_ref(struct object *o)\n \treturn o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);\n }\n \n-static void check_non_tip(void)\n+static int check_unreachable(struct object_array *src)\n {\n \tstatic const char *argv[] = {\n \t\t\"rev-list\", \"--stdin\", NULL,\n@@ -461,14 +461,6 @@ static void check_non_tip(void)\n \tchar namebuf[42]; /* ^ + SHA-1 + LF */\n \tint i;\n \n-\t/*\n-\t * In the normal in-process case without\n-\t * uploadpack.allowReachableSHA1InWant,\n-\t * non-tip requests can never happen.\n-\t */\n-\tif (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))\n-\t\tgoto error;\n-\n \tcmd.argv = argv;\n \tcmd.git_cmd = 1;\n \tcmd.no_stderr = 1;\n@@ -476,7 +468,7 @@ static void check_non_tip(void)\n \tcmd.out = -1;\n \n \tif (start_command(&cmd))\n-\t\tgoto error;\n+\t\treturn 0;\n \n \t/*\n \t * If rev-list --stdin encounters an unknown commit, it\n@@ -495,16 +487,16 @@ static void check_non_tip(void)\n \t\t\tcontinue;\n \t\tmemcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n \t\tif (write_in_full(cmd.in, namebuf, 42) < 0)\n-\t\t\tgoto error;\n+\t\t\treturn 0;\n \t}\n \tnamebuf[40] = '\\n';\n-\tfor (i = 0; i < want_obj.nr; i++) {\n-\t\to = want_obj.objects[i].item;\n+\tfor (i = 0; i < src->nr; i++) {\n+\t\to = src->objects[i].item;\n \t\tif (is_our_ref(o))\n \t\t\tcontinue;\n \t\tmemcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);\n \t\tif (write_in_full(cmd.in, namebuf, 41) < 0)\n-\t\t\tgoto error;\n+\t\t\treturn 0;\n \t}\n \tclose(cmd.in);\n \n@@ -516,7 +508,7 @@ static void check_non_tip(void)\n \t */\n \ti = read_in_full(cmd.out, namebuf, 1);\n \tif (i)\n-\t\tgoto error;\n+\t\treturn 0;\n \tclose(cmd.out);\n \n \t/*\n@@ -525,15 +517,29 @@ static void check_non_tip(void)\n \t * even when it showed no commit.\n \t */\n \tif (finish_command(&cmd))\n-\t\tgoto error;\n+\t\treturn 0;\n \n \t/* All the non-tip ones are ancestors of what we advertised */\n-\treturn;\n+\treturn 1;\n+}\n+\n+static void check_non_tip(void)\n+{\n+\tint i;\n+\n+\t/*\n+\t * In the normal in-process case without\n+\t * uploadpack.allowReachableSHA1InWant,\n+\t * non-tip requests can never happen.\n+\t */\n+\tif (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))\n+\t\t;\t\t/* error */\n+\telse if (check_unreachable(&want_obj))\n+\t\treturn;\n \n-error:\n \t/* Pick one of them (we know there at least is one) */\n \tfor (i = 0; i < want_obj.nr; i++) {\n-\t\to = want_obj.objects[i].item;\n+\t\tstruct object *o = want_obj.objects[i].item;\n \t\tif (!is_our_ref(o))\n \t\t\tdie(\"git upload-pack: not our ref %s\",\n \t\t\t    oid_to_hex(&o->oid));\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277390","messageId":"1454576641-29615-12-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 11/25] fetch-pack: use skip_prefix() instead of starts_with() when possible","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:47Z","receivedAt":"2016-02-04T09:03:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fetch-pack.c | 13 +++++++------\n 1 file changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c\nindex 9b2a514..0482077 100644\n--- a/builtin/fetch-pack.c\n+++ b/builtin/fetch-pack.c\n@@ -58,13 +58,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \n \tfor (i = 1; i < argc && *argv[i] == '-'; i++) {\n \t\tconst char *arg = argv[i];\n+\t\tconst char *value;\n \n-\t\tif (starts_with(arg, \"--upload-pack=\")) {\n-\t\t\targs.uploadpack = arg + 14;\n+\t\tif (skip_prefix(arg, \"--upload-pack=\", &value)) {\n+\t\t\targs.uploadpack = value;\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=\", &value)) {\n+\t\t\targs.uploadpack = value;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(\"--quiet\", arg) || !strcmp(\"-q\", arg)) {\n@@ -100,8 +101,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=\", &value)) {\n+\t\t\targs.depth = strtol(value, NULL, 0);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(\"--no-progress\", arg)) {\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277393","messageId":"1454576641-29615-13-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 12/25] fetch-pack: use a common function for verbose printing","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:48Z","receivedAt":"2016-02-04T09:03:48Z","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..16917f9 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\");\n \t\tallow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;\n \t}\n \tif (!server_supports(\"thin-pack\"))\n@@ -851,17 +848,16 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \t\targs->no_progress = 0;\n \tif (!server_supports(\"include-tag\"))\n \t\targs->include_tag = 0;\n-\tif (server_supports(\"ofs-delta\")) {\n-\t\tif (args->verbose)\n-\t\t\tfprintf(stderr, \"Server supports ofs-delta\\n\");\n-\t} else\n+\tif (server_supports(\"ofs-delta\"))\n+\t\tprint_verbose(args, \"Server supports ofs-delta\");\n+\telse\n \t\tprefer_ofs_delta = 0;\n \n \tif ((agent_feature = server_feature_value(\"agent\", &agent_len))) {\n \t\tagent_supported = 1;\n-\t\tif (args->verbose && agent_len)\n-\t\t\tfprintf(stderr, \"Server version is %.*s\\n\",\n-\t\t\t\tagent_len, agent_feature);\n+\t\tif (agent_len)\n+\t\t\tprint_verbose(args, \"Server version is %.*s\",\n+\t\t\t\t      agent_len, agent_feature);\n \t}\n \n \tif (everything_local(args, &ref, sought, nr_sought)) {\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277396","messageId":"1454576641-29615-14-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 13/25] fetch-pack: use a separate flag for fetch in deepening mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:49Z","receivedAt":"2016-02-04T09:03:49Z","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 16917f9..e947514 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -197,7 +197,7 @@ enum ack_type {\n \n static void consume_shallow_list(struct fetch_pack_args *args, int fd)\n {\n-\tif (args->stateless_rpc && args->depth > 0) {\n+\tif (args->stateless_rpc && args->deepen) {\n \t\t/* If we sent a depth we will get back \"duplicate\"\n \t\t * shallow and unshallow commands every time there\n \t\t * is a block of have lines exchanged.\n@@ -348,7 +348,7 @@ static int find_common(struct fetch_pack_args *args,\n \tpacket_buf_flush(&req_buf);\n \tstate_len = req_buf.len;\n \n-\tif (args->depth > 0) {\n+\tif (args->deepen) {\n \t\tchar *line;\n \t\tconst char *arg;\n \t\tunsigned char sha1[20];\n@@ -557,7 +557,7 @@ static void filter_refs(struct fetch_pack_args *args,\n \t\t}\n \n \t\tif (!keep && args->fetch_all &&\n-\t\t    (!args->depth || !starts_with(ref->name, \"refs/tags/\")))\n+\t\t    (!args->deepen || !starts_with(ref->name, \"refs/tags/\")))\n \t\t\tkeep = 1;\n \n \t\tif (keep) {\n@@ -627,7 +627,7 @@ static int everything_local(struct fetch_pack_args *args,\n \t\t}\n \t}\n \n-\tif (!args->depth) {\n+\tif (!args->deepen) {\n \t\tfor_each_ref(mark_complete_oid, NULL);\n \t\tfor_each_alternate_ref(mark_alternate_complete, NULL);\n \t\tcommit_list_sort_by_date(&complete);\n@@ -813,6 +813,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \n \tif ((args->depth > 0 || is_repository_shallow()) && !server_supports(\"shallow\"))\n \t\tdie(\"Server does not support shallow clients\");\n+\tif (args->depth > 0)\n+\t\targs->deepen = 1;\n \tif (server_supports(\"multi_ack_detailed\")) {\n \t\tprint_verbose(args, \"Server supports multi_ack_detailed\");\n \t\tmulti_ack = 2;\n@@ -873,7 +875,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \n \tif (args->stateless_rpc)\n \t\tpacket_flush(fd[1]);\n-\tif (args->depth > 0)\n+\tif (args->deepen)\n \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n \t\t\t\t\tNULL);\n \telse if (si->nr_ours || si->nr_theirs)\n@@ -940,7 +942,7 @@ static void update_shallow(struct fetch_pack_args *args,\n \tint *status;\n \tint i;\n \n-\tif (args->depth > 0 && alternate_shallow_file) {\n+\tif (args->deepen && alternate_shallow_file) {\n \t\tif (*alternate_shallow_file == '\\0') { /* --unshallow */\n \t\t\tunlink_or_warn(git_path_shallow());\n \t\t\trollback_lock_file(&shallow_lock);\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex bb7fd76..4d0adb0 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -25,6 +25,7 @@ struct fetch_pack_args {\n \tunsigned self_contained_and_connected:1;\n \tunsigned cloning:1;\n \tunsigned update_shallow:1;\n+\tunsigned deepen:1;\n };\n \n /*\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277394","messageId":"1454576641-29615-15-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 14/25] shallow.c: implement a generic shallow boundary finder based on rev-list","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:50Z","receivedAt":"2016-02-04T09:03:50Z","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\nNote: if a commit has one NOT_SHALLOW parent and one SHALLOW parent,\nthen it's considered the boundary. Which means in the client side, this\ncommit has _no_ parents. This could lead to surprising cuts if we're not\ncareful.\n\nAnother option is to include more commits and only mark commits whose\nall parents are SHALLOW as boundary.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n commit.h  |  2 ++\n shallow.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 87 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..f5d5c1d 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,89 @@ 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->object.flags |= *(int *)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 rev_info revs;\n+\tunsigned int i, nr;\n+\n+\t/*\n+\t * SHALLOW (excluded) and NOT_SHALLOW (included) should not be\n+\t * set at this point. But better be safe than sorry.\n+\t */\n+\tnr = get_max_object_index();\n+\tfor (i = 0; i < nr; i++) {\n+\t\tstruct object *o = get_indexed_object(i);\n+\t\tif (!o || o->type != OBJ_COMMIT)\n+\t\t\tcontinue;\n+\t\to->flags &= ~(shallow_flag | not_shallow_flag);\n+\t}\n+\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+\t/* Mark all reachable commits as NOT_SHALLOW */\n+\tif (prepare_revision_walk(&revs))\n+\t\tdie(\"revision walk setup failed\");\n+\ttraverse_commit_list(&revs, show_commit, NULL, &not_shallow_flag);\n+\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+\tnr = get_max_object_index();\n+\tfor (i = 0; i < nr; i++) {\n+\t\tstruct object *o = get_indexed_object(i);\n+\t\tstruct commit *c = (struct commit *)o;\n+\n+\t\tif (!o || o->type != OBJ_COMMIT ||\n+\t\t    !(o->flags & not_shallow_flag))\n+\t\t\tcontinue;\n+\n+\t\tif (parse_commit(c))\n+\t\t\tdie(\"unable to parse commit %s\",\n+\t\t\t    oid_to_hex(&c->object.oid));\n+\n+\t\tfor (p = c->parents; p; p = p->next)\n+\t\t\tif (!(p->item->object.flags & not_shallow_flag)) {\n+\t\t\t\to->flags |= shallow_flag;\n+\t\t\t\tcommit_list_insert(c, &result);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t}\n+\n+\t/*\n+\t * Now we can clean up NOT_SHALLOW on border commits. Having\n+\t * both flags set can confuse the caller.\n+\t */\n+\tfor (p = result; p; p = p->next) {\n+\t\tstruct object *ro = &p->item->object;\n+\t\tif ((ro->flags & not_shallow_flag) &&\n+\t\t    (ro->flags & shallow_flag))\n+\t\t\tro->flags &= ~not_shallow_flag;\n+\t}\n+\treturn result;\n+}\n+\n static void check_shallow_file_for_update(void)\n {\n \tif (is_shallow == -1)\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277395","messageId":"1454576641-29615-16-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 15/25] upload-pack: add deepen-since to cut shallow repos based on time","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:51Z","receivedAt":"2016-02-04T09:03:51Z","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..794736c 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(\"--depth and --shallow-since cannot be used together\");\n \tif (depth > 0)\n \t\tdeepen(depth, &shallows);\n+\telse if (deepen_rev_list) {\n+\t\tstruct argv_array av = ARGV_ARRAY_INIT;\n+\t\tint i;\n+\n+\t\targv_array_push(&av, \"rev-list\");\n+\t\tif (deepen_since)\n+\t\t\targv_array_pushf(&av, \"--max-age=%lu\", deepen_since);\n+\t\tfor (i = 0; i < want_obj.nr; i++) {\n+\t\t\tstruct object *o = want_obj.objects[i].item;\n+\t\t\targv_array_push(&av, oid_to_hex(&o->oid));\n+\t\t}\n+\t\tdeepen_by_rev_list(av.argc, av.argv, &shallows);\n+\t\targv_array_clear(&av);\n+\t}\n \telse\n \t\tif (shallows.nr > 0) {\n \t\t\tint i;\n@@ -756,7 +797,7 @@ static int send_ref(const char *refname, const struct object_id *oid,\n \t\t    int flag, void *cb_data)\n {\n \tstatic const char *capabilities = \"multi_ack thin-pack side-band\"\n-\t\t\" side-band-64k ofs-delta shallow no-progress\"\n+\t\t\" side-band-64k ofs-delta shallow deepen-since no-progress\"\n \t\t\" include-tag multi_ack_detailed\";\n \tconst char *refname_nons = strip_namespace(refname);\n \tstruct object_id peeled;\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277406","messageId":"1454576641-29615-17-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 16/25] fetch: define shallow boundary with --shallow-since","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:52Z","receivedAt":"2016-02-04T09:03: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 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                     | 12 ++++++++++--\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, 54 insertions(+), 5 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 0482077..192c1ae 100644\n--- a/builtin/fetch-pack.c\n+++ b/builtin/fetch-pack.c\n@@ -105,6 +105,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \t\t\targs.depth = strtol(value, NULL, 0);\n \t\t\tcontinue;\n \t\t}\n+\t\tif (skip_prefix(arg, \"--shallow-since=\", &value)) {\n+\t\t\targs.deepen_since = xstrdup(value);\n+\t\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..7d4d082 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@@ -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_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@@ -884,6 +889,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_SINCE, NULL);\n \tfetch_refs(transport, ref_map);\n \n \tif (gsecondary) {\n@@ -1167,6 +1173,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 e947514..db23998 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -21,6 +21,7 @@ static int fetch_unpack_limit = -1;\n static int unpack_limit = 100;\n static int prefer_ofs_delta = 1;\n static int no_done;\n+static int deepen_since_ok;\n static int fetch_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n static int agent_supported;\n@@ -326,6 +327,7 @@ static int find_common(struct fetch_pack_args *args,\n \t\t\tif (args->no_progress)   strbuf_addstr(&c, \" no-progress\");\n \t\t\tif (args->include_tag)   strbuf_addstr(&c, \" include-tag\");\n \t\t\tif (prefer_ofs_delta)   strbuf_addstr(&c, \" ofs-delta\");\n+\t\t\tif (deepen_since_ok)    strbuf_addstr(&c, \" deepen-since\");\n \t\t\tif (agent_supported)    strbuf_addf(&c, \" agent=%s\",\n \t\t\t\t\t\t\t    git_user_agent_sanitized());\n \t\t\tpacket_buf_write(&req_buf, \"want %s%s\\n\", remote_hex, c.buf);\n@@ -345,6 +347,10 @@ static int find_common(struct fetch_pack_args *args,\n \t\twrite_shallow_commits(&req_buf, 1, NULL);\n \tif (args->depth > 0)\n \t\tpacket_buf_write(&req_buf, \"deepen %d\", args->depth);\n+\tif (args->deepen_since) {\n+\t\tunsigned long max_age = approxidate(args->deepen_since);\n+\t\tpacket_buf_write(&req_buf, \"deepen-since %lu\", max_age);\n+\t}\n \tpacket_buf_flush(&req_buf);\n \tstate_len = req_buf.len;\n \n@@ -813,7 +819,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \n \tif ((args->depth > 0 || is_repository_shallow()) && !server_supports(\"shallow\"))\n \t\tdie(\"Server does not support shallow clients\");\n-\tif (args->depth > 0)\n+\tif (args->depth > 0 || args->deepen_since)\n \t\targs->deepen = 1;\n \tif (server_supports(\"multi_ack_detailed\")) {\n \t\tprint_verbose(args, \"Server supports multi_ack_detailed\");\n@@ -861,6 +867,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \t\t\tprint_verbose(args, \"Server version is %.*s\",\n \t\t\t\t      agent_len, agent_feature);\n \t}\n+\tif (server_supports(\"deepen-since\"))\n+\t\tdeepen_since_ok = 1;\n+\telse if (args->deepen_since)\n+\t\tdie(\"Server does not support --shallow-since\");\n \n \tif (everything_local(args, &ref, sought, nr_sought)) {\n \t\tpacket_flush(fd[1]);\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex 4d0adb0..f7eadb2 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -10,6 +10,7 @@ struct fetch_pack_args {\n \tconst char *uploadpack;\n \tint unpacklimit;\n \tint depth;\n+\tconst char *deepen_since;\n \tunsigned quiet:1;\n \tunsigned keep_pack:1;\n \tunsigned lock_pack:1;\ndiff --git a/remote-curl.c b/remote-curl.c\nindex 3ac5b6c..ce30961 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@@ -750,6 +755,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 67f3666..4902036 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -477,6 +477,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@@ -530,6 +533,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \targs.quiet = (transport->verbose < 0);\n \targs.no_progress = !transport->progress;\n \targs.depth = data->options.depth;\n+\targs.deepen_since = data->options.deepen_since;\n \targs.check_self_contained_and_connected =\n \t\tdata->options.check_self_contained_and_connected;\n \targs.cloning = transport->cloning;\ndiff --git a/transport.h b/transport.h\nindex 8ebaaf2..9c10a44 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -13,6 +13,7 @@ struct git_transport_options {\n \tunsigned self_contained_and_connected : 1;\n \tunsigned update_shallow : 1;\n \tint depth;\n+\tconst char *deepen_since;\n \tconst char *uploadpack;\n \tconst char *receivepack;\n \tstruct push_cas_option *cas;\n@@ -171,6 +172,9 @@ int transport_restrict_protocols(void);\n /* Limit the depth of the fetch if not null */\n #define TRANS_OPT_DEPTH \"depth\"\n \n+/* Limit the depth of the fetch based on time if not null */\n+#define TRANS_OPT_DEEPEN_SINCE \"deepen-since\"\n+\n /* Aggressively fetch annotated tags if possible */\n #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n \n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277397","messageId":"1454576641-29615-18-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 17/25] clone: define shallow clone boundary based on time with --shallow-since","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:53Z","receivedAt":"2016-02-04T09:03:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/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 789b668..1b6b639 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -194,6 +194,9 @@ objects from the source repository into a pack in the cloned repository.\n \t`--no-single-branch` is given to fetch the histories near the\n \ttips of all branches.\n \n+--shallow-since=<date>::\n+\tCreate a shallow clone with a history after the specified time.\n+\n --[no-]single-branch::\n \tClone only the history leading to the tip of a single branch,\n \teither specified by the `--branch` option or the primary\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex bcba080..dc2ef4f 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -40,7 +40,8 @@ static const char * const builtin_clone_usage[] = {\n \n static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;\n static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;\n-static char *option_template, *option_depth;\n+static int deepen;\n+static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n static char *option_branch = NULL;\n static const char *real_git_dir;\n@@ -86,6 +87,8 @@ static struct option builtin_clone_options[] = {\n \t\t   N_(\"path to git-upload-pack on the remote\")),\n \tOPT_STRING(0, \"depth\", &option_depth, N_(\"depth\"),\n \t\t    N_(\"create a shallow clone of that depth\")),\n+\tOPT_STRING(0, \"shallow-since\", &option_since, N_(\"time\"),\n+\t\t    N_(\"create a shallow clone since a specific time\")),\n \tOPT_BOOL(0, \"single-branch\", &option_single_branch,\n \t\t    N_(\"clone only one branch, HEAD or --branch\")),\n \tOPT_STRING(0, \"separate-git-dir\", &real_git_dir, N_(\"gitdir\"),\n@@ -849,8 +852,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n \t\t\tbuiltin_clone_usage, builtin_clone_options);\n \n+\tif (option_depth || option_since)\n+\t\tdeepen = 1;\n \tif (option_single_branch == -1)\n-\t\toption_single_branch = option_depth ? 1 : 0;\n+\t\toption_single_branch = deepen ? 1 : 0;\n \n \tif (option_mirror)\n \t\toption_bare = 1;\n@@ -976,6 +981,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tif (is_local) {\n \t\tif (option_depth)\n \t\t\twarning(_(\"--depth is ignored in local clones; use file:// instead.\"));\n+\t\tif (option_since)\n+\t\t\twarning(_(\"--shallow-since is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n@@ -994,6 +1001,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tif (option_depth)\n \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n \t\t\t\t     option_depth);\n+\tif (option_since)\n+\t\ttransport_set_option(transport, TRANS_OPT_DEEPEN_SINCE,\n+\t\t\t\t     option_since);\n \tif (option_single_branch)\n \t\ttransport_set_option(transport, TRANS_OPT_FOLLOWTAGS, \"1\");\n \n@@ -1001,7 +1011,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\ttransport_set_option(transport, TRANS_OPT_UPLOADPACK,\n \t\t\t\t     option_upload_pack);\n \n-\tif (transport->smart_options && !option_depth)\n+\tif (transport->smart_options && !deepen)\n \t\ttransport->smart_options->check_self_contained_and_connected = 1;\n \n \trefs = transport_get_remote_refs(transport);\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277398","messageId":"1454576641-29615-19-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 18/25] t5500, t5539: tests for shallow depth since a specific date","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:54Z","receivedAt":"2016-02-04T09:03: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 t/t5500-fetch-pack.sh         | 22 ++++++++++++++++++++++\n t/t5539-fetch-http-shallow.sh | 24 ++++++++++++++++++++++++\n 2 files changed, 46 insertions(+)\n\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex e5f83bf..453c571 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -637,4 +637,26 @@ 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+\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 37a4335..2a96ab3 100755\n--- a/t/t5539-fetch-http-shallow.sh\n+++ b/t/t5539-fetch-http-shallow.sh\n@@ -73,5 +73,29 @@ 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+\techo three >expected &&\n+\techo two  >>expected &&\n+\ttest_cmp expected actual\n+'\n+\n+\n stop_httpd\n test_done\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277399","messageId":"1454576641-29615-20-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 19/25] refs: add expand_ref()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:55Z","receivedAt":"2016-02-04T09:03:55Z","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.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n refs.c | 8 +++++++-\n refs.h | 1 +\n 2 files changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git a/refs.c b/refs.c\nindex e2d34b2..842e4d8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -392,6 +392,13 @@ static char *substitute_branch_name(const char **string, int *len)\n int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)\n {\n \tchar *last_branch = substitute_branch_name(&str, &len);\n+\tint   refs_found  = expand_ref(str, len, sha1, ref);\n+\tfree(last_branch);\n+\treturn refs_found;\n+}\n+\n+int expand_ref(const char *str, int len, unsigned char *sha1, char **ref)\n+{\n \tconst char **p, *r;\n \tint refs_found = 0;\n \n@@ -417,7 +424,6 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)\n \t\t\twarning(\"ignoring broken ref %s.\", fullref);\n \t\t}\n \t}\n-\tfree(last_branch);\n \treturn refs_found;\n }\n \ndiff --git a/refs.h b/refs.h\nindex 3c3da29..31a2fa6 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -90,6 +90,7 @@ extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned c\n  */\n extern int refname_match(const char *abbrev_name, const char *full_name);\n \n+extern int expand_ref(const char *str, int len, unsigned char *sha1, char **ref);\n extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);\n extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);\n \n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277400","messageId":"1454576641-29615-21-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 20/25] upload-pack: support define shallow boundary by excluding revisions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:56Z","receivedAt":"2016-02-04T09:03:56Z","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\ndeepen-not cannot be used with deepen the same way deepen-since cannot\nbe used with deepen. But deepen-not can be mixed with deepen-since. The\nresult is exactly how you do the command \"git rev-list --since=... --not\nref\".\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                                     | 22 ++++++++++++++++++++--\n 3 files changed, 31 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 794736c..a72ffc2 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(\"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(\"--depth and --shallow-since cannot be used together\");\n+\t\tdie(\"--depth and --shallow-since (or --shallow-exclude) cannot be used together\");\n \tif (depth > 0)\n \t\tdeepen(depth, &shallows);\n \telse if (deepen_rev_list) {\n@@ -746,6 +757,13 @@ static void receive_needs(void)\n \t\t\tstruct object *o = want_obj.objects[i].item;\n \t\t\targv_array_push(&av, oid_to_hex(&o->oid));\n \t\t}\n+\t\tif (deepen_not.nr) {\n+\t\t\targv_array_push(&av, \"--not\");\n+\t\t\tfor (i = 0; i < deepen_not.nr; i++) {\n+\t\t\t\tstruct string_list_item *s = deepen_not.items + i;\n+\t\t\t\targv_array_push(&av, s->string);\n+\t\t\t}\n+\t\t}\n \t\tdeepen_by_rev_list(av.argc, av.argv, &shallows);\n \t\targv_array_clear(&av);\n \t}\n@@ -797,7 +815,7 @@ static int send_ref(const char *refname, const struct object_id *oid,\n \t\t    int flag, void *cb_data)\n {\n \tstatic const char *capabilities = \"multi_ack thin-pack side-band\"\n-\t\t\" side-band-64k ofs-delta shallow deepen-since no-progress\"\n+\t\t\" side-band-64k ofs-delta shallow deepen-since deepen-not no-progress\"\n \t\t\" include-tag multi_ack_detailed\";\n \tconst char *refname_nons = strip_namespace(refname);\n \tstruct object_id peeled;\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277402","messageId":"1454576641-29615-22-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 21/25] fetch: define shallow boundary with --shallow-exclude","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:57Z","receivedAt":"2016-02-04T09:03:57Z","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                | 10 ++++++++++\n builtin/fetch.c                     | 17 ++++++++++++++++-\n fetch-pack.c                        | 15 ++++++++++++++-\n fetch-pack.h                        |  1 +\n remote-curl.c                       |  9 +++++++++\n transport-helper.c                  | 27 +++++++++++++++++++++++++++\n transport.c                         |  4 ++++\n transport.h                         |  6 ++++++\n 11 files changed, 101 insertions(+), 2 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 192c1ae..1a49184 100644\n--- a/builtin/fetch-pack.c\n+++ b/builtin/fetch-pack.c\n@@ -109,6 +109,16 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \t\t\targs.deepen_since = xstrdup(value);\n \t\t\tcontinue;\n \t\t}\n+\t\tif (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n+\t\t\tstatic struct string_list *deepen_not;\n+\t\t\tif (!deepen_not) {\n+\t\t\t\tdeepen_not = xmalloc(sizeof(*deepen_not));\n+\t\t\t\tstring_list_init(deepen_not, 1);\n+\t\t\t\targs.deepen_not = deepen_not;\n+\t\t\t}\n+\t\t\tstring_list_append(deepen_not, value);\n+\t\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 7d4d082..11b444b 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -41,6 +41,7 @@ static int max_children = 1;\n static const char *depth;\n static const char *deepen_since;\n static const char *upload_pack;\n+struct string_list deepen_not = STRING_LIST_INIT_NODUP;\n static struct strbuf default_rla = STRBUF_INIT;\n static struct transport *gtransport;\n static struct transport *gsecondary;\n@@ -50,6 +51,13 @@ static int shown_url = 0;\n static int refmap_alloc, refmap_nr;\n static const char **refmap_array;\n \n+static int option_parse_deepen_not(const struct option *opt,\n+\t\t\t\t   const char *arg, int unset)\n+{\n+\tstring_list_append(&deepen_not, arg);\n+\treturn 0;\n+}\n+\n static int option_parse_recurse_submodules(const struct option *opt,\n \t\t\t\t   const char *arg, int unset)\n {\n@@ -118,6 +126,9 @@ static struct option builtin_fetch_options[] = {\n \t\t   N_(\"deepen history of shallow clone\")),\n \tOPT_STRING(0, \"shallow-since\", &deepen_since, N_(\"time\"),\n \t\t   N_(\"deepen history of shallow repository based on time\")),\n+\t{ OPTION_CALLBACK, 0, \"shallow-exclude\", NULL, N_(\"revision\"),\n+\t\t    N_(\"deepen history of shallow clone by excluding rev\"),\n+\t\t    PARSE_OPT_NONEG, option_parse_deepen_not },\n \t{ OPTION_SET_INT, 0, \"unshallow\", &unshallow, NULL,\n \t\t   N_(\"convert to a complete repository\"),\n \t\t   PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },\n@@ -875,6 +886,9 @@ static struct transport *prepare_transport(struct remote *remote)\n \t\tset_option(transport, TRANS_OPT_DEPTH, depth);\n \tif (deepen_since)\n \t\tset_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since);\n+\tif (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@@ -890,6 +904,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)\n \ttransport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);\n \ttransport_set_option(transport, TRANS_OPT_DEPTH, \"0\");\n \ttransport_set_option(transport, TRANS_OPT_DEEPEN_SINCE, NULL);\n+\ttransport_set_option(transport, TRANS_OPT_DEEPEN_NOT, NULL);\n \tfetch_refs(transport, ref_map);\n \n \tif (gsecondary) {\n@@ -1173,7 +1188,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 db23998..b3f1775 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -22,6 +22,7 @@ static int unpack_limit = 100;\n static int prefer_ofs_delta = 1;\n static int no_done;\n static int deepen_since_ok;\n+static int deepen_not_ok;\n static int fetch_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n static int agent_supported;\n@@ -328,6 +329,7 @@ static int find_common(struct fetch_pack_args *args,\n \t\t\tif (args->include_tag)   strbuf_addstr(&c, \" include-tag\");\n \t\t\tif (prefer_ofs_delta)   strbuf_addstr(&c, \" ofs-delta\");\n \t\t\tif (deepen_since_ok)    strbuf_addstr(&c, \" deepen-since\");\n+\t\t\tif (deepen_not_ok)      strbuf_addstr(&c, \" deepen-not\");\n \t\t\tif (agent_supported)    strbuf_addf(&c, \" agent=%s\",\n \t\t\t\t\t\t\t    git_user_agent_sanitized());\n \t\t\tpacket_buf_write(&req_buf, \"want %s%s\\n\", remote_hex, c.buf);\n@@ -351,6 +353,13 @@ static int find_common(struct fetch_pack_args *args,\n \t\tunsigned long max_age = approxidate(args->deepen_since);\n \t\tpacket_buf_write(&req_buf, \"deepen-since %lu\", max_age);\n \t}\n+\tif (args->deepen_not) {\n+\t\tint i;\n+\t\tfor (i = 0; i < args->deepen_not->nr; i++) {\n+\t\t\tstruct string_list_item *s = args->deepen_not->items + i;\n+\t\t\tpacket_buf_write(&req_buf, \"deepen-not %s\", s->string);\n+\t\t}\n+\t}\n \tpacket_buf_flush(&req_buf);\n \tstate_len = req_buf.len;\n \n@@ -819,7 +828,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \n \tif ((args->depth > 0 || is_repository_shallow()) && !server_supports(\"shallow\"))\n \t\tdie(\"Server does not support shallow clients\");\n-\tif (args->depth > 0 || args->deepen_since)\n+\tif (args->depth > 0 || args->deepen_since || args->deepen_not)\n \t\targs->deepen = 1;\n \tif (server_supports(\"multi_ack_detailed\")) {\n \t\tprint_verbose(args, \"Server supports multi_ack_detailed\");\n@@ -871,6 +880,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \t\tdeepen_since_ok = 1;\n \telse if (args->deepen_since)\n \t\tdie(\"Server does not support --shallow-since\");\n+\tif (server_supports(\"deepen-not\"))\n+\t\tdeepen_not_ok = 1;\n+\telse if (args->deepen_not)\n+\t\tdie(\"Server does not support --shallow-exclude\");\n \n \tif (everything_local(args, &ref, sought, nr_sought)) {\n \t\tpacket_flush(fd[1]);\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex f7eadb2..144301f 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -11,6 +11,7 @@ struct fetch_pack_args {\n \tint unpacklimit;\n \tint depth;\n \tconst char *deepen_since;\n+\tconst struct string_list *deepen_not;\n \tunsigned quiet:1;\n \tunsigned keep_pack:1;\n \tunsigned lock_pack:1;\ndiff --git a/remote-curl.c b/remote-curl.c\nindex ce30961..fd80f7e 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@@ -757,6 +762,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@@ -978,6 +986,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 2e78c4d..cebb71b 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -282,6 +282,29 @@ 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+\tif (!list)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i < list->nr; i++) {\n+\t\tstrbuf_addf(&buf, \"option %s \", name);\n+\t\tquote_c_style(list->items[i].string, &buf, NULL, 0);\n+\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 +317,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 4902036..3094c6b 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -480,6 +480,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@@ -534,6 +537,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \targs.no_progress = !transport->progress;\n \targs.depth = data->options.depth;\n \targs.deepen_since = data->options.deepen_since;\n+\targs.deepen_not = data->options.deepen_not;\n \targs.check_self_contained_and_connected =\n \t\tdata->options.check_self_contained_and_connected;\n \targs.cloning = transport->cloning;\ndiff --git a/transport.h b/transport.h\nindex 9c10a44..ab61932 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -5,6 +5,8 @@\n #include \"run-command.h\"\n #include \"remote.h\"\n \n+struct string_list;\n+\n struct git_transport_options {\n \tunsigned thin : 1;\n \tunsigned keep : 1;\n@@ -14,6 +16,7 @@ struct git_transport_options {\n \tunsigned update_shallow : 1;\n \tint depth;\n \tconst char *deepen_since;\n+\tconst struct string_list *deepen_not;\n \tconst char *uploadpack;\n \tconst char *receivepack;\n \tstruct push_cas_option *cas;\n@@ -175,6 +178,9 @@ int transport_restrict_protocols(void);\n /* Limit the depth of the fetch based on time if not null */\n #define TRANS_OPT_DEEPEN_SINCE \"deepen-since\"\n \n+/* Limit the depth of the fetch based on revs if not null */\n+#define TRANS_OPT_DEEPEN_NOT \"deepen-not\"\n+\n /* Aggressively fetch annotated tags if possible */\n #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n \n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277401","messageId":"1454576641-29615-23-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 22/25] clone: define shallow clone boundary with --shallow-exclude","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:58Z","receivedAt":"2016-02-04T09:03:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/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 1b6b639..31e1610 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -197,6 +197,11 @@ objects from the source repository into a pack in the cloned repository.\n --shallow-since=<date>::\n \tCreate a shallow clone with a history after the specified time.\n \n+--shallow-exclude=<revision>::\n+\tCreate a shallow clone with a history, excluding commits\n+\treachable from a specified remote branch or tag.  This option\n+\tcan be specified multiple times.\n+\n --[no-]single-branch::\n \tClone only the history leading to the tip of a single branch,\n \teither specified by the `--branch` option or the primary\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex dc2ef4f..5ccf6b7 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -44,6 +44,7 @@ static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n static char *option_branch = NULL;\n+static struct string_list option_not = STRING_LIST_INIT_NODUP;\n static const char *real_git_dir;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbosity;\n@@ -52,6 +53,13 @@ static struct string_list option_config;\n static struct string_list option_reference;\n static int option_dissociate;\n \n+static int option_parse_deepen_not(const struct option *opt,\n+\t\t\t\t   const char *arg, int unset)\n+{\n+\tstring_list_append(&option_not, arg);\n+\treturn 0;\n+}\n+\n static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n@@ -89,6 +97,9 @@ static struct option builtin_clone_options[] = {\n \t\t    N_(\"create a shallow clone of that depth\")),\n \tOPT_STRING(0, \"shallow-since\", &option_since, N_(\"time\"),\n \t\t    N_(\"create a shallow clone since a specific time\")),\n+\t{ OPTION_CALLBACK, 0, \"shallow-exclude\", NULL, N_(\"revision\"),\n+\t\t    N_(\"deepen history of shallow clone by excluding rev\"),\n+\t\t    PARSE_OPT_NONEG, option_parse_deepen_not },\n \tOPT_BOOL(0, \"single-branch\", &option_single_branch,\n \t\t    N_(\"clone only one branch, HEAD or --branch\")),\n \tOPT_STRING(0, \"separate-git-dir\", &real_git_dir, N_(\"gitdir\"),\n@@ -852,7 +863,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tusage_msg_opt(_(\"You must specify a repository to clone.\"),\n \t\t\tbuiltin_clone_usage, builtin_clone_options);\n \n-\tif (option_depth || option_since)\n+\tif (option_depth || option_since || option_not.nr)\n \t\tdeepen = 1;\n \tif (option_single_branch == -1)\n \t\toption_single_branch = deepen ? 1 : 0;\n@@ -983,6 +994,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\twarning(_(\"--depth is ignored in local clones; use file:// instead.\"));\n \t\tif (option_since)\n \t\t\twarning(_(\"--shallow-since is ignored in local clones; use file:// instead.\"));\n+\t\tif (option_not.nr)\n+\t\t\twarning(_(\"--shallow-exclude is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n@@ -1004,6 +1017,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tif (option_since)\n \t\ttransport_set_option(transport, TRANS_OPT_DEEPEN_SINCE,\n \t\t\t\t     option_since);\n+\tif (option_not.nr)\n+\t\ttransport_set_option(transport, TRANS_OPT_DEEPEN_NOT,\n+\t\t\t\t     (const char *)&option_not);\n \tif (option_single_branch)\n \t\ttransport_set_option(transport, TRANS_OPT_FOLLOWTAGS, \"1\");\n \n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277403","messageId":"1454576641-29615-24-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 23/25] t5500, t5539: tests for shallow depth excluding a ref","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:03:59Z","receivedAt":"2016-02-04T09:03:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n 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 453c571..53de68c 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -659,4 +659,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 2a96ab3..95eaaff 100755\n--- a/t/t5539-fetch-http-shallow.sh\n+++ b/t/t5539-fetch-http-shallow.sh\n@@ -96,6 +96,28 @@ test_expect_success 'fetch shallow since ...' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'shallow clone exclude tag two' '\n+\ttest_create_repo shallow-exclude &&\n+\t(\n+\tcd shallow-exclude &&\n+\ttest_commit one &&\n+\ttest_commit two &&\n+\ttest_commit three &&\n+\tmv .git \"$HTTPD_DOCUMENT_ROOT_PATH/shallow-exclude.git\" &&\n+\tgit clone --shallow-exclude two $HTTPD_URL/smart/shallow-exclude.git ../shallow12 &&\n+\tgit -C ../shallow12 log --pretty=tformat:%s HEAD >actual &&\n+\techo three >expected &&\n+\ttest_cmp expected actual\n+\t)\n+'\n+\n+test_expect_success 'fetch exclude tag one' '\n+\tgit -C shallow12 fetch --shallow-exclude one origin &&\n+\tgit -C shallow12 log --pretty=tformat:%s origin/master >actual &&\n+\techo three >expected &&\n+\techo two  >>expected &&\n+\ttest_cmp expected actual\n+'\n \n stop_httpd\n test_done\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277404","messageId":"1454576641-29615-25-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 24/25] upload-pack: make check_reachable_object() return unreachable list if asked","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:04:00Z","receivedAt":"2016-02-04T09:04: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 object.h      |  2 +-\n upload-pack.c | 51 ++++++++++++++++++++++++++++++++++++++++++++-------\n 2 files changed, 45 insertions(+), 8 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 a72ffc2..1270aa3 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -451,8 +451,16 @@ static int is_our_ref(struct object *o)\n \t\t\t(ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));\n \treturn o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);\n }\n-\n-static int check_unreachable(struct object_array *src)\n+/*\n+ * If reachable is NULL, return 1 if there is no unreachable object,\n+ * zero otherwise.\n+ *\n+ * If reachable is not NULL, it's filled with reachable objects.\n+ * Return value is irrelevant. The caller has to compare reachable and\n+ * src to find out if there's any unreachable object.\n+ */\n+static int check_unreachable(struct object_array *reachable,\n+\t\t\t     struct object_array *src)\n {\n \tstatic const char *argv[] = {\n \t\t\"rev-list\", \"--stdin\", NULL,\n@@ -484,6 +492,8 @@ static int check_unreachable(struct object_array *src)\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 +503,13 @@ static int check_unreachable(struct object_array *src)\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 0;\n@@ -507,9 +522,31 @@ static int check_unreachable(struct object_array *src)\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-\tif (i)\n-\t\treturn 0;\n+\tif (!reachable) {\n+\t\ti = read_in_full(cmd.out, namebuf, 1);\n+\t\tif (i)\n+\t\t\treturn 0;\n+\t} else {\n+\t\twhile ((i = read_in_full(cmd.out, namebuf, 41)) == 41) {\n+\t\t\tstruct object_id sha1;\n+\n+\t\t\tif (namebuf[40] != '\\n' || get_oid_hex(namebuf, &sha1))\n+\t\t\t\tbreak;\n+\n+\t\t\to = lookup_object(sha1.hash);\n+\t\t\tif (o && o->type == OBJ_COMMIT) {\n+\t\t\t\to->flags &= ~TMP_MARK;\n+\t\t\t}\n+\t\t}\n+\t\tfor (i = get_max_object_index(); 0 < i; ) {\n+\t\t\to = get_indexed_object(--i);\n+\t\t\tif (o && o->type == OBJ_COMMIT &&\n+\t\t\t    (o->flags & TMP_MARK)) {\n+\t\t\t\tadd_object_array(o, NULL, reachable);\n+\t\t\t\to->flags &= ~TMP_MARK;\n+\t\t\t}\n+\t\t}\n+\t}\n \tclose(cmd.out);\n \n \t/*\n@@ -535,7 +572,7 @@ static void check_non_tip(void)\n \t */\n \tif (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))\n \t\t;\t\t/* error */\n-\telse if (check_unreachable(&want_obj))\n+\telse if (check_unreachable(NULL, &want_obj))\n \t\treturn;\n \n \t/* Pick one of them (we know there at least is one) */\n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277405","messageId":"1454576641-29615-26-git-send-email-pclouds@gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 25/25] fetch, upload-pack: --deepen=N extends shallow boundary by N commits","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-04T09:04:01Z","receivedAt":"2016-02-04T09:04:01Z","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                                       |  3 +++\n transport.h                                       |  4 ++++\n upload-pack.c                                     | 21 +++++++++++++-----\n 15 files changed, 128 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 1a49184..67a13af 100644\n--- a/builtin/fetch-pack.c\n+++ b/builtin/fetch-pack.c\n@@ -119,6 +119,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n \t\t\tstring_list_append(deepen_not, value);\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 11b444b..c5f0e9e 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)\n \tif (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@@ -905,6 +909,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)\n \ttransport_set_option(transport, TRANS_OPT_DEPTH, \"0\");\n \ttransport_set_option(transport, TRANS_OPT_DEEPEN_SINCE, NULL);\n \ttransport_set_option(transport, TRANS_OPT_DEEPEN_NOT, NULL);\n+\ttransport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);\n \tfetch_refs(transport, ref_map);\n \n \tif (gsecondary) {\n@@ -1176,6 +1181,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 b3f1775..69a99e5 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -324,6 +324,7 @@ static int find_common(struct fetch_pack_args *args,\n \t\t\tif (no_done)            strbuf_addstr(&c, \" no-done\");\n \t\t\tif (use_sideband == 2)  strbuf_addstr(&c, \" side-band-64k\");\n \t\t\tif (use_sideband == 1)  strbuf_addstr(&c, \" side-band\");\n+\t\t\tif (args->deepen_relative) strbuf_addstr(&c, \" deepen-relative\");\n \t\t\tif (args->use_thin_pack) strbuf_addstr(&c, \" thin-pack\");\n \t\t\tif (args->no_progress)   strbuf_addstr(&c, \" no-progress\");\n \t\t\tif (args->include_tag)   strbuf_addstr(&c, \" include-tag\");\n@@ -884,6 +885,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \t\tdeepen_not_ok = 1;\n \telse if (args->deepen_not)\n \t\tdie(\"Server does not support --shallow-exclude\");\n+\tif (!server_supports(\"deepen-relative\") && args->deepen_relative)\n+\t\tdie(\"Server does not support --deepen\");\n \n \tif (everything_local(args, &ref, sought, nr_sought)) {\n \t\tpacket_flush(fd[1]);\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex 144301f..c912e3d 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -12,6 +12,7 @@ struct fetch_pack_args {\n \tint depth;\n \tconst char *deepen_since;\n \tconst struct string_list *deepen_not;\n+\tunsigned deepen_relative:1;\n \tunsigned quiet:1;\n \tunsigned keep_pack:1;\n \tunsigned lock_pack:1;\ndiff --git a/remote-curl.c b/remote-curl.c\nindex fd80f7e..ea06c5d 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@@ -765,6 +775,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 53de68c..ed8d18c 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -681,4 +681,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 95eaaff..06389eb 100755\n--- a/t/t5539-fetch-http-shallow.sh\n+++ b/t/t5539-fetch-http-shallow.sh\n@@ -119,5 +119,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 cebb71b..3c1ae6f 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 3094c6b..2f3823a 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -482,6 +482,8 @@ static int set_git_option(struct git_transport_options *opts,\n \t\treturn 0;\n \t} else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) {\n \t\topts->deepen_not = (const struct string_list *)value;\n+\t} else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {\n+\t\topts->deepen_relative = !!value;\n \t\treturn 0;\n \t}\n \treturn 1;\n@@ -538,6 +540,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 1270aa3..f05971e 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@@ -632,7 +633,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@@ -641,7 +643,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\t(void)check_unreachable(&reachable_shallows, 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@@ -733,6 +742,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@@ -782,7 +793,7 @@ static void receive_needs(void)\n \tif (depth > 0 && deepen_rev_list)\n \t\tdie(\"--depth and --shallow-since (or --shallow-exclude) 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@@ -852,8 +863,8 @@ static int send_ref(const char *refname, const struct object_id *oid,\n \t\t    int flag, void *cb_data)\n {\n \tstatic const char *capabilities = \"multi_ack thin-pack side-band\"\n-\t\t\" side-band-64k ofs-delta shallow deepen-since deepen-not no-progress\"\n-\t\t\" include-tag multi_ack_detailed\";\n+\t\t\" side-band-64k ofs-delta shallow deepen-since deepen-not\"\n+\t\t\" deepen-relative no-progress include-tag multi_ack_detailed\";\n \tconst char *refname_nons = strip_namespace(refname);\n \tstruct object_id peeled;\n \n-- \n2.7.0.377.g4cd97dd\n"},{"id":"277481","messageId":"xmqqpowcf5et.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 01/25] remote-curl.c: convert fetch_git() to use argv_array","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-04T22:59:38Z","receivedAt":"2016-02-04T22:59:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n\nYay, it's about time ;-)\n\n> diff --git a/remote-curl.c b/remote-curl.c\n> index c704857..3ac5b6c 100644\n> --- a/remote-curl.c\n> +++ b/remote-curl.c\n> @@ -726,37 +726,31 @@ 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\",\n> +\t\t\t NULL);\n\nA lone NULL after a reasonably short line looks somewhat irritating,\nbut if you are planning to add more on that short line in the later\npatch, it is OK.  Let's keep reading.\n\nThe remainder of this patch looked obviously correct.\n\nThanks.\n"},{"id":"277483","messageId":"xmqqlh70f4jx.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 02/25] transport-helper.c: refactor set_helper_option()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-04T23:18:10Z","receivedAt":"2016-02-04T23:18:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nJust like other patches, this is thin on the rationale.  You are\nplanning to invent another function that prepares the requrest in a\nstrbuf and allow it to call this helper to perform a single round\ntrip with the other side?  If that is the case, the split is very\nsensible, but you need to say something about \"why\".\n\n>  transport-helper.c | 37 +++++++++++++++++++++++--------------\n>  1 file changed, 23 insertions(+), 14 deletions(-)\n>\n> diff --git a/transport-helper.c b/transport-helper.c\n> index 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"},{"id":"277484","messageId":"xmqqh9hof4bx.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 03/25] transport-helper.c: do not send null option to remote helper","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-04T23:22:58Z","receivedAt":"2016-02-04T23:22:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nThis is even more strange.  Are the current callers broken and some\nsends value==NULL for an option that is not is_bool, resulting in\na call to quote_c_style() with NULL?  I somehow find it hard to\nbelieve as that would lead to an immediate segfault.\n\nAssuming that no current caller passes NULL to value when is_bool is\nnot in effect, there needs an explanation why future new callers may\nneed to do so.  An alternative for a valueless option could be to\nsend \"option name\\n\" instead of the usual \"option name value\\n\", but\nwithout such an explanation, readers cannot tell why not sending\nanything about \"name\", which is what this patch chooses to implement,\nis a better idea.\n\n>  transport-helper.c | 6 +++++-\n>  1 file changed, 5 insertions(+), 1 deletion(-)\n>\n> diff --git a/transport-helper.c b/transport-helper.c\n> index 35023da..2e78c4d 100644\n> --- a/transport-helper.c\n> +++ b/transport-helper.c\n> @@ -309,8 +309,12 @@ static int set_helper_option(struct transport *transport,\n>  \tstrbuf_addf(&buf, \"option %s \", name);\n>  \tif (is_bool)\n>  \t\tstrbuf_addstr(&buf, value ? \"true\" : \"false\");\n> -\telse\n> +\telse if (value)\n>  \t\tquote_c_style(value, &buf, NULL, 0);\n> +\telse {\n> +\t\tstrbuf_release(&buf);\n> +\t\treturn 0;\n> +\t}\n>  \tstrbuf_addch(&buf, '\\n');\n>  \n>  \tret = strbuf_set_helper_option(data, &buf);\n"},{"id":"277485","messageId":"xmqqd1scf3zi.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-5-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 04/25] upload-pack: move shallow deepen code out of receive_needs()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-04T23:30:25Z","receivedAt":"2016-02-04T23:30:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This is a prep step for further refactoring. Besides reindentation and\n> s/shallows\\./shallows->/g, no other changes are expected.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nGood.\n\n>  upload-pack.c | 99 +++++++++++++++++++++++++++++++----------------------------\n>  1 file changed, 52 insertions(+), 47 deletions(-)\n>\n> diff --git a/upload-pack.c b/upload-pack.c\n> index 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"},{"id":"277486","messageId":"xmqq8u30f3wh.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-7-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 06/25] upload-pack: remove unused variable \"backup\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-04T23:32:14Z","receivedAt":"2016-02-04T23:32:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> After the last patch, \"result\" and \"backup\" are the same. \"result\" used\n> to move, but the movement is now contained in send_shallow(). Delete\n> this redundant variable.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nOK.  05 and 06 squashed together would also have been readable, but\neither way is fine; it is not worth merging them into one.\n\n>  upload-pack.c | 9 ++++-----\n>  1 file changed, 4 insertions(+), 5 deletions(-)\n>\n> diff --git a/upload-pack.c b/upload-pack.c\n> index 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"},{"id":"277487","messageId":"xmqq4mdof3kh.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-8-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 07/25] upload-pack: move \"unshallow\" sending code out of deepen()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-04T23:39:26Z","receivedAt":"2016-02-04T23:39:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Also add some more comments in this code because it takes too long to\n> understand what it does (to me, who should be familiar enough to\n> understand this code well!)\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nThe diff is harder to read than necessary (not your fault), but\nsplitting this into a separate helper makes sense to me.\n\nThanks.\n\n>  upload-pack.c | 39 ++++++++++++++++++++++++++-------------\n>  1 file changed, 26 insertions(+), 13 deletions(-)\n>\n> diff --git a/upload-pack.c b/upload-pack.c\n> index 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"},{"id":"277488","messageId":"xmqqzivgdou6.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-9-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 08/25] upload-pack: use skip_prefix() instead of starts_with() when possible","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-04T23:42:57Z","receivedAt":"2016-02-04T23:42:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nThe result is certainly easier to read.  \"when possible\" is not\nquite a right way to look at it, though.  I think the patch does it\nwhen it makes sense (i.e. when the result becomes easier to read),\nwhich is much better ;-)\n\nI initially thought that the name \"arg\" was too bland, but we can\nthink of these as pointing at an argument on each line that begins\nwith a command (e.g. \"have\", \"shallow\", etc.), and when viewed that\nway, calling the second token on the line an \"arg\" makes sense.\n\n\n\n>  upload-pack.c | 26 +++++++++++++++-----------\n>  1 file changed, 15 insertions(+), 11 deletions(-)\n>\n> diff --git a/upload-pack.c b/upload-pack.c\n> index 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"},{"id":"277489","messageId":"xmqqvb64doln.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-10-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 09/25] upload-pack: tighten number parsing at \"deepen\" lines","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-04T23:48:04Z","receivedAt":"2016-02-04T23:48:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nHmm, so \"deepen 10-by-the-way-let-me-tell-you-something-else\" was an\nacceptable input that some (third-party) version of \"git fetch\"\ncould have used, but now we are rejecting it.\n\nThat \"let me tell you something else\" part wouldn't have reached\noutside this code, so it is inconceivable that anybody would relied\non that looseness as a \"feature\", so the only practical risk would\nbe if somebody wrote a protocol driver, mumbling \"on the Internet,\nthe end of line is CRLF, just like SMTP does\", that sends a \"deepen\n10<CR><LF>\".  We used not to notice, but now we reject such a\nreimplementation of Git.\n\n>  upload-pack.c | 4 ++--\n>  1 file changed, 2 insertions(+), 2 deletions(-)\n>\n> diff --git a/upload-pack.c b/upload-pack.c\n> index 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"},{"id":"277492","messageId":"xmqqpowcdo6t.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-12-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 11/25] fetch-pack: use skip_prefix() instead of starts_with() when possible","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-04T23:56:58Z","receivedAt":"2016-02-04T23:56:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nOK, with the same comment on \"when possible\".  I would have reused\nthe same \"arg\" without inventing a separate variable \"value\" if I\nwere doing this conversion, but either way would be OK.\n\n>  builtin/fetch-pack.c | 13 +++++++------\n>  1 file changed, 7 insertions(+), 6 deletions(-)\n>\n> diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c\n> index 9b2a514..0482077 100644\n> --- a/builtin/fetch-pack.c\n> +++ b/builtin/fetch-pack.c\n> @@ -58,13 +58,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n>  \n>  \tfor (i = 1; i < argc && *argv[i] == '-'; i++) {\n>  \t\tconst char *arg = argv[i];\n> +\t\tconst char *value;\n>  \n> -\t\tif (starts_with(arg, \"--upload-pack=\")) {\n> -\t\t\targs.uploadpack = arg + 14;\n> +\t\tif (skip_prefix(arg, \"--upload-pack=\", &value)) {\n> +\t\t\targs.uploadpack = value;\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=\", &value)) {\n> +\t\t\targs.uploadpack = value;\n>  \t\t\tcontinue;\n>  \t\t}\n>  \t\tif (!strcmp(\"--quiet\", arg) || !strcmp(\"-q\", arg)) {\n> @@ -100,8 +101,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=\", &value)) {\n> +\t\t\targs.depth = strtol(value, NULL, 0);\n>  \t\t\tcontinue;\n>  \t\t}\n>  \t\tif (!strcmp(\"--no-progress\", arg)) {\n"},{"id":"277494","messageId":"xmqqlh70dnxt.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-13-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 12/25] fetch-pack: use a common function for verbose printing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-05T00:02:22Z","receivedAt":"2016-02-05T00:02:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This reduces the number of \"if (verbose)\" which makes it a bit easier\n> to read imo. It also makes it easier to redirect all these printouts,\n> to a file for example.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\n>  fetch-pack.c | 88 +++++++++++++++++++++++++++++-------------------------------\n>  1 file changed, 42 insertions(+), 46 deletions(-)\n>\n> diff --git a/fetch-pack.c b/fetch-pack.c\n> index 01e34b6..16917f9 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\nThis does \"print if told to be verbose\".  Sounds more like 'tracef',\n'reportf', 'debugf', etc.\n\nI am debating myself if it is a good idea to omit the final \"\\n\" on\nthe calling side and add one unconditionally here, but the benefit\nof brevity on the many callsites outweigh the reduced flexibility, I\nguess.\n\nThanks.\n"},{"id":"277495","messageId":"xmqqh9hodnw2.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-14-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 13/25] fetch-pack: use a separate flag for fetch in deepening mode","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-05T00:03:25Z","receivedAt":"2016-02-05T00:03:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> 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\n> repo. Stop relying on args->depth in this mode. Future deepening\n> methods can simply set this flag on instead of updating all these if\n> expressions.\n>\n> The new name \"deepen\" was chosen after the command to define shallow\n> boundary in pack protocol. New commands also follow this tradition.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nOK.  Up to here things look more-or-less sensible overall.\n\nThanks.\n\n>  fetch-pack.c | 14 ++++++++------\n>  fetch-pack.h |  1 +\n>  2 files changed, 9 insertions(+), 6 deletions(-)\n>\n> diff --git a/fetch-pack.c b/fetch-pack.c\n> index 16917f9..e947514 100644\n> --- a/fetch-pack.c\n> +++ b/fetch-pack.c\n> @@ -197,7 +197,7 @@ enum ack_type {\n>  \n>  static void consume_shallow_list(struct fetch_pack_args *args, int fd)\n>  {\n> -\tif (args->stateless_rpc && args->depth > 0) {\n> +\tif (args->stateless_rpc && args->deepen) {\n>  \t\t/* If we sent a depth we will get back \"duplicate\"\n>  \t\t * shallow and unshallow commands every time there\n>  \t\t * is a block of have lines exchanged.\n> @@ -348,7 +348,7 @@ static int find_common(struct fetch_pack_args *args,\n>  \tpacket_buf_flush(&req_buf);\n>  \tstate_len = req_buf.len;\n>  \n> -\tif (args->depth > 0) {\n> +\tif (args->deepen) {\n>  \t\tchar *line;\n>  \t\tconst char *arg;\n>  \t\tunsigned char sha1[20];\n> @@ -557,7 +557,7 @@ static void filter_refs(struct fetch_pack_args *args,\n>  \t\t}\n>  \n>  \t\tif (!keep && args->fetch_all &&\n> -\t\t    (!args->depth || !starts_with(ref->name, \"refs/tags/\")))\n> +\t\t    (!args->deepen || !starts_with(ref->name, \"refs/tags/\")))\n>  \t\t\tkeep = 1;\n>  \n>  \t\tif (keep) {\n> @@ -627,7 +627,7 @@ static int everything_local(struct fetch_pack_args *args,\n>  \t\t}\n>  \t}\n>  \n> -\tif (!args->depth) {\n> +\tif (!args->deepen) {\n>  \t\tfor_each_ref(mark_complete_oid, NULL);\n>  \t\tfor_each_alternate_ref(mark_alternate_complete, NULL);\n>  \t\tcommit_list_sort_by_date(&complete);\n> @@ -813,6 +813,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>  \n>  \tif ((args->depth > 0 || is_repository_shallow()) && !server_supports(\"shallow\"))\n>  \t\tdie(\"Server does not support shallow clients\");\n> +\tif (args->depth > 0)\n> +\t\targs->deepen = 1;\n>  \tif (server_supports(\"multi_ack_detailed\")) {\n>  \t\tprint_verbose(args, \"Server supports multi_ack_detailed\");\n>  \t\tmulti_ack = 2;\n> @@ -873,7 +875,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>  \n>  \tif (args->stateless_rpc)\n>  \t\tpacket_flush(fd[1]);\n> -\tif (args->depth > 0)\n> +\tif (args->deepen)\n>  \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n>  \t\t\t\t\tNULL);\n>  \telse if (si->nr_ours || si->nr_theirs)\n> @@ -940,7 +942,7 @@ static void update_shallow(struct fetch_pack_args *args,\n>  \tint *status;\n>  \tint i;\n>  \n> -\tif (args->depth > 0 && alternate_shallow_file) {\n> +\tif (args->deepen && alternate_shallow_file) {\n>  \t\tif (*alternate_shallow_file == '\\0') { /* --unshallow */\n>  \t\t\tunlink_or_warn(git_path_shallow());\n>  \t\t\trollback_lock_file(&shallow_lock);\n> diff --git a/fetch-pack.h b/fetch-pack.h\n> index 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"},{"id":"277503","messageId":"CAPig+cQCyH8nRViwt=_9SHT-5a_m4xDomu4xcGcWpKu2X2mPgQ@mail.gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-13-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 12/25] fetch-pack: use a common function for verbose printing","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-02-05T04:03:58Z","receivedAt":"2016-02-05T04:03:58Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 4, 2016 at 4:03 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> This reduces the number of \"if (verbose)\" which makes it a bit easier\n> to read imo. It also makes it easier to redirect all these printouts,\n> to a file for example.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/fetch-pack.c b/fetch-pack.c\n> @@ -810,39 +814,32 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>         else if (server_supports(\"side-band\")) {\n> -               if (args->verbose)\n> -                       fprintf(stderr, \"Server supports side-band\\n\");\n> +               print_verbose(args, \"Server supports side-band\");\n>                 use_sideband = 1;\n>         }\n>         if (server_supports(\"allow-tip-sha1-in-want\")) {\n> -               if (args->verbose)\n> -                       fprintf(stderr, \"Server supports allow-tip-sha1-in-want\\n\");\n> +               print_verbose(args, \"Server supports allow-tip-sha1-in-want\");\n>                 allow_unadvertised_object_request |= ALLOW_TIP_SHA1;\n>         }\n>         if (server_supports(\"allow-reachable-sha1-in-want\")) {\n> -               if (args->verbose)\n> -                       fprintf(stderr, \"Server supports allow-reachable-sha1-in-want\\n\");\n> +               print_verbose(args, \"Server supports allow-reachable-sha1-in-want\\n\");\n\nI think you want to drop the newline here as you did with all the\nother call-sites since print_verbose() adds its own.\n\n>                 allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;\n>         }\n>         if (!server_supports(\"thin-pack\"))\n"},{"id":"277504","messageId":"CAPig+cRC0YaBMjqNd9OXoU5AUke73GoB7cstxyK9nNbU=kZXaA@mail.gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-14-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 13/25] fetch-pack: use a separate flag for fetch in deepening mode","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-02-05T04:13:26Z","receivedAt":"2016-02-05T04:13:26Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 4, 2016 at 4:03 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> The shallow repo could be deepened or shortened when then user gives\n\ns/then/the/\n\n> --depth. But in future that won't be the only way to deepen/shorten a\n> repo. Stop relying on args->depth in this mode. Future deepening\n> methods can simply set this flag on instead of updating all these if\n> expressions.\n>\n> The new name \"deepen\" was chosen after the command to define shallow\n> boundary in pack protocol. New commands also follow this tradition.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n"},{"id":"277505","messageId":"CAPig+cQ2thjGWxar3BSJBU_O1WBsD0FHjv8i_sSMQKh+wO7bjQ@mail.gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-21-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 20/25] upload-pack: support define shallow boundary by excluding revisions","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-02-05T05:03:03Z","receivedAt":"2016-02-05T05:03:03Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 4, 2016 at 4:03 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> This should allow the user to say \"create a shallow clone of this branch\n> after version <some-tag>\".\n>\n> deepen-not cannot be used with deepen the same way deepen-since cannot\n> be used with deepen.\n\nAs written, this is a bit of a brain twister and required several\nre-reads to digest. Perhaps it might be clearer if rephrased like\nthis:\n\n    Like deepen-since, deepen-not cannot be used with deepen.\n\nor:\n\n    Like deepen-since, deepen-not is incompatible with deepen.\n\n> But deepen-not can be mixed with deepen-since. The\n> result is exactly how you do the command \"git rev-list --since=... --not\n> ref\".\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt\n> @@ -188,6 +188,15 @@ specific time, instead of depth. Internally it's equivalent of doing\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\nThis description, on the other hand, is easy to grasp.\n\n> diff --git a/upload-pack.c b/upload-pack.c\n> @@ -678,6 +679,16 @@ static void receive_needs(void)\n> +               if (skip_prefix(line, \"deepen-not \", &arg)) {\n> +                       char *ref = NULL;\n> +                       unsigned char sha1[20];\n> +                       if (expand_ref(arg, strlen(arg), sha1, &ref) != 1)\n> +                               die(\"Ambiguous deepen-not: %s\", line);\n\nWith just a few exceptions, die() invocations in upload-pack.c prefix\nthe message with \"git upload-pack:\" and start the actual diagnostic\nwith lowercase, so perhaps:\n\n    die(\"git upload-pack: ambiguous deepen-not: %s\", line);\n\n> +                       string_list_append(&deepen_not, ref);\n> +                       free(ref);\n> +                       deepen_rev_list = 1;\n> +                       continue;\n> +               }\n> @@ -746,6 +757,13 @@ static void receive_needs(void)\n>                         struct object *o = want_obj.objects[i].item;\n>                         argv_array_push(&av, oid_to_hex(&o->oid));\n>                 }\n> +               if (deepen_not.nr) {\n> +                       argv_array_push(&av, \"--not\");\n> +                       for (i = 0; i < deepen_not.nr; i++) {\n> +                               struct string_list_item *s = deepen_not.items + i;\n> +                               argv_array_push(&av, s->string);\n> +                       }\n\nThe documentation for rev-list --not says it \"Reverses the meaning ...\nup to the next --not\", so would it make sense to add a final:\n\n    argv_array_push(&av, \"--not\");\n\nhere to future-proof against some change pushing more arguments onto\n'av' following this code?\n\n> +               }\n>                 deepen_by_rev_list(av.argc, av.argv, &shallows);\n>                 argv_array_clear(&av);\n>         }\n"},{"id":"277506","messageId":"CAPig+cQ0V3KdHsJ2tNphkFx-2yDhtU4JFnrDU-zagXFz2jymbg@mail.gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-21-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 20/25] upload-pack: support define shallow boundary by excluding revisions","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-02-05T05:05:50Z","receivedAt":"2016-02-05T05:05:50Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 4, 2016 at 4:03 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> @@ -732,7 +743,7 @@ static void receive_needs(void)\n>         if (depth == 0 && !deepen_rev_list && shallows.nr == 0)\n>                 return;\n>         if (depth > 0 && deepen_rev_list)\n> -               die(\"--depth and --shallow-since cannot be used together\");\n> +               die(\"--depth and --shallow-since (or --shallow-exclude) cannot be used together\");\n\nAlso, does this change belong in patch 21/25?\n\n>         if (depth > 0)\n>                 deepen(depth, &shallows);\n>         else if (deepen_rev_list) {\n"},{"id":"277507","messageId":"CAPig+cQA6yV369b7hM_Q8aPuAwF8tR1xT=jr1r2PH1KsCtHWtQ@mail.gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-22-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 21/25] fetch: define shallow boundary with --shallow-exclude","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-02-05T05:26:43Z","receivedAt":"2016-02-05T05:26:43Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 4, 2016 at 4:03 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-pack.c b/builtin/fetch-pack.c\n> @@ -109,6 +109,16 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n> +               if (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n> +                       static struct string_list *deepen_not;\n> +                       if (!deepen_not) {\n> +                               deepen_not = xmalloc(sizeof(*deepen_not));\n> +                               string_list_init(deepen_not, 1);\n> +                               args.deepen_not = deepen_not;\n> +                       }\n> +                       string_list_append(deepen_not, value);\n> +                       continue;\n> +               }\n\nHmm, can't this be simplified to:\n\n    if (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n        if (!args.deepen_not) {\n            args.deepen_not = xmalloc(sizeof(*args.deepen_not));\n            string_list_init(args.deepen_not, 1);\n        }\n        string_list_append(args.deepen_not, value);\n        continue;\n    }\n\nOr, perhaps even better, declare it as plain 'struct string_list\ndeepen_not' in struct fetch_pack_args, rather than as a pointer, and\nthen in cmd_fetch_pack():\n\n    memset(&args, 0, sizeof(args));\n    args.uploadpack = \"git-upload-pack\";\n    string_list_init(&args.deepen_not, 1);\n\n    ...\n\n    if (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n        string_list_append(args.deepen_not, value);\n        continue;\n    }\n"},{"id":"277508","messageId":"CAPig+cQhYrS0LY5uv3KWSn0JTO8XHD74iUmV2cvCHHBRQH6nKA@mail.gmail.com","threadId":"41326","inReplyTo":"1454576641-29615-25-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 24/25] upload-pack: make check_reachable_object() return unreachable list if asked","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-02-05T05:41:17Z","receivedAt":"2016-02-05T05:41:17Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 4, 2016 at 4:04 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,8 +451,16 @@ static int is_our_ref(struct object *o)\n> -static int check_unreachable(struct object_array *src)\n> +/*\n> + * If reachable is NULL, return 1 if there is no unreachable object,\n> + * zero otherwise.\n> + *\n> + * If reachable is not NULL, it's filled with reachable objects.\n> + * Return value is irrelevant. The caller has to compare reachable and\n> + * src to find out if there's any unreachable object.\n> + */\n\nThis dual-purpose function serving up two entirely orthogonal modes\nfeel strange. Would it make sense to split this  into two functions,\none for each paragraph in the above documentation? (Of course, they\ncould share common implementation behind-the-scenes as needed.)\n\nMore below...\n\n> +static int check_unreachable(struct object_array *reachable,\n> +                            struct object_array *src)\n>  {\n>         static const char *argv[] = {\n>                 \"rev-list\", \"--stdin\", NULL,\n> @@ -507,9 +522,31 @@ static int check_unreachable(struct object_array *src)\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> -       if (i)\n> -               return 0;\n> +       if (!reachable) {\n> +               i = read_in_full(cmd.out, namebuf, 1);\n> +               if (i)\n> +                       return 0;\n> +       } else {\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; ) {\n> +                       o = get_indexed_object(--i);\n\nPerhaps code this as:\n\n    for (i = get_max_object_index(); 0 < i; i--) {\n        o = get_indexed_object(i - 1);\n\nin order to keep the loop control within the 'for' itself?\n\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> +       }\n"},{"id":"277649","messageId":"CACsJy8Az9rx_D0V+p2fEdNi64YJb2g_o0npUk2=wqCL2_moBPQ@mail.gmail.com","threadId":"41326","inReplyTo":"xmqqh9hof4bx.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 03/25] transport-helper.c: do not send null option to remote helper","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-06T09:38:15Z","receivedAt":"2016-02-06T09:38:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Feb 5, 2016 at 6:22 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>\n> This is even more strange.  Are the current callers broken and some\n> sends value==NULL for an option that is not is_bool, resulting in\n> a call to quote_c_style() with NULL?  I somehow find it hard to\n> believe as that would lead to an immediate segfault.\n>\n> Assuming that no current caller passes NULL to value when is_bool is\n> not in effect, there needs an explanation why future new callers may\n> need to do so.  An alternative for a valueless option could be to\n> send \"option name\\n\" instead of the usual \"option name value\\n\", but\n> without such an explanation, readers cannot tell why not sending\n> anything about \"name\", which is what this patch chooses to implement,\n> is a better idea.\n\nThe source is backfill_tags() which, in future, resets some transport\noptions back to defaults. The current set_option() in there only deals\nwith booleans or number (depth). But in future it resets deepen-since,\nwhich is a string.\n\nI think the main reason is, we do not have a way to reset (or unset) a\ntransport option. Should I keep this commit and explain about this, or\nhave a new transport API to reset option?\n-- \nDuy\n"},{"id":"277767","messageId":"xmqqziva7wk8.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"CACsJy8Az9rx_D0V+p2fEdNi64YJb2g_o0npUk2=wqCL2_moBPQ@mail.gmail.com","subject":"Re: [PATCH v2 03/25] transport-helper.c: do not send null option to remote helper","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-08T20:53:59Z","receivedAt":"2016-02-08T20:53:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Fri, Feb 5, 2016 at 6:22 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>>\n>>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>>> ---\n>>\n>> This is even more strange.  Are the current callers broken and some\n>> sends value==NULL for an option that is not is_bool, resulting in\n>> a call to quote_c_style() with NULL?  I somehow find it hard to\n>> believe as that would lead to an immediate segfault.\n>>\n>> Assuming that no current caller passes NULL to value when is_bool is\n>> not in effect, there needs an explanation why future new callers may\n>> need to do so.  An alternative for a valueless option could be to\n>> send \"option name\\n\" instead of the usual \"option name value\\n\", but\n>> without such an explanation, readers cannot tell why not sending\n>> anything about \"name\", which is what this patch chooses to implement,\n>> is a better idea.\n>\n> The source is backfill_tags() which, in future, resets some transport\n> options back to defaults. The current set_option() in there only deals\n> with booleans or number (depth). But in future it resets deepen-since,\n> which is a string.\n>\n> I think the main reason is, we do not have a way to reset (or unset) a\n> transport option. Should I keep this commit and explain about this, or\n> have a new transport API to reset option?\n\nAddition of new API is OK, but if this commit remains in the\nhistory, it itself has to explain why it is necessary.\n"},{"id":"277769","messageId":"xmqqvb5y7vuj.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-15-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 14/25] shallow.c: implement a generic shallow boundary finder based on rev-list","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-08T21:09:24Z","receivedAt":"2016-02-08T21:09:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\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> Note: if a commit has one NOT_SHALLOW parent and one SHALLOW parent,\n> then it's considered the boundary. Which means in the client side, this\n> commit has _no_ parents. This could lead to surprising cuts if we're not\n> careful.\n>\n> Another option is to include more commits and only mark commits whose\n> all parents are SHALLOW as boundary.\n\nThe second and third are greek to me at this point ;-) but hopefully\nthey will become clear as we read on.\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 rev_info revs;\n> +\tunsigned int i, nr;\n> +\n> +\t/*\n> +\t * SHALLOW (excluded) and NOT_SHALLOW (included) should not be\n> +\t * set at this point. But better be safe than sorry.\n> +\t */\n> +\tnr = get_max_object_index();\n> +\tfor (i = 0; i < nr; i++) {\n> +\t\tstruct object *o = get_indexed_object(i);\n> +\t\tif (!o || o->type != OBJ_COMMIT)\n> +\t\t\tcontinue;\n> +\t\to->flags &= ~(shallow_flag | not_shallow_flag);\n> +\t}\n\nThis is slightly different from clear_object_flags(), but I cannot\ntell if it is intended, or if you forgot that the function exists.\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> +\t/* Mark all reachable commits as NOT_SHALLOW */\n> +\tif (prepare_revision_walk(&revs))\n> +\t\tdie(\"revision walk setup failed\");\n> +\ttraverse_commit_list(&revs, show_commit, NULL, &not_shallow_flag);\n> +\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> +\tnr = get_max_object_index();\n> +\tfor (i = 0; i < nr; i++) {\n\nI'd really like not to see a loop over 0..get_max_object_index().\nAre there many codepaths that peek into the in-core entire object\nstore already?  Would it work equally well to keep track of the\ncommits discovered in show_commit() to use as the set of commits\nyou need to visit in this second pass?\n\n> +\t\tstruct object *o = get_indexed_object(i);\n> +\t\tstruct commit *c = (struct commit *)o;\n> +\n> +\t\tif (!o || o->type != OBJ_COMMIT ||\n> +\t\t    !(o->flags & not_shallow_flag))\n> +\t\t\tcontinue;\n> +\n> +\t\tif (parse_commit(c))\n> +\t\t\tdie(\"unable to parse commit %s\",\n> +\t\t\t    oid_to_hex(&c->object.oid));\n> +\n> +\t\tfor (p = c->parents; p; p = p->next)\n> +\t\t\tif (!(p->item->object.flags & not_shallow_flag)) {\n> +\t\t\t\to->flags |= shallow_flag;\n> +\t\t\t\tcommit_list_insert(c, &result);\n> +\t\t\t\tbreak;\n> +\t\t\t}\n> +\t}\n> +\n> +\t/*\n> +\t * Now we can clean up NOT_SHALLOW on border commits. Having\n> +\t * both flags set can confuse the caller.\n> +\t */\n> +\tfor (p = result; p; p = p->next) {\n> +\t\tstruct object *ro = &p->item->object;\n\nWhy \"ro\" only in this third pass, unlike the other two passes that\nsaid \"o\" which is in a sense more descriptive?\n\n> +\t\tif ((ro->flags & not_shallow_flag) &&\n> +\t\t    (ro->flags & shallow_flag))\n\nIf you introduce a \"both_flags = shallow_flag | not_shallow_flag\"\nat the very beginning, this will become\n\n\tif (o->flags & both_flags)\n        \to->flags &= ~not_shallow_flag;\n\nwhich would probably be easier to read.  You can pass the same to\nclear_object_flags() at the first pass.\n\n> +\t\t\tro->flags &= ~not_shallow_flag;\n> +\t}\n> +\treturn result;\n> +}\n\nOther than that, this step looks quite straight-forward to me.\n\nThanks.\n\n> +\n>  static void check_shallow_file_for_update(void)\n>  {\n>  \tif (is_shallow == -1)\n"},{"id":"277770","messageId":"xmqqr3gm7vm2.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-16-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 15/25] upload-pack: add deepen-since to cut shallow repos based on time","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-08T21:14:29Z","receivedAt":"2016-02-08T21:14:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This should allow the user to say \"create a shallow clone containing the\n> work from last year\" (once the client side is fixed up, of course).\n>\n> In theory deepen-since and deepen (aka --depth) can be used together to\n> draw the shallow boundary (whether it's intersection or union is up to\n> discussion, but if rev-list is used, it's likely intersection). However,\n> because deepen goes with a custom commit walker, we can't mix the two\n> yet.\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nLooks quite straight-forward.\n\nThanks.\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>\n> diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt\n> index 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)\n> diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt\n> index 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>  \n> diff --git a/upload-pack.c b/upload-pack.c\n> index c8dafe8..794736c 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(\"--depth and --shallow-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"},{"id":"277771","messageId":"xmqqmvra7vb9.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-18-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 17/25] clone: define shallow clone boundary based on time with --shallow-since","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-08T21:20:58Z","receivedAt":"2016-02-08T21:20:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n\nIt is kind of surprising that 16 & 17 can be so simple and does not\nhave to update the way the cut-off points at the client side are\ncomputed or recorded.  We must have done something right when we\ndesigned the initial \"--depth\" support ;-).\n\nOn the other hand, that probably means we have the same \"we clone\nonce, wait for a while and then do a shallow fetch with too short a\nhistory span--the objects in the original clone all go too stale\nthat they become invisible in the resulting history\" property (I\nwould not call that \"an issue\") as before.  It is just the way the\nshallow boundary is specified got more user friendly (from a number\nof parent-child hops to timespan).\n\nLet's keep reading...\n\n>  Documentation/git-clone.txt |  3 +++\n>  builtin/clone.c             | 16 +++++++++++++---\n>  2 files changed, 16 insertions(+), 3 deletions(-)\n>\n> diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\n> index 789b668..1b6b639 100644\n> --- a/Documentation/git-clone.txt\n> +++ b/Documentation/git-clone.txt\n> @@ -194,6 +194,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\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index 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"},{"id":"277772","messageId":"xmqqio1y7v5n.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-19-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 18/25] t5500, t5539: tests for shallow depth since a specific date","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-08T21:24:20Z","receivedAt":"2016-02-08T21:24:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\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\nDidn't --date=\"@10000000 +0700\" work?\n\nNot a complaint but genuinely curious.\n\n> +\tgit clone --shallow-since \"300000000 +0700\" \"file://$(pwd)/.\" ../shallow11 &&\n\nAre we required to add TZ to --shallow-since, or we merely tolerate\nif there is one (I am hoping that it is the latter)?\n\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> +\techo three >expected &&\n> +\techo two  >>expected &&\n\ntest_write_lines perhaps?\n\n> +\ttest_cmp expected actual\n> +'\n"},{"id":"277773","messageId":"xmqqegcm7v0k.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-20-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 19/25] refs: add expand_ref()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-08T21:27:23Z","receivedAt":"2016-02-08T21:27:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This is basically dwim_ref() without @{} support. To be used on the\n> server side where we want to expand abbreviated to full ref names and\n> nothing else.\n\nIt is unclear why we want to have such an expansion on the server,\nthough.  That is something this commit needs to justify, isn't it?\n\n>\n> Signed-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>\n> diff --git a/refs.c b/refs.c\n> index 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>  \n> diff --git a/refs.h b/refs.h\n> index 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"},{"id":"277774","messageId":"xmqqa8na7uoo.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"CAPig+cQ2thjGWxar3BSJBU_O1WBsD0FHjv8i_sSMQKh+wO7bjQ@mail.gmail.com","subject":"Re: [PATCH v2 20/25] upload-pack: support define shallow boundary by excluding revisions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-08T21:34:31Z","receivedAt":"2016-02-08T21:34:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n>> @@ -746,6 +757,13 @@ static void receive_needs(void)\n>>                         struct object *o = want_obj.objects[i].item;\n>>                         argv_array_push(&av, oid_to_hex(&o->oid));\n>>                 }\n>> +               if (deepen_not.nr) {\n>> +                       argv_array_push(&av, \"--not\");\n>> +                       for (i = 0; i < deepen_not.nr; i++) {\n>> +                               struct string_list_item *s = deepen_not.items + i;\n>> +                               argv_array_push(&av, s->string);\n>> +                       }\n>\n> The documentation for rev-list --not says it \"Reverses the meaning ...\n> up to the next --not\", so would it make sense to add a final:\n>\n>     argv_array_push(&av, \"--not\");\n>\n> here to future-proof against some change pushing more arguments onto\n> 'av' following this code?\n\nYup.\n\nWe enumerate the objects that are reachable are by traversing from\n\"want\", but \"--max-age\" and \"--not these refs\" are optionally used\nto truncate the enumeration, and usually we write \"options\" before\n\"parameters\", so from that point of view, if this new code comes\nbefore adding want_obj.objects[] (i.e. positive endpoints), that\nwould even be more readable.  And it would force this new code to\nhave a trailing \"--not\" ;-)\n\n>> +               }\n>>                 deepen_by_rev_list(av.argc, av.argv, &shallows);\n>>                 argv_array_clear(&av);\n>>         }\n"},{"id":"277775","messageId":"xmqq60xy7u6u.fsf@gitster.mtv.corp.google.com","threadId":"41326","inReplyTo":"1454576641-29615-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 00/25] More flexibility in making shallow clones","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-02-08T21:45:13Z","receivedAt":"2016-02-08T21:45:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This series brings three new options to shallow clone/fetch, to let\n> you specify cut point by time, or by excluding some refs, or to let\n> you extend shallow boundary by <N> commits.\n>\n> The series is now complete. Changes since v1 [1]\n>\n>  - smart http support\n>  - option names --not and --since are changed to --shallow-exclude\n>    and --shallow-since\n>  - fix the last patch per Eric's comments (the tests were totally\n>    broken but I didn't realize)\n>\n> The meat starts since 14/25. Before that is just cleanups and stuff.\n> Happy (shallowly) cloning!\n\nNicely done.  While I had a few \"Huh?\" moments, and I still feel\nsome changes are under-justified, it was a pleasant read overall.\n\nHow extensively have you been using this, or is this hot off the\npress?\n"},{"id":"277982","messageId":"CACsJy8DYkQe7DkY1AQbG5ZaJ-cLA3=1wjqAkekc3bdQQ1ANWMg@mail.gmail.com","threadId":"41326","inReplyTo":"xmqq60xy7u6u.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 00/25] More flexibility in making shallow clones","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-12T00:24:01Z","receivedAt":"2016-02-12T00:24:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Feb 9, 2016 at 4:45 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> This series brings three new options to shallow clone/fetch, to let\n>> you specify cut point by time, or by excluding some refs, or to let\n>> you extend shallow boundary by <N> commits.\n>>\n>> The series is now complete. Changes since v1 [1]\n>>\n>>  - smart http support\n>>  - option names --not and --since are changed to --shallow-exclude\n>>    and --shallow-since\n>>  - fix the last patch per Eric's comments (the tests were totally\n>>    broken but I didn't realize)\n>>\n>> The meat starts since 14/25. Before that is just cleanups and stuff.\n>> Happy (shallowly) cloning!\n>\n> Nicely done.  While I had a few \"Huh?\" moments, and I still feel\n> some changes are under-justified, it was a pleasant read overall.\n\nThanks. Will resend some time later.\n\n> How extensively have you been using this, or is this hot off the\n> press?\n\nNo I have not used it at all (can't use it for real when I can't\ncontrol the server side of github ;)\n-- \nDuy\n"},{"id":"278125","messageId":"CACsJy8DLnWpB+3+DRTq0MxAK82qe8yLVrLV29FDY=BmrWh4wWw@mail.gmail.com","threadId":"41326","inReplyTo":"xmqqvb64doln.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 09/25] upload-pack: tighten number parsing at \"deepen\" lines","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-15T03:07:42Z","receivedAt":"2016-02-15T03:07:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Feb 5, 2016 at 6:48 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n>> ---\n>\n> Hmm, so \"deepen 10-by-the-way-let-me-tell-you-something-else\" was an\n> acceptable input that some (third-party) version of \"git fetch\"\n> could have used, but now we are rejecting it.\n>\n> That \"let me tell you something else\" part wouldn't have reached\n> outside this code, so it is inconceivable that anybody would relied\n> on that looseness as a \"feature\", so the only practical risk would\n> be if somebody wrote a protocol driver, mumbling \"on the Internet,\n> the end of line is CRLF, just like SMTP does\", that sends a \"deepen\n> 10<CR><LF>\".  We used not to notice, but now we reject such a\n> reimplementation of Git.\n\nOn the other hand, if a broken client sends \"deepen 10f\" instead of\n\"deepen 271\", we should reject and let the client be fixed instead of\nsending a fetch of 10 commits deep back. \"10<CR>\" is not that bad, but\nfixing it is still a good idea.\n-- \nDuy\n"},{"id":"278126","messageId":"CACsJy8BFc=G0-4g+yk4otBfazEZT0V7y9DahEuZyE8MFzMTx3A@mail.gmail.com","threadId":"41326","inReplyTo":"CAPig+cQ0V3KdHsJ2tNphkFx-2yDhtU4JFnrDU-zagXFz2jymbg@mail.gmail.com","subject":"Re: [PATCH v2 20/25] upload-pack: support define shallow boundary by excluding revisions","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-15T03:31:11Z","receivedAt":"2016-02-15T03:31:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Feb 5, 2016 at 12:05 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Thu, Feb 4, 2016 at 4:03 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>> @@ -732,7 +743,7 @@ static void receive_needs(void)\n>>         if (depth == 0 && !deepen_rev_list && shallows.nr == 0)\n>>                 return;\n>>         if (depth > 0 && deepen_rev_list)\n>> -               die(\"--depth and --shallow-since cannot be used together\");\n>> +               die(\"--depth and --shallow-since (or --shallow-exclude) cannot be used together\");\n>\n> Also, does this change belong in patch 21/25?\n\n21/25 is about client side support, so no, it should belong here. But\nI probably should not mention client's option names here. After all\nupload-pack can be used with some other client that names things\ndifferently. I think I'm going with something like \"git upload-pack:\ndeepen and deepen-since (or deepen-not) cannot be used together\".\n-- \nDuy\n"},{"id":"278127","messageId":"CACsJy8B=p0frmU8ahc9bnk-uoDPNUT_6UB0MVRPiLc9DqNz3vQ@mail.gmail.com","threadId":"41326","inReplyTo":"CAPig+cQA6yV369b7hM_Q8aPuAwF8tR1xT=jr1r2PH1KsCtHWtQ@mail.gmail.com","subject":"Re: [PATCH v2 21/25] fetch: define shallow boundary with --shallow-exclude","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-15T03:53:27Z","receivedAt":"2016-02-15T03:53:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Feb 5, 2016 at 12:26 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Thu, Feb 4, 2016 at 4:03 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-pack.c b/builtin/fetch-pack.c\n>> @@ -109,6 +109,16 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n>> +               if (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n>> +                       static struct string_list *deepen_not;\n>> +                       if (!deepen_not) {\n>> +                               deepen_not = xmalloc(sizeof(*deepen_not));\n>> +                               string_list_init(deepen_not, 1);\n>> +                               args.deepen_not = deepen_not;\n>> +                       }\n>> +                       string_list_append(deepen_not, value);\n>> +                       continue;\n>> +               }\n>\n> Hmm, can't this be simplified to:\n>\n>     if (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n>         if (!args.deepen_not) {\n>             args.deepen_not = xmalloc(sizeof(*args.deepen_not));\n>             string_list_init(args.deepen_not, 1);\n>         }\n>         string_list_append(args.deepen_not, value);\n>         continue;\n>     }\n\nargs.deepen_not is const, so no, the compiler will complain at\nstring_list_init and string_list_append. Dropping \"const\" is one\noption, if you prefer.\n\n> Or, perhaps even better, declare it as plain 'struct string_list\n> deepen_not' in struct fetch_pack_args, rather than as a pointer, and\n> then in cmd_fetch_pack():\n>\n>     memset(&args, 0, sizeof(args));\n>     args.uploadpack = \"git-upload-pack\";\n>     string_list_init(&args.deepen_not, 1);\n\nThere's another place fetch_pack_args variable is declared, in\nfetch_refs_via_pack(), and we would need to string_list_copy() from\ntransport->data->options.deepen_not and then free it afterward. So I\nthink it's not really worth it.\n\n>     if (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n>         string_list_append(args.deepen_not, value);\n>         continue;\n>     }\n-- \nDuy\n"},{"id":"278131","messageId":"CAPig+cR01WCgyJQuDcq-j5Z6u3S-LO5kUVuT+g-jdu-hoH-5yw@mail.gmail.com","threadId":"41326","inReplyTo":"CACsJy8B=p0frmU8ahc9bnk-uoDPNUT_6UB0MVRPiLc9DqNz3vQ@mail.gmail.com","subject":"Re: [PATCH v2 21/25] fetch: define shallow boundary with --shallow-exclude","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-02-15T05:52:26Z","receivedAt":"2016-02-15T05:52:26Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Feb 14, 2016 at 10:53 PM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Fri, Feb 5, 2016 at 12:26 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n>> On Thu, Feb 4, 2016 at 4:03 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-pack.c b/builtin/fetch-pack.c\n>>> @@ -109,6 +109,16 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n>>> +               if (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n>>> +                       static struct string_list *deepen_not;\n>>> +                       if (!deepen_not) {\n>>> +                               deepen_not = xmalloc(sizeof(*deepen_not));\n>>> +                               string_list_init(deepen_not, 1);\n>>> +                               args.deepen_not = deepen_not;\n>>> +                       }\n>>> +                       string_list_append(deepen_not, value);\n>>> +                       continue;\n>>> +               }\n>>\n>> Hmm, can't this be simplified to:\n>>\n>>     if (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n>>         if (!args.deepen_not) {\n>>             args.deepen_not = xmalloc(sizeof(*args.deepen_not));\n>>             string_list_init(args.deepen_not, 1);\n>>         }\n>>         string_list_append(args.deepen_not, value);\n>>         continue;\n>>     }\n>\n> args.deepen_not is const, so no, the compiler will complain at\n> string_list_init and string_list_append. Dropping \"const\" is one\n> option, if you prefer.\n\nYes, dropping 'const' was implied. I didn't examine it too deeply, but\nit did not appear as if there would be any major fallout from dropping\n'const'. It feels a bit cleaner to keep it all self-contained than to\nhave that somewhat oddball static string_list*, but it's not such a\nbig deal that I'd insist upon a rewrite.\n\n>> Or, perhaps even better, declare it as plain 'struct string_list\n>> deepen_not' in struct fetch_pack_args, rather than as a pointer, and\n>> then in cmd_fetch_pack():\n>>\n>>     memset(&args, 0, sizeof(args));\n>>     args.uploadpack = \"git-upload-pack\";\n>>     string_list_init(&args.deepen_not, 1);\n>\n> There's another place fetch_pack_args variable is declared, in\n> fetch_refs_via_pack(), and we would need to string_list_copy() from\n> transport->data->options.deepen_not and then free it afterward. So I\n> think it's not really worth it.\n\nOkay.\n\n>>     if (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n>>         string_list_append(args.deepen_not, value);\n>>         continue;\n>>     }\n"},{"id":"278132","messageId":"CAPig+cSL--T7f5OdR2+b7t-AHof8TGb9=P8XKa2t3K3y2yu0MQ@mail.gmail.com","threadId":"41326","inReplyTo":"CAPig+cR01WCgyJQuDcq-j5Z6u3S-LO5kUVuT+g-jdu-hoH-5yw@mail.gmail.com","subject":"Re: [PATCH v2 21/25] fetch: define shallow boundary with --shallow-exclude","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-02-15T05:56:07Z","receivedAt":"2016-02-15T05:56:07Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Feb 15, 2016 at 12:52 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Sun, Feb 14, 2016 at 10:53 PM, Duy Nguyen <pclouds@gmail.com> wrote:\n>> On Fri, Feb 5, 2016 at 12:26 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n>>> Hmm, can't this be simplified to:\n>>>\n>>>     if (skip_prefix(arg, \"--shallow-exclude=\", &value)) {\n>>>         if (!args.deepen_not) {\n>>>             args.deepen_not = xmalloc(sizeof(*args.deepen_not));\n>>>             string_list_init(args.deepen_not, 1);\n>>>         }\n>>>         string_list_append(args.deepen_not, value);\n>>>         continue;\n>>>     }\n>>\n>> args.deepen_not is const, so no, the compiler will complain at\n>> string_list_init and string_list_append. Dropping \"const\" is one\n>> option, if you prefer.\n>\n> Yes, dropping 'const' was implied. I didn't examine it too deeply, but\n> it did not appear as if there would be any major fallout from dropping\n> 'const'. It feels a bit cleaner to keep it all self-contained than to\n> have that somewhat oddball static string_list*, but it's not such a\n> big deal that I'd insist upon a rewrite.\n>\n>>> Or, perhaps even better, declare it as plain 'struct string_list\n>>> deepen_not' in struct fetch_pack_args, rather than as a pointer, and\n>>> then in cmd_fetch_pack():\n>>>\n>>>     memset(&args, 0, sizeof(args));\n>>>     args.uploadpack = \"git-upload-pack\";\n>>>     string_list_init(&args.deepen_not, 1);\n>>\n>> There's another place fetch_pack_args variable is declared, in\n>> fetch_refs_via_pack(), and we would need to string_list_copy() from\n>> transport->data->options.deepen_not and then free it afterward. So I\n>> think it's not really worth it.\n\nUpon re-reading, I suppose this also is an argument for keeping the\nstatic string_list* rather than dropping the 'const'...(?)\n"},{"id":"278133","messageId":"CACsJy8DE063TDsS3NGOTf5=tYpNTusX+ymDT8XG_RVjbqCW=Fw@mail.gmail.com","threadId":"41326","inReplyTo":"xmqqio1y7v5n.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 18/25] t5500, t5539: tests for shallow depth since a specific date","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-15T07:17:58Z","receivedAt":"2016-02-15T07:17:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Feb 9, 2016 at 4:24 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\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>\n> Didn't --date=\"@10000000 +0700\" work?\n>\n> Not a complaint but genuinely curious.\n\nNope. --date sets author date, but rev-list --since only cares about\ncommitter date.\n\n>> +     git clone --shallow-since \"300000000 +0700\" \"file://$(pwd)/.\" ../shallow11 &&\n>\n> Are we required to add TZ to --shallow-since, or we merely tolerate\n> if there is one (I am hoping that it is the latter)?\n\nHmm.. I'm not familiar with date parsing code. I was sure that the\nparsing here (to timestamp) is the same as \"rev-list --since\" done on\nthe server side. The function used is approxidate() which seems to\nignore timezone.\n\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>> +     echo three >expected &&\n>> +     echo two  >>expected &&\n>\n> test_write_lines perhaps?\n\nI was lazy. probably could go with cat <<EOF.\n-- \nDuy\n"},{"id":"278135","messageId":"CACsJy8CjnD-f-fyizQ6DE6ik5vYQqYq+R-YSEJ7XZ9XTHEH1Tg@mail.gmail.com","threadId":"41326","inReplyTo":"xmqqvb5y7vuj.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 14/25] shallow.c: implement a generic shallow boundary finder based on rev-list","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-15T08:00:15Z","receivedAt":"2016-02-15T08:00:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Feb 9, 2016 at 4:09 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> +     is_repository_shallow(); /* make sure shallows are read */\n>> +\n>> +     init_revisions(&revs, NULL);\n>> +     save_commit_buffer = 0;\n>> +     setup_revisions(ac, av, &revs, NULL);\n>> +\n>> +     /* Mark all reachable commits as NOT_SHALLOW */\n>> +     if (prepare_revision_walk(&revs))\n>> +             die(\"revision walk setup failed\");\n>> +     traverse_commit_list(&revs, show_commit, NULL, &not_shallow_flag);\n>> +\n>> +     /*\n>> +      * mark border commits SHALLOW + NOT_SHALLOW.\n>> +      * We cannot clear NOT_SHALLOW right now. Imagine border\n>> +      * commit A is processed first, then commit B, whose parent is\n>> +      * A, later. If NOT_SHALLOW on A is cleared at step 1, B\n>> +      * itself is considered border at step 2, which is incorrect.\n>> +      */\n>> +     nr = get_max_object_index();\n>> +     for (i = 0; i < nr; i++) {\n>\n> I'd really like not to see a loop over 0..get_max_object_index().\n> Are there many codepaths that peek into the in-core entire object\n> store already?\n\nYou started it with check_non_tip(). At least that's how I know about\nthis loop. But I think that's the only code path, not counting this.\n\n> Would it work equally well to keep track of the\n> commits discovered in show_commit() to use as the set of commits\n> you need to visit in this second pass?\n\nWe can't do this in show_commit. In this loop, we check\nnot_shallow_flag of parent commits. If one parent commit is not\nshow_commit'd yet, the flag is not set and we may incorrectly think\nthis is a border commit. The only way to avoid going through the\nentire in-core object database is keeping a new commit_list and go\nthrough it here. Which way is preferred?\n\n>> +             struct object *o = get_indexed_object(i);\n>> +             struct commit *c = (struct commit *)o;\n>> +\n>> +             if (!o || o->type != OBJ_COMMIT ||\n>> +                 !(o->flags & not_shallow_flag))\n>> +                     continue;\n>> +\n>> +             if (parse_commit(c))\n>> +                     die(\"unable to parse commit %s\",\n>> +                         oid_to_hex(&c->object.oid));\n>> +\n>> +             for (p = c->parents; p; p = p->next)\n>> +                     if (!(p->item->object.flags & not_shallow_flag)) {\n>> +                             o->flags |= shallow_flag;\n>> +                             commit_list_insert(c, &result);\n>> +                             break;\n>> +                     }\n>> +     }\n-- \nDuy\n"},{"id":"278138","messageId":"20160215081539.GA12609@lanh","threadId":"41326","inReplyTo":"CAPig+cR01WCgyJQuDcq-j5Z6u3S-LO5kUVuT+g-jdu-hoH-5yw@mail.gmail.com","subject":"Re: [PATCH v2 21/25] fetch: define shallow boundary with --shallow-exclude","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-15T08:15:39Z","receivedAt":"2016-02-15T08:15:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Feb 15, 2016 at 12:52:26AM -0500, Eric Sunshine wrote:\n> Yes, dropping 'const' was implied. I didn't examine it too deeply, but\n> it did not appear as if there would be any major fallout from dropping\n> 'const'. It feels a bit cleaner to keep it all self-contained than to\n> have that somewhat oddball static string_list*, but it's not such a\n> big deal that I'd insist upon a rewrite.\n\nDropping 'const' is not a big deal. But before we do that, how about\nthis instead? I think the code looks better, and the compiler can\nstill catch invalid updates to deepen_not.\n\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++];\n--\nDuy\n"},{"id":"278605","messageId":"CAPig+cRHOKkaGmSRioEqYYmWzDazNinq2owwYmKyQwOFDLvjig@mail.gmail.com","threadId":"41326","inReplyTo":"20160215081539.GA12609@lanh","subject":"Re: [PATCH v2 21/25] fetch: define shallow boundary with --shallow-exclude","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-02-19T01:35:54Z","receivedAt":"2016-02-19T01:35:54Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Feb 15, 2016 at 3:15 AM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Mon, Feb 15, 2016 at 12:52:26AM -0500, Eric Sunshine wrote:\n>> Yes, dropping 'const' was implied. I didn't examine it too deeply, but\n>> it did not appear as if there would be any major fallout from dropping\n>> 'const'. It feels a bit cleaner to keep it all self-contained than to\n>> have that somewhat oddball static string_list*, but it's not such a\n>> big deal that I'd insist upon a rewrite.\n>\n> Dropping 'const' is not a big deal. But before we do that, how about\n> this instead? I think the code looks better, and the compiler can\n> still catch invalid updates to deepen_not.\n\nI like this better, too.\n\n> diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c\n> index 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>         struct child_process *conn;\n>         struct fetch_pack_args args;\n>         struct sha1_array shallow = SHA1_ARRAY_INIT;\n> +       struct string_list deepen_not = STRING_LIST_INIT_DUP;\n>\n>         packet_trace_identity(\"fetch-pack\");\n>\n> @@ -108,6 +109,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n>                         args.deepen_since = xstrdup(arg);\n>                         continue;\n>                 }\n> +               if (skip_prefix(arg, \"--shallow-exclude=\", &arg)) {\n> +                       string_list_append(&deepen_not, arg);\n> +                       continue;\n> +               }\n>                 if (!strcmp(\"--no-progress\", arg)) {\n>                         args.no_progress = 1;\n>                         continue;\n> @@ -135,6 +140,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)\n>                 }\n>                 usage(fetch_pack_usage);\n>         }\n> +       if (deepen_not.nr)\n> +               args.deepen_not = &deepen_not;\n>\n>         if (i < argc)\n>                 dest = argv[i++];\n> --\n> Duy\n"},{"id":"278631","messageId":"20160219093023.GA12470@lanh","threadId":"41326","inReplyTo":"xmqqvb5y7vuj.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 14/25] shallow.c: implement a generic shallow boundary finder based on rev-list","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-02-19T09:30:23Z","receivedAt":"2016-02-19T09:30:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Feb 08, 2016 at 01:09:24PM -0800, Junio C Hamano wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n> \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> > Note: if a commit has one NOT_SHALLOW parent and one SHALLOW parent,\n> > then it's considered the boundary. Which means in the client side, this\n> > commit has _no_ parents. This could lead to surprising cuts if we're not\n> > careful.\n> >\n> > Another option is to include more commits and only mark commits whose\n> > all parents are SHALLOW as boundary.\n> \n> The second and third are greek to me at this point ;-) but hopefully\n> they will become clear as we read on.\n\nYeah. Everything looks clearer with illustration. This should be a\nbetter. The question is should we do something about it now, or leave\nit as is.\n\nI'm tempted to go with \"the first way\" in future (so add some comments\nabout this in is_repository_shallow, instead of leaving it as commit\nmessage in this patch).\n\n-- 8< --\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 interesting observation, though somewhat off topic for this\npatch. In the following graph, \"x\" is unreachable commits. \"b\" is the\nparent of \"a\".\n\n           x -- a -- o\n\t       /    /\n         x -- b -- o\n\nAnd as a result, \"a\" and \"b\" are both considered shallow commits. After\ngrafting occurs at the client side, what we see is\n\n                a -- o\n\t            /\n              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 keeps \"b -- a\" connection if \"b\"\nexists and is a shallow commit.\n\nThe second way is produce this graph (at client side) instead\n\n           x -- a -- o\n\t       /    /\n              b -- o\n\nWhich means we mark \"x\" as a shallow commit instead of \"a\".\n-- 8< --\n--\nDuy\n"}]}