This article is part of our “Advanced Git” series. Be sure to follow us on Twitter or sign up for our newsletter to hear about future articles!
The “Reflog” is one of Git’s lesser-known features—but one that can be extremely helpful. Some people refer to it as a “safety net,” while I like to think of it as Git’s “diary.” That’s because Git uses it to keep a journal about every movement of the HEAD
pointer (i.e. every time you commit, merge, rebase, cherry-pick, reset, etc.). Git logs your actions in the Reflog which makes it a valuable logbook and a good starting point when something went wrong.
In this last part of our “Advanced Git” series, I’ll explain the differences between git log
and git reflog
, and I’ll show you how to use the Reflog to recover deleted commits as well as deleted branches.
Advanced Git series:
- Part 1: Creating the Perfect Commit in Git
- Part 2: Branching Strategies in Git
- Part 3: Better Collaboration With Pull Requests
- Part 4: Merge Conflicts
- Part 5: Rebase vs. Merge
- Part 6: Interactive Rebase
- Part 7: Cherry-Picking Commits in Git
- Part 8: Using the Reflog to Restore Lost Commits (You are here!)
git log
or git reflog
: what’s the difference?
In previous articles, I’ve recommended you use the git log
command to inspect previous events and look at your commit history, and that’s exactly what it does. It shows the current HEAD
and its ancestors, i.e. its parent, the next parent in line, etc. The log goes all the way back in the commit history by recursively printing every commit’s parent. It’s part of the repository which means it gets replicated after you push, fetch, or pull.
git reflog
, on the other hand, is a private and workspace-related recording. It doesn’t go through the list of ancestors. Instead, it shows an ordered list of all commits which HEAD
has pointed to in the past. That’s why you can think of it as some kind of “undo history” like you might see in word processors, text editors, etc.
This local recording technically isn’t part of the repository and it’s stored separately from the commits. The Reflog is a file in .git/logs/refs/heads/
and it tracks the local commits for every branch. Git’s diary usually gets cleaned up after 90 days (that’s the default setting), but you can easily adjust the expiration date of the Reflog. To change the number of days to 180, simply type the following command:
$ git config gc.reflogExpire 180.days.ago
Alternatively, you can decide that your Reflog should never expire:
$ git config gc.reflogExpire never
Tip: Remember that Git makes a distinction between the repository’s configuration file (.git/config
), the global per-user configuration ($HOME/.gitconfig
), and the system-wide settings (/etc/gitconfig
). To adjust the Reflog’s expiration date for the user or the system, add the --system
or --global
parameter to the commands shown above.
Enough theoretical background—let me show you how to work with git reflog
to correct mistakes.
Recovering deleted commits
Imagine the following scenario: After looking at your commit history, you decide to get rid of the last two commits. You courageously perform a git reset
, the two commits disappear from the commit history… and a while later, you notice that this was a mistake. You’ve just lost valuable changes and start to panic!
Do you really have to start from scratch again? You don’t. In other words: keep calm and use git reflog
!
So, let’s mess things up and make this mistake in real life. The next image shows our original commit history in Tower, a graphical Git client:
We want to get rid of two commits and make the “Change headlines for about and imprint” commit (ID: 2b504bee
) our last revision on the master
branch. All we need to do is copy the hash ID to the clipboard and then use git reset
on the command line and enter that hash:
$ git reset --hard 2b504bee
Voilà. The commits have disappeared. Now, let’s assume this was a mistake and take a look at the Reflog to recover the lost data. Type git reflog
to view the journal in your terminal:
You’ll notice that all entries are ordered chronologically. That means: the most recent—the newest—commits are at the top. And, if you look closely, you will notice the fatal git reset
action from a few minutes ago right at the top.
The journal seems to work—that’s good news. So, let’s use it to undo that last action and restore the state before the reset command. Copy the hash ID (which is e5b19e4
in this specific example) to the clipboard, like before. You could use git reset
again, which is totally valid. But in this case, I’m going to create a new branch based on the old state:
$ git branch happy-ending e5b19e4
Let’s take a look at our graphical Git client again:
As you can see, the new branch, happy-ending
, has been created and it includes the commits we deleted earlier—awesome, nothing is lost!
Let’s look at another example and use the Reflog to recover an entire branch.
Recovering deleted branches
The next example resembles our first scenario: we’re going to delete something—this time, it’s an entire branch that has to go. Maybe your customer or your team leader has told you to get rid of a feature branch, maybe it was your own idea to clean up. To make things worse, a commit (C3
in the picture) is not included in any of the other branches, so you’re definitely going to lose data:
Let’s actually do this and then recover the branch later:
Before you can delete the branch feature/login
, you need to step away from it. (As you can see in the screenshot, it’s the current HEAD
branch, and you can’t delete the HEAD
branch in Git.) So, we’re going to switch branches (to master
) and then we’re going to delete feature/login
:
Okay… now let’s say our customer or team lead had a change of heart. The feature/login
branch (including its commits) is wanted after all. What should we do?
Let’s take a look at Git’s diary:
$ git reflog
776f8ca (HEAD -> master) HEAD@{0}: checkout: moving from feature/login to master
b1c249b (feature/login) HEAD@{1}: checkout: moving from master to feature/login
[...]
Turns out we’re lucky again. The last entry shows our switch from feature/login
to master
. Let’s try to return to the state right before that and copy the hash ID b1c249b
to the clipboard. Next, we’re going to create a branch called feature/login
based on the desired state:
$ git branch feature/login b1c249b
$ git branch -vv feature/login b1c249b Change Imprint page title
* master 776f8ca Change about title and delete error page
Great—the branch is back from the dead and also includes that valuable commit we thought we had lost:
If you’re using Git in a desktop GUI like Tower, you can simply press CMD+Z to undo your last action, just like a text editor or word processor when you make a typo.
Keep calm and keep track
Git’s Reflog can be a real lifesaver! As you can see, it’s quite easy to bring lost commits or even entire branches out from the grave. What you need to do is find the correct hash ID in the Reflog—the rest is a piece of cake.
If you want to dive deeper into advanced Git tools, feel free to check out my (free!) “Advanced Git Kit”: it’s a collection of short videos about topics like branching strategies, Interactive Rebase, Reflog, Submodules and much more.
This was the last part in our series on “Advanced Git” here at CSS-Tricks. I hope you enjoyed the articles. Happy hacking!
Advanced Git series:
- Part 1: Creating the Perfect Commit in Git
- Part 2: Branching Strategies in Git
- Part 3: Better Collaboration With Pull Requests
- Part 4: Merge Conflicts
- Part 5: Rebase vs. Merge
- Part 6: Interactive Rebase
- Part 7: Cherry-Picking Commits in Git
- Part 8: Using the Reflog to Restore Lost Commits (You are here!)
The post Using the Reflog to Restore Lost Commits appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.