I would like to select precisely some disjoint commits using the git diff
command.
Here is an example. The commits are disposed on a one-branch tree, starting with A (the oldest commit), and ending with Z (the most recent commit):
A
|
B
|
C
|
D
|
...
|
Y
|
Z
I would like to select two ranges of commits, B-D and O-Q (included), and use them with git diff
. This can be made with two commands:
git diff B^..D
git diff O^..Q
Is it possible to do it using only one git diff
command, in order to have only one output? Just imagine that O reverts D, it is helpful not to have O and D described separately.
That should be possible with Git 2.19 (Q3 2018, three years later) with the new git range-diff
command.
git range-diff B..D O..Q
This is inspired by trast/tbdiff
, by Thomas Rast and Thomas Gummerer.
See commit a7be92a, commit 2752679, commit d1f87a2, commit 7190a67, commit ba931ed, commit 0b91faa, commit 31cf61a, commit f7c3b4e, commit 7188260, commit faa1df8, commit 4eba1fe, commit eb0be38, commit 1cdde29, commit 5e242e6, commit a142f97, commit c8c5e43, commit 9dc46e0, commit d9c66f0, commit 348ae56 (13 Aug 2018) by Johannes Schindelin (dscho
).
See commit 8884cf1 (13 Aug 2018) by Thomas Rast (``).
(Merged by Junio C Hamano -- gitster
-- in commit 81eab68, 20 Aug 2018)
range-diff
: show the diff between patches
Just like
tbdiff
, we now show the diff between matching patches. This is a "diff of two diffs", so it can be a bit daunting to read for the beginner.An alternative would be to display an interdiff, i.e. the hypothetical diff which is the result of first reverting the old diff and then applying the new diff.
Especially when rebasing frequently, an interdiff is often not feasible, though: if the old diff cannot be applied in reverse (due to a moving upstream), an interdiff can simply not be inferred.
This commit brings
range-diff
closer to feature parity with regard to tbdiff.To make
git range-diff
respect e.g.color.diff.*
settings, we have to adjustgit_branch_config()
accordingly.Note: while
tbdiff
accepts the--no-patches
option to suppress these diffs between patches, we prefer the-s
(or--no-patch
) option that is automatically supported via our use ofdiff_opt_parse()
.And finally note: to support diff options, we have to call
parse_options()
such that it keeps unknown options, and then loop over those and letdiff_opt_parse()
handle them. After that loop, we have to callparse_options()
again, to make sure that no unknown options are left.
And:
range-diff
: use dim/bold cues to improve dual color mode
It is a confusing thing to look at a diff of diffs. All too easy is it to mix up whether the
-/+
markers refer to the "inner" or the "outer" diff, i.e. whether a+
indicates that a line was added by either the old or the new diff (or both), or whether the new diff does something different than the old diff.To make things easier to process for normal developers, we introduced the dual color mode which colors the lines according to the commit diff, i.e. lines that are added by a commit (whether old, new, or both) are colored in green. In non-dual color mode, the lines would be colored according to the outer diff: if the old commit added a line, it would be colored red (because that line addition is only present in the first commit range that was specified on the command-line, i.e. the "old" commit, but not in the second commit range, i.e. the "new" commit).
However, this dual color mode is still not making things clear enough, as we are looking at two levels of diffs, and we still only pick a color according to one of them (the outer diff marker is colored differently, of course, but in particular with deep indentation, it is easy to lose track of that outer diff marker's background color).
Therefore, let's add another dimension to the mix. Still use green/red/normal according to the commit diffs, but now also dim the lines that were only in the old commit, and use bold face for the lines that are only in the new commit.
That way, it is much easier not to lose track of, say, when we are looking at a line that was added in the previous iteration of a patch series but the new iteration adds a slightly different version: the obsolete change will be dimmed, the current version of the patch will be bold.
At least this developer has a much easier time reading the range-diffs that way.
There are other ways than "..
" for a single token to denote a "commit range", namely "<rev>^!
" and "<rev>^-<n>
", but "git range-diff
"(man) did not understand them.
This is fixed with Git 2.31 (Q1 2021).
<commit>^!
is a perfectly valid commit range, equivalent to
<commit>^..<commit>
See commit 2cc543d, commit 359f0d7 (05 Feb 2021), and commit 679b591 (27 Jan 2021) by Johannes Schindelin (dscho
).
(Merged by Junio C Hamano -- gitster
-- in commit 77348b0, 17 Feb 2021)
range-diff/format-patch
: handle commit ranges other thanA..B
Signed-off-by: Johannes Schindelin
In the
SPECIFYING RANGES
section of gitrevisions[7], two ways are described to specify commit ranges thatrange-diff
does not yet accept: "<commit>^!
" and "<commit>^-<n>
".Let's accept them, by parsing them via the revision machinery and looking for at least one interesting and one uninteresting revision in the resulting
pending
array.This also finally lets us reject arguments that do contain
..
but are not actually ranges, e.g.HEAD^{/do.. match this}
.
And:
range-diff(docs)
: explain how to specify commit rangesSigned-off-by: Johannes Schindelin
There are three forms, depending whether the user specifies one, two or three non-option arguments.
We've never actually explained how this works in the manual, so let's explain it.
git range-diff
now includes in its man page:
There are three ways to specify the commit ranges:
<range1> <range2>
: Either commit range can be of the form<base>..<rev>
,<rev>^!
or<rev>^-<n>
. SeeSPECIFYING RANGES
in linkgit:gitrevisions[7] for more details.
<rev1>...<rev2>
. This is equivalent to<rev2>..<rev1> <rev1>..<rev2>
.
<base> <rev1> <rev2>
: This is equivalent to<base>..<rev1> <base>..<rev2>
.
Git 2.38 (Q3 2022) allows passing a pathspec to git range-diff
.
See commit b757478, commit 0087d7d, commit edd6a31 (26 Aug 2022) by Johannes Schindelin (dscho
).
(Merged by Junio C Hamano -- gitster
-- in commit 428dce9, 09 Sep 2022)
range-diff
: optionally accept pathspecsSigned-off-by: Johannes Schindelin
The
git range-diff
(man) command can be quite expensive, which is not a surprise given that the underlying algorithm to match up pairs of commits between the provided two commit ranges has a cubic runtime.Therefore it makes sense to restrict the commit ranges as much as possible, to reduce the amount of input to that O(N^3) algorithm.
In chatty repositories with wide trees, this is not necessarily possible merely by choosing commit ranges wisely.
Let's give users another option to restrict the commit ranges: by providing a pathspec.
That helps in repositories with wide trees because it is likely that the user has a good idea which subset of the tree they are actually interested in.Example:
git range-diff upstream/main upstream/seen HEAD -- range-diff.c
This shows commits that are either in the local branch or in
seen
, but not inmain
, skipping all commits that do not touchrange-diff.c
.Note: Since we piggy-back the pathspecs onto the
other_arg
mechanism that was introduced to be able to pass through the--notes
option to the revision machinery, we must now ensure that theother_arg
array is appended at the end (the revision range must come before the pathspecs, if any).
git range-diff
now includes in its man page:
[[--] <path>...]
git range-diff
now includes in its man page:
In the presence of
<path>
arguments, these commit ranges are limited accordingly.