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

[PATCH v3 09/12] clean: disambiguate the definition of -d

From
Elijah Newren <newren@gmail.com>
Date
Sep 12, 2019, 22:12 UTC
Message-ID
<20190912221240.18057-10-newren@gmail.com>
In-Reply-To
<20190912221240.18057-1-newren@gmail.com>

The -d flag pre-dated git-clean's ability to have paths specified. As such, the default for git-clean was to only remove untracked files in the current directory, and -d existed to allow it to recurse into subdirectories.

The interaction of paths and the -d option appears to not have been carefully considered, as evidenced by numerous bugs and a dearth of tests covering such pairings in the testsuite. The definition turns out to be important, so let's look at some of the various ways one could interpret the -d option:

  A) Without -d, only look in subdirectories which contain tracked
     files under them; with -d, also look in subdirectories which
     are untracked for files to clean.
  B) Without specified paths from the user for us to delete, we need to
     have some kind of default, so...without -d, only look in
     subdirectories which contain tracked files under them; with -d,
     also look in subdirectories which are untracked for files to clean.

The important distinction here is that choice B says that the presence or absence of '-d' is irrelevant if paths are specified. The logic behind option B is that if a user explicitly asked us to clean a specified pathspec, then we should clean anything that matches that pathspec. Some examples may clarify. Should

   git clean -f untracked_dir/file

remove untracked_dir/file or not? It seems crazy not to, but a strict reading of option A says it shouldn't be removed. How about

   git clean -f untracked_dir/file1 tracked_dir/file2
or
   git clean -f untracked_dir_1/file1 untracked_dir_2/file2

? Should it remove either or both of these files? Should it require multiple runs to remove both the files listed? (If this sounds like a crazy question to even ask, see the commit message of "t7300: Add some testcases showing failure to clean specified pathspecs" added earlier in this patch series.) What if -ffd were used instead of -f -- should that allow these to be removed? Should it take multiple invocations with -ffd? What if a glob (such as '*tracked*') were used instead of spelling out the directory names? What if the filenames involved globs, such as

   git clean -f '*.o'
or
   git clean -f '*/*.o'
?

The current documentation actually suggests a definition that is slightly different than choice A, and the implementation prior to this series provided something radically different than either choices A or B. (The implementation, though, was clearly just buggy). There may be other choices as well. However, for almost any given choice of definition for -d that I can think of, some of the examples above will appear buggy to the user. The only case that doesn't have negative surprises is choice B: treat a user-specified path as a request to clean all untracked files which match that path specification, including recursing into any untracked directories.

Change the documentation and basic implementation to use this definition.

There were two regression tests that indirectly depended on the current implementation, but neither was about subdirectory handling. These two tests were introduced in commit 5b7570cfb41c ("git-clean: add tests for relative path", 2008-03-07) which was solely created to add coverage for the changes in commit fb328947c8e ("git-clean: correct printing relative path", 2008-03-07). Both tests specified a directory that happened to have an untracked subdirectory, but both were only checking that the resulting printout of a file that was removed was shown with a relative path. Update these tests appropriately.

Signed-off-by: Elijah Newren <newren@gmail.com>
---
 Documentation/git-clean.txt | 10 ++++++----
 builtin/clean.c             |  8 ++++++++
 t/t7300-clean.sh            |  2 ++
 3 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index e84ffc9396..3ab749b921 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -26,10 +26,12 @@ are affected.
 OPTIONS
 -------
 -d::
-	Remove untracked directories in addition to untracked files.
-	If an untracked directory is managed by a different Git
-	repository, it is not removed by default.  Use -f option twice
-	if you really want to remove such a directory.
+	Normally, when no <path> is specified, git clean will not
+	recurse into untracked directories to avoid removing too much.
+	Specify -d to have it recurse into such directories as well.
+	If any paths are specified, -d is irrelevant; all untracked
+	files matching the specified paths (with exceptions for nested
+	git directories mentioned under `--force`) will be removed.
 
 -f::
 --force::
diff --git a/builtin/clean.c b/builtin/clean.c
index d5579da716..68d70e41c0 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -949,6 +949,14 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 
 	dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
 
+	if (argc) {
+		/*
+		 * Remaining args implies pathspecs specified, and we should
+		 * recurse within those.
+		 */
+		remove_directories = 1;
+	}
+
 	if (remove_directories)
 		dir.flags |= DIR_SHOW_IGNORED_TOO | DIR_KEEP_UNTRACKED_CONTENTS;
 
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index d83aeb7dc2..530dfdab34 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -117,6 +117,7 @@ test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '
 	would_clean=$(
 		cd docs &&
 		git clean -n ../src |
+		grep part3 |
 		sed -n -e "s|^Would remove ||p"
 	) &&
 	verbose test "$would_clean" = ../src/part3.c
@@ -129,6 +130,7 @@ test_expect_success C_LOCALE_OUTPUT 'git clean with absolute path' '
 	would_clean=$(
 		cd docs &&
 		git clean -n "$(pwd)/../src" |
+		grep part3 |
 		sed -n -e "s|^Would remove ||p"
 	) &&
 	verbose test "$would_clean" = ../src/part3.c
-- 
2.23.0.173.gad11b3a635.dirty
Previous: Junio C HamanoNext: Elijah Newren
Message 37 of 73 in “t7300-clean: demonstrate deleting nested repo with an ignored file breakage”
  1. t7300-clean: demonstrate deleting nested repo with an ignored file breakageSZEDER Gábor, Aug 25, 2019
  2. SZEDER GáborAug 25, 2019
  3. Philip OakleyAug 25, 2019
  4. SZEDER GáborAug 26, 2019
  5. 00/12 Fix some git clean issuesElijah Newren, Sep 5, 2019
  6. 01/12 t7300: Add some testcases showing failure to clean specified pathspecsElijah Newren, Sep 5, 2019
  7. 02/12 dir: fix typo in commentElijah Newren, Sep 5, 2019
  8. 04/12 dir: Directories should be checked for matching pathspecs tooElijah Newren, Sep 5, 2019
  9. 03/12 dir: fix off-by-one error in match_pathspec_itemElijah Newren, Sep 5, 2019
  10. 05/12 dir: Make the DO_MATCH_SUBMODULE code reusable for a non-submodule caseElijah Newren, Sep 5, 2019
  11. 07/12 dir: add commentary explaining match_pathspec_item's return valueElijah Newren, Sep 5, 2019
  12. 06/12 dir: If our pathspec might match files under a dir, recurse into itElijah Newren, Sep 5, 2019
  13. 10/12 clean: avoid removing untracked files in a nested git repositoryElijah Newren, Sep 5, 2019
  14. SZEDER GáborSep 5, 2019
  15. 09/12 clean: disambiguate the definition of -dElijah Newren, Sep 5, 2019
  16. 11/12 clean: rewrap overly long lineElijah Newren, Sep 5, 2019
  17. 12/12 clean: fix theoretical path corruptionElijah Newren, Sep 5, 2019
  18. SZEDER GáborSep 5, 2019
  19. Elijah NewrenSep 7, 2019
  20. 08/12 git-clean.txt: do not claim we will delete files with -n/--dry-runElijah Newren, Sep 5, 2019
  21. SZEDER GáborSep 5, 2019
  22. Elijah NewrenSep 7, 2019
  23. 00/12 Fix some git clean issuesElijah Newren, Sep 12, 2019
  24. 01/12 t7300: add testcases showing failure to clean specified pathspecsElijah Newren, Sep 12, 2019
  25. Junio C HamanoSep 13, 2019
  26. Elijah NewrenSep 13, 2019
  27. Junio C HamanoSep 13, 2019
  28. 03/12 dir: fix off-by-one error in match_pathspec_itemElijah Newren, Sep 12, 2019
  29. Junio C HamanoSep 13, 2019
  30. 02/12 dir: fix typo in commentElijah Newren, Sep 12, 2019
  31. 05/12 dir: make the DO_MATCH_SUBMODULE code reusable for a non-submodule caseElijah Newren, Sep 12, 2019
  32. 04/12 dir: also check directories for matching pathspecsElijah Newren, Sep 12, 2019
  33. 06/12 dir: if our pathspec might match files under a dir, recurse into itElijah Newren, Sep 12, 2019
  34. Junio C HamanoSep 13, 2019
  35. 07/12 dir: add commentary explaining match_pathspec_item's return valueElijah Newren, Sep 12, 2019
  36. Junio C HamanoSep 13, 2019
  37. 09/12 clean: disambiguate the definition of -dElijah Newren, Sep 12, 2019
  38. 10/12 clean: avoid removing untracked files in a nested git repositoryElijah Newren, Sep 12, 2019
  39. 11/12 clean: rewrap overly long lineElijah Newren, Sep 12, 2019
  40. 08/12 git-clean.txt: do not claim we will delete files with -n/--dry-runElijah Newren, Sep 12, 2019
  41. 12/12 clean: fix theoretical path corruptionElijah Newren, Sep 12, 2019
  42. 00/12 Fix some git clean issuesElijah Newren, Sep 17, 2019
  43. 02/12 dir: fix typo in commentElijah Newren, Sep 17, 2019
  44. 01/12 t7300: add testcases showing failure to clean specified pathspecsElijah Newren, Sep 17, 2019
  45. 03/12 dir: fix off-by-one error in match_pathspec_itemElijah Newren, Sep 17, 2019
  46. 04/12 dir: also check directories for matching pathspecsElijah Newren, Sep 17, 2019
  47. [BUG] git is segfaulting, was [PATCH v4 04/12] dir: also check directories for matching pathspecsDenton Liu, Sep 25, 2019
  48. Elijah NewrenSep 25, 2019
  49. Denton LiuSep 25, 2019
  50. Denton LiuSep 26, 2019
  51. Elijah NewrenSep 27, 2019
  52. SZEDER GáborSep 27, 2019
  53. SZEDER GáborSep 27, 2019
  54. Denton LiuSep 27, 2019
  55. dir: special case check for the possibility that pathspec is NULLElijah Newren, Sep 30, 2019
  56. Denton LiuSep 30, 2019
  57. Elijah NewrenOct 1, 2019
  58. dir: special case check for the possibility that pathspec is NULLElijah Newren, Oct 1, 2019
  59. Denton LiuOct 1, 2019
  60. Elijah NewrenOct 1, 2019
  61. dir: special case check for the possibility that pathspec is NULLElijah Newren, Oct 1, 2019
  62. Denton LiuOct 1, 2019
  63. Elijah NewrenOct 1, 2019
  64. Elijah NewrenOct 2, 2019
  65. SZEDER GáborOct 7, 2019
  66. 08/12 git-clean.txt: do not claim we will delete files with -n/--dry-runElijah Newren, Sep 17, 2019
  67. 11/12 clean: rewrap overly long lineElijah Newren, Sep 17, 2019
  68. 12/12 clean: fix theoretical path corruptionElijah Newren, Sep 17, 2019
  69. 09/12 clean: disambiguate the definition of -dElijah Newren, Sep 17, 2019
  70. 10/12 clean: avoid removing untracked files in a nested git repositoryElijah Newren, Sep 17, 2019
  71. 07/12 dir: add commentary explaining match_pathspec_item's return valueElijah Newren, Sep 17, 2019
  72. 06/12 dir: if our pathspec might match files under a dir, recurse into itElijah Newren, Sep 17, 2019
  73. 05/12 dir: make the DO_MATCH_SUBMODULE code reusable for a non-submodule caseElijah Newren, Sep 17, 2019

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.