Git na prática: conflito, colaboração e recuperação

Missão, hipótese e limites

Missão: simular uma equipe que troca o campo status por state no contrato da integração da OS 742 enquanto outro trabalho ainda consome status. Você observará as áreas do Git, um conflito real, uma falha de teste, um revert e um resgate.

Hipótese: worktrees impedem que duas linhas de trabalho misturem arquivos; merge detecta sobreposição textual; somente o teste detecta incompatibilidade comportamental; revert e reflog permitem recuperar sem apagar história compartilhada.

Ambiente: Git 2.43 ou superior, Python 3.11 ou superior, terminal PowerShell e editor. Duração: 120–180 minutos. O remoto será uma pasta local e não exige conta, rede ou credencial. Todos os nomes e dados são fictícios.

Crie uma pasta nova chamada git-os-lab fora de qualquer projeto importante. Confirme o caminho com Get-Location. Todos os comandos seguintes devem ser executados somente dentro dela. Se git status mostrar o repositório da enciclopédia ou outro projeto real, pare.

Entregáveis:

git-os-lab/
├── central.git/       # remoto local sem working tree
├── equipe/            # clone principal
├── ana/               # worktree da branch de Ana
├── bruno/             # worktree da branch de Bruno
├── verificacao/       # clone limpo final
└── evidencias.md      # criado por você fora do Git simulado

Registre em evidencias.md: caminhos absolutos, git --version, python --version, hashes, comandos, saídas relevantes, conflito, teste que falhou, teste corrigido, revert e resgate. Não use token real nem copie configuração corporativa.

Passo 1 — remoto local e primeiro snapshot

Na pasta git-os-lab, crie um repositório bare, que armazena objetos e referências sem uma pasta editável:

git init --bare central.git
git clone central.git equipe
Set-Location equipe
git config user.name "Equipe Laboratorio"
git config user.email "equipe@example.invalid"
git switch -c main

Crie no editor quatro arquivos com este conteúdo. O primeiro evita que o cache local do Python apareça como mudança do projeto.

.gitignore:

__pycache__/

contract.txt:

response_field=status

consumer.py:

def read_status(payload):
    return payload["status"]

test_consumer.py:

from consumer import read_status

assert read_status({"status": "open"}) == "open"
print("contract test: OK")

Antes de confirmar, execute e inspecione:

python test_consumer.py
git status --short
git diff
git add .gitignore contract.txt consumer.py test_consumer.py
git diff --staged
git commit -m "feat: define contrato inicial de status"
git push -u origin main
git log --oneline --decorate --graph --all

Evidência esperada: o teste passa; antes do add, os quatro arquivos aparecem não rastreados, mas __pycache__ não; depois, o diff preparado mostra exatamente o primeiro snapshot. Salve o hash com git rev-parse HEAD.

Passo 2 — Ana e Bruno em worktrees separados

Ainda em equipe, crie branches e pastas vinculadas:

git worktree add ../ana -b ana/contrato-v2 main
git worktree add ../bruno -b bruno/consumidor main
git worktree list

No worktree ana, substitua contract.txt por response_field=state. Em test_consumer.py, substitua as duas ocorrências de status por state. O teste deve falhar, pois o consumidor ainda usa o campo antigo; isso é esperado nesta fatia. Confirme contrato e teste juntos:

Set-Location ../ana
git diff
git add contract.txt test_consumer.py
git commit -m "feat: migra contrato de status para state"

No worktree bruno, substitua contract.txt por response_field=status # consumer-v1. Em consumer.py, altere o retorno para payload["status"].upper(). Ajuste o esperado no teste de "open" para "OPEN", execute e confirme:

Set-Location ../bruno
python test_consumer.py
git add contract.txt consumer.py test_consumer.py
git commit -m "feat: normaliza status no consumidor v1"

Cada pasta possui seus próprios arquivos e branch, mas compartilha o banco de objetos do repositório. Para coding agents, esse isolamento evita troca de branch sob os pés de outro agente. Ele não elimina conflito: Ana e Bruno ainda tomaram decisões incompatíveis.

Passo 3 — conflito textual e falha semântica

Volte a equipe, crie a branch de integração e una Ana:

Set-Location ../equipe
git switch -c integracao main
git merge --no-ff ana/contrato-v2 -m "merge: integra contrato v2"
git merge --no-ff bruno/consumidor

O segundo merge deve parar com conflito em contract.txt e test_consumer.py. Inspecione git status e abra os arquivos. Os marcadores <<<<<<<, ======= e >>>>>>> separam alternativas; não são conteúdo válido.

O requisito final é: campo state, resposta normalizada em maiúsculas e teste esperando OPEN. Resolva contract.txt para response_field=state. Resolva o teste para enviar {"state": "open"} e esperar "OPEN". Não altere ainda consumer.py. Prepare e conclua:

git add contract.txt test_consumer.py
git commit -m "merge: reconcilia contrato e teste v2"
python test_consumer.py

O merge terminou, mas o teste deve falhar com KeyError: 'status'. Esse é o conflito semântico: consumer.py ainda consulta o nome antigo. Corrija para payload["state"].upper(), revise e confirme:

git diff
git add consumer.py
git commit -m "fix: alinha consumidor ao campo state"
python test_consumer.py
git log --oneline --decorate --graph --all -12

O resultado agora deve ser contract test: OK. Registre por que simplesmente apagar marcadores não resolveu a intenção.

Passo 4 — revisão e remote simulado

Uma pull request real pertence à plataforma. Aqui, simule sua superfície com:

git diff --stat main...integracao
git diff main...integracao
git log --oneline main..integracao

Revise em dois eixos: o contrato e o consumidor satisfazem o requisito; os commits, nomes e testes satisfazem os padrões. Registre risco, evidência e rollback. Depois publique no remoto local:

git push -u origin integracao

origin/integracao é agora uma referência remota local observável. Não é aprovação: apenas registra o estado publicado.

Passo 5 — defeito publicado e revert auditável

Provoque um defeito substituindo payload["state"] por payload["state_typo"]. Confirme e envie:

git add consumer.py
git commit -m "bug: introduz chave incorreta para exercicio"
git push
python test_consumer.py

O teste deve falhar. Como o commit já foi compartilhado, não mova a branch para escondê-lo. Inverta-o com novo commit:

git revert --no-edit HEAD
python test_consumer.py
git push
git log --oneline -4

Aceite somente se o log mostrar o defeito e o revert, e o teste voltar a passar. Revert é textual: em caso mais complexo ainda seria necessário revisar e testar o comportamento.

Passo 6 — reprodução por commit

Registre o hash atual com git rev-parse HEAD. Volte à raiz git-os-lab e faça clone limpo:

Set-Location ..
git clone central.git verificacao
Set-Location verificacao
git switch integracao
git rev-parse HEAD
python test_consumer.py

Os hashes precisam coincidir e o teste deve passar. Se falhar apenas no clone, existe dependência implícita: arquivo não versionado, versão ausente, variável de ambiente ou comando omitido. Um hash identifica conteúdo Git; não captura sozinho o ambiente inteiro.

Passo 7 — branch movida e resgate pelo reflog

Volte a equipe. Este experimento move apenas uma branch descartável, nunca a branch atual:

Set-Location ../equipe
git branch resgate-demo integracao
git rev-parse resgate-demo
git branch -f resgate-demo integracao~1
git reflog show resgate-demo -5

No reflog, encontre o hash anterior ao movimento e o confirme com git show --stat <hash>. Em seguida, torne-o alcançável com novo nome:

git branch recuperado <hash-correto>
git show --stat recuperado

Não execute novas reescritas enquanto procura. O reflog é local e expira; a recuperação durável acontece quando uma referência volta a apontar para o objeto e, se necessário, é publicada.

Falhas e diagnóstico

Sintoma Causa provável Verificação Recuperação segura
Author identity unknown identidade ausente git config --local --list configure nome/e-mail fictícios apenas no laboratório
branch já está em uso outro worktree a selecionou git worktree list opere na pasta correta; não force
merge não conflitou arquivos não divergiram da mesma base git log --graph --all e git diff compare conteúdo e refaça somente o laboratório
marcadores sumiram, teste falha conflito semântico restante traceback e diff dos três arquivos alinhe contrato, consumidor e teste
clone não encontra main remoto bare sem HEAD padrão git branch -a execute git switch integracao explicitamente
hash “sumiu” referência foi movida git reflog show <branch> inspecione e crie branch de resgate

Critérios de aceite, transferência e limpeza

O laboratório está aprovado quando evidencias.md contém versões e caminhos; grafo com as duas branches; diff preparado do primeiro commit; marcadores do conflito sem segredo; traceback do KeyError; teste corrigido; log com defeito e revert; hashes iguais no clone; e branch recuperado no commit esperado. Outra pessoa deve reproduzir o teste apenas com remoto local, hash e comandos documentados.

Revisão de agente: imagine que Ana e Bruno são coding agents. Escreva instruções que proíbam edição fora do worktree, limitem cada agente ao ticket, exijam git diff e testes e reservem integração a um revisor. Explique por que dois agentes no mesmo worktree invalidariam parte da evidência.

Recuperação ativa: qual área git add modifica? Por que o merge textual pôde terminar com teste quebrado? Por que o defeito publicado foi revertido? O que o clone limpo provou e não provou? Por que criar branch no hash é mais seguro que continuar movendo referências?

Para limpar, primeiro confirme que está na raiz descartável git-os-lab, que ela contém central.git, equipe, ana, bruno e verificacao, e que nenhum caminho aponta para projeto real. Remova worktrees com git -C equipe worktree remove ../ana e git -C equipe worktree remove ../bruno. Depois arquive evidencias.md se desejado e remova somente a pasta temporária explicitamente conferida. Não use glob, variável não resolvida ou comando recursivo fora dessa raiz.

Teste de fixação

Comprove o que você aprendeu

Responda todas as questões. O gabarito comentado só aparece depois do envio.

1. Ana muda o contrato da API e Bruno implementa consumidor antigo. O merge compila após remover marcadores, mas o consumidor continua incompatível. O que falta?
2. Um defeito já faz parte da história publicada. Por que `revert` é preferível a apagar o commit?
3. Uma release funciona apenas no diretório original, mas falha em clone limpo do mesmo hash. Qual conclusão o laboratório sustenta?

Consulta universal

O que você quer encontrar?

Títulos, capítulos, conceitos, termos, laboratórios e ferramentas em uma única busca.

Digite pelo menos dois caracteres.