MÓDULO 01 · CONCEITO 07 DE 8

Git como ferramenta de pensamento

Commits atômicos com mensagens claras são forma de design — não cosmética.

Tempo de leitura ~22 min Pré-requisito Containers em profundidade Próximo GitHub Flow & PR workflow

Existe uma divisão silenciosa entre quem usa Git como sistema de backup — "salvar o que escrevi, baixar o que outros escreveram, resolver conflitos quando aparecem" — e quem usa Git como ferramenta de pensamento — "estruturar como uma mudança aconteceu, deixar pegadas inteligíveis para quem ler depois, encontrar a origem de bugs em segundos com bisect, reorganizar a história antes de publicá-la". A diferença não é técnica; é mental. As mesmas vinte ferramentas estão disponíveis para os dois grupos, mas só o segundo as usa para pensar.

Este conceito existe para garantir que você esteja no segundo grupo. A hipótese de fundo é: histórico bem-construído é forma de design. Um log de commits com mensagens descritivas, agrupadas por intenção, em ordem que conta uma história, é um documento técnico tão importante quanto o código em si — ele explica por que o código está como está, em incrementos pequenos o suficiente para serem entendidos isoladamente. Quem investe nisso tem uma vantagem cumulativa enorme: code reviews mais rápidos, debugging mais barato, onboarding mais suave, refactorings mais seguros.

O modelo mental do Git

A primeira coisa a destravar é o modelo mental. Git não é um sistema de "versões de arquivos" como CVS ou SVN antigos. Git é um sistema de snapshots imutáveis conectados por relações de "pai":

Essa é toda a estrutura. Tudo o que Git faz — branch, merge, rebase, cherry-pick, bisect — é manipulação dessa árvore de snapshots. Quando você internaliza esse modelo, comandos param de parecer mágicos: você sempre pode visualizar "antes e depois" da operação como duas configurações da mesma árvore.

Commits atômicos — a unidade fundamental

Um commit "atômico" é um commit que faz uma coisa, completa, compreensível em isolamento. Não duas mudanças misturadas; não meia feature; não "WIP" pendurado. A heurística é: se você precisar reverter este commit, deveria ser uma operação semanticamente coerente.

O critério prático de Tim Pope, no clássico ensaio "A Note About Git Commit Messages": "if applied, this commit will [imperative description]". Se você consegue completar essa frase com uma única descrição clara, o commit é atômico. Se a descrição precisa de "e" para conectar duas coisas ("adiciona endpoint X e refatora o parser Y"), são dois commits.

Vantagens de commits atômicos:

Mensagens de commit — anatomia

Boa mensagem de commit tem estrutura. A convenção mais usada (e a que ferramentas como GitHub Linear renderizam melhor) tem três partes:

  1. Linha de assunto (até 50 chars), no imperativo, sem ponto final. "Add retry logic to PaymentClient", não "Added retry logic" nem "Adding retry logic".
  2. Linha em branco.
  3. Corpo (opcional, mas recomendado para qualquer mudança não-trivial). Explica por quê, não o quê — o diff já mostra o quê. Linhas até 72 chars.
Add retry logic to PaymentClient

The payment gateway has been returning intermittent 503s
during peak hours, which today causes user-visible failures.
Add exponential backoff with jitter, max 3 retries.

Tested via integration test that simulates 503 from a
mock gateway. Production rollout via feature flag.

Refs: PAY-1234

O hábito de escrever mensagens assim é uma disciplina pequena que paga retornos enormes. Em três meses, alguém vai estar lendo o seu commit tentando entender por que aquilo foi feito. A mensagem é o presente que você dá para o futuro.

Rebase interativo — escultura de história

Rebase é a ferramenta de Git que mais separa quem flutua de quem comanda. Ela permite reescrever história local antes de publicá-la: reordenar commits, juntar (squash) commits relacionados, dividir um commit grande em vários menores, editar mensagens, eliminar commits indesejados.

# Rebase interativo dos últimos N commits
git rebase -i HEAD~N

# Abre editor com algo tipo:
pick a1b2c3d Add user model
pick d4e5f6g WIP fixing tests
pick h7i8j9k Add user endpoint
pick l0m1n2o WIP more fixes

# Você reorganiza:
pick a1b2c3d Add user model
fixup d4e5f6g WIP fixing tests
pick h7i8j9k Add user endpoint
fixup l0m1n2o WIP more fixes

# Resultado: 2 commits limpos em vez de 4 bagunçados.

Comandos interativos:

regra de ouro do rebase

Nunca faça rebase em commits que já foram pushados para uma branch compartilhada (ex: main). Rebase reescreve história, e isso confunde quem já clonou. Em branches pessoais (sua feature branch, antes do PR), rebase é totalmente seguro e desejável.

Merge vs rebase — o debate eterno

Quando integrar mudanças entre branches, há duas estratégias:

Vantagens de merge: preservação fiel do que aconteceu — você consegue reconstruir "essa feature foi feita em paralelo a essa outra durante esse período". Vantagens de rebase: log linear, mais fácil de ler. Cada equipe escolhe o estilo.

Padrão prático que funciona bem na maioria dos times modernos:

  1. Branches de feature: rebase em main antes de abrir PR. Mantém história linear, evita merges intermediários no log.
  2. Merge do PR para main: squash-and-merge (junta tudo num único commit atômico) ou merge commit explícito. Squash funciona bem se cada PR representa uma mudança coerente.

git bisect — a ferramenta que ninguém conhece

Bisect é provavelmente a ferramenta mais subutilizada de Git, e simultaneamente a que mais economiza tempo em situações específicas. O cenário: você sabe que algo funcionava ontem e quebrou hoje, mas não sabe qual dos cinquenta commits do dia introduziu o bug. Em vez de inspecionar cada um, bisect faz busca binária:

# Iniciar bisect
git bisect start

# Marcar o commit atual como bom (último que funcionava)
git bisect good v2.3.1

# Marcar o atual como ruim
git bisect bad HEAD

# Git checa out um commit no meio, você testa, e diz:
git bisect good   # ou
git bisect bad

# Repete log_2(N) vezes até identificar o commit culpado
# Para 64 commits = 6 testes; para 1024 commits = 10 testes

Você pode automatizar com git bisect run <script>: o Git checa out cada commit, roda o script, considera passou/falhou pelo exit code. Em casos onde você tem teste reproduzindo o bug, isso é inacreditavelmente eficiente — minutos em vez de horas.

A condição para bisect funcionar bem é que cada commit deveria estar em estado executável e testável. Commits "WIP" quebrados no meio do histórico atrapalham bisect. É uma das razões adicionais para commits atômicos.

git reflog — a rede de segurança

Reflog (reference log) é um histórico local de toda mudança em referências (branches, HEAD). Ele registra cada movimento, mesmo após operações "destrutivas" como reset hard, rebase ou checkout que parecem perder commits. Saber que reflog existe é a diferença entre pânico e calma.

# Ver últimos movimentos
git reflog

# Saída algo como:
abc1234 HEAD@{0}: rebase finished: returning to refs/heads/main
def5678 HEAD@{1}: rebase: pick xyz...
ghi9012 HEAD@{2}: checkout: moving from feature to main
...

# Recuperar commit "perdido"
git checkout def5678
# ou
git reset --hard def5678

Casos clássicos onde reflog salva:

Reflog é local — não viaja com push. E entradas eventualmente expiram (default 90 dias para entradas alcançáveis, 30 para não-alcançáveis). Mas enquanto está lá, é seu paraquedas.

Workflows: trunk-based vs Git Flow

Para times maduros com CI/CD funcional, o padrão moderno é trunk-based development: todas as mudanças vão para main rapidamente (horas a poucos dias), via PRs pequenos. Features em desenvolvimento ficam atrás de feature flags. Não há branches de longa vida.

Vantagens: integração contínua de fato, conflitos triviais (não há divergência longa para resolver), deploy contínuo possível, "branches" desnecessários para coordenação.

A alternativa histórica é Git Flow: branches de longa duração (develop, release/*, hotfix/*), cerimônias para mover entre eles. Faz sentido para software com ciclos de release longos (libs públicas, software embarcado, produtos com versionamento explícito). Para a maioria dos times de produto SaaS modernos, é overhead.

A discussão "trunk-based vs Git Flow" frequentemente reflete o estado da maturação de CI/CD do time mais do que preferência abstrata. Se você pode deployar segura e frequentemente (testes verdes, feature flags, monitoramento), trunk-based libera. Se você não pode, Git Flow protege.

Comandos que valem aprender

O Git tem mais de 150 comandos. A maioria você nunca vai usar. Mas há uma faixa intermediária — comandos que não estão entre os 5 primeiros tutoriais, mas que viram cotidiano de quem trabalha bem:

Configurações que mudam tudo

Algumas configurações globais que valem aplicar uma vez:

# Rebase ao puxar, em vez de criar merge commits
git config --global pull.rebase true

# Push só do branch atual
git config --global push.default current

# Cores no diff/log
git config --global color.ui auto

# Comparar com algoritmo melhor (mais legível em refactors)
git config --global diff.algorithm histogram

# Auto-correção de typos em comandos
git config --global help.autocorrect 30   # 3 segundos

# Rerere — lembra resoluções de conflito anteriores
git config --global rerere.enabled true

O último em particular — rerere (reuse recorded resolution) — é mágico: se você já resolveu um conflito uma vez, Git lembra e auto-resolve em rebases futuros do mesmo conflito. Salva tempo em rebases longos.

Como praticar

  1. Reescreva a história de uma branch sua. Pegue um branch com 10+ commits "WIP", "fix typo", "more fixes", e use rebase interativo para transformá-lo em 3-4 commits atômicos com mensagens decentes. Exercite squash, fixup, reword.
  2. Use bisect num projeto real. Se você não tem bug em mãos, plante um: introduza um defeito 20 commits atrás e use bisect para "encontrar" — mesmo sabendo onde está. A familiaridade com o comando é o ponto.
  3. Rastreie a origem de uma linha de código. Pegue um arquivo, escolha uma linha não-trivial. Use git blame + git log -p <arquivo> + git show <sha> para reconstruir a história daquela linha — quem escreveu, em que PR, por que.

Referências para aprofundar

  1. livro Pro Git (2nd ed.) — Scott Chacon & Ben Straub. git-scm.com/book — gratuito online. A referência definitiva. Capítulos 1-3 cobrem o essencial; 7 e 10 são para quem quer ir fundo.
  2. livro Building Git — James Coglan (2019). shop.jcoglan.com/building-git — você implementa Git do zero em Ruby. Para entender o modelo interno em profundidade.
  3. artigo A Note About Git Commit Messages — Tim Pope. tbaggery.com/2008/04/19 — texto canônico de 2008, ainda referência. Curto, formativo.
  4. artigo How to Write a Git Commit Message — Chris Beams. cbea.ms/git-commit/ — sete regras concretas. Use como checklist.
  5. artigo Trunk-Based Development — Paul Hammant. trunkbaseddevelopment.com — site dedicado ao tema, com diagramas e padrões. Inclui comparações com Git Flow.
  6. artigo git rebase in depth — git-rebase.io. git-rebase.io — tutorial interativo focado em rebase. Bom para construir intuição do que cada operação faz.
  7. artigo Git from the Bottom Up — John Wiegley. jwiegley.github.io/git-from-the-bottom-up — explica Git começando dos objetos (blobs, trees, commits) até comandos. Mental model preciso.
  8. docs Git Reference Manual. git-scm.com/docs — documentação oficial. git help <comando> abre offline.
  9. docs Conventional Commits. conventionalcommits.org — convenção para mensagens estruturadas (feat:, fix:, etc) que tools como semantic-release consomem.
  10. docs Atlassian Git Tutorials. atlassian.com/git/tutorials — tutoriais visuais de qualidade. Diagramas excelentes para entender merge vs rebase.
  11. vídeo So You Think You Know Git — Scott Chacon (FOSDEM 2024). YouTube. Co-fundador do GitHub mostra recursos avançados. Atualíssimo, denso.
  12. vídeo Deep Dive into Git — Edward Thomson. YouTube. Maintainer de libgit2. Detalhe técnico do que acontece embaixo dos comandos.