Come recuperare un singolo file da una revisione specifica in Git?


832

Ho un repository Git e mi piacerebbe vedere come apparivano alcuni file qualche mese fa. Ho trovato la revisione a quella data; lo è 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8. Devo vedere come appare un file e salvarlo anche come ("nuovo") file.

Sono riuscito a vedere il file usando gitk, ma non ha un'opzione per salvarlo. Ho provato con gli strumenti da riga di comando, il più vicino che ho ottenuto è stato:

git-show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8 my_file.txt

Tuttavia, questo comando mostra un diff e non il contenuto del file. So che in seguito potrò usare qualcosa di simile PAGER=cate reindirizzare l'output su un file, ma non so come arrivare al contenuto effettivo del file.

Fondamentalmente, sto cercando qualcosa come svn cat .


73
La chiave qui: git show(inutilmente) usa una sintassi diversa con due punti. git show 2c7cf:my_file.txt
Steve Bennett,

4
Per chiarire ulteriormente, il comando precedente chiede a git di mostrare due oggetti separati, una revisione e un file. La risposta accettata di seguito, che utilizza i due punti tra i due elementi, richiede un file specifico a una revisione specifica.
jhclark,

2
Su * nix non hai bisogno di PAGER, basta reindirizzare l'output della shell con>
Konstantin Pelepelin,


Checat ha un commento importante, per coloro che vogliono esportare il contenuto in un file. Hai bisogno di qualcosa del genere: git show {sha}: my_file.txt> old_my_file.txt
ormurin

Risposte:


744

Per completare la tua risposta, la sintassi è davvero

git show object
git show $REV:$FILE
git show somebranch:from/the/root/myfile.txt
git show HEAD^^^:test/test.py

Il comando utilizza il solito stile di revisione, il che significa che è possibile utilizzare uno dei seguenti:

  1. nome del ramo (come suggerito da ash )
  2. HEAD+ x numero di ^caratteri
  3. L'hash SHA1 di una data revisione
  4. I primi (forse 5) personaggi di un dato hash SHA1

Suggerimento È importante ricordare che quando si utilizza " git show", specificare sempre un percorso dalla radice del repository , non la posizione corrente della directory.

(Sebbene Mike Morearty menzioni che, almeno con git 1.7.5.4, è possibile specificare un percorso relativo inserendo " ./" all'inizio del percorso, ad esempio:

git show HEAD^^:./test.py

)


Con Git 2.23+ (agosto 2019), puoi anche utilizzare git restore che sostituisce il git checkoutcomando confuso

git restore -s <SHA1>     -- afile
git restore -s somebranch -- afile

Ciò ripristinerebbe sull'albero di lavoro solo il file presente nel "source" ( -s) commit SHA1 o ramo somebranch.
Per ripristinare anche l'indice:

git restore -s <SHA1> -SW -- afile

( -SW: abbreviazione di --staged --worktree)


Prima di git1.5.x, ciò è stato fatto con alcuni impianti idraulici:

git ls-tree <rev>
mostra un elenco di uno o più oggetti "BLOB" all'interno di un commit

git cat-file blob <file-SHA1>
cat un file come è stato eseguito il commit in una revisione specifica (simile a svn cat). usa git ls-tree per recuperare il valore di un dato file-sha1

git cat-file -p $(git-ls-tree $REV $file | cut -d " " -f 3 | cut -f 1)::

git-ls-tree elenca l'ID oggetto per $ file nella revisione $ REV, questo viene tagliato dall'output e usato come argomento per git-cat-file, che in realtà dovrebbe essere chiamato git-cat-object, e semplicemente dump quell'oggetto a stdout.


Nota: da Git 2.11 (Q4 2016), è possibile applicare un filtro di contenuto git cat-fileall'output!

Vedi commit 3214594 , commit 7bcf341 (09 set 2016), commit 7bcf341 (09 set 2016) e commit b9e62f6 , commit 16dcc29 (24 ago 2016) di Johannes Schindelin ( dscho) .
(Unita da Junio ​​C Hamano - gitster- in commit 7889ed2 , 21 set 2016)

cat-file: supporto --textconv/ --filtersin modalità batch

Anche se " git hash-objects", che è uno strumento per prendere un flusso di dati sul filesystem e inserirlo nell'archivio oggetti Git, ha permesso di eseguire le conversioni "da mondo esterno a Git" (ad esempio conversioni e applicazioni di fine linea del filtro pulito), e aveva la funzione attiva di default fin dai primissimi tempi, la sua operazione inversa " git cat-file", che prende un oggetto dal negozio di oggetti Git ed esternalizza per il consumo da parte del mondo esterno, mancava di un meccanismo equivalente a eseguire il "Git-to-outside-world"

git config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <"
git cat-file --textconv --batch

Nota: " git cat-file --textconv" ha iniziato a segfaulting di recente (2017), che è stato corretto in Git 2.15 (4 ° trimestre 2017)

Vedi commit cc0ea7c (21 set 2017) di Jeff King ( peff) .
(Unita da Junio ​​C Hamano - gitster- in commit bfbc2fc , 28 set 2017)


Nota che per sovrascrivere / sostituire un file con un contenuto passato, non dovresti più usare il comando confusogit checkout , ma git restore(Git 2.23+, Agosto 2019)

git restore -s <SHA1> -- afile

Ciò ripristinerebbe sull'albero di lavoro solo il file presente nel -scommit "source" ( ) SHA1.
Per ripristinare anche l'indice:

git restore -s <SHA1> -SW -- afile

( -SW: abbreviazione di --staged --worktree)


6
@Oscar poiché git showessenzialmente scarica il contenuto su stdout(output standard), potresti semplicemente reindirizzare l'output su qualsiasi file tu voglia ( tldp.org/LDP/abs/html/io-redirection.html ).
VonC,

8
git checkout [branch | revision] filepathè il comando giusto
Gaui,

12
@Gaui ma git checkoutsarebbe ignorare il file da un'altra versione, a differenza di git show, che consente di salvare con un nome diverso, in modo per voi per ottenere e vedere entrambi (la versione corrente e la vecchia versione). Non è chiaro dalla domanda se l'OP voglia sostituire la sua versione attuale con una vecchia.
VonC,

9
Vorrei sottolineare che ^^^può anche essere scritta più in generale come ~~~, o meglio ~3. L'uso delle tilde ha anche il vantaggio di non innescare la corrispondenza del nome file di alcune shell (ad esempio zsh).
Eric O Lebigot,

2
Non ho un git sufficientemente vecchio da controllare: pre-1.5.x git rev-parsegestisce la rev:pathsintassi? (In un git più recente puoi git cat-file -p $REV:path. Tuttavia, git showfunziona anche per i percorsi delle directory, quindi non è solo più breve, di solito è più vicino a ciò che si desidera.)
torek,

510

Se desideri sostituire / sovrascrivere il contenuto di un file nel tuo ramo corrente con il contenuto del file da un commit precedente o un ramo diverso, puoi farlo con questi comandi:

git checkout 08618129e66127921fbfcbc205a06153c92622fe path/to/file.txt

o

git checkout mybranchname path/to/file.txt

Dovrai quindi eseguire il commit di tali modifiche per renderle effettive nel ramo corrente.


4
soluzione più semplice e questo è ciò per cui git-checkout è progettato: specificare il nome del percorso significa che viene estratto solo il file corrispondente. Dalla pagina man di git-checkout: git checkout master ~ 2 Makefile
RichVel

1
Quindi, come tornare allo stato precedente prima di eseguire questo comando?
Flint,

@Flint se provieni dallo stato HEAD sarebbe semplice come git checkout HEAD - [percorso completo].
Tiago Espinha,

72
Si noti che ciò sovrascrive il file esistente in quel percorso, mentre la git show SHA1:PATHsoluzione stampa solo su stdout.
Flimm,

Bello! Non sarei stato in grado di capirlo guardando git help checkout. Ho dovuto fare il checkout di una sottodirectory a partire da una certa data e, usando questo approccio, ho potuto far funzionare questa sintassi:git checkout @{YYYY-MM-DD} sub-dir
haridsv

150

Devi fornire il percorso completo del file:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:full/repo/path/to/my_file.txt

7
non deve essere percorso completo. Percorso dalla directory radice di Git (anche quelli che sono entrati git show --name-onlysono abbastanza
Mohsen,

7
Ehm, percorso completo dalla radice del repository. Dai un'occhiata migliore all'esempio che ho dato. Non vi è alcuna barra iniziale prima di "pieno".
Milan Babuškov,

7
Cordiali saluti, se ci si trova in un sottodir, è possibile utilizzare anche ./filename.ext correttamente.
Viaggiatore

Penso che il punto sia, se ci sei full/repo/path/toe provi :,git show 27cf8e84:my_file.txt verrai ricompensato con un messaggio come: fatale: esiste il percorso 'full / repo / path / to / my_file.txt', ma non 'my_file.txt' . Intendevi '27cf8e84: full / repo / path / to / my_file.txt' aka '27cf8e84: ./ my_file.txt'? È come se Git avrebbe potuto aiutare direttamente, ma ha scelto di essere pedante qui.
Ed Randall,

101

Il modo più semplice è scrivere:

git show HASH:file/path/name.ext > some_new_name.ext

dove:

  • HASH è il numero di hash SHA-1 della revisione Git
  • file / path / name.ext è il nome del file che stai cercando
  • some_new_name.ext è il percorso e il nome in cui il vecchio file deve essere salvato

Esempio

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:my_file.txt > my_file.txt.OLD

Ciò salverà my_file.txt dalla revisione 27cf8e come nuovo file con nome my_file.txt.OLD

È stato testato con Git 2.4.5.

Se si desidera recuperare il file eliminato , è possibile utilizzare HASH~1(un commit prima di HASH specificato).

ESEMPIO:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8~1:deleted_file.txt > deleted_file.txt

1
Ulteriori informazioni: è possibile ottenere l'HASH, ad esempio con git log
xotix,

@xotix Grazie. Ho ottenuto tutta la cronologia HASH per un determinato file usandogit log file/path/name.ext
Sriram Kannan il

11

In Windows, con Git Bash:

  • nell'area di lavoro, cambia dir nella cartella in cui risiede il tuo file
  • git show cab485c83b53d56846eb883babaaf4dff2f2cc46:./your_file.ext > old.ext

8

E per scaricarlo bene in un file (almeno su Windows) - Git Bash:

$ echo "`git show 60d8bdfc:src/services/LocationMonitor.java`" >> LM_60d8bdfc.java

Le "virgolette sono necessarie per preservare le nuove righe.


Ben fatto. +1. Buona aggiunta alla git showsintassi che menziono sopra.
VonC

23
Davvero non capisco perché dovresti usare l'eco, con o senza le virgolette. E non capisco perché vorresti la forma di aggiunta del reindirizzamento dell'output. Non sarebbe meglio semplicemente scrivere: git show 60d8bdfc: src / services / LocationMonitor.java> LM_60d8bdfc.java Se per qualche motivo volessi davvero forzare terminazioni di linea in stile Dos, potresti reindirizzarlo attraverso unix2dos. Ma non ho mai trovato il minimo utile per conservare le terminazioni DOS su Windows, poiché qualsiasi strumento di testo diverso dal blocco note che ho usato su Windows gestisce bene le linee in stile unix.
sootsnoot,

4
git show 60d8bdfc: src / services / LocationMonitor.java >> LM_60d8bdfc.java ha lavorato per me.
Mike6679,

@Mike: sei su Windows?
Mr_and_Mrs_D

2
non usare virgolette doppie perché se i tuoi caratteri di file che assomigliano a una variabile di shell, ad esempio $ LANG, verranno sostituiti. @ LưuVĩnhPhúc è risparmiatore. inoltre non utilizzare >> Aggiungerà il file se esistesse e potrebbe causare errori
theguy

3

Questo ti aiuterà a ottenere tutti i file eliminati tra i commit senza specificare il percorso, utile se ci sono molti file eliminati.

git diff --name-only --diff-filter=D $commit~1 $commit | xargs git checkout $commit~1

1
git checkout {SHA1} -- filename

questo comando ottiene il file copiato dal commit specifico.


-2

Ottieni il file da un commit precedente tramite il check-out del commit precedente e la copia del file.

  • Nota su quale ramo ti trovi: git branch
  • Verifica il commit precedente che desideri: git checkout 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8
  • Copia il file che desideri in una posizione temporanea
  • Dai un'occhiata alla filiale da cui hai iniziato: git checkout theBranchYouNoted
  • Copia il file che hai inserito in una posizione temporanea
  • Commetti la tua modifica su git: git commit -m "added file ?? from previous 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.