.gitignore excluir pasta, mas inclui subpasta específica


965

Eu tenho a pasta application/que adiciono ao .gitignore. Dentro da application/pasta está a pasta application/language/gr. Como posso incluir esta pasta?

Eu tentei isso

application/
!application/language/gr/

sem sorte ...


1
Felizmente, a .gitignoredocumentação do "formato padrão" ficou mais clara (dezembro de 2013). Veja minha resposta abaixo
VonC:

Minha pergunta e resposta favorita, adicionadas aos favoritos e aos favoritos do navegador.
codekiddy

Respostas:


1614

Se você excluir application/, tudo o que estiver abaixo será sempre excluído (mesmo que algum padrão de exclusão negativa posterior (“não-reconhecido”) possa corresponder a algo abaixo application/).

Para fazer o que você deseja, é necessário "cancelar a assinatura" de todos os diretórios pai de tudo o que você deseja "cancelar a autenticação". Geralmente, você acaba escrevendo regras para essa situação em pares: ignora tudo em um diretório, mas não em um determinado subdiretório.

# you can skip this first one if it is not already excluded by prior patterns
!application/

application/*
!application/language/

application/language/*
!application/language/gr/

Nota
O final /*é significativo:

  • O padrão dir/exclui um diretório chamado dire (implicitamente) tudo sob ele.
    Com dir/, o Git nunca examinará nada abaixo dire, portanto, nunca aplicará nenhum dos padrões de "exclusão" a nada abaixo dir.
  • O padrão dir/*não diz nada sobre dirsi; apenas exclui tudo que está abaixo dir. Com dir/*, o Git processará o conteúdo direto dir, dando a outros padrões a chance de "excluir" um pouco do conteúdo ( !dir/sub/).

12
Os asteriscos à direita são significativos? Se sim, qual é a diferença de significado? De acordo com o algoritmo descrito na documentação do gitignore , terminar com uma barra final corresponde a um diretório e caminhos abaixo desse diretório. Terminar com um asterisco passaria a ser tratado como um padrão glob. A experiência mostra a variante de asterisco para funcionar, mas não a que termina em apenas uma barra final. Eu gostaria de entender por que isso é assim.
seh

123
@seh: Sim, a trilha /*é significativa. Se um diretório for excluído, o Git nunca examinará o conteúdo desse diretório. O padrão dir/exclui um diretório chamado dire (implicitamente) tudo sob ele. O padrão dir/*não diz nada sobre dirsi; apenas exclui tudo que está abaixo dir. Com dir/, o Git nunca examinará nada abaixo dire, portanto, nunca aplicará nenhum dos padrões de "exclusão" a nada abaixo dir. Com dir/*, o Git processará o conteúdo direto dir, dando a outros padrões a chance de "excluir" um pouco do conteúdo ( !dir/sub/).
Chris Johnsen

7
Ah, isso explica isso. Não importa quantas vezes eu tenha lido a documentação do gitignore , nunca entendi quando os padrões revertidos não funcionam. Com sua explicação, agora está claro. A documentação do gitignore precisa de uma seção "receita" para explicar como fazer isso.
seh

8
Eu não consegui fazer isso funcionar (arquivo louco .gitignore!), Então, em vez disso, apenas forcei a adição dos arquivos após o cd'ing no diretório que eu queria. git add -f .
K0D4 21/07

2
Observe que você não pode confiar na saída de git status, o que dirá apenas que o diretório de nível superior será adicionado. Em vez disso, faça um git adddiretório do nível superior e, em seguida git status, (esperançosamente) listará o subconjunto de arquivos que foram correspondidos pelo padrão.
Matthew Strawbridge 02/09

136

O compromisso 59856de de Karsten Blees (kblees) para o Git 1.9 / 2.0 (primeiro trimestre de 2014) esclarece esse caso:

gitignore.txt: esclarecer a natureza recursiva dos diretórios excluídos

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. ( *)
( *: a menos que determinadas condições sejam atendidas no git 2.8+, veja abaixo) O
Git não lista os diretórios excluídos por motivos de desempenho; portanto, quaisquer padrões nos arquivos contidos não têm efeito, independentemente de onde estejam definidos.

Coloque uma barra invertida (" \") na frente do primeiro " !" para padrões que começam com um literal " !", por exemplo, " \!important!.txt".

Exemplo para excluir tudo, exceto um diretório específico foo/bar(observe /*- sem a barra, o curinga também excluiria tudo dentro foo/bar):

 --------------------------------------------------------------
     $ cat .gitignore
     # exclude everything except directory foo/bar
     /*
     !/foo
     /foo/*
     !/foo/bar
 --------------------------------------------------------------

No seu caso:

application/*
!application/**/
application/language/*
!application/language/**/
!application/language/gr/**

Você deve primeiro pastas da lista branca , antes de poder listar arquivos em uma determinada pasta.


Atualização fevereiro / março de 2016:

Observe que, com o git 2.9.x / 2.10 (meados de 2016?), Pode ser possível incluir novamente um arquivo se um diretório pai desse arquivo for excluído se não houver curinga no caminho incluído novamente .

Nguyễn Thái Ngọc Duy ( pclouds) está tentando adicionar este recurso:

Portanto, com o git 2.9+, isso poderia realmente funcionar, mas acabou sendo revertido:

application/
!application/language/gr/

Eu tentei usar o atualizados re-incluem sintaxe publicado no final de sua resposta sobre git para Windows v2.8.1.windows.1, mas ele não aparece para trabalhar :(
David Hancock

1
@DavidHancock Desculpe, editei a resposta: isso ainda não está disponível.
VonC 12/04

1
@DavidHancock também: são mais de 13 respostas de estouro de pilha que tive que editar várias vezes!
VonC 12/04

5
O Git 2.9 foi lançado ontem. A confirmação do padrão application/+ !application/language/gr/mencionado na resposta está funcionando conforme o esperado.
Ray Shan

1
@RayShan Strange: Eu vi o commit revert cancelando esse recurso, mas não o vi (e a nota de lançamento github.com/git/git/blob/master/Documentation/RelNotes/2.9.0.txt não menciona) qualquer confirmação que melhore as regras .gitignore.
VonC

52

A resposta de @Chris Johnsen é ótima, mas com as versões mais recentes do Git (1.8.2 ou posterior), existe um padrão de asterisco duplo que você pode aproveitar para obter uma solução um pouco mais abreviada:

# assuming the root folder you want to ignore is 'application'
application/**/*

# the subfolder(s) you want to track:
!application/language/gr/

Dessa forma, você não precisa "cancelar a assinatura" do diretório pai da subpasta que deseja rastrear.


Com o Git 2.17.0 (Não sei ao certo quanto tempo antes desta versão. Possivelmente, retorne à 1.8.2), o uso do **padrão combinado com exclusões para cada subdiretório que leva ao (s) arquivo (s) funciona. Por exemplo:

# assuming the root folder you want to ignore is 'application'
application/**

# Explicitly track certain content nested in the 'application' folder:
!application/language/
!application/language/gr/
!application/language/gr/** # Example adding all files & folder in the 'gr' folder
!application/language/gr/SomeFile.txt # Example adding specific file in the 'gr' folder

9
Infelizmente, isso falha (Git 1.8.4.msysgit.0) porque o padrão **pode corresponder a subpastas zero e *irá corresponder languagee excluí-lo, impedindo a inclusão de gr. A cadeia completa de pais que Chris Johnson recomenda ainda parece necessária.
Sean Gugler 14/02

3
Parece perfeito, mas não funciona para mim no git 2.3.7 ... /www/**/* !/www/config.xml !/www/resconfig.xml e o diretório res ainda são ignorados.
Rob

@ Rob você também precisa adicionar !/www/res/. Você pode usar o folder/**/*padrão, mas ainda precisará adicionar exclusões para cada subdiretório que deseja adicionar. Ainda é mais curto e mais legível do que a combinação de ignorar / excluir.
21718 Ben Kane

Eu sei que é um comentário antigo, mas caso você esteja curioso. Adicionei uma edição à resposta que documenta essa abordagem.
21718 Ben Kane

21

Há várias perguntas semelhantes sobre isso, então vou postar o que escrevi antes:

A única maneira de fazer isso funcionar na minha máquina era fazê-lo desta maneira:

# Ignore all directories, and all sub-directories, and it's contents:
*/*

#Now ignore all files in the current directory 
#(This fails to ignore files without a ".", for example 
#'file.txt' works, but 
#'file' doesn't):
*.*

#Only Include these specific directories and subdirectories:
!wordpress/
!wordpress/*/
!wordpress/*/wp-content/
!wordpress/*/wp-content/themes/
!wordpress/*/wp-content/themes/*
!wordpress/*/wp-content/themes/*/*
!wordpress/*/wp-content/themes/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*/*

Observe como você deve permitir explicitamente o conteúdo para cada nível que você deseja incluir. Portanto, se eu tiver subdiretórios 5 em temas, ainda preciso esclarecer isso.

Isto é do comentário de @ Yarin aqui: https://stackoverflow.com/a/5250314/1696153

Estes foram tópicos úteis:

Eu também tentei

*
*/*
**/**

e **/wp-content/themes/**

ou /wp-content/themes/**/*

Nada disso funcionou para mim também. Muitas tentativas e erros!


1
Nota: Lembre-se de que a ordem também é importante em um arquivo .gitignore. Portanto, certifique-se de colocar as !regras na parte inferior.
Starbeamrainbowlabs 12/08/19


8

A maneira mais simples e provavelmente a melhor é tentar adicionar os arquivos manualmente (geralmente isso tem precedência sobre as .gitignoreregras de estilo):

git add /path/to/module

Você pode até querer -N adicionar o sinalizador, sugerindo que você os adicione, mas não imediatamente. Costumo fazer isso para novos arquivos que ainda não estou pronto para preparar.


Esta é uma cópia de uma resposta publicada sobre o que poderia ser facilmente um controle de qualidade duplicado. Estou reposicionando-o aqui para aumentar a visibilidade - acho mais fácil não ter uma confusão de regras de gitignore.


2
Obrigado por esta resposta. Eu tive que adicionar -f, pois está no meu .gitignore, assim: git add -f caminho / para / arquivo
jasonflaherty

6

Então, já que muitos programadores usam o nó. o caso de uso que atende a essa pergunta é excluir, node_modulesexceto um módulo, module-apor exemplo:

!node_modules/

node_modules/*
!node_modules/module-a/

2
Não funciona para a versão git 2.10.2.windows.1.
28416 Sebastian

1
Para a versão 2.10.2 do git, siga esta resposta
nalexn

👌 ótimo 👋 👋.
Abdennour TOUMI

6

Adicione uma resposta adicional:

!/.vs/              <== include this folder to source control, folder only, nothing else
/.vs/*              <== but ignore all files and sub-folder inside this folder
!/.vs/ProjectSettings.json <== but include this file to source control
!/.vs/config/       <== then include this folder to source control, folder only, nothing else
!/.vs/config/*      <== then include all files inside the folder

aqui está o resultado:

insira a descrição da imagem aqui


2
Essa foi a resposta mais útil para mim, pois o visual ajudou. Obrigado!
Joel Murphy

4

Especialmente para as versões mais antigas do Git, a maioria das sugestões não funciona tão bem. Se for esse o caso, eu colocaria um .gitignore separado no diretório em que desejo que o conteúdo seja incluído, independentemente de outras configurações, e permita o necessário.

Por exemplo: /.gitignore

# ignore all .dll files
*.dll

/dependency_files/.gitignore

# include everything
!*

Portanto, tudo em / dependency_files (mesmo arquivos .dll) está incluído.


4

Eu encontrei um caso semelhante aqui, em que no laravel, por padrão, .gitignoreignora todos usando asterix e substitui o diretório público.

*
!public
!.gitignore

Isso não é suficiente se você se deparar com o cenário OP.

Se você deseja confirmar uma subpasta específica de public, digamos, por exemplo, em seu public/productsdiretório, você deseja incluir arquivos com uma subpasta profunda, por exemplo, para incluir que public/products/a/b.jpgeles não serão detectados corretamente, mesmo se você os adicionar especificamente assim !/public/products,!public/products/* , etc ..

A solução é garantir que você adicione uma entrada para cada nível de caminho como esse para substituir todos eles.

*
!.gitignore
!public/
!public/*/
!public/products/
!public/products/*
!public/products/*/
!public/products/*/
!public/products/*/*

3

Apenas outro exemplo de percorrer a estrutura de diretórios para obter exatamente o que você deseja. Nota: não excluí, Library/masLibrary/**/*

# .gitignore file
Library/**/*
!Library/Application Support/
!Library/Application Support/Sublime Text 3/
!Library/Application Support/Sublime Text 3/Packages/
!Library/Application Support/Sublime Text 3/Packages/User/
!Library/Application Support/Sublime Text 3/Packages/User/*macro
!Library/Application Support/Sublime Text 3/Packages/User/*snippet
!Library/Application Support/Sublime Text 3/Packages/User/*settings
!Library/Application Support/Sublime Text 3/Packages/User/*keymap
!Library/Application Support/Sublime Text 3/Packages/User/*theme
!Library/Application Support/Sublime Text 3/Packages/User/**/
!Library/Application Support/Sublime Text 3/Packages/User/**/*macro
!Library/Application Support/Sublime Text 3/Packages/User/**/*snippet
!Library/Application Support/Sublime Text 3/Packages/User/**/*settings
!Library/Application Support/Sublime Text 3/Packages/User/**/*keymap
!Library/Application Support/Sublime Text 3/Packages/User/**/*theme

> git add Library

> git status

On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   Library/Application Support/Sublime Text 3/Packages/User/Default (OSX).sublime-keymap
    new file:   Library/Application Support/Sublime Text 3/Packages/User/ElixirSublime.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/Package Control.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/Preferences.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/RESTer.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/SublimeLinter/Monokai (SL).tmTheme
    new file:   Library/Application Support/Sublime Text 3/Packages/User/TextPastryHistory.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/ZenTabs.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/adrian-comment.sublime-macro
    new file:   Library/Application Support/Sublime Text 3/Packages/User/json-pretty-generate.sublime-snippet
    new file:   Library/Application Support/Sublime Text 3/Packages/User/raise-exception.sublime-snippet
    new file:   Library/Application Support/Sublime Text 3/Packages/User/trailing_spaces.sublime-settings

Exatamente o que eu queria fazer também ;-)
Kitze

3

No WordPress, isso me ajudou:

wp-admin/
wp-includes/
/wp-content/*
!wp-content/plugins/
/wp-content/plugins/*
!/wp-content/plugins/plugin-name/
!/wp-content/plugins/plugin-name/*.*
!/wp-content/plugins/plugin-name/**

3

gitignore - especifica arquivos não rastreados intencionalmente a serem ignorados.

Exemplo para excluir tudo, exceto um diretório específico foo / bar (observe o / * - sem a barra, o curinga também excluiria tudo dentro de foo / bar ):

$ cat .gitignore
# exclude everything except directory foo/bar
/*
!/foo
/foo/*
!/foo/bar

Outro exemplo para o WordPress :

!/wp-content
wp-content/*
!/wp-content/plugins
wp-content/plugins/*
!wp-content/plugins/my-awesome-plugin

Mais informações aqui: https://git-scm.com/docs/gitignore


2

Costumo usar essa solução alternativa na CLI, onde, em vez de configurar o meu .gitignore, crio um .includearquivo separado onde defino os (sub) diretórios que desejo que sejam incluídos, apesar dos diretórios serem ignorados direta ou recursivamente por.gitignore .

Assim, eu uso adicionalmente

git add `cat .include`

durante a preparação, antes de confirmar.

Para o OP, sugiro usar um .includeque tenha estas linhas:

<parent_folder_path>/application/language/gr/*

NOTA: O uso catnão permite o uso de aliases (dentro .include) para especificar $ HOME (ou qualquer outro diretório específico). Isso ocorre porque a linha homedir/app1/* quando passada para o git adduso do comando acima aparece comogit add 'homedir/app1/*' , e os caracteres entre aspas simples ('') preservam o valor literal de cada caractere entre aspas, impedindo o funcionamento de aliases (como homedir ) (consulte Bash Single Citações ).

Aqui está um exemplo de um .includearquivo que eu uso no meu repositório aqui .

/home/abhirup/token.txt
/home/abhirup/.include
/home/abhirup/.vim/*
/home/abhirup/.viminfo
/home/abhirup/.bashrc
/home/abhirup/.vimrc
/home/abhirup/.condarc

1

Eu queria rastrear arquivos js de produção jquery e isso funcionou:

node_modules/*
!node_modules/jquery
node_modules/jquery/*
!node_modules/jquery/dist/*

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.