Posso eliminare un commit git ma mantenere le modifiche


1057

In uno dei miei rami di sviluppo, ho apportato alcune modifiche alla mia base di codice. Prima di essere in grado di completare le funzionalità su cui stavo lavorando, ho dovuto cambiare il mio ramo attuale per master per demo alcune funzionalità. Ma solo usando un "git checkout master" ho preservato le modifiche che ho fatto anche nel mio ramo di sviluppo, interrompendo così alcune delle funzionalità di master. Quindi quello che ho fatto è stato eseguire il commit delle modifiche sul mio ramo di sviluppo con un messaggio di commit "commit temporaneo" e quindi controllare il master per la demo.

Ora che ho finito con la demo e sono tornato a lavorare sul mio ramo di sviluppo, vorrei rimuovere il "commit temporaneo" che ho fatto mantenendo le modifiche apportate. È possibile?


65
La prossima volta:git stash
Matt Ball,

51
@MattBall, non necessariamente. Sebbene git stashsia un buon strumento, i commit a eliminazione "work in progress" sono anche un dispositivo abbastanza legittimo.
kostix,

3
Questa è una grande risorsa proveniente da Github: come annullare (quasi) qualsiasi cosa con Git
jasonleonhard

9
@MattBall @kostix Sì, lo stash è particolarmente inadatto per lo stashing "a lungo termine", dato che si tratta di uno stack globale per l'intero repository. Voglio essere in grado di riporre i cambiamenti su un ramo e poi andare ad altri rami, fare qualsiasi altra cosa, tornare giorni dopo senza preoccuparmi che avrei potuto usare git stashsu qualche altro ramo nel frattempo.
Alec,

4
Una cosa degna di nota stashè che è completamente locale e sarà soggetto a perdita di codice a causa di re-cancellazione o ricreazione o, guasto o perdita dell'hardware. IMO, dovrebbe davvero essere usato solo per WIP a brevissimo termine. Adoro un commit WIP prima di andare in vacanza: P .. chiamalo un commit di scarico del cervello!
ϹοδεMεδιϲ

Risposte:


1620

È semplice come questo:

git reset HEAD^

Nota: alcune shell trattano ^come un carattere speciale (ad esempio alcune shell di Windows o ZSH con globbing abilitato ), quindi potresti dover citare "HEAD^"in questi casi.

git resetsenza a --hardo --softsposta il HEADpuntatore verso il commit specificato, senza modificare alcun file. HEAD^si riferisce al (primo) commit parent del tuo commit corrente, che nel tuo caso è il commit prima di quello temporaneo.

Si noti che un'altra opzione è continuare normalmente, quindi al successivo punto di commit eseguire invece:

git commit --amend [-m … etc]

che modificherà invece il commit più recente, con lo stesso effetto di cui sopra.

Nota che questo (come con quasi tutte le risposte git) può causare problemi se hai già spinto il cattivo commit in un posto in cui qualcun altro potrebbe averlo estratto. Cerca di evitarlo


47
Questo è di gran lunga il più semplice, ma se hai già spinto il tuo impegno su un telecomando e qualcun altro lo ha tirato, sarei molto titubante a fare qualsiasi cosa tranne scusarmi.
at13 13

11
@sicophrenic, non perdere l'occasione di leggere "Reset Demysified" in questa occasione.
kostix,

33
Ricevo More?dopo averlo fatto. Qualunque cosa fatal: ambiguous argument 'HEADwhateverItypedIn': unknown revision or path not in the working tree.
scrivo

50
@DaAwesomeP sembra che tu stia usando una shell che considera ^un personaggio speciale. Puoi citare il riferimento "HEAD^"o utilizzare la sintassi alternativa HEAD~1non quotata
Gareth,

8
Ha funzionato per me, ma ha dovuto sfuggire al personaggiogit reset HEAD\^
Cevaris il

189

Esistono due modi per gestirlo. Quale è più facile dipende dalla tua situazione

Ripristina

Se il commit di cui vuoi sbarazzarti è stato l'ultimo commit e non hai fatto alcun lavoro aggiuntivo che puoi semplicemente usare git-reset

git reset HEAD^

Riporta il tuo ramo al commit appena prima del tuo HEAD attuale. Tuttavia, in realtà non modifica i file nell'albero di lavoro. Di conseguenza, le modifiche apportate a quel commit vengono visualizzate come modificate: è come un comando "non impegnativo". In effetti, ho un alias per fare proprio questo.

git config --global alias.uncommit 'reset HEAD^'

Quindi puoi semplicemente utilizzarlo git uncommitin futuro per eseguire il backup di un commit.

schiacciamento

Annullare un commit significa combinare due o più commit in uno. Lo faccio abbastanza spesso. Nel tuo caso hai una funzione metà impegnata e poi la finiresti e commetterai di nuovo con il messaggio di commit permanente e corretto.

git rebase -i <ref>

Dico sopra perché voglio chiarire che potrebbe trattarsi di un numero qualsiasi di commit. Esegui git loge trova il commit di cui vuoi sbarazzarti, copia il suo SHA1 e usalo al posto di <ref>. Git ti porterà in modalità rebase interattiva. Mostrerà tutti gli impegni tra il tuo stato attuale e qualunque cosa tu abbia messo al posto di <ref>. Quindi, se <ref>10 commit fa, ti mostrerà tutti i 10 commit.

Di fronte a ogni commit, avrà la parola pick. Trova il commit si vuole sbarazzarsi di e cambiare da picka fixupo squash. Usare fixupsemplicemente i rigetti che eseguono il commit del messaggio e uniscono le modifiche al suo predecessore immediato nell'elenco. La squashparola chiave fa la stessa cosa, ma consente di modificare il messaggio di commit del commit appena combinato.

Si noti che i commit verranno nuovamente impegnati nell'ordine in cui vengono visualizzati nell'elenco quando si esce dall'editor. Quindi, se hai effettuato un commit temporaneo, hai fatto altri lavori sullo stesso ramo e hai completato la funzione in un commit successivo, l'utilizzo di rebase ti consentirebbe di riordinare i commit e di schiacciarli.

AVVERTIMENTO:

Rebasing modifica la cronologia - NON eseguire questa operazione per tutti i commit che hai già condiviso con altri sviluppatori.

stashing

In futuro, per evitare questo problema, è consigliabile utilizzare git stashper archiviare temporaneamente il lavoro senza commit.

git stash save 'some message'

Questo memorizzerà le tue attuali modifiche lateralmente nell'elenco degli stash. Sopra è la versione più esplicita del comando stash, che consente un commento per descrivere ciò che si sta nascondendo. Puoi anche semplicemente eseguire git stashe nient'altro, ma nessun messaggio verrà memorizzato.

Puoi sfogliare l'elenco di stash con ...

git stash list

Questo ti mostrerà tutti i tuoi stash, su quali rami sono stati fatti, e il messaggio e all'inizio di ogni riga e identificatore per quello stash che assomiglia a questo stash@{#}dove # è la sua posizione nella matrice di stash.

Per ripristinare uno stash (che può essere fatto su qualsiasi ramo, indipendentemente da dove lo stash è stato originariamente creato) devi semplicemente eseguire ...

git stash apply stash@{#}

Ancora una volta, c'è # la posizione nella matrice di stash. Se lo stash che si desidera ripristinare è nella 0posizione, ovvero se era lo stash più recente. Poi si può solo eseguire il comando senza specificare la posizione di scorta, git assumerà si intende l'ultimo: git stash apply.

Quindi, ad esempio, se mi trovo a lavorare sul ramo sbagliato, posso eseguire la seguente sequenza di comandi.

git stash
git checkout <correct_branch>
git stash apply

Nel tuo caso ti sei spostato un po 'di più nei rami, ma la stessa idea è ancora valida.

Spero che sia di aiuto.


6
git config --global alias.uncommit reset HEAD^solo gli alias si impegnano a ripristinare. Invece, faigit config --global alias.uncommit 'reset HEAD^'
mernst

3
Si noti che sarà necessario utilizzare ^^ invece di ^ se si utilizza in un prompt dei comandi di Windows.
Pramod BR

106

Penso che tu stia cercando questo

git reset --soft HEAD~1

Annulla il commit più recente mantenendo allo stesso tempo le modifiche apportate al commit.


7
Grazie. Questo ha funzionato per me. Chiamare git reset HEAD^su Windows richiede solo "Altro?" - qualunque cosa significhi
Tyron,

12
@Tyron ^è un personaggio di escape in DOS. Se associato a una nuova riga, funge da prompt di continuazione per il comando precedente. La digitazione git reset HEAD^^dovrebbe funzionare su Windows.
Trk,

40

Sì, è possibile eliminare il commit senza eliminare le modifiche: git reset @ ~


7
Questo è davvero quello che voglio, e sarà la risposta accettata secondo me, grazie mille!
Carlos Liu,

2
Sintassi interessante e compatta, non l'ho mai vista prima. In che modo è diverso da git reset --softo git reset --keep?
user776686

18

Stai cercando uno git reset HEAD^ --softo git reset HEAD^ --mixed.

Esistono 3 modalità per il comando di ripristino, come indicato nei documenti :

git reset HEAD^ --soft

annulla il git commit. Le modifiche esistono ancora nell'albero di lavoro (la cartella del progetto) + l'indice (--cached)

git reset HEAD^ --mixed

annulla git commit+ git add. I cambiamenti esistono ancora nell'albero di lavoro

git reset HEAD^ --hard

Come se non avessi mai apportato queste modifiche alla base di codice. I cambiamenti sono andati dall'albero di lavoro.


9

Per quelli che usano zsh, dovrai usare quanto segue:

git reset --soft HEAD\^

Spiegato qui: https://github.com/robbyrussell/oh-my-zsh/issues/449

Nel caso in cui l'URL si interrompa, la parte importante è:

Sfuggire a ^ al tuo comando

In alternativa puoi usare HEAD ~ in modo da non dover sfuggire ogni volta.


15
Non riesco mai a ricordare questo comando e devo
cercarlo

2
git reset HEAD^sta lavorando in zsh per me, potrebbe essere stato corretto.
Ben Kolya Mansley il

@BenKolyaMansley Quale versione di zsh stai usando?
Greg Hilston,

Sto usandozsh 5.3 (x86_64-apple-darwin18.0)
Ben Kolya Mansley il

7

Nel mio caso, ho già spinto al repository. Ahia!

È possibile ripristinare un commit specifico mantenendo le modifiche nei file locali effettuando:

git revert -n <sha>

In questo modo sono stato in grado di mantenere i cambiamenti di cui avevo bisogno e di annullare un commit che era già stato spinto.


Nel mio caso, avevo bisogno di ripristinare qualcosa che avevo già inviato al repository remoto. Ancora più ahi! Il modo migliore era git revert bad-commit-sha, quindi, git revert -n revert-commit-just-created-shae poi riparare da lì. Mi hai preso a metà strada. Grazie!
TinkerTenorSoftwareAcquista il

Questo sembra fare il contrario, no? Crea nuove modifiche che corrispondono a un ripristino delle modifiche apportate nel commit selezionato. Se dovessi impegnare queste nuove modifiche, avrai annullato il lavoro che volevi effettivamente conservare.
svaens,

3

Usare git 2.9 (precisamente 2.9.2.windows.1) git reset HEAD^richiede di più; non sono sicuro di cosa ci si aspetti input qui. Fare riferimento allo screenshot seguente

inserisci qui la descrizione dell'immagine

Abbiamo trovato un'altra soluzione git reset HEAD~#numberOfCommitscon la quale possiamo scegliere di selezionare il numero di commit locali che desideri ripristinare mantenendo intatte le modifiche. Pertanto, abbiamo l'opportunità di eliminare tutti gli commit locali e un numero limitato di commit locali.

Fai riferimento alle schermate di seguito visualizzate git reset HEAD~1in azione: inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine


Probabilmente dovrai uscire dal carattere ^ - prova a ripristinare git "HEAD ^" o a ripristinare git HEAD \ ^
Mark Fisher

Aggiungendo a ciò, un reset git HEAD ^^ funziona come un singolo ^ viene trattato come una nuova riga.
John Oss,

2

Un altro modo per farlo.

Aggiungi il commit all'inizio del commit temporaneo e poi fai:

git rebase -i

Per unire due commit in uno (il comando aprirà il file di testo con istruzioni esplicite, modificarlo).


2
Tecnicamente corretto, ma per nulla elegante quanto il semplice fare git reset HEAD^. Git rebase ha un sacco di spazio per l'errore qui.
TheBuzzSaw

0

2020 Modo semplice:

git reset <commit_hash>

(L'hash di commit dell'ultimo commit che vuoi conservare).

Se il commit è stato inviato, è possibile quindi eseguire:

git push -f

Manterrai localmente le modifiche ora senza commit

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.