Configuração & Inicialização
Configuração Global & Local
O Git armazena configurações em três níveis: system (/etc/gitconfig), global (~/.gitconfig para o usuário) e local (.git/config por repositório). Níveis inferiores sobrescrevem os superiores. Sempre defina user.name e user.email antes de commitar, caso contrário os commits usarão uma identidade padrão sem utilidade. Definir init.defaultBranch como main evita o padrão master obsoleto e se alinha com convenções modernas.
# set user identity (required for commits)
git config --global user.name "Alice Lee"
git config --global user.email "[email protected]"
# set default editor and branch name
git config --global core.editor "code --wait"
git config --global init.defaultBranch main
# view all settings and their origin
git config --list --show-origin
# edit config files directly
git config --global --edit # ~/.gitconfig
git config --edit # repo .git/configCriar & Clonar Repositórios
git init cria um repositório vazio adicionando um diretório oculto .git que armazena todos os dados de versão. git clone copia um repositório remoto incluindo seu histórico completo. Use --depth 1 para um clone raso quando você só precisa do snapshot mais recente (ex.: builds de CI) — isso reduz drasticamente o tamanho do download. --single-branch evita buscar branches não relacionados.
# create a new repo from scratch in current dir
mkdir my-app && cd my-app
git init
# clone a remote repository
git clone https://github.com/user/repo.git
# clone into a specific folder name
git clone https://github.com/user/repo.git my-folder
# clone a single branch (saves bandwidth)
git clone -b dev --single-branch https://github.com/user/repo.git
# shallow clone: only the latest commit
git clone --depth 1 https://github.com/user/repo.gitAliases & Atalhos
Aliases permitem definir nomes mais curtos para comandos usados com frequência ou sequências de comandos. Eles são armazenados na seção [alias] do seu gitconfig. Um alias começando com ! é executado como um comando shell, permitindo fluxos de trabalho complexos. O alias lg acima produz um grafo de histórico visual compacto que é extremamente útil para entender a estrutura de branches.
# create shortcuts for common commands
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.lg "log --oneline --graph --all"
# now use them
git co main
git lg
# alias that runs an external command (starts with !)
git config --global alias.unstage "reset HEAD --"
git unstage file.txtAjuda & Documentação
O Git vem com documentação integrada abrangente. git help <command> abre a man page no seu pager. A flag -h fornece um resumo rápido de uma tela das opções. Os guias (git help -g) incluem um tutorial, um glossário de termos e uma referência de fluxo de trabalho diário — excelentes para iniciantes aprendendo a terminologia.
# open the full manual for a command
git help commit
git commit --help
# show a concise synopsis
git commit -h
# list all git commands
git help -a
# list all guides (tutorial, glossary, etc.)
git help -g
git help glossaryPadrões .gitignore
.gitignore diz ao Git quais arquivos excluir do controle de versão — essencial para artefatos de build, dependências e segredos. Os padrões usam sintaxe glob; uma barra final corresponde a diretórios. Prefixar com ! nega um padrão, forçando um arquivo a ser rastreado. O próprio .gitignore deve ser commitado para que a equipe compartilhe as regras de ignore. Arquivos já rastreados não são afetados por novos padrões de ignore — remova-os com git rm --cached primeiro.
# .gitignore file — patterns for files to skip
node_modules/
*.log
.env
.env.local
dist/
build/
# but track this specific file even if it matches
!important.log
# ignore all .txt files in build/ but not subdirs
build/*.txt
# ignore everything in a folder except one file
secrets/*
!secrets/template.envStaging & Committing
Status & Staging
O Git usa um modelo de duas etapas: as mudanças vão para a área de staging (index) antes de serem commitadas. git status mostra o que está modificado, staged e untracked. git add -p permite stagear hunks individuais de um patch — inestimável para dividir uma árvore de trabalho bagunçada em commits focados e lógicos. Use git restore --staged (o comando moderno) para unstagear sem perder suas edições.
# see current state of working tree
git status
git status -s # short format
git status -sb # short + branch info
# stage changes
git add file.txt # one file
git add src/ # a directory
git add . # all changes in repo
git add -p # stage interactively by hunk
# unstage a file (keep changes in working dir)
git restore --staged file.txt
git reset HEAD file.txt # older syntaxCommitando Mudanças
Um commit registra um snapshot das mudanças staged. Escreva mensagens no modo imperativo ('add feature' não 'added feature'). A flag -a só stageia arquivos já rastreados — novos arquivos ainda precisam de git add. --amend reescreve o último commit; use-o para corrigir um erro de digitação ou adicionar um arquivo esquecido, mas nunca amende commits que você já fez push para um branch compartilhado.
# commit staged changes with a message
git commit -m "feat: add login form"
# multi-line commit message
git commit -m "feat: add login form" -m "Closes #42"
# stage tracked files AND commit in one step
git commit -am "fix: correct typo"
# amend the previous commit (keep message)
git commit --amend --no-edit
# amend and edit the message
git commit --amend -m "feat: add login form (v2)"Convenções de Mensagens de Commit
Conventional Commits é uma especificação amplamente adotada para mensagens de commit estruturadas. O prefixo de tipo (feat, fix, docs, etc.) permite geração automatizada de changelog e versionamento semântico. O marcador ! indica uma breaking change. Uma linha em branco separa o assunto (<=50 chars) do corpo, e outra linha em branco separa rodapés como 'Closes #123' que fecham issues automaticamente no GitHub.
# Conventional Commits format
feat: add user registration endpoint
fix: resolve crash on empty cart
docs: update API reference
style: format login component
refactor: extract validation logic
test: add unit tests for parser
chore: upgrade dependencies
# with scope and breaking change marker
feat(api): add pagination to list endpoint
fix!: change default port (BREAKING CHANGE)
# with body and footer
feat: add dark mode
Implement theme toggle using CSS variables.
Closes #128Visualizando Histórico com log
git log é a principal ferramenta para explorar o histórico. --oneline --graph --all é a combinação mais útil para entender a estrutura de branches de relance. Você pode filtrar por autor, intervalo de datas ou conteúdo de mensagem com --grep. --stat mostra quais arquivos mudaram e quantas linhas, enquanto --patch mostra o diff completo de cada commit.
# full history with details
git log
git log --oneline # one line per commit
git log --oneline --graph --all # visual branch graph
git log -n 5 # last 5 commits
# filter by author, date, or message
git log --author="Alice"
git log --since="2 weeks ago"
git log --grep="fix"
# show files changed in each commit
git log --stat
git log --patch # full diffsDiff & Show
git diff compara snapshots — sem argumentos, mostra as mudanças unstaged contra o index. --staged compara o index contra HEAD. git show exibe os metadados e o patch de um único commit. A sintaxe HEAD:path permite ver o conteúdo de qualquer arquivo em qualquer commit sem precisar fazer checkout, o que é útil para recuperar versões antigas ou inspecionar o histórico.
# compare working tree, index, and commits
git diff # unstaged changes
git diff --staged # staged but uncommitted
git diff HEAD # all changes vs last commit
git diff main feature # compare two branches
git diff abc1234 def5678 # compare two commits
# inspect a single commit
git show HEAD # latest commit diff + metadata
git show abc1234
git show HEAD:file.txt # view a file at a given commitBranching & Merging
Criando & Trocando de Branches
Branches no Git são ponteiros leves para um commit — criar um é quase instantâneo. git switch (Git 2.23+) é a alternativa moderna e mais segura ao checkout para trocar de branch, reservando checkout para restaurar arquivos. -d recusa deletar um branch não mergeado (protegendo seu trabalho); -D força a exclusão. Sempre delete branches após o merge para manter a lista de branches limpa.
# list branches
git branch # local branches
git branch -a # local + remote
git branch -vv # with tracking info
# create and switch
git branch feature # create only
git checkout feature # switch to it
git checkout -b feature # create + switch (classic)
git switch -c feature # create + switch (modern)
# delete branches
git branch -d feature # safe delete (merged only)
git branch -D feature # force deleteMergeando Branches
Um merge fast-forward simplesmente move o ponteiro do branch para frente quando o alvo não tem novos commits — produzindo histórico linear. --no-ff força um merge commit, preservando o fato de que um branch existiu (útil para rastreamento de features). --squash combina todos os commits do branch em uma única mudança staged que você então commita uma vez — ótimo para limpar um histórico de feature ruidoso antes de integrar ao main.
# merge feature into main
git checkout main
git merge feature
# fast-forward merge (default when possible)
# main just moves forward to feature's commit
# create a merge commit (preserves branch history)
git merge --no-ff feature -m "Merge feature branch"
# squash all feature commits into one
git merge --squash feature
git commit -m "feat: add feature (squashed)"
# abort a merge with conflicts
git merge --abortResolvendo Conflitos de Merge
Conflitos ocorrem quando as mesmas linhas são alteradas de forma diferente em dois branches. O Git insere marcadores de conflito (<<<<<<<, =======, >>>>>>>) mostrando ambos os lados. Resolva editando o arquivo para o estado final desejado, depois git add para marcá-lo como resolvido. Commit para completar o merge. git mergetool lança uma ferramenta visual de diff. Se você estiver sobrecarregado, git merge --abort retorna ao estado pré-merge.
# a conflict produces markers in the file
# <<<<<<< HEAD
# my changes (current branch)
# =======
# their changes (incoming branch)
# >>>>>>> feature
# edit the file to resolve, then:
git add resolved-file.txt
git commit # finalize the merge
# use a merge tool
git mergetool
# see which files conflict
git status
# abandon the merge
git merge --abortRebasing
Rebase reproduz os commits do seu branch sobre outro branch, produzindo histórico linear sem merge commits. O rebase interativo (-i) é uma ferramenta poderosa: squash mergeia commits, reword edita mensagens, drop remove commits, edit pausa para permitir que você modifique um commit. NUNCA faça rebase de commits que já foram feitos push e compartilhados — isso reescreve o histórico e quebra os repositórios dos colegas. Use rebase apenas nos seus próprios branches locais.
# rebase feature onto main (replay feature commits)
git checkout feature
git rebase main
# interactive rebase — rewrite last 5 commits
git rebase -i HEAD~5
# options: pick, reword, squash, fixup, drop, edit
# continue, skip, or abort during a rebase
git rebase --continue
git rebase --skip
git rebase --abort
# rebase while pulling
git pull --rebase origin mainCherry-Picking & Reflog
Cherry-pick aplica um commit individual de outro branch no seu branch atual — útil para backportar um bugfix sem mergear o branch inteiro. O reflog é um log local de todo movimento do HEAD (commits, checkouts, resets) mantido por ~90 dias. É sua rede de segurança: mesmo após um reset destrutivo, você pode encontrar o hash do commit antigo no reflog e recuperá-lo. Os dados do reflog são apenas locais e nunca são feitos push.
# apply a specific commit onto current branch
git cherry-pick abc1234
git cherry-pick abc1234 def5678 # multiple commits
git cherry-pick abc1234..def5678 # range (exclusive start)
# reflog: record of where HEAD has been
git reflog
git reflog show feature
# recover a 'lost' commit
git reset --hard HEAD@{2}
# view a branch's history of moves
git reflog show mainRepositórios Remotos
Gerenciando Remotes
Um remote é uma referência nomeada a outro repositório, tipicamente origin para o seu fork e upstream para o projeto original. git remote -v mostra as URLs de fetch e push. Forks no GitHub usam o remote upstream para sincronizar com o original: faça fetch do upstream, merge ou rebase, depois push para origin. set-url é útil para alternar entre autenticação HTTPS e SSH.
# list configured remotes
git remote -v
git remote show origin
# add a remote
git remote add origin https://github.com/user/repo.git
# add an upstream remote (for forks)
git remote add upstream https://github.com/original/repo.git
# rename or remove
git remote rename origin upstream
git remote remove origin
# change a remote URL (e.g. HTTPS to SSH)
git remote set-url origin [email protected]:user/repo.gitFetch, Pull & Push
fetch baixa dados remotos mas deixa sua árvore de trabalho intacta — seguro para inspecionar antes de mergear. pull = fetch + merge (ou rebase com --rebase). O primeiro push de um novo branch precisa de -u para configurar o tracking para que futuros git push/pull funcionem sem argumentos. --force-with-lease é a alternativa segura ao --force: só sobrescreve o remote se ninguém mais fez push nesse meio tempo, prevenindo sobrescrita acidental do trabalho dos colegas.
# download remote changes without merging
git fetch origin
git fetch --all --prune # all remotes, drop deleted branches
# fetch + merge in one step
git pull
git pull --rebase # rebase instead of merge
# push local commits to remote
git push origin main
git push -u origin feature # push + set upstream tracking
git push # subsequent pushes (uses tracking)
# force push (rewrites remote history — DANGER)
git push --force-with-lease # safer than --forceTracking & Sincronizando Branches
Tracking branches vinculam um branch local a um remoto para que git pull e git push saibam onde fazer fetch/push sem especificar. -u (abreviação de --set-upstream-to) configura isso no primeiro push. Quando colegas deletam branches remotos, suas refs de remote-tracking locais ficam desatualizadas — git remote prune origin as limpa. git fetch --prune faz isso automaticamente durante o fetch.
# set upstream for current branch
git branch -u origin/main
git branch --set-upstream-to=origin/main
# create a local branch tracking a remote one
git checkout -b feature origin/feature
git switch feature # auto-detects tracking branch
# delete a remote branch
git push origin --delete feature
# list all remote-tracking branches
git branch -r
git remote prune origin # remove stale remote refsFluxo de Trabalho de Pull Requests
O fluxo padrão do GitHub: crie um feature branch, faça push, abra um Pull Request para revisão e delete o branch após o merge. Para forks, o remote upstream permite puxar mudanças do repositório original. Manter o main do seu fork sincronizado com o upstream regularmente evita merges grandes e dolorosos depois. Muitas equipes ativam 'auto-delete branch on merge' para manter a lista de branches arrumada.
# typical feature workflow
git checkout -b feature/login
# ... make changes, commit ...
git push -u origin feature/login
# create PR on GitHub, then after merge:
git checkout main
git pull origin main
git branch -d feature/login
# sync a fork with its upstream
git fetch upstream
git checkout main
git merge upstream/main
git push origin mainRepositórios Bare & Mirrors
Um repositório bare não tem árvore de trabalho — ele só armazena os dados .git. Repos bare são usados em servidores (como Git auto-hospedado) como o remote central para o qual múltiplas pessoas fazem push e pull. --mirror clona tudo incluindo refs de remote-tracking e é usado para backups ou migração de um repo entre hosts. --all faz push de todos os branches; --tags faz push de todas as tags.
# create a bare repo (no working tree) — for servers
git init --bare project.git
# mirror clone (full copy including all refs)
git clone --mirror https://github.com/user/repo.git
# push all branches and tags
git push --all
git push --tags
# push a specific branch to a specific remote
git push origin local-name:remote-nameDesfazendo Mudanças
Reset: Soft, Mixed, Hard
reset move o ponteiro do branch atual. --soft mantém suas mudanças staged (apenas o commit é desfeito) — ideal para re-commitar. --mixed (padrão) unstageia mas mantém as mudanças da árvore de trabalho. --hard descarta tudo permanentemente — sua única recuperação é o reflog. Nunca use --hard em commits que você fez push, pois reescreve histórico compartilhado e causa divergência.
# reset moves HEAD and optionally the index/worktree
git reset --soft HEAD~1 # undo commit, keep staged
git reset --mixed HEAD~1 # undo commit + unstage (DEFAULT)
git reset --hard HEAD~1 # undo commit + discard changes
# reset to a specific commit
git reset --hard abc1234
# reset a single file to HEAD
git reset HEAD file.txt # unstage
git checkout -- file.txt # discard working changesRevert (Desfazer Seguro)
Diferente do reset (que reescreve histórico), revert adiciona um novo commit que inverte o commit alvo — seguro para branches compartilhados porque o histórico é preservado. Esta é a maneira correta de desfazer uma mudança que já foi feito push. Reverter um merge commit requer -m 1 para especificar qual linha pai manter (1 = o branch no qual você mergeou).
# revert creates a NEW commit that undoes another
git revert abc1234
git revert HEAD # undo last commit
# revert a range
git revert HEAD~3..HEAD
# revert without committing (stage the inverse)
git revert --no-commit abc1234
# revert a merge commit
git revert -m 1 abc1234 # -m 1 = keep main branch parentRestore & Clean
git restore (Git 2.23+) é o comando moderno e focado para operações de árvore de trabalho, separando as responsabilidades do checkout. --staged unstageia sem tocar nas mudanças de trabalho. git clean remove arquivos untracked — sempre execute com -n (dry run) primeiro para visualizar o que será deletado. -x é agressivo: também deleta arquivos gitignored, o que é útil para um build limpo mas pode apagar segredos ou saídas de build.
# discard unstaged changes in a file
git restore file.txt
git checkout -- file.txt # older syntax
# unstage a file (keep working changes)
git restore --staged file.txt
# restore a file from a specific commit
git restore --source=abc1234 file.txt
# remove untracked files
git clean -n # dry run (preview)
git clean -fd # remove untracked files + dirs
git clean -fdx # also remove ignored filesAmend & Fixup
--amend reescreve o último commit — útil para corrigir um erro de digitação ou adicionar um arquivo esquecido, mas nunca amende commits que receberam push. O fluxo de fixup é elegante: crie commits de fixup conforme notar pequenos problemas, então git rebase -i --autosquash reordena e squasha automaticamente nos commits alvo. Isso mantém o histórico limpo enquanto permite commitar pequenas correções incrementalmente.
# amend the last commit (message + content)
git add forgotten-file.txt
git commit --amend --no-edit
# change only the commit message
git commit --amend -m "better message"
# create a fixup commit (for later autosquash)
git commit --fixup=abc1234
# squash fixups during interactive rebase
git rebase -i --autosquash abc1234~1Recuperação com Reflog
O reflog é sua rede de segurança. Ele registra todo commit, checkout, reset e rebase — até operações que 'destroem' commits. As entradas persistem por ~90 dias. Se você acidentalmente fizer reset --hard ou deletar um branch, encontre o hash do commit órfão no reflog e faça reset para ele ou crie um novo branch apontando para ele. O reflog é apenas local, então isso funciona até offline.
# reflog records every HEAD movement
git reflog
# abc1234 HEAD@{0}: reset: moving to abc1234
# def5678 HEAD@{1}: commit: feat: add x
# ghi9012 HEAD@{2}: checkout: moving to feature
# recover a 'lost' commit
git reset --hard def5678
# recover a deleted branch
git branch recovered-feature def5678
# reflog for a specific branch
git reflog show featureStashing & Fluxos de Trabalho
Stashing de Mudanças
Stash arquiva mudanças não commitadas para que você possa trocar de branch ou puxar atualizações com uma árvore limpa. apply mantém o stash na lista (útil se quiser aplicá-lo a múltiplos branches); pop aplica e remove. Stashes são uma pilha LIFO referenciada por stash@{N}. Use clear com cautela — descarta permanentemente todos os stashes.
# save uncommitted changes (reverts working tree to HEAD)
git stash
git stash push -m "wip: login form" # with a message
# list, show, apply
git stash list
git stash show -p stash@{0} # show diff
git stash apply # apply latest, keep stash
git stash pop # apply latest, drop stash
# apply a specific stash
git stash apply stash@{2}
# drop a stash
git stash drop stash@{0}
git stash clear # drop ALL stashesStash Parcial & Seletivo
Essas flags dão controle refinado sobre o que é stashado. --keep-index é útil quando você stageou um commit lógico mas quer testar apenas essas mudanças — stashe o resto, execute os testes, depois pop. -u inclui arquivos untracked (caso contrário, são deixados na sua árvore de trabalho). -p permite escolher hunks específicos, espelhando o git add -p.
# stash only staged changes
git stash --staged
# stash only unstaged changes (keep staged)
git stash --keep-index
# stash interactively by hunk
git stash -p
# stash including untracked files
git stash -u
git stash --include-untracked
# stash everything (even ignored)
git stash -aStash Branches & Create
git stash branch cria um novo branch no commit pai original do stash e aplica o stash lá — perfeito quando um stash não se aplica mais de forma limpa ao branch atual devido a conflitos. Você também pode exportar um stash como um arquivo de patch com show -p para arquivamento ou compartilhamento. Stashes são locais e nunca recebem push, então não devem ser usados para armazenamento de longo prazo.
# create a branch from a stash (great for conflicts)
git stash branch feature/wip stash@{0}
# create a commit from a stash without applying
git stash store -m "saved wip" stash@{0}
# apply a stash to a different branch
git checkout other-branch
git stash apply stash@{0}
# inspect stash contents
git stash show stash@{0} --stat
git stash show -p stash@{0} > patch.diffModelos de Fluxo de Trabalho Git
GitHub Flow é o mais simples: um branch main, feature branches com PRs, deploy no merge — ideal para deployment contínuo. Git Flow (modelo de Vincent Driessen) adiciona branches develop, release e hotfix para gerenciamento estruturado de releases — adequado para produtos versionados. Trunk-Based Development usa branches de vida muito curta e é favorecido por equipes DevOps de alto desempenho para máxima velocidade de integração.
# GitHub Flow (simple, popular)
# main is always deployable; feature branches + PRs
git checkout -b feature/x
git push -u origin feature/x
# open PR, review, merge to main, deploy
# Git Flow (structured, with release branches)
# main: production releases
# develop: integration branch
# feature/*: features off develop
# release/*: release prep
# hotfix/*: urgent fixes off main
# Trunk-Based Development
# short-lived branches (1-2 days), frequent rebase on mainSubmodules
Submodules incorporam um repositório Git dentro de outro — útil para compartilhar uma biblioteca entre projetos enquanto a mantém versionada independentemente. O repo pai armazena um ponteiro para um commit específico do submodule. Clones não buscam conteúdo de submodule por padrão; --recurse-submodules faz isso em uma etapa. Submodules podem ser trabalhosos; para gerenciamento de dependências mais simples, considere Git subtrees ou um package manager.
# add another repo as a submodule
git submodule add https://github.com/user/lib.git libs/lib
# clone a repo with its submodules
git clone --recurse-submodules https://github.com/user/repo.git
# initialize submodules in an existing clone
git submodule update --init --recursive
# update submodules to their latest remote commit
git submodule update --remote
# record a submodule pointer change
git add libs/lib
git commit -m "chore: bump lib submodule"Inspeção & Debugging
Blame & Annotate
git blame (também chamado de annotate) mostra o commit e o autor de cada linha de um arquivo — essencial para entender por que o código parece do jeito que parece. -L restringe a um intervalo de linhas, útil para arquivos grandes. -w ignora mudanças puras de whitespace, e -C detecta código movido ou copiado de outro arquivo, dando um histórico mais preciso de onde as linhas realmente se originaram.
# show who last changed each line
git blame file.txt
git blame -L 10,20 file.txt # only lines 10-20
git blame -e file.txt # show author email
git blame -w file.txt # ignore whitespace changes
git blame -C file.txt # detect moved lines across files
# GUI annotation in some editors
git gui blame file.txtBisect (Busca Binária de Bugs)
bisect realiza uma busca binária no histórico para identificar o commit exato que introduziu um bug. Você marca um commit conhecido como bom e outro como ruim; o Git faz checkout do ponto médio, você testa, depois marca como bom ou ruim, dividindo o intervalo a cada vez. Com um script, todo o processo é totalmente automatizado — uma economia massiva de tempo para regressões em históricos grandes.
# find the commit that introduced a bug
git bisect start
git bisect bad # current commit is broken
git bisect good v1.0.0 # v1.0.0 was working
# Git checks out a midpoint; test it, then:
git bisect good # or git bisect bad
# automate with a script that exits non-zero on bug
git bisect start HEAD v1.0.0 -- npm test
# finish and return to original branch
git bisect reset
# view the bisect log
git bisect logBuscando Código & Histórico
git grep busca arquivos rastreados na árvore de trabalho — mais rápido que grep -r porque usa o index. A opção -S 'pickaxe' encontra commits que adicionaram ou removeram uma string específica, inestimável para rastrear quando uma função ou bug foi introduzido. -G é similar mas corresponde a um regex em qualquer lugar do diff. Combinado com filtros --author e --since, você pode identificar qualquer mudança no histórico.
# search working tree for a string
git grep "TODO"
git grep -n "TODO" -- "*.js"
git grep -i "error" src/
# search across all commits (history)
git log -S "functionName" --oneline # pickaxe: when added/removed
git log -G "functionName.*\(" --oneline # regex match in diffs
# search commit messages
git log --grep="fix.*login" --oneline
git log --author="Alice" --since="1 week ago"FSCK & Objetos Dangling
git fsck (file system check) verifica a integridade do banco de dados de objetos e pode encontrar commits dangling — útil para recuperação quando o reflog é insuficiente. git gc reorganiza e comprime objetos para economizar espaço em disco; --prune=now remove objetos inalcançáveis imediatamente. O Git faz gc automático periodicamente, mas executá-lo manualmente pode encolher um repo grande. --aggressive recalcula deltas para máxima compressão.
# check repository integrity
git fsck --full
git fsck --unreachable
# find dangling (unreachable) commits & blobs
git fsck --lost-found
# garbage collect and prune old objects
git gc
git gc --prune=now
git gc --aggressive
# count objects and disk usage
git count-objects -vArchive & Bundle
git archive exporta um snapshot limpo de um commit sem o diretório .git — ideal para distribuir releases ou enviar código-fonte para alguém que não precisa do histórico. git bundle empacota um repo (ou um intervalo de commits) em um único arquivo que pode ser clonado ou do qual se pode fazer fetch — perfeito para transferir repos por redes air-gapped ou por email quando você não pode usar um servidor remoto.
# export a snapshot as a tar/zip (no .git history)
git archive --format=zip --output=app.zip HEAD
git archive --format=tar HEAD | gzip > app.tar.gz
git archive -o release.zip v1.0.0
# bundle a repo (with history) into a single file
git bundle create repo.bundle --all
git bundle create patch.bundle main..feature
# clone from a bundle (offline transfer)
git clone repo.bundle new-repo
git fetch patch.bundle featureTécnicas Avançadas
Hooks
Hooks são scripts que rodam automaticamente em pontos específicos. Hooks do lado cliente (pre-commit, commit-msg, pre-push) impõem política local como linting ou testes. Hooks do lado servidor (pre-receive, post-receive) rodam no remote e podem impor proteção de branch ou disparar CI/CD. O diretório .git/hooks contém scripts de exemplo terminados em .sample — renomeie para ativar. Ferramentas como Husky ou o framework pre-commit gerenciam hooks no próprio repo para consistência da equipe.
# client-side hooks live in .git/hooks/
# pre-commit runs before a commit is created
# commit-msg validates the commit message
# pre-push runs before pushing to remote
# sample pre-commit hook (.git/hooks/pre-commit)
#!/bin/sh
npm run lint || exit 1
npm test || exit 1
# server-side hooks (on the remote)
# pre-receive, update, post-receive
# make a hook executable
chmod +x .git/hooks/pre-commitWorktrees
Worktrees permitem ter múltiplos diretórios de trabalho para um único repositório, cada um em um branch diferente — sem clonar. Isso é inestimável quando você precisa trabalhar em um hotfix enquanto mantém a árvore de trabalho do seu feature branch intacta, ou rodar um build demorado em um branch enquanto edita outro. Todos os worktrees compartilham o mesmo banco de dados de objetos .git, então o uso de disco é mínimo.
# create a second working tree of the same repo
git worktree add ../app-feature feature/x
# list worktrees
git worktree list
# create a worktree with a new branch
git worktree add -b hotfix/y ../app-hotfix main
# remove a worktree
git worktree remove ../app-feature
# prune stale worktree metadata
git worktree pruneFiltrar & Reescrever Histórico
Reescrever histórico é necessário quando segredos ou arquivos grandes foram commitados. git filter-repo é o substituto moderno e rápido do git filter-branch. BFG Repo-Cleaner é uma alternativa amigável para remover arquivos grandes ou padrões de senha. Após reescrever, você deve fazer force-push e notificar todos os colaboradores para re-clonar — commits antigos permanecem nos reflogs deles até expirarem. Sempre rotacione quaisquer segredos vazados imediatamente.
# remove a file from ALL of history (sensitive data)
git filter-repo --path secrets.env --invert-paths
# (install git-filter-repo; the old filter-branch is deprecated)
# rewrite author info across all commits
git filter-repo --mailmap mailmap.txt
# split a subdirectory into its own repo
git filter-repo --subdirectory-filter libs/lib
# the simpler BFG Repo-Cleaner (Java tool)
bfg --delete-files *.env
bfg --replace-text passwords.txtSparse Checkout & Partial Clone
Partial clone (--filter) busca commits e trees mas baixa blobs (conteúdo de arquivos) preguiçosamente conforme você acessa — acelerando dramaticamente clones de repos enormes. Sparse checkout limita sua árvore de trabalho a diretórios específicos, então você só vê as partes com que trabalha. Juntos, eles tornam monorepos enormes gerenciáveis: o clone é rápido e sua árvore de trabalho permanece pequena.
# partial clone: download history on demand
git clone --filter=blob:none https://github.com/user/huge-repo.git
# sparse checkout: only certain directories
git clone --no-checkout https://github.com/user/repo.git
cd repo
git sparse-checkout init --cone
git sparse-checkout set src docs
git checkout main
# add a directory to the sparse set later
git sparse-checkout add tests
# disable sparse checkout (fetch everything)
git sparse-checkout disableReflog, Refspec & Notes
Refspecs dão controle explícito sobre como as refs são mapeadas durante fetch/push — úteis para fluxos de trabalho incomuns como fazer push de um feature branch local para o main remoto. git notes anexa metadados a um commit sem reescrevê-lo, o que é útil para comentários de revisão ou links de CI. Notes são armazenados em uma ref separada (refs/notes/commits) e devem ser explicitamente feitos push e fetch.
# refspec: explicit fetch/push mapping
git fetch origin "refs/heads/*:refs/remotes/origin/*"
git push origin "refs/heads/feature:refs/heads/main"
# add a note to a commit (without changing it)
git notes add -m "Reviewed by Alice" abc1234
git notes show abc1234
git log --show-notes
# configure where notes are stored
git config --global notes.displayRef "refs/notes/*"
# push notes to a remote
git push origin refs/notes/commitsMelhores Práticas & Dicas
Higiene de Commits
Bons commits são pequenos, atômicos e autossuficientes: uma mudança lógica por commit. Isso torna a revisão de código mais fácil, o bisect mais rápido e os reverts cirúrgicos. Use git add -p para stagear apenas os hunks relevantes a uma única preocupação. Mensagens imperativas ('add' não 'added') leem-se como instruções para o codebase. Fazer rebase de feature branches sobre o main mais recente antes de mergear mantém o histórico linear e inteligível.
# commit small, focused, logical units
git add -p # stage only relevant hunks
git commit -m "fix: handle null user in login"
# write clear, imperative messages
# GOOD: "feat: add password reset endpoint"
# BAD: "stuff", "fix", "WIP", "asdf"
# one concern per commit
git add login.ts && git commit -m "feat: add login"
git add styles.css && git commit -m "style: format login"
# rebase before merging to keep history clean
git fetch && git rebase origin/mainProteção de Branch & Revisão de Código
Regras de proteção de branch previnem force-pushes, exigem revisões de PR e condicionam merges a CI passando — essencial para a segurança da equipe. Exigir histórico linear força rebase ou squash merges, mantendo o histórico legível. Commits assinados (GPG ou SSH) provam autoria e previnem personificação. Essas configurações são feitas na plataforma de hospedagem (GitHub/GitLab), não no próprio Git, mas impõem boa higiene de Git em toda a organização.
# on GitHub/GitLab: protect the main branch
# - require pull request reviews
# - require status checks (CI) to pass
# - require linear history (no merge commits)
# - dismiss stale reviews on push
# - require signed commits
# enforce via config (GitHub CLI)
gh api repos/:owner/:repo/branches/main/protection \
-X PUT -f required_status_checks[strict]=true
# sign commits and verify on receive
git config --global commit.gpgsign true
git config --global gpg.format sshIgnoring & Gerenciamento de Segredos
Segredos commitados são o incidente de segurança Git mais comum. Uma vez feito push, assuma que o segredo está comprometido — rotacione-o imediatamente, mesmo após removê-lo do histórico, porque clones e forks o retêm. A prevenção é a melhor: .gitignore segredos, use variáveis de ambiente ou um secret manager (Vault, AWS Secrets Manager), e instale git-secrets ou um hook pre-commit que escaneia por API keys e senhas antes de cada commit.
# never commit secrets — use .gitignore
echo ".env" >> .gitignore
echo "*.pem" >> .gitignore
echo "secrets/" >> .gitignore
# if you accidentally committed a secret:
# 1. rotate/revoke the secret immediately
# 2. remove from history
git filter-repo --path .env --invert-paths
git push --force-with-lease
# use environment files or secret managers
echo "API_KEY=$API_KEY" > .env.local # gitignored
# load via dotenv in your app
# use git-secrets to scan pre-commit
git secrets --install
git secrets --register-awsDicas de Performance
Repositórios grandes podem ser lentos. fsmonitor e untrackedcache tornam o git status muito mais rápido armazenando em cache o estado do sistema de arquivos. Clones rasos e parciais reduzem o download inicial. Execute git gc periodicamente para compactar objetos. --no-verify pula os hooks pre-commit e commit-msg — útil para emergências mas não deve se tornar um hábito, pois ignora as verificações de qualidade da sua equipe.
# speed up status on large repos
git config --global core.fsmonitor true
git config --global core.untrackedcache true
# shallow clone for CI
git clone --depth 1 https://github.com/user/repo.git
# partial clone for huge monorepos
git clone --filter=blob:none https://github.com/user/monorepo.git
# periodic garbage collection
git gc --prune=now
# disable slow hooks temporarily
git commit --no-verifyPegadinhas Comuns
Evite esses erros clássicos: nunca faça force-push para branches compartilhados (--force-with-lease é a opção segura); nunca faça rebase de commits nos quais outros possam ter baseado trabalho; nunca commit em um detached HEAD sem criar um branch primeiro. Use Git LFS para binários grandes — caso contrário, eles incham o histórico permanentemente. Configure finais de linha (autocrlf) por SO para evitar diffs ruidosos apenas de whitespace em equipes multiplataforma.
# PITFALL: force-pushing to shared branches
git push --force origin main # DON'T — use --force-with-lease
# PITFALL: rebasing pushed commits
# rebase only your local, unpushed branches
# PITFALL: committing on detached HEAD
git checkout v1.0.0
# changes here are not on a branch — create one:
git checkout -b fix/v1.0.1
# PITFALL: large binary files bloating history
# use Git LFS for binaries
git lfs install
git lfs track "*.psd"
git add .gitattributes
# PITFALL: CRLF/LF line endings across OS
git config --global core.autocrlf input # on Linux/Mac
git config --global core.autocrlf true # on WindowsFluxo de Trabalho Git Flow
Modelo de Branches do Git Flow
Git Flow é um modelo de branching estrito para projetos baseados em release. main sempre contém código de produção; develop contém trabalho de integração. Features saem de develop e mergeiam de volta. Releases saem de develop, estabilizam, depois mergeiam tanto para main (com uma tag) quanto para develop. Hotfixes saem de main e mergeiam tanto para main quanto para develop. Este modelo se adapta a projetos com releases programados (apps desktop, software on-premise). Para deployment contínuo, GitHub Flow (main + feature branches) é mais simples. A ferramenta CLI git-flow automatiza a dança de branches.
# Git Flow uses long-lived branches:
# main → production-ready code
# develop → latest delivered development changes
# feature/* → new features (branched from develop)
# release/* → release preparation (branched from develop)
# hotfix/* → urgent production fixes (branched from main)
# Initialize git-flow (interactive setup)
git flow init
# Start a new feature
git flow feature start my-feature
# Creates feature/my-feature from develop
# Finish a feature (merges to develop, deletes branch)
git flow feature finish my-feature
# Publish a feature to remote
git flow feature publish my-feature
# Start a release
git flow release start 1.2.0
# Creates release/1.2.0 from develop
# Finish a release (merges to main AND develop, tags)
git flow release finish 1.2.0GitHub Flow (Mais Simples)
GitHub Flow é o fluxo de trabalho mais simples: main é sempre deployável, feature branches são de vida curta, e tudo é mergeado via Pull Requests. Não há branch develop nem branches de release — main é deployado continuamente. Isso funciona bem para web apps com deployment contínuo. A regra principal: nunca commit diretamente em main; sempre use um PR para revisão. Mantenha feature branches pequenos e de vida curta (dias, não semanas). Delete branches após o merge para manter o repo limpo. Este modelo prioriza velocidade e simplicidade sobre o gerenciamento estruturado de releases do Git Flow.
# GitHub Flow: only main + feature branches
# 1. Create branch from main
git checkout main
git pull origin main
git checkout -b feature/add-login
# 2. Commit changes
git add .
git commit -m "feat: add login page"
# 3. Push to remote
git push -u origin feature/add-login
# 4. Open Pull Request on GitHub
# 5. Review, discuss, get approval
# 6. Merge to main (via GitHub UI or CLI)
git checkout main
git pull origin main
git branch -d feature/add-login # delete local branch
# 7. Deploy from main (continuous deployment)Trunk-Based Development
Trunk-Based Development é o mais extremo: desenvolvedores commitam diretamente em main (ou branches de vida muito curta mergeados em 24 horas). Isso permite integração contínua real — todos integram constantemente. Features incompletas usam feature flags (deployadas mas ocultas) em vez de branches de vida longa. Isso exige CI/CD forte, testes abrangentes e infraestrutura de feature flags. Usado por Google, Facebook e Netflix. Benefícios: nenhum inferno de merge, feedback rápido, mudanças pequenas. Desafios: exige disciplina, cobertura de testes e gerenciamento de feature flags. Melhor para equipes experientes com CI/CD robusto.
# Trunk-Based: everyone commits to main (trunk)
# Short-lived feature branches optional
# Direct commit to main (small teams)
git checkout main
git pull
# make changes
git add . && git commit -m "fix: typo in header"
git push
# With short-lived branches (larger teams)
git checkout -b quick-fix
git commit -m "fix: validation bug"
git push
# PR merged within 24 hours
# Feature flags enable incomplete code on main
# Code is deployed but hidden behind a flag
if (featureFlag.isEnabled("new-checkout")) {
showNewCheckout();
} else {
showOldCheckout();
}Fluxo de Trabalho Fork & Pull
O fluxo de trabalho Fork & Pull é padrão para open source. Contribuidores fazem fork do repo (criando sua própria cópia), fazem push de branches para seu fork, e abrem PRs para o repo original (upstream). O remote upstream permite sincronizar seu fork com o original. Sempre crie feature branches a partir de um main atualizado. Este fluxo permite que qualquer um contribua sem acesso de escrita. Mantenedores revisam PRs e mergeiam. Para manter seu fork sincronizado, faça fetch do upstream e merge/rebase regularmente. Alguns projetos usam um modelo 'clone, branch, PR' para contribuidores internos com acesso direto de push para feature branches.
# For open-source projects (contributors don't have push access)
# 1. Fork the repo on GitHub (UI button)
# 2. Clone YOUR fork
git clone https://github.com/YOUR-USERNAME/project.git
cd project
# 3. Add upstream (original repo)
git remote add upstream https://github.com/ORIGINAL/project.git
# 4. Keep your fork updated
git fetch upstream
git checkout main
git merge upstream/main # or: git rebase upstream/main
git push origin main
# 5. Create feature branch
git checkout -b feature/my-contribution
# 6. Push to YOUR fork
git push origin feature/my-contribution
# 7. Open Pull Request from your fork to upstreamConvenções de Nomenclatura de Branches
Nomenclatura consistente de branches melhora a clareza e permite automação. Prefixos comuns: feature, bugfix, hotfix, release, chore, docs, refactor, experiment. Incluir números de tickets (PROJ-123) vincula branches a issues e permite auto-linking. Barras criam hierarquia visual em GUIs do Git. Algumas equipes impõem nomenclatura via Git hooks ou verificações de CI. A convenção deve ser documentada em CONTRIBUTING.md. Mantenha nomes descritivos mas concisos. Evite nomes pessoais (johns-branch) — descreva o trabalho, não o autor. Nomenclatura consistente torna a limpeza de branches e a navegação do hist órico muito mais fáceis.
# Common naming patterns:
feature/add-user-authentication
feature/PROJ-123-user-profile
bugfix/fix-login-redirect
bugfix/PROJ-456-crash-on-startup
hotfix/security-patch-xss
release/v2.0.0
chore/update-dependencies
docs/api-documentation
refactor/extract-auth-module
experiment/new-algorithm
# Using ticket numbers (Jira, Linear, etc.)
git checkout -b feature/PROJ-123-oauth-login
# Auto-linking in commit messages
git commit -m "PROJ-123: implement OAuth login"
# GitHub auto-links PROJ-123 to the Jira ticket
# Branch naming with slashes creates hierarchy
# in Git GUI tools (GitHub, GitKraken, SourceTree)Rebase Aprofundado
Rebase Interativo
Rebase interativo (-i) é a ferramenta mais poderosa de edição de histórico. Permite reescrever, reordenar, combinar, dividir ou deletar commits antes de fazer push. squash combina um commit em seu pai (mergeando mensagens); fixup faz o mesmo mas descarta a mensagem do commit (limpa commits 'WIP'). edit pausa o rebase para que você possa amend o commit (adicionar arquivos, mudar conteúdo). reword permite mudar apenas a mensagem. drop remove um commit. Sempre faça rebase antes de push para manter o histórico limpo. Nunca faça rebase de commits que outros já puxaram — isso reescreve histórico compartilhado.
# Rebase last 5 commits interactively
git rebase -i HEAD~5
# Opens editor with:
# pick a1b2c3d First commit
# pick e4f5g6h Second commit
# pick i7j8k9l Third commit
# pick m0n1o2p Fourth commit
# pick q3r4s5t Fifth commit
# Commands:
# pick = use commit as-is
# reword = use commit, edit message
# edit = pause to amend the commit
# squash = combine with previous commit
# fixup = like squash, discard this message
# drop = remove commit entirely
# exec = run a shell command
# reorder = move lines to reorder commitsSquash Commits
Squashing combina múltiplos commits em um, criando histórico limpo. Isso é ideal para mergear um feature branch: squashe 20 commits 'WIP' em um commit significativo 'feat: add login'. A flag --fixup cria um commit especial que --autosquash automaticamente posiciona e squasha com seu alvo — ótimo para corrigir feedback de revisão sem poluir o histórico. git reset --soft main seguido de um único commit squasha tudo de uma vez (mais simples que rebase interativo para squashing total). Muitas equipes configuram merges de PR para auto-squash (opção 'Squash and merge' do GitHub).
# Squash the last 3 commits into one
git rebase -i HEAD~3
# In editor, change:
# pick a1b2c3d First
# squash e4f5g6h Second (or 'fixup' to discard message)
# squash i7j8k9l Third
# Git prompts for combined commit message
# Squash everything since branching from main
git rebase -i main
# Auto-squash fixup commits
git commit --fixup a1b2c3d # creates fixup! commit
git rebase -i --autosquash main
# Git automatically reorders and squashes fixup commits
# Squash all commits into one on a branch
git reset --soft main
git commit -m "feat: complete feature X"Rebase vs Merge
Merge preserva o histórico completo do branch (com merge commits mostrando onde as features divergiram e mergearam). Rebase reproduz commits sobre o alvo, criando histórico linear sem merge commits. Merge é mais seguro (sem reescrita de histórico) e mostra contexto da feature. Rebase é mais limpo mas reescreve o histórico de commits. Estratégia comum: faça rebase do seu feature branch sobre o main mais recente antes de mergear, depois merge (fast-forward ou com --no-ff para um merge commit). Isso dá commits limpos E um merge commit marcando a feature. Para branches públicos/compartilhados, prefira merge para evitar conflitos de histórico.
# MERGE: preserves complete history with merge commits
git checkout main
git merge feature
# Creates a merge commit, history looks like a graph:
# * merge commit
# |\
# | * feature commit 2
# | * feature commit 1
# * main commit
# REBASE: linear history, no merge commits
git checkout feature
git rebase main
git checkout main
git merge feature # fast-forward, no merge commit
# History is linear:
# * feature commit 2
# * feature commit 1
# * main commit
# When to use each:
# Merge: preserve context of feature branch, public repos
# Rebase: clean linear history, before pushing feature branchResolvendo Conflitos de Rebase
Conflitos de rebase ocorrem ao reproduzir commits sobre uma base alterada. Resolva cada conflito, git add os arquivos resolvidos, e git rebase --continue. O rebase processa um commit por vez, então você pode encontrar múltiplos conflitos. --skip descarta um commit (use se ficar vazio após o rebase). --abort cancela tudo e retorna ao estado pré-rebase — sempre uma saída segura. Para conflitos complexos, git mergetool lança uma ferramenta visual de merge (VS Code, Beyond Compare, etc.). A diferença principal dos conflitos de merge: o rebase pode exigir resolver o mesmo conflito múltiplas vezes (uma por commit reproduzido).
# During rebase, conflicts pause the process
git rebase main
# CONFLICT in file.txt
# 1. Resolve conflicts manually in the file
# (look for <<<<<<< ======= >>>>>>> markers)
# 2. Stage resolved files
git add file.txt
# 3. Continue the rebase
git rebase --continue
# Skip a commit (if it's empty after rebase)
git rebase --skip
# Abort the entire rebase (return to original state)
git rebase --abort
# Use a merge tool for conflicts
git mergetool
# After resolving, the rebase continues
# with the next commit automaticallyRebase --onto (Avançado)
git rebase --onto é uma forma avançada para transplante preciso de commits. A sintaxe: rebase --onto NEW-BASE OLD-BASE BRANCH — pega commits entre OLD-BASE e BRANCH, e os reproduz sobre NEW-BASE. Isso é útil para mudar o ponto de base de um branch (ex.: sua feature era baseada em outra feature que foi mergeada; rebase onto main para limpar). Também é usado para remover commits específicos do histórico (reproduz ao redor deles). Este é um recurso de usuário avançado — entenda o rebase regular primeiro. Sempre tenha um backup (reflog) antes de reescrita de histórico avançada.
# Scenario: feature branch was based on old-branch,
# but you want to rebase onto main instead
# git rebase --onto new-base old-base branch
git rebase --onto main old-branch feature
# This takes commits from old-branch..feature
# and replays them onto main
# Use case: remove commits from the middle
# Remove commit C from branch (replay A,B,D onto base)
# base -> A -> B -> C -> D
# git rebase --onto base C D
# Result: base -> A -> B -> D'
# Use case: move a branch to a different parent
git rebase --onto main feature/sub-feature feature/main-feature
# Moves main-feature's unique commits from sub-feature to mainCherry-pick & Bisect
git cherry-pick
cherry-pick aplica um commit específico de um branch em outro. Casos de uso: aplicar um bugfix a um branch de release, copiar um commit que você esqueceu de mergear, ou portar features seletivamente. O commit recebe um novo hash (pai diferente). Cherry-pick pode causar conflitos se o branch alvo divergiu. --no-commit stageia mudanças sem commitar (útil para combinar múltiplos cherry-picks). Evite cherry-picking excessivo — pode criar commits duplicados quando branches eventualmente mergeiam. Para backporting sistemático, use branches de release com merges.
# Apply a specific commit to current branch
git cherry-pick a1b2c3d
# Cherry-pick multiple commits
git cherry-pick a1b2c3d e4f5g6h i7j8k9l
# Cherry-pick a range (exclusive start, inclusive end)
git cherry-pick A..E # commits B, C, D, E
# Cherry-pick without committing (stage only)
git cherry-pick --no-commit a1b2c3d
# or: -n
# Cherry-pick and edit commit message
git cherry-pick --edit a1b2c3d
# Cherry-pick from another branch
git cherry-pick feature-branch~2
# If conflicts occur:
git add . && git cherry-pick --continue
git cherry-pick --abort # cancelgit bisect (Busca Binária)
git bisect realiza uma busca binária pelo histórico de commits para encontrar o commit exato que introduziu um bug. Você marca o estado atual como 'bad' e um commit conhecido como funcionando como 'good'. O Git faz checkout do ponto médio; você testa e marca good/bad. Cada etapa divide o espaço de busca pela metade — encontrar um bug em 1000 commits leva ~10 etapas. bisect reset retorna ao seu branch original. Isso é inestimável para rastrear regressões. Combine com git bisect log para salvar/restaurar sessões de bisect. O commit culpado frequentemente revela a causa raiz imediatamente.
# Find which commit introduced a bug
git bisect start
# Mark current (bad) commit
git bisect bad
# Mark a known-good commit (last working version)
git bisect good v1.0.0
# or: git bisect good a1b2c3d
# Git checks out a middle commit
# Test it, then mark:
git bisect good # if this commit works
git bisect bad # if this commit is broken
# Repeat until Git identifies the culprit:
# "a1b2c3d is the first bad commit"
# When done:
git bisect reset # return to original branch
# Skip a commit (can't test, e.g., doesn't build)
git bisect skipBisect Automatizado
Bisect automatizado roda um script de teste para cada commit, eliminando teste manual. O script sai com 0 (good), non-zero (bad), ou 125 (skip — ex.: falha de build). O Git automaticamente marca cada commit e encontra o culpado. Isso é extremamente poderoso com uma suíte de testes: git bisect run npm test encontra o commit que quebra em minutos. Você também pode restringir a busca a arquivos específicos (git bisect start -- path/to/file) para acelerar. O script pode ser qualquer coisa: um teste, uma verificação de build, ou um comando curl checando uma API. Salve o log do bisect para reproduzir ou resumir depois.
# Auto-bisect with a test script
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
# Git runs this script for each commit
# Exit 0 = good, exit 1 = bad, 125 = skip
git bisect run npm test
# Or a custom script
git bisect run ./scripts/check-bug.sh
# Example check script:
#!/bin/bash
npm run build || exit 125 # skip if build fails
npm test -- --grep "login bug"
# exit 0 if test passes (good), 1 if fails (bad)
# Git automatically finds the bad commit
# without manual testing
# Bisect with a file range (faster)
git bisect start -- src/auth/login.jsgit blame & annotate
git blame mostra o autor e o commit de cada linha de um arquivo — essencial para entender por que o código existe. -L restringe a um intervalo de linhas (mais rápido, mais focado). -w ignora mudanças apenas de whitespace (mostra o autor real do conteúdo). -M detecta código movido dentro do mesmo arquivo; -C detecta código copiado de outros arquivos (mostra o autor original, não o copiador). blame é para entender, não para culpar — use-o para encontrar contexto para o código, depois leia o commit completo com git show. O botão 'Blame' do GitHub fornece uma interface visual. Combine com git log -S para encontrar quando texto específico foi adicionado.
# Show who last modified each line
git blame file.txt
# Blame specific line range
git blame -L 10,20 file.txt
# Show full commit hash (not abbreviated)
git blame -l file.txt
# Show email instead of name
git blame -e file.txt
# Ignore whitespace changes
git blame -w file.txt
# Detect moved lines within file
git blame -M file.txt
# Detect moved lines from other files
git blame -C file.txt
# Blame with commit message
git blame --show-name --show-email file.txt | headgit revert (Desfazer Seguro)
git revert cria um novo commit que desfaz um commit anterior — é a maneira segura de desfazer mudanças em branches compartilhados (diferente do reset, que reescreve histórico). revert é ideal para branches de produção onde você não pode reescrever histórico. Reverter um merge commit requer -m 1 (mainline parent) — isso desfaz o merge enquanto mantém o histórico do branch. Reverter um revert reaplica a mudança original (comum quando um revert foi um engano). Para múltiplos commits, faça revert em ordem reversa (mais novo primeiro) para minimizar conflitos. Sempre use revert em branches compartilhados/públicos; use reset apenas em branches locais.
# Revert a commit (creates a NEW commit that undoes it)
git revert a1b2c3d
# Revert multiple commits
git revert a1b2c3d e4f5g6h
# Revert a range
git revert A..E
# Revert without committing (stage the reversal)
git revert --no-commit a1b2c3d
# Revert a merge commit
git revert -m 1 a1b2c3d
# -m 1 specifies the mainline parent (parent 1 = the branch
# you merged INTO, parent 2 = the branch you merged FROM)
# If revert conflicts:
git add . && git revert --continue
git revert --abortReflog & Recuperação
Básicos do git reflog
O reflog registra toda mudança no HEAD e nos ponteiros de branch — até operações que 'destroem' commits (reset --hard, rebase, deleção de branch). Esta é sua rede de segurança: commits 'perdidos' são recuperáveis via reflog por ~90 dias (padrão). reflog é local (nunca recebe push) e mostra o histórico cronológico de movimentos de ponteiros. Para recuperar, encontre o hash do commit no reflog e faça checkout/reset para ele. A sintaxe @{N} referencia entradas: HEAD@{0} é o atual, HEAD@{1} é o anterior. Se você acha que 'perdeu' trabalho, verifique o reflog primeiro — quase certamente ainda está lá.
# View the reflog (reference log)
git reflog
# Shows ALL reference changes, including:
# - commits, checkouts, resets, rebases, merges
# Example output:
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# e4f5g6h HEAD@{1}: commit: add feature
# i7j8k9l HEAD@{2}: checkout: moving from main to feature
# Reflog for a specific branch
git reflog show feature-branch
# Reflog with dates
git reflog --date=iso
# Recover a "lost" commit
git reflog
# Find the commit hash, then:
git checkout a1b2c3d # or: git reset --hard a1b2c3dRecuperando Branches Deletados
Branches deletados e commits resetados são recuperáveis via reflog. Os objetos de commit ainda existem no armazenamento de objetos do Git até o garbage collection (padrão: 90 dias para objetos inalcançáveis). Para recuperar um branch deletado, encontre seu commit tip no reflog e crie um novo branch apontando para ele. Para erros de reset --hard, o reflog mostra a posição anterior do HEAD — faça reset de volta para ela. Para rebases ruins, encontre a entrada do reflog antes de o rebase ter começado e faça reset para ela. A lição principal: no Git, quase nada é verdadeiramente perdido imediatamente. Sempre verifique o reflog antes de entrar em pânico.
# Accidentally deleted a branch?
git branch -D feature # force deleted
# Find it in reflog
git reflog
# e4f5g6h HEAD@{2}: commit: work on feature
# Recreate the branch at that commit
git branch feature e4f5g6h
# Or checkout and create
git checkout -b feature e4f5g6h
# Recover after reset --hard
git reset --hard HEAD~3 # oops, went too far
git reflog
# Find the commit before the reset
git reset --hard HEAD@{1} # undo the reset
# Recover after a bad rebase
git reflog
git reset --hard HEAD@{5} # before the rebase startedgit fsck (Objetos Dangling)
git fsck verifica a integridade do repositório e encontra objetos dangling — commits, blobs e trees não referenciados por nenhum branch ou tag. --lost-found grava esses em .git/lost-found/. Este é o último recurso quando o reflog não tem o que você precisa (entradas de reflog expiram, ou git gc rodou). Commits dangling são frequentemente resultado de operações abortadas ou entradas de reflog expiradas. Inspecione com git show, depois recupere criando um branch. fsck --full verifica a integridade de todos os objetos (útil para detectar corrupção). Execute fsck periodicamente em repos importantes para detectar problemas cedo.
# Find dangling (unreachable) commits
git fsck --lost-found
# Output:
# dangling commit a1b2c3d...
# dangling blob e4f5g6h...
# Inspect a dangling commit
git show a1b2c3d
# Recover it
git branch recovered a1b2c3d
# Full fsck (check repository integrity)
git fsck --full
# Check for dangling objects only
git fsck --no-reflogs --dangling
# When reflog doesn't have what you need
# (e.g., expired, or gc ran), fsck finds
# ALL dangling objects in the object storegit stash Aprofundado
git stash arquiva temporariamente mudanças não commitadas. push -m adiciona uma mensagem descritiva (essencial para gerenciar múltiplos stashes). -u inclui arquivos untracked; -a inclui arquivos ignorados também. apply reaplica sem remover; pop aplica e remove. stash branch cria um novo branch a partir do stash (útil se o stash conflita com o branch atual). Stashes são armazenados em uma pilha (LIFO) — referencie por stash@{N}. show -p exibe o diff. Stashes persistem entre reboots mas são locais (nunca recebem push). Limpe stashes antigos regularmente; eles se acumulam. Para trabalho de longo prazo, crie um branch em vez de stashing.
# Stash with message
git stash push -m "work in progress on login"
# Stash specific files
git stash push -m "wip" file1.txt file2.txt
# Stash including untracked files
git stash push -u # or --include-untracked
# Stash including ignored files
git stash push -a # or --all
# List stashes
git stash list
# stash@{0}: On feature: wip on login
# stash@{1}: On main: quick fix
# Apply a specific stash
git stash apply stash@{1}
# Apply and drop (pop)
git stash pop # applies stash@{0} and removes it
# Show stash contents
git stash show -p stash@{0}
# Create branch from stash
git stash branch new-branch stash@{0}
# Drop a stash
git stash drop stash@{1}
git stash clear # drop ALL stashesGerenciamento de git tag
Tags marcam commits específicos como importantes (releases, marcos). Tags anotadas (-a) armazenam metadados (tagger, data, mensagem) e são recomendadas para releases. Tags leves são apenas ponteiros nomeados (sem metadados). Tags assinadas (-s) usam GPG para verificação (importante para releases de segurança). Tags NÃO recebem push por padrão — use --tags para fazê-lo. Versionamento semântico (v1.2.3) é a convenção padrão de nomenclatura. Faça checkout de uma tag para um estado detached HEAD (para inspecionar ou buildar um release). Releases do GitHub são construídos sobre tags — crie uma tag, depois publique um release com notas.
# Create annotated tag (recommended)
git tag -a v1.0.0 -m "Release 1.0.0"
# Create lightweight tag
git tag v1.0.0
# Tag a specific commit
git tag -a v0.9.0 -m "beta" a1b2c3d
# List all tags
git tag
git tag -l "v1.*" # filter by pattern
# Push tags to remote
git push origin v1.0.0 # single tag
git push origin --tags # all tags
# Delete a tag
git tag -d v1.0.0 # local
git push origin --delete v1.0.0 # remote
# Show tag details
git show v1.0.0
# Checkout a tag (detached HEAD)
git checkout v1.0.0
# Signed tags (GPG)
git tag -s v1.0.0 -m "signed release"Worktree & Submodules
git worktree
git worktree cria diretórios de trabalho adicionais a partir do mesmo repositório — sem precisar clonar. Cada worktree faz checkout de um branch diferente simultaneamente. Isso é perfeito para: trabalhar em um hotfix enquanto mantém seu feature branch aberto, rodar testes em um branch enquanto codifica em outro, ou ter builds demorados em um worktree. Todos os worktrees compartilham o mesmo diretório .git (objetos, refs), então ficam sincronizados e economizam espaço em disco. Você não pode fazer checkout do mesmo branch em dois worktrees (o Git previne isso para evitar conflitos). Worktrees são mais rápidos que clonar para fluxos de trabalho multi-branch.
# Create a new working directory linked to the repo
git worktree add ../project-hotfix hotfix-branch
# Work in the new directory
cd ../project-hotfix
# This is a full working tree on hotfix-branch
# Shares the same .git, so no cloning needed
# List worktrees
git worktree list
# /path/to/project main
# /path/to/project-hotfix hotfix-branch
# Remove a worktree
git worktree remove ../project-hotfix
# Prune stale worktree entries
git worktree prune
# Create worktree at new branch
git worktree add -b feature-x ../project-feature mainBásicos de git submodule
Submodules incorporam um repositório Git dentro de outro — útil para incluir bibliotecas ou dependências compartilhadas. O repo pai armazena um ponteiro (hash de commit) para o submodule, não seu conteúdo. --recurse-submodules é essencial ao clonar (caso contrário, submodules ficam vazios). Atualizar submodules (--remote) busca os commits mais recentes; você deve então commitar o novo hash no repo pai. Submodules são complexos: branches, conflitos e atualizações exigem manuseio cuidadoso. Para gerenciamento de dependências mais simples, considere Git subtrees, package managers (npm, pip), ou estratégias de monorepo. Use submodules quando precisar rastrear um commit externo específico.
# Add a submodule to your repo
git submodule add https://github.com/user/lib.git libs/lib
# Clone a repo with submodules
git clone --recurse-submodules https://github.com/user/project.git
# If you forgot --recurse-submodules:
git submodule update --init --recursive
# Pull latest changes in all submodules
git submodule update --remote
# Pull specific submodule
git submodule update --remote libs/lib
# After updating submodules, commit the new hash
git add libs/lib
git commit -m "update lib submodule"
# Execute command in all submodules
git submodule foreach 'git status'Fluxos de Trabalho de Submodule
Trabalhar dentro de um submodule é como trabalhar em um repo normal — você commita e faz push de dentro do diretório do submodule. O repo pai rastreia o hash do commit, então após mudar um submodule, você deve commitar no pai também. Ao trocar de branch, o conteúdo do submodule pode não corresponder — execute git submodule update --init --recursive para sincronizar. Deletar submodules exige três etapas: deinit (desregistra), git rm (remove do tracking), e deleção manual de .git/modules. Fluxos de trabalho de submodule são propensos a erros; sempre comunique-se com sua equipe ao atualizar submodules para evitar incompatibilidades de hash.
# Change code inside a submodule
cd libs/lib
# Make changes, commit, and push
git add .
git commit -m "fix: bug in lib"
git push origin HEAD:main
# Back in parent repo, update the pointer
cd ../..
git add libs/lib
git commit -m "chore: update lib submodule"
# Switch branches with submodules
git checkout main
git submodule update --init --recursive
# Delete a submodule
git submodule deinit -f libs/lib
git rm libs/lib
rm -rf .git/modules/libs/lib
git commit -m "remove lib submodule"
# Move a submodule
git mv libs/lib libs/new-libGit Hooks
Git hooks rodam scripts em pontos específicos do ciclo de vida do Git. Hooks do lado cliente (pre-commit, pre-push, commit-msg) impõem padrões locais. pre-commit é ideal para linting/formatting; pre-push para rodar testes; commit-msg para impor conventional commits. Hooks NÃO são rastreados pelo Git (eles vivem em .git/hooks/), então não sincronizam entre clones. Para compartilhar hooks entre a equipe, use uma ferramenta como Husky (npm), pre-commit (Python), ou commite os hooks em um diretório versionado e crie symlinks. Hooks do lado servidor (pre-receive, post-receive) rodam no remote e podem impor políticas para todos os contribuidores.
# Hooks live in .git/hooks/ (not tracked by Git)
# Sample hooks are provided with .sample extension
# pre-commit: runs before commit is created
# .git/hooks/pre-commit
#!/bin/bash
npm run lint || exit 1 # block commit if lint fails
# pre-push: runs before pushing
#!/bin/bash
npm test || exit 1 # block push if tests fail
# commit-msg: validate commit message format
#!/bin/bash
# $1 is the path to the commit message file
if ! grep -qE "^(feat|fix|docs|chore):" "$1"; then
echo "Use conventional commit format: type: message"
exit 1
fi
# prepare-commit-msg: auto-populate message
#!/bin/bash
echo "# Branch: $(git branch --show-current)" >> "$1"
# Make hooks executable
chmod +x .git/hooks/pre-commitGit LFS (Large File Storage)
Git LFS substitui arquivos grandes (binários, vídeos, datasets) por ponteiros de texto no Git, armazenando o conteúdo real em um servidor LFS separado. Isso mantém o repo leve — sem LFS, arquivos binários incham o repo permanentemente (cada versão é armazenada). Rastreie padrões de arquivo com git lfs track, depois commite .gitattributes. Depois disso, arquivos grandes funcionam de forma transparente. git lfs migrate import converte retroativamente arquivos existentes para LFS (reescreve histórico — coordene com a equipe primeiro). LFS exige suporte do servidor (GitHub, GitLab, Bitbucket todos suportam). Nota: LFS tem cotas de bandwidth/armazenamento em plataformas hospedadas. Para arquivos verdadeiramente enormes, considere armazenamento externo com URLs.
# Initialize Git LFS
git lfs install
# Track large file types
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/**"
# This creates/updates .gitattributes
# Commit the .gitattributes file
git add .gitattributes
git commit -m "chore: configure LFS tracking"
# Add large files normally
git add design.psd
git commit -m "add design file"
git push
# View LFS tracked files
git lfs ls-files
# Pull LFS content (if not auto-downloaded)
git lfs pull
# Migrate existing files to LFS (rewrites history)
git lfs migrate import --include="*.psd" --everything
# Check LFS status
git lfs statusStash
Save & Pop
git stash arquiva mudanças não commitadas para que você possa trocar de branch ou puxar atualizações com uma árvore de trabalho limpa. pop aplica e remove o stash do topo; apply o mantém. O stash é uma pilha LIFO.
# Save uncommitted changes (tracked files only)
git stash
# Save including untracked files
git stash -u
# Pop the most recent stash (removes it)
git stash pop
# Apply without removing (keeps stash in list)
git stash applyStashes Nomeados
Sempre passe -m para rotular stashes — a mensagem padrão é o branch e o commit, que raramente é descritivo. stash@{N} referencia um stash específico pelo seu índice.
# Save with a descriptive message
git stash push -m "WIP: refactor auth flow"
# List all stashes with messages
git stash list
# Apply a specific stash by index
git stash apply stash@{1}Stash Branches
git stash branch cria um novo branch a partir do commit onde o stash foi originalmente feito, depois aplica o stash lá. Esta é a maneira mais limpa de recuperar quando um stash não se aplica mais de forma limpa.
# Create a new branch from a stash
git stash branch feature-branch stash@{0}
# Useful when stash conflicts with current branch
# - Creates branch from the commit where stash was made
# - Applies stash on top
# - Drops stash if apply succeedsStash Parcial
Stashe apenas o que você precisa com -p (seleção interativa de hunk) ou listando arquivos específicos. --keep-index stashe mudanças unstaged mas deixa mudanças staged no lugar.
# Interactively choose hunks to stash
git stash push -p
# Stash specific files only
git stash push -m "config tweaks" config.yml .env.local
# Stash with keep-index (staged changes stay)
git stash --keep-indexGerenciamento de Stash
git stash show -p exibe o diff completo de um stash. drop remove um único stash; clear apaga todos (irreversível). Stashes são apenas locais; nunca recebem push para remotes.
# Show what's in a stash
git stash show -p stash@{0}
# Drop a specific stash
git stash drop stash@{0}
# Clear all stashes (irreversible)
git stash clear
# Show stash list with dates
git stash list --date=relativeRebase
Rebase Básico
Rebase move os commits do seu branch sobre outro branch, produzindo um histórico linear. Diferente do merge, ele reescreve hashes de commit. Nunca faça rebase de commits que já receberam push e foram compartilhados.
# Rebase current branch onto main
git checkout feature
git rebase main
# Abort if conflicts are too messy
git rebase --abort
# Continue after resolving conflicts
git rebase --continueRebase Interativo
Rebase interativo (-i) permite reescrever o histórico antes de compartilhar: reordenar commits, squashar relacionados em um único commit limpo, reword mensagens, ou drop erros. Sempre faça isso em commits não enviados por push.
# Rebase last 5 commits interactively
git rebase -i HEAD~5
# Commands: pick, reword, edit, squash, fixup, drop
# squash = combine with previous, keep message
# fixup = combine with previous, discard message
# reword = change commit message onlySquash & Fixup
--fixup cria um commit marcado como correção para outro. --autosquash durante o rebase automaticamente posiciona commits fixup! e squash! ao lado de seus alvos. Isso simplifica o fluxo de trabalho 'commit cedo, limpe depois'.
# Create a fixup commit targeting an earlier commit
git commit --fixup a1b2c3
# Autosquash during rebase (auto-reorders fixups)
git rebase -i --autosquash HEAD~5Rebase --onto
--onto é rebase cirúrgico: move um intervalo de commits de uma base para outra. Use para re-paiar um branch, ou para descartar os primeiros commits de um branch.
# Move commits from one base to another
# git rebase --onto <new-base> <old-base> <branch>
git rebase --onto main feature-old feature-new
# Drop the first N commits of a branch
git rebase --onto main HEAD~3Conflitos de Rebase
Durante o rebase, conflitos param em cada commit. Resolva, git add, depois --continue para prosseguir. --skip descarta o commit conflitante inteiramente. --abort retorna ao estado pré-rebase.
# During rebase, conflicts pause the process
git rebase main
# CONFLICT (content): Merge conflict in file.js
# Resolve in editor, then:
git add file.js
git rebase --continue
# Skip a commit that conflicts (drop it)
git rebase --skip
# Give up entirely
git rebase --abortCherry-Pick
Cherry-Pick de um Commit
cherry-pick aplica um commit específico de outro branch sobre seu branch atual, criando um novo commit com as mesmas mudanças. O novo commit tem um hash diferente porque o pai é diferente.
# Apply a specific commit to current branch
git cherry-pick a1b2c3d
# Cherry-pick multiple commits
git cherry-pick a1b2c3d e4f5g6h
# Cherry-pick a range (exclusive of start, inclusive of end)
git cherry-pick A..ECherry-Pick sem Commit
--no-commit (-n) stageia as mudanças do cherry-pick sem criar um commit. Isso permite combinar múltiplos cherry-picks em um commit, ou modificar as mudanças antes de commitar.
# Apply changes to working tree without committing
git cherry-pick --no-commit a1b2c3d
# Stage the changes yourself
git add -p
git commit -m "Custom message"Conflitos de Cherry-Pick
Conflitos durante cherry-pick pausam a operação. Resolva, git add, e --continue. --skip abandona o commit atual. --abort cancela o cherry-pick e restaura o branch.
git cherry-pick a1b2c3d
# error: could not apply a1b2c3d...
# Resolve conflicts, then:
git add resolved-file.js
git cherry-pick --continue
# Skip this commit
git cherry-pick --skip
# Abort the entire cherry-pick
git cherry-pick --abortCherry-Pick de Outro Branch
O fluxo clássico de hotfix: corrija um bug em um branch de manutenção, depois cherry-pick o mesmo commit para main (e outros branches ativos). Isso evita mergear trabalho de feature não relacionado.
# Find the commit hash on another branch
git log --oneline feature-branch
# Switch to target branch
git checkout main
# Cherry-pick the commit
git cherry-pick <hash>
# Useful for hotfixes: fix on a feature branch,
# then cherry-pick the fix to mainEstratégia de Cherry-Pick
-X theirs/ours tende a resolução de conflitos. -x adiciona uma linha registrando o hash do commit original — essencial para trilhas de auditoria ao cherry-pickar hotfixes entre branches.
# Use a different merge strategy
git cherry-pick -X theirs a1b2c3d # prefer their changes on conflict
git cherry-pick -X ours a1b2c3d # prefer our changes on conflict
# Preserve original commit author
git cherry-pick -x a1b2c3d # adds "(cherry picked from ...)" to message
# Edit commit message before committing
git cherry-pick -e a1b2c3dBisect
Bisect Básico
git bisect realiza uma busca binária pelo histórico de commits para encontrar qual commit introduziu um bug. Você marca o commit atual como bad e um commit conhecido como bom como good. Após ~log2(N) etapas, o git nomeia o commit ofensor.
# Start bisecting
git bisect start
# Mark current commit as bad (has the bug)
git bisect bad
# Mark a known-good commit (older)
git bisect good v1.0.0
# Git checks out the midpoint. Test, then mark:
git bisect good # or
git bisect bad
# Exit bisect mode
git bisect resetBisect Log & Replay
git bisect log registra toda decisão good/bad. Se você marcar um commit errado (um erro comum), faça reset e replay o log, depois corrija a etapa errada.
# Record what happens during bisect
git bisect log > bisect.log
# If you make a mistake, replay from the log
git bisect reset
git bisect replay bisect.log
# Visualize the bisect state
git bisect visualizeBisect Automatizado
git bisect run automatiza a busca: o git faz checkout de cada candidato, roda seu script, e marca o commit com base no exit code. Isso é dramaticamente mais rápido que teste manual.
# Auto-bisect: git runs a test script and marks good/bad itself
git bisect start HEAD v1.0.0 -- # bad=HEAD, good=v1.0.0
git bisect run npm test # exit 0=good, 1-124=bad, 125=skip
# Or with a custom script
git bisect run ./scripts/check-bug.shBisect por Arquivo
Passar um path para git bisect start restringe a busca a commits que modificaram aquele arquivo. Isso pula centenas de commits irrelevantes e foca no arquivo onde o bug provavelmente está.
# Limit bisect to changes in a specific file
git bisect start -- path/to/file.js
# Combine with bad/good markers
git bisect bad HEAD
git bisect good v1.0.0
# Git only considers commits that touched that fileBisect Reset
Sempre execute git bisect reset quando terminar — ele retorna você ao seu branch original e limpa o estado do bisect. Sem reset, sua árvore de trabalho permanece no último commit testado.
# Exit bisect mode and return to original branch
git bisect reset
# Reset to a specific branch/commit instead
git bisect reset <branch>
# View current bisect state
git bisect statusSubmodules
Adicionar um Submodule
Submodules incorporam um repositório Git dentro de outro — útil para vendorizar bibliotecas compartilhadas. git submodule add registra o submodule em .gitmodules. Novos clones precisam de --recurse-submodules.
# Add a submodule at a specific path
git submodule add https://github.com/user/lib.git libs/lib
# This creates:
# - libs/lib/ (the submodule checkout)
# - .gitmodules (tracks submodule URLs and paths)
# Commit the new submodule
git commit -m "Add lib submodule"
# Clone a repo with submodules included
git clone --recurse-submodules https://github.com/user/repo.gitAtualizar Submodules
Um submodule é fixado em um commit específico. git submodule update --remote busca o commit mais recente no branch rastreado e atualiza o ponteiro. Você deve commitar essa mudança de ponteiro no repo pai.
# Pull latest changes in all submodules
git submodule update --remote
# Update a specific submodule
git submodule update --remote libs/lib
# Then commit the updated pointer
git add libs/lib
git commit -m "Bump lib submodule"
# Initialize submodules after cloning
git submodule update --init --recursiveSubmodule Foreach
foreach roda um comando shell em cada diretório de submodule — útil para operações em massa como checar status, puxar atualizações, ou buildar. --recursive desce em submodules aninhados.
# Run a command in every submodule
git submodule foreach 'git status'
# With recursion into nested submodules
git submodule foreach --recursive 'git checkout main'
# Use --quiet to suppress the "Entering..." messages
git submodule foreach --quiet 'git pull'Deinit & Remove
Remover um submodule é um processo de múltiplas etapas: deinit o desregistra, rm -rf .git/modules/... deleta os dados Git do submodule, e git rm remove a árvore de trabalho. Esquecer a limpeza de .git/modules deixa dados órfãos.
# Deinit a submodule (unregisters, keeps files)
git submodule deinit libs/lib
# Fully remove a submodule
git submodule deinit -f libs/lib
rm -rf .git/modules/libs/lib
git rm -f libs/lib
git commit -m "Remove lib submodule"Branches de Submodule
Por padrão, submodules estão em detached HEAD. Definir submodule.<name>.branch permite que --remote rastreie aquele branch. Para fazer mudanças dentro de um submodule, faça cd, checkout um branch, commit, e push.
# Configure a submodule to track a branch
git config -f .gitmodules submodule.libs/lib.branch main
# Now --remote updates from that branch
git submodule update --remote
# Work inside a submodule like a normal repo
cd libs/lib
git checkout feature-branch
git push
cd ../..
git add libs/lib
git commit -m "Update lib to feature-branch"Hooks
Hooks Comuns
Git hooks são scripts em .git/hooks/ que rodam automaticamente em pontos específicos. Hooks do lado cliente rodam na sua máquina e podem bloquear ações. Hooks do lado servidor rodam no remote e impõem política. Hooks não são versionados por padrão — use Husky para compartilhá-los.
# Client-side (run on your machine):
# pre-commit - runs before commit is created
# prepare-commit-msg - can edit commit message
# commit-msg - validates commit message
# pre-push - runs before pushing
# post-merge - runs after git merge
# Server-side (run on the remote):
# pre-receive - runs before refs are updated
# update - runs once per ref
# post-receive - runs after refs are updatedHook pre-commit
pre-commit roda antes de o commit ser criado; um exit non-zero aborta o commit. Usos comuns: lint arquivos staged, rodar testes focados, formatar código. Mantenha-o rápido (<5 segundos) ou desenvolvedores o ignorarão com --no-verify.
#!/bin/sh
# .git/hooks/pre-commit
# Run linter on staged files
npm run lint-staged
if [ $? -ne 0 ]; then
echo "Linting failed. Fix errors and try again."
exit 1
fi
# Run tests
npm test -- --findRelatedTests
if [ $? -ne 0 ]; then
echo "Tests failed."
exit 1
fi
exit 0Hook commit-msg
commit-msg recebe o path para o arquivo temporário de mensagem de commit como $1. Ele pode validar ou reescrever a mensagem. Um exit non-zero rejeita o commit. Esta é a maneira padrão de impor Conventional Commits.
#!/bin/sh
# .git/hooks/commit-msg
# Enforce Conventional Commits format
pattern='^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\(.*\))?: .{1,72}$'
if ! grep -qE "$pattern" "$1"; then
echo "Invalid commit message format."
echo "Use: <type>(<scope>): <description>"
exit 1
fiSetup do Husky
Husky instala Git hooks de um diretório versionado .husky/, então todo membro da equipe obtém os mesmos hooks após npm install. lint-staged roda comandos apenas em arquivos staged, mantendo o pre-commit rápido.
# Install Husky
npm install --save-dev husky
npx husky init
# Add a pre-commit hook
echo 'npm run lint-staged' > .husky/pre-commit
# Add a commit-msg hook
echo 'npx --no-install commitlint --edit $1' > .husky/commit-msg
# package.json
{
"scripts": { "prepare": "husky" },
"lint-staged": {
"*.{js,ts}": ["eslint --fix", "prettier --write"]
}
}Hook pre-push
pre-push roda antes de as refs serem enviadas por push; exit non-zero aborta. Ele lê os pushes propostos do stdin. Usos comuns: bloquear pushes para branches protegidos, rodar a suíte de testes completa.
#!/bin/sh
# .husky/pre-push
# Reads stdin: <local ref> <local sha> <remote ref> <remote sha>
while read local_ref local_sha remote_ref remote_sha; do
if [ "$remote_ref" = "refs/heads/main" ]; then
echo "Direct push to main is not allowed."
exit 1
fi
done
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Push aborted."
exit 1
fiWorktrees
Adicionar um Worktree
Um worktree é um diretório de trabalho separado vinculado ao mesmo repositório. Você pode ter múltiplos branches em checkout simultaneamente em diretórios diferentes — sem stashing para trocar de contexto.
# Create a worktree at a path, on a new branch
git worktree add ../project-feature feature-branch
# Worktree on an existing branch
git worktree add ../project-hotfix hotfix-branch
# Worktree at a specific commit (detached HEAD)
git worktree add --detach ../project-inspect v1.0.0
# List all worktrees
git worktree listFluxo de Trabalho de Worktree
Worktrees brilham para troca de contexto: um bug urgente chega enquanto você está imerso em uma feature. Em vez de stashing e perder seu estado do IDE, crie um worktree em main, corrija o bug lá, e retorne.
# Scenario: working on feature, urgent bug comes in
git worktree add ../project-hotfix main
cd ../project-hotfix
git checkout -b fix-urgent
# ... fix the bug, commit, push ...
cd ../project-feature
# Your feature work is untouched
# Clean up when done
git worktree remove ../project-hotfixRemove & Prune
remove deleta um diretório worktree e seus metadados administrativos. O branch em que ele estava permanece. Se o worktree tem mudanças não commitadas, remove recusa a menos que você passe --force.
# Remove a worktree (must be clean or use --force)
git worktree remove ../project-feature
# Force remove even with uncommitted changes
git worktree remove --force ../project-feature
# Prune worktree admin files for deleted directories
git worktree prune
# Show what would be pruned
git worktree prune --dry-run -vBenefícios do Worktree
Worktrees resolvem vários pontos de dor: sem stashing para trocas de contexto, builds/testes paralelos, node_modules isolados por branch, e comparação lado a lado de branches. O banco de dados de objetos compartilhado significa overhead mínimo de disco.
# 1. No stashing needed for context switches
# 2. Run long builds/tests in one worktree while
# continuing work in another
# 3. Each worktree has its own node_modules
# 4. Compare two branches side-by-side in two
# editor windows
# 5. Shared object database = minimal disk overhead
# Typical layout:
# ~/projects/
# main-repo/ (main branch)
# main-repo-feature/ (feature worktree)
# main-repo-hotfix/ (hotfix worktree)Worktrees Bloqueados
Bloqueie um worktree quando ele contém trabalho que não deve ser perturbado (builds demorados, debugger anexado). Worktrees bloqueados sobrevivem ao prune. move realoca um worktree; repair corrige os arquivos administrativos.
# Lock a worktree (prevents it from being pruned)
git worktree lock ../project-feature --reason "Running long build"
# Unlock when done
git worktree unlock ../project-feature
# Move a worktree to a new path
git worktree move ../project-feature ../new-location/project-feature
# Repair after moving manually
git worktree repair ../new-location/project-featureReflog
Ver Reflog
O reflog registra toda mudança no HEAD e nas pontas de branch — commits, checkouts, resets, rebases. É uma rede de segurança local: mesmo após uma operação destrutiva, os commits ainda estão no reflog.
# Show reflog for HEAD
git reflog
# a1b2c3d HEAD@{0}: commit: Fix bug
# e4f5g6h HEAD@{1}: checkout: moving to feature
# 789abc0 HEAD@{2}: reset: moving to HEAD~1
# Show reflog for a specific branch
git reflog show feature
# Show reflog with dates
git reflog --date=isoRecuperar Commits Perdidos
Após um hard reset ou rebase, commits 'perdidos' ainda são alcançáveis via reflog. Encontre o hash em git reflog, depois reset --hard ou cherry-pick para recuperar. É por isso que o Git é seguro — quase nada é verdadeiramente perdido.
# Find the commit you lost (e.g., after a hard reset)
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# e4f5g6h HEAD@{1}: commit: Important work <-- this one
# Reset back to the lost commit
git reset --hard e4f5g6h
# Or cherry-pick it onto your current branch
git cherry-pick e4f5g6hReflog & Reset
ORIG_HEAD é uma ref de conveniência que aponta para o HEAD anterior após operações destrutivas (reset, merge, rebase). git reset --hard ORIG_HEAD desfaz a última operação desse tipo em um comando.
# Undo a git reset --hard
git reflog
git reset --hard HEAD@{1}
# Undo a rebase
git reflog
git reset --hard HEAD@{5}
# Undo a merge
git reset --hard ORIG_HEAD
# ORIG_HEAD is set by merge, rebase, reset, and cherry-pickExpirar & Limpar
Entradas de reflog se acumulam e consomem espaço em disco. expire remove entradas antigas; --expire-unreachable tem como alvo apenas entradas não alcançáveis a partir de nenhuma ref. Após expirar, execute git gc --prune=now para deletar objetos inalcançáveis.
# Expire reflog entries older than 30 days
git reflog expire --expire=30.days
# Expire entries unreachable from current branches
git reflog expire --expire-unreachable=30.days
# Delete a specific reflog entry
git reflog delete HEAD@{2}
# Dry run (see what would be expired)
git reflog expire --dry-run --expire=now --allReflog para Branches
Cada branch mantém seu próprio reflog. Se você deletar um branch com git branch -D, os commits ainda estão no reflog — encontre o hash e recrie o branch com git branch <name> <hash>.
# Each branch has its own reflog
git reflog show main
# a1b2c3d main@{0}: commit: Update docs
# e4f5g6h main@{1}: pull origin main: Fast-forward
# Recover a deleted branch
git reflog # find the hash the branch pointed to
git branch recovered-branch e4f5g6h
# Compare current state with a past reflog entry
git diff HEAD@{1} HEADLFS
Instalar & Rastrear
Git LFS armazena arquivos grandes (imagens, vídeos, binários) fora do repositório Git, substituindo-os por arquivos ponteiro nos commits. git lfs track registra padrões em .gitattributes. Sempre commite .gitattributes antes de adicionar arquivos grandes.
# Install Git LFS (once per user)
git lfs install
# Track large file types
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/*"
# View tracking rules
cat .gitattributes
# Commit the .gitattributes file
git add .gitattributes
git commit -m "Configure LFS tracking"Adicionar & Commitar Arquivos LFS
Uma vez que um padrão de arquivo é rastreado, git add stageia o arquivo através do LFS automaticamente. O commit armazena um pequeno arquivo ponteiro (~130 bytes) em vez do binário. O conteúdo real é enviado ao servidor LFS no push.
# After configuring tracking, add files normally
git add design.psd
git commit -m "Add design file"
# Verify LFS is handling the file
git lfs ls-files
# a1b2c3d4 * design.psd
# The commit contains a pointer, not the actual file
git show HEAD -- design.psd
# version https://git-lfs.github.com/spec/v1
# oid sha256:...
# size 12345678Clonar & Pull
Clonar um repo com LFS baixa arquivos ponteiro primeiro, depois o conteúdo real. Se o download do LFS falhar, git lfs pull tenta novamente. git lfs fetch baixa objetos sem gravá-los na árvore de trabalho.
# Clone a repo with LFS files (downloads them automatically)
git clone https://github.com/user/repo.git
# If LFS files are missing, fetch them
git lfs pull
# Fetch LFS objects without checking them out
git lfs fetch
# Checkout LFS files for specific paths
git lfs checkout assets/Migrar para LFS
git lfs migrate import converte retroativamente arquivos grandes no histórico para ponteiros LFS. Isso reescreve todos os hashes de commit — todo colaborador deve re-clonar. Execute --dry-run primeiro para ver o impacto.
# Convert existing large files to LFS (rewrites history!)
git lfs migrate import --include="*.psd" --include-ref=refs/heads/main
# Migrate all branches
git lfs migrate import --include="*.psd" --everything
# Check what would be migrated (dry run)
git lfs migrate import --include="*.psd" --include-ref=refs/heads/main --dry-runGerenciamento de LFS
git lfs status mostra mudanças LFS pendentes. git lfs env exibe a configuração. git lfs fsck verifica se todos os objetos LFS referenciados por arquivos ponteiro estão presentes e sem corrupção.
# View LFS status
git lfs status
# List all LFS files in the repo
git lfs ls-files
# Check LFS configuration
git lfs env
# Untrack a file type (does not remove existing LFS objects)
git lfs untrack "*.zip"
# Verify LFS objects are present and valid
git lfs fsckIntegração CI/CD
GitHub Actions Básico
GitHub Actions roda workflows em push/PR. actions/checkout busca o repo; fetch-depth: 0 obtém o histórico completo. npm ci instala a partir do lockfile (mais rápido, mais rigoroso que install). Armazene dependências em cache com a chave de cache.
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test
- run: npm run buildWorkflows Condicionais
Filtre workflows por branch ou evento com on: push: branches. Use condições if: no nível de job para pular jobs com base no contexto. needs: cria dependências entre jobs — deploy só roda se test passar.
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- run: ./deploy.shGit Hooks em CI
Pipelines de CI frequentemente espelham hooks locais: lint primeiro (fail rápido), depois testes. needs: lint garante que testes só rodem se o linting passar, economizando minutos de CI. Dividir jobs permite runners paralelos para velocidade.
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- run: npm run lint
- run: npm run typecheck
test:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test -- --coverageSecrets & Environment
Armazene secrets (API keys, tokens) nas configurações do repo e referencie-os via ${{ secrets.NAME }}. Eles nunca são exibidos em logs. Environments podem exigir aprovação manual antes do deployment, adicionando um gate.
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # requires manual approval
steps:
- uses: actions/checkout@v4
- name: Deploy
env:
API_KEY: ${{ secrets.API_KEY }}
DB_URL: ${{ secrets.DATABASE_URL }}
run: ./scripts/deploy.sh "$API_KEY" "$DB_URL"Matrix Builds
Matrix builds rodam o mesmo job em múltiplas combinações de SO/linguagem em paralelo. Isso detecta bugs específicos de plataforma cedo. Use fail-fast: false para rodar todas as combinações mesmo se uma falhar. Mantenha matrizes razoáveis para controlar minutos de CI.
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: ['18', '20', '22']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm testSnippets de Git relacionados
Copy-paste ready code for common tasks.
Was this helpful?