{"thread":{"id":"15022","subject":"[PATCH 1/9] Introduce CE_NO_CHECKOUT bit","startedAt":"2008-08-15T14:24:58Z","lastAt":"2008-08-15T14:27:30Z","messageCount":9,"participants":["Nguyễn Thái Ngọc Duy"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"87306","messageId":"20080815142457.GA10653@laptop","threadId":"15022","inReplyTo":"cover.1218807249.git.pclouds@gmail.com","subject":"[PATCH 1/9] Introduce CE_NO_CHECKOUT bit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-08-15T14:24:58Z","receivedAt":"2008-08-15T14:24:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This bit is the basis of narrow checkout. If this bit is on, the entry\nis outside narrow checkout and therefore should be ignored (similar\nto CE_VALID)\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n cache.h      |    8 ++++++++\n read-cache.c |    6 +++---\n 2 files changed, 11 insertions(+), 3 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 30f1d62..86288b6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -126,6 +126,7 @@ struct cache_entry {\n \n #define CE_NAMEMASK  (0x0fff)\n #define CE_STAGEMASK (0x3000)\n+#define CE_NO_CHECKOUT (0x4000)\n #define CE_VALID     (0x8000)\n #define CE_STAGESHIFT 12\n \n@@ -138,6 +139,9 @@ struct cache_entry {\n #define CE_HASHED    (0x100000)\n #define CE_UNHASHED  (0x200000)\n \n+/* \"Assume unchanged\" mask */\n+#define CE_VALID_MASK (CE_VALID | CE_NO_CHECKOUT)\n+\n /*\n  * Copy the sha1 and stat state of a cache entry from one to\n  * another. But we never change the name, or the hash state!\n@@ -173,6 +177,10 @@ static inline size_t ce_namelen(const struct cache_entry *ce)\n #define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))\n #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)\n #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)\n+#define ce_no_checkout(ce) ((ce)->ce_flags & CE_NO_CHECKOUT)\n+#define ce_checkout(ce) (!ce_no_checkout(ce))\n+#define ce_mark_no_checkout(ce) ((ce)->ce_flags |= CE_NO_CHECKOUT)\n+#define ce_mark_checkout(ce) ((ce)->ce_flags &= ~CE_NO_CHECKOUT)\n #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)\n \n #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)\ndiff --git a/read-cache.c b/read-cache.c\nindex a940f8d..d7f6b97 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -253,7 +253,7 @@ int ie_match_stat(const struct index_state *istate,\n \t * If it's marked as always valid in the index, it's\n \t * valid whatever the checked-out copy says.\n \t */\n-\tif (!ignore_valid && (ce->ce_flags & CE_VALID))\n+\tif (!ignore_valid && (ce->ce_flags & CE_VALID_MASK))\n \t\treturn 0;\n \n \tchanged = ce_match_stat_basic(ce, st);\n@@ -943,10 +943,10 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,\n \t\treturn ce;\n \n \t/*\n-\t * CE_VALID means the user promised us that the change to\n+\t * CE_VALID_MASK means the user promised us that the change to\n \t * the work tree does not matter and told us not to worry.\n \t */\n-\tif (!ignore_valid && (ce->ce_flags & CE_VALID)) {\n+\tif (!ignore_valid && (ce->ce_flags & CE_VALID_MASK)) {\n \t\tce_mark_uptodate(ce);\n \t\treturn ce;\n \t}\n-- \n1.6.0.rc3.250.g8dd0\n"},{"id":"87307","messageId":"20080815142515.GA10667@laptop","threadId":"15022","inReplyTo":"cover.1218807249.git.pclouds@gmail.com","subject":"[PATCH 2/9] update-index: add --checkout/--no-checkout options to update CE_NO_CHECKOUT bit","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-08-15T14:25:15Z","receivedAt":"2008-08-15T14:25:15Z","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-update-index.c |   40 +++++++++++++++++++++++++---------------\n 1 files changed, 25 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin-update-index.c b/builtin-update-index.c\nindex 434cb8e..1d6bb65 100644\n--- a/builtin-update-index.c\n+++ b/builtin-update-index.c\n@@ -24,8 +24,9 @@ static int info_only;\n static int force_remove;\n static int verbose;\n static int mark_valid_only;\n-#define MARK_VALID 1\n-#define UNMARK_VALID 2\n+static int mark_no_checkout_only;\n+#define MARK_FLAG 1\n+#define UNMARK_FLAG 2\n \n static void report(const char *fmt, ...)\n {\n@@ -40,19 +41,15 @@ static void report(const char *fmt, ...)\n \tva_end(vp);\n }\n \n-static int mark_valid(const char *path)\n+static int mark_ce_flags(const char *path, int flag, int mark)\n {\n \tint namelen = strlen(path);\n \tint pos = cache_name_pos(path, namelen);\n \tif (0 <= pos) {\n-\t\tswitch (mark_valid_only) {\n-\t\tcase MARK_VALID:\n-\t\t\tactive_cache[pos]->ce_flags |= CE_VALID;\n-\t\t\tbreak;\n-\t\tcase UNMARK_VALID:\n-\t\t\tactive_cache[pos]->ce_flags &= ~CE_VALID;\n-\t\t\tbreak;\n-\t\t}\n+\t\tif (mark)\n+\t\t\tactive_cache[pos]->ce_flags |= flag;\n+\t\telse\n+\t\t\tactive_cache[pos]->ce_flags &= ~flag;\n \t\tcache_tree_invalidate_path(active_cache_tree, path);\n \t\tactive_cache_changed = 1;\n \t\treturn 0;\n@@ -276,7 +273,12 @@ static void update_one(const char *path, const char *prefix, int prefix_length)\n \t\tgoto free_return;\n \t}\n \tif (mark_valid_only) {\n-\t\tif (mark_valid(p))\n+\t\tif (mark_ce_flags(p, CE_VALID, mark_valid_only == MARK_FLAG))\n+\t\t\tdie(\"Unable to mark file %s\", path);\n+\t\tgoto free_return;\n+\t}\n+\tif (mark_no_checkout_only) {\n+\t\tif (mark_ce_flags(p, CE_NO_CHECKOUT, mark_no_checkout_only == MARK_FLAG))\n \t\t\tdie(\"Unable to mark file %s\", path);\n \t\tgoto free_return;\n \t}\n@@ -390,7 +392,7 @@ static void read_index_info(int line_termination)\n }\n \n static const char update_index_usage[] =\n-\"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] <file>...\";\n+\"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--checkout|--no-checkout] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] <file>...\";\n \n static unsigned char head_sha1[20];\n static unsigned char merge_head_sha1[20];\n@@ -647,11 +649,19 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(path, \"--assume-unchanged\")) {\n-\t\t\t\tmark_valid_only = MARK_VALID;\n+\t\t\t\tmark_valid_only = MARK_FLAG;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(path, \"--no-assume-unchanged\")) {\n-\t\t\t\tmark_valid_only = UNMARK_VALID;\n+\t\t\t\tmark_valid_only = UNMARK_FLAG;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(path, \"--checkout\")) {\n+\t\t\t\tmark_no_checkout_only = UNMARK_FLAG;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(path, \"--no-checkout\")) {\n+\t\t\t\tmark_no_checkout_only = MARK_FLAG;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(path, \"--info-only\")) {\n-- \n1.6.0.rc3.250.g8dd0\n"},{"id":"87308","messageId":"20080815142548.GA10680@laptop","threadId":"15022","inReplyTo":"cover.1218807249.git.pclouds@gmail.com","subject":"[PATCH 3/9] ls-files: add --checkout option to show checked out files","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-08-15T14:25:48Z","receivedAt":"2008-08-15T14:25:48Z","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-ls-files.c |   13 ++++++++++---\n 1 files changed, 10 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex e8d568e..bcb1536 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -14,6 +14,7 @@\n static int abbrev;\n static int show_deleted;\n static int show_cached;\n+static int show_checkout;\n static int show_others;\n static int show_stage;\n static int show_unmerged;\n@@ -235,7 +236,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)\n \t\tif (show_killed)\n \t\t\tshow_killed_files(dir);\n \t}\n-\tif (show_cached | show_stage) {\n+\tif (show_cached | show_stage | show_checkout) {\n \t\tfor (i = 0; i < active_nr; i++) {\n \t\t\tstruct cache_entry *ce = active_cache[i];\n \t\t\tint dtype = ce_to_dtype(ce);\n@@ -245,6 +246,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)\n \t\t\t\tcontinue;\n \t\t\tif (ce->ce_flags & CE_UPDATE)\n \t\t\t\tcontinue;\n+\t\t\tif (show_checkout && ce_no_checkout(ce))\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@@ -423,7 +426,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_\n }\n \n static const char ls_files_usage[] =\n-\t\"git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* \"\n+\t\"git ls-files [-z] [-t] [-v] (--[cached|checkout|deleted|others|stage|unmerged|killed|modified])* \"\n \t\"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] \"\n \t\"[ --exclude-per-directory=<filename> ] [--exclude-standard] \"\n \t\"[--full-name] [--abbrev] [--] [<file>]*\";\n@@ -465,6 +468,10 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\t\tshow_cached = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--checkout\")) {\n+\t\t\tshow_checkout = 1;\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@@ -593,7 +600,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \n \t/* With no flags, we default to showing the cached files */\n \tif (!(show_stage | show_deleted | show_others | show_unmerged |\n-\t      show_killed | show_modified))\n+\t      show_killed | show_modified | show_checkout))\n \t\tshow_cached = 1;\n \n \tread_cache();\n-- \n1.6.0.rc3.250.g8dd0\n"},{"id":"87310","messageId":"20080815142601.GA10705@laptop","threadId":"15022","inReplyTo":"cover.1218807249.git.pclouds@gmail.com","subject":"[PATCH 4/9] Prevent diff machinery from examining worktree outside narrow checkout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-08-15T14:26:01Z","receivedAt":"2008-08-15T14:26:01Z","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 diff-lib.c |    5 +++--\n diff.c     |    4 +++-\n 2 files changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/diff-lib.c b/diff-lib.c\nindex e7eaff9..4fffd31 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -159,7 +159,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)\n \t\t\t\tcontinue;\n \t\t}\n \n-\t\tif (ce_uptodate(ce))\n+\t\tif (ce_uptodate(ce) || ce_no_checkout(ce))\n \t\t\tcontinue;\n \n \t\tchanged = check_removed(ce, &st);\n@@ -346,6 +346,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,\n \tstruct rev_info *revs = cbdata->revs;\n \tint match_missing, cached;\n \n+\t/* if the entry is not checked out, don't examine work tree */\n+\tcached = o->index_only || ce_no_checkout(idx);\n \t/*\n \t * Backward compatibility wart - \"diff-index -m\" does\n \t * not mean \"do not ignore merges\", but \"match_missing\".\n@@ -353,7 +355,6 @@ static void do_oneway_diff(struct unpack_trees_options *o,\n \t * But with the revision flag parsing, that's found in\n \t * \"!revs->ignore_merges\".\n \t */\n-\tcached = o->index_only;\n \tmatch_missing = !revs->ignore_merges;\n \n \tif (cached && idx && ce_stage(idx)) {\ndiff --git a/diff.c b/diff.c\nindex a6c1432..ad164a5 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1716,8 +1716,10 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int\n \n \t/*\n \t * If ce matches the file in the work tree, we can reuse it.\n+\t * For narrow checkout case, ce_uptodate() may be true although\n+\t * the file may or may not exist in the work tree.\n \t */\n-\tif (ce_uptodate(ce) ||\n+\tif ((ce_uptodate(ce) && ce_checkout(ce)) ||\n \t    (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))\n \t\treturn 1;\n \n-- \n1.6.0.rc3.250.g8dd0\n"},{"id":"87309","messageId":"20080815142618.GA10721@laptop","threadId":"15022","inReplyTo":"cover.1218807249.git.pclouds@gmail.com","subject":"[PATCH 5/9] Clear CE_NO_CHECKOUT on checked out entries.","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-08-15T14:26:18Z","receivedAt":"2008-08-15T14:26:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"With this you can just do \"git checkout some-files\" to\nwiden your checkout. On caveat though: caller must save\nthe index.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n entry.c |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\ndiff --git a/entry.c b/entry.c\nindex 222aaa3..c1c01f4 100644\n--- a/entry.c\n+++ b/entry.c\n@@ -230,5 +230,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t\n \t} else if (state->not_new)\n \t\treturn 0;\n \tcreate_directories(path, state);\n+\tce_mark_checkout(ce);\n \treturn write_entry(ce, path, state, 0);\n }\n-- \n1.6.0.rc3.250.g8dd0\n"},{"id":"87311","messageId":"20080815142638.GA10736@laptop","threadId":"15022","inReplyTo":"cover.1218807249.git.pclouds@gmail.com","subject":"[PATCH 6/9] Add support for narrow checkout in unpack_trees()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-08-15T14:26:38Z","receivedAt":"2008-08-15T14:26:38Z","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 narrow area is\nchanged. There are three kind of changes:\n\n - new_narrow_path: reset workdir to a new narrow checkout\n - add_narrow_path: keep current narrow areas and add more entries\n - remove_narrow_path: remove some entries from current narrow areas\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 |  106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n unpack-trees.h |    4 ++\n 3 files changed, 113 insertions(+), 0 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 86288b6..f502b28 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -139,6 +139,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_WD_REMOVE (0x400000)\n+\n /* \"Assume unchanged\" mask */\n #define CE_VALID_MASK (CE_VALID | CE_NO_CHECKOUT)\n \ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex cba0aca..022c643 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -96,6 +96,21 @@ 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+\n+\t\t\t/*\n+\t\t\t * Special case: CE_WD_REMOVE may be set along with CE_NO_CHECKOUT\n+\t\t\t * when some files are going to leave narrow checkout, so it must\n+\t\t\t * go before ce_no_checkout() check\n+\t\t\t */\n+\t\t\tif (ce->ce_flags & CE_WD_REMOVE) {\n+\t\t\t\ttotal++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\t/* Now if there is any update outside narrow, ignore it */\n+\t\t\tif (ce_no_checkout(ce))\n+\t\t\t\tcontinue;\n+\n \t\t\tif (ce->ce_flags & (CE_UPDATE | CE_REMOVE))\n \t\t\t\ttotal++;\n \t\t}\n@@ -108,6 +123,18 @@ 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\t/* As stated earlier, CE_WD_REMOVE must bypass ce_no_checkout() check */\n+\t\tif (ce->ce_flags & CE_WD_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\t/* Now if there is any update outside narrow, ignore it */\n+\t\tif (ce_no_checkout(ce))\n+\t\t\tcontinue;\n+\n \t\tif (ce->ce_flags & CE_REMOVE) {\n \t\t\tdisplay_progress(progress, ++cnt);\n \t\t\tif (o->update)\n@@ -121,6 +148,9 @@ 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_no_checkout(ce))\n+\t\t\tcontinue;\n+\n \t\tif (ce->ce_flags & CE_UPDATE) {\n \t\t\tdisplay_progress(progress, ++cnt);\n \t\t\tce->ce_flags &= ~CE_UPDATE;\n@@ -725,6 +755,58 @@ static void show_stage_entry(FILE *o,\n }\n #endif\n \n+int match_narrow_spec(const char *spec_, const char *path)\n+{\n+\tint match = 0;\n+\tchar *spec, *cur_spec;\n+\n+\tif (!spec_)\n+\t\treturn 1; /* always match if spec_ is NULL */\n+\tspec = cur_spec = xstrdup(spec_);\n+\n+\twhile (!match) {\n+\t\tchar *next_spec = strchr(cur_spec, ':');\n+\t\tif (!next_spec) {\n+\t\t\tif (!fnmatch(cur_spec, path, 0))\n+\t\t\t\tmatch = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t\t*next_spec = '\\0';\n+\t\tif (!fnmatch(cur_spec, path, 0))\n+\t\t\tmatch = 1;\n+\t\tcur_spec = next_spec+1;\n+\t}\n+\tfree(spec);\n+\treturn match;\n+}\n+\n+static int apply_narrow_checkout(struct cache_entry *ce, struct cache_entry *old_ce, struct unpack_trees_options *o)\n+{\n+\tint checkout;\n+\tint was_no_checkout = old_ce && ce_no_checkout(old_ce);\n+\n+\tif (!(o->new_narrow_path | o->add_narrow_path | o->remove_narrow_path))\n+\t\treturn 0;\n+\n+\tcheckout = match_narrow_spec(o->narrow_spec, ce->name);\n+\n+\t/*\n+\t * The logic is a bit twisted here, when we expand checkout (add_narrow_path)\n+\t * we narrow no_checkout area. Similarly when we narrow checkout\n+\t * (remove_narrow_path), we expand no_checkout area. So there are three cases:\n+\t *\n+\t * [1] New narrow spec: do not care about old_ce\n+\t * [2] Expand spec: mark no_checkout if no_checkout previously _and_ now too\n+\t * [3] Narrow spec: mark no_checkout if previously no_checkout _or_ now checkout\n+\t */\n+\tif ((o->new_narrow_path && !checkout) /* [1] */\n+\t    || (o->add_narrow_path && !checkout && was_no_checkout) /* [2] */\n+\t    || (o->remove_narrow_path && (checkout || was_no_checkout))) /* [3] */\n+\t\tce_mark_no_checkout(ce);\n+\n+\treturn 1;\n+}\n+\n int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)\n {\n \tstruct cache_entry *index;\n@@ -899,6 +981,7 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)\n \tstruct cache_entry *current = src[0];\n \tstruct cache_entry *oldtree = src[1];\n \tstruct cache_entry *newtree = src[2];\n+\tint has_narrow_checkout = 0;\n \n \tif (o->merge_size != 2)\n \t\treturn error(\"Cannot do a twoway merge of %d trees\",\n@@ -909,6 +992,9 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)\n \tif (newtree == o->df_conflict_entry)\n \t\tnewtree = NULL;\n \n+\tif (newtree)\n+\t\thas_narrow_checkout = apply_narrow_checkout(newtree, current, o);\n+\n \tif (current) {\n \t\tif ((!oldtree && !newtree) || /* 4 and 5 */\n \t\t    (!oldtree && newtree &&\n@@ -918,6 +1004,21 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)\n \t\t    (oldtree && newtree &&\n \t\t     !same(oldtree, newtree) && /* 18 and 19 */\n \t\t     same(current, newtree))) {\n+\t\t\tif (has_narrow_checkout) {\n+\t\t\t\t/* enter narrow checkout, keep entry and add to workdir */\n+\t\t\t\tif (ce_no_checkout(current) && ce_checkout(newtree)) {\n+\t\t\t\t\tadd_entry(o, current, CE_UPDATE, CE_NO_CHECKOUT);\n+\t\t\t\t\treturn 1;\n+\t\t\t\t}\n+\n+\t\t\t\t/* leave narrow checkout, keep entry and remove from workdir */\n+\t\t\t\tif (ce_checkout(current) && ce_no_checkout(newtree)) {\n+\t\t\t\t\tif (verify_uptodate(current, o))\n+\t\t\t\t\t\treturn -1;\n+\t\t\t\t\tadd_entry(o, current, CE_WD_REMOVE | CE_NO_CHECKOUT, 0);\n+\t\t\t\t\treturn 1;\n+\t\t\t\t}\n+\t\t\t}\n \t\t\treturn keep_entry(current, o);\n \t\t}\n \t\telse if (oldtree && !newtree && same(current, oldtree)) {\n@@ -927,6 +1028,9 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)\n \t\telse if (oldtree && newtree &&\n \t\t\t same(current, oldtree) && !same(current, newtree)) {\n \t\t\t/* 20 or 21 */\n+\t\t\t/* enter narrow checkout, make sure always add  */\n+\t\t\tif (has_narrow_checkout && ce_no_checkout(current) && ce_checkout(newtree))\n+\t\t\t\tnewtree->ce_flags |= CE_UPDATE;\n \t\t\treturn merged_entry(newtree, current, o);\n \t\t}\n \t\telse {\n@@ -987,6 +1091,8 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)\n \tif (!a)\n \t\treturn deleted_entry(old, old, o);\n \n+\tapply_narrow_checkout(a, NULL, o);\n+\n \tif (old && same(old, a)) {\n \t\tint update = 0;\n \t\tif (o->reset) {\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 94e5672..880cef8 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -26,6 +26,9 @@ struct unpack_trees_options {\n \t\t     verbose_update:1,\n \t\t     aggressive:1,\n \t\t     skip_unmerged: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@@ -37,6 +40,7 @@ struct unpack_trees_options {\n \tint merge_size;\n \n \tstruct cache_entry *df_conflict_entry;\n+\tconst char *narrow_spec;\n \tvoid *unpack_data;\n \n \tstruct index_state *dst_index;\n-- \n1.6.0.rc3.250.g8dd0\n"},{"id":"87312","messageId":"20080815142656.GA10750@laptop","threadId":"15022","inReplyTo":"cover.1218807249.git.pclouds@gmail.com","subject":"[PATCH 7/9] ls-files: add --narrow-match=spec option to test narrow matching","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-08-15T14:26:56Z","receivedAt":"2008-08-15T14:26:56Z","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-ls-files.c |   10 +++++++++-\n 1 files changed, 9 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-ls-files.c b/builtin-ls-files.c\nindex bcb1536..0064f73 100644\n--- a/builtin-ls-files.c\n+++ b/builtin-ls-files.c\n@@ -29,6 +29,7 @@ static const char **pathspec;\n static int error_unmatch;\n static char *ps_matched;\n static const char *with_tree;\n+static const char *narrow_spec;\n \n static const char *tag_cached = \"\";\n static const char *tag_unmerged = \"\";\n@@ -219,6 +220,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)\n \twrite_name_quoted(ce->name + offset, stdout, line_terminator);\n }\n \n+int match_narrow_spec(const char *spec_, const char *path); /* unpack-trees.c */\n static void show_files(struct dir_struct *dir, const char *prefix)\n {\n \tint i;\n@@ -248,6 +250,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)\n \t\t\t\tcontinue;\n \t\t\tif (show_checkout && ce_no_checkout(ce))\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@@ -429,7 +433,7 @@ static const char ls_files_usage[] =\n \t\"git ls-files [-z] [-t] [-v] (--[cached|checkout|deleted|others|stage|unmerged|killed|modified])* \"\n \t\"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] \"\n \t\"[ --exclude-per-directory=<filename> ] [--exclude-standard] \"\n-\t\"[--full-name] [--abbrev] [--] [<file>]*\";\n+\t\"[--narrow-match=narrowspec] [--full-name] [--abbrev] [--] [<file>]*\";\n \n int cmd_ls_files(int argc, const char **argv, const char *prefix)\n {\n@@ -472,6 +476,10 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)\n \t\t\tshow_checkout = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!prefixcmp(arg, \"--narrow-match=\")) {\n+\t\t\tnarrow_spec = arg+15;\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-- \n1.6.0.rc3.250.g8dd0\n"},{"id":"87313","messageId":"20080815142709.GA10765@laptop","threadId":"15022","inReplyTo":"cover.1218807249.git.pclouds@gmail.com","subject":"[PATCH 8/9] clone: support narrow checkout with --path option","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-08-15T14:27:09Z","receivedAt":"2008-08-15T14:27:09Z","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-clone.c |   13 +++++++++++++\n 1 files changed, 13 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex c0e3086..0fcf53d 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 \n@@ -43,6 +44,8 @@ static struct option builtin_clone_options[] = {\n \tOPT__QUIET(&option_quiet),\n \tOPT_BOOLEAN('n', \"no-checkout\", &option_no_checkout,\n \t\t    \"don't create a checkout\"),\n+\tOPT_STRING(0, \"path\", &option_narrow_path, \"prefixes\",\n+\t\t    \"limit checkout to specified paths (narrow checkout)\"),\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@@ -376,10 +379,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 --path 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 --path options are incompatible.\");\n+\n \tif (!option_origin)\n \t\toption_origin = \"origin\";\n \n@@ -586,6 +594,11 @@ 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 = option_narrow_path;\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-- \n1.6.0.rc3.250.g8dd0\n"},{"id":"87314","messageId":"20080815142730.GA10781@laptop","threadId":"15022","inReplyTo":"cover.1218807249.git.pclouds@gmail.com","subject":"[PATCH 9/9] checkout: add new options to support narrow checkout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-08-15T14:27:30Z","receivedAt":"2008-08-15T14:27:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These options include:\n\n --full: return to full checkout (default)\n --path: narrow checkout to some areas according to given spec\n --add-path/--remove-path: adjust current narrow checkout\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin-checkout.c |   41 +++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 41 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex e95eab9..f2254c6 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -160,6 +160,11 @@ struct checkout_opts {\n \tchar *new_branch;\n \tint new_branch_log;\n \tenum branch_track track;\n+\n+\tchar *new_path;\n+\tchar *add_path;\n+\tchar *remove_path;\n+\tint no_narrow_checkout;\n };\n \n static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree)\n@@ -255,6 +260,23 @@ 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->no_narrow_checkout) {\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 = opts->new_path;\n+\t\t\ttopts.new_narrow_path = 1;\n+\t\t}\n+\t\telse if (opts->add_path) {\n+\t\t\ttopts.narrow_spec = opts->add_path;\n+\t\t\ttopts.add_narrow_path = 1;\n+\t\t}\n+\t\telse if (opts->remove_path) {\n+\t\t\ttopts.narrow_spec = opts->remove_path;\n+\t\t\ttopts.remove_narrow_path = 1;\n+\t\t}\n+\n \t\tret = unpack_trees(2, trees, &topts);\n \t\tif (ret == -1) {\n \t\t\t/*\n@@ -428,6 +450,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\tBRANCH_TRACK_EXPLICIT),\n \t\tOPT_BOOLEAN('f', NULL, &opts.force, \"force\"),\n \t\tOPT_BOOLEAN('m', NULL, &opts.merge, \"merge\"),\n+\t\tOPT_BOOLEAN(0, \"full\", &opts.no_narrow_checkout, \"quit sparse checkout\"),\n+\t\tOPT_STRING(0, \"path\", &opts.new_path, \"prefixes\", \"apply new narrow checkout path\"),\n+\t\tOPT_STRING(0, \"add-path\", &opts.add_path, \"prefixes\", \"add more checkout area\"),\n+\t\tOPT_STRING(0, \"remove-path\", &opts.remove_path, \"prefixes\", \"narrow checkout area\"),\n \t\tOPT_END(),\n \t};\n \tint has_dash_dash;\n@@ -460,6 +486,18 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (opts.track == -1)\n \t\topts.track = git_branch_track;\n \n+\tif (((opts.no_narrow_checkout ? 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: --path, --full, --add-path and --remove-path are incompatible\");\n+\n+\tif (opts.new_branch && (opts.add_path || opts.remove_path))\n+\t\tdie(\"git checkout: --add-path and --remove-path should only be used on current branch\");\n+\n+\tif (opts.new_branch && opts.no_narrow_checkout)\n+\t\tdie(\"git checkout: switching branch with --full does not make sense\");\n+\n \tif (opts.force && opts.merge)\n \t\tdie(\"git checkout: -f and -m are incompatible\");\n \n@@ -550,6 +588,9 @@ no_reference:\n \t\t\t}\n \t\t}\n \n+\t\tif (opts.no_narrow_checkout || 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);\n \t}\n \n-- \n1.6.0.rc3.250.g8dd0\n"}]}