.gitignore Синтаксис: bin vs bin/vs. bin/* vs. bin/**

В чем разница между добавлением bin, bin/, bin/* и bin/** в мой файл .gitignore? Я использовал bin/, но смотрел прочее .gitignore файлы (в файле eclipse двойные и одиночные звезды даже используются вместе следующим образом: tmp/**/* что с этим?) Я вижу, что первые два шаблона также широко используются. Может кто-нибудь объяснить различия между тремя?

Ответ 1

bin соответствует любым файлам или каталогам с именем 'bin'.

bin/ соответствует любым каталогам с именем "bin", что фактически означает все его содержимое, поскольку Git не отслеживает только каталоги.

bin/* соответствует всем файлам и каталогам непосредственно в любом bin/. Это предотвращает Git автоматически поиск любых файлов в своих подкаталогах, но если, скажем, создается подкаталог bin/foo, это правило будет не соответствовать foo содержимому.

bin/** соответствует всем файлам и каталогам в любом каталоге bin/ и во всех его подкаталогах.

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

ПРЕДУПРЕЖДЕНИЕ.. никогда не используйте правила, такие как dir/*, /dir/** и т.д., если вы также не игнорируете что-то, что существует внутри этого каталога. Опустите звездочку или вы можете навсегда потерять много данных из определенных вызовов git gc, git stash и т.д.

Я действительно не знаю, что делать tmp/**/*. Первоначально я думал, что он может использоваться для сопоставления файлов в подкаталогах tmp/, но не файлов, непосредственно присутствующих в tmp/. Но простой тест, кажется, предполагает, что это игнорирует все файлы в tmp/.

Ответ 2

bin и bin/ отличаются только тем, что последний соответствует только каталогу.

bin/**/* совпадает с bin/** (по-видимому, с 1.8.2, согласно ответу @VonC).

Трудный, что я просто потратил час или около того, вырывая мои волосы, заключается в том, что bin/ и bin/** не совсем то же самое! Поскольку ранее игнорирует каталог в целом, а последний игнорирует каждый из файлов внутри него, а git почти во всех случаях не заботится о каталогах, обычно нет разницы. Однако, если вы попытаетесь использовать !, чтобы игнорировать подпуть, вы обнаружите, что git (ahem) игнорирует его, если вы проигнорировали родительский каталог! (опять же, а не содержимое каталога)

Это яснее на примере, поэтому для недавно созданного репозитория настроено так:

$ cat .gitignore
ignored-file
or-dir
dir-only/
!dir-only/cant-reinclude
dir-contents/**
!dir-contents/can-reinclude

$ mkdir or-dir dir-only dir-contents

$ touch file ignored-file or-dir/ignored-file dir-only/cant-reinclude dir-contents/can-reinclude

Существуют следующие файлы без следа:

$ git ls-files --other
.gitignore
dir-contents/can-reinclude
dir-only/cant-reinclude
file
ignored-file
or-dir/ignored-file

Но вы можете видеть, что следующие файлы не игнорируются:

$ git ls-files --other --exclude-standard
.gitignore
dir-contents/can-reinclude
file

И если вы попытаетесь добавить, вы получите:

$ git add dir-only/cant-reinclude
The following paths are ignored by one of your .gitignore files:
dir-only/cant-reinclude
Use -f if you really want to add them.
fatal: no files added

Я считаю это поведение ошибкой. (Все это на git version 1.8.4.msysgit.0)

Ответ 3

Обратите внимание, что, строго говоря, git не отслеживает каталоги, только файлы. Поэтому невозможно добавить каталог, только его содержимое.

Однако в контексте .gitignore git делает вид, что понимает каталоги только по той причине, что

Невозможно повторно включить файл, если родительский каталог этого файла исключен.
https://git-scm.com/docs/gitignore#_pattern_format

Что это значит для шаблонов исключений? Прочтите их подробно:

bin

Это игнорирует

  • с именем bin.
  • содержимое папок с именем bin

Вы можете белыми списками игнорировать bin файлы и папки, добавляя последующие записи !, но вы не можете переадресовывать содержимое папок с именем bin

bin

!bin/file_in_bin # has no effect, since bin/ is blacklisted!
!bin/* # has no effect, since bin/ is blacklisted!
!file_in_bin # has no effect, since bin/ is blacklisted!

!bin # this works

bin/

То же, что и выше, за исключением того, что он не соответствует файлам с именем bin. Добавление конечного / сообщает git только для сопоставления каталогов.

bin/*

Это игнорирует

  • файлы, содержащиеся в папке с именем bin
  • содержимое прямых подпапок папок с именем bin
bin/*  # blacklists bin/file_in_bin and bin/subfolder/

!bin/subfolder/file_in_sub # has no effect, since bin/subfolder is blacklisted!
!bin # whitelists files named bin/bin, since bin/ itself is not blacklisted
!bin/ # has no effect, since bin/ itself is not blacklisted


!bin/file_in_bin # works since bin/ itself is not blacklisted
!file_in_bin # works too
!bin/subfolder # works (so implicitly whitelists bin/subfolder/file_in_sub)
!bin/subfolder/ # works just as well
!bin/* # works for file_in_bin and subfolder/

bin/**

Это игнорирует

  • содержимое bin
  • содержимое подпапок (любой уровень вложенности) в bin
bin/**  # blacklists bin/file_in_bin and
        # bin/subfolder/ and bin/subfolder/file_in_sub and
        # bin/subfolder/2/ and bin/subfolder/2/file_in_sub_2

!bin/subfolder/file_in_sub # has no effect, since bin/subfolder is blacklisted
!bin/subfolder/2/ # has no effect, since bin/subfolder is blacklisted
!bin/subfolder/2/file_in_sub_2 # has no effect, since bin/subfolder is blacklisted

!bin/subfolder # works only in combinations with other whitelist entries,
               # since all contents of subfolder are blacklisted (1)

!bin/file_in_bin # works since bin itself is not blacklisted
!bin/* # works for file_in_bin and subfolder; see (1)

Ответ 4

Я только что сделал новое репо и попробовал кое-что. Вот мои результаты:

НОВЫЕ РЕЗУЛЬТАТЫ

git версия 2.10.1.windows.1

  • Инициализировать почти пустое репо. Только файл README
  • Настройте каталог bin на несколько слоев
    • bin.txt
    • Test.txt
    • bin/a/b/bin.txt
    • bin/a/b/Test.txt
    • bin/a/bin/bin.txt
    • bin/a/bin/Test.txt
    • bin/a/bin.txt
    • bin/a/Test.txt
    • bin/bin.txt
    • bin/Test.txt
  • Добавить bin в gitignore: Результаты
    • Все ниже bin (и глубже) теперь игнорируется
    • Уровень корня не игнорируется (/bin.txt и /Test.txt все еще отображаются)
  • Изменить bin на bin/ в gitignore: Результаты
    • Без изменений
  • Изменить bin/ на bin/*
    • Без изменений
  • Изменить bin/* на bin/**
    • Без изменений
  • Изменить bin/** на bin/**/
    • bin/bin.txt и bin/Test.txt больше не игнорируются
  • Изменить bin/**/ на bin/**/*
    • bin/bin.txt и bin/Test.txt возвращаются к игнорированию

СТАРЫЕ РЕЗУЛЬТАТЫ

git версия: 2.7.0.windows.1

  • Инициализировать почти пустое репо. Только файл README
  • Настройте каталог bin на несколько слоев
    • bin/a/b/Test.txt
    • bin/a/bin/Test.txt
    • bin/a/Test.txt
    • bin/Test.txt
  • Добавить bin в gitignore: Результаты
    • Все ниже bin (и глубже) теперь игнорируется
  • Изменить bin на bin/ в gitignore: Результаты
    • Все под каталогом bin (и глубже) по-прежнему игнорируется (без изменений)
  • Изменить bin/ на bin/*
    • Все под каталогом bin (и глубже) по-прежнему игнорируется (без изменений)
  • Изменить bin/* на bin/**
    • Все под каталогом bin (и глубже) по-прежнему игнорируется (без изменений)
  • Изменить bin/** на bin/**/
    • bin/Test.txt больше не игнорируется
  • Изменить bin/**/ на bin/**/*
    • Все под каталогом bin (и глубже) снова игнорируется

Ответ 5

Обратите внимание, что '**', когда он объединен с подкаталогом (**/bar), должен был измениться по своему поведению, поскольку примечание к выпуску для git1.8.2 теперь упоминает:

Паттерны в файлах .gitignore и .gitattributes могут иметь **/, как шаблон, который соответствует 0 или более уровням подкаталога.

например. "foo/**/bar" соответствует "bar" в "foo" или в подкаталоге "foo".


Правило для запоминания (и которое помогает понять разницу намерений за этим синтаксисом):

Невозможно повторно включить файл, если родительский каталог этого файла исключен.


Обычно, если вы хотите исключить файлы из подпапки папки f игнорирования, вы должны:

f/**
!f/**/
!f/a/sub/folder/someFile.txt

То есть:

  • Если первое правило было f/, папка f/ была бы проигнорирована, а правила ниже относительно f не имели бы значения.
  • f/** достичь того же значения, что и f/, но игнорировать все подэлементы (файлы и подпапки).
    Это дает вам возможность вносить в белый список (исключить из gitignore) подпапки: !f/**/.
  • Поскольку все подтексты f не игнорируются, вы можете добавить правило для исключения файла (!f/a/sub/folder/someFile.txt)

Ответ 6

Существует еще одно отличие между bin/* и bin/.

bin/ соответствует foo/bin/test.txt (как и ожидалось), но bin/* нет, что кажется странным, но оно документировано: https://git-scm.com/docs/gitignore

"Документация/*. html" соответствует "Документация/ git.html", но не "Документация/ppc/ppc.html" или "tools/perf/Documentation/perf.html".

Причиной этого являются следующие правила:

  • Если шаблон заканчивается косой чертой, он удаляется с целью следующего описания & hellip;

  • Если шаблон не содержит косой черты /, Git рассматривает его как шаблон оболочки оболочки и проверяет соответствие имени пути относительно местоположения файла .gitignore & hellip;

  • В противном случае Git рассматривает шаблон как оболочку оболочки, подходящую для использования fnmatch (3) с флагом FNM_PATHNAME & hellip;

Итак, если шаблон заканчивается косой чертой, косая черта удаляется и обрабатывается как шаблон оболочки оболочки, и в этом случае bin соответствует foo/bin/test.txt. Если он заканчивается на /*, косая черта не удаляется и передается fnmatch, что не соответствует в подкаталогах.

Однако это не так для foo/bin/ и foo/bin/*, потому что даже после удаления конечной косой черты из foo/bin/ он все еще содержит косую черту, поэтому он рассматривается как шаблон fnmatch, а не glob. То есть он не будет соответствовать bar/foo/bin/test.txt