Git archive: exportando snapshots limpos para deploy

1. Introdução ao Git archive

O comando git archive é uma ferramenta nativa do Git que permite exportar um snapshot limpo do repositório, sem metadados de versionamento, histórico ou arquivos de configuração internos do Git. Diferentemente do git clone, que baixa o repositório completo com a pasta .git e todo o histórico de commits, o git archive gera um arquivo compactado contendo apenas os arquivos rastreados no momento do snapshot.

Essa característica torna o git archive ideal para cenários de deploy, distribuição de artefatos e backups de código-fonte onde apenas os arquivos finais importam. Em vez de transferir gigabytes de histórico para um servidor de produção, você pode gerar um arquivo leve e pronto para extração.

2. Sintaxe básica e primeiros passos

A estrutura fundamental do comando é:

git archive [opções] <tree-ish> -o <arquivo_saída>

O tree-ish pode ser um branch (main), uma tag (v1.0.0) ou um hash de commit (abc123). Os formatos de saída suportados incluem tar (padrão), zip, tar.gz e tar.bz2.

Exemplo simples exportando o HEAD da branch atual para um arquivo .tar.gz:

git archive --format=tar.gz --output=meu-projeto.tar.gz HEAD

Para gerar um arquivo ZIP:

git archive --format=zip --output=meu-projeto.zip HEAD

Se nenhum formato for especificado, o Git assume tar e envia para a saída padrão (stdout), permitindo encadeamento com outros comandos.

3. Selecionando o que exportar

É possível exportar qualquer referência do repositório:

# Exportar uma tag específica
git archive --format=tar.gz --output=release-v1.0.tar.gz v1.0.0

# Exportar um commit específico
git archive --format=zip --output=hotfix-abc123.zip abc123

# Exportar uma branch diferente da atual
git archive --format=tar.gz --output=develop-snapshot.tar.gz develop

O parâmetro --prefix adiciona um diretório raiz personalizado ao conteúdo do archive, útil para organizar a extração:

git archive --format=tar.gz --prefix=meu-app/ --output=deploy.tar.gz HEAD

Para exportar apenas um subdiretório específico, informe o caminho no final do comando:

git archive --format=tar.gz --output=api-only.tar.gz HEAD:src/api/

4. Controlando o conteúdo do snapshot

O git archive respeita o arquivo .gitattributes do repositório, onde você pode definir quais arquivos ou diretórios devem ser excluídos usando a diretiva export-ignore. Essa é a principal diferença para o .gitignore: enquanto o .gitignore impede que arquivos sejam rastreados pelo Git, o export-ignore atua exclusivamente no momento da geração do archive.

Exemplo de .gitattributes para deploy:

# Excluir diretórios de desenvolvimento
tests/          export-ignore
docs/           export-ignore

# Excluir arquivos de configuração local
.env            export-ignore
.env.local      export-ignore
*.log           export-ignore

# Excluir dependências que serão instaladas no servidor
node_modules/   export-ignore
vendor/         export-ignore

# Excluir arquivos de CI/CD
.gitlab-ci.yml  export-ignore
.github/        export-ignore

Com esse arquivo configurado, o comando:

git archive --format=tar.gz --output=deploy.tar.gz HEAD

gerará um snapshot limpo, sem os diretórios tests/, node_modules/ e outros itens desnecessários para produção.

5. Integração com pipelines de deploy

O git archive é particularmente útil em pipelines CI/CD, pois permite gerar o snapshot diretamente no pipeline e enviá-lo para o servidor de produção sem intermediários.

Exemplo de script para deploy via SSH:

#!/bin/bash
# Gera o archive e extrai diretamente no servidor
git archive --format=tar HEAD | ssh usuario@servidor "tar -x -C /var/www/meu-app"

# Com compressão explícita
git archive --format=tar HEAD | gzip | ssh usuario@servidor "gunzip | tar -x -C /var/www/meu-app"

Para enviar para um bucket S3:

git archive --format=tar.gz --output=deploy.tar.gz HEAD
aws s3 cp deploy.tar.gz s3://meu-bucket/deploy/$(date +%Y%m%d_%H%M%S).tar.gz

Em pipelines do GitHub Actions, GitLab CI ou Jenkins, o comando pode ser usado para gerar o artefato de deploy:

# Exemplo de job GitLab CI
deploy:
  script:
    - git archive --format=tar.gz --output=artefato.tar.gz HEAD
    - scp artefato.tar.gz usuario@servidor:/deploy/
    - ssh usuario@servidor "tar -xzf /deploy/artefato.tar.gz -C /var/www/app"

6. Boas práticas e otimizações

Para versionar snapshots de forma descritiva, combine o comando com informações da tag ou commit:

# Usando a tag atual
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "sem-tag")
git archive --format=tar.gz --output="app-${TAG}.tar.gz" HEAD

# Usando hash curto do commit
HASH=$(git rev-parse --short HEAD)
git archive --format=tar.gz --output="app-${HASH}.tar.gz" HEAD

Para compressão eficiente em cenários de larga escala, prefira tar.gz (gzip) para velocidade ou tar.bz2 (bzip2) para tamanho reduzido:

# Compressão rápida
git archive --format=tar.gz --output=rapido.tar.gz HEAD

# Compressão máxima (mais lenta, mas menor)
git archive --format=tar HEAD | bzip2 -9 > deploy.tar.bz2

Cuidado com arquivos binários grandes: considere pré-processá-los antes do archive, removendo versões desnecessárias ou usando LFS (Large File Storage) para evitar que o snapshot fique excessivamente pesado.

7. Comparação com alternativas

git archive vs rsync: O rsync é ideal para deploys incrementais, sincronizando apenas arquivos modificados. O git archive é mais adequado para deploys completos e imutáveis (snapshots). Se você precisa de atualizações frequentes com baixo tráfego, rsync vence. Para garantir que o ambiente de produção seja uma cópia exata do que está no repositório, git archive é superior.

git archive vs npm pack / pip wheel: Ferramentas específicas de linguagem geram pacotes otimizados para seus ecossistemas (excluindo devDependencies, por exemplo). O git archive é genérico e independente de linguagem, mas não gerencia dependências. Use npm pack para projetos Node.js e pip wheel para Python quando precisar de pacotes prontos para instalação.

Quando NÃO usar git archive: Repositórios enormes (acima de 1 GB) podem gerar snapshots lentos para processar. Se você precisa do histórico de versões no servidor (para rollback rápido), prefira git clone --depth 1. O git archive também não é recomendado quando você precisa de deploys incrementais ou de manter permissões específicas de arquivos Unix (o tar preserva permissões básicas, mas não ACLs complexas).

8. Exemplo completo e troubleshooting

Script completo de deploy usando git archive:

#!/bin/bash
# deploy.sh - Gera snapshot e envia para servidor

set -e  # Interrompe em caso de erro

REPO_DIR="/caminho/para/seu/repositorio"
DEPLOY_DIR="/var/www/meu-app"
SERVIDOR="usuario@192.168.1.100"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
NOME_ARQUIVO="deploy_${TIMESTAMP}.tar.gz"

cd "$REPO_DIR"

# Verifica se há alterações não commitadas
if [ -n "$(git status --porcelain)" ]; then
    echo "AVISO: Existem alterações não commitadas. O archive usará o último commit."
fi

# Gera o snapshot
echo "Gerando archive..."
git archive --format=tar.gz --output="/tmp/${NOME_ARQUIVO}" HEAD

# Envia para o servidor
echo "Enviando para servidor..."
scp "/tmp/${NOME_ARQUIVO}" "${SERVIDOR}:/tmp/"

# Extrai no servidor
echo "Extraindo no diretório de deploy..."
ssh "${SERVIDOR}" "tar -xzf /tmp/${NOME_ARQUIVO} -C ${DEPLOY_DIR} && rm /tmp/${NOME_ARQUIVO}"

# Limpa local
rm "/tmp/${NOME_ARQUIVO}"

echo "Deploy concluído em ${TIMESTAMP}"

Erros comuns e soluções:

  • "fatal: Path '...' does not exist": O caminho especificado para subdiretório não existe no commit/branch atual. Verifique a ortografia e se o diretório está rastreado.
  • Permissão negada ao extrair: Use sudo no servidor ou ajuste as permissões do diretório de destino.
  • Arquivos ignorados aparecem no archive: Verifique se o .gitattributes está commitado e se a diretiva export-ignore está correta. O Git só lê .gitattributes que esteja no commit sendo arquivado.
  • Archive muito grande: Verifique se há binários grandes ou dependências não ignoradas. Considere usar --verbose para listar os arquivos incluídos:
git archive --verbose --format=tar.gz --output=debug.tar.gz HEAD 2>&1 | head -50

Para inspecionar o conteúdo do archive sem extraí-lo:

# Para tar.gz
tar -tzf deploy.tar.gz | head -20

# Para zip
unzip -l deploy.zip | head -20

Referências