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

> git rev-list --count counts commits in a range and --left-right --count compares two branches (ahead/behind). Reference for the flags CI uses to gate merges.

Source: https://latchkey.dev/learn/command-reference/git-rev-list-count-ancestry  
Updated: 2026-06-30

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

| Flag | What it does |
| --- | --- |
| --count | Print the number of commits instead of listing them |
| --left-right | Mark/count each side of a symmetric difference (A...B) |
| --no-merges | Exclude merge commits |
| --first-parent | Follow only the first parent of merges |
| --since=<date> / --until=<date> | Bound by commit date |
| --all | Consider 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
```

> `git rev-parse --abbrev-ref HEAD` returns the literal string `HEAD` on a detached checkout rather than a branch name. On GitHub Actions read `github.ref_name` instead; the git command cannot know what it was checked out for.

## FAQ

### 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.

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
