git/Documentation/git-clean.txt

143 lines
4.3 KiB
Plaintext
Raw Normal View History

git-clean(1)
============
NAME
----
git-clean - Remove untracked files from the working tree
SYNOPSIS
--------
[verse]
'git clean' [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
DESCRIPTION
-----------
Cleans the working tree by recursively removing files that are not
under version control, starting from the current directory.
Normally, only files unknown to Git are removed, but if the `-x`
option is specified, ignored files are also removed. This can, for
example, be useful to remove all build products.
If any optional `<path>...` arguments are given, only those paths
are affected.
OPTIONS
-------
-d::
clean: disambiguate the definition of -d 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> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-09-17 16:35:01 +00:00
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::
If the Git configuration variable clean.requireForce is not set
to false, 'git clean' will refuse to delete files or directories
clean: avoid removing untracked files in a nested git repository Users expect files in a nested git repository to be left alone unless sufficiently forced (with two -f's). Unfortunately, in certain circumstances, git would delete both tracked (and possibly dirty) files and untracked files within a nested repository. To explain how this happens, let's contrast a couple cases. First, take the following example setup (which assumes we are already within a git repo): git init nested cd nested >tracked git add tracked git commit -m init >untracked cd .. In this setup, everything works as expected; running 'git clean -fd' will result in fill_directory() returning the following paths: nested/ nested/tracked nested/untracked and then correct_untracked_entries() would notice this can be compressed to nested/ and then since "nested/" is a directory, we would call remove_dirs("nested/", ...), which would check is_nonbare_repository_dir() and then decide to skip it. However, if someone also creates an ignored file: >nested/ignored then running 'git clean -fd' would result in fill_directory() returning the same paths: nested/ nested/tracked nested/untracked but correct_untracked_entries() will notice that we had ignored entries under nested/ and thus simplify this list to nested/tracked nested/untracked Since these are not directories, we do not call remove_dirs() which was the only place that had the is_nonbare_repository_dir() safety check -- resulting in us deleting both the untracked file and the tracked (and possibly dirty) file. One possible fix for this issue would be walking the parent directories of each path and checking if they represent nonbare repositories, but that would be wasteful. Even if we added caching of some sort, it's still a waste because we should have been able to check that "nested/" represented a nonbare repository before even descending into it in the first place. Add a DIR_SKIP_NESTED_GIT flag to dir_struct.flags and use it to prevent fill_directory() and friends from descending into nested git repos. With this change, we also modify two regression tests added in commit 91479b9c72f1 ("t7300: add tests to document behavior of clean and nested git", 2015-06-15). That commit, nor its series, nor the six previous iterations of that series on the mailing list discussed why those tests coded the expectation they did. In fact, it appears their purpose was simply to test _existing_ behavior to make sure that the performance changes didn't change the behavior. However, these two tests directly contradicted the manpage's claims that two -f's were required to delete files/directories under a nested git repository. While one could argue that the user gave an explicit path which matched files/directories that were within a nested repository, there's a slippery slope that becomes very difficult for users to understand once you go down that route (e.g. what if they specified "git clean -f -d '*.c'"?) It would also be hard to explain what the exact behavior was; avoid such problems by making it really simple. Also, clean up some grammar errors describing this functionality in the git-clean manpage. Finally, there are still a couple bugs with -ffd not cleaning out enough (e.g. missing the nested .git) and with -ffdX possibly cleaning out the wrong files (paying attention to outer .gitignore instead of inner). This patch does not address these cases at all (and does not change the behavior relative to those flags), it only fixes the handling when given a single -f. See https://public-inbox.org/git/20190905212043.GC32087@szeder.dev/ for more discussion of the -ffd[X?] bugs. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-09-17 16:35:02 +00:00
unless given -f or -i. Git will refuse to modify untracked
nested git repositories (directories with a .git subdirectory)
unless a second -f is given.
-i::
--interactive::
Show what would be done and clean files interactively. See
``Interactive mode'' for details.
-n::
--dry-run::
Don't actually remove anything, just show what would be done.
-q::
--quiet::
Be quiet, only report errors, but not the files that are
successfully removed.
-e <pattern>::
--exclude=<pattern>::
Use the given exclude pattern in addition to the standard ignore rules
(see linkgit:gitignore[5]).
-x::
Don't use the standard ignore rules (see linkgit:gitignore[5]), but
still use the ignore rules given with `-e` options from the command
line. This allows removing all untracked
files, including build products. This can be used (possibly in
conjunction with 'git restore' or 'git reset') to create a pristine
working directory to test a clean build.
-X::
Remove only files ignored by Git. This may be useful to rebuild
everything from scratch, but keep manually created files.
Interactive mode
----------------
When the command enters the interactive mode, it shows the
files and directories to be cleaned, and goes into its
interactive command loop.
The command loop shows the list of subcommands available, and
gives a prompt "What now> ". In general, when the prompt ends
with a single '>', you can pick only one of the choices given
and type return, like this:
------------
*** Commands ***
1: clean 2: filter by pattern 3: select by numbers
4: ask each 5: quit 6: help
What now> 1
------------
You also could say `c` or `clean` above as long as the choice is unique.
The main command loop has 6 subcommands.
clean::
Start cleaning files and directories, and then quit.
filter by pattern::
This shows the files and directories to be deleted and issues an
"Input ignore patterns>>" prompt. You can input space-separated
patterns to exclude files and directories from deletion.
E.g. "*.c *.h" will excludes files end with ".c" and ".h" from
deletion. When you are satisfied with the filtered result, press
ENTER (empty) back to the main menu.
select by numbers::
This shows the files and directories to be deleted and issues an
"Select items to delete>>" prompt. When the prompt ends with double
'>>' like this, you can make more than one selection, concatenated
with whitespace or comma. Also you can say ranges. E.g. "2-5 7,9"
to choose 2,3,4,5,7,9 from the list. If the second number in a
range is omitted, all remaining items are selected. E.g. "7-" to
choose 7,8,9 from the list. You can say '*' to choose everything.
Also when you are satisfied with the filtered result, press ENTER
(empty) back to the main menu.
ask each::
This will start to clean, and you must confirm one by one in order
to delete items. Please note that this action is not as efficient
as the above two actions.
quit::
This lets you quit without do cleaning.
help::
Show brief usage of interactive git-clean.
SEE ALSO
--------
linkgit:gitignore[5]
GIT
---
Part of the linkgit:git[1] suite