правила gitignore (применяются к стеку codeigniter) - PullRequest
0 голосов
/ 19 февраля 2019

Я добавляю сайт CodeIgniter в новое хранилище (в качестве начального добавления - хранилище в настоящее время пусто).

Я переместил каталоги application и system и index.php из корня CodeIgniter в подкаталог www.Я хотел игнорировать каталоги development и production, которые я создал в application\config.https://www.gitignore.io/?templates=codeigniter использует */config/development/*;это не сработало для меня, но

**/config/development/*

сработало, что привело к ряду вопросов:

  1. Прав ли я, думая, что */ наначало */config/development/* относится только к какой-либо непосредственной подпапке?
  2. Если это так, каков разумный подход для gitignore.io, включающий */ вместо явного использования `/ application / config / development / * '?
  3. Поскольку (?) Я переместил приложение в подпапку, я обнаружил, что мне нужно изменить */config/development/* на **/config/development/*
    • , однако https://stackoverflow.com/a/41761521/889604 означает, что **/избыточно , но , когда я попытался изменить его на config/development/*, файлы все еще были добавлены.Вам нужно только исключить *[*]/, если каталог находится в том же каталоге, что и .gitignore?

1 Ответ

0 голосов
/ 20 февраля 2019

TL; DR

В .gitignore шаблоны с косой чертой или встроенными обрабатываются специально.Они отличаются от шаблонов, которые не имеют начального или встроенного слеша.Поэтому вам может потребоваться **/config/development/* здесь из-за двух встроенных слешей.

Резюме

Чтобы ответить на ваши вопросы по порядку:

  1. Да.

  2. Вы должны спросить, кто бы ни написал эти файлы игнорирования.

  3. Как я заметил в комментарии, предположения в приводятзвездочки "** /" избыточны в синтаксисе соответствия пути .gitignore? неверны;принятый ответ здесь неприменим к этому делу.

Объяснение этого последнего пункта представляется здесь уместным.

Long

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

В качестве дополнительного примечания мне больше нравится термин directory , когда речь идет о сущностях, предоставляемых ОС.Если вы предпочитаете термин папка , вы можете заменить его мысленно - это, по сути, одно и то же.Если вы знакомы с C или Python или родственными языками, вы будете знать о opendir и / или readdir и / или os.listdir, а также о таких функциях, как os.walk, большинство из которых также используют слово"directory" для описания этих вещей.

Определение шаблонов глобуса

Давайте начнем с .gitignore записей, которые состоят из расширенных шаблонов глобуса .Термин шаблон глобуса довольно хорошо определен на этой странице Википедии , но мы могли бы использовать немного больше.

Самая базовая форма глобуса просто имеет *, ? и [...] метасимволы.Один знак вопроса соответствует одному символу в имени файла.Звездочка соответствует любому количеству символов (включая ноль символов), а строка в квадратных скобках соответствует любому символу в скобках. 1 Обратите внимание, что этот простой простой глобальный тип применяется только к файлам в пределах одного каталога .Какой бы объект ни работал с этим типом глобуса, он считывает список имен файлов - возможно, из фактического каталога - и затем выбирает те имена в этом каталоге, которые соответствуют этому выражению глобуса.

Очевидно, следующий уровеньвверх, чтобы добавить каталоги к этому виду простой шар.Например, мы можем написать dir/* для обозначения всех файлов в каталоге с именем dir.Это не очень сложно, хотя и поднимает вопрос, который мы проигнорировали в простейшем случае: соответствует ли шаблон glob имени directory ?То есть, что, если dir/sub сам является каталогом, dir/* соответствует ему?В этом отношении * соответствует dir?Типичный ответ: да, это совпадает, и пока мы придерживаемся dir/*, это просто означает, что dir/sub соответствует (как каталог).

Расширенные глобусы сильно различаются, Bash имеет собственный специальный расширенный синтаксис glob , использующий globstar для включения ** и extglob для включения еще больше.Что означает **, варьируется: некоторые реализации требуют, чтобы он соответствовал хотя бы одному каталогу, но допускает любое количество уровней каталогов.Другие реализации позволяют ** соответствовать , но не каталогам, так что **/sub соответствует dir/sub, но также просто sub.Git ** в основном ведет себя следующим образом: он соответствует нулю или большему количеству каталогов, согласно документации gitignore .


1 Обратите внимание, что, несмотря на сходство, шаблоны глобов совсем не совпадают с регулярными выражениями, где обычно . означает любой отдельный символ - эквивалент ? в глобусе, а * - суффикс оператор , означающий ноль или более того, что было раньше.Следовательно, в регулярных выражениях .* означает ноль или более любого символа.Квадратные скобки RE обычно допускают как диапазоны, так и инверсию, например, [^a-z] означает что-либо , а не в a - z, в то время как шаблоны оболочки оболочки обычно допускают только диапазоны.


Git хранит файлы через индекс Git

Важным образом, Git не заботится о каталогах.В частности, Git обязуется хранить файлы , а не хранить каталоги, полные файлов.Файлы просто имеют пути, которые выглядят как , они занимают каталоги.ОС требует, чтобы каталог dir существовал, чтобы dir/sub мог существовать;dir/sub в свою очередь должен быть каталогом, чтобы dir/sub/file мог существовать.Но что касается Git, Git просто нужно хранить содержимое, чтобы перейти в файл с именем dir/sub/file.Когда придет время записать этот контент в этот файл, Git просто создаст dir и dir/sub при необходимости в то время .Наличие или отсутствие каталогов не имеет значения.

Вот почему вы не можете хранить пустой каталог в репозитории Git: Git хранит содержимое файлов в файле имена в каждом коммите.Без файлов нечего хранить, поэтому пустые каталоги просто не присутствуют в коммите.

Тем не менее, в то время как Git хранит только файлы , Git должен использовать предоставляемые ОС службы чтения каталогов.чтобы найти файлы, которые вы поместили в свое рабочее дерево.Затем Git скопирует эти файлы - или, точнее, их содержимое, связанное с их (полными) именами, такими как dir/sub/file, в index Git при подготовке нового коммита.Индекс содержит имя каждого файла, режим (100644 или 100755) и хэш-идентификатор содержимого Git-ified.Это то, что входит в следующий коммит, который вы делаете.(Когда вы git checkout используете какой-то существующий коммит, Git заполняет индекс из этого коммита, так что индекс изначально совпадает с коммитом.)

Обход дерева каталогов

Как мы только что видели, Gitдолжен открывать и читать каждый каталог в вашем рабочем дереве, начиная с самого верхнего уровня самого рабочего дерева.Результаты вызова os.listdir (Python) или opendir и readdir (C) представляют собой список имен: имена файлов и подкаталогов в каталоге, который Git только что сказал ОС перечислить.Немного больше работы (вызов lstat) получает остальную необходимую информацию, и теперь Git знает, относится ли имя dir к обычному файлу или к каталогу.

С учетом имени directory , Git, как правило, должен открывать и читать этот каталог.Поэтому Git откроет и прочитает dir, найдет имя sub и обнаружит, что sub является каталогом.Затем Git откроет и прочитает dir/sub и найдет имя file, а file назовет файл.Этот процесс открытия и чтения, рекурсивно, каждого каталога в каталоге, называется обходом дерева каталогов .Это то, что делает, например, функция Python os.walk.

Стандарт C не имеет функции для обхода дерева, поэтому Git реализует все это как бы вручную.Это начинает иметь значение через мгновение, но сейчас подумайте об этом следующим образом: пройдя по дереву, Git находит все каталогов и все файлов в хранилище.При отсутствии .gitignore Git отбрасывает все имена каталогов, сохраняет все имена файлов - используя их полные пути сверху - и затем, по крайней мере для операции добавления «все», помещает все эти имена и обновленное содержимое в индексготов к следующему коммиту.

Об этом нужно знать несколько вещей:

  • Процесс ходьбы по своей природе является рекурсивным.То есть, найдя каталог, мы должны открыть и прочитать каталог, обрабатывая каждую запись.Если сама запись является каталогом, мы должны открыть и прочитать этот каталог и т. Д.

  • Между тем каждая запись в aкаталог - это просто имя: мы - или Git - должны собирать путь по ходу дела.Если мы работаем над dir и сталкиваемся с sub, полное имя теперь dir/sub.Если мы работаем над dir/sub и сталкиваемся с file, полное имя теперь dir/sub/file.Но dir просто перечисляет sub, а сам sub просто перечисляет file.Мы / Git должны помнить путь.

  • Ходьба идет медленно , условно говоря.Git хочет быть быстрым!

Все это вносит некоторые сложности в правила .gitignore.

Файлы Gitignore могут существовать на каждом уровне, а также имена и /или шаблоны глобуса

На верхнем уровне у вас может быть очень простой файл .gitignore:

# ignore files named *.o and *.pyc
*.o
*.pyc

Теперь Git может просматривать ваше рабочее дерево, находя файлы на каждом уровне каталога,Если имя файла - как указано в этом каталоге на любом уровне - совпадает с любым из этих простых шаблонов глобуса, , а полный путь к файлу еще не указан в индексе , Git сделает вид, что файлне существует: оно не будет добавлено автоматически, и git status не будет жаловаться на то, что оно не отслежено.

Но что, если мы хотим предотвратить попадание файлов dir/foo и dir/sub/foo вверхний уровень, а не защита от foo на верхнем уровне?Тогда мы можем сказать Git: игнорировать foo, только когда он содержится в dir. . Есть простой способ выразить это: создать файл dir/.gitignore.Имена файлов, перечисленные здесь , игнорируются, когда они обнаруживаются путем чтения dir или любого из его подкаталогов:

.gitignore:
    *.o
    *.pyc
dir/.gitignore:
    foo

Теперь, во время прогулки, когда Git открывается и читаетdir, он замечает, что есть dir/.gitignore.Там применяются правила ко всем файлам, найденным во время этого рекурсивного обхода: они применяются к файлам в dir и файлам в dir/sub, но не к файлам на верхнем уровне, или - если есть каталог верхнего уровня other/,к файлам там тоже.

Начальные и встроенные слэши избегают рекурсивных совпадений

Но что, если мы хотим игнорировать только dir/foo, а не dir/sub/foo, и неother/foo или /foo?Теперь у нас другая проблема, и Git предлагает два решения.Один из них - написать /foo как запись в dir/.gitignore:

.gitignore:
    *.o
    *.pyc
dir/.gitignore:
    /foo

Это игнорирует только dir/foo, а не dir/sub/foo.Он содержит начальную косую черту, которая говорит Git: Не применять это к подкаталогам .

Другой способ выразить это - поместить это право на верхний уровень .gitignore,что устраняет необходимость иметь dir/.gitignore вообще:

*.o
*.pyc
dir/foo

Содержит встроенную косую черту.Когда Git выполняет обход каталога, он, естественно, находит имена файлов без их путей - он находит foo, а не dir/foo, внутри dir при обходе dir.Таким образом, этот тип шаблона обрабатывается отдельно, после объединения полного имени пути.

Итак, это источник первых двух специальных правил, касающихся слешей в именах или шаблонах в * 1295.* files:

  • Символ косой черты соответствует только этому имени или простому глобусу в этом каталоге .
  • Встроенная косая черта соответствует только этому полномуимя пути или (расширенный) глобус относительно этого каталога.

Примечание tЕсли второй случай охватывает первый: оба будут работать правильно, сопоставляя только пути в каталоге этого , после того, как относительные имена путей собраны вместе (т. е. после того, как sub s foo превращен вdir/sub/foo).Но нам нужен первый случай, потому что голое имя или шаблон глобуса, такие как foo или *.pyc, будут применяться к этому каталогу и всем его подкаталогам.Мы могли бы обработать dir/foo, поднявшись на верхний уровень и проигнорировав dir/foo напрямую, но если мы хотим игнорировать /bar без игнорирования dir/bar и dir/sub/bar, у нас есть только верхний уровень .gitignore для этогоpath.

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

(Встроенное правило косой черты может фактически быть ошибкой. Формулировка в документации gitignore предполагает, что dir/sub означает игнорировать a/dir/sub, а также, что вам нужно было бы написать /dir/sub в , а не игнорировать a/dir/sub. Но тестирование показывает, что оно ведет себя так, как я здесь описываю:

$ git status -s -uall
?? a/dir/sub/file2
?? dir/sub/file
$ echo dir/sub > .gitignore
$ git status -s -uall
?? .gitignore
?? a/dir/sub/file2
$ git --version
git version 2.20.1

Примечаниечто игнорирование dir/sub заставило file исчезнуть, но a/dir/sub/file2 остается недовольным.)

Конечные косые черты отличаются

Помните, что мы говорили, что обход дерева идет медленно, относительноГоворящий.Довольно часто можно найти Git-репозиторий, где в рабочем дереве мы намеренно добавляем полный SDK поставщика или другую упакованную вещь - возможно, взятую из репозитория как один tarball, или, может быть, извлечены каким-либо методом вне Git полностью - и никогдахотите зафиксировать любой из файлов внутри этой упакованной вещи, какой бы она ни была.Прохождение Git через каждый уровень этого пакета, после его разархивации, является пустой тратой времени.

С этой целью, если у Git еще нет списка записей индекса, скажем, dir/sub/vendor/file, и- во время одной из его прогулок по деревьям каталогов - встречается каталог с именем vendor в dir/sub, вы можете сказать Git: Не пытайтесь заглянуть внутрь этого каталога vendor/. Oneспособ выразить это состоит в том, чтобы использовать то, что мы уже знаем:

.gitignore:
    *.o
    *.pyc
    dir/sub/vendor

или:

.gitignore:
    *.o
    *.pyc
dir/sub/.gitignore:
    /vendor

Мы уже знаем, какой здесь главный слеш: он гарантирует, что мы игнорируем только vendor в dir/sub.Это также относится и к верхнему уровню .gitignore.

Однако, что если мы хотим пропустить все каталоги с именем vendor, не пропуская ни одного файлы с именем vendor?Здесь мы можем использовать синтаксис косой черты :

.gitignore:
    *.o
    *.pyc
    vendor/

Этот vendor/ в некотором смысле выглядит как dir/sub.Но слеш здесь не встроенный , а трейлинг .Так что этот косая черта не включает код только для полного пути.Вместо этого он сообщает Git: Во время обхода дерева, когда вы сталкиваетесь с чем-то с именем vendor, и это каталог , не возвращайтесь к нему. Завершающий слеш сначала удаляется из этой строки, а vendor - элемент для сопоставления.Он не имеет ни передней косой черты, ни встроенной, поэтому он сопоставляется на любом подуровне этого уровня шага, но на самом деле он имеет конечную косую черту, поэтому он соответствует only , если на самом деледерево - это каталог.

Конечно, мы также можем просто сказать vendor, или v*r, или любую другую вещь, которая соответствует vendor, если мы хотим игнорировать файлы также.Или мы можем написать v*r/, если хотим игнорировать все каталоги, чье базовое имя - часть без полного пути - соответствует v*r.

Отмена игнорирования предыдущего правила игнорирования и проблема с игнорируемымкаталоги

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

То есть, если есть какое-либо правило, которое соответствует vendor в любой момент, и это правило гласит игнорируйте этот , а vendor является каталогом, Git не будет открывать vendor и читать его содержимое.Он не увидит vendor/file1, vendor/file2 и так далее.Эти имена никогда не будут перенесены в , если мы игнорируем это имя микроскоп, ни в формате их базового имени file1, ни в формате dir/sub/vendor/file1 полного пути.

Выводы: что вы должны знать о .gitignore

  • Косая черта имеет эффект привязки.Якорь находится на том же уровне, что и файл .gitignore.(Если файл игнорирования находится вне рабочего дерева - например, в $HOME/.gitignore или .git/info/exclude - уровень привязки является верхним уровнем рабочего дерева.)

  • Встроенные косые черты - но не завершающие косые черты - также включают эффект привязки, несмотря на неопределенный намек на документацию в противном случае.Это может быть ошибкой, но Git вел себя подобным образом последовательно во многих выпусках (так что, возможно, это ошибка документация ).

  • Соответствие двойного звездного шара (**/whatever) содержит косую черту почти по определению.Единственными двумя двойными звездочками, которые не имеют встроенной косой черты, являются **/ и **, ни один из которых, вероятно, не будет использоваться на практике.Встроенные косые черты имен якоря, но двойная звезда допускает здесь ноль или более уровней каталогов, так что привязка не имеет тормозного эффекта.Старшая двойная звезда требуется, если вы хотите, чтобы этот тип поведения с произвольным плавающим соответствием имел имя, которое без начального **/ будет также содержать встроенную косую черту.

  • Правила игнорирования требуют, чтобы Git открывал и читал каталог.Если вы хотите игнорировать какой-либо файл в глубине дерева каталогов, вы хотите, чтобы ни один из содержащихся в нем каталогов не игнорировался, или обнаружил, что что-то заставляет Git сканировать глубокий подкаталог.То есть, если у вас есть файл с именем long/path/to/important/file и вы хотите, чтобы этот файл хранился в каждом коммите, вам потребуется это имя , чтобы попасть в индекс Git, чтобыGit сохранит его в следующем коммите.

  • Файлы, существующие в индексе, по определению не игнорируются.Записи игнорирования применяются только к файлам, которые не в индексе, но находятся в рабочем дереве.

  • Индекс (всегда) существует, и он содержитимена файлов, которые - поскольку ОС настаивает - фактически появляются внутри каталогов.Так что, если индекс имеет long/path/to/important/file, Git проверит, есть ли long/path/to/important/file еще там и был или не был изменен.Но если вы проигнорировали long, или long/path/to/important, или что-то еще здесь, Git не будет читать каталог . 2 Если вы как-то случайно удалите long/path/to/important/file из индекса, игнорируя каталог long/path/to/important, Git не будет сам добавлять файл обратно и не будет предупреждать вас о том, что файл рабочего дерева стал неотслеживаемым.


2 Вы можете добавить файл, который в противном случае игнорировался бы, используя git add -f, и вы можете иметь набор файлов в каталогах, которые не игнорируются,добавьте некоторые из этих файлов в индекс, затем измените .gitignore, чтобы игнорировать содержащиеся в них каталоги.Все это приводит к файлам в индексе, которые бы не попали туда более прямым или менее принудительным (add -f) методом.Это случаи, которые я рассматриваю в отношении: они не неправильны , но они не соответствуют этой последней точке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...