git pull --rebase 是否与 git pull 当本地主分支上没有完成任何工作时相同?
Is git pull --rebase the same as git pull when no work is done on the local master branch?
我知道 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
在 C' 处给出了 HEAD,这是本地主分支的提交 C,而 git pull
HEAD was at R. 换句话说,通过 git pull
的提交历史的内容与来自 git pull --rebase
的提交历史的内容相同,但是提交中的提交顺序历史不同
但是当你在本地 master 分支上没有完成任何工作时从远程分支拉取时会发生什么:
本地master分支和远程master分支:
- o - o - o - H (master)
\
P - Q - R (origin/master)
HEAD 在 H
如果你 git pull
你最终会得到这个:
- o - o - o - H - X (master)
\ \
P - Q - R --- (origin/master)
HEAD 现在将位于 X,这是 repo 的提交 R,因为合并提交代表已合并的远程分支
另一方面,如果你这样做 git pull --rebase
,你会这样结束吗?:
- o - o - o - H - P - Q - R (master)
|
(origin/master)
意味着 HEAD 在 R 这意味着 git pull --rebase
与 git pull
相同,当没有在本地分支上完成任何工作时
当git pull
运行s git merge
时,它既不指定--ff-only
也不指定--no-ff
,除非你特别要求它。
[given the commit graph]
...--o--o--o--H <-- master (HEAD)
\
P--Q--R <-- origin/master
[you suggest that git merge, and therefore git pull, would produce]
...--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 称其为 fast-forward。当它专门 git merge
执行此操作时,1 Git 将其称为 fast-forward 合并 ,尽管没有实际合并发生:Git 实际上只是在移动分支名称 master
时执行提交 R
的 git checkout
,以便它指向提交 R
(并离开HEAD
附加到分支名称 master
).
--no-ff
选项告诉Git:即使可以也不要做fast-forward。结果是一个新的合并提交(或者错误,或者其他任何可能出错的地方,当然)。 --ff-only
选项告诉Git:如果你能做到fast-forward,就去做;如果不是,什么也不做,只是出错。
当您有 git pull
运行 git merge
时,您将获得相同的选项集。如果 fast-forward (a) 可能且 (b) 允许,则 git merge
的结果与 git rebase
的结果相同:名称 master
得到更新并且提交图中没有任何变化。如果 fast-forward 不可能 ,或者被 --no-ff
禁止,git merge
的结果不同于 git rebase
因为有一个新的合并提交 X
.
新合并提交 X
的 内容 — 保存的快照与您的提交 R
相匹配。但由于 R
和 X
是不同的提交,它们具有不同的哈希 ID,因此后续的 Git 操作将表现不同。
1git push
和 git fetch
都可以对通过 refspecs 更新的引用执行此操作。当 git push
以 fast-forward 方式移动服务器的分支时,您通常看不出有什么特别之处。当 git push
不能 以这种方式移动分支时,您通常会收到服务器的拒绝。 git fetch
注释,而不是 ! rejected (non-fast-forward)
,是 (forced update)
。 (好吧,这是三个 (!) git fetch
注释之一。)请参阅下面的示例。
这里要强调的是,fast-forwarding是一个属性的label motion。如果在动议之前标识的提交(在本例中为提交 H
)是之后要标识的提交的祖先,即提交 R
.[=73=,那么 fast-forward 是可能的]
这是一个非fast-forward 获取更新的示例。我有 Git 的 Git 存储库的本地克隆。上游的一个分支名为 pu
,代表 提议更新。这个分支会定期倒带并用新的提交重建:有人提出了一些新功能,编写了第一个尝试,然后进行测试。发现了错误,重新设计了功能,丢弃了进入的提交,以支持进入 pu
分支的新改进的提交。所以我的 origin/pu
将是 force-updated:
$ 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
$
请注意,我的 Git 更新了我的 origin/next
、我的 origin/pu
和我的 origin/todo
。其中两个更新是 fast-forward 操作:例如,提交 d8fdbe21b5
(我的旧 origin/next
)是 95628af9bb
(我更新的 origin/next
)的祖先。但是 b6268ac8c6
,我的老 origin/pu
, 不是 465df7fb28
的祖先。我的 Git 更新了我的 origin/pu
无论如何 ,无法访问提交 b6268ac8c6
(除了通过 reflogs)。
为了宣布这一事实,git fetch
:
- 在行前加上
+
;
- 在更新中打印了三个点,而不是两个;和
- 在行尾添加
(forced update)
。
因为对 origin/next
和 origin/todo
的更改是 fast-forward 操作,所以我的 Git 没有对它们做任何额外说明。
我知道 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
在 C' 处给出了 HEAD,这是本地主分支的提交 C,而 git pull
HEAD was at R. 换句话说,通过 git pull
的提交历史的内容与来自 git pull --rebase
的提交历史的内容相同,但是提交中的提交顺序历史不同
但是当你在本地 master 分支上没有完成任何工作时从远程分支拉取时会发生什么:
本地master分支和远程master分支:
- o - o - o - H (master)
\
P - Q - R (origin/master)
HEAD 在 H
如果你 git pull
你最终会得到这个:
- o - o - o - H - X (master)
\ \
P - Q - R --- (origin/master)
HEAD 现在将位于 X,这是 repo 的提交 R,因为合并提交代表已合并的远程分支
另一方面,如果你这样做 git pull --rebase
,你会这样结束吗?:
- o - o - o - H - P - Q - R (master)
|
(origin/master)
意味着 HEAD 在 R 这意味着 git pull --rebase
与 git pull
相同,当没有在本地分支上完成任何工作时
当git pull
运行s git merge
时,它既不指定--ff-only
也不指定--no-ff
,除非你特别要求它。
[given the commit graph]
...--o--o--o--H <-- master (HEAD) \ P--Q--R <-- origin/master
[you suggest that git merge, and therefore git pull, would produce]
...--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 称其为 fast-forward。当它专门 git merge
执行此操作时,1 Git 将其称为 fast-forward 合并 ,尽管没有实际合并发生:Git 实际上只是在移动分支名称 master
时执行提交 R
的 git checkout
,以便它指向提交 R
(并离开HEAD
附加到分支名称 master
).
--no-ff
选项告诉Git:即使可以也不要做fast-forward。结果是一个新的合并提交(或者错误,或者其他任何可能出错的地方,当然)。 --ff-only
选项告诉Git:如果你能做到fast-forward,就去做;如果不是,什么也不做,只是出错。
当您有 git pull
运行 git merge
时,您将获得相同的选项集。如果 fast-forward (a) 可能且 (b) 允许,则 git merge
的结果与 git rebase
的结果相同:名称 master
得到更新并且提交图中没有任何变化。如果 fast-forward 不可能 ,或者被 --no-ff
禁止,git merge
的结果不同于 git rebase
因为有一个新的合并提交 X
.
新合并提交 X
的 内容 — 保存的快照与您的提交 R
相匹配。但由于 R
和 X
是不同的提交,它们具有不同的哈希 ID,因此后续的 Git 操作将表现不同。
1git push
和 git fetch
都可以对通过 refspecs 更新的引用执行此操作。当 git push
以 fast-forward 方式移动服务器的分支时,您通常看不出有什么特别之处。当 git push
不能 以这种方式移动分支时,您通常会收到服务器的拒绝。 git fetch
注释,而不是 ! rejected (non-fast-forward)
,是 (forced update)
。 (好吧,这是三个 (!) git fetch
注释之一。)请参阅下面的示例。
这里要强调的是,fast-forwarding是一个属性的label motion。如果在动议之前标识的提交(在本例中为提交 H
)是之后要标识的提交的祖先,即提交 R
.[=73=,那么 fast-forward 是可能的]
这是一个非fast-forward 获取更新的示例。我有 Git 的 Git 存储库的本地克隆。上游的一个分支名为 pu
,代表 提议更新。这个分支会定期倒带并用新的提交重建:有人提出了一些新功能,编写了第一个尝试,然后进行测试。发现了错误,重新设计了功能,丢弃了进入的提交,以支持进入 pu
分支的新改进的提交。所以我的 origin/pu
将是 force-updated:
$ 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
$
请注意,我的 Git 更新了我的 origin/next
、我的 origin/pu
和我的 origin/todo
。其中两个更新是 fast-forward 操作:例如,提交 d8fdbe21b5
(我的旧 origin/next
)是 95628af9bb
(我更新的 origin/next
)的祖先。但是 b6268ac8c6
,我的老 origin/pu
, 不是 465df7fb28
的祖先。我的 Git 更新了我的 origin/pu
无论如何 ,无法访问提交 b6268ac8c6
(除了通过 reflogs)。
为了宣布这一事实,git fetch
:
- 在行前加上
+
; - 在更新中打印了三个点,而不是两个;和
- 在行尾添加
(forced update)
。
因为对 origin/next
和 origin/todo
的更改是 fast-forward 操作,所以我的 Git 没有对它们做任何额外说明。