删除所有不在主服务器上添加差异的git分支

时间:2016-11-23 18:34:16

标签: git

要进一步提高this question级别,有没有办法删除所有如果在主上重新定义则没有差异的分支。

虽然这个问题的答案适用于标准的合并工作流程,但它并没有选择已经被“压缩”的分支机构。合并'上游。因为这些分支在合并之前已经被压扁了,所以它们没有相同的提交哈希,所以git没有意识到他们已经有效地'已经合并:

$ git branch feature -d
error: The branch 'feature' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature.

但是如果我们首先对分支进行重新定位,它会毫无怨言地删除它们:

$ git checkout feature
Switched to branch 'feature'
Your branch is based on 'origin/feature', but the upstream is gone.
  (use "git branch --unset-upstream" to fixup) 

$ git rebase develop
First, rewinding head to replay your work on top of it...

$ git checkout develop
Switched to branch 'develop'
Your branch is up-to-date with 'origin/develop'.

$ git branch -d feature
Deleted branch feature (was 72214c7).

那么,有没有办法a)扫描分支并检查哪些分支可以安全删除,以及b)删除那些

4 个答案:

答案 0 :(得分:1)

你特别提到使用rebase,这使得壁球存在时问题变得更加困难"合并"。有一种更容易尝试的方法,这可能就足够了,也可能不会。

让我们来看看有时使用壁球"合并" 1 而不是实际合并或变基的工作流程,有时可能会使用变基。

我们从某个存储库中的某些提交链开始,可能是我们将调用origin的某个大服务器上的集中提交:

...--A--B   <-- branch

如果它集中在服务器上,我们将其克隆并最终在我们自己的存储库中调用此origin/branch

...--A--B   <-- origin/branch

现在我们git checkout branch开始工作,我们自己做了一些新的提交:

...--A--B   <-- origin/branch
         \
          C--D   <-- branch

也许我们还不够,或者当我们推送commit-chain C--D并从中提出拉取请求时我们会得到反馈,所以我们再添加一个或两个提交:

...--A--B   <-- origin/branch
         \
          C--D--E   <-- branch

虽然所有这些都在进行,但origin上的存储库可能也会获取新的提交,因此当我们真正准备好并将C-D-E作为拉取请求推送时,我们甚至可以在origin

上有这个
...--A--B--F   <-- branch

但无论如何,现在发生的事情是,无论谁控制origin(可能直接,通过GitHub上的clicky GUI界面,或者可能间接通过他们自己的存储库)最终都需要我们的C-D-E链和将它放在origin上的存储库中,但是通过创建一个新的,单个commit-let调用此CDE来表明它执行C-D-E执行的操作并放置 进入origin的序列,以便他们现在拥有:

...--A--B--CDE--F--G   <-- branch

或:

...--A--B--F--CDE--G   <-- branch

或类似。

我们现在git fetch这会使我们的存储库更新,为我们提供:

...--A--B--F--CDE--G   <-- origin/branch
         \
          C--D--E   <-- branch

或类似。

另一方面,可能是origin的守护者保留我们的个人提交但是将他们自己改为自我,以便上游现在拥有:

...--A--B--F--C'--D'--E'--G   <-- branch

我们结束了:

...--A--B--F--C'--D'--E'--G   <-- origin/branch
          \
           C--D--E   <-- branch

1 我喜欢引用&#34; merge&#34;对于git merge --squash,因为它使用合并机器,但不进行合并提交。使用git revertgit cherry-pick甚至git apply,我们常常使用合并机器,但人们不会将这些称为&#34;合并&#34 ;!似乎有一些事实是顶级命令拼写为git merge --squash,这导致人们称之为&#34;合并&#34;。也许如果顶级命令是git gimme,那么人们就会称之为“#g;&#34 ;? : - )

因为&#34;壁球&#34;尽管如此,我认为将它称为“#34;压缩&#34;”并将其称为“已被压缩的提交”#34;这是一个非常好的动词。 p>

最终目标

此处的目标是删除我们的分支branch 当且仅当有一些提交序列CDE或{{ 1}}或者其他一些,在我们的上游C'-D-E'中,这意味着不再需要我们原来的origin/branch链。

问题是我们不知道控制上游的人做了什么,因为他们从未告诉过我们。 (多么粗鲁!:-))

我们可以尝试许多事情。

方法1:rebase

我们可以尝试运行C-D-E,将git rebase重新定位到我们的branch如果 - 这是一个很大的&#34;如果&#34; -they,无论他们是谁,实际上将我们的origin/branch链复制到C-D-E链,我们的Git可能会 2 发现上游C'-D'-E'有我们的三个提交,因此将它们从我们的rebase中删除。如果确实如此,我们会得到这个:

origin/branch

我们将知道删除我们的标签...--A--B--F--C'--D'--E'--G <-- branch, origin/branch 是安全的。但如果他们被压扁而不是合并,我们的Git就不会放弃branch。相反,它会尝试一次应用一个(C-D-Egit apply)。如果我们幸运的话,它会发现每一个都没有减少到任何东西,并且经过三次手动&#34;跳过&#34;我们将得到这个步骤:

git cherry-pick

如果我们幸运,我们将遇到合并冲突并且必须通过Merge Hell生存,直到我们意识到,哦,嘿,提交...--A--B--F--CDE--G <-- branch, origin/branch 等于我们的三个提交的摘要和我们应该放弃它们。

2 当且仅当补丁ID(参见the git patch-id documentation)匹配时,我们的Git才会自行解决这个问题。

方法2:合并

我们可以尝试合并。这依赖于合并基础和端点。我们CDEbranch的合并基础是提交origin/branch。我们的Git会B vs BE vs B。然后我们的Git将尝试将两组更改结合起来。

生成的合并文件将匹配 G,在这种情况下,G中的所有内容都已包含在内,或者不会包含在内,在这种情况下......嗯,有两种可能性。

也许C-D-E 故意解除我们做过的事情 - 例如,G可能是G的还原,或部分还原。我们假设我们在文件D的中间添加了行Woohoo!。有人将其取回是因为它不合适,所以现在提交README.txt中的README.txt与提交G中的README.txt匹配。 d&#39;!哦

好吧,当Git比较BB时,赢了看到添加的行。当Git比较GB时,看到添加的行。所以Git会把它放进去,以为我们想要它。哇噢!但也许我们毕竟不想要它。 d&#39;!哦

总而言之,看起来合并是一种比重新定位更好的策略,因为它处理被压扁的情况(E中的C-D-E变为branch CDE })重新定位的案例(origin/branch中的C-D-Ebranch中变为C'-D'-E'。但它给我们留下了令人讨厌的合并提交。如果origin/branch是可能包含???CDE的神秘序列,我们就会这样做:

C'-D'-E'

到此:

...--A--B---???---G   <-- origin/branch
          \
           C--D--E   <-- branch

给我们留下合并提交...--A--B---???---G <-- origin/branch \ \ C--D--E--M <-- branch 。我们现在可以比较MM,看看G中是否有额外的行或其他(woohoo!),并根据此行,决定是否删除{{ 1}}。如果两者完全匹配,只要我们不关心README.txt序列的精确细节,就足以安全删除branch。如果没有,我们必须考虑其中的差异。

方法3:挤压

我们可以压缩,而不是定期合并branch。这以相同的方式使用合并机制,但随后:

  1. 强制我们进行最终提交,就好像我们已经运行C-D-E
  2. 在我们运行M后,将最后一次提交作为常规的非合并提交。
  3. 也就是说,我们得到与方法2(合并)完全相同的,但是使用不同的提交图

    git merge --no-commit

    和以前一样,我们只想git commit两个提交 - 两个分支提示 - 并查看是否有额外的内容,例如我们的...--A--B---???---G <-- origin/branch \ C--D--E--S <-- branch 更改(现在我们必须考虑它)或不(现在我们可以安全地删除git diff)。

    结论

    除了合并或挤压方法使一切都在一个步骤中发生,并且当运行上游存储库的人员也被压缩时,没有特别的理由选择其中任何一个。使用最适合你的方法。

    没有人会让整个问题消失,因为,好吧,#34;哇! d&#39;哦&#34!;什么让整个问题消失,如果您的上游人员,README.txt上运行存储库的人,告诉您复制或压缩您的提交时(假设,当然,你相信/可以相信它们。)

答案 1 :(得分:1)

我既不是git也不是bash专家,所以我想这里有不好的做法,但我有一个部分解决方案。这适用于分支具有相同树的情况(即使提交的内容不同):

git for-each-ref --shell --format='if [[ -z $(git diff master..%(refname)) ]]; then echo %(refname:short); fi' refs/heads/ | sh | grep -v 'master' | xargs git br -D

它遍历每个分支,如果diff to master为空,则删除该分支。即使他们没有共享相同的提交,这也有效。

对如何改善此

的建议持开放态度

答案 2 :(得分:1)

该库提供了执行此操作的方法:https://github.com/not-an-aardvark/git-delete-squashed

...使用以下bash脚本:

git checkout -q master && 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

答案 3 :(得分:-1)

如果我了解正在进行的操作,您就会使用git merge --squash。这是一个问题。有更好的选择。

git merge --squash失去了重要的考古历史。现在,代码考古学家不再能够通过精确的日志消息检查一系列小变化,而是将所有日志消息粉碎在一起。您无法知道,如果第185行的更改是针对&#34;修复Unicode错误&#34;或者&#34;改变退款方式&#34;。这将使你的代码在未来更加困难。

使用git merge --squash的主要原因是拥有良好的线性历史记录。这对于那些不太了解Git图形性质的人来说非常好。您可以在保留每个分支的完整历史的同时实现此目的。

假设您将feature合并到master ...

A - B - C - G - H [master]
         \ 
          D - E - F [feature]
  1. git checkout feature
  2. git rebase master
  3. A - B - C - G - H [master]
                     \ 
                      D1 - E1 - F1 [feature]
    

    现在功能与主人是最新的。您可以根据需要多次执行此操作,它可以更新&#34;更新&#34;步。重新绑定而不是合并避免了很多不感兴趣的更新合并提交,这使得阅读历史感到困惑。

    当您完成功能后,它已准备好合并......

    1. 测试功能。
    2. git checkout master
    3. git merge --no-ff feature
    4. git merge --no-ff阻止快进并始终创建合并提交。结果就是这样。

      A - B - C - G - H ------------- I [master]
                       \            /
                        D1 - E1 - F1 [feature]
      

      git log保持线性,会显示I, F1, E1, D1, H, G, C, B, A。这将使非Git人感到高兴。考古学家会很高兴,因为保留了分支中每个变化的完整历史。 QA和其他开发人员会很高兴,因为功能分支在之前已经完成了的最终形式的测试,并且它被合并到主服务器中,避免了对每个人的合并造成的可能错误。

      没有必要重新测试master,因为F1I具有相同的内容......但您的CI服务器仍然可以继续执行此操作。

      1. git branch -d feature
      2. 现在您可以安全地立即删除功能分支。合并提交I将保留分支的名称以及您可能希望保留的任何额外信息,例如指向问题跟踪器的链接。