Урок курса

Не коммитим мусор: .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. Коммит ещё не выполнен. Какое утверждение правильно объясняет эти два результата?

Выберите один вариант
Без регистрации · результат не сохраняется

Продолжить с сохранением прогресса

Перейдите к уроку в учебном кабинете.

Перейти к интерактивному уроку