Git é um sistema de controle de versão distribuído. Cada desenvolvedor tem uma cópia completa do repositório, incluindo todo o histórico. Commits são snapshots, não diffs — o Git calcula diffs sob demanda.
Configuração Inicial
# Identidade — obrigatório antes do primeiro commit
git config --global user.name "Rafael Marques"
git config --global user.email "rafael@empresa.com"
# Editor padrão
git config --global core.editor "nvim"
git config --global core.editor "code --wait" # VS Code
# Branch padrão
git config --global init.defaultBranch main
# Comportamento de push padrão
git config --global push.default current # push para branch de mesmo nome
# Rebase por padrão no pull
git config --global pull.rebase true
# Aliases úteis
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.lg "log --oneline --graph --decorate --all"
git config --global alias.unstage "restore --staged"
git config --global alias.last "log -1 HEAD --stat"
git config --global alias.aliases "config --get-regexp alias"
# Cores no output
git config --global color.ui auto
# Auto-correção de comandos com typo (após 1s)
git config --global help.autocorrect 10
# Mostrar configuração
git config --list
git config --list --show-origin # mostra qual arquivo define cada configStaging Area — Entendendo o Index
A staging area (index) é um espaço intermediário entre o working directory e o repositório. Permite compor commits cirurgicamente.
# Visualizar estado
git status # estado resumido
git status -s # short format: ?? = untracked, M = modified, A = staged
# Adicionar ao stage
git add arquivo.ts # arquivo específico
git add src/ # diretório inteiro
git add . # tudo no diretório atual
git add -p # interativo: escolhe hunks (recomendado!)
git add -p arquivo.ts # interativo para arquivo específico
# No modo -p: y = adiciona hunk, n = ignora, s = divide, e = edita manualmente
# Ver diferenças
git diff # working directory vs staged
git diff --staged # staged vs último commit (o que vai no commit)
git diff HEAD # working directory vs último commit (tudo)
git diff branch-a..branch-b # diff entre branches
git diff HEAD~3 -- arquivo.ts # diff de arquivo há 3 commits atrás
# Remover do stage (sem perder mudanças)
git restore --staged arquivo.ts # remove do stage, mantém no working directory
git restore --staged . # remove tudo do stage
# Descartar mudanças no working directory (irreversível!)
git restore arquivo.ts # descarta mudanças não-staged
git restore . # descarta tudo (cuidado!)
# Ver o que está staged em detalhe
git diff --staged --stat # estatística de mudanças staged
git diff --cached # mesmo que --staged (alias)Commits — Conventional Commits
Conventional Commits é uma especificação para mensagens de commit que permite automação de changelogs e versionamento semântico.
Formato:
<tipo>[escopo opcional]: <descrição>
[corpo opcional]
[rodapé opcional]# Tipos de commit
git commit -m "feat: adicionar endpoint de cancelamento de pedido"
git commit -m "fix: corrigir cálculo de desconto para clientes VIP"
git commit -m "docs: atualizar README com instruções de setup"
git commit -m "refactor: extrair PricingService de OrderService"
git commit -m "test: adicionar testes para CartService"
git commit -m "chore: atualizar dependências para versões LTS"
git commit -m "perf: otimizar query de listagem de pedidos com índice"
git commit -m "ci: configurar pipeline de deploy para produção"
git commit -m "style: formatar código com Prettier"
git commit -m "build: migrar de Maven para Gradle"
# Com escopo (módulo ou área afetada)
git commit -m "feat(payment): integrar Stripe como gateway de pagamento"
git commit -m "fix(cart): resolver problema de quantidade negativa"
git commit -m "refactor(auth): simplificar validação de token JWT"
# Breaking change — MAJOR version bump
git commit -m "feat!: alterar formato de resposta do endpoint /api/orders"
# ou via rodapé:
git commit -m "feat(api): alterar paginação para cursor-based
BREAKING CHANGE: o campo 'page' foi removido. Use 'cursor' para paginação."
# Commit com corpo detalhado
git commit -m "fix(order): prevenir criação de pedido duplicado
O endpoint POST /api/orders não verificava pedidos em andamento
para o mesmo cliente, permitindo duplicatas em condições de race condition.
Adiciona verificação de pedido pendente antes de criar um novo e
implementa idempotency key via header X-Idempotency-Key.
Closes #123"
# Amend — modifica o ÚLTIMO commit (apenas antes de push!)
git commit --amend # edita mensagem + staged
git commit --amend -m "nova mensagem" # apenas mensagem
git commit --amend --no-edit # adiciona staged sem mudar mensagemBranches — Criação, Merge e Estratégias
# Criação e navegação
git switch -c feature/checkout-flow # cria e muda para a branch
git switch main # volta para main
git switch - # volta para branch anterior
git branch # lista branches locais
git branch -a # lista locais + remotas
git branch -vv # com informação de tracking e ahead/behind
git branch -d feature/checkout-flow # deleta branch (seguro)
git branch -D feature/checkout-flow # força delete mesmo sem merge
git branch -m feature/old feature/new # renomeia branch
# Merge — Fast-Forward vs 3-Way
# Fast-Forward: quando main não avançou desde a criação da branch
# Resultado: histórico linear, sem merge commit
git merge feature/my-feature # FF quando possível
# Forçar merge commit mesmo em FF (mantém histórico de branch)
git merge --no-ff feature/my-feature -m "feat: adicionar checkout flow"
# 3-Way merge: main avançou, Git cria merge commit
git merge feature/my-feature
# Se houver conflitos:
git status # ver arquivos em conflito
# Editar arquivos com conflito manualmente
git add arquivo-conflitado.ts # marcar como resolvido
git merge --continue # finalizar merge
git merge --abort # cancelar e voltar ao estado anterior
# Rebase vs Merge — quando usar cada
# REBASE: histórico linear, ideal para feature branches antes de abrir PR
# - Reescreve histórico — nunca em branches compartilhadas/públicas
# MERGE: preserva histórico real, ideal para integrar main em branches longas
# - Seguro em qualquer branchGit Flow vs Trunk-Based Development
GIT FLOW — adequado para releases programadas, versionamento semântico
│
├── main → produção, protegida, tags de release
├── develop → integração, base para feature branches
├── feature/* → uma por funcionalidade, saem de develop
├── release/* → preparação de release (bug fixes + versão)
└── hotfix/* → correções urgentes em produção, mergem em main E develop
Vantagens: processo claro, suporte a múltiplas versões em paralelo
Desvantagens: complexo, branches de longa duração, merge hell
TRUNK-BASED DEVELOPMENT — adequado para CD, times ágeis, microserviços
│
├── main → sempre deployável, integração contínua
└── feature/* → curtas (1-2 dias máx), mergeadas com frequência
Vantagens: integração contínua real, menos conflitos, CD natural
Desvantagens: requer feature flags para features incompletas, disciplina maior
Na prática: a maioria dos times usa algo no meio:
- main (protegida) + feature branches curtas + PRs obrigatóriosRebase — Interativo e Avançado
# Rebase simples — atualiza a branch com base na main
git rebase main # rebases feature branch em main
# Rebase interativo — editar, reordenar, combinar commits
git rebase -i HEAD~3 # editar últimos 3 commits
git rebase -i origin/main # editar desde que divergiu de main
# No editor (commits aparecem do mais antigo para o mais recente):
# pick abc1234 feat: adicionar CartService
# pick def5678 fix: corrigir bug no CartService
# pick ghi9012 test: adicionar testes
# Comandos disponíveis:
# pick → mantém o commit como está
# reword → mantém o commit, edita a mensagem
# edit → para no commit para fazer alterações
# squash → combina com o commit anterior (mantém todas as mensagens)
# fixup → combina com o commit anterior (descarta a mensagem)
# drop → remove o commit completamente
# reorder → mova as linhas para reordenar commits
# Squash de commits — combinar vários commits em um
# Antes:
# abc1234 feat: início do CartService
# def5678 wip: continuando CartService
# ghi9012 fix: corrigir teste
# jkl0123 refactor: limpar código
# Depois do squash:
# abc1234 feat: implementar CartService com testes
# Rebase --onto — mover branch para outra base
git rebase --onto main feature-base feature-sub
# Pega commits exclusivos de feature-sub (em relação a feature-base)
# e os aplica em cima de main
# Resolver conflito durante rebase
git status # ver conflitos
# Editar arquivos com conflito
git add arquivo.ts # marcar como resolvido
git rebase --continue # continuar
git rebase --abort # cancelar e voltar ao ponto inicial
# REGRA DE OURO: nunca rebase em branches remotas compartilhadas
# Rebase reescreve histórico — force push é perigoso e antisocialCherry-Pick
# Aplica um commit específico na branch atual
git cherry-pick abc1234 # aplica o commit abc1234
git cherry-pick abc1234..def5678 # aplica intervalo de commits
git cherry-pick abc1234 def5678 ghi9012 # múltiplos commits específicos
# Cherry-pick sem commit automático (permite editar antes)
git cherry-pick abc1234 --no-commit
# Edite o que precisar, então:
git commit -m "feat: adaptado de abc1234"
# Implicações:
# Cherry-pick DUPLICA o commit — cria um novo commit com o mesmo conteúdo
# O commit original e o cherry-picked são objetos diferentes (SHA diferente)
# Use com moderação — pode complicar o histórico
# Cenário ideal: copiar hotfix para uma release branchReset vs Revert vs Checkout
# RESET — move o ponteiro HEAD (modifica histórico!)
git reset --soft HEAD~1 # desfaz commit, mantém mudanças STAGED
# como se você nunca tivesse feito o commit
git reset --mixed HEAD~1 # desfaz commit, mantém mudanças no working dir (padrão)
git reset --hard HEAD~1 # desfaz commit E descarta mudanças (irreversível!)
# QUANDO USAR RESET:
# --soft: "quero refazer o commit com um nome melhor" (antes de push)
# --mixed: "quero dividir este commit em dois" (antes de push)
# --hard: "quero descartar completamente o que fiz" (CUIDADO!)
# NUNCA reset em commits já publicados (shared history)
# REVERT — cria um novo commit que desfaz outro (seguro, não modifica histórico)
git revert abc1234 # cria commit que desfaz abc1234
git revert HEAD~3..HEAD # reverte últimos 3 commits
git revert HEAD~3..HEAD --no-commit # prepara reverts sem commitar (para commitar junto)
# QUANDO USAR REVERT:
# Para desfazer commits já publicados/compartilhados
# Em branches de produção — registro auditável de desfazer mudança
# CHECKOUT (restore) — restaura arquivos ou muda de branch
git checkout -- arquivo.ts # descarta mudanças no arquivo (old syntax)
git restore arquivo.ts # mesmo que acima (new syntax, preferida)
git restore --source abc1234 arq.ts # restaura arquivo para versão de um commit
# Comparativo:
# reset: afeta HEAD + index + working dir (dependendo da flag)
# revert: cria novo commit para desfazer
# restore: afeta apenas working dir e/ou index, não move HEADStash — Salvar Trabalho em Andamento
# Salvar
git stash # salva working dir + index
git stash push -m "wip: feature X" # com mensagem descritiva
git stash push --include-untracked # inclui arquivos não-rastreados
git stash push --all # inclui arquivos ignorados também
git stash push -p # interativo: escolhe o que guardar
# Listar e inspecionar
git stash list # lista todos os stashes
git stash show # resumo do último stash
git stash show -p # diff completo do último stash
git stash show stash@{2} # stash específico
# Aplicar
git stash pop # aplica e remove o último stash
git stash apply # aplica mas MANTÉM no stash
git stash apply stash@{2} # aplica stash específico
# Remover
git stash drop stash@{0} # remove stash específico
git stash clear # remove todos os stashes
# Criar branch a partir de stash
git stash branch feature/nova-branch stash@{0}
# Útil quando o stash não aplica mais limpo na branch atualRemote — Fetch, Pull, Push e Upstream
# Gerenciar remotes
git remote -v # listar remotes
git remote add origin git@github.com:usuario/repo.git
git remote add upstream git@github.com:original/repo.git # fork workflow
git remote remove origin
git remote rename origin backup
# Fetch — baixa mudanças SEM fazer merge/rebase
git fetch origin # fetch de todos os branches do origin
git fetch --all # fetch de todos os remotes
git fetch origin main # apenas o branch main
# Pull — fetch + merge (ou rebase se configurado)
git pull # pull do remote tracking
git pull --rebase # pull + rebase (histórico mais limpo)
git pull origin main # pull de branch específico
# Push
git push origin main # push da branch main
git push -u origin feature/my-feature # push + configura upstream tracking
git push --force-with-lease # ✅ force push SEGURO (falha se houver commits remotos que você não tem)
git push --force # ❌ PERIGOSO — sobrescreve sem verificar
git push origin --delete feature/old # deleta branch remota
git push origin :feature/old # mesmo que acima (sintaxe alternativa)
# Upstream tracking
git branch --set-upstream-to=origin/main main # configura tracking manualmente
git branch -u origin/main # idem, forma curta
# Ver status de ahead/behind
git fetch origin
git status # "Your branch is 2 commits ahead of 'origin/main'"
git log origin/main..main # commits locais não publicados
git log main..origin/main # commits remotos não integradosTags
# LIGHTWEIGHT TAG — apenas um ponteiro para um commit
git tag v1.0.0
git tag v1.0.0 abc1234 # tag em commit específico
# ANNOTATED TAG — objeto completo com mensagem, data, autor
# Preferida para releases — tem metadados completos
git tag -a v1.0.0 -m "Release 1.0.0 — checkout flow e novo sistema de pagamento"
git tag -a v1.0.0 abc1234 -m "Release 1.0.0"
# Listar
git tag # todas as tags
git tag -l "v1.*" # tags que combinam com padrão
git show v1.0.0 # detalhes da tag
# Push de tags
git push origin v1.0.0 # push de tag específica
git push origin --tags # push de todas as tags
git push --follow-tags # push do commit + tags anotadas associadas
# Deletar
git tag -d v1.0.0 # deleta tag local
git push origin --delete v1.0.0 # deleta tag remota
git push origin :refs/tags/v1.0.0 # mesmo que acimaBisect — Encontrando o Commit que Introduziu um Bug
# Início do bisect
git bisect start
# Marcar commits
git bisect bad # commit atual está bugado
git bisect good v1.0.0 # v1.0.0 estava OK
# Git vai fazer checkout de um commit no meio
# Teste o comportamento, então:
git bisect bad # este commit ainda tem o bug
git bisect good # este commit está OK
# Git continua o binário search — em ~10 iterações em 1000 commits
# Ao final, Git mostra o commit exato que introduziu o bug
# Automatizar — com script de teste
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
git bisect run ./test.sh # executa script automaticamente
# 0 = OK, 1+ = bug presente
# Sair do bisect
git bisect reset # volta ao HEAD original
git bisect log # histórico das marcaçõesReflog — Recuperar Commits “Perdidos”
O reflog é o histórico de todos os movimentos do HEAD — incluindo commits “descartados” com reset —hard, branches deletadas, etc.
# Ver reflog
git reflog # histórico do HEAD
git reflog show feature/branch # reflog de branch específica
git reflog --date=relative # com timestamps relativos
# Recuperar commit após reset --hard
git reset --hard HEAD~3 # "perdeu" 3 commits
git reflog # veja os commits antes do reset
# HEAD@{3} abc1234 commit: feat: funcionalidade X ← ainda existe!
git reset --hard abc1234 # recupera o estado anterior
# Recuperar branch deletada
git branch -D feature/deletada # deletou a branch
git reflog # encontre o último commit da branch
# HEAD@{5} def5678 commit: último commit da feature/deletada
git checkout -b feature/deletada def5678 # recria a branch
# Reflog tem expiração padrão de 90 dias
# git config gc.reflogExpire → padrão: 90 dias
# git config gc.reflogExpireUnreachable → padrão: 30 diasHooks — Automação no Git
Hooks são scripts executados automaticamente em eventos do Git. Ficam em .git/hooks/ (local) ou em ferramentas como Husky (compartilhado via repositório).
# pre-commit — valida antes de commitar
# .git/hooks/pre-commit (ou via Husky: .husky/pre-commit)
#!/bin/sh
set -e
echo "Executando validações pre-commit..."
# Linting
npm run lint || { echo "❌ Lint falhou. Corrija antes de commitar."; exit 1; }
# Testes unitários rápidos
npm run test:unit || { echo "❌ Testes falharam."; exit 1; }
echo "✅ Validações ok."# commit-msg — valida formato da mensagem de commit
# .git/hooks/commit-msg
#!/bin/sh
COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
# Valida Conventional Commits
PATTERN="^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\(.+\))?(!)?: .{1,100}"
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
echo "❌ Mensagem de commit inválida!"
echo " Use: tipo(escopo): descrição"
echo " Exemplo: feat(cart): adicionar validação de quantidade"
exit 1
fi# pre-push — validações mais pesadas antes do push
# .git/hooks/pre-push
#!/bin/sh
set -e
echo "Executando validações pre-push..."
# Testes completos (mais lentos, mas antes de ir para o servidor)
./gradlew test || { echo "❌ Testes falharam. Corrija antes de fazer push."; exit 1; }
# Verificar por TODOs críticos
if git diff origin/main..HEAD | grep -q "TODO(CRITICAL)"; then
echo "❌ TODOs críticos encontrados. Resolva antes do push."
exit 1
fi
echo "✅ Pronto para push."# Configuração com Husky (Node.js) — hooks compartilhados no repositório
npm install --save-dev husky
npx husky init
# .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
# .lintstagedrc.json — aplica apenas nos arquivos staged
{
"*.ts": ["eslint --fix", "prettier --write"],
"*.java": ["./gradlew spotlessApply"],
"*.{json,yml,yaml}": ["prettier --write"]
}.gitignore — Padrões e Boas Práticas
# .gitignore do projeto
# Build outputs
target/
dist/
build/
out/
*.class
*.jar
*.war
# Dependências
node_modules/
.gradle/
.mvn/wrapper/maven-wrapper.jar
# IDE — estes devem estar no gitignore GLOBAL, não do projeto
# Mas incluir se o time usa o mesmo IDE
.idea/
*.iml
.vscode/settings.json # mas NÃO .vscode/extensions.json (útil compartilhar)
.classpath
.project
# Ambiente e secrets — NUNCA commitar
.env
.env.local
.env.production
*.key
*.pem
secrets/
# Logs
*.log
logs/
# Temporários
*.tmp
*.temp
.cache/# .gitignore global — para sua máquina, não compartilhado
git config --global core.excludesfile ~/.gitignore_global
# ~/.gitignore_global
.DS_Store
Thumbs.db
*.swp
*.swo
.idea/
.vscode/# .gitkeep — manter diretório vazio no repositório
# Git não rastreia diretórios vazios — use .gitkeep para preservar a estrutura
mkdir -p src/main/resources/uploads
touch src/main/resources/uploads/.gitkeep
# No .gitignore do projeto:
# src/main/resources/uploads/*
# !src/main/resources/uploads/.gitkeep
# Ignorar arquivo já rastreado (após adicionar ao .gitignore)
git rm --cached arquivo.env # remove do índice, mantém no disco
git rm --cached -r node_modules/ # remove diretório do índice