有人可以解释一下 git 范围差异的用法吗?



Git 版本 2.19 引入了git range-diff,应该用于比较两个提交范围。我一直在阅读文档,但我无法理解此新功能的目的是什么。

我检查了官方的 Git 文档,但我很难理解它的语法(省略标志):

git range-diff ( <range1> <range2> | <rev1>...​<rev2> | <base> <rev1> <rev2> )

什么是rev1rev2

有人可以解释一下它们何时有用,我的意思是每种情况?

范围差异在解决合并冲突后非常有用(在rebasecherry-pick等之后),特别是当您有多个冲突提交时,并且您希望确保在此过程中没有意外破坏某些内容。

如果你在一个分支中执行多个提交,通常会发生这种情况.
假设我们有一个名为"our"的分支,我们的分支在主分支后面:

m1-m2-m3-m4  <- "master" branch

o1-o2-o3  <- "our" current branch

在变基之前,我们对分支进行备份(只需创建一个名为"our_bkp"的副本分支)

git branch our_bkp

然后我们开始与主站重新定位

git rebase master

并解决提交"o1"上的一些合并冲突...
注意:如果在"o2"或"o3"中也使用/更改了"o1"上的冲突文件,
那么我们还必须重新解决相同的合并冲突。

现在,假设经过一个令人筋疲力尽的变基过程,我们有这样的东西:

_<- branch "master"
/
m1-m2-m3-m4-o1'-o2'-o3'  <- branch "our" (after rebase)

o1-o2-o3   <- branch "our_bkp"

由于存在许多合并冲突,因此我们是否遗漏了某些内容并不清晰可见。

这就是范围差异的亮点。

为了确保我们没有错过任何更改或意外损坏任何东西,我们可以简单地 将旧版本分支的提交与新版本的提交进行比较:

git range-diff our_bkp~3..our_bkp our~3..our

git range-diff o1..o3 o1'..o3'

如果差异只与冲突的部分有关,那么我们很好,除了它们之外没有改变任何东西.
但是如果我们看到其他一些意外的差异,那么我们或 git 做错了什么,我们需要修复它们。

笔记

  • 上面的两个范围差异命令都在做完全相同的事情。
  • 通过说o1..o3我的意思是这些提交的数量,例如:0277a5883d132bebdb34e35ee228f4382dd2bb7。e415AEE3FA53A213DC53CA6A7944301066B72F24
  • our_bkp~3中的~3说 git 在分支上的最后一个提交之前3提交our_bkp提交。将数字替换为分支上的提交数量,当然不要忘记将分支名称our_bkp替换为备份分支的名称。
  • 您可以将范围差异视为执行两个差异的差异。这样就更容易记住和理解它在做什么。

我还没有真正使用过它们,但它们是对旧git cherry*流程的改进,用于分析/比较一些上游或下游更改集与您现在拥有的更改集。 为了使范围集有用,我们需要一些"这是我的提交"和"这是他们的",尽可能简单地表达。

范围 1 范围 2集合将写成,例如:

git range-diff theirs~5..theirs ours~4..ours

如果您有,例如:

T1--T2--T3--T4--T5   <-- theirs
/
...--o--*   <-- base

O1--O2--O3--O4   <-- ours

其中O提交是"我们的",T提交是"他们的"。

但是,给定此完全相同的配置,我们也可以编写:

git range-diff theirs...ours    # or ours...theirs

(注意三个点)。 (例如,这是与git rev-list --cherry-mark --left-right一起使用的语法。

或者,再次给定同样的情况,我们可以写:

git range-diff base theirs ours   # or base ours theirs

在这里,base是他们和我们的停止点,并且避免了倒数 5。

如果情况更复杂,如图所示:

X1--T1--T2--T3   <-- theirs
/
...--o--*   <-- base

Y1--Y2--O1--O2--O3--O4   <-- ours

三点和base ours theirs类型的语法都不太有效,所以两组范围(theirs~3..theirs ours~4..ours)是最好的。

Git 术语中的"范围"是一对修订标识符(开始和结束)。

git range-diff的第一种用法是<range1> <range2>。 由于我们知道范围是一对修订标识符,因此一些可能的示例是:

abc1234..def5678 9876foo..5432bar
HEAD..def5678 my_release_1_1..my_release_1_2

其他两种用法是为了方便起见,当四个修订标识符中的某些标识符彼此相同时。 即:

  1. 对于像abc..def def..abc这样的情况,您可以简单地指定def...abc
  2. 对于像abc..def abc..xyz这样的情况,您可以指定abc def xyz。 这对我来说似乎是一种常见情况:您想比较从同一点开始的两个范围。

Just Shadow 解释了如何使用git-range-diff来检查变基是否按预期进行合并冲突。 Torek解释了如何使用git-range-diff来比较"我们的"和"他们的"。

比较分支的版本

假设您正在开发一个基于main的功能分支,即主分支。您有两个版本的此分支:v1v2

$ git log --oneline v1
a058faf257b6 (v1) WIP Third
2e06d1cb89d0 WIP Second
c38f3355c04f (main) First
$ git log --oneline v2
3d8caecb46fc (HEAD -> v2) Third
b03b2cbb23c9 Second
c38f3355c04f (main) First

案例 1:仅更改提交消息

比较两个分支:[1]

git range-diff main..v1 main..v2
1:  2e06d1cb89d0 ! 1:  b03b2cbb23c9 WIP Second
@@ Metadata
Author: Victor Version Control <vvc@vcs-office.org>
## Commit message ##
-    WIP Second
+    Second
## readme.md ##
@@
2:  a058faf257b6 ! 2:  3d8caecb46fc WIP Third
@@ Metadata
Author: Victor Version Control <vvc@vcs-home.org>
## Commit message ##
-    WIP Third
+    Third
## readme.md ##
@@

唯一改变的是,我们从中删除了"WIP"前缀 提交消息。

案例 2:电子邮件元数据已更改

现在,作者v3,因为他注意到他在其中一个提交中使用了错误的电子邮件。

$ git range-diff main..v2 main..v3
1:  b03b2cbb23c9 = 1:  b03b2cbb23c9 Second
2:  3d8caecb46fc ! 2:  c95c9aee11f5 Third
@@
## Metadata ##
-Author: Victor Version Control <vvc@vcs-home.org>
+Author: Victor Version Control <vvc@vcs-office.org>
## Commit message ##
Third

案例 3:树(差异)已更改

v4,修复了一个错误:提交消息说"第三",但更改说"第四"。

v4v3之间的常规差异:

$ git diff v4 v3
diff --git a/readme.md b/readme.md
index ab7c514bba87..8ba854c1fe69 100644
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,3 @@
First
Second
-Third
+Fourth

范围差异:

git range-diff main..v4 main..v3
1:  b03b2cbb23c9 = 1:  b03b2cbb23c9 Second
2:  2b91ce078f8a < -:  ------------ Third
-:  ------------ > 2:  c95c9aee11f5 Third

好的,太好了。但是等等...树更改的差异在哪里?好吧,据我了解(在谷歌搜索两分钟后),我的更改太"剧烈"了(由于示例的琐碎);这两个提交是完全不同的,毕竟是两个完全不同的添加。但是我们可以通过使用--creation-factor=100来强制树差异:

$ git range-diff --creation-factor=100 main..v4 main..v3
1:  b03b2cbb23c9 = 1:  b03b2cbb23c9 Second
2:  2b91ce078f8a ! 2:  c95c9aee11f5 Third
@@ readme.md
@@
First
Second
-+Third
++Fourth

但是,让我们尝试一些有机的东西:

  1. 在一次提交中创建包含一些 lorem-ipsum 段落的v5
  2. 创建不同的提交v6:通过随机更改一些单词来修改v6(不是v6本身,而是您理解)

.

$ git range-diff main..v5 main..v6
1:  b03b2cbb23c9 = 1:  b03b2cbb23c9 Second
2:  2b91ce078f8a = 2:  2b91ce078f8a Third
3:  d82fd734d258 ! 3:  a0f7f0bf0cee Lorem ipsum
@@ readme.md
Third
+
+Nullam eu ante vel est convallis dignissim.  Fusce suscipit, wisi nec
-+facilisis facilisis, est dui fermentum leo, quis tempor ligula erat quis
++facilisis facilisis, etc. dui fermentum leo, quis tempor ligula erat quis
+odio.  Nunc porta vulputate tellus.  Nunc rutrum turpis sed pede.  Sed
+bibendum.  Aliquam posuere.  Nunc aliquet, augue nec adipiscing
+interdum, lacus tellus malesuada massa, quis varius mi purus non odio.
@@ readme.md
+ornare nulla, non luctus diam neque sit amet urna.  Curabitur vulputate
+vestibulum lorem.  Fusce sagittis, libero non molestie mollis, magna
+orci ultrices dolor, at vulputate neque nulla lacinia eros.  Sed id
-+ligula quis est convallis tempor.  Curabitur lacinia pulvinar nibh.  Nam
++ligula quis etc. convallis tempor.  Curabitur lacinia pulvinar nibh.  Nam
+a sapien.
+
+Pellentesque dapibus suscipit ligula.  Donec posuere augue in quam.

请注意两级差异(将两个"est"替换为"etc.")。

案例 4:在最新版本上再提交一次

常规差异:

$ git diff v6 v7
diff --git a/readme.md b/readme.md
index b48a079e18d3..57239ba86643 100644
--- a/readme.md
+++ b/readme.md
@@ -25,3 +25,11 @@ vitae lacus.  Nullam libero mauris, consequat quis, varius et, dictum
id, arcu.  Mauris mollis tincidunt felis.  Aliquam feugiat tellus ut
neque.  Nulla facilisis, risus a rhoncus fermentum, tellus tellus
lacinia purus, et dictum nunc justo sit amet elit.
+
+Lorem ipsum dolor sit amet, consectetuer adipiscing elit.  Donec
+hendrerit tempor tellus.  Donec pretium posuere tellus.  Proin quam
+nisl, tincidunt et, mattis eget, convallis nec, purus.  Cum sociis
+natoque penatibus et magnis dis parturient montes, nascetur ridiculus
+mus.  Nulla posuere.  Donec vitae dolor.  Nullam tristique diam non
+turpis.  Cras placerat accumsan nulla.  Nullam rutrum.  Nam vestibulum
+accumsan nisl.

范围差异:

$ git range-diff main..v6 main..v7
1:  b03b2cbb23c9 = 1:  b03b2cbb23c9 Second
2:  2b91ce078f8a = 2:  2b91ce078f8a Third
3:  a0f7f0bf0cee = 3:  a0f7f0bf0cee Lorem ipsum
-:  ------------ > 4:  568431442120 One more paragraph

案例 5:不同的笔记(git 笔记)

现在我们再添加一个提交到v7,并在testing命名空间上添加一个 git 注释:

Bah, couldn’t be bothered. How do you test a readme change?

v8中,我们只是改写该提交并更改注释(需要--notes=testing才能显示注释更改):

$ git range-diff --notes=testing main..v7 main..v8
1:  b03b2cbb23c9 = 1:  b03b2cbb23c9 Second
2:  2b91ce078f8a = 2:  2b91ce078f8a Third
3:  a0f7f0bf0cee = 3:  a0f7f0bf0cee Lorem ipsum
4:  568431442120 = 4:  568431442120 One more paragraph
5:  bb816b4f9fbe ! 5:  795df6f46595 Only lorem ipsum + add a paragraph
@@ Metadata
Author: Victor Version Control <vvc@vcs-office.org>
## Commit message ##
-    Only lorem ipsum + add a paragraph
+    Only lorem ipsum

## Notes (testing) ##
-    Bah, couldn’t be bothered. How do you test a readme change?
+    Ran CI (lorem ipsum linter)!
## readme.md ##
@@

来自 Git 邮件列表的真实示例

补丁系列:[补丁 v7 0/9] 配置 API:使"多"安全,修复段错误,传播"ret">

(作者:Ævar Arnfjörð Bjarmason)

题主告诉我们:

  • 这是补丁系列的"求职信"(电子邮件 介绍该系列,该系列由求职信和一封 每个补丁/提交的电子邮件)
  • 这是本系列的第 7 版

作者指出了与版本 6 的不同之处:

A larger general overview at v1[1], but note the API changes in
v2[2]. Changes since v6[3]:
* Glen pointed out that ejecting a commit in v6 orphaned a
corresponding forward-reference in a commit message, fix that.

事实上,提供的范围差分输出告诉我们很多:

Range-diff against v6:
1:  43fdb0cf50c =  1:  9f297a35e14 config tests: cover blind spots in git_die_config() tests
2:  4b0799090c9 =  2:  45d483066ef config tests: add "NULL" tests for *_get_value_multi()
3:  62fe2f04e71 !  3:  a977b7b188f config API: add and use a "git_config_get()" family of functions
@@ Commit message
"int" instead of "void". Let's leave that for now, and focus on
the *_get_*() functions.
-    In a subsequent commit we'll fix the other *_get_*() functions to so
-    that they'll ferry our underlying "ret" along, rather than normalizing
-    it to a "return 1". But as an intermediate step to that we'll need to
-    fix git_configset_get_value_multi() to return "int", and that change
-    itself is smaller because of this change to migrate some callers away
-    from the *_value_multi() API.
-
1. 3c8687a73ee (add `config_set` API for caching config-like files, 2014-07-28)
2. https://lore.kernel.org/git/xmqqczadkq9f.fsf@gitster.g/
3. 1e8697b5c4e (submodule--helper: check repo{_submodule,}_init()
4:  e36303f4d3d =  4:  3a5a323cd91 versioncmp.c: refactor config reading next commit
5:  e38523267e7 =  5:  dced12a40d2 config API: have *_multi() return an "int" and take a "dest"
6:  3a87b35e114 =  6:  d910f7e3a27 for-each-repo: error on bad --config
7:  66b7060f66f =  7:  57db0fcd91f config API users: test for *_get_value_multi() segfaults
8:  0da4cdb3f6a =  8:  b374a716555 config API: add "string" version of *_value_multi(), fix segfaults
9:  627eb15a319 =  9:  6791e1f6f85 for-each-repo: with bad config, don't conflate <path> and <cmd>

† 1:请注意,我们在这里丢失了颜色,与常规差异相比,这对于这种差异似乎更为重要。在带有着色的终端中体验更好。

命令git range-diff,您可以在此处看到比较两个补丁,已在 Git 2.23(2019 年第 3 季度)中重新访问,以便更轻松地识别显示的补丁是关于哪个部分的文件。

请参阅提交 499352c、提交 444e096、提交b66885a、提交 430be36、提交 e1db263、提交 44b67cb、提交 1ca6922、提交 ef283b3、提交 80e1841(2019 年 7 月 11 日)和提交 877a833、提交 570fe99、提交 85c3713、提交 d6c88c4、提交 5af4087(2019 年 7 月 8 日),作者:Thomas Gummerer (tgummerer).
(由 Junio C Hamano --gitster-- 合并于 commit 43ba21c,2019 年 7 月 25 日)

range-diff:将文件名添加到内部差异

在范围差异中,并不总是清楚哪个文件funcname内部差异属于,因为差异标头(或在先前提交中添加的节标头)在范围差异中并不总是可见。

将文件名添加到内部差异标头,使其始终可见 用户。

这也允许我们将文件名 + funcname 添加到外部 使用自定义用户差异模式比较大块头,这将完成 在下一次提交中。

range-diff:将标题添加到外部大块头

将我们在前面的提交中引入的节标题/hunk 标头添加到外部差异的 hunk 标头中.
这样可以更轻松地理解我们实际正在查看的更改。 例如,外部 hunk 标头现在可能如下所示:

@@  Documentation/config/interactive.txt

而以前它只会是

@@

这并没有为随后的更改提供很多上下文。

请参阅t3206-range-diff.sh作为示例。

和:

范围差异:添加节标题而不是差异标题

目前 range-diff 保持内部 diff 的 diff 标头完好无损(除了以 index 开头的剥离行).
这个 diff 标头有点用,特别是当文件变得不同时 不同范围内的名称。

但是,实际上没有必要为此保留整个diff标头.
我们目前这样做的主要原因可能是因为它很容易做到。

引入一个新的范围 diff hunk 标头,它被"##"包围,类似于 diff hunk 中的行号被"@@"包围,并提供人类可读的文件到底发生了什么的信息,包括文件名。

这通过提供更简洁的 IF 来提高范围差异的可读性 用户的信息.
例如,如果一个文件在一次迭代中被重命名,但在另一次迭代中没有重命名,则标头的差异将非常嘈杂.
但是,单行的差异是简洁的,应该更容易理解。

同样,t3206-range-diff.sh提供了一个示例:

git range-diff --no-color --submodule=log topic...renamed-file >actual &&
sed s/Z/ /g >expected <<-EOF &&
1:  4de457d = 1:  f258d75 s/5/A/
2:  fccce22 ! 2:  017b62d s/4/A/
@@ Metadata
ZAuthor: Thomas Rast <trast@inf.ethz.ch>
Z
Z ## Commit message ##
-    s/4/A/
+    s/4/A/ + rename file
Z
- ## file ##
+ ## file => renamed-file ##
Z@@
Z 1
Z 2

但是:请注意diff.noprefix配置设置:git range-diff会在 2.24(2019 年第 4 季度)之前出现 Git 段错误!

"git range-diff"在使用diff.noprefix配置时出现段错误,因为它盲目地期望内部生成的修补程序具有标准a/b/前缀。
该命令现在强制构建没有任何前缀的内部修补程序,不受任何最终用户配置的影响。

参见 提交 937b76e (02 Oct 2019) by Johannes Schindelin (dscho).
(由 Junio C Hamano --gitster-- 合并于 commit 159cdab,2019 年 10 月 11 日)

范围差:内部力diff.noprefix=true

解析差异时,range-diff希望看到前缀a/b/在差异标头中。

这些前缀可以通过配置设置diff.noprefix=true.
强制关闭,因为range-diff没有为这种情况做好准备,这将导致分段错误。

让我们通过将--no-prefix选项传递给生成range-diff想要解析的差异的git log进程来避免这种情况.
当然,期望输出没有前缀。


和"git range-diff"无法处理仅模式更改,这已经 使用 Git 2.24(2019 年第 4 季度)进行了更正:

参见 commit 2b6a9b1 (08 Oct 2019) by Thomas Gummerer (tgummerer).
(由 Junio C Hamano --gitster-- 合并于 提交 b6d712f,2019 年 10 月 15 日)

range-diff:不要对仅模式更改进行分段错误

报告人: Uwe Kleine-König
签名者: Thomas Gummerer
Acked-by: Johannes Schindelin

在 ef283b3699 ("apply:公开parse_git_diff_header", 2019-07-11, Git v2.23.0-rc0 -- 合并在批处理 #7 中列出) 'parse_git_diff_header' 函数已公开并可供apply.c外部调用者使用。

然而,它(当时)唯一的调用者'find_header'做了一些错误处理,并适当地完成了'struct patch'。

然后range-diff开始使用这个函数,并试图自己适当地处理这个问题,但在某些情况下失败了。

这反过来会导致当范围中仅存在模式更改时出现range-diff段错误

将结构的错误处理和完成移动到"parse_git_diff_header"函数中,以便其他调用方可以利用它。

这修复了"git range-diff"中的段错误。


在 Git 2.25(2020 年第 1 季度)中,"git range-diff"学会了采用"--notes=<ref>"和"--no-notes"选项来控制比较的日志消息中包含的提交记录。

请参阅提交 5b583e6、提交 bd36191、提交 9f726e1、提交 3bdbdfb、提交 75c5aa0、提交 79f3950、提交 3a6e48e、提交 26d9485(2019 年 11 月 20 日)和提交 9d45ac4、提交 828e829(2019 年 11 月 19 日),作者:Denton Liu (Denton-L).
(由 Junio C Hamano --gitster-- 合并于 提交 f3c7bfd,2019 年 12 月 5 日)

range-diff:输出## Notes ##标头

签约人:刘丹顿

当注释包含在range-diff的输出中时,它们只是与提交消息的其余部分混合在一起。因此,用户将无法清楚地区分提交消息的结束位置和注释的开始位置。

检测到笔记时输出## Notes ##标题,以便可以更清楚地比较笔记。

请注意,我们也使用此代码处理Notes (<ref>): -> ## Notes (<ref>) ##的情况。但是,我们无法在此补丁中对此进行测试,因为目前无法将不同的注释 ref 传递给git log.这将在将来的补丁中修复。

和:

请参阅提交 abcf857、提交 f867534、提交 828765d (2019 年 12 月 6 日)作者 Denton Liu (Denton-L).
(由 Junio C Hamano --gitster-- 合并于 提交 d1c0fe8,2019 年 12 月 16 日)

range-diff:功能结束时清除other_arg

签约人:刘丹顿

我们在使用完内存后没有清除other_arg,从而泄漏了内存.
在我们完成使用它后清除它。

请注意,这不是绝对必要的,因为一旦命令退出,内存将被回收.
但是,由于我们正在发布strbufs,我们还应该清除other_arg以保持一致性。


在 Git 2.27(2020 年第 2 季度)中,"git range-diff"更加健壮。

参见提交 8d1675e,提交 8cf5156(2020 年 4 月 15 日),作者:Vasil Dimov (vasild).
(由 Junio C Hamano --gitster-- 合并于 提交 93d1f19,2020 年 4 月 28 日)

range-diff:修复解析git-log输出时崩溃的问题

签名者:瓦西里·迪莫夫

git range-diff调用git log内部并尝试解析其输出。

但是git log输出可以由用户在其git config中自定义,对于某些配置,git range-diff将返回错误或崩溃。

要解决此问题,请使用--pretty=medium.
显式设置内部执行git log的输出格式,因为这会取消--notes,请在末尾显式添加--notes

此外,请确保我们永远不会以相同的方式崩溃 - 尝试取消util引用从未创建且一直保持NULL.
如果git log输出的第一行不以"提交"开头,就会发生这种情况。

考虑了替代方案但被丢弃了 - 以某种方式禁用所有 git 配置并表现得好像内部执行的git log中不存在配置,但这似乎是不可能的.
GIT_CONFIG_NOSYSTEM是最接近它的,但即使这样,我们仍然会阅读.git/config.


在 Git 2.31(2021 年第 1 季度)中,"git range-diff">(man)命令学会了--(left|right)-only选项,以仅显示比较范围的一侧

请参阅提交 1e79f97、提交 3e6046e、提交 f1ce6c1(2021 年 2 月 5 日)和提交 5189bb8、提交 a2d474a、提交 8c29b49(2021 年 2 月 4 日),作者:Johannes Schindelin (dscho).
(由 Junio C Hamano --gitster-- 合并于 提交 dadc91f,2021 年 2 月 17 日)

range-diff: 提供 --仅左/--仅右选项

签名:约翰内斯·辛德林

在比较提交范围时,人们通常只对一侧感兴趣,例如问"我提交给 Git 邮件列表的这个补丁是否已应用?":人们只关心输出中与本地分支中的提交相对应的部分。

为此,请模仿git rev-list(人)选项--left-only--right-only

这解决了gitgitgadget/git问题 206:"range-diff:添加对--left-only--right-only的支持">

git range-diff现在在其手册页中包含:

--left-only

禁止显示第一个指定范围内缺少的提交 (或使用<rev1>...<rev2>格式时的"左范围")。

--right-only

禁止显示第二个指定范围内缺少的提交 (或使用<rev1>...<rev2>格式时的"正确范围")。


Git 2.34(2021 年第 4 季度)处理了关于未终止行的"git range-diff">(man)的极端情况。

请参阅提交 c4d5907、提交 7c86d36、提交 47ac23d (2021 年 8 月 9 日),作者:Jeff King (peff).
(由 Junio C Hamano --gitster-- 合并于 提交 fb0b14d,2021 年 8 月 30 日)

range-diff:处理read_patches()中未终止的线路

签名者:杰夫·金
阿克德:德里克·斯托利

解析我们的git-log输出缓冲区时,我们有一个find_end_of_line()助手,它可以找到下一个换行符,并向我们提供要通过它的字节数,或者如果没有换行符,则提供整个剩余缓冲区的大小。

但是试图处理这两种情况会导致一些奇怪的事情:

  • 我们尝试在调用者中使用NUL覆盖换行符,方法是line[len-1]写入 .
    这充其量是多余的,因为如果帮助程序看到换行符,它已经这样做了。
  • 但是如果它没有看到换行符,它就是主动错误的;我们将覆盖(未终止)行末尾的字节。

为了解决这个问题,调用方需要知道我们是否真的找到了换行符.
我们可以修改find_end_of_line()以返回该信息,但我们可以进一步观察到它只有一个调用者.
因此,让我们将其内联到该调用方中。

似乎没有人注意到这种情况,可能是因为"git-log'(man)永远不会产生不以换行符结尾的输入。

最新更新