How to Git Undo Commit: Methods and Best Practices

Made a mistake in Git? No worries! This guide covers git reset, git revert, and git reflog to undo commits safely, whether they’re local or already pushed. Learn when to keep, unstage, or discard changes and how to recover lost commits.
Read the articles by Shantanu Das in the Aviator blog. Get professional advice on software product deployment, testing, and other engineering topics.

How to Git Undo Commit: Methods and Best Practices

Made a mistake in Git? No worries! This guide covers git reset, git revert, and git reflog to undo commits safely, whether they’re local or already pushed. Learn when to keep, unstage, or discard changes and how to recover lost commits.
how to git undo commit

Imagine you’re working on an infrastructure update for a SaaS product. The backend team merges a hotfix, only to realize it’s in the wrong branch, causing a break in provisioning scripts. You need to undo that commit fast but without disrupting the rest of the work, your team is pushing. Should you reset, revert, or rely on reflog? In this guide, we’ll show you how to undo commits the right way, whether they’re local, pushed, or accidentally erased, so you can fix mistakes without creating bigger issues down the line.

What Does “Git Undo Last Commit” Mean?

committing, you realize that the snapshot isn’t quite right as it might include the wrong files, be incomplete, or belong to a different branch of your project. Understanding various Git commands is crucial to effectively managing and undoing commits in these scenarios.

But where exactly is this useful? Let’s look at some real-world scenarios where undoing a commit is necessary:

  1. Accidentally Committing Incomplete Work: Let’s say you’re building a report export feature for a customer analytics SaaS. Midway through coding, you accidentally commit. If pushed, this half-done feature might break the staging environment. Undoing the commit lets you finish the work without affecting others.
  2. Committing to the Wrong Branch: If you’re on the main branch of a CRM project but meant to commit changes to the user-onboarding-improvements branch. A commit on the main branch could disrupt production workflows. Undoing it helps you move the changes to the correct branch quickly.
  3. Including Sensitive Data in a Commit: While working on a payment platform, you can mistakenly commit API keys or user credentials. If pushed to a shared or public repo, these secrets could leak. Undoing the commit immediately prevents exposure and secures your project.

Mistakes in Git history can confuse your teammates, introduce bugs, or even lead to serious security risks. By learning how to undo commits effectively, you can save time, avoid unnecessary complications, and ensure your project stays on track. In the next sections, we’ll explore the lifecycle of a Git commit and dive into specific commands you can use to undo your last commit in various situations.

Git Commit States

Git uses a three-step lifecycle to manage changes: the working directory, the staging area, and the commit history. Let’s break these down with examples and visuals.

Commit Lifecycle in Git

  1. Working Directory: This is where your files live as you work on them. Changes here are not yet tracked by Git until you explicitly stage them. Let’s say you’re developing a new feature for a SaaS dashboard; adding or modifying files in your project folder changes the working directory. You can use git checkout to revert changes in the working directory or switch to a different commit.
  2. Staging Area: Once you’re satisfied with a file, you stage it to prepare it for a commit. This step allows you to control exactly what goes into the next commit. After adding a chart component to the dashboard, you run git add < filename> to stage it for the next commit.
  3. Commit History: When you commit, Git permanently stores a snapshot of the staged changes in the project’s commit history. Each commit has a unique ID and becomes part of your project timeline. For example, you commit the chart component to your feature branch with a message like git commit -m “Add revenue chart to dashboard”.

Here’s a quick difference between staged and committed changes:

difference between staged and committed changes

Git’s Four States

Below is a simplified diagram of how Git tracks changes through its states:  

git four states

The above diagram shows the following flow:

  1. Working Directory
  • Files are modified, added, moved, or removed (git add/mv/rm).
  • Changes can be inspected using git diff.
  1. Staging Area
  • Changes are staged using git add .
  • Staged changes are committed to the local repository using git commit.
  • git commit -a directly commits all modified files.
  1. Local Repository
  • Contains the committed changes.
  • Changes can be undone using git reset < commit>.
  • Differences between commits can be checked using git diff HEAD.
  1. Remote Repository
  • Local commits are pushed using git push.
  • Updates from the remote repository are fetched using git fetch.
  • New or updated repositories are pulled/cloned using git clone/pull.

This illustrates the flow of changes from workspace to staging, from the local repo to the remote repo, and the commands used at each step. Understanding these states allows you to confidently manage and undo commits based on whether they’re staged, committed, or already pushed. The git reset command can be used to undo recent commits and manage commit history, but be cautious as it can overwrite commit history, especially for changes pushed to a remote repository. Here’s a quick difference between local commits and pushed commits:

local commits and pushed commits

You can learn more about how git commit works through Aviator’s git commit guide.

When you need to undo a local commit, you can use git reset <commit>, but how do you find the exact commit you want to revert to? This is where git reflog comes in. It keeps a history of all movements of HEAD, allowing you to track back to previous commit states and restore lost changes.

Using git reflog for Undoing Commits

You just ran git reset –hard on your feature branch, only to realize you wiped out hours of work. Or maybe you force-pushed the wrong commit, breaking the main branch. In such cases, git reflog is your safety net. It tracks every move, letting you recover lost commits, even after resets or branch deletions.

Imagine you’re working on an analytics dashboard for a product. You accidentally run:

git reset --hard HEAD~2

This wipes out the last two commits, losing key bug fixes. But instead of redoing everything, you can run:

git reflog

This shows a history of where your HEAD was, like this:

c7f1b2a HEAD@{0}: reset: moving to HEAD~2
f28d90c HEAD@{1}: commit: Fixed timezone bug in reports
b512acd HEAD@{2}: commit: Added revenue summary chart

Now, to restore the lost commit, simply run:

git reset --hard f28d90c

And with that, your commits are back. git reflog is a safety net for developers working on complex Git workflows, especially in teams where multiple engineers are contributing. Whether you’re fixing lost commits, recovering branches, or undoing resets, git reflog helps ensure you never lose your work.

Git Garbage Collection and Permanent Deletion

Git does not immediately delete commits. Even after a git reset –hard or git branch -D, the commits still exist in the repository’s object database and can be recovered using git reflog until Git runs garbage collection. Git services like GitHub, GitLab, and Bitbucket run garbage collection at unpredictable intervals, so deleted commits may persist longer.

When are Commits Permanently Deleted?

Commits remain accessible unless all references to them are removed and GC clears them. Git marks unreachable commits for deletion but doesn’t remove them immediately.

  1. If a commit exists in any branch, it is safe.
  2. If a commit is removed from all branches (git reset –hard or git rebase), it becomes dangling.
  3. By default, Git keeps reflog history for 90 days. Running git reflog expire –expire=now –all removes old references.
  4. When Git runs GC, dangling commits are deleted permanently.

To permanently erase commits:

git reflog expire --expire=now --all
git gc --prune=now

This ensures all unreachable commits are deleted.

Methods to Undo the Last Commit (Before Pushing)

Git offers multiple ways to undo the last commit depending on what you want to do with the changes, such as keeping them, unstaging them, or discarding them. Let’s explore these methods with examples and use cases.

1. Git Undo Last Commit Keep Changes

This undoes the last commit but keeps all changes in the staging area, so they’re ready for a new commit. For example, if you’re working on a billing module and realize you missed adding a necessary test file before committing, you don’t want to lose your changes or unstage them; you just want to modify the commit.

Commit your changes:

git add . 
git commit -m "Add billing module code without tests"  

Now, how can git undo last command? You can undo the commit using the following command:

git reset --soft HEAD~1  

Your changes are now back in the staging area. Add the missing test file:

touch billing_test.js 
git add billing_test.js 
git commit -m "Add billing module with tests"  

2. Undo the Last Commit and Unstage Changes: 

This undoes the last commit and moves the changes to the working or unstaged directory so you can edit or stage-specific parts again. For example, if you’re developing a feature for a user onboarding flow. After committing, you realize some changes shouldn’t be included. For instance, a debug log file accidentally got staged and committed.

Commit your changes:

git add . 
git commit -m "Add onboarding flow with debug logs"  

Undo the commit and unstage changes:

git reset --mixed HEAD~1  

Now, edit the files or remove unnecessary ones:

rm debug.log 
git add . 
git commit -m "Add onboarding flow without debug logs"  

The changes return to the working directory, allowing you to clean up mistakes. But how exactly does git compress these huge log files and other files? Check out this guide on How Git compresses files.

3. Git Undo Commit and Delete all Changes

This undoes the last commit and deletes all changes from the staging area and the working directory. The changes are gone permanently unless they were pushed or backed up. If you’re updating a pricing API, but after committing, you realize the changes were incorrect and should not exist at all. For example, you accidentally removed the API rate limit logic, which could crash the production environment.

Commit your changes:

git add . 
git commit -m "Remove API rate limits (oops!)"  

Realize it’s a mistake and discard the commit and changes:

git reset --hard HEAD~1  

Verify the reset worked:

git status  

The working directory should now be clean as the changes are gone. Be completely sure before using this command, as changes will be lost forever. When transitioning to a detached HEAD state, any new commits made will not belong to an existing branch and may be at risk of being lost. Therefore, it is important to create a new branch to preserve those new commits and continue development.

Methods to Undo in Remote Repository (After Pushing)

Git also provides tools for handling advanced scenarios. Whether you need to fix a mistake after pushing, tweak the last commit, or break down a large commit, these methods are practical, especially in collaborative environments.

1. Git Undo Last Commit After Push

This creates a new commit that reverses the changes of a specific commit, leaving the commit history intact. Imagine you’re working on a platform for customer subscriptions and pushing a commit that accidentally removes the logic for automatic renewals. Other developers may have already pulled the changes, so you can’t use git reset. Instead, you use git revert to undo the changes safely.

Check the history to find the commit hash:

git log


You will get something like this: 

commit abc12345 
Author: You 
Message: Remove auto-renewal logic (oops!)  

Revert the commit:

git revert abc12345  

Push the revert to the remote branch:

git push origin main

This creates a new commit with a message like:

Revert "Remove auto-renewal logic"  

When you’ve already pushed changes, git revert ensures the history is preserved, which is essential for team visibility. It doesn’t rewrite history, avoiding conflicts for other team members pulling the same branch. In remote repositories, careful handling and communication are crucial when undoing commits to preventing issues like merge conflicts.

Here’s a table comparing git revert and rewriting history (git reset / git rebase) for clarity:

git revert

By choosing the right method, you can undo mistakes efficiently while avoiding unnecessary conflicts in your Git workflow. 

2. Amending the Last Commit

This allows you to modify the most recent commit, including its message or staged changes, without creating a new commit. For example, if you’re working on an analytics dashboard and committing to a feature to add user activity tracking, Later, you realize you forgot to include the tracking script file or made a typo in the commit message.

Commit the initial changes:

git add . 
git commit -m "Add user activity tracking"  

You realize the typo or missing file. You can fix it using the following command:

git add tracking.js  # Add the missed file
git commit --amend 

You can modify the commit message if required in the editor:

Add user activity tracking  

Finally, push the updated commit:

git push --force

When you amend a commit, Git replaces the previous commit with a new one. If you’ve already pushed the original commit, Git sees the amended commit as different and rejects a regular git push. This is because the remote repository expects your local history to match its own. To override this, you need git push –force.

git push –force can be risky because it overwrites the remote branch, potentially deleting teammates’ changes if they’ve pushed updates. Instead, use:

git push --force-with-lease

This only forces the push if no one else has pushed new commits since your last pull. It acts as a safety check to prevent accidentally erasing someone else’s work.

3. Splitting a Commit

This breaks a large commit into smaller, meaningful commits for better history tracking. Suppose you’re working on a SaaS email marketing platform and commit a large update that includes both a new email editor and performance improvements. It’s better to split this into two commits for clarity.

Commit everything:

git add . 
git commit -m "Add email editor and improve email rendering performance"  

Reset the commit to undo it but keep changes in the working directory:

git reset HEAD~1  

Stage changes interactively:

git add -p  

During the interactive process, Git will show you chunks of changes and ask:

Stage this hunk [y,n,q,a,d,/,s,e,?]? 

Type y for changes related to the email editor and n for others.

Commit the staged changes:

git commit -m "Add email editor feature"  

Stage and commit the remaining changes:

git add -p 
git commit -m "Improve email rendering performance"  

Best Practices and Common Mistakes in Undoing Git Commits and Managing Commit History

Undoing commits can be tricky, especially in collaborative environments. Here’s how to safely undo changes, avoid mistakes, and protect your work: 

  1. Avoid rewriting history with git reset after pushing the commits on main or other shared branches, as it can cause conflicts if teammates have already pulled the changes. Use git revert instead, which safely undoes changes while preserving history.
  2. When working with local commits or feature branches, it is generally safe to use git reset, git commit –amend, or git rebase, as these are not shared. So, rewriting the history won’t affect others.
  3. Avoid using git reset –hard unless necessary since this command erases uncommitted work permanently. You should prefer using git reset –soft or git stash instead.
  4. Use git reflog to recover lost commits. git reflog tracks all movements in Git, even after a reset –hard.
  5. Running git reset without checking the status can erase staged or unstaged changes. Check git status and use git stash before resetting.
  6. Use git tags or branches before big changes to create a restore point if things go wrong.
  7. Use git stash for temporary storage. Stashing allows you to save work before you undo local commits.

Conclusion

Undoing commits in Git is simple with the right method. Use git reset for local changes and git revert for pushed commits. If something goes wrong, git reflog helps you recover lost work. Always use git stash or branches to prevent data loss and manage commits safely.

For even easier handling of complex Git operations, check out Aviator’s Stacked PRs CLI. It simplifies many challenging Git tasks with easy-to-use commands, making your workflow more efficient. Check out other guides for git.

FAQs

Q: Is there a git undo?

Yes, Git offers several ways to undo mistakes. You can use git reset to undo git commit before push​, git revert to safely undo commits that have already been pushed, and git reflog to recover lost commits or find previous states of your repository. These tools allow you to fix issues without permanently losing work.

Q: How to undo git commit and add?

To unstage a file after git add, use git reset HEAD <file-name>. This moves the file back to the working directory but keeps the changes. If you’ve already committed and want to undo it while keeping the changes staged, use git reset –soft HEAD~1. This helps you adjust or re-commit without losing work.

Subscribe

Be the first to know once we publish a new blog post

Join our Discord

Learn best practices from modern engineering teams

Get a free 30-min consultation with the Aviator team to improve developer experience across your organization.

Powered by WordPress