5 Comandos Git Além de Commit e Push
Cinco comandos Git além de commit e push: stash, reflog, cherry-pick, rebase -i e bisect para recuperar, limpar histórico e achar bugs.
A maioria dos desenvolvedores vive em add, commit, push e pull, mas a verdadeira força do Git está nos comandos de recuperação, edição de histórico e depuração que o ciclo diário nunca toca.
Você costuma conhecer o resto deles do jeito difícil. Um reset --hard engole uma tarde inteira de trabalho, ou um bug aparece em algum lugar dentro de 200 commits e ninguém sabe dizer qual deles quebrou tudo. Os cinco comandos abaixo resolvem problemas que o básico não consegue: guardar trabalho pela metade, recuperar commits que você achava perdidos, mover uma única correção entre branches, limpar um histórico bagunçado antes de um PR e localizar com precisão o commit que introduziu um bug. Cada um vem com um gatilho mental, a sintaxe exata e a pegadinha que costuma derrubar as pessoas.
Principais Conclusões
git stashguarda seu trabalho pela metade e devolve uma working tree limpa, permitindo trocar de contexto sem um commit descartável.git reflogregistra todos os lugares para onde o HEAD apontou, então, após um reset ou rebase malsucedido, você pode encontrar o hash do commit perdido e restaurá-lo comgit reset --hard <ref>.git cherry-pick <hash>copia um único commit para o seu branch atual, o que é ideal para mover um hotfix específico para um branch de release sem trazer todo o resto junto.- Comandos que reescrevem histórico, como
git rebase -iegit reset --hard, são seguros em trabalho local não publicado e perigosos em branches compartilhados;git reflogé o seu caminho de recuperação quando algo dá errado. git bisectfaz uma busca binária no seu histórico para encontrar o commit exato que introduziu um bug em log₂(n) passos.
Discover how at OpenReplay.com.
git stash: guarde o trabalho para trocar de contexto rapidamente
Use quando um relatório de bug chega enquanto sua working tree é uma bagunça pela metade e você precisa pular de branch agora. O git stash estaciona suas alterações não commitadas em um lugar seguro e devolve a working tree limpa, igual ao seu último commit. Nenhum commit “WIP” descartável é necessário.
git stash # shelve tracked changes, clean the tree
git stash --include-untracked # also shelve new, untracked files
git stash list # see all stashes: stash@{0}, stash@{1}, ...
git stash pop # reapply the latest stash and delete it
git stash apply stash@{1} # reapply a specific stash, keep it in the list
A pegadinha: pop e apply não são intercambiáveis. O git stash pop aplica suas alterações guardadas e apaga o stash; o git stash apply as aplica, mas mantém o stash, o que é mais seguro caso a reaplicação possa gerar conflito. Se um pop encontrar um conflito de merge, o stash não é descartado. Resolva o conflito primeiro e depois verifique, antes de presumir que ele sumiu.
git reflog: recupere trabalho que você achava perdido
Use quando um reset --hard mal feito, um rebase atrapalhado ou um branch apagado fazem commits sumirem e o pânico bater. O git reflog registra todas as posições que o HEAD ocupou no seu repositório local, então o commit “perdido” quase sempre ainda existe. Você só precisa do hash dele.
git reflog
# ab12cd3 HEAD@{0}: reset: moving to HEAD~1
# ef45gh6 HEAD@{1}: commit: the work you thought vanished
git reset --hard ef45gh6 # restore HEAD to that commit
# or, to inspect it on a new branch first:
git switch -c recovery ef45gh6
Encontre a ref do estado que você quer e então use git reset --hard <ref> para voltar até ele, ou faça o checkout em um novo branch para inspecioná-lo com segurança antes. Este é o herói do “ah não, perdi meu trabalho” do kit de ferramentas.
A pegadinha: o reflog é estritamente local e específico de cada clone. Ele não viaja para o remoto e não existe em um clone novo, então re-clonar para “recuperar seu trabalho” não vai ajudar. As entradas também são removidas ao longo do tempo pelo garbage collection, então recupere o quanto antes.
git cherry-pick: mova um commit entre branches
Use quando um único commit de hotfix precisa chegar a um branch de release, mas você não quer fazer merge de todo o branch de feature de onde ele veio. O git cherry-pick <hash> copia um commit específico para o seu branch atual, deixando todo o resto para trás.
git switch release-2.4
git cherry-pick 9f3c1a7 # apply one commit here
git cherry-pick 9f3c1a7 3b8e2d0 # apply several, in order
A pegadinha: o cherry-pick cria um novo commit com um novo hash. O original não é preservado nem movido. Se esse mesmo commit chegar depois a este branch através de um merge normal, você pode acabar com alterações duplicadas no histórico. Use-o de forma deliberada, não como substituto rotineiro do merge.
git rebase -i: limpe o histórico antes de um PR
Use quando seu branch de feature tem cinco commits de “fix typo” e “wip” e você quer um histórico organizado e revisável antes de abrir um pull request. O git rebase -i <base-branch> abre um editor onde você pode reordenar, esmagar (squash), editar ou descartar commits.
git rebase -i main
# pick a1b2c3d Add feature scaffold
# squash e4f5g6h fix typo
# reword h7i8j9k Wire up handler
# drop k0l1m2n debug logging
Marcar um commit como squash o funde com o de cima, reword altera apenas a mensagem dele e drop o deixa completamente de fora do resultado. Se você só precisa condensar um branch inteiro em uma única alteração no stage, o git merge --squash <branch> é a ferramenta irmã. Ele coloca as alterações combinadas no stage sem criar um commit, então você ainda executa o git commit você mesmo.
A pegadinha, e a regra de ouro: reescreva o histórico à vontade enquanto ele ainda for local, mas nunca faça rebase de commits que você já enviou para um branch compartilhado. Reescrever histórico compartilhado força todo mundo a uma reconciliação dolorosa, um risco que o Pro Git chama de “Os Perigos do Rebase”.
git bisect: encontre o commit que introduziu um bug
Use quando algo funcionava na semana passada e está quebrado agora, e você não faz ideia de qual dos 200 commits causou isso. O git bisect executa uma busca binária pelo histórico: marque um commit ruim e um commit bom, teste os pontos médios que o Git faz checkout, e ele localiza o primeiro commit ruim em log₂(n) passos.
git bisect start
git bisect bad # current commit is broken
git bisect good v2.3.0 # this older tag worked
# Git checks out a midpoint — test it, then tell Git:
git bisect good # this one is fine
git bisect bad # this one is broken
# ...repeat until Git reports "<hash> is the first bad commit"
git bisect reset # return HEAD to where you started
Você pode automatizar toda a busca: git bisect run <script> usa o status de saída de um script para marcar cada commit como bom ou ruim, sem nenhuma entrada manual. Encerre toda sessão com git bisect reset para devolver o HEAD ao ponto em que você estava antes da busca.
A pegadinha: o bisect presume que cada commit checado compila e roda seu teste sem problemas. Um commit que falha ao compilar por um motivo não relacionado distorce o resultado. Marque esses casos com git bisect skip em vez de chutar bom ou ruim.
Uma rede de segurança para os destrutivos
Dois desses comandos reescrevem o histórico, e a regra para ambos é a mesma: git rebase -i e git reset --hard são seguros em trabalho local não publicado e perigosos em branches compartilhados. Quando você precisa de um desfazer seguro em um branch que outras pessoas já puxaram, prefira o git revert, que registra um novo commit revertendo a alteração sem modificar o histórico existente. E quando uma reescrita local dá errado, o git reflog é a saída de emergência que traz seus commits de volta.
Estes cinco merecem seu lugar porque cada um responde a uma pergunta que commit e push não conseguem: salve isto, recupere aquilo, mova um, organize, cace o bug. Associe cada um ao seu gatilho e recorra a ele no momento em que a situação aparecer.
Perguntas Frequentes
Qual é a diferença entre git reset --hard e git revert?
O git reset --hard rebobina o HEAD e descarta commits, reescrevendo o histórico, o que o torna perigoso em qualquer branch que outras pessoas já tenham puxado. Já o git revert registra um novo commit que reverte a alteração desejada, mantendo o histórico existente intacto. Use reset --hard apenas em trabalho local não publicado; use revert como o desfazer seguro em branches compartilhados. Se um reset der errado, o git reflog pode recuperar os commits descartados pelo hash.
Como recupero um commit depois que o git reset --hard o apagou?
Execute git reflog, que registra todas as posições que o HEAD ocupou no seu repositório local. Encontre a entrada do estado que você quer, anote o hash e então execute git reset --hard <hash> para restaurá-lo, ou git switch -c recovery <hash> para inspecioná-lo primeiro em um novo branch. O reflog é local e específico de cada clone, então re-clonar não vai recuperar o trabalho, e as entradas são removidas ao longo do tempo pelo garbage collection, então recupere o quanto antes.
O git switch substitui o git checkout para trocar de branch?
O git switch e o git restore foram introduzidos no Git 2.23 (agosto de 2019) para dividir os papéis sobrecarregados do git checkout: o switch cuida da movimentação entre branches e o restore cuida da restauração de arquivos. Ambas as páginas de manual carregaram um aviso de experimental por anos, mas esse aviso foi removido a partir do Git 2.55.0, então agora eles são simplesmente os comandos recomendados para essas operações. O git checkout continua funcionando e não está descontinuado, então fluxos de trabalho existentes permanecem válidos.
Quando devo usar git cherry-pick em vez de merge?
Use o git cherry-pick quando você precisa exatamente de um commit em outro branch (por exemplo, mover um único hotfix para um branch de release) sem trazer o restante do histórico do branch de origem. Um merge traz as alterações do branch inteiro, o que é a ferramenta errada para isolar uma única correção. Note que o cherry-pick cria um novo commit com um novo hash, então, se a mesma alteração chegar depois através de um merge normal, você pode acabar com alterações duplicadas no histórico.