Riordino dei commit


105

Attualmente sto lavorando su un ramo e desidero che alcuni commit si uniscano in altri rami:

    a-b-c-d-e-f-g (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)
   \
    x-x-x-x-x (branchB)

(Le lettere indicano commit e la "x" sono commit irrilevanti.)

Tuttavia ho notato che sarebbe una buona idea mettere insieme alcuni commit. Voglio "concatenare" il commit a, d, eeg in una patch e inviarlo a master. I commit b e f dovrebbero andare come un commit nel branchB. C'è un buon modo "git" per ottenerlo?


4
Le due risposte già fornite menzionano il comando corretto, ma penso che con un'attività di base come questa, valga la pena avere istruzioni reali qui su StackOverflow, quindi ne ho pubblicate alcune. (Sono sorpreso di non riuscire a trovare una buona domanda precedente a cui collegarsi.)
Cascabel

Risposte:


126

Il comando che stai cercando è git rebase, in particolare l' -i/--interactiveopzione.

Assumerò che tu voglia lasciare il commit c sul ramo A, e che tu intenda davvero spostare gli altri commit sugli altri rami, piuttosto che unire, poiché le unioni sono semplici. Cominciamo manipolando il ramo A.

git rebase -i <SHA1 of commit a>^ branchA

I ^mezzi il commit precedente, quindi questo comando dice al rebase ramo Un utilizzando il commit prima "a" come base. Git ti presenterà un elenco dei commit in questo intervallo. Riordinali e dì a git di schiacciare quelli appropriati:

pick c ...
pick a ...
squash d ...
squash e ...
squash g ...
pick b
squash f

Ora la cronologia dovrebbe apparire così:

    c - [a+d+e+g] - [b+f] (branchA)
   /
--o-x-x-x-x-x-x-x-x-x-x (master)

Ora, prendiamo il commit b + f appena schiacciato per branchB.

git checkout branchB
git cherry-pick branchA  # cherry-pick one commit, the tip of branchA

E lo stesso per a + d + e + g per master:

git checkout master
git cherry-pick branchA^

Infine, aggiorna branchA in modo che punti a c:

git branch -f branchA branchA^^

Ora dovremmo avere:

    c (branch A) - [a+d+e+g] - [b+f] (dangling commits)
   /
--o-x-x-x-x-x-x-x-x-x-x-[a+d+e+g] (master)
   \
    x-x-x-x-x-[b+f] (branchB)

Nota che se avessi più commit che volevi spostare tra i rami, potresti usare di nuovo il rebase (non interattivamente):

# create a temporary branch
git branch fromAtoB branchA
# move branchA back two commits
git branch -f branchA branchA~2
# rebase those two commits onto branchB
git rebase --onto branchB branchA fromAtoB
# merge (fast-forward) these into branchB
git checkout branchB
git merge fromAtoB
# clean up
git branch -d fromAtoB

Infine, una dichiarazione di non responsabilità: è del tutto possibile riordinare i commit in modo tale che alcuni non vengano più applicati in modo pulito. Ciò potrebbe essere dovuto al fatto che hai scelto un cattivo ordine (mettere una patch prima del commit che introduce la funzionalità patchata); in quel caso vorrai abortire il rebase ( git rebase --abort). Altrimenti, dovrai risolvere in modo intelligente i conflitti (proprio come fai con i conflitti di unione), aggiungere le correzioni, quindi correre git rebase --continueper andare avanti. Queste istruzioni vengono fornite anche dal messaggio di errore stampato quando si verifica il conflitto.


il diagramma non è git branch -f branchA branchA^^sbagliato? Vale a dire, branchA non dovrebbe puntare a c a questo punto, in modo che la prima riga del diagramma sia c (branchA)e non c - [a+d+e+g] - [b+f] (branchA)?
ntc2

@enoksrd: Sì, c'è un errore, ma la tua modifica conteneva degli errori. Lo aggiusterò quando avrò la possibilità qui.
Cascabel

Invece di fare, git branch -f branchA branchA^^perché non puoi semplicemente fare un git reset --hard <sha1 of c>ramo A?
Mikhail Vasilyev

17
git rebase -i HEAD~3

Dov'è 3il numero di commit che devono essere riordinati ( sorgente ).

Questo aprirà vi , elencando i commit dal più vecchio (in alto) al più recente (in basso).
Ora riordina le righe, salva e esci dall'editor.

ddpsposterà la riga corrente in basso
ddkP sposterà la riga corrente in alto ( sorgente )


9

git rebase --interactive è il comando che desideri.

Esempio:

Lo stato attuale è questo:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git status
On branch master
nothing to commit, working tree clean
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
a6e3c6a (HEAD -> master) Fourth commit
9a24b81 Third commit
7bdfb68 Second commit
186d1e0 First commit

Voglio riordinare i commit 9a24b81(Terzo commit) e 7bdfb68(Secondo commit). Per fare ciò, trovo prima il commit prima del primo commit che vogliamo modificare . Questo è commit 186d1e0(primo commit).

Il comando da eseguire è git rebase --interactive COMMIT-BEFORE-FIRST-COMMIT-WE-WANT-TO-CHANGE, in questo caso:

bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git rebase --interactive 186d1e0

Questo apre un file nell'editor predefinito con i seguenti contenuti:

pick 7bdfb68 Second commit
pick 9a24b81 Third commit
pick a6e3c6a Fourth commit

# Rebase 186d1e0..a6e3c6a onto 186d1e0 (3 commands)
#
# 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
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#

Nota che l'ordine dei commit nel file è opposto a quello usato per git log. In git log, il commit più recente è in alto. In questo file, il commit più recente è in fondo.

Come spiega il commento in fondo al file, ci sono varie cose che posso fare, come schiacciare, eliminare e riordinare i commit. Per riordinare i commit, modifico il file in modo che assomigli a questo (i commenti in basso non vengono visualizzati):

pick 9a24b81 Third commit
pick 7bdfb68 Second commit
pick a6e3c6a Fourth commit

Il pickcomando all'inizio di ogni riga significa "usa (cioè includi) questo commit" e quando salvo il file temporaneo con i comandi rebase ed esco dall'editor, git eseguirà i comandi e aggiornerà il repository e la directory di lavoro:

$ git rebase --interactive 186d1e0
Successfully rebased and updated refs/heads/master.
bernt@le3180:~/src/stackoverflow/reordering_of_commits
$ git log --oneline
ba48fc9 (HEAD -> master) Fourth commit
119639c Second commit
9716581 Third commit
186d1e0 First commit

Nota la cronologia del commit riscritta.

Collegamenti:


1
Aggiungi i dettagli pertinenti dal tuo link, per fornire una risposta completa e autonoma. SO disapprova le "risposte solo link". Nello specifico, come si utilizzerebbe git rebase --interactiveper raggiungere l'obiettivo di OP?
SherylHohman

Il secondo collegamento ora non esiste.
devsaw

Contenuto aggiunto per rendere la risposta completa e autonoma. Secondo collegamento rimosso.
codeape


-1

git rebase è quello che vuoi. Controlla l'opzione --interactive.


4
Aggiungi i dettagli pertinenti dal tuo link, per fornire una risposta completa e autonoma. SO disapprova le "risposte solo link".
SherylHohman,
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.