Why is the meaning of “ours” and “theirs” reversed with git

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 eg 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:

  • the side reported as ours is the so-far rebased series, starting with <upstream> ,
  • and theirs is the working branch .
    In other words, the sides are swapped .
  • git rebase replays each commit from the working branch on top of the <upstream> branch.

    If you reconcile both definitions:

  • the commits coming from SVN are the ones on top of which local Git commits are replayed. They are part of the "so-far rebased series", and are referenced as "our" (in your case, the test.txt file with bar content)
  • the working branch (containing Git commits unknown to SVN, in your case, the test.txt file with baz content) is "their", and each of those local Git commits are being replayed.
  • In other words, SVN or not:

  • the " <upstream> " branch (on top of which anything is replayed, and which is part of the so far rebased commits") is " ours ".
  • what is being replayed (the working branch) is " theirs ".

  • 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:

  • the "working branch" is the one containing what is "so far merged", and is considered as "our",
  • while the other commit represent what is being -- not replayed but -- merge on top of the working branch, and considered as "their".
  • 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:

  • what we have on the checked out branch is ' ours ',
  • what we had (and is being merged or replayed) is ' theirs '.

  • 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
    
    链接地址: http://www.djcxy.com/p/1110.html

    上一篇: 我遇到了合并冲突。 我如何中止合并?

    下一篇: 为什么“我们”和“他们的”的含义与git相反