Dangit, Git!?!

Git é difícil: é muito fácil cometer um erro e é impossível descobrir como arrumá-lo. A documentação do Git é como o problema do ovo e da galinha, onde você não consegue pesquisar como resolver o seu problema a não ser que você já saiba o nome do que você precisa para resolve-lo.

Então aqui estão algumas situações complicadas em que me meti e como me safei delas, em português claro.

Saco, quebrei tudo. Por favor, me diz que o git tem uma máquina do tempo mágica?!?!

git reflog
# Você vai ver uma lista de tudo o que fez 
# em todos os branches no git!
# Cada alteração tem um índice HEAD@{índice}
# Encontre a alteração feita antes de você 
# cagar tudo 
git reset HEAD@{índice}
# máquina do tempo mágica 

Você pode utilizar isso para recuperar coisas excluídas por acidente, ou apenas remover algo que tentou e quebrou o repositório, ou recuperar coisas depois de um merge com problemas, ou, simplemesmente, voltar no tempo para quando as coisas realmente funcionavam. Eu utilizo MUITO o reflog. Eu tiro o chapéu para as várias várias várias várias pessoas que sugeriram adicioná-lo!

Saco, acabei de fazer um commit e percebi que tenho que fazer uma pequena alteração

# faça a sua alteração
git add . # ou adicione arquivos individualmente
git commit --amend --no-edit
# agora o seu último commit contém a alteração
# AVISO: nunca faça alterações em commits públicos

Isso normalmente acontece quando eu faço commit, executo testes/linters e... saco, eu esqueci de colocar um espaço depois de um sinal de igual. Você também pode fazer a alteração como um novo commit e, então, utilizar o rebase -i para fazer o squash desse commit com o anterior, mas o amend é um milhão de vezes mais rápido.

Aviso: Você nunca deve fazer um amend em commits que já foram enviados (push) para uma branch public/compartilhada! Somente faça isso em commits que existem apenas na sua cópia local ou você poderá ter problemas.

Saco, preciso alterar a mensagem do meu último commit!

git commit --amend
# siga as instruções na tela para alterar o commit

Malditas regras de formatação de mensagens de commit.

Saco, eu fiz um commit na master por engano e deveria ter feito commit em uma branch nova!

# Crie a nova branch a partir da master atual 
git branch minha-nova-branch
# remova o último commit da master
git reset HEAD~ --hard
git checkout minha-nova-branch
# o seu commit está nessa branch agora :)

Nota: isso não funciona se você já fez push do commit para a master. Se você já tentou outras coisas antes, pode utilizar git reset HEAD@{numero-de-commits-anteriores} em vez HEAD~. Muito triste. Muitas muitas muitas pessoas sugeriram um jeito foda para fazer isso que eu mesmo não conhecia. Obrigado!

Saco, fiz commit na branch errada!

# desfaça o último commit, mas mantenha as alterações 
git reset HEAD~ --soft
git stash
# mova as alterações para a branch correta
git checkout nome-da-branch-correta
git stash pop
git add . # ou adicione arquivos individualmente
git commit -m "sua mensagem aqui";
# agora as suas alterações estão na branch correta 

Muitas pessoas sugeriram utilizar cherry-pick para essa situação, então escolha ai o que faz mais sentido para você!

git checkout nome-da-branch-correta 
# pegue o último commit da master 
git cherry-pick master
# remova-o da master
git checkout master
git reset HEAD~ --hard

Saco, tentei fazer um diff e não aconteceu nada?!

Se você sabe que alterou alguns arquivos, mas o diff não retorna nada, você provavelmente já adicionou (git add) esses arquivos na área de staging e, então, vai precisar utilizar uma flag especial.

git diff --staged

Arquivar em ¯\_(ツ)_/¯ (sim, eu sei que isso é uma funcionalidade e não um bug, mas é muito frustrante e totalmente não óbvio na primeira vez que acontece com você!)

Saco, preciso desfazer um commit feito 5 commits atrás!

# encontre o commit que você precisa desfazer
git log
# utilize as setas para navegar pelo histórico 
# quando encontrar o commit, salve o hash 
git revert [hash salvo]
# o git vai criar um commit que desfaz o commit
# siga as instruções para alterar a mensagem de commit
# ou apenas salve e faça o commit

Parece que não é necessário encontrar, copiar e colar o conteúdo antigo do arquivo para desfazer as alterações! Se você fez commit de um bug, é possível desfazer a alteração de uma vez só com o revert.

Você também pode fazer revert de um único arquivo em vez do commit inteiro! Mas, claro, como tudo no git, você precisa de um monte de comandos diferentes para isso...

Saco, preciso desfazer as alterações em um arquivo!

# encontre o hash de um commit feito antes do arquivo ser alterado
git log
# utilize as setas para navegar pelo histórico
# quando encontrar o commit, salve o hash
git checkout [hash salvo] -- caminho/para/o/arquivo
# a versão antiga do arquivo estará no seu índice
git commit -m "Uau, você não precisa copiar e colar para desfazer!"

Quando eu finalmente descobri isso foi SENSACIONAL. SENSACIONAL. S-E-N-S-A-C-I-O-N-A-L. Mas, falando sério, em que planeta que checkout -- faz sentido como a melhor maneira de desfazer alterações em um arquivo? :ameaca-um-soco-no-linus-torvalds:

Esquece, eu desisto.

cd ..
sudo rm -r repositorio-git-estupido
git clone https://some.github.url/repositorio-git-estupido.git
cd repositorio-git-estupido

Agradecimentos ao Eric V. por essa. Reclamações sobre a utilização do sudo devem ser enviadas para ele.

Agora, falando sério, se a sua branch está tão bagunçada que você precisa resetar o seu repositório local para ficar igual ao repositório remoto, tente essa maneira "aprovada pelo git" de fazer isso. Mas lembre que esses comandos são destrutivos e não será possível recuperar as alterações!

# recupere a versão mais atual do repositório remoto 
git fetch origin
git checkout master
git reset --hard origin/master
# remova arquivos e diretórios não rastreados pelo git
git clean -d --force
# repita a sequência checkout/reset/clean para cada branch bagunçado

*Aviso legal: Este site não pretende ser uma referência completa. Sim, existem outras maneiras de se fazer as mesmas coisas com uma maior pureza teórica ou sei la o quê, mas eu aprendi esses passos através de tentativa e erro, muitos palavrões e socos na mesa, então tive essa ideia maluca de compartilha-los com uma dose saudável de humor. É pegar ou largar!

Muito obrigado a todos os voluntários que traduziram o site para novos idiomas. Vocês são demais! Moritz Stückler (de) · Daniil Golubev (ru) · Łukasz Wójcik (pl) · fedemcmac (it) · Michel (fr) · Andriy Sultanov (ua) · Meiko Hori (ja) · Alex Tzimas (gr) · Franco Fantini (es) · Catalina Focsa (ro) · Davi Alexandre (pt_BR) . Com a ajuda adicional de Iain Murray · Frank Taillandier · David Fyffe · Lucas Larson

Se você gostaria de ajudar e traduzi-lo para o seu idioma, envie uma PR em GitHub