Revisions and ranges

Understanding revisions and ranges is essential as many git commands take them as arguments.

Depending on the command, revisions denote a specific commit or, for commands which walk the revision graph … all commits which are reachable from that commit.

man gitrevisions

The example below is based on a repository due to Jon Loeliger (from the above man page). For convenience, with every commit we associate a branch whose name is the same as the (one-letter) commit message. Having branches is convenient as their names can be used to express revisions and ranges succinctly.

Revisions

The first two tabs below depict our example repository without and with branches (local branches can be visualized by passing the -l flag). The third tab includes annotations (each one is passed using the -a flag) with example uses of the caret (^) and tilde (~) symbols:

  • <rev>~<n>: the n-th generation ancestor of <rev>, following only the first parents

  • <rev>^<n>: the n-th parent of <rev>.

There are other ways to specify revisions. Some of them are shown in the last tab.

Visualize DAG
git dag -m 1
Visualize DAG
git dag -m 1 -l
Visualize DAG
git dag -m 1 -a A^0 -a A^ -a A^1 -a A~1 -a A^2 -a A^^ -a A^1^1 -a A~2 -a B^2 -a A^^2 -a B^3 -a A^^3 -a A^^^ -a A^1^1^1 -a A~3 -a D^2 -a B^^2 -a A^^^2 -a A~2^2 -a F^ -a B^3^ -a A^^3^ -a F^2 -a B^3^2 -a A^^3^2
Visualize DAG
git dag -m 1 -a C^{commit} -a HEAD -a :/H -a HEAD^{/F} -a @

Ranges

History traversing commands such as git log operate on a set of commits, not just a single commit.

man gitrevisions

Below we give examples with four ways to define such a set:

  • <rev>: commits reachable from <rev>
    • <rev1> <rev2>: union of commits reachable from <rev1> and <rev2>, etc.

  • <rev1>..<rev2>: commits reachable from <rev2> but not from <rev1>

  • <rev1>...<rev2>: commits reachable from either <rev1> or <rev2> but not from both

  • <rev>^@: all (direct/indirect) parents of <rev>.

In addition, the last two tabs depict the effect of using --ancestry-path in combination with a range [1]. For example, range D..A includes all reachable commits from A that are not reachable from D (i.e., {A, B, C, E, F, I, J}), while passing as well --ancestry-path=F, filters-out commit E from that set, because E cannot be reached from F and F cannot be reached from E.

Visualize DAG
git dag -m 1 -R D F
Visualize DAG
git dag -m 1 -R B..C
Visualize DAG
git dag -m 1 -R B...C
Visualize DAG
git dag -m 1 -R C^@
Visualize DAG
git dag -m 1 -R D..A
Visualize DAG
git dag -m 1 -R 'D..A --ancestry-path=F'

A diff particularity

It is worth pointing out that in the context of the git diff command, <rev1>..<rev2> and <rev1>...<rev2> do not represent ranges:

However, diff is about comparing two endpoints, not ranges, and the range notations … do not mean a range

man git-diff

A nice summary of using the range notation with git log and git diff can be found here.