Come inserire un commit tra alcuni due commit arbitrari in passato?


Risposte:


178

È ancora più facile che nella risposta di OP.

  1. git rebase -i <any earlier commit>. Questo visualizza un elenco di commit nell'editor di testo configurato.
  2. Trova il commit che vuoi inserire dopo (supponiamo che sia a1b2c3d). Nel tuo editor, per quella riga, cambia pickin edit.
  3. Inizia il rebase chiudendo il tuo editor di testo (salva le modifiche). Questo ti lascia al prompt dei comandi con il commit che hai scelto in precedenza ( a1b2c3d) come se fosse stato appena eseguito il commit .
  4. Apporta le tue modifiche e git commit( NON modificare, a differenza della maggior parte dei messaggi edit). Questo crea un nuovo commit dopo quello che hai scelto.
  5. 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.


1
Questo ha aggiunto il nuovo commit dopo il commit, che è dopo quello su cui stavo ribasando (anche l'ultimo commit), invece che subito dopo quello su cui stavo ribasando. Il risultato è stato lo stesso che se avessi semplicemente fatto un nuovo commit alla fine con le modifiche che volevo inserire. La mia storia è diventata A -- B -- C -- Dinvece che desiderata A -- D -- B -- C.
XedinUnknown

2
@XedinUnknown: Allora non hai usato Rebase correttamente.
SLaks

3
Ora Dpotrebbe essere un impegno ovunque. Supponiamo di avere A - B - Ce di avere qualche commit Dche non è nemmeno in questo ramo. Tuttavia, sappiamo che è SHA, possiamo farlo git rebase -i HEAD~3. Ora tra le righe Ae 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 -iseleziona semplicemente qualsiasi commit elencato per picklinee nel buffer; non devono essere quelli originali elencati per te.
Kaz

1
@Kaz Questa sembra una risposta diversa e valida.
BartoszKP

3
Ancora più semplice, puoi usare la breakparola chiave nell'editor sulla propria riga tra due commit (o sulla prima riga, per inserire un commit prima del commit specificato).
SimonT

30

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 addseguito da git commit)

  • rebase i commit che vuoi avere dopo il nuovo commit (in questo caso commit Be C) sul nuovo commit:

    git rebase temp branch
    

(forse è necessario utilizzare -pper 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.


2
Non sono riuscito a seguire la risposta accettata da SLaks, ma per me ha funzionato. Dopo aver ottenuto la cronologia di commit che volevo, ho dovuto git push --forcemodificare il repository remoto.
Escapecharacter

1
Quando si usa rebase usando l'opzione -Xtheirs auto risolve correttamente i conflitti, quindi git rebase temp branch -Xtheirs. Risposta utile per l'iniezione in uno script!
David C

Per i niubbi come me vorrei aggiungerlo dopo 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.
Pugsley

19

Soluzione ancora più semplice:

  1. Crea il tuo nuovo commit alla fine, D.Ora hai:

    A -- B -- C -- D
    
  2. Quindi esegui:

    $ git rebase -i hash-of-A
    
  3. Git aprirà il tuo editor e sarà simile a questo:

    pick 8668d21 B
    pick 650f1fc C
    pick 74096b9 D
    
  4. Sposta semplicemente D in alto in questo modo, quindi salva ed esci

    pick 74096b9 D
    pick 8668d21 B
    pick 650f1fc C
    
  5. Ora avrai:

    A -- D -- B -- C
    

6
Bella idea, tuttavia potrebbe essere difficile introdurre D su C, quando si intende che queste modifiche vengano apportate. ad A.
BartoszKP

Ho una situazione in cui ho 3 commit che voglio rebase insieme e un commit nel mezzo che non è correlato. È super bello poter spostare quel commit prima o poi lungo la linea dei commit.
unflores

13

Supponendo che la cronologia del commit sia preA -- A -- B -- C, se si desidera inserire un commit tra Ae B, i passaggi sono i seguenti:

  1. git rebase -i hash-of-preA

  2. Git aprirà il tuo editor. Il contenuto potrebbe essere questo:

    pick 8668d21 A
    pick 650f1fc B
    pick 74096b9 C
    

    Cambia il primo pickin edit:

    edit 8668d21 A
    pick 650f1fc B
    pick 74096b9 C
    

    Salva ed esci.

  3. Modifica il tuo codice e poi git add . && git commit -m "I"

  4. 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 diffper 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.


11

Ecco una strategia che evita di fare un "edit hack" durante il rebase visto nelle altre risposte che ho letto.

Usando git rebase -isi 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 rebaseora 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.


Mi dispiace, ma ho difficoltà a capire come si "evita il rebase". Si sta eseguendo rebasequi. Non fa molta differenza se creerai il commit durante il rebase o prima.
BartoszKP

Woops, intendevo evitare il "edit hack" durante il rebase, immagino di averlo espresso male.
axerologementy

Destra. La mia risposta inoltre non utilizza la funzione "modifica" di rebase. Tuttavia, questo è ancora un altro approccio valido - grazie! :-)
BartoszKP

Questa è di gran lunga la soluzione migliore!
Theodore R. Smith

6

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 masterper 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 Ce 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 masternuovo il checkout per ricollegarlo HEAD.

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.