Idioma:Português
FERRAMENTA DEV

Gitignore Debugger

Descubra exactamente porque é que o Git não ignora o seu ficheiro.

Cole um caminho de ficheiro e as suas regras .gitignore. Mostramos-lhe que regra corresponde, e porquê.

Tudo corre no seu navegador. Os seus ficheiros nunca são enviados.

O que é um ficheiro .gitignore?

Um ficheiro .gitignore é um ficheiro de texto que diz ao Git quais os ficheiros a deixar fora do controlo de versões. Cada linha é um padrão — *.log, node_modules/, .env — e qualquer caminho não seguido que corresponda a um padrão desaparece do git status, é saltado pelo git add . e nunca é submetido.

A expressão não seguido carrega quase todo o significado dessa frase e, por si só, explica mais casos de «o gitignore não funciona» do que todos os erros de padrão juntos. O Git só consulta o .gitignore para ficheiros que ainda não segue. A partir do momento em que um ficheiro é submetido, o Git continua a segui-lo indefinidamente, por muito que acrescente depois ao .gitignore.

O que faz o Gitignore Debugger

Cole um caminho de ficheiro e o conteúdo do seu .gitignore: este depurador de .gitignore devolve o mesmo veredicto que o Git — ignorado ou não ignorado — juntamente com a regra exacta e o número de linha responsáveis. Depois lista todas as regras que correspondem ao caminho, na ordem em que o Git as avalia, e assinala a que realmente decidiu.

É esta última parte que um simples verificador de gitignore deixa de fora. Saber que um ficheiro está ignorado raramente é a parte difícil. Saber qual das suas quarenta regras o fez — e porque é que a que esperava perdeu — é que é. Funciona nos dois sentidos: porque é que o Git ignora o ficheiro que quer submeter, e porque é que não ignora aquele de que se quer livrar.

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 / reservados a diretórios, as classes de caracteres, os espaços finais escapados. O seu comportamento é verificado contra a saída real do git check-ignore, não presumido.

Como utilizar

  1. Introduza o caminho tal como aparece no seu repositório, relativo à raiz — src/config/local.json, não C:\projects\app\src\config\local.json. Use barras; as barras invertidas do Windows são convertidas por nós.
  2. Cole o seu .gitignore por inteiro. Não o reduza à única regra de que suspeita — a precedência entre regras é geralmente o verdadeiro erro, e cortar o ficheiro deita fora essa informação.
  3. Leia o veredicto 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 ficheiro, termine o caminho com uma barra — build/. A distinção importa, porque uma regra terminada em / só corresponde a diretórios.

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

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

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

Esta é a que apanha todos. Dentro do mesmo .gitignore, é o último padrão que corresponde a um caminho que decide o seu destino, não o primeiro. Estes dois ficheiros contê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

Portanto ponha as regras amplas primeiro e as excepções depois. Entre ficheiros, um .gitignore num subdiretório vence outro acima dele para os caminhos abaixo. O Git também lê o .git/info/exclude, próprio do repositório e nunca submetido, e o seu core.excludesFile global. Uma regra que não encontra em nenhum .gitignore esconde-se muitas vezes num desses dois lugares — git check-ignore -v mostra o ficheiro e a linha que decidiram.

Regras de negação, e porque é que ! falha tantas vezes

Prefixar um padrão com ! volta a incluir algo que uma regra anterior tinha ignorado. Isto tem dois limites, e o segundo explica praticamente todos os casos de «a negação do gitignore não funciona».

Primeiro, algo anterior tem de ter ignorado o caminho. !important.log sozinho não faz nada, porque nunca houve nada a sobrepor.

Segundo, e bem mais surpreendente: não é possível voltar a incluir um ficheiro 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 lá. Não examina build/keep.txt, pelo que nunca chega à linha 2. Ignore em vez disso o conteúdo do diretório, o que deixa o próprio diretório percorrível:

build/*
!build/keep.txt

Para salvar algo mais profundo, cada diretório intermédio também tem de continuar acessível — build/*, depois !build/assets/, depois !build/assets/logo.svg.

Diretórios superiores ignorados

O percurso de diretórios do Git pára no primeiro diretório ignorado que encontra, e tudo o que está abaixo herda o veredicto sem ser examinado. É por isso que um diretório superior ignorado vence qualquer regra escrita para o próprio ficheiro, por específica que pareça.

Nesse caso, o depurador nomeia o diretório que bloqueou o percurso em vez de dizer apenas que o ficheiro está ignorado, e continua a mostrar as regras de negação que escreveu para o ficheiro — marcadas como inalcançáveis, porque o Git nunca foi longe o suficiente para as considerar. Corrija primeiro o diretório e as regras abaixo voltam a funcionar por si.

Erros comuns de .gitignore

Alguns exemplos práticos

O caminho src/logs/debug.log face a estas regras não está ignorado. A negação é a última correspondência e nenhum diretório superior bloqueia o percurso, pelo que é alcançada e vence:

*.log
!src/logs/debug.log

O caminho build/keep.txt face a estas regras está ignorado, por causa de build/ na linha 1, que corresponde ao diretório superior. A linha 2 nunca é activada:

build/
!build/keep.txt

O caminho docs/api/notes.md face a esta regra não está ignorado, porque o padrão está ancorado pela sua barra e o * não atravessa 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 integrado — nomeia o ficheiro de origem, o número de linha e o padrão que decidiram, e vale a pena conhecê-lo.

Perguntas frequentes

Porque é que o Git ignora o meu ficheiro?

Algum padrão corresponde-lhe, e muitas vezes não é aquele que está a olhar. Os culpados habituais são um .gitignore num subdiretório, um diretório superior ignorado, ou o seu ficheiro de exclusão global. Execute git check-ignore -v path/to/file: mostra exactamente o ficheiro de origem, o número de linha e o padrão que decidiram. Colar o caminho e as regras no depurador acima mostra o mesmo, mais as regras que correspondiam e perderam.

Porque é que a minha regra do .gitignore não funciona?

Quatro causas cobrem quase todos os casos, mais ou menos por ordem de frequência: o ficheiro já está a ser seguido, pelo que a regra é totalmente ignorada; uma regra posterior sobrepôs-se-lhe, porque vence a última correspondência; um diretório superior está ignorado, pelo que o Git nunca chegou à regra; ou o padrão está ancorado de forma diferente do que 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.

Porque é que o ! não volta a incluir o meu ficheiro?

Ou nada anterior tinha ignorado o caminho, e portanto a negação não tinha nada a sobrepor, ou — bem mais provável — o ficheiro está dentro de um diretório ignorado. O Git não entra em diretórios ignorados, pelo que nunca vê o ficheiro e nunca avalia a sua regra !.

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

Vence a última regra do .gitignore?

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

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

Porque o percurso de diretórios do Git pára no primeiro diretório ignorado. Quando build/ corresponde, o Git marca toda a subárvore como ignorada sem listar o que está dentro, pelo que !build/keep.txt não é sobreposto: simplesmente nunca é alcançado. Reordenar não muda nada; só voltar a tornar o diretório percorrível muda algo, e é isso que build/* faz.

Como testo uma regra do .gitignore?

Cole o caminho do ficheiro e as suas regras no depurador no topo desta página. Dá-lhe o veredicto, a regra e a linha decisivas, e todas as regras que corresponderam pelo caminho — incluindo negações que nunca poderiam ter surtido efeito.

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

O .gitignore afecta ficheiros já seguidos?

Não. O .gitignore só se aplica a caminhos não seguidos. A partir do momento em que um ficheiro é submetido, o Git continua a segui-lo e a regra é ignorada — é a razão mais frequente para uma regra perfeitamente correcta parecer não fazer nada. Para deixar de seguir um ficheiro sem o apagar, execute git rm --cached path/to/file e submeta essa alteração.

Um detalhe que engana: o git check-ignore continua a reportar uma correspondência para um ficheiro seguido, porque testa os padrões sem consultar o índice. Um «sim, ignorado» desse comando não significa que o Git tenha deixado de seguir o ficheiro.

As regras do .gitignore podem conter espaços?

Sim, no meio. my logs/ corresponde a um diretório cujo nome contém um espaço, sem qualquer escape. Os espaços finais são outra história: o Git corta-os, pelo que *.log seguido de um espaço comporta-se exactamente como *.log. Para corresponder a um nome de ficheiro que realmente termina em espaço, escape-o — foo\ .

Os espaços iniciais são significativos e nunca são cortados, pelo que uma regra indentada por acidente só corresponde a caminhos que começam por esse espaço. Se uma regra parecer inerte sem razão, verifique se não tem indentação a mais.

Onde é que 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 ficheiros .gitignore, vencendo o diretório mais profundo; depois o .git/info/exclude, próprio do repositório e nunca submetido; depois o que o core.excludesFile apontar, a sua lista de exclusão global.

Portanto uma regra que não encontra em parte alguma do repositório está geralmente num desses dois últimos. git config --get core.excludesFile revela o caminho global, e git check-ignore -v nomeia o ficheiro que realmente decidiu.

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

O * corresponde a qualquer sequência de caracteres excepto /, pelo que nunca sai de um único segmento de caminho. src/*/test.js desce exactamente um nível.

O ** atravessa separadores, mas apenas nas três posições que o Git reconhece — um **/ inicial para qualquer profundidade, um /** final para tudo o 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 extra comportam-se como um único *.