Testador de .gitignore
Um .gitignore é uma lista ordenada de padrões e vence o ÚLTIMO que casa com um caminho, portanto a resposta para «por que este arquivo está sendo ignorado?» quase nunca está na linha que você está olhando. Esta ferramenta recebe o arquivo e uma lista de caminhos e responde do jeito que `git check-ignore -v` responde: para cada caminho, o veredito, o número da linha e o texto do padrão que decidiu, mais o diretório pai quando a decisão veio dele e não do arquivo. É aí que mora a maior parte dos defeitos de .gitignore. O git nunca desce para dentro de um diretório excluído, então assim que `build/` passa a ser ignorado, `!build/keep.txt` vira texto morto: o arquivo é julgado pelo pai e a negação nem chega a ser consultada. A ferramenta sinaliza exatamente esse caso em vez de deixar você adivinhando, e ainda lista as regras que não casaram com nenhum dos caminhos colados, que é como aparece um Thumbs.db herdado ou um coverage/ que você renomeou anos atrás. A comparação segue a leitura do formato feita pelo próprio git, e não um glob de shell: um padrão sem barra é comparado com o nome base em qualquer profundidade; um padrão com barra fica ancorado ao diretório que contém o .gitignore; uma barra final o restringe a diretórios; `*` para na barra enquanto um `**` que ocupa um segmento inteiro a atravessa; classes de caracteres como [0-9] e [!a-z] funcionam; espaços no fim são descartados a menos que escapados com barra invertida; # e ! iniciais podem ser escapados; e a marca BOM e as quebras CRLF são removidas antes de qualquer análise. Como uma lista colada de caminhos não carrega tipos, marque diretórios com uma barra final: `build/` é julgado como diretório e `build` como arquivo, e só isso já inverte a resposta de qualquer regra de diretório. Duas coisas que ela não pode dizer. Ela lê um único .gitignore, então os arquivos por diretório mais abaixo na árvore, o .git/info/exclude e o global apontado por core.excludesFile ficam de fora — e qualquer um deles pode contrariar o que você vê aqui. E a comparação diferencia maiúsculas, como o git no Linux, de modo que um repositório com core.ignorecase no macOS ou no Windows pode ignorar mais do que esta tabela mostra. Tudo roda no navegador: nem o arquivo nem os caminhos são enviados para lugar nenhum.
Ignorados
5
Não ignorados
3
Regras
7
Regras mortas
1
O git não desce para dentro de um diretório excluído, então uma regra ! abaixo dele nunca é consultada. Exclua o conteúdo (dir/*) em vez do diretório (dir/) para que ela funcione.
- .vscode/settings.json — !.vscode/settings.json (Linha 10) · pelo pai excluído .vscode/
| Caminho | Veredito | Linha | Regra decisiva |
|---|---|---|---|
| src/index.ts | Não ignorado | — | Nenhuma regra casou |
| node_modules/react/index.js | Ignorado | 2 | node_modules/pelo pai excluído node_modules/ |
| dist/ | Ignorado | 3 | dist/ |
| dist/app.js | Ignorado | 3 | dist/pelo pai excluído dist/ |
| debug.log | Não ignorado | 6 | !debug.log |
| logs/debug.log | Não ignorado | 6 | !debug.log |
| server.log | Ignorado | 5 | *.log |
| .vscode/settings.json | Ignorado | 9 | .vscode/pelo pai excluído .vscode/esta regra ! não pode ter efeito |
Nenhum dos caminhos acima casou com estas linhas. Com uma lista representativa, são candidatas a remoção.
- Linha 4 coverage/
A comparação segue o git em um sistema de arquivos sensível a maiúsculas, com um único .gitignore na raiz.
Como usar
- Cole seu .gitignore no primeiro campo exatamente como está no disco: comentários, linhas em branco, quebras CRLF e a marca BOM são tratados como o git os trata.
- Escreva no segundo campo os caminhos que quer testar, um por linha e relativos à raiz do repositório. Termine a linha com barra para dizer que é um diretório: regras como `logs/` dependem disso.
- Leia a tabela. Cada linha traz o veredito, o número da linha que decidiu e o texto da regra — os mesmos três dados que `git check-ignore -v` imprime, mais o pai excluído quando houver.
- Confira o aviso âmbar. Ele aparece quando uma regra `!` casa com um caminho mas não pode ter efeito porque um diretório pai já está excluído, o motivo mais comum de uma negação parecer certa e não fazer nada.
- Veja a lista de regras mortas no fim: as que não casaram com nenhum caminho colado. Com uma lista de caminhos representativa, são as linhas que vale a pena apagar.
Perguntas frequentes
- Por que minha regra ! não traz o arquivo de volta?
- Porque o git nunca chegou a olhar o arquivo. Ao percorrer a árvore e descobrir que um diretório está excluído, ele para ali e não desce, então nenhuma regra sobre algo lá dentro chega a ser avaliada; a documentação diz isso sem rodeios: não é possível reincluir um arquivo se um diretório pai dele estiver excluído. Com `logs/` e `!logs/important.log`, o log continua ignorado, e esta ferramenta mostra que a decisão veio da regra do diretório. A correção é excluir o conteúdo em vez do diretório: `logs/*` seguido de `!logs/important.log` funciona, porque `logs/*` casa com o que está dentro sem excluir logs. Para uma subárvore inteira a receita é `/logs/**`, depois `!/logs/keep/` e depois `!/logs/keep/**`.
- Qual é a diferença entre build e /build?
- Um padrão sem nenhuma barra é comparado com o nome base de cada caminho, em qualquer profundidade, então `build` ignora igualmente /build, /src/build e /a/b/c/build. Assim que o padrão contém uma barra que não seja o último caractere, ele fica ancorado ao diretório que contém o .gitignore, e `/build` e `docs/output` só casam daquela raiz para baixo. É por isso que um `node_modules` solto no .gitignore da raiz também esconde o node_modules de um pacote aninhado: costuma ser o que as pessoas querem, mas raramente o que escreveram de propósito. A barra inicial é o jeito de dizer «só aqui».
- `logs/` casa com um arquivo chamado logs?
- Não. Uma barra final significa que o padrão só casa com diretórios, então um arquivo comum chamado logs fica em paz enquanto um diretório logs, em qualquer profundidade, é ignorado junto com tudo o que há dentro. Isso importa aqui porque uma lista colada de caminhos não carrega informação de tipo: o git decide olhando o sistema de arquivos real e a esta ferramenta é preciso avisar. Escreva `logs/` para o diretório e `logs` para o arquivo; a mesma string com e sem barra pode receber vereditos opostos, e isso não é uma esquisitice da ferramenta, é a razão de `git check-ignore` surpreender quando o caminho ainda não existe.
- O que as três formas de ** realmente significam?
- Um `**` só tem sentido especial quando ocupa um segmento inteiro do caminho. `**/foo` no começo casa com foo em qualquer profundidade e é a forma explícita do que um padrão sem barras já faz. Um `foo/**` no fim casa com tudo o que está dentro de foo, mas não com o próprio foo, que é justamente por que a receita de reinclusão funciona. E `a/**/b` no meio casa com a/b, a/x/b e a/x/y/b, ou seja, com zero ou mais diretórios. Em qualquer outra posição são asteriscos comuns: `a**b` é o mesmo que `a*b`, e um `*` sozinho nunca atravessa uma barra, então `src/*.c` casa com src/main.c mas não com src/lib/main.c.
- A regra parece certa, mas o git continua mostrando o arquivo como modificado.
- O .gitignore só vale para arquivos que o git ainda não rastreia. Depois que um arquivo foi comitado, acrescentar um padrão não muda nada: o git continua relatando suas mudanças e continua comitando. Pare de rastreá-lo primeiro com `git rm --cached caminho` (que mantém a cópia de trabalho) e comite essa remoção; do commit seguinte em diante quem manda é a regra de ignorar. Esta ferramenta responde à pergunta sobre arquivos não rastreados, a mesma que o `check-ignore` responde, então um caminho que aqui aparece como ignorado ainda pode surgir no `git status` se estiver no índice — e é a primeira coisa a conferir quando os dois discordam.
- Qual .gitignore vence quando há vários?
- O git lê várias fontes em uma ordem fixa e vence a mais específica: os padrões da linha de comando, depois os arquivos .gitignore, em que um de um diretório mais profundo se sobrepõe ao de cima, depois o .git/info/exclude para regras locais do repositório que não são compartilhadas e, por fim, o arquivo global apontado por core.excludesFile. Dentro de um mesmo arquivo decide a última linha que casa. Esta ferramenta modela um único .gitignore, então se o veredito dela não bater com o seu repositório, o provável culpado é um .gitignore aninhado, uma entrada do info/exclude ou o seu arquivo global: rode `git check-ignore -v` lá e a coluna de origem dirá qual arquivo foi.
Ferramentas relacionadas
Testador de EditorConfig
Cole um .editorconfig e alguns caminhos de arquivo: veja as propriedades que cada arquivo resolve e qual seção, em qual linha, decidiu cada valor.
Gerador de .gitignore
Marque linguagens, frameworks, editores e sistemas operacionais que você usa — obtenha um `.gitignore` combinado pronto pra colocar num repo novo.
Testador de padrões glob
Teste caminhos de arquivo contra um padrão glob (*, **, ?, [...], {a,b}) no seu navegador.
Inspetor de módulos WebAssembly
Analise um .wasm no navegador: tamanho de cada seção, importações e exportações com tipos, páginas de memória, função start e seções name e producers.
Validador de JSON Schema
Verifique um documento JSON contra um JSON Schema no navegador: cada erro traz o JSON Pointer, a linha onde cai e a palavra-chave que falhou.
Inspetor de patches e diffs
Leia um diff unificado ou arquivo .patch: arquivos tocados, inserções e remoções por arquivo, e os hunks malformados que fazem o git apply falhar.