When you screw up something in Git (bad merge/rebase, hard reset, etc.), you sometimes would like to go back before you screwed up, but you don’t know how. It’s very easy with the git reflog command!

Git is all about pointers. For example branches are just pointers to commits. When you move to a branch, you’re essentially moving the HEAD pointer to point to a branch name, which then points to a certain commit. The HEAD pointer is your current location in Git. Take a look at the image below.

HEAD points to master branch, which points to commit f30ab. The git checkout command moves your HEAD pointer somewhere.

When the HEAD pointer doesn’t point to a branch, you’re in so called detached HEAD state, because it’s detached from a branch.

The command that can save you from all kinds of trouble in Git is the git reflog command. It shows the history of where the HEAD pointer has been pointing to previously. That is every step before and after the screw up. When you do a bad merge, bad rebase, bad hard reset, reflog can save you, because it knows where you were before the screw up.

Let’s take some examples.

I have implemented three features: A, B and C. My log shows the following:

Bad merge

Now let’s say I implement a new feature D on a new branch, which deletes features A, B and C. And when I merge that into master, my features A, B and C are gone!

Take a look at the following image:

  1. I created a new branch feature/D.
  2. Deleted all *.txt files.
  3. Created D.txt and implemented feature D.
  4. Committed that.

Now if I merge branch feature/D into master, all of my features will be gone – except D. Let’s take a look at how that looks:

As you can see when I merge in feature/D branch it deletes everything except D.txt. This is not good! If you know a little bit around Git, you know you could just check out the log and reset hard to the point before the merge, but let’s use reflog.

Take a look at the list. 5dc60b1 HEAD@{0} is the merge and 9bd57cc HEAD@{1} is before the merge. So If I want to go back before the merge, all I have to do is: git reset --hard 9bd57cc or git reset --hard HEAD@{1}. Let’s take a look.

Woah, I’m back to where I was before the bad merge, and all my commits/files are back. Awesome!

Bad hard reset

Another scenario is when you’re trying to fix something but you accidentally hard reset to a bad place and all your commit history is gone and you don’t know if you can go back. Actually you can!

When you hard reset you’re just moving your HEAD pointer and resetting your working directory, but all commits are still there (even though they don’t show up in your log). However Git garbage collects commits that are not attached to branches, so the commits won’t be there forever, but after a screw up (e.g. a hard reset) you can always recover.

Let’s say I’m still at my bad merge before and I don’t know what I do, so I panic and accidentally hard reset to my first commit (feature A). Now my features B and C are also gone and there is no history in the log so I don’t know how I can go back.

Everything is gone. All I have is my feature A (A.txt) and my log only shows this:

Now I can check my reflog to see where my HEAD pointer was before I hard resetted it to the first commit.

As you can see 81031fa HEAD@{0} is where I went back to the first commit and ruined everything. 5dc60b1 HEAD@{1} is where I merged the bad feature which deleted everything and 9bd57cc HEAD@{2} is before that. So all I have to do is reset hard to 9bd57cc or HEAD@{2} and everything will be ok!

Now everything is back to how it was before all the failures.

Bad amend

Sometimes when I’m in a rush I accidentally amend to my previous commit but actually intended to create a new commit. This can also be fixed with reflog!

Let’s say I’m fixing feature C and amend to the commit for feature C, instead of creating a new one.

What have I done? Now my fix is a part of the feature C commit, which I do not want. I want to go back and turn the fix into a seperate commit. Let’s check out reflog!

9bd57cc HEAD@{1} is the commit before I amended my changes, so if I reset softly to that point, all of my changes will still be staged and I can commit those to a new commit. So I type in: git reset --soft HEAD@{1}

As you can see my changes with the fix are now seperated from feature C, are now staged and ready to be committed as a new commit. To keep my old commit message I can type git commit -C HEAD@{1} which commits the new commit like the commit at HEAD@{1} which is the commit with the amend.

As you can see above that worked and now my commit is seperate!