git add и git commit: как правильно сохранять изменения
Сохранение изменений в Git идёт в два шага: git add добавляет нужные правки в индекс (staging area), а git commit -m "сообщение" записывает их в историю как один снимок. git status показывает, что изменено и что уже подготовлено. Исправить последний коммит можно через git commit --amend.
В Git нет кнопки «сохранить». Изменения фиксируются в два шага: сначала git add отбирает, какие правки войдут в следующий снимок, затем git commit записывает этот снимок в историю. Промежуточная зона между ними называется индексом, или staging area. Именно её многие не понимают годами — и именно она делает коммиты осмысленными.
Три состояния файла: рабочая папка, индекс, история
Любое изменение проходит путь через три области:
- Рабочая директория — вы редактируете файл в редакторе.
- Индекс (stage) —
git addкладёт текущую версию файла в черновик следующего коммита. - История —
git commitпревращает содержимое индекса в коммит с автором, датой и сообщением.
Удобная аналогия — посылка. Рабочая папка — это комната с вещами, индекс — открытая коробка, куда вы складываете то, что хотите отправить, а коммит — запечатанная и подписанная посылка. Вы сами решаете, что положить в коробку, и не обязаны отправлять всё сразу.
Зачем нужен индекс
Представьте: за час вы починили баг в авторизации, заодно поправили опечатку в README и начали новую функцию. Без индекса всё это попало бы в один коммит «разное». С индексом вы делаете три отдельных коммита, каждый из которых легко понять, проверить на ревью и при необходимости откатить, не задев остальное.
git status: что изменено и что подготовлено
Перед каждым add и commit полезно смотреть git status. Это самая безопасная команда Git — она ничего не меняет.
$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: src/auth.js
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
src/profile.js
- Changes to be committed — уже в индексе, войдёт в коммит;
- Changes not staged — изменено, но не добавлено;
- Untracked files — новые файлы, о которых Git пока не знает.
Короткий формат удобнее, когда файлов много:
$ git status -s
M src/auth.js
M README.md
?? src/profile.js
Первая колонка — состояние в индексе, вторая — в рабочей папке. M слева значит «изменён и добавлен», справа — «изменён, не добавлен», ?? — неотслеживаемый.
git add: как добавить изменения в индекс
| Команда | Что добавляет |
|---|---|
git add file.js | Конкретный файл |
git add src/ | Всё изменённое в папке |
git add . | Все изменения в текущей папке и ниже, включая новые и удалённые файлы |
git add -A | Все изменения во всём репозитории |
git add -u | Только уже отслеживаемые файлы, новые не трогает |
git add -p | Интерактивно, по фрагментам (hunks) |
git add '*.css' | Файлы по шаблону |
Синоним git add — git stage: это одна и та же команда, отсюда и термин «stage», «застейджить».
git add -p: добавить часть файла
Самый недооценённый приём. Если в одном файле перемешаны две разные правки, git add -p покажет каждый фрагмент и спросит, добавлять ли его:
$ git add -p src/auth.js
@@ -12,7 +12,7 @@ function login(user) {
- if (user.password = input) {
+ if (user.password === input) {
(1/2) Stage this hunk [y,n,q,a,d,s,e,?]?
y — добавить, n — пропустить, s — разбить фрагмент мельче, q — выйти. Так из одного файла получаются два аккуратных коммита.
Как проверить, что именно уйдёт в коммит
git diff # изменения, ещё НЕ добавленные в индекс
git diff --staged # то, что уже в индексе и войдёт в коммит
Частая путаница: после git add обычный git diff ничего не показывает. Это не значит, что изменения пропали, — они в индексе, смотрите git diff --staged (синоним --cached).
Как убрать файл из индекса
git restore --staged file.js
Правки в файле при этом остаются, он просто перестаёт быть подготовленным к коммиту. В старых версиях Git для этого использовали git reset HEAD file.js — работает и сейчас.
git commit: фиксируем изменения
git commit -m "Исправить проверку пароля при входе"
[main 3f2a9c1] Исправить проверку пароля при входе
1 file changed, 1 insertion(+), 1 deletion(-)
В ответе — ветка, короткий хеш нового коммита и статистика. В коммит попадает только содержимое индекса. Если после git add вы ещё раз поправили файл, новые правки в коммит не войдут, пока не добавите их снова.
Полезные флаги git commit
| Команда | Что делает |
|---|---|
git commit | Откроет редактор для многострочного сообщения |
git commit -m "..." | Сообщение прямо в команде |
git commit -am "..." | Добавит все изменённые отслеживаемые файлы и закоммитит; новые файлы не попадут |
git commit -v | Покажет diff в редакторе, пока пишете сообщение |
git commit --amend | Перезапишет последний коммит |
git commit --allow-empty -m "..." | Пустой коммит, например чтобы перезапустить CI |
Если редактор по умолчанию — Vim и вы в нём застряли: напишите сообщение, нажмите Esc, затем введите :wq и Enter. Сменить редактор: git config --global core.editor "code --wait" или nano.
Как писать хорошее сообщение коммита
Сообщение читают через полгода, когда ищут, откуда взялся баг. Оно должно отвечать на вопрос «что и зачем», а не «что я делал сегодня».
- первая строка — до 50–72 символов, суть изменения;
- в повелительном наклонении или нейтрально: «Добавить валидацию email», «Fix login redirect»;
- при необходимости — пустая строка и подробности: почему так, какие есть ограничения;
- один коммит — одно логическое изменение.
Плохо: fix, правки, wip, asdf. Хорошо: Исправить падение корзины при пустом списке товаров. Многие команды используют формат Conventional Commits: feat: ..., fix: ..., docs: ....
git commit --amend: как исправить последний коммит
Опечатка в сообщении или забыли добавить файл — не нужно делать новый коммит «забыл файл». Для этого есть amend.
Исправить сообщение
git commit --amend -m "Исправить проверку пароля при входе"
Добавить забытый файл
git add forgotten.js
git commit --amend --no-edit
Флаг --no-edit оставляет прежнее сообщение.
Важное про amend
--amend не «редактирует» коммит, а создаёт новый с другим хешем и заменяет им старый. Пока коммит только у вас — это безопасно. Если вы уже сделали git push, после amend обычный push будет отклонён, и придётся использовать git push --force-with-lease. В общих ветках, где работают другие, так делать не стоит: вы перепишете историю, на которой они уже строят свою работу.
Если amend сделан по ошибке, старый коммит можно найти в git reflog и вернуться к нему через git reset --soft HEAD@{1}.
Типичные ошибки
«nothing to commit, working tree clean»
Изменений нет — всё уже закоммичено, или вы правите файл в другой папке. Проверьте git status и путь к проекту.
«no changes added to commit»
Файлы изменены, но не добавлены в индекс. Выполните git add или используйте git commit -am для уже отслеживаемых файлов.
«Please tell me who you are»
Не заданы имя и e-mail. Один раз выполните git config --global user.name "Имя" и git config --global user.email "почта".
Закоммитили лишнее
Если коммит ещё не отправлен, отмените его, сохранив изменения в индексе: git reset --soft HEAD~1. Затем уберите лишнее через git restore --staged и закоммитьте заново. Если в коммит попал секрет, считайте его скомпрометированным и перевыпустите.
git add . добавил мусор
Настройте .gitignore заранее и перед коммитом смотрите git status. Уже отслеживаемый файл перестаёт отслеживаться только после git rm --cached файл.
Что такое коммит на самом деле
Коммит хранит не разницу с предыдущей версией, а снимок всего проекта: ссылку на дерево файлов, ссылку на родительский коммит, автора, дату и сообщение. Неизменённые файлы не копируются — Git ссылается на уже сохранённые объекты, поэтому история занимает мало места.
Хеш коммита (например, 3f2a9c1) вычисляется из всего этого содержимого. Поменяйте хоть букву в сообщении — получится другой хеш. Именно поэтому --amend и rebase создают новые коммиты, а не правят старые, и поэтому переписанная история конфликтует с копиями у коллег.
Посмотреть историю коротко можно так:
git log --oneline --graph -10
Каждая строка — коммит; если сообщения в вашей истории понятны без открытия диффа, вы всё делаете правильно.
Рабочий цикл на каждый день
git status # что изменилось
git diff # посмотреть правки
git add -p # отобрать нужное
git diff --staged # проверить, что уйдёт в коммит
git commit -m "Понятное описание"
git log --oneline -5 # убедиться, что коммит на месте
git push # отправить на сервер
Коммит сохраняет изменения только локально. Чтобы они попали на GitHub или другой сервер, нужен git push.
Итог
git add собирает следующий снимок в индексе, git status и git diff --staged показывают, что в него войдёт, а git commit записывает снимок в историю. Делайте маленькие логичные коммиты с понятными сообщениями, а для мелких исправлений последнего коммита используйте git commit --amend — до push. Индекс и двухшаговая схема add → commit разобраны на практике в курсе по Git.
Частые вопросы
Как часто нужно делать коммиты?
Каждый раз, когда завершено одно логическое изменение и проект в рабочем состоянии. Лучше много маленьких коммитов, чем один огромный в конце дня.
Есть ли в Git команда git save?
Нет, такой команды нет. Сохранение — это git add и git commit, а временно отложить незаконченную работу можно через git stash.
Как посмотреть, что было в коммите?
Команда git show хеш покажет автора, сообщение и все изменения коммита. Без хеша она покажет последний коммит.
Чем git add . отличается от git add -A?
В современных версиях Git оба добавляют новые, изменённые и удалённые файлы. Разница в области: git add . работает с текущей папкой и вложенными, а git add -A — со всем репозиторием.
Можно ли изменить не последний, а более старый коммит?
Да, через интерактивный rebase: git rebase -i хеш-родителя, затем отметить нужный коммит как edit или reword. Делайте это только с коммитами, которые ещё не отправлены в общую ветку.
Эта тема подробно разобрана с практикой в курсе «Git с нуля до уверенного разработчика».
Перейти к курсу