Come si fondono git dopo il cherry-pick?


190

Immaginiamo di avere un masterramo.

Quindi creiamo un newbranch

git checkout -b newbranch

ed effettua due nuovi commit newbranch: commit1 e commit2

Quindi passiamo a master e make cherry-pick

git checkout master
git cherry-pick hash_of_commit1

Esaminando gitk, vediamo che commit1 e la sua versione selezionata ciliegia hanno hash diversi, quindi tecnicamente sono due commit diversi.

Infine ci uniamo newbranchin master:

git merge newbranch

e vedere che questi due commit con hash diversi sono stati uniti senza problemi sebbene implicino che le stesse modifiche dovrebbero essere applicate due volte, quindi uno di loro dovrebbe fallire.

Git esegue davvero un'analisi intelligente del contenuto di commit durante l'unione e decide che le modifiche non devono essere applicate due volte o che questi commit sono contrassegnati internamente come collegati tra loro?

Risposte:


131

Risposta breve

Non preoccuparti, Git ce la farà.

Risposta lunga

A differenza, ad esempio, di SVN 1 , Git non memorizza i commit nel formato delta, ma è basato su un'istantanea 2,3 . Mentre SVN tenta ingenuamente di applicare ogni commit unito come patch (e fallisce, per il motivo esatto che hai descritto), Git è generalmente in grado di gestire questo scenario.

Durante l'unione, Git proverà a combinare le istantanee di entrambi i commit HEAD in una nuova istantanea. Se una parte di codice o un file è identica in entrambe le istantanee (ovvero perché un commit è già stato selezionato in modo corretto), Git non lo toccherà.

fonti

1 Skip-Delta in Subversion
2 Nozioni di base di Git
3 Il modello di oggetti Git


40
In realtà, direi che dovresti preoccuparti della fusione e "git lo gestirà" non è una buona regola empirica.
Cregox,

4
In effetti l'unione PUO 'risultare in contenuti duplicati in alcuni casi. Git lo gestisce a volte, ma a volte no.
donquixote,

Questo è terribilmente sbagliato. Praticamente memorizza i file in tutte le forme possibili. E se ricordo bene SVN utilizzato per memorizzare le istantanee.
he_the_great

2
@he_the_great, no. Il formato di archiviazione skip-delta di SVN (! = Snapshot) è ben documentato nel manuale . E davvero non capisco cosa intendi per condannabile . Non sono un madrelingua, ma sono abbastanza sicuro che non sia una parola vera.
helmbert

2
@he_the_great Ma anche come file pack qualsiasi hash dato per un file risulta nel file completo. Sì, si comprime utilizzando delta, ma non è un delta per le modifiche in un commit, ma invece un delta tra gli hash per un file. Per quanto riguarda l'oggetto commit, fa riferimento a un albero che fa riferimento all'hash per un file completo. Che sotto il cofano i dati siano compressi non influisce sul modo in cui git funziona. Git memorizza i file completi per quanto riguarda un commit, SVN archivia i delta per i commit, a quanto ho capito.
Peter Olson,

44

Dopo tale fusione potresti avere commessi nella storia due volte ciliegia.

Soluzione per evitarlo Cito dall'articolo che raccomanda per i rami con duplicati (selezionati con ciliegie) si impegna a utilizzare rebase prima di unire:

git merge after git cherry-pick: evitando doppioni

Immagina di avere il ramo principale e un ramo b:

   o---X   <-- master
    \
     b1---b2---b3---b4   <-- b

Ora abbiamo urgentemente bisogno dei commit b1 e b3 in master, ma non dei restanti commit in b. Quindi quello che facciamo è controllare il ramo principale e cherry-pick commette b1 e b3:

$ git checkout master
$ git cherry-pick "b1's SHA"
$ git cherry-pick "b3's SHA"

Il risultato sarebbe:

   o---X---b1'---b3'   <-- master
    \
     b1---b2---b3---b4   <-- b

Diciamo che facciamo un altro commit sul master e otteniamo:

   o---X---b1'---b3'---Y   <-- master
    \
     b1---b2---b3---b4   <-- b

Se ora uniamo il ramo b nel master:

$ git merge b

Otterremmo quanto segue:

   o---X---b1'---b3'---Y--- M  <-- master
     \                     /
      b1----b2----b3----b4   <-- b

Ciò significa che le modifiche introdotte da b1 e b3 appariranno due volte nella storia. Per evitare che possiamo riformulare invece di unire:

$ git rebase master b

Che produrrebbe:

   o---X---b1'---b3'---Y   <-- master
                        \
                         b2'---b4'   <-- b

Finalmente:

$ git checkout master
$ git merge b

ci da:

   o---X---b1'---b3'---Y---b2'---b4'   <-- master, b

Correzioni EDIT supposte dal commento di David Lemon


1
ottimo suggerimento su rebase! salterà automaticamente tutti i commit selezionati dalla ciliegia.
iTake

2
Onestamente, sembra troppo bello per essere vero, devo vederlo con i miei occhi. Anche rebase modifica i commit, la tua ultima sequenza temporale dovrebbe essere---Y---b2'---b4'
David Lemon,

Funziona perfettamente. Molto utile se non vuoi che la ciliegina selezionata si impegni due volte nella storia.
user2350644

1
Non si dovrebbe notare che mentre rebase è bello, il pericolo di usarlo è che eventuali forcelle o rami creati dalla vecchia b non saranno sincronizzati e gli utenti potrebbero aver bisogno di ricorrere a cose come git reset --hard e git push -f?
JHH,

1
@JHH è per questo che
rifacciamo
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.