Git Além do Básico: Fluxos de Trabalho Que Economizam Horas Toda Semana
Rebase interativo, cherry-pick, bisect, worktrees, resgate com reflog e as estratégias de branching que realmente funcionam. Comandos Git que eu uso todo dia e que a maioria dos desenvolvedores não sabe que existem.
A maioria dos desenvolvedores aprende cinco comandos Git e para por aí. add, commit, push, pull, merge. Talvez checkout e branch se estiverem se sentindo aventureiros. Isso te leva pelo primeiro ano. Depois sua branch tem 47 commits com mensagens como "fix" e "wip" e "por favor funciona", você acidentalmente reseta algo que não deveria, e passa 40 minutos no Stack Overflow tentando desfazer um merge que deu errado.
Eu uso Git há anos. Não casualmente — intensamente. Múltiplas branches, múltiplos repos, múltiplos colaboradores, o dia inteiro, todo dia. O que se segue são os comandos e fluxos de trabalho que eu realmente uso. Não os que ficam bonitos em um tutorial. Os que me economizam tempo real, toda semana.
Rebase Interativo: Limpe Sua Bagunça Antes Que Alguém Veja#
Sua branch tem doze commits. Metade deles é "fix typo." Um diz "desfazer commit anterior." Outro diz "agora sim arrumei de verdade." Você está prestes a abrir um PR. Ninguém precisa ver esse histórico.
O rebase interativo é como você reescreve o histórico na sua branch antes de compartilhá-la. Ele permite comprimir commits juntos, reescrever mensagens, reordená-los ou removê-los inteiramente.
O Comando Básico#
git rebase -i HEAD~5Isso abre um editor mostrando seus últimos 5 commits, do mais antigo primeiro:
pick a1b2c3d Add user authentication endpoint
pick d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
pick l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth testsCada linha começa com um comando. Mude pick para um destes:
squash(ous) — Mesclar este commit com o acima, combinar as mensagensfixup(ouf) — Igual ao squash, mas descarta a mensagem deste commitreword(our) — Manter o commit mas alterar sua mensagemdrop(oud) — Deletar este commit inteiramenteedit(oue) — Pausar o rebase neste commit para que você possa alterá-lo
Uma Sessão Real de Limpeza#
Aqui está o que eu realmente faço. Aquele histórico bagunçado acima se torna:
pick a1b2c3d Add user authentication endpoint
fixup d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
fixup l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth testsSalve e feche. Agora você tem três commits limpos em vez de cinco. A correção de typo é incorporada ao commit de auth. A correção do bug de rate limit é incorporada ao commit de rate limit. Seu reviewer de PR vê uma progressão limpa e lógica.
Reordenando Commits#
Você pode literalmente rearranjar as linhas. Se o commit de teste deveria vir antes do commit de rate limiting, basta mover a linha:
pick a1b2c3d Add user authentication endpoint
pick p3q4r5s Update auth tests
pick h7i8j9k Add rate limitingO Git vai replay seus commits nessa nova ordem. Se houver conflitos, ele vai pausar e deixar você resolvê-los.
O Atalho Autosquash#
Se você sabe que um commit é uma correção para um anterior, marque-o no momento do commit:
git commit --fixup=a1b2c3dIsso cria um commit com a mensagem fixup! Add user authentication endpoint. Depois quando você faz rebase:
git rebase -i --autosquash HEAD~5O Git automaticamente reordena os commits de fixup logo abaixo de seus alvos e marca-os como fixup. Você só salva e fecha. Sem edição manual.
Eu uso isso constantemente. É a forma mais rápida de iterar em uma branch mantendo o histórico final limpo.
A Regra de Ouro#
Nunca faça rebase de commits que foram pushed para uma branch compartilhada. Se outras pessoas basearam trabalho nesses commits, reescrever o histórico vai causar problemas reais. Faça rebase das suas branches de feature antes de mergear. Nunca faça rebase da main.
Se você já fez push da sua branch de feature e precisa fazer rebase:
git push --force-with-leaseA flag --force-with-lease é mais segura que --force. Ela recusa fazer push se outra pessoa fez push para a mesma branch desde seu último fetch. Não vai prevenir todos os problemas, mas pega o mais comum.
Cherry-Pick: Transferências Cirúrgicas de Commits#
O cherry-pick pega um commit específico de uma branch e aplica em outra. Não é um merge. Não é um rebase. Apenas um commit, aplicado de forma limpa.
Quando Eu Realmente Uso Isso#
O cenário mais comum: eu corrigi um bug na minha branch de feature, mas a correção também precisa ir para a main ou uma branch de release agora mesmo. Eu não quero mergear toda minha branch de feature. Eu quero apenas aquela correção.
# Encontrar o hash do commit da correção
git log --oneline feature/user-auth
# a1b2c3d Fix null pointer in session validation
# Mudar para main e cherry-pick
git checkout main
git cherry-pick a1b2c3dPronto. A correção está na main como um novo commit com as mesmas mudanças.
Cherry-Pick Sem Commitar#
Às vezes você quer aplicar as mudanças mas não commitar ainda. Talvez você queira combinar vários cherry-picks em um commit, ou modificar as mudanças ligeiramente:
git cherry-pick --no-commit a1b2c3dAs mudanças são staged mas não commitadas. Você pode modificá-las, adicionar mais mudanças, depois commitar quando estiver pronto.
Cherry-Pick de Intervalo#
Precisa de múltiplos commits consecutivos? Use sintaxe de intervalo:
git cherry-pick a1b2c3d..f6g7h8iIsso faz cherry-pick de tudo após a1b2c3d até e incluindo f6g7h8i. Note que a1b2c3d em si é excluído. Se você quer incluí-lo:
git cherry-pick a1b2c3d^..f6g7h8iLidando com Conflitos#
Conflitos de cherry-pick funcionam como conflitos de merge. Quando um acontece:
# O Git vai avisar que há um conflito
# Corrija os arquivos conflitantes, depois:
git add .
git cherry-pick --continueOu se você mudar de ideia:
git cherry-pick --abortUma coisa a observar: commits cherry-picked criam novos hashes de commit. Se você depois mergear a branch original, o Git geralmente é inteligente o suficiente para lidar com a duplicação. Mas se você fizer cherry-pick agressivamente entre branches que eventualmente farão merge, pode ver conflitos inesperados. Use-o cirurgicamente, não como uma estratégia de merge.
Git Bisect: Busca Binária para Bugs#
Algo quebrou. Você sabe que funcionava duas semanas atrás. Houve 200 commits desde então. Qual deles quebrou?
Você poderia verificar cada commit manualmente. Ou poderia usar git bisect, que usa busca binária para encontrar o commit exato que quebrou em log2(n) passos. Para 200 commits, são aproximadamente 7-8 verificações em vez de 200.
O Método Manual#
# Começar bisecting
git bisect start
# Marcar o commit atual como ruim (o bug existe aqui)
git bisect bad
# Marcar um commit bom conhecido (o bug não existia aqui)
git bisect good v2.1.0O Git faz checkout de um commit na metade entre bom e ruim. Teste-o. Depois:
# Se o bug existe neste commit:
git bisect bad
# Se o bug não existe neste commit:
git bisect goodO Git reduz o intervalo pela metade a cada vez. Após 7-8 passos, ele te diz:
a1b2c3d4e5f6g7h is the first bad commit
commit a1b2c3d4e5f6g7h
Author: Some Developer <dev@example.com>
Date: Tue Feb 18 14:23:01 2026 +0300
Refactor session handling to use async middlewareAgora você sabe exatamente qual commit introduziu o bug. Quando terminar:
git bisect resetIsso te leva de volta para onde você começou.
O Método Automatizado (Este É o Verdadeiro Poder)#
Se você tem um teste que pode detectar o bug, pode automatizar a coisa toda:
git bisect start
git bisect bad HEAD
git bisect good v2.1.0
git bisect run npm test -- --grep "session validation"O Git vai automaticamente fazer checkout de commits, executar o teste e marcá-los como bom ou ruim baseado no código de saída. Zero significa bom, não-zero significa ruim. Saia, volte, e ele te diz o commit exato.
Você pode usar qualquer script:
git bisect run ./test-regression.shOnde test-regression.sh é:
#!/bin/bash
npm run build 2>/dev/null || exit 125 # 125 significa "pular este commit"
npm test -- --grep "session" || exit 1 # 1 significa "ruim"
exit 0 # 0 significa "bom"O código de saída 125 é especial — ele diz ao bisect para pular aquele commit (útil se um commit não compila). Essa é uma daquelas funcionalidades que parece nicho até você precisar, e então ela salva uma tarde inteira.
Uma Sessão Real de Bisect#
Aqui está como parece na prática:
$ git bisect start
$ git bisect bad HEAD
$ git bisect good abc1234
Bisecting: 97 revisions left to test after this (roughly 7 steps)
[def5678...] Commit message here
$ npm test -- --grep "login flow"
# Testes falham
$ git bisect bad
Bisecting: 48 revisions left to test after this (roughly 6 steps)
[ghi9012...] Another commit message
$ npm test -- --grep "login flow"
# Testes passam
$ git bisect good
Bisecting: 24 revisions left to test after this (roughly 5 steps)
...
# Após ~7 iterações:
abc1234def5678ghi is the first bad commitSete passos para encontrar uma agulha em um palheiro de 200 commits.
Git Worktrees: Múltiplas Branches, Zero Stashing#
Esta é a funcionalidade mais subutilizada do Git. Estou convencido de que a maioria dos desenvolvedores não sabe que ela existe.
O problema: você está mergulhado em uma branch de feature. Arquivos foram alterados em todo lugar. Então alguém diz "pode dar uma olhada nesse bug de produção rapidinho?" Você tem três opções:
- Stash tudo, trocar de branch, corrigir, trocar de volta, pop stash. Torcer para nada dar errado.
- Commitar seu trabalho meio feito com uma mensagem "wip". Feio mas funcional.
- Clonar o repo de novo em outro diretório.
Ou opção 4: worktrees.
O Que É um Worktree#
Um worktree é um segundo (ou terceiro, ou quarto) diretório de trabalho vinculado ao mesmo repositório. Cada worktree tem sua própria branch com checkout, seus próprios arquivos de trabalho, seu próprio index. Mas eles compartilham os mesmos dados .git, então você não está duplicando o repo inteiro.
Adicionando um Worktree#
# Você está em feature/user-auth, mergulhado no trabalho
# Precisa corrigir um bug na main:
git worktree add ../hotfix-session mainIsso cria um novo diretório ../hotfix-session com main em checkout. Seu diretório atual permanece exatamente como estava. Nada em stash, nada commitado, nada interrompido.
cd ../hotfix-session
# Corrija o bug
git add .
git commit -m "Fix null pointer in session validation"
git push origin main
cd ../my-project
# Continue trabalhando na sua feature como se nada tivesse acontecidoCriando uma Nova Branch em um Worktree#
git worktree add ../hotfix-nav -b hotfix/nav-crash mainIsso cria o worktree E cria uma nova branch hotfix/nav-crash baseada na main.
Gerenciando Worktrees#
# Listar todos os worktrees
git worktree list
# /home/dev/my-project abc1234 [feature/user-auth]
# /home/dev/hotfix-session def5678 [main]
# Remover um worktree quando terminar
git worktree remove ../hotfix-session
# Se o diretório já foi deletado:
git worktree prunePor Que Isso É Melhor Que Stashing#
Stashing é bom para trocas rápidas de contexto. Mas worktrees são melhores para qualquer coisa que leve mais de cinco minutos:
- Sua IDE permanece aberta na sua branch de feature. Sem reindexar, sem perder sua posição de scroll.
- Você pode executar testes em ambos os diretórios simultaneamente.
- Sem risco de conflitos de stash ou esquecer o que você guardou.
- Você pode ter um servidor dev de longa duração em um worktree e um build limpo em outro.
Eu tipicamente mantenho dois ou três worktrees ativos: minha branch de feature principal, um worktree na main para verificações rápidas, e às vezes um worktree de review onde eu faço checkout do PR de outra pessoa.
A Única Ressalva#
Você não pode ter a mesma branch em checkout em dois worktrees. Isso é por design — previne que você faça mudanças conflitantes na mesma branch em dois lugares. Se você tentar, o Git vai recusar.
Reflog: O Botão de Desfazer para Tudo#
Você fez um hard reset e perdeu commits. Deletou uma branch. Fez rebase e algo deu horrivelmente errado. Você acha que seu trabalho foi embora.
Não foi. O Git quase nunca deleta nada de verdade. O reflog é sua rede de segurança.
O Que É o Reflog#
Toda vez que o HEAD se move — todo commit, checkout, rebase, reset, merge — o Git registra no reflog. É um log de todos os lugares onde seu HEAD esteve, em ordem.
git reflogSaída:
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
f4e5d6c HEAD@{1}: commit: Add payment processing
b7a8c9d HEAD@{2}: commit: Update user dashboard
e0f1g2h HEAD@{3}: commit: Fix auth token refresh
i3j4k5l HEAD@{4}: checkout: moving from feature/payments to mainCada entrada tem um índice (HEAD@{0}, HEAD@{1}, etc.) e uma descrição do que aconteceu.
Recuperando Após um Hard Reset#
Você acidentalmente rodou git reset --hard HEAD~3 e perdeu três commits. Eles estão bem ali no reflog:
# Ver o que você perdeu
git reflog
# O commit antes do reset é HEAD@{1}
git reset --hard f4e5d6cTodos os três commits voltaram. Crise evitada.
Recuperando uma Branch Deletada#
Você deletou uma branch que tinha trabalho não mergeado:
git branch -D feature/experimental
# Oh não, isso tinha duas semanas de trabalhoOs commits ainda existem. Encontre-os:
git reflog | grep "feature/experimental"
# Ou apenas procure no reflog pelo último commit naquela branch
# Encontrei. Recriar a branch naquele commit:
git branch feature/experimental a1b2c3dA branch voltou, com todos os seus commits.
Recuperando Após um Rebase Ruim#
Você fez rebase e tudo deu errado. Conflitos em todo lugar, commits errados, caos:
# O reflog mostra onde você estava antes do rebase
git reflog
# a1b2c3d HEAD@{0}: rebase (finish): ...
# ...
# f4e5d6c HEAD@{5}: rebase (start): checkout main
# b7a8c9d HEAD@{6}: commit: Your last good commit
# Voltar para antes do rebase
git reset --hard b7a8c9dVocê está de volta exatamente onde estava antes do rebase começar. Como se nunca tivesse acontecido.
A Rede de Segurança de 30 Dias#
Por padrão, o Git mantém entradas do reflog por 30 dias (90 dias para commits alcançáveis). Depois disso, eles podem ser coletados pelo garbage collector. Então você tem um mês para perceber que cometeu um erro. Na prática, isso é mais que suficiente.
Você pode verificar a expiração:
git config gc.reflogExpire
# padrão: 90.days.ago (para alcançáveis)
git config gc.reflogExpireUnreachable
# padrão: 30.days.ago (para inalcançáveis)Se você é paranoico, aumente:
git config --global gc.reflogExpireUnreachable "180.days.ago"Uma Regra Pessoal#
Antes de qualquer operação destrutiva — hard reset, force push, deleção de branch — eu rodo git log --oneline -10 primeiro. Mentalmente noto o HEAD atual. Leva dois segundos e me salvou mais de uma vez de um pânico que eu não precisava ter.
Stash Corretamente: Não É Só git stash#
A maioria das pessoas usa stash assim:
git stash
# faz algo
git stash popIsso funciona, mas é o equivalente a jogar tudo em uma caixa com a etiqueta "coisas." Quando você tem três stashes, não faz ideia de qual é qual.
Nomeie Seus Stashes#
git stash push -m "WIP: user auth form validation"Agora quando você lista stashes:
git stash list
# stash@{0}: On feature/auth: WIP: user auth form validation
# stash@{1}: On main: Quick fix attempt for nav bug
# stash@{2}: On feature/payments: Experiment with Stripe webhooksVocê pode ver exatamente o que cada stash contém.
Incluir Arquivos Não Rastreados#
Por padrão, git stash só guarda arquivos rastreados. Arquivos novos que você ainda não adicionou ficam para trás:
# Stash de tudo, incluindo arquivos novos
git stash push --include-untracked -m "WIP: new auth components"
# Ou até incluir arquivos ignorados (raramente necessário)
git stash push --all -m "Full workspace snapshot"Eu uso --include-untracked quase toda vez. Deixar arquivos novos para trás quando você troca de branch causa confusão.
Stash Parcial#
Este é o que a maioria das pessoas não conhece. Você pode fazer stash de arquivos específicos:
# Stash apenas de arquivos específicos
git stash push -m "Just the auth changes" src/auth/ src/middleware.tsOu usar modo patch para fazer stash de hunks específicos dentro de arquivos:
git stash push --patch -m "Partial: only the validation logic"O Git vai percorrer cada mudança interativamente e perguntar se você quer guardar. y para sim, n para não, s para dividir o hunk em pedaços menores.
Apply vs Pop#
# Pop: aplicar e remover da lista de stash
git stash pop stash@{2}
# Apply: aplicar mas manter na lista de stash
git stash apply stash@{2}Eu uso apply quando não tenho certeza se o stash vai aplicar de forma limpa. Se houver um conflito, o stash é preservado. Com pop, se houver um conflito, o stash fica na lista de qualquer forma (muitas pessoas não sabem disso), mas eu prefiro a intenção explícita do apply.
Visualizando Conteúdo do Stash#
# Ver quais arquivos mudaram em um stash
git stash show stash@{0}
# Ver o diff completo
git stash show -p stash@{0}Criando uma Branch a Partir de um Stash#
Se seu stash cresceu para algo mais substancial:
git stash branch feature/auth-validation stash@{0}Isso cria uma nova branch a partir do commit onde você originalmente fez stash, aplica o stash e o remove. Limpo.
Estratégias de Branching: Qual Realmente Funciona#
Existem três estratégias de branching tradicionais. Cada uma tem um contexto onde brilha e contextos onde causa dor.
Gitflow#
O clássico. main, develop, feature/*, release/*, hotfix/*. Criado por Vincent Driessen em 2010.
# Branch de feature
git checkout -b feature/user-auth develop
# ... trabalho ...
git checkout develop
git merge --no-ff feature/user-auth
# Branch de release
git checkout -b release/2.1.0 develop
# ... correções finais ...
git checkout main
git merge --no-ff release/2.1.0
git tag -a v2.1.0 -m "Release 2.1.0"
git checkout develop
git merge --no-ff release/2.1.0
# Hotfix
git checkout -b hotfix/session-fix main
# ... correção ...
git checkout main
git merge --no-ff hotfix/session-fix
git checkout develop
git merge --no-ff hotfix/session-fixQuando funciona: Apps mobile, software desktop, qualquer coisa com releases versionadas e nomeadas e múltiplas versões suportadas simultaneamente. Se você distribui v2.1 e v3.0 e precisa corrigir ambas, o Gitflow lida com isso.
Quando não funciona: Aplicações web com deploy contínuo. Se você faz deploy para produção 5 vezes por dia, a cerimônia de branches de release e develop é pura sobrecarga. A maioria dos times web que adota Gitflow acaba com uma branch develop perpetuamente quebrada e branches de release que ninguém entende.
GitHub Flow#
Simples. Você tem main. Cria branches de feature. Abre PRs. Mergeia na main. Faz deploy da main.
git checkout -b feature/user-auth main
# ... trabalho ...
git push origin feature/user-auth
# Abre PR, recebe review, mergeia
# main é sempre deployávelQuando funciona: Times pequenos a médios entregando apps web. Deploy contínuo. Se main é sempre deployada, isso é tudo que você precisa. É o que este site usa.
Quando não funciona: Quando você precisa manter múltiplas versões de release, ou quando tem um ciclo longo de QA antes do deploy. O GitHub Flow assume que main vai para produção rapidamente.
Trunk-Based Development#
Todo mundo commita na main (o "trunk") diretamente ou via branches de vida muito curta (menos de um dia). Sem branches de feature de longa duração.
# Branch de vida curta (mergeada no mesmo dia)
git checkout -b fix/auth-token main
# ... mudança pequena e focada ...
git push origin fix/auth-token
# PR revisado e mergeado em horas, não diasQuando funciona: Times de alta performance com bom CI/CD, suítes de teste abrangentes e feature flags. Google, Meta e a maioria das grandes empresas de tecnologia usam trunk-based development. Força mudanças pequenas e incrementais e elimina o inferno de merges.
Quando não funciona: Times sem boa cobertura de testes ou CI. Se mergear no trunk significa fazer deploy de código não testado, você vai quebrar produção constantemente. Você também precisa de feature flags para qualquer coisa que leve mais de um dia para construir:
# Feature flag no código
if (featureFlags.isEnabled('new-checkout-flow')) {
renderNewCheckout();
} else {
renderLegacyCheckout();
}Minha Recomendação#
Para a maioria dos times de desenvolvimento web: comece com GitHub Flow. É simples, funciona e não requer ferramentas além do que GitHub/GitLab já oferece.
Se seu time crescer para mais de 15-20 engenheiros e vocês estiverem fazendo deploy múltiplas vezes por dia, olhe para trunk-based development com feature flags. O investimento em infraestrutura de feature flags se paga em redução de conflitos de merge e iteração mais rápida.
Se você está entregando software versionado (apps mobile, ferramentas CLI, bibliotecas): Gitflow ou uma versão simplificada dele. Você realmente precisa daquelas branches de release.
Não escolha uma estratégia porque um post de blog disse que é a melhor. Escolha a que combina com como você realmente entrega.
Git Hooks: Automatize as Coisas Que Você Sempre Esquece#
Git hooks são scripts que executam automaticamente em pontos específicos do fluxo de trabalho Git. Eles são locais à sua máquina (não são pushed para o remote), o que significa que você precisa de uma forma de compartilhá-los com seu time.
Os Hooks Que Realmente Importam#
pre-commit — Executa antes de cada commit. Use para linting e formatação:
#!/bin/bash
# .git/hooks/pre-commit
# Executar ESLint apenas em arquivos staged
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|tsx)$')
if [ -n "$STAGED_FILES" ]; then
echo "Running ESLint on staged files..."
npx eslint $STAGED_FILES --quiet
if [ $? -ne 0 ]; then
echo "ESLint failed. Fix errors before committing."
exit 1
fi
fi
# Executar Prettier em arquivos staged
if [ -n "$STAGED_FILES" ]; then
echo "Running Prettier..."
npx prettier --check $STAGED_FILES
if [ $? -ne 0 ]; then
echo "Prettier check failed. Run 'npx prettier --write' first."
exit 1
fi
ficommit-msg — Valida o formato da mensagem de commit. Perfeito para impor conventional commits:
#!/bin/bash
# .git/hooks/commit-msg
COMMIT_MSG=$(cat "$1")
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,72}"
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
echo "Invalid commit message format."
echo "Expected: type(scope): description"
echo "Example: feat(auth): add session refresh endpoint"
echo ""
echo "Valid types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
exit 1
fipre-push — Executa antes do push. Use para testes:
#!/bin/bash
# .git/hooks/pre-push
echo "Running tests before push..."
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Push aborted."
exit 1
fiHusky vs Hooks Nativos#
Hooks nativos ficam em .git/hooks/. O problema: o diretório .git não é rastreado pelo Git, então você não consegue compartilhar hooks através do repo. Todo mundo tem que configurá-los manualmente.
Husky resolve isso. Ele armazena configurações de hooks no repo e configura .git/hooks automaticamente no npm install:
npx husky initIsso cria um diretório .husky/. Adicione hooks como arquivos:
# .husky/pre-commit
npx lint-stagedCombinado com lint-staged, você obtém verificações de pre-commit rápidas e direcionadas:
{
"lint-staged": {
"*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.css": ["prettier --write"],
"*.json": ["prettier --write"]
}
}Isso executa ESLint e Prettier apenas nos arquivos que você está realmente commitando. Não no codebase inteiro. Rápido.
Quando Pular Hooks#
Às vezes você precisa commitar sem hooks executando. Hotfixes de emergência, commits work-in-progress na sua própria branch:
git commit --no-verify -m "WIP: debugging production issue"Use isso com moderação. Se você se pega pulando hooks regularmente, seus hooks provavelmente são lentos demais ou estritos demais.
Aliases Que Economizam Tempo#
Meu .gitconfig evoluiu ao longo dos anos. Estes são os aliases que sobreviveram — os que eu realmente uso diariamente, não os que eu adicionei porque pareciam inteligentes.
Log Bonito#
O git log padrão é verboso. Isso dá uma visualização limpa, colorida e baseada em grafo:
git config --global alias.lg "log --oneline --graph --all --decorate"Uso:
git lg
# * a1b2c3d (HEAD -> main) Fix session validation
# | * d4e5f6g (feature/payments) Add Stripe integration
# | * h7i8j9k Update payment models
# |/
# * l0m1n2o Merge PR #42
# * p3q4r5s Add user dashboardEu executo isso 20 vezes por dia. É a forma mais rápida de entender o estado do seu repositório.
Desfazer Último Commit#
Manter as mudanças, apenas desfazer o commit:
git config --global alias.undo "reset HEAD~1 --mixed"Uso:
git undo
# Commit foi embora, mas todas as mudanças ainda estão no seu diretório de trabalhoEu uso isso quando commito cedo demais, esqueço um arquivo, ou quero reestruturar as mudanças.
Desfazer Stage de Tudo#
git config --global alias.unstage "reset HEAD --"Uso:
git unstage src/auth/session.ts
# Arquivo saiu do stage mas as mudanças são preservadasListar Todos os Aliases#
Porque você vai esquecer o que configurou:
git config --global alias.aliases "config --get-regexp ^alias\\."Mais Aliases Que Eu Uso#
# Mostrar o que você está prestes a commitar
git config --global alias.staged "diff --staged"
# Status curto
git config --global alias.st "status -sb"
# Amend sem alterar a mensagem
git config --global alias.amend "commit --amend --no-edit"
# Mostrar o último commit
git config --global alias.last "log -1 HEAD --stat"
# Pull com rebase em vez de merge
git config --global alias.up "pull --rebase --autostash"
# Deletar branches que foram mergeadas na main
git config --global alias.cleanup "!git branch --merged main | grep -v '^[ *]*main$' | xargs git branch -d"O alias up é particularmente bom. pull --rebase mantém seu histórico linear em vez de criar commits de merge para cada pull. --autostash automaticamente faz stash e restaura suas mudanças se você tiver arquivos sujos. É o que pull deveria ser por padrão.
O alias cleanup deleta branches locais que foram mergeadas na main. Com o tempo, você acumula dezenas de branches obsoletas. Execute isso semanalmente.
Os Comandos Que Eu Uso Diariamente#
Estes não são aliases ou funcionalidades avançadas. São apenas comandos que eu executo constantemente e que muitos desenvolvedores parecem não conhecer.
O Comando de Log Definitivo#
git log --oneline --graph --allIsso mostra cada branch, cada merge, toda a topologia do seu repo em uma visualização compacta. É a primeira coisa que eu executo quando faço pull de mudanças. Responde "o que está acontecendo neste repo agora?"
Diff de Mudanças Staged#
git diff --stagedIsso mostra o que está prestes a ser commitado. Não o que mudou no seu diretório de trabalho — o que está realmente staged. Eu sempre executo isso antes de commitar. Sempre. Pega inclusões acidentais, statements de debug, console.logs que não deveriam estar lá.
# Ver mudanças não staged
git diff
# Ver mudanças staged
git diff --staged
# Ver ambas
git diff HEADMostrar um Commit Específico#
git show a1b2c3dMostra o diff completo de um único commit. Útil ao revisar histórico, entendendo o que um commit realmente mudou.
# Mostrar apenas os arquivos que mudaram
git show --stat a1b2c3d
# Mostrar um arquivo específico em um commit específico
git show a1b2c3d:src/auth/session.tsEsse último é incrivelmente útil. Você pode visualizar qualquer arquivo em qualquer ponto do histórico sem fazer checkout daquele commit.
Blame com Intervalos de Linhas#
git blame -L 42,60 src/auth/session.tsMostra quem modificou por último as linhas 42-60. Mais útil do que blame no arquivo inteiro, que geralmente é avassalador.
# Mostrar a mensagem do commit também, não apenas o hash
git blame -L 42,60 --show-name src/auth/session.ts
# Ignorar mudanças de whitespace (muito útil)
git blame -w -L 42,60 src/auth/session.ts
# Mostrar o commit antes do atribuído (ir mais fundo)
git log --follow -p -- src/auth/session.tsA flag -w é importante. Sem ela, blame vai atribuir linhas a quem por último reformatou o arquivo, que raramente é a pessoa que você está procurando.
Encontrar uma String em Todo o Histórico#
# Encontrar quando uma função foi adicionada ou removida
git log -S "validateSession" --oneline
# Encontrar quando um padrão regex apareceu
git log -G "session.*timeout" --oneline-S (o "pickaxe") encontra commits onde o número de ocorrências de uma string mudou. -G encontra commits onde o diff corresponde a uma regex. Ambos são poderosos para arqueologia — descobrir quando algo foi introduzido ou removido.
Mostrar O Que Mudou Entre Dois Pontos#
# O que mudou entre duas branches
git diff main..feature/auth
# O que mudou nesta branch desde que divergiu da main
git diff main...feature/auth
# Listar apenas os arquivos alterados
git diff main...feature/auth --name-only
# Visualização stat (arquivos + inserções/deleções)
git diff main...feature/auth --statDois pontos vs três pontos importa. Dois pontos mostra a diferença entre as pontas de ambas as branches. Três pontos mostra o que mudou no lado direito desde que divergiu do lado esquerdo. Três pontos é geralmente o que você quer ao revisar uma branch de feature.
Limpar Arquivos Não Rastreados#
# Ver o que seria deletado (dry run)
git clean -n
# Deletar arquivos não rastreados
git clean -f
# Deletar arquivos e diretórios não rastreados
git clean -fd
# Deletar arquivos não rastreados e ignorados (opção nuclear)
git clean -fdxSempre execute com -n primeiro. git clean -fdx vai deletar seus node_modules, .env, artefatos de build — tudo que não é rastreado pelo Git. Útil para um recomeço verdadeiramente limpo, mas destrutivo.
Restaurar um Único Arquivo de Outra Branch#
# Obter a versão da main de um arquivo sem trocar de branch
git restore --source main -- src/config/database.tsOu de um commit específico:
git restore --source a1b2c3d -- src/config/database.tsIsso é mais limpo que a sintaxe antiga git checkout main -- path/to/file, e não afeta o HEAD.
Juntando Tudo: Um Fluxo de Trabalho Real#
Aqui está como um dia típico se parece com essas ferramentas:
# Manhã: verificar o que está acontecendo
git lg
git fetch --all
# Começar uma feature
git checkout -b feature/session-refresh main
# Trabalhar, commitar incrementalmente
git add -p # Stage de hunks específicos, não arquivos inteiros
git commit -m "Add token refresh endpoint"
git commit -m "Add refresh token rotation"
git commit -m "Fix: handle expired refresh tokens"
git commit -m "Add integration tests"
# Interrompido: preciso corrigir um bug de produção
git worktree add ../hotfix main
cd ../hotfix
# ... corrigir, commitar, push, PR mergeado ...
cd ../my-project
git worktree remove ../hotfix
# De volta ao trabalho na feature
git commit -m "Fix edge case in token validation"
# Pronto para PR: limpar histórico
git rebase -i main
# Squash dos commits de correção, reword para clareza
# Push e abrir PR
git push -u origin feature/session-refresh
# Algo quebrou em staging? Descobrir qual commit:
git bisect start
git bisect bad HEAD
git bisect good main
git bisect run npm test
# Ops, eu fiz hard-reset na coisa errada
git reflog
git reset --hard HEAD@{2}Cada um desses comandos leva segundos. Juntos, eles economizam horas. Não horas hipotéticas — horas reais, toda semana, que eu gastaria de outra forma desemaranhando confusões do Git, buscando bugs manualmente, ou perdendo trabalho em trocas de contexto.
Git é uma ferramenta que recompensa profundidade. O básico te leva pelo dia. Mas os comandos neste post são o que separa "eu uso Git" de "Git realmente me torna mais rápido." Aprenda-os incrementalmente. Escolha uma técnica nova esta semana e use-a até virar memória muscular. Depois escolha outra.
Seu eu futuro, olhando para um bug de produção às 23h, vai agradecer por conhecer git bisect.