Pourquoi le sens de «notre» et de «leur» est-il inversé avec git-svn

90

J'utilise git-svn et j'ai remarqué que lorsque je dois corriger un conflit de fusion après avoir exécuté a git svn rebase, la signification des options --ourset --theirsde par exemple git checkoutest inversée. Autrement dit, s'il y a un conflit et que je veux conserver la version qui provient du serveur SVN et rejeter les modifications que j'ai apportées localement, je dois utiliser ours, quand je m'attendais à ce que ce soit le cas theirs.

Pourquoi donc?

Exemple:

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
la source
Jut a mis à jour ma réponse avec de nombreux schémas pour mieux illustrer les côtés «nôtre» et «le leur».
VonC le
1
Voir aussi github.com/git/git/commit/…
VonC

Réponses:

230

Cela semble cohérent avec ce que fait un rebase.

  • git svn rebase récupérera les révisions du parent SVN du HEAD actuel et rebase le travail actuel (non validé vers SVN).

  • git rebasementionne:
    Notez qu'une fusion de rebase fonctionne en rejouant chaque commit depuis la branche de travail au-dessus de la <upstream>branche.
    Pour cette raison, lorsqu'un conflit de fusion se produit:

    • le côté rapporté comme le nôtre est la série rebasée jusqu'à présent, en commençant par<upstream> ,
    • et la leur est la branche de travail .
      En d'autres termes, les côtés sont échangés .

git rebase rejoue chaque commit de la branche de travail au-dessus de la <upstream>branche.

Si vous conciliez les deux définitions:

  • les commits provenant de SVN sont ceux sur lesquels les commits Git locaux sont rejoués. Ils font partie de la "série rebasée jusqu'à présent" et sont référencés comme "notre" (dans votre cas, le test.txtfichier avec le barcontenu)
  • la branche de travail (contenant les commits Git inconnus de SVN, dans votre cas, le test.txtfichier avec le bazcontenu) est "leur", et chacun de ces commits Git locaux est en cours de relecture.

En d'autres termes, SVN ou non:

  • la <upstream>branche " " (sur laquelle tout est rejoué, et qui fait partie des commits rebasés jusqu'à présent ") est" la nôtre ".
  • ce qui est rejoué (la branche active) est « leur ».

Bon conseil mnémotechnique de CommaToast :

tout ce que HEAD pointe est "le nôtre"

(et la première chose à faire pour extraire git rebase upstreamla upstreambranche au-dessus de laquelle vous voulez rebaser: HEAD fait référence upstream- oursmaintenant.)


La confusion vient probablement du rôle de la branche de travail dans un classique git merge.
Lorsque vous fusionnez:

  • la "branche de travail" est celle contenant ce qui est "jusqu'ici fusionné", et est considérée comme "notre",
  • tandis que l'autre commit représente ce qui est en train d'être - non rejoué mais - fusionné au-dessus de la branche de travail, et considéré comme "leur".

Comme l'indique la git rebasepage de manuel, une fusion pendant un rebase signifie que le côté est permuté.


Une autre façon de dire la même chose est de considérer que:

  • ce que nous avons sur la succursale vérifiée est «le nôtre »,
  • ce que nous avions (et est fusionné ou rejoué) est «le leur ».

Lors d'une fusion :

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

, nous ne changeons pas la branche actuelle 'B', donc ce que nous avons est toujours ce sur quoi nous travaillions (et nous fusionnons depuis une autre branche)

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

Mais sur un rebase , on change de côté car la première chose qu'un rebase fait est de vérifier la branche amont! (pour rejouer les commits actuels par-dessus)

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

A git rebase upstreampassera d'abord HEADde B à la branche amont HEAD(d'où le changement de «le nôtre» et «le leur» par rapport à la branche de travail «actuelle» précédente).

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

, puis le rebase rejouera `` leurs '' commits sur la nouvelle branche `` notre '' 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

La seule étape supplémentaire avec git svn rebaseest qu'un svn "fetch" est d'abord effectué sur la branche distante Git représentant les commits SVN.
Vous avez au départ:

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

, vous mettez d'abord à jour la branche de suivi SVN avec de nouveaux commits provenant de SVN

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

, puis vous basculez la branche actuelle du côté SVN (qui devient "la nôtre")

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

, avant de rejouer les commits sur lesquels vous étiez en train de travailler (mais qui sont désormais "les leurs" pendant ce 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
la source
9
Wow, quelle excellente réponse, merci! J'ai dû complètement manquer cette remarque dans la git rebasepage de manuel ...
Marc Liyanage
@epologee: vous êtes les bienvenus. C'est également utile lorsque vous n'utilisez que git, pour comprendre ce qui se passe pendant un rebase par rapport à une fusion. Et cela ajoute à la définition en amont: stackoverflow.com/questions/2739376/…
VonC
5
Mon Dieu!!! Quel genre de drogue Torvalds prenait-il? C'est bien trop compliqué! Git est un outil très dangereux. Vous pouvez facilement détruire tout votre travail si vous essayez d'utiliser des connaissances extérieures ou votre intuition. Le développement de logiciels est tombé dans le trou du ver!
ATL_DEV
@ user148298 Il n'y a rien de mal pour cette fonction. Vous n'êtes pas obligé de connaître toutes ces sortes de choses à moins d'être un expert git. Et si vous avez besoin de fonctions avancées, vous devrez d'abord les apprendre.
Earth Engine du