Reputation: 67
Basically I've a feature
branch and a master
branch.
Here is the graph:
What I want to do is update the feature
branch from the master
branch without doing a merge commit on the feature
branch (basically 'move' the first commit of the branch to 'master 9') .
So I've tried the the following commands:
git checkout feature
git rebase master
git push
But after the push, git returns an error.
! [rejected] feature -> feature (non-fast-forward)
error: failed to push some refs to '...'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
And if I execute the suggested git pull
, after a git push
my repository locks like this:
Which it's not what I wanted.
I've already tried using the parameter --rebase
with the command git pull
, but unfortunately the result is this:
I just want to the 'initial commit' from the feature
branch to be the commit master 9
but I've already tried anything without executing git push --force
(which does work).
Can anyone explain to me why I need to use the force parameter to push the changes to the server?
Upvotes: 2
Views: 371
Reputation: 487745
Rebase fundamentally works by copying commits. You have some chain of commits:
...--A--B--F--G <-- master
\
C--D--E <-- feature
(this is a similar drawing to the graph image you posted: mine is just sideways with newer commits towards the right, vs yours with newer commits towards the top). Some of these commits are now old-and-lousy and you'd like to replace them with new-and-improved commits. In particular, commit C
is based off B
and you'd like a copy of C
, which we can call C'
, that points to G
rather than B
. After that, you'd like to make a copy of D
, which we can call D'
, that is based off the copy you just made, C'
. This repeats for E
as well:
C'-D'-E' <-- (new-and-improved-feature)
/
...--A--B--F--G <-- master
\
C--D--E <-- feature
Now, each commit in Git actually has a unique hash ID: a big ugly string of letters and digits that no human wants to deal with. That's OK, we mostly don't have to deal with them: that's why we have a computer, after all. We have Git remember these commit hash IDs. The branch names in particular remember the hash ID of the last commit in the series, at the tip of the branch.
So, having made these copies, we now yank the name feature
off of commit E
and make it point instead to copy E'
:
C'-D'-E' <-- feature
/
...--A--B--F--G <-- master
\
C--D--E [abandoned]
This all happens in your repository. The other repository, over at origin
, doesn't have the new commits at all yet.
You now use git push
to have your Git call up their Git. Your Git sends them your three shiny new replacement commits, C'-D'-E'
, which they place in quarantine for a moment (in case they reject them) but which are now logically in their repository:
C'-D'-E' [ready]
/
...--A--B--F--G <-- master
\
C--D--E <-- feature
Note that their name feature
—which is their name, not your name—still points to commit E
.
Your Git now says: Please, if it's OK, make your name feature
point to commit E'
. Their Git looks at this picture and says: Whoa there buddy, that's not OK! If I do that, I'll forget the hash ID of existing commit E
! I'll never be able to find that commit again!
! [rejected] feature -> feature (non-fast-forward)
This means Whoa there, not OK, I'd lose some commits!
But losing some commits is exactly what you want them to do. It's what your Git already did: you can't find your original commits any more; you've thrown them into the recycling bin, in favor of the shiny new commits you just made.
You now must tell that other Git, over on origin
, to do the same thing. To do that, you have two options:
--force
: this says Set your feature
! There is no checking; you just send them a forceful command, instead of a polite request. They obey, or don't, according to whatever rules they have for their branches. If you're authorized—and you probably are—they just obey.
--force-with-lease
: this has your Git say I think your feature
points to old commit E
, that is now outdated. If so, make your feature
point to new commit E'
instead. Here, they'll answer OK if your Git is correct and no if your Git is wrong.
Obviously, --force-with-lease
is safer, in some sense: if your Git is wrong about them, you'll get an error back.
The interesting questions you should ask here are:
feature
points to?feature
point somewhere I didn't expect?Those questions are answered here on StackOverflow, if you go looking. (So is your original question.)
Upvotes: 4