Работа с коммитами
Создание осмысленных коммитов и управление историей: amend, revert, reset, cherry-pick
Зачем нужно
- Писать понятные сообщения коммитов для себя и команды
- Исправлять ошибки в последнем коммите
- Откатывать неудачные изменения
- Переносить отдельные коммиты между ветками
Где используется
- Ежедневная разработка — каждый коммит
- Code review — история должна быть читаемой
- Исправление ошибок — откат проблемных коммитов
- Релизы — чистая и понятная история
Предпосылки
- Основные команды
- Понимание staging area и working directory
Анатомия хорошего коммита
Структура сообщения
<тип>: <краткое описание> ← заголовок (до 72 символов)
← пустая строка
<подробное описание> ← тело (опционально)
← пустая строка
<ссылки на задачи> ← футер (опционально)
Пример
feat: добавить авторизацию через Google OAuth
Реализована авторизация через Google с использованием
passport-google-oauth20. Добавлены роуты /auth/google
и /auth/google/callback.
Closes #15
Правила хороших сообщений
- Начинать с глагола в инфинитиве: "добавить", "исправить", "обновить"
- Заголовок до 72 символов
- Не ставить точку в конце заголовка
- Один коммит = одна логическая задача
- Тело объясняет зачем, а не что (что видно из diff)
git commit --amend — исправить последний коммит
# Ситуация: забыли добавить файл
git add forgotten-file.js
git commit --amend
# Откроется редактор — можно изменить сообщение
# Только изменить сообщение (без добавления файлов)
git commit --amend -m "Новое сообщение коммита"
# Добавить файл, но не менять сообщение
git add extra-file.js
git commit --amend --no-edit
Важно:
--amendперезаписывает историю. Не используйте для коммитов, которые уже отправлены в удалённый репозиторий (pushed).
git revert — отмена коммита новым коммитом
# Отменить конкретный коммит (создаёт НОВЫЙ коммит с обратными изменениями)
git revert a1b2c3d
# Отменить последний коммит
git revert HEAD
# Отменить без автоматического коммита
git revert --no-commit a1b2c3d
# Изменения попадут в staging — можно отредактировать перед коммитом
# Отменить диапазон коммитов
git revert HEAD~3..HEAD
До revert: A ── B ── C (проблемный)
После: A ── B ── C ── C' (отмена C)
Revert безопасен — не перезаписывает историю, создаёт новый коммит.
git reset — перемещение HEAD
Три режима reset:
Working Dir Staging Area Commits
--soft сохранены сохранены ← HEAD сдвинут
--mixed (default) сохранены очищена ← HEAD сдвинут
--hard очищена очищена ← HEAD сдвинут
--soft — откатить коммит, оставить всё в staging
git reset --soft HEAD~1
# Последний коммит отменён, но все изменения в staging area
# Удобно чтобы переделать коммит
--mixed — откатить коммит, оставить в working directory
git reset HEAD~1
# или
git reset --mixed HEAD~1
# Последний коммит отменён, изменения в working directory (unstaged)
--hard — полный откат (ОПАСНО!)
git reset --hard HEAD~1
# Последний коммит И все изменения УДАЛЕНЫ
# Используйте только если уверены!
# Откатить к конкретному коммиту
git reset --hard a1b2c3d
Практический пример
# Сделали 3 коммита, хотим объединить в один
git reset --soft HEAD~3
git commit -m "Реализовать модуль авторизации"
# Все 3 коммита сжаты в один
Отменить git reset
# Если случайно сделали reset --hard
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# f4e5d6a HEAD@{1}: commit: Важный коммит ← вот он!
git reset --hard f4e5d6a
# Восстановлено!
git cherry-pick — перенос отдельных коммитов
# Перенести коммит из другой ветки в текущую
git cherry-pick a1b2c3d
# Перенести несколько коммитов
git cherry-pick a1b2c3d f4e5d6a
# Перенести без автоматического коммита
git cherry-pick --no-commit a1b2c3d
feature: A ── B ── C ── D
↓ cherry-pick C
main: X ── Y ── C'
Когда использовать cherry-pick
- Нужен один конкретный фикс из другой ветки
- Hotfix: перенести исправление в release-ветку
- Ошибочно закоммитили в неправильную ветку
Сравнение revert vs reset
| git revert | git reset | |
|---|---|---|
| Создаёт новый коммит | Да | Нет |
| Перезаписывает историю | Нет | Да |
| Безопасно для shared-веток | Да | Нет |
| Можно отменить конкретный коммит | Да | Только последние N |
Когда что использовать
Коммит УЖЕ в remote → git revert (безопасно)
Коммит ТОЛЬКО локально → git reset (чисто)
Нужен один фикс из другой ветки → git cherry-pick
Опечатка в последнем коммите → git commit --amend
git stash — временное сохранение
# Ситуация: работаете над фичей, нужно срочно переключиться
git stash
# Все изменения сохранены, рабочая директория чистая
# Вернуть изменения
git stash pop
# Посмотреть список stash
git stash list
# Применить конкретный stash (не удаляя его)
git stash apply stash@{1}
# Stash с сообщением
git stash push -m "Работа над формой входа"
# Удалить stash
git stash drop stash@{0}
# Удалить все stash
git stash clear
Частые ошибки
git reset --hardна shared-ветке — потеря чужих коммитов, конфликты при pushamendпосле push — придётся делать force push, что опасно- Огромные коммиты — "Обновить всё" с 50 изменёнными файлами нечитаемо
- Revert merge-коммита без -m — Git не знает какого родителя сохранить
- Забыть про reflog — после ошибочного reset можно восстановить данные через
git reflog
Практика
- Сделайте 5 коммитов с разными изменениями
- Используйте
git commit --amendчтобы исправить сообщение последнего - Отмените предпоследний коммит через
git revert - Попробуйте
git reset --soft HEAD~2и объедините коммиты - Создайте две ветки и перенесите коммит через
cherry-pick - Попробуйте
git stashпри переключении между ветками - Изучите
git reflog— найдите в нём предыдущие состояния