Annullare un rebase git


3180

Come posso facilmente annullare un rebase git?

Le mie idee attuali sono solo approcci manuali:

  1. git checkout sul genitore di commit in entrambi i rami
  2. Crea un ramo temporaneo da lì
  3. git cherry-pick tutto si impegna a mano
  4. sostituisci il ramo in cui ho modificato il ramo creato manualmente

Nella mia situazione attuale questo funzionerebbe perché posso facilmente individuare i commit da entrambi i rami (uno era il mio materiale, l'altro era quello del mio collega).

Tuttavia il mio approccio mi sembra subottimale e soggetto a errori (diciamo che avevo appena riformato con 2 dei miei rami).

Chiarimento : sto parlando di un rebase durante il quale sono stati rigiocati un sacco di commit. Non solo uno.


6
Si noti inoltre che durante un rebase è possibile escludere commit o schiacciarli; queste modifiche non sono riverificabili senza un puntatore ai nodi originali o setacciando il reflog, quindi il cherrypicking non funzionerebbe.
ANeves,

Risposte:


4337

Il modo più semplice sarebbe trovare il commit principale del ramo com'era immediatamente prima dell'inizio del rebase nel reflog ...

git reflog

e reimpostare il ramo corrente su di esso (con le solite avvertenze sull'essere assolutamente sicuri prima di ripristinare con l' --hardopzione).

Supponiamo che il vecchio commit fosse HEAD@{5}nel registro ref:

git reset --hard HEAD@{5}

In Windows, potrebbe essere necessario citare il riferimento:

git reset --hard "HEAD@{5}"

Puoi controllare la storia del vecchio capo candidato semplicemente facendo un git log HEAD@{5}( Windows:) git log "HEAD@{5}" .

Se non hai disabilitato i reflog per ramo, dovresti essere in grado di fare semplicemente git reflog branchname@{1}come un rebase stacca la testa del ramo prima di ricollegarla alla testa finale. Lo ricontrollerei, anche se non l'ho verificato di recente.

Per impostazione predefinita, tutti i reflog sono attivati ​​per i repository non bare:

[core]
    logAllRefUpdates = true

115
Git reflog è fantastico, ricorda solo che puoi ottenere un output formattato migliore git log -g(suggerimento dal progit.org/book di Scott Chacon ).
Karmi,

60
@Zach: git rebase --abort( -inon ha senso --abort) è per abbandonare un rebase che non è stato completato - o perché c'erano conflitti o perché era interattivo o entrambi; non si tratta di annullare un rebase di successo, ed è di questo che si tratta. Utilizzeresti rebase --aborto reset --harddipenderai dalla situazione in cui ti trovavi. Non dovresti fare entrambe le cose.
CB Bailey,

311
Solo nel caso, fare un backup prima: git tag BACKUP. Puoi tornare ad esso se qualcosa va storto:git reset --hard BACKUP
kolypto

6
Se hai fatto molti commit, HEAD @ {#} che stai cercando sarà preceduto commit:invece di rebase:. Sembra ovvio ma mi ha confuso per un po '.
Warpling,

4
Partecipare alla festa dopo un rebase accidentale: D. Non git reset --hard ORIG_HEADfarebbe il trucco anche subito dopo il rebase accidentale?
quaylar,

1488

In realtà, rebase salva il tuo punto di partenza, ORIG_HEADquindi di solito è semplice come:

git reset --hard ORIG_HEAD

Tuttavia, il reset, rebasee mergetutti salvano il HEADpuntatore originale in ORIG_HEADcosì, se hai eseguito uno di questi comandi dal rebase che stai cercando di annullare, dovrai usare il reflog.


34
Nel caso in cui ORIG_HEADnon sia più utile, puoi anche utilizzare la branchName@{n}sintassi, dove si ntrova l'ennesima posizione precedente del puntatore del ramo. Quindi, ad esempio, se rinnovi il featureAramo sul masterramo, ma non ti piace il risultato del rebase, allora puoi semplicemente fare git reset --hard featureA@{1}per ripristinare il ramo esattamente dove era prima di eseguire il rebase. Puoi leggere di più sulla sintassi branch @ {n} nei documenti Git ufficiali per le revisioni .

15
Questo è il più semplice. Seguitelo con un git rebase --abortpensiero.
Seph

1
@DaBlick questo ha funzionato bene per me dopo un rebase completamente riuscito senza conflitti git 2.17.0.
dvlsg,

4
E lasciatemi integrare: git reset --hard ORIG_HEADpuò usare ripetutamente per tornare indietro ancora e ancora. Di ', se A --- rifare in --- B --- rifare in --- C, ora sono a C, posso tornare ad A usando due voltegit reset --hard ORIG_HEAD
CalvinChe

5
@Seph Puoi spiegare perché suggerisci di seguire git rebase --abort?
UpTheCreek

386

La risposta di Charles funziona, ma potresti voler fare questo:

git rebase --abort

per pulire dopo il reset.

Altrimenti, potresti ricevere il messaggio " Interactive rebase already started".


4
Questo ha rimosso la parte "| REBASE" nel mio prompt. +1
Wouter Thielen,

62
Non era questa la domanda. La domanda chiede come annullare un rebase finito.
Arunav Sanyal,

2
Dovrebbe essere un commento sulla risposta di Charles, perché non risponde alla domanda sul suo on
Murmel

Hm, non dovevo farlo.
Viktor Seč,

1
L'unico che ha funzionato per me!
Alexandre Picard,

90

Il ripristino del ramo sull'oggetto commit in sospensione del suo vecchio suggerimento è ovviamente la soluzione migliore, perché ripristina lo stato precedente senza spendere alcuno sforzo. Ma se ti capita di aver perso quei commit (es. Perché nel frattempo hai raccolto il tuo repository, o questo è un nuovo clone), puoi sempre rifare nuovamente il ramo. La chiave di questo è l' --ontointerruttore.

Supponiamo che tu abbia un ramo di argomento chiamato in modo fantasioso topic, che hai ramificato masterquando la punta di masterera il 0deadbeefcommit. Ad un certo punto, mentre sul topicramo, l'hai fatto git rebase master. Ora vuoi annullare questo. Ecco come:

git rebase --onto 0deadbeef master topic

Questo prenderà tutti i commit topicche non sono masterattivi e li rigioverà 0deadbeef.

Con --onto, puoi riorganizzare la tua storia praticamente in qualsiasi forma .

Divertiti. :-)


3
Penso che questa sia l'opzione migliore per la sua flessibilità. Ho ramificato b1 dal master, quindi ho riassemblato b1 in un nuovo ramo b2, quindi ho voluto ripristinare b1 per essere nuovamente basato sul master. Adoro git - grazie!
ripper234,

2
Questa è l'opzione migliore qui! Ha mantenuto tutte le modifiche che ho sul mio ramo attuale e ha rimosso tutte quelle indesiderate!
Alicia Tang,

Direi che con una combinazione di --ontoe -ipuoi riorganizzare la tua storia praticamente in qualsiasi forma. Usa gitk (o gitx su mac) per vedere le forme che crei :-).
rjmunro,

69

In realtà ho messo un tag di backup sul ramo prima di fare qualsiasi operazione non banale (la maggior parte dei rebase sono banali, ma lo farei se sembra ovunque complesso).

Quindi, ripristinare è facile come git reset --hard BACKUP.


2
Faccio anche questo. È anche utile git diff BACKUP..HEAD per essere sicuri di aver cambiato quello che volevi dire.
Paul Bone,

5
Lo facevo anch'io, ma da quando mi sono sentito più a mio agio con il reflog non mi sento più necessario. Il reflog essenzialmente sta facendo questo per tuo conto ogni volta che cambi HEAD.
Pete Hodgson,

4
Bene, preferisco nomi significativi, perché la ricerca dell'elemento giusto nel reflog a volte non è affatto divertente.
Alex Gontmakher,

12
In realtà non hai nemmeno bisogno di creare un ramo di backup, puoi semplicemente usare la branchName@{n}sintassi, ecco nl'ennesima posizione precedente del puntatore di ramo. Quindi, ad esempio, se rinnovi il featureAramo sul tuo masterramo, ma non ti piace il risultato del rebase, puoi semplicemente fare git reset --hard featureA@{1}per ripristinare il ramo esattamente dove era prima di eseguire il rebase. Puoi leggere di più sulla branch@{n}sintassi nei documenti Git ufficiali per le revisioni .

2
In realtà, non è nemmeno necessario utilizzare la sintassi sopra, secondo la risposta di Pat Notz , l'originale HEADdel ramo è temporaneamente archiviato ORIG_HEAD. Ma anche la tecnica di utilizzo di un'etichetta del ramo di backup funziona, sono solo altri passaggi.

68

Nel caso in cui tu abbia spinto il tuo ramo nel repository remoto (di solito è di origine) e poi hai fatto un rebase riuscito (senza unione) ( git rebase --abortdà "Nessun rebase in corso") puoi facilmente ripristinare il ramo usando il comando:

git reset --hard origin / {branchName}

Esempio:

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is ahead of 'origin/{branchName}' by 135 commits.
  (use "git push" to publish your local commits)

nothing to commit, working directory clean

$ ~/work/projects/{ProjectName} $ git reset --hard origin/{branchName}
HEAD is now at 6df5719 "Commit message".

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is up-to-date with 'origin/{branchName}.

nothing to commit, working directory clean

Questa è la risposta corretta per me. Rebase e commit prima di rebase avevano lo stesso ID commit, e tornare a HEAD {1} non ripristinabbe rebase!
Bill Kotsias,

23

L'uso reflognon ha funzionato per me.

Ciò che ha funzionato per me era simile a come descritto qui . Apri il file in .git / logs / refs che prende il nome dal ramo che è stato riformulato e trova la riga che contiene "rebase finsihed", qualcosa del tipo:

5fce6b51 88552c8f Kris Leech <me@example.com> 1329744625 +0000  rebase finished: refs/heads/integrate onto 9e460878

Acquista il secondo commit elencato sulla riga.

git checkout 88552c8f

Una volta confermato che ciò conteneva i miei cambiamenti persi, mi sono ramificato e ho emesso un sospiro di sollievo.

git log
git checkout -b lost_changes


3
Whoa - da quel link, "C'è un avvertimento: ho perso la storia del ramo, ma in questo caso non aveva molta importanza. Ero solo felice di aver recuperato le mie modifiche". ?
ruffin,

16

Per più commit, ricorda che qualsiasi commit fa riferimento a tutta la cronologia che porta a quel commit. Quindi, nella risposta di Charles, leggi "il vecchio commit" come "il più nuovo dei vecchi commit". Se si reimposta a quel commit, riapparirà tutta la cronologia che porta a quel commit. Questo dovrebbe fare quello che vuoi.


11

Seguendo la soluzione di @Allan e @Zearin, vorrei poter semplicemente fare un commento, ma non ho abbastanza reputazione, quindi ho usato il seguente comando:

Invece di fare git rebase -i --abort (notare il -i ) dovevo semplicemente fare git rebase --abort( senza il -i ).

L'uso di entrambi -ie --abortallo stesso tempo fa sì che Git mi mostri un elenco di utilizzo / opzioni.

Quindi il mio stato di filiale precedente e attuale con questa soluzione è:

matbhz@myPc /my/project/environment (branch-123|REBASE-i)
$ git rebase --abort

matbhz@myPc /my/project/environment (branch-123)
$

11

Se ti sei ribadito con successo contro il ramo remoto e non git rebase --abortpuoi ancora fare alcuni trucchi per salvare il tuo lavoro e non avere spinte forzate. Supponiamo che il tuo ramo corrente che è stato riformulato per errore sia chiamato your-branche stia monitorandoorigin/your-branch

  • git branch -m your-branch-rebased # rinomina ramo corrente
  • git checkout origin/your-branch # checkout allo stato più recente noto per essere di origine
  • git checkout -b your-branch
  • controllare git log your-branch-rebased, confrontare git log your-branche definire i commit mancantiyour-branch
  • git cherry-pick COMMIT_HASH per ogni impegno your-branch-rebased
  • spingi le tue modifiche. Si noti che sono associati due rami locali remote/your-branche si dovrebbe solo spingereyour-branch

4

Diciamo che rifaccio master al mio ramo di funzionalità e ricevo 30 nuovi commit che interrompono qualcosa. Ho scoperto che spesso è più semplice rimuovere solo i cattivi commit.

git rebase -i HEAD~31

Reimpostazione interattiva per gli ultimi 31 commit (non fa male se ne scegli troppi).

Basta prendere le commit che si desidera eliminare e contrassegnarle con "d" invece di "pick". Ora i commit vengono eliminati in modo efficace annullando il rebase (se rimuovi solo i commit che hai appena ottenuto durante il rebasing).


3

Per i principianti / chiunque abbia troppa paura di eseguire un hard reset, è possibile effettuare il checkout del commit dal reflog, quindi salvarlo come nuovo branch.

git reflog

Trova il commit appena prima di iniziare il rebasing. Potrebbe essere necessario scorrere ulteriormente verso il basso per trovarlo (premere Invio o PageDown). Prendi nota del numero HEAD e sostituisci 57:

git checkout HEAD@{57}

Esamina il ramo / commit, se sembra buono crea un nuovo ramo usando questo HEAD:

git checkout -b new_branch_name

2

Se sei su un ramo puoi usare:

git reset --hard @{1}

Non esiste solo un registro di riferimento per HEAD (ottenuto da git reflog), ci sono anche reflog per ogni ramo (ottenuto da git reflog <branch>). Quindi, se sei masterattivo git reflog master, elencherà tutte le modifiche a quel ramo. È possibile fare riferimento a quei cambiamenti da master@{1}, master@{2}, ecc

git rebase di solito cambierà HEAD più volte ma il ramo corrente verrà aggiornato solo una volta.

@{1}è semplicemente una scorciatoia per il ramo corrente , quindi è uguale a master@{1}se ci sei master.

git reset --hard ORIG_HEADnon funzionerà se utilizzato git resetdurante un'interattività rebase.


1

Quello che faccio di solito è git reset #commit_hash

fino all'ultimo commit in cui penso che rebase non abbia avuto alcun effetto.

poi git pull

Ora il tuo ramo dovrebbe corrispondere esattamente come i master e gli commit rinnovati non dovrebbero esserci.

Ora si può semplicemente scegliere i commit su questo ramo.


1

Ho provato tutti i suggerimenti con reset e reflog senza successo. Il ripristino della cronologia locale di IntelliJ ha risolto il problema dei file persi


Grazie! Non ho mai usato la cronologia locale prima, ma questa si è rivelata l'opzione più semplice per me per recuperare un codice accidentalmente riformulato. Funzionalità molto bella ma un po 'nascosta.
Magnus W,

0

git reset --hard origin / {branchName}

è la soluzione corretta per ripristinare tutte le modifiche locali eseguite da rebase.


1
se si ripristina a fondo origin/branchpotrebbe perdere le modifiche tra HEAD e quel punto. Non lo vuoi in generale.
bluesmonk,

Questo è esattamente quello che volevo nel mio caso. Così positivo.
Mandar Vaze,

-4

Se sbagli qualcosa all'interno di un git rebase, ad esempio git rebase --abort, mentre hai file non impegnati, questi andranno persi e git reflognon aiuteranno. Questo è successo a me e dovrai pensare fuori dagli schemi qui. Se sei fortunato come me e usi IntelliJ Webstorm, puoi right-click->local historye tornare a uno stato precedente di file / cartelle, indipendentemente dagli errori che hai fatto con il software di controllo delle versioni. È sempre bene avere un altro fail-safe in esecuzione.


5
git rebase --abortinterrompe un rebase attivo, non annulla un rebase. Inoltre, utilizzare due VCS contemporaneamente è una cattiva idea. È una bella funzionalità nel software Jetbrains ma non dovresti usare entrambi. È meglio imparare Git, in particolare quando si risponde a domande su Stack Overflow che riguardano Git.
Dudewad,
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.