列出祖先曾经触及某个文件的合并提交

List merge commits where once of the ancestors touched a certain file

我只想列出给定文件实际进入当前分支的合并提交。例如:

-A--B----C-------D--E------F----G--------H---
  \   \---P--Q--/    \---X--Y--/        /
   \-----------------------------M-----/

假设 P 和 Y 是唯一修改了 abc.txt 的提交。在这种情况下,如何让 git 日志只列出 D 和 G?请注意,D 和 G 实际上并没有直接触及 abc.txt:它们只是具有触及 abc.txt.

祖先的合并提交

我试过了:

git log --merges -m --name-only --follow abc.txt

不幸的是,这也包括 H,因为 H 曾经有一个祖先 (G) 接触过(通过 Y)abc.txt。

(注意:List merge commits affecting a file 提出了一个不同的问题:它询问哪些合并提交(不是它们的祖先,而是合并提交本身)实际上触及了 abc.txt。)

你不能,至少不能直接。 (我认为——这是基于这样一个事实,即 --simplify-merges 是,apparently,没有做你想做的事。通常它似乎会做你想做的事。请注意,你需要两者 --simplify-merges --merges ;您将在下面的 git rev-list 中使用 --simplify-merges 而不是 --merges,在“第一步。)

Note that D and G did not actually touch abc.txt directly: they were just merge commits that had ancestors that touched abc.txt.

好吧,是的,也不是。与 Git 中的任何提交一样,合并提交有一个树和一些 parent(s)。提交 D 和 G,作为合并,至少有两个 parent 提交。每个 parent 也有一棵树,所以我们可以 diff(或有 Git diff)D^1(即 C)与 D,以及 D^2(即 Q)与 D。其中之一这两个——第一个,如果我有正确排序的两个 parent——将显示对 abc.txt 的更改。我们可以以相同的方式将 G^1 和 G^2 与 G 进行比较,并获得相同的效果。但我想我们都知道你的意思:我们以这种方式发现的变化存在 因为 一些较早的提交。

不过H也是如此!比较 H^2(又名 M)与 H 显示了对同一文件的更改。这个变化是通过G带来的,G的变化也是通过Y带来的。那么问题就来了:为什么要算G,而不是算H?

认为 我们应该在这里计算 G 而不是 H 的原因是“带来”变化的祖先(即 Y)有一个“下游”提交,即 G,即合并,我们希望声称“吸收”了更改。

(按照同样的逻辑,如果 M 也对 abc.txt 进行了更改,那么我们最终还是希望将 H 包含在我们的集合中。)

如果该推理是正确的,则建议使用一种算法来查找您关心的合并:

  • 首先,找到文件以任何方式更改的所有合并。这将选择所有 D、G 和 H。

  • 其次,对于每个这样的合并,找到它的 parents(至少有两个)。如果 abc.txt 的合并提交版本与 both parent 版本不同,请将合并标记为“明确包含”在集合中:我们从“both”中引入了更改sides” and/or 这是一个“邪恶的合并”(它引入了两个 parent-fork 中都没有进行的更改)。但是,如果不是,请将合并标记为“可能可移除”。

  • 最后,以“大多数 parent-y”顺序遍历所有“可能可移动”的合并(在任何 children 之前执行所有 parent:你我会在第一步中从适当的 git rev-list --topo-order --reverse 中自然地得到这个,或者你可以以其他顺序得到列表,然后反向工作)。任何标记为“绝对保留”的提交都会保留;任何标记为“可能保留,可能可移动,取决于 parent #N”的提交都会保留 除非 它有一个保留的 parent 合并 与 parent-in-that-direction 相同 abc.txt;并且任何标记为“可能保留,可能可移动”的标记在具有相同 abc.txt.

    的保留 parent-in-that-direction 时将被删除

    (我不确定 off-hand 如何为章鱼合并修改这个,所以我会把这个留给你考虑。)