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

时间:2019-02-01 17:46:51

标签: git git-diff git-filter-branch

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

顺便说一句,我知道如何显示来自stackoverflow question 40883798的init commit有很好的答案。

2 个答案:

答案 0 :(得分:1)

  

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

(为什么?通常的命令是git show <commit>。)

  

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

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

  
    

r1 ^!表示法包括提交 r1 ,但不包括其所有父代。       就其本身而言,此表示法表示单个提交 r1

  

不幸的是,这个解释太短了。在这种情况下,提供对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的内部版本,并获得与 same “相同于”和“起始于”的ID。

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

但是,当您找到一个 root 提交时-在我的Git的Git存储库中,有很多东西,所以我只用第一个:

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

-当我们给带有{-{1}}的后缀为 this 的哈希ID时,我们得到:

git rev-parse

也就是说,$ git rev-parse 0ca71b3737cbb26fbf037aa15b3f58735785e6e3^! 0ca71b3737cbb26fbf037aa15b3f58735785e6e3 对提交表示是,对所有提交的父母都拒绝了,但是没有父母,所以它什么都不说!

如果将 this 馈送到git rev-parse(只是一个提交哈希),git diff命令会认为:啊,您想将给定的提交与任何提交进行比较在您的工作树中。那就是你得到的差异。


请注意,git diff后缀会为提交的所有 all 父母产生“ not”。对于合并提交,至少有两个(通常正好是两个)父母:

^!

所以:

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

当您给$ $ git rev-parse a562a11983^! a562a119833b7202d5c9b9069d1abb40c1f9b59a ^7fa92ba40abbe4236226e7d91e664bbeab8c43f2 ^ad6f028f067673cadadbc2219fcb0bb864300a6c 的修订说明符扩展到三个或更多提交时,有时会做一些不同的事情。但是,在这种特定情况下,它只是将提交本身与第一个父对象进行比较。默认情况下,git diff命令将生成一个 combined diff ,在这种情况下,它根本不显示任何内容,因为组合的diff旨在向您显示合并可能存在冲突的地方。

答案 1 :(得分:0)

当您运行git diff并通过提交时,git将显示当前工作树中与该提交相关的更改

From the git docs

  

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.