删除合并的分支给出“错误:分支 X 未完全合并......”

Posted

技术标签:

【中文标题】删除合并的分支给出“错误:分支 X 未完全合并......”【英文标题】:Deleting merged branch gives "error: The branch X is not fully merged...' 【发布时间】:2017-03-23 18:01:31 【问题描述】:

我被泡菜夹住了。我无法从其他 SO 问题或阅读 git 文档中找到我的答案。非常感谢您的帮助。

在 Github 上合并我的分支后,我从 UI(远程)中删除了我的分支。 我会定期修剪本地分支:

git checkout master
git pull origin master
git fetch -p
git branch -d X
Error: error: The branch X is not fully merged. If you are sure you want to delete it, run 'git branch -D X'

分支 X 已合并。当我在 master 上执行 git log 时,我可以看到来自 X 的提交。为什么会这样?我想使用-d 而不是-D,因为这可能会导致灾难性地删除真正未合并的分支。

【问题讨论】:

可以使用-f强制本地删除 【参考方案1】:

这里有几个不同的棘手部分。

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)。

    TU的祖先吗?

如果问题 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”,合并(动词)但产生非合并(由于某些莫名其妙的原因,您必须手动提交)。

只有在您提出要求时才会发生“挤压合并”,而只有当它可以并且您不禁止时才会发生“快进非合并” > 它。

【讨论】:

这些都是有用的信息,但我希望它提供有关如何解决(或至少改善)问题中描述的问题的指南。 @AlexJohnson:问题是没有解决办法 很公平。令我惊讶的是,对于合并到上游后本地和远程删除功能分支的极其常见的情况,没有更好的工作流程。 @AlexJohnson:如果你使用真正的合并(包括真正合并模式下的 GitHub 的“合并”按钮),git branch -d 本地将起作用(在git fetch-ing 并更新您的其他本地分支之后) .如果你使用 GitHub 的“rebase and merge”或“squash and merge”,你会被强制删除。不是很漂亮。 我在那个提交被合并之前被压扁了,所以这是有道理的。我想我必须选择合并(壁球)或删除(非强制删除)时是否漂亮。谢谢你的解释。【参考方案2】:

当提交的内容与您尝试删除的分支足够不同时,就会发生这种情况。这可能是因为 squash 提交或附加到先前基础的分支,然后当它们被远程上的其他人合并时。

如果您确信更改实际上已合并(如您所说),那么branch -D 是安全的。

最后,

灾难性地删除分支

不真实。删除分支不是可撤销的问题。所有操作都存储在git reflog 中,分支指向的提交至少要保留 30 天。如果你真的很偏执,你可以重命名分支。但实际上删除分支更容易。

换句话说,分支只是一个指针。删除指针不会删除内容。因此,如果需要,您可以轻松地复活分支。

【讨论】:

小心删除,因为删除分支名称也会删除该分支的引用日志。 HEAD reflog 可能仍然有分支的 reflog 将保护的提交的条目,但那是 可能(如可能),不一定 确实。但总的来说,对象本身确实会持续一段时间:即使它们没有受到保护,也会发生这种情况。您必须解除对它们的保护触发(或手动运行)git gcgit prune 才能让它们消失。

以上是关于删除合并的分支给出“错误:分支 X 未完全合并......”的主要内容,如果未能解决你的问题,请参考以下文章

合并后是不是应该删除分支?

Atom 编辑器仍然允许使用合并和删除的分支

eclipse git 创建新分支 合并分支 删除分支

如何删除所有已合并的 Git 分支?

从主分支中删除单个合并分支

git分支的创建与合并