问题
I am working on a branch, foo
. I have no unstaged changes, no working changes, perfectly clean state, where HEAD
== foo
== origin/foo
according to my box.
$ 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
I am then asked to look at some changes that a colleague pushed, so I do:
$ 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.
At this point, my graph looks like:
$ 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
Why does it look like my local foo
is ahead of origin/foo
? Neither D
nor E
are my commits, I just pulled both of those from origin
- I'd expect at this point to still have HEAD
== foo
== origin/foo
.
回答1:
What you had before the pull --rebase
was:
x--x--x (foo, HEAD, origin/foo)
\
y--y (actual origin/foo)
When you pull --rebase
, you are asking to replay your local commits on top of origin/foo
:foo
is reset/checked out at origin/foo
(which is fetched in FETCH_HEAD
), and since your commits 'x
' are already part of the new updated FETCH_HEAD
, foo
is simply fast-forwarded:
x--x--x--y--y (foo, HEAD, FETCH_HEAD)
|
(origin/foo)
The fact that origin/foo
has not changed is typical of a git older than 1.8.4 (found for instance in Ubuntu Precise 12.04, which comes with and old git-core
1.7.9.5)
A simple git fetch
should be enough to update origin/foo
, and not just FETCH_HEAD
.
A more recent git (1.8.4+) would have updated both FETCH_HEAD
and origin/foo
, making the additional git fetch
redundant.
Note: with Git 2.12 (Q1 2017), this case (pull --rebase) will be a simple fast-forward merge.
See commit 33b842a (29 Jun 2016) by Junio C Hamano (gitster). (Merged by Junio C Hamano -- gitster -- in commit 2fb11ec, 19 Dec 2016)
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.
回答2:
Since it is not pushed to the repo that is a normal behavior. How come you can have your local commits before origin? They will always be on top.
回答3:
Why does it look like my local foo is ahead of origin/foo?
That foo
is ahead of remote/foo
is expected, because rebase plays back your current branch on top of the upstream branch. You rebased foo
on top of the remote/foo
, so foo
should be ahead of remote/foo
.
HEAD == foo == origin/foo according to my box.
As a result, the order of commits is also as expected. The following is how your branching would look before a rebase or merge where A
is the actual state of the remote and C
is your stale representation of the remote.
D <------C (HEAD, foo, remote/foo) <------B <------A (remote/foo)
You're doing the equivalent of a "fast-forward" rebase. I.e. it doesn't do anything to the commits; there is nothing to play back onto remote/foo
! The result should only change ref locations:
D <------C <------B <------A (HEAD, foo, remote/foo)
Why does it look like my local foo is ahead of origin/foo?
That's the question I haven't been able to answer. After the rebase, I would expect again that HEAD
== foo
== remote/foo
but it doesn't. Why after the rebase of foo
onto remote/foo
does the latter appear to be behind?
来源:https://stackoverflow.com/questions/28502116/git-pull-rebase-leads-to-unexpected-graph