Почему значения «наши» и «их» поменялись местами с помощью git-svn - PullRequest
89 голосов
/ 02 июня 2010

Я использую git-svn и заметил, что когда мне нужно исправить конфликт слияния после выполнения git svn rebase, значение параметров --ours и --theirs, например, git checkout перевернуто. То есть, если есть конфликт, и я хочу сохранить версию, которая пришла с сервера SVN, и выбросить изменения, которые я сделал локально, я должен использовать ours, когда я ожидаю, что это будет theirs.

Почему это?

Пример:

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"

1 Ответ

223 голосов
/ 02 июня 2010

Это похоже на то, что делает ребаз.

  • git svn rebase извлекает ревизии из родительского SVN-файла текущей HEAD и перебазирует текущую (незафиксированную в SVN) работу против него.

  • git rebase упоминает:
    Обратите внимание, что слияние rebase работает путем воспроизведения каждого коммита из рабочей ветви поверх ветви <upstream>.
    Из-за этого, когда возникает конфликт слияния:

    • сторона, о которой сообщается как наша, - это перебазированная серия, начиная с <upstream>,
    • и их рабочая ветвь .
      Другими словами, стороны поменялись местами .

git rebase воспроизводит каждый коммит из рабочей ветви поверх ветви <upstream>.

Если вы согласуете оба определения:

  • коммиты из SVN - это те, поверх которых воспроизводятся локальные коммиты Git. Они являются частью «до сих пор перебазированной серии» и называются «нашими» (в вашем случае это файл test.txt с содержанием bar)
  • рабочая ветвь (содержащая коммиты Git, неизвестные SVN, в вашем случае файл test.txt с содержанием baz) - это "их", и каждый из этих локальных коммитов Git воспроизводится.

Другими словами, SVN или нет:

  • ветвь "<upstream>" (поверх которой воспроизводится что-либо, и которая является частью пока что перебазированных коммитов ") равна" ours".
  • что воспроизводится (рабочая ветвь) это " их ".

Хорошо Мнемонический наконечник от CommaToast :

независимо от того, на что указывает HEAD "наше"

(и в первую очередь git rebase upstream делает это для извлечения ветки upstream, поверх которой вы хотите перебазировать: HEAD относится к upstream - ours сейчас.)


Путаница, вероятно, связана с ролью рабочей ветви в классическом git merge.
Когда вы объединяете:

  • "рабочая ветвь" - это та, которая содержит то, что "до сих пор объединено", и считается "нашей",
  • в то время как другие коммиты представляют то, что происходит - не воспроизводится, а - сливается поверх рабочей ветви и рассматривается как «их».

Как упоминается на справочной странице git rebase, слияние во время ребазинга означает, что сторона поменялась местами.


Другой способ сказать то же самое - учесть, что:

  • что у нас есть в проверенной ветке ' наше ',
  • то, что у нас было (и объединяется или переигрывается), это ' их '.

При слиянии :

x--x--x--x--x(*) <- current branch B ('*'=HEAD)
    \
     \
      \--y--y--y <- other branch to merge

, мы не меняем текущую ветвь 'B', так что у нас все еще остается то, над чем мы работали (и мы объединяемся из другой ветки)

x--x--x--x--x---------o(*)  MERGE, still on branch B
    \       ^        /
     \     ours     /
      \            /
       --y--y--y--/  
               ^
              their

Но в отношении rebase , мы переключаемся на сторону, потому что первое, что делает rebase, это извлекает ветку upstream! (чтобы воспроизвести текущие коммиты поверх него)

x--x--x--x--x(*) <- current branch B
    \
     \
      \--y--y--y <- upstream branch

A git rebase upstream сначала изменит HEAD B на ветвь восходящего потока HEAD (следовательно, переключение «наших» и «их» по сравнению с предыдущей «текущей» рабочей ветвью. )

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

, а затем ребаз будет воспроизводить «свои» коммиты в новой «нашей» ветке B:

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

Единственный дополнительный шаг с git svn rebase заключается в том, что svn "выборка" выполняется сначала в удаленной ветви Git, представляющей фиксации SVN.
У вас изначально:

x--x--x--x--x(*) <- current branch B, "ours" for now.
    \                                   
     \
      \--y--y--y <- SVN tracking branch, "theirs for now"

, вы сначала обновляете ветку отслеживания SVN новыми коммитами, поступающими из SVN

x--x--x--x--x(*) <- current branch B, still "ours", not for long
    \                                   
     \
      \--y--y--y--y'--y' <- SVN tracking branch updated

, затем вы переключаете текущую ветвь на сторону SVN (которая становится "нашей")

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")

, перед воспроизведением коммитов, над которыми вы работали (но которые теперь "их" во время этой перебазировки)

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
...