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 rebasewill fetch revisions from the SVN parent of the current HEAD and rebases the current (uncommitted to SVN) work against it.git rebasedoes 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.txtfile withbarcontent)test.txtfile withbazcontent) 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 upstreamdoes is to check out theupstreambranch on top of which you want to rebase: HEAD refers toupstream—oursnow.)This does not contradict:
git checkout --ours codefile.jsselects a file frommasterduring agit rebase mastermaster, the upstream branch you are rebasing onto, is referenced as "ours"The confusion is likely coming from the role of the working branch in a classic
git merge.When you are merging:
As the
git rebaseman 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 (as in
git switch, mentioned ingit rebaseman page) because the first thing a rebase does is to check out the upstream branch! (to replay the current commits on top of it)A
git rebase upstreamwill first changeHEADof 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 rebaseis 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)