要进一步提高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)删除那些
答案 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 revert
,git 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
链。
问题是我们不知道控制上游的人做了什么,因为他们从未告诉过我们。 (多么粗鲁!:-))
我们可以尝试许多事情。
我们可以尝试运行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-E
或git apply
)。如果我们幸运的话,它会发现每一个都没有减少到任何东西,并且经过三次手动&#34;跳过&#34;我们将得到这个步骤:
git cherry-pick
如果我们不幸运,我们将遇到合并冲突并且必须通过Merge Hell生存,直到我们意识到,哦,嘿,提交...--A--B--F--CDE--G <-- branch, origin/branch
等于我们的三个提交的摘要和我们应该放弃它们。
2 当且仅当补丁ID(参见the git patch-id
documentation)匹配时,我们的Git才会自行解决这个问题。
我们可以尝试合并。这依赖于合并基础和端点。我们CDE
和branch
的合并基础是提交origin/branch
。我们的Git会B
vs B
和E
vs B
。然后我们的Git将尝试将两组更改结合起来。
生成的合并文件将匹配 G
,在这种情况下,G
中的所有内容都已包含在内,或者不会包含在内,在这种情况下......嗯,有两种可能性。
也许C-D-E
故意解除我们做过的事情 - 例如,G
可能是G
的还原,或部分还原。我们假设我们在文件D
的中间添加了行Woohoo!
。有人将其取回是因为它不合适,所以现在提交README.txt
中的README.txt
与提交G
中的README.txt
匹配。 d&#39;!哦
好吧,当Git比较B
与B
时,赢了看到添加的行。当Git比较G
与B
时,将看到添加的行。所以Git会把它放进去,以为我们想要它。哇噢!但也许我们毕竟不想要它。 d&#39;!哦
总而言之,看起来合并是一种比重新定位更好的策略,因为它处理被压扁的情况(E
中的C-D-E
变为branch
CDE
})和重新定位的案例(origin/branch
中的C-D-E
在branch
中变为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
。我们现在可以比较M
与M
,看看G
中是否有额外的行或其他(woohoo!),并根据此行,决定是否删除{{ 1}}。如果两者完全匹配,只要我们不关心README.txt
序列的精确细节,就足以安全删除branch
。如果没有,我们必须考虑其中的差异。
我们可以压缩,而不是定期合并branch
。这以相同的方式使用合并机制,但随后:
C-D-E
。M
后,将最后一次提交作为常规的非合并提交。也就是说,我们得到与方法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]
git checkout feature
git rebase master
A - B - C - G - H [master]
\
D1 - E1 - F1 [feature]
现在功能与主人是最新的。您可以根据需要多次执行此操作,它可以更新&#34;更新&#34;步。重新绑定而不是合并避免了很多不感兴趣的更新合并提交,这使得阅读历史感到困惑。
当您完成功能后,它已准备好合并......
git checkout master
git merge --no-ff feature
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
,因为F1
和I
具有相同的内容......但您的CI服务器仍然可以继续执行此操作。
git branch -d feature
现在您可以安全地立即删除功能分支。合并提交I
将保留分支的名称以及您可能希望保留的任何额外信息,例如指向问题跟踪器的链接。