这里有几个不同的棘手部分。
git branch -d(不强制删除1)的关键问题不是“分支合并了吗?”,因为这个问题实际上是无法回答的。问题是“分支是否合并到 ?”
此测试在 Git 版本 1.7 中进行了更改(这意味着每个人今天都应该拥有它,但请检查您的 Git 版本),commit 99c419c91554e9f60940228006b7d39d42704da7 by Junio C Hamano:
branch -d:将“已经合并”的安全性建立在它合并的分支上
当一个分支被标记为与另一个 ref 合并时(例如本地 'next'
使用 'branch.next.merge' 合并并推回原点的 'next'
设置为'refs/heads/next'),以“branch -d”为基础没有什么意义
安全性,其目的是不丢失未合并到其他的提交
分支,在当前分支上。检查它是否更明智
与它合并的另一个分支合并。
因此,如果您查看上述提交,您(或 Git)必须回答两个(或有时三个)问题,以确定是否允许 -d:
- 我们建议删除的分支的上游名称是什么? (如果没有,请参见下文。)
-
分支名称指向一个特定的提交(我们称其为 T 表示 Tip)。上游分支名称也指向一个特定的提交(将此提交称为 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.");
}
(我在这里修剪了一些代码以供显示):基本上,如果HEAD 解析为我们在 T ≤ 测试中使用的提交之外的其他提交,and 对于 T ≤ HEAD,我们会得到一个 不同 的结果,而不是 T ≤ U,我们添加了额外的警告消息。 (请注意,测试的第一部分是多余的:如果我们因为 1.7 之前的兼容性和缺少上游而与 HEAD 进行比较,那么如果我们再次与 HEAD 比较,我们将得到 same 结果. 我们真正需要的是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 东西? git merge-base --is-ancestor 命令对 shell 脚本执行此操作,因此:
$ git merge-base --is-ancestor master origin/master && echo yes || echo no
会告诉你master 是否是origin/master 的祖先(为此,“指向同一个提交”算作“是一个祖先”,即,这是相同的≤测试)。
当这个过渡期最终结束时,“使用 HEAD”测试可能会完全消失,或者可能会继续用于没有上游设置的分支。但是,无论如何,问题始终是“要删除的分支是否合并到 ____?(填空)”,您必须查看填空的内容。
与您提议删除的分支上的提交相对应的上游提交必须是(或至少在其历史中具有)相同的提交,通过提交哈希ID,以便“合并到”测试成功。如果 feature/X 通过所谓的“squash merge”(不是合并,虽然它是通过 merging2 完成的)合并到 origin/develop,那么提交 ID 将不匹配,并且“被合并到”测试总是会失败。
1顺便说一句,从 Git 2.3 开始,您现在可以将 --force 添加到 git branch -d,而不是被要求使用 git branch -D,当然 -D 仍然有效。
2这里的区别在于“a merge”——作为名词的merge,意思是“作为合并提交的提交”(使用merge作为形容词)和“to merge” "——merge 作为动词,意思是组合一些变化的动作。 git merge 命令可以:
- 进行快进,既不合并为动词,也不合并为名词,因此根本没有合并;或
- 进行真正的合并,其中合并(动词)一些变化以产生合并(名词);或
- 执行“squash merge”,合并(动词)但产生非合并(由于某些莫名其妙的原因,您必须手动提交)。
只有在您提出要求时才会发生“挤压合并”,而只有当它可以并且您不禁止时才会发生“快进非合并” > 它。