{"thread":{"id":"24981","subject":"[PATCH 01/17] rev-list: do not do commit simplification if simplify_history = 0","startedAt":"2010-09-05T06:47:27Z","lastAt":"2010-09-06T20:40:29Z","messageCount":25,"participants":["Nguyễn Thái Ngọc Duy","Sverre Rabbelier","Nguyen Thai Ngoc Duy","Elijah Newren"],"isPatch":true,"patchVersion":1,"patchTotal":17},"messages":[{"id":"149940","messageId":"1283669264-15759-1-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":null,"subject":"[PATCH 00/17] Narrow clone v3 (was subtree clone)","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:27Z","receivedAt":"2010-09-05T06:47:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I'll describe differences between this series and Elijah's one [1].\nI think it's more interesting. Changes from v2 [2] will follow later.\n\nIn short I think the two series are converging. The outstanding\ndifference is Elijah drops shallow clone in favor of more flexible\nhistory cutting while I only focus on tree cutting.\n\nTwo other differences are tree traversal and tree generating. I admit\nthat changing traverse_trees() the way Elijah does is more flexible\nand is probably the only way to support negative pathspec. And I think\nhis sparse clone supports even cloning a single file. Mine does not\nsupport that. I'm going to steal some of his patches at some point.\n\nTree generating from index, Elijah merges the base tree inside\nwrite_cache_as_tree() while it does it inside commit_tree(). Again the\nprinciple is pretty much the same. I'll see if I can resist from\nstealing some more :)\n\nOK, changes from v2:\n\n - Require en/object-list-with-pathspec\n - Use lower-case index extension instead of stepping index version to 4\n - Support cloning multiple tree\n - Remote merge is dropped. It may be resurrected at some point if\n   people like to merge so much.\n\nThings that won't work:\n\n - fsck/prune/...\n - Shell scripts that use \"git write-tree\"\n\nWhat's next:\n - stealing\n - fsck (will probably make fsck use rev-list in narrow repo)\n - only send commits that have changes in narrow area and graft it at\n   client side\n\n[1] http://mid.gmane.org/1283645647-1891-1-git-send-email-newren@gmail.com\n[2] http://mid.gmane.org/1282688422-7738-1-git-send-email-pclouds@gmail.com \n\nNguyễn Thái Ngọc Duy (17):\n  rev-list: do not do commit simplification if simplify_history = 0\n  tree.c: add path_to_sha1()\n  Introduce $GIT_DIR/narrow\n  index: make narrow index incompatible with older git\n  pack-objects: support narrow packs with pathspecs\n  {fetch,upload}-pack: support narrow repository\n  unpack-trees: split traverse_trees() code into a separate function\n  unpack-trees: support unpack trees in narrow repository\n  cache-tree: only cache tree within narrow area\n  get_pathspec(): support narrow pathspec rewriting\n  pathspec retrieval fix\n  clone: support --narrow option\n  commit: add narrow's commit_tree version\n  commit: use commit_narrow_tree() to support narrow repo\n  write-tree: requires --narrow-base in narrow repository\n  merge: try to do local merge if possible in narrow repo\n  Add narrow clone demonstration test\n\n .gitignore                                |    1 +\n Documentation/git-clone.txt               |   14 ++-\n Documentation/git-fetch-pack.txt          |    3 +\n Documentation/git-merge.txt               |   16 ++\n Documentation/git-pack-objects.txt        |    2 +-\n Documentation/git-rev-parse.txt           |    3 +\n Documentation/gitrepository-layout.txt    |    5 +\n Documentation/technical/pack-protocol.txt |    3 +\n Makefile                                  |    3 +\n builtin/clone.c                           |   43 +++++\n builtin/commit.c                          |   16 +-\n builtin/fetch-pack.c                      |    9 +\n builtin/grep.c                            |    5 +-\n builtin/ls-files.c                        |    2 +-\n builtin/ls-tree.c                         |    2 +-\n builtin/merge.c                           |   60 ++++++-\n builtin/pack-objects.c                    |   19 ++-\n builtin/reset.c                           |    3 +-\n builtin/rev-parse.c                       |    8 +\n builtin/write-tree.c                      |   25 +++\n cache-tree.c                              |   41 ++++-\n cache.h                                   |    5 +\n commit.c                                  |   16 ++\n commit.h                                  |    5 +\n environment.c                             |    2 +\n git-am.sh                                 |    2 +-\n narrow-tree.c                             |  285 +++++++++++++++++++++++++++++\n narrow-tree.h                             |    8 +\n read-cache.c                              |   32 +++-\n revision.c                                |    7 +-\n setup.c                                   |  172 +++++++++++++++++-\n t/t0062-narrow-pathspec.sh                |  150 +++++++++++++++\n t/t0063-narrow-repo.sh                    |   74 ++++++++\n t/t1013-read-tree-narrow.sh               |   72 ++++++++\n t/t6000-rev-list-misc.sh                  |    9 +\n t/t7510-commit-narrow.sh                  |   30 +++\n t/t9999-narrow.sh                         |   87 +++++++++\n test-get-pathspec.c                       |   17 ++\n tree.c                                    |   35 ++++\n tree.h                                    |    2 +\n unpack-trees.c                            |  153 +++++++++++++---\n upload-pack.c                             |   39 ++++-\n 42 files changed, 1420 insertions(+), 65 deletions(-)\n create mode 100644 narrow-tree.c\n create mode 100644 narrow-tree.h\n create mode 100755 t/t0062-narrow-pathspec.sh\n create mode 100755 t/t0063-narrow-repo.sh\n create mode 100755 t/t1013-read-tree-narrow.sh\n create mode 100755 t/t7510-commit-narrow.sh\n create mode 100755 t/t9999-narrow.sh\n create mode 100644 test-get-pathspec.c\n"},{"id":"149939","messageId":"1283669264-15759-2-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 01/17] rev-list: do not do commit simplification if simplify_history = 0","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:28Z","receivedAt":"2010-09-05T06:47:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is just a workaround to make my series work. Currently, commit\nchain will be simplified if pathspecs are given. That would make\npack-objects generate commit chain with gaps.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Equivalence of Elijah's evil commit, 14/15.\n\n builtin/pack-objects.c |    1 +\n revision.c             |    2 +-\n upload-pack.c          |    1 +\n 3 files changed, 3 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 0e81673..789f6bf 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -2072,6 +2072,7 @@ static void get_object_list(int ac, const char **av)\n \n \tinit_revisions(&revs, NULL);\n \tsave_commit_buffer = 0;\n+\trevs.simplify_history = 0;\n \tsetup_revisions(ac, av, &revs, NULL);\n \n \twhile (fgets(line, sizeof(line), stdin) != NULL) {\ndiff --git a/revision.c b/revision.c\nindex 55c4586..ea64970 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -372,7 +372,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)\n \t/*\n \t * If we don't do pruning, everything is interesting\n \t */\n-\tif (!revs->prune)\n+\tif (!revs->prune || !revs->simplify_history)\n \t\treturn;\n \n \tif (!commit->tree)\ndiff --git a/upload-pack.c b/upload-pack.c\nindex fc79dde..cc1983f 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -115,6 +115,7 @@ static int do_rev_list(int in, int out, void *user_data)\n \trevs.tag_objects = 1;\n \trevs.tree_objects = 1;\n \trevs.blob_objects = 1;\n+\trevs.simplify_history = 0;\n \tif (use_thin_pack)\n \t\trevs.edge_hint = 1;\n \n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149941","messageId":"1283669264-15759-3-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 02/17] tree.c: add path_to_sha1()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:29Z","receivedAt":"2010-09-05T06:47:29Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Given a tree and a path, it will traverse down the tree, follow the\npath and return sha1 of the path.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n tree.c |   35 +++++++++++++++++++++++++++++++++++\n tree.h |    2 ++\n 2 files changed, 37 insertions(+), 0 deletions(-)\n\ndiff --git a/tree.c b/tree.c\nindex 5ab90af..2c7ae45 100644\n--- a/tree.c\n+++ b/tree.c\n@@ -284,3 +284,38 @@ struct tree *parse_tree_indirect(const unsigned char *sha1)\n \t\t\tparse_object(obj->sha1);\n \t} while (1);\n }\n+\n+int path_to_tree_sha1(unsigned char *newsha1, const unsigned char *sha1, const char *path)\n+{\n+\tstruct name_entry entry;\n+\tstruct tree_desc desc;\n+\tenum object_type type;\n+\tunsigned long size;\n+\tvoid *buffer;\n+\tconst char *slash;\n+\tint len;\n+\n+\tbuffer = read_sha1_file(sha1, &type, &size);\n+\tif (!buffer || type != OBJ_TREE)\n+\t\tdie(\"%s is not a tree\", sha1_to_hex(sha1));\n+\n+\tslash = strchr(path, '/');\n+\tlen = slash ? slash - path : strlen(path);\n+\n+\tinit_tree_desc(&desc, buffer, size);\n+\twhile (tree_entry(&desc, &entry)) {\n+\t\tif (!S_ISDIR(entry.mode))\n+\t\t\tcontinue;\n+\t\tif (!strncmp(entry.path, path, len)) {\n+\t\t\tfree(buffer);\n+\t\t\tif (slash)\n+\t\t\t\treturn path_to_tree_sha1(newsha1, entry.sha1, slash+1);\n+\t\t\telse {\n+\t\t\t\thashcpy(newsha1, entry.sha1);\n+\t\t\t\treturn 1;\n+\t\t\t}\n+\t\t}\n+\t}\n+\tfree(buffer);\n+\treturn 0;\n+}\ndiff --git a/tree.h b/tree.h\nindex 2ff01a4..d246a6c 100644\n--- a/tree.h\n+++ b/tree.h\n@@ -30,4 +30,6 @@ extern int read_tree_recursive(struct tree *tree,\n \n extern int read_tree(struct tree *tree, int stage, const char **paths);\n \n+extern int path_to_tree_sha1(unsigned char *newsha1, const unsigned char *sha1, const char *path);\n+\n #endif /* TREE_H */\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149942","messageId":"1283669264-15759-4-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 03/17] Introduce $GIT_DIR/narrow","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:30Z","receivedAt":"2010-09-05T06:47:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This file contains a sorted list of narrow prefix, used in narrow\nrepositories.\n\nrev-parse also learns --narrow-prefix to print $GIT_DIR/narrow out\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-rev-parse.txt        |    3 +\n Documentation/gitrepository-layout.txt |    5 ++\n Makefile                               |    2 +\n builtin/rev-parse.c                    |    8 +++\n cache.h                                |    3 +\n environment.c                          |    2 +\n narrow-tree.c                          |  106 ++++++++++++++++++++++++++++++++\n narrow-tree.h                          |    3 +\n t/t0063-narrow-repo.sh                 |   46 ++++++++++++++\n 9 files changed, 178 insertions(+), 0 deletions(-)\n create mode 100644 narrow-tree.c\n create mode 100644 narrow-tree.h\n create mode 100755 t/t0063-narrow-repo.sh\n\ndiff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt\nindex be4c053..5d5e77b 100644\n--- a/Documentation/git-rev-parse.txt\n+++ b/Documentation/git-rev-parse.txt\n@@ -148,6 +148,9 @@ shown.  If the pattern does not contain a globbing character (`?`,\n --is-bare-repository::\n \tWhen the repository is bare print \"true\", otherwise \"false\".\n \n+--narrow-prefix::\n+\tPrint narrow prefix git reads from $GIT_DIR/narrow.\n+\n --local-env-vars::\n \tList the GIT_* environment variables that are local to the\n \trepository (e.g. GIT_DIR or GIT_WORK_TREE, but not GIT_EDITOR).\ndiff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt\nindex eb3d040..82e0350 100644\n--- a/Documentation/gitrepository-layout.txt\n+++ b/Documentation/gitrepository-layout.txt\n@@ -193,6 +193,11 @@ shallow::\n \tand maintained by shallow clone mechanism.  See `--depth`\n \toption to linkgit:git-clone[1] and linkgit:git-fetch[1].\n \n+narrow::\n+\tThis file contains narrow prefix in sorted order. It is\n+\tinternally used and maintained by narrow clone mechanism.\n+\tSee `--narrow-tree` option to linkgit:git-clone[1].\n+\n SEE ALSO\n --------\n linkgit:git-init[1],\ndiff --git a/Makefile b/Makefile\nindex b4745a5..f1aaba9 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -525,6 +525,7 @@ LIB_H += sigchain.h\n LIB_H += strbuf.h\n LIB_H += string-list.h\n LIB_H += submodule.h\n+LIB_H += narrow-tree.h\n LIB_H += tag.h\n LIB_H += transport.h\n LIB_H += tree.h\n@@ -629,6 +630,7 @@ LIB_OBJS += sigchain.o\n LIB_OBJS += strbuf.o\n LIB_OBJS += string-list.o\n LIB_OBJS += submodule.o\n+LIB_OBJS += narrow-tree.o\n LIB_OBJS += symlinks.o\n LIB_OBJS += tag.o\n LIB_OBJS += trace.o\ndiff --git a/builtin/rev-parse.c b/builtin/rev-parse.c\nindex a5a1c86..590df6f 100644\n--- a/builtin/rev-parse.c\n+++ b/builtin/rev-parse.c\n@@ -675,6 +675,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)\n \t\t\t\t\t\t: \"false\");\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strcmp(arg, \"--narrow-prefix\")) {\n+\t\t\t\tconst char **p = get_narrow_prefix();\n+\t\t\t\tif (!p)\n+\t\t\t\t\tcontinue;\n+\t\t\t\twhile (*p)\n+\t\t\t\t\tprintf(\"%s\\n\", *p++);\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!prefixcmp(arg, \"--since=\")) {\n \t\t\t\tshow_datestring(\"--max-age=\", arg+8);\n \t\t\t\tcontinue;\ndiff --git a/cache.h b/cache.h\nindex eb77e1d..d09c4fc 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1105,4 +1105,7 @@ const char *split_cmdline_strerror(int cmdline_errno);\n /* builtin/merge.c */\n int checkout_fast_forward(const unsigned char *from, const unsigned char *to);\n \n+/* narrow-tree.c */\n+extern const char **get_narrow_prefix();\n+\n #endif /* CACHE_H */\ndiff --git a/environment.c b/environment.c\nindex 83d38d3..41fcbd4 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -8,6 +8,7 @@\n  * are.\n  */\n #include \"cache.h\"\n+#include \"narrow-tree.h\"\n \n char git_default_email[MAX_GITNAME];\n char git_default_name[MAX_GITNAME];\n@@ -105,6 +106,7 @@ static void setup_git_env(void)\n \t\tgit_graft_file = git_pathdup(\"info/grafts\");\n \tif (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))\n \t\tread_replace_refs = 0;\n+\tcheck_narrow_prefix();\n }\n \n int is_bare_repository(void)\ndiff --git a/narrow-tree.c b/narrow-tree.c\nnew file mode 100644\nindex 0000000..85dbab4\n--- /dev/null\n+++ b/narrow-tree.c\n@@ -0,0 +1,106 @@\n+#include \"cache.h\"\n+#include \"narrow-tree.h\"\n+\n+static const char **narrow_prefix;\n+static char *narrow_buf;\n+\n+int valid_narrow_prefix(const char *prefix, const char *prev_prefix, int quiet)\n+{\n+\tint len = strlen(prefix);\n+\n+\tif (!*prefix) {\n+\t\tif (!quiet)\n+\t\t\terror(\"Empty line in $GIT_DIR/narrow\");\n+\t\treturn 0;\n+\t}\n+\n+\tif (prefix[len-1] == '/') {\n+\t\tif (!quiet)\n+\t\t\terror(\"Trailing slash not allowed in $GIT_DIR: %s\", prefix);\n+\t\treturn 0;\n+\t}\n+\n+\tif (prev_prefix) {\n+\t\tif (strcmp(prev_prefix, prefix) >= 0) {\n+\t\t\tif (!quiet)\n+\t\t\t\terror(\"$GIT_DIR/narrow is unsorted at %s\", prefix);\n+\t\t\treturn 0;\n+\t\t}\n+\t\tlen = strlen(prev_prefix);\n+\t\tif (!strncmp(prev_prefix, prefix, len) &&\n+\t\t    prefix[len] == '/') {\n+\t\t\tif (!quiet) {\n+\t\t\t\terror(\"$GIT_DIR/narrow has nested prefix (%s and %s)\",\n+\t\t\t\t      prev_prefix, prefix);\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn 1;\n+}\n+\n+int check_narrow_prefix()\n+{\n+\tstruct stat st;\n+\tint fd, i, n, len;\n+\tchar *p, *pp;\n+\n+\tif (stat(git_path(\"narrow\"), &st) || st.st_size == 0)\n+\t\treturn 0;\n+\n+\tnarrow_buf = xmalloc(st.st_size+2); /* NULL and \\n */\n+\n+\tfd = open(git_path(\"narrow\"), O_RDONLY);\n+\tif (fd == -1)\n+\t\treturn 0;\n+\n+\tif (xread(fd, narrow_buf, st.st_size) != st.st_size)\n+\t\tdie(\"failed to read $GIT_DIR/narrow\");\n+\tclose(fd);\n+\n+\tif (narrow_buf[st.st_size-1] == '\\n')\n+\t\tnarrow_buf[st.st_size] = '\\0';\n+\telse {\n+\t\tnarrow_buf[st.st_size] = '\\n';\n+\t\tnarrow_buf[st.st_size+1] = '\\0';\n+\t}\n+\tn = 0;\n+\tfor (p = narrow_buf; *p; p = strchr(p, '\\n')+1)\n+\t\tn++;\n+\tif (!n)\n+\t\treturn 0;\n+\tnarrow_prefix = xmalloc(sizeof(*narrow_prefix)*(n+1));\n+\tp = narrow_buf;\n+\tfor (i = 0; i < n; i++) {\n+\t\tpp = p;\n+\t\tp = strchr(p, '\\n') + 1;\n+\t\tlen = p - pp;\n+\t\twhile (len &&\n+\t\t       (pp[len-1] == '\\n' ||\n+\t\t\tpp[len-1] == '\\r'))\n+\t\t\tlen--;\n+\t\tpp[len] = '\\0';\n+\t\tif (!valid_narrow_prefix(pp, i ? narrow_prefix[i-1] : NULL, 0))\n+\t\t\tdie(\"Invalid $GIT_DIR/narrow\");\n+\t\tnarrow_prefix[i] = pp;\n+\t}\n+\tnarrow_prefix[n] = NULL;\n+\treturn 0;\n+}\n+\n+const char **get_narrow_prefix()\n+{\n+\treturn narrow_prefix;\n+}\n+\n+char *get_narrow_string()\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tconst char **prefix = get_narrow_prefix();\n+\twhile (*prefix) {\n+\t\tstrbuf_addstr(&sb, *prefix);\n+\t\tstrbuf_addch(&sb, '\\n');\n+\t\tprefix++;\n+\t}\n+\treturn strbuf_detach(&sb, NULL);\n+}\ndiff --git a/narrow-tree.h b/narrow-tree.h\nnew file mode 100644\nindex 0000000..2097436\n--- /dev/null\n+++ b/narrow-tree.h\n@@ -0,0 +1,3 @@\n+extern int valid_narrow_prefix(const char *prefix, const char *prev_prefix, int quiet);\n+extern int check_narrow_prefix();\n+extern char *get_narrow_string();\ndiff --git a/t/t0063-narrow-repo.sh b/t/t0063-narrow-repo.sh\nnew file mode 100755\nindex 0000000..51b753d\n--- /dev/null\n+++ b/t/t0063-narrow-repo.sh\n@@ -0,0 +1,46 @@\n+#!/bin/sh\n+\n+test_description='narrow index'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'empty $GIT_DIR/narrow' '\n+\t: >.git/narrow &&\n+\tgit rev-parse --narrow-prefix >result &&\n+\t: >expected\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'empty line' '\n+\techo >.git/narrow &&\n+\ttest_must_fail git rev-parse --narrow-prefix\n+'\n+\n+test_expect_success 'single prefix' '\n+\techo a >.git/narrow &&\n+\tgit rev-parse --narrow-prefix >result &&\n+\techo a >expected\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'trailing slash in prefix' '\n+\techo a/ >.git/narrow &&\n+\ttest_must_fail git rev-parse --narrow-prefix\n+'\n+\n+test_expect_success 'sorted multiple prefix' '\n+\techo a >.git/narrow &&\n+\techo b >>.git/narrow &&\n+\tgit rev-parse --narrow-prefix >result &&\n+\techo a >expected\n+\techo b >>expected\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success 'unsorted multiple prefix' '\n+\techo b >.git/narrow &&\n+\techo a >>.git/narrow &&\n+\ttest_must_fail git rev-parse --narrow-prefix\n+'\n+\n+test_done\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149944","messageId":"1283669264-15759-5-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 04/17] index: make narrow index incompatible with older git","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:31Z","receivedAt":"2010-09-05T06:47:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Index in narrow repos is also narrowed and should not be used to\ncreate commits straight away (to be explained later on). On the other\nhand, while normal index can still be used in narrow repos, it should\nbe narrowed when the repo becomes narrow. Disallow that case too.\n\nAdd non-optional extension \"narw\" so that older git will refuse to\nread it. Also check narrow prefix in index against $GIT_DIR/narrow,\njust in case.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n cache.h                |    1 +\n read-cache.c           |   32 ++++++++++++++++++++++++++++++--\n t/t0063-narrow-repo.sh |   28 ++++++++++++++++++++++++++++\n 3 files changed, 59 insertions(+), 2 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex d09c4fc..88a2ec6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -292,6 +292,7 @@ struct index_state {\n \tstruct string_list *resolve_undo;\n \tstruct cache_tree *cache_tree;\n \tstruct cache_time timestamp;\n+\tchar *narrow_prefix;\n \tvoid *alloc;\n \tunsigned name_hash_initialized : 1,\n \t\t initialized : 1;\ndiff --git a/read-cache.c b/read-cache.c\nindex 1f42473..250013c 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -12,6 +12,7 @@\n #include \"commit.h\"\n #include \"blob.h\"\n #include \"resolve-undo.h\"\n+#include \"narrow-tree.h\"\n \n static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);\n \n@@ -25,8 +26,9 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int reall\n  */\n \n #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )\n-#define CACHE_EXT_TREE 0x54524545\t/* \"TREE\" */\n-#define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* \"REUC\" */\n+#define CACHE_EXT_TREE\t\t0x54524545\t/* \"TREE\" */\n+#define CACHE_EXT_RESOLVE_UNDO\t0x52455543\t/* \"REUC\" */\n+#define CACHE_EXT_NARROW\t0x6e617277\t/* \"narw\" */\n \n struct index_state the_index;\n \n@@ -1188,6 +1190,9 @@ static int read_index_extension(struct index_state *istate,\n \tcase CACHE_EXT_RESOLVE_UNDO:\n \t\tistate->resolve_undo = resolve_undo_read(data, sz);\n \t\tbreak;\n+\tcase CACHE_EXT_NARROW:\n+\t\tistate->narrow_prefix = xstrdup(data);\n+\t\tbreak;\n \tdefault:\n \t\tif (*ext < 'A' || 'Z' < *ext)\n \t\t\treturn error(\"index uses %.4s extension, which we do not understand\",\n@@ -1352,6 +1357,18 @@ int read_index_from(struct index_state *istate, const char *path)\n \t\tsrc_offset += extsize;\n \t}\n \tmunmap(mmap, mmap_size);\n+\n+\tif ((!get_narrow_prefix() && !istate->narrow_prefix))\n+\t\t;\t\t/* good */\n+\telse if (get_narrow_prefix() && istate->narrow_prefix) {\n+\t\tchar *buf = get_narrow_string();\n+\t\tif (strcmp(buf, istate->narrow_prefix))\n+\t\t\tdie(\"Invalid index, narrow prefix does not match $GIT_DIR/narrow\");\n+\t\tfree(buf);\n+\t}\n+\telse\n+\t\tdie(\"Invalid index, not suitable for narrow repository\");\n+\n \treturn istate->cache_nr;\n \n unmap:\n@@ -1378,6 +1395,8 @@ int discard_index(struct index_state *istate)\n \tfree(istate->alloc);\n \tistate->alloc = NULL;\n \tistate->initialized = 0;\n+\tfree(istate->narrow_prefix);\n+\tistate->narrow_prefix = NULL;\n \n \t/* no need to throw away allocated active_cache */\n \treturn 0;\n@@ -1607,6 +1626,15 @@ int write_index(struct index_state *istate, int newfd)\n \t\tif (err)\n \t\t\treturn -1;\n \t}\n+\tif (get_narrow_prefix()) {\n+\t\tchar *buf = get_narrow_string();\n+\t\tint len = strlen(buf)+1;\n+\t\terr = write_index_ext_header(&c, newfd, CACHE_EXT_NARROW, len) < 0 ||\n+\t\t\tce_write(&c, newfd, buf, len) < 0;\n+\t\tfree(buf);\n+\t\tif (err)\n+\t\t\treturn -1;\n+\t}\n \n \tif (ce_flush(&c, newfd) || fstat(newfd, &st))\n \t\treturn -1;\ndiff --git a/t/t0063-narrow-repo.sh b/t/t0063-narrow-repo.sh\nindex 51b753d..926802c 100755\n--- a/t/t0063-narrow-repo.sh\n+++ b/t/t0063-narrow-repo.sh\n@@ -43,4 +43,32 @@ test_expect_success 'unsorted multiple prefix' '\n \ttest_must_fail git rev-parse --narrow-prefix\n '\n \n+test_expect_success 'create narrow index' '\n+\techo a >.git/narrow &&\n+\t: >foo\n+\tgit add foo &&\n+\ttest -f .git/index\n+'\n+\n+test_expect_success '$GIT_DIR/narrow and index do not match' '\n+\techo b >.git/narrow &&\n+\ttest_must_fail git add foo\n+'\n+\n+test_expect_success 'narrow index and normal repo' '\n+\trm .git/narrow &&\n+\ttest_must_fail git add foo\n+'\n+\n+test_expect_success 'turn to normal index again' '\n+\trm .git/index &&\n+\tgit add foo &&\n+\ttest -f .git/index\n+'\n+\n+test_expect_success 'normal index and narrow repo' '\n+\techo a >.git/narrow &&\n+\ttest_must_fail git add foo\n+'\n+\n test_done\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149943","messageId":"1283669264-15759-6-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 05/17] pack-objects: support narrow packs with pathspecs","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:32Z","receivedAt":"2010-09-05T06:47:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-pack-objects.txt |    2 +-\n builtin/pack-objects.c             |   18 ++++++++++++++++--\n t/t6000-rev-list-misc.sh           |    9 +++++++++\n 3 files changed, 26 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt\nindex 8ed09c0..cad8c06 100644\n--- a/Documentation/git-pack-objects.txt\n+++ b/Documentation/git-pack-objects.txt\n@@ -13,7 +13,7 @@ SYNOPSIS\n \t[--no-reuse-delta] [--delta-base-offset] [--non-empty]\n \t[--local] [--incremental] [--window=N] [--depth=N]\n \t[--revs [--unpacked | --all]*] [--stdout | base-name]\n-\t[--keep-true-parents] < object-list\n+\t[--keep-true-parents] [ -- path... ] < object-list\n \n \n DESCRIPTION\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 789f6bf..26044b8 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -30,8 +30,8 @@ static const char pack_usage[] =\n   \"        [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset]\\n\"\n   \"        [--threads=N] [--non-empty] [--revs [--unpacked | --all]*]\\n\"\n   \"        [--reflog] [--stdout | base-name] [--include-tag]\\n\"\n-  \"        [--keep-unreachable | --unpack-unreachable \\n\"\n-  \"        [<ref-list | <object-list]\";\n+  \"        [--keep-unreachable | --unpack-unreachable]\\n\"\n+  \"        [ -- path ... ] [<ref-list | <object-list]\";\n \n struct object_entry {\n \tstruct pack_idx_entry idx;\n@@ -2278,6 +2278,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \t\t\tgrafts_replace_parents = 0;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--\")) {\n+\t\t\tif (!pack_to_stdout)\n+\t\t\t\tdie(\"either --stdout of pack basename must be specified\");\n+\t\t\tbreak;\n+\t\t}\n \t\tusage(pack_usage);\n \t}\n \n@@ -2300,6 +2305,15 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \tif (pack_to_stdout != !base_name)\n \t\tusage(pack_usage);\n \n+\tif (!argv[i] || !strcmp(argv[i], \"--\")) {\n+\t\tif (rp_ac+(argc-i)+1 >= rp_ac_alloc) {\n+\t\t\trp_ac_alloc = rp_ac + (argc-i) + 1;\n+\t\t\trp_av = xrealloc(rp_av, rp_ac_alloc * sizeof(*rp_av));\n+\t\t}\n+\t\tmemcpy(rp_av+rp_ac, argv+i, sizeof(*argv)*(argc-i));\n+\t\trp_ac += argc-i;\n+\t}\n+\n \tif (!pack_to_stdout && !pack_size_limit)\n \t\tpack_size_limit = pack_size_limit_cfg;\n \tif (pack_to_stdout && pack_size_limit)\ndiff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh\nindex b6a7c9c..0386313 100755\n--- a/t/t6000-rev-list-misc.sh\n+++ b/t/t6000-rev-list-misc.sh\n@@ -31,6 +31,15 @@ test_expect_success 'rev-list --objects with pathspecs and deeper paths' '\n \t! grep unwanted_file output\n '\n \n+test_expect_success 'pack-objects with pathspecs' '\n+\techo HEAD|git pack-objects --revs --stdout -- foo > output.pack &&\n+\tgit index-pack --stdin -o output.idx < output.pack &&\n+\tgit verify-pack -v output.pack >output &&\n+\tgrep \"^e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 blob\" output &&\n+\ttest \"$(grep blob output|wc -l)\" = 1 &&\n+\ttest \"$(grep tree output|wc -l)\" = 2\n+'\n+\n test_expect_success 'rev-list --objects with pathspecs and copied files' '\n \tgit checkout --orphan junio-testcase &&\n \tgit rm -rf . &&\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149945","messageId":"1283669264-15759-7-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 06/17] {fetch,upload}-pack: support narrow repository","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:33Z","receivedAt":"2010-09-05T06:47:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If $GIT_DIR/narrow is set, fetch-pack will send \"narrow-tree\" lines to\nupload-pack, where the prefixes are passed to rev-list mechanism to\nproduce a narrow pack.\n\n\"narrow-tree\" capability is required for this to work.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-fetch-pack.txt          |    3 ++\n Documentation/technical/pack-protocol.txt |    3 ++\n builtin/fetch-pack.c                      |    9 +++++++\n upload-pack.c                             |   38 +++++++++++++++++++++++++++-\n 4 files changed, 51 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt\nindex 4a8487c..19b0d92 100644\n--- a/Documentation/git-fetch-pack.txt\n+++ b/Documentation/git-fetch-pack.txt\n@@ -25,6 +25,9 @@ This command degenerates to download everything to complete the\n asked refs from the remote side when the local side does not\n have a common ancestor commit.\n \n+In narrow repositories, it asks 'git-upload-pack' to send only objects\n+within narrow area defined in $GIT_DIR/narrow.\n+\n \n OPTIONS\n -------\ndiff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt\nindex 369f91d..39474d2 100644\n--- a/Documentation/technical/pack-protocol.txt\n+++ b/Documentation/technical/pack-protocol.txt\n@@ -215,6 +215,9 @@ discovery phase as 'want' lines. Clients MUST send at least one\n obj-id in a 'want' command which did not appear in the response\n obtained through ref discovery.\n \n+If client is requesting narrow clone, it must send 'narrow-tree'\n+lines before 'want' lines.\n+\n If client is requesting a shallow clone, it will now send a 'deepen'\n line with the depth it is requesting.\n \ndiff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c\nindex dbd8b7b..591e0e4 100644\n--- a/builtin/fetch-pack.c\n+++ b/builtin/fetch-pack.c\n@@ -37,6 +37,7 @@ static int marked;\n \n static struct commit_list *rev_list;\n static int non_common_revs, multi_ack, use_sideband;\n+const char **narrow_prefix;\n \n static void rev_list_push(struct commit *commit, int mark)\n {\n@@ -237,6 +238,12 @@ static int find_common(int fd[2], unsigned char *result_sha1,\n \tfor_each_ref(rev_list_insert_ref, NULL);\n \n \tfetching = 0;\n+\tnarrow_prefix = get_narrow_prefix();\n+\tif (narrow_prefix) {\n+\t\tconst char **p = narrow_prefix;\n+\t\twhile (*p)\n+\t\t\tpacket_buf_write(&req_buf, \"narrow-tree %s\\n\", *p++);\n+\t}\n \tfor ( ; refs ; refs = refs->next) {\n \t\tunsigned char *remote = refs->old_sha1;\n \t\tconst char *remote_hex;\n@@ -692,6 +699,8 @@ static struct ref *do_fetch_pack(int fd[2],\n \n \tif (is_repository_shallow() && !server_supports(\"shallow\"))\n \t\tdie(\"Server does not support shallow clients\");\n+\tif (narrow_prefix && !server_supports(\"narrow-tree\"))\n+\t\tdie(\"Server does not support narrow-tree\");\n \tif (server_supports(\"multi_ack_detailed\")) {\n \t\tif (args.verbose)\n \t\t\tfprintf(stderr, \"Server supports multi_ack_detailed\\n\");\ndiff --git a/upload-pack.c b/upload-pack.c\nindex cc1983f..f3747e5 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -41,6 +41,8 @@ static int use_sideband;\n static int debug_fd;\n static int advertise_refs;\n static int stateless_rpc;\n+const char **narrow_prefix;\n+int narrow_prefix_nr;\n \n static void reset_timeout(void)\n {\n@@ -118,6 +120,10 @@ static int do_rev_list(int in, int out, void *user_data)\n \trevs.simplify_history = 0;\n \tif (use_thin_pack)\n \t\trevs.edge_hint = 1;\n+\tif (narrow_prefix) {\n+\t\trevs.prune_data = narrow_prefix;\n+\t\tnarrow_prefix[narrow_prefix_nr] = NULL;\n+\t}\n \n \tfor (i = 0; i < want_obj.nr; i++) {\n \t\tstruct object *o = want_obj.objects[i].item;\n@@ -154,9 +160,10 @@ static void create_pack_file(void)\n \t\t\"corruption on the remote side.\";\n \tint buffered = -1;\n \tssize_t sz;\n-\tconst char *argv[10];\n+\tconst char **argv;\n \tint arg = 0;\n \n+\targv = xmalloc(sizeof(*argv)*(11+narrow_prefix_nr));\n \tif (shallow_nr) {\n \t\tmemset(&rev_list, 0, sizeof(rev_list));\n \t\trev_list.proc = do_rev_list;\n@@ -180,6 +187,11 @@ static void create_pack_file(void)\n \t\targv[arg++] = \"--delta-base-offset\";\n \tif (use_include_tag)\n \t\targv[arg++] = \"--include-tag\";\n+\tif (narrow_prefix) {\n+\t\targv[arg++] = \"--\";\n+\t\tmemcpy(argv+arg, narrow_prefix, narrow_prefix_nr*sizeof(*argv));\n+\t\targ += narrow_prefix_nr;\n+\t}\n \targv[arg++] = NULL;\n \n \tmemset(&pack_objects, 0, sizeof(pack_objects));\n@@ -307,6 +319,7 @@ static void create_pack_file(void)\n \t\terror(\"git upload-pack: git-pack-objects died with error.\");\n \t\tgoto fail;\n \t}\n+\tfree(argv);\n \tif (shallow_nr && finish_async(&rev_list))\n \t\tgoto fail;\t/* error was already reported */\n \n@@ -500,6 +513,22 @@ static void receive_needs(void)\n \t\tif (debug_fd)\n \t\t\twrite_in_full(debug_fd, line, len);\n \n+\t\tif (!prefixcmp(line, \"narrow-tree \")) {\n+\t\t\tchar *s;\n+\t\t\tint len;\n+\t\t\tlen = strlen(line+12);\n+\t\t\ts = malloc(len+1);\n+\t\t\tmemcpy(s, line+12, len-1);\n+\t\t\ts[len-1] = '\\0'; /* \\n */\n+\t\t\tif (s[len-2] == '/')\n+\t\t\t\tdie(\"trailing slash in narrow prefix not allowed, %s\", line);\n+\n+\t\t\tnarrow_prefix_nr++;\n+\t\t\tnarrow_prefix = xrealloc(narrow_prefix,\n+\t\t\t\t\t\t sizeof(*narrow_prefix)*(narrow_prefix_nr+1));\n+\t\t\tnarrow_prefix[narrow_prefix_nr-1] = s;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!prefixcmp(line, \"shallow \")) {\n \t\t\tunsigned char sha1[20];\n \t\t\tstruct object *object;\n@@ -563,6 +592,9 @@ static void receive_needs(void)\n \tif (!use_sideband && daemon_mode)\n \t\tno_progress = 1;\n \n+\tif (narrow_prefix)\n+\t\tuse_thin_pack = 0;\n+\n \tif (depth == 0 && shallows.nr == 0)\n \t\treturn;\n \tif (depth > 0) {\n@@ -620,7 +652,7 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo\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\" include-tag multi_ack_detailed\";\n+\t\t\" include-tag multi_ack_detailed narrow-tree\";\n \tstruct object *o = parse_object(sha1);\n \n \tif (!o)\n@@ -725,6 +757,8 @@ int main(int argc, char **argv)\n \t\tdie(\"'%s' does not appear to be a git repository\", dir);\n \tif (is_repository_shallow())\n \t\tdie(\"attempt to fetch/clone from a shallow repository\");\n+\tif (get_narrow_prefix())\n+\t\tdie(\"attempt to fetch/clone from a narrow repository\");\n \tif (getenv(\"GIT_DEBUG_SEND_PACK\"))\n \t\tdebug_fd = atoi(getenv(\"GIT_DEBUG_SEND_PACK\"));\n \tupload_pack();\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149946","messageId":"1283669264-15759-8-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 07/17] unpack-trees: split traverse_trees() code into a separate function","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:34Z","receivedAt":"2010-09-05T06:47:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n unpack-trees.c |   64 ++++++++++++++++++++++++++++++-------------------------\n 1 files changed, 35 insertions(+), 29 deletions(-)\n\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 3c7a7c9..4889e24 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -792,6 +792,39 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \treturn mask;\n }\n \n+static int unpack_traverse(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)\n+{\n+\tconst char *prefix = o->prefix ? o->prefix : \"\";\n+\tstruct traverse_info info;\n+\n+\tif (!len)\n+\t\treturn 0;\n+\n+\tsetup_traverse_info(&info, prefix);\n+\tinfo.fn = unpack_callback;\n+\tinfo.data = o;\n+\tinfo.show_all_errors = o->show_all_errors;\n+\n+\tif (o->prefix) {\n+\t\t/*\n+\t\t * Unpack existing index entries that sort before the\n+\t\t * prefix the tree is spliced into.  Note that o->merge\n+\t\t * is always true in this case.\n+\t\t */\n+\t\twhile (1) {\n+\t\t\tstruct cache_entry *ce = next_cache_entry(o);\n+\t\t\tif (!ce)\n+\t\t\t\tbreak;\n+\t\t\tif (ce_in_traverse_path(ce, &info))\n+\t\t\t\tbreak;\n+\t\t\tif (unpack_index_entry(ce, o) < 0)\n+\t\t\t\treturn 1;\n+\t\t}\n+\t}\n+\n+\treturn traverse_trees(len, t, &info) < 0;\n+}\n+\n /*\n  * N-way merge \"len\" trees.  Returns 0 on success, -1 on failure to manipulate the\n  * resulting index, -2 on failure to reflect the changes to the work tree.\n@@ -831,35 +864,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \t\tdfc = xcalloc(1, cache_entry_size(0));\n \to->df_conflict_entry = dfc;\n \n-\tif (len) {\n-\t\tconst char *prefix = o->prefix ? o->prefix : \"\";\n-\t\tstruct traverse_info info;\n-\n-\t\tsetup_traverse_info(&info, prefix);\n-\t\tinfo.fn = unpack_callback;\n-\t\tinfo.data = o;\n-\t\tinfo.show_all_errors = o->show_all_errors;\n-\n-\t\tif (o->prefix) {\n-\t\t\t/*\n-\t\t\t * Unpack existing index entries that sort before the\n-\t\t\t * prefix the tree is spliced into.  Note that o->merge\n-\t\t\t * is always true in this case.\n-\t\t\t */\n-\t\t\twhile (1) {\n-\t\t\t\tstruct cache_entry *ce = next_cache_entry(o);\n-\t\t\t\tif (!ce)\n-\t\t\t\t\tbreak;\n-\t\t\t\tif (ce_in_traverse_path(ce, &info))\n-\t\t\t\t\tbreak;\n-\t\t\t\tif (unpack_index_entry(ce, o) < 0)\n-\t\t\t\t\tgoto return_failed;\n-\t\t\t}\n-\t\t}\n-\n-\t\tif (traverse_trees(len, t, &info) < 0)\n-\t\t\tgoto return_failed;\n-\t}\n+\tif (unpack_traverse(len, t, o))\n+\t\tgoto return_failed;\n \n \t/* Any left-over entries in the index? */\n \tif (o->merge) {\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149947","messageId":"1283669264-15759-9-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 08/17] unpack-trees: support unpack trees in narrow repository","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:35Z","receivedAt":"2010-09-05T06:47:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"By definition, narrow repository is incomplete. It does not even have\nenough tree for a single commit. So populating a full index is\nimpossible.\n\nBecause of this, unpack_trees() is modified to only unpack trees\nspecified in $GIT_DIR/narrow, which narrow repo has all needed trees\nand blobs. This makes the result index unsuitable for creating commits\n(you would have \"narrow commits\", which are not what anybody\nwants). An extra step will be needed to create a proper commit.\n\nOn the other hand, because all paths in narrow index are reachable,\nand other worktree/index or tree/index operations rely on\nunpack_trees() to traverse, which is already narrow-adapted, most\noperations will not need modification to work in narrow repositories.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Will probably drop this and the previous patch in favor of Elijah's\n 04/15 and 05/15.\n\n\n t/t1013-read-tree-narrow.sh |   72 ++++++++++++++++++++++++++++++++++\n unpack-trees.c              |   91 ++++++++++++++++++++++++++++++++++++++++++-\n 2 files changed, 162 insertions(+), 1 deletions(-)\n create mode 100755 t/t1013-read-tree-narrow.sh\n\ndiff --git a/t/t1013-read-tree-narrow.sh b/t/t1013-read-tree-narrow.sh\nnew file mode 100755\nindex 0000000..8fa37d9\n--- /dev/null\n+++ b/t/t1013-read-tree-narrow.sh\n@@ -0,0 +1,72 @@\n+#!/bin/sh\n+\n+test_description='read-tree in narrow mode'\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\ttest_tick &&\n+\ttest_commit 1 &&\n+\tmkdir t1 t2 t1/t12 &&\n+\techo 0 >f0 &&\n+\techo 10 >t1/f10 &&\n+\techo 120 >t1/t12/f120 &&\n+\techo 20 >t2/f20\n+\tgit add t1 t2 f0 && git commit -m initial &&\n+\tHEAD=`git rev-parse HEAD` &&\n+\tgit rev-parse HEAD | git pack-objects --revs pack -- t1/t12 &&\n+\ttest_create_repo narrow &&\n+\tmv pack-* narrow/.git/objects/pack &&\n+\tcd narrow &&\n+\techo $HEAD >.git/refs/heads/master &&\n+\techo \"ref: refs/heads/master\" >.git/HEAD &&\n+\techo t1/t12 >.git/narrow\n+'\n+\n+test_expect_failure ls-tree '\n+\tgit ls-tree -r HEAD &&\n+\tgit ls-files --stage >result &&\n+\techo \"100644 blob 52bd8e43afb01d0c9747f1fedf2fc94684ee4cc4\tt1/t12/f120\" >expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success read-tree '\n+\tgit read-tree HEAD &&\n+\tgit ls-files --stage >result &&\n+\techo \"100644 52bd8e43afb01d0c9747f1fedf2fc94684ee4cc4 0\tt1/t12/f120\" >expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success checkout '\n+\tgit checkout . &&\n+\ttest_cmp ../t1/t12/f120 t1/t12/f120\n+'\n+\n+cat <<EOF >diff.expected\n+diff --git a/t1/t12/f120 b/t1/t12/f120\n+index 52bd8e4..645fb94 100644\n+--- a/t1/t12/f120\n++++ b/t1/t12/f120\n+@@ -1 +1,2 @@\n+ 120\n++modified\n+EOF\n+\n+test_expect_success diff '\n+\techo modified >>t1/t12/f120 &&\n+\tgit diff >result &&\n+\ttest_cmp diff.expected result\n+'\n+\n+test_expect_success 'diff HEAD' '\n+\tgit diff HEAD >result &&\n+\ttest_cmp diff.expected result\n+'\n+\n+test_expect_success 'diff --cached' '\n+\tgit add -u . &&\n+\tgit diff --cached >result &&\n+\ttest_cmp diff.expected result\n+'\n+\n+test_done\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 4889e24..f945e5f 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -792,7 +792,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \treturn mask;\n }\n \n-static int unpack_traverse(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)\n+static int unpack_traverse_1(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)\n {\n \tconst char *prefix = o->prefix ? o->prefix : \"\";\n \tstruct traverse_info info;\n@@ -826,6 +826,95 @@ static int unpack_traverse(unsigned len, struct tree_desc *t, struct unpack_tree\n }\n \n /*\n+ * path_to_tree_sha1() does not accept tree_desc, so we need to\n+ * process the first tree. path_to_tree_sha1() can then do the rest\n+ */\n+static int find_tree_desc(struct tree_desc *desc, unsigned char *newsha1, const char *path)\n+{\n+\tstruct name_entry entry;\n+\tconst char *slash;\n+\tint len;\n+\n+\tslash = strchr(path, '/');\n+\tlen = slash ? slash - path : strlen(path);\n+\n+\twhile (tree_entry(desc, &entry)) {\n+\t\tif (!S_ISDIR(entry.mode))\n+\t\t\tcontinue;\n+\t\tif (!strncmp(entry.path, path, len)) {\n+\t\t\tif (slash)\n+\t\t\t\treturn path_to_tree_sha1(newsha1, entry.sha1, slash+1);\n+\t\t\telse {\n+\t\t\t\thashcpy(newsha1, entry.sha1);\n+\t\t\t\treturn 1;\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static int unpack_traverse_narrow(unsigned len, struct tree_desc *t,\n+\t\t\t\t  struct unpack_trees_options *o,\n+\t\t\t\t  const char *narrow_prefix)\n+{\n+\tstruct tree_desc *t2;\n+\tchar *prefix = NULL;\n+\tconst char *old_prefix;\n+\tvoid **buf;\n+\tint i, ret;\n+\tt2 = xmalloc(sizeof(*t2)*len);\n+\tbuf = xmalloc(sizeof(*buf)*len);\n+\tfor (i = 0; i < len; i++) {\n+\t\tunsigned char sha1[20];\n+\t\tunsigned long size;\n+\t\tenum object_type type;\n+\n+\t\tif (!find_tree_desc(t+i, sha1, narrow_prefix))\n+\t\t\treturn -1;\n+\t\tbuf[i] = read_sha1_file(sha1, &type, &size);\n+\t\tif (type != OBJ_TREE)\n+\t\t\treturn -1;\n+\t\tinit_tree_desc(t2+i, buf[i], size);\n+\t}\n+\told_prefix = o->prefix;\n+\tif (!old_prefix)\n+\t\tprefix = xstrdup(narrow_prefix);\n+\telse {\n+\t\tprefix = xmalloc(strlen(narrow_prefix) + strlen(o->prefix)+1);\n+\t\tstrcpy(prefix, o->prefix);\n+\t\tstrcat(prefix, narrow_prefix);\n+\t}\n+\to->prefix = prefix;\n+\tret = unpack_traverse_1(len, t2, o);\n+\to->prefix = old_prefix;\n+\tfor (i = 0; i < len; i++)\n+\t\tfree(buf[i]);\n+\tfree(prefix);\n+\tfree(t2);\n+\tfree(buf);\n+\treturn ret;\n+}\n+\n+static int unpack_traverse(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)\n+{\n+\tconst char **p = get_narrow_prefix();\n+\n+\tif (!len)\n+\t\treturn 0;\n+\n+\tif (!p)\n+\t\treturn unpack_traverse_1(len, t, o);\n+\n+\twhile (*p) {\n+\t\tint ret = unpack_traverse_narrow(len, t, o, *p);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tp++;\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n  * N-way merge \"len\" trees.  Returns 0 on success, -1 on failure to manipulate the\n  * resulting index, -2 on failure to reflect the changes to the work tree.\n  */\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149948","messageId":"1283669264-15759-10-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 09/17] cache-tree: only cache tree within narrow area","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:36Z","receivedAt":"2010-09-05T06:47:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n cache-tree.c |   41 +++++++++++++++++++++++++++++++++++++----\n 1 files changed, 37 insertions(+), 4 deletions(-)\n\ndiff --git a/cache-tree.c b/cache-tree.c\nindex c60cf91..6aaf265 100644\n--- a/cache-tree.c\n+++ b/cache-tree.c\n@@ -602,7 +602,8 @@ int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)\n \treturn 0;\n }\n \n-static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)\n+static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree,\n+\t\t\t\t char *base, int baselen)\n {\n \tstruct tree_desc desc;\n \tstruct name_entry entry;\n@@ -611,17 +612,48 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)\n \thashcpy(it->sha1, tree->object.sha1);\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \tcnt = 0;\n+\tif (base && baselen)\n+\t\tbase[baselen++] = '/';\n \twhile (tree_entry(&desc, &entry)) {\n \t\tif (!S_ISDIR(entry.mode))\n \t\t\tcnt++;\n \t\telse {\n \t\t\tstruct cache_tree_sub *sub;\n-\t\t\tstruct tree *subtree = lookup_tree(entry.sha1);\n+\t\t\tstruct tree *subtree;\n+\t\t\tint found = 2; /* all interesting */\n+\t\t\tconst char **p = get_narrow_prefix();\n+\t\t\tint len = 0;\n+\n+\t\t\tif (p) {\n+\t\t\t\tfound = 0; /* not interesting */\n+\t\t\t\tlen = strlen(entry.path);\n+\t\t\t\tif (base)\n+\t\t\t\t\tmemcpy(base+baselen, entry.path, len+1);\n+\t\t\t\twhile (*p) {\n+\t\t\t\t\tif (!prefixcmp(entry.path, *p)) {\n+\t\t\t\t\t\tfound = 2;\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t\t}\n+\t\t\t\t\tif (!prefixcmp(*p, entry.path)) {\n+\t\t\t\t\t\tfound = 1;\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t\t}\n+\t\t\t\t\tp++;\n+\t\t\t\t}\n+\t\t\t\tif (!found) {\n+\t\t\t\t\tcnt++;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tsubtree = lookup_tree(entry.sha1);\n \t\t\tif (!subtree->object.parsed)\n \t\t\t\tparse_tree(subtree);\n \t\t\tsub = cache_tree_sub(it, entry.path);\n \t\t\tsub->cache_tree = cache_tree();\n-\t\t\tprime_cache_tree_rec(sub->cache_tree, subtree);\n+\t\t\tprime_cache_tree_rec(sub->cache_tree, subtree,\n+\t\t\t\t\t     found == 1 ? base : NULL,\n+\t\t\t\t\t     baselen+len);\n \t\t\tcnt += sub->cache_tree->entry_count;\n \t\t}\n \t}\n@@ -630,9 +662,10 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)\n \n void prime_cache_tree(struct cache_tree **it, struct tree *tree)\n {\n+\tchar path[PATH_MAX];\n \tcache_tree_free(it);\n \t*it = cache_tree();\n-\tprime_cache_tree_rec(*it, tree);\n+\tprime_cache_tree_rec(*it, tree, get_narrow_prefix() ? path : NULL, 0);\n }\n \n /*\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149949","messageId":"1283669264-15759-11-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 10/17] get_pathspec(): support narrow pathspec rewriting","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:37Z","receivedAt":"2010-09-05T06:47:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Operations in a narrow repository would only work if you limit\nyourself within narrow area. Usually that means users have to either\nstay inside narrow area, or do \"git blah blah -- narrow/prefix\" so\nthat git won't step on the line. (Of course that only affects commands\nthat take pathspec)\n\nMake their life easier by appending narrow prefix automatically, as\nlong as the given pathspecs are simple enough. In other words, fancy\nwildcards might not work.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore                 |    1 +\n Makefile                   |    1 +\n cache.h                    |    1 +\n setup.c                    |  172 ++++++++++++++++++++++++++++++++++++++++++--\n t/t0062-narrow-pathspec.sh |  150 ++++++++++++++++++++++++++++++++++++++\n test-get-pathspec.c        |   17 +++++\n 6 files changed, 337 insertions(+), 5 deletions(-)\n create mode 100755 t/t0062-narrow-pathspec.sh\n create mode 100644 test-get-pathspec.c\n\ndiff --git a/.gitignore b/.gitignore\nindex fcdd822..af9bae6 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -165,6 +165,7 @@\n /test-delta\n /test-dump-cache-tree\n /test-genrandom\n+/test-get-pathspec\n /test-index-version\n /test-match-trees\n /test-parse-options\ndiff --git a/Makefile b/Makefile\nindex f1aaba9..3bbb571 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -408,6 +408,7 @@ TEST_PROGRAMS_NEED_X += test-date\n TEST_PROGRAMS_NEED_X += test-delta\n TEST_PROGRAMS_NEED_X += test-dump-cache-tree\n TEST_PROGRAMS_NEED_X += test-genrandom\n+TEST_PROGRAMS_NEED_X += test-get-pathspec\n TEST_PROGRAMS_NEED_X += test-match-trees\n TEST_PROGRAMS_NEED_X += test-parse-options\n TEST_PROGRAMS_NEED_X += test-path-utils\ndiff --git a/cache.h b/cache.h\nindex 88a2ec6..9c014ef 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -417,6 +417,7 @@ extern void set_git_work_tree(const char *tree);\n \n #define ALTERNATE_DB_ENVIRONMENT \"GIT_ALTERNATE_OBJECT_DIRECTORIES\"\n \n+extern const char **get_pathspec_narrow(const char *prefix, const char **pathspec, int narrow);\n extern const char **get_pathspec(const char *prefix, const char **pathspec);\n extern void setup_work_tree(void);\n extern const char *setup_git_directory_gently(int *);\ndiff --git a/setup.c b/setup.c\nindex 2769160..c19d53d 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -123,20 +123,61 @@ void verify_non_filename(const char *prefix, const char *arg)\n \t    \"Use '--' to separate filenames from revisions\", arg);\n }\n \n-const char **get_pathspec(const char *prefix, const char **pathspec)\n+static const char **dup_pathspec(const char **pathspec)\n+{\n+\tconst char **p = pathspec;\n+\tint n = 1;\n+\tif (!p)\n+\t\treturn NULL;\n+\twhile (*p) {\n+\t\tn++;\n+\t\tp++;\n+\t}\n+\tp = xmalloc(sizeof(*p)*n);\n+\tmemcpy(p, pathspec, sizeof(*p)*n);\n+\treturn p;\n+}\n+\n+\n+const char **get_pathspec_narrow(const char *prefix, const char **pathspec, int narrow)\n {\n \tconst char *entry = *pathspec;\n-\tconst char **src, **dst;\n+\tconst char **src, **dst, **p;\n \tint prefixlen;\n+\tconst char **narrow_prefix = narrow ? get_narrow_prefix() : NULL;\n+\tint ps_hit_count, np_hit_count;\n+\tint *ps_hitmap, *np_hitmap;\n+\tint i, n;\n+\n+\tif (!entry && !prefix) {\n+\t\tpathspec = dup_pathspec(narrow_prefix);\n+\t\tgoto done;\n+\t}\n \n-\tif (!prefix && !entry)\n-\t\treturn NULL;\n+\t/*\n+\t * if prefix is also a prefix of narrow_prefix, use\n+\t * it. Otherwise just return narrow_prefix\n+\t */\n+\tif (!entry && narrow_prefix) {\n+\t\tp = narrow_prefix;\n+\t\twhile (*p) {\n+\t\t\tif (!prefixcmp(prefix, *p))\n+\t\t\t\tbreak;\n+\t\t\tp++;\n+\t\t}\n+\t\tif (!*p) {\n+\t\t\tpathspec = dup_pathspec(narrow_prefix);\n+\t\t\tgoto done;\n+\t\t}\n+\t\t/* otherwise fall through */\n+\t}\n \n \tif (!entry) {\n \t\tstatic const char *spec[2];\n \t\tspec[0] = prefix;\n \t\tspec[1] = NULL;\n-\t\treturn spec;\n+\t\tpathspec = spec;\n+\t\tgoto done;\n \t}\n \n \t/* Otherwise we have to re-write the entries.. */\n@@ -149,11 +190,132 @@ const char **get_pathspec(const char *prefix, const char **pathspec)\n \t\tsrc++;\n \t}\n \t*dst = NULL;\n+\n+\t/*\n+\t * And filter rewritten entries against narrow_prefix..  The\n+\t * rule is, if a prefix is \"x\" and we have narrow prefix x/y,\n+\t * then that prefix is taken out and replaced by x/y. If we\n+\t * have two narrow prefix x/y and x/z, both will be in.\n+\t */\n+\tif (narrow_prefix) {\n+\t\tfor (n = 0, p = narrow_prefix; *p; p++)\n+\t\t\tn++;\n+\n+\t\tnp_hitmap = xmalloc(sizeof(*np_hitmap) * ((n+32)/32));\n+\t\tmemset(np_hitmap, 0, sizeof(*np_hitmap) * ((n+32)/32));\n+\t\tps_hitmap = xmalloc(sizeof(*ps_hitmap) * ((dst-pathspec+32)/32));\n+\t\tmemset(ps_hitmap, 0, sizeof(*ps_hitmap) * ((dst-pathspec+32)/32));\n+\n+#define HIT(hitmap, x) hitmap[(x) / 32] |= 1 << ((x) % 32)\n+#define GOT_HIT(hitmap, x) (hitmap[(x) / 32] & (1 << ((x) % 32)))\n+\t\t/*\n+\t\t * Let's see how many narrow prefix we hit, then we\n+\t\t * know if pathspec has enough space, or we need a\n+\t\t * bigger one.\n+\t\t */\n+\t\tsrc = pathspec;\n+\t\tps_hit_count = 0;\n+\t\t/* np_hit_count can't be counted here because one\n+\t\t   narrow prefix can be hit many times */\n+\t\twhile (*src) {\n+\t\t\tint pathspec_hit = 0, hit = 0;\n+\t\t\tp = narrow_prefix;\n+\t\t\twhile (*p) {\n+\t\t\t\tif (!prefixcmp(*p, *src)) {\n+\t\t\t\t\tHIT(np_hitmap, p - narrow_prefix);\n+\t\t\t\t\tpathspec_hit++;\n+\t\t\t\t}\n+\t\t\t\telse if (!prefixcmp(*src, *p)) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * If any of previous pathspec has hit\n+\t\t\t\t\t * a narrow prefix, that narrow prefix\n+\t\t\t\t\t * will be included as a replacement for\n+\t\t\t\t\t * the pathspec. So any other pathspecs\n+\t\t\t\t\t * that are stricter than that pathspec\n+\t\t\t\t\t * is redundant, mark pathspec_hit to remove\n+\t\t\t\t\t * it.\n+\t\t\t\t\t */\n+\t\t\t\t\tif (GOT_HIT(np_hitmap, p - narrow_prefix))\n+\t\t\t\t\t\tpathspec_hit++;\n+\t\t\t\t\thit++;\n+\t\t\t\t}\n+\t\t\t\tp++;\n+\t\t\t}\n+\t\t\tif (!hit && !pathspec_hit)\n+\t\t\t\tdie(\"Pathspec %s is outside narrow area\", *src);\n+\t\t\tif (pathspec_hit) {\n+\t\t\t\tHIT(ps_hitmap, src-pathspec);\n+\t\t\t\tps_hit_count++;\n+\t\t\t}\n+\t\t\tsrc++;\n+\t\t}\n+\n+\t\t/* All pathspec is inside narrow area */\n+\t\tif (!ps_hit_count)\n+\t\t\tgoto done;\n+\n+\t\tnp_hit_count = 0;\n+\t\tfor (i = 0; i < n; i++)\n+\t\t\tif (GOT_HIT(np_hitmap, i))\n+\t\t\t\tnp_hit_count++;\n+\n+\t\t/*\n+\t\t * All pathspec is a prefix of narrow_prefix, or one\n+\t\t * pathspec hits all narrow_prefix, just use\n+\t\t * narrow_prefix instead. Of course we need to check\n+\t\t * what narrow_prefix is hit.\n+\t\t */\n+\t\tif (ps_hit_count == (dst - pathspec) || n == np_hit_count) {\n+\t\t\tpathspec = dup_pathspec(narrow_prefix);\n+\t\t\tfor (i = 0; i < n; i++) {\n+\t\t\t\tif (GOT_HIT(np_hitmap, i))\n+\t\t\t\t\tcontinue; /* hit, go on */\n+\t\t\t\tmemcpy(pathspec+i, pathspec+i+1, (n-i) * sizeof(*pathspec));\n+\t\t\t}\n+\t\t\tgoto done;\n+\t\t}\n+\n+\t\t/*\n+\t\t * So we have ps_hit pathspecs hit, which will be\n+\t\t * removed, and a np_hit_count prefixes hit, which\n+\t\t * will be in.\n+\t\t *\n+\t\t * Check if we have enough space for the new\n+\t\t * np_hit_count prefix.\n+\t\t */\n+\t\tp = ps_hit_count < np_hit_count ? xmalloc(sizeof(*p)*(dst-pathspec-ps_hit_count+np_hit_count+1)) : pathspec;\n+\t\tsrc = pathspec;\n+\t\tdst = p;\n+\t\twhile (*src) {\n+\t\t\tif (GOT_HIT(ps_hitmap, src-pathspec)) {\n+\t\t\t\tsrc++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\t*dst++ = *src++;\n+\t\t}\n+\t\tfor (i = 0; i < n; i++) {\n+\t\t\tif (GOT_HIT(np_hitmap, i))\n+\t\t\t\t*dst++ = narrow_prefix[i];\n+\t\t}\n+\t\t*dst = NULL;\n+\t\tpathspec = p;\n+\t}\n+\n \tif (!*pathspec)\n \t\treturn NULL;\n+done:\n+\tif (pathspec)\n+\t\ttrace_argv_printf(pathspec, \"trace: pathspec: \");\n+\telse\n+\t\ttrace_printf(\"trace: pathspec: <empty>\\n\");\n \treturn pathspec;\n }\n \n+const char **get_pathspec(const char *prefix, const char **pathspec)\n+{\n+\treturn get_pathspec_narrow(prefix, pathspec, 1);\n+}\n+\n /*\n  * Test if it looks like we're at a git directory.\n  * We want to see:\ndiff --git a/t/t0062-narrow-pathspec.sh b/t/t0062-narrow-pathspec.sh\nnew file mode 100755\nindex 0000000..ef5d059\n--- /dev/null\n+++ b/t/t0062-narrow-pathspec.sh\n@@ -0,0 +1,150 @@\n+#!/bin/sh\n+\n+test_description='Narrow pathspec rewrite tests'\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\tmkdir a b c &&\n+\tmkdir a/aa a/ab a/ac &&\n+\tmkdir b/ba b/bb b/bc &&\n+\tmkdir a/aa/aaa a/aa/aab a/aa/aac &&\n+\tmkdir a/ab/aba a/ab/abb a/ab/abc\n+'\n+\n+test_expect_success '() no pathspec' '\n+\ttest-get-pathspec >result &&\n+\t: >expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success '() [a] no pathspec' '\n+\techo a >.git/narrow &&\n+\ttest-get-pathspec >result &&\n+\techo a >expected &&\n+\ttest_cmp expected result\n+'\n+\n+# Because narrow prefix is \"a\". put a/ to check that the prefix is\n+# actually from command line not from narrow prefix\n+test_expect_success '() [a] a/' '\n+\techo a >.git/narrow &&\n+\ttest-get-pathspec a/ >result &&\n+\techo a/ >expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success '() [a] a/aa' '\n+\techo a >.git/narrow &&\n+\ttest-get-pathspec a/aa >result &&\n+\techo a/aa >expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success '() [a] b' '\n+\techo a >.git/narrow &&\n+\ttest_must_fail test-get-pathspec b\n+'\n+\n+test_expect_success '() [a/aa] a' '\n+\techo a/aa >.git/narrow &&\n+\ttest-get-pathspec a >result &&\n+\techo a/aa >expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success '() [a/aa] a/ab' '\n+\techo a/aa >.git/narrow &&\n+\ttest_must_fail test-get-pathspec a/ab\n+'\n+\n+test_expect_success '() [a/aa a/ab] a' '\n+\techo a/aa >.git/narrow &&\n+\techo a/ab >>.git/narrow &&\n+\ttest-get-pathspec a >result &&\n+\techo a/aa >expected &&\n+\techo a/ab >>expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success '() [a/aa a/ab] a a/aa/aab' '\n+\techo a/aa >.git/narrow &&\n+\techo a/ab >>.git/narrow &&\n+\ttest-get-pathspec a a/aa/aab >result &&\n+\techo a/aa >expected &&\n+\techo a/ab >>expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success '() [a/aa a/ab] a/aa a/ab/abc' '\n+\techo a/aa >.git/narrow &&\n+\techo a/ab >>.git/narrow &&\n+\ttest-get-pathspec a/aa a/ab/abc >result &&\n+\techo a/ab/abc >expected &&\n+\techo a/aa >>expected &&\n+\ttest_cmp expected result\n+'\n+\n+# a/aa is replaced by a/aa/aaa and a/aa/aab\n+# reallocation must be done\n+test_expect_success '() [a/aa/aaa a/aa/aab a/ab] a/aa a/ab/abc' '\n+\techo a/aa/aaa >.git/narrow &&\n+\techo a/aa/aab >>.git/narrow &&\n+\techo a/ab >>.git/narrow &&\n+\ttest-get-pathspec a/aa a/ab/abc >result &&\n+\techo a/ab/abc >expected &&\n+\techo a/aa/aaa >>expected &&\n+\techo a/aa/aab >>expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success '() [a b] no pathspec' '\n+\techo a >.git/narrow &&\n+\techo b >>.git/narrow &&\n+\ttest-get-pathspec >result &&\n+\techo a >expected &&\n+\techo b >>expected &&\n+\ttest_cmp expected result\n+'\n+\n+test_expect_success '(a) no pathspec' '\n+\t: >.git/narrow\n+\t(\n+\tcd a\n+\ttest-get-pathspec >result &&\n+\techo a/ >expected &&\n+\ttest_cmp expected result\n+\t)\n+'\n+\n+test_expect_success '(a) [a] no pathspec' '\n+\techo a >.git/narrow &&\n+\t(\n+\tcd a\n+\ttest-get-pathspec >result &&\n+\techo a/ >expected &&\n+\ttest_cmp expected result\n+\t)\n+'\n+\n+test_expect_success '(a) [a] aa' '\n+\techo a >.git/narrow &&\n+\t(\n+\tcd a\n+\ttest-get-pathspec aa >result &&\n+\techo a/aa >expected &&\n+\ttest_cmp expected result\n+\t)\n+'\n+\n+test_expect_success '(b) [a] no pathspec' '\n+\techo a >.git/narrow &&\n+\t(\n+\tcd b\n+\ttest-get-pathspec >result &&\n+\techo a >expected &&\n+\ttest_cmp expected result\n+\t)\n+'\n+\n+test_done\ndiff --git a/test-get-pathspec.c b/test-get-pathspec.c\nnew file mode 100644\nindex 0000000..413c5b0\n--- /dev/null\n+++ b/test-get-pathspec.c\n@@ -0,0 +1,17 @@\n+#include \"cache.h\"\n+\n+int main(int argc, char **argv)\n+{\n+\tint gitdir;\n+\tconst char *prefix = setup_git_directory_gently(&gitdir); /* get narrow_prefix */\n+\tconst char **p;\n+\n+\tp = get_pathspec(prefix, (const char **)argv+1);\n+\tif (!p)\n+\t\treturn 0;\n+\twhile (*p) {\n+\t\tprintf(\"%s\\n\", *p);\n+\t\tp++;\n+\t}\n+\treturn 0;\n+}\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149950","messageId":"1283669264-15759-12-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 11/17] pathspec retrieval fix","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:38Z","receivedAt":"2010-09-05T06:47:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In normal repos, when there is no command line argument, it's safe to\nsay we don't have pathspec. In narrow repos, get_pathspec() in that\ncase will still generate some pathspecs (the narrow prefix). Make sure\nget_pathspec() is always called.\n\nSome commands however, especially plumbings, won't want narrow prefix\nto get in the way. Use get_pathspec_narrow(...., 0); in those cases to\ndisable narrow pathspec rewriting.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/commit.c   |    6 ++----\n builtin/grep.c     |    5 ++---\n builtin/ls-files.c |    2 +-\n builtin/ls-tree.c  |    2 +-\n builtin/reset.c    |    3 +--\n revision.c         |    5 +++++\n 6 files changed, 12 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 66fdd22..d8c5273 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -303,8 +303,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int\n \t\treturn get_index_file();\n \t}\n \n-\tif (*argv)\n-\t\tpathspec = get_pathspec(prefix, argv);\n+\tpathspec = get_pathspec(prefix, argv);\n \n \tif (read_cache_preload(pathspec) < 0)\n \t\tdie(\"index file corrupt\");\n@@ -1083,8 +1082,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \thandle_untracked_files_arg(&s);\n \tif (show_ignored_in_status)\n \t\ts.show_ignored_files = 1;\n-\tif (*argv)\n-\t\ts.pathspec = get_pathspec(prefix, argv);\n+\ts.pathspec = get_pathspec(prefix, argv);\n \n \tread_cache_preload(s.pathspec);\n \trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex cf6c29f..b55a0a4 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -1058,9 +1058,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\t\tverify_filename(prefix, argv[j]);\n \t}\n \n-\tif (i < argc)\n-\t\tpaths = get_pathspec(prefix, argv + i);\n-\telse if (prefix) {\n+\tpaths = get_pathspec(prefix, argv + i);\n+\tif (prefix && !paths) {\n \t\tpaths = xcalloc(2, sizeof(const char *));\n \t\tpaths[0] = prefix;\n \t\tpaths[1] = NULL;\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex bb4f612..27610ab 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -565,7 +565,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)\n \tif (require_work_tree && !is_inside_work_tree())\n \t\tsetup_work_tree();\n \n-\tpathspec = get_pathspec(prefix, argv);\n+\tpathspec = get_pathspec_narrow(prefix, argv, 0);\n \n \t/* be nice with submodule paths ending in a slash */\n \tif (pathspec)\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex dc86b0d..d37eaa5 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -164,7 +164,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tif (get_sha1(argv[0], sha1))\n \t\tdie(\"Not a valid object name %s\", argv[0]);\n \n-\tpathspec = get_pathspec(prefix, argv + 1);\n+\tpathspec = get_pathspec_narrow(prefix, argv + 1, 0);\n \ttree = parse_tree_indirect(sha1);\n \tif (!tree)\n \t\tdie(\"not a tree object\");\ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex 1283068..ae96c8b 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -181,8 +181,7 @@ static int interactive_reset(const char *revision, const char **argv,\n {\n \tconst char **pathspec = NULL;\n \n-\tif (*argv)\n-\t\tpathspec = get_pathspec(prefix, argv);\n+\tpathspec = get_pathspec(prefix, argv);\n \n \treturn run_add_interactive(revision, \"--patch=reset\", pathspec);\n }\ndiff --git a/revision.c b/revision.c\nindex ea64970..73fe894 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1625,6 +1625,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \n \tif (prune_data)\n \t\trevs->prune_data = get_pathspec(revs->prefix, prune_data);\n+\telse if (get_narrow_prefix()) {\n+\t\tconst char *nul = NULL;\n+\t\tprune_data = &nul;\n+\t\trevs->prune_data = get_pathspec(revs->prefix, prune_data);\n+\t}\n \n \tif (revs->def == NULL)\n \t\trevs->def = opt ? opt->def : NULL;\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149951","messageId":"1283669264-15759-13-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 12/17] clone: support --narrow option","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:39Z","receivedAt":"2010-09-05T06:47:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-clone.txt |   14 +++++++++++++-\n builtin/clone.c             |   43 +++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 56 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex dc7d3d1..d9a5729 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -12,7 +12,8 @@ SYNOPSIS\n 'git clone' [--template=<template_directory>]\n \t  [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]\n \t  [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]\n-\t  [--depth <depth>] [--recursive] [--] <repository> [<directory>]\n+\t  [--depth <depth>] [--narrow <path>] [--recursive]\n+\t  [--] <repository> [<directory>]\n \n DESCRIPTION\n -----------\n@@ -161,6 +162,17 @@ objects from the source repository into a pack in the cloned repository.\n \twith a long history, and would want to send in fixes\n \tas patches.\n \n+--narrow <path>::\n+\tCreate a 'narrow' clone with all commit trees limited to\n+\tthe given path. A narrow repository has a number of\n+\tlimititations (you cannot clone or fech from it, nor push\n+\tinto it), but is adequate if you are only interested in\n+\tcertain paths of a large repository, and would want to\n+\tpush some fixes.\n++\n+Multiple --narrow can be given. This option can also be used together\n+with --depth to truncate both history and path.\n+\n --recursive::\n \tAfter the clone is created, initialize all submodules within,\n \tusing their default settings. This is equivalent to running\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex efb1e6f..439ce8a 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -23,6 +23,7 @@\n #include \"branch.h\"\n #include \"remote.h\"\n #include \"run-command.h\"\n+#include \"narrow-tree.h\"\n \n /*\n  * Overall FIXMEs:\n@@ -40,11 +41,15 @@ static const char * const builtin_clone_usage[] = {\n static int option_no_checkout, option_bare, option_mirror;\n static int option_local, option_no_hardlinks, option_shared, option_recursive;\n static char *option_template, *option_reference, *option_depth;\n+static const char **option_narrow;\n static char *option_origin = NULL;\n static char *option_branch = NULL;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbosity;\n static int option_progress;\n+static int narrow_nr;\n+\n+static int add_narrow_prefix(const struct option *opt, const char *arg, int unset);\n \n static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n@@ -78,6 +83,8 @@ static struct option builtin_clone_options[] = {\n \t\t   \"path to git-upload-pack on the remote\"),\n \tOPT_STRING(0, \"depth\", &option_depth, \"depth\",\n \t\t    \"create a shallow clone of that depth\"),\n+\tOPT_CALLBACK(0, \"narrow\", NULL, \"prefix\", \"narrow clone\",\n+\t\t     add_narrow_prefix),\n \n \tOPT_END()\n };\n@@ -86,6 +93,22 @@ static const char *argv_submodule[] = {\n \t\"submodule\", \"update\", \"--init\", \"--recursive\", NULL\n };\n \n+static int add_narrow_prefix(const struct option *opt, const char *arg, int unset)\n+{\n+\tif (unset)\n+\t\tdie(\"--no-narrow is not supported\");\n+\n+\tnarrow_nr++;\n+\toption_narrow = xrealloc(option_narrow, sizeof(*option_narrow)*narrow_nr);\n+\toption_narrow[narrow_nr-1] = arg;\n+\treturn 0;\n+}\n+\n+static int narrow_cmp(const void *a, const void *b)\n+{\n+\treturn strcmp(*(const char**)a, *(const char **)b);\n+}\n+\n static char *get_repo_path(const char *repo, int *is_bundle)\n {\n \tstatic char *suffix[] = { \"/.git\", \".git\", \"\" };\n@@ -443,6 +466,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tgit_dir = xstrdup(mkpath(\"%s/.git\", dir));\n \t}\n \n+\tif (option_narrow) {\n+\t\tint i;\n+\t\tqsort(option_narrow, narrow_nr, sizeof(*option_narrow), narrow_cmp);\n+\t\tfor (i = 0; i < narrow_nr; i++)\n+\t\t\tif (!valid_narrow_prefix(option_narrow[i], i ? option_narrow[i-1] : NULL, 0))\n+\t\t\t\tdie(\"Invalid narrow prefix\");\n+\t}\n+\n \tif (!option_bare) {\n \t\tjunk_work_tree = work_tree;\n \t\tif (safe_create_leading_directories_const(work_tree) < 0)\n@@ -515,6 +546,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tstrbuf_reset(&value);\n \n \tif (path && !is_bundle) {\n+\t\tif (option_narrow)\n+\t\t\tdie(\"--narrow is not really for local clone. Please consider --shared\");\n \t\trefs = clone_local(path, git_dir);\n \t\tmapped_refs = wanted_peer_refs(refs, refspec);\n \t} else {\n@@ -530,6 +563,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n \t\t\t\t\t     option_depth);\n \n+\t\t/* Do this early in order to set get_narrow_prefix() != NULL */\n+\t\tif (option_narrow) {\n+\t\t\tint i;\n+\t\t\tFILE *fp = fopen(git_path(\"narrow\"), \"w+\");\n+\t\t\tfor (i = 0; i < narrow_nr; i++)\n+\t\t\t\tfprintf(fp, \"%s\\n\", option_narrow[i]);\n+\t\t\tfclose(fp);\n+\t\t\tcheck_narrow_prefix(); /* Install the prefix */\n+\t\t}\n+\n \t\ttransport_set_verbosity(transport, option_verbosity, option_progress);\n \n \t\tif (option_upload_pack)\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149952","messageId":"1283669264-15759-14-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 13/17] commit: add narrow's commit_tree version","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:40Z","receivedAt":"2010-09-05T06:47:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"As stated somewhere, the index in narrow repo is also narrowed. When a\nusers are done with his changes and about to commit, the new narrow\ntree created from index will be grafted back to a base toplevel tree\n(usually from parent commit). The result is a new toplevel tree with\nuser's changes and suitable for commits.\n\nThe narrow version uses join_narrow_tree() to recreate a full tree\nfrom a base toplevel tree (typically commit parent's tree) and a tree\ncreated from index.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Equivalence to Elijah's 07/15 and 08/15. My way generates some throw away\n trees at write_cache_as_tree(), not good.\n\n commit.c      |   16 +++++++++\n commit.h      |    5 +++\n narrow-tree.c |  101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n narrow-tree.h |    4 ++\n 4 files changed, 126 insertions(+), 0 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex 0094ec1..c7fe7fc 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -6,6 +6,7 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"notes.h\"\n+#include \"narrow-tree.h\"\n \n int save_commit_buffer = 1;\n \n@@ -864,3 +865,18 @@ int commit_tree(const char *msg, unsigned char *tree,\n \tstrbuf_release(&buffer);\n \treturn result;\n }\n+\n+int commit_narrow_tree(const char *msg, unsigned char *tree,\n+\t\t       const unsigned char *narrow_base,\n+\t\t       struct commit_list *parents, unsigned char *ret,\n+\t\t       const char *author)\n+{\n+\tunsigned char sha1[20];\n+\n+\tif (get_narrow_prefix()) {\n+\t\tif (join_narrow_tree(sha1, narrow_base, tree, get_narrow_prefix()))\n+\t\t\tdie(\"Failed to join tree\");\n+\t\ttree = sha1;\n+\t}\n+\treturn commit_tree(msg, tree, parents, ret, author);\n+}\ndiff --git a/commit.h b/commit.h\nindex 9113bbe..c718439 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -170,5 +170,10 @@ struct commit_list *reduce_heads(struct commit_list *heads);\n extern int commit_tree(const char *msg, unsigned char *tree,\n \t\tstruct commit_list *parents, unsigned char *ret,\n \t\tconst char *author);\n+extern int commit_narrow_tree(const char *msg, unsigned char *tree,\n+\t\t\t      const unsigned char *narrow_base,\n+\t\t\t      struct commit_list *parents,\n+\t\t\t      unsigned char *ret,\n+\t\t\t      const char *author);\n \n #endif /* COMMIT_H */\ndiff --git a/narrow-tree.c b/narrow-tree.c\nindex 85dbab4..110fac2 100644\n--- a/narrow-tree.c\n+++ b/narrow-tree.c\n@@ -1,4 +1,14 @@\n #include \"cache.h\"\n+#include \"commit.h\"\n+#include \"tree.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n+#include \"refs.h\"\n+#include \"tag.h\"\n+#include \"progress.h\"\n+#include \"pack.h\"\n+#include \"sha1-lookup.h\"\n+#include \"csum-file.h\"\n #include \"narrow-tree.h\"\n \n static const char **narrow_prefix;\n@@ -104,3 +114,94 @@ char *get_narrow_string()\n \t}\n \treturn strbuf_detach(&sb, NULL);\n }\n+\n+/*\n+ * The opposite of narrow_tree(). Put the subtree back to the original tree.\n+ */\n+static int join_narrow_tree_rec(unsigned char *result,\n+\t\t\t\tconst unsigned char *basetree,\n+\t\t\t\tconst unsigned char *newtree,\n+\t\t\t\tconst char **prefix,\n+\t\t\t\tchar *base,\n+\t\t\t\tint baselen)\n+{\n+\tstruct tree_desc desc;\n+\tstruct name_entry entry;\n+\tstruct strbuf buffer;\n+\tenum object_type type;\n+\tunsigned long size;\n+\tconst char **p;\n+\tint len, found;\n+\tchar *buf;\n+\n+\tbuf = read_sha1_file(basetree, &type, &size);\n+\tif (!buf || type != OBJ_TREE)\n+\t\tdie(\"Bad tree %s\", sha1_to_hex(basetree));\n+\n+\tif (baselen)\n+\t\tbase[baselen++] = '/';\n+\n+\tinit_tree_desc(&desc, buf, size);\n+\tstrbuf_init(&buffer, 8192);\n+\twhile (tree_entry(&desc, &entry)) {\n+\t\tstrbuf_addf(&buffer, \"%o %.*s%c\", entry.mode, strlen(entry.path), entry.path, '\\0');\n+\n+\t\tif (!S_ISDIR(entry.mode)) {\n+\t\t\tstrbuf_add(&buffer, entry.sha1, 20);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tp = prefix;\n+\t\tlen = strlen(entry.path);\n+\t\tfound = 0;\n+\n+\t\twhile (*p) {\n+\t\t\tif (!strcmp(entry.path, *p)) {\n+\t\t\t\tfound = 2;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (!prefixcmp(*p, entry.path)) {\n+\t\t\t\tfound = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tp++;\n+\t\t}\n+\t\tswitch (found) {\n+\t\tcase 1:\n+\t\t\tmemcpy(base+baselen, entry.path, len+1);\n+\t\t\tjoin_narrow_tree_rec(result, entry.sha1, newtree,\n+\t\t\t\t\t     prefix, base, baselen+len+1);\n+\t\t\tbreak;\n+\t\tcase 2:\n+\t\t\tif (!path_to_tree_sha1(result, newtree, *p))\n+\t\t\t\tdie(\"Could not find tree %s in the new tree\", *p);\n+\t\t\tbreak;\n+\t\tcase 0:\n+\t\t\thashcpy(result, entry.sha1);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* FIXME, what if placeholder tree does not exist? */\n+\n+\t\tstrbuf_add(&buffer, result, 20);\n+\t}\n+\n+\tfree(buf);\n+\tif (write_sha1_file(buffer.buf, buffer.len, tree_type, result)) {\n+\t\tbase[baselen] = '\\0';\n+\t\terror(\"Could not write tree %s\", base);\n+\t\tstrbuf_release(&buffer);\n+\t\treturn 1;\n+\t}\n+\tstrbuf_release(&buffer);\n+\treturn 0;\n+}\n+\n+int join_narrow_tree(unsigned char *result,\n+\t\t     const unsigned char *basetree,\n+\t\t     const unsigned char *newtree,\n+\t\t     const char **prefix)\n+{\n+\tchar path[PATH_MAX];\n+\treturn join_narrow_tree_rec(result, basetree, newtree, prefix, path, 0);\n+}\ndiff --git a/narrow-tree.h b/narrow-tree.h\nindex 2097436..e7d84c4 100644\n--- a/narrow-tree.h\n+++ b/narrow-tree.h\n@@ -1,3 +1,7 @@\n extern int valid_narrow_prefix(const char *prefix, const char *prev_prefix, int quiet);\n extern int check_narrow_prefix();\n extern char *get_narrow_string();\n+extern int join_narrow_tree(unsigned char *result,\n+\t\t\t    const unsigned char *base,\n+\t\t\t    const unsigned char *newtree,\n+\t\t\t    const char **prefix);\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149953","messageId":"1283669264-15759-15-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 14/17] commit: use commit_narrow_tree() to support narrow repo","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:41Z","receivedAt":"2010-09-05T06:47:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/commit.c         |   10 +++++++++-\n t/t7510-commit-narrow.sh |   30 ++++++++++++++++++++++++++++++\n 2 files changed, 39 insertions(+), 1 deletions(-)\n create mode 100755 t/t7510-commit-narrow.sh\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex d8c5273..2321642 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1349,7 +1349,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\texit(1);\n \t}\n \n-\tif (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1,\n+\tif (get_narrow_prefix()) {\n+\t\tif (!parents)\n+\t\t\tdie(\"Narrow mode does not support initial commit (yet)\");\n+\t\tif (parse_commit(parents->item))\n+\t\t\tdie(\"Bad commit %s\", parents->item->object.sha1);\n+\t}\n+\tif (commit_narrow_tree(sb.buf, active_cache_tree->sha1,\n+\t\t\tparents ? parents->item->tree->object.sha1 : NULL,\n+\t\t\tparents, commit_sha1,\n \t\t\tfmt_ident(author_name, author_email, author_date,\n \t\t\t\tIDENT_ERROR_ON_NO_NAME))) {\n \t\trollback_index_files();\ndiff --git a/t/t7510-commit-narrow.sh b/t/t7510-commit-narrow.sh\nnew file mode 100755\nindex 0000000..1232cee\n--- /dev/null\n+++ b/t/t7510-commit-narrow.sh\n@@ -0,0 +1,30 @@\n+#!/bin/sh\n+\n+test_description='commit in narrow repositories'\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\ttouch a b c &&\n+\tmkdir x y z &&\n+\ttouch x/a y/b z/c &&\n+\tgit add . &&\n+\tgit commit -q -m initial &&\n+\techo x >.git/narrow &&\n+\trm .git/index &&\n+\tgit reset --hard HEAD\n+'\n+\n+X1_TREE=496d6428b9cf92981dc9495211e6e1120fb6f2ba\n+X2_TREE=4d593e935186bcc35450336864a1aad148210a14\n+\n+test_expect_success 'commit' '\n+\tgit ls-tree HEAD | sed \"s/$X1_TREE/$X2_TREE/\" >expected &&\n+\techo x >>x/a &&\n+\tgit add x/a &&\n+\tgit commit -m x/a &&\n+\tgit ls-tree HEAD >result &&\n+\ttest_cmp expected result\n+'\n+\n+test_done\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149954","messageId":"1283669264-15759-16-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 15/17] write-tree: requires --narrow-base in narrow repository","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:42Z","receivedAt":"2010-09-05T06:47:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Without a base, a full tree cannot be generated, because the index\ndoes not contain all trees.\n\n--narrow-base is ignored if the repository is not a narrow one.\n\ngit-am is modified to \"test\" write-tree, which makes git-rebase works\nin simple cases.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/write-tree.c |   25 +++++++++++++++++++++++++\n git-am.sh            |    2 +-\n 2 files changed, 26 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin/write-tree.c b/builtin/write-tree.c\nindex b223af4..5da3cb3 100644\n--- a/builtin/write-tree.c\n+++ b/builtin/write-tree.c\n@@ -8,6 +8,7 @@\n #include \"tree.h\"\n #include \"cache-tree.h\"\n #include \"parse-options.h\"\n+#include \"narrow-tree.h\"\n \n static const char * const write_tree_usage[] = {\n \t\"git write-tree [--missing-ok] [--prefix=<prefix>/]\",\n@@ -18,6 +19,7 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)\n {\n \tint flags = 0, ret;\n \tconst char *prefix = NULL;\n+\tconst char *narrow_str = NULL;\n \tunsigned char sha1[20];\n \tconst char *me = \"git-write-tree\";\n \tstruct option write_tree_options[] = {\n@@ -26,6 +28,9 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)\n \t\t{ OPTION_STRING, 0, \"prefix\", &prefix, \"<prefix>/\",\n \t\t  \"write tree object for a subdirectory <prefix>\" ,\n \t\t  PARSE_OPT_LITERAL_ARGHELP },\n+\t\t{ OPTION_STRING, 0, \"narrow-base\", &narrow_str, \"base tree\",\n+\t\t  \"base tree in narrow index\" ,\n+\t\t  PARSE_OPT_LITERAL_ARGHELP },\n \t\t{ OPTION_BIT, 0, \"ignore-cache-tree\", &flags, NULL,\n \t\t  \"only useful for debugging\",\n \t\t  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL,\n@@ -40,6 +45,26 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)\n \tret = write_cache_as_tree(sha1, flags, prefix);\n \tswitch (ret) {\n \tcase 0:\n+\t\tif (get_narrow_prefix()) {\n+\t\t\tunsigned char sha1_param[20];\n+\t\t\tunsigned char narrow_base[20];\n+\t\t\tunsigned char old_sha1[20];\n+\t\t\tunsigned long size;\n+\t\t\tvoid *buf;\n+\n+\t\t\tif (!narrow_str)\n+\t\t\t\tdie(\"write-tree in narrow repo requires --narrow-base\");\n+\n+\t\t\tif (get_sha1(narrow_str, sha1_param))\n+\t\t\t\tdie(\"Invalid SHA1 %s\", narrow_str);\n+\t\t\tbuf = read_object_with_reference(sha1_param, tree_type, &size, narrow_base);\n+\t\t\tif (!buf)\n+\t\t\t\tdie(\"Bad treeish %s\", sha1_to_hex(sha1));\n+\t\t\tfree(buf);\n+\t\t\thashcpy(old_sha1, sha1);\n+\t\t\tif (join_narrow_tree(sha1, narrow_base, old_sha1, get_narrow_prefix()))\n+\t\t\t\tdie(\"Failed to join tree\");\n+\t\t}\n \t\tprintf(\"%s\\n\", sha1_to_hex(sha1));\n \t\tbreak;\n \tcase WRITE_TREE_UNREADABLE_INDEX:\ndiff --git a/git-am.sh b/git-am.sh\nindex e7f008c..6adda43 100755\n--- a/git-am.sh\n+++ b/git-am.sh\n@@ -783,7 +783,7 @@ do\n \t\t\"$GIT_DIR\"/hooks/pre-applypatch || stop_here $this\n \tfi\n \n-\ttree=$(git write-tree) &&\n+\ttree=$(git write-tree --narrow-base=HEAD) &&\n \tcommit=$(\n \t\tif test -n \"$ignore_date\"\n \t\tthen\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149955","messageId":"1283669264-15759-17-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 16/17] merge: try to do local merge if possible in narrow repo","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:43Z","receivedAt":"2010-09-05T06:47:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"commit_narrow_tree() works with a single narrow base. Unfortunately a\nmerge may have more than one parent. If all parents have the same\ntrees outside $GIT_DIR/narrow tree, then it's actually \"a single\nnarrow base\".\n\nOther than that, refuse to merge because we do not have enough trees\nto peform a trivial merge outside narrow tree. A merge in such case\nwill need server support.\n\nSome simple cases though can be handled local. One of such cases are\nwhere the narrow base of head and ancestor are exactly the same (IOW\nwe don't change anything outside narrow tree) we can just use remote\nas narrow base. This case happens when we work on narrow repo then do\na pull.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-merge.txt |   16 +++++++++\n builtin/merge.c             |   60 ++++++++++++++++++++++++++++++---\n narrow-tree.c               |   78 +++++++++++++++++++++++++++++++++++++++++++\n narrow-tree.h               |    1 +\n 4 files changed, 150 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 84043cc..4285f18 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -239,6 +239,22 @@ You can work through the conflict with a number of tools:\n    version.\n \n \n+MERGE IN NARROW REPOSITORIES\n+----------------------------\n+\n+Because narrow repositories do not have all tree objects, abitrary\n+merge may not work in these repositories. A merge can only be\n+performed local in such a repository if\n+\n+ * There are two commits given\n+\n+ * A single common commit is found\n+\n+ * The commit content outside narrow area must be the same as in the\n+   other commit, or in the common commit\n+\n+Merges that do not meet these requirements cannot be done locally.\n+\n EXAMPLES\n --------\n \ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 47e705b..c6dfb44 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -25,6 +25,7 @@\n #include \"help.h\"\n #include \"merge-recursive.h\"\n #include \"resolve-undo.h\"\n+#include \"narrow-tree.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -56,6 +57,7 @@ static size_t xopts_nr, xopts_alloc;\n static const char *branch;\n static int verbosity;\n static int allow_rerere_auto;\n+static unsigned char narrow_base[20];\n \n static struct strategy all_strategy[] = {\n \t{ \"recursive\",  DEFAULT_TWOHEAD | NO_TRIVIAL },\n@@ -781,7 +783,8 @@ static int merge_trivial(void)\n \tparent->next = xmalloc(sizeof(*parent->next));\n \tparent->next->item = remoteheads->item;\n \tparent->next->next = NULL;\n-\tcommit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);\n+\tcommit_narrow_tree(merge_msg.buf, result_tree, narrow_base,\n+\t\t\t   parent, result_commit, NULL);\n \tfinish(result_commit, \"In-index merge\");\n \tdrop_save();\n \treturn 0;\n@@ -810,7 +813,8 @@ static int finish_automerge(struct commit_list *common,\n \t}\n \tfree_commit_list(remoteheads);\n \tstrbuf_addch(&merge_msg, '\\n');\n-\tcommit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);\n+\tcommit_narrow_tree(merge_msg.buf, result_tree, narrow_base,\n+\t\t\t   parents, result_commit, NULL);\n \tstrbuf_addf(&buf, \"Merge made by %s.\", wt_strategy);\n \tfinish(result_commit, buf.buf);\n \tstrbuf_release(&buf);\n@@ -886,6 +890,48 @@ static int evaluate_result(void)\n \treturn cnt;\n }\n \n+static struct commit_list *find_narrow_base(struct commit *head, struct commit_list *remoteheads)\n+{\n+\tstruct commit_list *remote = remoteheads;\n+\tconst char **narrow_prefix = get_narrow_prefix();\n+\n+\tif (!narrow_prefix)\n+\t\treturn NULL;\n+\n+\tparse_commit(head);\n+\twhile (remote) {\n+\t\tparse_commit(remote->item);\n+\t\tif (!same_narrow_base(head->tree->object.sha1,\n+\t\t\t\t      remote->item->tree->object.sha1,\n+\t\t\t\t      narrow_prefix))\n+\t\t\tbreak;\n+\t\tremote = remote->next;\n+\t}\n+\n+\tif (!remote) {\t\t/* all same narrow base */\n+\t\thashcpy(narrow_base, head->tree->object.sha1);\n+\t\treturn NULL;\n+\t}\n+\n+\t/*\n+\t * If it's three-way merge and head has the same narrow base\n+\t * as in common, then we could just use remote as the base\n+\t * because we haven't changed anything outside narrow tree.\n+\t */\n+\tif (remoteheads && !remoteheads->next) {\n+\t\tstruct commit_list *common;\n+\t\tcommon = get_merge_bases(head, remoteheads->item, 1);\n+\t\tif (common && !common->next &&\n+\t\t    same_narrow_base(head->object.sha1,\n+\t\t\t\t     common->item->object.sha1,\n+\t\t\t\t     narrow_prefix)) {\n+\t\t\thashcpy(narrow_base, remoteheads->item->tree->object.sha1);\n+\t\t\treturn common;\n+\t\t}\n+\t}\n+\tdie(\"Different narrow base, couldn't do merge (yet)\");\n+}\n+\n int cmd_merge(int argc, const char **argv, const char *prefix)\n {\n \tunsigned char result_tree[20];\n@@ -1050,9 +1096,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\tallow_trivial = 0;\n \t}\n \n-\tif (!remoteheads->next)\n-\t\tcommon = get_merge_bases(lookup_commit(head),\n-\t\t\t\tremoteheads->item, 1);\n+\tcommon = find_narrow_base(lookup_commit(head), remoteheads);\n+\n+\tif (!remoteheads->next) {\n+\t\tif (!common) /* might have been calculated in find_narrow_base */\n+\t\t\tcommon = get_merge_bases(lookup_commit(head),\n+\t\t\t\t\t\t remoteheads->item, 1);\n+\t}\n \telse {\n \t\tstruct commit_list *list = remoteheads;\n \t\tcommit_list_insert(lookup_commit(head), &list);\ndiff --git a/narrow-tree.c b/narrow-tree.c\nindex 110fac2..76581f2 100644\n--- a/narrow-tree.c\n+++ b/narrow-tree.c\n@@ -205,3 +205,81 @@ int join_narrow_tree(unsigned char *result,\n \tchar path[PATH_MAX];\n \treturn join_narrow_tree_rec(result, basetree, newtree, prefix, path, 0);\n }\n+\n+static int same_narrow_base_rec(const unsigned char *t1,\n+\t\t\t\tconst unsigned char *t2,\n+\t\t\t\tconst char **prefix,\n+\t\t\t\tchar *base, int baselen)\n+{\n+\tstruct tree_desc desc1, desc2;\n+\tstruct name_entry entry1, entry2;\n+\tchar *buf1, *buf2;\n+\tenum object_type type;\n+\tunsigned long size;\n+\n+\tif (baselen)\n+\t\tbase[baselen++] = '/';\n+\n+\tbuf1 = read_sha1_file(t1, &type, &size);\n+\tif (type != OBJ_TREE)\n+\t\tdie(\"Bad tree %s\", sha1_to_hex(t1));\n+\tinit_tree_desc(&desc1, buf1, size);\n+\n+\tbuf2 = read_sha1_file(t2, &type, &size);\n+\tif (type != OBJ_TREE)\n+\t\tdie(\"Bad tree %s\", sha1_to_hex(t2));\n+\tinit_tree_desc(&desc2, buf2, size);\n+\n+\twhile (tree_entry(&desc1, &entry1) && tree_entry(&desc2, &entry2)) {\n+\t\tif (strcmp(entry1.path, entry2.path) ||\n+\t\t    entry1.mode != entry2.mode) {\n+\t\t\tfree(buf1);\n+\t\t\tfree(buf2);\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\tif (!hashcmp(entry1.sha1, entry2.sha1))\n+\t\t\tcontinue;\n+\n+\t\tif (S_ISDIR(entry1.mode)) {\n+\t\t\tint len = strlen(entry1.path);\n+\t\t\tconst char **p = prefix;\n+\t\t\tint found = 0;\n+\t\t\twhile (*p) {\n+\t\t\t\tif (!strcmp(entry1.path, *p)) {\n+\t\t\t\t\tfound = 2;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tif (!prefixcmp(*p, entry1.path)) {\n+\t\t\t\t\tfound = 1;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tp++;\n+\t\t\t}\n+\n+\t\t\tif (found == 2) /* narrow area */\n+\t\t\t\tcontinue;\n+\t\t\telse if (found == 1) {\n+\t\t\t\tmemcpy(base+baselen, entry1.path, len+1);\n+\t\t\t\tif (same_narrow_base_rec(entry1.sha1, entry2.sha1,\n+\t\t\t\t\t\t\t prefix, base, baselen+len))\n+\t\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\n+\t\tfree(buf1);\n+\t\tfree(buf2);\n+\t\treturn 0;\n+\t}\n+\tfree(buf1);\n+\tfree(buf2);\n+\treturn !desc1.size && !desc2.size;\n+}\n+\n+int same_narrow_base(const unsigned char *t1,\n+\t\t     const unsigned char *t2,\n+\t\t     const char **prefix)\n+{\n+\tchar path[PATH_MAX];\n+\treturn same_narrow_base_rec(t1, t2, prefix, path, 0);\n+}\ndiff --git a/narrow-tree.h b/narrow-tree.h\nindex e7d84c4..c574227 100644\n--- a/narrow-tree.h\n+++ b/narrow-tree.h\n@@ -5,3 +5,4 @@ extern int join_narrow_tree(unsigned char *result,\n \t\t\t    const unsigned char *base,\n \t\t\t    const unsigned char *newtree,\n \t\t\t    const char **prefix);\n+int same_narrow_base(const unsigned char *t1, const unsigned char *t2, const char **prefix);\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149956","messageId":"1283669264-15759-18-git-send-email-pclouds@gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 17/17] Add narrow clone demonstration test","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T06:47:44Z","receivedAt":"2010-09-05T06:47:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t9999-narrow.sh |   87 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 87 insertions(+), 0 deletions(-)\n create mode 100755 t/t9999-narrow.sh\n\ndiff --git a/t/t9999-narrow.sh b/t/t9999-narrow.sh\nnew file mode 100755\nindex 0000000..2974ddc\n--- /dev/null\n+++ b/t/t9999-narrow.sh\n@@ -0,0 +1,87 @@\n+#!/bin/sh\n+\n+test_description='narrow misc tests'\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\tgit clone --mirror ../../.git src.git\n+'\n+\n+test_expect_success clone '\n+\tgit clone --depth=50 --narrow=Documentation --narrow=block-sha1 file://\"`pwd`\"/src.git dst &&\n+\ttest -f dst/Documentation/git.txt &&\n+\ttest -f dst/block-sha1/sha1.c &&\n+\t! test -f git.c\n+'\n+\n+test_expect_success 'into dst' '\n+\tcd dst\n+'\n+\n+#test_expect_failure 'fsck' 'git fsck'\n+\n+test_expect_success 'checkout -b ' '\n+\tgit checkout -b mybranch\n+'\n+\n+test_expect_success 'log' '\n+\tgit log --stat HEAD~20.. >/dev/null\n+'\n+\n+test_expect_success 'modify worktree' '\n+\techo 1 >>Documentation/git.txt\n+'\n+\n+test_expect_success 'diff' '\n+\tgit diff\n+'\n+\n+test_expect_success 'diff HEAD' '\n+\tgit diff HEAD\n+'\n+\n+test_expect_success 'diff --cached' '\n+\tgit add Documentation/git.txt &&\n+\tgit diff --cached\n+'\n+\n+test_expect_success 'commit' '\n+\tgit commit -a -m 1 &&\n+\tgit tag branch0 &&\n+\techo 2 >>block-sha1/sha1.c &&\n+\tgit commit -a -m 2 &&\n+\techo 3 >>Documentation/git.txt &&\n+\techo 3 >>block-sha1/sha1.c &&\n+\tgit commit -a -m 3 &&\n+\tgit tag branch1\n+'\n+\n+test_expect_success 'internal merge' '\n+\tgit checkout branch0 &&\n+\techo 4 >>block-sha1/sha1.h &&\n+\tgit commit -a -m 4 && false\n+\tgit tag branch2 &&\n+\tgit merge branch1 &&\n+\tgrep \"^2$\" block-sha1/sha1.c &&\n+\tgit tag merge1.2\n+'\n+\n+test_expect_success 'rebase' '\n+\tgit checkout branch1 &&\n+\tgit rebase --onto branch2 branch0\n+'\n+\n+test_expect_success 'push' '\n+\tgit push ../src.git +merge1.2:refs/heads/mybranch &&\n+\tgit rev-parse merge1.2 >../expected &&\n+\t(\n+\t\tcd ../src.git &&\n+\t\tgit rev-parse mybranch >result &&\n+\t\ttest_cmp ../expected result &&\n+\t\techo fscking... &&\n+\t\tgit fsck --no-full\n+\t)\n+'\n+\n+test_done\n-- \n1.7.1.rc1.69.g24c2f7\n"},{"id":"149957","messageId":"AANLkTimf_EJcZtJk_X7FiKqQy-oRU4NvZsXMCEcsB=Wa@mail.gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 00/17] Narrow clone v3 (was subtree clone)","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2010-09-05T06:55:44Z","receivedAt":"2010-09-05T06:55:44Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\n2010/9/5 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n> Tree generating from index, Elijah merges the base tree inside\n> write_cache_as_tree() while it does it inside commit_tree(). Again the\n> principle is pretty much the same. I'll see if I can resist from\n> stealing some more :)\n\nWhy don't you two combine your series instead? :)\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"149959","messageId":"AANLkTikwKgMy4x3n7jUKmpbxvpwtwHD-KBynu87WM==Y@mail.gmail.com","threadId":"24981","inReplyTo":"AANLkTimf_EJcZtJk_X7FiKqQy-oRU4NvZsXMCEcsB=Wa@mail.gmail.com","subject":"Re: [PATCH 00/17] Narrow clone v3 (was subtree clone)","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-05T07:13:39Z","receivedAt":"2010-09-05T07:13:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/9/5 Sverre Rabbelier <srabbelier@gmail.com>:\n> Heya,\n>\n> 2010/9/5 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n>> Tree generating from index, Elijah merges the base tree inside\n>> write_cache_as_tree() while it does it inside commit_tree(). Again the\n>> principle is pretty much the same. I'll see if I can resist from\n>> stealing some more :)\n>\n> Why don't you two combine your series instead? :)\n\nHm.. the two series have different, conflicting goals. I think we'll\nend up sharing a lot though.\n-- \nDuy\n"},{"id":"149999","messageId":"AANLkTikxr_uYu69RCcDNGzVcEexdykde0+ixh_Ti8rBx@mail.gmail.com","threadId":"24981","inReplyTo":"AANLkTikwKgMy4x3n7jUKmpbxvpwtwHD-KBynu87WM==Y@mail.gmail.com","subject":"Re: [PATCH 00/17] Narrow clone v3 (was subtree clone)","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2010-09-05T21:05:09Z","receivedAt":"2010-09-05T21:05:09Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Sep 5, 2010 at 1:13 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n> 2010/9/5 Sverre Rabbelier <srabbelier@gmail.com>:\n>> 2010/9/5 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n>>> Tree generating from index, Elijah merges the base tree inside\n>>> write_cache_as_tree() while it does it inside commit_tree(). Again the\n>>> principle is pretty much the same. I'll see if I can resist from\n>>> stealing some more :)\n>>\n>> Why don't you two combine your series instead? :)\n>\n> Hm.. the two series have different, conflicting goals. I think we'll\n> end up sharing a lot though.\n\nI think we'll probably eventually converge, and have been some\nalready.  For example, Nguyen dropped his tree-rewriting and\nremote-merge; and I'm going to drop my passing of arguments to upload\npack on the command line, and my setup_revisions changes.  I'll\nreplace them with some of his patches, because his changes are a lot\nbetter there.  Since he's planning to work on fsck next (it was down a\nways on my list), I should be able to just adopt that too (especially\nif he does it by making it use rev-list).  Finally, we've also adopted\nideas in our discussions from each other.  Even if we don't converge\nall the way, if we keep cherry-picking good stuff from each other and\ngiving each other feedback, we'll get close and then others on the git\nlist can chime in on what they'd prefer for any remaining differences\nbetween us.\n"},{"id":"150040","messageId":"AANLkTinNnm=Z=e_=P_auuNgSrDynt_P+FLiZgWeNNgps@mail.gmail.com","threadId":"24981","inReplyTo":"1283669264-15759-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 00/17] Narrow clone v3 (was subtree clone)","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2010-09-06T05:17:07Z","receivedAt":"2010-09-06T05:17:07Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Hi,\n\n2010/9/5 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n> I'll describe differences between this series and Elijah's one [1].\n> I think it's more interesting. Changes from v2 [2] will follow later.\n\nSo, I downloaded your patches and even made sure to sort them\nappropriately to fix the order, but I'm getting conflicts trying to\napply them (on top of current pu).  What version did you base them on?\n\n> In short I think the two series are converging. The outstanding\n> difference is Elijah drops shallow clone in favor of more flexible\n> history cutting while I only focus on tree cutting.\n>\n> Two other differences are tree traversal and tree generating. I admit\n> that changing traverse_trees() the way Elijah does is more flexible\n> and is probably the only way to support negative pathspec. And I think\n> his sparse clone supports even cloning a single file. Mine does not\n> support that. I'm going to steal some of his patches at some point.\n\nYes, I can clone a single file.\n\n> Tree generating from index, Elijah merges the base tree inside\n> write_cache_as_tree() while it does it inside commit_tree(). Again the\n> principle is pretty much the same. I'll see if I can resist from\n> stealing some more :)\n\nYou're too modest; your comparison simply omitted some of the areas\nwhere your series shines, such as the get_pathspec fixes (my stuff was\nbroken and much less complete), merge support, and nicer\nfetch/push/clone support.  You also had some other nice touches\n(documentation updates, new rev-parse flag) that may not have been a\nbig deal, but they're still nice.  I'm going to be cherry-picking a\nlot of that stuff, and replacing the relevant bits of my series.  Who\nknows, maybe we're converging quicker than it looked at first glance?\n:-)\n\n> Things that won't work:\n>\n>  - Shell scripts that use \"git write-tree\"\n\nYeah, write-tree didn't work in mine either; I had to make it throw an\nerror.  But wouldn't your idea to make a tree object (referenced for\nsha1sums outside the sparse/narrow paths) part of the index allow even\nwrite-tree to work?\n\n>  - only send commits that have changes in narrow area and graft it at\n>   client side\n\nAfter reviewing more of your changes, and replacing various patches of\nmine with yours, this is fairly high on my priority list as well\n(whereas fsck & prune are a bit lower).  Maybe we can discuss ideas on\ntackling this when we start working on it.  I've got some rough\ninitial ideas (though I have no idea if they'll pan out); I'll see if\nI can write some of them up in the next day or two.\n\nElijah\n"},{"id":"150041","messageId":"AANLkTimyZfkMCFEeAX_Cpwscb0+EiZam0Hy0UBsB47C3@mail.gmail.com","threadId":"24981","inReplyTo":"AANLkTinNnm=Z=e_=P_auuNgSrDynt_P+FLiZgWeNNgps@mail.gmail.com","subject":"Re: [PATCH 00/17] Narrow clone v3 (was subtree clone)","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-09-06T05:24:33Z","receivedAt":"2010-09-06T05:24:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2010/9/6 Elijah Newren <newren@gmail.com>:\n> Hi,\n>\n> 2010/9/5 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>:\n>> I'll describe differences between this series and Elijah's one [1].\n>> I think it's more interesting. Changes from v2 [2] will follow later.\n>\n> So, I downloaded your patches and even made sure to sort them\n> appropriately to fix the order, but I'm getting conflicts trying to\n> apply them (on top of current pu).  What version did you base them on?\n\nb5442ca (was master in Aug 24) with en/object-list-with-pathspec\napplied on top. Sorry I don't want to change my base too often so it's\nquite old.\n\n>> Things that won't work:\n>>\n>>  - Shell scripts that use \"git write-tree\"\n>\n> Yeah, write-tree didn't work in mine either; I had to make it throw an\n> error.  But wouldn't your idea to make a tree object (referenced for\n> sha1sums outside the sparse/narrow paths) part of the index allow even\n> write-tree to work?\n\nDefinitely.\n\n>>  - only send commits that have changes in narrow area and graft it at\n>>   client side\n>\n> After reviewing more of your changes, and replacing various patches of\n> mine with yours, this is fairly high on my priority list as well\n> (whereas fsck & prune are a bit lower).  Maybe we can discuss ideas on\n> tackling this when we start working on it.  I've got some rough\n> initial ideas (though I have no idea if they'll pan out); I'll see if\n> I can write some of them up in the next day or two.\n\nWell, looking forward to your fake commit implementation. I think it's\nmore or less the same as graft.\n-- \nDuy\n"},{"id":"150123","messageId":"AANLkTin3VykgwJyMaVNmDnjAU3_pkA0+cKn-GO0OSnBN@mail.gmail.com","threadId":"24981","inReplyTo":"AANLkTinNnm=Z=e_=P_auuNgSrDynt_P+FLiZgWeNNgps@mail.gmail.com","subject":"Re: [PATCH 00/17] Narrow clone v3 (was subtree clone)","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2010-09-06T20:29:54Z","receivedAt":"2010-09-06T20:29:54Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\n2010/9/6 Elijah Newren <newren@gmail.com>:\n> So, I downloaded your patches and even made sure to sort them\n> appropriately to fix the order, but I'm getting conflicts trying to\n> apply them (on top of current pu).  What version did you base them on?\n\nIt's considered best practice to base your patches on master if\npossible. You should only base them on next if you rely on a\nparticular series that has already landed there. You should never base\nyour patches on pu itself, on a topic in pu perhaps, but never on pu.\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"150124","messageId":"AANLkTimaY6MmS8N+UHt43FCKh5v_hhWzWoRzdy_dPvOr@mail.gmail.com","threadId":"24981","inReplyTo":"AANLkTin3VykgwJyMaVNmDnjAU3_pkA0+cKn-GO0OSnBN@mail.gmail.com","subject":"Re: [PATCH 00/17] Narrow clone v3 (was subtree clone)","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2010-09-06T20:40:29Z","receivedAt":"2010-09-06T20:40:29Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"2010/9/6 Sverre Rabbelier <srabbelier@gmail.com>:\n> Heya,\n>\n> 2010/9/6 Elijah Newren <newren@gmail.com>:\n>> So, I downloaded your patches and even made sure to sort them\n>> appropriately to fix the order, but I'm getting conflicts trying to\n>> apply them (on top of current pu).  What version did you base them on?\n>\n> It's considered best practice to base your patches on master if\n> possible. You should only base them on next if you rely on a\n> particular series that has already landed there. You should never base\n> your patches on pu itself, on a topic in pu perhaps, but never on pu.\n\nAh, thanks for the reminder.  I probably should have mentioned that I\ntried master and next too and had similar conflicts (I believe from\nsome of AEvar's testing changes in master modifying the makefile in\nnearby locations, among other things).  But in any event, I'm in good\nshape now.\n\nI might have been able to make progress too, if I weren't being\ndistracted from sparse clones by this rename+D/F conflict problem\n(though I think I've got it licked now...)\n\nElijah\n"}]}