Come posso unire due commit in uno se ho già iniziato a rebase?


1159

Sto cercando di unire 2 commit in 1, quindi ho seguito "schiacciando commit con rebase" da git ready .

ho corso

git rebase --interactive HEAD~2

Nell'editor risultante, cambio picka squashe quindi save-quit, ma il rebase fallisce con l'errore

Impossibile "schiacciare" senza un precedente commit

Ora che il mio albero di lavoro ha raggiunto questo stato, ho problemi a recuperare.

Il comando ha git rebase --interactive HEAD~2esito negativo con:

Rifacimento interattivo già avviato

e git rebase --continuefallisce con

Impossibile "schiacciare" senza un precedente commit


22
Ho colpito anche questo. Il mio errore è stato causato dal fatto che git rebase -i elenca i commit nell'ordine opposto di git log; l'ultimo commit è in fondo!
lmsurprenant,


Risposte:


1734

Sommario

Il messaggio di errore

Impossibile "schiacciare" senza un precedente commit

significa che probabilmente hai tentato di "schiacciare verso il basso". Git annulla sempre un nuovo commit in un vecchio commit o "verso l'alto" come visualizzato nell'elenco todo rebase interattivo, ovvero in un commit su una riga precedente. La modifica del comando sulla prima riga dell'elenco di todo squashprodurrà sempre questo errore in quanto non vi è nulla per cui eseguire il primo commit.

La correzione

Per prima cosa torna da dove hai iniziato

$ git rebase --abort

Di 'che la tua storia è

$ git log --pretty=oneline
a931ac7c808e2471b22b5bd20f0cad046b1c5d0d c
b76d157d507e819d7511132bdb5a80dd421d854f b
df239176e1a2ffac927d8b496ea00d5488481db5 a

Cioè, a è stato il primo commit, quindi b e infine c. Dopo aver commesso c decidiamo di schiacciare b e c insieme:

(Nota: eseguendo il git logpipe il suo output in un cercapersone, lessper impostazione predefinita sulla maggior parte delle piattaforme. Per uscire dal cercapersone e tornare al prompt dei comandi, premere il qtasto.)

Running git rebase --interactive HEAD~2ti dà un editor con

pick b76d157 b
pick a931ac7 c

# Rebase df23917..a931ac7 onto df23917
#
# 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
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

(Notare che questo elenco di cose da fare è nell'ordine inverso rispetto all'output di git log.)

La modifica di b pickin squashcomporterà l'errore che hai visto, ma se invece schiacci c in b (il più recente impegna nel più vecchio o "schiaccia verso l'alto") cambiando la lista delle cose da fare in

pick   b76d157 b
squash a931ac7 c

e salvando il tuo editor, otterrai un altro editor i cui contenuti sono

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

b

# This is the 2nd commit message:

c

Quando si salva e si chiude, il contenuto del file modificato diventa un messaggio di commit del nuovo commit combinato:

$ git log --pretty=oneline
18fd73d3ce748f2a58d1b566c03dd9dafe0b6b4f b and c
df239176e1a2ffac927d8b496ea00d5488481db5 a

Nota sulla riscrittura della cronologia

Il rebase interattivo riscrive la storia. Il tentativo di spingere su un telecomando che contiene la cronologia precedente non riuscirà perché non è un avanzamento rapido.

Se il ramo che hai rigenerato è un argomento o ramo di una funzione in cui lavori da solo , non è un grosso problema. Il push su un altro repository richiederà l' --forceopzione, o in alternativa potresti essere in grado, a seconda delle autorizzazioni del repository remoto, di eliminare prima il vecchio ramo e poi di inviare la versione ridisegnata. Esempi di quei comandi che potenzialmente distruggeranno il lavoro non rientrano nell'ambito di questa risposta.

Riscrivere la storia già pubblicata su un ramo in cui si sta lavorando con altre persone senza molto buona ragione, come perdite di una password o altri dati sensibili forze di lavoro sui vostri collaboratori ed è antisociale e sarà infastidire altri sviluppatori. La sezione "Recupero da una ripresa a monte" nella git rebasedocumentazione spiega, con maggiore enfasi.

Rebasing (o qualsiasi altra forma di riscrittura) su un ramo su cui altri hanno basato il lavoro è una cattiva idea: chiunque a valle di esso è costretto a riparare manualmente la propria cronologia. Questa sezione spiega come eseguire la correzione dal punto di vista del downstream. La vera soluzione, tuttavia, sarebbe quella di evitare di reinserire l'upstream in primo luogo. ...


Se uso rebase per annullare un commit, viene creato un nuovo commit "combinato" contenente i due changeset ma l'hash è diverso. anche i commit originali sono conservati da Git?
fabsenet,

@fabsenet Sì e no. I commit originali sono ancora accessibili ma probabilmente non sono più raggiungibili da nessun riferimento (a seconda dei dettagli della tua storia). I commit senza referenze vengono infine espulsi attraverso il processo di garbage collection.
Greg Bacon,

stavo solo giocando ... l'ho fatto git log hashoftheoldcommite ha funzionato, ma ero curioso di vedere un git log --graphcon tutti questi impegni irraggiungibili inclusi
fabsenet,

questo squash è un buon strumento per organizzare i commit prima del push, ma se ne premo uno, non riesco a schiacciarlo? git afferma: HEAD distaccato e aggiornato con successo.
Sérgio,

Non sto ottenendo l'editor nella seconda istanza, il git bash, sembra bloccato in qualche processo. Cosa fare?

411

Se ci sono più commit, puoi usare git rebase -iper schiacciare due commit in uno.

Se si desidera unire solo due commit e questi sono i "due più recenti", è possibile utilizzare i seguenti comandi per combinare i due commit in uno solo:

git reset --soft "HEAD^"
git commit --amend

6
Qual è il rovescio della medaglia rispetto a rebase? Ne trovo uno molto più semplice da usare.
Guillaume86,

17
Non puoi unirti ad un ordine arbitrario - solo gli ultimi due commit .
dr0i,

50
@ dr0i Puoi unire tutti i commit che desideri, purché siano gli ultimi X commit e non da qualche parte nel mezzo. Basta eseguire git reset --soft HEAD~10, dove 10 è il numero di commit che si desidera unire.
fregante

2
Questo può essere usato se non hai un set di origini remoto e hai solo due commit.
Atedja,

8
È inoltre possibile reimpostare un commit specifico se non si desidera contare quanti ce ne sono HEADusando utilizzando git reset --soft 47b5c5...dove si 47b5c5...trova l'ID SHA1 del commit.
dguay,

112

Rebase: non ne avrai bisogno:

Un modo più semplice per lo scenario più frequente.

Nella maggior parte dei casi:

In realtà, se invece si è solo semplicemente combinare diversi commit recenti in uno , ma non hanno bisogno drop, reworde altri lavori rebase.

puoi semplicemente fare:

git reset --soft "HEAD~n"
  • Supponendo che ~nè il numero di commit a bassa voce non-commit (vale a dire ~1, ~2, ...)

Quindi, utilizzare il comando seguente per modificare il messaggio di commit.

git commit --amend

che è praticamente lo stesso di una lunga distanza di squashe uno pick.

E funziona per n commit, ma non solo per due commit come richiesto sopra.


3
Questo è utile se vuoi fare qualche ripulitura aggiuntiva oltre a schiacciare i commit, come rimuovere 1 commit nel mezzo o cambiare una riga di codice.
Styfle

1
Supponendo che ~nsia il numero di commit da disimpegnare dolcemente (ad es ~1. ~2, ...)
Ray

1
Che cosa succede se desidero unire non nultimi commit, ma ncommit nel mezzo? Posso farlo facilmente?
Chumakoff,

1
Quindi git rebase -iè quello che devi fare per squashlavorare. @chumakoff
pambda

3
Quindi, per unirti nagli impegni più recenti in un primo utilizzo git reset --soft @~m, dovem = n - 1
Łukasz Rajchel,

55

Per prima cosa dovresti controllare quanti commit hai:

git log

Esistono due stati:

Uno è che ci sono solo due commit:

Per esempio:

commit A
commit B

(In questo caso, non puoi usare git rebase per fare) che devi seguire.

$ git reset --soft HEAD^1

$ git commit --amend

Un altro è che ci sono più di due commit; vuoi unire il commit C e D.

Per esempio:

commit A
commit B
commit C
commit D

(in questa condizione, puoi usare git rebase)

git rebase -i B

E poi usa "squash" per fare. Il resto è molto semplice. Se ancora non lo sai, leggi http://zerodie.github.io/blog/2012/01/19/git-rebase-i/


Il reset --soft e commit --amend è l'unico modo che funziona se hai già un rebase in corso (e scegli 'modifica' invece di 'squash' per questo commit). +1
Jacek Lach,

1
Unendo il primo e il solo due commit in un repository, esattamente il mio caso limite :-)
Chris Huang-Leaver

1
Si prega di aggiungere che git push -f origin masterpotrebbe essere necessario.
Rishabh Agrahari

33

Supponendo che tu fossi nel tuo ramo argomento. Se vuoi unire gli ultimi 2 commit in uno e sembrare un eroe, dirama il commit appena prima di aver effettuato gli ultimi due commit.

git checkout -b temp_branch HEAD^2

Quindi squash impegna l'altro ramo in questo nuovo ramo:

git merge branch_with_two_commits --squash

Ciò porterà i cambiamenti ma non li impegnerà. Quindi commettili e il gioco è fatto.

git commit -m "my message"

Ora puoi unire nuovamente questo nuovo argomento nel tuo ramo principale.


5
Questa è stata in realtà la risposta più utile per me, perché non ha richiesto il rebasing manuale, ma riduce semplicemente tutti i commit di un intero ramo in un unico commit. Molto bella.
Robert,

Grazie per questo! Questo è come fare in modo che Git faccia come immagino lo schiacciare nella mia testa!
Marjan Venema,

risposta straordinaria, molto più semplice delle alternative

Apparentemente, questa risposta non è adatta al caso in cui ae cdeve essere unita e mantenuta bcosì com'è.
Talha Ashraf,

2
Qualcosa è cambiato nelle ultime versioni di git? Quando provo il primo comando ( git checkout -b combine-last-two-commits "HEAD^2") nella versione 2.17 di git, ricevo un errore:fatal: 'HEAD^2' is not a commit and a branch 'combine-last-two-commits' cannot be created from it
mhucka

23

puoi annullare il rebase con

git rebase --abort

e quando si esegue di nuovo il comando interattivo rebase 'squash; il commit deve essere al di sotto del pick commit nell'elenco


16

Uso spesso git reset --mixed per ripristinare una versione di base prima di più commit che desideri unire, quindi eseguo un nuovo commit, in questo modo potrei lasciare il tuo commit più recente, assicurandoti che la tua versione sia HEAD dopo aver spinto sul server.

commit ac72a4308ba70cc42aace47509a5e
Author: <me@me.com>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <me@me.com>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <ralph@me.com>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

Se voglio unire testa due commit in uno, per prima cosa uso:

git reset --mixed 249cf9392da197573a17c8426c282

"249cf9392da197573a17c8426c282" era la terza versione, inoltre è la versione di base prima di unire, dopodiché eseguo un nuovo commit:

git add .
git commit -m 'some commit message'

È tutto, la speranza è un altro modo per tutti.

Cordiali saluti, da git reset --help:

 --mixed
     Resets the index but not the working tree (i.e., the changed files are
     preserved but not marked for commit) and reports what has not been
     updated. This is the default action.

Non ho letto i documenti per "--mixed" ma sono sicuro che altre persone leggono il post e si chiedono la stessa cosa: qual è il vantaggio di usare --mixed? Potrebbe migliorare il tuo post per includere uno snippet della pagina man.
funroll

@funroll non lo sapevo - mescolato molto prima di scrivere questa risposta, secondo la mia esperienza, l' operazione mista trasformerà la versione specificata che passo come argomento come versione HEAD del repository e nulla può essere perso dopo quella versione, così possiamo ancora gestire quei cambiamenti.
VinceStyling,

14

$ git rebase --abort

Esegui questo codice in qualsiasi momento se desideri annullare git rebase

$ git rebase -i HEAD~2

Riapplicare gli ultimi due commit. Il comando sopra aprirà un editor di codice

  • [ L'ultimo commit sarà in fondo ]. Cambia l'ultimo commit in squash (s). Poiché la zucca si fonderà con il commit precedente.
  • Quindi premere il tasto esc e digitare: wq per salvare e chiudere

Dopo: wq sarai in modalità rebase attiva

Nota : otterrai un altro editor se non ci sono messaggi di avviso / errore. Se si verifica un errore o un avviso che non verrà visualizzato da un altro editor, potresti interrompere$ git rebase --aborteseguendorunnning se vedi un errore o un avviso altrimenti continua semplicemente eseguendo$ git rebase --continue

Vedrai il tuo messaggio di 2 commit. Scegline uno o scrivi il tuo messaggio di commit, salva ed esci [: wq]

Nota 2: potrebbe essere necessario forzare l'invio delle modifiche al repository remoto se si esegue il comando rebase

$ git push -f

$ git push -f origin master


1
Nota 2: git push -f origin/masterè ciò che mancano altre risposte. +1
Rishabh Agrahari

2

Dal momento che uso git cherry-pickper quasi tutto, per me diventa naturale farlo anche qui.

Dato che ho branchXverificato e ci sono due commit in punta di esso, di cui voglio creare un commit combinando il loro contenuto, lo faccio:

git checkout HEAD^ // Checkout the privious commit
git cherry-pick --no-commit branchX // Cherry pick the content of the second commit
git commit --amend // Create a new commit with their combined content

Se voglio aggiornare branchXanche (e suppongo che questo sia il lato negativo di questo metodo) Devo anche:

git checkout branchX
git reset --hard <the_new_commit>

1

Se il tuo ramo master git logè simile al seguente:

commit ac72a4308ba70cc42aace47509a5e
Author: <me@me.com>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <me@me.com>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <ralph@me.com>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

e vuoi unire i primi due commit semplicemente seguendo i semplici passaggi:

  1. Prima di essere sul sicuro checkout il secondo ultimo commit in un ramo separato. Puoi nominare qualsiasi ramo.git checkout 77df2a40e53136c7a2d58fd847372 -b merged-commits
  2. Ora, scegli semplicemente le tue modifiche dall'ultimo commit in questo nuovo ramo come: git cherry-pick -n -x ac72a4308ba70cc42aace47509a5e . (Risolvi i conflitti, se presenti)
  3. Quindi ora, le tue modifiche nell'ultimo commit sono presenti nel tuo ultimo ultimo commit. Ma devi ancora impegnarti, quindi prima aggiungi le modifiche che hai appena scelto e poi esegui git commit --amend.

Questo è tutto. Se lo desideri, puoi inserire questa versione unita nel ramo "commit-commit".

Inoltre, ora puoi scartare i due commit back-to-back nel tuo ramo principale. Aggiorna il tuo ramo principale come:

git checkout master
git reset --hard origin/master (CAUTION: This command will remove any local changes to your master branch)
git pull

0

Se si desidera combinare i due commit più recenti e utilizzare semplicemente il messaggio di commit precedente, è possibile automatizzare il processo mediante expect.

Presumo:

  • Stai usando vi come editor
  • I tuoi commit sono di una riga ciascuno

Ho provato con git version 2.14.3 (Apple Git-98).


#!/usr/bin/env expect
spawn git rebase -i HEAD~2

# change the second "pick" to "squash"
# down, delete word, insert 's' (for squash), Escape, save and quit
send "jdwis \033:wq\r"

expect "# This is a"

# skip past first commit message (assumed to be one line), delete rest of file
# down 4, delete remaining lines, save and quit
send "4jdG\r:wq\r"

interact

Non è chiaro cosa fa la tua sceneggiatura.
buhtz,

@buhtz Ho aggiunto altri commenti. Fammi sapere se lo trovi ancora confuso e, in tal caso, quale parte.
Erwaman,

Non è ancora chiaro cosa fa il tuo scrpit. Inoltre expectè non descritto.
buhtz,

@buhtz quale parte non è chiara? Ho fornito un link a una pagina con più documentazione per expect.
Erwaman,

Manca una descrizione generale dello script. Non è chiaro cosa faccia. Nessuna parte non è chiara. L'intenzione della sceneggiatura stessa non è chiara.
buhtz
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.