git 我怎样才能看到另一个分支是从哪个分支分叉出来的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4803492/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 04:59:03  来源:igfitidea点击:

How can I see what branch another branch was forked from?

gitbranch

提问by Frerich Raabe

My git repository has three branches, devel, stableand customers/acme_patches. A long time ago, stablewas forked from devel, and all the bugfixing takes place in stable. Every now and then, stableis merged back into devel. customers/acme_patchesis a branch with a few customer-specific patches. The branch wasn't merged into either of develand stable.

我的 git 存储库有三个分支develstablecustomers/acme_patches。很久以前,stable是从 分叉出来的devel,所有的错误修复都发生在stable. 时不时地,stable被合并回devel. customers/acme_patches是一个分支,有一些客户特定的补丁。分支没有合并到devel和 中的任何一个stable

A bit of ASCII art to illustrate the scenario:

一些 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?

Now I wonder:

现在我想知道:

What branch was customers/acme_patchesforked from - develor stable? I only know that it was forked off one of them in the past, but I don't know which. E.g. it might have been commit 1or 2in the above diagram.

哪个分支是customers/acme_patches从 -devel或分叉出来的stable?我只知道它过去是从其中一个分叉出来的,但我不知道是哪个。例如,它可能已提交12在上图中。

I've been playing around with git log --oneline --graphand gitkbut since customers/acme_patcheswas forked a few hundred commits ago, it's hard to follow the lines being drawn.

我一直在玩git log --oneline --graphgitk但是自从customers/acme_patches几百个提交前分叉以来,很难遵循所绘制的线条。

Is there maybe a quick command (a little script is fine, too) which can somehow follow the commits in customers/acme_patchesbackwards to find the first commit with two children (the fork point) and then determines whether that commit was done in stableor in devel?

是否可能有一个快速命令(一个小脚本也可以),它可以以某种方式customers/acme_patches向后跟踪提交以找到具有两个孩子的第一个提交(分叉点),然后确定该提交是在 中stable还是在中完成的devel

In the best case, I could just execute something like (excuse the prompt, I'm on Windows):

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

C:\src> git fork-origin customers/acme_patches
stable

采纳答案by Antoine Pelisse

Well, there is probably no perfect solution to this answer. I mean there is no fork-originequivalent in git (to my knowledge). Because the stablebranch is merged into devel, your acme_patches(from 1) is on both develand stablebranch.

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

What you could possibly do is:

你可能会做的是:

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

If you have stable and not devel, or devel and not stable, then you know where it comes from.

如果你有稳定而不是发展,或者发展和不稳定,那么你就知道它来自哪里。

For example, in the case 2, you would have

例如,在情况 2 中,您将有

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

while in case 1 you would have

而在情况 1 你会有

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

As it's now on both branches (because of the merge from stable to dev)

因为它现在在两个分支上(因为从 stable 到 dev 的合并)

回答by VonC

With git 1.9/2.0 (Q1 2014), you can use git merge-base --fork-pointto ask for the best common ancestor according to Git.

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

You can see that new option:

你可以看到这个新选项:



And since commit ad8261dfrom John Keeping (johnkeeping), git rebasecan use that same new --fork-pointoption, which can come in handy should you need to rebase a branch like customers/acme_patchesonto devel.
(I am not saying this would make sense in your specific scenario)

而且,由于提交ad8261d约翰饲养(johnkeepinggit rebase可以使用相同的新--fork-point选项,它可以派上用场,如果您需要衍合分支像customers/acme_patchesdevel
(我并不是说这在您的特定情况下有意义)



Note: Git 2.16 (Q1 2018) does clarify and enhance documentation for "merge-base --fork-point", as it was clear what it computed but not why/what for.

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

See commit 6d1700b(09 Nov 2017) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster--in commit 022dd4a, 27 Nov 2017)

请参阅Junio C Hamano() 的commit 6d1700b(2017 年 11 月 9 日(由Junio C Hamano合并-- --commit 022dd4a,2017 年 11 月 27 日)gitster
gitster

merge-base --fork-pointdoc: clarify the example and failure modes

The illustrated history used to explain the --fork-pointmode named three keypoint commits B3, B2 and B1 from the oldest to the newest, which was hard to read.
Relabel them to B0, B1, B2.
Also illustrate the history after the rebase using the --fork-pointfacility was made.

The text already mentions use of reflog, but the description is not clear what benefit we are trying to gain by using reflog.
Clarify that it is to find the commits that were known to be at the tip of the remote-tracking branch.
This in turn necessitates users to know the ramifications of the underlying assumptions, namely, expiry of reflog entries will make it impossible to determine which commits were at the tip of the remote-tracking branches and we fail when in doubt (instead of giving a random and incorrect result without even warning).
Another limitation is that it won't be useful if you did not fork from the tip of a remote-tracking branch but from in the middle.
Describe them.

merge-base --fork-pointdoc:阐明示例和故障模式

图解历史用于解释--fork-point从最旧到最新的三个关键点提交 B3、B2 和 B1的模式,这很难阅读。
将它们重新标记为 B0、B1、B2。
还说明使用该--fork-point设施进行变基后的历史。

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

So the documentationnow reads:

所以文档现在是这样的:

After working on the topicbranch created with git checkout -b topic origin/master, the history of remote-tracking branch origin/mastermay have been rewound and rebuilt, leading to a history of this shape:

在使用topic创建的分支上工作后git checkout -b topic origin/master,远程跟踪分支的历史 origin/master可能已经被倒带和重建,导致这种形状的历史:

                 o---B2
                /
---o---o---B1--o---o---o---B (origin/master)
        \
         B0
          \
           D0---D1---D (topic)

where origin/masterused to point at commits B0, B1, B2 and now it points at B, and your topicbranch was started on top of it back when origin/masterwas at B0, and you built three commits, D0, D1, and D, on top of it.
Imagine that you now want to rebase the work you did on the topicon top of the updated origin/master.

In such a case, git merge-base origin/master topicwould return the parent of B0 in the above picture, but B0^..Dis notthe range of commits you would want to replay on top of B (it includes B0, which is not what you wrote; it is a commit the other side discarded when it moved its tip from B0 to B1).

git merge-base --fork-point origin/master topicis designed to help in such a case.
It takes not only B but also B0, B1, and B2 (i.e. old tips of the remote-tracking branches your repository's reflog knows about) into account to see on which commit your topic branch was built and finds B0, allowing you to replay only the commits on your topic, excluding the commits the other side later discarded.

Hence

其中,origin/master在提交B0,B1,B2用于地步,现在它在乙组分,和你的topic分支开始在它的上面回来时,origin/master在B0,和你建立三个提交,D0,D1和d,在它的上面.
想象一下,您现在想要topic在更新的origin/master.

在这种情况下,git merge-base origin/master topic将返回B0的家长在上面的图片,但是B0^..D不是你想重播对B的顶部提交的范围(它包括B0,这是不是你写的东西,它是一个提交其他将其尖端从 B0 移至 B1 时丢弃的一侧)。

git merge-base --fork-point origin/master topic旨在帮助这种情况。
它不仅需要 B 还需要 B0、B1 和 B2(即您的存储库的引用日志知道的远程跟踪分支的旧提示)来查看您的主题分支是在哪个提交上构建并找到 B0,允许您只重播关于您主题的提交,不包括另一方后来丢弃的提交。

因此

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

will find B0, and

会找到 B0,并且

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

will replay D0, D1 and D on top of B to create a new history of this shape:

将在 B 之上重放 D0、D1 和 D,以创建此形状的新历史:

         o---B2
        /
---o---o---B1--o---o---o---B (origin/master)
    \                   \
     B0                  D0'--D1'--D' (topic - updated)
      \
       D0---D1---D (topic - old)

A caveat is that older reflog entries in your repository may be expired by git gc.
If B0 no longer appears in the reflog of the remote-tracking branch origin/master, the --fork-pointmode obviously cannot find it and fails, avoiding to give a random and useless result (such as the parent of B0, like the same command without the --fork-pointoption gives).

Also, the remote-tracking branch you use the --fork-pointmode with must be the one your topic forked from its tip.
If you forked from an older commit than the tip, this mode would not find the fork point (imagine in the above sample history B0 did not exist, origin/masterstarted at B1, moved to B2 and then B, and you forked your topic at origin/master^when origin/masterwas B1; the shape of the history would be the same as above, without B0, and the parent of B1 is what git merge-base origin/master topiccorrectly finds, but the --fork-pointmode will not, because it is not one of the commits that used to be at the tip of origin/master).

需要注意的是,存储库中较旧的 reflog 条目可能会过期git gc
如果 B0 不再出现在远程跟踪分支的 reflog 中origin/master--fork-point模式显然找不到它并失败,避免给出随机和无用的结果(例如 B0 的父级,就像没有--fork-point选项的相同命令给出)。

此外,您使用该--fork-point模式的远程跟踪分支必须是您的主题从其尖端分叉出来的分支。
如果你从旧犯比前端分叉,这种模式不会找到叉点(想象一下在上面的示例历史B0是不存在的, origin/master在B1开始,转移到B2,然后B,你在分叉您的主题origin/master^时,origin/master是B1;历史的形状将与上面相同,没有 B0,并且 B1 的父项是git merge-base origin/master topic正确找到的,但--fork-point模式不会,因为它不是曾经位于origin/master)尖端的提交之一.

回答by araqnid

well, git merge-base customers/acme_patches stableshould show the common ancestor of those two branches.

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

You could try, for instance, gitk --left-right customers/acme_patches...stable(note three dots!). This will show all the commits that are in those branches and not in the merge base. Using --left-rightwill mark each commit with a left or right arrow according to which branch they are in- a left arrow if they are in customers/acme_patches and a right arrow if they are in stable.

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

Possibly also add --date-orderwhich I've found sometimes helps make sense of the output.

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

(You can use this syntax with git log --graphrather than gitkbut imho this is a case where the visual graph display is a big improvement).

(您可以使用这种语法git log --graph而不是gitk但恕我直言,这是可视化图形显示有很大改进的情况)。

回答by x-yuri

Not sure if it covers all cases, but here's the functions that I came up with:

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

git_branch_contains() {
    local b=
    local c=
    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=
    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
}

And here's the script to test them (git-upstream-branch-test.sh):

这是测试它们的脚本 ( 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" 
}

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

Use it like so,

像这样使用它,

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

Where NUMBER is a number from 1 to 11 to specify which case (topology) to test.

其中 NUMBER 是一个从 1 到 11 的数字,用于指定要测试的情况(拓扑)。