Por que o significado de “nosso” e “deles” é revertido com git-svn

90

Eu uso git-svn e percebi que quando preciso consertar um conflito de mesclagem depois de executar um git svn rebase, o significado das opções --ourse --theirspara, por exemplo, git checkouté invertido. Ou seja, se houver um conflito e eu quiser manter a versão que veio do servidor SVN e descartar as alterações que fiz localmente, tenho que usar ours, quando esperava que fosse theirs.

Por que é que?

Exemplo:

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"
Marc Liyanage
fonte
Jut atualizou minha resposta com vários diagramas para ilustrar melhor os lados "nosso" e "deles".
VonC
1
Veja também github.com/git/git/commit/…
VonC

Respostas:

230

Isso parece consistente com o que um rebase faz.

  • git svn rebase irá buscar revisões do SVN pai do HEAD atual e realocar o trabalho atual (não comprometido com o SVN) contra ele.

  • git rebasemenciona:
    Observe que uma junção de rebase funciona repetindo cada commit do branch de trabalho no topo do <upstream>branch.
    Por causa disso, quando ocorre um conflito de mesclagem:

    • o lado relatado como nosso é a série rebaseada até agora, começando com<upstream> ,
    • e deles é o ramo de trabalho .
      Em outras palavras, os lados são trocados .

git rebase reproduz cada commit do branch de trabalho no topo do <upstream>branch.

Se você reconciliar as duas definições:

  • os commits vindos do SVN são aqueles sobre os quais os commits locais do Git são reproduzidos. Eles fazem parte da "série rebaseada até agora" e são referenciados como "nosso" (no seu caso, o test.txtarquivo com barconteúdo)
  • o branch de trabalho (contendo commits Git desconhecidos do SVN, no seu caso, o test.txtarquivo com bazconteúdo) é "deles", e cada um desses commits Git locais estão sendo reproduzidos.

Em outras palavras, SVN ou não:

  • o <upstream>branch " " (no topo do qual qualquer coisa é reproduzida, e que faz parte dos commits rebased até agora ") é" nosso ".
  • o que está sendo reproduzido (o ramo de trabalho) é " deles ".

Boa dica mnemônica da CommaToast :

tudo o que o HEAD está apontando é "nosso"

(e a primeira coisa a git rebase upstreamfaz para verificar o upstreambranch no topo do qual você deseja realocar: HEAD refere-se a upstream- oursagora.)


A confusão provavelmente vem do papel do ramo de trabalho em um clássico git merge.
Quando você está mesclando:

  • o "branch de trabalho" é aquele que contém o que está "até agora fundido", e é considerado como "nosso",
  • enquanto o outro commit representa o que está sendo - não repetido, mas - mesclado no topo do branch de trabalho, e considerado como "deles".

Como a git rebasepágina do manual menciona, uma mesclagem durante um rebase significa que o lado foi trocado.


Outra maneira de dizer a mesma coisa é considerar que:

  • o que temos na agência de check-out é ' nosso ',
  • o que tivemos (e está sendo fundido ou reproduzido) é ' deles '.

Em uma fusão :

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

, não alteramos o branch atual 'B', então o que temos ainda é o que estávamos trabalhando (e nos unimos de outro branch)

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

Mas em um rebase , mudamos de lado porque a primeira coisa que um rebase faz é verificar o branch upstream! (para repetir os commits atuais em cima dele)

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

A git rebase upstreamprimeiro mudará HEADde B para o ramal upstream HEAD(daí a troca de 'nosso' e 'deles' em comparação com o branch de trabalho "atual" anterior.)

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

, e então o rebase irá repetir 'seus' commits no novo 'nosso' branch 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

O único passo extra git svn rebaseé que um svn "fetch" é executado primeiro no branch remoto Git representando os commits SVN.
Você tem inicialmente:

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

, você primeiro atualiza o branch de rastreamento SVN com novos commits vindos do SVN

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

, então você muda o branch atual para o lado SVN (que se torna "nosso")

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

, antes de repetir os commits nos quais você estava trabalhando (mas que agora são "deles" durante o 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
VonC
fonte
9
Uau, que ótima resposta, obrigado! Devo ter perdido completamente aquele comentário na git rebasepágina de manual ...
Marc Liyanage
@epologee: de nada. Também é útil quando você está usando apenas git, para entender o que está acontecendo durante um rebase vs. uma fusão. E acrescenta à definição upstream: stackoverflow.com/questions/2739376/…
VonC
5
Meu Deus!!! Que tipo de drogas Torvalds estava tomando? Isso é muito complicado! Git é uma ferramenta muito perigosa. Você pode destruir facilmente todo o seu trabalho se tentar usar o conhecimento externo ou a sua intuição. O desenvolvimento de sofware foi por água abaixo!
ATL_DEV
@ user148298 Não há nada de errado com esta função. Você não precisa saber todo esse tipo de coisa, a menos que seja um especialista em git. E se você precisar de funções avançadas, terá que aprendê-las primeiro.
Earth Engine