Como recuperar arquivos deletados acidentalmente com o Git
1. Cenários comuns de deleção acidental e primeiros passos
Deletar arquivos acidentalmente é um dos erros mais frequentes no uso diário do Git. A boa notícia é que, na maioria dos casos, a recuperação é possível. Antes de entrar em pânico, é essencial entender em que estágio a deleção ocorreu:
- Deleção antes do commit: o arquivo foi removido, mas ainda não foi commitado.
- Deleção após o commit: o arquivo foi removido e a deleção foi commitada.
- Deleção em branches ou stashes: o arquivo foi perdido em contextos mais complexos.
Para diagnosticar o problema, comece sempre com:
git status
Este comando mostra arquivos modificados, deletados ou não rastreados. Se o arquivo aparecer como "deleted" e estiver na staging area, você sabe que a deleção foi preparada para commit. Se aparecer como "untracked", o arquivo nunca foi rastreado e a recuperação pode ser mais difícil.
Outro comando útil para verificar arquivos rastreados é:
git ls-files
Isso lista todos os arquivos que o Git está monitorando. Se o arquivo deletado não aparecer, ele pode ter sido removido da árvore de trabalho.
Para deleções antes do commit, o histórico com git log e git diff ajuda a identificar a versão perdida:
git log --oneline -5
git diff HEAD -- <arquivo>
2. Recuperação com git checkout e git restore (arquivos não commitados)
Quando você deleta um arquivo que ainda não foi commitado, a recuperação é simples. O Git mantém uma cópia do arquivo no último commit (HEAD) ou na staging area.
Usando git checkout (clássico):
git checkout -- <arquivo>
Este comando restaura o arquivo da versão do último commit, descartando quaisquer alterações não commitadas.
Usando git restore (moderno, Git 2.23+):
git restore <arquivo>
Para restaurar da staging area:
git restore --staged <arquivo>
Para restaurar da árvore de trabalho (worktree):
git restore --worktree <arquivo>
Diferenças principais:
git checkouté mais versátil, mas pode ser confuso (também usado para trocar de branch).git restoreé mais específico e seguro, pois não altera o branch atual.- Ambos funcionam para arquivos não commitados, mas
git restoreoferece controle mais granular com as flags--stagede--worktree.
3. Usando git reset para voltar no tempo sem perder dados
Se você já commitou a deleção, pode usar git reset para "voltar no tempo" sem perder o trabalho atual. Existem três modos principais:
git reset --soft: mantém todas as alterações na staging area. Útil se você quer desfazer o commit, mas manter os arquivos prontos para um novo commit.
git reset --soft HEAD~1
git reset --mixed (padrão): desfaz o staging, mas mantém os arquivos na árvore de trabalho. Você pode reavaliar o que deseja commitado.
git reset HEAD~1
git reset --hard: restaura o estado exato do commit especificado, descartando todas as alterações não commitadas. Use com extrema cautela.
git reset --hard HEAD~1
⚠️ Cuidados com git reset --hard: Este comando pode causar perda permanente de dados se houver alterações não commitadas. Sempre verifique com git status antes de usá-lo. Uma alternativa mais segura é usar git stash para salvar temporariamente as alterações antes do reset.
4. Recuperação com git reflog: a rede de segurança do Git
O reflog é um registro local de todos os movimentos do HEAD (commits, resets, checkouts, etc.). Ele funciona como uma "máquina do tempo" do Git, mesmo após commits serem "perdidos" por resets ou rebases.
Navegando no reflog:
git reflog show
A saída mostra uma lista de entradas como:
e5a6b7c HEAD@{0}: commit: Removendo arquivo antigo
d8e9f0a HEAD@{1}: commit: Adicionando novo recurso
a1b2c3d HEAD@{2}: reset: moving to HEAD~1
Cada entrada tem um hash e uma descrição. Para restaurar um arquivo a partir de uma entrada específica:
git checkout <hash> -- <arquivo>
Ou, se preferir criar um novo branch a partir desse ponto:
git branch recuperacao <hash>
O reflog é especialmente útil quando você fez git reset --hard e percebeu que perdeu commits importantes. Basta encontrar a entrada anterior ao reset e restaurar.
5. Buscando arquivos deletados com git log e git show
Para arquivos que foram deletados em commits antigos, você pode vasculhar o histórico com filtros específicos.
Listando todos os arquivos deletados no histórico:
git log --diff-filter=D --summary
Isso mostra cada commit que deletou arquivos, com o nome do arquivo e o hash do commit.
Rastreando um arquivo específico mesmo após deleção:
git log --all --full-history -- <caminho/do/arquivo>
O --all inclui todos os branches, e --full-history garante que commits de deleção sejam exibidos.
Extraindo o conteúdo do arquivo antes da deleção:
git show <hash>^:<caminho/do/arquivo>
O ^ refere-se ao commit pai (antes da deleção). Por exemplo, se o commit abc123 deletou o arquivo, abc123^ é o commit imediatamente anterior, que ainda contém o arquivo.
Se você quiser restaurar o arquivo para a árvore de trabalho:
git show <hash>^:<caminho/do/arquivo> > <caminho/do/arquivo>
6. Técnicas avançadas: git cherry-pick e git revert para deleções
Quando a deleção foi commitada e você não quer alterar o histórico (por exemplo, em um branch compartilhado), use git revert em vez de git reset.
Revertendo um commit de deleção:
git revert <hash-do-commit-de-delecao>
Isso cria um novo commit que desfaz a deleção, restaurando o arquivo. O histórico permanece intacto e a reversão é registrada.
Cherry-picking um commit específico:
Se a deleção faz parte de um commit maior, você pode cherry-pick apenas o commit que contém o arquivo desejado:
git cherry-pick <hash-do-commit-que-tem-o-arquivo>
Isso aplica as alterações desse commit no branch atual. Útil para recuperar um arquivo isolado sem trazer outras alterações indesejadas.
Diferenças entre reverter e restaurar:
git revert: seguro para branches compartilhados, cria um novo commit, não altera o histórico.git restore: funciona apenas para arquivos não commitados ou para restaurar versões do histórico sem criar commits.git checkout <hash> -- <arquivo>: restaura uma versão específica sem criar commits, mas pode causar conflitos se houver alterações locais.
7. Casos especiais: arquivos deletados em branches ou stashes
Recuperando arquivos deletados em um branch específico:
Use git branch --contains para encontrar branches que contêm um commit específico:
git branch --contains <hash-do-commit-que-tem-o-arquivo>
Depois, faça checkout no branch e restaure o arquivo.
Restaurando arquivos de um stash deletado:
Liste todos os stashes:
git stash list
Para ver o conteúdo de um stash específico:
git stash show -p stash@{0}
Para restaurar um arquivo de um stash sem aplicar todo o stash:
git checkout stash@{0} -- <arquivo>
Lidando com deleções em merges:
Se um merge deletou arquivos acidentalmente, use git merge --no-commit para inspecionar o estado antes de finalizar o merge:
git merge --no-commit <branch>
git status
Isso permite ver quais arquivos serão deletados e, se necessário, abortar o merge com git merge --abort.
8. Prevenção e boas práticas para evitar perda de dados
A melhor recuperação é a que nunca precisa ser feita. Algumas práticas recomendadas:
Configuração de hooks de pré-commit:
Crie um hook .git/hooks/pre-commit que verifica se arquivos importantes estão sendo deletados:
#!/bin/bash
# Verifica se arquivos .env ou config estão sendo deletados
if git diff --cached --name-only --diff-filter=D | grep -E '\.env$|config\.'; then
echo "Erro: Tentativa de deletar arquivo de configuração!"
exit 1
fi
Uso de git stash como backup temporário:
Antes de deletar arquivos, faça um stash:
git stash push -- <arquivo>
Isso salva o arquivo no stash, permitindo recuperação fácil.
Estratégias de backup:
- Faça commits frequentes e pequenos.
- Crie branches de segurança antes de operações arriscadas:
git branch backup-antes-do-reset
- Use
git config --global core.autocrlf inputpara evitar problemas de final de linha que podem causar deleções acidentais.
Comandos de verificação rápida:
# Verificar o que será deletado antes de commit
git diff --cached --diff-filter=D --name-only
# Listar arquivos deletados nos últimos 10 commits
git log --diff-filter=D --name-only -10
Referências
- Documentação oficial do Git - git-restore — Documentação completa do comando
git restore, incluindo opções para restaurar arquivos deletados. - Documentação oficial do Git - git-reflog — Guia detalhado sobre o reflog, a ferramenta de recuperação mais poderosa do Git.
- Atlassian - Como recuperar arquivos deletados no Git — Tutorial prático da Atlassian com exemplos de recuperação usando
git restoreegit checkout. - Git SCM - Undoing Changes — Capítulo do livro Pro Git sobre como desfazer alterações, incluindo recuperação de arquivos deletados.
- Stack Overflow - Como recuperar um arquivo deletado no Git — Pergunta clássica do Stack Overflow com múltiplas soluções e discussão da comunidade.
- GitHub Blog - 12 formas de recuperar dados perdidos no Git — Artigo do GitHub Engineering com técnicas avançadas de recuperação, incluindo reflog e cherry-pick.