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

146

Estou tentando resolver um problema de gitignore em uma grande estrutura de diretórios, mas para simplificar minha pergunta, reduzi-a ao seguinte.

Eu tenho a seguinte estrutura de diretórios de dois arquivos (foo, bar) em um novo repositório git (nenhum commit até agora):

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

Obviamente, um 'status git -u' mostra:

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

O que eu quero fazer é criar um arquivo .gitignore que ignore tudo dentro de a / b / c, mas não ignore o arquivo 'foo'.

Se eu criar um .gitignore assim:

c/

Em seguida, um 'git status -u' mostra foo e bar como ignorados:

# Untracked files:
...
#       .gitignore

O que é como eu espero.

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

c/
!foo

De acordo com a página de manual do gitignore, eu esperaria que isso funcionasse. Mas não - ainda ignora:

# Untracked files:
...
#       .gitignore

Isso também 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, bar também não é ignorado.

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

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

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

Aquele ignora tanto foo quanto bar.

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

*
!foo

Mas o problema é que, eventualmente, haverá outros subdiretórios sob a / b / ce não quero colocar um .gitignore separado em cada um deles - eu esperava criar um .gitignore baseado em projeto arquivos que podem ficar no diretório superior de cada projeto e abranger toda a estrutura de subdiretórios 'padrão'.

Isso também parece ser equivalente:

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

Isso pode ser a coisa mais próxima do "trabalho" 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 com o nome 'foo' em diferentes níveis da árvore do subdiretório.

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

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

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

Estou usando e observe isso com o git-1.6.6.1 no Cygwin / bash3 e o git-1.7.1 no Ubuntu / bash3.

davidA
fonte
pergunta relacionada: stackoverflow.com/questions/2820255/…
Cascabel
4
Excelentemente pergunta escrita! A quantidade de casos que você tentou realmente me ajudou a entender o que estava fazendo de errado no meu caso semelhante. Graças a Deus, estou procurando há muito tempo hoje.
Alex G

Respostas:

153
/abc/*
! foo

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

Pense nas exclusões dizendo "mas não este" em vez de "mas inclua este" - "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)". Para citar a página do manual:

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

ou seja, o arquivo já deve ter sido excluído para ser incluído novamente. Espero que isso mostre alguma luz.

Chris
fonte
Sim, o exemplo que você dá também funciona bem para mim. O problema é que / a / b / * não funciona se foo estiver em c, o que significa que se eu tiver vários arquivos foo em vários subdiretórios em c (por exemplo, d /, e /, f / g / h /, i / j /, etc), então a regra de negação '! foo' não os captura.
Davida
1
@meowsqueak Na verdade, não vai. Se você ignorar / a / b / *, não haverá um diretório para o foo estar! Se você está tentando ignorar toda a árvore de diretórios em / a / b, mas inclui qualquer arquivo chamado "foo" que possa estar em qualquer lugar da árvore, não acho que seja possível com padrões .gitignore: \
Chris
Na verdade, isso pode explicar por que isso não funciona para mim - as seguintes regras não funcionam: "/ a / b / *", "! C / foo". Se isso funcionou, pode não haver um problema.
Davida
5
"Eu não acho que seja possível com padrões .gitignore" - acho que você está certo, infelizmente.
Davida
@meowsqueak Você poderia ignorar /a/b/ce, em seguida, escrever um gancho para git add --forcequalquer arquivo correspondente pré-compromisso ou algo assim
Chris
24

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.

medge799
fonte
6

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 é aberto se for excluído. Portanto, se você deseja ignorar *, mas alguns arquivos, é necessário criar 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 quiser fazer outro mais tarde, basta adicioná-lo e todo o resto é ignorado.


fonte
6

Aqui está outra opção:

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

Isso ignoraria todos os arquivos e diretórios, exceto arquivos / diretórios com três níveis de profundidade em um arquivo.

Peter Petrik
fonte
5

Em uma observação mais geral, o git1.8.2 incluirá o patch (também em sua v4 , solicitado por alguma pergunta do Stack Overflow ) de Adam Spiers sobre como determinar qual gitignoreregra realmente ignora seu arquivo.

Veja as notas de versão do git1.8.2 e a pergunta SO " que regra do gitignore está ignorando meu arquivo ":
esse será o comando git check-ignore.

VonC
fonte
1
Obrigado por transmitir esta informação. check-ignoretambém informará qual regra está impedindo que seu arquivo seja ignorado, se for esse o caso.
Adam Spires
@AdamSpiers parece ótimo. Com certeza vou ter que brincar com isso.
VonC