Git reflog
All operations in git like commits, merges etc. are stored in a particular place named “reference logs”. Git keeps track of every action on your local repository, every! Git has its own quiet worker who notes each of your moves. The first time when I needed to learn what git reflog means, was when I f*cked up. I lost some of my last commits and I started searching for help in a panic to save hours of my work. If you are reading it, I see two reasons. You are in a similar situation 🙂 (Then don’t worry! I’ll try to save you) or you want to learn something new.
What does the git reflog know?
You can treat the reflog as a sandbox. It is the local history of all your changes made in the repository. Word “local” is important here because records in the reflog are available only in your local instance of the git repository. At the same moment, they are invisible to someone who downloaded the repository from the origin. To visualise everything I created a simple example. I initialised the new git repository with one file and then I did three commits. Let’s compare outputs for commands git reflog
and git log --oneline
.
➜ git-reflog-example git:(master) git log --oneline 44c6588 (HEAD -> master) Third commit c64349c Second commit 368ea35 First commit ➜ git-reflog-example git:(master) git reflog 44c6588 (HEAD -> master) HEAD@{0}: commit: Third commit c64349c HEAD@{1}: commit: Second commit 368ea35 HEAD@{2}: commit (initial): First commit
As you can notice, in both command results we have three records but git reflog
has more information about each step. We can extract the hash of each commit, the current HEAD pointer location, moves counter (e.g. @{2} means that it is the step done 2 moves ago), the name of an operation and a commit message.
Where are the differences?
To this moment everything looks the same, isn’t it? So where are the differences? To make it simple, let’s say I would like to change the message of the last commit. To reach this result I used git commit --amend
command.
➜ git-reflog-example git:(master) git commit --amend -m "Third commit after amend" ➜ git-reflog-example git:(master) git log --oneline 35244af (HEAD -> master) Third commit after amend c64349c Second commit 368ea35 First commit ➜ git-reflog-example git:(master) git reflog 35244af (HEAD -> master) HEAD@{0}: commit (amend): Third commit after amend 44c6588 HEAD@{1}: commit: Third commit c64349c HEAD@{2}: commit: Second commit 368ea35 HEAD@{3}: commit (initial): First commit
We have a different amount of records for git log --oneline
and git reflog
. In the first case, we lost information about the previous commit. I can say that because the hash is different (in comparison with the previous git log
output), it was changed due to a different value of the time factor which is included in the hash calculation. When we run git commit --amend
, the new hash was calculated and the new record was created in the history.
But nothing in the world is lost. Check the output of git reflog
. This commit is still there! It means I should be able to back to it. In more specific words, I should be able to move my HEAD pointer to point exactly to the commit with hash 44c6588
again. Let’s try it!
➜ git-reflog-example git:(master) git reset --hard 44c6588 ➜ git-reflog-example git:(master) git log --oneline 44c6588 (HEAD -> master) Third commit c64349c Second commit 368ea35 First commit ➜ git-reflog-example git:(master) git reflog 44c6588 (HEAD -> master) HEAD@{0}: reset: moving to 44c6588 35244af HEAD@{1}: commit (amend): Third commit after amend 44c6588 (HEAD -> master) HEAD@{2}: commit: Third commit c64349c HEAD@{3}: commit: Second commit 368ea35 HEAD@{4}: commit (initial): First commit
I hope you started seeing how it works. We brought back the commit with hash 44c6588
to life again and we gained the new record in the reflog history. We exactly know what operation was done (commit, amend, reset) and where our HEAD pointer is. But all of it is available only to us and no one can see it outside of our local git repository.
Where is reflog stored?
Going a little bit deeper we can see all reflog records in .git/logs/HEAD
file. Below I included the content of my reflog file.
0000000000000000000000000000000000000000 368ea355b3db79db6566e19fba8fadbd8dea160f Oskar Bogacz <oskarbogacz@Oskars-MacBook-Pro.local> 1658582451 +0200 commit (initial): First commit 368ea355b3db79db6566e19fba8fadbd8dea160f c64349cfe18d164850cb9d42223f022a375ad6f7 Oskar Bogacz <oskarbogacz@Oskars-MacBook-Pro.local> 1658582467 +0200 commit: Second commit c64349cfe18d164850cb9d42223f022a375ad6f7 44c6588ad112a0e16b131a25bb263fa88665a634 Oskar Bogacz <oskarbogacz@Oskars-MacBook-Pro.local> 1658582486 +0200 commit: Third commit 44c6588ad112a0e16b131a25bb263fa88665a634 35244affdde83342e2d133ef37f9cf9451b39d34 Oskar Bogacz <oskarbogacz@Oskars-MacBook-Pro.local> 1658582711 +0200 commit (amend): Third commit after amend 35244affdde83342e2d133ef37f9cf9451b39d34 44c6588ad112a0e16b131a25bb263fa88665a634 Oskar Bogacz <oskarbogacz@Oskars-MacBook-Pro.local> 1658582827 +0200 reset: moving to 44c6588
There is no situation from which it is impossible to get out. Especially when you lost some commits! With git reflog
you can resurrect them from the afterlife by git reset
, git cherry-pick
or another mechanism to do commits movements. The last but not least, here you can find documentation of git reflog command.
The same programmers as us work everywhere.
Oskar K. Bogacz