Selecting the revision range

Now that you can specify individual revisions in multiple ways, let's see how to specify ranges of revisions, a subset of the DAG we want to examine. Revision ranges are particularly useful for viewing selected parts of history of a project.

For example, you can use range specifications to answer questions such as, "What work is on this branch that I haven't yet merged into my main branch?" and "What work is on my main branch I haven't yet published?", or simply "What was done on this branch since its creation?".

Single revision as a revision range

History traversing commands such as git log operate on a set of commits, walking down a chain of revisions from child to parent. These kind of commands, given a single revision as an argument (as described in the Single revision selection section of this chapter), will show the set of commits reachable from that revision, following the commit ancestry chain, all the way down to the root commits.

For example, git log master would show all the commits reachable from the tip of a master branch (all the revisions that are or were based on the current work on the said branch), which means that it would show the whole master branch, the whole line of development.

Double dot notation

The most common range specification is the double-dot syntax, A..B. For a linear history, it means all the revisions between A and B, or to be more exact, all the commits that are in B but not in A, as shown in Fig 3. For example, the range, HEAD~4..HEAD, means four commits: HEAD, HEAD^, HEAD^^, and HEAD^^^ or in other words, HEAD~0, HEAD~1, HEAD~2, and HEAD~3, assuming that there are no merge commits starting between the current branch and its fourth ancestor.

Double dot notation

Fig 3. A double dot notation A..B for linear history; the selected revision range is shown in orange

Note

If you want to include a starting commit (in general case: boundary commits), which Git considers uninteresting, you can use the --boundary option to git log.

Double dot notation

Fig 4. A double dot notation A..B for a non-linear history (revision A is not an ancestor of revision B); where, the selected revision range is orange, while the excluded revisions are shaded, and boundary revision is marked with a thick outline

The situation is more complicated for history that is not a straight line. One such case is when A is not the ancestor of B (there is no path in the DAG of revisions leading from B to A) but both have a common ancestor, like in Fig 4. Another situation with non-linear history is when there are merge commits between A and B, as shown in Fig 5. Precisely in view of nonlinear history the double-dot notation A..B, or "between A and B", is defined as reachable from A and not reachable from B.

Double dot notation

Fig 5 A double dot notation for a non-linear history, with merge commits between A and B. To exclude commits marked with star "*" use --strict-ancestor.

Git A..B means a range of all the commits that are reachable from one revision (B) but are not reachable from another revision (A), following the ancestry chain. In the case of divergent A and B, like in Fig 4, this is simply all commits in B from the branch point of A.

For example, say your branches master and experiment diverge. You want to see what is in your experiment branch that hasn't yet been merged into your master branch. You can ask Git to show you a log of just those commits with master..experiment.

If, on the other hand, you want to see the opposite—all the commits in master that aren't in experiment—you can reverse the branch names. The experiment..master notation shows you everything in master not reachable from experiment.

Note

Another example, origin/master..HEAD, shows what you're about to push to the remote (commits in your current branch that are not yet present in the master branch in the remote repository origin), while HEAD..origin/master shows what you have fetched but not yet merged in. You can also leave off one side of the syntax to have Git assume HEAD: origin/master.. is origin/master..HEAD and ..origin/master is HEAD..origin/master; Git substitutes HEAD if one side is missing.

Git uses double-dot notation in many places, for example in git fetch and git push output for an ordinary fast-forward case, so you can just copy and paste a fragment of output as parameters to git log. In this case, the beginning of the range is the ancestor of the end of the range; the range is linear:

$ git push
To https://git.company.com/random
   8c4ceca..493e222  master -> master

Multiple points – including and excluding revisions

The double-dot A..B syntax is very useful and quite intuitive, but it is really a shorthand notation. Usually it is enough, but sometimes you might want more than it provides. Perhaps, you want to specify more than two branches to indicate your revision, such as seeing what commits are in any of the several branches that aren't in the branch you're currently on. Perhaps you want to see only those changes on the master branch that are not in any of the other long-lived branches.

Git allows you to exclude the commits that are reachable from a given revision by prefixing the said revision with a ^. For example, to view all the revisions which are on maint or master but are not in next, you can use git log maint master ^next. This means that the A..B notation is just a shorthand for B ^A.

Instead of having to use ^ character as a prefix for each of the revisions we want to exclude, Git allows you to use the --not option, which negates all the following revisions. For example, B ^A ^C might be written as B --not A C. This is useful, for example, when generating those excluded revisions programmatically.

Thus, these three commands are equivalent:

$ git log A..B
$ git log B ^A
$ git log B --not A

The revision range for a single revision

There is another useful shortcut, namely A^!, which is a range composed of a single commit. For non-merge commits, it is simply A^..A.

For merge commits, the A^!, of course, excludes all the parents. With the help of yet another special notation, namely A^@, denoting all the parents of A (A^1, A^2,…, A^n), we can say that A^! is a shortcut for A --not A^@.

Triple-dot notation

The last major syntax for specifying revision ranges is the triple-dot syntax, A...B. It selects all the commits that are reachable by either of the two references, but not by both of them, see Fig 6. This notation is called the symmetric difference of A and B.

Triple-dot notation

Fig 6. A triple-dot notation A...B for a non-linear history, where the selected range is shown in orange color, boundary commit O is marked with a bold outline, and the characters below and above the nodes show --left-right markers

It is a shortcut notation for A B --not $(git merge-base --all A B), where $(…) denotes shell command substitution (using POSIX shell syntax). Here, it means that the shell will first run the git merge-base command to find out all the best common ancestors (all merge bases), and then paste back its output on the command line, to be negated.

A common switch to use with the git log command with triple dot notation is --left-right. This option makes it show which side of the range each commit is in by prefixing commits from the left side (A in A...B) with <, and those from the right (B in A...B) with >, as shown in Fig 6 and the following example. This helps make the data more useful:

$ git log --oneline --left-right 37ec5ed...8cd8cf8
>8cd8cf8 Merge branch 'fc/remote-helper-refmap' into next
>efcd02e Merge branch 'rs/more-starts-with' into next
>831aa30 Merge branch 'jm/api-strbuf-doc' into next
>1aeca19 Merge branch 'jc/revision-dash-count-parsing' into next
<1a7e8e8 Revert "replace: add --graft option"
<7a30690 t9001: avoid non-portable '
' with sed
>5cc3268 fetch doc: remove "short-cut" section

Note

If the --left-right option is combined with --boundary, these normally uninteresting boundary commits are prefixed with -.

In the case of using a triple-dot A...B revision range, these boundary commits are git merge-base --all A B.

Git uses triple-dot notation in the git fetch and git push output when there was a forced update, in cases where the old version (left-hand side) and the updated version (right-hand side) diverged, and the new version was forced to overwrite the old version:

$ git fetch
From git://git.kernel.org/pub/scm/git/git
 + 37ec5ed...8cd8cf8 next       -> origin/next  (forced update)
 + 9478935...16067c9 pu         -> origin/pu  (forced update)
   d0b0081..1f58507  todo       -> origin/todo

Tip

Using revision range notation in diff

To make it easier to copy and paste revisions between log and diff commands, Git allows us to use revision range double-dot notation A..B and triple-dot A...B to denote a set of revisions (endpoints) in the git diff command.

For Git, using git diff A..B is the same as git diff A B, which means the difference between revision A and revision B. If the revision on either side of double dot is omitted, it will have the same effect as using HEAD instead. For example, git diff A.. is equivalent to git diff A HEAD.

The git diff A...B notation is intended to show the incoming changes on the branch B. Incoming changes mean revisions up to B, starting at a common ancestor, that is, a merge base of both A and B. Thus, writing git diff A...B is equivalent to git diff $(git merge-base A B) B; note that git merge-base is without --all here. The result of this convention makes it so that a copy and paste of the git fetch output (whether with double-dot or triple-dot) as an argument to git diff will always show fetched changes. Note, however, that it does not include the changes that were made on A since divergence!

Additionally, this feature makes it possible to use a git diff A^! command to view how revision A differs from its parent (it is a shortcut for git diff A^ A).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset