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


89

У чому різниця між додаванням bin, bin/, bin/*і bin/**в моєму файлі .gitignore? Я використовував bin/, але переглядаючи інші файли .gitignoreфайлі eclipse подвійна і одинарна зірки навіть використовуються разом так: tmp/**/*що з цим?) Я бачу, що перші два шаблони також широко використовуються. Чи може хтось пояснити різницю між цими трьома?


4
@unutbu: Прийнята відповідь на це питання, мабуть, оскаржується. Один із найкращих коментарів стверджує, що відповідь насправді є повним міфом.
chandsie

Поведінка повністю вказана в керівництві, і я впевнений, що тут (або десять) є питання / відповідь, що включає всю цю інформацію.
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

1
Я підозрюю, що bin/буде ігнорувати каталог bin, тоді як bin/**буде включати каталог bin, але не будь-який його вміст
Robin Winslow

1
Це здається несумісним із відповіддю Сіддхартхи. Виходячи з відповіді, bin/буде ігнорувати сам каталог (включаючи всі підкаталоги та файли), тоді як bin/**ігноруватиме всі файли в каталозі bin та його підкаталогах, але не сам каталог bin. Точно це чи ні, я не впевнений.
Крістофер Берман,

3
зверніть увагу, що якщо ви хочете відстежувати всі файли в каталозі bin /, але ігнорувати всі файли в його підкаталогах, ви можете зробити це (у наступних рядках) bin/** \n !bin/*(оскільки я не бачу, як примусити розрив рядка в міні-Markdown)
TomRoche

9
Ця відповідь неправильна у багатьох відношеннях. По-перше, git не відстежує каталоги, тому запис .gitignore може завжди відповідати вмісту каталогу, ніколи не каталогу як такому. По-друге, binвідповідає як іменованому файлу, так bin і вмісту binпапки. По-третє, bin/* відповідає будь-яким файлам у його підкаталогах. Ви, хлопці, навіть перевіряли це?
ThomasR

46

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)


1
+1, справді. Вам слід подумати про подання фактичного звіту про помилку, оскільки поведінка видається несподіваною.
chandsie

1
Саме мій варіант використання. Дякую!
Себастьян Граф,

3
Різна поведінка dir/і dir/**повторність. скасування ігнорування з !відбувається, оскільки "Неможливо повторно включити файл, якщо виключено батьківський каталог цього файлу" [джерело ]. Заплутане, але зроблене з міркувань продуктивності. Дивіться відповідне запитання SO .
tanius

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)

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каталогом (і глибше), тепер ігнорується
    • Кореневий рівень не ігнорується (/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)

Як це відповідає на питання?
ThomasR

0

Існує ще одна різниця між 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".

Причиною цього є ці правила:

  • Якщо шаблон закінчується косою рисою, він видаляється з метою наступного опису ...

  • Якщо шаблон не містить косу риску /, 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.