DEV Community

Cover image for Git & GitHub made simple - Undoing commits
Francesco Di Donato
Francesco Di Donato

Posted on

Git & GitHub made simple - Undoing commits

It can happen to everyone (especially at 4 am 😴) to push a commit and immediately realize that it shouldn't be done. Why mess with your commit history with other commits when you can just take one (or more) back steps?

As usual, for simplicity, let's establish a simple situation that allows us to easily understand the matter: you have connected your local project to a remote repository on GitHub via Git. You only have one and obviously, the concept you are about to learn is independent of the type and number of files involved.
The that you already pushed to the remote repository contains:

Tap-tap-tap πŸ‘©β€πŸ’», you tweak a little your file
and finally, decide it's time to git add, thus git commit -m "regrettable commit".
Oh, no! You would like to go back, so try git status but

Well, you must know that in the hidden .git folder πŸ‘», among the various files, there is one called HEAD. It is a pointer to the commits. When it is not followed by anything else it is referencing the last commit made. Otherwise, by posting a ~ (tilde) and an integer to it you can point to previous commits:

  • HEAD - the current commit
  • HEAD~1 - one commit ago
  • HEAD~2 - two commits ago

And so on...

Then, with a magical phrase, revert time to your heart's content:
git reset HEAD~1

Now the staging step is repopulated from what you added or changed before committing
You can fix what needs to be fixed and proceed as you already know: add, commit and push it.

Bonus: compare the differences between two versions

As soon as you have reset to a previous commit you can quickly see the differences between it and the current one using git diff:

When commits can't be counted on two hands πŸ€šβœ‹

Using the git log command, you are shown a list of all commits (on this branch) in reversed chronological order (the latest at the top).

Notice how HEAD lays on the most recent commit

As you have certainly noticed, an ID is associated with each commit. Choose the commit you want to go back to, copy the ID, and type git reset [commit_ID]
Now in the staging step there are the files added or modified at that point and you are free to modify and commit them again.

One last note, in case you want to go back to a previous commit and have all the changes made completely removed, both from the IDE and from the staging step, you can add the --hard flag: git reset --herd [commit_ID]

This command causes the HEAD pointer to now be associated with the newly obtained state. Beware: in fact, by doing so you have lost the various commits made after the one you just hardly returned to.

There is one last thing any programmer looking to improve should know, and that is the concept of forking.
What do you say, tiger 🐯?
Okay, let's go

🐀 twitter:

Top comments (6)

thajeztah profile image
Sebastiaan van Stijn

Nice write-up!

There's also HEAD^ (one commit "ago") HEAD^^ (two commits ago), etc.

If you went back some commits, and want to move "forward" again, use HEAD@{1} ("go back to previous action in history").

What's "history"? You can find a log of everything you did with git reflog. git reflog can be your rescue if you really messed up :D

here's history of "undoing" 3 commits (HEAD^^^) and forward again (HEAD@{1})

git reflog

d6c1b650 (HEAD -> more_cleanups) HEAD@{0}: reset: moving to HEAD@{1}
d0951081 HEAD@{1}: reset: moving to HEAD^^^
d6c1b650 HEAD@{2}: commit: drivers/bridge: dont use types.ParseCIDR() for fixed value
6cef9c81 HEAD@{3}: commit: windows: remove redundant init()
d511c60c (upstream/master, origin/master, origin/HEAD, master) HEAD@{4}: checkout: moving from master to more_cleanups
Enter fullscreen mode Exit fullscreen mode

You can "reset" your state to any of those, using either the commit (e.g. git reset d6c1b650) or the "convenience" references (HEAD, HEAD@{1}, HEAD@{2} etc)

leodido profile image
Leo Di Donato

I was waiting a bit to introduce my little brother to reflog!
There’s a lot of pain and gain there ahahaha

Glad that Sebastian introduced him with this comment! πŸ€—

Very informative and well written post, bravo FrΓ 

didof profile image
Francesco Di Donato

Thank you so much for your comment, I appreciate it and it's really informative. I'm planning on next days to edit this post and add the "git revert" command.
With your permission I'd like to add your information (quoting your name of course) 😁

thajeztah profile image
Sebastiaan van Stijn

Ha! I didn't know you were brothers ☺️

Yes definitely avoid reflog if you can; it's not something you'd use in your daily flow.

Tips I would have;

  • branches are "cheap" when using git; working on some changes and want to try some variations? create a new branch from your current one and try what it looks like. It's easy to remove the branch later
  • working on a merge conflict, and afraid you'll screw up? just create a new branch as backup before you start.

Adding to the "merge conflict" case; if you need to rebase a pull request (and/or fix a merge conflict); GitHub can be your backup; as long as you don't "push", you can always return to the previous state (without using reflog πŸ˜‰)

Feel free to use whatever I wrote in your blog; I enjoyed reading your write up, and hope it helps others get comfortable in the world of Git black magic

didof profile image
Francesco Di Donato

You are not the only one πŸ˜‹ Who knows how many commits I could have saved!