Модели версирования

Всем системам управления версиями приходиться решать одну фундаментальную проблему: как система будет позволять пользователям совместно использовать информацию, не давая им при этом наступать друг другу на пятки? Для пользователей может оказаться чересчур легко нечаянно перезаписать в хранилище изменения друг друга.

Проблема совместного использования файлов

Рассмотрим такой сценарий: предположим, что у нас есть два сотрудника, Гарри и Салли. Каждый из них решил отредактировать один и тот же файл из хранилища в одно и то же время. Если Гарри сохранит свои изменения первым, тогда, возможно, Салли (несколькими секундами позже) может непреднамеренно перезаписать их своей новой версией файла. Несмотря на то, что версия Гарри не будет потеряна навсегда (т.к. система помнит каждую версию), внесённые Гарри изменения не будут отражены в новой версии файла Салли, потому что она никогда не видела изменений Гарри, которые могла бы учесть. Работа Гарри фактически потеряна - или, по крайней мере, отсутствует в последней версии файла, - и, вероятно, непредумышленно. Как раз та ситуация, которой мы и хотим избежать!

Рисунок 2.2. Проблема потери изменений

Проблема потери изменений

Модель Блокирование-Изменение-Разблокирование

Многие системы управления версиями используют для решения этой проблемы модель блокирование-изменение-разблокирование. В такой системе хранилище разрешает вносить изменения в файл только одному человеку за раз. До того, как Гарри сможет внести изменения в файл, он должен сначала его заблокировать. Блокирование файла подобно взятию книги в библиотеке: если Гарри заблокировал файл, Салли не сможет сделать в нём никаких изменений. Хранилище отклонит её запрос, если она попытается заблокировать файл. Всё, что она может - читать файл и ждать, когда Гарри закончит свои изменения и снимет блокировку. После того, как Гарри разблокирует файл, его ход окончен, и теперь Салли, в свою очередь, сможет заблокировать и отредактировать.

Рисунок 2.3. Модель Блокирование-Изменение-Разблокирование

Модель Блокирование-Изменение-Разблокирование

Проблема с моделью блокирование-изменение-разблокирование состоит в том, что она накладывает некоторые ограничения и часто создаёт неудобства пользователям:

  • Блокирование может вызвать административные проблемы. Иногда Гарри, заблокировав файл, забывает об этом. Между тем, поскольку Салли всё ещё ждёт, когда она сможет приступить к редактированию файла, её руки связаны. А потом Гарри уезжает в отпуск. Теперь Салли для снятия блокировки Гарри должна обратиться к администратору. Ситуация приводит к ненужной задержке и потере времени.

  • Блокирование может вызвать излишнюю поочерёдность. Что, если Гарри редактирует начало текстового файла, а Салли хочет просто подправить окончание этого же файла? Эти изменения вообще не пересекаются. Они могли бы легко редактировать файл одновременно и никакого вреда это бы не принесло (предполагая корректное слияние изменений). В этой ситуации им не надо делать свои ходы по очереди.

  • Блокирование может вызвать ложное чувство безопасности. Предположим, что Гарри заблокировал и редактирует файл А, в то время, как Салли заблокировала и редактирует файл В. Но допустим, что А и В зависят друг от друга и сделанные в каждом изменения семантически не совместимы. Неожиданно А и В вместе больше не работают. Блокирующая система бессильна в предотвращении проблемы - вместо этого она обеспечила ложное чувство безопасности. Гарри и Салли запросто могут представить, что, блокируя файлы, каждый начинает безопасную изолированную задачу и поэтому это представление изначально препятствует обсуждению их несовместимых изменений.

Модель Копирование-Изменение-Слияние

Subversion, CVS и другие системы управления версиями используют модель копирование-изменение-слияние в качестве альтернативы блокированию. В этой модели клиент каждого пользователя считывает из хранилища проект и создаёт персональную рабочую копию - локальное отражение файлов и каталогов хранилища. После этого пользователи работают параллельно, изменяя свои личные копии. В конце концов, личные копии сливаются в новую, финальную версию. Обычно система управления версиями помогает в слиянии, но, разумеется, в конечном итоге за его корректное выполнение всё равно отвечает человек.

Вот пример: скажем, и Гарри, и Салли создали свои рабочие копии одного и того же проекта, скопировав их из хранилища. Они работают одновременно, и делают изменения в файле A в своих рабочих копиях. Первой свои изменения в хранилище сохраняет Салли. Затем, когда Гарри пытается сохранить свои изменения, хранилище информирует его, что его файл А устарел. Другими словами, файл А в хранилище был как-то изменён с тех пор, как Гарри получил его. Поэтому Гарри просит своего клиента слить (merge) любые изменения из хранилища с его рабочей копией файла А. Возможно, что изменения Салли не пересекаются с его собственными, и, поскольку теперь в его рабочей копии объединены оба набора изменений, он записывает её обратно в хранилище.

Рисунок 2.4. Модель Копирование-Изменение-Слияние

Модель Копирование-Изменение-Слияние

Рисунок 2.5. ...Копирование-Изменение-Слияние. Продолжение

...Копирование-Изменение-Слияние. Продолжение

Но что будет, если изменения Салли всё-таки пересекаются с изменениями Гарри? Что происходит в этом случае? Эта ситуация, называемая конфликтом, обычно не такая уж большая проблема. Когда Гарри просит объединить свои изменения с изменениями из хранилища, его копия файла А помечается как находящаяся в состоянии конфликта: он имеет возможность видеть оба набора конфликтующих изменений, и вручную выбирать между ними. Обратите внимание, программа не может автоматически разрешать конфликты, только человек способен понять и сделать необходимый осмысленный выбор. Когда Гарри вручную разрешил пересекающиеся изменения (возможно, путём их обсуждения с Салли!), он может безопасно сохранить объединённый файл обратно в хранилище.

Модель копирование-изменение-слияние может выглядеть немного хаотично, но на практике она отлично работает. Пользователи могут работать параллельно, никогда не ожидая друг друга. При работе над одними и теми же файлами обычно оказывается, что большинство одновременно вносимых изменений вообще не пересекаются; конфликты бывают редко. И время, потраченное на разрешение конфликтов, значительно меньше времени, отнимаемого системой с блокированием.

В конце концов, всё сводится к одному решающему фактору: взаимодействию пользователей. При плохом взаимодействии пользователей, увеличивается количество и семантических, и синтаксических конфликтов. Нет такой системы, которая сможет заставить пользователей общаться, и нет системы, которая может обнаруживать семантические конфликты. Не стоит успокаиваться ложным обещанием блокирующей системы как-то предотвращать конфликты; на практике, блокирование снижает производительность как ничто другое.

Однако, есть одна распространённая ситуация, когда модель блокирование-изменение-разблокирование оказывается лучше: если вы имеете дело с файлами, не поддающимися слиянию. Например, если ваше хранилище содержит некоторые графические изображения, и два человека изменяют их в одно и тоже время, то нет возможности слить эти изменения вместе. Всё равно, либо Гарри, либо Салли, потеряют свои изменения.

Что же делает Subversion?

По умолчанию Subversion использует модель копирование-изменение-слияние, и в большинстве случаев это всё, что вам нужно. Однако, начиная с версии 1.2, Subversion также поддерживает блокирование файлов, так что если у вас есть необъединяемые файлы, или если руководство просто вынудило вас работать в режиме с блокировками, Subversion сможет предоставить вам такую возможность.