- Como no Git é difícil pesquisar a solução sem saber o nome do tipo de erro, este material organiza os comandos por cenários comuns de recuperação para consultar e usar rapidamente
- Se você bagunçou muito o repositório ou quer desfazer erros de exclusão ou merge, pode encontrar o histórico de trabalho com
git reflog e voltar ao estado anterior com git reset HEAD@{index}
- Pequenos ajustes no último commit ou mudanças na mensagem podem ser resolvidos rapidamente com
git commit --amend, mas isso não deve ser usado em commits já enviados para uma branch pública/compartilhada
- Se você fez commit na branch errada, pode mover com o fluxo
reset, stash, checkout, stash pop ou com cherry-pick, mas o método seguro muda conforme o commit já foi enviado ou não
reset --hard e git clean -d --force, usados para forçar a recuperação para o estado do repositório remoto, podem apagar até arquivos não rastreados, então é preciso conferir antes de executar
Por que é difícil recuperar erros no Git
- É fácil cometer erros no Git, e muitas vezes para pesquisar como corrigir um problema você já precisa saber o nome do conceito necessário
- Este material organiza situações ruins frequentes no Git em inglês simples, com exemplos de comandos
- Não é uma referência completa, e podem existir outras formas de fazer a mesma tarefa
Voltando o repositório para um estado anterior
- Se você bagunçou muito o repositório ou quer recuperar algo apagado, verifique o histórico de trabalho com
git reflog
git reflog
# Você pode ver a lista de operações do Git em todas as branches
# Cada item tem um índice no formato HEAD@{index}
git reset HEAD@{index}
reflog pode ser usado nas seguintes situações
- Recuperar conteúdo apagado por engano
- Remover uma tentativa que bagunçou o repositório
- Recuperar após um merge ruim
- Voltar para um ponto em que tudo realmente funcionava
Como corrigir o último commit
- Se precisar adicionar uma pequena correção logo após o último commit, use
git commit --amend --no-edit
# Após as mudanças
git add . # ou adicione arquivos individualmente
git commit --amend --no-edit
- Esse comando inclui as novas mudanças no último commit
- Também daria para criar um novo commit e depois juntar com
rebase -i, mas amend é muito mais rápido
- Não use
amend em commits já enviados para uma branch pública/compartilhada
- Se quiser mudar apenas a mensagem do último commit, use o comando abaixo
git commit --amend
# Siga as instruções para alterar a mensagem do commit
Quando você fez commit na branch errada
- Se você fez commit por engano na
master, mas ele deveria estar em uma nova branch, crie a nova branch no estado atual e remova o último commit da master
git branch some-new-branch-name
git reset HEAD~ --hard
git checkout some-new-branch-name
- Esse método não é adequado para commits já enviados para uma branch pública/compartilhada
- Se você tentou outras coisas antes, talvez precise usar
HEAD@{number-of-commits-back} em vez de HEAD~
- Se o commit foi parar completamente na branch errada, você pode mover usando o fluxo
reset --soft, stash, checkout, stash pop
git reset HEAD~ --soft
git stash
git checkout name-of-the-correct-branch
git stash pop
git add . # ou adicione arquivos individualmente
git commit -m "your message here"
- Também existe uma forma de resolver a mesma situação com
cherry-pick
git checkout name-of-the-correct-branch
git cherry-pick master
git checkout master
git reset HEAD~ --hard
Desfazendo mudanças e commits antigos
- Se você alterou um arquivo mas
git diff está vazio, ele pode já estar em staging, então você precisa da flag --staged
git diff --staged
- Para desfazer uma mudança de alguns commits atrás, encontre o hash com
git log e execute git revert
git log
git revert [saved hash]
git revert cria um novo commit que desfaz aquele commit
- Quando quiser desfazer apenas um arquivo específico, e não o commit inteiro, use o fluxo de colocar a versão antiga do arquivo no índice e depois fazer commit
git log
git checkout [saved hash] -- path/to/file
git commit -m "Wow, you don't have to copy-paste to undo"
Forçando a recuperação para o estado do repositório remoto
- Em vez de apagar o repositório e clonar de novo, você pode usar comandos do Git para alinhar com o estado do repositório remoto
git fetch origin
git checkout master
git reset --hard origin/master
git clean -d --force
git reset --hard origin/master ajusta a branch para o estado da master remota
git clean -d --force apaga arquivos e diretórios não rastreados
- É preciso repetir
checkout, reset e clean para cada branch quebrada
- Essas operações são destrutivas e irreversíveis, então é preciso cuidado antes de executar
Escopo e contribuições
- Este material não é uma referência completa de Git, mas uma coletânea prática de procedimentos de recuperação encontrados por tentativa e erro
- Se quiser ajudar a adicionar traduções, você pode enviar um PR no GitHub
1 comentários
Opiniões do Hacker News
Há algumas coisas que eu mudaria: sempre usaria
git switchem vez degit checkout, e evitariareset --hardao máximoPor exemplo, no caso de “fiz por engano no master um commit que deveria estar em um novo branch”, dá para resolver com
git branch some-new-branch-name,git switch -d HEAD~,git switch -C master,git switch some-new-branch-name, nessa ordemNo caso de “fiz commit no branch errado”, eu também faria
git switchpara o branch correto e depoisgit cherry-pick master,git switch -d master~,git switch -C masterPara “desistir de tudo e voltar ao estado remoto”, eu montaria algo como
git fetch origin,git restore -WS .,git clean -d --force,git switch -d origin/master,git switch -C masterMesmo entendendo bem o modelo do Git, essas receitas não são intuitivas, e é preciso conhecer até os comportamentos peculiares de cada comando
Por exemplo,
git switch -c some-new-branch-namejá existe, mas um comando comogit move-branch master HEAD~, que apenas movesse um branch para outro commit, pareceria mais intuitivogit switchegit restoreem vez degit checkout, mas esses comandos foram introduzidos no Git v2.23, e esse lançamento foi há cerca de 5 anosAs páginas de ajuda ainda trazem o aviso
THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.Claro que esse aviso existe praticamente desde que os comandos surgiram, e, depois de 5 anos, parece que já está na hora de deixar de chamá-los de experimentais
Ainda assim,
git checkouté mais compatível com versões antigas e pode ser visto como refletindo o modo de usar Git da época em que este site foi criadohttps://github.com/git/git/blob/757161efcca150a9a96b312d9e78...
https://github.com/git/git/releases/tag/v2.23.0
https://github.com/git/git/commit/4e43b7ff1ea4b6f16b93a432b6...
git add .como padrão para colocar alterações na área de staging não é uma boaAdicionar arquivos específicos explicitamente deixa menos margem para um “ah, ferrou” depois
Quero saber por que
switché melhor quecheckoute por que não se deve usarreset --hardreset --hard”Tenho curiosidade sobre por que ele de fato não seria suficiente; eu uso com bastante frequência junto com
alias git-restore-file='git restore --source=HEAD --', e parece funcionar bemAo aprender Git, deveríamos começar a recomendar clientes GUI como caminho padrão
Isso resolveria um terço desses problemas, e outro terço nem chegaria a surgir
Se depois a pessoa concluir que a linha de comando é mais rápida, pode usá-la então; primeiro, as pessoas precisam ver visualmente como interagem com a árvore
Pessoalmente, gosto do fork.dev, mas hoje os clientes são, em geral, parecidos
Com o Magit, ações como “abortar cherry-pick” são descobríveis dentro da interface e usam o mesmo atalho de outras operações de “abortar X”
Se eu tivesse que usar apenas a linha de comando do Git, não teria ideia de por onde começar
Já apaguei, durante um rebase interativo, um commit que não deveria ter apagado; como a cada início de rebase é criado um snapshot acessível pelo reflog, é seguro desfazer um rebase incorreto
A UI de reflog do Magit é igual à UI de log, então não fiquei perdido mesmo vendo aquilo pela primeira vez; com a CLI do Git, é bem possível que eu não entendesse o que estava acontecendo
Gosto do fato de que, ao executar
jjsem argumentos, ele mostra a parte relevante da árvore de commits e a posição atualDá para ver de relance o commit em que você está trabalhando, o branch associado e o histórico, outros branches ativos no repositório, a mensagem de cada commit e se houve alterações, o que é ótimo para se reorientar
Mas quase ninguém escuta, acaba sofrendo na CLI, estragando branches, ficando para trás em relação ao main e criando medo de rebase
Muitas coisas seriam resolvidas na UI com arrastar e soltar ou algo parecido, e isso parece uma espécie de autoflagelação
Mesmo que a interface fosse horrível, era mais fácil se comunicar usando a mesma interface que o resto da equipe usava, e os materiais online de solução de problemas em geral assumiam o “jeito comum”, então também eram mais úteis
Parece bem estranho pensar que, em abril de 2025, o Git fará 20 anos
Eu estava lá na época e lembro de ficar em dúvida sobre o que aprender entre git, darcs e bazaar para substituir SVN ou CVS
Não sei se também cheguei a experimentar o Mercurial
Fico me perguntando se o “efeito GitHub” praticamente matou a necessidade de novos entrantes na área de sistemas de controle de versão
Talvez, em algum momento, o yak shaving tenha acabado o suficiente
Migrei completamente para jj e, como há compatibilidade com Git, não preciso esperar que outras pessoas mudem junto
Estou ficando cada vez mais frustrado com vários aspectos do GitHub e espero que um dia todo mundo também supere isso, mas nesse caso o caminho adiante não parece tão simples
SVN é intuitivo e funciona bem o bastante em 99% dos casos, então não é preciso “ensinar” as pessoas separadamente
Gostaria que todos nós parássemos de fingir ser hackers 1337 e admitíssemos que Git é uma ferramenta exagerada para a maioria
Acho que tenho um modelo mental bastante bom do que o Git faz, mas, quando os comandos ficam um pouco mais complexos, não consigo lembrar os argumentos
Os comandos não são fáceis de descobrir nem de memorizar
Não sei se a UI de texto é ruim ou se descrever em texto a manipulação de árvores é que é difícil por natureza
Manipular árvores complexas por texto não é fácil, e a UI de texto é objetivamente ruim: https://stevelosh.com/blog/2013/04/git-koans/
Fico feliz em ver resultados para hg e Mercurial nesta thread
Ainda não consigo acreditar que o Mercurial tenha perdido a guerra dos sistemas de controle de versão distribuídos, e considero uma ferramenta melhor
É utilizável, estável, comprovado em produção e ativamente suportado
É algo próximo de “seja a mudança que você quer ver”, então basta usar
Falar que ele perdeu é bastante contraproducente, e a inibição implícita causada por esse tipo de frase mantém baixa a adoção do Mercurial
Hospedagem é possível ao menos no Sourcehut e no heptapod.host
Também opero uma instância privada do Heptapod, que é um fork do Gitlab com suporte direto a Mercurial, e simplesmente funciona bem
Git foi uma daquelas tecnologias que nunca entrou direito na minha cabeça, porque em muitos aspectos não segue a intuição
Se você não o usa há muito tempo, é bem provável que precise procurar no Google ou ver a página man para quase qualquer operação
Especialmente depois de passar pelo terror de tentar manter uma base de código grande no SVN com muitos branches e depois fazer merge de volta, dá para ver que o Git é absurdamente melhor nessa parte
Para quem começa direto pelo Git, a curva de aprendizado pode ser muito maior, mas às vezes ainda acho difícil entender onde as pessoas travam
Ao usar a forma embutida,
filter-branch, aparece um aviso, depois de uma longa demora, para apertar Control+C e baixar um script Python de terceiros chamadofilter-repoNão me orgulho disso, mas minha operação Git de “deu ruim” mais frequente é apagar o repositório local, clonar de novo e reaplicar as alterações
Em 95% dos casos funciona muito bem; no restante, peço ajuda para o pessoal de DevOps
cp -R .git ../git-backupantes de realizar alguma operação mais arriscada no GitPorque, se eu estragar tudo demais, posso simplesmente substituir todo o diretório
.gitpela cópia antigaÉ muito mais rápido do que descobrir a forma correta de reverter uma operação específica ou um conjunto de operações
Uma coisa que eu gostaria que as pessoas realmente internalizassem sobre o Git é que ele é um armazenamento de dados somente de acréscimo
Se quiser colocar trabalho no repositório, faça commit; uma vez que entrou, ele não pode ser removido por comandos do Git
O reflog também funciona com base nesse princípio
Faça o que fizer, você pode voltar ao estado anterior de um branch e restaurar o trabalho a partir do armazenamento de dados
Mas, se você não fizer commit, o Git não pode ajudar; então simplesmente faça commit
Faça commit sempre e, se o atrito for grande demais ou você vive esquecendo, conserte as ferramentas
Se você passa o dia inteiro escrevendo código, em geral deveria fazer commit pelo menos uma vez por hora
É um detalhe bobo, mas toda vez que vejo
git reflog, leio como re-flogTalvez seja por isso que eu tenha dificuldade para memorizar esse comando, e aparentemente não consigo pensar nele inconscientemente como “reference log”
Em vez disso, parece que estou mandando o Git vender algo barato de novo
Não sou usuário de Git, mas quando vejo algo assim, fico cada vez mais com a impressão de que o
git commitem si não tem significado, e que o que importa é apenas o momento em que o commit é enviado por push ou recebe mergeÉ parecido com salvar um arquivo de texto
Você não escreve uma mensagem curta toda vez que salva um arquivo, então é só seguir em frente
Não necessariamente curta; quanto menor a mudança, às vezes mais longa fica a explicação
Já escrevi 2 ou 3 parágrafos para uma mudança de 2 ou 3 linhas com grande impacto
Commits locais são uma forma de criar checkpoints em pontos em que tudo compila e funciona
Assim, dá para colocar mudanças em stash ou voltar para um estado que funcionava quando algo dá errado
Antes de fazer push, você pode dar reset e dividir em commits adequados, mantendo o histórico remoto limpo e sem virar um monte de
wipOu pode fazer squash merge do PR e obter o melhor dos dois mundos
Só que não há necessidade de inserir mensagens sem sentido como
more fixesNormalmente crio um commit inicial como
(WIP) nome da funcionalidade, repito commits com amend até ficar satisfeito e, no fim, removo o(WIP)da mensagemVejo o log de revisões do Git como algo parecido com esse diário
Ele é mais organizado do que um documento TODO separado e incentiva a explicar, de forma legível, o que já foi feito
Ao decidir até onde reverter, pode ser útil saber o que você estava pensando naquele commit; e, se a abordagem estava errada, isso também ajuda na análise posterior
Quando um trabalho que ainda não recebeu merge nem push está organizado em mudanças separadas com objetivos definidos, fica muito mais fácil preservar só as partes boas se algumas delas forem boas e outras ruins
Ainda assim, tecnicamente commits não são sem significado
Sem commits, não há nada para enviar por push nem para fazer merge
E, de forma não técnica, registrar em texto claro por que algo está sendo alterado é muito útil
Se você precisa escrever esse tipo de explicação com frequência demais, deve reduzir o escopo da unidade de “mudança” ou ser mais seletivo sobre o que mexe ao mesmo tempo
Se não usa Git, fico curioso para saber com o que faz controle de versão e gestão de mudanças