CURSOS / TECNOLOGIA / TRILHA PRO GIT (ETAPA 4 DE 4 · ENCERRAMENTO)
Controle de Versão · Etapa 4 de 4 · Administração e Arquitetura Interna

Por dentro do
motor do Git.

Chegamos ao nível mais profundo de Pro Git: levante servidores bare blindados com git-shell, automatize políticas inquebráveis com Server-Side Hooks, construa commits manualmente com comandos plumbing e opere objetos, packfiles e resgates na raiz do diretório .git.

Bancada de engenharia e relojoaria mecânica com diagrama de grafos de objetos em linho creme e terminal plumbing
8 oficinasbaseadas nos Capítulos 4, 8, 9 e 10 de Pro Git
Etapa 4 de 4fechando a trilha completa
Seu cadernode arquitetura, hooks e objetos
Percurso completo em 4 cursos · Pro Git

Trilha de formação: Domínio do Git do básico à arquitetura interna

Você está na Etapa 4 (último curso da formação), onde conectamos a administração de servidores, customização avançada, migração e a engenharia interna do banco de objetos do Git.

Percurso da Etapa 4 (Capítulos 4, 8, 9 e 10)

Este curso revela como o Git funciona como um sistema de arquivos endereçável por conteúdo. Todos os laboratórios podem ser executados localmente no seu terminal.

Cubos geométricos de vidro e hastes de latão interconectadas sobre mesa de carvalho representando grafos de objetos Git

“Na sua essência, o Git é um sistema de arquivos endereçável por conteúdo com uma interface de controle de versão construída por cima.”

No Capítulo 10 de Pro Git, Scott Chacon e Ben Straub mostram que toda a mágica do Git repousa sobre apenas quatro tipos de objetos imutáveis (blob, tree, commit e tag) armazenados por chave-valor em .git/objects.
Oficina 01 / 08

Escolha o protocolo certo e levante um servidor Git seguro com git-shell

Uma equipe decide hospedar seu próprio servidor Git via SSH em uma máquina Linux interna, mas percebe que qualquer desenvolvedor com chave cadastrada consegue abrir um terminal interativo no servidor e navegar pelo sistema operacional. Como permitir git push e git pull bloqueando o login de shell?

Os quatro protocolos de transporte e o Repositório Bare

No Capítulo 4 (Seção 4.1), Chacon e Straub comparam os quatro protocolos que o Git usa para conversar com um servidor: o Protocolo Local (quando o remoto está em uma pasta ou montagem NFS como `/srv/git/projeto.git`), o HTTP (dividido entre o moderno Smart HTTP, que autentica e negocia pacotes de forma inteligente na mesma URL `https://`, e o Dumb HTTP, que expõe o repositório como arquivos estáticos somente leitura exigindo o hook `post-update` com `git update-server-info`), o SSH (autenticado, criptografado e ubiquamente disponível) e o Protocolo Git (`git://` na porta 9418) (o mais veloz por não ter criptografia nem autenticação, usado para leitura pública quando existe o arquivo `git-daemon-export-ok`).

Para hospedar um repositório no servidor que receberá `git push` de várias pessoas, ele nunca deve ter uma Working Tree (caso contrário, um push para a branch ativa deixaria a cópia de trabalho do servidor defasada em relação ao índice). Por isso, todo repositório de servidor é um Bare Repository (Repositório Puro), criado com git init --bare --shared ou clonado a partir de um projeto existente com git clone --bare meu_projeto meu_projeto.git (que copia apenas o conteúdo interno da pasta `.git`).

Comparação prática dos protocolos de servidor Git (Cap. 4.1)
ProtocoloExemplo de URLPontos fortesLimitações principais
Local/srv/git/proj.git ou file:///srv/git/proj.gitSimples para equipes que já compartilham sistema de arquivos; sem `file://` usa hardlinks rápidos.Exige montar o disco na rede; sem `file://` não limpa objetos quebrados na cópia.
Smart HTTPhttps://git.empresa.com/proj.gitUma única URL para leitura pública e escrita autenticada; atravessa qualquer firewall corporativo (porta 443).Exige configurar servidor web (Apache/Nginx + `git-http-backend`).
SSHgit@servidor:/srv/git/proj.gitFácil de levantar, criptografado e com compressão de dados nativa.Não permite acesso anônimo direto sem criar chaves ou contas.
Git (9418)git://servidor/proj.gitMaior velocidade de transferência pura sem custo de CPU de criptografia.Zero autenticação nativa; exige porta `9418` aberta.

Blindando a conta git no servidor com git-shell e authorized_keys

Na Seção 4.4, ao configurar o acesso SSH para a equipe criando um usuário `git` no servidor e adicionando as chaves públicas (`~/.ssh/id_rsa.pub` ou `id_ed25519.pub`) ao arquivo `/home/git/.ssh/authorized_keys`, você deve aplicar duas travas de segurança imediatamente: primeiro, trocar o shell de login do usuário `git` para o utilitário restrito git-shell (que aceita apenas conexões de `git push`/`git fetch` e rejeita login interativo); segundo, prefixar cada chave no `authorized_keys` bloqueando redirecionamento de portas e terminal:

Comando para experimentar
# 1. Transforme um repositório normal em um repositório BARE (.git puro) de servidor:
mkdir -p ~/lab-git-etapa4 && cd ~/lab-git-etapa4
git init -b main projeto-origem
cd projeto-origem && echo "codigo v1" > main.py && git add main.py && git commit -m "Commit inicial" && cd ..

git clone --bare projeto-origem servidor-bare.git
ls -F1 servidor-bare.git

# 2. Comandos de administração para restringir o usuário "git" no servidor Linux (Cap. 4.4):
#    which git-shell
#    sudo chsh git -s $(which git-shell)

# 3. Formato blindado para cada linha do arquivo ~/.ssh/authorized_keys no servidor:
#    no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAA... dev@empresa
Qual a diferença entre clonar /srv/git/proj.git vs. file:///srv/git/proj.git?

Como detalha a Seção 4.1, se você especificar apenas o caminho do diretório (`git clone /srv/git/proj.git`), o Git usa links físicos (*hardlinks*) ou copia os arquivos diretamente no sistema operacional — o que é extremamente rápido. Já se você adicionar **`file://`**, o Git dispara os processos normais de transferência de rede (`upload-pack`), o que é um pouco mais lento, mas excelente quando você quer gerar uma cópia limpa deixando para trás objetos órfãos ou referências externas!

Entrega da etapa

Crie um repositório `--bare` local conforme o comando acima, inspecione os arquivos na raiz dele (notando a ausência de Working Tree), adicione-o como remoto de um segundo clone e documente como sua equipe protege chaves SSH com `git-shell` e restrições de `authorized_keys`.

Conexão com o livro · Capítulo 4 (Seções 4.1 a 4.10) · protocolos Local, Smart/Dumb HTTP, SSH e Git (9418), git clone --bare, init --bare --shared, git-shell, authorized_keys e GitLab.

Oficina 02 / 08

Padronize quebras de linha e blinde o servidor contra pushes destrutivos

Um desenvolvedor no Windows abre um arquivo criado no Linux e, sem alterar nenhuma linha de código, o git diff acusa que o arquivo inteiro foi apagado e reescrito por causa do retorno de carro (CRLF). Além disso, alguém roda git push --force na main e apaga commits do servidor. Como resolver os dois problemas via configuração?

Configurações de Cliente: templates, excludesfile, autocrlf e whitespace

Na Seção 8.1, Chacon e Straub exploram as chaves de configuração que elevam o padrão técnico de uma estação de trabalho:

  1. Template de mensagem e exclusão global: aponte commit.template para um arquivo `~/.gitmessage.txt` (para abrir todo `git commit` já com a estrutura de resumo, motivação e número de ticket pronta para preencher) e aponte core.excludesfile para `~/.gitignore_global` (para ignorar arquivos do seu sistema operacional e editor, como `.DS_Store`, `*~` e `.swp`, em todos os projetos da máquina sem sujar o `.gitignore` compartilhado da equipe).
  2. Fim da guerra entre CRLF (Windows) e LF (Linux/macOS): configure core.autocrlf de acordo com o sistema operacional: no Windows, use git config --global core.autocrlf true (converte LF para CRLF ao colocar arquivos no disco e converte de volta para LF ao gravar no Git); no Linux e macOS, use git config --global core.autocrlf input (não converte nada no checkout, mas limpa qualquer CRLF acidental transformando em LF na hora do commit!).
  3. Detecção automática de problemas de espaçamento (core.whitespace): o Git vem com quatro regras ativadas por padrão (`blank-at-eol`, `blank-at-eof`, `space-before-tab` e `cr-at-eol`) e duas opcionais que você pode ligar com indent-with-non-tab e tab-in-indent — permitindo até corrigir automaticamente patches recebidos rodando git apply --whitespace=fix arquivo.patch.

As três travas de integridade no Servidor Git

Ainda na Seção 8.1, três chaves configuradas no arquivo `config` do repositório `--bare` no servidor protegem toda a equipe contra corrupção de objetos e reescrita forçada:

Três configurações essenciais de servidor na Seção 8.1 de Pro Git
Chave no ServidorValor recomendadoProteção que ativa imediatamente
receive.fsckObjectstrueObriga o servidor a verificar a integridade SHA-1 e a validade do grafo de todos os objetos em cada git push antes de aceitá-los.
receive.denyNonFastForwardstrueBloqueia git push --force em commits que já estão no servidor, exigindo que toda atualização seja um avanço limpo (fast-forward).
receive.denyDeletestrueProíbe apagar branches e tags remotas via push (evitando que alguém contorne o denyNonFastForwards apagando e recriando a branch!).
Comando para experimentar
# 1. Configure o tratamento correto de quebras de linha no Linux/macOS (input) e ignore arquivos globais:
git config --global core.autocrlf input
# No Windows, use: git config --global core.autocrlf true

# 2. Ative as três travas de proteção diretamente no nosso servidor bare de laboratório:
cd ~/lab-git-etapa4/servidor-bare.git
git config --local receive.fsckObjects true
git config --local receive.denyNonFastForwards true
git config --local receive.denyDeletes true

# 3. Confira as configurações gravadas no servidor:
git config --local --list
Cuidado com help.autocorrect em scripts ou digitação rápida

A chave help.autocorrect aceita um número inteiro representando décimos de segundo. Se você digitar `git chekcout main` com `help.autocorrect = 0` (padrão), o Git apenas sugere o comando correto e para. Se você definir `help.autocorrect = 20`, ele avisa do erro, espera 2,0 segundos para dar tempo de cancelar com `Ctrl+C` e executa automaticamente o comando mais parecido!

Entrega da etapa

Aplique `receive.fsckObjects`, `receive.denyNonFastForwards` e `receive.denyDeletes` no seu repositório `servidor-bare.git` e tente fazer um `git push --force` ou `git push origin --delete` a partir do repositório cliente para ver o servidor bloqueando a operação Destrutiva em tempo real.

Conexão com o livro · Capítulo 8 (Seção 8.1) · commit.template, core.pager, core.excludesfile, help.autocorrect, color.*, extMerge/extDiff, core.autocrlf, core.whitespace e receive.fsckObjects/denyNonFastForwards/denyDeletes.

Oficina 03 / 08

Domine o .gitattributes: diff de arquivos binários, filtros smudge/clean e exportação

Quando alguém altera um documento Word (.docx) ou a resolução de uma imagem .png no projeto, o git diff padrão diz apenas Binary files differ. Como ensinar o Git a mostrar exatamente quais palavras do .docx ou quais metadados da imagem mudaram?

Transformando binários em diffs legíveis com textconv

Na Seção 8.2, Chacon e Straub mostram o poder do arquivo .gitattributes. Diferente do `.git/config`, o `.gitattributes` é versionado na raiz do projeto para que toda a equipe compartilhe as mesmas regras por tipo de arquivo. Você pode declarar explicitamente que um arquivo gerado não deve sofrer conversão de linha nem diff com *.pbxproj binary, ou fazer muito melhor: associar um filtro diff com textconv para arquivos binários estruturados!

Quando você coloca *.docx diff=word no `.gitattributes` e configura `git config diff.word.textconv docx2txt` (ou `*.png diff=exif` com `exiftool`), o Git continua guardando o arquivo binário original intacto nos commits, mas, toda vez que você roda git diff, ele passa as duas versões pelo conversor em memória e imprime o diff linha por linha do texto ou dos metadados!

Filtros de conteúdo (Smudge / Clean), export-ignore e merge=ours

O `.gitattributes` também habilita três recursos avançados de engenharia de releases:

Recursos avançados do arquivo .gitattributes na Seção 8.2
Atributo no .gitattributesComo funciona nos bastidoresAplicação prática imediata
*.c filter=indentExecuta o comando clean ao entrar na Staging Area (git add) e o comando smudge ao sair para a Working Tree (checkout).Garantir que todo arquivo `.c` entre no repositório formatado pelo `indent` antes do commit.
test/ export-ignoreInstrui o comando git archive a omitir aquele arquivo ou diretório ao gerar pacotes `.zip` / `.tar.gz`.Entregar o pacote de produção sem a pasta de testes automatizados nem arquivos de CI.
LAST_COMMIT export-substSubstitui marcadores $Format:...$ dentro do arquivo usando a formatação do git log durante o git archive.Gravar automaticamente o hash, a data e o autor da release dentro de um arquivo de versão no pacote exportado.
database.xml merge=oursAciona a estratégia `ours` (`git config --global merge.ours.driver true`) apenas para aquele arquivo específico em caso de conflito.Manter o arquivo de banco de dados da branch de produção intacto ao mesclar uma branch de homologação.
Comando para experimentar
# 1. Configure export-ignore e export-subst no .gitattributes para automatizar metadados de release:
cd ~/lab-git-etapa4/projeto-origem
mkdir -p test && echo "teste interno" > test/suite.py
echo 'Versao empacotada do commit: $Format:%h por %aN em %cd$' > VERSAO.txt

cat << 'EOF' > .gitattributes
test/ export-ignore
VERSAO.txt export-subst
database.xml merge=ours
EOF

git add test/suite.py VERSAO.txt .gitattributes
git commit -m "Configura atributos de exportacao e versao automatica"

# 2. Exporte o pacote com git archive e veja como VERSAO.txt ganha o hash real e a pasta test/ some!
mkdir -p ~/lab-git-etapa4/pacote-extraido
git archive HEAD | tar -x -C ~/lab-git-etapa4/pacote-extraido
ls -la ~/lab-git-etapa4/pacote-extraido
cat ~/lab-git-etapa4/pacote-extraido/VERSAO.txt
Como funcionam exatamente as direções smudge e clean em um filtro?

Pense na roupa voltando para o armário: quando você faz git add para guardar o arquivo no Index do Git, roda o filtro clean (limpando o arquivo para o formato canônico do repositório). Quando você faz git checkout para trazer o arquivo para a bancada de trabalho (Working Tree), roda o filtro smudge (adaptando o arquivo para o ambiente local do desenvolvedor).

Entrega da etapa

Execute o laboratório acima na sua máquina e comprove que o arquivo `VERSAO.txt` extraído pelo `git archive` contém o hash curto e o seu nome preenchidos automaticamente, enquanto a pasta `test/` foi deixada de fora do pacote de distribuição.

Conexão com o livro · Capítulo 8 (Seção 8.2) · Git Attributes, arquivos binários, diff textconv (docx2txt, exiftool), ident ($Id$), filtros smudge/clean, export-ignore, export-subst e merge=ours.

Oficina 04 / 08

Automatize regras com Git Hooks de Cliente e Políticas no Servidor

Você configurou um hook local `commit-msg` nas máquinas da equipe para exigir o número do chamado em todo commit, mas um desenvolvedor apressado rodou `git commit --no-verify` e subiu cinco commits fora do padrão. Como impor políticas que ninguém consegue burlar?

Hooks do Lado do Cliente vs. Hooks do Lado do Servidor

Nas Seções 8.3 e 8.4, aprendemos que a pasta .git/hooks/ contém scripts de exemplo terminados em `.sample`. Para ativar qualquer hook, basta criar um script executável (`chmod +x`) com o nome exato do evento (sem `.sample`): se o script sair com código `0`, o Git prossegue; se sair com qualquer código diferente de zero, o Git aborta a ação e exibe sua mensagem de erro.

Os Client-Side Hooks rodam na estação do desenvolvedor para acelerar o feedback: pre-commit (verifica lint, testes rápidos e espaços em branco antes mesmo de pedir a mensagem), prepare-commit-msg (injeta textos dinâmicos no template antes de abrir o editor), commit-msg (recebe o caminho do arquivo temporário da mensagem em `$1` e aborta o commit se ela não seguir a expressão regular da equipe), post-commit, `pre-rebase`, `post-checkout` e `pre-push`. Porém, como os hooks locais não são copiados no `git clone` e podem ser ignorados com `--no-verify`, a fiscalização definitiva ocorre nos três Server-Side Hooks dentro do repositório `--bare`:

Os três Server-Side Hooks que governam o git push no servidor (Cap. 8.3 e 8.4)
Hook no ServidorQuando roda e o que recebeComo atua se retornar erro (exit != 0)
pre-receiveRoda primeiro no `push`; lê do `stdin` uma linha `oldrev newrev refname` para cada ref enviada.Tudo ou nada: rejeita o `git push` inteiro se qualquer commit ou branch violar a política.
updateRoda uma vez para cada branch enviada, recebendo `refname`, `oldrev` e `newrev` nos argumentos `$1`, `$2` e `$3`.Seletivo por branch: rejeita apenas a branch que violou a regra (ex.: ACL de diretório) e aceita as demais branches do push.
post-receiveRoda depois que todos os objetos e referências já foram gravados no servidor (mesmo `stdin` do `pre-receive`).Não aborta o push; usado para disparar CI/CD, e-mails e integração com rastreadores de chamados.
Comando para experimentar
# 1. Crie um hook "commit-msg" local que exige "[ref: NUMERO]" na mensagem de todo commit (Cap. 8.4):
cd ~/lab-git-etapa4/projeto-origem
cat << 'EOF' > .git/hooks/commit-msg
#!/bin/sh
if ! grep -iqE "\[ref: [0-9]+\]" "$1"; then
  echo "[POLITICA GIT] Sua mensagem deve conter a referencia do chamado, ex: [ref: 132]" >&2
  exit 1
fi
EOF
chmod +x .git/hooks/commit-msg

# 2. Teste fazer um commit fora do padrão (será bloqueado!) e depois um commit válido:
echo "nova linha" >> main.py
git commit -a -m "Ajuste sem chamado" || true
git commit -a -m "Ajuste validado pelo hook [ref: 132]"

# 3. Como um hook pre-receive no servidor lista todos os commits novos enviados em um push:
#    git rev-list $oldrev..$newrev
Como o hook de servidor verifica os arquivos e a mensagem sem ter Working Tree?

Como o repositório no servidor é `--bare` (não tem arquivos abertos em pastas comuns), na Seção 8.4 os scripts de servidor usam comandos *plumbing* para inspecionar o push diretamente no banco de objetos: git rev-list $oldrev..$newrev lista todos os hashes SHA-1 que estão tentando entrar; git cat-file commit $sha | sed '1,/^$/d' extrai a mensagem de cada commit para validar a regex `[ref: NNN]`; e git log -1 --name-only --pretty=format:'' $sha lista quais arquivos foram alterados para bloquear caso um usuário tente modificar uma pasta restrita!

Entrega da etapa

Ative o hook `commit-msg` no seu laboratório local, confirme que ele bloqueia mensagens sem `[ref: NNN]` e desenhe a regra do seu hook `pre-receive` no servidor para garantir que nem mesmo um `git commit --no-verify` passe sem referência de chamado.

Conexão com o livro · Capítulo 8 (Seções 8.3 e 8.4) · Client-Side Hooks (pre-commit, prepare-commit-msg, commit-msg, pre-push), Server-Side Hooks (pre-receive, update, post-receive) e política ACL/formato.

Oficina 05 / 08

Conduza migrações de outros sistemas e simule o ganho de infraestrutura Git

Da ponte bidirecional com sistemas legados à migração definitiva com fast-import

No Capítulo 9, Chacon e Straub resolvem o desafio de empresas que ainda possuem sistemas críticos em Subversion (SVN), Mercurial ou Perforce, mas querem dar à equipe toda a produtividade local do Git — ou migrar o acervo histórico inteiro de vez para um servidor Git moderno.

Regras de ouro ao operar como cliente (git svn / git p4) e ao migrar

Quando você usa o Git como cliente de um servidor Subversion via git svn clone <url> -s (onde `-s` reconhece o layout padrão `trunk`, `branches` e `tags`), você pode criar branches locais, usar `git add -p`, `stash` e `rebase -i` normalmente. Porém, ao sincronizar com o servidor SVN, duas regras da Seção 9.1 são vitais:

  1. Mantenha o histórico linear antes do dcommit: use git svn rebase para buscar novidades do servidor SVN e git svn dcommit para enviar seus commits locais (cada commit vira uma revisão no SVN e é reescrito localmente com o metadado `git-svn-id`). Nunca publique merges não-lineares do Git diretamente para o SVN, pois o modelo do SVN não registra múltiplos pais como o Git.
  2. Mapeie os usuários antes de migrar em definitivo (Seção 9.2): sistemas como SVN gravam apenas o login de rede (ex.: `jsilva`), enquanto o Git exige `Nome Completo `. Na migração definitiva (`git svn clone file:///... --authors-file=users.txt --no-metadata`), fornecer o arquivo `users.txt` converte cada revisão histórica em um commit Git limpo, e um script de pós-processamento transforma as referências `refs/remotes/origin/tags/*` em tags anotadas reais do Git.
  3. Importação de alta velocidade com git fast-import: para sistemas proprietários ou pastas de versões arquivadas, o comando plumbing git fast-import lê um fluxo de texto simples na entrada padrão (`blob`, `mark :1`, `data`, `commit refs/heads/main`) e grava dezenas de milhares de commits diretamente em packfiles em poucos segundos.
Comando para experimentar
# Demonstração prática do protocolo "git fast-import" (Cap. 9.2) criando um commit
# completo a partir de um fluxo de texto puro em milissegundos, sem tocar na Working Tree:
mkdir -p ~/lab-git-etapa4/importado && cd ~/lab-git-etapa4/importado
git init -b main

cat << 'EOF' | git fast-import
blob
mark :1
data 29
 Sistema legado migrado v1.0
commit refs/heads/main
mark :2
author Engenheiro Migracao  1700000000 -0300
committer Engenheiro Migracao  1700000000 -0300
data 38
Importa revisao inicial via fast-import
M 100644 :1 legado.txt
done
EOF

git checkout main
git log --stat -1

Meça o impacto operacional da governança de servidor e compressão Git

Henrique administra a plataforma de repositórios de uma indústria em Joinville que migrou 14 projetos de um servidor legado para um servidor Git centralizado com hooks `pre-receive` (bloqueando commits sem ticket e arquivos binários gigantes) e rotinas de compactação `git gc`. Cada projeto migrado economiza R$ 190 mensais em tempo de build, armazenamento e sincronização da equipe. O custo mensal de manutenção, backup e execução dos hooks por projeto é de R$ 40. O trabalho inicial de migração com mapeamento de autores e criação dos scripts de servidor custou R$ 900. Já cada incidente em que um repositório sem governança aceita um arquivo gigante ou push forçado indevido e exige intervenção manual do administrador custa R$ 110.

Balanço operacional por repositório migrado e protegido
Indicador mensalValor por projeto
Economia em tempo de clone, build e armazenamento (Packfiles)R$ 190
Custo de operação, backup e validação por repositórioR$ 40
Margem operacional líquida por repositórioR$ 150

A economia líquida é de 190 − 40 = R$ 150 por repositório. Com 6 projetos migrados (6 × 150 = R$ 900), o investimento inicial de migração se paga integralmente. Com os 14 projetos migrados e apenas 1 incidente de limpeza de histórico no período (R$ 110), o saldo líquido fecha em 14 × 150 − 900 − 110 = R$ 1.090 positivos.

Sua decisão ao planejar a migração de um repositório legado

Se uma equipe pedir para migrar um repositório Subversion de oito anos para o Git hoje à tarde apenas copiando os arquivos da última versão (perdendo todo o histórico de quem escreveu cada linha), que argumento técnico das Etapas 3 e 4 você apresentaria para mostrar por que vale usar `git svn clone --authors-file`?

Conferir uma decisão possível

Copiar apenas a última fotografia destrói a capacidade de usar `git blame -C`, `git log -S` e `git log -L` para descobrir por que uma regra de negócio crítica foi escrita há dois anos. Usar `git svn clone` com `--authors-file` e `--no-metadata` preserva toda a inteligência forense da história em um pacote compactado (packfile) muitas vezes menor que o repositório SVN original.

Simule o retorno da migração e blindagem de repositórios

Os valores iniciais reproduzem o projeto de Henrique. Altere um parâmetro por vez para avaliar o ponto de equilíbrio da migração e o impacto de bloquear incidentes com Server-Side Hooks.

Saldo = repositórios × (economia − custo operacional) − investimento inicial − incidentes × custo por incidente. O ponto de equilíbrio exibido considera o investimento inicial antes dos incidentes.

Conexão com o livro · Capítulo 9 (Seções 9.1 e 9.2) · git svn (clone, rebase, dcommit), git-remote-hg, git p4, migração com authors-file e importador customizado com git fast-import.

Oficina 06 / 08

Construa blobs, trees e commits manualmente com comandos Plumbing

Abra a caixa-preta: como o Git grava objetos por chave-valor em .git/objects

Chegamos ao coração do Capítulo 10 (Seções 10.1 e 10.2). Chacon e Straub explicam que os comandos que usamos no dia a dia (`add`, `commit`, `status`, `checkout`) são chamados de Porcelain (Porcelana) porque foram criados como uma camada amigável sobre os comandos de baixo nível chamados de Plumbing (Encanamento).

Por baixo do capô, o Git é um sistema de arquivos endereçável por conteúdo: um banco de dados simples de chave-valor onde você entrega qualquer conteúdo, o Git devolve uma chave única de 40 caracteres hexadecimais (SHA-1) e grava o objeto comprimido em .git/objects/ (usando os 2 primeiros caracteres do hash como nome da subpasta e os 38 caracteres restantes como nome do arquivo para evitar diretórios gigantescos no sistema operacional).

Construindo um commit completo na mão sem usar git add nem git commit

Existem três objetos fundamentais para registrar a história de um projeto, e você pode criar todos eles manualmente no terminal agora mesmo:

  1. Objeto blob (o conteúdo puro do arquivo, sem nome!): um blob armazena exclusivamente os bytes do arquivo — ele não sabe o nome do arquivo nem suas permissões! Você grava um blob com git hash-object -w <arquivo> e lê seu conteúdo com git cat-file -p <hash> (ou descobre seu tipo com `git cat-file -t `).
  2. Objeto tree (o diretório que dá nome e permissão aos blobs): é a árvore que guarda o nome de cada arquivo (`test.txt`), o modo de permissão (`100644` para arquivo normal, `100755` para executável, `120000` para symlink e `040000` para outra sub-tree) e o ponteiro para o hash do blob correspondente. Você registra o blob no Index com git update-index --add --cacheinfo 100644 <blob-hash> test.txt e grava o objeto tree no banco com git write-tree!
  3. Objeto commit (quem salvou, quando, por quê e qual era a tree): você passa o hash da tree para echo 'Mensagem' | git commit-tree <tree-hash> [-p <commit-pai>] e o Git grava o objeto commit apontando para aquela árvore!
Comando para experimentar
# Laboratório clássico da Seção 10.2: crie um repositório vazio e construa um commit
# inteiro usando EXCLUSIVAMENTE comandos plumbing (sem git add e sem git commit!):
mkdir -p ~/lab-git-etapa4/plumbing-puro && cd ~/lab-git-etapa4/plumbing-puro
git init -b main

# Passo 1: Grave um objeto BLOB diretamente no banco .git/objects e inspecione-o:
BLOB_HASH=$(echo "versao 1 construida na mao" | git hash-object -w --stdin)
echo "Hash do Blob: $BLOB_HASH"
find .git/objects -type f
git cat-file -t "$BLOB_HASH"
git cat-file -p "$BLOB_HASH"

# Passo 2: Adicione o blob ao Index com o nome "manual.txt" e grave o objeto TREE:
git update-index --add --cacheinfo 100644 "$BLOB_HASH" manual.txt
TREE_HASH=$(git write-tree)
echo "Hash da Tree: $TREE_HASH"
git cat-file -p "$TREE_HASH"

# Passo 3: Grave o objeto COMMIT apontando para a Tree e aponte a branch main para ele:
COMMIT_HASH=$(echo "Primeiro commit via plumbing" | git commit-tree "$TREE_HASH")
git update-ref refs/heads/main "$COMMIT_HASH"

# Comprove que o git log normal enxerga o commit que você montou peça por peça!
git log --stat -1
A fórmula exata do SHA-1 do Git · Por que sha1sum direto no arquivo dá um hash diferente?

Na Seção 10.2 (“Object Storage”), os autores revelam a fórmula exata: antes de calcular o SHA-1, o Git constrói um cabeçalho em texto com o tipo do objeto, um espaço, o tamanho do conteúdo em bytes e um byte nulo (\0), concatenando o conteúdo logo depois: "blob 16\0what is up, doc?". É sobre essa string inteira (cabeçalho + `\0` + conteúdo) que o SHA-1 é calculado (`bd9dbf5aae1a3862dd1526723246b20206e5fc37`), e em seguida o conjunto é comprimido com zlib antes de ir para `.git/objects/bd/9dbf5...`!

Os 4 tipos de objetos do banco de dados do Git e o que cada um armazena
Tipo (`cat-file -t`)Criado pelo comando plumbingO que contém internamente (`cat-file -p`)
blobgit hash-object -wApenas o conteúdo bruto de um arquivo (sem nome de arquivo e sem data).
treegit write-treeLista de entradas modo tipo hash nome apontando para blobs e sub-trees.
commitgit commit-treePonteiro para a tree raiz, ponteiros parent, author, committer e mensagem.
taggit mktag / git tag -aPonteiro para o objeto marcado, tipo, nome da tag, tagger, mensagem e assinatura GPG.

Faça o raio-X de um commit real

Execute o laboratório acima e, em seguida, rode git cat-file -p HEAD, copie o hash da linha `tree` e rode `git cat-file -p ` para navegar manualmente do commit até a árvore e da árvore até o blob.

Conexão com o livro · Capítulo 10 (Seções 10.1 e 10.2) · Plumbing vs. Porcelain, hash-object, cat-file (-p, -t, -s), update-index, write-tree, read-tree, commit-tree e Object Storage (cabeçalho + \0 + zlib).

Oficina 07 / 08

Domine Referências Simbólicas, Packfiles (Delta Reverso) e o Refspec

Se o Git guarda uma fotografia completa (blob) toda vez que você altera uma linha de um arquivo de 22 KB, por que dez versões desse arquivo não ocupam 220 KB depois que você roda git gc — e qual versão (a mais antiga ou a mais recente) o Packfile guarda inteira?

update-ref, symbolic-ref e a física das Tags Anotadas

Na Seção 10.3, vimos no laboratório anterior que nunca devemos editar arquivos dentro de `.git/refs/` na mão em produção: o comando plumbing seguro para mover qualquer referência é git update-ref refs/heads/main <hash>. Já o arquivo `.git/HEAD` geralmente não contém um hash direto, e sim uma referência simbólica (`ref: refs/heads/main`), lida e alterada de forma segura com git symbolic-ref HEAD refs/heads/main. E quando você cria uma tag anotada (`git tag -a v1.1`), o arquivo `.git/refs/tags/v1.1` aponta para um objeto do tipo `tag`, que por sua vez aponta para o commit (ou para qualquer outro objeto, como o blob da chave pública GPG do mantenedor!).

Packfiles e a sacada genial da Compressão Delta Reversa

Na Seção 10.4, Chacon e Straub respondem a uma dúvida clássica: inicialmente, cada versão de um arquivo modificado é salva como um objeto solto (*loose object*) comprimido com zlib. Se um arquivo `repo.rb` de 22 KB ganhar apenas uma linha nova, o novo blob ocupará outros 22 KB! Porém, quando você roda git gc (ou quando envia objetos pela rede num `git push`), o Git analisa nomes e tamanhos de arquivos parecidos e empacota tudo em um único Packfile (`.pack`) acompanhado de um índice (`.idx`).

O detalhe brilhante do design do Git está em quem vira delta de quem: como na imensa maioria das vezes você precisa acessar rápido a versão mais recente (HEAD) do arquivo, o packfile do Git armazena o objeto mais recente inteiro e transforma as versões antigas em deltas (diferenças) apontando para a versão nova! Você comprova isso inspecionando o índice do pacote com git verify-pack -v .git/objects/pack/pack-*.idx.

Comando para experimentar
# 1. No laboratório plumbing, crie duas versões de um arquivo grande e veja os objetos soltos:
cd ~/lab-git-etapa4/plumbing-puro
seq 1 1000 > numeros.txt
git add numeros.txt && git commit -m "Adiciona 1000 linhas"
echo "1001" >> numeros.txt
git commit -a -m "Adiciona linha 1001"
find .git/objects -type f

# 2. Execute o coletor/empacotador (git gc) e inspecione a cadeia de deltas no Packfile:
git gc
git verify-pack -v .git/objects/pack/pack-*.idx

# 3. Manipule referências com comandos plumbing seguros (Cap. 10.3):
git symbolic-ref HEAD
git rev-parse HEAD

Teste de raciocínio: A Gramática do Refspec (+?<src>:<dst>)

Na Seção 10.5, todo mapeamento entre referências remotas e locais é governado pelo Refspec, escrito no formato +<src>:<dst> (onde `+` autoriza atualização mesmo que não seja fast-forward, `` é o padrão no servidor e `` é onde salvar localmente). Quando você adiciona um remoto, o Git grava `fetch = +refs/heads/*:refs/remotes/origin/*`. Suponha que a equipe de Qualidade mantenha dezenas de branches dentro do namespace `refs/heads/qa/*` no servidor, e você quer configurar seu `.git/config` para buscar automaticamente apenas as branches de QA, e também quer saber qual comando `git push` com Refspec envia sua `main` local para atualizar `refs/heads/qa/main` no servidor. Resolva antes de abrir a resposta!

Consultar uma pista

No `fetch =`, substitua o `*` geral por `qa*` nos dois lados dos dois-pontos (`:`). No `git push`, lembre-se de que `` é a sua branch local e `` é o caminho no servidor.

Conferir a solução comentada

Para buscar apenas as branches de QA no `.git/config`, você usa fetch = +refs/heads/qa*:refs/remotes/origin/qa* (nota: o Git aceita um único curinga `*` por lado, mas permite namespaces como `refs/heads/qa/*:refs/remotes/origin/qa/*`). E para enviar sua `main` local para a branch `qa/main` do servidor, basta rodar git push origin main:refs/heads/qa/main! (É também por isso que `git push origin :topic` — com `` vazio antes dos dois-pontos — significa literalmente “envie *nada* para a branch remota `topic`, apagando-a”, hoje abreviado como `git push origin --delete topic`).

Para testar a transferência do conceito (Seção 10.6): quando o Git transfere dados pelo protocolo inteligente (*Smart Protocol*), quais são os dois processos executados no servidor para enviar dados ao cliente (`fetch`/`clone`) e para receber dados do cliente (`push`)?

Conferir a transferência

Quando você faz `git push` (enviando dados ao servidor), o servidor executa o processo git-receive-pack. Quando você faz `git fetch` ou `git clone` (baixando dados do servidor), o servidor executa o processo git-upload-pack, trocando metadados em pacotes prefixados pelo tamanho em hexadecimal de 4 dígitos (o formato pkt-line).

Entrega da etapa

Rode `git verify-pack -v .git/objects/pack/pack-*.idx` no seu laboratório após o `git gc`, identifique na listagem qual blob de `numeros.txt` ficou armazenado por inteiro (`non delta`) e qual virou delta apontando para o hash do outro.

Conexão com o livro · Capítulo 10 (Seções 10.3 a 10.6) · update-ref, symbolic-ref, objetos tag, Packfiles (git gc, verify-pack -v, delta reverso), Refspec (+src:dst) e Smart Protocol (receive-pack / upload-pack).

Oficina 08 / 08

Resgate objetos órfãos com git fsck, expurgue arquivos gigantes e depure com GIT_TRACE

Um colega apagou uma branch com `git branch -D` e, para piorar, apagou também a pasta `.git/logs/` (destruindo todo o `reflog`). Em outro projeto, alguém comitou um arquivo `.tar.gz` de 50 MB no mês passado e, mesmo depois de rodar `git rm`, o repositório continua pesando 50 MB. Como resolver os dois casos na camada mais profunda do Git?

Resgate extremo sem Reflog: git fsck --full e dangling commits

Na Seção 10.7, Chacon e Straub mostram o que fazer quando até o `git reflog` não está disponível (ou quando você adicionou um arquivo com `git add` e o sobrescreveu antes de commitar). Enquanto o `git gc` não tiver podado objetos antigos expirados, os objetos continuam salvos em `.git/objects`! Basta executar git fsck --full: ele verifica a integridade de todo o banco e lista todos os objetos que não são alcançáveis por nenhuma referência atual como dangling commit <sha> ou dangling blob <sha>. Para recuperar a branch inteira, basta apontar uma branch nova para aquele hash: git branch resgate <sha-do-dangling-commit>!

O roteiro de 6 passos para expurgar um arquivo gigante do Packfile

Quando alguém adiciona um arquivo binário imenso no commit A e roda `git rm` no commit B, o arquivo some da Working Tree atual, mas o blob gigante continua dentro do Packfile para permitir fazer checkout do commit A! Na Seção 10.7, o livro ensina o procedimento forense completo para encontrar o blob pesado sem saber o nome dele e expurgá-lo definitivamente:

Comando para experimentar
# --- PARTE A: Resgatar um commit apagado mesmo SEM reflog (git fsck --full) ---
cd ~/lab-git-etapa4/plumbing-puro
git checkout -b secreta
echo "trabalho valioso" > joia.txt && git add joia.txt && git commit -m "Commit valioso"
git checkout main
git branch -D secreta
rm -rf .git/logs/
# O reflog sumiu! Mas o git fsck encontra o "dangling commit" no banco de objetos:
git fsck --full

# --- PARTE B: Roteiro de 6 passos do Cap. 10.7 para achar e expurgar o maior arquivo do Packfile ---
# 1. Veja o tamanho real do packfile (size-pack em KB):
git count-objects -v

# 2. Liste os 3 maiores objetos dentro do packfile ordenando pela 3ª coluna (tamanho em bytes):
git verify-pack -v .git/objects/pack/pack-*.idx | sort -k 3 -n | tail -3

# 3. Descubra qual nome de arquivo corresponde ao hash do objeto gigante:
#    git rev-list --objects --all | grep 

# 4. Reescreva a história removendo do índice e limpe os logs/refs antes do prune imediato:
#    git filter-branch --index-filter 'git rm --ignore-unmatch --cached ' -- ^..
#    rm -rf .git/refs/original/ .git/logs/
#    git gc && git prune --expire now

# --- PARTE C: Variáveis de ambiente de diagnóstico profundo (Cap. 10.8) ---
GIT_TRACE=true GIT_TRACE_PERFORMANCE=true git status -s

Variáveis de Ambiente do Git (Seção 10.8)

Para fechar o Capítulo 10 e o livro, a Seção 10.8 reúne as variáveis de ambiente que alteram ou radiografam qualquer execução do Git sem tocar nos arquivos de configuração:

Variáveis de ambiente essenciais de diagnóstico e operação (Cap. 10.8)
Variável de AmbienteO que ela faz na execução do comando
GIT_TRACE=trueImprime no terminal cada comando interno e expansão de alias ou subprocesso que o Git executa.
GIT_TRACE_PACK_ACCESS=trueMostra exatamente quais packfiles e deslocamentos de bytes estão sendo lidos no disco.
GIT_TRACE_PACKET=trueExibe pacote por pacote (em formato pkt-line) a conversa de rede durante `fetch`, `pull` ou `push`.
GIT_TRACE_PERFORMANCE=trueMede e imprime o tempo exato em segundos gasto por cada subcomando interno do Git.
GIT_DIR e GIT_WORK_TREEPermite apontar onde fica a pasta `.git` e onde fica a árvore de arquivos quando elas moram em pastas separadas.
GIT_SSH_COMMANDPermite passar flags customizadas de SSH (como `ssh -i ~/.ssh/chave_especifica -p 2222`) para um comando Git.

Entrega da etapa

Execute a Parte A do bloco de comandos acima para ver o `git fsck --full` encontrar o `dangling commit` mesmo depois de apagar a pasta `.git/logs/`, recupere-o com `git branch resgate ` e rode um comando com `GIT_TRACE=true GIT_TRACE_PERFORMANCE=true` para observar os bastidores do motor do Git.

Conexão com o livro · Capítulo 10 (Seções 10.7 a 10.9) · git gc --auto, .git/packed-refs, git fsck --full, git count-objects -v, expurgo com verify-pack + rev-list + filter-branch + prune e variáveis GIT_TRACE*.

Projeto de aplicação · Etapa 4 (Conclusão da Trilha)

Construa e blinde uma plataforma Git de ponta a ponta.

Reúna os conhecimentos dos Capítulos 4, 8, 9 e 10 para desenhar a governança, os hooks, os atributos e o protocolo de manutenção e recuperação de dados dos repositórios da sua equipe.

Roteiro de prática de uma semana

  1. Dia 1. Crie um repositório `--bare` de teste, ative `receive.fsckObjects`, `receive.denyNonFastForwards` e `receive.denyDeletes` e documente o bloqueio de login interativo com `git-shell`.
  2. Dias 2 e 3. Configure `core.autocrlf`, `core.excludesfile` e `commit.template` na sua estação, e crie o arquivo `.gitattributes` com regras de `export-ignore`, `export-subst` e `textconv`.
  3. Dias 4 e 5. Implemente um hook `commit-msg` no cliente e um hook `pre-receive` ou `update` no servidor bare para validar o padrão de mensagens e permissões de branch.
  4. Dia 6. Em uma pasta vazia, construa um commit do zero usando apenas comandos plumbing (`hash-object -w`, `update-index`, `write-tree`, `commit-tree` e `update-ref`).
  5. Dia 7. Simule o resgate de um commit órfão com `git fsck --full`, audite os maiores objetos de um packfile com `git verify-pack -v` + `git rev-list --objects --all` e exporte seu caderno final.

Decisões para praticar

Escolha uma resposta em cada desafio de arquitetura interna e administração do Git. A correção detalha os mecanismos dos Capítulos 4, 8, 9 e 10.

1. Onde o Git armazena o nome de um arquivo (como "relatorio.py") e suas permissões de execução dentro do banco de objetos ".git/objects"?
2. Quando o comando "git gc" cria um Packfile comprimindo várias versões históricas de um mesmo arquivo, qual versão ele armazena inteira e quais viram deltas (diferenças)?
3. Por que regras críticas de governança (como proibir commits fora do padrão de chamado ou restringir quem pode alterar certa branch) devem ser validadas nos Server-Side Hooks (pre-receive / update) e não apenas no pre-commit local?
4. Um desenvolvedor apagou uma branch com "git branch -D" e também limpou o reflog (".git/logs/"), mas o "git gc" ainda não podou os objetos. Qual comando varre o banco ".git/objects" e encontra o hash do commit órfão (dangling commit)?

Minha síntese da Etapa 4

Checklist de domínio da Etapa 4 (Formação Completa)

Consultar anotações das oito oficinas
Suas anotações ficam neste navegador.
Formação Concluída · Trilha Pro Git (Etapas 1 a 4)

Parabéns! Você percorreu os 10 capítulos de Pro Git na prática.

Você completou a jornada desde os primeiros snapshots e os três estados na Etapa 1, passando por branches e fluxos de equipe na Etapa 2 e ferramentas cirúrgicas na Etapa 3, até a arquitetura interna de objetos e servidores na Etapa 4.

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 4: Administração e Arquitetura Interna cobre integralmente os Capítulos 4 (Git on the Server), 8 (Customizing Git), 9 (Git and Other Systems) e 10 (Git Internals) 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) · Etapa 3: Produtividade e Limpeza de Histórico (Cap. 7) · Etapa 4: Administração e Arquitetura Interna (Caps. 4, 8, 9 e 10 — Atual).