I'm trying to rebase a branch on top of master, something I've done a thousand times before. But today, it's not working:
> git status
On branch mystuff
Your branch and 'master' have diverged,
and have 6 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
nothing to commit, working directory clean
> git rebase
First, rewinding head to replay your work on top of it...
> git status
On branch mystuff
Your branch is up-to-date with 'master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
[a directory from the project]
nothing added to commit but untracked files present (use "git add" to track)
>
Everything starts like normal, but then Git finishes the rebase without putting any of my commits there; my branch mystuff ends up on the same commit as master.
The obvious conclusion would be that my commits are already in master somewhere. But they're not, I swear. I've gone back through the history. The commits are on a couple of other feature branches, but they're not in the history of master anywhere. (And I can tell they're not in master anyway by the state of the files when I have master checked out.)
So, if the commits aren't already in my upstream history, why else would git rebase
refuse to stack my commits on top?
Oddly enough, if I cherry-pick the commits onto master one-by-one, that works. And then I can move my mystuff branch to the end, and back master up to where it was. (But why would I need to do it that way?)
EDIT:
The documentation on git rebase
says this:
The current branch is reset to
<upstream>
, or<newbase>
if the--onto
option was supplied. This has the exact same effect asgit reset --hard <upstream>
(or<newbase>
).ORIG_HEAD
is set to point at the tip of the branch before the reset.The commits that were previously saved into the temporary area are then reapplied to the current branch, one by one, in order. Note that any commits in
HEAD
which introduce the same textual changes as a commit inHEAD..<upstream>
are omitted (i.e., a patch already accepted upstream with a different commit message or timestamp will be skipped).
This would be consistent with the behavior I'm seeing if the commits actually existed upstream...but they don't. And as mentioned in the comments, git rebase master
works correctly and applies all the commits. But git rebase
without master
doesn't, even though master is set as the upstream branch.
Configuration of my branches:
[branch "master"]
remote = origin
merge = refs/heads/master
[branch "mystuff"]
remote = .
merge = refs/heads/master
You can run git rebase --abort to completely undo the rebase. Git will return you to your branch's state as it was before git rebase was called. You can run git rebase --skip to completely skip the commit.
git rebase rewrites the commit history. It can be harmful to do it in shared branches. It can cause complex and hard to resolve merge conflicts.
The rebase will continue moving your commits up to HEAD . At any point, you may or may not run into more conflicts. If you don't, you'll see that the rebase worked successfully and then you'll be able to push your changes to your branch.
First of all, you must understand that Git rebase is a destructive operation. Git generates new commits based on your previous commits onto the target branch. Your former commits will, therefore, be destroyed. Basically, you rewrite your Git history!
This has bitten me at least a dozen times after a certain git upgrade. There is now a difference between git rebase
and git rebase master
: the former was changed to use same fancy "fork-point" machinery. There is a detailed explanation in answer to this question:
Today for the first time I figured out concrete steps to reproduce it.
MY SCENARIO: I have 4 commits on master
which I've decided should now move into a topic
branch, plus I want to reorder them. If I do it this way...
Create a new topic
branch, tracking the current branch (master
)
git checkout -b topic -t
Rewind master
back :
git checkout master
git reset --hard origin/master
Reorder the commits on topic
git checkout topic
git status # "Your branch is ahead of 'master' by 4 commits" Good!
git rebase --interactive
... then the interactive rebase screen comes up with this ominous list of commits:
# no-op
Uh-oh... I save the file and continue anyway. Yup, looks like git's thrown away my work again. topic
now points to the same commit as master
and origin/master
.
So I presume what triggered this for you is:
upgrading git
You had done something like my step 2 on your master
branch.
In my lay-man's understanding, the fork-point
machinery searches back through the reflog and notices that those commits had been removed from the upstream branch, and comes to the conclusion that to bring your topic branch "up to date" they should be removed there too.
The solution is, instead of:
git rebase
Use one of:
git rebase --no-fork-point
git rebase master
But I suspect like me you wouldn't do this every time. (I mean, we set an upstream branch for a reason, right?) So you'd just learn to recognise when this disaster strikes, use git reflog
and git reset --hard
to recover, and then use the above command.
Still, you need to be careful now - I think this is extremely dangerous. I have had times when I've rebased a large branch and a few commits silently disappeared from the beginning, and I didn't notice for days! I'm fairly comfortable mining git reflog
to do disaster recovery, but I'm not sure everyone is. I wonder if git has started being too clever here.
Run git branch -vv
to see upstream branches. It sounds like your upstream for mystuff
isn't what you think it is. Maybe you had an editing accident and ended up with multiple [branch "mystuff"]
entries in your config?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With