Git Reflog
The git reflog
command keeps track of every change made in the references (branches or tags). This mechanism is called reference logs, or "reflog". Different Git commands take a parameter for specifying a reference or "ref" like git merge
, git reset
, git checkout
... . This parameter serves to point to a commit.
The "reflog" mechanism takes back even commits that are not referenced by any refs. It tracks when the refs are updated in the local repository. Reflogs are saved in directories under the local repository .git
directory.
When rewriting history, the reflog holds information about the past states of branches and makes it possible to return to that state if required.
The Basic Usage of Git Reflog Command
The common use of the git reflog
command is as follows:
$ git reflog
This is a shortcut that is equal to:
$ git reflog show HEAD
The command above will output the HEAD reflog
. The output looks similar to the following:
c6db2c8 HEAD@{0}: commit: changing content
90bb007 HEAD@{1}: checkout: moving from master to git_reflog_branch
90bb007 HEAD@{2}: commit: adding more content
90bb007 HEAD@{3}: reset: moving to HEAD
90bb007 HEAD@{4}: commit: modified content of reflog_file_demo
1a03d4b HEAD@{6}: commit (initial): initial commit
Reflog References
By default, the git reflog
command will output the reflog of HEAD
ref. The HEAD
is a reference to the currently active branch. The reflog mechanism works on all refs. A Git ref can be accessed using the name@{qualifier}
syntax. Additionally, to HEAD
refs, we can reference other refs like branches, remotes, tags, GiT stash.
To show the entire reflog of all the references, execute the following command:
$ git reflog show -all
If you want to see just the reflog of a specific branch, you can pass the branch's name as a parameter to the git reflog show
command. The command bellow outputs a reflog of 'demo_reflog_branch':
$ git reflog show demo_reflog_branch
128be37 demo_reflog_branch@{1}: commit: Add more content
eed1cef demo_reflog_branch@{2}: commit (initial): Initial commit
To show the reflog for a git stash, assuming that some changes are already stashed, run the following command:
$ git reflog stash
1a03d4b stash@{0}: WIP on demo_reflog_branch: a4d1cef git reflog more content
The displayed ref pointers of the git reflog
command can be passed to other Git commands like git diff as follows:
$ git diff stash@{0} demo_reflog_branch@{0}
The command above will display Git diff output comparing the stash@{0} changes against the demo_reflog_branch@{0} ref.
Timed Reflogs
Every reflog record has a corresponding timestamp associated with it. These timestamps can be leveraged as a qualifier, which is a token of Git ref pointer syntax. This permits to filter Git reflogs by time. Here are some examples of available time qualifiers:
- 1.minute.ago
- 1.hour.ago
- 1.day.ago
- yesterday
- 1.week.ago
- 1.month.ago
- 1.year.ago
- 2016-06-23.10:25:00
Time qualifiers can be used as a combination (e.g. 1.month.ago.2.hours.ago), or as plural forms (e.g. 3.weeks.ago).
Time qualifiers can be given to other git commands.
$ git diff master@{0} master@{1.month.ago}
The command above display a diff between the current master branch and the master 1 month ago.
Subcommands of Git Reflog
Some additional arguments of the git reflog
command are considered to be subcommands. These subcommands are presented as follows:
Show - git reflog show subcommand
The show
is the default option. For example, the following command:
$ git reflog maste@{0}
is similar to the command:
$ git reflog show master@{0}
The git reflog show
command is an alias for the git log -g --abbrev-commit --pretty=oneline
command.
Expire - git reflog expire subcommand
The git reflog expire
subcommand is used for removing old or unattainable reflog entries. This command may cause data loss. Git uses the expire
subcommand internally to change the expiration date of reflog entries. Adding a -n
or --dry-run
to the git reflog expire
command will execute a "dry run" that output which reflog entries are marked to be pruned without pruning them.
By default, the reflog expiration date is set to 90 days. To change the expiration time, we can pass a command-line argument --expire=time
to the git reflog expire
command or set a git configuration name of gc.reflogExpire.
Delete - git reflog delete subcommand
The git reflog delete
subcommand is used for deleting passed reflog entries. The git reflog delete
may cause data loss as git reflog expire
, so caution should be taken when utilizing the delete
option.
Recovering Lost Commits
Git never loses commits, even during history rewriting operations like rebasing or commit amending. For our example, we have made some new changes to our repository.
The output of the git log --pretty=online
command looks like the following:
$ git log --pretty=oneline
92b836f1a893d0ef628d1d6628ac9887655c7d08 migrating content
128be37f7bf911163de43239338479d1d66bd53f adding content
eed1cef01bdc42c4cbd0058a94e4cf80671513eb initial commit
Now we add some new changes to the repository and run the following:
# make changes to HEAD
$ git commit -am "DAO changes"
After the above command, the log will look like this:
$ git log --pretty=oneline
1a03d4bb1604fd7958d328909faafd90347403cf DAO changes
92b836f1a893d0ef628d1d6628ac9887655c7d08 migrating content
128be37f7bf911163de43239338479d1d66bd53f adding content
eed1cef01bdc42c4cbd0058a94e4cf80671513eb initial commit
At this stage, we perform an interactive rebase against the master by running the following command:
$ git rebase -i origin\master
Through the rebase, we mark some commits for squash using the s
rebase subcommand. In our example, we will squash a few commits into the most recent "DAO changes".
After the squash, the git log --pretty=oneliney
command will look like:
1a03d4bb1604fd7958d328909faafd90347403mp API changes eed1cef01bdc42c4cbd0058a94e4cf80671513eb initial commit
Now commits that are marked for squashing are no longer there. If we need to work on a squashed commit, we can use the reflog
command.
1a03d4b HEAD@{0}: rebase -i (finish): returning to refs/heads/git_reflog
c6db2c8 HEAD@{1}: rebase -i (start): checkout origin/master
90bb007 HEAD@{2}: commit: DAO changes
The above output shows reflog entries for the start and finish of the rebase, and before that is the "DAO changes" commit. The reflog ref can be passed to the git reset command to recover a commit before the rebase.
$ git reset HEAD@{2}
The command above will move HEAD
to point to the commit "DAO changes", and at the same time, restore the other squashed commits.