Portfólio Blog Sobre
GIT - O melhor controle de versão

GIT - O melhor controle de versão

Controlar versões de código sempre foi um problema. Antes do Git, equipes usavam pastas com datas, e-mail com anexo ou ferramentas centralizadas que travavam arquivos enquanto alguém editava. Em 2005, Linus Torvalds criou o Git para resolver o problema no kernel do Linux: distribuído, rápido e projetado para trabalho paralelo em escala.

Hoje o Git está em praticamente todo projeto de software. A pesquisa Stack Overflow de 2025 mostra o GitHub, principal plataforma de hospedagem de código, com 81% de adoção entre os respondentes, o que reflete como o Git virou infraestrutura universal do desenvolvimento. O que varia é o domínio das ferramentas, e esse domínio é o que separa quem sofre com merge às sextas-feiras de quem entrega com calma toda semana.

Conceitos fundamentais

ConceitoO que é
VCS (Version Control System, Sistema de Controle de Versão)Ferramenta que registra o histórico de mudanças de arquivos ao longo do tempo, permitindo reverter, comparar e colaborar
GitVCS distribuído criado por Linus Torvalds em 2005; cada clone do repositório é uma cópia completa do histórico
Repositório (repo)Diretório versionado pelo Git; contém o histórico completo de commits, branches e configurações
Branch (cópia)Cópia isolada do código para desenvolver uma mudança sem afetar a linha principal
Commit (registro de alteração)Snapshot do código em um momento, com uma mensagem descrevendo o que mudou e por quê
Merge (mesclagem)Integração das alterações de uma branch de volta à principal
Rebase (reorganização de commits)Reaplica commits de uma branch sobre outro ponto do histórico, produzindo um histórico linear
PR (Pull Request, Pedido de Integração)Solicitação de mesclagem de uma branch para a main, com revisão obrigatória
TBD (Trunk-Based Development, Desenvolvimento baseado no tronco)Estratégia onde a main é a fonte única da verdade; branches são curtas e focadas
Stash (área temporária)Área que guarda alterações locais sem criar commit, liberando a branch para outra tarefa
Squash (compactação de commits)Agrupa vários commits em um único antes do merge, mantendo o histórico limpo
Reflog (registro local de movimentos)Histórico interno de todas as posições que HEAD ocupou; usado para recuperar commits perdidos
CI/CD (Continuous Integration/Delivery, Integração Contínua/Entrega Contínua)Pipeline que automatiza lint, testes, build e deploy a cada mudança de código
Pipeline (sequência de verificações)Conjunto ordenado de estágios que todo código deve passar; cada estágio é um portão
Feature flag (interruptor de funcionalidade)Configuração que ativa ou desativa uma feature em produção sem novo deploy
Pre-commit hook (gancho de pré-commit)Script executado localmente antes de cada commit para detectar problemas antes do push
Fix forward (corrigir para frente)Estratégia de corrigir bugs com um novo commit e deploy, sem reverter o histórico
Rollback (reversão)Retorno do sistema à versão anterior; reservado para emergências quando fix forward é inviável

Branches: trabalho isolado

Trunk-Based Development é a estratégia adotada pela maioria dos times de alto desempenho. A regra é simples: a main é a fonte única da verdade. Cada branch deriva dela, tem um único propósito e dura poucos dias antes de voltar via PR.

main → branch → commits → PR → review → merge → main
RegraMotivo
Branch derivada da mainEvita divergência acumulada e conflitos tardios
Curta e focadaPR pequeno é mais fácil de revisar e tem menor risco de regressão
Um propósito por branchMisturar feat + fix + refactor dificulta reversão e rastreabilidade
Nunca branch de branchDependências implícitas complicam o merge

A nomenclatura segue o padrão <tipo>/<descricao-em-kebab-case>:

❌ Ruim
feature-nova
minha-branch
fix
branch-do-joao-refactor-e-tambem-o-bug-do-login
✅ Bom
feat/user-email-verification
fix/order-discount-rounding
docs/git-conventions
refactor/payment-service-split

Gitflow ainda tem lugar?

Para a maioria das aplicações web e SaaS com deploy contínuo, TBD é o caminho. O GitFlow (com branches develop, release, hotfix) ainda faz sentido para projetos open-source com múltiplas versões suportadas em paralelo ou produtos enterprise com ciclos de release longos e homologações formais. Fora desses contextos, a complexidade do GitFlow cria mais atrito do que resolve.

Commits: o registro que importa

O commit não documenta o como, documenta o o que e o por quê. Conventional Commits é a convenção mais adotada: um formato simples que torna o histórico navegável por humanos e por ferramentas de CI/CD.

<tipo>[escopo opcional]: <descrição no imperativo, em inglês, sem ponto final>
TipoQuando usar
featNova funcionalidade visível ao usuário ou sistema
fixCorreção de bug
docsApenas documentação
refactorMudança interna sem alterar comportamento
testAdição ou correção de testes
perfMelhoria de performance
styleFormatação, whitespace, sem mudança de lógica
choreTarefas de manutenção (build, deps, config)
ciMudanças em pipelines de CI/CD
revertReverte um commit anterior

O escopo é opcional. Use quando o contexto não é óbvio pelo tipo: auth, order, payment, user.

A descrição responde sempre à mesma pergunta: o que esse commit faz? Escreva no imperativo, como se completasse a frase “esse commit vai…”: add email verification, fix discount rounding, remove unused dependency. Nunca no passado (added, fixed) nem no gerúndio (adding, fixing). Inglês, sem maiúscula inicial, sem ponto final, até 72 caracteres.

❌ Ruim
fix bug
update
arrumei o login
WIP
feat: adiciona validação no campo de e-mail do usuário no formulário de cadastro da tela de onboarding
✅ Bom
feat(auth): add email verification on signup
fix(order): correct discount rounding for fractional quantities
docs: add git conventions
refactor(payment): extract charge logic into PaymentService
chore: upgrade eslint to v9

Pull Requests: a porta da main

O PR é o ponto de verificação antes que qualquer mudança toque a main. Revisão obrigatória, checks verdes e squash no merge são as três práticas que mantêm o histórico saudável.

PráticaMotivo
PR pequeno e focadoReview mais rápido, menor superfície de bug
Review obrigatórioNinguém faz merge do próprio PR sem aprovação
Checks verdes antes do mergeCI/CD valida antes de tocar a main
Squash antes do mergeUm commit por PR mantém o histórico legível e viabiliza git bisect
Menos de 400 linhas como referênciaDiffs grandes cansam e geram reviews superficiais

Antes de abrir o PR, preencha o título no formato Conventional Commits e uma descrição com contexto e decisões não óbvias. O reviewer não precisa perguntar o que você já sabe.

❌ Ruim
título: update
descrição: (vazia)
commits: WIP, fix, arrumei, agora vai, update 2
checks: falhando
linhas alteradas: 1.200
✅ Bom
título: feat(auth): add email verification on signup

descrição:
Adiciona verificação de e-mail no cadastro. Token expira em 24h.
Optei por gerar o token no backend para evitar previsibilidade no cliente.

commits: 1 commit limpo via squash
checks: todos verdes
linhas alteradas: 180

Rotina prática

O ciclo correto parte da main atualizada, usa uma branch com um único propósito e termina com o PR mergeado e a branch removida.

pull main → nova branch → commits focados → fetch origin → merge origin/main → PR → squash and merge → deletar branch
✅ Passo a passo completo
# 1. atualizar main antes de começar
git checkout main
git pull origin main

# 2. criar branch com um único propósito
git checkout -b feat/user-email-verification

# 3. commits focados durante o trabalho
git add src/auth/email.js
git commit -m "feat(auth): add email verification token generation"

git add src/auth/email.test.js
git commit -m "test(auth): cover token expiry and reuse scenarios"

# 4. incorporar atualizações da main antes do PR
git fetch origin
git merge origin/main
# sem conflitos: merge commit criado automaticamente
# com conflitos: ver seção Troubleshooting

# 5. enviar para review
git push origin feat/user-email-verification

# 6. após o merge: confirmar e deletar
git log origin/main --oneline -3

# -d rejeita se a branch não foi mergeada, proteção extra
git branch -d feat/user-email-verification
git push origin --delete feat/user-email-verification

Quando uma tarefa de maior prioridade interrompe o trabalho em andamento, o Stash guarda o estado sem criar um commit de WIP no histórico.

# guardar o estado atual sem commitar
git stash push -m "wip: email verification form"

# trocar para a tarefa prioritária
git checkout feat/other-priority-task

# ... trabalhar, commitar, abrir PR ...

# voltar para a tarefa original e restaurar
git checkout feat/user-email-verification
git stash pop

Squash: histórico limpo

Um PR com 30 commits fragmentados dificulta git bisect (busca binária de regressão) e git blame. O squash compacta todo o trabalho em um único commit descritivo na hora do merge.

WIP → fix typo → esqueci de salvar → arrumei → squash → feat(auth): add email verification (#42)

No GitHub, clique no dropdown ao lado de “Merge pull request” e selecione Squash and merge. Ajuste a mensagem antes de confirmar.

Para limpar commits locais antes do primeiro push, use o Interactive rebase (rebase interativo):

# compactar os 4 últimos commits locais em um
git rebase -i HEAD~4

# no editor: 'pick' no primeiro, 's' nos demais
# pick abc1234 feat(auth): add email verification token
# s   def5678 fix typo
# s   ghi9012 WIP
# s   jkl3456 forgot test file

Rebase local antes do primeiro push: seguro. Rebase em branch compartilhada: nunca. Reescreve o histórico que outros já baixaram e gera divergências irreconciliáveis.

Troubleshooting

O que não fazer

AçãoConsequência
git push --force origin mainSobrescreve o histórico da main para toda a equipe
git push --force sem --leasePode destruir commits de um colega que chegaram ao remoto enquanto você trabalhava
git reset --hard sem stash antesDescarta alterações locais sem chance de recuperação
git clean -fd sem revisar com -n antesRemove arquivos não rastreados que podem não estar no .gitignore
git rebase em branch compartilhadaReescreve histórico que outros já baixaram; cria divergências

Inspecione antes de agir:

# ver o que mudou nos últimos 3 commits antes de resetar
git diff HEAD~3

# histórico resumido da branch atual
git log --oneline -10

# ver quais arquivos mudaram em cada commit
git log --stat -5

# simular limpeza sem executar
git clean -n -fd

Recuperando commits perdidos com Reflog

O Reflog registra todas as posições que HEAD ocupou. Funciona como um histórico de desfazer local, independente de push.

# listar todas as posições recentes do HEAD
git reflog

# saída:
# abc1234 HEAD@{0}: rebase: feat(auth): add email verification
# def5678 HEAD@{1}: commit: test(auth): cover token expiry
# ghi9012 HEAD@{2}: commit: feat(auth): add email verification token

# restaurar para um estado anterior
git checkout HEAD@{2}

# criar branch a partir de um commit perdido
git checkout -b recovery/email-verification ghi9012

Conflitos com a main

Se a main avançou enquanto você trabalhava, incorpore as mudanças com um merge commit na branch. O squash no merge do PR limpa tudo depois.

git fetch origin
git merge origin/main

# sem conflitos: Git cria o merge commit automaticamente
# com conflitos: resolver cada arquivo e commitar

git add .
git commit -m "chore: merge main into feat/user-email-verification"

Corrigindo um problema em produção

Quando um bug aparece em produção, o primeiro caminho é criar uma branch de fix a partir da main e entregar via PR. Mantém o histórico avançando sem desfazer mudanças de outros devs.

git checkout main
git pull origin main

git checkout -b fix/user-email-token-expiry

git add src/auth/email.js
git commit -m "fix(auth): correct token expiry on email verification"

git fetch origin
git log origin/main --oneline -3

# PR → squash and merge → confirmar deploy → deletar branch

Se o tempo for crítico e não houver janela para review e deploy, o caminho é reverter.

Revertendo um deploy com problema

git revert não apaga o commit: cria um novo commit que desfaz o efeito. O commit original permanece no histórico.

# reverter o commit problemático na main
git revert <hash-do-squash-commit>
git push origin main

# criar branch de fix a partir da main já revertida
git checkout -b fix/user-email-verification

git add src/auth/email.js
git commit -m "fix(auth): correct token expiry on email verification"

# PR → squash and merge → deploy

CI/CD: do commit ao usuário

CI/CD é o processo que garante que qualquer mudança passe por verificação automática antes de chegar ao usuário.

  • CI (Continuous Integration, Integração Contínua): valida qualidade a cada push, com lint, testes e build.
  • CD (Continuous Delivery, Entrega Contínua): promove o artefato pelos ambientes até produção com aprovação manual no último estágio.
  • CD (Continuous Deployment, Deploy Contínuo): promove até produção sem aprovação manual.
Lint → Segurança → Testes → Build → Deploy Staging → Smoke → Deploy Prod
EstágioO que verificaCritério de falha
LintEstilo e formataçãoQualquer violação
SegurançaSecrets expostos, vulnerabilidades conhecidasQualquer secret; CVE explorado
TestesComportamento esperado do sistemaQualquer falha
BuildCompilação e empacotamento do artefatoQualquer erro
Deploy StagingPromoção para ambiente espelhoFalha no health check
SmokeFluxo crítico funciona em stagingQualquer falha no caminho crítico
Deploy ProdPromoção para produçãoAprovação manual ou canary gate

O artefato que vai para produção é o mesmo que passou por staging. Fazer rebuild entre ambientes invalida a garantia dos testes.

Deploy e release são eventos independentes

merge na main → deploy (automático) → feature flag desativada → release gradual → 100%

Deploy é o ato técnico de colocar o código em produção, acontece automaticamente após merge na main com pipeline verde. Release é o ato de tornar a funcionalidade visível ao usuário, controlado por feature flag, acontece quando o time decide.

SituaçãoAção
Feature em desenvolvimentoSobe desativada, código na main sem risco
Feature pronta, aguardando validaçãoAtiva para % do tráfego ou grupo interno
Feature com problemaDesativa sem rollback de código
Feature validadaAtiva para 100%, flag removida

Flags têm prazo de validade. Uma flag que nunca é removida vira débito técnico.

Fix forward vs rollback

Fix forward é a abordagem preferida: a main segue para frente com histórico linear.

bug em prod → PR na main → pipeline → merge → deploy
EtapaAção
IdentificarConfirmar o comportamento inesperado via logs e métricas
IsolarDesativar a feature flag se o bug estiver coberto por uma
CorrigirAbrir PR na main com a correção
ValidarPipeline verde: lint, testes, build
EntregarMerge e deploy seguindo o fluxo normal
ConfirmarMonitorar logs após deploy para garantir estabilização

Rollback fica reservado para emergências: sistema indisponível e fix forward inviável no tempo necessário.

Pre-commit: detectar antes do push

Pre-commit hooks detectam problemas antes do commit, onde o custo de correção é mínimo. O CI detecta tarde, após o push, na esteira. O custo do hook deve ficar abaixo de 5 segundos para não criar atrito no fluxo de trabalho.

código staged → lint → auto-fix → commit

Novidades em 2026

O Git não parou. Algumas melhorias recentes que valem atenção:

Git Worktrees: múltiplas branches em paralelo

Git Worktrees permitem ter múltiplos diretórios de trabalho do mesmo repositório ao mesmo tempo, cada um em uma branch diferente, sem precisar fazer checkout. Útil para revisar um PR enquanto trabalha em uma feature, ou testar duas versões lado a lado.

Em julho de 2025, o VS Code recebeu suporte nativo a worktrees na integração Git.

# criar um worktree em uma pasta separada com a branch fix/auth-token
git worktree add ../meu-repo-fix fix/auth-token

# listar worktrees ativos
git worktree list

# remover após o uso
git worktree remove ../meu-repo-fix

Scalar: Git para repositórios grandes

Scalar está integrado ao Git 2.47+ e ativa automaticamente um conjunto de otimizações: sparse-checkout (cone mode), partial clone, multi-pack-index, commit-graph e manutenção em background. Para monorepos grandes, a diferença no tempo de clone e operações do dia a dia é expressiva.

# clonar um repositório grande com todas as otimizações ativas
scalar clone <url-do-repositorio>

Push seguro com —force-with-lease

Substituir --force por --force-with-lease adiciona uma verificação: o push só prossegue se o remoto não avançou desde o seu último fetch. Evita sobrescrever commits de um colega que chegaram ao remoto enquanto você trabalhava.

# seguro: falha se o remoto avançou desde o último fetch
git push --force-with-lease origin feat/user-email-verification

# perigoso: sobrescreve qualquer estado remoto sem verificação
git push --force origin feat/user-email-verification

Assinatura de commits com SSH

O GitHub suporta assinatura de commits com chaves SSH desde 2022, reutilizando a chave que você provavelmente já tem. Uma configuração de uma linha e todo commit fica verificado:

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true

TBD como padrão consolidado

O debate “GitFlow vs TBD” perdeu força em 2025. Para aplicações web e SaaS com deploy contínuo, TBD é o padrão consolidado. A pergunta no mercado deixou de ser “qual estratégia de branch usar?” e virou “como automatizar o CI/CD para suportar TBD de verdade?”


Git é fundação. Dominar branches curtas, commits descritivos, PRs pequenos e um pipeline automatizado transforma a entrega de software de evento estressante em rotina confiável. Os comandos estão todos na documentação oficial. Os padrões de processo ficam no code-style.

Happy coding!

Referências

Ver todos os artigos →