我有一个带master
(相当于$ git branch
* mainline
feature1
feature2
feature3
)和一些本地功能分支的git仓库。例如:
mainline
当我执行以下操作时,我能够将功能分支中的所有编辑内容合并到$ git checkout mainline
$ git pull
$ git checkout feature1
$ git rebase mainline
$ git checkout mainline
$ git merge --squash feature1
$ git commit
$ git push
的一个提交中:
feature1
我的问题是,此时,当我尝试删除$ git branch -d feature1
error: The branch 'feature1' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature1'.
分支时,它告诉我它没有完全合并:
git merge --squash feature1
导致此错误的原因是什么?我认为feature1
将mainline
合并到{{1}}。
答案 0 :(得分:34)
这是因为Git不知道squash merge是“等同于”各种特定于分支的提交。您必须使用git branch -D
而不是git branch -d
强制删除分支。
(其余部分只是为什么就是这种情况。)
让我们绘制(部分)提交图(这一步适用于Git中的许多内容......)。事实上,让我们再退一步,以便我们在你的git rebase
之前开始,有这样的事情:
...--o--o--o <-- mainline
\
A--B--C <-- feature1
分支名称,如mainline
或feature1
,仅指向一个特定提交。该提交指向(向左)到先前的提交,依此类推,这是形成实际分支的这些向后指针。
提交的最上一行,在这里所谓的o
都有点无聊,所以我们没有给他们字母。提交的最后一行A-B-C
仅在分支feature1
上 。 C
是最新的提交;它返回B
,返回A
,返回到无聊的o
提交之一。 (顺便说一下:最左边的o
提交以及...
部分中的所有早期提交都位于两个分支上。)
当您运行git rebase
时,三个A-B-C
提交被复制到新提交附加到mainline
的提示,给我们:
...--o--o--o <-- mainline
\ \
\ A'-B'-C' <-- feature1
\
A--B--C [old feature1, now abandoned]
新的A'-B'-C'
提交与原始提交大致相同,但它们在图表中移动。 (请注意,现在所有三个无聊的o
提交都在两个分支上。)放弃原来的三个意味着Git通常不会拥有来将副本与原始文件进行比较。 (如果原件可以通过其他名称访问 - 例如附加到旧feature1
的分支,例如-Git 可以计算出来,至少在大多数情况下是这样。具体细节Git如何解释这一点在这里并不是特别重要。)
无论如何,现在你继续运行git checkout mainline; git merge --squash feature1
。这使得一个新的提交是feature1
上的三个或多个提交的“压缩副本”。我将停止绘制旧的废弃的,并为壁球调用新的壁球提交S
:
...--o--o--o--S <-- mainline
\
A'-B'-C' <-- feature1
当您要求Git删除feature1
时,会执行安全检查:“feature1
是否合并到mainline
?”这个“合并到”测试完全基于图形连接。名称mainline
指向提交S
;提交S
指向第一个无聊的o
提交,这会导致更多无聊的o
提交。提案C'
,feature1
的提示 <{1}} <我>不允许向右移动,只能向左移动。
将此与“正常”合并进行对比S
:
M
使用相同的测试 - “...--o--o--o---------M <-- mainline
\ /
A'-B'-C' <-- feature1
的提示可以从feature1
提示?” - 答案现在是是,因为提交{{ 1}}有一个左下角链接提交mainline
。 (在Git内部说法中,提交M
是合并提交C'
的第二个父。)
由于壁球合并实际上并非合并,因此C'
与M
之间没有任何联系。
同样,Git甚至尝试看看S
是否与“C'
,S
或A'
相同。但是,如果确实如此,它会说“不一样”,因为B'
只与所有三次提交的 sum 相同。让C'
匹配压缩提交的唯一方法是只有一个这样的提交(在这种情况下,首先不需要压缩)。
答案 1 :(得分:7)
实际上,您可以确定并删除壁球合并的分支,尽管它并不简单。
git for-each-ref refs/heads/ "--format=%(refname:short)" | while read branch; do mergeBase=$(git merge-base master $branch) && [[ $(git cherry master $(git commit-tree $(git rev-parse $branch\^{tree}) -p $mergeBase -m _)) == "-"* ]] && git branch -D $branch; done
要确定分支是否被合并,git-delete-squashed将创建一个git commit-tree的临时悬挂的提交。然后,它使用git cherry来检查压缩的提交是否已应用于master。如果是这样,它将删除分支。
所有学分归Teddy Katz,https://github.com/not-an-aardvark/git-delete-squashed