.gitignore исключить папку, но включить определенную подпапку


965

У меня есть папка, application/которую я добавляю в .gitignore. Внутри application/папки находится папка application/language/gr. Как я могу включить эту папку?

Я пробовал это

application/
!application/language/gr/

без удачи ...


1
Надеюсь, документация по « .gitignoreшаблонному формату» стала более понятной (декабрь 2013 г.). Смотрите мой ответ ниже
VonC

Мой любимый вопрос и ответ, добавленный в избранное, а также в закладки браузера.
codekiddy

Ответы:


1614

Если вы исключите application/, то все, что находится под ним, всегда будет исключено (даже если какой-то более поздний шаблон отрицательного исключения («unignore») может совпадать с чем-то ниже application/).

Чтобы делать то, что вы хотите, вы должны «игнорировать» каждый родительский каталог всего, что вы хотите «игнорировать». Обычно вы в конечном итоге пишете правила для этой ситуации попарно: игнорируйте все в каталоге, но не какой-то определенный подкаталог.

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

application/*
!application/language/

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

Примечание
Трейлинг /*является значительным:

  • Шаблон dir/исключает каталог с именем dirи (неявно) все под ним.
    С помощью dir/Git никогда не будет смотреть на что-либо ниже dir, и, таким образом, никогда не будет применять какие-либо шаблоны «неисключения» к чему-либо ниже dir.
  • Шаблон dir/*ничего не говорит о dirсебе; это просто исключает все под dir. С помощью dir/*Git будет обрабатывать прямое содержимое dir, давая другим шаблонам возможность «исключить» некоторую часть содержимого ( !dir/sub/).

12
Важны ли конечные звездочки? Если так, то в чем разница в значении? В соответствии с алгоритмом, описанным в документации gitignore , конец косой черты соответствует каталогу и путям под этим каталогом. Окончание со звездочкой тогда относилось бы к лечению как шаблон шара. Эксперимент показывает, что работает вариант звездочки, но не тот, который заканчивается только косой чертой. Я хотел бы понять, почему это так.
SEH

123
@seh: Да, отставание /*является значительным. Если каталог исключен, Git никогда не будет просматривать содержимое этого каталога. Шаблон dir/исключает каталог с именем dirи (неявно) все под ним. Шаблон dir/*ничего не говорит о dirсебе; это просто исключает все под dir. С помощью dir/Git никогда не будет смотреть на что-либо ниже dir, и, таким образом, никогда не будет применять какие-либо шаблоны «неисключения» к чему-либо ниже dir. С помощью dir/*Git будет обрабатывать прямое содержимое dir, давая другим шаблонам возможность «исключить» некоторую часть содержимого ( !dir/sub/).
Крис Джонсен

7
Ах, это объясняет это. Независимо от того, сколько раз я читал документацию gitignore , я так и не понял, когда не работают обратные шаблоны. С твоим объяснением теперь понятно. Документация gitignore нуждается в разделе «рецепт», чтобы объяснить, как это сделать.
SEH

8
Я не мог заставить это работать (безумный файл .gitignore!), Поэтому вместо этого я просто принудительно добавил файлы после cd'ing в каталог, который хотел. git add -f .
K0D4

2
Обратите внимание, что вы не можете полагаться на вывод git status, который просто скажет вам, что каталог верхнего уровня будет добавлен. Вместо этого сделайте git addкаталог верхнего уровня и затем git status(будем надеяться) перечислите подмножество файлов, которые были сопоставлены с шаблоном.
Мэтью Строубридж

136

Коммит 59856de от Karsten Blees (kblees) для Git 1.9 / 2.0 (первый квартал 2014 года) проясняет этот случай:

gitignore.txt: уточнить рекурсивный характер исключенных каталогов

Необязательный префикс " !", который отменяет шаблон; любой соответствующий файл, исключенный предыдущим шаблоном, снова будет включен.

Невозможно повторно включить файл, если родительский каталог этого файла исключен. ( *)
( *: если в git 2.8+ не соблюдаются определенные условия, см. ниже)
Git не выводит список исключенных каталогов по соображениям производительности, поэтому любые шаблоны для содержащихся файлов не влияют, независимо от того, где они определены.

Поставьте обратную косую черту (" \") перед первым " !" для шаблонов, начинающихся с буквального " !", например, " \!important!.txt".

Пример для исключения всего, кроме определенного каталога foo/bar(обратите внимание на то, что /*- без косой черты, подстановочный знак также будет исключать все внутри foo/bar):

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

В твоем случае:

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

Сначала вы должны занести в белый список папки , прежде чем сможете занести в белый список файлы в данной папке.


Обновление февраль / март 2016:

Обратите внимание, что в git 2.9.x / 2.10 (середина 2016 года?) Может быть возможно повторно включить файл, если родительский каталог этого файла исключен, если в пути не указан подстановочный знак .

Nguyễn Thái Ngọc Duy ( pclouds) пытается добавить эту функцию:

Так что с git 2.9+ это могло бы сработать, но в конечном итоге было отменено:

application/
!application/language/gr/

Я пытался использовать обновленный синтаксис повторного включения, который был опубликован в конце вашего ответа на git для windows, v2.8.1.windows.1но он не работает :(
Дэвид Хэнкок,

1
@DavidHancock Извините, я отредактировал ответ: это еще не доступно.
VonC

1
@DavidHancock меня тоже: это более 13 ответов Stack Overflow, которые мне приходилось редактировать несколько раз!
VonC

5
Git 2.9 был выпущен вчера. Подтверждение шаблона application/+ !application/language/gr/, упомянутого в ответе, работает должным образом.
Рэй Шан

1
@RayShan Странно: я вижу, что отмена фиксации отменяет эту функцию, но я не вижу (и примечание к выпуску github.com/git/git/blob/master/Documentation/RelNotes/2.9.0.txt не упоминается) любой коммит, улучшающий правила .gitignore.
VonC

52

@Chris Johnsen ответил великолепно, но в более новых версиях Git (1.8.2 или новее) существует шаблон с двумя звездочками, который можно использовать для более краткого решения:

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

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

Таким образом, вам не нужно «игнорировать» родительский каталог подпапки, которую вы хотите отслеживать.


В Git 2.17.0 (Не знаю, как раньше, до этой версии. Возможно, до версии 1.8.2), работает **шаблон, объединенный с exclude для каждого подкаталога, ведущего к вашим файлам. Например:

# 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
Увы, это терпит неудачу (Git 1.8.4.msysgit.0), потому что шаблон **может соответствовать нулевым подпапкам, и *будет совпадать languageи исключать его, предотвращая включение gr. Полная цепочка родителей, которую рекомендует Крис Джонсон, кажется необходимой.
Шон Гуглер

3
Звучит отлично, но у меня это не работает в git 2.3.7 ... /www/**/* !/www/config.xml !/www/resconfig.xml, а каталог res все еще игнорируется.
Роб

@Rob вам также нужно добавить !/www/res/. Вы можете использовать folder/**/*шаблон, но вам все равно нужно добавить исключения для каждого подкаталога, который вы хотите добавить. Это все еще короче и более читабельно, чем комбинация игнорирования / исключения.
Бен Кейн

Я знаю, что это старый комментарий, но если вам интересно. Я добавил правку в ответ, документирующую этот подход.
Бен Кейн

21

Есть множество похожих вопросов по этому поводу, поэтому я выложу то, что написал ранее:

Единственный способ заставить это работать на моей машине - это сделать так:

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

Обратите внимание, как вы должны явно разрешить контент для каждого уровня, который вы хотите включить. Так что, если у меня есть подкаталоги 5 глубоко под темами, мне все еще нужно разобрать это.

Это из комментария @ Ярина здесь: https://stackoverflow.com/a/5250314/1696153

Это были полезные темы:

Я тоже пробовал

*
*/*
**/**

а также **/wp-content/themes/**

или /wp-content/themes/**/*

Ничто из этого не сработало для меня. Много проб и ошибок!


1
Примечание: помните, что порядок имеет значение и в файле .gitignore, поэтому убедитесь, что вы поставили свои !правила внизу.
starbeamrainbowlabs


8

Самый простой и, вероятно, лучший способ - попытаться добавить файлы вручную (обычно это имеет приоритет над .gitignoreправилами-стилей):

git add /path/to/module

Вы можете даже хотите -N намерение добавить флаг, чтобы предложить вам будет добавить их, но не сразу. Я часто делаю это для новых файлов, которые я еще не готов подготовить.


Это копия ответа, размещенного на том, что легко может быть дубликатом QA. Я размещаю его здесь для большей наглядности - мне проще не иметь беспорядка в правилах gitignore.


2
Спасибо за этот ответ. Мне пришлось добавить -f, так как он есть в моем .gitignore, вот так: git add -f path / to / file
jasonflaherty

6

Итак, поскольку многие программисты используют ноды. вариант использования, который отвечает этому вопросу, должен исключать, node_modulesкроме одного модуля, module-aнапример:

!node_modules/

node_modules/*
!node_modules/module-a/

2
Не работает для git-версии 2.10.2.windows.1.
Себастьян

1
Для git версии 2.10.2 следуйте этому ответу
nalexn

👌 отлично 👋 👋.
Abdennour TOUMI

6

Добавить дополнительный ответ:

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

вот результат:

введите описание изображения здесь


2
Это был самый полезный ответ для меня, поскольку визуальные эффекты помогли. Спасибо!
Джоэл Мерфи

4

Особенно для старых версий Git большинство предложений не будут работать так хорошо. Если это так, я бы поместил отдельный каталог .gitignore в каталог, куда я хочу, чтобы контент был включен, независимо от других настроек, и разрешил там, что нужно.

Например: /.gitignore

# ignore all .dll files
*.dll

/dependency_files/.gitignore

# include everything
!*

Так что все в / dependency_files (даже .dll файлы) включены просто отлично.


4

Я нашел похожий случай, когда в laravel по умолчанию .gitignoreигнорирует все, используя asterix, а затем переопределяет публичный каталог.

*
!public
!.gitignore

Этого недостаточно, если вы столкнетесь с сценарием OP.

Если вы хотите зафиксировать определенные подпапки public, скажем, например, в свой public/productsкаталог, вы хотите включить файлы, которые имеют глубину в одну подпапку, например, включить, что public/products/a/b.jpgони не будут обнаружены правильно, даже если вы добавите их именно так !/public/products,!public/products/* и т. Д.

Решение состоит в том, чтобы убедиться, что вы добавляете запись для каждого уровня пути, как этот, чтобы переопределить их все.

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

3

Еще один пример перехода по структуре каталогов, чтобы получить именно то, что вы хотите. Примечание: я не исключаю, Library/ноLibrary/**/*

# .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

Именно то, что я тоже хотел сделать ;-)
Китце

3

В WordPress это помогло мне:

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 - Определяет намеренно не отслеживаемые файлы для игнорирования.

Пример, чтобы исключить все, кроме определенного каталога foo / bar (обратите внимание на / * - без косой черты подстановочный знак также исключит все, что находится внутри foo / bar ):

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

Еще один пример для WordPress :

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

Больше информации здесь: https://git-scm.com/docs/gitignore


2

Я часто использую этот обходной путь в CLI, где вместо настройки моего .gitignoreя создаю отдельный .includeфайл, в котором я определяю (под) каталоги, которые я хочу включить, несмотря на каталоги, которые прямо или рекурсивно игнорируются.gitignore .

Таким образом, я дополнительно использую

git add `cat .include`

во время постановки, перед совершением.

Для OP, я предлагаю использовать, .includeкоторый имеет эти строки:

<parent_folder_path>/application/language/gr/*

ПРИМЕЧАНИЕ. Использование catне позволяет использовать псевдонимы (в пределах .include) для указания $ HOME (или любого другого конкретного каталога). Это связано с тем, что строка, homedir/app1/* переданная с git addиспользованием вышеуказанной команды, выглядит как git add 'homedir/app1/*', а заключенные в одинарные кавычки символы ('') сохраняют буквальное значение каждого символа в кавычках, тем самым предотвращая функционирование псевдонимов (таких как homedir ) (см. Bash Single). Цитаты ).

Вот пример .includeфайла, который я использую в своем репо здесь .

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

1

Я хотел отследить JS-файлы JQuery производства, и это сработало:

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.