Idioma:Português (Brasil)
FERRAMENTA DEV

Gitignore Debugger

Descubra exatamente por que o Git não está ignorando seu arquivo.

Cole um caminho de arquivo e suas regras .gitignore. Mostramos qual regra corresponde, e por quê.

Tudo roda no seu navegador. Seus arquivos nunca são enviados.

O que é um arquivo .gitignore?

Um arquivo .gitignore é um arquivo de texto que diz ao Git quais arquivos deixar de fora do controle de versão. Cada linha é um padrão — *.log, node_modules/, .env — e qualquer caminho não rastreado que corresponda a um padrão desaparece do git status, é pulado pelo git add . e nunca vai para um commit.

A expressão não rastreado carrega quase todo o sentido dessa frase e, sozinha, explica mais casos de “o gitignore não funciona” do que todos os erros de padrão somados. O Git só consulta o .gitignore para arquivos que ele ainda não rastreia. A partir do momento em que um arquivo entrou num commit, o Git continua rastreando ele para sempre, não importa o que você adicione depois ao .gitignore.

O que o Gitignore Debugger faz

Cole um caminho de arquivo e o conteúdo do seu .gitignore: este depurador de .gitignore dá o mesmo veredito que o Git — ignorado ou não ignorado — junto com a regra exata e o número da linha responsáveis. Depois ele lista todas as regras que correspondem ao caminho, na ordem em que o Git as avalia, e aponta a que realmente decidiu.

É essa última parte que um simples verificador de gitignore deixa de fora. Saber que um arquivo está ignorado raramente é a parte difícil. Saber qual das suas quarenta regras fez isso — e por que a que você esperava perdeu — é que é. Funciona nos dois sentidos: por que o Git ignora o arquivo que você quer commitar, e por que ele não ignora aquele que você quer tirar do caminho.

O motor reimplementa as regras de correspondência de padrões do Git: padrões por nome base contra padrões ancorados, * que nunca atravessa uma /, as três formas de **, os sufixos / só para diretórios, as classes de caracteres, os espaços finais escapados. O comportamento dele é verificado contra a saída real do git check-ignore, não presumido.

Como usar

  1. Digite o caminho como ele aparece no seu repositório, relativo à raiz — src/config/local.json, não C:\projects\app\src\config\local.json. Use barras normais; as barras invertidas do Windows são convertidas para você.
  2. Cole o seu .gitignore inteiro. Não reduza ele à única regra da qual você suspeita — a precedência entre regras geralmente é o bug de verdade, e recortar o arquivo joga essa informação no lixo.
  3. Leia o veredito e depois a cadeia de regras abaixo. É aí que uma regra de gitignore que não funciona deixa de ser um mistério.

Para testar regras de gitignore contra um diretório em vez de um arquivo, termine o caminho com uma barra — build/. A distinção importa, porque uma regra terminada em / só corresponde a diretórios.

Por que as regras de gitignore às vezes não funcionam

Os culpados, mais ou menos em ordem de frequência:

Precedência das regras de gitignore: a última correspondência vence

Essa é a que pega todo mundo. Dentro do mesmo .gitignore, é o último padrão que corresponde a um caminho que decide o destino dele, não o primeiro. Estes dois arquivos têm regras idênticas e dão resultados opostos para important.log:

*.log
!important.log        → important.log fica no Git
!important.log
*.log                 → important.log é ignorado

Então coloque as regras amplas primeiro e as exceções depois. Entre arquivos, um .gitignore num subdiretório vence outro acima dele para os caminhos que ficam abaixo. O Git também lê o .git/info/exclude, que é do repositório e nunca vai para um commit, e o seu core.excludesFile global. Uma regra que você não acha em nenhum .gitignore costuma estar escondida num desses dois lugares — git check-ignore -v mostra o arquivo e a linha que decidiram.

Regras de negação, e por que ! falha tanto

Colocar ! na frente de um padrão reinclui algo que uma regra anterior tinha ignorado. Isso tem dois limites, e o segundo explica praticamente todos os casos de “a negação do gitignore não funciona”.

Primeiro, alguma coisa antes precisa ter ignorado o caminho. !important.log sozinho não faz nada, porque nunca houve nada para sobrescrever.

Segundo, e bem mais surpreendente: você não pode reincluir um arquivo que está dentro de um diretório ignorado. Isto não funciona, e nenhuma reordenação resolve:

build/
!build/keep.txt

Quando build/ está ignorado, o Git nunca entra ali. Ele não examina build/keep.txt, então nunca chega à linha 2. Ignore em vez disso o conteúdo do diretório, o que deixa o diretório em si percorrível:

build/*
!build/keep.txt

Para salvar algo mais fundo, cada diretório intermediário também precisa continuar acessível — build/*, depois !build/assets/, depois !build/assets/logo.svg.

Diretórios pais ignorados

A varredura de diretórios do Git para no primeiro diretório ignorado que ela encontra, e tudo que está abaixo herda o veredito sem ser examinado. É por isso que um pai ignorado vence qualquer regra escrita para o próprio arquivo, por específica que ela pareça.

Nesse caso o depurador nomeia o pai que bloqueou a varredura em vez de apenas dizer que o arquivo está ignorado, e ainda mostra as regras de negação que você escreveu para o arquivo — marcadas como inalcançáveis, porque o Git nunca foi longe o suficiente para considerar elas. Conserte o diretório primeiro e as regras abaixo voltam a funcionar por conta própria.

Erros comuns de .gitignore

Alguns exemplos reais

O caminho src/logs/debug.log contra estas regras não está ignorado. A negação é a última correspondência e nenhum diretório pai bloqueia a varredura, então ela é alcançada e vence:

*.log
!src/logs/debug.log

O caminho build/keep.txt contra estas regras está ignorado, por causa do build/ na linha 1, que corresponde ao diretório pai. A linha 2 nunca é acionada:

build/
!build/keep.txt

O caminho docs/api/notes.md contra esta regra não está ignorado, porque o padrão está ancorado pela barra dele e o * não vai atravessar a de api/notes.md. O que se queria era docs/**/*.md:

docs/*.md

Cole qualquer um destes exemplos no testador de gitignore acima para ver a cadeia de regras completa. De volta ao terminal, git check-ignore -v path/to/file é o equivalente embutido — ele nomeia o arquivo de origem, o número da linha e o padrão que decidiram, e vale a pena conhecer.

Perguntas frequentes

Por que o Git está ignorando meu arquivo?

Algum padrão corresponde a ele, e muitas vezes não é o que você está olhando. Os culpados de sempre são um .gitignore num subdiretório, um diretório pai ignorado, ou o seu arquivo de exclusão global. Rode git check-ignore -v path/to/file: ele imprime exatamente o arquivo de origem, o número da linha e o padrão que decidiram. Colar o caminho e as regras no depurador acima mostra a mesma coisa, mais as regras que correspondiam e perderam.

Por que minha regra do .gitignore não funciona?

Quatro causas cobrem quase todos os casos, mais ou menos em ordem de frequência: o arquivo já está sendo rastreado, então a regra é totalmente ignorada; uma regra posterior sobrescreveu ela, porque a última correspondência vence; um diretório pai está ignorado, então o Git nunca chegou na regra; ou o padrão está ancorado de um jeito diferente do que você pensava — uma barra em qualquer posição que não seja o fim ancora um padrão ao diretório que contém o .gitignore.

Por que o ! não reinclui meu arquivo?

Ou nada antes tinha ignorado o caminho, e aí a negação não tinha nada para sobrescrever, ou — bem mais provável — o arquivo está dentro de um diretório ignorado. O Git não entra em diretórios ignorados, então ele nunca vê o arquivo e nunca avalia a sua regra !.

Ignore o conteúdo do diretório em vez do diretório em si: troque build/ por build/*, mantenha !build/keep.txt depois, e a negação é alcançada.

A última regra do .gitignore vence?

Sim. Dentro de um arquivo, é o último padrão que corresponde a um caminho que decide o destino dele — não o primeiro, nem o mais específico. *.log seguido de !important.log mantém o important.log; inverta essas duas linhas e ele fica ignorado. Coloque as regras amplas primeiro e as exceções depois.

Por que uma pasta ignorada impede uma regra de negação de funcionar?

Porque a varredura de diretórios do Git para no primeiro diretório ignorado. Quando build/ corresponde, o Git marca toda a subárvore como ignorada sem listar o que tem dentro, então !build/keep.txt não é sobrescrito: ele simplesmente nunca é alcançado. Reordenar não muda nada; a única coisa que muda algo é tornar o diretório percorrível de novo, e é isso que build/* faz.

Como eu testo uma regra do .gitignore?

Cole o caminho do arquivo e suas regras no depurador no topo desta página. Ele dá o veredito, a regra e a linha decisivas, e todas as regras que corresponderam no caminho — incluindo negações que nunca poderiam ter surtido efeito.

Num repositório de verdade, git check-ignore -v path/to/file nomeia o arquivo de origem, a linha e o padrão responsáveis, e git status --ignored lista tudo que está ignorado agora.

O .gitignore afeta arquivos que já estão sendo rastreados?

Não. O .gitignore só vale para caminhos não rastreados. A partir do momento em que um arquivo entrou num commit, o Git continua rastreando ele e a regra é ignorada — é o motivo mais comum para uma regra perfeitamente correta parecer não fazer nada. Para parar de rastrear um arquivo sem apagar ele, rode git rm --cached path/to/file e faça o commit dessa mudança.

Um detalhe que confunde: o git check-ignore ainda vai reportar uma correspondência para um arquivo rastreado, porque ele testa os padrões sem consultar o índice. Um “sim, ignorado” desse comando não significa que o Git parou de rastrear o arquivo.

As regras do .gitignore podem ter espaços?

Sim, no meio. my logs/ corresponde a um diretório com um espaço no nome, sem escape nenhum. Espaços no fim são outra história: o Git corta eles, então *.log seguido de um espaço se comporta exatamente como *.log. Para corresponder a um nome de arquivo que realmente termina em espaço, escape ele — foo\ .

Espaços no começo são significativos e nunca são cortados, então uma regra indentada por acidente só corresponde a caminhos que começam com aquele espaço. Se uma regra parecer inerte sem motivo, confira se não tem indentação sobrando.

Onde o Git procura regras de exclusão além do .gitignore?

Em três outros lugares, consultados nesta ordem de precedência: os próprios arquivos .gitignore, com o diretório mais fundo vencendo; depois o .git/info/exclude, que é do repositório e nunca vai para um commit; depois o que o core.excludesFile apontar, sua lista de exclusão global.

Então uma regra que você não acha em lugar nenhum do repositório geralmente está num desses dois últimos. git config --get core.excludesFile revela o caminho global, e git check-ignore -v nomeia o arquivo que realmente decidiu.

Qual é a diferença entre * e **?

O * corresponde a qualquer sequência de caracteres menos /, então ele nunca sai de um único segmento de caminho. src/*/test.js desce exatamente um nível.

O ** atravessa separadores, mas só nas três posições que o Git reconhece — um **/ no começo para qualquer profundidade, um /** no fim para tudo que está dentro de um diretório, e /**/ no meio para zero ou mais diretórios. Em qualquer outro lugar, como em a**b, os asteriscos extras se comportam como um * só.