Skip to content
LatchkeyLatchkey home

git rev-list --count and --left-right in CI

git rev-list --count <range> prints how many commits are in a range, and git rev-list --left-right --count A...B prints the ahead/behind counts between two branches.

When a pipeline needs to know "how many commits since the tag" or "is this branch behind main", rev-list is the plumbing that gives an exact number. The symmetric-difference form is how you compute ahead/behind.

What it does

git rev-list lists commit SHAs in a range. With --count it prints just the total. The triple-dot symmetric difference A...B with --left-right --count yields two numbers: commits in A not in B, and in B not in A, which is the ahead/behind relationship.

Common usage

Terminal
# commits since the last tag
git rev-list --count v1.4.0..HEAD
# total commits on the branch
git rev-list --count HEAD
# ahead/behind vs origin/main (prints: <ahead>  <behind>)
git rev-list --left-right --count origin/main...HEAD
# count excluding merges
git rev-list --count --no-merges main..feature

Options

FlagWhat it does
--countPrint the number of commits instead of listing them
--left-rightMark/count each side of a symmetric difference (A...B)
--no-mergesExclude merge commits
--first-parentFollow only the first parent of merges
--since=<date> / --until=<date>Bound by commit date
--allConsider all refs as starting points

In CI

Ahead/behind gates need both refs present; a shallow clone may not have the merge base, giving wrong counts, so --unshallow or fetch enough depth first. Remember A...B (three dots) is symmetric difference for --left-right, while A..B (two dots) is the one-sided range used with plain --count.

Common errors in CI

"fatal: bad revision 'v1.4.0..HEAD'" means v1.4.0 is unknown (shallow clone or missing tag). On a shallow clone, counts can be capped at the shallow boundary and silently wrong, no error, just a smaller number than reality. "fatal: ambiguous argument" means a ref name does not resolve; check it with git rev-parse --verify.

Using this in CI

CI checkouts are shallow and detached by default, which changes the answer this command gives you. Commands that read history, branch names, or tags need the checkout configured for it.

.github/workflows/ci.yml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0   # history, tags, and git describe all need this

- run: |
    git rev-parse --is-shallow-repository   # expect false
    git rev-parse --abbrev-ref HEAD          # prints HEAD when detached

Frequently asked questions

git rev-list --count and --left-right in CI?
When a pipeline needs to know "how many commits since the tag" or "is this branch behind main", rev-list is the plumbing that gives an exact number. The symmetric-difference form is how you compute ahead/behind.
What it does?
git rev-list lists commit SHAs in a range. With --count it prints just the total. The triple-dot symmetric difference A...B with --left-right --count yields two numbers: commits in A not in B, and in B not in A, which is the ahead/behind relationship.
In CI?
Ahead/behind gates need both refs present; a shallow clone may not have the merge base, giving wrong counts, so --unshallow or fetch enough depth first. Remember A...B (three dots) is symmetric difference for --left-right, while A..B (two dots) is the one-sided range used with plain --count.
Common errors in CI?
"fatal: bad revision 'v1.4.0..HEAD'" means v1.4.0 is unknown (shallow clone or missing tag). On a shallow clone, counts can be capped at the shallow boundary and silently wrong, no error, just a smaller number than reality. "fatal: ambiguous argument" means a ref name does not resolve; check it with git rev-parse --verify.

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card