我了解到git rebase
和git merge
之间的区别在于git rebase
将分支的开头移到另一个分支的尖端,其中git merge
会创建一个新的表示(即包括或集成)来自另一个分支的分支提交的提交。例如:
由于git pull
等效于git fetch
+ git merge
,而git pull --rebase
等效于git fetch
+ git rebase
,因此假设您有一个本地主分支和远程主分支:
- o - o - o - H - A - B - C (master)
\
P - Q - R (origin/master)
HEAD在C
如果您git pull
会得到以下结果:
- o - o - o - H - A - B - C - X (master)
\ /
P - Q - R --- (origin/master)
HEAD现在将位于X处,它是远程存储库的提交历史记录的提交R,因为合并提交(X)表示(即包括或集成了)已合并的远程分支的提交历史记录
如果您另一方面做了git pull --rebase
,您将得到以下结果:
- o - o - o - H - P - Q - R - A' - B' - C' (master)
|
(origin/master)
您可以看到git pull
与git pull --rebase
不同,因为git pull --rebase
在HEAD的C'处给HEAD,这是本地主分支的提交C,而{{1} }的使用是HEAD在R处。换句话说,通过git pull
提交历史的内容与来自git pull
的提交历史的内容是相同的,但是提交历史中的提交顺序是
但是当您在本地master分支上未完成任何工作时从远程分支中拉出时会发生什么:
本地主分支和远程主分支:
git pull --rebase
HEAD在H
如果您- o - o - o - H (master)
\
P - Q - R (origin/master)
会得到以下结果:
git pull
HEAD现在将位于X(即回购的提交R)处,因为合并提交代表了已合并的远程分支
如果您另一方面做了- o - o - o - H - X (master)
\ \
P - Q - R --- (origin/master)
,您是否会得到这个结果?:
git pull --rebase
意味着HEAD位于R处,这意味着- o - o - o - H - P - Q - R (master)
|
(origin/master)
与git pull --rebase
相同,而在本地分支上没有任何工作
答案 0 :(得分:2)
当git pull
运行git merge
时,除非您明确要求,否则它既不指定--ff-only
也不--no-ff
。
[给出提交图]
...--o--o--o--H <-- master (HEAD) \ P--Q--R <-- origin/master
[您建议git合并,从而产生git pull,会产生]
...--o--o--o--H---------X <-- master (HEAD) \ / P--Q--R <-- origin/master
情况并非如此-缺省情况下也不是。如果运行git merge --no-ff origin/master
,则执行会得到此结果。如果您运行git merge --ff-only
或允许执行默认操作,则会得到:
...--o--o--o--H
\
P--Q--R <-- master (HEAD), origin/master
Git将此称为快进。当具体是git merge
进行此操作时, 1 Git将其称为快进合并,尽管没有实际的合并发生:Git实际上只是执行{{ 1}}提交git checkout
,同时移动分支名称R
,使其指向提交master
(并将R
附加到分支名称HEAD
)。
master
选项告诉Git:即使可以也不要进行快速转发。结果是新的合并提交(或错误,或者其他可能出错的错误)。 , 当然)。 --no-ff
选项告诉Git:如果可以进行快进,请执行;如果没有,则什么也不要做,只是出错。
在--ff-only
运行git pull
时,您将获得相同的选项集。如果可以(a)允许(b)进行快进,则git merge
的结果与git merge
的结果相同:名称git rebase
得到更新,里面没有任何内容提交图以任何方式改变。如果快进不可能 或通过master
禁止,则--no-ff
的结果与git merge
的结果不同,因为存在新的合并提交git rebase
。
新合并提交X
的内容(保存的快照)与提交X
的快照相同。但是,由于R
和R
是不同的提交,因此它们具有不同的哈希ID,并且随后的Git操作将具有不同的行为。
1 X
和git push
都可以对通过refspecs更新的引用执行此操作。当git fetch
以快进方式移动服务器的分支时,通常没有什么特别的。当git push
不能以这种方式移动分支时,通常会收到服务器的拒绝。 git push
注释是git fetch
,而不是! rejected (non-fast-forward)
。 (好吧,这是三个(!)(forced update)
注释之一。)请参见下面的示例。
我在这里要强调的是,快进是标签运动的属性。如果在动作之前识别出的提交(在这种情况下为提交git fetch
)是之后要识别的提交的始祖,即提交H
,则可以进行快进。
这是一个非快速获取更新的示例。我有一个Git Git存储库的本地克隆。上游的一个分支称为R
,代表建议的更新。该分支定期重绕并使用新提交进行重建:有人提出了一些新功能,在其中写了第一个刺,然后进行测试。发现了错误,对功能进行了重新设计,并且放弃了提交的提交,而转而使用了经过改进的新提交pu
分支。因此,我的pu
将被强制更新:
origin/pu
请注意,我的Git更新了我的$ git fetch
remote: Counting objects: 509, done.
remote: Compressing objects: 100% (214/214), done.
remote: Total 509 (delta 325), reused 400 (delta 295)
Receiving objects: 100% (509/509), 468.38 KiB | 1.52 MiB/s, done.
Resolving deltas: 100% (325/325), done.
From [url]
d8fdbe21b5..95628af9bb next -> origin/next
+ b6268ac8c6...465df7fb28 pu -> origin/pu (forced update)
8051bb0ca1..f0bab95d47 todo -> origin/todo
$
,我的origin/next
和我的origin/pu
。其中有两个更新是快进操作:例如,commit origin/todo
(我的旧d8fdbe21b5
)是origin/next
(我的更新95628af9bb
)的祖先。但是我的旧origin/next
b6268ac8c6
不是origin/pu
的祖先。无论如何,我的Git更新了我的465df7fb28
,失去了提交origin/pu
的权限(通过引用日志除外)。
要宣布这一事实,b6268ac8c6
:
git fetch
; +
。由于对(forced update)
和origin/next
的更改是快进操作,因此我的Git对此没有多说。