The Git Undo Toolkit – Part 1 – How To Undo Working Directory Changes In Git?

Have you ever faced a situation where due to high work load, you made a bunch of changes, and then suddenly thought:

  • “Oh no, I totally messed up this file and now I have to revert to the last saved version!”
  • “I want to discard all my recent work and start from scratch again from the last committed state.”
  • “I have created some temporary files for testing, and now they are cluttering my project – how do I get rid of them?”

If these scenarios sound familiar, you are in the right place. Your git “working directory” is where all the action happens, where you write, modify, and delete files. It’s also the first line of defense when it comes to undoing mistakes. The good news is, mistakes here are the easiest and safest to fix because Git has not yet started tracking these changes.

In this guide, we will walk you through the commands to undo changes in your Git working directory, ensuring you can quickly recover from minor mistakes and keep your development flow smooth. But before we begin, lets complete the initial setup –

Initial Setup

Code:

# Prerequisite: Any system with latest version of git

# Step 1: Create a new directory and get inside it
mkdir git_undo_demo && cd git_undo_demo

# Step 2: Initialize an empty git local repository
git init

# Output:
# Initialized empty Git repository in /home/debjeet/git_undo_demo/.git/

# Step 3: Set email and user (Optional)
git config user.email "ckdbtech@gmail.com" &&
git config user.name "Debjeet Bhowmick"

# Step 4: Create some files  
echo "# Master Undo In Git" > README.md &&
mkdir code && echo "hello world" > code/file.txt

# Step 5: Add and commit the file
git add . && git commit -m "initial commit"

# Output:
# [main (root-commit) a3092f6] initial commit
#  2 files changed, 2 insertions(+)
#  create mode 100644 README.md
#  create mode 100644 code/file.txt

# Step 6: Check if files and directory created
tree

# Output:
# .
# ├── README.md
# └── code
#     └── file.txt
# 
# 2 directories, 2 files

# Step 7: Check your repository status
git status

# Output:
# On branch main
# nothing to commit, working tree clean

Explanation:

  • mkdir: To create a new empty directory
  • cd: Change directory, used to get inside the new directory
  • git init: Initialize an empty git repository under current directory
  • git config user.email and git config user.name: Optional configuration to set your email and user name which will appear in git commit log
  • git status: Check the status of a git repositor
  • tree: command line tool to check directory structure

Scenario 1: How to Discard Changes in a Single File in Your Working Directory?

This is a common scenario. You have been editing a file, and you realized that you have gone down the wrong path, or you just want to revert that specific file back to how it was in your last commit and start a fresh again.

Question: How do I discard all un-staged changes in a specific file and revert it to its last committed state?

Answer: Use the git restore command (recommended for modern Git) or git checkout -- (for older Git versions).

Code:

# ----------------------------------------------------------
# Discard Changes in a Single File in Your Working Directory
# ----------------------------------------------------------

# Step 1: Update README.md
echo "## some mistake" >> README.md

# Step 2: Check file (README.md) content and git status
cat README.md && git status

# Output:
# # Master Undo In Git
# ## some mistake
# On branch main
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git restore <file>..." to discard changes in working directory)
#         modified:   README.md
#
# no changes added to commit (use "git add" and/or "git commit -a")

# Step 3: Now, discard changes in a specific file (README.md)
git restore README.md 

# Or, for older Git versions
# git checkout -- README.md

# Step 4: Check file (README.md) content and git status
cat README.md && git status

# Output:
# # Master Undo In Git
# On branch main
# nothing to commit, working tree clean

Explanation:

  • git restore: This command is designed specifically for restoring working tree files. When used without the --staged option, it takes the version of the file from the HEAD commit (your last committed state) and overwrites the content in your working directory.
  • README.md: Replace this with the actual path and name of the file you want to revert.
  • git checkout --: In older Git versions, git checkout was overloaded for multiple purposes, including restoring files. The -- (double dash) is crucial here; it tells Git that what follows are file paths, not branch names, preventing accidental branch switching.

Notes/Warnings:

  • Irreversible: Once you run this command, any unsaved changes in README.md that were not committed will be permanently lost. There is no “undo” for this undo.
  • Only Affects Working Directory: This command only affects the file in your working directory. If you had already git added changes to the staging area, those staged changes would remain staged (we have covered how to undo staged changes in the next part).

Scenario 2: How to Discard ALL Changes in the Working Directory?

Sometimes, you may have made a flurry of changes across many files, and you have decided to scrap them all and return your entire project to the state of the last commit and start from scratch again.

Question: How do I discard all un-staged changes across all files in my working directory and revert everything to the last committed state?

Answer: Use git restore . (recommended) or git checkout -- ..

Code:

# --------------------------------------------
# Discard ALL Changes in the Working Directory
# --------------------------------------------

# Step 1: Make multiple changes
echo "## some mistake" >> README.md &&
echo "some mistake" >> code/file.txt

# Step 2: Check git status
git status

# Output:
# On branch main
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git restore <file>..." to discard changes in working directory)
#         modified:   README.md
#         modified:   code/file.txt
# 
# no changes added to commit (use "git add" and/or "git commit -a")

# Step 3: Now, discard all changes in the current directory and its subdirectories
git restore .

# Or, for older Git versions
# git checkout -- .

# Step 4: Check git status again
git status

# Output:
# On branch main
# nothing to commit, working tree clean

Explanation:

  • git restore .: Similar to the single-file version, but the . (dot) signifies the current directory, instructing Git to restore all tracked files within the current directory and its subdirectories to their last committed state.
  • git checkout -- .: The same logic applies here for older versions, applying to the entire working directory.

Notes/Warnings:

  • DANGER – Irreversible! This command will wipe out all un-staged changes across your entire working directory. Be absolutely sure you want to discard everything before running it.
  • Only Affects Tracked Files: This command only discards changes to files that Git is already tracking. It will not remove new, untracked files or directories. See the next scenario for that.

Scenario 3: How to Remove New, Untracked Files and Directories?

You have created some temporary files, compiled code, built some artifacts or downloaded dependencies that are not part of your Git repository and are not ignored by .gitignore. They are just “there,” cluttering your git status output, and you want them gone.

Question: How do I remove untracked files and/or directories from my working directory that Git is not tracking?

Answer: Use the git clean command.

Code:

# -------------------------------------------
# Remove New, Untracked Files and Directories
# -------------------------------------------

# Step 1: Create a new file and directory
echo "hello again" > file1.txt &&
mkdir new && echo "hello once again" > new/file2.txt

# Step 2: Check git status
git status

# Output:
# On branch main
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#         file1.txt
#         new/
# 
# nothing added to commit but untracked files present (use "git add" to track)

# Step 3: Check which file/directory will get removed (dry run)
git clean -nd

# Output:
# Would remove file1.txt
# Would remove new/

# Step 4: To remove untracked files (but not directories)
git clean -f

# Output:
# Removing file1.txt

# Step 5: Check git status again
git status

# Output:
# On branch main
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#         new/
# 
# nothing added to commit but untracked files present (use "git add" to track)

# Step 6: To remove untracked files AND untracked directories
git clean -fd

# Output:
# Removing new/

# Step 7: Check git status one last time
git status

# Output:
# On branch main
# nothing to commit, working tree clean

Explanation:

  • git clean: This command is specifically for cleaning up the working directory by removing untracked files.
  • -n (or --dry-run): This is your best friend here! It simulates the command without actually deleting anything. It will list all the files and directories that would be removed. Always run this first!
  • -f (or --force): git clean requires a force flag because it performs destructive operations. This tells Git to actually remove the files listed.
  • -d: Include untracked directories in the cleanup.
  • -x: (Less common, but useful) Also remove files ignored by Git (e.g., in .gitignore). Use with extreme caution!

Notes/Warnings:

  • EXTREMELY DANGEROUS – Irreversible! git clean permanently deletes files from your file system. There is no Git-specific “undo” once they are gone.
  • Always Dry Run: Make git clean -n your mantra before you ever run git clean -f or git clean -fd. This allows you to verify the list of files to be deleted.
  • Respects .gitignore: By default, git clean will not remove files that are listed in your .gitignore file. If you want to remove even those (e.g., temporary build artifacts), you would use git clean -fx.

Summary:

Scenario / GoalCommandDescriptionNotes / Warnings
Discard changes in a single filegit restore <file>Reverts a specific file to its last committed state.Irreversible for un-staged changes.
Discard ALL changes in working dirgit restore .Reverts all tracked files to their last committed state.DANGER! Irreversible for all un-staged changes to tracked files.
Remove untracked files (dry run)git clean -nShows what untracked files/dirs would be removed.ALWAYS run this first!
Remove untracked filesgit clean -fPermanently deletes untracked files.DANGER! Irreversible file deletion.
Remove untracked files & directoriesgit clean -fdPermanently deletes untracked files AND directories.DANGER! Irreversible file and directory deletion.

Reference:

Author

Debjeet Bhowmik

Experienced Cloud & DevOps Engineer with hands-on experience in AWS, GCP, Terraform, Ansible, ELK, Docker, Git, GitLab, Python, PowerShell, Shell, and theoretical knowledge on Azure, Kubernetes & Jenkins.
In my free time, I write blogs on ckdbtech.com

Leave a Comment