查找合并两个祖先的提交

时间:2018-09-24 13:12:48

标签: git

如果我有一个看起来像的git树

A---o---o---o
             \
              C---o---o---o---D
             /
    B---o---o

如何找到给定A,B和D(如果有帮助)的C?我猜想这与git merge-base的相反之处:(大致)发现了两次提交的“最新的共同祖先”,这里我正在寻找“最早的共同后代”。

理论上可以有多个候选对象(例如,如果我添加了直接合并A和B的提交)。但是在这种情况下没有,所以我不必担心这种情况。

2 个答案:

答案 0 :(得分:2)

一种方法是查看提交A..DB..D的列表,并采用出现在两​​个列表中的最早的行。

您可以使用comm来保持两种历史的共同点

git log --ancestry-path --oneline A..D > /tmp/logA
git log --ancestry-path --oneline B..D > /tmp/logB
comm -12 /tmp/logA /tmp/logB | tail -1

一个内衬版本:

comm -12 <(git log --ancestry-path --oneline A..D) <(git log --ancestry-path --oneline B..D) | tail -1

答案 1 :(得分:1)

这肯定是一件很奇怪的事情,我很想知道您的用例,它经常执行此操作,以至于您正在寻找命令。

无论如何,“最简单”的方法是从D向后浏览历史记录,直到找到所需的内容为止。我敢肯定这可以改善,但这是一个可以满足您需求的shell脚本。

#!/bin/sh

startingCommit=$1
target1=$2
target2=$3

#https://stackoverflow.com/a/8574392/3980115
containsElement () {
        local e match="$1"
        shift
        for e; do [[ "$e" == "$match" ]] && return 0; done
        return 1
}

currentCommit=$startingCommit
resultCommit=$startingCommit
result=1

while true ; do
        echo "Trying $currentCommit"
        #https://stackoverflow.com/a/11426834/3980115
        list=( $(git rev-list $currentCommit) )
        if (containsElement $target1 "${list[@]}" && containsElement $target2 "${list[@]}"); then
                resultCommit=$currentCommit
                currentCommit=${list[1]}
                result=0
                continue
        else
                break
        fi
done

if [ "$result" ] ; then
        echo "Earliest descendent: $resultCommit"
else
        echo "No common commit found"
fi

从命令行调用,提交方式为:

./getDecesdent.sh D A B

输出:

Earliest descendent: C

我不知道这是否可以在更复杂的分支场景中使用,但是我怀疑如果D和C之间的任何提交都合并,它可能不会获得正确的结果-老实说,我不知道。 >

但是,这将在问题描述的情况下找到正确的提交-只是非常缓慢。该脚本通过使用rev-list来获取给定修订版中的所有可用提交,并缓慢地沿历史回溯,直到找到无法同时达到两个目标的提交为止。目标提交的时间越早,速度越慢。