Git is behaving weirdly in a particular case and I am able to reproduce the issue. I have two branches, say master and feature, pointing to the same commit. When I reset the master to a parent commit and try to rebase feature on top of master, the feature branch also points to the parent commit although I expect rebase to do nothing. I am not sure why it's happening.
Steps to reproduce:
Run the following commands to reproduce the initial state:
deepakgupta @ git init
Initialized empty Git repository in /tmp/hello/.git/
deepakgupta (master)@ touch hello
deepakgupta (master)@ git add hello
deepakgupta (master)@ git commit -m "First commit"
[master (root-commit) 1750f9c] First commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 hello
deepakgupta (master)@ touch world
deepakgupta (master)@ git add world
deepakgupta (master)@ git commit -m "Second commit"
[master 7411ad0] Second commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 world
deepakgupta (master)@ git branch feature
At this point of time, this is how git log
looks:
commit 7411ad0984841b28dca630d54c3b079ebd01c7e9 (HEAD -> master, feature)
Author: Deepak Gupta <[email protected]>
Date: Tue Apr 7 13:58:09 2020 +0530
Second commit
commit 1750f9c5ba95f53742005657e9cee41390165dc7
Author: Deepak Gupta <[email protected]>
Date: Tue Apr 7 13:57:54 2020 +0530
First commit
Now try out:
git reset --hard HEAD^
git checkout feature
git branch --set-upstream-to master
This is now git log looks now:
commit 7411ad0984841b28dca630d54c3b079ebd01c7e9 (HEAD -> feature)
Author: Deepak Gupta <[email protected]>
Date: Tue Apr 7 13:58:09 2020 +0530
Second commit
commit 1750f9c5ba95f53742005657e9cee41390165dc7 (master)
Author: Deepak Gupta <[email protected]>
Date: Tue Apr 7 13:57:54 2020 +0530
First commit
What should happen when I try git rebase
? I feel that the state should remain same.
but it changes to:
commit 1750f9c5ba95f53742005657e9cee41390165dc7 (HEAD -> feature, master)
Author: Deepak Gupta <[email protected]>
Date: Tue Apr 7 13:57:54 2020 +0530
First commit
(END)
Can someone explain the behavior?
This problem—or feature, as the Git folks prefer to refer to it—is due to Git's fork-point mode. The git rebase
documentation describes this as follows:
If
upstream
is not specified, the upstream configured inbranch.name.remote
andbranch.name.merge
options will be used (see git-config[1] for details) and the--fork-point
option is assumed. If you are currently not on any branch or if the current branch does not have a configured upstream, the rebase will abort.
(formatting, especially the bold part, is mine here). The next paragraph offers this cryptic summary:
All changes made by commits in the current branch but that are not in
upstream
are saved to a temporary area. This is the same set of commits that would be shown bygit log <upstream>..HEAD
; or bygit log 'fork_point'..HEAD
, if--fork-point
is active (see the description on--fork-point
below); or bygit log HEAD
, if the--root
option is specified.
(This description omits a few important items: in particular, merges and commits that are patch-ID equivalent are also discarded by default. But neither of these affects your particular case.)
The fork-point description is partway down the page. It mentions that:
fork_point
is the result ofgit merge-base --fork-point <upstream> <branch>
command (see git-merge-base[1]). Iffork_point
ends up being empty, theupstream
will be used as a fallback.
The upstream
is master
and the branch
is the current branch, feature
. In my repository, I now run:
$ git merge-base --fork-point master feature
af07c3c48ff0d499400ac539aa9c665a8dddcd6f
(using the hash IDs in the repository I made while reproducing your example). Let's use git log
on my repository to see the two commits:
$ git log --decorate --oneline
af07c3c (HEAD -> feature) Second commit
dbf1741 (master) First commit
So, given this output from git merge-base --fork-point
, rebase
promises that the commits that it will copy are those that would be shown by git log af07c3c48ff0d499400ac539aa9c665a8dddcd6f..HEAD
:
$ git log af07c3c48ff0d499400ac539aa9c665a8dddcd6f..HEAD
$
No commits are listed, so no commits are copied.
To disable fork-point mode, run:
git rebase --no-fork-point
or:
git rebase master
Even though the upstream of the current branch feature
is master
, an explicit git rebase master
disables fork-point mode by default. To force Git to use fork-point mode when using an explicit branch name—should you wish to do this; in this case, you presumably don't want it—you could also run:
git rebase --fork-point master
With no arguments at all, you get the effect of git rebase --fork-point master
.