这里有几个棘手的问题。
git branch -d
(不强制删除)的关键问题不是“分支是否已合并?”,因为这个问题根本无法回答。而是“该分支是否已经合并到了 <填写内容> 中?”
这个测试在 Git 1.7 版本中发生了改变(这意味着每个人今天都应该拥有它,但请检查您的 Git 版本),由 Junio C Hamano 在 提交记录 99c419c91554e9f60940228006b7d39d42704da7 中进行了更改:
"branch -d: 基于合并的分支来保证“已经合并”的安全性。当一个分支被标记为与另一个引用进行合并(例如,本地的'next'与远程'next'合并,并将其推回到远程,'branch.next.merge'设置为'refs/heads/next'),则基于当前分支来确保“branch -d”安全性没有太多意义。它的目的是不要丢失未合并到其他分支的提交,因此检查它是否与它合并的另一个分支合并更加明智。因此,有两个或三个问题(如果查看上面的提交,则有三个)需要您(或Git)回答,以确定是否允许使用-d:
- 我们打算删除的分支的上游名称是什么?(如果没有,请参见下文。)
- 分支名称指向一个特定的提交(我们称之为T)。上游分支名称也指向一个特定的提交(将此提交称为U)。
T是U的祖先吗?
"
如果问题2的答案是肯定的(即
T ≤ U),则允许删除。如果没有上游呢?这就是“在1.7中更改”的意义所在:最初的测试不是
T ≤ U,而是
T ≤ HEAD。该测试仍然存在。现在,如果没有上游,则使用HEAD测试而不是上游测试。同时,在“过渡期”内,当每个人都适应新的Git 1.7行为时,您还可以获得警告或额外的解释。即使在Git 2.10中(现在已经过去了近7年:这是一个非常长的过渡期!),此警告仍然存在。
if ((head_rev != reference_rev) &&
in_merge_bases(rev, head_rev) != merged) {
if (merged)
warning("deleting branch ... not yet merged to HEAD.");
else
warning("not deleting branch ... even though it is merged to HEAD.");
}
(I trimmed some of the code for display purposes here): 基本上,如果
HEAD
解析到的提交与我们在
T ≤测试中使用的提交不同,并且我们对
T ≤ HEAD和
T ≤ U得到了不同的结果,那么我们会添加额外的警告信息。(请注意,测试的第一部分是多余的:如果由于1.7之前的兼容性和缺少上游而将其与HEAD进行比较,则如果我们再次将其与HEAD进行比较,则会得到相同的结果。我们真正需要的只是
in_merge_bases
测试。)
如何知道上游是什么?实际上,有一种简单的命令行方式可以找出:
$ git rev-parse --abbrev-ref master@{u}
origin/master
$ git rev-parse --symbolic-full-name master@{u}
refs/remotes/origin/master
--abbrev-ref
是Git的典型缩写版本,--symbolic-full-name
提供完整引用名称。如果在没有上游设置的分支上运行,两者都会失败。当然,您也可以使用git branch -vv
来查看所有分支的简写上游。
如何测试整个T ≤ U?对于shell脚本,git merge-base --is-ancestor
命令可以实现此功能:
$ git merge-base --is-ancestor master origin/master && echo yes || echo no
如果master
是origin/master
的祖先,它会告诉你(对于此目的,“指向相同提交”视为“是祖先”,即这是那个≤测试)。
无论何时这个过渡期最终结束,“使用HEAD”测试可能会完全消失,或者可能继续用于没有设置上游的分支。无论如何,问题始终是“要删除的分支是否已合并到____中?(填空)”,您必须查看填充的内容。
与您建议删除的分支上的提交对应的上游提交必须(或至少在其历史记录中)通过提交哈希ID相同的提交来进行“合并到”测试才能成功。如果feature/X
通过所谓的“压缩合并”(虽然是通过合并完成的2)合并到origin/develop
,则该提交ID将不匹配,并且“合并到”测试将始终失败。
1顺便提一下,从Git 2.3开始,您现在可以在git branch -d
后添加--force
,而不是必须使用git branch -D
,尽管当然,-D
仍然有效。
2这里的区别在于“合并”——作为名词的合并,意思是“一个提交是一个合并提交”(使用合并作为形容词),以及“合并”——作为动词的合并,表示组合某些更改集的操作。 git merge
命令可以:
- 进行快进,既不合并也不产生合并,因此根本没有合并;或者
- 进行实际合并,将一些更改合并(动词)以生成一个合并(名词);或者
- 进行“压缩合并”,即进行合并(动词)但生成非合并(由于某种无法解释的原因,您必须手动提交)。
“挤压合并”仅在您请求时发生,而“快进非合并”仅在它可以且您不禁止时发生。
-f
强制本地删除。 - panza