如何使用“git merge -s ours”选项查找历史记录中的合并提交?

时间:2018-01-29 15:45:51

标签: git merge git-merge

当将origin / develop合并到本地开发时,滥用“-s ours”合并选项基本上会丢弃自上次开发更新以来所完成的工作。我通过故意查看我知道丢失的文件的历史记录,在git历史记录中手动找到了这两个实例。

有没有办法找到使用“-s ours”合并选项完成的所有提交?

我想通过比较合并提交与他们的父母并检查是否只有一个父母分支内容实际上被采用但我不知道如何将这个理论转换为git命令是可能的。我甚至可以从哪个命令开始?

1 个答案:

答案 0 :(得分:5)

TL; DR(但未经测试)

作为shell脚本:

git rev-list --merges HEAD |
while read rev; do
    thistree=$(git rev-parse $rev^{tree})
    p1tree=$(git rev-parse $rev^1^{tree})
    if [ $thistree = $p1tree ]; then
        echo "commit $rev has the effect of -s ours"
    fi
done

直接答案是“不”(但请继续阅读)。 使用的实际策略不会自动记录。 (如果进行所有合并的人都非常自律,也许他们会在每个合并提交消息中记录这些信息。基于你的问题描述,这显然没有发生......)但这不一定是有趣的问题无论如何。有人可以在不实际使用-s ours策略的情况下创建具有ours 效果的合并。

更有趣的问题是自动查找具有-s ours效果的合并,无论是否使用该选项;并且 很容易自动化。

问题归结为:

  • 查找合并提交:此工具为git rev-list
  • 测试合并提交以查看它们是否具有-s ours的效果:此工具是......嗯,见下文;有几种可能性。

一旦你知道git rev-list如何枚举提交,找到合并提交是微不足道的。与git log一样,您为git rev-list提供一个起点,然后Git从该点开始向后,通过该提交可以访问的历史记录,直到它耗尽历史记录或遇到你告诉它用作停止点的提交。

通常,一个好的起点是HEAD,这是您现在已经签出的提交,这可能是分支的提示。您甚至可以给出多个起点,例如--branches(每个分支的每个尖端)或--all(每个带有标签的提交,无论该标签是分支名称,标签名称,远程 - 跟踪名称,甚至是隐藏的refs/stash等特殊名称之一。

然后,您希望将输出限制为仅列出合并提交。合并提交是指至少有两个父项的任何提交:--min-parents=2。不过,你可能更喜欢这个的同义词,所以我们得到了,例如:

git rev-list --merges HEAD

从现在的位置开始,然后通过HEAD可以访问的所有提交向后工作。 (有关此可访问性概念的更多信息,请参阅Think Like (a) Git。)

git rev-list的输出是满足rev-list条件的提交哈希ID流,在本例中为--merges(每次可达到的合并提交):对人类不是很有用,但非常适合计算机程序。现在我们只需要编写标识有趣的合并的程序。

我们已经看到合并提交是一个至少有两个父级的提交。 -s ours或等价物的作用是告诉Git新合并提交的快照(其存储的树)应该与两个父节点中的一个匹配,特别是第一个父节点。您可能希望放宽条件并检查树是否与第二个父项(theirs - 策略合并的那个匹配,除了没有内置-s theirs),或者用于章鱼合并,任何父母,但“匹配第一父母”可能会做,并且稍微容易。

这两种显而易见的方法是:

  • 将合并提交的树与其第一个父树的比较:git diff(或其管道等效物)与两个特定的提交或树哈希。
  • 或者,因为我们只关心整个树,所以只需直接比较顶级树形哈希。这会检查纯粹的完全匹配;使用完整的git diff或等效标准可以让您在必要时稍微放宽匹配条件。

我们将对第二个进行编码 - 比较合并中存储的tree哈希及其第一个父级shell脚本,因为它可能更快,并且在任何情况下都演示了更多的各种Git工具。我们从泛型“read every commit hash”循环开始:

while read rev; do ...; done

每个版本都将在循环中以$rev

的形式提供

现在我们只需将$rev(提交)转换为自己的树:

git rev-parse $rev^{tree}

使用the gitrevisions syntax来查找指定的提交树哈希ID。我们必须将其保存在变量中:

thistree=$(git rev-parse $rev^{tree})

然后我们想找到第一个父树的树。第一个父级是gitrevisions语法${rev}^1。这里的括号在技术上是不必要的,所以我会省略它们。然后我们想要那个提交的树,我们需要将它保存在另一个变量中:

p1tree=$(git rev-parse $rev^1^{tree})

如果两棵树匹配,则提交$rev具有-s ours效果,无论它是否是-s ours,所以我们应该打印它的哈希ID,甚至可以在其上运行git show

此循环的最终版本(将所有部分放在一起)是本答案的顶部。