【问题标题】:What is git diff <first_commit>^<exclamation-point>?什么是 git diff <first_commit>^<exclamation-point>?
【发布时间】:2023-03-28 11:50:01
【问题描述】:

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

顺便说一句,我知道有很好的答案如何显示来自stackoverflow question 40883798 的初始化提交的差异。

【问题讨论】:

  • 如果你想查看提交的具体内容,你可以使用git show &lt;commit&gt;。它只会显示在该提交中所做的更改。
  • 插入符号的意思是“父母”。初始提交将没有父级。
  • 我发现 StackOverflow 吃“!”在标题中。我放了其他东西来突出显示它,希望没关系。

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


【解决方案1】:

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

(为什么?通常的命令是git show &lt;commit&gt;。)

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

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

r1^! 表示法包括提交 r1 但不包括其所有父项。 该符号本身表示单个提交 r1

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

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

从这两个输出中,我们可以看到HEAD代表哈希IDb5101f929789889c2e536d915698f58d5c5c6b7a,它的第一个parent是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 在内部将 &lt;something&gt; 传递给 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。

当您将提交哈希用于具有一个父级的任何提交时,这同样适用:^! 后缀为父级哈希生成一个“not”,为哈希生成一个“use”。

但是,当你找到一个 root 提交时——在我的 Git 存储库中,有一堆,所以我只取第一个:

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

——当我们给git rev-parse 加上一个帽子爆炸后缀的这个哈希ID,我们得到:

$ git rev-parse 0ca71b3737cbb26fbf037aa15b3f58735785e6e3^!
0ca71b3737cbb26fbf037aa15b3f58735785e6e3

也就是说,git rev-parse 对提交说“是”,对其所有父级都说“否”,但没有任何父级,所以它对任何内容都说“不”!

如果你将 this 提供给 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 show 命令将改为生成 组合差异,在这种情况下,它什么也不显示,因为组合差异旨在向您显示合并可能发生冲突的位置。

【讨论】:

    【解决方案2】:

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

    From the git docs

    git diff [] [--] […​] 这个表格是用来查看 您在工作树中相对于命名的 . 您可以使用 HEAD 将其与最新提交或分支进行比较 名称以与不同分支的尖端进行比较。

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

    通过运行git diff &lt;firstCommit&gt;^,您在语义上要求“给我当前工作目录与第一个提交的父级第一个父级之间的更改”。第一次提交的 Parent 不存在,因此在语义上没有意义。

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

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

    【讨论】:

    • 他使用的是^!,而不仅仅是^
    • 问题的标题是“What is git diff ^”,但还算公平。
    • 是的,似乎 stackoverflow 吃掉了“!”特点。我无法将其添加到标题中!
    猜你喜欢
    • 2018-06-10
    • 2023-03-06
    • 1970-01-01
    • 2015-11-28
    • 2015-08-06
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多