如果我有一个看起来像的git树
A---o---o---o
\
C---o---o---o---D
/
B---o---o
如何找到给定A,B和D(如果有帮助)的C?我猜想这与git merge-base
的相反之处:(大致)发现了两次提交的“最新的共同祖先”,这里我正在寻找“最早的共同后代”。
理论上可以有多个候选对象(例如,如果我添加了直接合并A和B的提交)。但是在这种情况下没有,所以我不必担心这种情况。
答案 0 :(得分:2)
一种方法是查看提交A..D
和B..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
来获取给定修订版中的所有可用提交,并缓慢地沿历史回溯,直到找到无法同时达到两个目标的提交为止。目标提交的时间越早,速度越慢。