2009-11-12 15 views
49

Volevo avere una soluzione semplice per mettere insieme due commit di unione durante un rebase interattivo.git rebase interattivo: l'unione di squash si impegna insieme

mio repository sembra:

X --- Y --------- M1 -------- M2 (my-feature) 
/    /  /
/    /  /
a --- b --- c --- d --- e --- f (stable) 

Cioè, ho un ramo my-feature che è stata fusa per due volte di recente, senza alcun reale impegna in mezzo. Io non voglio solo rebase al ramo my-feature dal momento che è un ramo pubblicato proprio, voglio solo schiacciare insieme gli ultimi due si fondono impegna in uno (non hanno ancora pubblicato i commit)

X --- Y ---- M (my-feature) 
/  /
/  /
a --- ... -- f (stable) 

ho provato:

git rebase -p -i M1^ 

Ma ho ottenuto:

Refusing to squash a merge: M2 

Quello che ho finalmente fatto è:

git checkout my-feature 
git reset --soft HEAD^ # remove the last commit (M2) but keep the changes in the index 
git commit -m toto  # redo the commit M2, this time it is not a merge commit 
git rebase -p -i M1^ # do the rebase and squash the last commit 
git diff M2 HEAD  # test the commits are the same 

Ora, il nuovo commit di unione non è più considerato un commit di unione (ha mantenuto solo il primo elemento principale). Quindi:

git reset --soft HEAD^    # get ready to modify the commit 
git stash       # put away the index 
git merge -s ours --no-commit stable # regenerate merge information (the second parent) 
git stash apply      # get the index back with the real merge in it 
git commit -a      # commit your merge 
git diff M2 HEAD      # test that you have the same commit again 

Ma questo può complicarsi se ho molti commit, avete una soluzione migliore? Grazie.

Mildred

+0

Beh, quando si fai la tua seconda unione, puoi sempre usare '--squash' per evitare di creare un commit, e quindi usare' git commit --amend' per modificare la precedente unione. – Mildred

+0

Questo non funzionerà, non salverà la nuova versione del ramo da cui è stato effettuato il commit nel commit – Mildred

risposta

8

se non avete pubblicato gli ultimi due merge impegna, si potrebbe fare un reset e un semplice unione.

git reset --hard Y 
git merge stable 
+2

Sì, ma l'unione è stata difficile, preferirei unire il minor numero possibile di modifiche. Non voglio risolvere i conflitti che ho già risolto. – Mildred

+9

se non si desidera risolvere nuovamente i conflitti, è necessario "utilizzare" git-rerere (e con "si intende" si intende realmente "l'attivazione" perché le maniglie di git correggono automaticamente identici conflitti una volta che questo è abilitato). –

47

Questo è un vecchio argomento, ma l'ho appena trovato mentre cercavo informazioni simili.

Un trucco simile a quello descritto nel Subtree octopus merge è davvero una buona soluzione a questo tipo di problema:

git checkout my-feature 
git reset --soft Y 
git rev-parse f > .git/MERGE_HEAD 
git commit 

che porterà l'indice come esiste sulla punta della mia-funzione, e usarlo per creare un nuovo commit off di Y, con 'f' come secondo genitore. Il risultato è lo stesso come se non avessi mai eseguito M1, ma è andato direttamente all'esecuzione di M2.

+0

Nota che questo tipo di unione non ha nulla a che fare con una fusione di sottostrutture o polpi. Il blog che colleghi usa semplicemente la tecnica per combinare * un'unione di sottostrati e un'unione di polpo in un commit di unione (poiché git non può eseguire entrambe le fusioni in un colpo solo). – sleske

+0

Lo svantaggio di questo è che git non sarà in grado di generare messaggi di commit corretti. Ho copiato i messaggi dai vecchi commit di unione. Altrimenti una soluzione piacevole e facile. –

+0

così tanto in alto! abbiamo appena fatto una fusione molto difficile e il mio approccio ai colleghi è stato quello di unire ogni commit dal suo albero al mio, uno alla volta. poi è arrivato lo schiacciamento e abbiamo trovato questa soluzione. è un peccato che tu abbia bisogno di scrivere il tuo messaggio di commit. – Sam

0

Nessuno dei metodi menzionati funziona per me con una versione git recente. Nel mio caso il seguente ha fatto il trucco:

git reset --soft Y 
git reset --hard $(git commit-tree $(git write-tree) -p HEAD -p stable < commit_msg) 

Dovrete scrivere il messaggio impegnarsi per il file commit_msg prima, però.

5

Sono giunto a questo argomento volendo eseguire lo schiacciamento di un singolo commit di unione; quindi la mia risposta non è così utile alla domanda originale.

   X 
       \ 
       \ 
a --- b --- c --- M1 (subtree merge) 

Quello che volevo era quello di rebase l'unione M1 e squash tutto come un unico impegnarsi in cima b.

a --- b --- S (include the changes from c, X and M1) 

Ho provato tutti i tipi di combinazioni diverse, ma questo è ciò che ha funzionato:

git checkout -b rebase b (checkout a working branch at point b) 
git merge --squash M1 

Ciò applicare le modifiche nell'indice in cui possono essere commessi git commit

+1

In questo caso, si può fare 'git diff b> diff.patch', quindi' git checkout b', 'cat diff.patch | patch -p1' e poi 'git commit'. Funziona se * unisci * include risoluzioni. La domanda originale è diversa; ma penso che tu sia venuto qui cercando la stessa cosa di me. È possibile ottenere prima i messaggi di check-in con 'git log'. –

+0

passaggi aggiuntivi necessari se vuoi avere la situazione sul tuo master: 'git checkout master && git reset --hard b && git rebase rebase'. Questo è solo per recuperare me stesso. Potresti aver scelto un altro nome per il ramo piuttosto che "rebase" :) – eis

Problemi correlati