{"thread":{"id":"43635","subject":"[PATCH 1/2] read-tree: further loosen \"working file will be lost\" check.","startedAt":"2006-12-05T01:08:42Z","lastAt":"2006-12-05T01:21:50Z","messageCount":2,"participants":["Junio C Hamano","Jakub Narebski"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"296149","messageId":"7vlklnkv39.fsf@assigned-by-dhcp.cox.net","threadId":"43635","inReplyTo":null,"subject":"[PATCH 1/2] read-tree: further loosen \"working file will be lost\" check.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-12-05T01:08:42Z","receivedAt":"2006-12-05T01:08:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This follows up commit ed93b449 where we removed overcautious\n\"working file will be lost\" check.\n\nA new option \"--ignore=.gitignore\" can be used to tell the\n\"git-read-tree\" command that the user does not mind losing\ncontents in untracked files in the working tree if it needs to\nbe overwritten by a merge (either a two-way \"switch branch\"\nmerge, or a three-way merge).\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n builtin-read-tree.c         |   20 ++++++++++\n dir.c                       |    4 +-\n dir.h                       |    3 ++\n t/t1004-read-tree-m-u-wf.sh |   82 ++++++++++++++++++++++++++++++++++++++----\n unpack-trees.c              |   24 ++++++++++---\n unpack-trees.h              |    1 +\n 6 files changed, 119 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin-read-tree.c b/builtin-read-tree.c\nindex c1867d2..37b2214 100644\n--- a/builtin-read-tree.c\n+++ b/builtin-read-tree.c\n@@ -10,6 +10,7 @@\n #include \"tree-walk.h\"\n #include \"cache-tree.h\"\n #include \"unpack-trees.h\"\n+#include \"dir.h\"\n #include \"builtin.h\"\n \n static struct object_list *trees;\n@@ -178,6 +179,23 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)\n \t\t\tcontinue;\n \t\t}\n \n+\t\tif (!strncmp(arg, \"--ignore=\", 9)) {\n+\t\t\tstruct dir_struct *dir;\n+\n+\t\t\tif (opts.dir)\n+\t\t\t\tdie(\"more than one --ignore is given.\");\n+\n+\t\t\tdir = calloc(1, sizeof(*opts.dir));\n+\t\t\tdir->show_ignored = 1;\n+\t\t\tdir->exclude_per_dir = arg + 9;\n+\t\t\topts.dir = dir;\n+\t\t\t/* We do not need to nor want to do read-directory\n+\t\t\t * here; we are merely interested in reusing the\n+\t\t\t * per directory ignore stack mechanism.\n+\t\t\t */\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\t/* using -u and -i at the same time makes no sense */\n \t\tif (1 < opts.index_only + opts.update)\n \t\t\tusage(read_tree_usage);\n@@ -190,6 +208,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)\n \t}\n \tif ((opts.update||opts.index_only) && !opts.merge)\n \t\tusage(read_tree_usage);\n+\tif ((opts.dir && !opts.update))\n+\t\tdie(\"--ignore is meaningless unless -u\");\n \n \tif (opts.prefix) {\n \t\tint pfxlen = strlen(opts.prefix);\ndiff --git a/dir.c b/dir.c\nindex 96389b3..e6a61ee 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -156,7 +156,7 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)\n \t\tdie(\"cannot use %s as an exclude file\", fname);\n }\n \n-static int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)\n+int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)\n {\n \tchar exclude_file[PATH_MAX];\n \tstruct exclude_list *el = &dir->exclude_list[EXC_DIRS];\n@@ -170,7 +170,7 @@ static int push_exclude_per_directory(struct dir_struct *dir, const char *base,\n \treturn current_nr;\n }\n \n-static void pop_exclude_per_directory(struct dir_struct *dir, int stk)\n+void pop_exclude_per_directory(struct dir_struct *dir, int stk)\n {\n \tstruct exclude_list *el = &dir->exclude_list[EXC_DIRS];\n \ndiff --git a/dir.h b/dir.h\nindex 313f8ab..550551a 100644\n--- a/dir.h\n+++ b/dir.h\n@@ -43,6 +43,9 @@ extern int common_prefix(const char **pathspec);\n extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);\n \n extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);\n+extern int push_exclude_per_directory(struct dir_struct *, const char *, int);\n+extern void pop_exclude_per_directory(struct dir_struct *, int);\n+\n extern int excluded(struct dir_struct *, const char *);\n extern void add_excludes_from_file(struct dir_struct *, const char *fname);\n extern void add_exclude(const char *string, const char *base,\ndiff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh\nindex 018fbea..3f6f361 100755\n--- a/t/t1004-read-tree-m-u-wf.sh\n+++ b/t/t1004-read-tree-m-u-wf.sh\n@@ -8,23 +8,27 @@ test_description='read-tree -m -u checks working tree files'\n \n test_expect_success 'two-way setup' '\n \n+\tmkdir subdir &&\n \techo >file1 file one &&\n \techo >file2 file two &&\n-\tgit update-index --add file1 file2 &&\n+\techo >subdir/file1 file one in subdirectory &&\n+\techo >subdir/file2 file two in subdirectory &&\n+\tgit update-index --add file1 file2 subdir/file1 subdir/file2 &&\n \tgit commit -m initial &&\n \n \tgit branch side &&\n \tgit tag -f branch-point &&\n \n \techo file2 is not tracked on the master anymore &&\n-\trm -f file2 &&\n-\tgit update-index --remove file2 &&\n-\tgit commit -a -m \"master removes file2\"\n+\trm -f file2 subdir/file2 &&\n+\tgit update-index --remove file2 subdir/file2 &&\n+\tgit commit -a -m \"master removes file2 and subdir/file2\"\n '\n \n test_expect_success 'two-way not clobbering' '\n \n \techo >file2 master creates untracked file2 &&\n+\techo >subdir/file2 master creates untracked subdir/file2 &&\n \tif err=`git read-tree -m -u master side 2>&1`\n \tthen\n \t\techo should have complained\n@@ -34,20 +38,82 @@ test_expect_success 'two-way not clobbering' '\n \tfi\n '\n \n+echo file2 >.gitignore\n+\n+test_expect_success 'two-way with incorrect --ignore (1)' '\n+\n+\tif err=`git read-tree -m --ignore=.gitignore master side 2>&1`\n+\tthen\n+\t\techo should have complained\n+\t\tfalse\n+\telse\n+\t\techo \"happy to see $err\"\n+\tfi\n+'\n+\n+test_expect_success 'two-way with incorrect --ignore (2)' '\n+\n+\tif err=`git read-tree -m -u --ignore=foo --ignore=.gitignore master side 2>&1`\n+\tthen\n+\t\techo should have complained\n+\t\tfalse\n+\telse\n+\t\techo \"happy to see $err\"\n+\tfi\n+'\n+\n+test_expect_success 'two-way clobbering a ignored file' '\n+\n+\tgit read-tree -m -u --ignore=.gitignore master side\n+'\n+\n+rm -f .gitignore\n+\n # three-tree test\n \n-test_expect_success 'three-way not complaining' '\n+test_expect_success 'three-way not complaining on an untracked path in both' '\n \n-\trm -f file2 &&\n+\trm -f file2 subdir/file2 &&\n \tgit checkout side &&\n \techo >file3 file three &&\n-\tgit update-index --add file3 &&\n-\tgit commit -a -m \"side adds file3\" &&\n+\techo >subdir/file3 file three &&\n+\tgit update-index --add file3 subdir/file3 &&\n+\tgit commit -a -m \"side adds file3 and removes file2\" &&\n \n \tgit checkout master &&\n \techo >file2 file two is untracked on the master side &&\n+\techo >subdir/file2 file two is untracked on the master side &&\n \n \tgit-read-tree -m -u branch-point master side\n '\n \n+test_expect_success 'three-way not cloberring a working tree file' '\n+\n+\tgit reset --hard &&\n+\trm -f file2 subdir/file2 file3 subdir/file3 &&\n+\tgit checkout master &&\n+\techo >file3 file three created in master, untracked &&\n+\techo >subdir/file3 file three created in master, untracked &&\n+\tif err=`git read-tree -m -u branch-point master side 2>&1`\n+\tthen\n+\t\techo should have complained\n+\t\tfalse\n+\telse\n+\t\techo \"happy to see $err\"\n+\tfi\n+'\n+\n+echo >.gitignore file3\n+\n+test_expect_success 'three-way not complaining on an untracked file' '\n+\n+\tgit reset --hard &&\n+\trm -f file2 subdir/file2 file3 subdir/file3 &&\n+\tgit checkout master &&\n+\techo >file3 file three created in master, untracked &&\n+\techo >subdir/file3 file three created in master, untracked &&\n+\n+\tgit read-tree -m -u --ignore=.gitignore branch-point master side\n+'\n+\n test_done\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 7cfd628..79d21e2 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -1,6 +1,7 @@\n #include <signal.h>\n #include <sys/time.h>\n #include \"cache.h\"\n+#include \"dir.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"cache-tree.h\"\n@@ -77,6 +78,12 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,\n {\n \tint baselen = strlen(base);\n \tint src_size = len + 1;\n+\tint i_stk = i_stk;\n+\tint retval = 0;\n+\n+\tif (o->dir)\n+\t\ti_stk = push_exclude_per_directory(o->dir, base, strlen(base));\n+\n \tdo {\n \t\tint i;\n \t\tconst char *first;\n@@ -143,7 +150,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,\n \t\t}\n \t\t/* No name means we're done */\n \t\tif (!first)\n-\t\t\treturn 0;\n+\t\t\tgoto leave_directory;\n \n \t\tpathlen = strlen(first);\n \t\tce_size = cache_entry_size(baselen + pathlen);\n@@ -240,13 +247,20 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,\n \t\t\tnewbase[baselen + pathlen] = '/';\n \t\t\tnewbase[baselen + pathlen + 1] = '\\0';\n \t\t\tif (unpack_trees_rec(subposns, len, newbase, o,\n-\t\t\t\t\t     indpos, df_conflict_list))\n-\t\t\t\treturn -1;\n+\t\t\t\t\t     indpos, df_conflict_list)) {\n+\t\t\t\tretval = -1;\n+\t\t\t\tgoto leave_directory;\n+\t\t\t}\n \t\t\tfree(newbase);\n \t\t}\n \t\tfree(subposns);\n \t\tfree(src);\n \t} while (1);\n+\n+ leave_directory:\n+\tif (o->dir)\n+\t\tpop_exclude_per_directory(o->dir, i_stk);\n+\treturn retval;\n }\n \n /* Unlink the last component and attempt to remove leading\n@@ -456,7 +470,7 @@ static void invalidate_ce_path(struct cache_entry *ce)\n \n /*\n  * We do not want to remove or overwrite a working tree file that\n- * is not tracked.\n+ * is not tracked, unless it is ignored.\n  */\n static void verify_absent(const char *path, const char *action,\n \t\tstruct unpack_trees_options *o)\n@@ -465,7 +479,7 @@ static void verify_absent(const char *path, const char *action,\n \n \tif (o->index_only || o->reset || !o->update)\n \t\treturn;\n-\tif (!lstat(path, &st))\n+\tif (!lstat(path, &st) && !(o->dir && excluded(o->dir, path)))\n \t\tdie(\"Untracked working tree file '%s' \"\n \t\t    \"would be %s by merge.\", path, action);\n }\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex c460162..191f744 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -16,6 +16,7 @@ struct unpack_trees_options {\n \tint verbose_update;\n \tint aggressive;\n \tconst char *prefix;\n+\tstruct dir_struct *dir;\n \tmerge_fn_t fn;\n \n \tint head_idx;\n-- \n1.4.4.1.ga37e\n\n"},{"id":"298004","messageId":"el2hfe$oi0$2@sea.gmane.org","threadId":"43635","inReplyTo":"7vlklnkv39.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] read-tree: further loosen \"working file will be lost\" check.","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-05T01:21:50Z","receivedAt":"2006-12-05T01:21:50Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n> A new option \"--ignore=.gitignore\" can be used to tell the\n> \"git-read-tree\" command that the user does not mind losing\n> contents in untracked files in the working tree if it needs to\n> be overwritten by a merge (either a two-way \"switch branch\"\n> merge, or a three-way merge).\n[...]\n>  builtin-read-tree.c         |   20 ++++++++++\n>  dir.c                       |    4 +-\n>  dir.h                       |    3 ++\n>  t/t1004-read-tree-m-u-wf.sh |   82 ++++++++++++++++++++++++++++++++++++++----\n>  unpack-trees.c              |   24 ++++++++++---\n>  unpack-trees.h              |    1 +\n>  6 files changed, 119 insertions(+), 15 deletions(-)\n\nDocumentation, please?\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n\n"}]}