【问题标题】:Why git doesn't show revert and reverted commit?为什么git不显示revert和reverted commit?
【发布时间】:2021-11-23 07:29:59
【问题描述】:

如下所示,虽然我有来自 git log 的 revert commit 和 reverted commit,但是当我 git log 文件本身时,它无法显示这两个提交

lchen@sh-lchen ~/p/k/v5.15 ((v5.15))> git log --stat --grep='6206b798'
commit 91bed5565bba03b2a9f7334b58ae4be9df7c3840
Author: Jia He <justin.he@arm.com>
Date:   Tue Jul 20 21:26:55 2021 +0800

    Revert "qed: fix possible unpaired spin_{un}lock_bh in _qed_mcp_cmd_and_union()"

    This reverts commit 6206b7981a36476f4695d661ae139f7db36a802d.

    That patch added additional spin_{un}lock_bh(), which was harmless
    but pointless. The orginal code path has guaranteed the pair of
    spin_{un}lock_bh().

    We'd better revert it before we find the exact root cause of the
    bug_on mentioned in that patch.

    Fixes: 6206b7981a36 ("qed: fix possible unpaired spin_{un}lock_bh in _qed_mcp_cmd_and_union()")
    Cc: David S. Miller <davem@davemloft.net>
    Cc: Prabhakar Kushwaha <pkushwaha@marvell.com>
    Signed-off-by: Jia He <justin.he@arm.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>

 drivers/net/ethernet/qlogic/qed/qed_mcp.c | 23 ++++++-----------------
 1 file changed, 6 insertions(+), 17 deletions(-)
lchen@sh-lchen ~/p/k/v5.15 ((v5.15))>
lchen@sh-lchen ~/p/k/v5.15 ((v5.15))>
lchen@sh-lchen ~/p/k/v5.15 ((v5.15))> git log --oneline drivers/net/ethernet/qlogic/qed/qed_mcp.c | grep "fix possible unpaired"
lchen@sh-lchen ~/p/k/v5.15 ((v5.15)) [0|1]>

lchen@sh-lchen ~/p/k/v5.15 ((v5.15))> git branch HEAD --contains 6206b7981a36476f4695d661ae139f7db36a802d
* (no branch)

这是我的 gitconfig,不包括用户名、邮件和 HTTP 代理:

[core]
        editor = vim
        whitespace = fix,-indent-with-non-tab,trailing-space,cr-at-eol
        excludesfile = ~/.gitignore
    filemode = false
[am]
    threeWay = true
[core]
    autocrlf = input
    eol = lf
    whitespace = cr-at-eol
[auto]
    crlf = false
[pull]
    rebase = true

repo 是 Linux 主线内核和分支:

lchen@sh-lchen ~/p/k/v5.15 ((v5.15))> git describe HEAD --all
tags/v5.15
lchen@sh-lchen ~/p/k/v5.15 ((v5.15))> git rev-parse HEAD
8bb7eca972ad531c9b149c0a51ab43a417385813

git 版本:

lchen@sh-lchen ~/p/k/v5.15 ((v5.15))> git --version
git version 2.33.1

为什么git log --oneline drivers/net/ethernet/qlogic/qed/qed_mcp.c 不能显示还原和还原的提交?

【问题讨论】:

  • HEAD 中的 git branch HEAD --contains 6260... 没有意义。要列出包含提交 6260... 的分支,请运行 git branch --contain 6260...(将仅列出本地分支),如果只需要远程分支,请添加 -r,如果需要本地和远程分支,请添加 -a
  • git log --stat 中列出的路径从 repo 的根目录开始,但git log --oneline some/path 将相对于您的当前目录查找some/path。当您运行 git log --oneline ... 时,您是否位于您的存储库的根目录?另外,如果你删除你的grep 命令,你会看到一些提交吗?
  • 是的,我在根路径上。 如果你删除你的 grep 命令,你是否看到列出了一些提交? 是的,列出了一些提交。
  • 好的,并且(带有文件过滤器部分但没有 grep 部分)未列出提交 91bed55,对吗?
  • 是的,它不包含 91bed55。

标签: git linux-kernel


【解决方案1】:

您省略了一些重要的项目,但是(相对明显)有问题的存储库是 Linux 内核的线索,我能够重现它并诊断问题。

有问题的两个提交是(正如您所展示的):

91bed5565bba03b2a9f7334b58ae4be9df7c3840
6206b7981a36476f4695d661ae139f7db36a802d

这些都可以从标签 v5.15 访问,所以如果我们克隆一个 Linux 内核并检查 v5.15 并运行 git log 我们会遇到它们:

$ git switch v5.15
[output snipped]
$ git log
[output snipped]
/91bed5565bba03b2a9f7334b58ae4be9df7c3840
[the commit shows up - same goes for the other]

然而,这些提交被深埋在多个合并之后。运行git log -- drivers/net/ethernet/qlogic/qed/qed_mcp.c 并搜索显示这些提交不会出现,无论是否使用--oneline,但是:

$ git log --oneline -- drivers/net/ethernet/qlogic/qed/qed_mcp.c | grep 91bed5565
$ git log --oneline --full-history -- drivers/net/ethernet/qlogic/qed/qed_mcp.c | grep 91bed5565
91bed5565bba Revert "qed: fix possible unpaired spin_{un}lock_bh in _qed_mcp_cmd_and_union()"

因此,有趣的问题是:为什么添加 --full-history 会使提交显示出来?答案 in the documentation, under the History Simplification section。但这可能不仅仅是参考 git log 手册页。

git log

记住git log的工作是:

  • 访问存储库中历史的一些选定子集;那么
  • 显示上述子集的一些选定子集。

我们还需要定义一些术语。我们应该定义的第一件事是历史

第一个近似值,存储库中的历史是存储库中的一组提交。不过,这遗漏了一些重要的细节:

  • 每个提交都会存储其所有文件的快照:或多或少地,是一组 tarball 或其他档案的更节省空间的版本。
  • 每个提交还存储一些元数据。元数据是关于提交本身的信息,包括提交作者的姓名和电子邮件地址等信息。
  • 每个提交都是一个 Git对象,这些对象都被编号(带有诸如 91bed5565bba03b2a9f7334b58ae4be9df7c3840 之类的哈希 ID)。

在任何一次提交的元数据中,您都会找到其他提交的哈希 ID 列表。我们称这些为提交的父级或父级。这些其他提交必须实际存在,1 这意味着提交总是“向后指向”它们的父母,永远不会转发给任何未来的孩子。

因此,可以在 Git 存储库中获取一组提交并将它们形成Directed Acyclic Graph 或 DAG。这意味着我们还定义了一种遍历图的方法,即访问历史记录:我们选择一些起点提交并访问它们,然后访问它们的父母,然后拜访父母的父母,依此类推。有些父母可能已经在这个特定的过程中访问过,所以我们必须定义我们是否要使用深度优先、广度优先或其他方式,并确保我们不会多次访问提交,但那是visit 提交的含义的要点:我们遍历从提交到早期提交的父链接诱导的图。

这定义了我们的两个术语:我们访问(遍历图表)历史的某些选定子集(存储库中的提交)。然后,我们将显示子集的一些子集,稍后定义单词 show我们遗漏了一些重要的东西:这些子集是什么?


1Git 并不总是检查这条规则,但它源于 Git 确实检查的规则。稀疏和部分克隆会破坏检查,但不允许破坏规则,即使 Git 无法检查超过故意切断检查的点。


子集

git log 中的子集操作很复杂。有一些显而易见的:例如,我们不一定会在最近的提交时开始遍历。相反,git log 采用类似HEAD 的参数或分支或标签名称来指示从哪里开始。如果我们不给它任何这些,它使用HEAD;这里就是这样,所以它从HEAD开始,这是通过标签v5.15找到的提交。

然后,我们可以使用-n 11 来让git log 访问11 个提交然后退出。数量限制很简单:如果我们达到限制,git log 会提前退出“访问更多提交”循环(一旦实际显示的提交计数变为零)。

如果我们不使用 路径名 和其他各种选项——也就是说,如果我们运行 git log 而不是 git log -- drivers/net/ethernet/qlogic/qed/qed_mcp.c——这些是我们设置的唯一真正限制子集,因此我们将访问并显示所有提交可从 HEAD 访问,从而找到感兴趣的两个。但是如果我们添加一个路径名,Git 需要确保不显示不“有趣”的提交。

也许不幸的是,列出路径名会启用两个单独的子集操作。一个是在展示部分,我们稍后再讨论。另一个在参观部分。

当 Git 进行访问时,Git 会使用优先级队列进行访问。优先级队列开始保存来自您的 git log 命令的提交哈希 ID:如果您运行 git log a123456 master v5.15,Git 会将这三件事中的每一个解析为哈希 ID,并将生成的哈希 ID 放入队列中。2 如果您在没有任何指定起点的情况下运行 git log,Git 会将 HEAD 解析为一个哈希 ID,并将该(单个)哈希 ID 放入队列中。

然后Git开始它的访问循环,其结构大致如下:

  • 当队列不为空时
    • 从队列中取出最高优先级的提交
    • 根据子集显示或不显示(可能退出循环)
    • 在队列中插入部分或全部未访问的父项

最后一步有点奇怪:some 或 all? 为什么只有 some? 嗯,这是我们的第一个子集操作。这就是历史简化的用武之地。

如果您使用--first-parent 选项,这里的子集操作是仅将任何合并提交的第一个父级放入队列中。 (我不确定这是否/如何与下一个选项相互作用。)

如果使用路径名,这里的子集操作取决于历史简化模式。使用--full-history,Git 将所有父级放入队列中,但默认情况下,Git 会选择任何精简后的树相同的父级。这最后一个短语可能很令人困惑,因为我已经介绍了剥离一棵树的想法,但没有解释为什么“相同”很重要。要了解这些内容的真正含义,请查阅链接文档并搜索 TREESAME。

但是,在我们的特定情况下,重要的是一个文件 drivers/net/ethernet/qlogic/qed/qed_mcp.c,如合并提交的快照和每个父提交的快照中所示。 Git 将选择drivers/net/ethernet/qlogic/qed/qed_mcp.c未修改的父级。如果它没有在 both 父级中修改,Git 会随机选择一个父级(尽管我认为实际上它总是选择第一个父级)。

因此,如果在普通的双亲合并提交之后有一组提交,Git 可能根本不会访问一个父级,而只追求另一个父级。在这种情况下会发生这种情况:drivers/net/ethernet/qlogic/qed/qed_mcp.c 未被某些合并修改,因此 Git 跟随其中一位父级。您正在寻找的两个提交——在 6206b798a3... 中触及 drivers/net/ethernet/qlogic/qed/qed_mcp.c 的较早一个提交和将其放回原处的 91bed5565bb... 提交——位于这些修剪链之一的后面。所以我们甚至没有机会访问这两个提交,更不用说决定是否显示它们了。

如果我们确实访问了某个提交,然后我们选择是否显示该提交。在这里,TREESAME 测试再次生效:如果子提交和父提交包含不同版本的 drivers/net/ethernet/qlogic/qed/qed_mcp.c,“显示提交”部分实际上会显示一些内容(如果有,则减少 -n 计数) .如果没有,它根本不会显示任何内容。这就是第二个子集操作。 (而show 仅表示git log 打印的内容。)


2警告:如果有否定项(例如git log --not origin/mastergit log ^origin/master),则任何在否定范围内的提交都不会首先进入队列。因此git log HEAD --not HEAD 什么也没有显示。这也适用于稍后的父插入。


结论:为什么添加--full-history 会显示提交?

在这种情况下,第二个子集操作使我们无法看到不涉及drivers/net/ethernet/qlogic/qed/qed_mcp.c 的主线提交。我们确实想要这个!但是第一个子集操作——历史简化部分——通过从不将某些合并父提交哈希 ID 输入到优先级队列中来从图遍历中删除整个提交链。这使我们无法看到这两个提交。

--full-history 选项告诉 Git 根本不要执行 first 子集操作。通过跳过它,我们使第二个更努力,但看到我们想看到的提交。

【讨论】:

    【解决方案2】:

    你基本上有这种情况:

        o--Q--o--R--o        side branch (2nd parent)
       /             \
    --o--o--o--o--o---M--o   "main" branch
    

    Q 对文件进行更改,R 还原更改,并且没有其他提交对同一文件进行更改。

    因此,在合并提交 M 时,文件的内容在 M 的两个父级中是相同的。在这种情况下,当使用路径限制调用git log 时,历史简化会选择其中一个父级,通常是第一个,并且只跟随那个分支。 Git 基本上忽略了侧分支,因为它没有提供任何必要的信息来解释M 处的文件状态。 (毕竟,总而言之,侧分支并没有对文件进行任何更改。)

    【讨论】:

      猜你喜欢
      • 2018-07-28
      • 2016-02-13
      • 2020-04-03
      • 2015-01-17
      • 2011-07-07
      • 2022-10-20
      • 1970-01-01
      • 2013-02-08
      • 2018-03-27
      相关资源
      最近更新 更多