Non proprio correlato a questa risposta, ma me ne sarei andato git pull
, che viene semplicemente git fetch
seguito da git merge
. Stai facendo tre unioni, che faranno eseguire a Git tre operazioni di recupero, quando un recupero è tutto ciò di cui hai bisogno. Quindi:
git fetch origin # update all our origin/* remote-tracking branches
git checkout demo # if needed -- your example assumes you're on it
git merge origin/demo # if needed -- see below
git checkout master
git merge origin/master
git merge -X theirs demo # but see below
git push origin master # again, see below
Controllare l'unione più complicata
La parte più interessante qui è git merge -X theirs
. Come notato da root545 , le -X
opzioni vengono passate alla strategia di unione e sia la recursive
strategia predefinita che la resolve
strategia alternativa accettano -X ours
o -X theirs
(l'una o l'altra, ma non entrambe). Per capire cosa fanno, però, devi sapere come Git trova e tratta, unire i conflitti .
Un conflitto di unione può verificarsi all'interno di alcuni file 1 quando la versione di base è diversa dalla versione corrente (chiamata anche locale, HEAD o --ours
) e dall'altra (chiamata anche remota o --theirs
) dello stesso file. Cioè, l'unione ha identificato tre revisioni (tre commit): base, nostro e loro. La versione "base" proviene dalla base merge base tra il nostro commit e il loro commit, come si trova nel grafico del commit (per molto di più su questo, vedi altri post di StackOverflow). Git ha quindi trovato due serie di modifiche: "cosa abbiamo fatto" e "cosa hanno fatto". Questi cambiamenti si trovano (in generale) riga per riga, puramente testualibase. Git non ha una reale comprensione del contenuto dei file; sta semplicemente confrontando ogni riga di testo.
Questi cambiamenti sono ciò che vedi git diff
nell'output e, come sempre, hanno anche un contesto . È possibile che le cose che abbiamo modificato siano su linee diverse da quelle che sono cambiate, in modo che le modifiche sembrino non entrare in collisione, ma anche il contesto è cambiato (ad esempio, a causa del fatto che la nostra modifica è vicina all'inizio o alla fine del file, in modo che il file si esaurisca nella nostra versione, ma nella loro hanno anche aggiunto più testo in alto o in basso).
Se le modifiche avvengono su righe diverse , ad esempio, passiamo color
alla colour
riga 17 e passano fred
alla barney
riga 71, allora non c'è conflitto: Git accetta semplicemente entrambe le modifiche. Se le modifiche avvengono sulle stesse righe, ma sono modifiche identiche , Git esegue una copia della modifica. Solo se le modifiche sono sulla stessa linea, ma sono modifiche diverse, o quel caso speciale di contesto che interferisce, si ottiene un conflitto di modifica / modifica.
Le opzioni -X ours
e -X theirs
dicono a Git come risolvere questo conflitto, scegliendo solo una delle due modifiche: la nostra o la loro. Dato che hai detto che ti stai fondendo demo
(il loro) nel master
(nostro) e vuoi le modifiche da demo
, vorresti -X theirs
.
L'applicazione alla cieca -X
, tuttavia, è pericolosa. Solo perché le nostre modifiche non sono entrate in conflitto riga per riga, non significa che le nostre modifiche non siano effettivamente in conflitto! Un classico esempio si verifica nelle lingue con dichiarazioni di variabili. La versione base potrebbe dichiarare una variabile non utilizzata:
int i;
Nella nostra versione, cancelliamo la variabile inutilizzata per far scomparire un avviso del compilatore e nella loro versione aggiungono un ciclo alcune righe dopo, usando i
come contatore dei cicli. Se combiniamo le due modifiche, il codice risultante non viene più compilato. L' -X
opzione non è di aiuto qui poiché le modifiche sono su linee diverse .
Se disponi di una suite di test automatizzata, la cosa più importante da fare è eseguire i test dopo l'unione. Puoi farlo dopo aver eseguito il commit e sistemare le cose in un secondo momento, se necessario; oppure puoi farlo prima di eseguire il commit, aggiungendo --no-commit
al git merge
comando. Lasceremo i dettagli per tutto questo ad altri post.
1 Puoi anche ottenere conflitti rispetto alle operazioni "a livello di file", ad esempio, forse correggiamo l'ortografia di una parola in un file (in modo da avere una modifica), e cancellano l'intero file (in modo che abbiano un Elimina). Git non risolverà questi conflitti da solo, indipendentemente dagli -X
argomenti.
Esecuzione di meno fusioni e / o unioni più intelligenti e / o utilizzo di rebase
Ci sono tre unioni in entrambe le nostre sequenze di comandi. Il primo è portare origin/demo
nel locale demo
(il tuo usa git pull
che, se il tuo Git è molto vecchio, non si aggiornerà origin/demo
ma produrrà lo stesso risultato finale). Il secondo è portare origin/master
dentro master
.
Non mi è chiaro chi stia aggiornando demo
e / o master
. Se scrivi il tuo codice sul tuo demo
ramo e altri lo scrivono e lo inviano al demo
ramo origin
, allora questa unione del primo passaggio può avere conflitti o produrre una unione reale. Il più delle volte, è meglio usare rebase, piuttosto che merge, per combinare il lavoro (ammettiamolo, questa è una questione di gusti e opinioni). Se è così, potresti voler usare git rebase
invece. D'altra parte, se non esegui mai nessuno dei tuoi commit demo
, non hai nemmeno bisogno di un : questo farà avanzare velocemente il tuo per abbinare il aggiornatodemo
ramo. In alternativa, se vuoi automatizzare molto di questo, ma essere in grado di controllare attentamente quando ci sono commit che sia tu che altri, potresti voler usaregit merge --ff-only origin/demo
demo
origin/demo
se possibile, e semplicemente fallirà in caso contrario (a quel punto puoi ispezionare i due set di modifiche e scegliere un'unione reale o un rebase a seconda dei casi).
Questa stessa logica vale per master
, anche se si sta facendo l'unione su master
, quindi è sicuramente bisogno di una master
. Tuttavia, è ancora più probabile che si desideri che l'unione fallisca se non può essere eseguita come una non unione in avanti veloce, quindi probabilmente dovrebbe essere anche questo git merge --ff-only origin/master
.
Diciamo che non ti impegni mai da solo demo
. In questo caso possiamo abbandonare completamente il nome demo
:
git fetch origin # update origin/*
git checkout master
git merge --ff-only origin/master || die "cannot fast-forward our master"
git merge -X theirs origin/demo || die "complex merge conflict"
git push origin master
Se si sta facendo il proprio demo
commit ramo, questo non è utile; potresti anche mantenere l'unione esistente (ma magari aggiungerla a --ff-only
seconda del comportamento che vuoi), o cambiarla per fare un rebase. Nota che tutti e tre i metodi potrebbero non riuscire: l'unione potrebbe non riuscire con un conflitto, l'unione con --ff-only
potrebbe non essere in grado di avanzare rapidamente e il rebase potrebbe non riuscire con un conflitto (il rebase funziona, in sostanza, con commit selettivi, che utilizza l'unione macchinari e quindi può ottenere un conflitto di unione).
git push -f origin master