CURSOS / TECNOLOGIA / TRILHA PRO GIT (ETAPA 2 DE 4)
Controle de Versão · Etapa 2 de 4 · Trabalho em Equipe e Fluxos

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.

Mesa colaborativa de engenharia com planta arquitetônica de ramificações convergentes e dois notebooks
8 oficinasbaseadas nos Capítulos 3, 5 e 6 de Pro Git
Etapa 2 de 4na formação completa de Git
Seu cadernocom fluxos, revisões e decisões
Percurso completo em 4 cursos · Pro Git

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.

Pinos de latão e fios verdes convergindo sobre linho creme representando ramificações e integração de código

“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.
Oficina 01 / 08

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.

O que acontece nos ponteiros e no disco ao manipular branches (Cap. 3.1)
Comando executadoPonteiro da branchPonteiro HEADArquivos na Working Tree
git branch testingCria o ponteiro testing no commit atualContinua apontando para a branch onde você já estavaNenhuma alteração nos arquivos locais.
git checkout testing
(ou git switch testing)
Mantém testing onde estáMove HEAD para apontar para testingSubstitui 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 atualMove HEAD imediatamente para iss53Mantém o snapshot atual pronto para receber novos commits isolados.
Comando para experimentar
# 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.

Oficina 02 / 08

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.

Comparação entre os três desfechos possíveis de um git merge
Tipo de MergeQuando aconteceO que o Git faz no histórico
Fast-ForwardA 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 limpoAs 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 MergeAs 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:

  1. Tudo entre <<<<<<< HEAD e ======= é o que estava na branch em que você estava quando rodou `git merge` (por exemplo, `main`).
  2. Tudo entre ======= e >>>>>>> iss53 é o que veio da branch que você pediu para mesclar.
  3. 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 rode git commit para 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).

Comando para experimentar
# 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).

Oficina 03 / 08

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:

Comando para experimentar
# 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.

Oficina 04 / 08

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”.

Filosofia e comparação prática: git merge vs. git rebase (Cap. 3.6)
Critériogit mergegit rebase
Filosofia de históricoO 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 existentesNunca 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 usadoAo 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).
Comando para experimentar
# 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.

Oficina 05 / 08

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:

  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`.
  2. 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).
  3. 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.

Comando para experimentar
# 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.

Conta operacional por funcionalidade integrada no fluxo de Topic Branches
Item avaliadoValor por entrega
Valor gerado por funcionalidade integrada limpa na mainR$ 220
Custo de revisão de Pull Request e bateria de testesR$ 60
Contribuição líquida por funcionalidadeR$ 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.

Oficina 06 / 08

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

  1. 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 branch contrib desde 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`).
  2. 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.
  3. Memória automática de conflitos com git rerere (Reuse Recorded Resolution): ao ativar git 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!
  4. Preparar a release oficial (`describe`, `archive` e `shortlog`): use git describe main para 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.zip para exportar um pacote limpo sem a pasta `.git` e git shortlog --no-merges v1.0.0..main para gerar o changelog agrupado por autor.
Comando para experimentar
# 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!

Atenção à diferença entre dois pontos (..) e três pontos (...) em log vs. diff
ComandoSintaxe recomendadaO que ele exibe para o mantenedor
Ver commits exclusivos da branchgit 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 branchgit 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.

Oficina 07 / 08

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):

  1. 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.
  2. 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).
  3. 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.
Comando para experimentar
# 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 ` e rodar `git fetch upstream`, qual opção do `git branch` aponta o rastreamento da sua `main` local para `upstream/main`?

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.

Oficina 08 / 08

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//merge` apontando para o teste de merge com a branch alvo).

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`.

Comando para experimentar
# 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:

  1. 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 arquivo CONTRIBUTING.md faz 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.
  2. 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.
  3. 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.

Projeto de aplicação · Etapa 2

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

  1. 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`.
  2. 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`.
  3. 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`.
  4. 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.
  5. 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.

1. Por que criar uma nova branch no Git é uma operação quase instantânea mesmo em projetos com dezenas de milhares de arquivos?
2. Qual é a Regra de Ouro do Rebase destacada por Scott Chacon e Ben Straub no Capítulo 3.6?
3. Você é o mantenedor do repositório e quer ver no terminal apenas as alterações de código que a branch "featureA" introduziu desde que ela se separou da "main" (ignorando commits novos que entraram na "main" depois). Qual comando você executa?
4. Você abriu um Pull Request no GitHub a partir da sua branch "fix-login" e o revisor pediu um pequeno ajuste em uma função. Como você deve proceder?

Minha síntese da Etapa 2

Checklist de domínio da Etapa 2

Consultar anotações das oito oficinas
Suas anotações ficam neste navegador.
Próximo passo na Trilha Pro Git · Etapa 3 de 4

Pronto para dominar as ferramentas avançadas de produtividade e limpeza de histórico?

Na Etapa 3: Produtividade e Limpeza de Histórico (Capítulo 7), você vai aprender seleção cirúrgica de revisões, staging interativo (git add -p), git stash, reescrita interativa de histórico (rebase -i), caça a bugs com git bisect e submódulos.

Continuar para a Etapa 3 →
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).