Git de verdade: commit, branch, merge, rebase, reflog
Git é o sistema de controle de versão mais usado do mundo — e também o mais mal-entendido. A maioria aprende os comandos sem entender o modelo mental subjacente. Quando você entende o que Git realmente armazena, tudo faz sentido: merge, rebase, reset, cherry-pick viram operações previsíveis.
O que Git realmente armazena: objetos e o DAG
Git não armazena diffs — armazena snapshots. Cada commit é uma foto completa do projeto naquele momento. Git é eficiente porque objetos idênticos (mesmo conteúdo → mesmo hash SHA-1) são armazenados apenas uma vez.
Existem 4 tipos de objetos no Git:
# Blob — conteúdo de um arquivo (sem nome, sem permissão)
git cat-file -p a1b2c3 # mostra conteúdo de um blob
# Tree — diretório (lista de blobs e outras trees com nomes e permissões)
# 100644 blob a1b2c3 README.md
# 100755 blob d4e5f6 script.sh
# 040000 tree 789abc src/
# Commit — aponta para uma tree + metadados + referências ao(s) pai(s)
# tree 789abc...
# parent def012... ← SHA do commit anterior
# author Fernando ...
# committer Fernando ...
# (linha em branco)
# Mensagem do commit
# Tag — aponta para um commit com anotação
# O histórico é um DAG (Directed Acyclic Graph) de commits:
# A ← B ← C ← D (main)
# ↖
# E ← F (feature)
# D e F são a mesma base C. Após merge: A ← B ← C ← D ← G
# ↗
# E ← F# Explorar os objetos internamente
git log --oneline --graph --all # visualiza o DAG
git cat-file -t SHA # tipo do objeto (blob/tree/commit/tag)
git cat-file -p SHA # conteúdo do objeto
ls .git/objects/ # os arquivos físicos no discoAs três áreas do Git
# Working Directory → Staging Area (Index) → Repository (.git/)
# Estado das mudanças
git status # resumo das três áreas
git diff # working directory vs staging area
git diff --staged # staging area vs último commit
git diff HEAD # working directory vs último commit
# Movendo mudanças para staging
git add arquivo.txt # adiciona arquivo inteiro
git add src/ # adiciona diretório
git add -p # interativo: seleciona hunks (partes) a adicionar
git add -A # todos os arquivos (novos, modificados, deletados)
# Commit: salva staging no repositório
git commit -m "feat: adiciona autenticação JWT"
git commit --amend # modifica o ÚLTIMO commit (não use se já fez push)
git commit -v # abre editor com o diff completo visível
# Desfazendo staging (sem perder mudanças)
git restore --staged arquivo.txt # retira do staging, mantém no working dir
git reset HEAD arquivo.txt # equivalente (forma antiga)
# Desfazendo mudanças no working directory (DESTRUTIVO)
git restore arquivo.txt # descarta mudanças locais (irrecuperável)
git checkout -- arquivo.txt # equivalente (forma antiga)Branches: são só ponteiros para commits
Uma branch no Git é literalmente um arquivo de texto com 40 caracteres: o SHA do commit que ela aponta. Criar uma branch é instantâneo — não copia nada. HEAD é um ponteiro para a branch atual (ou diretamente para um commit, "detached HEAD state").
# Criar e navegar branches
git branch feature/auth # cria branch no commit atual
git checkout feature/auth # muda para ela
git checkout -b feature/auth # cria E muda (atalho)
git switch -c feature/auth # forma moderna (Git 2.23+)
# Ver branches
git branch # branches locais
git branch -r # branches remotas
git branch -a # todas
git branch -v # com último commit de cada
# Deletar branch
git branch -d feature/auth # deleta se já mergeada
git branch -D feature/auth # força delete (mesmo sem merge)
# Renomear
git branch -m nome-novo # renomeia branch atual
git branch -m nome-velho nome-novo
# Mudar de branch (com mudanças em andamento)
git stash # salva mudanças temporariamente
git switch outra-branch
git stash pop # recupera as mudançasMerge vs Rebase: qual escolher
| Aspecto | git merge | git rebase |
|---|---|---|
| Histórico | Preserva — cria commit de merge | Reescreve — commits novos com mesmos changes |
| Resultado | Commit com 2 pais (não-linear) | Histórico linear |
| Conflitos | Resolve uma vez no merge commit | Resolve em cada commit rebased |
| Seguro para push | Sempre | Somente para branches não compartilhadas |
| Quando usar | Integrar branches públicas (main) | Limpar histórico local antes do push |
# MERGE — preserva histórico real
git checkout main
git merge feature/auth
# Cria: main → ... → merge-commit (tem dois pais: ultimo-main e ultimo-feature)
# REBASE — reescreve o histórico da feature sobre main
git checkout feature/auth
git rebase main
# Move commits da feature para após o último commit do main
# Os commits da feature são NOVOS (SHAs diferentes), mas com os mesmos changes
# MERGE SQUASH — todos os commits da feature viram 1 commit só em main
git checkout main
git merge --squash feature/auth
git commit -m "feat: autenticação JWT (squash de 5 commits)"
# Útil para manter main com histórico limpo
# INTERACTIVE REBASE — editar, reordenar, squash, fixup commits
git rebase -i HEAD~5 # edita últimos 5 commits
# pick SHA1 commit 1 → manter
# squash SHA2 commit 2 → juntar com anterior
# fixup SHA3 commit 3 → juntar sem preservar mensagem
# reword SHA4 commit 4 → mudar mensagem
# drop SHA5 commit 5 → deletarReflog: a rede de segurança do Git
O reflog registra para onde HEAD aponta a cada operação. É local (não vai para o remote) e mantém objetos por ~90 dias. É o que salva quando você faz reset --hard acidentalmente.
# Ver o reflog
git reflog # últimas ~100 entradas do HEAD
git reflog show main # reflog de uma branch específica
# Saída típica:
# abc1234 HEAD@{0}: reset: moving to HEAD~3
# def5678 HEAD@{1}: commit: feat: adiciona login
# ghi9012 HEAD@{2}: commit: fix: corrige validação
# jkl3456 HEAD@{3}: checkout: moving from main to feature/auth
# RECUPERAR após reset --hard acidental
git reflog # encontra SHA antes do reset
git reset --hard def5678 # volta para lá
# ou: criar nova branch naquele ponto
git checkout -b recuperado def5678
# RECUPERAR commit que nunca foi commitado em nenhuma branch
git fsck --lost-found # lista dangling objects
ls .git/lost-found/ # blobs e commits "perdidos"git stash também aparece no reflog como stash@. Se fizer git stash drop por acidente, git stash show stash@ e git stash apply stash@ podem recuperar se o objeto ainda não foi coletado.
Operações avançadas do dia-a-dia
# cherry-pick: aplicar um commit específico em outra branch
git checkout main
git cherry-pick abc1234 # aplica as mudanças desse commit em main
# Desfazer um commit de forma segura (não-destrutivo)
git revert abc1234 # cria um novo commit que desfaz as mudanças
# Bisect: encontra qual commit introduziu um bug (busca binária)
git bisect start
git bisect bad # commit atual tem o bug
git bisect good v2.1.0 # esta versão estava OK
# Git faz checkout automático de commits intermediários
# Você testa e informa: git bisect good / git bisect bad
git bisect reset # termina
# Tags: marcar releases
git tag v1.0.0 # tag leve (só aponta para commit)
git tag -a v1.0.0 -m "Release 1.0.0" # tag anotada (com metadados)
git push origin v1.0.0 # tags não sobem com push padrão
git push origin --tags # sobe todas as tags
# Stash avançado
git stash push -m "WIP: auth endpoint" # com nome
git stash list # lista todos os stashes
git stash apply stash@{2} # aplica sem remover da lista
git stash pop # aplica e removeRegras que evitam desastres: (1) Nunca force-push em branches compartilhadas. (2) Antes de reset --hard, crie uma branch no ponto atual. (3) Se perdeu algo, cheque git reflog antes de entrar em pânico. (4) Commits pequenos e focados — mais fácil de reverter e entender.
Próximo: GitHub profissional — Pull Requests, code review, issues, e GitHub Actions para CI/CD básico.
Próximos passos sugeridos
Discussão
Carregando…