Remove Untracked Files Safely with git clean
Half your working tree is build output and scratch, and you want it gone without wiping something you needed: that is what git clean is for. This guide gets you a repeatable way to remove disposable files without guessing what the command will delete. The examples use Git 2.43.0, matching the installed git-clean(1) manual on this machine. Allow about ten minutes, plus time to read the dry-run list carefully.
The route
Jump straight to the step you need, or tick off Done means at the end.
- Ordinary user command. No
sudoneeded when the repository and its files belong to you. - It is destructive.
git cleanremoves files from the working tree, and those files are not recoverable from Git because Git never tracked them. Keep anything you may need before you continue.
1. Check the repository before cleaning
Change to the repository you intend to clean, then look at its state. Nothing here changes anything:
$ cd /path/to/repository
$ git --version
git version 2.43.0
$ git status --short
- Entries beginning with
??are untracked. Those are whatgit cleancan remove. - Modified or staged tracked files are not the target. A clean working tree is not the same thing as a safe deletion list.
- An untracked directory can hide several files behind one condensed status line.
Checkpoint
You know the repository path, the installed Git version, and which untracked files are actually disposable.
2. Preview ordinary untracked files
Start with a dry run. -n prints what would be removed and removes nothing:
$ git clean -n
Would remove .cache-marker
Your names will differ. By default, ignored files are left alone, and untracked directories are not entered when you supply no pathspec. That conservative default is exactly why a plain dry run can show one file but skip an entire directory of other disposable files sitting right next to it.
Tip
Do not add -f until you understand every line in the dry-run output. If the list includes a file you need, stop and narrow the scope or add an exclusion instead.
3. Include untracked directories deliberately
Add -d when you want the command to recurse into untracked directories:
$ git clean -nd
Would remove .cache-marker
Would remove build/
Would remove scratch/
-n still means preview; -d only widens the candidate list. A supplied pathspec makes -d beside the point, since files matching that pathspec are considered regardless, including files below a directory. Prefer a narrow pathspec once you already know the disposable area:
$ git clean -nd -- build/
Would remove build/
The trailing -- separates options from paths. It earns its keep when a path starts with a dash, or when you want the command shape to read unambiguously in a script.
4. Decide how ignored files should be treated
Build products are often ignored, so they will not show up in a normal dry run. Pick one of these modes and preview it first:
-xignores the repository's normal ignore rules and includes ignored files, while still applying any command-line-eexclusions. This is the broadest cleanup mode there is.-Xremoves only files ignored by Git, and keeps anything untracked that you created by hand.-e PATTERNadds an exclusion for this run. For example,git clean -ndx -e '*.local'previews an ignored-inclusive cleanup while keeping matching local files.
$ git clean -ndx
Would remove build/
Would remove .env.local
$ git clean -ndX
Would remove build/
Would remove .env.local
The exact lists depend on .gitignore and your global excludes. Treat -x as high-risk: it can sweep up generated credentials, local configuration and downloaded data. Read the list rather than assuming "ignored" means "safe to lose".
5. Perform a reviewed cleanup
Once the matching dry run looks right, add -f. Git demands force or interactive mode by default, because deletion here is irreversible:
$ git clean -fd -- build/
Removing build/
$ git status --short
Use the same scope, ignored-file mode and pathspec you just reviewed, for example git clean -fdx -- build/ for a reviewed cleanup of ignored build output. Do not turn a successful dry run into something wider by dropping the pathspec or adding -x at the last moment.
Warning
A nested untracked Git repository is an extra boundary. Git refuses to touch one unless you pass a second -f, as in git clean -ffdx -- vendor-copy/. That command can destroy an entire nested repository, and there is no Git undo for the files it removes. Use it only after checking the nested directory on its own.
6. Use interactive mode for a mixed directory
When the candidate list mixes disposable and useful files, reach for -i instead of forcing a broad deletion:
$ git clean -i -d
Would remove the following items:
build/
notes.txt
*** Commands ***
1: clean 2: filter by pattern
3: select by numbers 4: ask each
5: quit 6: help
What now>
- Choose 3 to select particular numbered entries.
- Choose 4 to confirm items one at a time.
- Choose 5 to quit without cleaning anything.
Interactive mode still ends in deletion once you confirm it: it just shows you the candidate list first. Quit if that list surprises you.
7. Verify the result and recover sensibly
After cleaning, check both the status and the filesystem:
$ git status --short
$ test ! -e build/ && echo 'build directory removed'
build directory removed
Tracked files and their history are untouched by any of this.
Recovery
Files removed by git clean cannot be brought back with git restore or git reset, since those commands only operate on content Git already knows about. Getting them back needs an independent copy, a filesystem snapshot or an undelete tool, and none of those are guaranteed to work. If you have not yet run the force command, recovery is simple: quit and rerun the dry run with a narrower pathspec or an exclusion.
Done means
- Checked the repository and Git version before changing anything.
- Reviewed the exact
git clean -nor-ndcandidate list, line by line. - Chose ordinary, ignored-only or ignored-inclusive cleanup on purpose.
- Used a pathspec or exclusion instead of a whole-repository sweep where you could.
- Understood that
-f, especially repeated-f, permanently removes untracked data. git status --shortand a filesystem check confirm the result you wanted.