2. Работа с изменениями и индекс (staging)
Цель темы: уверенно управлять тем, что попадает в коммит, и отменять изменения на разных этапах — рабочая директория, индекс, последний коммит.
Определения терминов
Индекс (staging area, индекс)
Индекс — промежуточная область между рабочей директорией и историей коммитов. Физически это файл .git/index. В нём хранится «снимок» того, что должно войти в следующий коммит. Команда git add не сразу пишет в историю, а обновляет индекс; git commit сохраняет в репозиторий только содержимое индекса.
В контексте этой темы важно различать три зоны:
| Зона | Описание | Команды просмотра / изменения |
|---|---|---|
| Рабочая директория | Файлы, которые вы редактируете | git status, git diff |
| Индекс (staged) | То, что подготовлено к коммиту | git diff --staged, git restore --staged |
| Последний коммит | Уже сохранённая история | git log, git show |
Staged / unstaged
- Staged (подготовлено) — файл или изменение уже добавлено в индекс командой
git addи попадёт в следующий коммит. - Unstaged (не в индексе) — изменение есть только в рабочей копии и в коммит не попадёт, пока не выполнить
git add.
Amend (исправление последнего коммита)
Amend — изменение последнего коммита: можно поменять сообщение или добавить в него новые файлы/правки без создания нового коммита. История переписывается (меняется хеш последнего коммита), поэтому на общих ветках после amend обычно нужен осторожный push (или не делать amend уже запушенных коммитов).
Цепочка: рабочая директория → индекс → коммит
[Рабочая директория] --git add--> [Индекс] --git commit--> [Коммит]
(файлы на диске) (.git/index) (история)
git add <file>— копирует текущее состояние файла из рабочей директории в индекс.git commit— создаёт новый коммит из текущего состояния индекса и сдвигает текущую ветку на этот коммит.- Изменения только в рабочей директории не попадут в коммит, пока вы не добавите их в индекс.
Добавление в индекс
Добавление целиком
git add readme.md # один файл
git add src/ # каталог и всё внутри
git add . # все изменения в репозитории
Добавление по частям (интерактивно)
Чтобы разбить изменения в одном файле на несколько коммитов:
git add -p readme.md
# или
git add --patch readme.md
Git покажет каждый «кусок» (hunk) изменений и спросит: stage (y), skip (n), split (s) и т.д. Так можно включить в коммит только часть правок.
Практика
Перед коммитом выполните git diff --staged, чтобы убедиться, что в индекс попало только нужное.
Снятие с индекса
Убрать файл из индекса, не трогая изменения в рабочей директории:
git restore --staged readme.md # Git 2.23+
git reset HEAD readme.md # классический вариант
После этого файл снова в состоянии «modified», но не «staged».
git restore --staged .
# убрать из индекса все файлы
Отмена изменений в рабочей директории
Вернуть файл к состоянию последнего коммита (или к состоянию в индексе), удалив локальные правки:
git restore readme.md # Git 2.23+
git checkout -- readme.md # старый синтаксис
Внимание
После git restore <file> несохранённые изменения в этом файле теряются. Убедитесь, что не отменяете то, что нужно.
Просмотр различий
Рабочая директория vs индекс
Показывает, что вы изменили в файлах, но ещё не добавили в индекс:
git diff
git diff -- readme.md # только по файлу
Индекс vs последний коммит
Показывает, что уже в индексе и попадёт в следующий коммит:
git diff --staged
git diff --cached # то же самое
Между двумя коммитами
git diff abc123 def456
git diff HEAD~2 HEAD -- src/ # последние два коммита по каталогу src/
Пример
Типичный порядок перед коммитом: git status → git diff (что меняли) → git diff --staged (что попадёт в коммит) → git commit -m "...".
Изменение последнего коммита (amend)
Поменять сообщение последнего коммита
git commit --amend -m "Новое сообщение"
Добавить файл в последний коммит
Например, забыли добавить файл:
git add forgotten.txt
git commit --amend --no-edit
--no-edit оставляет прежнее сообщение коммита.
Production
На общих ветках (например, main) лучше не менять уже запушенные коммиты. Amend уместен для локальных коммитов до первого git push или в личных ветках.
.gitignore
Файл .gitignore в корне репозитория задаёт шаблоны файлов и каталогов, которые Git не отслеживает. Синтаксис:
- Одна строка — один шаблон.
#— комментарий.*— любое количество символов;?— один символ./в начале — только от корня репозитория; в конце — только каталог.!— исключение (файл не игнорировать).
# Зависимости и артефакты
node_modules/
__pycache__/
*.pyc
build/
dist/
# Локальные настройки и секреты
.env
.env.local
*.pem
# IDE
.idea/
.vscode/
*.swp
Исключение: не игнорировать один конкретный файл внутри игнорируемого каталога:
logs/
!logs/.gitkeep
Файлы, уже отслеживаемые Git, из .gitignore не исчезнут автоматически — их нужно перестать отслеживать: git rm --cached <file>.
Паттерны использования
| Паттерн | Описание |
|---|---|
| Проверять diff перед коммитом | Всегда смотреть git diff и git diff --staged, чтобы не закоммитить лишнее и не забыть нужное. |
| Дробить изменения | Использовать git add -p для логического разбиения правок по разным коммитам. |
| Атомарные коммиты | Один коммит — одна логическая правка; так проще ревью и откат. |
| Актуальный .gitignore | Добавлять шаблоны для артефактов и секретов до первого коммита, чтобы они не попали в историю. |
Антипаттерны
| Антипаттерн | Почему плохо | Что делать |
|---|---|---|
git add . без просмотра |
В коммит могут попасть билды, секреты, временные файлы. | Проверять git status и git diff --staged; добавлять файлы выборочно или использовать git add -p. |
| Игнорировать индекс | Путаница между «что изменено» и «что попадёт в коммит». | Явно различать git diff и git diff --staged. |
| Amend уже запушенных коммитов | Переписывание истории на общих ветках ломает коллег и CI. | Amend только до первого push или в личной ветке; в общих ветках использовать git revert. |
| Коммитить секреты | Утечка ключей и паролей в истории. | Держать секреты в .env и в .gitignore; не добавлять конфиги с паролями. |
Примеры из production
Pre-commit хуки
В CI или локально через pre-commit проверяют список файлов в коммите: нет ли билдов, секретов, больших бинарников. Понимание индекса и git diff --staged помогает писать такие проверки (например, «запретить коммитить файлы из списка»).
Проверка в CI
Пайплайн может проверять, что в коммите нет определённых паттернов (например, TODO в продакшен-коде или отладочных выводов). Всё это опирается на то, что коммит — снимок конкретного набора файлов из индекса.
Восстановление после случайного add
Если случайно сделали git add на секрет или лишний файл, достаточно снять его с индекса до коммита: git restore --staged <file>. После коммита отмена сложнее (revert или переписывание истории).
Дополнительные материалы
- Pro Git — глава 2.2 «Запись изменений в репозиторий»
- git add — документация
- git restore — документация
- gitignore — синтаксис
- Pre-commit framework — фреймворк для хуков перед коммитом