Come unire un commit specifico in Git


1035

Ho biforcuto un ramo da un repository in GitHub e ho commesso qualcosa di specifico per me. Ora ho scoperto che il repository originale aveva una buona funzionalità che era a HEAD.

Voglio unirlo solo senza precedenti commit. Cosa dovrei fare? So come unire tutti gli commit:

git branch -b a-good-feature
git pull repository master
git checkout master
git merge a-good-feature
git commit -a
git push

Se stai cercando di farlo in relazione a Github, questo articolo ti guiderà attraverso di esso. markosullivan.ca/how-to-handle-a-pull-request-from-github
johndpope

Risposte:


1177

' git cherry-pick' dovrebbe essere la tua risposta qui.

Applica la modifica introdotta da un commit esistente.

Non dimenticare di leggere la risposta di bdonlan sulle conseguenze della raccolta delle ciliegie in questo post:
"Estrai tutti i commit da un ramo, spingi gli commit specificati in un altro" , dove:

A-----B------C
 \
  \
   D

diventa:

A-----B------C
 \
  \
   D-----C'

Il problema con questo commit è che git considera che i commit includano tutta la cronologia prima di loro

Dove C 'ha un SHA-1ID diverso .
Allo stesso modo, ciliegia che seleziona un commit da un ramo all'altro in pratica comporta la generazione di una patch, quindi l'applicazione, perdendo così anche la storia in quel modo.

Questa modifica degli ID di commit interrompe la funzionalità di fusione di git tra le altre cose (anche se se usata con parsimonia ci sono euristiche che documenteranno questo).
Ancora più importante, ignora le dipendenze funzionali: se C effettivamente utilizzava una funzione definita in B, non lo saprai mai .


1
@ openid000: "rami a grana più fine": che è esattamente ciò che bdonlan ha suggerito nella sua risposta.
VonC,

8
Nota: "git rebase" cambia anche SHA-1. Vedi anche "git rebase vs. git merge ( stackoverflow.com/questions/804115/git-rebase-vs-git-merge ) e" git workflow "( stackoverflow.com/questions/457927/… ) per i casi in cui" git rebase "è legittimo.
VonC,

1
Tra "rami a grana fine", "cherry-pick" e "rebase", avrai quindi tutte le possibilità per gestire il codice nei rami con git.
VonC,

@VonC "rami a grana fine" sì. Avevo due rami e avevo effettuato gli aggiornamenti a un particolare modulo di visualizzazione in un ramo. Questo modulo era indipendente da tutti gli altri codici, quindi cherry-pick era dannatamente conveniente applicare queste modifiche anche ad altri rami
Cheeku,

1
Tuttavia, quando si uniscono, sia i commit C che C prevarranno nella cronologia dei commit.
Rahul Shah,

739

Puoi usare git cherry-pick per applicare un singolo commit da solo al tuo ramo attuale.

Esempio: git cherry-pick d42c389f


68
+1 per il tuo precedente post sulla raccolta delle ciliegie ( stackoverflow.com/questions/880957/… ). Mi sono preso la libertà di copiarne un estratto nella mia risposta sopra.
VonC,

4
Probabilmente git cherry-pick d42co git cherry-pick d42c3 funzionerà. Git è intelligente. ;)
guneysus l'

2
fatale: revisione negativa
Ievgen Naida

3
Ho appena eseguito questo comando e sembra che abbia funzionato, ma quando eseguo lo stato git, non dice "niente da impegnare, albero pulito". Quando aggiorno la pagina Commits nella mia pagina web bitbucket, non viene visualizzata. Ma appare quando eseguo git log. E vedo il codice modificato. Qualcuno può spiegare se devo fare ulteriori passaggi?
Ray

1
Purtroppo questo non risponde alla domanda: in realtà non crea un'unione. Non ci sono antenati che indicano d42c389f. Forse all'OP non importava creare un'unione di per sé, ma la differenza a volte importa.
LarsH il

29

Proviamo a fare un esempio e capire:

Ho un ramo, dire master , che punta a X <commit-id>, e ho un nuovo ramo che punta a Y <sha1>.

Dove Y <commit-id> = <master> si impegna ramo - pochi commit

Ora diciamo che per il ramo Y devo separare gli commit tra il ramo master e il nuovo ramo. Di seguito è la procedura che possiamo seguire:

Passo 1:

git checkout -b local origin/new

dove local è il nome della filiale. Qualsiasi nome può essere dato.

Passo 2:

  git merge origin/master --no-ff --stat -v --log=300

Unisci i commit dal ramo master al nuovo ramo e crea anche un commit unione del messaggio di registro con descrizioni di una riga al massimo da <n> commit effettivi che vengono uniti.

Per ulteriori informazioni e parametri sull'unione Git, fare riferimento a:

git merge --help

Inoltre, se è necessario unire un commit specifico, è possibile utilizzare:

git cherry-pick <commit-id>

hai cambiato la definizione di Ynella tua frase 3d? "Ho un nuovo ramo che punta a Y" vs "Ora dì per ramo Y", sembra che Y fosse un commit e poi è diventato un ramo
Purefan

come posso farlo da un certo punto di impegno di un ramo che voglio unire
Kulbhushan Singh,

3

Nel mio caso d'uso avevamo un bisogno simile di CD CI. Abbiamo usato git flow con branch di sviluppo e master. Gli sviluppatori sono liberi di unire le modifiche direttamente allo sviluppo o tramite una richiesta pull da un ramo di funzionalità. Tuttavia, per padroneggiare uniamo solo gli commit stabili dal ramo di sviluppo in modo automatizzato tramite Jenkins.

In questo caso fare cherry-pick non è una buona opzione. Tuttavia creiamo un ramo locale dall'ID commit, quindi uniamo quel ramo locale da padroneggiare ed eseguiamo mvn cleanify (usiamo maven). In caso di successo, rilasciare artefatto della versione di produzione su nexus utilizzando il plugin di rilascio maven con localCheckout = opzione true e pushChanges = false. Alla fine, quando tutto ha successo, spingi le modifiche e tag all'origine.

Un frammento di codice di esempio:

Supponendo che tu sia un maestro se fatto manualmente. Tuttavia su jenkins, quando effettui il checkout del repository sarai sul ramo predefinito (master se configurato).

git pull  // Just to pull any changes.
git branch local-<commitd-id> <commit-id>  // Create a branch from the given commit-id
git merge local-<commit-id>  // Merge that local branch to master.
mvn clean verify   // Verify if the code is build able
mvn <any args> release:clean release:prepare release:perform // Release artifacts
git push origin/master  // Push the local changes performed above to origin.
git push origin <tag>  // Push the tag to origin

Questo ti darà il pieno controllo con una fusione senza paura o un inferno di conflitto.

Sentiti libero di avvisare nel caso in cui ci sia qualche opzione migliore.


3

Le risposte principali descrivono come applicare le modifiche da un commit specifico al ramo corrente. Se questo è ciò che intendi per "come unire", usa semplicemente cherry-pick come suggeriscono.

Ma se in realtà vuoi unire , cioè vuoi un nuovo commit con due genitori - il commit esistente sul ramo corrente e il commit da cui vuoi applicare le modifiche - allora una scelta ciliegia non lo realizzerà.

Avere una vera storia di merge può essere desiderabile, ad esempio, se il tuo processo di compilazione sfrutta i vantaggi di git ancestry per impostare automaticamente le stringhe di versione in base all'ultimo tag (usando git describe).

Invece di cherry-pick, puoi fare un vero git merge --no-commite proprio , quindi regolare manualmente l'indice per rimuovere tutte le modifiche che non desideri.

Supponiamo di essere sul ramo Ae di voler unire il commit sulla punta del ramo B:

git checkout A
git merge --no-commit B

Ora sei pronto per creare un commit con due genitori, il suggerimento attuale è di Ae B. Tuttavia, è possibile che vengano applicate più modifiche di quanto si desideri, comprese le modifiche da precedenti commit nel ramo B. È necessario annullare queste modifiche indesiderate, quindi eseguire il commit.

(Potrebbe esserci un modo semplice per riportare lo stato della directory di lavoro e l'indice come prima della fusione, in modo da avere una lavagna pulita su cui scegliere il commit desiderato in primo luogo. Ma Non so come raggiungere quella tabula rasa git checkout HEADe git reset HEADrimuoverò entrambi lo stato di unione, vanificando lo scopo di questo metodo.)

Quindi annulla manualmente le modifiche indesiderate. Ad esempio, potresti

git revert --no-commit 012ea56

per ogni commit indesiderato 012ea56.

Al termine delle modifiche, crea il tuo commit:

git commit -m "Merge in commit 823749a from B which tweaked the timeout code"

Ora hai solo il cambiamento che volevi e l'albero degli antenati mostra che tecnicamente ti sei unito a B.

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.