I use git-svn and I noticed that when I have to fix a merge conflict after performing a git svn rebase
, the meaning of the --ours
and --theirs
options to e.g. git checkout
is reversed. That is, if there's a conflict and I want to keep the version that came from the SVN server and throw away the changes I made locally, I have to use ours
, when I would expect it to be theirs
.
Why is that?
Example:
mkdir test
cd test
svnadmin create svnrepo
svn co file://$PWD/svnrepo svnwc
cd svnwc
echo foo > test.txt
svn add test.txt
svn ci -m 'svn commit 1'
cd ..
git svn clone file://$PWD/svnrepo gitwc
cd svnwc
echo bar > test.txt
svn ci -m 'svn commit 2'
cd ..
cd gitwc
echo baz > test.txt
git commit -a -m 'git commit 1'
git svn rebase
git checkout --ours test.txt
cat test.txt
# shows "bar" but I expect "baz"
git checkout --theirs test.txt
cat test.txt
# shows "baz" but I expect "bar"
That seems consistent with what a rebase does.
git svn rebase
will fetches revisions from the SVN parent of the current HEAD and rebases the current (uncommitted to SVN) work against it.git rebase
does mention:Note that a rebase merge works by replaying each commit from the working branch on top of the
<upstream>
branch.Because of this, when a merge conflict happens:
<upstream>
,In other words, the sides are swapped.
If you reconcile both definitions:
test.txt
file withbar
content)test.txt
file withbaz
content) is "their", and each of those local Git commits are being replayed.In other words, SVN or not:
<upstream>
" branch (on top of which anything is replayed, and which is part of the so far rebased commits") is "ours".Good mnemonic tip by CommaToast:
(and the first thing a
git rebase upstream
does it to checkout theupstream
branch on top of which you want to rebase: HEAD refers toupstream
--ours
now.)The confusion is likely coming from the role of the working branch in a classic
git merge
.When you are merging:
As the
git rebase
man page mentions, a merge during a rebase means the side are swapped.Another way to say the same thing is to consider that:
On a merge:
, we don't change the current branch 'B', so what we have is still what we were working on (and we merge from another branch)
But on a rebase, we switch side because the first thing a rebase does is to checkout the upstream branch! (to replay the current commits on top of it)
A
git rebase upstream
will first changeHEAD
of B to the upstream branchHEAD
(hence the switch of 'ours' and 'theirs' compared to the previous "current" working branch.), and then the rebase will replay 'their' commits on the new 'our' B branch:
The only extra step with
git svn rebase
is that a svn "fetch" is performed first on the Git remote branch representing SVN commits.You have initially:
, you first update the SVN tracking branch with new commits coming from SVN
, then you switch the current branch to the SVN side (which becomes "ours")
, before replaying the commits you were working on (but which are now "theirs" during that rebase)