Git diff - 两个不相交的修订范围

Git diff - two disjoint revision ranges

我想 select 使用 git diff 命令精确地进行一些不相交的提交。

这是一个例子。提交放置在单分支树上,从 A(最旧的提交)开始,到 Z(最近的提交)结束:

A 
|
B
|
C
|
D
|
...
|
Y
|
Z

我想要 select 两个提交范围,B-D 和 O-Q(包括),并将它们与 git diff 一起使用。这可以用两个命令来完成:

git diff B^..D
git diff O^..Q

是否可以仅使用一个 git diff 命令,以便只有一个输出?想象一下 O 还原 D,不分开描述 O 和 D 是有帮助的。

Git 2.19(2018 年第 3 季度,三年后)和 new git range-diff command.

应该可以实现
git range-diff B..D O..Q

这是受 Thomas Rast 和 Thomas Gummerer 的 trast/tbdiff 启发。

参见 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)。 参见 commit 8884cf1 (13 Aug 2018) by Thomas Rast (``)(由 Junio C Hamano -- gitster -- in commit 81eab68 合并,2018 年 8 月 20 日)

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 adjust git_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 of diff_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 let diff_opt_parse() handle them. After that loop, we have to call parse_options() again, to make sure that no unknown options are left.


并且:

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.


除了“..”之外,还有其他方法可以让单个标记表示“提交范围”,即“<rev>^!”和“<rev>^-<n>”,但是“git range-diff"(man)没看懂。

此问题已通过 Git 2.31(2021 年第一季度)修复。

<commit>^! 是一个完全有效的提交范围,相当于 <commit>^..<commit>

参见 commit 2cc543d, commit 359f0d7 (05 Feb 2021), and commit 679b591 (27 Jan 2021) by Johannes Schindelin (dscho)
(由 Junio C Hamano -- gitster -- in commit 77348b0 合并,2021 年 2 月 17 日)

range-diff/format-patch: handle commit ranges other than A..B

Signed-off-by: Johannes Schindelin

In the SPECIFYING RANGES section of gitrevisions[7], two ways are described to specify commit ranges that range-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}.

并且:

range-diff(docs): explain how to specify commit ranges

Signed-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 现在包含在其 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>. See SPECIFYING 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>.