TL; DR:如果我将远程更改提取到本地git仓库,然后进行合并,一段时间后我会获取一些新的更改,但这次我做了rebase而不是merge,然后先前创建的合并提交消失。为什么呢?
示例
考虑以下起点,由命令git log --all --graph --decorate --oneline
创建:
* 28992d3 (repo1/master) hello4
* 3610bdf hello3
| * f113d63 (HEAD -> master) bye-bye
| * cabc896 bye
|/
* 75f7ca9 hello2
* 525cb4a hello1
即,有一个git repo,其中master
分支包含一些本地的未刷新更改。刚刚从遥控器获取了一些其他更改(在本例中为repo1
)。
下一个命令:git merge repo1/master
。结果:
* b94aa29 (HEAD -> master) Merge remote-tracking branch 'repo1/master'
|\
| * 28992d3 (repo1/master) hello4
| * 3610bdf hello3
* | f113d63 bye-bye
* | cabc896 bye
|/
* 75f7ca9 hello2
* 525cb4a hello1
现在假设本地以及远程repo1
都有一些新的提交,然后再次通过repo1
从git fetch repo1 master
获取远程内容。结果如下:
* 2e3d749 (repo1/master) hello6
* b17983d hello5
| * 2e49819 (HEAD -> master) see ya
| * c2f2d5a good-bye
| * b94aa29 Merge remote-tracking branch 'repo1/master'
| |\
| |/
|/|
* | 28992d3 hello4
* | 3610bdf hello3
| * f113d63 bye-bye
| * cabc896 bye
|/
* 75f7ca9 hello2
* 525cb4a hello1
到目前为止一切顺利。
现在让我们做git rebase repo1/master
,结果是一个很好的线性提交日志:
* 101e524 (HEAD -> master) see ya
* 3ce7543 good-bye
* 849cbd4 bye-bye
* 483bab8 bye
* 2e3d749 (repo1/master) hello6
* b17983d hello5
* 28992d3 hello4
* 3610bdf hello3
* 75f7ca9 hello2
* 525cb4a hello1
问题:提交b94aa29 Merge remote-tracking branch 'repo1/master'
去了哪里?(据我所知,它甚至没有被保留为“死”提交,例如在独立头部进行提交。)
说明:
b94aa29
,”因为我们将无论如何都会拥有所有内容“,但是,请你详细解释一下它的内容上?而且,这总是如此,在先前合并的分支上的变基将丢弃所有合并提交吗?答案 0 :(得分:1)
git rebase
在功能上意味着:
git cherry-pick
; 复制字面意思无法复制合并,所以它通常不会尝试。
这里的一般想法是进行一系列提交:
A--B--C--D <-- topic (HEAD)
/
...--o--o--*--o--o <-- mainline
并将它们移植到一系列新的改进提交中:
A--B--C--D [abandoned]
/
...--o--o--*--o--o <-- mainline
\
A'-B'-C'-D' <-- topic (HEAD)
&#34;改进&#34;是将新链基于其他分支的顶端,例如mainline
。为了实现这一点,Git确实必须将原始提交 - A-B-C-D
复制到具有不同哈希ID的不同提交,因为每次提交都是永久性的 1 并陷入困境;即使只有一位不同的提交也会为您提供一个新的不同的提交哈希ID,即使唯一的区别是存储在新提交中的父ID。因此,即使快照A'
中的源树与快照A
中的源树匹配 - 并且它可能没有 - A'
的提交ID与提交ID不同A
。
(当然,这也会通过其余的提交进行。)
您提供给git rebase
的参数选择:
通常情况下,你可以使用这两个名称。例如,git rebase mainline
表示将提交后的副本放入mainline
个点,并复制那些可以从topic
(当前分支名称)指向的提交中获取的提交-ie,D
- 排除从mainline
提示可以访问的任何提交。 >
*
,其中两个分支重新加入(在这种情况下永远)。
在某些情况下,您可能需要使用git rebase --onto
来区分这两个概念。使用--onto
,您可以告诉rebase 放置副本的位置,释放剩余的参数以表示不要复制的内容。这不是必需的。
有一堆种类/风格的rebase:git rebase
没有参数使用git format-patch | git am
来复制提交,而不是实际运行git cherry-pick
,而git rebase -i
实际使用git cherry-pick
。 (在早期版本的Git中,git rebase -i
是一个shell脚本,它实际上运行git cherry-pick
。为了使Windows更快,git rebase
被修改,以便内置-i
Git的音序器,这是实现cherry-pick和revert的代码。)
请注意,所有这一次复制(一次一个)最终会构建提交的线性链。 即使输入可能包含合并,也会发生这种情况,如:
A--B--M--C--D <-- master
/ /
...--o--*--o--S------o--T <-- repo1/master
你现在要求Git重新设置(即复制)一些提交 - 在这种情况下,一些提交在master
上 - --onto
目标为T
,并且限制是* T
/ origin/master
可以从master
开始的第一个提交,即提交*
。
此类提交的完整列表为A
,然后是B
,然后是M
,然后是C
,然后是D
。但Git应该如何复制M
?如果尝试,结果可能看起来很像:
A--B--M--C--D [abandoned]
/ /
...--o--*--o--S------o--T <-- repo1/master
\
A'-B'-M'-C'-D <-- master (HEAD)
/
???----------
除M'
之外的要合并,需要两个父项。其他父母应该有什么?如果其他父母是S
,那么可能,但它带来了什么价值?
(合并的目的是合并两个不同开发线的变化。由于A'
基于T
,基于S
,A'
已经包括S
中的任何内容,并且不需要合并它。)
一般来说,Git只是省略合并提交,因此最终只复制A-B-C-D
。请注意,如果您重新定义包含内部合并的内容,则会发生同样的事情:Git只是复制&#34; side&#34;合并,线性化结果:
C--D
/ \
A--B M--G <-- topic (HEAD)
/ \ /
/ E--F
/
...--o--*--o--o <-- mainline
此处git rebase
将复制A-B-C-D-E-F-G
或A-B-E-F-C-D-G
,删除M
并展平拓扑。
-p
有一个git rebase -i
标记,其拼写时间--preserve-merges
较长,但实际上并没有保留合并(也不是樱桃挑选他们,这是不可能的)。相反,它进行新的合并(通过运行git merge
)。这非常棘手,但可用于修改上述A-B-(C-D, E-F)-M-G
拓扑。请注意,如果您在M
中解决了合并冲突,那么当Git进行合并M'
和D'
F'
的新合并git rerere
时,您必须再次解决它们这里可能很有用。)
1 永久,也就是说,直到整个提交被放弃足够长的时间让Git确定没有人想要它;然后它被git gc
清除掉。