Ripristino di una parte di un commit con git


144

Voglio ripristinare un particolare commit in git. Sfortunatamente, la nostra organizzazione utilizza ancora CVS come standard, quindi quando commetto di nuovo su CVS più commit git vengono raggruppati in uno. In questo caso, mi piacerebbe individuare l'originale git commit, ma è impossibile.

Esiste un approccio simile a git add --patchquello che mi consentirebbe di modificare in modo selettivo le differenze per decidere quali parti di un commit ripristinare?


Altre soluzioni qui , ma concentrandosi sulla limitazione del ripristino parziale di file specifici.
ntc2,

Risposte:


226

Utilizzare l' opzione --no-commit( -n) per git revert, quindi annullare la messa in scena delle modifiche, quindi utilizzare git add --patch:

$ git revert -n $bad_commit    # Revert the commit, but don't commit the changes
$ git reset HEAD .             # Unstage the changes
$ git add --patch .            # Add whatever changes you want
$ git commit                   # Commit those changes

Nota: i file che aggiungi usando git add --patch sono i file che vuoi ripristinare, non i file che vuoi conservare.


13
Potrebbe valere la pena aggiungere l'ultimo comando richiesto, per coloro che non hanno familiarità con Git: dopo aver git reset --hardeseguito il commit, per annullare le altre modifiche che non si desidera ripristinare.
tremby,

15
git reset --hardè pericoloso per i neofiti, in quanto potrebbe perdere le modifiche desiderate. Invece, abituatevi git status, questo suggerisce git checkout -- FILE..di ripristinare le cose in modo più sicuro.
Tino,

Che omissione spalancata git; git revertdovrebbe solo prendere una --patchdiscussione.
Kaz

@Kaz: git revertviene utilizzato per ripristinare interi commit. È possibile utilizzare git checkout -pper selezionare interattivamente i bit da ripristinare.
mipadi,

1
Vorrei anche aggiungere l'evidente (forse), che è prima di tutto salvare il tuo lavoro . O commitprima, o stashpoi prova revert.
Felipe Alvarez,

39

Ho usato con successo quanto segue.

Innanzitutto ripristina il commit completo (lo mette in indice) ma non eseguire il commit.

git revert -n <sha1>  # -n is short for --no-commit

Quindi rimuovere interattivamente le modifiche GOOD ripristinate dall'indice

git reset -p          # -p is short for --patch  

Quindi eseguire il reverse reverse delle modifiche errate

git commit -m "Partially revert <sha1>..."

Infine, le modifiche GOOD ripristinate (che non sono state messe in scena dal comando reset) sono ancora nell'albero di lavoro. Devono essere ripuliti. Se nella struttura di lavoro non vengono lasciate altre modifiche non impegnate, è possibile farlo

git reset --hard

5
Questa non è un'alternativa superiore alla risposta accettata (che utilizza reset HEAD .), perché non richiede la pulizia finale della directory di lavoro?
Steven Lu,

2
Questa risposta è superiore, perché reset -pè più breve di quella reset HEADseguita da add -p. Ma richiede ancora la pulizia, poiché i blocchi "buoni" che sono stati ripristinati sono ancora nella directory di lavoro dopo il commit.
Chiel ten Brinke

Questa risposta non è superiore perché la rimozione interattiva delle modifiche desiderate è spesso confusa e soggetta a errori, specialmente se una di esse richiede una modifica.
Kaz

5

Personalmente, preferisco questa versione, che riutilizza il messaggio di commit generato automaticamente e offre all'utente la possibilità di modificare e inserire la parola "Parzialmente" prima di eseguire finalmente il commit.

# generate a revert commit
# note the hash printed to console on success
git revert --no-edit <hash to revert>

# undo that commit, but not its changes to the working tree
# (reset index to commit-before-last; that is, one graph entry up from HEAD)
git reset HEAD~1

# interactively add reversions
git add -p

# commit with pre-filled message
git commit -c <hash from revert commit, printed to console after first command>

# reset the rest of the current directory's working tree to match git
# this will reapply the excluded parts of the reversion to the working tree
# you may need to change the paths to be checked out
# be careful not to accidentally overwrite unsaved work
git checkout -- .

4

Soluzione:

git revert --no-commit <commit hash>
git reset -p        # every time choose 'y' if you want keep the change, otherwise choose 'n'
git commit -m "Revert ..."
git checkout -- .   # Don't forget to use it.

Aiuterebbe le persone se dicessi come è diverso dalla soluzione accettata
CharlesB,

1
@Krzysztof Perché il checkout alla fine è importante e perché questa soluzione è diversa da quella dell'utente1338062?
martin,

3

Un'altra alternativa (se la versione corrente di un file non è troppo lontana dalla versione che si sta tentando di ripristinare) è ottenere l'hash del commit immediatamente prima di quello da cui si desidera ripristinare parzialmente (da git log). Quindi il tuo comando diventa:

$ git checkout -p <hash_preceding_commit_to_revert> -- file/you/want/to/fix.ext

Questo modifica i file nella tua struttura di lavoro ma non crea alcun commit, quindi se riesci davvero a fare il pieno puoi semplicemente ricominciare git reset --hard -- file/you/want/to/fix.ext.


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.