Git is the most widely used version control system today, and it keeps track of changes in a variety of ways to ensure that you never lose a committed change. Furthermore, the control it gives you over development workflows means you can determine exactly what your project's history looks like. Git has several mechanisms for rewriting the commit history to help with this, including git commit --amend
, git rebase
, and git reflog
.
In this article, you'll learn how to utilize git reflog
to re-organize and rewrite your Git commit history effectively and easily, while mitigating the risk that rewriting the commit history often brings.
Git uses a system called reference log, or simply "reflog," to keep track of modifications to the branches' tips. A reference, often known as a "ref," is a pointer to a commit or branch that many Git commands accept as a parameter. git checkout
, git reset
, and git merge
are examples of some common git commands that accept refs as parameters.
By default, reflogs keep track of each HEAD
position throughout the last 90 days. Furthermore, the reflog history is exclusive to the repository and is not accessible remotely. Apart from branch tip reflogs, there is a separate reflog for the Git stash.
Reflogs are stored in specific directories under the local repository's .git
directory. These git reflog
directories can be found at .git/logs/refs/heads/.
, .git/logs/HEAD
, and also .git/logs/refs/stash
if the git stash
has been used on the repository.
The basic reflog commands are as follows:
# To see activity on HEAD
git reflog show
The output of the above command looks similar to the following:
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
Other common usages are as follows:
# To see activity on HEAD, including timestamp
git reflog show --date=relative
#or
git reflog --relative-date
# same, on some_branch
git reflog show --date=relative some_branch
By default, git reflog
outputs the HEAD
ref's reflog. The symbol HEAD
denotes the currently active branch. There are also reflogs available for other refs. The syntax used to access a git ref is name@{qualifier}
, for example - otherbranch@{0}
. In addition to HEAD
refs, other branches, tags, remotes, and Git stash can be referenced as well.
To see the complete reflog of all refs, you can execute:
git reflog show --all
Other than the ordered indexes, each reflog entry has a timestamp attached to it that can also be leveraged as a qualifier token for the Git ref pointer syntax. This is what enables the filtering of these reflog entries by time. Examples of commonly used time qualifiers include:
@{0}
@{6.minutes.ago}
@{2.hour.ago}
@{3.day.ago}
@{5.weeks.ago}
@{8.years.ago}
@{today}
@{2022-01-23.08:30:00}
@{1.day.10.hours.ago}
These time qualifiers can be combined (e.g. 1.day.3.hours.ago
). Plural forms of these time qualifiers are also accepted (e.g. 5.minutes.ago
). They can be used along with the git reflog
command as follows:
git reflog show develop@{3.days.ago}
git reflog
accepts some additional arguments which are thus considered as sub-commands, such as show
, expire
, and delete
. Let’s discuss these sub-commands in detail.
As discussed earlier, show
is implicitly passed by default. Executing git reflog show
will display the log for the passed arguments.
For instance:
git reflog develop@{0}
is the same as
git reflog show develop@{0}
In addition, git reflog show
is an alias for git log -g --abbrev-commit --pretty=oneline
.
The expire
sub-command helps in cleaning up old or unreachable reflog entries.
The
expire
sub-command has the potential to cause data loss.
However, this sub-command is not typically used by the end users but by git internally.
A “dry run” can be performed by passing a -n
or --dry-run
option to git reflog expire
to output which reflog entries are marked to be pruned, such that they will not be actually pruned. This can help as a safety net while cleaning up the expired reflog entries.
Morevover, the expiration time can be specified by passing a command line argument --expire=time
to git reflog expire
or by setting a git configuration name of gc.reflogExpire
.
The delete subcommand, as its name implies, deletes the passed reflog entry. Delete, like expire, has the potential to cause data loss and is not frequently used by end-users.
git reflog
and git log
are the two similarly named components provided by Git that let us sneak into the repository’s commit history, logs, and reflogs. The fact that the two components often exhibit the same history, especially when a developer completes a number of local commits without a fetch or pull, is one of the reasons for the Git reflog vs. log confusion.
However, these are essentially different and have different use cases.
Let’s understand the underlying differences as well as similarities of the above two commands.
The most notable difference between Git reflog and log is that the log is a public record of the repository's commit history, whereas the reflog is a private, workspace-specific record of the repo's local commits.
After a push, fetch or pull, the Git log is replicated as part of the Git repository. The Git reflog, on the other hand, is not included in the duplicated repo. Without physical access to the computer where the local repository is kept, a developer cannot examine the reflog.
The reflog is a file found in .git\logs\refs\heads
that tracks the history of local commits for a specific branch and excludes any commits that may have been pruned away by Git garbage collection processes. The Git log, on the other hand, provides a historical commit traversal of a branch that begins with the most recent commit and concludes with the very first commit in the branch's history.
Git reflog can be used as a safety net during development as you can't lose data from your repo once it's been committed if you understand the concept of reflog correctly. You can use the reflog to see where you were before and git reset --hard
to get back to that ref to restore your prior state if you unintentionally reset to an older commit, rebase incorrectly, or perform any other operation that visibly "removes" commits.
Remember that refs refer to the full history of the commit, not just the commit itself.
In this article, we discussed the extended configuration options of git reflog
, common use-cases, and pitfalls of git reflog
.
To summarize, Git keeps a reflog, which is a log of where your HEAD
and branch references have been for the last few months(90 days), in the background while you're working. Git saves information in this temporary history every time your branch tip is modified for any reason.
The reflog command can also be used to delete or expire entries that are too old from the reflog. The expire
the sub-command is used to remove outdated reflog entries and the delete
sub-command is used to delete and specify the specific entry to be deleted from the reflog.