.gitignore: что не должно попасть в репозиторий

Рейтинг: 55.2% · 12 голосов
Самый подробный курс по Git - от первого коммита до объектной базы и packfiles. Терминал и первый репозиторий, ветки и слияние, конфликты, удалённая работа и форджи (GitHub, GitLab), rebase и переписывание истории, спасение кода через reflog и fsck, stash и worktree, bisect и blame, подмодули и монорепо, хуки и подпись коммитов, внутреннее устройство и масштаб. Для новичков и профи. Актуально на 2026 (Git 2.54, дорожная карта 3.0).
Ответить
Аватара пользователя
Pavel_Git
Сообщения: 52
Зарегистрирован: 11 май 2026, 05:31

.gitignore: что не должно попасть в репозиторий

Сообщение Pavel_Git »

Оглавление курса (52)
  1. Что такое контроль версий и почему победил Git
  2. Установка Git на Windows, macOS и Linux и первая настройка
  3. Терминал и как спрашивать у Git: выживание в командной строке
  4. Ментальная модель Git: три состояния, коммит-снимок и граф
  5. Первый репозиторий: init, add, commit, status
  6. Просмотр изменений: status, diff и индексация по частям
  7. История проекта: git log, диапазоны и поиск
  8. .gitignore: что не должно попасть в репозиторий (вы здесь)
  9. .gitattributes: окончания строк, бинарники и атрибуты
  10. Ветки как указатели: branch, switch и detached HEAD
  11. Слияние веток: fast-forward против трёхстороннего merge
  12. Конфликты слияния: анатомия и уверенное разрешение
  13. Практикум: твой первый день с Git от нуля до push
  14. Удалённые репозитории: remote, fetch, базовый pull и refspec
  15. git push: безопасная отправка и защита от перезаписи
  16. Клонирование: shallow, partial clone, bundle и backfill
  17. GitHub, GitLab и форки: pull request, rulesets и защита веток
  18. Рабочие процессы команды: GitFlow, GitHub Flow, trunk-based
  19. Merge queue и merge trains: безопасное вливание в trunk
  20. Культура code review и Conventional Commits
  21. git reset: три дерева и отмена индексации и коммитов
  22. git revert: безопасная отмена в общей истории
  23. Правка последнего коммита: git commit --amend
  24. git rebase: пересадка истории и золотое правило
  25. Интерактивный rebase: переписать серию коммитов
  26. git cherry-pick: перенос отдельных коммитов
  27. Stacked diffs: цепочка зависимых PR
  28. Массовое переписывание истории: git filter-repo и git replace
  29. git reflog: журнал, который помнит всё
  30. Восстановление утерянных коммитов, веток и файлов
  31. Откат опасных операций: reset --hard, сломанный rebase, плохой merge
  32. git stash: тайник для незавершённой работы
  33. git worktree: несколько рабочих деревьев одного репозитория
  34. git bisect: бинарный поиск регрессии
  35. git blame, mailmap и археология кода
  36. Теги, релизы, archive и bundle
  37. Подмодули: внешние репозитории внутри проекта
  38. Subtree, монорепозитории и sparse-checkout
  39. Git LFS и большие файлы
  40. Вспомогательные команды: clean, mv, rm, restore
  41. Псевдонимы и кастомизация .gitconfig
  42. Хуки Git: автоматизация на клиенте и сервере
  43. Подпись коммитов и тегов: SSH, GPG и Sigstore
  44. Внутреннее устройство I: объектная база Git
  45. Внутреннее устройство II: ссылки, HEAD, notes и reftable
  46. Внутреннее устройство III: индекс, packfiles, gc и обслуживание
  47. От SHA-1 к SHA-256: переход на новый хеш
  48. Огромные репозитории: partial clone, FSMonitor, Scalar
  49. Git в CI/CD: shallow, кеш, merge_group и безопасность
  50. Куда движется Git: дорожная карта 3.0 и экосистема
  51. Профессиональный setup: собираем рабочее окружение Git
  52. 30+ типичных ошибок Git и как из них выбираться
Зачем вообще что-то игнорировать

Открой любой свежий проект, набери

Код: Выделить всё

git status
и ужаснись: в списке кандидатов на коммит лежат сотни файлов, которых ты в глаза не видел. node_modules на 80 мегабайт, папка build с перекомпилированными бинарниками, .idea от твоей IDE, файл .env с паролём от прода и токеном платёжки. Если всё это улетит в репозиторий, ты получишь раздутую историю, конфликты на каждом merge и утечку секретов, которую уже не отмотать назад без переписывания истории.

Репозиторий должен хранить исходники и только исходники - то, из чего проект собирается. Всё, что можно восстановить из исходников (артефакты сборки, скачанные зависимости, кеши) или что относится к конкретной машине разработчика (настройки IDE, локальные пути, секреты), в git попадать не должно. Именно для этого существует механизм игнорирования. Когда люди спрашивают, как заставить git игнорировать файлы, ответ почти всегда один - файл

Код: Выделить всё

.gitignore
. Но за этим скромным текстовым файлом прячется довольно тонкая механика приоритетов и краевых случаев, и именно их мы сейчас разберём до дна.

Главное, что надо усвоить сразу: gitignore влияет только на неотслеживаемые (untracked) файлы. Если файл уже закоммичен - git его отслеживает и будет отслеживать, что бы ты ни написал в .gitignore. Это источник боли номер один, к нему вернёмся отдельно.

Изображение

Синтаксис паттернов: glob, слеши, отрицание

Файл .gitignore - это просто список паттернов, по одному на строку. Git читает их сверху вниз, и последний совпавший паттерн решает судьбу файла. Это критично для отрицаний - запомни порядок. Разберём gitignore синтаксис по кусочкам.
  • Пустая строка ничего не игнорирует, используется для читаемости.
  • Строка, начинающаяся с , - комментарий. Если нужен буквальный символ решётки в начале имени, экранируй: .
  • Обычный glob: совпадает с любым числом символов кроме слеша, - ровно один символ, - диапазон.
  • Завершающий пробелы игнорируются, если их не экранировать через .
Теперь про слеши - тут вся соль.

Код: Выделить всё

# совпадёт с файлом/папкой build в ЛЮБОМ месте дерева
build

# ведущий или серединный слеш привязывает паттерн к корню репозитория
# совпадёт только с /build в корне, но НЕ с src/build
/build

# завершающий слеш = совпадать только с директорией, не с файлом
logs/

# два слеша вокруг - тоже привязка к расположению паттерна
doc/frontpage.txt
Правило простое: если в паттерне есть слеш где-либо кроме самого конца, паттерн считается привязанным к каталогу, где лежит .gitignore (обычно к корню репо). Если слеша внутри нет - паттерн "плавающий" и сработает на любой вложенности. Поэтому поймает логи везде, а - только в корне.

Отдельно про - два астериска для произвольного числа каталогов:

Код: Выделить всё

# любой .json в любом подкаталоге config (config/a/b/x.json тоже)
config/**/*.json

# ведущий ** = в любом месте дерева
**/temp

# завершающий ** = всё внутри
build/**
И самое мощное - отрицание через . Восклицательный знак переигрывает предыдущее совпадение и возвращает файл обратно:

Код: Выделить всё

# игнорируем все логи
*.log
# ...кроме одного важного
!important.log
Грабля с отрицанием, которая ловит всех: нельзя вернуть файл, если игнорируется его родительская директория. Git ради скорости даже не заходит внутрь исключённой папки.

Код: Выделить всё

# так файл НЕ вернётся - git не заглядывает в logs/ вообще
logs/
!logs/keep.log

# а так заработает - сначала глубокий glob, потом исключение папки внутри
logs/**
!logs/keep.log
То есть исключай не саму папку, а её содержимое, тогда отрицание сработает.

Три уровня игнорирования: репо, локально, глобально

Многие думают, что .gitignore один. На деле git собирает правила из нескольких источников, и у них строгий приоритет от высшего к низшему:
  • Паттерны из командной строки - для команд, которые их принимают (например

    Код: Выделить всё

    git add --ignore-errors
    и подобные внутренние механизмы). Высший приоритет.
  • .gitignore в каталогах проекта - тот самый файл, который коммитится и едет всем в команду. Можно класть в подкаталоги, и правила из ближнего к файлу .gitignore перебивают правила из родительского.
  • Код: Выделить всё

    .git/info/exclude
    - локальный список исключений конкретного клона. Он НЕ коммитится и НЕ виден другим. Сюда кладёшь то, что мусорит лично у тебя, но засорять общий .gitignore нет смысла.
  • Код: Выделить всё

    core.excludesFile
    - глобальный файл на всю машину, для всех твоих репозиториев. Низший приоритет.
Глобальный настраивается так:

Код: Выделить всё

git config --global core.excludesFile ~/.gitignore_global
В

Код: Выделить всё

~/.gitignore_global
логично держать всё, что относится к твоему рабочему окружению, а не к проекту:

Код: Выделить всё

.DS_Store
(macOS),

Код: Выделить всё

Thumbs.db
(Windows), мусор твоего редактора. Это правильное место - не нужно засорять командный .gitignore твоими личными IDE-файлами, у коллеги может стоять другой редактор.

Запомни иерархию на пальцах: глобальный = "моя машина", info/exclude = "мой клон этого репо", .gitignore = "весь проект для всей команды".

Главная боль: добавил .gitignore, а файл всё равно в git

Классика жанра и самый частый вопрос новичка. Ты закоммитил

Код: Выделить всё

config.php
с паролями, потом спохватился, дописал его в .gitignore - а git упрямо показывает изменения файла в

Код: Выделить всё

git status
. Почему gitignore не работает? Потому что он и не должен работать на уже отслеживаемых файлах. gitignore фильтрует только untracked. Файл уже в индексе - значит, отслеживается, значит, игнор его не касается.

Лечится через git rm cached - удаляем файл из индекса, но оставляем на диске:

Код: Выделить всё

$ git rm --cached config.php
rm 'config.php'

$ git status
On branch main
Changes to be committed:
        deleted:    config.php

Untracked files:
        config.php
Разбираем вывод. Git показывает две вещи: файл удалён из индекса (deleted - то есть из репозитория он пропадёт при следующем коммите) и тут же снова появился как untracked (на диске-то он остался). А раз теперь он untracked - .gitignore наконец начинает на него действовать. Коммитим удаление:

Код: Выделить всё

$ git commit -m "stop tracking config.php, add to gitignore"
Для целой папки добавляй :

Код: Выделить всё

git rm -r --cached node_modules/
Ключевой нюанс безопасности. git rm --cached убирает файл из будущих коммитов, но он остаётся в истории. Если в config.php лежал боевой пароль, любой, у кого есть клон, достанет его из старых коммитов командой

Код: Выделить всё

git log -p
. Поэтому если ты утопил секрет - мало вынести файл из индекса. Нужно переписать историю (git filter-repo или BFG - filter-branch с 2026 официально не рекомендуют) и, что важнее всего, ротировать сам секрет: сменить пароль, перевыпустить токен. Считай, что утёкший в git ключ скомпрометирован навсегда.

assume-unchanged и skip-worktree - и почему это НЕ для секретов

Есть два флага индекса, которые периодически советуют как "способ игнорировать локальные изменения отслеживаемого файла". Звучит заманчиво, но применять их надо осторожно и точно не для секретов.

Код: Выделить всё

# git перестанет замечать изменения файла
git update-index --assume-unchanged config.php

# почти то же, но с другой семантикой
git update-index --skip-worktree config.php
В чём разница. assume-unchanged - это обещание git'у: "я этот файл не трогаю, не трать время на проверку его mtime". Флаг придуман ради производительности на медленных ФС с тысячами файлов. Git вправе сбросить этот флаг когда угодно (например при checkout или pull), и тогда твои локальные правки вылезут наружу. skip-worktree честнее для задачи "хочу держать локальную версию файла": git защищает твои изменения и старается их не перезатирать, а при checkout не трогает рабочую копию. Но и тут: если файл изменится в индексе сверху (например прилетит при pull), флаг может слететь.

Почему ни тот, ни другой не подходят для секретов и вообще для игнорирования:
  • Файл остаётся в репозитории и в истории - ты просто прячешь локальные правки, а не убираешь файл из git.
  • Флаг живёт только в твоём индексе, его не видят коллеги. У них файл отслеживается как обычно.
  • Флаг ненадёжен: git его сбрасывает в ряде операций, и однажды твой "скрытый" пароль уедет в коммит.
Правильный паттерн для конфигов с секретами другой: держи в репо шаблон

Код: Выделить всё

config.example.php
(отслеживается), а реальный

Код: Выделить всё

config.php
добавь в .gitignore и никогда не коммить. Тогда никаких флагов-костылей не нужно.

Посмотреть, на каких файлах висят эти флаги:

Код: Выделить всё

git ls-files -v | grep -E '^[hsS]'
Строчная буква в начале (h, s) означает, что флаг установлен.

Готовые шаблоны и поиск, почему файл игнорируется

Не пиши .gitignore с нуля. Есть официальная коллекция github/gitignore на GitHub - сотни выверенных шаблонов под языки и среды: Node, Python, Java, JetBrains, VisualStudio и так далее. Скопировал нужный, докинул своё - готово. Хорошие .gitignore примеры там же.

Теперь инструмент, который экономит часы отладки -

Код: Выделить всё

git check-ignore -v
. Когда файл загадочно игнорируется (или наоборот не игнорируется), эта команда показывает, какой именно паттерн и из какого файла виноват:

Код: Выделить всё

$ git check-ignore -v build/output.bin
.gitignore:7:build/        build/output.bin
Читаем вывод слева направо: источник правила (

Код: Выделить всё

.gitignore
), номер строки (), сам паттерн () и после табуляции - проверяемый путь. Теперь ты точно знаешь, что файл срубило седьмой строкой корневого .gitignore. Если источником окажется глобальный файл, путь будет абсолютным; если info/exclude - увидишь именно его. Пустой вывод означает, что файл не игнорируется ни одним правилом.

Пустая папка и .gitkeep

Маленький, но вечный вопрос: git не умеет хранить пустые директории - он версионирует файлы, а не папки. Если тебе нужна структура каталогов (например пустая

Код: Выделить всё

uploads/
или ), кладут туда служебный файл-заглушку. По соглашению его называют

Код: Выделить всё

.gitkeep
:

Код: Выделить всё

$ touch uploads/.gitkeep
$ git add uploads/.gitkeep
Важно понимать: .gitkeep - это не фича git, а просто договорённость. Имя могло быть любым, хоть

Код: Выделить всё

.placeholder
. Сложнее, когда саму папку ты игнорируешь, но хочешь сохранить её пустой каркас - тогда комбинируй игнор содержимого с исключением заглушки:

Код: Выделить всё

# игнорируем всё в logs, кроме самой заглушки
logs/*
!logs/.gitkeep
Мини-лаба: проверь руками

Пройди по шагам, это 5 минут и всё уляжется в голове.

Код: Выделить всё

# 1. свежий репозиторий (вручную ставим main, в ванильном git 2026 дефолт ещё master)
git init -b main ignore-lab && cd ignore-lab

# 2. насорим
mkdir build logs
echo "secret=123" > .env
echo "binary" > build/app.bin
echo "log line" > logs/run.log
touch logs/.gitkeep

# 3. посмотри, что git хочет добавить - всё подряд
git status

# 4. напиши .gitignore
printf '.env\nbuild/\nlogs/*\n!logs/.gitkeep\n' > .gitignore

# 5. теперь status чистый, кроме .gitignore и .gitkeep
git status

# 6. узнай, почему .env игнорируется
git check-ignore -v .env

# 7. демонстрация боли: закоммить файл, ПОТОМ запрети
echo "tracked" > tracked.tmp
git add tracked.tmp && git commit -m "oops"
echo 'tracked.tmp' >> .gitignore
git status            # игнор не сработал, файл отслеживается
git rm --cached tracked.tmp
git status            # теперь deleted + untracked, всё как надо
Обрати внимание на шаг 5: ни , ни , ни логи не маячат в status, но

Код: Выделить всё

logs/.gitkeep
виден - отрицание сработало, потому что мы игнорировали (содержимое), а не (саму папку).

Типичные грабли
  • Игнорируешь папку, а отрицание не работает. Замени на - git не заходит внутрь исключённой директории.
  • Добавил в .gitignore, файл всё равно в git. Он уже отслеживается.

    Код: Выделить всё

    git rm --cached
    и коммит.
  • Лишний завершающий пробел в паттерне - git его учтёт и паттерн не совпадёт. Экранируй или убери.
  • Слеш в середине привязывает к корню.

    Код: Выделить всё

    src/temp
    не поймает

    Код: Выделить всё

    a/src/temp
    . Если нужно везде -

    Код: Выделить всё

    **/src/temp
    или просто .
  • Прячешь секрет через assume-unchanged - однажды флаг слетит и пароль уедет в коммит. Используй .gitignore + example-шаблон.
  • Думаешь, что rm --cached стёр секрет. Нет, он в истории. Переписывай историю и ротируй ключ.
Контрольные вопросы
  • Ты дописал файл в .gitignore, но

    Код: Выделить всё

    git status
    продолжает его показывать. Что произошло и какие две команды это чинят?
  • Чем отличается от , если ниже стоит

    Код: Выделить всё

    !logs/.gitkeep
    ?
  • В каком из трёх уровней (.gitignore / info/exclude / global) логично держать

    Код: Выделить всё

    .DS_Store
    и почему?
  • Почему

    Код: Выделить всё

    skip-worktree
    - плохая идея для хранения секретного config.php, и что правильно вместо него?
Итог

.gitignore - это фильтр для untracked-файлов, собранный из трёх уровней с чётким приоритетом: глобальный, локальный info/exclude и командный .gitignore. Паттерны - это glob со своей логикой слешей и отрицаний, где последнее совпадение решает всё. Главная ловушка - уже отслеживаемый файл: его лечит только

Код: Выделить всё

git rm --cached
. assume-unchanged и skip-worktree - инструменты для локальных правок, но никак не для секретов и не для игнорирования. А

Код: Выделить всё

git check-ignore -v
всегда скажет, какой паттерн виноват. Держи репозиторий чистым - в нём должны жить исходники, а не их следы.
👍1 ❤️2 🔥4 😄 🤔2
Аватара пользователя
envoychan
Сообщения: 1
Зарегистрирован: 18 май 2026, 03:35

Re: .gitignore: что не должно попасть в репозиторий

Сообщение envoychan »

Вот это про logs/ vs logs/* реально спасло. Час сидел не понимал почему gitkeep не возвращается, оказывается папку нельзя игнорить целиком если хочешь исключение внутри. Спасибо!
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
solidity123
Сообщения: 1
Зарегистрирован: 12 май 2026, 13:35

Re: .gitignore: что не должно попасть в репозиторий

Сообщение solidity123 »

А если секрет уже улетел в репо и его клонировали 5 человек - rm --cached же не поможет получается? Правильно понимаю что только пароль менять и историю переписывать, а старый ключ считать спалённым?
👍3 ❤️1 🔥 😄 🤔
Ответить
← Предыдущая глава
История проекта: git log, диапазоны и поиск
Следующая глава →
.gitattributes: окончания строк, бинарники и атрибуты

Все главы курса «Git профессионально: от первого коммита до внутреннего устройства»

Поделиться темой: ✈ Telegram VK
Похожие запросы: .gitignore: как игнорировать файлыGit команды для начинающихgit commit: фиксация измененийgit clone: клонирование репозитория

Вернуться в «Git профессионально: от первого коммита до внутреннего устройства»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость