【问题标题】:How to find merge commits in history done using "git merge -s ours" option?如何使用“git merge -s ours”选项在历史记录中查找合并提交?
【发布时间】:2018-07-08 08:52:24
【问题描述】:

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

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

我想可以通过将合并提交与其父级进行比较并检查是否只有一个父级分支内容被实际采用,但我不知道如何将这一理论转化为 git 命令。我什至从什么命令开始?

【问题讨论】:

    标签: git merge git-merge


    【解决方案1】:

    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 新合并提交的快照(其存储的树)应该匹配两个父级之一,特别是 first 父级。您可能想要放宽您的条件并检查树是否与第二个父级的树匹配(theirs-strategy 合并,除了-s theirs 没有内置),或者对于章鱼合并,任何 的父母,但“匹配第一父母”可能会这样做,而且稍微容易一些。

    两种明显的方法是:

    • 将合并提交的树与其第一个父级的树进行比较:git diff(或其管道等效项)与两个特定的提交或树哈希。
    • 或者,由于我们只关心整棵树,直接比较顶层树哈希即可。这会检查纯粹的完全匹配;如有必要,使用完整的 git diff 或等效项可以让您稍微放宽匹配标准。

    我们将在 shell 脚本中编写第二个代码——比较存储在合并中的 tree 哈希和它的第一个父级中的哈希值——因为它可能更快,并且无论如何演示了更多的各种 Git 工具。我们从通用的“读取每个提交哈希”循环开始:

    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

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

    【讨论】:

      猜你喜欢
      • 2023-03-20
      • 2011-03-14
      • 2013-03-24
      • 1970-01-01
      • 2011-11-08
      • 1970-01-01
      • 2012-08-30
      • 2015-10-19
      • 1970-01-01
      相关资源
      最近更新 更多