我怎样才能看到另一个分支从哪个分支分叉?

时间:2011-01-26 10:29:43

标签: git branch

我的git存储库有三个分支,develstablecustomers/acme_patches。很久以前,stabledevel分叉,所有错误修正都发生在stable。我们时不时地将stable合并回develcustomers/acme_patches是一个包含一些客户特定补丁的分支。该分支未合并为develstable

用一些ASCII艺术来说明这个场景:

            o---o---o          customers/acme_patches?
           /
  o---o---1---o---o---o        stable
 /     \           \
o---o---o---2---o---o---o---o  devel
             \
              o---o---o        customers/acme_patches?

现在我想知道:

来自customers/acme_patchesdevel的{​​{1}}分支是什么?我只是知道它过去曾分离过其中一个,但我不知道哪个。例如。它可能已在上图中提交stable1

我一直在玩2git log --oneline --graph,但由于gitk在几百次提交之前被分叉,所以很难跟上被绘制的线条。

是否有一个快速命令(一个小脚本也可以),它可以某种方式跟随customers/acme_patches中的提交向后找到第一个提交有两个孩子(fork point)然后确定是否提交是在customers/acme_patches还是stable

完成的

在最好的情况下,我可以执行类似的操作(原谅提示,我在Windows上):

devel

4 个答案:

答案 0 :(得分:23)

使用git 1.9 / 2.0(2014年第一季度),您可以根据Git使用git merge-base --fork-point来询问最佳共同祖先。

您可以看到新选项:


由于来自commit ad8261dJohn Keeping (johnkeeping)git rebase可以使用相同的新--fork-point选项,如果你需要像{{1}那样重新分支一个分支,它可以派上用场转到customers/acme_patches
(我不是说这在你的具体情况下会有意义)


注意:Git 2.16(2018年第一季度)确实澄清并增强了“devel”的文档,因为很清楚它的计算方法,但不是为什么/为什么。

commit 6d1700bJunio C Hamano (gitster)(2017年11月9日) Junio C Hamano -- gitster --于2017年11月27日commit 022dd4a合并)

  

merge-base --fork-point doc:澄清示例和失败模式

     

用于解释merge-base --fork-point模式的图示历史记录   命名三个关键点提交B3,B2和B1从最旧到   最新的,很难读   将它们重新标记为B0,B1,B2   还说明了使用--fork-point工具进行rebase后的历史记录。

     

该文本已提及使用reflog,但描述不是   通过使用reflog 清楚我们想要获得什么好处   澄清是找到已知的提交的提示   远程跟踪分支
  这反过来又需要用户知道基本假设的后果,即,reflog条目的到期将使得无法确定哪些提交位于远程跟踪分支的顶端而我们在有疑问时失败(而不是随机给出)甚至没有错误的结果   警告)。
  另一个限制是,如果你没有从远程跟踪分支的尖端而是从中间分叉,那么它将没有用。
  描述它们。

所以the documentation现在写着:

  

处理使用--fork-point创建的topic分支后,远程跟踪分支的历史记录   git checkout -b topic origin/master可能已被重绕并重建,导致a   这种形状的历史:

origin/master
  

其中 o---B2 / ---o---o---B1--o---o---o---B (origin/master) \ B0 \ D0---D1---D (topic) 用于指向提交B0,B1,B2以及现在它   B点,你的origin/master分支在它上面开始   当topic在B0时,你构建了三个提交,D0,D1,   和D,在它之上。
  想象一下,您现在想要在更新的origin/master之上重新设定您在topic上所做的工作。

     

在这种情况下,origin/master会返回   上图中B0的父级,但git merge-base origin/master topic 的范围   提交你想要在B之上重播(它包括B0,哪个   不是你写的;这是另一方丢弃的提交   它把它的尖端从B0移到了B1)。

     

B0^..D旨在帮助解决此类情况   它不仅需要B,还需要B0,B1和B2(即存储库的reflog知道的远程跟踪分支的旧技巧),以查看构建主题分支的提交并找到B0,允许您仅重放关于你的主题的提交,不包括以后提交的另一方   丢弃。

     

因此

git merge-base --fork-point origin/master topic
  

会找到B0和

$ fork_point=$(git merge-base --fork-point origin/master topic)
  

将重放B顶部的D0,D1和D以创建新的历史记录   形状:

$ git rebase --onto origin/master $fork_point topic
  

需要注意的是,您的存储库中较旧的reflog条目可能是   已过期 o---B2 / ---o---o---B1--o---o---o---B (origin/master) \ \ B0 D0'--D1'--D' (topic - updated) \ D0---D1---D (topic - old)
  如果远程跟踪分支git gc的reflog中不再出现B0,则origin/master模式显然无法找到并失败,从而避免给出随机且无用的结果(例如B0的父级,就像没有--fork-point选项的相同命令一样。)

     

此外,您使用--fork-point模式的远程跟踪分支   必须是你的主题从它的尖端分叉的那个。
  如果你从一个比提示更早的提交分叉,这个模式将找不到fork point(想象在上面的示例历史中B0不存在,   --fork-point从B1开始,移到B2然后B,然后你分叉了   当origin/master为B1时,您的主题为origin/master^;的形状   历史将与上面相同,没有B0和父母   B1是origin/master正确找到的,   但git merge-base origin/master topic模式不会,因为它不是其中之一   以前的提交位于--fork-point)的顶端。

答案 1 :(得分:5)

嗯,这个答案可能没有完美的解决方案。我的意思是git中没有fork-origin等价物(据我所知)。 由于stable分支已合并到devel,因此acme_patches(来自1)位于develstable分支上。

你可能做的是:

git branch --contains $(git merge-base customers/acme_patches devel stable)

如果你有稳定但没有开发,或者开发并且不稳定,那么你就知道它来自哪里。

例如,在案例2中,您将拥有

$ git branch --contains $(git merge-base customers/acme_patches devel stable)
customers/acme_patches
devel

而在案例1中你会有

$ git branch --contains $(git merge-base customers/acme_patches devel stable)
customers/acme_patches
devel
stable

因为它现在在两个分支上(因为从稳定到开发的合并)

答案 2 :(得分:3)

好吧,git merge-base customers/acme_patches stable应该显示这两个分支的共同祖先。

你可以尝试,例如,gitk --left-right customers/acme_patches...stable(注意三个点!)。这将显示那些分支中的所有提交,而不是合并库中的提交。使用--left-right将使用左箭头或右箭头标记每个提交,如果它们位于customers / acme_patches中则按左箭头显示,如果它们处于稳定状态则使用右箭头。

可能还添加--date-order我发现有时候有助于理解输出。

(您可以将此语法与git log --graph而不是gitk一起使用,但imho就是这种情况,可视化图形显示是一个很大的改进。)

答案 3 :(得分:2)

不确定它是否涵盖所有情况,但这是我提出的功能:

git_branch_contains() {
    local b=$1
    local c=$2
    IFS_=$IFS
    IFS=$'\n'
    local branches=($(git branch --contains "$c" | sed -E 's/^(\*| ) //'))
    IFS=$IFS_
    for b2 in "${branches[@]:+${branches[@]}}"; do
        if [ "$b2" = "$b" ]; then
            return 0
        fi
    done
    return 1
}

git_upstream_branch() {
    local b=$1
    local c1=$(git merge-base --fork-point master "$b") || local c1=
    local c2=$(git merge-base --fork-point dev "$b") || local c2=
    if ! [ "$c1" ]; then
        echo dev
        return
    fi
    if ! [ "$c2" ]; then
        echo master
        return
    fi
    local fp
    if git merge-base --is-ancestor "$c1" "$c2"; then
        fp=$c2
    else
        fp=$c1
    fi
    if git_branch_contains master "$fp" && ! git_branch_contains dev "$fp"; then
        echo master
    else
        echo dev
    fi
}

这是测试它们的脚本(git-upstream-branch-test.sh):

#!/usr/bin/env bash
set -eu

. git-upstream-branch.sh

git_commit() {
    if ! [ "${commit_i:-}" ]; then
        commit_i=0
    fi
    (( commit_i++ )) || true
    echo "$commit_i" > "$commit_i"
    git add "$commit_i"
    git commit -qm "c$commit_i"
}

git_merge() {
    if ! [ "${merge_i:-}" ]; then
        merge_i=0
    fi
    (( merge_i++ )) || true
    git merge -m "$merge_i" $1
}

A_TOPOLOGY=${1:-}

mkdir git-upstream-branch-test-repo
cd git-upstream-branch-test-repo
git init -q
if [ "$A_TOPOLOGY" = 10 ]; then
    git_commit
    git_commit
    git checkout -qb dev
    git_commit
    git_commit
    git checkout -q master
    git_commit
    git_commit
    c=$(git rev-parse HEAD)
    git_commit
    git_commit
    git checkout -q dev
    git checkout -qb t1
    git_commit
    git_commit
    git checkout -q dev
    git_commit
    git_commit
    git rebase --onto "$c" dev t1
elif [ "$A_TOPOLOGY" = 11 ]; then
    git_commit
    git_commit
    git checkout -qb dev
    git_commit
    git_commit
    git checkout -q master
    git_commit
    git_commit
    git checkout -q dev
    c=$(git rev-parse HEAD)
    git_commit
    git_commit
    git checkout -q master
    git checkout -qb t1
    git_commit
    git_commit
    git checkout -q master
    git_commit
    git_commit
    git rebase --onto "$c" master t1
else
    git_commit
    git_commit
    git checkout -qb dev
    git_commit
    git_commit
    git checkout -q master
    git_commit
    git_commit
    if [ "$A_TOPOLOGY" = 4 ] || [ "$A_TOPOLOGY" = 5 ] || [ "$A_TOPOLOGY" = 6 ]; then
        git_merge dev
        git_commit
        git_commit
        git checkout -q dev
        git_commit
        git_commit
        git checkout -q master
    elif [ "$A_TOPOLOGY" = 7 ] || [ "$A_TOPOLOGY" = 8 ] || [ "$A_TOPOLOGY" = 9 ]; then
        git checkout -q dev
        git_merge master
        git_commit
        git_commit
        git checkout -q master
        git_commit
        git_commit
    fi
    git checkout -qb t1
    git_commit
    git_commit
    git checkout -q master
    git_commit
    git_commit
    if [ "$A_TOPOLOGY" = 2 ] || [ "$A_TOPOLOGY" = 5 ] || [ "$A_TOPOLOGY" = 8 ]; then
        git_merge dev
    elif [ "$A_TOPOLOGY" = 3 ] || [ "$A_TOPOLOGY" = 6 ] || [ "$A_TOPOLOGY" = 9 ]; then
        git checkout -q dev
        git_merge master
    fi
fi
git --no-pager log --oneline --graph --decorate --all
git_upstream_branch t1

像这样使用,

$ rm -rf git-upstream-branch-test-repo && ./git-upstream-branch-test.sh NUMBER

其中NUMBER是1到11之间的数字,用于指定要测试的案例(拓扑)。