Git Internals

Revision Selection and Commit Ranges

Many advanced Git commands are really working over sets of commits expressed through revision syntax, not just single named commits.

Who This Is For
  • Readers building a durable Git mental model
  • Developers who keep running into history, ref, or recovery confusion
Prerequisites
  • Comfort reading basic Git output
  • A rough idea of commits, branches, and HEAD
Common Risks
  • Learning low-level terms without connecting them to commands
  • Collapsing objects, refs, and working state into one concept

Many people experience git log, git diff, and git show syntax as a pile of unrelated tricks.
But Git is actually sharing a common language across many commands:

revision selection syntax

That language is not only about naming one commit. It is often about describing a set of commits, a range, or even trees and blobs reachable from revisions.

Why revision syntax matters

Because many Git commands are not really operating on a single point. They are operating on a subset of the commit graph.

Examples:

  • git log A..B
  • git diff main...feature
  • git show HEAD~2
  • git rev-list --ancestry-path

These may look different on the surface, but they are all asking Git to select some portion of repository history.

Naming single revisions

The most literal way is a full SHA, but in practice people rely on friendlier expressions such as:

  • HEAD
  • main
  • feature
  • HEAD~1
  • HEAD^

Those names matter because of how they walk the commit graph.

Why ~ and ^ are different

This is one of the first useful distinctions:

  • HEAD~3 means “walk back three times along the first-parent chain”
  • HEAD^ means “the parent of this commit”
  • HEAD^2 matters on merge commits and means “the second parent”

So:

  • ~ is about repeated first-parent walking
  • ^ is about selecting a particular parent edge

That matters especially when merge commits are involved.

What two-dot and three-dot really do

People often memorize:

  • A..B
  • A...B

without building a durable model.

A more stable way to think about them is:

  • two-dot often expresses “what is reachable from B but not from A”
  • three-dot often brings merge-base logic into the comparison

That is why:

  • git log A..B
  • git diff A...B

use similar notation but do not always mean the exact same thing in the exact same way.

Why merge-base keeps showing up

When Git compares two lines of development, it does not only care that you named two refs. It also cares about:

  • where their common ancestry is
  • what both sides already share
  • what is unique on each side

That is why revision syntax ties so naturally into commit-graph and merge-base understanding.

What revision syntax is selectingWhether you write `HEAD~3`, `A..B`, or `A...B`, you are really slicing the commit graph in different ways.
single revision
HEADHEAD~1HEAD^2
two-dot range
A..BB only
three-dot / merge base
AMBBA...B
Revision syntax is a slicing language for the commit graph

Expressions like HEAD~3, A..B, and A...B are easiest to understand as graph-slicing instructions rather than random command-line tricks.

Why this clarifies many commands

Once you see revision syntax as a shared language, many commands feel more unified:

  • log displays a selected set
  • rev-list walks a selected set
  • diff compares states or ranges around selected revisions
  • show explains an object or revision target

That makes the syntax feel less arbitrary and more like a common substrate.

A conclusion worth keeping

Many advanced Git commands operate on sets of commits and objects described by revision syntax, not only on single named revisions.