Branches e equipe
sem atrito.
Domine o recurso que diferencia o Git de todos os outros sistemas: crie ramificações em milissegundos, resolva conflitos de merge sem adivinhação, aplique rebase com segurança e coordene fluxos distribuídos e Pull Requests.
Trilha de formação: Domínio do Git do básico à arquitetura interna
Você concluiu os fundamentos locais e agora está na Etapa 2: Trabalho em Equipe e Fluxos, cobrindo integralmente os Capítulos 3, 5 e 6 do livro.
Percurso da Etapa 2 (Capítulos 3, 5 e 6)
Crie um repositório de laboratório (ou dois clones locais simulando dois desenvolvedores) para experimentar na prática cada comando de ramificação, mesclagem e revisão.
Entenda por que uma branch no Git é apenas um ponteiro de 41 bytes e como o HEAD sabe onde você está.
OFICINA 02Merge e conflitos reais ↗Compare Fast-Forward com 3-Way Merge, resolva conflitos linha a linha e gerencie branches com --merged.
OFICINA 03Tracking branches ↗Domine origin/branch, configure upstream com -u, audite atrasos com git branch -vv e limpe branches remotas.
OFICINA 04Rebase com critério ↗Reaplique commits para manter o histórico linear, use rebase --onto e nunca viole a Regra de Ouro do Rebase.
OFICINA 05Fluxos distribuídos ↗Compare arquiteturas de equipe, valide espaços com git diff --check e simule o custo de branches longas.
OFICINA 06Manter e integrar código ↗Inspecione branches com diff de três pontos (...), aplique cherry-pick, ative o rerere e empacote releases.
OFICINA 07GitHub Flow e Forks ↗Configure SSH, conduza Pull Requests iterativos, sincronize com upstream e escreva em Markdown técnico.
OFICINA 08Refs ocultas, API e equipes ↗Baixe Pull Requests via refs/pull/, estruture o CONTRIBUTING.md e automatize integrações com Webhooks e API.
“As ramificações do Git mudam a maneira como você desenvolve: criar, alternar e mesclar branches várias vezes por dia deixa de ser um risco e vira rotina.”
No Capítulo 3 de Pro Git, Scott Chacon e Ben Straub chamam o modelo de branches de “killer feature” do Git — justamente porque ele trabalha com ponteiros instantâneos em vez de copiar pastas inteiras.Entenda o que uma branch realmente é: blobs, árvores, commits e HEAD
Em sistemas antigos, criar uma branch de um projeto grande exigia copiar todos os arquivos para um novo diretório no servidor, o que podia levar vários minutos. Por que o Git cria uma branch de um projeto com cem mil arquivos em menos de um milissegundo?
A anatomia interna de um commit e o ponteiro de 41 bytes
Na Seção 3.1, Chacon e Straub abrem o modelo de dados do Git para explicar o segredo da velocidade das branches. Quando você executa git commit em um repositório com três arquivos, o Git grava no banco de dados um objeto blob para o conteúdo de cada arquivo, um objeto tree (árvore) que lista os nomes dos arquivos apontando para seus respectivos blobs, e um objeto commit que contém o nome do autor, a mensagem, o ponteiro para essa árvore raiz e o ponteiro para o commit imediatamente anterior (o parent ou pai). O primeiro commit da história tem zero pais; um commit normal tem um pai; e um commit de merge tem dois ou mais pais.
Com essa estrutura pronta, uma branch no Git é simplesmente um ponteiro leve e móvel para um desses commits — na prática, um pequeno arquivo dentro de .git/refs/heads/ contendo os 40 caracteres do hash SHA-1 do commit apontado mais uma quebra de linha (41 bytes no total!). Criar uma branch nova com git branch testing não copia nenhum arquivo do projeto: apenas grava um novo ponteiro de 41 bytes apontando para o commit onde você já está.
E como o Git sabe em qual branch você está trabalhando quando existem duas apontando para o mesmo commit? Ele mantém um ponteiro especial chamado HEAD. Quando você roda git branch testing, a branch é criada, mas o HEAD continua apontando para main. É somente quando você executa git checkout testing (ou o comando moderno git switch testing) que o HEAD passa a apontar para testing — e cada novo commit avança apenas a branch para a qual o HEAD aponta.
| Comando executado | Ponteiro da branch | Ponteiro HEAD | Arquivos na Working Tree |
|---|---|---|---|
git branch testing | Cria o ponteiro testing no commit atual | Continua apontando para a branch onde você já estava | Nenhuma alteração nos arquivos locais. |
git checkout testing(ou git switch testing) | Mantém testing onde está | Move HEAD para apontar para testing | Substitui os arquivos do diretório para refletir o snapshot de testing. |
git checkout -b iss53(ou git switch -c iss53) | Cria o ponteiro iss53 no commit atual | Move HEAD imediatamente para iss53 | Mantém o snapshot atual pronto para receber novos commits isolados. |
# 1. Crie um repositório de teste para observar o ponteiro HEAD e a divergência de branches: mkdir -p ~/lab-git-etapa2 && cd ~/lab-git-etapa2 git init -b main echo "versao base" > sistema.txt git add sistema.txt && git commit -m "Commit inicial (f30ab)" # 2. Crie a branch "testing", veja onde HEAD aponta com --decorate e troque para ela: git branch testing git log --oneline --decorate git checkout testing echo "mudanca na branch testing" >> sistema.txt git commit -a -m "Adiciona experimento na testing" # 3. Volte para a main, faça outra alteração e veja a árvore bifurcada com --all --graph: git checkout main echo "mudanca paralela na main" >> sistema.txt git commit -a -m "Segue evolucao na main" git log --oneline --decorate --graph --all
Por que git log parece esconder outra branch quando você volta para a main?
Como lembra a nota de destaque da Seção 3.1, o comando git log sem opções adicionais mostra apenas os commits ancestrais a partir do ponto onde o seu HEAD está agora. Quando você faz checkout de volta para main, se a branch testing estiver um commit à frente, ela não aparecerá no git log simples — mas não foi perdida! Basta passar git log --oneline --decorate --graph --all para visualizar todas as branches do repositório.
Entrega da etapa
Reproduza o experimento de bifurcação acima na sua máquina e inspecione o conteúdo do arquivo .git/HEAD e da pasta .git/refs/heads/ com o comando cat para comprovar que uma branch é literalmente um arquivo de 41 bytes guardando um hash SHA-1.
Conexão com o livro · Capítulo 3 (Seção 3.1) · objetos blob/tree/commit, ponteiros de branch, HEAD, git branch, git checkout (-b) / git switch (-c) e git log --decorate --graph --all.
Domine Fast-Forward, 3-Way Merge, conflitos e gestão de branches
Você está no meio do desenvolvimento do chamado #53 quando o telefone toca: há um erro crítico em produção que precisa ser corrigido em dez minutos. Como voltar à versão estável, publicar o reparo urgente e retomar o chamado #53 sem misturar os códigos?
A diferença entre Fast-Forward Merge e 3-Way Merge
Na Seção 3.2, os autores percorrem o cenário clássico que todo profissional vive na prática. Você está na branch iss53 com seus commits já salvos (ou guardados no stash, que veremos na Etapa 3) e faz checkout de volta para a branch de produção main. A partir dela, cria uma branch hotfix (`git checkout -b hotfix`), corrige o bug, faz o commit, volta para `main` e executa `git merge hotfix`.
Nesse primeiro merge, você verá a mensagem Fast-forward no terminal. Por quê? Porque o commit para onde `hotfix` aponta estava diretamente à frente do commit da `main`, sem que a `main` tivesse recebido outros commits paralelos nesse intervalo. O Git não precisa criar um commit novo de mesclagem: ele apenas desliza o ponteiro `main` para a frente!
Já quando você apaga a `hotfix` (`git branch -d hotfix`), volta para `iss53`, termina o trabalho e finalmente roda `git merge iss53` a partir da `main`, a situação é diferente: a `main` avançou com o `hotfix` e a `iss53` avançou por outro caminho. Como não há linha reta entre elas, o Git realiza um Three-Way Merge (mesclagem de três pontas) usando os dois snapshots das pontas das branches mais o snapshot do ancestral comum entre elas, criando automaticamente um novo merge commit com dois pais.
| Tipo de Merge | Quando acontece | O que o Git faz no histórico |
|---|---|---|
| Fast-Forward | A branch atual não recebeu nenhum commit desde que a outra branch nasceu dela. | Apenas avança o ponteiro da branch atual para o mesmo commit; não cria commit extra. |
| 3-Way Merge limpo | As duas branches divergiram, mas alteraram arquivos ou regiões diferentes. | Une os três snapshots (ancestral comum + duas pontas) e cria um novo commit de merge com 2 pais. |
| Conflito de Merge | As duas branches alteraram a mesma parte do mesmo arquivo de modos diferentes. | Pausa o merge, marca o arquivo como Unmerged e insere marcadores visuais para decisão humana. |
Como resolver um conflito de merge e limpar branches antigas
Quando ocorre um conflito (`CONFLICT (content): Merge conflict`), não há motivo para pânico: o Git ainda não criou o commit de merge e deixa claro no `git status` quais arquivos estão sob Unmerged paths. Dentro do arquivo conflitante, o Git delimita as duas versões:
- Tudo entre
<<<<<<< HEADe=======é o que estava na branch em que você estava quando rodou `git merge` (por exemplo, `main`). - Tudo entre
=======e>>>>>>> iss53é o que veio da branch que você pediu para mesclar. - Para resolver, substitua todo o bloco (apagando completamente as linhas `<<<<<<<`, `=======` e `>>>>>>>`) pelo código final desejado, execute
git add <arquivo>(que é a forma oficial de avisar ao Git que o conflito daquele arquivo foi resolvido) e rodegit commitpara concluir o merge.
Na Seção 3.3, mantenha o repositório limpo auditando suas branches com git branch -v (que mostra o último commit de cada uma), git branch --merged (que lista as branches já integradas na atual e que podem ser removidas com segurança usando git branch -d) e git branch --no-merged (que contém trabalho inédito e que o Git se recusa a apagar com `-d`, exigindo `-D` se você realmente quiser descartá-las).
# 1. No laboratório da Oficina 01, tente mesclar a branch "testing" na "main" (vai gerar conflito!): cd ~/lab-git-etapa2 git checkout main git merge testing # 2. Veja o estado do conflito no git status e os marcadores <<<<<<<, ======= e >>>>>>> no arquivo: git status cat sistema.txt # 3. Resolva o conflito gravando o texto consolidado, marque com git add e finalize o commit: cat << 'EOF' > sistema.txt versao base mudanca paralela na main + experimento da testing integrados EOF git add sistema.txt git commit -m "Resolve conflito integrando testing na main" # 4. Audite quais branches já foram mescladas e apague a branch temporária com segurança: git branch --merged git branch -d testing
Como renomear uma branch sem quebrar o trabalho de quem já clonou o repositório?
Na Seção 3.3, os autores alertam que nunca se deve mudar diretamente o nome da branch principal sem alinhar com a equipe. Para renomear uma branch qualquer localmente, use git branch --move nome-antigo nome-novo. Em seguida, publique a branch com o nome novo (git push --set-upstream origin nome-novo), confira com git branch --all e só então remova o nome antigo do servidor com git push origin --delete nome-antigo.
Entrega da etapa
Provoca deliberadamente um conflito editando a mesma linha de um arquivo em duas branches diferentes, resolva-o manualmente removendo os três marcadores, confirme com git add + git commit e verifique com git branch --merged que a branch lateral pode ser removida com git branch -d.
Conexão com o livro · Capítulo 3 (Seções 3.2 e 3.3) · Fast-Forward vs. 3-Way Merge, marcadores de conflito, git mergetool, git branch (-v, --merged, --no-merged, -d, -D, --move).
Coordene Topic Branches e Tracking Branches remotas
Seu colega diz que acabou de subir a branch serverfix para o servidor. Você roda git fetch origin, mas quando digita git branch ela não aparece na sua lista de branches locais. Onde ela foi parar e como começar a trabalhar nela?
Branches de longa duração, Topic Branches e referências origin/branch
Na Seção 3.4, Chacon e Straub contrastam dois usos complementares das ramificações: as Long-Running Branches (branches permanentes como main para código 100% estável e develop ou next para integração e testes de estabilidade) e as Topic Branches (branches curtas criadas para resolver uma única issue ou funcionalidade, que são mescladas e apagadas assim que passam nos testes).
Quando essas branches precisam ser compartilhadas com a equipe (Seção 3.5), entram em cena as Remote-Tracking Branches (como origin/main ou origin/serverfix). Elas são referências locais que você não consegue mover manualmente: funcionam como marcadores de página que lembram onde cada branch do servidor estava na última vez que você rodou git fetch ou git push.
Por isso, quando você roda git fetch origin e o servidor traz uma branch nova chamada serverfix, você não ganha automaticamente uma branch editável local chamada serverfix — você ganha apenas o ponteiro somente leitura origin/serverfix. Para criar uma branch local editável que rastreie (track) automaticamente essa referência remota, você usa uma Tracking Branch:
# 1. Publique uma topic branch local para o servidor remoto configurando o vínculo (-u): git checkout -b serverfix echo "correcao de timeout" > timeout.conf git add timeout.conf && git commit -m "Ajusta timeout do servidor" git push -u origin serverfix # 2. Formas equivalentes de criar uma Tracking Branch local quando um colega publicou uma branch remota: # git checkout -b serverfix origin/serverfix # git checkout --track origin/serverfix # git checkout serverfix (ou: git switch serverfix) # 3. Audite todas as suas branches locais para ver quem está à frente (ahead) ou atrás (behind) do servidor: git fetch --all git branch -vv # 4. Quando a topic branch já tiver sido mesclada na main, apague-a também no servidor remoto: git checkout main git merge serverfix git push origin --delete serverfix
Observe a importância da combinação git fetch --all; git branch -vv explicada na Seção 3.5: o comando git branch -vv mostra para qual upstream cada branch aponta e informa se você está ahead 2 (tem 2 commits locais ainda não enviados) ou behind 1 (o servidor tem 1 commit que você ainda não mesclou). Porém, como o git branch -vv consulta apenas os dados já cacheados na sua máquina sem ir à internet, você deve sempre rodar git fetch --all imediatamente antes para ter números reais.
Como usar o atalho @{u} e alterar o upstream de uma branch já existente?
Se você já criou uma branch local sem vínculo e quer apontá-la para uma branch remota existente a qualquer momento, basta rodar git branch -u origin/serverfix (ou --set-upstream-to). Uma vez configurado o upstream, você pode usar o atalho @{u} (ou @{upstream}) em qualquer comando para se referir à branch remota correspondente — por exemplo, rodar git diff @{u} ou git merge @{u} sem precisar digitar origin/serverfix.
Entrega da etapa
Suba uma Topic Branch para o seu repositório remoto, execute git fetch --all; git branch -vv para inspecionar o rastreamento, teste o atalho git log @{u}..HEAD para ver commits locais pendentes de push e remova a branch remota com git push origin --delete após mesclá-la.
Conexão com o livro · Capítulo 3 (Seções 3.4 e 3.5) · Long-Running vs. Topic Branches, Remote-Tracking Branches, git checkout --track, git branch -u / -vv, @{u} e git push --delete.
Aplique Rebase para manter o histórico linear sem cair na sua armadilha
Sua branch de funcionalidade demorou três dias para ficar pronta e, nesse meio-tempo, a branch main recebeu dez commits da equipe. Quando você deve usar git rebase para deixar o histórico limpo e quando usar rebase causa um caos de commits duplicados para todo o time?
Como o Rebase funciona por dentro e o poder do --onto
Na Seção 3.6, aprendemos que existem duas formas de integrar mudanças entre duas branches: merge e rebase. Enquanto o git merge une apenas os snapshots finais das duas pontas e cria um commit de junção, o git rebase vai até o ancestral comum das duas branches, calcula o diff introduzido por cada commit da sua branch atual, guarda esses patches temporariamente, reposiciona sua branch na ponta da branch de destino (por exemplo, `main`) e reaplica cada alteração uma por uma em ordem.
O resultado final do código nos arquivos é exatamente o mesmo de um merge, mas o histórico de commits fica completamente linear — como se você tivesse escrito seus commits em série logo após o último trabalho da `main`, ideal antes de enviar uma contribuição para que o mantenedor consiga integrá-la com um simples fast-forward.
O livro também apresenta o rebase cirúrgico de três argumentos com git rebase --onto: imagine que você criou uma branch server a partir da `main` e, antes de `server` ficar pronta, criou uma sub-branch client a partir de `server`. Agora você quer levar para a `main` apenas os commits exclusivos de `client`, deixando os commits de `server` para depois. O comando git rebase --onto main server client diz literalmente ao Git: “Pegue a branch client, descubra os patches desde que ela divergiu de server e reaplique esses patches diretamente em cima da main”.
| Critério | git merge | git rebase |
|---|---|---|
| Filosofia de histórico | O histórico é o registro literal do que aconteceu, quando aconteceu e como as linhas se cruzaram. | O histórico é a narrativa limpa e editada de como o projeto foi construído para facilitar a leitura. |
| O que faz com os commits existentes | Nunca altera commits existentes; adiciona um novo commit de merge quando há divergência. | Abandona os commits antigos da branch e cria novos commits com novos hashes SHA-1. |
| Onde deve ser usado | Ao integrar branches públicas e compartilhadas entre várias pessoas. | Em commits ainda locais antes de compartilhá-los (para limpar e atualizar sua topic branch). |
# 1. Crie uma branch "experiment" e faça os dois caminhos divergirem: cd ~/lab-git-etapa2 git checkout -b experiment echo "funcionalidade experimental" > exp.txt git add exp.txt && git commit -m "Adiciona modulo experimental (C4)" git checkout main echo "atualizacao na main" >> sistema.txt git commit -a -m "Atualiza base na main (C3)" # 2. Faça o rebase da "experiment" sobre a ponta da "main" e conclua com Fast-Forward: git checkout experiment git rebase main git checkout main git merge experiment git log --oneline --graph -4 # 3. Exemplo de sintaxe de transplante cirúrgico com --onto: # git rebase --onto main server client
A Regra de Ouro do Rebase: nunca reescreva commits públicos
Como o rebase abandona os commits antigos e cria commits novos com novos hashes SHA-1 para o mesmo código, Chacon e Straub resumem todos os perigos do rebase em uma única diretriz inegociável: Não faça rebase de commits que já existem fora do seu repositório e nos quais outras pessoas possam ter baseado trabalho. Se você fizer rebase de commits que já subiram para o servidor e outra pessoa já tiver puxado a versão antiga, ao fazer merge ela reenviará os commits antigos junto com os novos, duplicando o mesmo trabalho com dois hashes diferentes no histórico.
Rebase When You Rebase · Como se recuperar se um colega reescreveu uma branch remota?
Se alguém da equipe fez um rebase e forçou o envio (git push --force) em uma branch compartilhada, rodar um git pull comum na sua máquina criará um commit de merge confuso contendo as duas histórias duplicadas. A solução ensinada na Seção 3.6 é rodar git pull --rebase (ou git fetch seguido de git rebase origin/main): o Git reconhece quais commits eram da branch antiga reescrita pelo colega e reaplica apenas o seu trabalho inédito por cima da nova ponta remota.
Entrega da etapa
Explique a estratégia recomendada no final do Capítulo 3 que combina o melhor dos dois mundos: fazer rebase apenas dos commits locais que você ainda não enviou ao servidor (para manter seu trabalho atualizado sobre `origin/main`) e nunca fazer rebase de nada que já tenha sido publicado.
Conexão com o livro · Capítulo 3 (Seção 3.6) · rebase básico, git rebase --onto, The Perils of Rebasing, git pull --rebase e Rebase vs. Merge.
Escolha o fluxo distribuído certo, padronize commits e simule o ganho da equipe
Das arquiteturas de colaboração ao custo real dos conflitos de merge
Rafael coordena uma equipe de oito desenvolvedores em Porto Alegre que trabalhava em uma única branch compartilhada com entregas quinzenais. Como cada pessoa passava quatro ou cinco dias editando arquivos sem sincronizar e subia commits com erros de espaçamento e mensagens vagas, toda sexta-feira a equipe perdia horas destrinchando conflitos cruzados. Baseado nas Seções 5.1 e 5.2 de Pro Git, Rafael redesenha o fluxo da equipe.
Compare os três fluxos distribuídos e as regras de contribuição
Por ser distribuído, o Git permite três grandes topologias de colaboração no Capítulo 5.1:
- Fluxo Centralizado (Centralized Workflow): há um único repositório central (`origin`) e toda a equipe tem permissão de push. Funciona bem para equipes pequenas ou médias, desde que cada funcionalidade nasça em uma Topic Branch isolada e seja revisada antes de entrar na `main`.
- Fluxo com Gerente de Integração (Integration-Manager Workflow): cada desenvolvedor tem permissão de escrita no seu próprio fork público e apenas leitura no repositório oficial. O mantenedor oficial busca as branches dos colaboradores, testa localmente e faz o push para o repositório canônico (o modelo padrão do GitHub e GitLab).
- Fluxo Ditador e Tenentes (Dictator and Lieutenants Workflow): variante para projetos com centenas de colaboradores (como o kernel Linux), onde tenentes integram subsistemas em seus repositórios e o ditador integra apenas as branches dos tenentes.
Independentemente da topologia, a Seção 5.2 estabelece as Commit Guidelines que determinam se suas contribuições serão aceitas rapidamente: primeiro, sempre execute git diff --check antes de commitar para flagrar espaços em branco sobrando no fim das linhas (trailing whitespace) ou mistura de tabs e espaços; segundo, faça commits atômicos (um commit por problema lógico); terceiro, escreva a mensagem com um resumo no modo imperativo de até 50 caracteres na primeira linha (ex.: Corrige arredondamento de frete em vez de Corrigi ou Corrigindo), seguido de uma linha em branco e uma explicação detalhada quebrada em cerca de 72 caracteres por linha.
# 1. Antes de criar qualquer commit, verifique se há erros de espaço em branco (trailing whitespace): cd ~/lab-git-etapa2 printf "linha com espacos sobrando no final \n" >> sistema.txt git diff --check # 2. Limpe o espaço sobrando e compare o que entrou em origin/main enquanto você trabalhava na sua branch: sed -i 's/[[:space:]]*$//' sistema.txt git diff --check # 3. No fluxo de equipe privada (Cap. 5.2), inspecione os commits que chegaram em origin/main # e que ainda não estão na sua branch de trabalho atual: # git fetch origin # git log --no-merges issue54..origin/main
Meça o saldo operacional de adotar Topic Branches curtas
Na equipe de Rafael, cada funcionalidade concluída e integrada na `main` sem quebrar a versão gera um valor operacional de R$ 220 para o produto. O tempo de revisão de código e testes automatizados por branch consome R$ 60. Estruturar o guia de contribuição, treinar os oito desenvolvedores e configurar as proteções de branch custou R$ 480 iniciais. Porém, toda vez que uma branch fica longa demais, acumula conflitos mal resolvidos e exige que dois desenvolvedores parem para refazer o merge manualmente, esse incidente de conflito custa R$ 85.
| Item avaliado | Valor por entrega |
|---|---|
Valor gerado por funcionalidade integrada limpa na main | R$ 220 |
| Custo de revisão de Pull Request e bateria de testes | R$ 60 |
| Contribuição líquida por funcionalidade | R$ 160 |
A contribuição unitária é 220 − 60 = R$ 160 por entrega. Três entregas limpas (3 × 160 = R$ 480) cobrem o investimento inicial de implantação do fluxo. Com 9 funcionalidades entregues na sprint e apenas 1 conflito complexo de retrabalho (R$ 85), o saldo operacional fecha em 9 × 160 − 480 − 85 = R$ 875 positivos.
Sua decisão sobre o tamanho das branches da equipe
Rafael combinou com o time que branches deveriam durar no máximo 2 dias e ter no máximo 1 incidente de conflito complexo por sprint. Se uma funcionalidade grande precisasse de 6 dias de trabalho, como você orientaria a equipe usando os conceitos do Capítulo 5 para não acumular um conflito gigante no sexto dia?
Conferir uma decisão possível
Em vez de manter uma única branch isolada por 6 dias divergindo da `main`, quebre a funcionalidade em 3 topic branches menores de até 2 dias cada (ou sincronize diariamente buscando `git fetch origin` e integrando `origin/main` na branch de feature, como mostra o exemplo de Jessica e John na Seção 5.2). Assim, qualquer divergência é resolvida quando ainda tem poucas linhas.
Três técnicas de contribuição distribuída do Capítulo 5.2
Publicar uma branch local com um nome diferente no servidor remoto
Se a sua branch local chama-se `featureB`, mas no servidor da equipe ela deve aparecer como `featureBee` para não colidir com outro experimento, você não precisa renomear sua branch local: basta rodar git push -u origin featureB:featureBee (sintaxe local:remota).
Compactar uma branch experimental inteira em um único commit com --squash
Quando você fez 15 commits de tentativa e erro em uma branch `featureB` e o mantenedor pede uma implementação limpa em um único commit sobre a versão mais recente da `origin/main`, crie uma branch nova `git checkout -b featureBv2 origin/main`, traga todas as mudanças sem trazer o histórico bagunçado com git merge --squash featureB e faça um único `git commit` bem documentado.
Gerar resumo de Pull Request via linha de comando e patches por e-mail
Mesmo fora do GitHub, o Git possui comandos nativos para colaboração: git request-pull origin/main meu-fork gera um resumo textual completo dos commits e arquivos alterados para enviar ao mantenedor, enquanto git format-patch -M origin/main transforma cada commit em um arquivo `.patch` numerado pronto para envio com `git send-email`.
Simule o saldo operacional do fluxo de branches da equipe
Os valores iniciais reproduzem a sprint da equipe de Rafael. Altere um parâmetro por vez para observar o impacto de reduzir o retrabalho com conflitos de merge.
Saldo = entregas × (valor − custo de revisão) − investimento inicial − conflitos × custo por conflito. O ponto de equilíbrio exibido considera o investimento inicial antes dos conflitos.
Conexão com o livro · Capítulo 5 (Seções 5.1 e 5.2) · fluxos Centralizado, Gerente de Integração e Ditador/Tenentes, git diff --check, regras de commit 50/72, merge --squash e format-patch.
Integre contribuições com diff de três pontos, cherry-pick, rerere e releases
Ferramentas de mantenedor para auditar, pinçar e lançar código com precisão
Na Seção 5.3, você assume o papel de quem mantém o repositório principal e precisa avaliar o código que outras pessoas enviaram antes de colocá-lo na `main`. Se você rodar um `git diff main contrib` comum (com dois pontos ou espaço), e a branch `main` tiver avançado desde que `contrib` nasceu, o diff mostrará todas as mudanças novas da `main` como se a branch `contrib` estivesse tentando apagá-las!
Quatro comandos que separam um usuário comum de um mantenedor experiente
- A sintaxe de três pontos no diff (
git diff main...contrib): ao usar reticências (`...`) entre duas branches no `git diff`, o Git encontra automaticamente o ancestral comum entre as duas branches (o mesmo que rodar `git merge-base contrib main`) e mostra exclusivamente o trabalho introduzido pela branchcontribdesde que ela se separou da `main`, ignorando o que mudou na `main` depois. Já no `git log`, para listar os commits que estão em `contrib` mas não estão em `main`, você usa dois pontos (`git log main..contrib` ou `git log contrib --not main`). - Pinçar um único commit com
git cherry-pick <sha>: um colaborador enviou uma branch com cinco commits, mas você quer trazer para a `main` apenas um commit específico de correção de bug sem mesclar os outros quatro. Basta estar na `main` e rodar `git cherry-pick e43a6`: o Git calcula o patch daquele único commit e o aplica como um commit novo na sua branch atual. - Memória automática de conflitos com
git rerere(Reuse Recorded Resolution): ao ativargit config --global rerere.enabled true, o Git grava em cache a imagem antes e depois de cada conflito de merge que você resolve manualmente. Se você precisar refazer aquele merge mais tarde ou fazer rebase da mesma branch, o Git reconhece o mesmo conflito e aplica sua resolução anterior automaticamente! - Preparar a release oficial (`describe`, `archive` e `shortlog`): use
git describe mainpara gerar um identificador de build legível a partir da última tag anotada (como `v1.6.2-rc1-20-g8c5b85c`, indicando 20 commits após a tag e o prefixo `g` do hash),git archive main --prefix='projeto/' --format=zip > release.zippara exportar um pacote limpo sem a pasta `.git` egit shortlog --no-merges v1.0.0..mainpara gerar o changelog agrupado por autor.
# 1. Ative o Reuse Recorded Resolution (rerere) globalmente na sua máquina: git config --global rerere.enabled true # 2. Crie uma branch de contribuição para testar o diff de 3 pontos (...) e o cherry-pick: cd ~/lab-git-etapa2 git checkout -b contrib echo "correcao pontual importante" > hotfix-pontual.txt git add hotfix-pontual.txt && git commit -m "Corrige validacao de CPF" # 3. Volte para a main, compare exatamente o que "contrib" introduziu com 3 pontos (...) # e pince o commit com git cherry-pick: git checkout main git diff main...contrib git log --oneline main..contrib git cherry-pick contrib # 4. Gere uma tag anotada, um identificador de build (describe) e o resumo de lançamento (shortlog): git tag -a v1.0.0 -m "Release 1.0.0" git describe main git shortlog --no-merges
Qual a diferença entre aplicar um patch com git apply vs. git am?
Quando alguém envia um arquivo de diferenças gerado por `git diff`, você aplica com git apply arquivo.patch (podendo testar antes se ele aplica limpo com git apply --check arquivo.patch), mas ele apenas altera os arquivos sem criar commit. Já quando o patch foi gerado com `git format-patch` (formato mailbox), você usa git am arquivo.patch, que lê automaticamente o autor original, a data e a mensagem do commit e já grava o commit preservando a autoria — e, se houver conflito, a flag git am -3 usa o hash dos blobs para realizar um merge inteligente de três vias!
| Comando | Sintaxe recomendada | O que ele exibe para o mantenedor |
|---|---|---|
| Ver commits exclusivos da branch | git log main..contrib(2 pontos no log) | Lista todos os commits presentes em contrib que ainda não chegaram na main. |
| Ver o código exclusivo da branch | git diff main...contrib(3 pontos no diff) | Compara a ponta de contrib com o ancestral comum entre main e contrib. |
Monte seu roteiro de fechamento de release
Escreva os quatro comandos que você rodará sempre que for revisar uma branch de terceiros e fechar uma nova versão: o comando para ver os commits novos (`git log ..`), o comando para ver o diff isolado desde o ancestral comum (`git diff ...`), o comando para gerar o resumo de mudanças por autor (`git shortlog`) e o comando para exportar o pacote `.zip` ou `.tar.gz` (`git archive`).
Conexão com o livro · Capítulo 5 (Seção 5.3) · git apply (--check), git am (-3), git log --not / .., git merge-base, git diff ..., git cherry-pick, rerere, git describe, git archive e git shortlog.
Domine o GitHub Flow, Pull Requests iterativos e sincronização com Upstream
Você abriu um Pull Request no GitHub, o revisor pediu para renomear uma variável na linha 42 e um iniciante da equipe sugere fechar o Pull Request, apagar a branch e abrir outro do zero. Por que você nunca deve fechar o Pull Request para fazer ajustes?
Chaves SSH, Forks e o ciclo vivo de um Pull Request
Nas Seções 6.1 e 6.2, Chacon e Straub detalham o funcionamento do maior host de repositórios Git do mundo. O primeiro passo prático é configurar a autenticação por chave pública SSH (`~/.ssh/id_ed25519.pub` ou `~/.ssh/id_rsa.pub`) nas configurações da conta para não digitar credenciais a cada push, além de ativar a autenticação em dois fatores (2FA) e cadastrar todos os e-mails que você usa no seu `git config` para que seus commits sejam vinculados ao seu perfil.
Para colaborar, o GitHub Flow funciona tanto em repositórios externos (onde você cria um Fork pessoal na sua conta) quanto em projetos internos da empresa (onde todos usam branches no mesmo repositório):
- Crie sempre uma Topic Branch a partir da `main` antes de escrever código (`git checkout -b slow-blink`). Nunca faça seus commits de contribuição diretamente na branch `main` do seu fork, pois isso dificulta enviar mais de um Pull Request independente ao mesmo tempo.
- Envie sua branch para o seu remoto (`git push -u origin slow-blink`) e abra o Pull Request (PR) descrevendo a motivação da mudança com GitHub Flavored Markdown (usando listas de tarefas `- [x]`, blocos de código com destaque de sintaxe e referências como `Fixes #12` que fecham a Issue #12 automaticamente assim que o PR é aceito).
- Iterando no Pull Request: se durante a revisão de código alguém pedir uma correção, você não fecha o PR! Basta fazer um novo commit na mesma branch local (`slow-blink`) e rodar `git push origin slow-blink`. O GitHub detecta o push e atualiza o Pull Request imediatamente, preservando todo o histórico dos comentários na revisão.
# 1. Configure o repositório original como remoto "upstream" para manter seu Fork sempre atualizado: # (Supondo que "origin" seja o seu fork e "upstream" seja o repositório oficial do projeto) git remote add upstream https://github.com/organizacao/projeto-oficial.git # 2. Busque as novidades do projeto oficial e configure sua branch main para puxar do upstream: git fetch upstream git checkout main git branch --set-upstream-to=upstream/main main # 3. Agora, sempre que quiser atualizar seu fork com o projeto oficial, bastam dois comandos: git pull git push origin main
Teste de raciocínio: Mantendo seu Fork em dia com o Upstream
Suponha que você fez um Fork do repositório `progit/progit2` para a sua conta `seu-usuario/progit2` há um mês. Hoje o repositório original está 5 commits à frente do seu fork. Você quer que, quando estiver na sua branch `main`, o comando `git pull` busque automaticamente do repositório oficial (`progit/progit2`), enquanto o comando `git push origin main` envie atualizações para o seu próprio fork. Resolva sem olhar acima: qual comando conecta sua `main` local ao remoto oficial?
Consultar uma pista
Após adicionar o remoto do projeto original com `git remote add upstream
Conferir a solução comentada
Basta executar git branch --set-upstream-to=upstream/main main (ou `git branch -u upstream/main main`). A partir desse momento, `git pull` na branch `main` buscará e mesclará diretamente de `upstream/main`, e `git push origin main` publicará a `main` atualizada no seu fork pessoal no GitHub.
Para testar a transferência do conceito: durante uma revisão de Pull Request no GitHub, qual é a diferença entre o mantenedor clicar em Merge pull request, Squash and merge ou Rebase and merge?
Conferir a transferência
Merge pull request executa um merge tradicional (`--no-ff`), preservando todos os commits da branch e criando um commit de merge; Squash and merge condensa todos os commits do Pull Request em um único commit limpo na `main`; e Rebase and merge reaplica os commits da branch diretamente sobre a ponta da `main` sem criar commit de merge, gerando um histórico linear.
Entrega da etapa
Escreva um modelo de descrição de Pull Request em GitHub Flavored Markdown para a sua equipe contendo: resumo da mudança, referência à Issue (`Fixes #...`), bloco de código ou comando de teste e uma Task List (`- [ ]`) com os critérios de verificação antes do merge.
Conexão com o livro · Capítulo 6 (Seções 6.1 e 6.2) · configuração SSH/2FA, Forks, GitHub Flow, iteração em Pull Requests, remoto upstream, referências (#issue) e GitHub Flavored Markdown.
Inspecione referências refs/pull/, estruture a governança e automatize via API
Um colaborador externo abriu o Pull Request #958 no GitHub, mas você precisa rodar a bateria pesada de testes na sua estação local antes de aprovar — sem querer adicionar o fork dele manualmente como um novo remoto. Como o GitHub guarda cada Pull Request dentro do próprio repositório oficial?
O segredo das referências refs/pull/*/head e arquivos especiais
Na Seção 6.3, Chacon e Straub revelam um recurso de bastidores que pouquíssimos desenvolvedores conhecem: sempre que alguém abre um Pull Request em um repositório no GitHub, o servidor cria automaticamente uma referência interna chamada refs/pull/<numero>/head apontando para o commit exato daquele Pull Request (e `refs/pull/
Por não começar com `refs/heads/`, essa referência não vem num `git clone` ou `git fetch` padrão, mas você pode listá-la com `git ls-remote` e baixá-la diretamente com git fetch origin refs/pull/958/head! Melhor ainda: se você adicionar a linha fetch = +refs/pull/*/head:refs/remotes/origin/pr/* na seção [remote "origin"] do seu arquivo .git/config, todo `git fetch` baixará todos os Pull Requests abertos e permitirá testar qualquer PR localmente digitando apenas git checkout pr/958! Além disso, adicionar `.patch` ou `.diff` ao final da URL de qualquer Pull Request no navegador (ex.: `https://github.com/usuario/repo/pull/1.patch`) entrega o patch pronto para aplicar com `git am`.
# 1. Veja todas as referências internas (incluindo refs/pull/) de um repositório remoto: # git ls-remote https://github.com/libgit2/libgit2 # 2. Configure seu .git/config para poder dar "git checkout pr/" em qualquer Pull Request: git config --local --add remote.origin.fetch "+refs/pull/*/head:refs/remotes/origin/pr/*" git config --local --get-all remote.origin.fetch # 3. Exemplo de chamada à API REST do GitHub (Cap. 6.5) para obter um template oficial de .gitignore: # curl -s https://api.github.com/gitignore/templates/Python
Arquivos especiais (README e CONTRIBUTING), Organizações e Automação
Para escalar o trabalho em equipe (Seções 6.3 a 6.5), o capítulo destaca três pilares de governança:
- Arquivos especiais na raiz do projeto: além do
README.md(que apresenta o objetivo, instalação e licença na página inicial), criar um arquivoCONTRIBUTING.mdfaz com que o GitHub exiba automaticamente um banner com o link para as diretrizes da sua equipe toda vez que alguém abrir uma nova Issue ou um novo Pull Request. - Organizações, Times e Audit Log: em contas organizacionais, agrupe pessoas em Teams (como `@empresa/backend` ou `@empresa/seguranca`), que podem ser mencionados em comentários de PR exatamente como usuários individuais, aplique o menor nível de permissão necessário em cada repositório e audite eventos sensíveis (como transferências ou chaves adicionadas) no Audit Log.
- Automação com Webhooks e GitHub API / Octokit: em vez de depender de verificações manuais, configure Webhooks para que o GitHub envie um payload JSON via HTTP POST a cada evento de `push` ou `pull_request` para o seu servidor de CI, e use a API (ou a biblioteca oficial Octokit) para atualizar o Status de Commit (verde/vermelho) diretamente na tela do Pull Request antes de liberar o botão de merge.
Entrega da etapa
Escreva a estrutura enxuta do arquivo CONTRIBUTING.md do seu projeto (definindo como nomear topic branches, como formatar commits, o comando de teste obrigatório antes de abrir o PR e quem mencionar na revisão) e configure a regra `refs/pull/*/head` no seu repositório local.
Conexão com o livro · Capítulo 6 (Seções 6.3 a 6.5) · URLs .patch, git ls-remote, refs/pull/*/head, README e CONTRIBUTING, Organizações/Teams, Webhooks, GitHub API e Octokit.
Implemente um fluxo completo de branches e revisão de código.
Aplique os conceitos dos Capítulos 3, 5 e 6 em um repositório real (ou em dupla com um colega). Sua entrega é um protocolo prático de ramificação, resolução de conflitos e revisão de Pull Requests.
Roteiro de prática de uma semana
- Dia 1. Defina as branches permanentes do projeto (`main`) e a convenção de nomes para Topic Branches curtas (`feat/...`, `fix/...`). Ative `git config --global rerere.enabled true`.
- Dias 2 e 3. Crie duas topic branches que alterem o mesmo arquivo, execute um `Fast-Forward` na primeira e provoque um `3-Way Merge` com conflito na segunda. Resolva o conflito e limpe as branches mescladas com `git branch --merged` e `git branch -d`.
- Dias 4 e 5. Pratique atualizar uma topic branch local usando `git rebase main` (respeitando a Regra de Ouro do Rebase) e inspecione a diferença exata de uma branch usando `git diff main...sua-branch` e `git diff --check`.
- Dia 6. Crie o arquivo `CONTRIBUTING.md`, configure o refspec `+refs/pull/*/head:refs/remotes/origin/pr/*` no `.git/config` e conduza um Pull Request completo com iteração de código.
- Dia 7. Feche uma versão usando `git tag -a`, gere o identificador com `git describe`, monte o resumo de entregas com `git shortlog --no-merges` e exporte o pacote limpo com `git archive`.
Decisões para praticar
Escolha uma resposta em cada situação de trabalho em equipe. A correção explica o raciocínio dos Capítulos 3, 5 e 6 e permite tentar novamente.
Minha síntese da Etapa 2
Checklist de domínio da Etapa 2
Consultar anotações das oito oficinas
Livro-base e mapa das 4 etapas do curso
Scott Chacon e Ben Straub. Pro Git, 2ª edição. Apress, 2014 (com atualizações contínuas da comunidade). Esta Etapa 2: Trabalho em Equipe e Fluxos cobre integralmente os Capítulos 3 (Git Branching), 5 (Distributed Git) e 6 (GitHub) em uma adaptação didática e prática em português brasileiro. Edição oficial aberta em git-scm.com.
Mapa completo das 4 etapas desta formação: Etapa 1: O Básico Essencial (Caps. 1 e 2) · Etapa 2: Trabalho em Equipe e Fluxos (Caps. 3, 5 e 6 — Atual) · Etapa 3: Produtividade e Limpeza de Histórico (Cap. 7) · Etapa 4: Administração e Arquitetura Interna (Caps. 4, 8, 9 e 10).