试图理解为什么命令
git merge --squash mybranch
不被称为
git rebase -squash mybranch
因为看起来它的操作更像是一个rebase而不是一个合并。我的理解是它在提交树中搜索,直到找到当前分支和mybranch
的公共基础提交。然后,它将从该基节点到mybranch
的头部的所有提交重新应用(重新绑定)到当前分支的头部。但这是否进入索引/工作区,因此它可以作为单个提交应用。完成后,没有合并节点,因为正常合并显示合并的两个分支。我有正确的理解吗?
答案 0 :(得分:2)
嗯,合并和变基是根本不同的操作。合并,我的意思是创建新的合并提交的常规git merge
- 确实通过提交图搜索最近的常见提交:
...--o--*--o--o--A <-- mainbr
\
B--C--D--E <-- sidebr
此处,最新的常见提交是*
。然后,合并过程将比较(如git diff
)commit *
和commit A
以找出“我们做了什么”,并将diff *
与提交E
进行比较找出“他们做了什么”。然后,它使用两个父项进行新的单个合并提交:
...--o--*--o--o--A---M <-- mainbr
\ /
B--C--D--E <-- sidebr
加入两个历史,并结合“我们做了什么”和“他们做了什么”,以便差异*
vs M
给出“每个变化之一”。
请注意,您在此处无法选择合并基础:Git将其计算出来,就是这样。
另一方面,Rebase可以分别告知哪些提交要复制,哪些复制。确实,默认情况下,它再次找到提交*
1 - 然后使用git cherry-pick
逐个复制原始提交或同等的;最后,它将分支标签移动到指向最后一次复制的提交。
...--o--*--o--o--A <-- mainbr
\ \
\ B'-C'-D'-E' <-- sidebr
\
B--C--D--E
原始提交链(在这种情况下为B-C-D-E
)仍然在存储库中,并且仍然可以找到:它们可以通过哈希ID找到,也可以在sidebr
的reflog中找到,如果任何其他分支或标记名称都可以访问它们,它们仍然可以通过该名称访问。
git merge --squash
做的是稍微修改合并过程:而不是像往常一样进行合并提交M
,Git像往常一样浏览合并机器,使合并基础不同 - 您无法选择 - 反对当前提交和另一个提交,并组合索引和工作树中的更改。然后 - 没有明显的原因 2 -stops并让你运行git commit
来提交结果,当你这样做时,它是一个普通的非合并提交,所以整个图-fragment看起来像这样:
...--o--*--o--o--A--F <-- mainbr
\
B--C--D--E <-- sidebr
现在,提交F
的内容 - 合并产生的快照树与提交M
的内容相同当我们进行真正的合并时, - 这是真正的踢球者 - 当我们进行重组时,它的也与commit E'
的内容相同。
此外,假设在分支B
上只有一个提交(sidebr
)。现在,merge
,merge --squash
和rebase
中的所有三个都会为您提供一张图片,其中的结尾可能是我们可能会画出的内容:
...--o--*--o--o--A--B' <-- ???
\ ?
B????????? <-- ???
和新提交B'
的内容,即最终快照,在所有三种情况下都是相同的。但是,对于git merge
,新提交将位于分支mainbr
上,并将指回提交A
和B
,其中sidebr
指向{{1} }};对于B
,新提交将在git merge --squash
上,并且仅指向mainbr
;对于A
,新提交将在git rebase
上,没有明显指向sidebr
,我们应该将其绘制为:
B
因为...--o--*--o--o--A <-- mainbr
\ \
B B' <-- sidebr
将继续指向提交mainbr
。
最后,这看起来更像是一个合并而不是像一个rebase。 (但是,如果它根本不被称为“壁球合并”,我会更高兴。)
1 Git找到A
的方法有些不同:它实际上不是单个提交,而是最后一个(通常)非常大的提交,即,从*
参数到<upstream>
可以到达的所有参数。 (令人困惑的是,合并基础也可以是一组提交,但它是一个更受限制的集合。最好不要深入研究图论。:-))
2 如果我们想它停止,我们可以像使用常规非挤压git rebase
一样使用--no-commit
。那么 为什么会自动停止?