提问者:小点点

什么是git diff<first_commit>^<感叹号>?


我通常通过“git diff提交^!”来检查提交的内容。然而,当我将其应用于初始提交时,我看到了来自不同提交的混合更改,而我认为它应该是提交的初始副本。有人能帮助我从语义学角度理解它吗?

BTW,我知道如何显示stackoverflow问题40883798中init提交的差异有很好的答案。


共2个答案

匿名用户

我通常通过“git diff提交^!”来检查提交的内容。

(为什么?通常的命令是git show

但是,当我将其应用于初始提交时,我看到[一些不太有用的东西]

找到任何修订语法(简短)解释的地方是gitrevisons留档,它说明了^!后缀:

r1^!表示法包括提交r1,但不包括它的所有父节点。这个表示法本身表示单个提交r1。

不幸的是,这个解释太短了。在这种情况下,为git rep-parse提供修订版更有帮助:

$ git rev-parse HEAD
b5101f929789889c2e536d915698f58d5c5c6b7a
$ git rev-parse HEAD^1
a562a119833b7202d5c9b9069d1abb40c1f9b59a

从这两个输出中,我们可以看到HEAD表示哈希IDb5101f929789889c2e536d915698f58d5c5c6b7a,它的第一个父级是a562a119833b7202d5c9b9069d1abb40c1f9b59a。这就是我们需要理解的:

$ git rev-parse HEAD^!
b5101f929789889c2e536d915698f58d5c5c6b7a
^a562a119833b7202d5c9b9069d1abb40c1f9b59a

这里的输出实际上意味着:

  • b5101f929789889c2e536d915698f58d5c5c6b7a开始
  • 但停在a562a119833b7202d5c9b9069d1abb40c1f9b59a

(哈希ID前面的^表示不:提交B,但不提交A。)

将此与:

$ git rev-parse HEAD^..HEAD
b5101f929789889c2e536d915698f58d5c5c6b7a
^a562a119833b7202d5c9b9069d1abb40c1f9b59a

输出是一样的!

这对git diff意味着什么有点棘手和微妙,因为我们在这里谈论的是git rep-parse而不是git diff。但事实上,当你运行时:

git diff <something>

Git内部将<代码>

git diff HEAD^..HEAD

git diff内部将整个字符串HEAD^… HEAD交给git rep-parse的内部版本,并获取“停止在”和“开始在”ID。如果您输入:

git diff HEAD^!

git diff内部将整个字符串HEAD^!交给git rep-parse的内部版本,并获得相同的“停止在”和“开始在”ID。

当您对具有一个父级的任何提交使用提交哈希时,情况也是如此:^!后缀为父哈希生成“not”,为哈希生成“use”。

但是,当您找到根提交时-在我的Git存储库中有一堆,所以我将只取第一个:

$ git rev-list --max-parents=0 HEAD | head -1
0ca71b3737cbb26fbf037aa15b3f58735785e6e3

-当我们给这个哈希ID一个hat-ang后缀git rep-parse时,我们得到:

$ git rev-parse 0ca71b3737cbb26fbf037aa15b3f58735785e6e3^!
0ca71b3737cbb26fbf037aa15b3f58735785e6e3

也就是说,git rep-parse对提交说是,对其所有父级都说否,但没有任何父级,所以它根本不说不!

如果您将其提供给git diff-只有一个提交哈希-git diff命令认为:啊,您想将给定的提交与您现在工作树中的任何内容进行比较。这就是你得到的差异。

请注意,^!后缀为提交的所有父级生成“not”s。对于合并提交,至少有两个(通常正好两个)父级:

$ git show -s a562a11983
commit a562a119833b7202d5c9b9069d1abb40c1f9b59a
Merge: 7fa92ba40a ad6f028f06
Author: Junio C Hamano ...

所以:

$ git rev-parse a562a11983^!
a562a119833b7202d5c9b9069d1abb40c1f9b59a
^7fa92ba40abbe4236226e7d91e664bbeab8c43f2
^ad6f028f067673cadadbc2219fcb0bb864300a6c

当您给git diff一个扩展到三个或更多提交的修订说明符时,它有时会做一些不同的事情。

在2.28之前的Git版本中,git diff

有可能rev^!语法在未来的Git版本(2.39或更高版本)中再次更改git diff行为,要么恢复旧行为,要么在有多个父级时完全拒绝此行为。

1这样做的原因是,虽然git rep-parse首先显示正引用,然后显示所有负引用-即,

a562a119833b7202d5c9b9069d1abb40c1f9b59a
^7fa92ba40abbe4236226e7d91e664bbeab8c43f2
^ad6f028f067673cadadbc2219fcb0bb864300a6c

如上所示-git diff调用的内部rev解析器最终按以下顺序将它们存储到数组中:

^7fa92ba40abbe4236226e7d91e664bbeab8c43f2
^ad6f028f067673cadadbc2219fcb0bb864300a6c
a562a119833b7202d5c9b9069d1abb40c1f9b59a

在Git-2.28之前,它选择第一个条目作为第二次提交,最后一个条目作为第一次提交。在Git 2.28及更高版本中,这最终会与列出的顺序中的修订运行组合diff,这不是很有用。组合diff需要第一个修订是正引用。当您只传递单个提交时,git show命令会正确处理此问题。

匿名用户

当您运行git diff并传递提交时,git将显示当前工作树中相对于该提交的更改

从git文档

git diff [] [--] […] 此表单用于查看您在工作树中相对于命名的更改。您可以使用HEAD将其与最新提交进行比较,或者使用分支名称与不同分支的尖端进行比较。

Caret(^)是对该提交的第一个父级的引用。以类似的方式,您可以引用第二个(^2)、第三个(^3)等等。

通过运行git diff

在git 2.17.1中运行这样的命令会产生错误:

$> git diff 9f3f6c8e4b1dea1de25febbb8248a6c430966236^
$> fatal: ambiguous argument '9f3f6c8e4b1dea1de25febbb8248a6c430966236^': unknown revision or path not in the working tree.