Encontre e restaure um arquivo excluído em um repositório Git

2802

Digamos que eu esteja em um repositório Git. Excluo um arquivo e confirmo essa alteração. Continuo trabalhando e faço mais algumas confirmações. Então, acho que preciso restaurar esse arquivo.

Sei que posso fazer check-out de um arquivo usando git checkout HEAD^ foo.bar, mas realmente não sei quando esse arquivo foi excluído.

  1. Qual seria a maneira mais rápida de encontrar a confirmação que excluiu um determinado nome de arquivo?
  2. Qual seria a maneira mais fácil de recuperar esse arquivo na minha cópia de trabalho?

Espero não ter que procurar meus logs manualmente, fazer check-out de todo o projeto para um determinado SHA e copiar manualmente esse arquivo no check-out do projeto original.

avdgaag
fonte
39
observe que o comentário anterior responde à pergunta no título, não no corpo - isso inclui descobrir quando o arquivo foi excluído.
Avdgaag
8
Para encontrar o commit, um arquivo foi excluído em:git log --diff-filter=D -- path/to/file
titaniumdecoy
54
@hhh git checkout deletedFilecancelará a exclusão deletedFilese tiver sido excluído, mas essa exclusão ainda não foi realizada ou confirmada . Não é isso que a pergunta aqui está pedindo; Esta pergunta é sobre como restaurar um arquivo cuja exclusão foi confirmada há muitos commits atrás.
Mark Amery

Respostas:

3150

Encontre a última confirmação que afetou o caminho especificado. Como o arquivo não está no commit HEAD, esse commit deve ter excluído.

git rev-list -n 1 HEAD -- <file_path>

Em seguida, faça o check-out da versão no commit antes, usando o ^símbolo caret ( ):

git checkout <deleting_commit>^ -- <file_path>

Ou em um comando, se $fileé o arquivo em questão.

git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file"

Se você estiver usando zsh e tiver a opção EXTENDED_GLOB ativada, o símbolo de sinal de intercalação não funcionará. Você pode usar em seu ~1lugar.

git checkout $(git rev-list -n 1 HEAD -- "$file")~1 -- "$file"
CB Bailey
fonte
94
A parte complicada é fazer o checkout do commit ANTES, usando o sufixo ^. Obrigado.
Christian Oudard
4
Por alguma razão, isso não funcionará no zsh. ± git checkout $(git rev-list -n 1 HEAD "spec/Sporkfile_example.rb")^ -- "spec/Sporkfile_example.rb" zsh: no matches found: b71c152d8f38dcd23ad7600a93f261a7252c59e9^ Eu mudei para o bash e funcionou bem.
Zoras
20
Na linha de comando do Windows, recebi um erro. error: pathspec <filename> did not match any file(s) known to git.. A solução foi usar o git bash.
Donturner 26/07/12
56
@zoras zsh tem sua própria expansão em '^' eu acredito, mas você pode usar a sintaxe alternativa de '~ 1': git checkout <deleting-commit>~1 -- <file-path> ~ X permite especificar X confirmações antes da confirmação especificada, então ~ 1 é a confirmação antes, ~ 2 é duas submissões antes, etc
Nils Luxton
22
No prompt do cmd do windows, o ^caractere é o caractere de escape! Portanto, no cmd, você deve digitar ^^para dizer ao cmd que deseja um único literal ^ e que não está escapando de outra coisa depois dele. O que está acontecendo com muitas pessoas é que isso ^é seguido por um espaço. Portanto, o cmd acha que você está escapando do espaço - o que gera apenas um caractere de espaço. Assim, no momento em que o git obtém os argumentos cli, ele vê SHA1e não SHA1^ . É realmente irritante. ~não é um personagem de escape, é por isso que ainda funciona. (PS. Se você acha que os googlers desejam essas informações, vote este comentário com mais satisfação) #
Alexander Bird
875
  1. Usar git log --diff-filter=D --summary para obter todas as confirmações que excluíram arquivos e os arquivos excluídos;
  2. Use git checkout $commit~1 path/to/file.extpara restaurar o arquivo excluído.

Onde $commitestá o valor do commit que você encontrou na etapa 1, por exemploe4cf499627

Robert Munteanu
fonte
10
curioso, a que se refere o ~ 1?
tommy Chheng
7
@ tommy - a especificação til lhe dará o enésimo neto do commit nomeado. Consulte book.git-scm.com/4_git_treeishes.html para obter mais detalhes.
Robert Munteanu 23/07
5
essa é de longe a abordagem mais fácil e intuitiva. git log -- *PartOfMyFileName*. Obrigado por$commit~1
bgs
3
a git checkout $commit~1 filenamesintaxe funciona perfeitamente para arquivos individuais e também para diretórios inteiros. ou seja: para restaurar todas as imagens apagadas em ./images de sha 12345: git checkout 12345~1 images. obrigado por esta resposta!
noinput
34
@Alexar $commit~1significa que você deve adicionar o nome da confirmação. Algo como 1d0c9ef6eb4e39488490543570c31c2ff594426conde $commitestá.
927 Eugene
319

Para restaurar todos os arquivos excluídos em uma pasta, digite o seguinte comando.

git ls-files -d | xargs git checkout --
Manu
fonte
1
Para onde os arquivos são direcionados? Não vejo mudança.
William Grand
21
Este é provavelmente o método mais fácil. É pervertido o quão difícil gittornou até a tarefa mais simples.
JWW
git checkout - [arquivo] reverterá as alterações no [arquivo]. O canal substituirá [arquivo] pelo nome dos arquivos excluídos.
Manu
6
O ls-filessubcomando é útil, mas parece não funcionar para arquivos que foram removidos com git rm, por exemplo, estadiados, muito menos confirmados, que é o que o OP pediu.
MarkHu
Isso funcionou para restaurar os arquivos excluídos, mas como posso atualizar os arquivos que foram alterados e exibidos como M myChangedFiledepois git checkout?
Libby
124

Cheguei a esta pergunta procurando restaurar um arquivo que acabei de excluir, mas ainda não havia confirmado a alteração. Caso você se encontre nessa situação, tudo o que você precisa fazer é o seguinte:

git checkout HEAD -- path/to/file.ext

Brett
fonte
93

Se você é louco, use git-bisect. Aqui está o que fazer:

git bisect start
git bisect bad
git bisect good <some commit where you know the file existed>

Agora é hora de executar o teste automatizado. O comando shell'[ -e foo.bar ]' retornará 0 se foo.barexistir e 1 caso contrário. O comando "run" do git-bisectusará a pesquisa binária para encontrar automaticamente o primeiro commit no qual o teste falha. Começa na metade do intervalo fornecido (de bom a ruim) e o corta ao meio com base no resultado do teste especificado.

git bisect run '[ -e foo.bar ]'

Agora você está no commit que o excluiu. A partir daqui, você pode voltar ao futuro e usargit-revert para desfazer a alteração,

git bisect reset
git revert <the offending commit>

ou você pode voltar um commit e inspecionar manualmente o dano:

git checkout HEAD^
cp foo.bar /tmp
git bisect reset
cp /tmp/foo.bar .
Josh Lee
fonte
2
Você poderia elaborar git bisect run '[ -e foo.bar ]'?
avdgaag 04/06/09
Você também pode usar boas e más manualmente, se for algo que não pode ser verificado automaticamente. Veja a página de manual do bisect.
Josh Lee
1
@avdgaag git bisect rundiz ao Git para automatizar a bissecção, executando o comando após a palavra 'run', onde o comando deve retornar 0para uma goodversão (veja git help bisectpara detalhes). A '[ -e foo.bar ]'é uma expressão padrão para testar se o arquivo foo.barnão existe (a implementação é geralmente no arquivo /usr/bin/[que geralmente é hardlinked a /usr/bin/test) e as marcas quation individuais são usados para colocar isso tudo como um único argumento de linha de comando.
Mikko Rantalainen 18/03/2013
Boa ideia. Tentei essa abordagem e ele identificou uma confirmação antes da exclusão, mas não a confirmação que realmente excluiu o arquivo. E em outro teste, identificou 2 confirmações antes da exclusão.
Michael Osofsky
Insano? Talvez. Mas o bisect é uma ótima maneira de ajudar a encontrar onde um bug foi introduzido e, portanto, é uma habilidade valiosa para aprender. Portanto, embora talvez não seja o caminho 'correto' ou o mais 'correto' aqui, ainda é uma boa ideia e vale um +1 definitivamente!
Pryftan
77

Meu novo alias favorito, com base em bonyiii 's resposta (upvoted), e minha própria resposta sobre ' passar um argumento para um comando apelido Git ':

git config alias.restore '!f() { git checkout $(git rev-list -n 1 HEAD -- $1)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- $1)~1 | grep '^D' | cut -f 2); }; f'

Perdi um arquivo, excluído por engano há alguns commits atrás?
Rápido:

git restore my_deleted_file

Crise evitada.

Aviso, com o Git 2.23 (terceiro trimestre de 2019), vem o comando experimental chamado git restore(!).
Então, renomeie esse alias (como mostrado abaixo).


Robert Dailey propõe nos comentários o seguinte apelido:

restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"

E jegan acrescenta nos comentários :

Para definir o alias na linha de comando, usei este comando:

git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\"" 
VonC
fonte
7
Isso restaura todo o commit, não apenas o arquivo solicitado.
Daniel bang
5
Aqui é o meu apelido, funciona maravilhosamente:restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"
void.pointer
1
@RobertDailey Isso parece ótimo! Incluí seu apelido na resposta para obter mais visibilidade.
VonC 13/03/14
1
Para definir o alias a partir da linha de comando, usei este comando:git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\""
jegan
2
Expansion of alias 'restore' failed; '!git' is not a git command
Karl Morrison
55

Se você conhece o nome do arquivo, é uma maneira fácil com os comandos básicos:

Liste todas as confirmações para esse arquivo.

git log -- path/to/file

A última confirmação (mais acima) é a que excluiu o arquivo. Portanto, você precisa restaurar o penúltimo commit.

git checkout {second to last commit} -- path/to/file
wisbucky
fonte
Apenas usei esta solução e não houve confirmação para a exclusão. Consegui restaurar o arquivo usando o ID de confirmação mais recente.
Adam
A penúltima confirmação (confirmação anterior à exclusão) não conteria a versão mais recente do arquivo excluído? O penúltimo (o commit antes do commit anterior para a exclusão) poderia estar irremediavelmente desatualizado.
precisa saber é o seguinte
1
Esta é a primeira solução que vi que é simples o suficiente para que não precise voltar aqui para encontrá-la na próxima vez. Talvez.
Eloff
@ Suncat2000 "penúltimo a último" significa "confirmação anterior à exclusão", o mesmo que "penúltimo". en.wiktionary.org/wiki/penultimate#Synonyms
wisbucky
Agradecemos um zilhão de vezes por esta resposta !!!!!
Rakesh Bk
29

Para restaurar um arquivo excluído e confirmado:

git reset HEAD some/path
git checkout -- some/path

Foi testado no Git versão 1.7.5.4.

Fedir RYKHTIK
fonte
1
Isso não funcionou para mim. Após a finalização da compra, error: pathspec 'foo' did not match any file(s) known to git.verifiquei se o nome do arquivo estava correto. Git versão 2.7.0
wisbucky 27/02
-1; isto está errado. Esses comandos desfarão uma exclusão que ainda não foi confirmada (o primeiro desfaz a exclusão, se for preparada, e o segundo descarta alterações não preparadas no arquivo), mas você está afirmando aqui que eles restaurarão uma restauração confirmada exclusão do arquivo, o que simplesmente não é verdadeiro e falhará com um erro como esse no comentário do @ wisbucky acima.
Mark Amery
@MarkAmery De fato, acho que esse comando funcionou bem para os desenvolvedores, que não fizeram uma preparação explícita para confirmar os arquivos removidos git add -A, mas o arquivo restaurado ainda estava no estágio não confirmado.
Fedir RYKHTIK
25

Se você fez apenas alterações e excluiu um arquivo, mas não o comprometeu, e agora você terminou suas alterações

git checkout -- .

mas seus arquivos excluídos não retornaram, basta executar o seguinte comando:

git checkout <file_path>

E pronto, seu arquivo está de volta.

Paulo Linhares - Packapps
fonte
24

Eu tenho essa solução .

  1. Obtenha o ID do commit no qual o arquivo foi excluído usando uma das maneiras abaixo.

    • git log --grep=*word*
    • git log -Sword
    • git log | grep --context=5 *word*
    • git log --stat | grep --context=5 *word* # recomendado se você mal se lembra de nada
  2. Você deve obter algo como:

confirmar bfe68bd117e1091c96d2976c99b3bcc8310bebe7 Autor: Alexander Orlov Data: quinta-feira 12 de maio 23:44:27 2011 +200

replaced deprecated GWT class
- gwtI18nKeySync.sh, an outdated (?, replaced by a Maven goal) I18n generation script

commit 3ea4e3af253ac6fd1691ff6bb89c964f54802302 Autor: Alexander Orlov Data: quinta-feira 12 de maio 22:10:22 2011 +0200

3 . Agora, usando o ID de confirmação bfe68bd117e1091c96d2976c99b3bcc8310bebe7, faça:

git checkout bfe68bd117e1091c96d2976c99b3bcc8310bebe7^1 yourDeletedFile.java

Como o id de confirmação faz referência ao commit em que o arquivo já foi excluído, é necessário referenciar o commit imediatamente antes do bfe68b, o que você pode fazer anexando ^1. Isso significa: me dê o commit antes do bfe68b.

Alex
fonte
Essa é a mesma abordagem que a resposta aceita, mas com mais algumas maneiras de encontrar a confirmação de exclusão. Ainda gosto da abordagem adotada na resposta aceita, mas essas são boas alternativas. Obrigado!
avdgaag
Suponho que primeiro verificar o arquivo excluído e depois (sem alterá-lo) enviá-lo não crie uma cópia do arquivo. Direita? (Eu preciso fazer isso com imagens e um cópias faria o repositório maior)
Stonecrusher
15
git checkout /path/to/deleted.file
user1528493
fonte
Este para minha situação (removido sem intenção) foi a solução mais direta.
Paulo Oliveira
Uma explicação estaria em ordem.
Peter Mortensen
12

git undelete path/to/file.ext

  1. Coloque isso no seu .bash_profile(ou outro arquivo relevante carregado quando você abre um shell de comando):

    git config --global alias.undelete '!sh -c "git checkout $(git rev-list -n 1 HEAD -- $1)^ -- $1" -'
    
  2. Então use:

    git undelete path/to/file.ext
    

Esse alias primeiro verifica para encontrar o último commit onde este arquivo existia e, em seguida, faz um checkout do Git desse caminho do arquivo a partir do último commit onde este arquivo existia. Fonte .

Beau Smith
fonte
11

Em muitos casos, pode ser útil usar coreutils (grep, sed, etc.) em conjunto com o Git. Eu já conheço essas ferramentas muito bem, mas Git menos. Se eu quisesse pesquisar por um arquivo excluído, faria o seguinte:

git log --raw | grep -B 30 $'D\t.*deleted_file.c'

Quando encontro a revisão / confirmação:

git checkout <rev>^ -- path/to/refound/deleted_file.c

Assim como outros declararam antes de mim.

O arquivo agora será restaurado para o estado que tinha antes da remoção. Lembre-se de reenviá-lo para a árvore de trabalho, se quiser mantê-lo por perto.

Thomas E
fonte
7

Eu tive que restaurar um monte de arquivos excluídos de uma confirmação específica e o gerenciei com dois comandos:

git show <rev> --diff-filter=D --summary --name-only --no-commit-id | xargs git checkout <rev>^ -- 
git show <rev> --diff-filter=D --summary --name-only --no-commit-id | xargs git reset HEAD 

(Observe o espaço à direita no final de cada comando.)

Os arquivos foram adicionados ao arquivo .gitignore e depois limpos com git rm. Eu precisava restaurar os arquivos, mas depois descompactá-los. Eu tinha centenas de arquivos para restaurar, e digitar manualmente as coisas para cada arquivo, como nos outros exemplos, seria muito lento.

kzar
fonte
7

Na verdade, essa pergunta é diretamente sobre o Git, mas alguém como eu trabalha com ferramentas da GUI como o WebStorm VCS, além de conhecer os comandos da CLI do Git.

Clique com o botão direito do mouse no caminho que contém o arquivo excluído e, em seguida, vá para Git e clique em Mostrar histórico .

Digite a descrição da imagem aqui

As ferramentas VCS mostram todas as revisões de treinamento e eu posso ver todos os commits e alterações de cada uma delas.

Digite a descrição da imagem aqui

Depois, seleciono as confirmações que meu amigo exclui o PostAd.jsarquivo. agora veja abaixo:

Digite a descrição da imagem aqui

E agora, posso ver meu arquivo excluído. Eu apenas clico duas vezes no nome do arquivo e ele se recupera.

Digite a descrição da imagem aqui

Eu sei que minha resposta não são comandos do Git, mas é rápido, confiável e fácil para desenvolvedores iniciantes e profissionais. As ferramentas WebStorm VCS são incríveis e perfeitas para trabalhar com o Git e não precisam de nenhum outro plug-in ou ferramentas.

AmerllicA
fonte
1
Isso é incrível! Obrigado. Definitivamente, uma solução mais fácil para quem usa qualquer um dos IDEs da JetBrains.
Fabiano Arruda
Como restauramos o arquivo se for imagem?
Nodirabegimxonoyim 18/06/19
Caro @FabianoArruda, os JetEs da IDB são ferramentas poderosas para o desenvolvimento. obrigado pelo seu comentário adorável.
AmerllicA
Obrigado a você, PeterMortensen, pela edição.
AmerllicA
6

Eu tive a mesma pergunta. Sem saber, eu havia criado um commit pendente .

Lista dangling confirma

git fsck --lost-found

Inspecione cada confirmação pendente

git reset --hard <commit id>

Meus arquivos reapareceram quando mudei para o commit dangling.

git status pela razão:

“HEAD detached from <commit id where it detached>”

rustyMagnet
fonte
2
Muito obrigado. Você me ajudou a restaurar milhares de linhas de códigos.
Reuben
5
user@bsd:~/work/git$ rm slides.tex
user@bsd:~/work/git$ git pull 
Already up-to-date.
user@bsd:~/work/git$ ls slides.tex
ls: slides.tex: No such file or directory

Restaure o arquivo excluído:

user@bsd:~/work/git$ git checkout
D       .slides.tex.swp
D       slides.tex
user@bsd:~/work/git$ git checkout slides.tex 
user@bsd:~/work/git$ ls slides.tex
slides.tex
user1797498
fonte
2
A pergunta era sobre restaurar um arquivo depois que ele foi excluído e a alteração foi confirmada. Esta resposta é sobre como restaurar um arquivo que foi removido apenas no diretório de trabalho.
akaihola
Isso é verdade, e era isso que eu estava procurando.
Hola Soy Edu Feliz Navidad
4

Se você conhece a confirmação que excluiu o (s) arquivo (s), execute este comando onde <SHA1_deletion>está a confirmação que excluiu o arquivo:

git diff --diff-filter=D --name-only <SHA1_deletion>~1 <SHA1_deletion> | xargs git checkout <SHA1_deletion>~1 --

A parte antes do canal lista todos os arquivos que foram excluídos na confirmação; todos eles fazem checkout do commit anterior para restaurá-los.

Tony Wickham
fonte
4

Encontre a confirmação que excluiu seu arquivo:

git log --diff-filter=D --oneline -- path/to/file | cut -f -d ' '

Saída de amostra:

4711174

A partir do Git 2.23, existe realmente um restorecomando. Ainda é experimental, mas para restaurar algo que você removeu em um commit (4711174 nesse caso), você pode digitar:

git restore --source=4711174^ path/to/file

Observe o ^ após o ID de confirmação, pois queremos restaurar algo do commit antes daquele que excluiu o arquivo.

O --sourceargumento informa ao restorecomando onde procurar o (s) arquivo (s) a restaurar e pode haver qualquer confirmação e até o índice.

Consulte: documento git-restore para o git 2.23.0

cb2
fonte
4

No nosso caso, excluímos acidentalmente arquivos em um commit e alguns confirmamos mais tarde, percebemos nosso erro e queríamos recuperar todos os arquivos que foram excluídos, mas não aqueles que foram modificados.

Com base na excelente resposta de Charles Bailey, aqui está minha frase:

git co $(git rev-list -n 1 HEAD -- <file_path>)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- <file_path>)~1 head | grep '^D' | cut -f 2)
bonyiii
fonte
2

Simples e preciso

Primeiro, obtenha uma confirmação estável mais recente na qual você tem esse arquivo -

git log 

Digamos que você encontre $ commitid 1234567 ... e, em seguida,

git checkout <$commitid> $fileName

Isso restaurará a versão do arquivo que estava nessa confirmação.

Sudhanshu Jain
fonte
1

Para a melhor maneira de fazer isso, tente.


Primeiro, encontre o ID da confirmação que excluiu seu arquivo. Ele fornecerá um resumo das confirmações dos arquivos excluídos.

git log --diff-filter = D - resumo

git checkout 84sdhfddbdddf ~ 1

Nota: 84sdhfddbdddé o seucommit id

Com isso, você pode recuperar facilmente todos os arquivos excluídos.

Ritesh Adulkar
fonte
1

Você sempre pode git revertconfirmar o seu que excluiu o arquivo. ( Isso pressupõe que a exclusão foi a única alteração no commit. )

> git log
commit 2994bda49cd97ce49099953fc3f76f7d3c35d1d3
Author: Dave <[email protected]>
Date:   Thu May 9 11:11:06 2019 -0700

    deleted readme.md

E se você continuou o trabalho e percebeu mais tarde que não queria confirmar essa confirmação de exclusão, poderá revertê-lo usando:

> git revert 2994bd

Agora git logmostra:

> git log
Author: Dave <[email protected]>
Date:   Thu May 9 11:17:41 2019 -0700

    Revert "deleted readme"

    This reverts commit 2994bda49cd97ce49099953fc3f76f7d3c35d1d3.

E readme.mdfoi restaurado no repositório.

Dave Baghdanov
fonte
Como a pergunta supõe que várias confirmações foram feitas após a exclusão do arquivo e como não há indicação de que as confirmações subsequentes sejam indesejadas, isso não parece provável que ajude o OP na situação descrita.
Jonathan Leffler
1
Sim! Você pode fazer confirmações subsequentes e ainda reverter a confirmação de exclusão. Então, se comprometer 111 apaga o arquivo, e comprometer-222, 333, 444, adiciona / modifica as coisas, você ainda pode reverter cometer 111 para anular a eliminação, e ele vai se tornar cometer 555
Dave Baghdanov
0

Eu também tenho esse problema usando o código abaixo para recuperar um arquivo anterior para um diretório local:

git checkout <file path with name>

O exemplo abaixo está funcionando para mim:

git checkout resources/views/usaSchools.blade.php

Akbor
fonte
Por favor, mencione qual é o problema
Akbor
A exclusão já foi confirmada. Você precisa especificar a confirmação da qual restaurar neste caso.
S20 /
-1
$ git log --diff-filter=D --summary  | grep "delete" | sort
kujiy
fonte
Uma explicação estaria em ordem.
Peter Mortensen
-1

Se a exclusão não tiver sido confirmada, o comando abaixo restaurará o arquivo excluído na árvore de trabalho.

$ git checkout -- <file>

Você pode obter uma lista de todos os arquivos excluídos na árvore de trabalho usando o comando abaixo.

$ git ls-files --deleted

Se a exclusão foi confirmada, localize a confirmação onde ocorreu e recupere o arquivo dessa confirmação.

$ git rev-list -n 1 HEAD -- <file>
$ git checkout <commit>^ -- <file>

Caso esteja procurando o caminho do arquivo para recuperar, o comando a seguir exibirá um resumo de todos os arquivos excluídos.

$ git log --diff-filter=D --summary
Muhammad Soliman
fonte
-1

Para restaurar todos os arquivos excluídos com o Git, você também pode:

git checkout $(git ls-files --deleted)

Onde git ls-files --deletedlista todos os arquivos excluídos e git checkout $(git command)restaura a lista de arquivos em um parâmetro.

Charles Duporge
fonte