open atlas
↑ К треку
Git: от нуля до сеньора GIT · 02 · 03

Игнорирование файлов через .gitignore

Файл .gitignore говорит git, какие пути оставить неотслеживаемыми, через glob-шаблоны. Разбираем якорение, правила каталогов и отрицания, шаблон ** и главный подвох: gitignore влияет только на НЕотслеживаемые файлы — уже отслеживаемому нужен git rm --cached, чтобы не коммититься.

GIT Junior ◷ 16 min
Уровень
ОсновыJuniorMiddleSenior

Не всё в папке проекта должно попадать в контроль версий. Скомпилированный вывод, 200-мегабайтный node_modules, настройки редактора и — опаснее всего — файл .env, полный секретов, лежат прямо рядом с исходниками. Закоммить их — и ты раздуваешь репозиторий, утекаешь учётками и ловишь конфликты слияния на машинно-специфичном мусоре. Фильтр, который держит их снаружи, — это .gitignore.

К концу этого урока ты сможешь точно писать шаблоны .gitignore — якорные, рекурсивные, отрицающие — и чинить провал номер один gitignore: файл, который продолжает коммититься, хотя ты его «проигнорировал».

Цель

После этого урока ты сможешь писать glob-шаблоны .gitignore для файлов, каталогов, якорных путей и исключений, объяснять, почему gitignore влияет только на неотслеживаемые файлы, и использовать git rm --cached, чтобы перестать отслеживать файл, который уже в истории.

1

Файл .gitignore перечисляет шаблоны путей, которые git должен оставить неотслеживаемыми. Ты кладёшь его в корень репозитория (или в любой подкаталог, ограничивая той папкой), и git пропускает совпавшие файлы в git status и git add .:

# комментарии начинаются с решётки
*.log            # игнорировать каждый файл, кончающийся на .log
node_modules/    # игнорировать весь каталог node_modules
.env             # игнорировать файл секретов
build            # игнорировать всё с именем build (файл или каталог, где угодно)

Каждая строка — это glob-шаблон. Пустая строка не совпадает ни с чем (для отступов); строка, начинающаяся с #, — комментарий. Сам файл .gitignore обычно коммитится — он общий, чтобы вся команда игнорировала одно и то же.

2

Glob-шаблоны: * совпадает внутри сегмента пути, ** совпадает через каталоги. Подстановочные знаки — это базовый словарь:

*.log            # любой файл, кончающийся .log, в любом каталоге
temp?.txt        # temp1.txt, tempA.txt (? = один символ)
logs/**/*.log    # файлы .log на любой глубине под logs/
**/cache/        # каталог cache где угодно в дереве

* останавливается на слэше — он совпадает в пределах одного уровня каталога. ** — рекурсивный подстановочный знак, переходящий границы каталогов. Большинство реальных строк .gitignore — это просто правило расширения *.ext или правило каталога dir/; тяжёлые шаблоны встречаются редко.

3

Ведущий слэш якорит шаблон к каталогу самого .gitignore; без него совпадение идёт где угодно. Это различие подводит многих:

/build           # ТОЛЬКО каталог build в корне репозитория
build            # ЛЮБОЙ файл/каталог с именем build, на любой глубине

build (без слэша) игнорирует build, src/build, lib/x/build — каждый build в дереве. /build игнорирует только верхнеуровневый, оставляя исходник src/build отслеживаемым. Завершающий слэш вроде build/ дополнительно ограничивает совпадение каталогами, никогда — файлом с именем build.

4

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

*.log            # игнорировать все логи
!important.log   # ...кроме этого — продолжать отслеживать

Порядок и родительские каталоги важны: нельзя заново включить файл, если его родительский каталог исключён, потому что git никогда не заходит в игнорируемый каталог, чтобы вообще увидеть исключение. Чтобы !secrets/keep.txt сработало, игнорируй содержимое (secrets/*), а не сам каталог (secrets/), чтобы git всё ещё заглядывал внутрь.

Частая ошибка

Критический подвох: .gitignore влияет только на НЕотслеживаемые файлы. Если файл уже отслеживается — закоммичен хоть раз — добавление его в .gitignore ничего не делает; git продолжает записывать его изменения. Это классическое «я добавил .env в gitignore, но он всё равно появляется». Чинится так — перестать отслеживать, сохранив файл на диске:

git rm --cached .env      # перестать отслеживать; файл остаётся на диске
echo ".env" >> .gitignore # убедиться, что он игнорируется впредь
git commit -m "Stop tracking .env; ignore it"

git rm --cached удаляет файл из индекса (так что следующий коммит его выбросит), но оставляет твою копию в рабочем дереве нетронутой. Заметь: содержимое файла всё ещё живёт в прошлых коммитах — если там были реальные секреты, смени их, потому что историю можно достать.

Разбор примера

Секрет, который не хочет игнорироваться.

В начале проекта ты случайно закоммитил .env. Спустя недели ты добавляешь его в .gitignore, но git status всё равно показывает .env как изменённый каждый раз, когда ты его правишь. Причина: он уже отслеживается, так что правило игнорирования инертно.

git rm --cached .env
# rm '.env'   — удалён из индекса, всё ещё на диске
cat .gitignore
# .env        — уже есть, хорошо
git commit -m "Stop tracking .env and ignore it"

Теперь .env неотслеживаемый, и правило gitignore наконец применяется — будущие правки больше не появляются в git status. Но старые коммиты всё ещё содержат то, что было в .env, когда ты впервые его закоммитил, так что ты меняешь эти учётки. Для всего остального машинно-специфичного берёшь готовый шаблон: репозиторий GitHub github/gitignore и gitignore.io генерируют файлы под язык и инструмент (Node, Python, JetBrains, macOS), чтобы не писать каждое правило руками.

Почему это работает

Почему git игнорирует файл игнорирования для отслеживаемых путей? Потому что .gitignore — про решение, что входит в контроль версий, а не про удаление записанной истории. Git считает отслеживаемый файл намеренным решением, которое ты уже принял; молча выбросить его из будущих коммитов из-за появившегося шаблона было бы неожиданно и опасно. Заставляя тебя запустить git rm --cached, git вынуждает сделать решение явным — та же философия, что у области индекса: ничто не покидает и не входит в историю случайно.

Проверь себя
Викторина

Ты закоммитил config.json месяцы назад. Сегодня добавляешь config.json в .gitignore, но git всё равно отслеживает твои правки в нём. Почему и что это чинит?

Итог

Файл .gitignore перечисляет glob-шаблоны путей, которые git должен оставить неотслеживаемыми: *.log (расширение), node_modules/ (каталог), # (комментарий). Ведущий слэш якорит шаблон к каталогу файла (/build = только корень) против совпадения где угодно (build); ** — рекурсивный подстановочный знак; ведущий ! отрицает, заново включая исключение (но не если его родительский каталог исключён). Критический подвох: gitignore влияет только на неотслеживаемые файлы — уже отслеживаемый файл игнорирует правило, пока ты не сделаешь git rm --cached <file>, чтобы выбросить его из индекса, сохранив на диске (и смени любые секреты, ведь история хранит старое содержимое). Берись за шаблоны github/gitignore или gitignore.io вместо ручного написания правил. Дальше ты узнаешь, что отличает хороший коммит от шумного.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.