Git attributes: controlando line endings, diff drivers e export-ignore
1. Introdução ao .gitattributes e sua sintaxe
O arquivo .gitattributes é um mecanismo de configuração do Git que permite definir como arquivos específicos devem ser tratados durante operações como commit, checkout, merge e diff. Diferente do .gitignore, que apenas exclui arquivos do versionamento, o .gitattributes controla o comportamento do Git em relação ao conteúdo dos arquivos.
Por padrão, o arquivo deve ser colocado na raiz do repositório, mas também pode ser definido em subdiretórios para sobrescrever configurações anteriores. A sintaxe básica segue o padrão:
*.txt text
*.jpg binary
docs/** linguist-detectable=false
Cada linha contém um padrão de nome de arquivo seguido por um ou mais atributos. Os atributos podem ser booleanos (presentes ou ausentes), com valor específico (ex.: text=auto) ou com valor vazio para desativar o atributo.
A diferença fundamental entre .gitattributes e configurações locais ou globais (git config) é que o arquivo versionado é compartilhado com toda a equipe, garantindo consistência independentemente das configurações individuais de cada desenvolvedor.
2. Controlando line endings com text e eol
Um dos problemas mais comuns em equipes com sistemas operacionais mistos é a inconsistência no final de linha (line endings). O Git oferece três atributos principais para controlar esse comportamento:
-
text: Define se o arquivo deve ser tratado como texto. Quando ativado, o Git converte automaticamente os line endings durante checkout e commit. Valores possíveis:text,-text(desativa),text=auto(deixa o Git decidir). -
eol: Força um tipo específico de line ending. Valores:lf(Unix/macOS),crlf(Windows).
Exemplo prático de configuração:
# Força LF para todos os arquivos de texto
* text=auto eol=lf
# Arquivos específicos com CRLF (Windows)
*.bat text eol=crlf
*.cmd text eol=crlf
# Arquivos binários que não devem ser convertidos
*.png -text
*.jpg -text
*.pdf -text
A melhor prática para equipes mistas é usar * text=auto na primeira linha. Isso instrui o Git a detectar automaticamente arquivos de texto e converter seus line endings para LF ao fazer commit, mantendo o line ending do sistema local durante o checkout.
# Configuração recomendada para equipes mistas
* text=auto
*.sh text eol=lf
*.bat text eol=crlf
3. Customizando diff drivers para arquivos especializados
O atributo diff permite associar arquivos a drivers de diff personalizados. Isso é especialmente útil para arquivos que não são texto puro, como documentos do Microsoft Word, PDFs ou arquivos de serialização.
Para configurar um driver de diff, primeiro defina o atributo no .gitattributes:
*.md diff=markdown
*.pdf diff=pdf
*.docx diff=word
Em seguida, configure o driver no Git (cada desenvolvedor precisa fazer isso localmente ou via configuração global):
git config --global diff.markdown.textconv "pandoc -t plain"
git config --global diff.pdf.textconv "pdftotext -layout -nopgbrk -"
git config --global diff.word.textconv "pandoc -t plain"
Exemplo prático: comparar versões de um arquivo PDF usando pdftotext:
# Configuração do driver
git config --global diff.pdf.textconv "pdftotext -layout -nopgbrk -"
# No .gitattributes
*.pdf diff=pdf
Agora, ao executar git diff, o Git converterá automaticamente o PDF para texto antes de comparar, mostrando mudanças significativas no conteúdo.
4. Ignorando arquivos em exportações com export-ignore
O atributo export-ignore é usado exclusivamente com o comando git archive. Ele remove arquivos ou diretórios do arquivo gerado, sendo útil para excluir conteúdo que não deve fazer parte de distribuições.
Casos comuns de uso:
# Excluir diretórios de desenvolvimento
.gitignore export-ignore
.github/ export-ignore
tests/ export-ignore
docs/ export-ignore
# Excluir arquivos específicos
Makefile export-ignore
docker-compose.yml export-ignore
.editorconfig export-ignore
Importante: export-ignore não afeta o repositório normal. Os arquivos continuam versionados e aparecem em checkouts comuns. A diferença crucial para .gitignore é que este último impede que arquivos sejam versionados, enquanto export-ignore apenas os exclui de arquivos gerados por git archive.
Exemplo de geração de arquivo sem os diretórios ignorados:
git archive --format=zip --output=projeto-v1.0.zip HEAD
5. Outros atributos essenciais: merge, binary e working-tree-encoding
Atributo merge
Define drivers de merge customizados para resolver conflitos em arquivos específicos:
*.json merge=union
*.lock merge=ours
Atributo binary
Atalho que combina -diff -merge -text, indicando que o arquivo não deve ser tratado como texto:
*.zip binary
*.exe binary
*.dll binary
Atributo working-tree-encoding
Permite especificar codificação de caracteres diferente de UTF-8:
*.csv working-tree-encoding=ISO-8859-1
*.txt working-tree-encoding=Shift_JIS
O Git converte automaticamente para UTF-8 ao fazer commit e para a codificação especificada ao fazer checkout, facilitando o trabalho com arquivos legados.
6. Depuração e verificação de atributos
Para verificar quais atributos se aplicam a um arquivo específico, use git check-attr:
git check-attr -a src/main.js
# src/main.js: text: auto
# src/main.js: diff: java
# src/main.js: export-ignore: unspecified
Para verificar atributos específicos:
git check-attr text eol export-ignore -- config.json
# config.json: text: set
# config.json: eol: lf
# config.json: export-ignore: unspecified
Já git check-ignore verifica se um arquivo seria ignorado pelo .gitignore:
git check-ignore -v node_modules/package.json
# .gitignore:2:node_modules/ node_modules/package.json
7. Boas práticas e armadilhas comuns
Boas práticas:
-
Versionar
.gitattributes: Sempre inclua o arquivo no repositório para garantir consistência entre todos os membros da equipe. -
Usar
text=autocomo padrão: Isso evita a maioria dos problemas com line endings. -
Especificar binários explicitamente: Use
binarypara arquivos que nunca devem ser tratados como texto. -
Testar antes de aplicar: Execute
git check-attrem arquivos suspeitos antes de fazer commit.
Armadilhas comuns:
-
Misturar
text=autocomeolexplícito: Quandotext=autoestá ativo, definireol=lfpode causar comportamento imprevisível. Prefira usar um ou outro. -
Esquecer
binarypara binários: Arquivos binários sem o atributo-textpodem ser corrompidos pelo Git ao tentar converter line endings. -
Sobrescrita em subdiretórios: Atributos definidos em subdiretórios sobrescrevem completamente os definidos na raiz, não fazendo merge.
-
export-ignorenão afetagit clone: Lembre-se que este atributo só funciona comgit archive.
Exemplo de configuração robusta para projetos modernos:
# Configuração base
* text=auto
# Documentação
*.md text diff=markdown
*.rst text
# Scripts
*.sh text eol=lf
*.bat text eol=crlf
# Binários
*.png binary
*.jpg binary
*.ico binary
*.woff2 binary
# Exportação
.gitattributes export-ignore
.github/ export-ignore
tests/ export-ignore
Referências
- Git Attributes Documentation (Official) — Documentação oficial completa sobre o arquivo .gitattributes, incluindo todos os atributos e sintaxe detalhada.
- Pro Git Book - Git Attributes — Capítulo do livro Pro Git que aborda configurações avançadas de atributos com exemplos práticos.
- Dealing with line endings (GitHub Docs) — Guia oficial do GitHub sobre configuração de line endings em equipes com sistemas operacionais mistos.
- Customizing Git - Diff Drivers — Seção do Pro Git sobre como configurar drivers de diff para arquivos binários e especializados.
- Git Archive and export-ignore — Documentação do comando git archive, incluindo detalhes sobre como o atributo export-ignore afeta a geração de arquivos.
- Atlassian Git Tutorial - .gitattributes — Tutorial da Atlassian com exemplos práticos de configuração de .gitattributes para projetos reais.
- Working-tree-encoding Attribute — Documentação oficial sobre o atributo working-tree-encoding para lidar com codificações de caracteres não-UTF-8.