git:为什么我不能在壁球合并后删除我的分支?

use*_*119 20 git merge git-merge

我有一个git仓库mainline(相当于master)和一些本地功能分支.例如:

$ git branch
* mainline
  feature1
  feature2
  feature3
Run Code Online (Sandbox Code Playgroud)

当我执行以下操作时,我能够将功能分支中的所有编辑内容合并为一个提交到mainline:

$ git checkout mainline
$ git pull
$ git checkout feature1
$ git rebase mainline
$ git checkout mainline
$ git merge --squash feature1
$ git commit
$ git push
Run Code Online (Sandbox Code Playgroud)

我的问题是,在这一点上,当我尝试删除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'.
Run Code Online (Sandbox Code Playgroud)

是什么导致这个错误?我以为git merge --squash feature1合并feature1mainline.

tor*_*rek 44

这是因为Git不知道squash merge是"等同于"各种特定于分支的提交.您必须强行删除分支,git branch -D而不是git branch -d.

(其余部分仅仅是为什么会出现这种情况.)

绘制提交图

让我们绘制(部分)提交图(这一步适用于Git中的许多内容......).事实上,让我们再退一步,以便我们在你之前开始git rebase,有这样的事情:

...--o--o--o     <-- mainline
      \
       A--B--C   <-- feature1
Run Code Online (Sandbox Code Playgroud)

分支名称(如mainlinefeature1)仅指向一个特定提交.该提交指回(向左)到先前的提交,依此类推,并且这些反向指针构成了实际的分支.

提交的最上一行,都在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]
Run Code Online (Sandbox Code Playgroud)

A'-B'-C'提交与原始提交大致相同,但它们在图中移动.(注意,这三种无聊o提交上两个分支了.)放弃了Git通常不会原有三种方式在拷贝比较原件.(如果原件可以通过其他名称访问 - feature1例如附加到旧的分支 - 例如-Git 可以解决这个问题,至少在大多数情况下.Git如何计算出这一点的确切细节在这里并不是特别重要. )

无论如何,现在你继续跑git checkout mainline; git merge --squash feature1.这使得一个新的提交是三个或多个提交的"压缩副本" feature1.我将停止绘制旧的废弃的,并S为Squash 调用新的壁球:

...--o--o--o--S         <-- mainline
            \
             A'-B'-C'   <-- feature1
Run Code Online (Sandbox Code Playgroud)

"删除安全"完全由提交历史决定

当你要求Git删除时feature1,它会执行安全检查:"已feature1合并到mainline?" 这个"合并到"测试完全基于图形连接.该名称mainline指向提交S; 将S点提交回第一个无聊的o提交,这会导致更多无聊的o提交.提交C',提示feature1,是不可达到的S:我们不允许向右移动,只向左移动.

将此与"正常"合并进行对比M:

...--o--o--o---------M   <-- mainline
            \       /
             A'-B'-C'    <-- feature1
Run Code Online (Sandbox Code Playgroud)

使用相同的测试 - "提示是否feature1可以从提示提交mainline?" - 答案现在是肯定的,因为提交M有一个左下角提交链接C'.(C'在Git内部说法中,Commit 是合并提交的第二个父级M.)

但是,由于壁球合并实际上并不合并,所以从S后面没有连接C'.

此外,Git不会甚至尝试,看看是否S是"同" A',B'C'.但是,如果确实如此,它会说"不一样",因为S它只与所有三次提交的总和相同.获得S匹配压缩提交的唯一方法是只有一个这样的提交(在这种情况下,首先不需要压缩).

  • 那么,除了手动跟踪您合并的分支外,没有更好的解决方案吗? (2认同)
  • @Alec:对于壁球合并,通常正确的做法是*删除*您刚刚合并的分支,从而丢失您刚刚为单个壁球提交而交换的所有提交。换句话说,如果我们已经放弃了 `ABC` 以支持 `A'-B'-C'` 而我们现在使用 `S` 而支持 `A'-B'-C'`,我们也应该丢弃`A'-B'-C'`-并且名称`feature1`可以与它们一起进入垃圾箱,实际上,扔掉功能分支名称是*如何*它们都消失了。如果我们在一个大的一次性流程中完成这一切,我们就没事了。如果我们逐项进行,那么是的,您必须仔细跟踪所有内容。 (2认同)
  • @torek感觉很麻烦,特别是在使用GitHub时。您将PR与南瓜提交合并,但随后您需要记住删除该分支。如果您忘记了,git将无法告诉您可以安全删除哪些分支。 (2认同)
  • @Alec:我同意;但我认为挤压合并应该*永远不会*被使用,除非是谁首先编写代码,因此控制了分支。 (2认同)

Rus*_*Kie 10

实际上,您可以确定和删除压缩合并的分支,尽管这很重要。

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

为了确定一个分支是否被压缩合并, git-delete-squashed 使用git commit-tree创建一个临时的悬空压缩提交。然后它使用git cherry检查压缩的提交是否已经应用到 master。如果是,则删除该分支。

所有学分归 Teddy Katz,https://github.com/not-an-aardvark/git-delete-squashed

  • 非常有趣,但我有点害怕使用这样的自动程序;另外,我压缩提交的主要原因只是因为我想摆脱主题分支上的中间提交,那么为什么我不应该在合并分支后简单地删除它呢? (2认同)