Unignore subdiretórios de diretórios ignorados no Git

99

Digamos que eu ignorei um diretório, mas desejo cancelar a ignorar subdiretórios específicos nele. Então, eu tenho a configuração:

/uploads/
/uploads/rubbish/
/uploads/rubbish/stuff/KEEP_ME/
/uploads/foo/
/uploads/foo/bar/lose/

E eu quero ignorar tudo, exceto o KEEP_MEdiretório. Eu espero que o ignorar seja algo como:

/uploads/*
!/uploads/rubbish/stuff/KEEP_ME/

Mas isso não está funcionando, e nem várias permutações no mesmo tema.

Aquele que funciona é

/uploads/**/**/**/
!/uploads/rubbish/stuff/KEEP_ME/

Mas isso parece um pouco restritivo e prolixo?

Wil
fonte
1
O código que você forneceu não funcionou para mim (usando git versão 1.9.1). Um que funcionou é / uploads / *! / Uploads / lixo / / uploads / lixo / *! / Uploads / lixo / coisas / uploads / lixo / coisas / *! / Uploads / lixo / coisas / manter (que é semelhante ao exemplo em git-scm.com/docs/gitignore ). Minha estrutura de pasta; └ README.md └ uploads / foo / bar / Loss / Loss.txt └ uploads / lixo / coisas / keep / keep.txt
gihanchanuka

Respostas:

119

De acordo com a seção de formato de padrão da documentação do gitignore :

Um prefixo opcional "!" que nega o padrão; qualquer arquivo correspondente excluído por um padrão anterior será incluído novamente. Não é possível incluir novamente um arquivo se um diretório pai desse arquivo for excluído. Git não lista diretórios excluídos por motivos de desempenho, portanto, quaisquer padrões nos arquivos contidos não têm efeito, não importa onde sejam definidos. Coloque uma barra invertida ("\") antes do primeiro "!" para padrões que começam com um "!" literal, por exemplo, "! important! .txt".

Portanto, o /uploads/rubbish/stuff/keep/padrão do diretório pai excluído anteriormente deve ser negado exclusivamente antes de negar seu conteúdo:

#ignore everything within /uploads/ 
/uploads/*

#include everything within /uploads/rubbish/stuff/keep
!/uploads/rubbish/stuff/keep/  
!/uploads/rubbish/stuff/keep/*

Para incluir subdiretórios dentro, /uploads/rubbish/stuff/keep/adicione a terceira linha:

!/uploads/rubbish/stuff/keep/**/*
Art Shayderov
fonte
1
Qual versão do git você estava usando? Talvez seja algo disponível apenas em algumas versões.
Envergonhado. Eu esqueci que postei este comentário. Eu consegui fazer isso funcionar, no entanto. FWIW, eu não uso as barras iniciais. Não me lembro se esse era o problema. Estou usando o 1.7.2.5.
Eu tenho um arquivo gitignore global onde ignorei todos os arquivos * .zip. No entanto, para um projeto específico, desejo incluir arquivos zip. Eu adicionei essa linha àquele projeto .gitignoree funciona muito bem !:!*.zip
Jinghao Shi,
Achei necessário especificar explicitamente diretórios arbitrários antes e depois de determinada pasta , excluindo a pasta e incluindo a subpasta . ( Resposta completa ).
Josiah Yoder
Este método não funciona (a partir do git 2.25 - talvez algo no git tenha mudado). Postei stackoverflow.com/a/60288435/295691 abaixo como uma correção.
user295691
34

Mesmo se você adicionar algo .gitignore, você pode forçar o git a adicioná-lo ao índice

git add --force uploads/rubbish/stuff/KEEP_ME/

No entanto, "KEEP_ME" parece ser um diretório e o git geralmente não gosta de pastas vazias, então você deve adicionar um arquivo de "espaço reservado", se a pasta estiver vazia

git add --force uploads/rubbish/stuff/KEEP_ME/.keep_me
KingCrunch
fonte
O único problema com isso é que este exemplo específico se refere a um gitignore global que eu deveria ter mencionado. Sei que posso forçar a adição de itens, mas terei que fazer isso se quaisquer novos itens forem adicionados, bem como inicialmente para cada novo repositório. O .keep garante que o conteúdo não seja ignorado?
Wil
1
Fiz um pequeno teste. Sim, você deve usar git add --forcepara cada novo item ou pasta em KEEP_ME. mas depois disso, as atualizações são adicionadas (ao fazer um, git add -upor exemplo) como se os arquivos estivessem 'não ignorados'. O .keep_menão faz nada, é apenas um arquivo para que o git opere na pasta.
commonpike de
1
git add --forceé a solução errada para isso. Se você mover um diretório ou mover o arquivo para outro diretório, o rastreamento do git excluirá o arquivo, mas não o adicionará ao novo local e você terá que fazer git add --forcenovamente. ao cancelar a ignição em um padrão genérico, o git rastreará o movimento de um arquivo dentro do repo.
Merlin
Esta é a única solução que funcionou para mim. Minha pasta NÃO foi ignorada, mas não pude adicioná-la de forma alguma. Assim resolveu para mim.
Akito
6

Estava tentando descobrir como incluir uma pasta específica ao excluir todas as pastas com a exclusão genérica

**/build

se você adicionar o /*ao final de sua exclusão genérica, você continuará a excluir todos os arquivos de construção**/build/*

Então você adiciona outra linha para corrigir a aparência do caminho que deseja incluir

!**/folder/build/* 

deixando-nos um gitignore que lê

**/build/* 
!**/folder/build/* 
John
fonte
2

As respostas de Buo-Ren Lin e John são bastante úteis, mas eu precisava combinar as duas.

Ao querer excluir outras subpastas, bem como arquivos dentro de uploads, achei necessário especificar explicitamente diretórios arbitrários antes da pasta fornecida , excluindo a pasta e incluindo a subpasta

**/uploads/*
!**/uploads/rubbish/
!**/uploads/rubbish/*

Também achei necessário reincluir explicitamente a subpasta e seu conteúdo para mostrar os itens dentro da pasta e das subpastas.

Josiah Yoder
fonte
Suspiro. Sorte zero aqui. Nisto há horas. Git 2.20.1
RichieHH
@ HörmannHH Lamento ouvir isso. Talvez seja hora de fazer uma nova pergunta?
Josiah Yoder
2

A solução apresentada como a resposta mais votada é incorreta e facilmente demonstrável como tal.

Comece ignorando tudo nos uploads / *:

mkdir -p uploads/rubbish/stuff/KEEP_ME
touch uploads/a uploads/rubbish/a uploads/rubbish/stuff/a uploads/rubbish/stuff/KEEP_ME/a
echo '/uploads/*' >> .gitignore
git init
git add .
git commit -m "Initial commit"

Agora cancele a ignorar o diretório pai das coisas ignoradas como acima:

echo 'uploads/rubbish/stuff/KEEP_ME/' >> .gitignore
echo 'uploads/rubbish/stuff/KEEP_ME/*' >> .gitignore
git status -u

Não mostra arquivos não rastreados.

Para fazê-lo funcionar, você precisa ignorar todos os arquivos sob a uploads/árvore ( uploads/**/*não apenas o nível superior uploads/*) e, em seguida, adicionar todos os diretórios pais da árvore que deseja manter

echo '/uploads/**/*' > .gitignore
echo '!/uploads/rubbish/' >> .gitignore
echo '!/uploads/rubbish/stuff' >> .gitignore
echo '!/uploads/rubbish/stuff/KEEP_ME' >> .gitignore
echo '!/uploads/rubbish/stuff/KEEP_ME/*' >> .gitignore
git status -u

Que dá:

On branch master
...
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        uploads/rubbish/stuff/KEEP_ME/a

Se tivéssemos usado uploads/*na .gitignoreacima, em seguida, todos os arquivos intermediários teria sido incluído, bem como, assim, por exemplo uploads/rubbish/airia aparecer no comando status acima.

user295691
fonte