.gitignore exclut le dossier mais inclut un sous-dossier spécifique

965

J'ai le dossier application/que j'ajoute au .gitignore. À l'intérieur du application/dossier se trouve le dossier application/language/gr. Comment puis-je inclure ce dossier?

J'ai essayé

application/
!application/language/gr/

sans chance ...

chchrist
la source
1
Espérons que la .gitignoredocumentation sur le «format de modèle» soit devenue plus claire (décembre 2013). Voir ma réponse ci
VonC
Ma question et réponse préférée, ajoutée aux favoris ainsi qu'aux signets du navigateur.
codekiddy

Réponses:

1614

Si vous excluez application/, tout ce qui se trouve en dessous sera toujours exclu (même si un modèle d'exclusion négatif ultérieur («non ignoré») pourrait correspondre à quelque chose en dessous application/).

Pour faire ce que vous voulez, vous devez «désignorer» chaque répertoire parent de tout ce que vous voulez «désignorer». Habituellement, vous finissez par écrire des règles pour cette situation par paires: ignorez tout dans un répertoire, mais pas certains sous-répertoires.

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

application/*
!application/language/

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

Remarque
Le suivi /*est significatif:

  • Le modèle dir/exclut un répertoire nommé diret (implicitement) tout ce qu'il contient.
    Avec dir/, Git ne regardera jamais rien en dessous dir, et n'appliquera donc aucun des motifs de «non-exclusion» à quoi que ce soit sous dir.
  • Le motif dir/*ne dit rien sur dirlui-même; il exclut juste tout sous dir. Avec dir/*, Git traitera le contenu direct de dir, donnant à d'autres modèles une chance de «dé-exclure» une partie du contenu ( !dir/sub/).
Chris Johnsen
la source
12
Les astérisques de fin sont-ils significatifs? Si oui, quelle est la différence de sens? Selon l'algorithme décrit dans la documentation de gitignore , se terminant par une barre oblique de fin correspond à un répertoire et des chemins sous ce répertoire. Se terminant par un astérisque tomberait alors dans le traitement comme un motif global. L'expérimentation montre que la variante astérisque fonctionne, mais pas celle qui se termine par une barre oblique de fin. J'aimerais comprendre pourquoi il en est ainsi.
seh
123
@seh: Oui, le suivi /*est significatif. Si un répertoire est exclu, Git ne regardera jamais le contenu de ce répertoire. Le modèle dir/exclut un répertoire nommé diret (implicitement) tout ce qu'il contient. Le motif dir/*ne dit rien sur dirlui-même; il exclut juste tout sous dir. Avec dir/, Git ne regardera jamais rien en dessous dir, et n'appliquera donc aucun des motifs de «non-exclusion» à quoi que ce soit sous dir. Avec dir/*, Git traitera le contenu direct de dir, donnant à d'autres modèles une chance de «dé-exclure» une partie du contenu ( !dir/sub/).
Chris Johnsen
7
Ah, ça explique ça. Peu importe combien de fois j'ai lu la documentation de gitignore , je n'ai jamais compris quand les modèles inversés ne fonctionnent pas. Avec votre explication, c'est maintenant clair. La documentation de gitignore a besoin d'une section "recette" pour expliquer comment faire cela.
seh
8
Je ne pouvais pas vraiment faire fonctionner cela (fichier .gitignore fou!), Donc à la place, j'ai juste ajouté de force les fichiers après le cd'ing dans le répertoire que je voulais. git add -f .
K0D4
2
Notez que vous ne pouvez pas vous fier à la sortie de git status, qui vous indiquera simplement que le répertoire de niveau supérieur va être ajouté. Au lieu de cela, faites un git adddes répertoires de niveau supérieur, puis git statusrépertoriez (espérons-le) le sous-ensemble de fichiers qui ont été mis en correspondance par le modèle.
Matthew Strawbridge du
136

Commit 59856de de Karsten Blees (kblees) pour Git 1.9 / 2.0 (Q1 2014) clarifie ce cas:

gitignore.txt: clarifier la nature récursive des répertoires exclus

Un préfixe facultatif " !" qui annule le modèle; tout fichier correspondant exclu par un modèle précédent sera à nouveau inclus.

Il n'est pas possible de ré-inclure un fichier si un répertoire parent de ce fichier est exclu. ( *)
( *: sauf si certaines conditions sont remplies dans git 2.8+, voir ci-dessous)
Git ne répertorie pas les répertoires exclus pour des raisons de performances, donc les modèles sur les fichiers contenus n'ont aucun effet, peu importe où ils sont définis.

Mettez une barre oblique inversée (" \") devant le premier " !" pour les motifs qui commencent par un " !" littéral , par exemple, " \!important!.txt".

Exemple pour tout exclure sauf un répertoire spécifique foo/bar(notez le /*- sans la barre oblique, le caractère générique exclurait également tout ce qu'il contient foo/bar):

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

Dans ton cas:

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

Vous devez d' abord mettre les dossiers en liste blanche avant de pouvoir mettre les fichiers en liste blanche dans un dossier donné.


Mise à jour février / mars 2016:

Notez qu'avec git 2.9.x / 2.10 (mi 2016?), Il pourrait être possible de ré-inclure un fichier si un répertoire parent de ce fichier est exclu s'il n'y a pas de caractère générique dans le chemin ré-inclus .

Nguyễn Thái Ngọc Duy ( pclouds) tente d'ajouter cette fonctionnalité:

Donc, avec git 2.9+, cela aurait pu fonctionner, mais a finalement été inversé:

application/
!application/language/gr/
VonC
la source
J'ai essayé d'utiliser la syntaxe de ré-inclusion mise à jour publiée à la fin de votre réponse sur git pour Windows, v2.8.1.windows.1mais cela ne semble pas fonctionner :(
David Hancock
1
@DavidHancock Désolé, j'ai modifié la réponse: ce n'est pas encore disponible.
VonC
1
@DavidHancock moi aussi: c'est plus de 13 réponses Stack Overflow que j'ai dû modifier plusieurs fois!
VonC
5
Git 2.9 est sorti hier. La confirmation du modèle application/+ !application/language/gr/mentionné dans la réponse fonctionne comme prévu.
Ray Shan
1
@RayShan Strange: J'ai vu le retour annuler l'annulation de cette fonctionnalité, mais je ne l'ai pas vu (et la note de publication github.com/git/git/blob/master/Documentation/RelNotes/2.9.0.txt ne mentionne pas) tout commit améliorant les règles .gitignore.
VonC
52

La réponse de @Chris Johnsen est excellente, mais avec les versions plus récentes de Git (1.8.2 ou ultérieur), il existe un double motif d'astérisque que vous pouvez utiliser pour une solution un peu plus abrégée:

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

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

De cette façon, vous n'avez pas à "désignorer" le répertoire parent du sous-dossier que vous souhaitez suivre.


Avec Git 2.17.0 (Je ne sais pas combien de temps avant cette version. Peut-être revenir à 1.8.2), l'utilisation du **modèle combiné avec excludes pour chaque sous-répertoire menant à vos fichiers fonctionne. Par exemple:

# 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
rpyzh
la source
9
Hélas, cela échoue (Git 1.8.4.msysgit.0) car le modèle **peut correspondre à zéro sous-dossier, et le *fera correspondre languageet l'exclure, empêchant l'inclusion de gr. La chaîne complète des parents que recommande @Chris Johnson semble encore nécessaire.
Sean Gugler
3
Cela semble parfait mais cela ne fonctionne pas pour moi sur git 2.3.7 ... /www/**/* !/www/config.xml !/www/resconfig.xml et le répertoire res sont toujours ignorés.
Rob
@Rob vous devez également ajouter !/www/res/. Vous pouvez utiliser le folder/**/*modèle, mais vous devez toujours ajouter des exclusions pour chaque sous-répertoire que vous souhaitez ajouter. Il est toujours plus court et plus lisible que le combo ignorer / exclure.
Ben Kane
Je sais que c'est un vieux commentaire, mais au cas où vous seriez curieux. J'ai ajouté une modification à la réponse documentant cette approche.
Ben Kane
21

Il y a un tas de questions similaires à ce sujet, donc je posterai ce que j'ai écrit avant:

La seule façon de faire fonctionner cela sur ma machine était de le faire de cette façon:

# 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/*/*/*/*/*

Remarquez comment vous devez autoriser explicitement le contenu pour chaque niveau que vous souhaitez inclure. Donc, si j'ai des sous-répertoires 5 sous les thèmes, je dois encore le préciser.

Ceci est du commentaire de @ Yarin ici: https://stackoverflow.com/a/5250314/1696153

Ces sujets étaient utiles:

J'ai aussi essayé

*
*/*
**/**

et **/wp-content/themes/**

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

Rien de tout cela n'a fonctionné pour moi non plus. Beaucoup d'essais et d'erreurs!

Katie
la source
1
Remarque: N'oubliez pas que l'ordre est également important dans un fichier .gitignore, alors assurez-vous de mettre vos !règles en bas.
starbeamrainbowlabs
15

Je n'ai trouvé que cela fonctionne réellement .

**/node_modules/*
!**/node_modules/keep-dir
Steve Kling
la source
génial, sauvez ma journée)
Yegor Zaremba
sauvé le mien aussi
tomrlh
8

La façon la plus simple et probablement la meilleure consiste à essayer d'ajouter les fichiers manuellement (généralement cela a priorité sur les .gitignorerègles de style):

git add /path/to/module

Vous pouvez même vouloir l' -N intention d'ajouter un indicateur, pour suggérer que vous les ajouterez, mais pas immédiatement. Je le fais souvent pour de nouveaux fichiers que je ne suis pas encore prêt à mettre en scène.


Il s'agit d'une copie d'une réponse publiée sur ce qui pourrait facilement être un AQ en double. Je le redistribue ici pour une visibilité accrue — je trouve plus facile de ne pas avoir un désordre de règles gitignore.

D. Ben Knoble
la source
2
Merci pour cette réponse. J'ai dû ajouter -f car il est dans mon .gitignore donc comme ça: git add -f path / to / file
jasonflaherty
6

Donc, puisque de nombreux programmeurs utilisent le nœud. le cas d'utilisation qui répond à cette question est d'exclure node_modulessauf un module module-apar exemple:

!node_modules/

node_modules/*
!node_modules/module-a/
Abdennour TOUMI
la source
2
Ne fonctionne pas pour la version git 2.10.2.windows.1.
Sebastian
1
Pour la version git 2.10.2, suivez cette réponse
nalexn
👌 super 👋 👋.
Abdennour TOUMI
6

Ajoutez une réponse supplémentaire:

!/.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

voici le résultat:

entrez la description de l'image ici

Dongdong
la source
2
Ce fut la réponse la plus utile pour moi car les visuels m'ont aidé. Merci!
Joel Murphy
4

Surtout pour les anciennes versions de Git, la plupart des suggestions ne fonctionneront pas aussi bien. Si c'est le cas, je mettrais un .gitignore séparé dans le répertoire où je veux que le contenu soit inclus indépendamment des autres paramètres et y autoriser ce qui est nécessaire.

Par exemple: /.gitignore

# ignore all .dll files
*.dll

/dependency_files/.gitignore

# include everything
!*

Donc, tout dans / dependency_files (même les fichiers .dll) est très bien inclus.

Breit
la source
4

J'ai trouvé un cas similaire ici, où dans laravel par défaut, .gitignoreignore tout en utilisant astérisque, puis remplace le répertoire public.

*
!public
!.gitignore

Cela ne suffit pas si vous exécutez le scénario OP.

Si vous souhaitez valider un sous-dossier spécifique de public, par exemple, dans votre public/productsrépertoire, vous voulez inclure des fichiers qui sont profonds d'un sous-dossier, par exemple, pour inclure, public/products/a/b.jpgils ne seront pas détectés correctement, même si vous les ajoutez spécifiquement comme ceci !/public/products,!public/products/* , etc ..

La solution consiste à vous assurer d'ajouter une entrée pour chaque niveau de chemin comme celui-ci pour les remplacer tous.

*
!.gitignore
!public/
!public/*/
!public/products/
!public/products/*
!public/products/*/
!public/products/*/
!public/products/*/*
blamb
la source
3

Juste un autre exemple de marche dans la structure du répertoire pour obtenir exactement ce que vous voulez. Remarque: je n'ai pas exclu Library/maisLibrary/**/*

# .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
Adrian Dunston
la source
Exactement ce que je voulais faire aussi ;-)
Kitze
3

Dans WordPress, cela m'a aidé:

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/**
T.Todua
la source
3

gitignore - Spécifie les fichiers intentionnellement non suivis à ignorer.

Exemple pour tout exclure sauf un répertoire spécifique foo / bar (notez le / * - sans la barre oblique, le caractère générique exclurait également tout dans foo / bar ):

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

Un autre exemple pour WordPress :

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

Plus d'informations ici: https://git-scm.com/docs/gitignore

Ali
la source
2

J'utilise souvent cette solution de contournement en CLI où au lieu de configurer mon .gitignore, je crée un .includefichier séparé où je définis les (sous) répertoires que je veux inclure malgré les répertoires directement ou récursivement ignorés par.gitignore .

Ainsi, j'utilise également

git add `cat .include`

pendant la mise en scène, avant de commettre.

Pour l'OP, je suggère d'utiliser un .includequi a ces lignes:

<parent_folder_path>/application/language/gr/*

REMARQUE: l'utilisation catn'autorise pas l'utilisation d'alias (à l'intérieur .include) pour spécifier $ HOME (ou tout autre répertoire spécifique). En effet, la ligne homedir/app1/* transmise à l' git addaide de la commande ci-dessus apparaît sous la forme git add 'homedir/app1/*'et le fait de mettre des caractères entre guillemets simples ('') préserve la valeur littérale de chaque caractère entre guillemets, empêchant ainsi les alias (tels que homedir ) de fonctionner (voir Bash Single). Citations ).

Voici un exemple de .includefichier que j'utilise dans mon référentiel ici .

/home/abhirup/token.txt
/home/abhirup/.include
/home/abhirup/.vim/*
/home/abhirup/.viminfo
/home/abhirup/.bashrc
/home/abhirup/.vimrc
/home/abhirup/.condarc
Abhirup Das
la source
1

Je voulais suivre les fichiers js de production jquery et cela a fonctionné:

node_modules/*
!node_modules/jquery
node_modules/jquery/*
!node_modules/jquery/dist/*
Indrek Ruubel
la source