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>
,git rebase replays each commit from the working branch on top of the
<upstream>
branch.
If you reconcile both definitions:
test.txt
file with bar
content)test.txt
file with baz
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:
whatever HEAD's pointing to is "ours"
(and the first thing a git rebase upstream
does it to checkout the upstream
branch on top of which you want to rebase: HEAD refers to upstream
-- 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:
x--x--x--x--x(*) <- current branch B ('*'=HEAD) \ \ \--y--y--y <- other branch to 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)
x--x--x--x--x---------o(*) MERGE, still on branch B \ ^ / \ ours / \ / --y--y--y--/ ^ their
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)
x--x--x--x--x(*) <- current branch B \ \ \--y--y--y <- upstream branch
A git rebase upstream
will first change HEAD
of B to the upstream branch HEAD
(hence the switch of 'ours' and 'theirs' compared to the previous "current" working branch.)
x--x--x--x--x <- former "current" branch, new "theirs" \ \ \--y--y--y(*) <- upstream branch with B reset on it, new "ours", to replay x's on it
, and then the rebase will replay 'their' commits on the new 'our' B branch:
x--x..x..x..x <- old "theirs" commits, now "ghosts", available through reflogs \ \ \--y--y--y--x'--x'--x'(*) <- branch B with HEAD updated ("ours") ^ | upstream 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:
x--x--x--x--x(*) <- current branch B, "ours" for now. \ \ \--y--y--y <- SVN tracking branch, "theirs for now"
, you first update the SVN tracking branch with new commits coming from SVN
x--x--x--x--x(*) <- current branch B, still "ours", not for long \ \ \--y--y--y--y'--y' <- SVN tracking branch updated
, then you switch the current branch to the SVN side (which becomes "ours")
x--x--x--x--x <- for "B", now "their" during the rebase \ \ \--y--y--y--y'--y'(*) <- SVN tracking branch updated, and branch B: now "ours" (this is "what we now have")
, before replaying the commits you were working on (but which are now "theirs" during that rebase)
x--x..x..x..x <- old "theirs" commits, now "ghosts", available through reflogs \ \ \--y--y--y--y'--y'--x'--x'--x'(*) <- branch B with HEAD updated ("ours") ^ | upstream SVN tracking branch
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