删除合并分支会出现"错误:分支X未完全合并......"

Str*_*ler 10 git merge github

我被腌渍了.我无法从其他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'
Run Code Online (Sandbox Code Playgroud)

分支X已合并.我可以看到从X提交当我这样做git logmaster.为什么会这样?我想使用-d而不是-D因为这可能导致灾难性地删除真正未合并的分支.

tor*_*rek 9

这里有几个不同的棘手的位.

关键问题git branch -d(删除而不强制1)不是"是否合并分支?",因为这个问题实际上是无法回答的.问题是"是分支合并 <填写这里的东西>?"

在Junio C Hamano提交99c419c91554e9f60940228006b7d39d42704da7中,这个测试在Git 1.7版本中更改了(这意味着每个人都应该拥有它,但请检查您的Git版本):

branch -d:在它合并的分支上建立"已合并"的安全性

当一个分支被标记为与另一个ref合并时(例如,本地'next'合并并推回原点的'next','branch.next.merge'设置为'refs/heads/next'),它几乎没有基于"branch -d"安全性的意义,其目的不是丢失当前分支上未合并到其他分支的提交.检查它是否与它合并的另一个分支合并是更明智的.

因此,有两个或有时三个,如果你看一下你(或Git)必须回答的上述提交问题,看看是否-d允许:

  1. 我们建议删除的分支的上游名称是什么?(如果没有,请参阅下文.)
  2. 分支名称指向一个特定的提交(让我们将此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.");
}
Run Code Online (Sandbox Code Playgroud)

(我削减了部分用于显示目的的代码在这里):基本上,如果HEAD解析为其他一些犯下比我们使用的承诺:T≤测试,并且我们会得到一个不同的结果:T≤HEAD比我们得到了:T≤你,我们添加额外的警告信息.(注意,测试的第一部分是多余的:如果我们比较HEAD因为1.7之前的兼容性和缺少上游,那么如果我们再次与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
Run Code Online (Sandbox Code Playgroud)

--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
Run Code Online (Sandbox Code Playgroud)

会告诉你是否master是它的祖先origin/master(为此目的,"指向相同的提交"计为"是一个祖先",即,这是非常相同的≤测试).

每当此过渡期最终结束时,"使用HEAD"测试可能完全消失,或者可能继续用于没有上游设置的分支.但无论如何,问题始终是"待删除的分支是否合并 ____?(填空)",你必须看看填空的内容.

上游中与您要删除的分支上的提交相对应的提交必须(或者至少在其历史记录中)通过提交哈希ID进行相同的提交,因为"已合并到"测试中成功.如果feature/X被合并到origin/develop通过所谓的"南瓜合并"(这不是合并,但它是由合并完成2),该提交ID不匹配,并且"合并到"测试总是失败.


1顺便说一句,作为Git的2.3,现在就可以添加--forcegit branch -d,而不是必须使用git branch -D的,当然虽然-D仍然有效.

2这里的区别在于"合并" - 作为名词的合并,意味着"作为合并提交的提交"(使用合并作为形容词),以及"合并" - 合并作为动词,意味着动作结合一些变化.该git merge命令可以:

  • 做一个快进,既没有合并动词,也没有合并作为名词,所以根本就没有合并; 要么
  • 做一个真正的合并,它合并(动词)一些变化来产生一个合并(名词); 要么
  • 做一个"squash merge",它合并(动词)但产生一个非合并(由于一些莫名其妙的原因,你必须手动提交).

"壁球合并"只有在您要求时才会发生,而"快速非合并"只有在可以您不禁止的情况下才会发生.

  • @AlexJohnson:如果您使用真正的合并(包括在真正合并模式下的GitHub的“合并”按钮),则本地git branch -d将起作用(在git fetch加载并更新其他本地分支之后)。如果您使用GitHub的“ rebase and merge”或“ squash and merge”,则必须强制执行删除操作。不是很漂亮 (2认同)
  • 在该提交被合并之前我就被压扁了,所以这是有道理的。我想我必须选择合并(挤压)或删除(非强制删除)时是否漂亮。感谢您的解释。 (2认同)

Suk*_*ima 6

当提交的内容与您尝试删除的分支的差异足够大时,就会发生这种情况。这可能是由于壁球提交或分支附加到先前的基础上,然后又被远程上的其他人合并。

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

最后,

灾难性地删除分支

是不真实的。删除分支不是不可撤消的问题。所有操作都存储在git reflog和分支指向的提交中,该提交至少保留30天。如果确实是偏执狂,则可以改名为分支。但是实际上,删除分支会更容易。

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

  • 删除时要小心,因为删除分支名称也会删除该分支的引用日志。`HEAD` reflog可能仍然具有分支的reflog将保护的提交条目,但是*可以*(如可能),不一定*确实*。但是,总的来说,对象本身倾向于保留一段时间是正确的:即使它们没有受到保护,也会发生这种情况。您将不得不取消保护它们并触发(或手动运行)git gc或git prune使它们消失。 (2认同)