Як насправді діють правила виключення .gitignore


146

Я намагаюся вирішити проблему gitignore у великій структурі каталогів, але для спрощення запитання я звів її до наступного.

Я маю таку структуру каталогів із двох файлів (foo, bar) у абсолютно новому сховищі git (поки що не вноситься жодних комісій):

a/b/c/foo
a/b/c/bar

Очевидно, що "git status -u" показує:

# Untracked files:
...
#       a/b/c/bar
#       a/b/c/foo

Що я хочу зробити, це створити .gitignore файл, який ігнорує все, що знаходиться в / b / c, але не ігнорує файл 'foo'.

Якщо я створити .gitignore таким чином:

c/

Тоді "git status -u" показує foo та bar як ігноровані:

# Untracked files:
...
#       .gitignore

Що таке, як я очікую.

Тепер, якщо я додаю правило виключення для foo, таким чином:

c/
!foo

За даними сторінки gitignore, я б очікував, що це спрацює. Але це не так - він все ще ігнорує foo:

# Untracked files:
...
#       .gitignore

Це також не працює:

c/
!a/b/c/foo

Це також не робить:

c/*
!foo

Дає:

# Untracked files:
...
#       .gitignore
#       a/b/c/bar
#       a/b/c/foo

У цьому випадку, хоч foo вже не ігнорується, бар також не ігнорується.

Порядок правил у .gitignore теж не має значення.

Це також не робить те, що я очікував:

a/b/c/
!a/b/c/foo

Цей ігнорує і foo, і bar.

Одна з ситуацій, яка спрацьовує - це створити файл a / b / c / .gitignore і помістити туди:

*
!foo

Але проблема в тому, що врешті-решт під a / b / c з'являться інші підкаталоги, і я не хочу вводити окремий .gitignore в кожний - я сподівався створити "проектний" .gitignore файли, які можуть міститись у верхній папці кожного проекту та охоплювати всю "стандартну" структуру підкаталогів.

Це також здається рівнозначним:

a/b/c/*
!a/b/c/foo

Це може бути найближчим до "роботи", яку я можу досягти, але потрібно вказати повний відносний шлях та явні винятки, що буде біль, якщо у мене буде багато файлів імені 'foo' на різних рівнях дерева підкаталога.

У будь-якому випадку, я не зовсім розумію, як працюють правила виключення, або вони взагалі не працюють, коли каталоги (а не маніпулятори) ігноруються - правилом, що закінчується в /

Хтось може, будь ласка, пролити трохи світла на це?

Чи є спосіб змусити gitignore використовувати щось розумне, як регулярні вирази замість цього незграбного синтаксису на оболонці?

Я використовую та спостерігаю це за допомогою git-1.6.6.1 на Cygwin / bash3 та git-1.7.1 на Ubuntu / bash3.


пов’язане запитання: stackoverflow.com/questions/2820255/…
Cascabel

4
Відмінно написане запитання! Кількість справ, які ви намагалися, справді допомогли мені зрозуміти, що я робив не так у своєму подібному випадку. Слава Богу, сьогодні я довго шукав.
Алекс Г

Відповіді:


153
/ a / b / c / *
! foo

Здається, для мене працює (git 1.7.0.4 в Linux). Це *важливо, оскільки в іншому випадку ви ігноруєте сам каталог (так що git не загляне всередину) замість файлів у каталозі (що дозволяє виключити).

Подумайте про виключення як "але не цей", а не ", але включайте це" - "ігноруйте цей каталог ( /a/b/c/), але не цей ( foo)" не має великого сенсу; "ігноруйте всі файли в цьому каталозі ( /a/b/c/*), але не цей ( foo)". Щоб процитувати сторінку людини:

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

тобто файл повинен бути виключений, щоб вже знову його включити. Сподіваюся, що проливає трохи світла.


Так, приклад, який ви наводите, працює і для мене добре. Проблема полягає в тому, що / a / b / * не працює, якщо foo знаходиться в c, це означає, що у мене є кілька файлів foo в різних підкаталогах під c (наприклад, d /, e /, f / g / h /, i / j / і т. д.) тоді заперечне правило "! foo" не сприйме їх.
davidA

1
@meowsqueak Дійсно, це не буде. Якщо ви ігноруєте / a / b / *, тоді не існує каталогу для foo! Якщо ви намагаєтесь ігнорувати все дерево каталогів під / a / b, але включаєте будь-який файл з назвою "foo", який може бути в будь-якому місці дерева, я не думаю, що це можливо з .gitignore шаблонами: \
Chris

Насправді це може пояснити, чому це не працює для мене - такі правила не працюють: "/ a / b / *", "! C / foo". Якщо це спрацювало, може не виникнути проблем.
davidA

5
"Я не думаю, що це вдається з .gitignore шаблонами" - я думаю, ви праві, на жаль.
davidA

@meowsqueak Ви можете, можливо, проігнорувати, /a/b/cа потім написати гачок у git add --forceбудь-який файл, що відповідає файлу, перед тим, як зробити його, або щось подібне
Кріс

24

У мене схожа ситуація, моє рішення було використовувати:

/a/**/*
!/a/**/foo

Це повинно працювати для довільної кількості проміжних каталогів, якщо я читаю **правильно.


6

це точно не зрозуміло зі сторінки .gitignore man. Це працює:

*
!/a
!/a/b
!/a/b/c
!/a/b/c/foo

# don't forget this one
!.gitignore

Як згадував Кріс, каталог навіть не відкривається, якщо його виключити. Отже, якщо ви хочете ігнорувати *, але деякі файли, ви повинні побудувати шлях до цих файлів, як зазначено вище. Для мене це зручно, тому що я хочу зробити перевірку коду на 1 файлі бібліотеки, і якщо я хочу зробити інший пізніше, я просто додаю його, а все інше ігнорується.


6

Ось ще один варіант:

*
!/a*
!/a/*
!/a/*/*
!/a/*/*/*

Це ігнорує кожен файл і каталог, за винятком файлів / каталогів трьох рівнів глибоко в межах.


5

Що стосується більш загальної примітки, git1.8.2 буде включати виправлення (також у свою версію 4 , запропоновану запитанням щодо переповнення стека ) від Адама Шпієра щодо визначення того, яке gitignoreправило насправді ігнорує ваш файл.

Дивіться примітки до випуску git1.8.2 та питання SO " яке правило gitignore ігнорує мій файл ":
це буде командою git check-ignore.


1
Дякуємо за трансляцію цієї інформації. check-ignoreтакож підкаже, яке правило забороняє ігнорувати ваш файл, якщо це так.
Адам Шпіерс

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