All this time you’ve been committing things, branching, doing whatever. And Git’s been watching you, listening like Big Brother, recording everything you do.
And you can use this to your benefit.
Let’s say you’ve done something like a hard reset because you wanted to abandon the branch you were on.
But then, wait! You actually needed something from one of those commits you just reset past! Is there any way to get back to it? There’s no branch there, and you can’t remember the commit ID. And since it’s not an ancestor to anything, git log
won’t help you.
How can you get it back?
git reflog
to the rescue!
The reflog contains a record of all manner of things you’ve done along with commit IDs, and it keeps them for 90 days30. After that time, orphan commits (that is commits with no branch above them) will be garbage collected.
You can use it for all kinds of things.
Basically it gives you a way to look back on the linear history of the repo, and tells you the commit UUIDs along the way.
This means if you want to, say, hard reset the repo to some earlier state, you could look up that earlier commit in the reflog31.
Let’s run an example where we do the following:
foo.txt
, on the main
branch.topic1
.bar.txt
, and commit it.bar.txt
and commit the modification.topic1
. Switch back to the main
branch and force delete topic1
.topic1
for some reason. But you deleted the branch. Whoops.topic1
that you want.HEAD
).And here that is in Git, at least the first five steps:
$ echo 'Line 1' > foo.txt # Create foo.txt
$ git add foo.txt
$ git commit -m 'added foo.txt'
[main (root-commit) 90bd7cc] added foo.txt
1 file changed, 1 insertion($)
create mode 100644 foo.txt
$ git switch -c topic1 # Switch to topic1
Switched to a new branch 'topic1'
$ echo 'Line 1' > bar.txt # Create bar.txt
$ git add bar.txt
$ git commit -m 'added bar.txt'
[topic1 4219f83] added bar.txt
1 file changed, 1 insertion($)
create mode 100644 bar.txt
$ echo 'Line 2' >> bar.txt # Modify bar.txt
$ git add bar.txt
$ git commit -m 'appended to bar.txt'
[topic1 bf8b8cf] appended to bar.txt
1 file changed, 1 insertion($)
$ git switch - # Switch back to main
Switched to branch 'main'
$ git branch -D topic1 # Delete topic1 Deleted branch topic1 (was bf8b8cf).
At this point let’s say we want to look back at the commits we made on bar.txt
. Good luck with git log
!
$ git log
commit 90bd7cc6c3c530798872827ba02cb7db4fd422c2 (HEAD -> main)
Author: User <user@example.com>
Date: Fri Oct 4 16:24:56 2024 -0700
added foo.txt
That’s it? Where’s all the bar.txt
stuff? Well, it was on the topic1
commits, which were descendants from this commit 90bd7
. Because git log
only shows ancestors, we’re not seeing any of the bar.txt
changes.
So, finally, we arrive at the entire topic of this chapter: the reflog. Let’s take a peek.
$ git reflog
90bd7cc (HEAD -> main) HEAD@{0}: checkout: moving from topic1 to
main
bf8b8cf HEAD@{1}: commit: appended to bar.txt
4219f83 HEAD@{2}: commit: added bar.txt
90bd7cc (HEAD -> main) HEAD@{3}: checkout: moving from main to
topic1 90bd7cc (HEAD -> main) HEAD@{4}: commit (initial): added foo.txt
Hey, that’s more like it! I see the changes I made to bar.txt
in there! And I see the commit UUID on the left! This means I can switch to that commit!
$ git switch --detach bf8b8cf
HEAD is now at bf8b8cf appended to bar.txt
$ git log
commit bf8b8cf826bbf667cdd088cfcecbc1086c24de3b (HEAD)
Author: Brian "Beej Jorgensen" Hall <beej@beej.us>
Date: Fri Oct 4 16:24:56 2024 -0700
appended to bar.txt
commit 4219f83f22f8a90cb8d57128501facb58b292003
Author: Brian "Beej Jorgensen" Hall <beej@beej.us>
Date: Fri Oct 4 16:24:56 2024 -0700
added bar.txt
commit 90bd7cc6c3c530798872827ba02cb7db4fd422c2 (main)
Author: Brian "Beej Jorgensen" Hall <beej@beej.us>
Date: Fri Oct 4 16:24:56 2024 -0700
added foo.txt
There’s the log… and we have the file contents?
$ cat bar.txt
Line 1 Line 2
Yup!
Let’s switch back to main
and see what happens.
$ git switch -
Warning: you are leaving 2 commits behind, not connected to
any of your branches:
bf8b8cf appended to bar.txt
4219f83 added bar.txt
If you want to keep them by creating a new branch, this may be a good
time to do so with:
git branch <new-branch-name> bf8b8cf
Switched to branch 'main'
This is Git telling us, “Hey, I’m going to garbage collect these two commits after the 90 days are up. If you want to keep them, attach a branch to them.”
And it’s helpfully telling us how to do that.
So even though we force deleted topic1
earlier, we could now simply recreate it if we didn’t mean to do that. Let’s do that.
$ git branch topic1 bf8b8cf
$ git switch topic1
Switched to branch 'topic1'
$ cat bar.txt
Line 1 Line 2
As you can see, the reflog can get you out of all kinds of trouble when you thought you’d lost commits for good.
Let’s take a look at that example reflog output again:
$ git reflog
598c84e (HEAD -> main) HEAD@{0}: checkout: moving from topic1 to
main
dc3d6a3 HEAD@{1}: commit: appended to bar.txt
0789880 HEAD@{2}: commit: added bar.txt
598c84e (HEAD -> main) HEAD@{3}: checkout: moving from main to
topic1 598c84e (HEAD -> main) HEAD@{4}: commit (initial): added foo.txt
See that HEAD@{3}
-type stuff in there? You can use those to check out specific commits (instead of using the UUID, for example).
Now, HEAD@{3}
doesn’t mean “3 commits before HEAD
”. But it is an identifier you can use to switch to a particular commit.
$ git switch --detach HEAD@{1} HEAD is now at dc3d6a3 appended to bar.txt
Just like that.