Коммит в системах контроля версий¶
Коммит (от англ. commit — «совершать, фиксировать») — базовая операция в системах контроля версий (SCM), представляющая собой атомарное сохранение набора изменений в файловом дереве с созданием уникальной точки в истории проекта. Каждый коммит фиксирует состояние всех отслеживаемых файлов на определённый момент, сопровождается уникальным идентификатором (обычно в форме короткого хеша SHA-1 или SHA-256), автором, отметкой времени и комментарием, описывающим суть изменений. Коммиты образуют связанную цепочку, формирующую историю проекта, и позволяют откатываться к любому сохранённому состоянию, сравнивать версии и объединять параллельную работу нескольких разработчиков.
¶Основные системы контроля версий
Коммит как операция реализована в большинстве современных SCM. Наиболее распространённые из них:
| Система | Тип | Особенности коммитов |
|---|---|---|
| Git | распределённая | Коммиты хранятся локально, идентификатор — SHA-хеш содержимого |
| Mercurial | распределённая | Коммит с числовым номером, локальное хранение |
| Subversion (SVN) | централизованная | Коммит — отправка изменений на сервер, глобальный номер ревизии |
| CVS | централизованная | Коммит пофайловый, без единого номера ревизии |
| Bazaar | распределённая | Коммит с локальным идентификатором |
В распределённых системах (Git, Mercurial) коммиты создаются локально и не требуют подключения к серверу; их синхронизация с общим репозиторием выполняется отдельными командами push и pull. В централизованных системах (SVN, CVS) коммит обычно означает немедленную отправку изменений в центральный репозиторий, доступный всем участникам.
¶Устройство коммита в Git
Git — наиболее популярная на сегодня система контроля версий, и именно в ней коммит имеет наиболее проработанную структуру. Коммит в Git — это объект, содержащий:
- ссылку на родительский коммит (или несколько родительских при слиянии);
- корень дерева файлов (tree-объект), описывающий полное состояние проекта;
- автора и коммитора (часто одно и то же лицо);
- временную метку с часовым поясом;
- сообщение коммита;
- собственный SHA-1 хеш, вычисляемый из всех этих данных.
Типичный цикл работы с коммитами в Git включает три шага:
- Изменение файлов — редактирование исходного кода, конфигураций, документации.
- Индексация (stage) — команда
git addпомещает выбранные изменения в промежуточную область (staging area, индекс). - Фиксация — команда
git commitсоздаёт новый коммит из содержимого индекса.
Такая трёхшаговая модель даёт возможность формировать осмысленные коммиты, включающие только логически завершённые изменения, даже если в рабочем каталоге одновременно присутствуют правки по нескольким задачам.
¶Идентификатор коммита
В Git каждый коммит получает уникальный 40-символьный SHA-1 хеш, вычисляемый из содержимого коммита, дерева и родительских ссылок. На практике для краткости используется первые 7–12 символов хеша. Хеш является детерминированным: одинаковое содержимое всегда даёт одинаковый идентификатор, что обеспечивает целостность истории — любое изменение старого коммита неизбежно меняет хеши всех последующих.
В Subversion коммиты нумеруются монотонно возрастающими целыми числами (ревизии), причём номер присваивается центральным сервером. В Mercurial используется гибрид: локальный числовой идентификатор, дополняемый SHA-хешем.
¶Сообщение коммита
Сообщение коммита — текстовое описание, поясняющее, что и зачем было изменено. Качественное сообщение — важная часть культуры работы с версиями. Существуют устоявшиеся соглашения:
- Первая строка — краткое резюме (обычно до 50 символов), в повелительном наклонении («Добавить обработку ошибок»).
- Пустая строка, затем подробное описание мотивов и деталей при необходимости.
- Конвенциональные коммиты (Conventional Commits) — формат, где первая строка начинается с типа:
feat:,fix:,docs:,refactor:,test:и т. д. Этот стандарт позволяет автоматически генерировать журнал изменений и определять версию по семантическому версионированию.
¶Слияние и ветвление
Коммиты тесно связаны с моделью ветвления. Каждая ветка в Git — это ссылка на определённый коммит; при добавлении нового коммита ветка перемещается вперёд. Операция слияния (merge) создаёт новый коммит с двумя или более родителями, объединяя истории двух веток. При конфликтах слияния Git требует ручного разрешения перед созданием итогового коммита.
Отдельной разновидностью является «rebase» — перенос коммитов из одной ветки на другую с созданием их копий (новых хешей) и изменением родительской цепочки. Это позволяет поддерживать линейную историю ценой изменения идентификаторов.
¶Откат и исправление
Git допускает отмену последствий коммита несколькими способами:
git revert— создаёт новый коммит, инвертирующий изменения указанного;git reset— перемещает указатель ветки на более ранний коммит (с сохранением, очисткой или переносом изменений в рабочий каталог);git commit --amend— заменяет последний коммит, изменяя его содержимое или сообщение (создаётся новый хеш).
Первые два способа безопасны для уже опубликованной истории; amend и rebase меняют хеши и потому не рекомендуются для коммитов, отправленных в общий репозиторий.
¶Практические принципы
Существует ряд общепринятых практик работы с коммитами:
- Атомарность — каждый коммит должен содержать одно логически завершённое изменение.
- Инвертируемость — коммит по возможности должен легко откатываться.
- Регулярность — фиксация на каждом завершённом этапе работы, а не «гигантский» коммит в конце дня.
- Читаемая история — предпочтение небольших осмысленных коммитов перед массовыми правками.
Эти принципы легли в основу методологий непрерывной интеграции, где автоматизированные сборки и тесты запускаются на каждом коммите.
¶История термина
Операция фиксации изменений существовала ещё в ранних SCM: RCS (1982), SCCS (1972) и CVS (1986) использовали понятие «commit» или его эквиваленты. С появлением BitKeeper и随后 Git (создан Линусом Торвальдсом в 2005 году для разработки ядра Linux) коммит приобрёл распределённый характер и стал центральной единицей истории проекта.
