Урок курса
Не коммитим мусор: .gitignore
Git и GitHub с нуля: бесплатный курс с практикойВ прошлом уроке мы научились формировать атомарные коммиты — каждый фиксирует ровно одну задачу. Но прежде чем добавлять файлы в индекс, нужно убедиться, что туда не попадёт лишнее: временные файлы, кэши, артефакты сборки. Именно для этого существует .gitignore.
Назначение .gitignore и границы его действия
.gitignore — обычный текстовый файл в корне репозитория. В нём перечислены шаблоны путей, и Git использует их, чтобы решить: показывать ли файл в разделе «Untracked files» при git status или нет.
Ключевое слово здесь — «неотслеживаемый». Неотслеживаемый файл отсутствует в текущем индексе Git. Если такой файл совпадает с шаблоном, он перестаёт появляться в разделе «Untracked files» вывода git status — и git add . его не захватит.
Шаблон не удаляет уже отслеживаемый путь из текущего индекса. Новый файл после git add уже отслеживается, даже если первого коммита ещё нет. Если report.tmp сейчас находится в индексе, добавление строки *.tmp в .gitignore не остановит его отслеживание: Git продолжит сообщать об изменениях в нём. Присутствие пути в старом коммите само по себе не определяет его текущий статус; после прекращения отслеживания файл на диске может стать неотслеживаемым и подпадать под шаблон.
Сам .gitignore — тоже файл. Пока он не добавлен в индекс, Git видит его как «Untracked». Шаблоны начинают работать сразу после сохранения файла на диск — перезапускать Git не нужно. Но чтобы правила попали в историю проекта и были доступны другим участникам, .gitignore нужно закоммитить.
Синтаксис шаблонов: имя файла, маска расширения, каталог
Каждая строка .gitignore — один шаблон. Git сравнивает шаблон с путями относительно расположения самого .gitignore. Есть три основных формы.
Точное имя файла. Строка notes.tmp игнорирует файл с таким именем — и только его. Если в проекте есть notes.tmp и old_notes.tmp, под шаблон попадёт только первый.
Маска по расширению. Звёздочка заменяет любое количество символов в имени файла. Строка *.tmp скроет все файлы с расширением .tmp в любом подкаталоге: и report.tmp, и debug.tmp, и subdir/cache.tmp.
Каталог. Завершающий слеш говорит Git, что шаблон описывает каталог, а не файл. Строка .cache/ игнорирует весь каталог .cache и всё его содержимое — включая вложенные файлы и подкаталоги.
Пустые строки Git пропускает. Строки, начинающиеся с #, воспринимаются как комментарии и тоже игнорируются — удобно документировать, зачем добавлен тот или иной шаблон:
# Временные файлы редактора *.tmp # Кэш инструментов сборки .cache/
Таким образом, чтобы определить нужный шаблон, достаточно ответить на один вопрос: это конкретный файл, все файлы с определённым расширением или целый каталог?
Создание .gitignore и сравнение git status до и после
Разберём конкретный сценарий. Для него подготовьте отдельный учебный репозиторий способом из урока о первом коммите: в ветке main уже есть один коммит с README.md, файла .gitignore пока нет. Проверьте через git status, что индекс и рабочие файлы чистые. Это подготовка самого примера; готового состояния для него в учебном архиве нет.
До первого показанного ниже git status создайте обычным текстовым редактором файлы build.tmp и cache.tmp с любым учебным текстом. Через окно открытия или сохранения редактора создайте каталог .cache и сохраните в нём файл data.json с содержимым {}. Не выполняйте git add для этих файлов: они должны отсутствовать в индексе. Теперь в рабочей папке есть два неотслеживаемых временных файла и каталог с неотслеживаемым файлом внутри.
Шаг 1. git status до создания .gitignore
$ git status
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
.cache/
build.tmp
cache.tmp
nothing added to commit but untracked files present
Все три объекта видны. Git предлагает их добавить, но нам это не нужно.
Шаг 2. Создать .gitignore
В редакторе создайте в корне репозитория текстовый файл с точным именем .gitignore и сохраните в нём две строки:
*.tmp .cache/
Шаг 3. git status после создания .gitignore
$ git status
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
nothing added to commit but untracked files present
build.tmp, cache.tmp и .cache/ исчезли из вывода — шаблоны сработали сразу. В «Untracked files» остался только сам .gitignore.
Шаг 4. Добавить .gitignore в индекс и проверить
$ git add .gitignore
$ git diff --staged
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a36991f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.tmp
+.cache/
git diff --staged подтверждает: единственное подготовленное изменение — добавление .gitignore с двумя строками. Индекс сохраняет и ранее отслеживаемый README.md: следующий коммит включает его в полный снимок без изменений. Временные файлы в этот снимок не попадают.
Шаг 5. Закоммитить
$ git commit -m "Добавить .gitignore: игнорировать *.tmp и .cache"
[main 3a7f1c2] Добавить .gitignore: игнорировать *.tmp и .cache
1 file changed, 2 insertions(+)
create mode 100644 .gitignore
$ git status
On branch main
nothing to commit, working tree clean
Рабочее дерево чистое. Правила теперь зафиксированы в истории — при следующем git status и после клонирования репозитория они продолжат работать.
Шаблон не скрывает уже отслеживаемый файл и не защищает секреты
Два инварианта, которые важно запомнить.
Инвариант первый. .gitignore применяется к неотслеживаемым файлам — тем, чьих путей нет в текущем индексе. Добавление шаблона не удаляет отслеживаемый путь из индекса, в том числе после git add ещё до первого коммита.
Предположим, config.env был случайно закоммичен и всё ещё находится в текущем индексе. Теперь его имя добавляют в .gitignore:
config.env
Отслеживание продолжается. git status по-прежнему покажет изменения в config.env; если добавить их в индекс и создать коммит, они попадут в него. Шаблон не применяется к отслеживаемому пути.
Инвариант второй. .gitignore не является средством защиты секретов. Если пароль, ключ API или токен попал в коммит, добавление имени файла в .gitignore не удаляет его из существующих коммитов.
Правильная стратегия — не допускать секреты в коммит с самого начала: указать их имена в .gitignore до первого git add и проверять содержимое индекса перед коммитом. Если ключ или токен уже раскрыт, его обязательно нужно отозвать или заменить у выдавшего его сервиса, сделав старый ключ недействительным. Очистка истории при необходимости выполняется отдельно и не заменяет отзыв: она не отключает доступ по действующему ключу и не удаляет чужие копии. Порядок работы с сервисом и очистки истории выходит за рамки этого урока.
Практический вывод: .gitignore — инструмент для порядка в рабочей папке, а не для безопасности.
Задача 1
В корне учебного репозитория лежат неотслеживаемые файлы build.tmp, notes.tmp и report.md, а также неотслеживаемый каталог .cache с data.json. В корневом .gitignore нужны правила, скрывающие оба .tmp-файла и весь каталог .cache, но сохраняющие report.md видимым. Какая пара строк подходит?
Задача 2
README уже закоммичен и не менялся. temp.tmp — неотслеживаемый файл. Других изменений нет. Создали новый .gitignore с *.tmp, затем выполнили только git add .gitignore, но ещё не commit. Какой status ожидается?
Задача 3
В учебном условии файл passwords.txt с вымышленными данными уже отслеживается и присутствует в старом коммите. Позднее его имя добавили в .gitignore и сохранили правила коммитом. Письменно объясните коллеге влияние нового правила на текущий индекс и прошлые снимки файла. Если бы данные были действующими раскрытыми ключами, самостоятельно определите, как правило влияет на их действительность и какие дальнейшие действия нужны; в упражнении используйте только вымышленные данные.
Платформа проверяет только ответ ниже; действия в своём репозитории и письменные ответы вы проверяете самостоятельно.
Какое утверждение о действии нового правила верно?
Задача 4
Практика: начните с отдельного репозитория с чистым состоянием и одним коммитом README. Создайте редактором build.tmp и cache.tmp с произвольным учебным текстом, а через менеджер файлов — каталог .cache и data.json внутри с текстом {}. Пока не добавляйте их в индекс. Проверьте status до правил, создайте редактором .gitignore с *.tmp и .cache/ на отдельных строках, проверьте status после сохранения. Подготовьте только .gitignore, проверьте staged diff и закоммитьте правила. Запишите выводы status до правил, после сохранения, после add и после commit.
Платформа проверяет только ответ ниже; действия в своём репозитории и письменные ответы вы проверяете самостоятельно.
В контрольной записи после сохранения правил временные файлы исчезли из status, а .gitignore ещё в Untracked. После add .gitignore показан как new file. Коммит ещё не выполнен. Какое утверждение правильно объясняет эти два результата?
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
