open atlas
↑ К треку
Git: от нуля до сеньора GIT · 02 · 04

Что делает коммит хорошим

Хороший коммит атомарен — одно логическое изменение само по себе — с понятным сообщением: тема в повелительном наклонении до ~50 символов, пустая строка, затем тело про «зачем». Разбираем Conventional Commits (type(scope): subject) и как разбить шумную работу на чистые коммиты.

GIT Junior ◷ 17 min
Уровень
ОсновыJuniorMiddleSenior

Двое разработчиков выкатывают одну и ту же фичу. Один оставляет единственный коммит: git commit -m "stuff", сваливший вместе исправление бага, переименование и новый эндпоинт. Другой оставляет три коммита, каждый про одно, каждый с предложением, объясняющим зачем. Полгода спустя кто-то ловит регрессию и запускает git bisect или git log. Первая история бесполезна — каждый коммит спутанный ком. Вторая читается как changelog. Тот же код, разительно разная поддерживаемость.

К концу этого урока ты сможешь писать атомарные коммиты с сообщениями, которыми будущий инженер реально воспользуется, и разбивать грязный сеанс правок на чистые коммиты.

Цель

После этого урока ты сможешь объяснить, что делает коммит атомарным, писать сообщение коммита с темой в повелительном наклонении и телом про «зачем», применять формат Conventional Commits и разбивать одно шумное изменение на несколько чистых коммитов.

1

Хороший коммит атомарен: он фиксирует ровно одно логическое изменение, и проект на этом коммите всё ещё собирается и проходит тесты. Атомарный не значит «маленький» — он значит самодостаточный. Исправление бага — один коммит; переименование переменной — другой; новая фича — третий. Проверка: можешь ли ты откатить этот единственный коммит начисто, не утащив с ним несвязанную работу? Если откат «исправить логин» отменяет и переименование, коммит не был атомарным. Атомарные коммиты заставляют git revert, git bisect, git cherry-pick и код-ревью работать так, как они задуманы.

2

Строка темы — это однострочное резюме в повелительном наклонении, до примерно 50 символов. Пиши её как команду — что применение коммита сделает, — а не как отчёт в прошедшем времени:

Add rate limiting to login endpoint     ← повелительное, хорошо
Added rate limiting                      ← прошедшее время, избегай
Fixes the thing where login breaks       ← размыто, избегай

Повелительное наклонение естественно читается с собственной фразой git: «если применить, этот коммит Add rate limiting…». Держи тему до ~50 символов, чтобы git log --oneline и GitHub её не обрезали. Заглавная первая буква, без точки в конце — это заголовок, а не предложение.

3

После темы оставь пустую строку, затем тело, объясняющее ЗАЧЕМ — не что. Diff и так показывает, что изменилось; задача тела — рассуждение, которое diff передать не может:

Add rate limiting to login endpoint

Brute-force attempts were spiking auth CPU and occasionally
locking out real users. Cap to 5 attempts per IP per minute;
chose a sliding window over a fixed one to avoid burst gaming
at minute boundaries.

Closes #482

Переноси тело примерно на 72 символах, чтобы оно хорошо читалось в терминалах и git log. Пустая строка между темой и телом обязательна — git и его инструменты опираются на неё, чтобы отделить заголовок от описания. Спустя месяцы «зачем» — единственное, что объясняет неочевидную строку.

4

Conventional Commits придают теме машиночитаемую структуру: type(scope): subject. Небольшой, фиксированный словарь типов префиксует сообщение:

feat(auth): add rate limiting to login
fix(api): correct off-by-one in pagination
refactor(db): extract query builder
docs(readme): document env vars
test(cart): cover empty-basket checkout
chore(deps): bump eslint to 9.x

Частые типы — feat, fix, refactor, docs, test и chore; необязательный (scope) называет затронутую область. Команды принимают это, потому что оно разбираемо: инструменты автоматически генерируют changelog, а feat против fix против маркера !/BREAKING CHANGE автоматически двигает семантическое версионирование. Формат превращает сообщения коммитов в инфраструктуру релизов.

Почему это работает

Почему атомарность делает git bisect волшебным? Bisect бинарным поиском проходит твою историю, чтобы найти коммит, внёсший баг, — он переключается на коммит, ты помечаешь его хорошим или плохим, и он делит диапазон пополам. Это работает, только если каждый коммит собирается и представляет одно изменение: «плохой» коммит тогда указывает на одно логическое изменение, а не на путаницу из пяти. Один мега-коммит, мешающий исправление, переименование и фичу, заставляет bisect приземлиться на ком, который тебе всё равно придётся распутывать руками, — ты выбросил ту самую точность, ради которой инструмент существует.

Разбор примера

Разбиваем спутанный день на чистую историю.

Ты провёл день, и в рабочем дереве теперь три несвязанные вещи: исправление в auth.js, переименование util.jshelpers.js по всему коду и новый эндпоинт /health. Коммит всего разом дал бы один неоткатываемый ком. Вместо этого ты индексируешь по смыслу:

git add auth.js
git commit -m "fix(auth): reject expired session tokens"

git add util.js helpers.js src/   # переименование и места вызова
git commit -m "refactor: rename util to helpers"

git add routes/health.js app.js
git commit -m "feat(api): add /health liveness endpoint"

Для исправления бага ты добавляешь тело с объяснением соображений безопасности (git commit без -m открывает редактор). Теперь git log --oneline читается как три намеренных шага. Если эндпоинт /health позже вызовет проблему, git revert уберёт только тот коммит — исправление и переименование нетронуты. Каша из дня стала историей, которую коллега может прочитать и пройти bisect.

Частая ошибка

Распространённый антипаттерн — «свалка WIP»: кодить весь день, затем git add . && git commit -m "wip" в конце. Это быстро, но уничтожает всякую пользу истории — нельзя откатить одну часть, bisect приземляется на стену несвязанных изменений, а ревью безнадёжно. Если ты честно закоммитил грязный work-in-progress локально, причеши его перед публикацией (интерактивным rebase, позже), чтобы опубликованная история была атомарной. Правило большого пальца: сообщения коммитов пишутся для того, кто будет дебажить твой код в 2 часа ночи через полгода, — часто это ты сам.

Проверь себя
Викторина

Какой коммит лучше всего следует конвенциям атомарного коммита с хорошим сообщением?

Итог

Хороший коммит атомарен — одно логическое изменение, которое собирается и проходит само по себе, так что git revert, git bisect и ревью работают начисто. У его сообщения тема в повелительном наклонении до ~50 символов («Add», не «Added»), обязательная пустая строка, затем тело, объясняющее зачем, с переносом на ~72 символах. Conventional Commits структурируют тему как type(scope): subject с типами feat, fix, refactor, docs, test, chore, что позволяет инструментам автоматически генерировать changelog и двигать семантические версии. Разбивай шумную работу на односмысловые коммиты, индексируя подмножества, — избегай свалки «wip», выбрасывающей всякую пользу истории. Теперь ты завершил цикл записи изменений: индексируй точно, читай, что изменил, игнорируй лишнее и записывай это как чистую атомарную историю.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.