paint-brush
Time to Rewrite your Git History Effectively with git reflogby@pragativerma
5,743 reads
5,743 reads

Time to Rewrite your Git History Effectively with git reflog

by Pragati VermaMay 9th, 2022
Read on Terminal Reader
Read this story w/o Javascript

Too Long; Didn't Read

Git records changes in a variety of ways to ensure that you never lose a committed change. A reflog is a reference, often known as a "ref**," is a pointer to a commit or branch that many Git commands accept as a parameter. By default, reflogs keep track of each `HEAD` position throughout the last 90 days. The reflog history is exclusive to the repository and is not accessible remotely. Each reflog entry has a timestamp attached to it that can also be leveraged as a qualifier token for the Git ref pointer syntax.

Company Mentioned

Mention Thumbnail
featured image - Time to Rewrite your Git History Effectively with git reflog
Pragati Verma HackerNoon profile picture


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.


What is a reflog?

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.


Basic Configuration

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


References to Reflog

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}


Sub-commands and configuration options

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.


git reflog show

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.


git reflog expire

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.


git reflog delete

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 vs git log

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 as your safety net

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.


Conclusion

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.