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

89

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

Chandsie
источник
4
@unutbu: Принятый ответ на этот вопрос, по-видимому, оспаривается. Один из главных комментариев утверждает, что ответ на самом деле является полным мифом.
chandsie 09
Поведение полностью описано на странице руководства, и я уверен, что здесь есть вопрос / ответ (или десять), которые включают всю эту информацию.
Cascabel
3
Что касается **: stackoverflow.com/questions/1470572/…
Cascabel

Ответы:

84

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

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

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

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

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

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

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

Сиддхартха Редди
источник
10
просто чтобы уточнить, в чем разница между bin/и bin/**?
chandsie 09
1
Я подозреваю bin/, что каталог bin будет игнорироваться, но bin/**будет включать каталог bin, но не его содержимое
Робин Уинслоу,
1
Это кажется несовместимым с ответом Сиддхартхи. Рисование из ответа bin/будет игнорировать сам каталог (включая все подкаталоги и файлы), тогда как bin/**будет игнорировать все файлы в каталоге bin и его подкаталогах, но не сам каталог bin. Точно это или нет, я не уверен.
Кристофер Берман
3
обратите внимание, что если вы хотите отслеживать все файлы в каталоге bin /, но игнорировать все файлы в его подкаталогах, вы можете сделать это (в следующих строках) bin/** \n !bin/*(поскольку я не вижу, как принудительно
выполнить разрыв
9
Этот ответ во многих отношениях неверен. Во-первых, git не отслеживает каталоги, поэтому запись .gitignore может соответствовать только содержимому каталога, а не каталогу как таковому. Во-вторых, binсоответствует как названному файлу, так bin и содержимому binпапки. В-третьих, bin/* соответствует любым файлам в своих подкаталогах. Ребята, вы вообще это тестировали?
ThomasR
46

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

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

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

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

$ 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)

Саймон Бьюкен
источник
1
+1, действительно. Вам следует подумать о том, чтобы заполнить фактический отчет об ошибке, потому что поведение действительно кажется неожиданным.
Chandsie 06
1
Точно мой вариант использования. Благодарность!
Себастьян Граф
3
Различное поведение dir/и dir/**re. отмену игнорирования с помощью !происходит потому, что «невозможно повторно включить файл, если родительский каталог этого файла исключен» [источник ]. Непонятно, но сделано из соображений производительности. См. Связанный вопрос SO .
tanius 05
23

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

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

Невозможно повторно включить файл, если родительский каталог этого файла исключен.
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)
ThomasR
источник
9

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

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

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

  1. Инициализировать почти пустое репо. Только файл README
  2. Заполните 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
  3. Добавить binв gitignore: Результаты
    • Все, что находится ниже binкаталога (и глубже), теперь игнорируется
    • Уровень root не игнорируется (/bin.txt и /Test.txt по-прежнему отображаются)
  4. Редактировать , binчтобы bin/в gitignore: Результаты
    • Без изменений
  5. Изменить bin/наbin/*
    • Без изменений
  6. Изменить bin/*наbin/**
    • Без изменений
  7. Изменить bin/**наbin/**/
    • bin/bin.txtи bin/Test.txtбольше не игнорируются
  8. Изменить bin/**/наbin/**/*
    • bin/bin.txtи bin/Test.txtснова игнорируются

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

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

  1. Инициализировать почти пустое репо. Только файл README
  2. Заполните binкаталог на несколько уровней
    • bin/a/b/Test.txt
    • bin/a/bin/Test.txt
    • bin/a/Test.txt
    • bin/Test.txt
  3. Добавить binв gitignore: Результаты
    • Все, что находится ниже binкаталога (и глубже), теперь игнорируется
  4. Редактировать , binчтобы bin/в gitignore: Результаты
    • Все, что находится ниже binкаталога (и глубже), по-прежнему игнорируется (без изменений)
  5. Изменить bin/наbin/*
    • Все, что находится ниже binкаталога (и глубже), по-прежнему игнорируется (без изменений)
  6. Изменить bin/*наbin/**
    • Все, что находится ниже binкаталога (и глубже), по-прежнему игнорируется (без изменений)
  7. Изменить bin/**наbin/**/
    • bin/Test.txt больше не игнорируется
  8. Изменить bin/**/наbin/**/*
    • Все, что находится ниже binкаталога (и глубже), снова игнорируется
Джо Филлипс
источник
1
Это хороший тест, я думаю, что переименование text.txt в bin.txt лучше поможет выявить некоторые ключевые отличия. Если бы вы сделали это, я бы проголосовал за этот ответ.
Мэтт Джонсон
@MattJohnson Верно ... к сожалению, сейчас я использую более новую версию git
Джо Филлипс
@MattJohnson Я наконец-то дошел до этого
Джо Филлипс
8

Обратите внимание, что ' **' в сочетании с подкаталогом ( **/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)
VonC
источник
Как это отвечает на вопрос?
ThomasR
0

Есть еще одно различие между bin/*и bin/.

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

«Documentation / *. Html» соответствует «Documentation / git.html», но не соответствует «Documentation / ppc / ppc.html» или «tools / perf / Documentation / perf.html».

Причина этого, похоже, заключается в следующих правилах:

  • Если узор заканчивается косой чертой, он удаляется для следующего описания ...

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

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

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

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

user1431317
источник