Understanding the Basics and Use Cases of git rev-list
Introduction
Recently, I came across the git rev-list
command, which I used to retrieve the total number of commits in a repository, as mentioned in the following article:
Fetching All Commit History in a Repository with GitHub Actions
I decided to explore the usage of this command and gather some useful options. Additionally, I considered the practical scenarios where this command could be handy.
Basic Usage
The git rev-list
command lists commit objects in reverse chronological order, following the parent commits from the specified branch (or commit).
For example, if you specify the main
branch, the output will be as follows:
$ git rev-list main
# Output
ebf897c26a983f936c2c280e5a50e171fa8e1dc0
a4df745d848e0992dd3a208332be48febf997241
0ed481ad7d21138c79a3785737830d5bd8018093
You can also specify a branch or commit. Let’s pass the second commit object from the previous output:
$ git rev-list a4df745d848e0992dd3a208332be48febf997241
# Output
a4df745d848e0992dd3a208332be48febf997241
0ed481ad7d21138c79a3785737830d5bd8018093
By specifying multiple branches or commits, you can perform set-like operations. The following example outputs the set difference between the union of commits reachable from hoge
and fuga
(excluding the commits reachable from hoga
):
git rev-list hoge fuga ^hoga
Additionally, you can use the <commit1>..<commit2>
syntax to retrieve the commit objects within that range. From the perspective of set operations mentioned earlier, this is equivalent to ^<commit1> <commit2>
, which excludes the common commits reachable from both branches.
Thus, the following two commands produce the same result:
$ git rev-list origin..HEAD
$ git rev-list HEAD ^origin
Additionally, there seems to be a notation using three dots <commit1>...<commit2>
. The documentation states that the result is “the symmetric difference between the two operands,” but to be honest, I’m not entirely sure what that means.
It is mentioned that the following two forms are equivalent, so it might mean outputting the difference excluding the merge base. If there are any experts out there, please enlighten us.
$ git rev-list A B --not $(git merge-base --all A B)
$ git rev-list A...B
That covers the basic usage of git rev-list
.
Useful Options
Let’s take a quick look at some useful options.
--count
Outputs the number of commits. This option is handy for obtaining the total count.
$ git rev-lsit --count HEAD
-<number>
, -n <number>
, --max-count=<number>
Limits the number of outputted commit objects. Useful when dealing with a large number of commits.
$ git rev-list -5 HEAD
--since=<date>
, --after=<date>
Specifies the date range of commit objects.
$ git rev-list --after='2022-10-10' HEAD
$ git rev-list --after='1 hour ago' HEAD
$ git rev-list --after=1.hour.ago HEAD
--author=<pattern>
, --committer=<pattern>
Filters commit objects by author or committer.
$ git rev-list --author=okaryo HEAD
--merges
Outputs only merge commits.
$ git rev-list --merges HEAD
Use Cases
While exploring rev-list, it’s important to consider practical use cases. Let’s explore a few scenarios.
Obtaining the Total Number of Commits
While this can be achieved using rev-list
alone, it is still a common use case. It was my first encounter with rev-list
, so it’s likely a popular use case.
$ git rev-list --count HEAD
Cherry-picking Multiple Commits
For me personally, the primary use of commit hashes is when cherry-pick
. git diff is another scenario where commit hashes may be used. These combined operations can be useful when cherry-pick
multiple commits without having to pick them one by one.
# To commit
$ git rev-list --reverse branch1..branch2 | git cherry-pick --stdin
# To only cherry-pick without committing
$ git rev-list --reverse branch1..branch2 | git cherry-pick -n --stdin
Reviewing Recent Changes for a Specific File
Another potential use case for commit hashes is when working with git diff
. By combining it with a file path, you can review recent changes for a specific file.
Note: --
is used as an identifier to pass file paths.
$ git diff $(git rev-list -1 HEAD -- package.json)^ -- package.json
These are just a few examples of potential use cases. There are countless other possibilities to explore and discover.
Conclusion
Although I haven’t used rev-list
extensively, the statement in the documentation stood out to me:
rev-list is a very essential Git command, since it provides the ability to build and traverse commit ancestry graphs.
This suggests that rev-list
possesses powers that I can’t even imagine yet.
Git offers numerous commands, and the Git journey seems to have endless depths to explore.