Supponiamo di avere la seguente cronologia di commit sul mio ramo solo locale:
A -- B -- C
Come inserisco un nuovo commit tra A
e B
?
Supponiamo di avere la seguente cronologia di commit sul mio ramo solo locale:
A -- B -- C
Come inserisco un nuovo commit tra A
e B
?
Risposte:
È ancora più facile che nella risposta di OP.
git rebase -i <any earlier commit>
. Questo visualizza un elenco di commit nell'editor di testo configurato.a1b2c3d
). Nel tuo editor, per quella riga, cambia pick
in edit
.a1b2c3d
) come se fosse stato appena eseguito il commit .git commit
( NON modificare, a differenza della maggior parte dei messaggi edit
). Questo crea un nuovo commit dopo quello che hai scelto.git rebase --continue
. Questo riproduce i commit successivi, lasciando il tuo nuovo commit inserito nella posizione corretta.Fai attenzione che questo riscriverà la storia e distruggerà chiunque cerchi di tirare.
A -- B -- C -- D
invece che desiderata A -- D -- B -- C
.
D
potrebbe essere un impegno ovunque. Supponiamo di avere A - B - C
e di avere qualche commit D
che non è nemmeno in questo ramo. Tuttavia, sappiamo che è SHA, possiamo farlo git rebase -i HEAD~3
. Ora tra le righe A
e B
pick
, inseriamo una nuova pick
riga che dice pick SHA
, dando l'hash del desiderato D
. Non è necessario che sia l'hash completo, ma solo quello abbreviato. git rebase -i
seleziona semplicemente qualsiasi commit elencato per pick
linee nel buffer; non devono essere quelli originali elencati per te.
break
parola chiave nell'editor sulla propria riga tra due commit (o sulla prima riga, per inserire un commit prima del commit specificato).
Risulta essere abbastanza semplice, la risposta la trovate qui . Supponi di essere su un ramo branch
. Eseguire questi passaggi:
creare un ramo temporaneo dal commit dopo aver inserito il nuovo commit (in questo caso commit A
):
git checkout -b temp A
eseguire le modifiche e eseguirne il commit, creando un commit, chiamiamolo N
:
git commit -a -m "Message"
(o git add
seguito da git commit
)
rebase i commit che vuoi avere dopo il nuovo commit (in questo caso commit B
e C
) sul nuovo commit:
git rebase temp branch
(forse è necessario utilizzare -p
per preservare le unioni, se ce ne fossero, grazie a un commento non più esistente di ciekawy )
eliminare il ramo temporaneo:
git branch -d temp
Dopo questo, la cronologia appare come segue:
A -- N -- B -- C
È ovviamente possibile che durante il rebasing compaiano alcuni conflitti.
Nel caso in cui il tuo ramo non sia solo locale, questo introdurrà la riscrittura della cronologia, quindi potrebbe causare seri problemi.
git push --force
modificare il repository remoto.
git rebase temp branch -Xtheirs
. Risposta utile per l'iniezione in uno script!
git rebase temp branch
, ma prima git branch -d temp
, tutto ciò che devi fare è sistemare e mettere in scena conflitti e problemi di fusione git rebase --continue
, cioè non c'è bisogno di impegnare nulla, ecc.
Soluzione ancora più semplice:
Crea il tuo nuovo commit alla fine, D.Ora hai:
A -- B -- C -- D
Quindi esegui:
$ git rebase -i hash-of-A
Git aprirà il tuo editor e sarà simile a questo:
pick 8668d21 B
pick 650f1fc C
pick 74096b9 D
Sposta semplicemente D in alto in questo modo, quindi salva ed esci
pick 74096b9 D
pick 8668d21 B
pick 650f1fc C
Ora avrai:
A -- D -- B -- C
Supponendo che la cronologia del commit sia preA -- A -- B -- C
, se si desidera inserire un commit tra A
e B
, i passaggi sono i seguenti:
git rebase -i hash-of-preA
Git aprirà il tuo editor. Il contenuto potrebbe essere questo:
pick 8668d21 A
pick 650f1fc B
pick 74096b9 C
Cambia il primo pick
in edit
:
edit 8668d21 A
pick 650f1fc B
pick 74096b9 C
Salva ed esci.
Modifica il tuo codice e poi git add . && git commit -m "I"
git rebase --continue
Ora la cronologia dei commit di Git è preA -- A -- I -- B -- C
Se incontri un conflitto, Git si fermerà a questo commit. È possibile utilizzare git diff
per individuare i marcatori di conflitto e risolverli. Dopo aver risolto tutti i conflitti, devi usare git add <filename>
per dire a Git che il conflitto è stato risolto e quindi rieseguire git rebase --continue
.
Se vuoi annullare il rebase, usa git rebase --abort
.
Ecco una strategia che evita di fare un "edit hack" durante il rebase visto nelle altre risposte che ho letto.
Usando git rebase -i
si ottiene un elenco di commit da quel commit. Basta aggiungere una "interruzione" all'inizio del file, questo farà sì che il rebase si interrompa a quel punto.
break
pick <B's hash> <B's commit message>
pick <C's hash> <C's commit message>
Una volta lanciato, git rebase
ora si fermerà al punto di "rottura". Ora puoi modificare i tuoi file e creare normalmente il tuo commit. È quindi possibile continuare il rebase con git rebase --continue
. Ciò potrebbe causare conflitti che dovrai risolvere. Se ti perdi, non dimenticare che puoi sempre interromperne l'uso git rebase --abort
.
Questa strategia può essere generalizzata per inserire un commit ovunque, basta mettere il "break" nel punto in cui si desidera inserire un commit.
Dopo aver riscritto la storia, non dimenticare di farlo git push -f
. Si applicano i soliti avvertimenti su altre persone che recuperano il tuo ramo.
rebase
qui. Non fa molta differenza se creerai il commit durante il rebase o prima.
Molte buone risposte già qui. Volevo solo aggiungere una soluzione "no rebase", in 4 semplici passaggi.
Sommario
git checkout A
git commit -am "Message for commit D"
git cherry-pick A..C
git branch -f master HEAD
Spiegazione
(Nota: un vantaggio di questa soluzione è che non tocchi il tuo ramo fino al passaggio finale, quando sei sicuro al 100% di essere OK con il risultato finale, quindi hai un passaggio di "pre-conferma" molto pratico consentendo il test AB .)
Stato iniziale (ho ipotizzato master
per il nome del tuo ramo)
A -- B -- C <<< master <<< HEAD
1) Inizia puntando la TESTA nel punto giusto
git checkout A
B -- C <<< master
/
A <<< detached HEAD
(Facoltativamente qui, invece di scollegare HEAD, avremmo potuto creare un ramo temporaneo con git checkout -b temp A
, che avremmo dovuto eliminare alla fine del processo. Entrambe le varianti funzionano, fai come preferisci poiché tutto il resto rimane lo stesso)
2) Creare il nuovo commit D da inserire
# at this point, make the changes you wanted to insert between A and B, then
git commit -am "Message for commit D"
B -- C <<< master
/
A -- D <<< detached HEAD (or <<< temp <<< HEAD)
3) Quindi porta le copie degli ultimi commit mancanti B e C (sarebbe la stessa riga se ci fossero più commit)
git cherry-pick A..C
# (if any, resolve any potential conflicts between D and these last commits)
B -- C <<< master
/
A -- D -- B' -- C' <<< detached HEAD (or <<< temp <<< HEAD)
(comodo AB Testing qui se necessario)
Ora è il momento di ispezionare il tuo codice, testare tutto ciò che deve essere testato e puoi anche diff / confrontare / ispezionare ciò che avevi e ciò che avresti ottenuto dopo le operazioni.
4) A seconda dei tuoi test tra C
e C'
, o è OK o è KO.
(ENTRAMBI) 4-OK) Infine, sposta il riferimento dimaster
git branch -f master HEAD
B -- C <<< (B and C are candidates for garbage collection)
/
A -- D -- B' -- C' <<< master
(OR) 4-KO) Vattene e bastamaster
invariato
Se hai creato un ramo temporaneo, eliminalo semplicemente con git branch -d <name>
, ma se hai scelto la rotta HEAD scollegata, nessuna azione necessaria a questo punto, i nuovi commit saranno idonei per la raccolta dei rifiuti subito dopo che ti sarai ricollegatoHEAD
con ungit checkout master
In entrambi i casi (OK o KO), a questo punto basta fare di master
nuovo il checkout per ricollegarlo HEAD
.