DEV Community

Cover image for Git Remove Untracked Files: Safe git clean Guide
Mr Say Nothing
Mr Say Nothing

Posted on Edited on Originally published at mrsaynothing.dev

Git Remove Untracked Files: Safe git clean Guide

Related on the site: Gitignore Not Working? Here's the Real Fix.

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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?

Command Touches Removes from disk Use when
git clean -fd Untracked files/dirs Yes Delete files git has never tracked
git clean -fdx Untracked + ignored Yes Full reset including node_modules, build output
git rm <file> Tracked files Yes (staged) Delete a file and record the deletion in git
git rm --cached <file> Tracked files No Stop tracking a file, keep it on disk
git restore <file> Tracked files No Discard 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
Enter fullscreen mode Exit fullscreen mode

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

This ships daily at mrsaynothing.dev — the full archive, every piece in 19 languages, zero missed days. New posts land in the newsletter the moment they ship: join it here. Code at GitHub.

Top comments (1)

Collapse
 
mrsaynothing profile image
Mr Say Nothing •

The -n dry-run is the whole safety story here: git clean is the one command in the family that deletes files git never tracked, so once they go, no reflog brings them back. Has clean ever eaten something you meant to keep, or do you still dry-run it every time like the rest of us?