简体   繁体   English

git - 查找创建分支的提交

[英]git - Find commit from which was branch created

Is it possible to find commit (ideally only the commit hash) from which was branch created, even if the branch was already merged ?即使分支已经合并,是否有可能找到创建分支的提交(理想情况下只有提交哈希)?

For example:例如:

master   A-B-C-D-E
          \   /
feature    F-G

feature branch is already merged in master , but it still exists and can be checkout to. feature分支已经合并到master中,但它仍然存在并且可以被 checkout 到。 I would like to find, that feature branch started from A .我想找到,该feature分支从A开始。

( Disclaimer : off-topic answer - not adressing already merged branches) 免责声明:题外话 - 不涉及已经合并的分支)


If feature and master are not yet merged , you can just get their "merge-base" ( doc ):如果featuremaster没有合并,你可以得到他们的“merge-base”( doc ):

git merge-base master feature

It will output your commit A 's long-form hash.它将 output 您的提交A的长格式 hash。

Branches don't really have parent branches.分支实际上没有父分支。 (Related: How to find the nearest parent of a Git branch? ) You can find the hash of A pretty easily, but consider this diagram: (相关:如何找到 Git 分支的最近父级? )您可以很容易地找到A的 hash,但请考虑下图:

H--I--L--N--O--R---T--U   <-- master
 \     \     \    /
  J--K--M--P--Q--S   <-- feature

Which commit(s) would you like to find here?您想在这里找到哪些提交? O , L , and H are all viable candidates for "where feature started from". OLH都是“ feature从哪里开始”的可行候选者。 But maybe feature didn't start from any of those;但也许feature不是从其中任何一个开始的; maybe it started from P , which used to have a label main-feature .也许它是从P开始的,它曾经有一个 label main-feature See the related question for details.有关详细信息,请参阅相关问题。

To find commit A from your diagram, which I'll reproduce here a form I consider more accurate (the name master merely points directly to commit E for instance—commits F and G are on branch master too:):要从您的图表中找到提交A ,我将在这里复制一个我认为更准确的形式(例如,名称master仅直接指向提交E - 提交FG也在分支master上:):

A--B--C--D--E   <-- master
 \      /
  F----G   <-- feature

we can start from commit E (tip of master) and step back to commit D (the merge).我们可以从提交E (master 的提示)开始,然后退回到提交D (合并)。 We know we have hit D when one of its parents is the tip of feature , ie, is commit G .我们知道我们已经击中D当它的父母之一是feature的尖端,即提交G We then get both parent hash IDs from commit D :然后我们从提交D中获取父 hash ID:

git rev-parse <hash-of-D>^@

and make sure there are exactly two of them (a three-parent merge makes this hard), and then invoke git merge-base --all on those two parent hash IDs.并确保其中恰好有两个(三父合并使这变得困难),然后在这两个父 hash ID 上调用git merge-base --all This immediately produces the hash ID of commit A .这会立即产生提交A的 hash ID。

When we use this same procedure on my sample graph, we step back from U to T , discover that it has two parents and the second is S which is the tip of feature , and ask Git to find the merge base of R and S .当我们在我的示例图上使用相同的过程时,我们从U退回到T ,发现它有两个父级,第二个是S ,它是feature的尖端,并要求 Git 找到RS的合并基。 The output of this git merge-base --all is the hash of commit O .这个git merge-base --all是提交O的 hash。

To do this relatively quickly in shell script, start with:要在 shell 脚本中相对快速地执行此操作,请从以下开始:

git rev-list --merges --first-parent master

which produces a list of all merge commit hash IDs reachable by walking first-parent descendants of master .这会生成一个列表,其中列出了所有合并提交 hash ID 可通过步行master的第一父后代到达。 (You may or may not want --first-parent ; if you've been using git pull heavily, the first parent linkages of master may be somewhat damaged by "foxtrot merges" .) That is, this gives us commit hashes T and D in the two diagrams here (and probably more). (你可能想要也可能不想要--first-parent ;如果你一直在使用git pullmaster的第一个父链接可能会被“foxtrot 合并”损坏。)也就是说,这给了我们提交哈希T和此处的两个图中的D (可能更多)。 If you omit --first-parent , think about what you want from cases where other branches have merged into master , bringing with them commits from the feature branch in question, and decide whether you might want --topo-order in your git rev-list .如果您省略--first-parent ,请考虑从其他分支合并到master的情况下您想要什么,并从相关feature分支中进行提交,并决定您是否可能需要--topo-order在您的git rev-listgit rev-list

Next, walk through these commits, getting their parent hashes:接下来,遍历这些提交,获取它们的父哈希:

looking_for=$(git rev-parse feature)  # this is the hash ID we're looking for
found=false
for hash in (output of above git rev-list); do
    set -- $(git rev-parse ${hash}^@)
    case $# in
    2) # two parents, good
        if [ $2 == $looking_for ]; then found=true; break; fi;;
    *) # more than two parents, we might go wrong
        echo "commit $hash has $# parents, help"; exit 1;;
    esac
done
if ! $found; then
    echo "unable to find the commit hash you were looking for"
    exit 1
fi

At this point, you have the desired commit pair in $1 and $2 due to the way set parses arguments, so:此时,由于set解析 arguments 的方式,您在$1$2中有所需的提交对,因此:

# found the desired commit pair, so now just invoke git merge-base
set -- $(git merge-base --all $1 $2)
case $# in
1) echo "the commit you are looking for is $1"; exit 0;;
*) echo "I found $# candidate commits $@"; exit 1;;
esac

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM