Como as regras de exclusão de gitignore realmente funcionam?

Eu estou tentando resolver um problema gitignore em uma estrutura de diretório grande, mas para simplificar a minha pergunta eu reduzi-lo para o seguinte.

Eu tenho a seguinte estrutura de diretórios de dois arquivos (foo, bar) em um novo repository git (sem commits até agora):

a/b/c/foo a/b/c/bar 

Obviamente, um ‘git status -u’ mostra:

 # Untracked files: ... # a/b/c/bar # a/b/c/foo 

O que eu quero fazer é criar um arquivo .gitignore que ignora tudo dentro de um / b / c, mas não ignora o arquivo ‘foo’.

Se eu criar um .gitignore assim:

 c/ 

Então um ‘git status -u’ mostra ambos foo e bar como ignorados:

 # Untracked files: ... # .gitignore 

Qual é o que eu espero.

Agora, se eu adicionar uma regra de exclusão para foo, assim:

 c/ !foo 

De acordo com o gitignore manpage, eu esperaria que isso funcionasse. Mas isso não acontece – ainda ignora foo:

 # Untracked files: ... # .gitignore 

Isso não funciona:

 c/ !a/b/c/foo 

Nem isso:

 c/* !foo 

Dá:

 # Untracked files: ... # .gitignore # a/b/c/bar # a/b/c/foo 

Nesse caso, embora foo não seja mais ignorado, a barra também não é ignorada.

A ordem das regras no .gitignore também não parece importar.

Isso também não faz o que eu esperava:

 a/b/c/ !a/b/c/foo 

Aquele ignora tanto foo e bar.

Uma situação que funciona é se eu criar o arquivo a / b / c / .gitignore e colocar lá:

 * !foo 

Mas o problema com isso é que eventualmente haverá outros subdiretórios em um / b / c e eu não quero ter que colocar um .gitignore separado em cada um deles – eu estava esperando criar um ‘projeto baseado’ .gitignore arquivos que podem ser colocados no diretório superior de cada projeto e cobrem toda a estrutura de subdiretórios ‘padrão’.

Isso também parece ser equivalente:

 a/b/c/* !a/b/c/foo 

Esta pode ser a coisa mais próxima de “trabalhar” que eu posso alcançar, mas os caminhos relativos completos e exceções explícitas precisam ser declarados, o que será uma dor se eu tiver muitos arquivos de nome ‘foo’ em diferentes níveis. da tree de subdiretórios.

De qualquer forma, ou eu não entendo muito bem como as regras de exclusão funcionam, ou elas não funcionam quando diretórios (em vez de curingas) são ignorados – por uma regra que termina em um /

Alguém pode por favor lançar alguma luz sobre isso?

Existe uma maneira de fazer o gitignore usar algo sensato como expressões regulares em vez dessa syntax desajeitada baseada em shell?

Eu estou usando e observe isso com git-1.6.6.1 no Cygwin / bash3 e git-1.7.1 no Ubuntu / bash3.

  /abc/*
 foo 

Parece funcionar para mim (git 1.7.0.4 no Linux). O * é importante, caso contrário você está ignorando o próprio diretório (assim o git não irá olhar para dentro) em vez dos arquivos dentro do diretório (o que permite a exclusão).

Pense nas exclusões como dizendo “mas não este” em vez de “mas inclua isto” – “ignore este diretório ( /a/b/c/ ) mas não este ( foo )” não faz muito sentido; “ignora todos os arquivos neste diretório ( /a/b/c/* ) mas não este ( foo )” faz. Para citar a página man:

Um prefixo opcional! que nega o padrão; qualquer arquivo correspondente excluído por um padrão anterior será incluído novamente.

isto é, o arquivo deve ter sido excluído já para ser incluído novamente. Espero que lance alguma luz.

Eu tenho uma situação semelhante, minha solução foi usar:

 /a/**/* !/a/**/foo 

Isso deve funcionar para um número arbitrário de diretórios intermediários se eu ler ** corretamente.

Aqui está outra opção:

 * !/a* !/a/* !/a/*/* !/a/*/*/* 

Isso iria ignorar todos os arquivos e diretórios, exceto arquivos / diretórios de três níveis profundos dentro de um arquivo.

isso definitivamente não está claro na página do manual .gitignore. Isso funciona:

 * !/a !/a/b !/a/b/c !/a/b/c/foo # don't forget this one !.gitignore 

Como mencionado por Chris, um diretório nem sequer é aberto se for excluído. Então, se você quer ser capaz de ignorar *, mas alguns arquivos, você tem que construir o caminho para esses arquivos como acima. Para mim isso é conveniente, porque eu quero fazer uma revisão de código em um arquivo de uma biblioteca e se eu quiser fazer outra depois eu apenas adiciono, e todo o resto é ignorado.

Em uma nota mais geral, git1.8.2 includeá o patch (também em sua v4 , solicitado por alguma questão do Stack Overflow ) de Adam Spiers sobre como determinar qual regra gitignore realmente ignora seu arquivo.

Veja as notas de lançamento do git1.8.2 e a pergunta SO ” qual regra do gitignore está ignorando o meu arquivo “:
esse será o comando git check-ignore .