git pull --rebase 导致意外的图表

git pull --rebase leads to unexpected graph

我在分支机构工作,foo。我没有未暂存的更改,没有工作更改,完全干净的状态,其中 HEAD == foo == origin/foo 根据我的框。

$ git status
# On branch foo
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   some_irrelevant_file_here

$ git log --pretty=...
* 456520c 2015-02-13 (HEAD, origin/foo, foo) Commit A
* 23bfcd1 2015-02-11 Commit B
* b0bdd18 2015-02-12 Commit C

然后我被要求查看同事推送的一些更改,所以我做了:

$ git pull --rebase origin foo
remote: Counting objects: 47, done.
remote: Compressing objects: 100% (34/34), done.
remote: Total 36 (delta 22), reused 0 (delta 0)
Unpacking objects: 100% (36/36), done.
From ...
 * branch            foo       -> FETCH_HEAD
First, rewinding head to replay your work on top of it...
Fast-forwarded foo to 43dad88c737762e0f1e84fdcd135155080bdce2a.

此时,我的图表如下所示:

$ git log --pretty=...
* 43dad88 2015-02-13 (HEAD, foo) Commit D
* 40039f9 2015-02-13 Commit E
* 456520c 2015-02-13 (origin/foo) Commit A
* 23bfcd1 2015-02-11 Commit B
* b0bdd18 2015-02-12 Commit C

为什么我的本地 foo 看起来比 origin/foo 领先? DE 都不是我的提交,我只是从 origin 中提取了这两个 - 我希望此时仍然有 HEAD == foo == origin/foo.

因为它没有被推送到 repo,这是一种正常行为。为什么你可以在 origin 之前进行本地提交?他们永远在最前面。

Why does it look like my local foo is ahead of origin/foo?

foo 领先于 remote/foo 是意料之中的,因为 rebase plays back your current branch on top of the upstream branch。您将 foo 重新定位在 remote/foo 之上,因此 foo 应该领先于 remote/foo

HEAD == foo == origin/foo according to my box.

因此,提交的顺序也符合预期。以下是 变基或合并之前分支的外观,其中 A 是远程的实际状态,C 是远程的陈旧表示。

D <------C (HEAD, foo, remote/foo) <------B <------A (remote/foo)

您所做的相当于 "fast-forward" 变基。 IE。它对提交没有任何作用; remote/foo 上没有什么可播放的!结果应该只改变参考位置:

D <------C <------B <------A (HEAD, foo, remote/foo)

Why does it look like my local foo is ahead of origin/foo?

这是我无法回答的问题。在变基之后,我会再次期望 HEAD == foo == remote/foo 但事实并非如此。为什么在 foo 变基到 remote/foo 之后后者似乎落后了?

您在 pull --rebase 之前拥有的是:

x--x--x (foo, HEAD, origin/foo)
       \
        y--y (actual origin/foo)

当您 pull --rebase 时,您要求在 origin/foo 之上重播您的本地提交:
fooorigin/foo 处 reset/checked(在 FETCH_HEAD 中获取),并且由于您的提交 'x' 已经是新更新 FETCH_HEAD, foo 只是快进:

x--x--x--y--y (foo, HEAD, FETCH_HEAD)
      |
 (origin/foo)

origin/foo 更改的事实是 git older than 1.8.4 (found for instance in Ubuntu Precise 12.04 的典型特征,它与旧的 git-core 1.7.9.5 一起提供)

一个简单的 git fetch 应该足以更新 origin/foo,而不仅仅是 FETCH_HEAD.

较新的 git (1.8.4+) 会同时更新 FETCH_HEADorigin/foo,从而使额外的 git fetch 变得多余。


注意:对于 Git 2.12(2017 年第一季度),这种情况 (pull --rebase) 将是一个简单的快进合并。

参见 commit 33b842a (29 Jun 2016) by Junio C Hamano (gitster)(由 Junio C Hamano -- gitster -- in commit 2fb11ec 合并,2016 年 12 月 19 日)

pull: fast-forward "pull --rebase=true"

"git pull --rebase" always runs "git rebase" after fetching the commit to serve as the new base, even when the new base is a descendant of the current HEAD, i.e. we haven't done any work.

In such a case, we can instead fast-forward to the new base without invoking the rebase process.