• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

Justin Joyce

Practical tips and tutorials about software development.

  • Standing Invitation
  • Featured Posts
  • Latest
  • About

Undo a Git Commit

Posted Sep 6, 2023 — Updated Jan 10, 2024

Need to undo a git commit? You have a few options.

  1. git revert
  2. git reset
  3. git commit amend

Git revert

Git revert is the most straightforward way to undo some committed code. Git revert will create a new commit on your branch which does the exact opposite of the commit you want to revert. Let’s see an example:

screenshot of commit messages

The most recent commit was a mistake, let’s undo it with a git revert head:

git revert result
You can also target the commit hash, e.g. “git revert 322446d”

The revert command above added a new commit to my branch which does exactly the opposite of the commit it reverted. I accidentally deleted something, and git revert put it back. This is what the history looks like now:

result of git lg
this is my custom git lg alias

The full history of my mistake is still there for posterity, but If I compare the current state of the branch with HEAD~2 (the commit before the mistake), there are no differences:

git diff showing no differences
nothing to see here

Using git revert has the advantage of preserving the entire git history—it keeps the record of the bad commit and the revert commit in the git log. For that reason, I’ve used this many times when rolling back bad code on a production branch, where you really want to keep the full git history intact, and where you really don’t want to git push –force.

Git reset

The simplest version of git reset will un-stage some changes you have ready to be committed:

results of git reset
git reset un-staged the changes to app.py

If your changes have already been committed you can reset to an earlier commit, and git reset has a few options specifying how to handle the changing code.

Git reset –hard

Git reset –hard is destructive, anything in the way of the reset will be deleted. If you reset –hard back 4 commits, the code from the most recent 3 commits will be lost.

That said, it’s very handy if you need to scrap your most recent commit (or commits). If you know for sure your most recent commit is not worth saving, just git reset --hard HEAD~1. In English, that command says “reset the HEAD of this branch back one commit”1. Here’s an example:

three lines of git log output

In the screenshot above, I accidentally committed something directly to the main branch, which is generally not a good idea. Let’s assume we can just delete that commit—we don’t care what’s in it. That’s where git reset --hard comes in:

a screenshot of git reset --hard

My local HEAD has now been moved back one commit, and if I run git log again the first commit above–”did something awesome”–is gone:

A note on HEAD~1

Using HEAD~12 is not commonly seen in git commands other than this one, but it is available; most git command examples use a specific commit hash instead.

Head~1 literally means “one commit before HEAD”. If you wanted to reset back two commits, you would reset --hard HEAD~2, and so on. You can also git reset to a specific commit hash—the example above could equally have been written using git reset --hard b1385919.

Git reset –soft

This is a more gentle version of git reset. Running git reset --soft HEAD~1 will roll your branch back one commit, but instead of deleting the most recent code it will put it back in your “changes to be committed”, and let you modify the commit as you normally would. If you git reset --soft back several commits, all of the intermediate commits will be added to your staged files:

git reset soft example
The code from the most recent 4 commits is now staged

Force Pushing

With any of the above git reset options, you may have to git push --force to get your code to push to the remote. Git reset can change the history of your branch, and if the local git history does not match the repository’s remote history, github will raise error: failed to push some refs. Assuming your code is the latest, you can get around this error with a git push --force-with-lease3.

Git reset –keep

I have never actually used this option, but it allows you to run a git reset and keep any existing uncommitted changes you might have. In practice, I prefer the following:

  1. git stash
  2. git reset
  3. git stash pop

I find the three-step workflow above more clear than using –keep.

Git commit –amend

This one doesn’t exactly undo a commit, but it does allow you to modify the most recent commit. I have a whole post about this command, but here are a few quick examples:

# Modify the message of your most recent commit
git commit --amend -m "new message for commit"

# Add a file to your most recent commit without changing the message
git add forgotten_file.txt
git commit --amend --no-edit

With either of these versions of git commit amend you will have to force push, or better yet: git push --force-with-lease. For an explanation of why, go to this section of my post about amend.


Helpful Links

  • What is HEAD in git? – Great explanation on StackOverflow
  • Official git reset docs

Notes

  1. That’s not exactly what it means, but HEAD is a complicated git concept. For a great explanation, see this StackOverflow answer. ↩︎
  2. Another plug for the link about HEAD in note 1 above ^ ↩︎
  3. I have more detail about force pushing in this post about amending commits ↩︎

Filed Under: Git

Primary Sidebar

Recent Posts

  • Every Built-In Vim Color Scheme (with screenshots)
  • Reverse a string in Python
  • Meeting Cost Calculator
  • Vim find and replace
  • What makes an effective development team

Categories

  • Arrays (5)
  • Command Line (9)
  • Dates (3)
  • Featured (7)
  • Git (7)
  • Golang (5)
  • Javascript (8)
  • Productivity (8)
  • Projects (4)
  • Python (15)
  • Regex (2)
  • Ruby (3)
  • Shell (2)
  • Thoughts (2)
  • Tips (11)
  • Tools (3)
  • Tutorials (1)
  • Vim (4)

Archives

  • July 2024 (1)
  • February 2024 (1)
  • January 2024 (1)
  • December 2023 (1)
  • November 2023 (1)
  • October 2023 (4)
  • September 2023 (1)
  • August 2023 (2)
  • July 2023 (5)
  • June 2023 (3)
  • May 2023 (6)
  • April 2023 (5)
  • March 2023 (5)
  • February 2023 (10)
  • January 2023 (6)
  • December 2022 (7)

Copyright © 2025 · Contact me at justin [at] {this domain}

  • Privacy Policy