Как на самом деле работают правила исключения .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 больше не игнорируется, bar также не игнорируется.

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

Это также не делает то, что я ожидал:

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

Тот игнорирует как foo, так и bar.

Одна ситуация, которая работает, если я создаю файл a / b / c / .gitignore и вставляю туда:

*
!foo

Но проблема в том, что со временем в / 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.

Davida
источник
связанный вопрос: stackoverflow.com/questions/2820255/…
Каскабель
4
Отлично написанный вопрос! Количество дел, которые вы попробовали, действительно помогло мне понять, что я делаю неправильно в моем аналогичном деле. Слава богу, я давно искал сегодня.
Алекс Г

Ответы:

153
/ A / B / C / *
! Foo

Кажется, работает для меня (git 1.7.0.4 на Linux). Это *важно, так как в противном случае вы игнорируете сам каталог (так что git не будет заглядывать внутрь) вместо файлов внутри каталога (что позволяет исключить).

Думайте об исключениях как о том, что «но не этот», а не «но включать это» - «игнорировать этот каталог ( /a/b/c/), но не этот ( foo)» не имеет особого смысла; msgstr "игнорировать все файлы в этом каталоге ( /a/b/c/*), но не этот ( foo)". Чтобы процитировать man страницу:

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

то есть, файл должен быть исключен уже должны быть включены снова. Надеюсь, что проливает свет.

Крис
источник
Да, пример, который вы приводите, тоже хорошо работает для меня. Проблема в том, что / 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

Это должно работать для произвольного числа промежуточных каталогов, если я **правильно прочитал .

medge799
источник
6

это определенно не ясно из справочной страницы .gitignore. Это работает:

*
!/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 будет включать патч (также в его v4 , вызванный каким-то вопросом о переполнении стека ) от Адама Спирса о том, какое gitignoreправило фактически игнорирует ваш файл.

См. Примечания к выпуску git1.8.2 и SO вопрос « какое правило gitignore игнорирует мой файл »:
это будет команда git check-ignore.

VonC
источник
1
Спасибо за передачу этой информации. check-ignoreтакже скажет вам, какое правило предотвращает игнорирование вашего файла, если это так.
Адам Спайерс
@AdamSpiers звучит великолепно. Я обязательно буду играть с этим.
VonC