Como recuperar um único arquivo de uma revisão específica no Git?

832

Eu tenho um repositório Git e gostaria de ver como alguns arquivos pareciam alguns meses atrás. Encontrei a revisão nessa data; ele é 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8. Preciso ver como é um arquivo e também salvá-lo como um arquivo ("novo").

Consegui ver o arquivo usando gitk, mas ele não tem uma opção para salvá-lo. Tentei com ferramentas de linha de comando, o mais próximo que cheguei foi:

git-show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8 my_file.txt

No entanto, este comando mostra um diff, e não o conteúdo do arquivo. Sei que depois posso usar algo como PAGER=cate redirecionar a saída para um arquivo, mas não sei como obter o conteúdo real do arquivo.

Basicamente, estou procurando algo como o svn cat .

Milan Babuškov
fonte
73
A chave aqui: git show(sem ajuda) usa sintaxe diferente com dois pontos. git show 2c7cf:my_file.txt
Steve Bennett
4
Para esclarecer melhor, o comando acima está pedindo ao git para mostrar dois objetos separados, uma revisão e um arquivo. A resposta aceita abaixo, que usa dois pontos entre os dois itens, está solicitando um arquivo específico em uma revisão específica.
Jhclark 9/07
2
Em * nix, você não precisa pager, apenas shell redirecionamento de saída com>
Konstantin Pelepelin
Checat tem um comentário importante, para quem deseja que o conteúdo seja exportado para algum arquivo. Você precisa de algo como isto: show de git {sha}: meu_arquivo.txt> old_my_file.txt
ormurin

Respostas:

744

Para completar sua própria resposta, a sintaxe é de fato

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

O comando segue o estilo usual de revisão, o que significa que você pode usar qualquer um dos seguintes:

  1. nome do ramo (como sugerido por ash )
  2. HEAD+ x número de ^caracteres
  3. O hash SHA1 de uma determinada revisão
  4. Os primeiros (talvez 5) caracteres de um determinado hash SHA1

Dica É importante lembrar que, ao usar " git show", sempre especifique um caminho a partir da raiz do repositório , não da posição atual do diretório.

(Embora Mike Morearty mencione que, pelo menos com o git 1.7.5.4, você pode especificar um caminho relativo colocando " ./" no início do caminho - por exemplo:

git show HEAD^^:./test.py

)


Com o Git 2.23+ (agosto de 2019), você também pode usar o git restore que substitui o git checkoutcomando confuso

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

Isso restauraria na árvore de trabalho somente o arquivo como presente na "origem" ( -s) confirma o SHA1 ou ramo somebranch.
Para restaurar também o índice:

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

( -SW: abreviação de --staged --worktree)


Antes do git1.5.x, isso era feito com algum encanamento:

git ls-tree <rev>
mostra uma lista de um ou mais objetos 'blob' dentro de uma confirmação

git cat-file blob <file-SHA1>
cat um arquivo como foi confirmado em uma revisão específica (semelhante ao svn cat). use git ls-tree para recuperar o valor de um determinado arquivo sha1

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

O git-ls-tree lista o ID do objeto para o arquivo $ na revisão $ REV, isso é cortado da saída e usado como argumento para o git-cat-file, que realmente deve ser chamado de git-cat-object e simplesmente despeja esse objeto para stdout.


Nota: desde o Git 2.11 (quarto trimestre de 2016), você pode aplicar um filtro de conteúdo à git cat-filesaída!

Consulte commit 3214594 , commit 7bcf341 (09 de setembro de 2016), commit 7bcf341 (09 de setembro de 2016) e commit b9e62f6 , commit 16dcc29 (24 de agosto de 2016) por Johannes Schindelin ( dscho) .
(Mesclado por Junio ​​C Hamano - gitster- na confirmação 7889ed2 , 21 de setembro de 2016)

cat-file: suporte --textconv/ --filtersem lote

Mesmo que " git hash-objects", que é uma ferramenta para pegar um fluxo de dados no sistema de arquivos e colocá-lo no repositório de objetos Git, tenha permissão para executar as conversões "fora do mundo para o Git" (por exemplo, conversões de fim de linha e aplicativos do filtro limpo), e tinha o recurso ativado por padrão desde os primeiros dias, sua operação reversa " git cat-file", que pega um objeto do armazenamento de objetos Git e externaliza para o consumo pelo mundo externo, não tinha um mecanismo equivalente ao execute o "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" iniciou o segfaulting recentemente (2017), que foi corrigido no Git 2.15 (quarto trimestre de 2017)

Veja commit cc0ea7c (21 de setembro de 2017) por Jeff King ( peff) .
(Mesclado por Junio ​​C Hamano - gitster- in commit bfbc2fc , 28 de setembro de 2017)


Observe que, para substituir / substituir um arquivo por um conteúdo passado, você não deve mais usar o comando confusogit checkout , mas git restore(Git 2.23+, agosto de 2019)

git restore -s <SHA1> -- afile

Isso restauraria na árvore de trabalho apenas o arquivo presente na -sconfirmação "source" ( ) SHA1.
Para restaurar também o índice:

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

( -SW: abreviação de --staged --worktree)

VonC
fonte
6
@Oscar, já que git showbasicamente despeja o conteúdo na stdout(saída padrão), você pode simplesmente redirecionar essa saída para qualquer arquivo que desejar ( tldp.org/LDP/abs/html/io-redirection.html ).
VonC
8
git checkout [branch | revision] filepathé o comando certo
Gaui
12
@Gaui mas git checkoutiria substituir o arquivo por uma outra versão, ao contrário git show, o que permite que você salve-o com um nome diferente, para que você chegar e ver tanto (a versão atual ea versão antiga). Não está claro se o OP deseja substituir sua versão atual por uma antiga.
VonC 6/06/2013
9
Gostaria de observar que ^^^também pode ser escrito de maneira mais geral como ~~~ou, melhor ~3. O uso de tildes também tem a vantagem de não acionar a correspondência de nome de arquivo de alguns shells (zsh, por exemplo).
Eric O Lebigot
2
Não tenho um git suficientemente antigo para verificar: o pré-1.5.x git rev-parselida com rev:pathsintaxe? (Em um git mais recente que puder git cat-file -p $REV:path. No entanto, git showtrabalha para caminhos de diretório, bem como, portanto, não é apenas mais curto, geralmente é mais perto do que se quer.)
Torek
510

Se você deseja substituir / substituir o conteúdo de um arquivo em sua ramificação atual pelo conteúdo de um arquivo de uma consolidação anterior ou de uma ramificação diferente, você pode fazer isso com estes comandos:

git checkout 08618129e66127921fbfcbc205a06153c92622fe path/to/file.txt

ou

git checkout mybranchname path/to/file.txt

Você precisará confirmar essas alterações para que sejam efetivas na ramificação atual.

ca
fonte
4
solução mais simples e é para isso que o git-checkout foi projetado - especificar o nome do caminho significa que apenas o arquivo correspondente é retirado. Da página de manual do git-checkout: mestre do git checkout ~ 2 Makefile
RichVel
1
Então, como você volta ao estado anterior antes de executar este comando?
Flint
@Flint se você estiver vindo do estado HEAD, seria tão simples quanto o git checkout HEAD - [caminho completo].
Tiago Espinha
72
Observe que isso substitui o arquivo existente nesse caminho, enquanto a git show SHA1:PATHsolução é impressa apenas no stdout.
Flimm
Agradável! Eu não teria sido capaz de descobrir isso olhando git help checkout. I fez check-out um subdiretório a partir de uma determinada data, e usando esta abordagem, eu poderia obter esta sintaxe de trabalho:git checkout @{YYYY-MM-DD} sub-dir
haridsv
150

Você precisa fornecer o caminho completo para o arquivo:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:full/repo/path/to/my_file.txt
Milan Babuškov
fonte
7
não precisa ser o caminho completo. Caminho do diretório raiz git (aqueles que vieram git show --name-onlyé suficiente também
Mohsen
7
Caminho completo da raiz do repositório. Dê uma olhada melhor no exemplo que eu dei. Não há barra antes de "cheia".
Milan Babuškov 26/06
7
Para sua informação, se você estiver em um subdiretório, poderá usar ./filename.ext com êxito também.
viajante
Eu acho que o ponto é que, se você estiver dentro full/repo/path/toe tentar:, git show 27cf8e84:my_file.txtvocê será recompensado com uma mensagem como: fatal: O caminho 'full / repo / path / to / my_file.txt' existe, mas não 'my_file.txt' . Você quis dizer '27cf8e84: full / repo / path / to / my_file.txt' 'aka' 27cf8e84: ./ my_file.txt '? É como se o Git pudesse ter ajudado diretamente, mas optou por ser pedante aqui.
Ed Randall
101

A maneira mais fácil é escrever:

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

Onde:

  • HASH é o número de hash da revisão Git SHA-1
  • file / path / name.ext é o nome do arquivo que você está procurando
  • some_new_name.ext é o caminho e o nome em que o arquivo antigo deve ser salvo

Exemplo

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

Isso salvará my_file.txt da revisão 27cf8e como um novo arquivo com o nome my_file.txt.OLD

Foi testado com o Git 2.4.5.

Se você deseja recuperar o arquivo excluído, pode usar HASH~1(uma confirmação antes do HASH especificado).

EXEMPLO:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8~1:deleted_file.txt > deleted_file.txt
jmarceli
fonte
1
Informações adicionais: Você pode obter o HASH, por exemplo, com o git log
xotix
@xotix Obrigado. Eu tenho toda a história de hash para um determinado arquivo usandogit log file/path/name.ext
Sriram Kannan
11

No Windows, com Git Bash:

  • no seu espaço de trabalho, altere o diretório para a pasta onde o arquivo está localizado
  • git show cab485c83b53d56846eb883babaaf4dff2f2cc46:./your_file.ext > old.ext
Alessandro Jacopson
fonte
8

E para despejá-lo em um arquivo (pelo menos no Windows) - Git Bash:

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

As "aspas são necessárias para preservar novas linhas.

Mr_and_Mrs_D
fonte
Agradável. +1. Boa adição à git showsintaxe que mencionei acima.
VonC 01/01
23
Realmente não entendo por que você usaria eco, com ou sem as aspas. E eu não entendo por que você desejaria a forma de redirecionamento de saída anexada. Não seria melhor simplesmente escrever: git show 60d8bdfc: src / services / LocationMonitor.java> LM_60d8bdfc.java Se, por algum motivo, você realmente quisesse forçar finais de linha no estilo dos, você poderia canalizá-lo através do unix2dos. Mas nunca achei tão útil reter os finais de linha dos no Windows, pois qualquer ferramenta de texto que não seja o bloco de notas que usei no Windows lida com as linhas de estilo unix.
sootsnoot
4
git show 60d8bdfc: src / services / LocationMonitor.java >> O LM_60d8bdfc.java funcionou para mim.
precisa saber é o seguinte
@ Mike: você está no Windows?
precisa saber é o seguinte
2
não use aspas duplas porque se os caracteres do seu arquivo que se parecem com uma variável do shell, ou seja, $ LANG, serão substituídos. @ LưuVĩnhPhúc é protetor. também não use >> Ele anexará o arquivo se ele existir e poderá levar a erros
theguy
3

Isso ajudará você a obter todos os arquivos excluídos entre confirmações sem especificar o caminho, útil se houver muitos arquivos excluídos.

git diff --name-only --diff-filter=D $commit~1 $commit | xargs git checkout $commit~1
Adrian Gunawan
fonte
1
git checkout {SHA1} -- filename

este comando obtém o arquivo copiado de confirmação específica.

jsina
fonte
-2

Obtenha o arquivo de uma consolidação anterior através do check-out da consolidação anterior e do arquivo de cópia.

  • Observe em qual filial você está: git branch
  • Faça o checkout do commit anterior que você deseja: git checkout 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8
  • Copie o arquivo que você deseja para um local temporário
  • Faça o checkout da filial em que você começou: git checkout theBranchYouNoted
  • Copie o arquivo que você colocou em um local temporário
  • Confirme sua alteração no git: git commit -m "added file ?? from previous commit"
rocketInABog
fonte