Рабочая область, индекс и коммит
Git становится понятнее, если разделить работу на три зоны.
| Зона | Что там находится |
|---|---|
| Working tree | файлы, которые ты прямо сейчас редактируешь |
| Index / staging area | изменения, выбранные для следующего коммита |
| Repository | история уже созданных коммитов |
Команда git add не "сохраняет навсегда", а кладёт изменения в staging area. Команда git commit берёт staged-изменения и создаёт новый снимок в истории.
edit file -> git add -> git commit
working tree -> index -> repository
Почему staging важен
Можно изменить три файла, но добавить в коммит только два. Так история получается чище: один коммит отвечает за одну мысль.
Пример: ты одновременно поправил README.md, добавил app.py и случайно изменил debug.log.
git status
git add README.md app.py
git commit -m "Add app skeleton"
debug.log останется в working tree и не попадёт в коммит. Это нормальная ситуация: Git не требует коммитить всё сразу.
Если нужно добавить только часть изменений внутри одного файла, используется patch mode:
git add -p app.py
Git покажет отдельные "hunks" - фрагменты diff - и спросит, какие добавить в индекс. Это полезно, когда в одном файле случайно оказались две разные логические правки.
git status: карта текущего состояния
git status отвечает на вопрос: что Git видит прямо сейчас?
git status
Типичные состояния:
Untracked files: Git ещё не отслеживает файл
Changes not staged: файл изменён, но не добавлен в index
Changes to be committed: изменения уже staged
nothing to commit: рабочее дерево чистое
Короткий формат:
git status -s
Пример:
?? notes.txt
M app.py
A README.md
??- untracked file;M- файл изменён в working tree, но не staged;A- файл staged как новый.
Если git status -s ничего не выводит, репозиторий чистый.
git diff: что именно изменилось
git status говорит, какие файлы изменены. git diff показывает сами изменения.
Изменения в working tree, которые ещё не staged:
git diff
Изменения, которые уже staged и попадут в следующий коммит:
git diff --staged
Полезная привычка перед коммитом:
git status
git diff
git diff --staged
Так ты видишь и список файлов, и содержимое будущего коммита.
Пример diff:
-print("hello")
+print("hello")
+version = "1.0"
Строки с - были удалены, строки с + добавлены. В реальном diff Git ещё показывает имена файлов и номера строк.
git restore: отменить изменения
git restore работает с файлами в working tree и staging area. Это современная команда для операций, которые раньше часто делали через checkout или reset.
Откатить файл в working tree к версии из последнего коммита:
git restore app.py
Это удалит незакоммиченные изменения в файле. Используй только если правки действительно не нужны.
Убрать файл из staging, но оставить изменения на диске:
git restore --staged app.py
После этого файл вернётся из зоны "Changes to be committed" в "Changes not staged". Содержимое файла не откатится.
Типичный сценарий:
git add app.py
git status
git restore --staged app.py
git status
Это не удаление и не откат. Это только снятие файла с подготовки к коммиту.
git mv: переименование под контролем Git
Переименовать файл можно обычным mv, а потом сделать git add. Но удобнее использовать git mv:
git mv README.md readme.md
git status
git commit -m "Rename README"
git mv выполняет файловое переименование и сразу готовит изменение в index. Git обычно сам понимает переименования по содержимому, но git mv делает намерение явным.
Для DevOps это часто встречается при переименовании конфигов:
git mv values-dev.yaml values-development.yaml
git rm: удалить файл из репозитория
git rm удаляет файл из working tree и готовит удаление к коммиту:
git rm old_config.txt
git commit -m "Remove old config"
Если удалить файл обычным rm, Git тоже заметит удаление, но его нужно будет добавить в staging:
rm old_config.txt
git add old_config.txt
git rm короче и яснее.
Есть отдельный важный режим:
git rm --cached secret.env
Он убирает файл из отслеживания Git, но оставляет его на диске. Это нужно, если файл случайно начали отслеживать, а потом добавили в .gitignore.
.gitignore и отслеживаемые файлы
.gitignore говорит Git, какие неотслеживаемые файлы не нужно предлагать к добавлению.
Пример:
.env
*.log
node_modules/
dist/
Важно: .gitignore не действует на файлы, которые уже отслеживаются. Если secret.env уже был закоммичен, запись .env или secret.env в .gitignore сама по себе не остановит tracking.
Правильный порядок:
echo "secret.env" >> .gitignore
git rm --cached secret.env
git add .gitignore
git commit -m "Stop tracking local secret file"
Файл останется локально, но исчезнет из будущих коммитов.
Чистый цикл перед коммитом
Хороший порядок работы:
git status
git diff
git add README.md app.py
git diff --staged
git commit -m "Add app skeleton"
git status
Разбор:
git statusпоказывает, что изменено.git diffпоказывает незастейдженные изменения.git addвыбирает, что войдёт в коммит.git diff --stagedпроверяет будущий коммит.git commitфиксирует снимок.- Финальный
git statusподтверждает чистое состояние.
Если привыкнуть к этому циклу, Git перестаёт быть чёрным ящиком: ты постоянно видишь, что происходит в каждой зоне.
