How Git’s reflog can save your ass

I think we all know, and hopefully love Git. Git is very powerful and even so, a lot of people (including myself) haven’t experienced the full power of Git. I for myself haven’t used reflog or even heard about it for a long time. However, reflogs can probably become really important.

What is this reflog?

Git describes reflogs as:

Reference logs, or “reflogs”, record when the tips of branches and other references were updated in the local repository. Reflogs are useful in various Git commands, to specify the old value of a reference.

Sure, this sounds a bit complicated, but in the end it’s fairly simple:

For every “update” we do in our local repository, Git will create a reference log entry.

An example

Let’s say you’ve a git project with some files in it:

-rw-r--r--  1 dbarton  staff  6  9 Dec 14:02 file1
-rw-r--r--  1 dbarton  staff  6  9 Dec 14:03 file2
-rw-r--r--  1 dbarton  staff  6  9 Dec 14:03 file3

We check the status of our local repository and all looks fine:

$ git status
On branch develop
nothing to commit, working tree clean

Now we want to add a new feature and therefor we create a new branch:

$ git checkout -b awesome-feature
Switched to a new branch 'awesome-feature'

Let’s say our feature needs a new file and a change on an existing one:

$ git status
On branch awesome-feature
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   file3

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	file4

no changes added to commit (use "git add" and/or "git commit -a")

Looks fine, so let’s stage the files and commit them into our awesome-feature branch:

$ git add file3 file4

$ git status
On branch awesome-feature
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   file3
	new file:   file4

$ git commit -m"awesome feature"
[awesome-feature a760e09] awesome feature
 2 files changed, 3 insertions(+)
 create mode 100644 file4

Time flies and we’ve to work on another topic now, however our feature isn’t finished yet. So we’ll switch back to the develop branch:

git checkout develop

After some changes our develop branch diverged from the awesome-feature branch and we have all those new shiny commits:

$ git log --oneline --decorate --graph --all

* 7befa5b (HEAD -> develop) Refactoring some code bits
* 23666bd Another great feature
* 4c0680f Another important bugfix
| * a760e09 (awesome-feature) awesome feature
|/
* de5ed6e Initial commit

Now we could easily jump between branches or commits we’ve done in the past, just by using our lovely git checkout command.

However, we could also use our reference log to see what we’ve done so far!

Reflog

The reference log is a list of local actions, if you want so:

$ git reflog

7befa5b HEAD@{0}: commit: Refactoring some code bits
23666bd HEAD@{1}: commit: Another great feature
4c0680f HEAD@{2}: commit: Another important bugfix
de5ed6e HEAD@{3}: checkout: moving from awesome-feature to develop
a760e09 HEAD@{4}: commit: awesome feature
de5ed6e HEAD@{5}: checkout: moving from develop to awesome-feature
de5ed6e HEAD@{6}: commit (initial): Initial commit

As you can see, these are all the actions you’ve done with git – not only commits! While git log shows only the logs of commits, git reflog shows you all your local actions. This might be comparable with the bash history.

For example I see that “4 changes ago” I’ve commited the commit awesome feature. I can now reference to this commit with its SHA string de5ed6e or by using the HEAD@{4} syntax.

Now you might say: All fine comrade, but I can also use the normal git log as I can see the same commit in there too. So why/when the heck should I use the reflog over log?

First of all, the reflog is a “changelog” of all the steps you’ve done so far. This is quite nice, but trust me it gets better!

Let’s say you’ve pushed your awesome-feature branch to the remote origin, or already merged it into your develop. So you might no longer need the branch in your local repository and therefor you delete it:

$ git branch -D awesome-feature
Deleted branch awesome-feature (was a760e09).

Now you’ll have a look at the log again:

$ git log --oneline --decorate --graph --all

* 7befa5b (HEAD -> develop) Refactoring some code bits
* 23666bd Another great feature
* 4c0680f Another important bugfix
* de5ed6e Initial commit

Oh, farts… Did I just delete the awesome-feature branch before merging it locally or pushing it to the origin? Did I just lost all my changes? Exactly this happened to me twice so far. It’s late night, you’re jumping between branches and accidentally deleting the wrong branch (it was really late and I was tired as f*).

Now you’re lost, aren’t you? No, you’re not, because there’s our saviour, our magic reference log:

$ git reflog

7befa5b HEAD@{0}: commit: Refactoring some code bits
23666bd HEAD@{1}: commit: Another great feature
4c0680f HEAD@{2}: commit: Another important bugfix
de5ed6e HEAD@{3}: checkout: moving from awesome-feature to develop
<strong>a760e09 HEAD@{4}: commit: awesome feature</strong>
de5ed6e HEAD@{5}: checkout: moving from develop to awesome-feature
de5ed6e HEAD@{6}: commit (initial): Initial commit

And there it is, our missing commit. Let’s get it and restore our branch:

$ git checkout HEAD@{4}
Note: checking out 'HEAD@{4}'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at a760e09... awesome feature

$ git checkout -b awesome-feature
Switched to a new branch 'awesome-feature'

A quick look on our log tells us we’re safe again:

$ git log --oneline --decorate --graph --all

* 7befa5b (develop) Refactoring some code bits
* 23666bd Another great feature
* 4c0680f Another important bugfix
| * a760e09 (HEAD -> awesome-feature) awesome feature
|/
* de5ed6e Initial commit

The Git reference log finally saved our asses – YAY! This is awesome, innit? And the best thing is, it also works when you messed up your (local) rebase 😉

Reflog and expire

But be warned. Git reference logs can be expire, which can be controlled with the expire argument. The expire time can also be configured via gitconfig! However this must be explicitly configured.

If you didn’t knew reference logs so far then you might ask yourself: Wait, you say all those sensitive commits (e.g. accidentally commited passwords) I removed in the past by rewriting the history are still there in the reflog? Yes mate, all those sensitive commits might still be existing if they didn’t expire – which they don’t do by default 😉

3 Comments

  • Ivan Reply

    nice one

  • Bill Gale Reply

    Very helpful. Here are some additional useful reflog tricks.
    $ git reflog

    display file from reflog commit
    $ git show HEAD@{}:./

    create local copy of file from reflog commit
    $ git show HEAD@{}:./ >

    examples:
    $ git show HEAD@{31}:./.gitlab-ci.yml
    $ git show HEAD@{31}:./.gitlab-ci.yml > ./.gitlab-ci.yml.31

    also git diff works with commits in the reflog.
    $ git diff ./

    example:
    $ git diff e9f1f87 25dae48 ./.gitlab-ci.yml

  • Bill Gale Reply

    Trying again the web page swallowed my code :(.

    display file from reflog commit:
    $ git show HEAD@{##}:./filepath

    create local copy of file from reflog commit:
    $ git show HEAD@{##}:./filepath > newfile

    diffing file across reflog commits:
    $ git diff oldsha newsha ./filepath

    examples:
    $ git show HEAD@{31}:./.gitlab-ci.yml
    $ git show HEAD@{31}:./.gitlab-ci.yml > ./.gitlab-ci.yml.31
    $ git diff e9f1f87 25dae48 ./.gitlab-ci.yml

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.