Rebasing di un commit di merge Git


183

Prendi il seguente caso:

Ho un po 'di lavoro in una sezione tematica e ora sono pronto per ricollegarmi al master:

* eb3b733 3     [master] [origin/master]
| * b62cae6 2   [topic]
|/  
* 38abeae 1

Eseguo l'unione dal master, risolvo i conflitti e ora ho:

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | eb3b733 3                     [origin/master]
|/  
* 38abeae 1

Ora, l'unione mi ha richiesto del tempo, quindi faccio un altro recupero e noto che il ramo principale remoto ha nuove modifiche:

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
| | * e7affba 4                   [origin/master]
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Se provo 'git rebase origin / master' dal master, sono costretto a risolvere nuovamente tutti i conflitti e perdo anche il commit di unione:

* d4de423 2       [master]
* e7affba 4       [origin/master]
* eb3b733 3
| * b62cae6 2     [topic]
|/  
* 38abeae 1

Esiste un modo chiaro per reimpostare il commit di unione in modo da finire con una cronologia come quella mostrata di seguito?

*   51984c7 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | e7affba 4                     [origin/master]
* | eb3b733 3
|/  
* 38abeae 1

74
TL; DR:git rebase --preserve-merges origin/master
Ilia K.

6
Per quanto riguarda la necessità di risolvere nuovamente i conflitti, potresti dare un'occhiata a git rerere .
Parker Coates,

git config --global pull.rebase preserve per preservare sempre i merge di commit durante un rebase
galath

4
Attenzione: a partire da Git 2.18 (Q2 2018, 5 anni dopo), git --rebase-mergesalla fine sostituirà il vecchio git --preserve-merges. Vedi cosa fa esattamente " rebase --preserve-merges" (e perché?) Di
Git

1
--preserve-mergesè deprecato. Usagit rebase --rebase-merges origin/master
Arjun Sreedharan l'

Risposte:


127

Ci sono due opzioni qui.

Uno è fare un rebase interattivo e modificare il commit di unione, ripetere manualmente l'unione e continuare il rebase.

Un altro è usare l' --rebase-mergesopzione on git rebase, che è descritta come segue dal manuale: "Per impostazione predefinita, un rebase eliminerà semplicemente i commit di merge dall'elenco dei todo e metterà i commit di rebased in un singolo ramo lineare. Con --rebase- si fonde, il rebase tenterà invece di preservare la struttura di diramazione all'interno dei commit che devono essere riformulati, ricreando i commit di unione. Eventuali conflitti di unione risolti o modifiche manuali in questi commit di unione dovranno essere risolti / riapplicati manualmente. "


16
Ho provato l'opzione -p, e in effetti lascia la cronologia di commit come volevo, ma mi costringe a risolvere nuovamente i conflitti, anche in file che non sono stati modificati in origin / master. A partire dal tuo primo suggerimento, quale sarebbe la sequenza precisa di comandi?
jipumarino,

2
@jipumarino: git rebase -i (digli di modificare il commit di unione), quando arriva al commit di unione, git reset --hard HEAD ^, git merge, correggi conflitti, git commit, git rebase --continua. Potresti anche voler guardare Git Rerere che dovrebbe aiutare con questo tipo di cose (ma non l'ho mai usato, quindi non posso offrire alcun consiglio o aiuto).
siride,

2
Grazie. Ho abilitato rerere e provato con rebase -p e funziona come dovrebbe.
jipumarino,

3
Ecco un eccellente post sul blog che descrive questa situazione esatta: Rebasing Merge Commits in Git
kynan,

1
rere non è la soluzione, in quanto è ancora necessario risolvere manualmente le fusioni la prima volta.
Flimm,

29

Ok, questa è una vecchia domanda e ha già accettato la risposta @siride, ma quella risposta non è stata sufficiente nel mio caso, poiché --preserve-mergesti costringe a risolvere tutti i conflitti una seconda volta. La mia soluzione basata sull'idea @Tobi Bma con esatti comandi passo-passo

Quindi inizieremo su tale stato in base all'esempio nella domanda:

*   8101fe3 Merge branch 'topic'  [HEAD -> master]
|\  
| * b62cae6 2                     [topic]
| |
| | * f5a7ca8 5                   [origin/master]
| | * e7affba 4
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Si noti che abbiamo 2 commit in anticipo master, quindi cherry-pick non funzionerebbe.

  1. Prima di tutto, creiamo la cronologia corretta che vogliamo:

    git checkout -b correct-history # create new branch to save master for future
    git rebase --strategy=ours --preserve-merges origin/master
    

    Usiamo --preserve-mergesper salvare il nostro commit di unione nella cronologia. Usiamo --strategy=oursper ignorare tutti i conflitti di unione poiché non ci interessa quali contenuti saranno presenti in quel commit di unione, abbiamo solo bisogno di una bella storia ora.

    La storia apparirà così (ignorando il maestro):

    *   51984c7 Merge branch 'topic'  [HEAD -> correct-history]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1
    
  2. Otteniamo l'indice corretto ora.

    git checkout master # return to our master branch
    git merge origin/master # merge origin/master on top of our master
    

    Potremmo ottenere alcuni conflitti di unione aggiuntivi qui, ma si tratterebbe solo di conflitti tra file cambiati tra 8101fe3e f5a7ca8, ma non include conflitti già risolti datopic

    La storia sarà simile a questa (ignorando la cronologia corretta):

    *   94f1484 Merge branch 'origin/master'  [HEAD -> master]
    |\  
    * | f5a7ca8 5                   [origin/master]
    * | e7affba 4
    | *   8101fe3 Merge branch 'topic'
    | |\  
    | | * b62cae6 2                     [topic]
    |/ /
    * / eb3b733 3
    |/  
    * 38abeae 1
    
  3. L'ultima fase è quella di combinare la nostra filiale con la cronologia corretta e la filiale con l'indice corretto

    git reset --soft correct-history
    git commit --amend
    

    Usiamo reset --softper reimpostare il nostro ramo (e la cronologia) su cronologia corretta, ma lascia l'indice e l'albero di lavoro così come sono. Quindi usiamo commit --amendper riscrivere il nostro commit di unione, che aveva un indice errato, con il nostro buon indice dal master.

    Alla fine avremo tale stato (notare un altro ID di top commit):

    *   13e6d03 Merge branch 'topic'  [HEAD -> master]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1
    

Questo è fantastico e mi ha aiutato molto! Ma non ho capito il trucco con commit --amend: potresti aggiungere ulteriori informazioni a riguardo? Cosa succede esattamente dopo averlo eseguito - ho notato che SHA di commit è stato modificato - ma perché? O cosa succede se non esegui questo?
ZenJ,

1
@ZenJ git commit --amendaggiunge le modifiche all'ultimo commit (HEAD, in questo caso il commit di unione). Poiché il contenuto del commit cambia, l'hash viene aggiornato.
Dries Staelens,

1
Per le persone che non hanno "rerere" abilitato prima di risolvere i conflitti, questa soluzione è eccezionale perché ti evita di dover risolvere nuovamente i conflitti. Grazie!
Shackleford,

6

Dato che ho appena perso un giorno cercando di capire questo e in realtà ho trovato una soluzione con l'aiuto di un collega, ho pensato di dover intervenire.

Abbiamo una grande base di codice e dobbiamo occuparci di 2 rami pesantemente modificati allo stesso tempo. C'è un ramo principale e un ramo secondario se tu quale.

Mentre unisco il ramo secondario nel ramo principale, il lavoro continua nel ramo principale e quando ho finito, non posso inviare le modifiche perché sono incompatibili.

Ho quindi bisogno di "riformulare" la mia "unione".

Ecco come l'abbiamo finalmente fatto:

1) prendere nota dello SHA. es .: c4a924d458ea0629c0d694f1b9e9576a3ecf506b

git log -1

2) Crea la cronologia corretta ma questo interromperà l'unione.

git rebase -s ours --preserve-merges origin/master

3) prendere nota dello SHA. es .: 29dd8101d78

git log -1

4) Ora ripristina dove eri prima

git reset c4a924d458ea0629c0d694f1b9e9576a3ecf506b --hard

5) Ora unisci l'attuale master nel tuo ramo di lavoro

git merge origin/master
git mergetool
git commit -m"correct files

6) Ora che hai i file giusti, ma la cronologia sbagliata, ottieni la cronologia giusta in cima alla tua modifica con:

git reset 29dd8101d78 --soft

7) E quindi - modifica i risultati nel commit di unione originale

git commit --amend

Ecco!


1

Sembra che quello che vuoi fare sia rimuovere la tua prima unione. È possibile seguire la seguente procedura:

git checkout master      # Let's make sure we are on master branch
git reset --hard master~ # Let's get back to master before the merge
git pull                 # or git merge remote/master
git merge topic

Questo ti darebbe quello che vuoi.


4
Con rerere abilitato, questo sembra dare lo stesso risultato della soluzione rebase -p data sopra da siride.
jipumarino,

0
  • Dalla tua unione commetti
  • Scegli la nuova modifica che dovrebbe essere facile
  • copia le tue cose
  • ripetere l'unione e risolvere i conflitti semplicemente copiando i file dalla copia locale;)

1
Questa risposta sembra buona ma sarebbe più utile se avessi dato i comandi git effettivi, nel caso in cui un utente sia nuovo a git
Louise Davies
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.