I have a feature branch, and a master branch.
Master branch has evolved and I mean to have those updates to diverging as little as possible from master branch.
So I git pull
in both branches, git checkout feature/branch
and finally git rebase master
.
Now here I either expect everything to work smoothly or conflicts showing up that I need to resolve before continuing rebase until all master commits are re-applied successfully on feature branch.
Now what really happened in my case is something I do not understand:
$>git rebase master
First, rewinding head to replay your work on top of it...
Applying: myFirstCommitDoneOnTheBranch
Applying: myOtherCommitDoneOnTheBranch
$>git status
On branch feature/branch
Your branch and 'origin/feature/feature' have diverged,
and have 27 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean
$>git pull
*load of conflicts*
Now, as much as I can understand he load of conflicts after the pull; I do not understand the need for a pull. Logically, it should rollback to master when it got branched, save the commits made on the branch, forward to latest commit on master and then apply the saved commits.
I do not understand to what the Applying
message refers to: what is applying the commits on which version?
Best Answer
tl;dr You should update both
master
andfeature
withgit pull
andgit pull --rebase
before rebasingfeature
on top ofmaster
. There is no need to do agit pull
after you have rebased yourfeature
branch on top ofmaster
.With your current workflow, the reason why
git status
is telling you this:is because your rebased
feature
branch now has 25 new commits that aren't reachable fromorigin/feature
(since they came from the rebase onmaster
) plus 2 commits that are reachable fromorigin/feature
but have different commit IDs. Those commits contain the same changes (i.e. they're patch equivalent) but they have different SHA-1 hashes because they are based off of a different commit inorigin/feature
than the one you rebased them on in your local repository.Here's an example. Let's assume that this is your history before doing
git pull
onmaster
:After
git pull
,master
got commitF
:At that point, you rebase
feature
on top ofmaster
, which appliesD
andE
:In the meantime, the remote branch
origin/feature
is still based off of commitC
:If you do a
git status
onfeature
, Git will tell you that yourfeature
branch has diverged fromorigin/feature
with 3 (F
,D'
,E'
) and 2 (D
,E
) commits, respectively.The solution is to do
git pull
on bothmaster
andfeature
before rebasingfeature
onmaster
. However, since you may have commits onfeature
that you haven't yet pushed toorigin
, you would want to do:to avoid creating a merge commit between
origin/feature
and your localfeature
.Update on the consequences of rebasing:
In light of this comment, I expanded on the diverging branches. The reason why
git status
reports thatfeature
andorigin/feature
diverge after the rebase is due to the fact that rebasing brings in new commits tofeature
, plus it rewrites the commits that were previously pushed toorigin/feature
.Consider the situation after the pull but before the rebase:
At this point,
feature
andorigin/feature
point to the same commitE
—in other words, they're in "sync". After rebasingfeature
on top ofmaster
, history will look like this:As you can see,
feature
andorigin/feature
have diverged, their common ancestor being commitC
. This is becausefeature
now contains the new commitF
frommaster
plusD'
andE'
(read as "D prime" and "E prime") which are commitsD
andE
applied on top ofF
. Even though they contain the same changes, Git considers them to be different because they have different commit IDs. Meanwhile,origin/feature
still referencesD
andE
.At this point, you've rewritten history: you've modified existing commits by virtue of rebasing them, effectively creating "new" ones.
Now, if you were to run
git pull
onfeature
this is what would happen:Since
git pull
doesgit fetch
+git merge
, this would result in the creation of the merge commitM
, whose parents areE'
andE
.If, instead, you ran
git pull --rebase
(that is,git fetch
+git rebase
) then Git would:feature
to commitC
(the common ancestor offeature
andorigin/feature
)D
andE
fromorigin/feature
F
,D'
andE'
However, noticing that
D'
andE'
contain the same changes asD
andE
, Git would just discard them, resulting in a history looking like this:Notice how commit
F
, previously reachable fromfeature
, got applied on top oforigin/feature
resulting inF'
. At this point,git status
would tell you this:That commit being, of course,
F'
.