git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v3 03/11] read-tree, merge-recursive: overwrite ignored files by default

From
Elijah Newren via GitGitGadget <gitgitgadget@gmail.com>
Date
Sep 27, 2021, 16:33 UTC
Message-ID
<2501a0c552ad5147f61a96b9ebe45c5199e1dbfd.1632760428.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.1036.v3.git.1632760428.gitgitgadget@gmail.com>
From: Elijah Newren <newren@gmail.com>

This fixes a long-standing patchwork of ignored files handling in read-tree and merge-recursive, called out and suggested by Junio long ago. Quoting from commit dcf0c16ef1 ("core.excludesfile clean-up" 2007-11-16):

    git-read-tree takes --exclude-per-directory=<gitignore>,
    not because the flexibility was needed.  Again, this was
    because the option predates the standardization of the ignore
    files.
    ...
    On the other hand, I think it makes perfect sense to fix
    git-read-tree, git-merge-recursive and git-clean to follow the
    same rule as other commands.  I do not think of a valid use case
    to give an exclude-per-directory that is nonstandard to
    read-tree command, outside a "negative" test in the t1004 test
    script.
    This patch is the first step to untangle this mess.
    The next step would be to teach read-tree, merge-recursive and
    clean (in C) to use setup_standard_excludes().
History shows each of these were partially or fully fixed:
  * clean was taught the new trick in 1617adc7a0 ("Teach git clean to
    use setup_standard_excludes()", 2007-11-14).
  * read-tree was primarily used by checkout & merge scripts.  checkout
    and merge later became builtins and were both fixed to use the new
    setup_standard_excludes() handling in fc001b526c ("checkout,merge:
    loosen overwriting untracked file check based on info/exclude",
    2011-11-27).  So the primary users were fixed, though read-tree
    itself was not.
  * merge-recursive has now been replaced as the default merge backend
    by merge-ort.  merge-ort fixed this by using
    setup_standard_excludes() starting early in its implementation; see
    commit 6681ce5cf6 ("merge-ort: add implementation of checkout()",
    2020-12-13), largely due to its design depending on checkout() and
    thus being influenced by the checkout code.  However,
    merge-recursive itself was not fixed here, in part because its
    design meant it had difficulty differentiating between untracked
    files, ignored files, leftover tracked files that haven't been
    removed yet due to order of processing files, and files written by
    itself due to collisions).

Make the conversion more complete by now handling read-tree and handling at least the unpack_trees() portion of merge-recursive. While merge-recursive is on its way out, fixing the unpack_trees() portion is easy and facilitates some of the later changes in this series. Note that fixing read-tree makes the --exclude-per-directory option to read-tree useless, so we remove it from the documentation (though we continue to accept it if passed).

The read-tree changes happen to fix a bug in t1013.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
 Documentation/git-read-tree.txt | 18 +-----------------
 builtin/read-tree.c             | 25 ++++++++++---------------
 merge-recursive.c               | 11 ++++++++++-
 t/t1013-read-tree-submodule.sh  |  1 -
 4 files changed, 21 insertions(+), 34 deletions(-)
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 5fa8bab64c2..0222a27c5af 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -10,8 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
-		[-u [--exclude-per-directory=<gitignore>] | -i]]
-		[--index-output=<file>] [--no-sparse-checkout]
+		[-u | -i]] [--index-output=<file>] [--no-sparse-checkout]
 		(--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])
 
 
@@ -88,21 +87,6 @@ OPTIONS
 	The command will refuse to overwrite entries that already
 	existed in the original index file.
 
---exclude-per-directory=<gitignore>::
-	When running the command with `-u` and `-m` options, the
-	merge result may need to overwrite paths that are not
-	tracked in the current branch.  The command usually
-	refuses to proceed with the merge to avoid losing such a
-	path.  However this safety valve sometimes gets in the
-	way.  For example, it often happens that the other
-	branch added a file that used to be a generated file in
-	your branch, and the safety valve triggers when you try
-	to switch to that branch after you ran `make` but before
-	running `make clean` to remove the generated file.  This
-	option tells the command to read per-directory exclude
-	file (usually '.gitignore') and allows such an untracked
-	but explicitly ignored file to be overwritten.
-
 --index-output=<file>::
 	Instead of writing the results out to `$GIT_INDEX_FILE`,
 	write the resulting index in the named file.  While the
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 96102c222bf..73cb957a69b 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -38,7 +38,7 @@ static int list_tree(struct object_id *oid)
 }
 
 static const char * const read_tree_usage[] = {
-	N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
+	N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
 	NULL
 };
 
@@ -53,24 +53,16 @@ static int index_output_cb(const struct option *opt, const char *arg,
 static int exclude_per_directory_cb(const struct option *opt, const char *arg,
 				    int unset)
 {
-	struct dir_struct *dir;
 	struct unpack_trees_options *opts;
 
 	BUG_ON_OPT_NEG(unset);
 
 	opts = (struct unpack_trees_options *)opt->value;
 
-	if (opts->dir)
-		die("more than one --exclude-per-directory given.");
-
-	dir = xcalloc(1, sizeof(*opts->dir));
-	dir->flags |= DIR_SHOW_IGNORED;
-	dir->exclude_per_dir = arg;
-	opts->dir = dir;
-	/* We do not need to nor want to do read-directory
-	 * here; we are merely interested in reusing the
-	 * per directory ignore stack mechanism.
-	 */
+	if (!opts->update)
+		die("--exclude-per-directory is meaningless unless -u");
+	if (strcmp(arg, ".gitignore"))
+		die("--exclude-per-directory argument must be .gitignore");
 	return 0;
 }
 
@@ -209,8 +201,11 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
 	if ((opts.update || opts.index_only) && !opts.merge)
 		die("%s is meaningless without -m, --reset, or --prefix",
 		    opts.update ? "-u" : "-i");
-	if ((opts.dir && !opts.update))
-		die("--exclude-per-directory is meaningless unless -u");
+	if (opts.update && !opts.reset) {
+		CALLOC_ARRAY(opts.dir, 1);
+		opts.dir->flags |= DIR_SHOW_IGNORED;
+		setup_standard_excludes(opts.dir);
+	}
 	if (opts.merge && !opts.index_only)
 		setup_work_tree();
 
diff --git a/merge-recursive.c b/merge-recursive.c
index e594d4c3fa1..233d9f686ad 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -408,8 +408,13 @@ static int unpack_trees_start(struct merge_options *opt,
 	memset(&opt->priv->unpack_opts, 0, sizeof(opt->priv->unpack_opts));
 	if (opt->priv->call_depth)
 		opt->priv->unpack_opts.index_only = 1;
-	else
+	else {
 		opt->priv->unpack_opts.update = 1;
+		/* FIXME: should only do this if !overwrite_ignore */
+		CALLOC_ARRAY(opt->priv->unpack_opts.dir, 1);
+		opt->priv->unpack_opts.dir->flags |= DIR_SHOW_IGNORED;
+		setup_standard_excludes(opt->priv->unpack_opts.dir);
+	}
 	opt->priv->unpack_opts.merge = 1;
 	opt->priv->unpack_opts.head_idx = 2;
 	opt->priv->unpack_opts.fn = threeway_merge;
@@ -423,6 +428,10 @@ static int unpack_trees_start(struct merge_options *opt,
 	init_tree_desc_from_tree(t+2, merge);
 
 	rc = unpack_trees(3, t, &opt->priv->unpack_opts);
+	if (opt->priv->unpack_opts.dir) {
+		dir_clear(opt->priv->unpack_opts.dir);
+		FREE_AND_NULL(opt->priv->unpack_opts.dir);
+	}
 	cache_tree_free(&opt->repo->index->cache_tree);
 
 	/*
diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh
index b6df7444c05..bfc90d4cf27 100755
--- a/t/t1013-read-tree-submodule.sh
+++ b/t/t1013-read-tree-submodule.sh
@@ -6,7 +6,6 @@ test_description='read-tree can handle submodules'
 . "$TEST_DIRECTORY"/lib-submodule-update.sh
 
 KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1
-KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1
 
 test_submodule_switch_recursing_with_args "read-tree -u -m"
 
-- 
gitgitgadget
Previous: Elijah Newren via GitGitGadgetNext: Jack O'Connor
Message 36 of 82 in “Fix various issues around removal of untracked files/directories”
  1. 0/6 Fix various issues around removal of untracked files/directoriesElijah Newren via GitGitGadget, Sep 18, 2021
  2. 1/6 t2500: add various tests for nuking untracked filesElijah Newren via GitGitGadget, Sep 18, 2021
  3. Ævar Arnfjörð BjarmasonSep 19, 2021
  4. Elijah NewrenSep 20, 2021
  5. 4/6 unpack-trees: avoid nuking untracked dir in way of locally deleted fileElijah Newren via GitGitGadget, Sep 18, 2021
  6. Ævar Arnfjörð BjarmasonSep 19, 2021
  7. Elijah NewrenSep 20, 2021
  8. 3/6 unpack-trees: avoid nuking untracked dir in way of unmerged fileElijah Newren via GitGitGadget, Sep 18, 2021
  9. 2/6 Split unpack_trees 'reset' flag into two for untracked handlingElijah Newren via GitGitGadget, Sep 18, 2021
  10. Ævar Arnfjörð BjarmasonSep 19, 2021
  11. Elijah NewrenSep 20, 2021
  12. Phillip WoodSep 20, 2021
  13. Elijah NewrenSep 20, 2021
  14. Phillip WoodSep 20, 2021
  15. Elijah NewrenSep 24, 2021
  16. 6/6 Documentation: call out commands that nuke untracked files/directoriesElijah Newren via GitGitGadget, Sep 18, 2021
  17. Philip OakleySep 19, 2021
  18. Philip OakleySep 19, 2021
  19. Elijah NewrenSep 20, 2021
  20. 5/6 Comment important codepaths regarding nuking untracked files/dirsElijah Newren via GitGitGadget, Sep 18, 2021
  21. Luke DiamandSep 24, 2021
  22. Elijah NewrenSep 24, 2021
  23. 0/6 Fix various issues around removal of untracked files/directoriesElijah Newren via GitGitGadget, Sep 24, 2021
  24. 2/6 Change unpack_trees' 'reset' flag into an enumElijah Newren via GitGitGadget, Sep 24, 2021
  25. Junio C HamanoSep 24, 2021
  26. Elijah NewrenSep 26, 2021
  27. 1/6 t2500: add various tests for nuking untracked filesElijah Newren via GitGitGadget, Sep 24, 2021
  28. 3/6 unpack-trees: avoid nuking untracked dir in way of unmerged fileElijah Newren via GitGitGadget, Sep 24, 2021
  29. 4/6 unpack-trees: avoid nuking untracked dir in way of locally deleted fileElijah Newren via GitGitGadget, Sep 24, 2021
  30. 5/6 Comment important codepaths regarding nuking untracked files/dirsElijah Newren via GitGitGadget, Sep 24, 2021
  31. Eric SunshineSep 24, 2021
  32. Elijah NewrenSep 26, 2021
  33. 6/6 Documentation: call out commands that nuke untracked files/directoriesElijah Newren via GitGitGadget, Sep 24, 2021
  34. 00/11 Fix various issues around removal of untracked files/directoriesElijah Newren via GitGitGadget, Sep 27, 2021
  35. 02/11 checkout, read-tree: fix leak of unpack_trees_options.dirElijah Newren via GitGitGadget, Sep 27, 2021
  36. 03/11 read-tree, merge-recursive: overwrite ignored files by defaultElijah Newren via GitGitGadget, Sep 27, 2021
  37. Jack O'ConnorDec 13, 2021
  38. Elijah NewrenDec 13, 2021
  39. 01/11 t2500: add various tests for nuking untracked filesElijah Newren via GitGitGadget, Sep 27, 2021
  40. 06/11 Remove ignored files by default when they are in the wayElijah Newren via GitGitGadget, Sep 27, 2021
  41. 05/11 unpack-trees: make dir an internal-only structElijah Newren via GitGitGadget, Sep 27, 2021
  42. 04/11 unpack-trees: introduce preserve_ignored to unpack_trees_optionsElijah Newren via GitGitGadget, Sep 27, 2021
  43. Ævar Arnfjörð BjarmasonSep 29, 2021
  44. Elijah NewrenSep 29, 2021
  45. Ævar Arnfjörð BjarmasonSep 29, 2021
  46. Elijah NewrenSep 30, 2021
  47. Ævar Arnfjörð BjarmasonSep 30, 2021
  48. Elijah NewrenOct 1, 2021
  49. Ævar Arnfjörð BjarmasonOct 1, 2021
  50. Ævar Arnfjörð BjarmasonOct 1, 2021
  51. Elijah NewrenOct 1, 2021
  52. Ævar Arnfjörð BjarmasonOct 2, 2021
  53. Ævar Arnfjörð BjarmasonOct 3, 2021
  54. Elijah NewrenOct 4, 2021
  55. Elijah NewrenOct 4, 2021
  56. Ævar Arnfjörð BjarmasonOct 4, 2021
  57. Elijah NewrenOct 4, 2021
  58. 07/11 Change unpack_trees' 'reset' flag into an enumElijah Newren via GitGitGadget, Sep 27, 2021
  59. 08/11 unpack-trees: avoid nuking untracked dir in way of unmerged fileElijah Newren via GitGitGadget, Sep 27, 2021
  60. 09/11 unpack-trees: avoid nuking untracked dir in way of locally deleted fileElijah Newren via GitGitGadget, Sep 27, 2021
  61. 10/11 Comment important codepaths regarding nuking untracked files/dirsElijah Newren via GitGitGadget, Sep 27, 2021
  62. 11/11 Documentation: call out commands that nuke untracked files/directoriesElijah Newren via GitGitGadget, Sep 27, 2021
  63. Junio C HamanoSep 27, 2021
  64. Elijah NewrenSep 27, 2021
  65. Elijah NewrenSep 27, 2021
  66. Phillip WoodSep 30, 2021
  67. 00/10 Fix various issues around removal of untracked files/directoriesÆvar Arnfjörð Bjarmason, Oct 4, 2021
  68. 01/10 t2500: add various tests for nuking untracked filesÆvar Arnfjörð Bjarmason, Oct 4, 2021
  69. 02/10 read-tree, merge-recursive: overwrite ignored files by defaultÆvar Arnfjörð Bjarmason, Oct 4, 2021
  70. 03/10 unpack-trees: introduce preserve_ignored to unpack_trees_optionsÆvar Arnfjörð Bjarmason, Oct 4, 2021
  71. 04/10 unpack-trees: rename "dir" to "private_dir"Ævar Arnfjörð Bjarmason, Oct 4, 2021
  72. 05/10 Remove ignored files by default when they are in the wayÆvar Arnfjörð Bjarmason, Oct 4, 2021
  73. 06/10 Change unpack_trees' 'reset' flag into an enumÆvar Arnfjörð Bjarmason, Oct 4, 2021
  74. 07/10 unpack-trees: avoid nuking untracked dir in way of unmerged fileÆvar Arnfjörð Bjarmason, Oct 4, 2021
  75. 08/10 unpack-trees: avoid nuking untracked dir in way of locally deleted fileÆvar Arnfjörð Bjarmason, Oct 4, 2021
  76. 09/10 Comment important codepaths regarding nuking untracked files/dirsÆvar Arnfjörð Bjarmason, Oct 4, 2021
  77. 10/10 Documentation: call out commands that nuke untracked files/directoriesÆvar Arnfjörð Bjarmason, Oct 4, 2021
  78. Elijah NewrenOct 4, 2021
  79. Ævar Arnfjörð BjarmasonOct 4, 2021
  80. Elijah NewrenOct 5, 2021
  81. Junio C HamanoOct 4, 2021
  82. Elijah NewrenOct 1, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.