Perché il significato di "nostro" e "loro" è invertito con git-svn


90

Uso git-svn e ho notato che quando devo correggere un conflitto di unione dopo aver eseguito a git svn rebase, il significato delle opzioni --ourse --theirsad es. git checkoutÈ invertito. Cioè, se c'è un conflitto e voglio mantenere la versione che proviene dal server SVN e buttare via le modifiche che ho fatto localmente, devo usare ours, quando mi aspetto che sia theirs.

Perché?

Esempio:

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"

Ho aggiornato la mia risposta con molti diagrammi per illustrare meglio i lati "nostro" e "loro".
VonC

Risposte:


230

Questo sembra coerente con ciò che fa un rebase.

  • git svn rebase recupererà le revisioni dal genitore SVN dell'attuale HEAD e ribaserà l'attuale (non sottoposto a commit a SVN) contro di esso.

  • git rebasemenziona:
    Nota che una fusione rebase funziona riproducendo ogni commit dal ramo di lavoro in cima al <upstream>ramo.
    Per questo motivo, quando si verifica un conflitto di unione:

    • il lato segnalato come il nostro è la serie finora ribasata, a partire da<upstream> ,
    • e il loro è il ramo lavorativo .
      In altre parole, i lati vengono scambiati .

git rebase riproduce ogni commit dal ramo di lavoro in cima al <upstream>ramo.

Se riconcili entrambe le definizioni:

  • i commit provenienti da SVN sono quelli in cima ai quali vengono riprodotti i commit Git locali. Fanno parte della "serie finora ribasata" e sono indicati come "nostro" (nel tuo caso, il test.txtfile con il barcontenuto)
  • il ramo di lavoro (contenente i commit Git sconosciuti a SVN, nel tuo caso, il test.txtfile con il bazcontenuto) è "loro", e ciascuno di quei commit Git locali viene riprodotto.

In altre parole, SVN o no:

  • il <upstream>ramo " " (in cima al quale viene riprodotto qualsiasi cosa e che fa parte dei commit finora ribasati ") è" nostro ".
  • ciò che viene riprodotto (il ramo di lavoro) è " loro ".

Buon consiglio mnemonico di CommaToast :

qualunque cosa HEAD stia indicando è "nostra"

(e la prima cosa che git rebase upstreamfa per eseguire il checkout del upstreamramo in cima al quale si desidera rebase: HEAD si riferisce a upstream- oursora.)


La confusione deriva probabilmente dal ruolo del ramo lavorativo in un classico git merge.
Quando stai unendo:

  • il "ramo di lavoro" è quello contenente ciò che è "finora unito", ed è considerato come "nostro",
  • mentre l'altro commit rappresenta ciò che viene - non riprodotto ma - unito in cima al ramo di lavoro, e considerato come "loro".

Come git rebasemenzionato nella pagina man, un'unione durante un rebase significa che il lato viene scambiato.


Un altro modo per dire la stessa cosa è considerare che:

  • quello che abbiamo nella filiale controllata è " nostro ",
  • quello che avevamo (ed è stato unito o riprodotto) è " loro ".

In una fusione :

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

, non cambiamo l'attuale ramo "B", quindi quello che abbiamo è ancora quello su cui stavamo lavorando (e ci fondiamo da un altro ramo)

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

Ma su un rebase , cambiamo lato perché la prima cosa che fa un rebase è controllare il ramo upstream! (per riprodurre i commit correnti sopra di esso)

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

A git rebase upstreamcambierà dapprima HEADB al ramo a monte HEAD(da qui il passaggio di "nostro" e "loro" rispetto al precedente ramo di lavoro "corrente".)

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 quindi il rebase riprodurrà i "loro" commit sul nuovo ramo B "nostro":

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

L'unico passaggio aggiuntivo git svn rebaseè che un "fetch" svn viene eseguito prima sul ramo remoto di Git che rappresenta i commit SVN.
Inizialmente hai:

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

, aggiorna prima il ramo di tracciamento SVN con nuovi commit provenienti da SVN

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

, quindi sposti il ​​ramo corrente sul lato SVN (che diventa "nostro")

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

, prima di riprodurre i commit su cui stavi lavorando (ma che ora sono "loro" durante il 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

9
Wow, che bella risposta, grazie! Devo aver perso completamente quell'osservazione nella git rebasepagina man ...
Marc Liyanage

@epologee: sei il benvenuto. È anche utile quando si utilizza solo git, per capire cosa sta succedendo durante un rebase rispetto a un merge. E si aggiunge alla definizione monte: stackoverflow.com/questions/2739376/...
VonC

5
Mio Dio!!! Che tipo di droghe stava prendendo Torvalds? È troppo complicato! Git è uno strumento molto pericoloso. Puoi facilmente distruggere tutto il tuo lavoro se cerchi di utilizzare la conoscenza esterna o la tua intuizione. Lo sviluppo del software è andato nel vuoto!
ATL_DEV

@ user148298 Non c'è niente di sbagliato in questa funzione. Non devi sapere tutto questo genere di cose a meno che tu non sia un esperto di git. E se hai bisogno di funzioni avanzate dovrai prima impararle.
Earth Engine,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.