Git 版本 2.19 引入了git range-diff
,应该用于比较两个提交范围。我一直在阅读文档,但我无法理解此新功能的目的是什么。
我检查了官方的 Git 文档,但我很难理解它的语法(省略标志):
git range-diff ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )
什么是rev1
和rev2
?
有人可以解释一下它们何时有用,我的意思是每种情况?
范围差异在解决合并冲突后非常有用(在rebase
、cherry-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
其他两种用法是为了方便起见,当四个修订标识符中的某些标识符彼此相同时。 即:
- 对于像
abc..def def..abc
这样的情况,您可以简单地指定def...abc
。 - 对于像
abc..def abc..xyz
这样的情况,您可以指定abc def xyz
。 这对我来说似乎是一种常见情况:您想比较从同一点开始的两个范围。
Just Shadow 解释了如何使用git-range-diff
来检查变基是否按预期进行合并冲突。 Torek解释了如何使用git-range-diff
来比较"我们的"和"他们的"。
比较分支的版本
假设您正在开发一个基于main
的功能分支,即主分支。您有两个版本的此分支:v1
和v2
。
$ 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
,修复了一个错误:提交消息说"第三",但更改说"第四"。
v4
和v3
之间的常规差异:
$ 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
但是,让我们尝试一些更有机的东西:
- 在一次提交中创建包含一些 lorem-ipsum 段落的
v5
- 创建不同的提交
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)永远不会产生不以换行符结尾的输入。