{"thread":{"id":"16527","subject":"[PATCH 0/8] Sparse checkout, the last half of the series","startedAt":"2008-11-30T10:54:30Z","lastAt":"2008-12-01T15:39:43Z","messageCount":13,"participants":["Nguyễn Thái Ngọc Duy","Johannes Schindelin","Nguyen Thai Ngoc Duy","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"96771","messageId":"1228042478-1886-1-git-send-email-pclouds@gmail.com","threadId":"16527","inReplyTo":null,"subject":"[PATCH 0/8] Sparse checkout, the last half of the series","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-11-30T10:54:30Z","receivedAt":"2008-11-30T10:54:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This series is almost identical to my last sent series with bug fixes.\nDocumentation is hopefully detail enough so I don't have to say more here.\n\nNguyá»n ThÃ¡i Ngá»c Duy (8):\n  generate-cmdlist.sh: avoid selecting synopsis at wrong place\n  Introduce \"sparse patterns\"\n  unpack_trees(): keep track of unmerged entries\n  unpack_trees(): add support for sparse checkout\n  clone: support sparse checkout with --sparse-checkout option\n  checkout: add new options to support sparse checkout\n  Introduce default sparse patterns (core.defaultsparse)\n  wt-status: show sparse checkout info\n\n Documentation/config.txt         |   10 +\n Documentation/git-checkout.txt   |  133 ++++++++++++++-\n Documentation/git-clone.txt      |   11 +-\n Documentation/git-ls-files.txt   |    8 +-\n builtin-checkout.c               |   93 ++++++++++\n builtin-clone.c                  |   16 ++\n builtin-ls-files.c               |   23 ++-\n builtin-read-tree.c              |    3 +-\n cache.h                          |    5 +\n config.c                         |    5 +\n environment.c                    |    1 +\n generate-cmdlist.sh              |    2 +-\n t/t2011-checkout-sparse.sh       |  147 ++++++++++++++++\n t/t3003-ls-files-narrow-match.sh |   41 +++++\n t/t3003/1                        |    4 +\n t/t3003/12                       |    8 +\n t/t3003/clone-escape             |    5 +\n t/t3003/cur-12                   |    2 +\n t/t3003/prefix-sub2              |    3 +\n t/t3003/root-sub-1               |    1 +\n t/t3003/slash-1                  |    1 +\n t/t3003/sub-1                    |    2 +\n t/t3003/sub-only                 |    3 +\n t/t3003/subsub-slash             |    3 +\n t/t5703-clone-narrow.sh          |   42 +++++\n unpack-trees.c                   |  360 +++++++++++++++++++++++++++++++++++++-\n unpack-trees.h                   |   29 +++\n wt-status.c                      |   44 +++++\n wt-status.h                      |    1 +\n 29 files changed, 988 insertions(+), 18 deletions(-)\n create mode 100755 t/t2011-checkout-sparse.sh\n create mode 100755 t/t3003-ls-files-narrow-match.sh\n create mode 100644 t/t3003/1\n create mode 100644 t/t3003/12\n create mode 100644 t/t3003/clone-escape\n create mode 100644 t/t3003/cur-12\n create mode 100644 t/t3003/prefix-sub2\n create mode 100644 t/t3003/root-sub-1\n create mode 100644 t/t3003/slash-1\n create mode 100644 t/t3003/sub\n create mode 100644 t/t3003/sub-1\n create mode 100644 t/t3003/sub-only\n create mode 100644 t/t3003/subsub-slash\n create mode 100755 t/t5703-clone-narrow.sh\n"},{"id":"96772","messageId":"1228042478-1886-2-git-send-email-pclouds@gmail.com","threadId":"16527","inReplyTo":"1228042478-1886-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/8] generate-cmdlist.sh: avoid selecting synopsis at wrong place","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-11-30T10:54:31Z","receivedAt":"2008-11-30T10:54:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In \"common\" man pages there is luckily no \"NAME\" anywhere except at\nbeginning of documents. If there is another \"NAME\", sed could\nmis-select it and lead to common-cmds.h corruption. So better nail it\nat beginning of line, which would reduce corruption chance.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n generate-cmdlist.sh |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/generate-cmdlist.sh b/generate-cmdlist.sh\nindex a2913c2..75c68d9 100755\n--- a/generate-cmdlist.sh\n+++ b/generate-cmdlist.sh\n@@ -14,7 +14,7 @@ sort |\n while read cmd\n do\n      sed -n '\n-     /NAME/,/git-'\"$cmd\"'/H\n+     /^NAME/,/git-'\"$cmd\"'/H\n      ${\n             x\n             s/.*git-'\"$cmd\"' - \\(.*\\)/  {\"'\"$cmd\"'\", \"\\1\"},/\n-- \n1.6.0.3.890.g95457\n"},{"id":"96773","messageId":"1228042478-1886-3-git-send-email-pclouds@gmail.com","threadId":"16527","inReplyTo":"1228042478-1886-2-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/8] Introduce \"sparse patterns\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-11-30T10:54:32Z","receivedAt":"2008-11-30T10:54:32Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Sparse patterns are basically .gitignore patterns, with the following\nexceptions:\n\n - a leading slash means toplevel working directory\n - ./ will be used if you want to apply \"slash in pattern\" rule,\n   similar to leading slash case in .gitignore\n - prefix can be specified in form prefix//pattern\n - patterns are separated by colons\n\n\"git ls-files --narrow-match\" is added to test sparse patterns.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt   |   57 +++++++++++++\n Documentation/git-ls-files.txt   |    8 ++-\n builtin-ls-files.c               |   23 +++++-\n t/t3003-ls-files-narrow-match.sh |   41 +++++++++\n t/t3003/1                        |    4 +\n t/t3003/12                       |    8 ++\n t/t3003/clone-escape             |    5 +\n t/t3003/cur-12                   |    2 +\n t/t3003/prefix-sub2              |    3 +\n t/t3003/root-sub-1               |    1 +\n t/t3003/slash-1                  |    1 +\n t/t3003/sub-1                    |    2 +\n t/t3003/sub-only                 |    3 +\n t/t3003/subsub-slash             |    3 +\n unpack-trees.c                   |  170 ++++++++++++++++++++++++++++++++++++++\n unpack-trees.h                   |   20 +++++\n 16 files changed, 347 insertions(+), 4 deletions(-)\n create mode 100755 t/t3003-ls-files-narrow-match.sh\n create mode 100644 t/t3003/1\n create mode 100644 t/t3003/12\n create mode 100644 t/t3003/clone-escape\n create mode 100644 t/t3003/cur-12\n create mode 100644 t/t3003/prefix-sub2\n create mode 100644 t/t3003/root-sub-1\n create mode 100644 t/t3003/slash-1\n create mode 100644 t/t3003/sub\n create mode 100644 t/t3003/sub-1\n create mode 100644 t/t3003/sub-only\n create mode 100644 t/t3003/subsub-slash\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ea05a7a..0813d9f 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -205,6 +205,63 @@ is \"assume unchanged\" bit just ignores corresponding files in working\n directory while sparse checkout goes a bit farther, remove those files\n when it is safe to do so.\n \n+Sparse patterns\n+---------------\n+\n+Sparse patterns specify how do you want to form your checkout area.\n+Many patterns can be specified on one line, separated by colons.\n+The patterns specify what files should or should not be checked out\n+on working directory (depends on the option used with the patterns).\n+Patterns have the following format:\n+\n+ - An optional prefix '!' which negates the pattern; any\n+   matching file by a previous pattern will become\n+   unmatched again.  If a negated pattern matches, this will\n+   override lower precedence patterns sources.\n+\n+ - If the pattern ends with a slash, it is removed for the\n+   purpose of the following description, but it would only find\n+   a match with a directory.  In other words, `foo/` will match a\n+   directory `foo` and paths underneath it, but will not match a\n+   regular file or a symbolic link `foo` (this is consistent\n+   with the way how pathspec works in general in git).\n+\n+ - If the pattern does not contain a slash '/', git treats it as\n+   a shell glob pattern and checks for a match against the\n+   pathname without leading directories.\n+\n+ - If the pattern contains two consecutive slashes '//', git uses\n+   the first part of the pattern before '//' as prefix to apply\n+   the pattern on, and the rest as \"true\" sparse pattern. The\n+   pattern will be only applied if pathname has the same prefix. It\n+   is the same as you apply the pattern with the current directory\n+   being the extracted prefix. Prefix will always be relative to\n+   top-level working directory. This could be used to if you want to\n+   apply the above rule only in a specific subdirectory.\n+\n+ - Otherwise, git treats the pattern as a shell glob suitable\n+   for consumption by fnmatch(3) with the FNM_PATHNAME flag:\n+   wildcards in the pattern will not match a / in the pathname.\n+   For example, \"Documentation/\\*.html\" matches\n+   \"Documentation/git.html\" but not\n+   \"Documentation/ppc/ppc.html\".  A leading slash matches the\n+   beginning of the pathname; for example, \"/*.c\" matches\n+   \"cat-file.c\" but not \"mozilla-sha1/sha1.c\".\n+\n+ - Patterns with a leading slash will match against full pathname,\n+   as opposed to normal case when it only matches pathnames relative\n+   to current working directory.\n+\n+ - Patterns begin with \"./\" are treated like normal patterns. That is\n+   it will follow above rules. But since it has a slash inside,\n+   \"fnmatch rule\" will apply. This is a work-around when you do not\n+   want to apply \"no slash\" rule.\n+\n+ - Because colons are used to separate patterns, you cannot put them\n+   in patterns directly. You must quote them using backslash.\n+\n+ - Sparse patterns do not apply to .gitignore and .gitattributes\n+   files. They are always checked out.\n \n EXAMPLES\n --------\ndiff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt\nindex 1de68e2..fbed73b 100644\n--- a/Documentation/git-ls-files.txt\n+++ b/Documentation/git-ls-files.txt\n@@ -12,7 +12,7 @@ SYNOPSIS\n 'git ls-files' [-z] [-t] [-v]\n \t\t(--[cached|deleted|others|ignored|stage|unmerged|killed|modified|orphaned|no-checkout])\\*\n \t\t(-[c|d|o|i|s|u|k|m])\\*\n-\t\t[--sparse]\n+\t\t[--sparse] [--narrow-match=<sparse patterns>]\n \t\t[-x <pattern>|--exclude=<pattern>]\n \t\t[-X <file>|--exclude-from=<file>]\n \t\t[--exclude-per-directory=<file>]\n@@ -90,6 +90,12 @@ OPTIONS\n \tNo-checkout entries can be shown using --orphaned or\n \t--no-checkout (or both).\n \n+--narrow-match=<sparse patterns>::\n+\tThis option can be used to test sparse patterns. The given sparse patterns will\n+\tbe used to filter ls-files output. Entries not matching the spec will be\n+\tignored. This option can only be used with --cached or --stage.\n+\tSee linkgit:git-checkout[1] for more information about sparse patterns.\n+\n -z::\n \t\\0 line termination on output.\n \ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex 7606ce1..1b67ae2 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -10,6 +10,8 @@\n #include \"dir.h\"\n #include \"builtin.h\"\n #include \"tree.h\"\n+#include \"tree-walk.h\"\n+#include \"unpack-trees.h\"\n \n static int abbrev;\n static int show_deleted;\n@@ -31,6 +33,7 @@ static const char **pathspec;\n static int error_unmatch;\n static char *ps_matched;\n static const char *with_tree;\n+static struct narrow_spec *narrow_spec;\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n@@ -158,7 +161,8 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)\n \tint len = prefix_len;\n \tint offset = prefix_offset;\n \n-\tif (len >= ce_namelen(ce))\n+\t/* when narrow_spec->full_index is true, the_index is not pruned */\n+\tif (len >= ce_namelen(ce) && (!narrow_spec || !narrow_spec->full_index))\n \t\tdie(\"git ls-files: internal error - cache entry not superset of prefix\");\n \n \tif (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len))\n@@ -231,6 +235,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)\n \t\t\t}\n \t\t\tif (!(show_cached | show_stage))\n \t\t\t\tcontinue;\n+\t\t\tif (narrow_spec && !match_narrow_spec(narrow_spec, ce->name))\n+\t\t\t\tcontinue;\n \t\t\tshow_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);\n \t\t}\n \t}\n@@ -412,7 +418,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_\n \n static const char ls_files_usage[] =\n \t\"git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified|orphaned|no-checkout])* \"\n-\t\"[ --sparse ] \"\n+\t\"[ --sparse ] [--narrow-match=<narrow_spec>] \"\n \t\"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] \"\n \t\"[ --exclude-per-directory=<filename> ] [--exclude-standard] \"\n \t\"[--full-name] [--abbrev] [--] [<file>]*\";\n@@ -471,6 +477,10 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\t\tsparse_checkout = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!prefixcmp(arg, \"--narrow-match=\")) {\n+\t\t\tnarrow_spec = parse_narrow_spec(arg+15, prefix);\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"-d\") || !strcmp(arg, \"--deleted\")) {\n \t\t\tshow_deleted = 1;\n \t\t\tcontinue;\n@@ -602,8 +612,14 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t      show_killed | show_modified | show_orphaned | show_no_checkout))\n \t\tshow_cached = 1;\n \n+\tif (narrow_spec && !show_cached && !show_stage)\n+\t\tdie(\"ls-files: --narrow-match can only be used with either --cached or --stage\");\n+\n+\tif (narrow_spec && narrow_spec->full_index && prefix_offset != 0)\n+\t\tdie(\"ls-files: --narrow-match with root matching patterns requires --full-name\");\n+\n \tread_cache();\n-\tif (prefix)\n+\tif (prefix && (!narrow_spec || !narrow_spec->full_index))\n \t\tprune_cache(prefix);\n \tif (with_tree) {\n \t\t/*\n@@ -625,5 +641,6 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\treturn bad ? 1 : 0;\n \t}\n \n+\tfree_narrow_spec(narrow_spec);\n \treturn 0;\n }\ndiff --git a/t/t3003-ls-files-narrow-match.sh b/t/t3003-ls-files-narrow-match.sh\nnew file mode 100755\nindex 0000000..9879525\n--- /dev/null\n+++ b/t/t3003-ls-files-narrow-match.sh\n@@ -0,0 +1,41 @@\n+#!/bin/sh\n+\n+test_description='This test is for narrow spec matching'\n+\n+. test-lib.sh\n+\n+D=\"$(cd ..;pwd)\"/t3003\n+\n+test_pattern() {\n+\ttest_expect_success \"pattern $1\" '\n+\t\t(\n+\t\tif [ -n \"'$3'\" ]; then cd '$3'; fi\n+\t\tgit ls-files --full-name --narrow-match=\"'\"$2\"'\" > result &&\n+\t\tdiff -u result \"'\"$D/$1\"'\"\n+\t\t)\n+\t'\n+}\n+\n+test_expect_success 'setup' '\n+\ttouch ./1 ./2 ./3 ./\"1:2\" &&\n+\tmkdir -p sub/subsub sub2 &&\n+\ttouch sub/1 sub/2 sub/3 &&\n+\ttouch sub2/1 sub2/2 sub2/3 &&\n+\ttouch sub/subsub/1 sub/subsub/2 sub/subsub/3 &&\n+\tgit add .\n+'\n+\n+test_pattern 1 1\n+test_pattern sub sub\n+test_pattern sub-1 1 sub\n+test_pattern root-sub-1 /1 sub\n+test_pattern subsub-slash subsub/ sub\n+test_pattern sub-only 'sub/:!sub/subsub/'\n+test_pattern 12 1:2\n+test_pattern cur-12 ./1:./2\n+test_pattern slash-1 'sub/*1'\n+test_pattern clone-escape '1\\:2:1'\n+test_pattern prefix-sub2 1:sub2//2 sub\n+\n+test_done\n+\ndiff --git a/t/t3003/1 b/t/t3003/1\nnew file mode 100644\nindex 0000000..4c7381e\n--- /dev/null\n+++ b/t/t3003/1\n@@ -0,0 +1,4 @@\n+1\n+sub/1\n+sub/subsub/1\n+sub2/1\ndiff --git a/t/t3003/12 b/t/t3003/12\nnew file mode 100644\nindex 0000000..0eeb4b8\n--- /dev/null\n+++ b/t/t3003/12\n@@ -0,0 +1,8 @@\n+1\n+2\n+sub/1\n+sub/2\n+sub/subsub/1\n+sub/subsub/2\n+sub2/1\n+sub2/2\ndiff --git a/t/t3003/clone-escape b/t/t3003/clone-escape\nnew file mode 100644\nindex 0000000..31f866e\n--- /dev/null\n+++ b/t/t3003/clone-escape\n@@ -0,0 +1,5 @@\n+1\n+1:2\n+sub/1\n+sub/subsub/1\n+sub2/1\ndiff --git a/t/t3003/cur-12 b/t/t3003/cur-12\nnew file mode 100644\nindex 0000000..1191247\n--- /dev/null\n+++ b/t/t3003/cur-12\n@@ -0,0 +1,2 @@\n+1\n+2\ndiff --git a/t/t3003/prefix-sub2 b/t/t3003/prefix-sub2\nnew file mode 100644\nindex 0000000..27c415c\n--- /dev/null\n+++ b/t/t3003/prefix-sub2\n@@ -0,0 +1,3 @@\n+sub/1\n+sub/subsub/1\n+sub2/2\ndiff --git a/t/t3003/root-sub-1 b/t/t3003/root-sub-1\nnew file mode 100644\nindex 0000000..d00491f\n--- /dev/null\n+++ b/t/t3003/root-sub-1\n@@ -0,0 +1 @@\n+1\ndiff --git a/t/t3003/slash-1 b/t/t3003/slash-1\nnew file mode 100644\nindex 0000000..5798e42\n--- /dev/null\n+++ b/t/t3003/slash-1\n@@ -0,0 +1 @@\n+sub/1\ndiff --git a/t/t3003/sub b/t/t3003/sub\nnew file mode 100644\nindex 0000000..e69de29\ndiff --git a/t/t3003/sub-1 b/t/t3003/sub-1\nnew file mode 100644\nindex 0000000..3ef951a\n--- /dev/null\n+++ b/t/t3003/sub-1\n@@ -0,0 +1,2 @@\n+sub/1\n+sub/subsub/1\ndiff --git a/t/t3003/sub-only b/t/t3003/sub-only\nnew file mode 100644\nindex 0000000..3115212\n--- /dev/null\n+++ b/t/t3003/sub-only\n@@ -0,0 +1,3 @@\n+sub/1\n+sub/2\n+sub/3\ndiff --git a/t/t3003/subsub-slash b/t/t3003/subsub-slash\nnew file mode 100644\nindex 0000000..bc585b0\n--- /dev/null\n+++ b/t/t3003/subsub-slash\n@@ -0,0 +1,3 @@\n+sub/subsub/1\n+sub/subsub/2\n+sub/subsub/3\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 54f301d..83888ae 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -726,6 +726,176 @@ static void show_stage_entry(FILE *o,\n }\n #endif\n \n+struct narrow_spec *parse_narrow_spec(const char *spec, const char *prefix)\n+{\n+\tstruct narrow_spec *ns;\n+\tstruct narrow_pattern *p;\n+\tconst char *start, *end, *prefix_end;\n+\tint has_wildcards, has_slashes;\n+\n+\tns = xmalloc(sizeof(*ns));\n+\tmemset(ns, 0, sizeof(*ns));\n+\tif (prefix)\n+\t\tns->prefix = xstrdup(prefix);\n+\n+\tstart = spec;\n+\twhile (*start) {\n+\t\tend = start;\n+\t\tprefix_end = NULL;\n+\t\thas_slashes = has_wildcards = 0;\n+\t\twhile (*end && *end != ':') {\n+\t\t\tif (*end == '*' || *end == '[' || *end == '?')\n+\t\t\t\thas_wildcards = 1;\n+\t\t\tif (*end == '/') {\n+\t\t\t\thas_slashes = 1;\n+\n+\t\t\t\t/* A prefix is terminated by double slashes */\n+\t\t\t\tif (end[1] == '/' && !prefix_end) {\n+\t\t\t\t\tprefix_end = end+2;\n+\t\t\t\t\t/* start over again */\n+\t\t\t\t\thas_slashes = has_wildcards = 0;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (*end == '\\\\') {\n+\t\t\t\tend++;\n+\t\t\t\thas_wildcards = 1;\n+\t\t\t\tif (*end == '\\0') /* trailing backslash */\n+\t\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tend++;\n+\t\t}\n+\t\t/* empty pattern or pattern with prefix only */\n+\t\tif (start == end || prefix_end == end)\n+\t\t\tgoto cont;\n+\n+\t\tp = xmalloc(sizeof(*p));\n+\t\tmemset(p, 0, sizeof(*p));\n+\t\tif (prefix_end) {\n+\t\t\tint len = prefix_end - start - 1; /* only take one slash */\n+\t\t\tp->prefix = xmalloc(len + 1);\n+\t\t\tmemcpy(p->prefix, start, len);\n+\t\t\tp->prefix[len] = '\\0';\n+\t\t\tstart = prefix_end;\n+\t\t}\n+\t\tif (*start == '!') {\n+\t\t\tp->negative = 1;\n+\t\t\tstart++;\n+\t\t}\n+\t\tp->has_slashes = has_slashes;\n+\t\tp->has_wildcards = has_wildcards;\n+\t\tp->has_trailing_slash = end[-1] == '/';\n+\t\tif (*start == '/') {\n+\t\t\tp->has_root = 1;\n+\t\t\tstart++;\n+\t\t}\n+\t\telse if (*start == '.' && start[1] == '/')\n+\t\t\tstart += 2;\n+\t\tp->len = end-start;\n+\t\tp->pattern = xmalloc(p->len+1);\n+\t\tmemcpy(p->pattern, start, p->len);\n+\t\tp->pattern[p->len] = '\\0';\n+\n+\t\tALLOC_GROW(ns->patterns, ns->nr + 1, ns->alloc);\n+\t\tns->patterns[ns->nr++] = p;\n+\n+\t\t/*\n+\t\t * ls-files needs to know whether it can prune the index\n+\t\t * in these cases, index cannot be pruned\n+\t\t */\n+\t\tif (p->has_root || p->prefix)\n+\t\t\tns->full_index = 1;\n+\n+cont:\n+\t\tif (*end != ':')\n+\t\t\tbreak;\n+\t\tstart = end + 1;\n+\t}\n+\treturn ns;\n+}\n+\n+void free_narrow_spec(struct narrow_spec *spec)\n+{\n+\tint i;\n+\tif (!spec)\n+\t\treturn;\n+\tfor (i = 0; i < spec->nr; i++) {\n+\t\tstruct narrow_pattern *p = spec->patterns[i];\n+\t\tif (p->prefix)\n+\t\t\tfree(p->prefix);\n+\t\tif (p->pattern)\n+\t\t\tfree(p->pattern);\n+\t\tfree(p);\n+\t}\n+\tif (spec->patterns)\n+\t\tfree(spec->patterns);\n+\tif (spec->prefix)\n+\t\tfree(spec->prefix);\n+\tfree(spec);\n+}\n+\n+int match_narrow_spec(struct narrow_spec *spec, const char *path)\n+{\n+\tint i, match;\n+\n+\tif (!spec)\n+\t\treturn 1; /* always match if spec is NULL */\n+\n+\tfor (i = spec->nr - 1;i >= 0; i--) {\n+\t\tstruct narrow_pattern\t*p\t    = spec->patterns[i];\n+\t\tconst char\t\t*prefix\t    = p->prefix   ? p->prefix      : spec->prefix;\n+\t\tint\t\t\t prefix_len = prefix      ? strlen(prefix) : 0;\n+\t\tconst char\t\t*new_path   = p->has_root ? path           : path + prefix_len;\n+\n+\t\tif (!p->has_root && prefix && prefixcmp(path, prefix))\n+\t\t\tcontinue;\n+\n+\t\tif (p->has_trailing_slash) {\n+\t\t\t/* the only \"wildcard\" here is backslash escape */\n+\t\t\tif (p->has_wildcards) {\n+\t\t\t\tchar *unescaped_pattern = xstrdup(p->pattern);\n+\t\t\t\tchar *src, *dst;\n+\n+\t\t\t\tsrc = dst = unescaped_pattern;\n+\t\t\t\twhile (*src) {\n+\t\t\t\t\tif (*src == '\\\\')\n+\t\t\t\t\t\tsrc++;\n+\t\t\t\t\tif (src != dst)\n+\t\t\t\t\t\t*dst = *src;\n+\t\t\t\t\tsrc++;\n+\t\t\t\t\tdst++;\n+\t\t\t\t}\n+\t\t\t\t*dst = '\\0';\n+\t\t\t\tmatch = prefixcmp(new_path, unescaped_pattern) == 0;\n+\t\t\t\tfree(unescaped_pattern);\n+\t\t\t}\n+\t\t\telse\n+\t\t\t\tmatch = prefixcmp(new_path, p->pattern) == 0;\n+\t\t}\n+\t\telse if (p->has_slashes) {\n+\t\t\tif (p->has_wildcards)\n+\t\t\t\tmatch = fnmatch(p->pattern, new_path, FNM_PATHNAME) == 0;\n+\t\t\telse\n+\t\t\t\tmatch = strcmp(p->pattern, new_path) == 0;\n+\t\t}\n+\t\telse {\n+\t\t\tconst char *basename = strrchr(path + prefix_len, '/');\n+\t\t\tif (basename)\n+\t\t\t\tbasename++;\n+\t\t\telse\n+\t\t\t\tbasename = path + prefix_len;\n+\t\t\tif (p->has_wildcards)\n+\t\t\t\tmatch = fnmatch(p->pattern, basename, 0) == 0;\n+\t\t\telse\n+\t\t\t\tmatch = strcmp(p->pattern, basename) == 0;\n+\t\t}\n+\t\tif (match)\n+\t\t\treturn p->negative ? 0 : 1;\n+\t}\n+\n+\t/* no pattern is matched */\n+\treturn 0;\n+}\n+\n int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)\n {\n \tstruct cache_entry *index;\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 0d26f3d..93ec3c6 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -16,6 +16,23 @@ struct unpack_trees_error_msgs {\n \tconst char *bind_overlap;\n };\n \n+struct narrow_spec {\n+\tint nr;\n+\tint alloc;\n+\tint full_index;\n+\tchar *prefix;\n+\tstruct narrow_pattern {\n+\t\tint len;\n+\t\tint has_root:1;\n+\t\tint has_slashes:1;\n+\t\tint has_wildcards:1;\n+\t\tint has_trailing_slash:1;\n+\t\tint negative:1;\n+\t\tchar *pattern;\n+\t\tchar *prefix;\n+\t} **patterns;\n+};\n+\n struct unpack_trees_options {\n \tunsigned int reset:1,\n \t\t     merge:1,\n@@ -48,6 +65,9 @@ struct unpack_trees_options {\n extern int unpack_trees(unsigned n, struct tree_desc *t,\n \t\tstruct unpack_trees_options *options);\n \n+struct narrow_spec *parse_narrow_spec(const char *spec, const char *prefix);\n+void free_narrow_spec(struct narrow_spec *spec);\n+int match_narrow_spec(struct narrow_spec *spec, const char *path);\n int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);\n int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);\n int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);\n-- \n1.6.0.3.890.g95457\n"},{"id":"96774","messageId":"1228042478-1886-4-git-send-email-pclouds@gmail.com","threadId":"16527","inReplyTo":"1228042478-1886-3-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/8] unpack_trees(): keep track of unmerged entries","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-11-30T10:54:33Z","receivedAt":"2008-11-30T10:54:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git read-tree --reset\" will currently remove all unmerged entries in\nindex before feeding the index to unpack_trees(). Because the lack of\nunmerged entries, these entries, when read from tree, will be seen as\n\"new entries\" by {one,two,three}way_merge().\n\nThis is fine for now. But for sparse checkout, it needs to know\nwhether an entry is new entry, because it will handle it different way\nthan already-in entry.\n\nSo the patch moves \"unmerged entries removal\" part into\nunpack_trees(), actually unpack_callback(). The function then can turn\non o->has_unmerged flag, which can be utilized by sparse checkout.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin-read-tree.c |    3 ++-\n unpack-trees.c      |    7 +++++++\n unpack-trees.h      |    2 ++\n 3 files changed, 11 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-read-tree.c b/builtin-read-tree.c\nindex 38fef34..528134c 100644\n--- a/builtin-read-tree.c\n+++ b/builtin-read-tree.c\n@@ -136,9 +136,10 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)\n \t\t\tif (stage || opts.merge || opts.prefix)\n \t\t\t\tusage(read_tree_usage);\n \t\t\topts.reset = 1;\n+\t\t\topts.prune_unmerged = 1;\n \t\t\topts.merge = 1;\n \t\t\tstage = 1;\n-\t\t\tread_cache_unmerged();\n+\t\t\tread_cache();\n \t\t\tcontinue;\n \t\t}\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 83888ae..7d99051 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -301,6 +301,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \n \t/* Are we supposed to look at the index too? */\n \tif (o->merge) {\n+\t\to->has_unmerged = 0;\n \t\twhile (o->pos < o->src_index->cache_nr) {\n \t\t\tstruct cache_entry *ce = o->src_index->cache[o->pos];\n \t\t\tint cmp = compare_entry(ce, info, p);\n@@ -320,6 +321,12 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str\n \t\t\t\t\t\tadd_entry(o, ce, 0, 0);\n \t\t\t\t\t\treturn mask;\n \t\t\t\t\t}\n+\n+\t\t\t\t\tif (o->prune_unmerged) {\n+\t\t\t\t\t\to->has_unmerged = 1;\n+\t\t\t\t\t\t/* leave src[0] as NULL and go over all other staged entries */\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\t}\n \t\t\t\t}\n \t\t\t\tsrc[0] = ce;\n \t\t\t}\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 93ec3c6..86f0989 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -43,6 +43,8 @@ struct unpack_trees_options {\n \t\t     verbose_update:1,\n \t\t     aggressive:1,\n \t\t     skip_unmerged:1,\n+\t\t     prune_unmerged:1,\n+\t\t     has_unmerged:1,\n \t\t     initial_checkout:1,\n \t\t     gently:1;\n \tconst char *prefix;\n-- \n1.6.0.3.890.g95457\n"},{"id":"96775","messageId":"1228042478-1886-5-git-send-email-pclouds@gmail.com","threadId":"16527","inReplyTo":"1228042478-1886-4-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/8] unpack_trees(): add support for sparse checkout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-11-30T10:54:34Z","receivedAt":"2008-11-30T10:54:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This patch teaches unpack_trees() to checkout/remove entries\non working directories appropriately when sparse checkout area is\nchanged. There are three kind of changes:\n\n - new_narrow_path: reset workdir to a completely new checkout area\n - add_narrow_path: keep current areas and add more entries\n - remove_narrow_path: remove some entries from current areas\n\nWhen unpack_trees() is called without any of those options, changes in\nno-checkout entries will not get checked out on working directory. New\nfiles still appear on working directory though. That will be handled\nlater by core.defaultsparse\n\nCE_WD_REMOVE is introduced to remove entries from working directories,\nbut still keep them in index\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n cache.h        |    3 +\n unpack-trees.c |  131 +++++++++++++++++++++++++++++++++++++++++++++++++++++---\n unpack-trees.h |    6 +++\n 3 files changed, 134 insertions(+), 6 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 321fc54..b13df06 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -173,6 +173,9 @@ struct cache_entry {\n #define CE_HASHED    (0x100000)\n #define CE_UNHASHED  (0x200000)\n \n+/* Only remove in work directory, not index */\n+#define CE_WT_REMOVE (0x400000)\n+\n /*\n  * Extended on-disk flags\n  */\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 7d99051..a2794b8 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -31,6 +31,12 @@ static struct unpack_trees_error_msgs unpack_plumbing_errors = {\n \n \t/* bind_overlap */\n \t\"Entry '%s' overlaps with '%s'.  Cannot bind.\",\n+\n+\t/* sparse_not_uptodate_file */\n+\t\"Entry '%s' not uptodate. Cannot update sparse checkout.\",\n+\n+\t/* would_lose_orphaned */\n+\t\"Orphaned working tree file '%s' would be %s by sparse checkout update.\",\n };\n \n #define ERRORMSG(o,fld) \\\n@@ -96,7 +102,7 @@ static int check_updates(struct unpack_trees_options *o)\n \tif (o->update && o->verbose_update) {\n \t\tfor (total = cnt = 0; cnt < index->cache_nr; cnt++) {\n \t\t\tstruct cache_entry *ce = index->cache[cnt];\n-\t\t\tif (ce->ce_flags & (CE_UPDATE | CE_REMOVE))\n+\t\t\tif (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WT_REMOVE))\n \t\t\t\ttotal++;\n \t\t}\n \n@@ -108,6 +114,13 @@ static int check_updates(struct unpack_trees_options *o)\n \tfor (i = 0; i < index->cache_nr; i++) {\n \t\tstruct cache_entry *ce = index->cache[i];\n \n+\t\tif (ce->ce_flags & CE_WT_REMOVE) {\n+\t\t\tdisplay_progress(progress, ++cnt);\n+\t\t\tif (o->update)\n+\t\t\t\tunlink_entry(ce);\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tif (ce->ce_flags & CE_REMOVE) {\n \t\t\tdisplay_progress(progress, ++cnt);\n \t\t\tif (o->update)\n@@ -133,6 +146,82 @@ static int check_updates(struct unpack_trees_options *o)\n \treturn errs != 0;\n }\n \n+static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);\n+static int verify_absent_sparse(struct cache_entry *ce, const char *action, struct unpack_trees_options *o);\n+static int apply_narrow_spec(struct unpack_trees_options *o)\n+{\n+\tstruct index_state *index = &o->result;\n+\tint i, len, namelen;\n+\tint header = 0;\n+\tchar *special_files[] = { \".gitignore\", GITATTRIBUTES_FILE, NULL };\n+\tchar **special_file;\n+\n+\tfor (i = 0; i < index->cache_nr; i++) {\n+\t\tstruct cache_entry *ce = index->cache[i];\n+\t\tint was_checkout = ce_checkout(ce);\n+\n+\t\tif (ce_stage(ce))\n+\t\t\tcontinue;\n+\n+\t\t/* Special case: .gitignore and .gitattributes should stay */\n+\t\tfor (special_file = special_files; *special_file; special_file++) {\n+\t\t\tlen = strlen(*special_file);\n+\t\t\tnamelen = ce_namelen(ce);\n+\t\t\tif ((namelen == len ||\n+\t\t\t     (namelen > len && ce->name[namelen-len-1] == '/')) &&\n+\t\t\t    !strcmp(ce->name+namelen-len, *special_file))\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (*special_file)\n+\t\t\tcontinue;\n+\n+\t\tif (o->new_narrow_path | o->add_narrow_path | o->remove_narrow_path) {\n+\t\t\tint match = match_narrow_spec(o->narrow_spec, ce->name);\n+\t\t\tif (o->new_narrow_path) {\n+\t\t\t\tif (match)\n+\t\t\t\t\tce_mark_checkout(ce);\n+\t\t\t\telse\n+\t\t\t\t\tce_mark_no_checkout(ce);\n+\t\t\t}\n+\t\t\tif (o->add_narrow_path && match)\n+\t\t\t\tce_mark_checkout(ce);\n+\t\t\tif (o->remove_narrow_path && match)\n+\t\t\t\tce_mark_no_checkout(ce);\n+\t\t}\n+\n+\t\t/* Update worktree, add/remove entries if needed */\n+\n+\t\t/*\n+\t\t * We only care about files getting into the checkout area\n+\t\t * If merge strategies want to remove some, go ahead\n+\t\t */\n+\t\tif (ce->ce_flags & CE_REMOVE)\n+\t\t\tcontinue;\n+\n+\t\tif (was_checkout && ce_no_checkout(ce)) {\n+\t\t\t/*\n+\t\t\t * If CE_UPDATE is set, verify_uptodate() must be called already\n+\t\t\t * also stat info may have lost after merged_entry() so calling\n+\t\t\t * verify_uptodate() again may fail\n+\t\t\t */\n+\t\t\tif (!(ce->ce_flags & CE_UPDATE) && verify_uptodate_sparse(ce, o))\n+\t\t\t\treturn -1;\n+\t\t\tce->ce_flags |= CE_WT_REMOVE;\n+\t\t}\n+\t\tif (!was_checkout && ce_checkout(ce)) {\n+\t\t\tif (verify_absent_sparse(ce, \"overwritten\", o))\n+\t\t\t\treturn -1;\n+\t\t\tce->ce_flags |= CE_UPDATE;\n+\t\t}\n+\n+\t\t/* merge strategies may set CE_UPDATE outside checkout area */\n+\t\tif (ce_no_checkout(ce))\n+\t\t\tce->ce_flags &= ~CE_UPDATE;\n+\n+\t}\n+\treturn 0;\n+}\n+\n static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)\n {\n \tint ret = o->fn(src, o);\n@@ -416,6 +505,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tif (o->trivial_merges_only && o->nontrivial_merge)\n \t\treturn unpack_failed(o, \"Merge requires file-level merging\");\n \n+\tif (apply_narrow_spec(o))\n+\t\treturn unpack_failed(o, NULL);\n+\n \to->src_index = NULL;\n \tret = check_updates(o) ? (-2) : 0;\n \tif (o->dst_index)\n@@ -445,8 +537,9 @@ static int same(struct cache_entry *a, struct cache_entry *b)\n  * When a CE gets turned into an unmerged entry, we\n  * want it to be up-to-date\n  */\n-static int verify_uptodate(struct cache_entry *ce,\n-\t\tstruct unpack_trees_options *o)\n+static int verify_uptodate_generic(struct cache_entry *ce,\n+\t\t\t\t   struct unpack_trees_options *o,\n+\t\t\t\t   const char *error_msg)\n {\n \tstruct stat st;\n \n@@ -471,7 +564,18 @@ static int verify_uptodate(struct cache_entry *ce,\n \tif (errno == ENOENT)\n \t\treturn 0;\n \treturn o->gently ? -1 :\n-\t\terror(ERRORMSG(o, not_uptodate_file), ce->name);\n+\t\terror(error_msg, ce->name);\n+}\n+\n+static int verify_uptodate(struct cache_entry *ce,\n+\t\t\t   struct unpack_trees_options *o)\n+{\n+\treturn verify_uptodate_generic(ce, o, ERRORMSG(o, not_uptodate_file));\n+}\n+static int verify_uptodate_sparse(struct cache_entry *ce,\n+\t\t\t\t  struct unpack_trees_options *o)\n+{\n+\treturn verify_uptodate_generic(ce, o, ERRORMSG(o, sparse_not_uptodate_file));\n }\n \n static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)\n@@ -583,8 +687,9 @@ static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst,\n  * We do not want to remove or overwrite a working tree file that\n  * is not tracked, unless it is ignored.\n  */\n-static int verify_absent(struct cache_entry *ce, const char *action,\n-\t\t\t struct unpack_trees_options *o)\n+static int verify_absent_generic(struct cache_entry *ce, const char *action,\n+\t\t\t\t struct unpack_trees_options *o,\n+\t\t\t\t const char *error_msg)\n {\n \tstruct stat st;\n \n@@ -662,6 +767,16 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n \t}\n \treturn 0;\n }\n+static int verify_absent(struct cache_entry *ce, const char *action,\n+\t\t\t struct unpack_trees_options *o)\n+{\n+\treturn verify_absent_generic(ce, action, o, ERRORMSG(o, would_lose_untracked));\n+}\n+static int verify_absent_sparse(struct cache_entry *ce, const char *action,\n+\t\t\t struct unpack_trees_options *o)\n+{\n+\treturn verify_absent_generic(ce, action, o, ERRORMSG(o, would_lose_orphaned));\n+}\n \n static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n \t\tstruct unpack_trees_options *o)\n@@ -684,6 +799,8 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n \t\t\t\treturn -1;\n \t\t\tinvalidate_ce_path(old, o);\n \t\t}\n+\t\tif (ce_no_checkout(old))\n+\t\t\tupdate |= CE_NO_CHECKOUT;\n \t}\n \telse {\n \t\tif (verify_absent(merge, \"overwritten\", o))\n@@ -1182,6 +1299,8 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)\n \t\t\t    ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID))\n \t\t\t\tupdate |= CE_UPDATE;\n \t\t}\n+\t\tif (ce_no_checkout(old))\n+\t\t\tupdate |= CE_NO_CHECKOUT;\n \t\tadd_entry(o, old, update, 0);\n \t\treturn 0;\n \t}\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 86f0989..7e9febd 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -14,6 +14,8 @@ struct unpack_trees_error_msgs {\n \tconst char *not_uptodate_dir;\n \tconst char *would_lose_untracked;\n \tconst char *bind_overlap;\n+\tconst char *sparse_not_uptodate_file;\n+\tconst char *would_lose_orphaned;\n };\n \n struct narrow_spec {\n@@ -46,6 +48,9 @@ struct unpack_trees_options {\n \t\t     prune_unmerged:1,\n \t\t     has_unmerged:1,\n \t\t     initial_checkout:1,\n+\t\t     new_narrow_path:1,\n+\t\t     add_narrow_path:2,\n+\t\t     remove_narrow_path:2,\n \t\t     gently:1;\n \tconst char *prefix;\n \tint pos;\n@@ -57,6 +62,7 @@ struct unpack_trees_options {\n \tint merge_size;\n \n \tstruct cache_entry *df_conflict_entry;\n+\tstruct narrow_spec *narrow_spec;\n \tvoid *unpack_data;\n \n \tstruct index_state *dst_index;\n-- \n1.6.0.3.890.g95457\n"},{"id":"96776","messageId":"1228042478-1886-6-git-send-email-pclouds@gmail.com","threadId":"16527","inReplyTo":"1228042478-1886-5-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/8] clone: support sparse checkout with --sparse-checkout option","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-11-30T10:54:35Z","receivedAt":"2008-11-30T10:54:35Z","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 |   10 +++++++++-\n builtin-clone.c             |   14 ++++++++++++++\n t/t5703-clone-narrow.sh     |   41 +++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 64 insertions(+), 1 deletions(-)\n create mode 100755 t/t5703-clone-narrow.sh\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 95f08b9..acdced2 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>] [-u <upload-pack>] [--reference <repository>]\n-\t  [--depth <depth>] [--] <repository> [<directory>]\n+\t  [--depth <depth>] [-S|--sparse-checkout=<sparse patterns>] [--]\n+\t  <repository> [<directory>]\n \n DESCRIPTION\n -----------\n@@ -99,6 +100,13 @@ then the cloned repository will become corrupt.\n -n::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n+-S=<sparse patterns>::\n+--sparse-checkout=<sparse patterns>::\n+\tMake a sparse checkout instead of a full one.\n+\tThis option will not work with either --no-checkout or --bare.\n+\tPlease refer to linkgit:git-checkout[1] for more detail on\n+\tsparse checkout and sparse patterns.\n+\n --bare::\n \tMake a 'bare' GIT repository.  That is, instead of\n \tcreating `<directory>` and placing the administrative\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex 2feac9c..ea341a1 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -36,6 +36,7 @@ static const char * const builtin_clone_usage[] = {\n static int option_quiet, option_no_checkout, option_bare, option_mirror;\n static int option_local, option_no_hardlinks, option_shared;\n static char *option_template, *option_reference, *option_depth;\n+static char *option_narrow_path;\n static char *option_origin = NULL;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbose;\n@@ -45,6 +46,8 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSE(&option_verbose),\n \tOPT_BOOLEAN('n', \"no-checkout\", &option_no_checkout,\n \t\t    \"don't create a checkout\"),\n+\tOPT_STRING('S', \"sparse-checkout\", &option_narrow_path, \"sparse patterns\",\n+\t\t   \"only checkout certain files based on patterns\"),\n \tOPT_BOOLEAN(0, \"bare\", &option_bare, \"create a bare repository\"),\n \tOPT_BOOLEAN(0, \"naked\", &option_bare, \"create a bare repository\"),\n \tOPT_BOOLEAN(0, \"mirror\", &option_mirror,\n@@ -383,10 +386,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (option_origin)\n \t\t\tdie(\"--bare and --origin %s options are incompatible.\",\n \t\t\t    option_origin);\n+\t\tif (option_narrow_path)\n+\t\t\tdie(\"--bare and --sparse-checkout options are incompatible.\");\n \t\toption_no_checkout = 1;\n \t\tuse_separate_remote = 0;\n \t}\n \n+\tif (option_no_checkout && option_narrow_path)\n+\t\tdie(\"--no-checkout and --sparse-checkout options are incompatible.\");\n+\n \tif (!option_origin)\n \t\toption_origin = \"origin\";\n \n@@ -597,10 +605,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\topts.src_index = &the_index;\n \t\topts.dst_index = &the_index;\n \n+\t\tif (option_narrow_path) {\n+\t\t\topts.new_narrow_path = 1;\n+\t\t\topts.narrow_spec = parse_narrow_spec(option_narrow_path, NULL);\n+\t\t}\n+\n \t\ttree = parse_tree_indirect(remote_head->old_sha1);\n \t\tparse_tree(tree);\n \t\tinit_tree_desc(&t, tree->buffer, tree->size);\n \t\tunpack_trees(1, &t, &opts);\n+\t\tfree_narrow_spec(opts.narrow_spec);\n \n \t\tif (write_cache(fd, active_cache, active_nr) ||\n \t\t    commit_locked_index(lock_file))\ndiff --git a/t/t5703-clone-narrow.sh b/t/t5703-clone-narrow.sh\nnew file mode 100755\nindex 0000000..e34246d\n--- /dev/null\n+++ b/t/t5703-clone-narrow.sh\n@@ -0,0 +1,41 @@\n+#!/bin/sh\n+\n+test_description='narrow clone'\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\trm -fr .git &&\n+\ttest_create_repo src &&\n+\t(\n+\t\tcd src\n+\t\tmkdir -p work/sub/dir\n+\t\ttouch untracked tracked modified added\n+\t\ttouch work/untracked work/tracked work/modified work/added\n+\t\tgit add tracked work/tracked\n+\t\tgit add modified work/modified\n+\t\tgit commit -m initial\n+\t)\n+\n+'\n+\n+test_expect_success 'narrow clone incompatible with --bare' '\n+\trm -fr dst &&\n+\ttest_must_fail git clone --sparse-checkout=work/ --bare src dst\n+'\n+\n+test_expect_success 'narrow clone incompatible with --no-checkout' '\n+\trm -fr dst &&\n+\ttest_must_fail git clone --sparse-checkout=work/ -n src dst\n+'\n+\n+test_expect_success 'clone with --sparse-checkout' '\n+\t(\n+\trm -fr dst &&\n+\tgit clone --sparse-checkout=work/ src dst &&\n+\tcd dst &&\n+\ttest -z \"$(git ls-files --sparse | grep -v ^work/)\"\n+\t)\n+'\n+\n+test_done\n-- \n1.6.0.3.890.g95457\n"},{"id":"96777","messageId":"1228042478-1886-7-git-send-email-pclouds@gmail.com","threadId":"16527","inReplyTo":"1228042478-1886-6-git-send-email-pclouds@gmail.com","subject":"[PATCH 6/8] checkout: add new options to support sparse checkout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-11-30T10:54:36Z","receivedAt":"2008-11-30T10:54:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This patch adds main interface to manipulate sparse checkout.\nNew options are added to support entering/updating/leaving sparse\ncheckout:\n\n --full: return to full checkout (default)\n --sparse: set checkout area according to given spec\n --{include,exclude}-sparse: adjust current sparse checkout area\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-checkout.txt |   55 ++++++++++++++++-\n builtin-checkout.c             |   39 ++++++++++++\n t/t2011-checkout-sparse.sh     |  128 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 219 insertions(+), 3 deletions(-)\n create mode 100755 t/t2011-checkout-sparse.sh\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 0813d9f..b32043f 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -8,8 +8,10 @@ git-checkout - Checkout a branch or paths to the working tree\n SYNOPSIS\n --------\n [verse]\n-'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>]\n-'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...\n+'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m]\n+\t  [<sparse checkout options>] [<branch>]\n+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>]\n+\t  [<sparse checkout options>] [--] <paths>...\n \n DESCRIPTION\n -----------\n@@ -34,6 +36,10 @@ used to specify a specific tree-ish (i.e. commit, tag or tree)\n to update the index for the given paths before updating the\n working tree.\n \n+<sparse checkout options> include --full, --sparse, --include-sparse\n+and --exclude-sparse. The last three require sparse patterns. Please refer\n+to \"sparse checkout\" section for more information about this mode.\n+\n The index may contain unmerged entries after a failed merge.  By\n default, if you try to check out such an entry from the index, the\n checkout operation will fail and nothing will be checked out.\n@@ -117,6 +123,35 @@ should result in deletion of the path).\n When checking out paths from the index, this option lets you recreate\n the conflicted merge in the specified paths.\n \n+--full::\n+\tQuit sparse checkout mode. Return to full checkout.\n+\tThis option cannot be used with either --sparse,\n+\t--include-sparse, --exclude-sparse or <paths>.\n+\n+-S=<sparse patterns>::\n+--sparse=<sparse patterns>::\n+\tRe-apply new sparse patterns on current working directory to\n+\tform new checkout area. All no-checkout bits will be wiped\n+\tout before applying the patterns.\n+\tThis option cannot be used with --full, --include-sparse,\n+\t--exclude-sparse or <paths>. Multiple --sparse is not allowed.\n+\n+-I=<sparse patterns>::\n+--include-sparse=<sparse patterns>::\n+\tCheckout more areas specified by sparse patterns to current\n+\tcheckout area. Already checked out entries are not affected.\n+\tThis option cannot be used with --full, --sparse,\n+\t--exclude-sparse or <paths>. Multiple --include-sparse is not allowed.\n+\n+-E=<sparse patterns>::\n+--exclude-sparse=<sparse patterns>::\n+\tNarrow checkout area by removing files specified by sparse patterns\n+\tfrom current checkout area. This operation will fail if there\n+\tare unmerged or modified files in the removing areas. No-checkout\n+\tentries are not affected.\n+\tThis option cannot be used with --full, --sparse,\n+\t--include-sparse or <paths>. Multiple --exclude-sparse is not allowed.\n+\n --conflict=<style>::\n \tThe same as --merge option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n@@ -186,7 +221,10 @@ Because sparse checkout uses a new index format, it will be\n incompatible with git prior to 1.6.0 regarding worktree operations.\n Operations that only need access to the repository itself, such as\n clone, push, or pull/fetch from another (normal) repository... should\n-not be affected by sparse checkout.\n+not be affected by sparse checkout. In order to make your working\n+directory work again with those versions, you can use\n+`git checkout --full` to return to normal mode (and compatible index\n+format).\n \n In sparse checkout mode, checkout status of every files in your\n working directory will be recorded in index. If a file is marked\n@@ -263,6 +301,17 @@ Patterns have the following format:\n  - Sparse patterns do not apply to .gitignore and .gitattributes\n    files. They are always checked out.\n \n+When you apply new sparse patterns to your working directory using either\n+--sparse, --include-sparse or --exclude-sparse, it will update \"checkout\" status\n+in index accordingly. Moreover, if a file is marked \"no-checkout\" and\n+is present in working directory, it will be removed. If a file is\n+turned from \"no-checkout\" to \"checkout\", then it will be added again\n+to working directory. Unmerged entries will always be \"checkout\" regardless\n+the sparse patterns. Modified entries will refuse to become \"no-checkout\".\n+\n+You can form your checkout area in one go with --sparse option,\n+or do it incrementally with --include-sparse and --exclude-sparse.\n+\n EXAMPLES\n --------\n \ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex 7f3bd7b..be4cd3a 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -33,6 +33,12 @@ struct checkout_opts {\n \tconst char *new_branch;\n \tint new_branch_log;\n \tenum branch_track track;\n+\n+\tconst char *prefix;\n+\tchar *new_path;\n+\tchar *add_path;\n+\tchar *remove_path;\n+\tint all_path;\n };\n \n static int post_checkout_hook(struct commit *old, struct commit *new,\n@@ -388,6 +394,7 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\ttopts.dst_index = &the_index;\n \n \t\ttopts.msgs.not_uptodate_file = \"You have local changes to '%s'; cannot switch branches.\";\n+\t\ttopts.msgs.sparse_not_uptodate_file = \"You have local changes to '%s'; cannot update sparse checkout.\";\n \n \t\trefresh_cache(REFRESH_QUIET);\n \n@@ -411,7 +418,25 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t\ttree = parse_tree_indirect(new->commit->object.sha1);\n \t\tinit_tree_desc(&trees[1], tree->buffer, tree->size);\n \n+\t\tif (opts->all_path) {\n+\t\t\t/* leave narrow_spec NULL */\n+\t\t\ttopts.new_narrow_path = 1;\n+\t\t}\n+\t\telse if (opts->new_path) {\n+\t\t\ttopts.narrow_spec = parse_narrow_spec(opts->new_path, opts->prefix);\n+\t\t\ttopts.new_narrow_path = 1;\n+\t\t}\n+\t\telse if (opts->add_path) {\n+\t\t\ttopts.narrow_spec = parse_narrow_spec(opts->add_path, opts->prefix);\n+\t\t\ttopts.add_narrow_path = 1;\n+\t\t}\n+\t\telse if (opts->remove_path) {\n+\t\t\ttopts.narrow_spec = parse_narrow_spec(opts->remove_path, opts->prefix);\n+\t\t\ttopts.remove_narrow_path = 1;\n+\t\t}\n+\n \t\tret = unpack_trees(2, trees, &topts);\n+\t\tfree_narrow_spec(topts.narrow_spec);\n \t\tif (ret == -1) {\n \t\t\t/*\n \t\t\t * Unpack couldn't do a trivial merge; either\n@@ -598,6 +623,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOLEAN('m', \"merge\", &opts.merge, \"merge\"),\n \t\tOPT_STRING(0, \"conflict\", &conflict_style, \"style\",\n \t\t\t   \"conflict style (merge or diff3)\"),\n+\t\tOPT_BOOLEAN(0, \"full\", &opts.all_path, \"quit sparse checkout\"),\n+\t\tOPT_STRING('S', \"sparse\", &opts.new_path, \"sparse patterns\", \"set new sparse checkout\"),\n+\t\tOPT_STRING('I', \"include-sparse\", &opts.add_path, \"sparse patterns\", \"widen checkout area\"),\n+\t\tOPT_STRING('E', \"exclude-sparse\", &opts.remove_path, \"sparse patterns\", \"narrow checkout area\"),\n \t\tOPT_END(),\n \t};\n \tint has_dash_dash;\n@@ -608,6 +637,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tgit_config(git_checkout_config, NULL);\n \n \topts.track = BRANCH_TRACK_UNSPECIFIED;\n+\topts.prefix = prefix;\n \n \targc = parse_options(argc, argv, options, checkout_usage,\n \t\t\t     PARSE_OPT_KEEP_DASHDASH);\n@@ -634,6 +664,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\n \t}\n \n+\tif (((opts.all_path ? 1 : 0) +\n+\t     (opts.new_path ? 1 : 0) +\n+\t     (opts.add_path ? 1 : 0) +\n+\t     (opts.remove_path ? 1 : 0)) > 1)\n+\t\tdie(\"git checkout: --full, --sparse, --include-sparse and --exclude-sparse are incompatible\");\n+\n \tif (opts.force && opts.merge)\n \t\tdie(\"git checkout: -f and -m are incompatible\");\n \n@@ -727,6 +763,9 @@ no_reference:\n \t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n \t\t\tdie(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\nchecking out of the index.\");\n \n+\t\tif (opts.all_path || opts.new_path || opts.add_path || opts.remove_path)\n+\t\t\tdie(\"git checkout: updating paths is incompatible with setting sparse checkout\");\n+\n \t\treturn checkout_paths(source_tree, pathspec, &opts);\n \t}\n \ndiff --git a/t/t2011-checkout-sparse.sh b/t/t2011-checkout-sparse.sh\nnew file mode 100755\nindex 0000000..67aea96\n--- /dev/null\n+++ b/t/t2011-checkout-sparse.sh\n@@ -0,0 +1,128 @@\n+#!/bin/sh\n+\n+test_description='sparse checkout'\n+\n+. ./test-lib.sh\n+\n+test_expect_success setup '\n+\tmkdir work1 work2 work3\n+\ttouch one two three\n+\ttouch work1/one work2/two work3/three\n+\ttouch .gitignore .gitattributes\n+\ttouch work3/.gitignore work3/.gitattributes\n+\tgit add .gitignore .gitattributes\n+\tgit add work3/.gitignore work3/.gitattributes\n+\tgit add one work1/one\n+\tgit commit -m work1 && WORK1=$(git rev-parse HEAD)\n+\techo conflict >> work1/one\n+\tgit add work1/one\n+\tgit commit -m \"work1 conflict\" && WORK1CONFLICT=$(git rev-parse HEAD)\n+\tgit checkout $WORK1\n+\techo one >> one\n+\techo work1/1 >> work1/one\n+\tgit add one work1/one\n+\tgit commit -m \"work1 modified\" && WORK1MODIFIED=$(git rev-parse HEAD)\n+\tgit add two work2/two\n+\tgit commit -m work2 && WORK2=$(git rev-parse HEAD)\n+\tgit add three work3/three\n+\tgit commit -m work3 && WORK3=$(git rev-parse HEAD)\n+'\n+\n+test_expect_success '--full on no-narrow checkout' '\n+\tgit checkout --full\n+'\n+\n+test_expect_success '--full and --sparse incompatible' '\n+\ttest_must_fail git checkout --full --sparse=work1\n+'\n+\n+test_expect_success 'limit worktree to work1 and work2' '\n+\tgit checkout --sparse=work1/:work2/ &&\n+\ttest -f work1/one &&\n+\ttest -f work2/two &&\n+\t! test -f work3/three\n+'\n+\n+test_expect_success 'exit sparse checkout' '\n+\tgit checkout --full &&\n+\ttest -f work1/one &&\n+\ttest -f work2/two &&\n+\ttest -f work3/three &&\n+\ttest -f one\n+'\n+\n+test_expect_success 'sparse checkout does not touch .git*' '\n+\tgit checkout --sparse=work1/:work2/ &&\n+\ttest -f .gitignore &&\n+\ttest -f .gitattributes &&\n+\ttest -f work3/.gitignore &&\n+\ttest -f work3/.gitattributes\n+'\n+\n+# merged_entry case with old != NULL, same_entry = 1\n+# work1/ turns to no-checkout\n+# work3/ turns to checkout\n+test_expect_success 'update worktree to work2 and work3' '\n+\tgit checkout --sparse=work2/:work3/ &&\n+\t! test -f work1/one &&\n+\ttest -f work2/two &&\n+\ttest -f work3/three\n+'\n+\n+test_expect_success 'update narrow prefix with modification' '\n+\techo modified >> work2/two &&\n+\tgit checkout --sparse=work1/:work2/ &&\n+\ttest -f work1/one &&\n+\ttest -f work2/two &&\n+\t! test -f work3/three &&\n+\tgrep -q modified work2/two &&\n+\n+\t! git checkout --sparse=work1/:work3/ &&\n+\ttest -f work1/one &&\n+\ttest -f work2/two &&\n+\t! test -f work3/three &&\n+\tgrep -q modified work2/two &&\n+\tgit checkout work2/two\n+'\n+\n+test_expect_success 'widen checkout area' '\n+\tgit checkout --include-sparse=work3/ &&\n+\ttest -f work1/one &&\n+\ttest -f work2/two &&\n+\ttest -f work3/three\n+'\n+\n+test_expect_success 'narrow checkout area' '\n+\tgit checkout --exclude-sparse=work3/ &&\n+\ttest -f work1/one &&\n+\ttest -f work2/two &&\n+\t! test -f work3/three\n+'\n+\n+test_expect_success 'update outside checkout area' '\n+\tgit checkout --full &&\n+\tgit checkout $WORK1 &&\n+\tgit checkout --sparse work2/ &&\n+\t! test -f work1/one &&\n+\tgit checkout $WORK1MODIFIED &&\n+\t! test -f work1/one &&\n+\tgit checkout --full master\n+'\n+\n+test_expect_success 'conflict outside checkout area' '\n+\tgit checkout --sparse=\"work2/\" $WORK1MODIFIED &&\n+\ttest -z \"$(git ls-files --sparse work1/one)\" &&\n+\tgit merge $WORK1CONFLICT\n+\ttest $? = 1 &&\n+\ttest -n \"$(git ls-files --sparse work1/one)\" &&\n+\tgit reset --hard &&\n+\tgit checkout master\n+'\n+\n+test_expect_success 'removal outside checkout area' '\n+\tgit rm work1/one &&\n+\tgit commit -m remove &&\n+\tgit checkout --sparse=work2/ HEAD^\n+'\n+\n+test_done\n-- \n1.6.0.3.890.g95457\n"},{"id":"96779","messageId":"1228042478-1886-8-git-send-email-pclouds@gmail.com","threadId":"16527","inReplyTo":"1228042478-1886-7-git-send-email-pclouds@gmail.com","subject":"[PATCH 7/8] Introduce default sparse patterns (core.defaultsparse)","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-11-30T10:54:37Z","receivedAt":"2008-11-30T10:54:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"As noted in the previous commit message, new files will always get added into\nworking directory by unpack_trees(). This can be annoying so we keep\ntrack of the sparse patterns people use to form their checkout area\nand apply the patterns on new files. Those files that get filtered out\nby this will be reported if verbose_update is TRUE.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt       |   10 +++++++\n Documentation/git-checkout.txt |   21 ++++++++++++++-\n Documentation/git-clone.txt    |    1 +\n builtin-checkout.c             |   54 ++++++++++++++++++++++++++++++++++++++++\n builtin-clone.c                |    2 +\n cache.h                        |    2 +\n config.c                       |    5 +++\n environment.c                  |    1 +\n t/t2011-checkout-sparse.sh     |   19 ++++++++++++++\n t/t5703-clone-narrow.sh        |    1 +\n unpack-trees.c                 |   52 ++++++++++++++++++++++++++++++++++++++\n unpack-trees.h                 |    1 +\n 12 files changed, 167 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex b233fe5..2b2dad1 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -422,6 +422,16 @@ relatively high IO latencies.  With this set to 'true', git will do the\n index comparison to the filesystem data in parallel, allowing\n overlapping IO's.\n \n+core.defaultsparse::\n+\tThe default sparse patterns that will be used to decide\n+\twhether a new file should be added into working\n+\tdirectory. If a new file matches the default sparse\n+\tpatterns, then it will be checked out. Otherwise it will\n+\tbe ignored and marked \"no-checkout\" in index.\n+\tSparse update operations via \"git clone\" or \"git checkout\" will\n+\tautomatically update this config. See linkgit:git-clone[1] and\n+\tlinkgit:git-checkout[1] for more information.\n+\n alias.*::\n \tCommand aliases for the linkgit:git[1] command wrapper - e.g.\n \tafter defining \"alias.last = cat-file commit HEAD\", the invocation\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex b32043f..892a4bb 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -125,6 +125,7 @@ the conflicted merge in the specified paths.\n \n --full::\n \tQuit sparse checkout mode. Return to full checkout.\n+\t`core.defaultsparse` will be removed.\n \tThis option cannot be used with either --sparse,\n \t--include-sparse, --exclude-sparse or <paths>.\n \n@@ -133,6 +134,7 @@ the conflicted merge in the specified paths.\n \tRe-apply new sparse patterns on current working directory to\n \tform new checkout area. All no-checkout bits will be wiped\n \tout before applying the patterns.\n+\t`core.defaultsparse` will be set with the given patterns.\n \tThis option cannot be used with --full, --include-sparse,\n \t--exclude-sparse or <paths>. Multiple --sparse is not allowed.\n \n@@ -140,6 +142,11 @@ the conflicted merge in the specified paths.\n --include-sparse=<sparse patterns>::\n \tCheckout more areas specified by sparse patterns to current\n \tcheckout area. Already checked out entries are not affected.\n+\t`core.defaultsparse` will be appended with the given patterns.\n+\tRepetitive use of --include-sparse and --exclude-sparse may\n+\tleave cruft in `core.defaultsparse`, and may slow down\n+\tcheckout operations. You may want to clean up\n+\t`core.defaultsparse` with --sparse.\n \tThis option cannot be used with --full, --sparse,\n \t--exclude-sparse or <paths>. Multiple --include-sparse is not allowed.\n \n@@ -149,6 +156,11 @@ the conflicted merge in the specified paths.\n \tfrom current checkout area. This operation will fail if there\n \tare unmerged or modified files in the removing areas. No-checkout\n \tentries are not affected.\n+\t`core.defaultsparse` will be appended with the negated version of the given patterns.\n+\tRepetitive use of --include-sparse and --exclude-sparse may\n+\tleave cruft in `core.defaultsparse`, and may slow down\n+\tcheckout operations. You may want to clean up\n+\t`core.defaultsparse` with --sparse.\n \tThis option cannot be used with --full, --sparse,\n \t--include-sparse or <paths>. Multiple --exclude-sparse is not allowed.\n \n@@ -230,8 +242,11 @@ In sparse checkout mode, checkout status of every files in your\n working directory will be recorded in index. If a file is marked\n \"no-checkout\", it means that file is not needed to be present in\n working directory by user or any git command. When a new file is added\n-to index, it will be marked \"checkout\" unless sparse patterns are\n-applied.  Unmerged files are always \"checkout\". When you checkout new\n+to index, it will be checked with default sparse pattern (see\n+`core.defaultsparse` in linkgit:git-config.txt[1]). If the file\n+matches the pattern, it will be marked \"checkout\" and added to\n+working directory. Otherwise it will be marked \"no-checkout\" in\n+index.  Unmerged files are always \"checkout\". When you checkout new\n files using \"git checkout <file>\" they will be automatically marked\n \"checkout\". Other commands such as \"git apply\" can also checkout new\n files if they are needed. linkgit:git-update-index[1] can be used to\n@@ -311,6 +326,8 @@ the sparse patterns. Modified entries will refuse to become \"no-checkout\".\n \n You can form your checkout area in one go with --sparse option,\n or do it incrementally with --include-sparse and --exclude-sparse.\n+Patterns used in those options will be automatically recorded in\n+`core.defaultsparse`.\n \n EXAMPLES\n --------\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex acdced2..f7ccf15 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -103,6 +103,7 @@ then the cloned repository will become corrupt.\n -S=<sparse patterns>::\n --sparse-checkout=<sparse patterns>::\n \tMake a sparse checkout instead of a full one.\n+\t`core.defaultsparse` will be set with the given sparse patterns.\n \tThis option will not work with either --no-checkout or --bare.\n \tPlease refer to linkgit:git-checkout[1] for more detail on\n \tsparse checkout and sparse patterns.\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex be4cd3a..8c6d5c5 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -592,6 +592,60 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n \n \tupdate_refs_for_switch(opts, &old, new);\n \n+\t/* Update core.defaultsparse */\n+\tif (opts->all_path ||\n+\t    (opts->new_path && !opts->prefix)) /* optimization when prefix is NULL */\n+\t\tgit_config_set(\"core.defaultsparse\", opts->new_path);\n+\telse if (opts->new_path || opts->add_path || opts->remove_path) {\n+\t\tconst char *narrow_spec;\n+\t\tstruct narrow_spec *spec;\n+\t\tstruct strbuf sb;\n+\t\tint i;\n+\n+\t\tif (opts->new_path)\n+\t\t\tnarrow_spec = opts->new_path;\n+\t\telse if (opts->add_path)\n+\t\t\tnarrow_spec = opts->add_path;\n+\t\telse\n+\t\t\tnarrow_spec = opts->remove_path;\n+\t\tspec = parse_narrow_spec(narrow_spec, NULL) ;\n+\t\tstrbuf_init(&sb, 0);\n+\n+\t\t/*\n+\t\t * --sparse does not save old core.defaultsparse\n+\t\t * --include-sparse and --exclude-sparse on the other hand\n+\t\t * append to the old core.defaultsparse\n+\t\t */\n+\t\tif (core_default_sparse && !opts->new_path) {\n+\t\t\tstrbuf_addstr(&sb, core_default_sparse);\n+\t\t\tstrbuf_addch(&sb, ':');\n+\t\t}\n+\n+\t\t/*\n+\t\t * This happens when we do full checkout,\n+\t\t * then --exclude-sparse.\n+\t\t * Add full pattern first so the next\n+\t\t *  pattern has something to negate\n+\t\t */\n+\t\tif (!core_default_sparse && opts->remove_path)\n+\t\t\tstrbuf_addstr(&sb, \"/:\");\n+\n+\t\tfor (i = 0; i < spec->nr; i++) {\n+\t\t\tstruct narrow_pattern *p = spec->patterns[i];\n+\n+\t\t\tif (i)\n+\t\t\t\tstrbuf_addch(&sb, ':');\n+\t\t\tif (!p->prefix && opts->prefix)\n+\t\t\t\tp->prefix = xstrdup(opts->prefix);\n+\t\t\tif (opts->remove_path)\n+\t\t\t\tp->negative = !p->negative;\n+\t\t\tnarrow_spec_to_strbuf(p, &sb);\n+\t\t}\n+\t\tgit_config_set(\"core.defaultsparse\", sb.buf);\n+\t\tfree_narrow_spec(spec);\n+\t\tstrbuf_release(&sb);\n+\t}\n+\n \tret = post_checkout_hook(old.commit, new->commit, 1);\n \treturn ret || opts->writeout_error;\n }\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex ea341a1..209030a 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -619,6 +619,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (write_cache(fd, active_cache, active_nr) ||\n \t\t    commit_locked_index(lock_file))\n \t\t\tdie(\"unable to write new index file\");\n+\n+\t\tgit_config_set(\"core.defaultsparse\", option_narrow_path);\n \t}\n \n \tstrbuf_release(&reflog_msg);\ndiff --git a/cache.h b/cache.h\nindex b13df06..a3366da 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -175,6 +175,7 @@ struct cache_entry {\n \n /* Only remove in work directory, not index */\n #define CE_WT_REMOVE (0x400000)\n+#define CE_NEW       (0x800000)\n \n /*\n  * Extended on-disk flags\n@@ -522,6 +523,7 @@ extern size_t delta_base_cache_limit;\n extern int auto_crlf;\n extern int fsync_object_files;\n extern int core_preload_index;\n+extern const char *core_default_sparse;\n \n enum safe_crlf {\n \tSAFE_CRLF_FALSE = 0,\ndiff --git a/config.c b/config.c\nindex d2fc8f5..ca2cf03 100644\n--- a/config.c\n+++ b/config.c\n@@ -495,6 +495,11 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.defaultsparse\")) {\n+\t\tcore_default_sparse = xstrdup(value);\n+\t\treturn 0;\n+\t}\n+\n \t/* Add other config variables here and to Documentation/config.txt. */\n \treturn 0;\n }\ndiff --git a/environment.c b/environment.c\nindex e278bce..5a66e30 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -30,6 +30,7 @@ int zlib_compression_level = Z_BEST_SPEED;\n int core_compression_level;\n int core_compression_seen;\n int fsync_object_files;\n+const char *core_default_sparse;\n size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;\n size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;\n size_t delta_base_cache_limit = 16 * 1024 * 1024;\ndiff --git a/t/t2011-checkout-sparse.sh b/t/t2011-checkout-sparse.sh\nindex 67aea96..15f3761 100755\n--- a/t/t2011-checkout-sparse.sh\n+++ b/t/t2011-checkout-sparse.sh\n@@ -45,6 +45,7 @@ test_expect_success 'limit worktree to work1 and work2' '\n \n test_expect_success 'exit sparse checkout' '\n \tgit checkout --full &&\n+\ttest -z \"$(git config core.defaultsparse)\" &&\n \ttest -f work1/one &&\n \ttest -f work2/two &&\n \ttest -f work3/three &&\n@@ -125,4 +126,22 @@ test_expect_success 'removal outside checkout area' '\n \tgit checkout --sparse=work2/ HEAD^\n '\n \n+test_expect_success 'core.defaultsparse, new files get filtered' '\n+\tgit checkout --sparse=work2/ $WORK2 &&\n+\tgit config core.defaultsparse \"./*:work2/\"\n+\tgit checkout $WORK3 &&\n+\ttest -f three &&\n+\t! test -f work3/three &&\n+\tgit config --unset core.defaultsparse\n+'\n+\n+test_expect_success 'save core.defaultsparse' '\n+\tgit checkout --sparse=\"./*:work2/\" $WORK2 &&\n+\ttest \"$(git config core.defaultsparse)\" = \"./*:work2/\" &&\n+\tcd work3 && git checkout --include-sparse=1 $WORK2 && cd .. &&\n+\ttest \"$(git config core.defaultsparse)\" = \"./*:work2/:work3//1\" &&\n+\tgit checkout --exclude-sparse=work3/ $WORK2 &&\n+\ttest \"$(git config core.defaultsparse)\" = \"./*:work2/:work3//1:!work3/\"\n+'\n+\n test_done\ndiff --git a/t/t5703-clone-narrow.sh b/t/t5703-clone-narrow.sh\nindex e34246d..691bb40 100755\n--- a/t/t5703-clone-narrow.sh\n+++ b/t/t5703-clone-narrow.sh\n@@ -34,6 +34,7 @@ test_expect_success 'clone with --sparse-checkout' '\n \trm -fr dst &&\n \tgit clone --sparse-checkout=work/ src dst &&\n \tcd dst &&\n+\ttest \"$(git config core.defaultsparse)\" = \"work/\" &&\n \ttest -z \"$(git ls-files --sparse | grep -v ^work/)\"\n \t)\n '\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex a2794b8..ad26987 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -151,6 +151,7 @@ static int verify_absent_sparse(struct cache_entry *ce, const char *action, stru\n static int apply_narrow_spec(struct unpack_trees_options *o)\n {\n \tstruct index_state *index = &o->result;\n+\tstatic struct narrow_spec *default_spec = NULL;\n \tint i, len, namelen;\n \tint header = 0;\n \tchar *special_files[] = { \".gitignore\", GITATTRIBUTES_FILE, NULL };\n@@ -188,6 +189,25 @@ static int apply_narrow_spec(struct unpack_trees_options *o)\n \t\t\tif (o->remove_narrow_path && match)\n \t\t\t\tce_mark_no_checkout(ce);\n \t\t}\n+\t\t/*\n+\t\t * Default spec only applies if no directive is given\n+\t\t * and it only applies to new files\n+\t\t */\n+\t\telse if (ce->ce_flags & CE_NEW) {\n+\t\t\tif (!default_spec && core_default_sparse)\n+\t\t\t\tdefault_spec = parse_narrow_spec(core_default_sparse, NULL);\n+\n+\t\t\tif (default_spec && !match_narrow_spec(default_spec, ce->name)) {\n+\t\t\t\tif (o->update && o->verbose_update) {\n+\t\t\t\t\tif (!header) {\n+\t\t\t\t\t\tfprintf(stderr, \"New files get filtered out by default sparse patterns:\\n\");\n+\t\t\t\t\t\theader = 1;\n+\t\t\t\t\t}\n+\t\t\t\t\tfprintf(stderr, \" %s\\n\", ce->name);\n+\t\t\t\t}\n+\t\t\t\tce_mark_no_checkout(ce);\n+\t\t\t}\n+\t\t}\n \n \t\t/* Update worktree, add/remove entries if needed */\n \n@@ -805,6 +825,8 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n \telse {\n \t\tif (verify_absent(merge, \"overwritten\", o))\n \t\t\treturn -1;\n+\t\tif (!o->has_unmerged)\n+\t\t\tupdate |= CE_NEW;\n \t\tinvalidate_ce_path(merge, o);\n \t}\n \n@@ -957,6 +979,36 @@ void free_narrow_spec(struct narrow_spec *spec)\n \tfree(spec);\n }\n \n+void narrow_spec_to_strbuf(struct narrow_pattern *p, struct strbuf *sb)\n+{\n+\tconst char *s;\n+\tif (p->prefix && !p->has_root) {\n+\t\ts = p->prefix;\n+\t\twhile (*s) {\n+\t\t\tif (*s == ':')\n+\t\t\t\tstrbuf_addch(sb, '\\\\');\n+\t\t\tstrbuf_addch(sb, *s);\n+\t\t\ts++;\n+\t\t}\n+\t\tstrbuf_addch(sb, '/'); /* double slashes to end prefix */\n+\t}\n+\tif (p->negative)\n+\t\tstrbuf_addch(sb, '!');\n+\tif (p->has_root)\n+\t\tstrbuf_addch(sb, '/');\n+\telse if (p->has_slashes && strchr(p->pattern, '/') == NULL) {\n+\t\tstrbuf_addch(sb, '.');\n+\t\tstrbuf_addch(sb, '/');\n+\t}\n+\ts = p->pattern;\n+\twhile (*s) {\n+\t\tif (*s == ':')\n+\t\t\tstrbuf_addch(sb, '\\\\');\n+\t\tstrbuf_addch(sb, *s);\n+\t\ts++;\n+\t}\n+}\n+\n int match_narrow_spec(struct narrow_spec *spec, const char *path)\n {\n \tint i, match;\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 7e9febd..c5b19e7 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -75,6 +75,7 @@ extern int unpack_trees(unsigned n, struct tree_desc *t,\n \n struct narrow_spec *parse_narrow_spec(const char *spec, const char *prefix);\n void free_narrow_spec(struct narrow_spec *spec);\n+void narrow_spec_to_strbuf(struct narrow_pattern *p, struct strbuf *sb);\n int match_narrow_spec(struct narrow_spec *spec, const char *path);\n int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);\n int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);\n-- \n1.6.0.3.890.g95457\n"},{"id":"96778","messageId":"1228042478-1886-9-git-send-email-pclouds@gmail.com","threadId":"16527","inReplyTo":"1228042478-1886-8-git-send-email-pclouds@gmail.com","subject":"[PATCH 8/8] wt-status: show sparse checkout info","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-11-30T10:54:38Z","receivedAt":"2008-11-30T10:54:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This will make \"git status\" show core.defaultsparse and list of\norphaned files if present.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wt-status.c |   44 ++++++++++++++++++++++++++++++++++++++++++++\n wt-status.h |    1 +\n 2 files changed, 45 insertions(+), 0 deletions(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex 3edae43..71cf809 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -20,6 +20,7 @@ static char wt_status_colors[][COLOR_MAXLEN] = {\n \t\"\\033[31m\", /* WT_STATUS_CHANGED: red */\n \t\"\\033[31m\", /* WT_STATUS_UNTRACKED: red */\n \t\"\\033[31m\", /* WT_STATUS_NOBRANCH: red */\n+\t\"\\033[31m\", /* WT_STATUS_ORPHANED: red */\n };\n \n enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;\n@@ -83,6 +84,16 @@ static void wt_status_print_dirty_header(struct wt_status *s,\n \tcolor_fprintf_ln(s->fp, c, \"#\");\n }\n \n+static void wt_status_print_orphaned_header(struct wt_status *s)\n+{\n+\tconst char *c = color(WT_STATUS_HEADER);\n+\tcolor_fprintf_ln(s->fp, c, \"# Orphaned files:\");\n+\tcolor_fprintf_ln(s->fp, c, \"#   (these are tracked, but marked no-checkout and should not be present)\");\n+\tcolor_fprintf_ln(s->fp, c, \"#   (use \\\"git update-index --checkout\\\" to remove no-checkout status)\");\n+\tcolor_fprintf_ln(s->fp, c, \"#   (otherwise remove them to avoid confusion because git will ignore them)\");\n+\tcolor_fprintf_ln(s->fp, c, \"#\");\n+}\n+\n static void wt_status_print_untracked_header(struct wt_status *s)\n {\n \tconst char *c = color(WT_STATUS_HEADER);\n@@ -211,6 +222,33 @@ static void wt_status_print_changed(struct wt_status *s)\n \trun_diff_files(&rev, 0);\n }\n \n+static void wt_status_print_orphaned(struct wt_status *s)\n+{\n+\tint i, show_header = 0;\n+\tstruct stat st;\n+\tstruct strbuf buf;\n+\n+\tstrbuf_init(&buf, 0);\n+\tfor (i = 0; i < the_index.cache_nr; i++) {\n+\t\tstruct cache_entry *ce = the_index.cache[i];\n+\n+\t\tif (ce_checkout(ce) || lstat(ce->name, &st))\n+\t\t\tcontinue;\n+\n+\t\tif (!show_header) {\n+\t\t\twt_status_print_orphaned_header(s);\n+\t\t\tshow_header = 1;\n+\t\t}\n+\n+\t\tcolor_fprintf(s->fp, color(WT_STATUS_HEADER), \"#\\t\");\n+\t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_ORPHANED), \"%s\",\n+\t\t\t\t quote_path(ce->name, -1, &buf, s->prefix));\n+\t}\n+\n+\tif (show_header)\n+\t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER),\"#\");\n+}\n+\n static void wt_status_print_submodule_summary(struct wt_status *s)\n {\n \tstruct child_process sm_summary;\n@@ -344,8 +382,14 @@ void wt_status_print(struct wt_status *s)\n \t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER), \"#\");\n \t}\n \n+\tif (core_default_sparse) {\n+\t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER), \"# Sparse patterns: %s\", core_default_sparse);\n+\t\tcolor_fprintf_ln(s->fp, color(WT_STATUS_HEADER), \"#\");\n+\t}\n+\n \twt_status_print_updated(s);\n \twt_status_print_changed(s);\n+\twt_status_print_orphaned(s);\n \tif (wt_status_submodule_summary)\n \t\twt_status_print_submodule_summary(s);\n \tif (show_untracked_files)\ndiff --git a/wt-status.h b/wt-status.h\nindex 78add09..52f1eb5 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -9,6 +9,7 @@ enum color_wt_status {\n \tWT_STATUS_CHANGED,\n \tWT_STATUS_UNTRACKED,\n \tWT_STATUS_NOBRANCH,\n+\tWT_STATUS_ORPHANED,\n };\n \n enum untracked_status_type {\n-- \n1.6.0.3.890.g95457\n"},{"id":"96843","messageId":"alpine.DEB.1.00.0812011505551.30769@pacific.mpi-cbg.de","threadId":"16527","inReplyTo":"1228042478-1886-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 1/8] generate-cmdlist.sh: avoid selecting synopsis at wrong place","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-12-01T14:10:38Z","receivedAt":"2008-12-01T14:10:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 30 Nov 2008, Nguy�n Th�i Ng�c Duy wrote:\n\n> In \"common\" man pages there is luckily no \"NAME\" anywhere except at \n> beginning of documents. If there is another \"NAME\", sed could mis-select \n> it and lead to common-cmds.h corruption. So better nail it at beginning \n> of line, which would reduce corruption chance.\n\nI have no idea why you put this into the sparse checkout patch series.\n\nAs it is, the patch series is _already_ hard to review (as it is large not \nonly in term of number of patches, but also individual patch size), \n_especially_ given the fact that there is no clear, precise and short \ndescription of why/how the sparse checkout is implemented.\n\nFor example, instead of using the BLURP area of the cover letter to put \nforth convincing arguments why this is needed, and why it is implemented \nin the best possible manner, you wasted two lines stating the obvious.\n\nIMO that is almost like _asking_ people not to have a look at it.\n\nCiao,\nDscho"},{"id":"96845","messageId":"fcaeb9bf0812010611p68b48f65x3c078180d1089d41@mail.gmail.com","threadId":"16527","inReplyTo":"alpine.DEB.1.00.0812011505551.30769@pacific.mpi-cbg.de","subject":"Re: [PATCH 1/8] generate-cmdlist.sh: avoid selecting synopsis at wrong place","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-12-01T14:11:24Z","receivedAt":"2008-12-01T14:11:24Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On 12/1/08, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> Hi,\n>\n>\n>  On Sun, 30 Nov 2008, Nguy­n Thái Ng÷c Duy wrote:\n>\n>  > In \"common\" man pages there is luckily no \"NAME\" anywhere except at\n>  > beginning of documents. If there is another \"NAME\", sed could mis-select\n>  > it and lead to common-cmds.h corruption. So better nail it at beginning\n>  > of line, which would reduce corruption chance.\n>\n>\n> I have no idea why you put this into the sparse checkout patch series.\n\nIt's more an independent bug fix. I posted this once but it was not\ncaught up. I intended to just put some comments in BLURB area to tell\npeople to get this patch from mail archive. But that way seems\ninconvenient. Without this patch, you won't be able to compile the\nseries because of common-cmds.h corruption.\n-- \nDuy\n"},{"id":"96846","messageId":"7vtz9oq92i.fsf@gitster.siamese.dyndns.org","threadId":"16527","inReplyTo":"alpine.DEB.1.00.0812011505551.30769@pacific.mpi-cbg.de","subject":"Re: [PATCH 1/8] generate-cmdlist.sh: avoid selecting synopsis at wrong place","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-12-01T14:11:33Z","receivedAt":"2008-12-01T14:11:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Sun, 30 Nov 2008, Nguyễn Thái Ngọc Duy wrote:\n>\n>> In \"common\" man pages there is luckily no \"NAME\" anywhere except at \n>> beginning of documents. If there is another \"NAME\", sed could mis-select \n>> it and lead to common-cmds.h corruption. So better nail it at beginning \n>> of line, which would reduce corruption chance.\n>\n> I have no idea why you put this into the sparse checkout patch series.\n\nThat is because a documentation that triggers the misfortune this patch\nfixes is introduced in the series.  I actually scratched head myself,\nthough, and come to think of it, I should have complained that this patch\nshould state that because it does not look like it has anything do with\nthe main topic of the series.\n\n> As it is, the patch series is _already_ hard to review (as it is large not \n> only in term of number of patches, but also individual patch size), \n> _especially_ given the fact that there is no clear, precise and short \n> description of why/how the sparse checkout is implemented.\n\nHmm, can you really tell the lack of such description without reading the\nseries, I have to wonder...\n"},{"id":"96858","messageId":"alpine.DEB.1.00.0812011546560.30769@pacific.mpi-cbg.de","threadId":"16527","inReplyTo":"7vtz9oq92i.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 1/8] generate-cmdlist.sh: avoid selecting synopsis at wrong place","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-12-01T15:39:43Z","receivedAt":"2008-12-01T15:39:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 1 Dec 2008, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > As it is, the patch series is _already_ hard to review (as it is large \n> > not only in term of number of patches, but also individual patch \n> > size), _especially_ given the fact that there is no clear, precise and \n> > short description of why/how the sparse checkout is implemented.\n> \n> Hmm, can you really tell the lack of such description without reading \n> the series, I have to wonder...\n\nOkay, I thought it was obvious, but here is a template for the BLURP of \nthe cover letter that would at least get me started:\n\n-- snip --\nA \"sparse checkout\" is an index/working directory pair where not all \nfiles/directories of the HEAD commit are actually checked out in the \nworking directory.  Instead, they are marked as \"not being checked out\" in \nthe index.\n\nThe real meat of this series is patch *** M/N *** which teaches Git to \nunderstand the *** XYZ flag *** for index entries.\n\nThe following operations are affected by sparse checkout: *** X, Y, Z ***\n\nThese operations respect sparse checkouts by *** THIS, THIS AND THIS ***.\n\nThe first patch really should be independent, but patch *** M/N *** would \nfail without it.\n-- snap --\n\nAnd of course, the whole BLURP should not consist of 10-20 line \nparagraphs, but try to fit everything into 3-4 line paragraphs (I seem to \nremember that there was a mail on this list saying than more than 4 \nlines/paragraph are too much for the average attention span...).\n\nCiao,\nDscho\n"}]}