Home / Alt manpages / git-clean(1)

  • git-clean(1)
  • User command
  • linux

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.

  • Ordinary user command. No sudo needed when the repository and its files belong to you.
  • It is destructive. git clean removes 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 what git clean can 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:

  • -x ignores the repository's normal ignore rules and includes ignored files, while still applying any command-line -e exclusions. This is the broadest cleanup mode there is.
  • -X removes only files ignored by Git, and keeps anything untracked that you created by hand.
  • -e PATTERN adds 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 -n or -nd candidate 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 --short and a filesystem check confirm the result you wanted.