Come posso ripristinare o ripristinare un file a una revisione specifica?


4519

Ho apportato alcune modifiche a un file che è stato impegnato alcune volte come parte di un gruppo di file, ma ora voglio ripristinare / ripristinare le modifiche su di esso su una versione precedente.

Ho fatto una ricerca git loginsieme git diffper trovare la revisione di cui ho bisogno, ma non ho idea di come riportare il file al suo stato precedente in passato.


11
Dopo il ripristino, non dimenticare --cacheddurante il controllo git diff. link
Geoffrey Hale,

5
Ho trovato la tua domanda quando ho cercato su Google la mia. Ma dopo aver letto la soluzione, ho controllato il mio registro e ho scoperto che ho apportato le modifiche alla società come commit autonomo, quindi ho fatto ripristinare git per quel commit e tutto il resto è rimasto come lo desideravo. Non è una soluzione, solo un altro modo per farlo a volte.
sudo97,

Risposte:


6139

Supponendo che l'hash del commit desiderato sia c5f567:

git checkout c5f567 -- file1/to/restore file2/to/restore

La pagina man di git checkout fornisce maggiori informazioni.

Se vuoi tornare al commit prima c5f567, aggiungi ~1(dove 1 è il numero di commit che vuoi tornare indietro, può essere qualsiasi cosa):

git checkout c5f567~1 -- file1/to/restore file2/to/restore

Come nota a margine, mi sono sempre sentito a disagio con questo comando perché è usato sia per le cose ordinarie (cambiando tra i rami) sia per cose insolite e distruttive (scartando le modifiche nella directory di lavoro).


12
@shadowhand: c'è un modo per invertire ciò, quindi è la versione subito dopo?
aliteralmind

16
@aliteralmind: No, sfortunatamente la notazione della scorciatoia della storia di Git risale solo alla storia.
Greg Hewgill,

46
Se hai intenzione di usare un nome di ramo per abcde (ad esempio develop) ti consigliamo git checkout develop -- file/to/restore(nota il doppio trattino)
Ohad Schneider

8
@aliteralmind: In realtà, sì, c'è un modo per farlo: "git log --reverse -1 --ancestry-path yourgitrev..master" e quindi usare le opzioni appropriate per ottenere solo git rev. --ancestry-path "traccia una linea" tra due commit e -1 mostrerà solo una versione, e --reverse assicurerà che la prima voce emessa sia la più vecchia.
Chris Cogdon,

6
Personalmente trovo HEAD ^ più facile da scrivere rispetto a HEAD ~ 1 :)
juzzlin

606

È possibile rivedere rapidamente le modifiche apportate a un file utilizzando il comando diff:

git diff <commit hash> <filename>

Quindi per ripristinare un file specifico su quel commit utilizzare il comando reset:

git reset <commit hash> <filename>

Potrebbe essere necessario utilizzare l' --hardopzione se si dispone di modifiche locali.

Un buon flusso di lavoro per la gestione dei waypoint consiste nell'utilizzare i tag per contrassegnare in modo pulito i punti nella sequenza temporale. Non riesco a capire bene la tua ultima frase, ma quello che potresti desiderare è divergere un ramo da un momento precedente. Per fare ciò, utilizzare il comodo comando di pagamento:

git checkout <commit hash>
git checkout -b <new branch name>

È quindi possibile rifare questo rispetto alla linea principale quando si è pronti a unire tali modifiche:

git checkout <my branch>
git rebase master
git checkout master
git merge <my branch>

7
Il comando 'git checkout <commit hash>' mi ha restituito la versione precedente del progetto esattamente questo per cui stavo cercando Grazie Chris.
Vidur Punj,

48
Per ripristinare il file ha git checkout <commit hash> <filename>funzionato meglio per me digit reset
Motti Strom

3
Volevo una versione iniziale di un singolo file perché avevo sovrascritto 150 righe con una copia / incolla scelta male. git checkout <commit hash> <filename>ha funzionato per me. Questa non dovrebbe essere la risposta accettata, IMHO. git resetno.
Harperville,

24
non è possibile utilizzare git resetper ripristinare un singolo file, si otterrà un errorefatal: Cannot do hard reset with paths
slier

13
Cosa ha detto slier: non puoi git reset --hard <commit hash> <filename>. Questo errore con fatal: Cannot do hard reset with paths.ciò che ha detto Motti Strom: utilizzaregit checkout <commit hash> <filename>
Hawkeye Parker il

366

Puoi usare qualsiasi riferimento a un commit git, incluso SHA-1 se è più conveniente. Il punto è che il comando è simile al seguente:

git checkout [commit-ref] -- [filename]


22
Qual è la differenza tra questa risposta, che ha --, e quella accettata che no?
ore 2

80
In git, un '-' prima dell'elenco dei file dice a git che tutti gli argomenti successivi dovrebbero essere interpretati come nomi di file, non come nomi di rami o altro. A volte è utile chiarire le ambiguità.
foxxtrot

49
'-' non è solo una convenzione git, ma qualcosa che trovi in ​​vari punti nella riga di comando * nix. rm -- -f(rimuovere un file chiamato -f) sembra essere l'esempio canonico. Maggiori dettagli qui
Hawkeye Parker,

7
Basta aggiungere a ciò che ha detto @HawkeyeParker, il rmcomando usa getopt (3) per analizzare i suoi argomenti. getoptè il comando per analizzare gli argomenti del comando. gnu.org/software/libc/manual/html_node/Getopt.html
Devy

2
@Soney Sì, questo è ciò che intendo, e sì, probabilmente non è affatto comune. Ho visto quell'esempio in vari posti, forse solo per renderlo memorabile: rm -f è ben noto per essere spaventoso / pericoloso. Ma il punto è che in * nix un nome di file può iniziare con un '-', e questo confonderà vari interpreti della riga di comando che, quando vedono un '-', si aspettano che segua un'opzione di comando. Potrebbe essere qualsiasi file che inizia con '-'; ad es. "-mySpecialFile".
Hawkeye Parker,

288
git checkout -- foo

Questo verrà ripristinato foosu HEAD. Puoi anche:

git checkout HEAD^ foo

per una revisione indietro, ecc.


12
Suggerirei di usare la sintassi git checkout -- fooper evitare errori se fooè qualcosa di speciale (come una directory o un file chiamato -f). Con git, se non sei sicuro, aggiungi sempre tutti i file e le directory con l'argomento speciale --.
Mikko Rantalainen il

8
Un'ulteriore nota al commento di Mikko: --non è un comando git e non è speciale per git. È un bash incorporato per indicare la fine delle opzioni di comando. Puoi usarlo anche con molti altri comandi bash.
matthaeus

14
@matthaeus non è nemmeno specifico per bash né una funzione shell. È una convenzione implementata in molti comandi diversi (e supportata da getopt).
Greg Hewgill

2
No, non-- è una parola speciale incorporata in bash. Ma è una convenzione comune supportata da molti parser a riga di comando e utilizzata da molti CLI, incluso Git.
Emil Lundberg,

123

E per tornare all'ultima versione impegnata, che è più frequentemente necessaria, è possibile utilizzare questo comando più semplice.

git checkout HEAD file/to/restore

2
qual è la differenza tra questo (git checkout HEAD file / to / restore) e git reset --hard file / to / restore ???
Motti Shneor,

2
1) più facile da ricordare in modo più generale 2) nessuna preoccupazione per premere Invio prima di inserire il nome del file
Roman Susi

105

Ho avuto lo stesso problema proprio ora e ho trovato questa risposta più facile da capire ( commit-refè il valore SHA della modifica nel registro in cui vuoi tornare):

git checkout [commit-ref] [filename]

Questo metterà quella vecchia versione nella tua directory di lavoro e da lì puoi commetterla se vuoi.


91

Se sai di quanti commit hai bisogno per tornare indietro, puoi usare:

git checkout master~5 image.png

Ciò presuppone che tu sia sul masterramo e che la versione desiderata sia 5 ripristina.


80

Penso di averlo trovato .... da http://www-cs-students.stanford.edu/~blynn/gitmagic/ch02.html

A volte vuoi solo tornare indietro e dimenticare ogni cambiamento oltre un certo punto perché sono tutti sbagliati.

Iniziare con:

$ git log

che mostra un elenco di commit recenti e i loro hash SHA1.

Quindi, digitare:

$ git reset --hard SHA1_HASH

per ripristinare lo stato a un determinato commit e cancellare definitivamente tutti i commit più recenti dal record.


24
Git non rimuove mai nulla. I tuoi vecchi commit sono ancora lì, ma a meno che non ci sia una punta del ramo che li punta non sono più raggiungibili. git reflog li mostrerà comunque fino a quando non pulirai il tuo repository con git-gc.
Bombe,

1
@Bombe: grazie per l'informazione. Avevo verificato una vecchia versione di un file. Dopo aver letto il tuo commento, sono stato in grado di utilizzare "gitref" per cercare l'hash SHA1 parziale e utilizzare "checkout" per tornare alla versione più recente. Altri utenti git potrebbero trovare utili queste informazioni.
Winston C. Yang,

4
eventualmente seguito da agit push --force
bshirley il

4
Se hai delle modifiche non impegnate, le perderai se
esegui

5
@Bombe - "Git non rimuove mai nulla. I tuoi vecchi commit sono ancora lì, ma a meno che non ci sia una punta del ramo che li punta non sono più raggiungibili." - ma i commit in questo modo vengono eliminati dopo un determinato periodo di tempo, quindi "Git non rimuove mai nulla" non è vero.
Bulwersator,

61

Questo ha funzionato per me:

git checkout <commit hash> file

Quindi eseguire il commit della modifica:

git commit -a

54

Devi stare attento quando dici "rollback". Se in passato avevi una versione di un file in commit $ A, e successivamente hai apportato due modifiche in due commit separati $ B e $ C (quindi quello che vedi è la terza iterazione del file) e se dici " Voglio tornare al primo ", vuoi dire sul serio?

Se vuoi sbarazzarti delle modifiche sia della seconda che della terza iterazione, è molto semplice:

$ git checkout $A file

e poi commetti il ​​risultato. Il comando chiede "Voglio estrarre il file dallo stato registrato dal commit $ A".

D'altra parte, ciò che intendevi è eliminare la modifica introdotta dalla seconda iterazione (ovvero commit $ B), mentre mantenendo quello che $ C ha fatto al file, vorrai ripristinare $ B

$ git revert $B

Si noti che chiunque abbia creato il commit $ B potrebbe non essere stato molto disciplinato e potrebbe aver commesso una modifica totalmente non correlata nello stesso commit e questo ripristino potrebbe toccare file diversi da quelli che si vedono come modifiche offensive, quindi si consiglia di controllare attentamente il risultato dopo aver fatto così.


L'ho fatto, ma poi un "file di registro git" avrebbe detto che ero sul commit originale, HEAD. Sembrava che "git checkout" non funzionasse. Tuttavia, uno stato git ha mostrato che il file è stato effettivamente modificato e che un "file diffstag git" mostrerebbe le modifiche effettive. Inoltre, uno "stato git" ha mostrato anche che il file è cambiato. Quindi non usare "git log" qui per tenere traccia dei file modificati.
Frederick Ollinger l'

37

In modo divertente, git checkout foonon funzionerà se la copia di lavoro si trova in una directory denominata foo; tuttavia, entrambi git checkout HEAD fooe git checkout ./foo:

$ pwd
/Users/aaron/Documents/work/foo
$ git checkout foo
D   foo
Already on "foo"
$ git checkout ./foo
$ git checkout HEAD foo

26
oppuregit checkout -- foo
knittl

32

Ecco come rebasefunziona:

git checkout <my branch>
git rebase master
git checkout master
git merge <my branch>

Supponiamo di avere

---o----o----o----o  master
    \---A----B       <my branch>

I primi due comandi ... commit git checkout git rebase master

... controlla il ramo delle modifiche che desideri applicare al masterramo. Il rebasecomando prende i commit da <my branch>(che non si trovano in master) e li riapplica al capo di master. In altre parole, il genitore del primo commit <my branch>non è più un commit precedente nella masterstoria, ma l'attuale responsabile di master. I due comandi sono gli stessi di:

git rebase master <my branch>

Potrebbe essere più facile ricordare questo comando poiché entrambi i rami "base" e "modifica" sono espliciti.

. Il risultato della cronologia finale è:

---o----o----o----o   master
                   \----A'----B'  <my branch>

Gli ultimi due comandi ...

git checkout master
git merge <my branch>

... esegui un'unione di avanzamento rapido su cui applicare tutte le <my branch>modifiche master. Senza questo passaggio, il commit rebase non viene aggiunto a master. Il risultato finale è:

---o----o----o----o----A'----B'  master, <my branch>

mastered <my branch>entrambi i riferimenti B'. Inoltre, da questo punto è sicuro eliminare il <my branch>riferimento.

git branch -d <my branch>

26

A partire da git v2.23.0 c'è un nuovo metodo di ripristino di git che dovrebbe assumere parte di ciò che git checkoutera responsabile (anche la risposta accettata menziona un git checkoutpo 'di confusione). Vedi i punti salienti delle modifiche sul blog di github .

Il comportamento predefinito di questo comando è di ripristinare lo stato di un albero di lavoro con il contenuto proveniente dal sourceparametro (che nel tuo caso sarà un hash di commit).

Quindi in base alla risposta di Greg Hewgill (supponendo che l'hash di commit sia c5f567) il comando sarebbe simile al seguente:

git restore --source=c5f567 file1/to/restore file2/to/restore

O se si desidera ripristinare il contenuto di un commit prima di c5f567:

git restore --source=c5f567~1 file1/to/restore file2/to/restore

24

Primo ripristino testa per il file di destinazione

git reset HEAD path_to_file

Secondo controllo quel file

git checkout -- path_to_file

4
+1, anche se non sono sicuro dell'intenzione di ripristinare HEAD. Potrebbe essere o non essere necessario. Nella mia situazione volevo solo ripristinare un particolare file nella versione in repository (che ha mantenuto intatte le modifiche locali rimanenti. Basta eseguire il secondo passaggio sopra per me
fkl

Sì, devo solo eseguire il secondo comando. Mi piace -> shellhacks.com/git-revert-file-to-previous-commit
javaPlease42

22

git-alias, awk e shell-funzioni in soccorso!

git prevision <N> <filename>

dove <N>è il numero di revisioni del file da ripristinare per il file <filename>.
Ad esempio, per eseguire il checkout della revisione precedente immediata di un singolo file x/y/z.c, eseguire

git prevision -1 x/y/z.c

Come funziona la previsione git?

Aggiungi quanto segue al tuo gitconfig

[alias]
        prevision = "!f() { git checkout `git log --oneline $2 |  awk -v commit="$1" 'FNR == -commit+1 {print $1}'` $2;} ;f"

Il comando in pratica

  • esegue un git logfile specificato e
  • seleziona l'id di commit appropriato nella cronologia del file e
  • esegue git checkouta nell'ID commit per il file specificato.

In sostanza, tutto ciò che si farebbe manualmente in questa situazione,
racchiuso in un bellissimo ed efficiente git-alias - git-prevision


20

Devo collegare EasyGit qui, che è un wrapper per rendere git più accessibile ai principianti senza confondere gli utenti esperti. Una delle cose che fa è dare più significati agit revert . In questo caso, diresti semplicemente:

eg revert foo/bar foo/baz


1
Dovrebbe essere eg revert --in REVISON -- FILENAME. Il --inè importante. Per gli utenti Windows: Apri git bash. Eseguire echo %PATH. Il primo percorso dovrebbe trovarsi nella directory dell'utente che termina con bin. Crea quel percorso. Conservare ad es . Lì. Dillo eg. Non eg.txt.
Koppor,

20

Nel caso in cui si desideri ripristinare un file con un commit precedente (e il file che si desidera ripristinare già con commit) è possibile utilizzare

git checkout HEAD^1 path/to/file

o

git checkout HEAD~1 path/to/file

Quindi basta mettere in scena e impegnare la "nuova" versione.

Con la consapevolezza che un commit può avere due genitori in caso di unione, dovresti sapere che HEAD ^ 1 è il primo genitore e HEAD ~ 1 è il secondo genitore.

Funzioneranno entrambi se nella struttura è presente un solo genitore.


19

Molti suggerimenti qui, la maggior parte sulla falsariga di git checkout $revision -- $file. Un paio di alternative oscure:

git show $revision:$file > $file

Inoltre, lo uso molto solo per vedere temporaneamente una versione particolare:

git show $revision:$file

o

git show $revision:$file | vim -R -

(OBS: $filedeve essere preceduto dal prefisso ./se si tratta di un percorso relativo per git show $revision:$filefunzionare)

E ancora più strano:

git archive $revision $file | tar -x0 > $file

1
Questa è una buona alternativa se non sei sicuro di quale versione di commit desideri e devi "sbirciare" senza sovrascrivere la tua directory di lavoro.
Wisbucky,

18

Si noti, tuttavia, che git checkout ./fooe git checkout HEAD ./foo non sono esattamente la stessa cosa; caso in questione:

$ echo A > foo
$ git add foo
$ git commit -m 'A' foo
Created commit a1f085f: A
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 foo
$ echo B >> foo
$ git add foo
$ echo C >> foo
$ cat foo
A
B
C
$ git checkout ./foo
$ cat foo
A
B
$ git checkout HEAD ./foo
$ cat foo
A

(Il secondo mette in addscena il file nell'indice, ma non viene eseguito il commit.)

Git checkout ./foomezzi revert percorso ./foodal dell'indice ; l'aggiunta HEADindica a Git di riportare quel percorso nell'indice alla sua HEADrevisione prima di farlo.


14

Per me nessuna delle risposte mi è sembrata molto chiara e quindi vorrei aggiungere la mia che sembra super facile.

Ho un commit abc1e dopo aver fatto diverse (o una modifica) su un file file.txt.

Ora dì che ho incasinato qualcosa nel file file.txte voglio tornare a un precedente commit abc1.

1.git checkout file.txt .: questo rimuoverà le modifiche locali, se non ne hai bisogno

2 git checkout abc1 file.txt.: questo porterà il tuo file alla versione desiderata

3 git commit -m "Restored file.txt to version abc1".: questo impegnerà la tua inversione.

  1. git push : questo spingerà tutto sul repository remoto

Tra i passaggi 2 e 3, ovviamente, puoi fare git statusper capire cosa sta succedendo. Di solito dovresti vedere il file.txtgià aggiunto ed è per questo che non c'è bisogno di a git add.


2
OK, quindi immagino che i passaggi 1. e 2. si escludano a vicenda: se abc1 è il tuo ultimo commit non è necessario per 2. e se ci fossero altri commit dopo abc1 puoi fare direttamente 2.
Jean Paul

13
  1. Ripristina il file su un commit specifico
git checkout Last_Stable_commit_Number -- fileName

2. Ripristina il file su un ramo specifico

git checkout branchName_Which_Has_stable_Commit fileName

11

Per passare a una versione precedente del file di commit, ottenere il numero di commit, quindi dire eb917a1

git checkout eb917a1 YourFileName

Se hai solo bisogno di tornare all'ultima versione confermata

git reset HEAD YourFileName
git checkout YourFileName

Questo ti porterà semplicemente all'ultimo stato di commit del file


10

git checkout ref | commitHash - filePath

per esempio

git checkout HEAD~5 -- foo.bar
or 
git checkout 048ee28 -- foo.bar

10

Molte risposte qui affermano di usare git reset ... <file>o, git checkout ... <file>ma facendo così, perderai tutte le modifiche su <file>commit dopo il commit che desideri ripristinare.

Se vuoi ripristinare le modifiche da un commit solo su un singolo file, proprio come git revertfarebbe ma solo per un file (o dire un sottoinsieme dei file di commit), ti suggerisco di usare entrambi git diffe git applycome quello (con <sha>= l'hash del commetti che vuoi ripristinare):

git diff <sha>^ <sha> path/to/file.ext | git apply -R

Fondamentalmente, prima genererà una patch corrispondente alle modifiche che si desidera ripristinare, quindi applicherà la patch in modo inverso per eliminare tali modifiche.

Naturalmente, non funzionerà se le linee ripristinate fossero state modificate da un commit tra <sha1>e HEAD(conflitto).


Questa dovrebbe essere la risposta approvata. Vorrei suggerire una versione leggermente semplificata:git show -p <sha> path/to/file.ext|git apply -R
Amaury D

puoi usare <sha>^!invece di<sha>^ <sha>
cambunctious

8

Utilizzare git logper ottenere la chiave hash per la versione specifica e quindi utilizzaregit checkout <hashkey>

Nota: non dimenticare di digitare l'hash prima dell'ultimo. L'ultimo hash indica la posizione corrente (HEAD) e non cambia nulla.


7

Ovviamente qualcuno deve scrivere un libro intelligibile su git, oppure git deve essere meglio spiegato nella documentazione. Di fronte a questo stesso problema l'ho immaginato

cd <working copy>
git revert master

annullerebbe l'ultimo commit che sembra fare.

Ian


7

Puoi farlo in 4 passaggi:

  1. ripristina l'intero commit con il file che desideri ripristinare in modo specifico: creerà un nuovo commit sul tuo ramo
  2. soft reset quel commit: rimuove il commit e sposta le modifiche nell'area di lavoro
  3. selezionare manualmente i file per ripristinarli e eseguirne il commit
  4. rilascia tutti gli altri file nell'area di lavoro

Cosa devi digitare nel tuo terminale :

  1. git revert <commit_hash>
  2. git reset HEAD~1
  3. git add <file_i_want_to_revert> && git commit -m 'reverting file'
  4. git checkout .

in bocca al lupo


questo non ripristina TUTTE le modifiche?
arcee123,

1
@ arcee123 Sì, ma il ripristino successivo annulla il ripristino di tutte le modifiche. Il problema è che git-revertfunziona solo sull'intero repository, quindi per compensare dobbiamo annullare tutto il resto.
Timothy,

2
Ti consiglio di utilizzare: 1. git revert --no-commit <commit_hash>2. In git reset HEADquesto modo si salva un commit extra fluttuando e si apportano tutte le modifiche solo nella directory di lavoro.
Timothy,

La risposta di @greg-hewgill è migliore e precisa. Questo è scadente e non dovrebbe essere usato.
Daniel Tranca,

Questo è esattamente ciò che è necessario per un vero ripristino di file specifici. Avevo bisogno di annullare le modifiche a pochi file da un commit precedente che erano già state trasferite nel repository remoto. Ho ripristinato, ripristinato e eseguito il commit del risultato: git revert _oldcommit_ --no-commit git reset -- _unchanged1_ _unchanged2_ ... git commit -m "branch without changes to specific files"la nuova punta del ramo rifletteva tutte le modifiche tranne i file ripristinati.
Suncat2000,

7

Questo è un passaggio molto semplice. Esegui il checkout del file sull'ID commit che vogliamo, qui un ID commit prima, e poi basta modificare commit e il gioco è fatto.

# git checkout <previous commit_id> <file_name>
# git commit --amend

Questo è molto utile. Se vogliamo portare qualsiasi file a qualsiasi ID di commit precedente nella parte superiore del commit, possiamo facilmente farlo.


5
git revert <hash>

Ripristina un determinato commit. Sembra che tu pensigit revert che influenzi solo il commit più recente.

Ciò non risolve il problema, se si desidera ripristinare una modifica in un file specifico e il commit è cambiato più di quel file.


5

se si commette un file errato negli ultimi commit, seguire le istruzioni:

  1. albero open source, passare a questo commit

albero open source

  1. cambia le righe e trova il tuo commit che il file sbagliato ha inviato come commit

inserisci qui la descrizione dell'immagine

  1. puoi vedere l'elenco delle tue modifiche in quel commit elenco di file nella struttura di origine
  2. selezionarlo e quindi fare clic su ... pulsanti sul lato destro ... fare clic sul file inverso
  3. quindi puoi vederlo nella scheda dello stato del file in basso a sinistra, quindi fai clic sul palco:

scheda di stato del file

  1. apri il codice di Visual Studio e ripristina ripristinando i file rimossi
  2. dopo tutto, puoi vedere i risultati del tuo ultimo commit nell'albero dei sorgenti

inserisci qui la descrizione dell'immagine

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.