我的git存储库有三个分支,devel
,stable
和customers/acme_patches
。很久以前,stable
从devel
分叉,所有错误修正都发生在stable
。我们时不时地将stable
合并回devel
。 customers/acme_patches
是一个包含一些客户特定补丁的分支。该分支未合并为devel
和stable
。
用一些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_patches
或devel
的{{1}}分支是什么?我只是知道它过去曾分离过其中一个,但我不知道哪个。例如。它可能已在上图中提交stable
或1
。
我一直在玩2
和git log --oneline --graph
,但由于gitk
在几百次提交之前被分叉,所以很难跟上被绘制的线条。
是否有一个快速命令(一个小脚本也可以),它可以某种方式跟随customers/acme_patches
中的提交向后找到第一个提交有两个孩子(fork point)然后确定是否提交是在customers/acme_patches
还是stable
?
在最好的情况下,我可以执行类似的操作(原谅提示,我在Windows上):
devel
答案 0 :(得分:23)
使用git 1.9 / 2.0(2014年第一季度),您可以根据Git使用git merge-base --fork-point
来询问最佳共同祖先。
您可以看到新选项:
由于来自commit ad8261d的John Keeping (johnkeeping
),git rebase
可以使用相同的新--fork-point
选项,如果你需要像{{1}那样重新分支一个分支,它可以派上用场转到customers/acme_patches
。
(我不是说这在你的具体情况下会有意义)
注意:Git 2.16(2018年第一季度)确实澄清并增强了“devel
”的文档,因为很清楚它的计算方法,但不是为什么/为什么。
commit 6d1700b见Junio 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)位于devel
和stable
分支上。
你可能做的是:
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之间的数字,用于指定要测试的案例(拓扑)。