Системы контроля версий
Проблемы, возникающие при работе с кодом
Контрольные точки
- Пусть имеется немаленький проект, над которым работаете хотя бы Вы, и код которого не будет предан забвению сразу по завершению.
- В проекте периодически добавляется и изменяется различный код.
- В какой-то момент может случится (и случается!) так, что происходит регрессия, и никто не может понять, почему.
- Примерно в эти момент становится понятно, что без контрольных точек, к которым можно вернуться в любой момент, грустно.
- Как следствие, в целях идентификации, необходимо давать этим снапшотам определённые уникальные идентификаторы.
Ветки
- Как только появляется нечто, объявляемое релизом, сразу возникает новая проблема: с одной стороны, нужно исправлять находящиеся в данном релизе ошибки, с другой — развивать код, добавляя новые возможности (и ошибки же).
- Естественным следствием является заведение отдельных снапшотов, которые будут относиться к исправлению имеющегося релиза.
- В связи с появляющейся нелинейностью истории, нужна дополнительная информация для нахождения предыдущего (родительского) снапшота для данного.
- Кроме того, полезно иметь имя для последнего снапшота в рамках данной ветки, чтобы упростить его нахождение среди прочих снапшотов.
- Аналогично можно именовать отдельные выдающиеся снапшоты (например, всё те же релизы).
Сравнение версий
Имея две версии одного кода, можно попытаться вычленить ту часть, которая изменилась. Существуют алгоритмы (например, для текста каноничным является алгоритм Ханта-МакИлроя), позволяющие это делать с тем или иным успехом.
- Обратная операция: применение полученной разности (изменений).
- Отдельный интерес представляют дифф между соседними версиями (показывает изменения, внесённые в рамках данной версии), дифф между ветками (дабы понять, насколько они разъехались), дифф между корнем и концом ветки (сумма всех изменений в рамках ветки).
Слияние изменений
- В дополнение к проблеме, приводящей к созданию различных ветвей, возникает проблема обратная: объединение наработок, сделанных в рамках разных веток (например, багфиксы в стабильной и функции в девелоперской).
Можно попытаться взять изменения, сделанные в рамках одной ветки, и попытаться применить к другой (эта операция в случае взятия изменений, внесённых в очередной версии, называется cherry-pick). Понятно, что это будет плохо работать в случае, если в обоих ветках активно менялись одни и те же места одних и тех же файлов.
- Можно брать изменения, сделанные в рамках отдельных версий, и пытаться применять их в той или иной последовательности. Может получиться чуть лучше.
- Можно написать алгебру исчисления патчей, которая позволит делать мерж автоматом всегда, когда это вообще возможно (здесь уже всё упирается в алгоритм).
Системы контроля версий
Внезапно, система контроля версий
Если вынести все снапшоты на отдельный сервер, получим каноничную централизованную VCS (CVS, subversion). При этом, при использовании централизованной VCS работа происходит следующим образом:
- Локально существует только рабочая копия
- При необходимости получения свежей версии, она выкачивается с сервера
- При необходимости создания нового коммита, изменения отправляются на сервер, где уже формируется новый коммит.
С централизованными VCS связан ряд проблем, среди которых важно выделить следующие:
- Необходимость иметь доступ к серверу для выполнения практически любых действий (в том числе, например, diff'ов).
- Все коммиты становятся доступны сразу всем — любые коррективы вносятся исключительно дополнительными коммитами.
Распределённые VCS
В случае DVCS вместо создания рабочей копии реозиторий клонируется локально, после чего работа уже ведётся с ним. Каковы следствия этого решения:
- Автономность при работе с локальным репозиторием.
- Гибкость в том, кто с кем и как взаимодействует.
- Merge на каждую синхронизацию репозиториев.
- Имена снапшотов должны быть уникальны глобально.
Последние два пункта довольно важны, так как являются проблемами, от качества решения которых напрямую зависит удобство работы с DVCS (помимо наличия развесистых пользовательских интерфейсов).
Другие полезные возможности VCS
Поскольку одна из основных целей создания и использования VCS — работа с архивом исходных кодов, они имеют различный инструментарий для упрощения и автоматизации. В частности:
- Annotate (blame, praise) — позволяет ответить на вопрос «откуда что взялось?».
- Bisect — позволяет ответить на вопрос «когда начало падать?».
- Hooks — основной механизм администратора репозитория. Позволяет вызывать выполнение дополнительных действий при возникновении определённых событий в репозитории (например, коммит, клонирование, и. т. п.).
Git
Git как пример DVCS
% TODO: что вообщу сюда писать? история, почему git
Возможности Git
- Staging
- History rewriting
Best practices
Пара слов о том, каковы должны быть изменения
- Атомарность — не нужно делать коммит, в котором содержится много различных изменений. Этому способствует, например, наличие поддержги staging area в системе контроля версий.
- Независимость — не следует растягивать одно изменение на несколько коммитов. В нераспределённых VCS с этим бороться тяжко, DVCS же имеют средства по rewrite'у истории (в частности, \texttt{commit --amend})
- Внятные описания изменений
- Заведение отдельной ветки на каждое крупное изменение
Порядок работы в случае использования DVCS
Использование DVCS предоставляет большую гибкость в плане организации процедуры внесения изменений в код. Можно выделить две категории свободы:
- Свобода локальных изменений
- Свобода взаимодействия репозиториев
Управление репозиториями
Задачи:
- Создание репозиториев
- Предоставление доступа к репозиториям
- Разграничение прав доступа
- Настройка workflow
git-daemon
Встроенные средства git: git-daemon
Gitolite
- Есть чудесный механизм hook'ов, который позволяет как контролировать совершаемые действия, разрешая или запрещая их на основании их параметров и внутренней логики (pre-* хуки), так и выполнять некие дополнительные действия по совершению различных событий (post-* хуки).
- Можно старательно писать эти хуки самому, но есть уже готовые решения.
Среди них можно отметить такой проект, как gitolite (https://github.com/sitaramc/gitolite). Он интересен тем, что сама его конфигурация также хранится в репозитории (что вполне логично; более того, сия идея была позаимствована из другого проекта — gitosis, развитием которого gitolite и является).
Развёртывание Gitolite
- Заведение пользователя, под которым будет работать gitolite и осуществляться доступ к репозиторию.
root@server\# useradd git
- Развёртывание gitolite
- Копирование ключа администратора gitolite на сервер
client\$ ssh-keygen -f ~/.ssh/gitolite client\$ ssh-copy-id -i ~/.ssh/gitolite.pub git@server
- Выкачивание исходных текстов gitolite и развёртывание оного
client\$ git clone git://github.com/sitaramc/gitolite gitolite client\$ cd gitolite/src client\$ ./gl-easy-install git server git-admin
- Last steps
client\$ tail -31 ./gl-easy-install \\ client\$ cd ~/gitolite-admin/}}
- Конфигурирование: добавление репозиториев и ключей
- Использование
- Копирование ключа администратора gitolite на сервер