By admin , 28 April 2026

Introduction

"Reachability" is the most important property of a Git object. An object is reachable if some ref leads to it via parent or tree links. Unreachable objects are eligible for garbage collection.

Reachability rules

Starting from any ref:

  • A commit reaches its root tree, all parents, and (transitively) those parents' trees and parents.
  • A tree reaches all entries (blobs and subtrees).
  • An annotated tag reaches the tagged object.

Refs include local branches, remote-tracking branches, tags, stash, notes, and the reflog (until expiration).

Listing reachable objects

git rev-list --objects --all | head
git rev-list --objects HEAD
git rev-list --count HEAD

Ancestry tests

git merge-base --is-ancestor X Y && echo "X is ancestor of Y"
git rev-list X..Y --count                # how many commits lie between
git log --ancestry-path A..B --oneline   # only commits on path A to B

--ancestry-path is invaluable for finding the chain of commits that brought a fix from one tag to another.

Range operators

  • A..B: reachable from B, not from A.
  • A...B: symmetric difference (either side, not both).
  • ^A B C: reachable from B or C but not A.
git log main..feature                 # commits added by feature
git log main...feature --left-right   # both sides labelled

Unreachable objects

git fsck --unreachable
git fsck --dangling
git fsck --lost-found      # writes them to .git/lost-found

Unreachable objects survive until git gc --prune removes them; the default grace period is two weeks (gc.pruneExpire).

The role of the commit-graph file

Reachability and ancestry queries are the bottleneck of many Git operations. The commit-graph file precomputes generation numbers and parent lists, making these queries O(log n) instead of O(n):

git commit-graph write --reachable
git config --global core.commitGraph true

Bitmap reachability

Server-side, reachability bitmaps let Git answer "what objects are reachable from this commit?" in microseconds, by storing a precomputed bit per object. They are written by:

git repack -adb

Walks limited by date or path

The reachability machinery accepts many limiters that combine with range syntax:

git rev-list --since=2024-01-01 main
git rev-list main -- path/to/file
git rev-list --no-merges main..feature
git rev-list --first-parent main
git rev-list HEAD --count

Combine these to answer specific questions like "how many non-merge commits did Ada land on main last quarter?" without resorting to shell pipelines. Plumbing's stable output makes the result trivial to feed into reporting tools.

Common mistakes

Believing a deleted branch erases its commits; they remain reachable through the reflog for 90 days, and unreachable but unpruned for two more weeks. Confusing A..B with A...B; one is asymmetric, the other symmetric. Using HEAD~3 to navigate across merges and being confused by first-parent traversal; use HEAD^2 to switch to the second parent. Finally, expecting git fsck to find lost commits after a fresh clone; the reflog and dangling objects only exist locally where the work was done.