<h2>Git Avançado: Rebase, Cherry-pick, Bisect e Recuperação de Histórico</h2>
<h3>Introdução ao Workflow Avançado</h3>
<p>Quando você trabalha em equipes grandes ou em projetos complexos, git básico (commit, push, pull) não é suficiente. Você precisa dominar ferramentas que permitem reescrever histórico, selecionar commits específicos, encontrar bugs e recuperar trabalho perdido. Este artigo é para quem já sabe usar git diariamente e quer evoluir para um nível profissional, dominando as operações que separam desenvolvedores que apenas usam git daqueles que realmente o dominam.</p>
<p>O conhecimento dessas técnicas não é opcional em empresas que levam qualidade a sério. Elas resolvem problemas reais: um commit foi feito na branch errada? Use cherry-pick. O histórico está bagunçado? Use rebase. Qual commit introduziu o bug? Use bisect. Deletou a branch por acidente? Recupere com reflog. Vamos começar.</p>
<p>---</p>
<h2>Rebase: Reorganizando e Limpando Histórico</h2>
<h3>O que é Rebase e Por que Importa</h3>
<p>Rebase é o conceito mais mal compreendido de git. Não é merge. Enquanto merge cria um commit de fusão que preserva ambas as histórias, rebase <strong>reaplica seus commits em cima de outra branch</strong>, criando um histórico linear e limpo. Pense em rebase como "mover sua base de trabalho para um novo ponto".</p>
<p>A primeira razão para usar rebase é estética: um histórico linear é mais fácil de ler e debugar. A segunda é prática: quando você trabalha em uma feature branch por dias enquanto main recebe outros commits, rebase garante que sua feature está construída em cima da versão mais recente do código. Isso é especialmente importante antes de fazer merge.</p>
<h3>Rebase Interativo: O Poder está aqui</h3>
<pre><code class="language-bash"># Você está na branch 'feature' com 5 commits
Quer reorganizar, combinar ou editar 3 últimos commits
git rebase -i HEAD~3</code></pre>
<p>O comando acima abre um editor com algo assim:</p>
<pre><code>pick a1b2c3d Adiciona validação de email
pick d4e5f6g Corrige bug de formatação
pick h7i8j9k Adiciona testes unitários</code></pre>
<p>Você pode modificar para:</p>
<pre><code>pick a1b2c3d Adiciona validação de email
squash d4e5f6g Corrige bug de formatação
reword h7i8j9k Adiciona testes unitários</code></pre>
<ul>
<li><code>pick</code>: mantém o commit como está</li>
<li><code>squash</code> (ou <code>s</code>): combina este commit com o anterior e permite editar a mensagem</li>
<li><code>reword</code> (ou <code>r</code>): mantém as mudanças mas permite editar a mensagem</li>
<li><code>drop</code> (ou <code>d</code>): remove o commit completamente</li>
<li><code>fixup</code> (ou <code>f</code>): como squash mas descarta a mensagem do commit</li>
</ul>
<pre><code class="language-bash"># Exemplo real: você tem 3 commits e quer deixar apenas 1
git rebase -i HEAD~3
Edita: pick primeiro, squash segundo, squash terceiro
Resultado: 1 commit com todas as mudanças</code></pre>
<h3>Rebase sobre outra Branch</h3>
<p>Cenário comum: você criou feature a partir de main, mas main evoluiu enquanto você trabalhava. Você quer seus commits em cima da versão atual de main.</p>
<pre><code class="language-bash"># Você está em 'feature'
git rebase main
Isso reaplica seus commits em cima de main
Se houver conflitos:
1. Resolva manualmente os arquivos
2. git add .
3. git rebase --continue
Se quiser cancelar:
git rebase --abort</code></pre>
<blockquote><p><strong>Regra de Ouro</strong>: Nunca faça rebase em commits que já foram pusheados para um repositório compartilhado. Rebase reescreve histórico. Se alguém já fez pull desses commits, você criará problemas. Use rebase apenas em branches locais ou branches que você controla sozinho.</p></blockquote>
<p>---</p>
<h2>Cherry-pick: Selecionando Commits Específicos</h2>
<h3>Quando e Por que Usar</h3>
<p>Cherry-pick é simples: você copia um commit específico de uma branch e o aplica em outra. Diferente de rebase que move uma sequência inteira, cherry-pick permite seletividade cirúrgica.</p>
<p>Casos de uso reais: um bug foi corrigido em develop mas precisa estar urgentemente em production; você commitou em feature por engano e precisa em main; uma hotfix foi feita em uma branch e precisa em várias outras. Em todos esses casos, cherry-pick é sua ferramenta.</p>
<h3>Cherry-pick Prático</h3>
<pre><code class="language-bash"># Você está em 'main' e quer trazer o commit abc123d de 'develop'
git cherry-pick abc123d
Se houver conflitos:
git cherry-pick --continue
ou desistir:
git cherry-pick --abort
Cherry-pick múltiplos commits em sequência
git cherry-pick abc123d def456e ghi789f</code></pre>
<p>Exemplo realista: seu repositório tem main (production) e develop. Um bug crítico foi corrigido em develop no commit <code>a3b2c1d</code>. Você precisa desse fix em production hoje.</p>
<pre><code class="language-bash"># Em main
git cherry-pick a3b2c1d
O commit é agora parte de main com um novo hash (porque mudou a parent)
Isso é esperado e correto</code></pre>
<h3>Cuidado com Cherry-pick</h3>
<pre><code class="language-bash"># Ruim: fazer cherry-pick de 50 commits de uma vez
git cherry-pick develop~50..develop
Bom: usar rebase se precisa de uma sequência inteira
git rebase develop</code></pre>
<p>Cherry-pick deixa rastros claros no histórico (o hash muda, mas a mensagem e o autor são preservados), o que é bom para auditoria. Mas se você está movendo muitos commits de uma vez, rebase é a ferramenta correta.</p>
<p>---</p>
<h2>Bisect: Encontrando o Commit que Quebrou Tudo</h2>
<h3>Binary Search no Histórico</h3>
<p>Imagine: seu código estava funcionando há 50 commits atrás, agora está quebrado. Você não sabe qual commit introduziu o bug. Procurar manualmente é insano. Aqui entra <code>git bisect</code>: uma busca binária que reduz 50 commits para descobrir em ~6 iterações.</p>
<p>Bisect funciona assim: você marca um commit como "bom" (sem o bug) e outro como "ruim" (com o bug), e git divide o espaço no meio, pedindo para você testar. Baseado na resposta, ele elimina metade dos commits suspeitos. Repete até encontrar o commit exato.</p>
<h3>Bisect Manual</h3>
<pre><code class="language-bash"># Iniciar bisect
git bisect start
Marcar o commit atual como ruim (tem o bug)
git bisect bad
Marcar um commit antigo como bom (não tem o bug)
git bisect good abc123d
Git agora te coloca em um commit no meio
Você testa se tem o bug ou não
Se este commit tem o bug:
git bisect bad
Se não tem:
git bisect good
Repita até git encontrar o commit exato
Quando terminar:
git bisect reset</code></pre>
<p>Exemplo com números. Digamos que você tem 64 commits. Bisect funciona assim:</p>
<pre><code>Commits: [bom] ........ [meio=commit 32] ........ [ruim]
Você testa: ainda é ruim
Commits: [bom] .... [meio=commit 16] .... [ruim]
Você testa: é bom
Commits: [bom] [meio=commit 24] [ruim]
... em ~6 testes você encontra o exato</code></pre>
<h3>Bisect Automatizado</h3>
<pre><code class="language-bash"># Se você tem um script/teste que pode detectar o bug automaticamente
git bisect start
git bisect bad HEAD
git bisect good abc123d
Comando automático que executa seu teste
git bisect run ./test_script.sh
test_script.sh retorna:
0 = sucesso (commit é bom)
1 = falha (commit é ruim)
125 = skip este commit</code></pre>
<pre><code class="language-bash">#!/bin/bash
test_script.sh
npm test
Retorna 0 se passar, 1 se falhar</code></pre>
<p>---</p>
<h2>Recuperação de Histórico: Reflog e Dangling Commits</h2>
<h3>Reflog: Seu Seguro contra Perdas</h3>
<p>Reflog (reference log) é um log de todas as mudanças nos ponteiros das branches localmente. Deletou uma branch por acidente? Fez um reset errado? Reflog tem tudo. É diferente do histórico normal de commits — é um histórico de "onde você esteve" no seu repositório.</p>
<pre><code class="language-bash"># Ver reflog
git reflog
Saída algo assim:
a1b2c3d HEAD@{0}: commit: Adiciona feature X
d4e5f6g HEAD@{1}: rebase -i (finish): returning to refs/heads/feature
h7i8j9k HEAD@{2}: rebase -i (start): checkout main
l0m1n2o HEAD@{3}: checkout: moving from main to feature</code></pre>
<p>Cada linha representa um movimento. <code>HEAD@{0}</code> é o estado atual, <code>HEAD@{1}</code> é um estado atrás, etc. Você pode usar reflog para ir para qualquer estado anterior.</p>
<h3>Recuperando uma Branch Deletada</h3>
<pre><code class="language-bash"># Você acidentalmente fez
git branch -D feature
Use reflog para ver onde estava
git reflog
Encontre algo como:
a1b2c3d HEAD@{5}: commit: Último commit da feature
Recupere criando uma nova branch no mesmo ponto
git branch feature a1b2c3d
Pronto, a branch está recuperada</code></pre>
<h3>Dangling Commits</h3>
<p>Commits não referenciados (não estão em nenhuma branch) aparecem como "dangling". Reflog mantém referências a eles por padrão durante 90 dias.</p>
<pre><code class="language-bash"># Ver commits orfãos
git fsck --lost-found
Buscar commits perdidos
git log --all --graph --oneline --decorate
Se quiser recuperar um commit específico
git show a1b2c3d # veja o conteúdo
git branch recover-branch a1b2c3d # crie uma branch nele</code></pre>
<h3>Reset, Revert e Restore: Diferenças Críticas</h3>
<p>Essas três operações parecem fazer a mesma coisa, mas não fazem:</p>
<pre><code class="language-bash"># git reset: move o HEAD (reescreve histórico local)
git reset --soft HEAD~1 # desfaz commit, mantém mudanças staged
git reset --mixed HEAD~1 # desfaz commit, mantém mudanças unstaged (padrão)
git reset --hard HEAD~1 # desfaz commit, descarta mudanças (CUIDADO!)
git revert: cria um NOVO commit que desfaz as mudanças (seguro)
git revert abc123d # cria novo commit que inverte as mudanças do abc123d
git restore: descarta mudanças em arquivos específicos
git restore arquivo.txt # descarta mudanças não commitadas
git restore --source=abc123d arquivo.txt # restaura arquivo de um commit</code></pre>
<p><strong>Quando usar cada um:</strong></p>
<ul>
<li><code>reset</code>: você quer desfazer commits <strong>locais não pusheados</strong></li>
<li><code>revert</code>: você quer desfazer commits <strong>que foram pusheados</strong> (seguro para histórico compartilhado)</li>
<li><code>restore</code>: você quer desfazer edições em <strong>arquivos específicos</strong></li>
</ul>
<pre><code class="language-bash"># Exemplo: você commitou credenciais por acidente em abc123d
Opcão 1 (se ninguém fez pull):
git reset --hard HEAD~1
Opção 2 (se foi pusheado):
git revert abc123d
git push
Opção 3 (remover apenas um arquivo de um commit passado):
git restore --source=abc123d~1 credenciais.env
git add credenciais.env
git commit --amend # adiciona à mensagem anterior</code></pre>
<p>---</p>
<h2>Casos Práticos Integrados</h2>
<h3>Cenário 1: Limpando uma Feature Branch Bagunçada</h3>
<pre><code class="language-bash"># Você tem 8 commits em 'feature' mas alguns são fixups/ajustes
Quer limpar antes de fazer merge em main
git checkout feature
git rebase -i main
Editor abre:
pick a1b2c3d Implementa login
pick d4e5f6g Adiciona validação email
pick h7i8j9k Corrige typo em login
pick l0m1n2o Testa login com mobile
...
Você edita para:
pick a1b2c3d Implementa login
squash h7i8j9k Corrige typo em login
pick d4e5f6g Adiciona validação email
squash l0m1n2o Testa login com mobile
pick ...
Resultado: 4 commits bem organizados em vez de 8 bagunçados
git push -f # força push (apenas em branches suas!)</code></pre>
<h3>Cenário 2: Hotfix em Production que precisa voltar para Develop</h3>
<pre><code class="language-bash"># main (production) tinha um bug urgente
Criou hotfix, commitmou fix abc123d, pushou para main
Agora precisa voltar o fix para develop também
git checkout develop
git cherry-pick abc123d
Se houver conflitos (código evoluiu diferente):
Resolve manualmente
git add .
git cherry-pick --continue
git push</code></pre>
<h3>Cenário 3: Encontrando qual deploy quebrou</h3>
<pre><code class="language-bash"># Sabia que breaks entre v1.2.3 e v1.3.0
v1.2.3 é bom, v1.3.0 é ruim
git bisect start
git bisect bad v1.3.0
git bisect good v1.2.3
Git coloca você em um commit no meio
Você testa sua aplicação
Se funciona:
git bisect good
Se quebra:
git bisect bad
Repete até descobrir o commit exato
Quando encontra:
Commit abc123d é o primeiro bad commit
Você pode revert ou cherry-pick um fix para ele</code></pre>
<p>---</p>
<h2>Conclusão</h2>
<p>Você aprendeu três lições fundamentais que elevam seu git de "usuário" para "profissional":</p>
<ol>
<li><strong>Rebase e cherry-pick são ferramentas de poder responsável</strong>: eles reescrevem histórico (rebase) ou selecionam commits cirurgicamente (cherry-pick). Ambos deixam um repositório mais limpo e legível, mas exigem cuidado com branches compartilhadas. A regra é simples: rebase local, revert compartilhado.</li>
</ol>
<ol>
<li><strong>Bisect transforma "encontrar o bug" de uma tarefa manual exaustiva em um problema de busca binária que se resolve em minutos</strong>, mesmo com centenas de commits. É especialmente poderoso quando combinado com scripts automatizados que detectam o problema.</li>
</ol>
<ol>
<li><strong>Reflog, reset, revert e restore formam uma rede de segurança contra erros</strong>. Quase nada é permanentemente perdido em git — reflog segura seus commits órfãos, revert permite desfazer publicamente sem reescrever histórico, e reset/restore lidam com situações locais. Memorize a diferença entre essas operações; é exatamente onde mais gente erra.</li>
</ol>
<p>O domínio dessas técnicas não é vanidade — é profissionalismo. Projetos reais têm histórico bagunçado, bugs misteriosos e situações onde alguém commitou algo na branch errada às 3 da manhã. Você será a pessoa que resolve isso em 5 minutos em vez de 5 horas.</p>
<p>---</p>
<h2>Referências</h2>
<ul>
<li><a href="https://git-scm.com/docs/git-rebase" target="_blank" rel="noopener noreferrer">Git Official Documentation - git-rebase</a></li>
<li><a href="https://git-scm.com/docs/git-cherry-pick" target="_blank" rel="noopener noreferrer">Git Official Documentation - git-cherry-pick</a></li>
<li><a href="https://git-scm.com/docs/git-bisect" target="_blank" rel="noopener noreferrer">Git Official Documentation - git-bisect</a></li>
<li><a href="https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History" target="_blank" rel="noopener noreferrer">Pro Git Book - Chapter 7: Git Tools</a></li>
<li><a href="https://www.atlassian.com/git/tutorials/advanced-overview" target="_blank" rel="noopener noreferrer">Atlassian Git Tutorials - Advanced Git</a></li>
</ul>
<p><!-- FIM --></p>