Come schiaccio due commit non consecutivi?


183

Sono un po 'nuovo per l'intera funzione di rebasing all'interno di git. Diciamo che ho fatto i seguenti commit:

A -> B -> C -> D

Successivamente, mi rendo conto che Dcontiene una correzione che dipende da un nuovo codice aggiunto Ae che questi commit appartengono insieme. Come posso schiacciare Ae stare Dinsieme e partire Be Csolo?

Risposte:


258

Puoi eseguire git rebase --interactivee riordinare D prima di B e schiacciare D in A.

Git aprirà un editor e vedrai un file come questo, ad esempio: git rebase --interactive HEAD~4

pick aaaaaaa Commit A
pick bbbbbbb Commit B
pick ccccccc Commit C
pick ddddddd Commit D

# Rebase aaaaaaa..ddddddd onto 1234567 (4 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Ora cambi il file in questo modo:

pick aaaaaaa Commit A
squash ddddddd Commit D
pick bbbbbbb Commit B
pick ccccccc Commit C

E git ora combinerà i cambiamenti di A e D in un unico commit e inserirà successivamente B e C. Quando non si desidera mantenere il messaggio di commit di D, anziché squashutilizzare la fixupparola chiave. Per ulteriori informazioni fixup, puoi consultare i git rebasedocumenti o consultare questa domanda che ha delle buone risposte.


5
Inizialmente, l'ho letto come "rebase D su A, schiaccia D in A, quindi rebase B su DA". Non è chiaro dalla risposta che ciò può essere fatto riordinando le righe in un editor di testo.
Victor Sergienko,

3
Se la tua filiale è locale, otterrai un There is no tracking information for the current brancherrore durante il rebasing. In questo caso è necessario specificare il numero di commit si desidera lavorare con, in questo modo: git rebase -i HEAD~4. Vedere questa risposta .
johndodo,

1
Uso la modalità interattiva (git rebase -i) per anni, ho appena realizzato che può essere riordinato . Grazie 🤟🏻
CalvinChe,

43

Nota: non è necessario modificare gli commit che sono stati trasferiti in un altro repository in alcun modo se non si conoscono le conseguenze .

git log --oneline -4

D commit_message_for_D
C commit_message_for_C
B commit_message_for_B
A commit_message_for_A

git rebase --interactive

pick D commit_message_for_D
pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A

Tipo i(Metti VIM in modalità inserimento)

Modificare l'elenco in questo modo (non è necessario rimuovere o includere il messaggio di commit). Non sbagliare squash! :

pick C commit_message_for_C
pick B commit_message_for_B
pick A commit_message_for_A
squash D

Digita Escquindi ZZ(Salva ed esci da VIM)

# This is a combination of 2 commits.
# The first commit's message is:

commit_message_for_D

# This is the 2nd commit message:

commit_message_for_A

genere i

Cambia il testo in quello che vuoi che assomigli al nuovo messaggio di commit. Raccomando che questa sia una descrizione delle modifiche in commit Ae D:

new_commit_message_for_A_and_D

Digita EscquindiZZ

git log --oneline -4

E new_commit_message_for_A_and_D
C commit_message_for_C
B commit_message_for_B

git show E

(You should see a diff showing a combination of changes from A and D)

Ora hai creato un nuovo commit E. Si impegna Ae Dnon è più nella tua storia ma non se ne sono andati. Puoi ancora recuperarli a questo punto e per un po 'di tempo git rebase --hard D( git rebase --harddistruggerà eventuali modifiche locali! ).


3

Per coloro che utilizzano SourceTree :

Assicurati di non aver già spinto le commit.

  1. Repository> Rebase interattivo ...
  2. Trascina D (il nuovo commit) per essere direttamente sopra A (il vecchio commit)
  3. Assicurati che il commit D sia evidenziato
  4. Clic Squash with previous

1

Il rebase interattivo funziona bene fino a quando non si dispone di un grande ramo di funzionalità con 20-30 commit e / o un paio di fusioni da master o / e risoluzione di conflitti mentre si stava eseguendo il commit nel proprio ramo. Anche trovando i miei impegni attraverso la storia e sostituendoli pickcon squashnon ha funzionato qui. Quindi stavo cercando un altro modo e ho trovato questo articolo . Ho apportato le mie modifiche per farlo funzionare su un ramo separato:

git checkout master
git fetch
git pull
git merge branch-name
git reset origin/master
git branch -D branch-name
git checkout -b branch-name
git add --all
#Do some commit
git push -f --set-upstream origin branch-name

Prima di questo ho ricevuto la mia richiesta pull con circa ~ 30 commit con 2-3 fusioni da conflitti master + fix. E dopo questo ho ottenuto le PR chiare con un solo commit.

PS qui è bash script per eseguire questa procedura in automode.


La prima soluzione in quell'articolo è davvero carina, grazie per il link
Hoody,

-1

$ git checkout master

$ git log --oneline

D
C
B
A

$ git rebase --onto HEAD ^^^ HEAD ^

$ git log --oneline

D
A

Penso che intendi --oneline? E sembra che tu sia caduto Ce B, che non è quello che l'OP intendeva.
bstpierre,

Non ha funzionato per me. Ha spostato sia il mio HEAD sia il master in A, ma non ha unito D in A ( git show A) e D, C e B sono stati persi nel mio registro. Dovevo git rebase Dtornare indietro.
Nate,
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.