git reflog: como recuperar commits e branches perdidos
O git reflog é um registro local em que o Git anota cada movimento do HEAD e das branches: commits, reset, checkout, rebase, merge. Encontre no git reflog a entrada anterior ao erro, por exemplo HEAD@{2}, e restaure o estado com git reset --hard HEAD@{2} ou crie uma branch com git branch rescue HEAD@{2}.
git reflog é o log de referências (reference log): o Git registra nele cada movimento do HEAD e das pontas das branches. Fez um commit, mudou de branch, rodou reset, rebase ou merge — aparece no reflog uma linha com o hash para onde a referência apontava depois da operação. Por isso, mesmo um commit que não é mais referenciado por nenhuma branch pode ser encontrado e recuperado.
O principal a saber: o reflog é local. Ele existe só no seu clone, não é enviado ao servidor e não vem com o git clone. Em compensação, no seu repositório ele salva você de praticamente qualquer erro com o histórico.
Como ler a saída do git reflog
git reflog
3c4d5e6 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~2
9a8b7c6 HEAD@{1}: commit: Adicionar validação de e-mail
1f2e3d4 HEAD@{2}: commit: Mover config para arquivo separado
3c4d5e6 (HEAD -> main) HEAD@{3}: checkout: moving from feature to main
7b6a5c4 (feature) HEAD@{4}: commit: Rascunho do formulário de login
Cada linha é um estado do HEAD, com o mais recente no topo:
- hash — o commit para onde o HEAD apontava depois da operação;
- HEAD@{n} — "onde o HEAD estava n movimentos atrás"; é uma referência completa e pode ser passada para qualquer comando;
- descrição — o tipo de operação (commit, reset, checkout, rebase, merge, pull, cherry-pick, commit (amend)) e os detalhes.
No exemplo dá para ver: dois commits — "Mover config" e "Adicionar validação" — foram feitos e depois o reset HEAD~2 voltou a branch. No git log eles não aparecem mais, mas no reflog continuam lá.
Reflog de uma branch específica
Cada branch tem seu próprio log. Ele é mais curto e mais útil quando você alternou bastante entre branches:
git reflog show feature
git reflog show origin/main # como a branch remota mudou a cada fetch
Referências por tempo
Em vez de um número, você pode indicar um momento — o Git encontra para onde a referência apontava naquela hora:
git show main@{yesterday}
git diff main@{"2 hours ago"} main
git log -g --since="1 day ago" --oneline
A flag -g (--walk-reflogs) do git log percorre o reflog em vez das ligações de parentesco e permite usar os formatos e filtros de sempre. Para exibir datas, é prático usar git reflog --date=iso.
Como recuperar commits depois de um git reset --hard
A situação mais comum: você rodou git reset --hard para o commit errado e o trabalho "sumiu". Na verdade, os commits não foram a lugar nenhum — só não estão mais visíveis a partir das branches.
- Abra o log:
git reflog. - Encontre a linha logo antes de
reset: moving to ...— é o estado anterior ao erro. No exemplo acima,HEAD@{1}com o hash9a8b7c6. - Confira se é isso mesmo:
git log --oneline -5 HEAD@{1}. - Restaure a branch:
git reset --hard HEAD@{1}.
git reset --hard HEAD@{1}
HEAD is now at 9a8b7c6 Adicionar validação de e-mail
Atenção: a cada operação os números se deslocam. O que era HEAD@{1} vira HEAD@{2} depois do reset. Por isso é mais seguro copiar o hash, e não o número.
Uma limitação importante: o reflog só guarda o que foi commitado. Alterações não commitadas apagadas por reset --hard ou git checkout -- arquivo não voltam pelo reflog. Ainda resta alguma esperança se os arquivos tiverem sido adicionados ao índice — falamos disso mais abaixo.
Como recuperar uma branch apagada
Você apagou a branch com git branch -D feature, e ela tinha commits que não foram enviados. O log da própria branch é apagado junto com ela, mas o log do HEAD continua — e o HEAD passou pelos commits dela enquanto você trabalhava ali.
git reflog | grep feature
7b6a5c4 HEAD@{4}: commit: Rascunho do formulário de login
3c4d5e6 HEAD@{3}: checkout: moving from feature to main
# O último commit da branch é aquele de onde você saiu dela
git branch feature 7b6a5c4
Procure a linha checkout: moving from feature to ...: o hash da entrada anterior (mais abaixo na lista) é o último estado da branch. Aliás, ao apagar, o próprio Git imprime o hash: Deleted branch feature (was 7b6a5c4). — se o terminal ainda estiver aberto, isso basta.
Como desfazer um rebase que deu errado
O rebase reescreve commits, e se no meio do caminho você resolveu mal os conflitos, o resultado pode ficar pior que o original. O reflog guarda o estado anterior ao início do rebase.
git reflog
e1f2a3b HEAD@{0}: rebase (finish): returning to refs/heads/feature
e1f2a3b HEAD@{1}: rebase (pick): Rascunho do formulário de login
c4d5e6f HEAD@{2}: rebase (start): checkout main
7b6a5c4 HEAD@{3}: commit: Rascunho do formulário de login
git reset --hard HEAD@{3}
Você precisa da entrada anterior a rebase (start). Há também um atalho: antes de operações perigosas (rebase, merge, reset), o Git salva a posição anterior na referência ORIG_HEAD, então logo após o rebase funciona git reset --hard ORIG_HEAD. Mas o ORIG_HEAD é sobrescrito pela próxima operação desse tipo, enquanto o reflog guarda todo o histórico.
Como voltar ao commit anterior ao git commit --amend
O --amend não altera o commit, ele cria um novo no lugar. O antigo continua no reflog:
git reflog
5a6b7c8 HEAD@{0}: commit (amend): Adicionar validação de e-mail
9a8b7c6 HEAD@{1}: commit: Adicionar validação de e-mail
git diff HEAD@{1} HEAD # o que o amend mudou
git reset --soft HEAD@{1} # volta ao commit antigo; as mudanças do amend ficam no índice
Recuperação segura: branch em vez de reset
Se não tiver certeza de qual commit precisa, não faça reset --hard às cegas. Crie uma branch de resgate — ela não quebra nada e protege o commit encontrado de ser apagado:
git branch rescue HEAD@{5}
git log --oneline rescue
git switch rescue
Depois, os commits da rescue podem ser trazidos com cherry-pick ou merge, e a branch pode ser apagada quando tudo estiver de volta no lugar.
Por quanto tempo o reflog é mantido
As entradas não são eternas. Elas são limpas pelo git gc, que o Git roda sozinho de tempos em tempos. Os prazos são definidos por configurações:
| Configuração | Padrão | O que significa |
|---|---|---|
gc.reflogExpire | 90 dias | Prazo para entradas com commits alcançáveis a partir das branches atuais |
gc.reflogExpireUnreachable | 30 dias | Prazo para entradas com commits que nenhuma branch alcança — justamente os "perdidos" |
gc.pruneExpire | 2 semanas | Depois de quanto tempo são apagados os próprios objetos inalcançáveis que mais nada referencia |
Na prática: um commit perdido fica garantidamente acessível pelo reflog por cerca de um mês. Não adie a recuperação e não rode manualmente git gc --prune=now ou git reflog expire --expire=now --all antes de recuperar o que precisa.
Se o reflog não ajudou: git fsck
Há casos em que o commit não está no reflog: por exemplo, ele foi criado pelo git stash e depois o stash foi apagado com git stash drop, ou as alterações estavam só no índice. Aí procure objetos "soltos" (dangling):
git fsck --lost-found
dangling commit 2b3c4d5e6f...
dangling blob 8e9f0a1b2c...
git show 2b3c4d5e6f # ver o conteúdo
git branch recovered 2b3c4d5e6f
Commits soltos vindos de um stash apagado têm a mensagem "WIP on ...". Blobs soltos são o conteúdo de arquivos que em algum momento foram adicionados com git add, mas não commitados. Eles não têm nome de arquivo, então é preciso identificá-los pelo conteúdo — mas às vezes é assim que se salva o trabalho depois de um reset --hard. Com a flag --lost-found, o Git ainda os copia para .git/lost-found/.
O que o reflog não faz
- Não funciona entre máquinas. Se um colega fez force push e apagou seus commits no servidor, procure-os no reflog de quem os tinha localmente — por exemplo,
git reflog show origin/mainna máquina de qualquer pessoa que tenha feito fetch antes da sobrescrita. - Não enxerga o que não foi commitado. Por isso, o melhor seguro antes de uma operação arriscada é um commit ou um
git stash. - Não guarda o histórico para sempre. Valem os prazos acima, e um clone novo começa com o log vazio.
Na prática: um seguro antes de operações perigosas
O reflog salva, mas recuperar por ele exige atenção: é preciso achar a entrada certa entre dezenas de parecidas. É bem mais simples deixar uma marca antes. Antes de um rebase, de um reset grande ou de experimentos com o histórico, crie uma branch ou tag temporária:
git branch backup/before-rebase
git rebase -i main
# algo deu errado
git reset --hard backup/before-rebase
# deu tudo certo
git branch -D backup/before-rebase
Custa um comando e poupa a caça ao hash. O reflog fica como plano B para quando você se lembrou do seguro tarde demais.
Como achar rápido a entrada certa
Num log longo, filtrar ajuda. Busque pelo tipo de operação ou pelo texto da mensagem do commit e, em caso de dúvida, veja o conteúdo:
git reflog | grep -E "reset|rebase \(start\)"
git log -g --grep="validação" --oneline
git show --stat HEAD@{7}
O git log -g com --grep busca nas mensagens dos commits do reflog, e o git show --stat mostra quais arquivos foram afetados — com isso fica fácil reconhecer o commit certo, mesmo que a mensagem fosse pouco informativa, tipo "wip".
Conclusão
O git reflog é a rede de segurança do Git: tudo o que foi commitado no seu repositório nas últimas semanas pode ser encontrado e recuperado. O roteiro é sempre o mesmo: abrir o log, achar a entrada anterior ao erro, conferir com git log ou git show e então criar uma branch ou fazer reset --hard. O uso de restore, reset, revert e a recuperação depois de erros são praticados no curso de Git.
Perguntas frequentes
Qual a diferença entre git reflog e git log?
O git log segue as ligações de parentesco dos commits e mostra o histórico da branch. O git reflog mostra para onde o HEAD e as branches se moveram no seu repositório, incluindo commits que já não estão no histórico de nenhuma branch.
O reflog vai para o GitHub ou GitLab?
Não, o reflog fica só na pasta local .git/logs e não é transferido no push nem no clone.
Dá para limpar o reflog?
Sim, com git reflog expire --expire=now --all seguido de git gc --prune=now, mas depois disso não será mais possível recuperar commits perdidos.
Onde o reflog fica fisicamente?
Nos arquivos de texto .git/logs/HEAD e .git/logs/refs/heads/nome-da-branch, com uma linha para cada movimento.
O reflog funciona em repositórios bare no servidor?
Por padrão, o reflog vem desativado em repositórios bare (core.logAllRefUpdates), então não dá para contar com ele no servidor.
Este tema é abordado em profundidade, com prática, no curso “Git do zero ao desenvolvedor confiante”.
Ir para o curso