DEV Community

loading...

Bring your lost git commits back from the grave.

Afif Sohaili
・3 min read

The background

I was trying to rebase my branch against the latest master main branch one day, which had some new changes from my colleague. Unfortunately, those changes actually had conflicts with the changes on my branch. Obviously, git then asked me to resolve the conflicts when I tried to do a rebase, and so I did. But after I was done, I realized that the changes on my branch no longer worked, and upon further digging, I seemed to have not resolved the conflicts correctly, hence introducing a bug in my branch.

So I tried git diff-ing against the latest main branch again to see what I did wrong. But because the conflicts were quite complex and with all the lines of additions and deletions jumbled in-between each other, detecting the problem couldn't just be done in a glance. I had to resort to a more extreme approach, starting over on my rebase.

That means I had to reset my current branch to the state before I did the rebase, so I had to get that commit hash from before. I cannot get that from my branch now, since if I run git log on my branch, it will show the commit hashes after the rebase. But not to worry, right? Because I could obtain the old commit hash by referring to my branch on remote (in this case BitBucket). And here was when I realized that I did another crucial mistake: I did not push my branch to remote, at all. All my commits were just in my local repository.

GIF of a cat realizing a messed up situation

So, now what?

git reflog to the rescue

And that was when I stumbled upon git reflog, a handy, little-less-popular feature of git that I love. According to the documentation, git-reflog:

record when the tips of branches and other references were updated in the local repository.

That means, whenever you do any operation in your local git repository, it gets logged into reflog. That's right; when you create a new commit, that gets logged. When you amend a commit, that gets logged. When you checkout to a different branch, that gets logged. When you cherry-pick a commit, that gets logged. When you rebase, each steps git takes to rebase your branch gets logged. You can try git reflog now on any of your git repos and you will see all the operations you did in your local repo.

Image showing git reflog on my local repo

Bringing back the lost commit from reflog the grave

So, with this new knowledge equipped, I could solve the problem I described earlier. I ran git reflog, traced the last commit I made before the rebase and copied its commit hash, and simply ran git reset --hard <the old commit hash>. And voilà, I was back at square 1 (sort of, see Footnote 1). I then re-ran git rebase master to repeat the merge conflict resolution process, and resolved it properly this time.

Conclusion

And that's how you bring back commits from the grave. Yes, you might not need this everyday. But when you do need it, you'd be glad it exists. I know I did.


Footnote 1

git will actually record your merge conflict resolution the first time you do it, so even if you rerun git rebase master, it will apply those recorded resolutions and you might see your buggy merge conflict resolution again. To resolve this, run:

# rerere stands for reuse recorded resolution
git rerere forget path/to/file/with/buggy/resolution

or if there's a ton of files, just run rm -rf .git/rr-cache.

I actually wish I could disable git rerere. I don't find it useful tbh.


Footnote 2

You can run git reflog expire to delete these dangling commits. By default, it'll keep the commits created within the last 90 days. I believe they'll never get deleted ever if you don't run this command or git gc command, but I could be wrong. In any case, I don't think you'll want to dig up dangling commits from beyond 90 days ago. There's gonna be so much noise.

Discussion (3)

Collapse
jamesdon profile image
James-Don

Quite helpful! After resolving merge conflict git log command just shows a single thread of history.

Collapse
abbadev profile image
Abdulla T

Super useful..Glad you fixed your rebase. Why didn't you go with a git merge instead? Also, first time I learn about git rerere (I thought Rihanaa had her own git command 😂)

Collapse
afifsohaili profile image
Afif Sohaili Author

I believe merge would still produce the same result since the merge conflicts would still happen. Plus, I don't like what git merge does to the tree. 😅 I cringe every time I see a merge from master into one branch and later a merge from the branch back into master. 😅