我通常通过 "git diff commit^!" 命令来检查提交内容。然而,当我对初始提交应用此命令时,我看到了来自不同提交的混合更改,而我本以为它应该是提交的最初拷贝。能否有人帮助我从语义上理解这个问题?
顺便说一句,我知道有很好的答案可以显示如何从stackoverflow问题40883798显示初始化提交的差异。
我通常通过 "git diff commit^!" 命令来检查提交内容。然而,当我对初始提交应用此命令时,我看到了来自不同提交的混合更改,而我本以为它应该是提交的最初拷贝。能否有人帮助我从语义上理解这个问题?
顺便说一句,我知道有很好的答案可以显示如何从stackoverflow问题40883798显示初始化提交的差异。
git show <commit>
。请注意,git diff commit^!
不受官方支持,并且对于合并提交,其意外行为在Git 2.28中发生了变化。)^!
后缀的内容如下:git rev-parse
更有帮助。$ git rev-parse HEAD
b5101f929789889c2e536d915698f58d5c5c6b7a
$ git rev-parse HEAD^1
a562a119833b7202d5c9b9069d1abb40c1f9b59a
HEAD
代表哈希 ID b5101f929789889c2e536d915698f58d5c5c6b7a
,它的第一个父提交是 a562a119833b7202d5c9b9069d1abb40c1f9b59a
。这就是我们需要理解的内容。$ git rev-parse HEAD^!
b5101f929789889c2e536d915698f58d5c5c6b7a
^a562a119833b7202d5c9b9069d1abb40c1f9b59a
b5101f929789889c2e536d915698f58d5c5c6b7a
开始...a562a119833b7202d5c9b9069d1abb40c1f9b59a
。(散列ID前面的^
表示“不包括”:提交B,但不包括提交A。)
与此相比:
$ git rev-parse HEAD^..HEAD
b5101f929789889c2e536d915698f58d5c5c6b7a
^a562a119833b7202d5c9b9069d1abb40c1f9b59a
输出结果相同!
对于git diff
,这意味着有点棘手和微妙,因为我们在这里讨论的是git rev-parse
而不是git diff
。但实际上,当你运行以下命令时:
git diff <something>
Git内部将<something>
交给git rev-parse
。因此,如果您输入:
git diff HEAD^..HEAD
git diff
命令会将整个字符串 HEAD^..HEAD
传递给内部版本的 git rev-parse
,并获取“停止于”和“开始于”的 ID。如果您键入:
git diff HEAD^!
git diff
会将整个字符串HEAD^!
传递给内部版本的git rev-parse
,并获得相同的“停止于”和“开始于”ID。
当您使用具有一个父级的任何提交哈希时,情况也是如此:^!
后缀为父哈希生成“非”,为哈希生成“使用”。
但是,当您找到一个根提交时,在我的Git存储库中有很多个,因此我将只取第一个:
$ git rev-list --max-parents=0 HEAD | head -1
0ca71b3737cbb26fbf037aa15b3f58735785e6e3
当我们将带有感叹号后缀的哈希ID this 提供给git rev-parse
时,会得到:
$ git rev-parse 0ca71b3737cbb26fbf037aa15b3f58735785e6e3^!
0ca71b3737cbb26fbf037aa15b3f58735785e6e3
也就是说,git rev-parse
对该提交说“是”,对其所有父提交说“否”,但是没有任何父提交,因此它什么也没有拒绝!
如果你将这个提交哈希值输入git diff
,git diff
命令会认为:啊,你想比较给定的提交和当前工作树中的内容。所以你得到的就是这个差异。
请注意,^!
后缀为提交的所有父提交生成“不”操作。对于合并提交,至少有两个(通常恰好两个)父提交:
$ git show -s a562a11983
commit a562a119833b7202d5c9b9069d1abb40c1f9b59a
Merge: 7fa92ba40a ad6f028f06
Author: Junio C Hamano ...
$ git rev-parse a562a11983^!
a562a119833b7202d5c9b9069d1abb40c1f9b59a
^7fa92ba40abbe4236226e7d91e664bbeab8c43f2
^ad6f028f067673cadadbc2219fcb0bb864300a6c
git diff
一个扩展到三个或更多提交的修订说明符时,它有时会做一些不同的事情。git diff <rev>^!
意外地将提交本身与第一个父提交进行比较。1相反,git show
命令默认会生成一个合并差异,在这种情况下,合并差异不显示任何内容,因为合并差异旨在显示可能存在冲突的合并位置。rev^!
语法对git diff
行为可能会再次更改,无论是恢复旧行为还是拒绝多个父提交。
git rev-parse
首先显示正引用,然后是所有负引用。a562a119833b7202d5c9b9069d1abb40c1f9b59a
^7fa92ba40abbe4236226e7d91e664bbeab8c43f2
^ad6f028f067673cadadbc2219fcb0bb864300a6c
如上所示——git diff
调用的内部rev解析器会按照以下顺序将它们存储到数组中:
^7fa92ba40abbe4236226e7d91e664bbeab8c43f2
^ad6f028f067673cadadbc2219fcb0bb864300a6c
a562a119833b7202d5c9b9069d1abb40c1f9b59a
在Git 2.28之前,这将选择第一个条目作为第二个提交,并将最后一个条目作为第一个提交。在Git 2.28及更高版本中,这将以列出顺序的修订版运行合并差异,这并不是很有用。组合差异需要第一个修订版是正引用。当您仅传递单个提交时,git show
命令会正确处理此问题。
当你运行git diff并传递一个提交时,git会显示相对于该提交的当前工作树中的更改。
git diff [] [--] […]此形式用于查看您在工作树中相对于命名的提交所做的更改。您可以使用HEAD将其与最新提交进行比较,或者使用分支名称将其与不同分支的末端进行比较。
插入符号(^
)是对该提交的第一个父级的引用。类似地,您可以引用第二个(^2
),第三个(^3
)等。
通过运行git diff <firstCommit>^
,您语义上要求“给我当前工作目录和第一个提交的第一个父级之间的更改”。第一个提交的父级不存在,因此它在语义上没有意义。
在git 2.17.1中运行这样的命令会产生错误:
$> git diff 9f3f6c8e4b1dea1de25febbb8248a6c430966236^
$> fatal: ambiguous argument '9f3f6c8e4b1dea1de25febbb8248a6c430966236^': unknown revision or path not in the working tree.
^!
,而不仅仅是 ^
。 - torek
git show <commit>
命令。它只会显示该提交中所做的更改。 - Rmahajan