Using git 2.11, git rebase documentation says:
The current branch is reset to <upstream>, or <newbase> if the --onto option was supplied. This has the exact same effect as git reset --hard (or ). ORIG_HEAD is set to point at the tip of the branch before the reset.
I understand it as if upstream
and newbase
points to the same "base reference", then this means the two rebase syntaxes below are equivalent:
git rebase ABC
git rebase --onto ABC
Here is the demo I setup. Let's assume the current branch is FeatureABC
it is perfectly in sync with the remote branch.
#---create two identical branches, behind current branch by 5 commits
(FeatureABC) git branch Demo1-Rebase-ABC HEAD~4
(FeatureABC) git branch Demo2-Rebase-onto-ABC HEAD~4
#---Make a new commit in branch Demo1
git checkout Demo1-Rebase-ABC
echo "Demo of: git rebase FeatureABC Demo1-Rebase-ABC" > ./Demo1_BogusFile.txt
git add ./Demo1_BogusFile.txt
git commit -m "Create file Demo1_BogusFile.txt"
git rebase FeatureABC
First, rewinding head to replay your work on top of it
... Applying: Create file Demo1_BogusFile.txt
git log --oneline -3
shows that the branch Demo1-Rebase-ABC
is sync'ed with the HEAD of FeatureABC. And the commit "Create file Demo1_BogusFile.txt" was correctly applied on top of it.
#---Make a new commit in branch Demo2
git checkout Demo2-Rebase-onto-ABC
echo "Demo of: git rebase --onto FeatureABC Demo2-Rebase-onto-ABC" > ./Demo2_Onto_BogusFile.txt
git add ./Demo2_Onto_BogusFile.txt
git commit -m "Create file Demo2_Onto_BogusFile.txt"
git rebase --onto FeatureABC
There is no tracking information for the current branch. Please specify which branch you want to rebase against. See git-rebase(1) for details.
git rebase <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=origin/<branch> Demo2-Rebase-onto-ABC
I misunderstood the warning message. Thinking that git was confused in the defaults when --onto is used. So I just want to "help" by telling git the current branch I want to rebase
git rebase --onto FeatureABC Demo2-Rebase-onto-ABC
First, rewinding head to replay your work on top of it...
git log --oneline -3
shows that the branch Demo2-Rebase-onto-ABC
becomes identical than FeatureABC
. The last commit "Create file Demo2_Onto_BogusFile.txt" has vanished and the file ./Demo2_Onto_BogusFile.txt
is deleted.
Question: what is the reason the git rebase --onto FeatureABC Demo2-Rebase-onto-ABC
didn't apply the new commits made on the Demo2-Rebase-onto-ABC
branch?
They are not the same, and this can get complicated by the
--fork-point
option as well. I think this may be what bit you, although it's not possible to be sure, just from what you have described, as one of the steps you outlined merely produces an error.I start with a reasonable guess, but it is a guess
To see what's really happening it is very helpful to draw (part of) the commit graph, with special attention to labeling since you are using multiple names that all point to a single commit.
Hence we have something like this—but something like is not really good enough; you have the repository, so you should draw the graph; I have to guess:
Now you run:
Since
HEAD~4
names commitA
(HEAD~1
isD
,HEAD~2
isC
, and so on), we need to do something to mark the fact that these two new names point to commitA
. I'm going to shorten the names to justDemo1
andDemo2
though. (I've created a repository with only commitso
throughE
at this point, and actually rungit branch Demo1 HEAD~4; git branch Demo2 HEAD~4
here.)Incidentally,
git log --all --decorate --oneline --graph
("get help from A DOG" as someone put it) shows this test repository this way (there is noorigin/
branch in my case):Next, you check out Demo1, moving
HEAD
:and modify the work-tree, add the modified file to the index, and commit, to make a new commit which I will call
F
, which updates theHEAD
branch and therefore separatesDemo1
andDemo2
. I'll now use my own commands and their output here:Drawing the graph gets a bit harder; I'll use a row up:
Now we get to your first
git rebase
command. I have to usemaster
, of course:This works on the current branch (
HEAD
orDemo1
). It finds commits that are onHEAD
that are not onFeatureABC
(FeatureABC..
in gitrevisions syntax). That's commitF
. These commits get put into a list of commits to maybe copy—git rebase
will check for commits with the samegit patch-id
and skip them, although clearly that did not happen here. So now commitF
is copied to new commitF'
, with different hash ID and different base:(Here's the actual
git log
output, showing the new commit hash for the copy. The original, now-abandonedF
is not shown unless I addDemo1@{1}
to the command, which I did here. The original is the secondF
shown, i.e., the earlier commit:I like the horizontal graph better, but this one has more information, specifically the abbreviated hash IDs.)
Reproducer fails, and I'll have to guess again
Now we try to repeat this with
Demo2
, but it fails. Here are my actual commands, cut-and-pasted. The first step works fine:No longer drawing the original
F
, here is the new graph. I putG
whereF
used to be, although I could draw this as just...--o--A--G
:The rebase, however, does not work. Again I have to use
master
instead ofFeatureABC
, but this would behave the same way in your example, given that thegit branch
command did not set an upstream ("tracking") name:The reason
git rebase
failed with this error message is that--onto
has absorbed the argument as<newtarget>
, leaving us with no<upstream>
:The boldface here is mine, but it's also, I think, the key. I assume you ran a
git rebase --onto <somename>
that did not fail. For it to have not-failed, your branch must have had an upstream set. That upstream probably wasorigin/FeatureABC
or similar, and that meant that as far as Git was concerned, you were running:and not:
Some further reading in the (overly cryptic, in my opinion)
git rebase
documentation will turn up this sentence:In other words:
turns off the
--fork-point
option, as does:but:
or:
leaves the
--fork-point
option on.What
--fork-point
is aboutThe goal of
--fork-point
is to specifically drop commits that used to be, at one time, in your upstream, but are no longer in your upstream. See Git rebase - commit select in fork-point mode for an example. The specific mechanism is complicated and relies on the upstream branch's reflog. Since I don't have either your repository or your reflog, I cannot test out your specific case—but that's one reason, and probably the most likely reason given the hints in your question, that a commit that would affect the rebase tree result would get dropped. The commits that are dropped due to having the same patch ID as an upstream commit are ones that [edit:] often1 will not affect the final tree of the last-copied commit: they would just cause merge conflicts and/or force you to usegit rebase --skip
to skip over them, if they were included.1It occurred to me after writing this that there is an important exception (which probably has nothing to do with the original question, but which I should mention). Rebasing a feature or topic branch onto a more mainline branch, when a commit was first cherry-picked out of the feature into the mainline, and then reverted in the mainline, will cause a problem. Consider, e.g.:
where
C'
is a copy of commitC
, andX
is a revert of commitC
that should not have been put intomainline
yet. Doing:will instruct Git to put commits
A
throughE
into the "candidates to copy" list, but also look atP
throughT
to see if any were already adopted. CommitC
was adopted, asC'
. IfC
andC'
have the same patch ID—usually, they will—Git will dropC
from the list as "already copied". However,C
was explicitly reverted in commitX
.Whoever does the rebase needs to notice, and carefully restore
C
if it is required and appropriate.This particular behavior is not a problem with
git merge
(since merge ignores intermediate commits), only withgit rebase
.