{"thread":{"id":"14629","subject":"[PATCH 10/12] git-checkout: support --full and --path to manipulate sparse checkout","startedAt":"2008-07-23T14:57:44Z","lastAt":"2008-07-23T19:05:55Z","messageCount":3,"participants":["Nguyễn Thái Ngọc Duy","Petr Baudis"],"isPatch":true,"patchVersion":1,"patchTotal":12},"messages":[{"id":"84518","messageId":"20080723145744.GA29156@laptop","threadId":"14629","inReplyTo":null,"subject":"[PATCH 10/12] git-checkout: support --full and --path to manipulate sparse checkout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2008-07-23T14:57:44Z","receivedAt":"2008-07-23T14:57:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"git checkout --full\" will quit sparse checkout mode while\n\"git checkout --sparse=prefix\" will turn on sparse checkout or\nupdate sparse prefix.\n\ntwoway_merge has been updated to deal with sparse checkout update.\nFiles that are in old sparse prefix only will get removed while\nfiles that are in new sparse prefix only will get added.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin-checkout.c |   44 +++++++++++++++++++\n cache.h            |    2 +\n unpack-trees.c     |  120 ++++++++++++++++++++++++++++++++++++++++++++++++----\n unpack-trees.h     |    3 +-\n 4 files changed, 160 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex eec1bde..410a53a 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -160,6 +160,9 @@ struct checkout_opts {\n \tchar *new_branch;\n \tint new_branch_log;\n \tenum branch_track track;\n+\n+\tchar *sparse_prefix;\n+\tint no_sparse_checkout;\n };\n \n static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree)\n@@ -255,7 +258,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->no_sparse_checkout)\n+\t\t\ttopts.new_sparse_prefix = 1;\n+\n+\t\tif (opts->sparse_prefix) {\n+\t\t\tchar **new_sparse_prefix = split_prefix(opts->sparse_prefix);\n+\n+\t\t\tif (!new_sparse_prefix) {\n+\t\t\t\terror(\"new sparse prefix invalid\");\n+\t\t\t\treturn 1;\n+\t\t\t}\n+\t\t\ttopts.new_sparse_prefix = 1;\n+\t\t\ttopts.unpack_data = new_sparse_prefix;\n+\t\t}\n+\n \t\tret = unpack_trees(2, trees, &topts);\n+\n+\t\tif (topts.new_sparse_prefix && topts.unpack_data)\n+\t\t\tfree_prefix_list((char **)topts.unpack_data);\n+\n \t\tif (ret == -1) {\n \t\t\t/*\n \t\t\t * Unpack couldn't do a trivial merge; either\n@@ -299,6 +320,14 @@ static int merge_working_tree(struct checkout_opts *opts,\n \t    commit_locked_index(lock_file))\n \t\tdie(\"unable to write new index file\");\n \n+\t/*\n+\t * update sparse checkout accordingly\n+\t * show_local_changes will need it\n+\t */\n+\tif (opts->no_sparse_checkout)\n+\t\tfree_prefix_list(save_sparse_prefix());\n+\tif (opts->sparse_prefix)\n+\t\tset_sparse_prefix(opts->sparse_prefix);\n \tif (!opts->force)\n \t\tshow_local_changes(&new->commit->object);\n \n@@ -409,6 +438,13 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n \n \tupdate_refs_for_switch(opts, &old, new);\n \n+\t/* now save new sparse prefix */\n+\tif (opts->no_sparse_checkout)\n+\t\tgit_config_set(\"core.sparsecheckout\", NULL);\n+\n+\tif (opts->sparse_prefix)\n+\t\tgit_config_set(\"core.sparsecheckout\", opts->sparse_prefix);\n+\n \tret = post_checkout_hook(old.commit, new->commit, 1);\n \treturn ret || opts->writeout_error;\n }\n@@ -428,6 +464,8 @@ 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_STRING(0, \"path\", &opts.sparse_prefix, \"prefixes\", \"update sparse checkout\"),\n+\t\tOPT_BOOLEAN(0, \"full\", &opts.no_sparse_checkout, \"quit sparse checkout\"),\n \t\tOPT_END(),\n \t};\n \n@@ -468,6 +506,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (!opts.new_branch && (opts.track != git_branch_track))\n \t\tdie(\"git checkout: --track and --no-track require -b\");\n \n+\tif (opts.no_sparse_checkout && opts.sparse_prefix)\n+\t\tdie(\"git checkout: --path and --full are incompatible\");\n+\n \tif (opts.force && opts.merge)\n \t\tdie(\"git checkout: -f and -m are incompatible\");\n \n@@ -486,6 +527,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t}\n \t\t}\n \n+\t\tif (opts.no_sparse_checkout || opts.sparse_prefix)\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 \ndiff --git a/cache.h b/cache.h\nindex b9a1d96..9e7f146 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -138,6 +138,8 @@ struct cache_entry {\n #define CE_HASHED    (0x100000)\n #define CE_UNHASHED  (0x200000)\n \n+#define CE_WD_REMOVE (0x400000)\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!\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 0a30d68..a88c53f 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -105,13 +105,29 @@ static int check_updates(struct unpack_trees_options *o)\n \tstruct index_state *index = &o->result;\n \tint i;\n \tint errs = 0;\n+\tchar **old_sparse_prefix = NULL;\n+\tchar **new_sparse_prefix = NULL;\n+\n+\t/*\n+\t * for sparse checkout update, we need to widen the prefix a bit\n+\t * so that updating in new sparse prefix won't get caught by\n+\t * sparse prefix checks\n+\t */\n+\tif (o->new_sparse_prefix) {\n+\t\told_sparse_prefix = save_sparse_prefix();\n+\t\tif (o->unpack_data) {\n+\t\t\tnew_sparse_prefix = o->unpack_data;\n+\t\t\tnew_sparse_prefix = combine_prefix_list(old_sparse_prefix, new_sparse_prefix);\n+\t\t\trestore_sparse_prefix(new_sparse_prefix);\n+\t\t}\n+\t}\n \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 (outside_sparse_prefix(ce->name))\n \t\t\t\tcontinue;\n-\t\t\tif (ce->ce_flags & (CE_UPDATE | CE_REMOVE))\n+\t\t\tif (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WD_REMOVE))\n \t\t\t\ttotal++;\n \t\t}\n \n@@ -125,12 +141,14 @@ static int check_updates(struct unpack_trees_options *o)\n \n \t\tif (outside_sparse_prefix(ce->name))\n \t\t\tcontinue;\n-\t\tif (ce->ce_flags & CE_REMOVE) {\n+\t\tif (ce->ce_flags & (CE_REMOVE | 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\tremove_index_entry_at(&o->result, i);\n-\t\t\ti--;\n+\t\t\tif (ce->ce_flags & CE_REMOVE) {\n+\t\t\t\tremove_index_entry_at(&o->result, i);\n+\t\t\t\ti--;\n+\t\t\t}\n \t\t\tcontinue;\n \t\t}\n \t}\n@@ -149,6 +167,10 @@ static int check_updates(struct unpack_trees_options *o)\n \t\t}\n \t}\n \tstop_progress(&progress);\n+\tif (o->new_sparse_prefix) {\n+\t\trestore_sparse_prefix(old_sparse_prefix);\n+\t\tfree_prefix_list(new_sparse_prefix);\n+\t}\n \treturn errs != 0;\n }\n \n@@ -680,8 +702,8 @@ static int verify_absent(struct cache_entry *ce, const char *action,\n \treturn 0;\n }\n \n-static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n-\t\tstruct unpack_trees_options *o)\n+static int merged_entry_internal(struct cache_entry *merge, struct cache_entry *old,\n+\t\tstruct unpack_trees_options *o, int set, int clear)\n {\n \tint update = CE_UPDATE;\n \n@@ -708,10 +730,28 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n \t\tinvalidate_ce_path(merge, o);\n \t}\n \n-\tadd_entry(o, merge, update, CE_STAGEMASK);\n+\tadd_entry(o, merge, (update & (~clear)) | set, CE_STAGEMASK);\n \treturn 1;\n }\n \n+static int merged_entry(struct cache_entry *merge, struct cache_entry *old,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\treturn merged_entry_internal(merge, old, o, 0, 0);\n+}\n+\n+static int merged_entry_and_no_add(struct cache_entry *merge, struct cache_entry *old,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\treturn merged_entry_internal(merge, old, o, 0, CE_UPDATE);\n+}\n+\n+static int merged_entry_and_add(struct cache_entry *merge, struct cache_entry *old,\n+\t\tstruct unpack_trees_options *o)\n+{\n+\treturn merged_entry_internal(merge, old, o, CE_UPDATE, 0);\n+}\n+\n static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,\n \t\tstruct unpack_trees_options *o)\n {\n@@ -734,6 +774,20 @@ static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o)\n \treturn 1;\n }\n \n+static int keep_entry_and_add(struct cache_entry *ce, struct unpack_trees_options *o)\n+{\n+\tadd_entry(o, ce, CE_UPDATE, 0);\n+\treturn 1;\n+}\n+\n+static int keep_entry_and_remove(struct cache_entry *ce, struct unpack_trees_options *o)\n+{\n+\tif (verify_uptodate(ce, o))\n+\t\treturn -1;\n+\tadd_entry(o, ce, CE_WD_REMOVE, 0);\n+\treturn 1;\n+}\n+\n #if DBRT_DEBUG\n static void show_stage_entry(FILE *o,\n \t\t\t     const char *label, const struct cache_entry *ce)\n@@ -918,12 +972,31 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)\n  * over a merge failure when it makes sense.  For details of the\n  * \"carry forward\" rule, please see <Documentation/git-read-tree.txt>.\n  *\n+ * Two-way merge with sparse prefix update.\n+ *\n+ * The rules are as same as plain two-way merge, except:\n+ *  1) if it's in old sparse checkout, but not the new one, mark it CE_WD_REMOVE\n+ *  2) if it's in the new one, but not the old one, mark it CE_UPDATE\n+ *  3) otherwise, let old two-way merge take it\n+ *\n+ *  case 1)\n+ *     keep index implies CE_WD_REMOVE (remove workdir only, keep file in index)\n+ *     use M implies no CE_UPDATE\n+ *     if index is not clean, fail\n+ *\n+ *  case 2)\n+ *     keep index implies CE_UPDATE\n+ *     use M implies CE_UPDATE\n+ *     index can't be unclean, so ignore this\n+ *\n  */\n int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)\n {\n \tstruct cache_entry *current = src[0];\n \tstruct cache_entry *oldtree = src[1];\n \tstruct cache_entry *newtree = src[2];\n+\tint remove = 0, add = 0;\n+\tchar **new_sparse_prefix = (char **)o->unpack_data;\n \n \tif (o->merge_size != 2)\n \t\treturn error(\"Cannot do a twoway merge of %d trees\",\n@@ -935,6 +1008,13 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)\n \t\tnewtree = NULL;\n \n \tif (current) {\n+\t\tif (o->new_sparse_prefix) {\n+\t\t\tremove = !outside_sparse_prefix(current->name) &&\n+\t\t\t\t outside_prefix_list(new_sparse_prefix, current->name);\n+\t\t\tadd = outside_sparse_prefix(current->name) &&\n+\t\t\t      !outside_prefix_list(new_sparse_prefix, current->name);\n+\t\t}\n+\n \t\tif ((!oldtree && !newtree) || /* 4 and 5 */\n \t\t    (!oldtree && newtree &&\n \t\t     same(current, newtree)) || /* 6 and 7 */\n@@ -943,6 +1023,12 @@ 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 (o->new_sparse_prefix) {\n+\t\t\t\tif (remove)\n+\t\t\t\t\treturn keep_entry_and_remove(current, o);\n+\t\t\t\tif (add)\n+\t\t\t\t\treturn keep_entry_and_add(current, o);\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@@ -952,6 +1038,12 @@ 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\tif (o->new_sparse_prefix) {\n+\t\t\t\tif (remove)\n+\t\t\t\t\treturn merged_entry_and_no_add(newtree, current, o);\n+\t\t\t\tif (add)\n+\t\t\t\t\treturn merged_entry_and_add(newtree, current, o);\n+\t\t\t}\n \t\t\treturn merged_entry(newtree, current, o);\n \t\t}\n \t\telse {\n@@ -965,8 +1057,20 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)\n \t\t\treturn -1;\n \t\t}\n \t}\n-\telse if (newtree)\n+\telse if (newtree) {\n+\t\tif (o->new_sparse_prefix) {\n+\t\t\tremove = !outside_sparse_prefix(newtree->name) &&\n+\t\t\t\toutside_prefix_list(new_sparse_prefix, newtree->name);\n+\t\t\tadd = outside_sparse_prefix(newtree->name) &&\n+\t\t\t\t!outside_prefix_list(new_sparse_prefix, newtree->name);\n+\n+\t\t\tif (remove)\n+\t\t\t\treturn merged_entry_and_no_add(newtree, current, o);\n+\t\t\tif (add)\n+\t\t\t\treturn merged_entry_and_add(newtree, current, o);\n+\t\t}\n \t\treturn merged_entry(newtree, current, o);\n+\t}\n \treturn deleted_entry(oldtree, current, o);\n }\n \ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex a1b46f9..5a29fc4 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -27,7 +27,8 @@ struct unpack_trees_options {\n \t\t     aggressive:1,\n \t\t     skip_unmerged:1,\n \t\t     gently:1,\n-\t\t     check_index_prefix:1;\n+\t\t     check_index_prefix:1,\n+\t\t     new_sparse_prefix:1; /* unpack_data must contain new prefix */\n \tconst char *prefix;\n \tint pos;\n \tstruct dir_struct *dir;\n-- \n1.5.5.GIT\n"},{"id":"84575","messageId":"20080723190129.GM32184@machine.or.cz","threadId":"14629","inReplyTo":"20080723145744.GA29156@laptop","subject":"Re: [PATCH 02/12] git-grep: support --no-external-grep","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2008-07-23T19:01:30Z","receivedAt":"2008-07-23T19:01:30Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"  Hi,\n\n  the patches 2, 3, 8 and 10 of your series introduce config options,\ncommandline switches and environment variables that are not documented\nanywhere - can you please include documentation updates in your patch\nseries as well? It might be worth it to find some suitable place in the\ndocumentation where to explain the concept itself; it took me quite\nsometime before I actually realized (I think) what are your patches\nabout. (Which is also why is it useful to write cover letters for\npatch series.)\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nAs in certain cults it is possible to kill a process if you know\nits true name.  -- Ken Thompson and Dennis M. Ritchie\n"},{"id":"84579","messageId":"20080723190555.GN32184@machine.or.cz","threadId":"14629","inReplyTo":"20080723190129.GM32184@machine.or.cz","subject":"Re: [PATCH 02/12] git-grep: support --no-external-grep","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2008-07-23T19:05:55Z","receivedAt":"2008-07-23T19:05:55Z","isPatch":true,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"  Hi!\n\nOn Wed, Jul 23, 2008 at 09:01:30PM +0200, Petr Baudis wrote:\n> (Which is also why is it useful to write cover letters for\n> patch series.)\n\n  sorry, I did not notice the cover letter, already separated from the\nrest by a sprouting thread. Which is also why it is useful to thread up\nall the patches. ;-)\n\n\t\t\t\tPetr \"Pasky\" Baudis\n"}]}