Cleaning your Repo with git clean: How to Safely and Professionally Remove Untracked Files

Git tutorial - IT technology blog
Git tutorial - IT technology blog

Context: When your workspace turns into a “battlefield”

After a day of work with dozens of builds, library installations, or debugging sessions, your project folder is often cluttered with junk. From log files and build folders (dist, target) to thousands of thumbnails generated during script testing.

Using git status, you’ll see a never-ending list of “Untracked files.” Deleting a few files manually is easy. But what if there are hundreds scattered across dozens of subdirectories? That’s a real nightmare. Many people mistakenly think that git checkout . or git reset --hard will clear this mess. In reality, those commands only affect files already managed by Git. New files that have never been added via git add will be ignored by Git.

I once worked on a migration project where a script generated over 5,000 temporary JSON files. At that moment, git clean was a lifesaver, allowing me to wipe everything clean in just 2 seconds.

Git’s Default Protection Mechanism

Git is extremely cautious with deletion commands. Once git clean is executed, data is permanently lost—it doesn’t go to the trash and cannot be undone. Therefore, Git implements a safety catch called clean.requireForce. If you run the command without any parameters, Git will refuse to execute and display an error message:

fatal: clean.requireForce defaults to true and neither -i, -n, nor -f given; refusing to clean

This error forces you to confirm: “I know what I’m doing.” You can disable this mechanism with the configuration git config --global clean.requireForce false, but I wouldn’t recommend it. Keep that safety catch in place to protect yourself from occasional “brain fogs.”

Practical git clean Commands You Need to Know

To clean up safely, memorize these three levels:

1. “Preview” Mode (Dry Run) – Always run this first

Before actually deleting anything, use the -n (or --dry-run) flag. Git will list the files slated for deletion without actually touching your hard drive.

git clean -n

The output will look something like: Would remove debug.log. Carefully check this list to ensure no important code files are accidentally deleted.

2. Delete Single Files (Force)

Once you’re certain, replace -n with -f (force) to execute.

git clean -f

Note: By default, this command only deletes individual files and does not touch directories.

3. Clean Entire Folders (Recursive)

To delete all junk directories (like the dist/ or temp_data/ folders), you need to add the -d flag.

git clean -fd

This is the most common combination, helping restore absolute cleanliness to your workspace.

4. Clean Files in .gitignore as well

Normally, git clean spares anything you’ve declared in .gitignore. However, if you want to reset the project to its “pristine” state as if it were just cloned, use the -x flag.

git clean -fdx

Extreme Warning: This command will wipe out your .env file containing database passwords or library folders like node_modules. Only use this when you want to rebuild everything from scratch.

5. Interactive Mode (Interactive)

If you want to selectively keep or delete individual files, use the -i flag.

git clean -fd -i

Git will display a selection menu. This method is slightly slower but extremely safe if you’re dealing with a massive pile of messy files.

Maintaining a Clean Repo in Real Projects

After cleaning, check again with git status. If you see the message “working tree clean,” you’ve succeeded.

In my team, whenever we merge a large branch, we usually run git clean -fd. This prevents junk files from the old branch from being accidentally committed to the new one, which can cause noise during code reviews.

A small tip: If you find yourself using git clean too frequently for a specific type of file, add it to .gitignore. For IDE configuration folders like .vscode or .idea, configure them in your global gitignore so you don’t have to worry every time you clean up.

Remember the standard workflow: Check status -> Dry run (-n) -> Execute (-f). Happy committing!

Share: