Bring your lost git commits back from the grave.
Tags: git
Published: 2020-10-02 10:05:15

---
title: Bring your lost git commits back from the grave.
published: true
description: How to undo almost anything in git
tags: git
//cover_image: https://direct_url_to_image.jpg
---

## 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](https://media.giphy.com/media/WMfho5VxnIaOc/source.gif)

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](https://git-scm.com/docs/git-reflog), `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](https://i.imgur.com/HR1Tmas.png)

## 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:
```sh
# 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.