git rebase, tenendo traccia di ‘local’ e ‘remote’

Quando eseguo una git rebase, spesso ho difficoltà a capire cosa sta succedendo con “locale” e “remoto” quando risolvo i conflitti. A volte ho l’impressione che scambino i lati da un impegno all’altro.

Questo è probabilmente (sicuramente) perché non ho ancora capito bene.

Quando si ridimensiona, chi è “locale” e chi è “remoto”?

(Uso P4Merge per risolvere i conflitti)

TL; DR;

Riassumendo (come commenta Benubird ), quando:

 git checkout A git rebase B # rebase A on top of B 
  • local is B (rebase onto ),
  • remote è A

E:

 git checkout A git merge B # merge B into A 
  • local è A (si fondono in ),
  • remote è B

Un rebase cambia il ours (il ramo corrente prima di iniziare rebase) e il theirs (il ramo in cima al quale si desidera rebase).


kutschkem sottolinea che, in un contesto di fusione di GUI :

  • riferimenti locali i commit parzialmente ribatte : ” our ” (il ramo upstream)
  • remote fa riferimento alle modifiche in entrata : ” theirs ” – il ramo corrente prima del rebase.

Vedi le illustrazioni nell’ultima parte di questa risposta.


Inversione quando rebase

La confusione potrebbe essere correlata all’inversione della ours e della theirs durante un rebase .
(estratti rilevanti)

pagina man di git rebase :

Si noti che un’unione di rebase funziona riproducendo ogni commit dal ramo di lavoro in cima al ramo .

Per questo motivo, quando si verifica un conflitto di unione:

  • la parte riportata come ‘ ours ‘ è la serie a lungo termine, a partire da ,
  • e ” theirs ” è il ramo di lavoro. In altre parole, i lati vengono scambiati.

Inversione illustrata

In una fusione

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

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

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

Su un rebase:

Ma su un rebase , cambiamo lato perché la prima cosa che fa un rebase è il checkout del ramo upstream! (per riprodurre l'attuale commit su di esso)

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

A git rebase upstream per prima cosa cambierà HEAD di B al ramo upstream 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 poi il rebase ripeterà i "loro" commit sul nuovo ramo "nostro" 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 

Nota: la nozione "upstream" è l'insieme di dati referenziale (un repository completo o, come in questo caso, un ramo, che può essere un ramo locale ) da cui vengono letti i dati o ai quali vengono aggiunti / creati nuovi dati.


" local " e " remote " vs. " mine " e " theirs "

Pandawood aggiunge nei commenti :

Per me rimane ancora la domanda, che è "locale" e che è "remoto" (dal momento che i termini "nostro" e "loro" non sono usati quando si ridisegna in git, riferirsi a loro sembra solo rendere una risposta più confusa) .

GUI git mergetool

kutschkem aggiunge, giustamente:

Quando risolvono i conflitti, git dirà qualcosa come:

 local: modified file and remote: modified file. 

Sono abbastanza sicuro che la domanda mira alla definizione di locale e remoto a questo punto. A quel punto, mi sembra dalla mia esperienza che:

  • riferimenti locali i commit parzialmente ribatte : " our " (il ramo upstream)
  • remote fa riferimento alle modifiche in entrata : " theirs " - il ramo corrente prima del rebase.

git mergetool indica effettivamente "locale" e "remoto" :

 Merging: f.txt Normal merge conflict for 'f.txt': {local}: modified file {remote}: modified file Hit return to start merge resolution tool (kdiff3): 

Ad esempio, KDiff3 mostrerebbe la risoluzione di fusione in questo modo :

KDiff3

E la combinazione lo mostrerebbe anche :

Fusione diff

Lo stesso per VimDiff , che visualizza :

Richiamare Vimdiff come un mergetool con git mergetool -t gvimdiff. Le versioni recenti di Git invocano Vimdiff con il seguente layout di finestra:

 +--------------------------------+ | LOCAL | BASE | REMOTE | +--------------------------------+ | MERGED | +--------------------------------+ 
  • LOCAL :
    Un file temporaneo contenente i contenuti del file sul ramo corrente.
  • BASE :
    Un file temporaneo contenente la base comune per l'unione.
  • REMOTE :
    Un file temporaneo contenente i contenuti del file da unire.
  • MERGED
    Il file contenente gli indicatori di conflitto.

Git ha eseguito il più ansible la risoluzione automatica dei conflitti e lo stato di questo file è una combinazione di LOCAL e REMOTE con indicatori di conflitto che circondano tutto ciò che Git non è stato in grado di risolvere autonomamente.
Il mergetool dovrebbe scrivere il risultato della risoluzione in questo file.

La linea di fondo

git rebase

  • LOCAL = la base su cui stai riposizionando
  • REMOTE = i commit che stai salendo in cima

git si fondono

  • LOCAL = il ramo originale in cui ti stai fondendo
  • REMOTE = l’altro ramo di cui si sta effettuando l’unione

In altre parole, LOCAL è sempre l’originale e REMOTE è sempre il tipo i cui commit non erano presenti prima, perché sono stati uniti o rebased in cima

Provalo!

Certamente. Non fidarti della mia parola! Ecco un semplice esperimento che puoi fare per vedere di persona.

Innanzitutto, assicurati di aver git mergetool configurato correttamente. (Se non lo facessi, probabilmente non leggeresti comunque questa domanda). Poi trova una directory su cui lavorare.

Configura il tuo repository:

 md LocalRemoteTest cd LocalRemoteTest 

Creare un commit iniziale (con un file vuoto):

 git init notepad file.txt (use the text editor of your choice) (save the file as an empty file) git add -A git commit -m "Initial commit." 

Crea un commit su un ramo che non è il master:

 git checkout -b notmaster notepad file.txt (add the text: notmaster) (save and exit) git commit -a -m "Add notmaster text." 

Creare un commit sul ramo principale:

 git checkout master notepad file.txt (add the text: master) (save and exit) git commit -a -m "Add master text." gitk --all 

A questo punto il tuo repository dovrebbe assomigliare a questo:

Repository con un commit di base e due rami one-commit

Ora per il test di rebase:

 git checkout notmaster git rebase master (you'll get a conflict message) git mergetool LOCAL: master REMOTE: notmaster 

Ora il test di unione. Chiudi il tuo mergetool senza salvare le modifiche, quindi cancella il rebase:

 git rebase --abort 

Poi:

 git checkout master git merge notmaster git mergetool LOCAL: master REMOTE: notmaster git reset --hard (cancels the merge) 

I risultati dovrebbero essere uguali a quelli mostrati in alto.

Non ho capito esattamente il tuo problema, ma penso che il seguente diagramma risolva il tuo problema. (Rebase: Remote Repository —> Workspace)

http://assets.osteele.com/images/2008/git-transport.png

Fonte: My Git Workflow