Back to blog

Git Remove Untracked Files: Safe git clean Guide

9 September 2026

TL;DR: to remove untracked files in git, run git clean -fd — but always preview with git clean -nd first, because clean deletes permanently and they never hit the trash. Use -f for files, -fd for files and directories, and -fdx if gitignored build output should go too. The single most common complaint — “git clean is not removing my untracked files” — almost always means the files live inside an untracked directory (add -d) or they are ignored files (add -x). Untracked clutter is the normal byproduct of experiments, builds and clone-adjacent scripts; this guide shows how to preview every deletion, remove files from one directory only, and which combinations to never run in a repo you care about.

What does “untracked files” mean in git status?

Untracked means git sees the file on disk but has never been told to track it — it is not in the index and has no commit history. git status groups everything into three buckets:

$ git status --short
 M src/app.ts        # modified: tracked, changed
?? notes.txt         # untracked: new file git doesn't know
?? build/            # untracked directory: entirely new to git

That distinction matters because each bucket needs a different removal tool. Tracked-but-changed files are reverted with git restore or committed away — git clean will not touch them. Only the ?? lines are git clean territory. Ignored files (anything matched by .gitignore) are a hidden fourth bucket: they do not even show as ??, and git clean skips them unless you explicitly opt in with -x.

If your real problem is a tracked file that should never have been committed, cleaning is the wrong tool — that is a git rm --cached job, or a reset of the last commit as covered in git undo last commit: keep the changes.

How do I remove untracked files in git?

The core command is git clean -f. Without -f git refuses to delete anything and just prints a warning — a deliberate safety rail. The full routine looks like this:

# 1. See exactly what will be deleted (dry run — deletes nothing)
git clean -nd

# Would remove:
# notes.txt
# build/
# scratch/

# 2. Confirm nothing precious is in the list, then delete for real
git clean -fd

Flag by flag:

  • -f / --force — required. Actually deletes untracked files.
  • -d — recurse into untracked directories. Plain -f only removes untracked files at the top level and reports the directories it refused to touch.
  • -n / --dry-run — show what would be removed. Always run this first.
  • -x — also delete ignored files (node_modules, build output, .env).
  • -X — delete only ignored files, keeping untracked-but-not-ignored ones.
  • -i — interactive mode; useful when the dry-run list is long.

A habit worth copying: treat git clean -nd like git diff — you look before you commit, you look before you clean.

Why is git clean not removing my untracked files?

Three real causes, in order of how often they bite:

1. The files are inside an untracked directory. With only -f, git removes loose untracked files but stops at directories, even reporting Would remove build/ without deleting it in a real run. Add -d:

git clean -fd

2. The files are gitignored. node_modules/, dist/, .venv/ — ignored paths are invisible to a plain clean. The dry run will not list them, and neither will the clean remove them. Opt in explicitly:

git clean -fdx   # untracked + ignored files and directories

3. A nested git repository or submodule is in the way. Git never deletes another repo’s contents from the outside. Remove the submodule properly or pass --force twice (git clean -ffd), and prefer the first option.

If the dry run lists nothing but git status still shows ?? entries, you are probably in the wrong working tree — run git rev-parse --show-toplevel and check you are inside the repo you meant to clean.

How do I remove untracked files from a specific directory only?

Scope the clean by passing a path — everything else is left alone:

git clean -fd build/          # only inside build/
git clean -fd src/generated   # one specific tree

This is the answer to “I want to remove untracked files and folders in build/ but keep my scratch notes in the repo root”. The path is relative to your current directory, so running from the repo root scopes to the whole repo; running from a subdirectory scopes to that subtree.

How do I remove untracked files without deleting them?

When the dry run shows files you might want later, do not gamble — preserve first, clean second:

# Stash untracked files (including ignored ones with -a) without deleting
git stash push --include-untracked
git clean -fd                      # tree is clean
git stash pop                      # bring them back when needed

git stash -u moves untracked files out of the tree but keeps them recoverable — that is the “remove without deleting” semantics people are actually looking for. For a preview you can keep, git clean -nd > clean-plan.txt gives you the exact list before committing to anything. There is no undo after git clean -f: deleted means gone.

git clean vs git rm vs git restore: which one when?

CommandTouchesRemoves from diskUse when
git clean -fdUntracked files/dirsYesDelete files git has never tracked
git clean -fdxUntracked + ignoredYesFull reset including node_modules, build output
git rm <file>Tracked filesYes (staged)Delete a file and record the deletion in git
git rm --cached <file>Tracked filesNoStop tracking a file, keep it on disk
git restore <file>Tracked filesNoDiscard local edits, keep the file

The one-line rule: clean manages what git doesn’t know about; rm and restore manage what it does. Mixing them up is how people lose work — running git clean -fdx while believing it behaves like git restore.

What should you never run git clean on?

Two habits to avoid outright:

  1. Never run git clean -fdx blindly in a monorepo or workspace. It deletes every ignored directory — that is every node_modules, every virtualenv, every local .env in the tree. Regaining them can mean an hour of reinstallation, and a deleted .env may not be recoverable at all.
  2. Never alias clean with force baked in. git config alias.wipe "clean -fd" feels efficient until you typo the path. Keep the dry run one keystroke away (git clean -nd) and make it a two-command ritual, preview then delete.

Also worth knowing: cleaning untracked files right before a [sync a fork with upstream] pull keeps the merge surface small — a tidy tree is the cheapest conflict insurance there is: sync a fork with upstream, step by step.

The safe git clean workflow, condensed

git status --short        # what's in the tree?
git clean -nd             # preview: what WOULD go?
git clean -fd             # delete untracked files + directories
git clean -fdX            # (optional) clear only ignored build output
git status --short        # confirm: working tree clean

Preview, delete, verify — thirty seconds, zero regret, and git status finally reads clean again.

— mrsaynothing

Rsync vs SCP: Which Linux Copy Command to Use