Subversion или CVS, Bazaar или Mercurial

Материал из CustisWiki

Перейти к: навигация, поиск

Перевод статьи «Subversion or CVS, Bazaar or Mercurial» [1], выполнен сообществом компании «Заказные ИнформСистемы».

Введение

Контроль версий и CVS когда-то были практически синонимами, но эти времена уже давно прошли. Сейчас же, кроме того, что можно выбирать из множества систем, существуют две существенно разные модели управления исходным кодом. В этой статье Джон Фергюсон Смарт (John Ferguson Smart) тестирует четыре популярные open-source системы контроля версий и резюмирует «за» и «против» для каждой из них.

Управление версиями — это ключевой аспект любого проекта разработки ПО, его важность трудно переоценить. Как бы это громко не звучало, но выбор системы управления версиями может предопределить успех или провал процесса разработки. Хорошая система управления исходным кодом (source code management, SCM) органично вписывается в цикл разработки и является естественным расширением инструментария разработчика. С другой стороны, несовершенная система управления исходным кодом легко может сделать жизнь команды разработчиков невыносимой, или, по крайней мере, препятствовать внедрению хороших техник разработки: рефакторинга, частых коммитов, стратегий непрерывной сборки и интеграции ПО.

В поисках совершенного управления версиями, мы познакомим читателей с четырьмя ведущими open-source системами управлениями версиями. Система-ветеран — это CVS, поэтому мы начнём с неё, а затем перейдём к её возможному наследнику — Subversion. Затем мы познакомимся с системами Bazaar и Mercurial, эти две системы представляют собой примеры распределенных (а не централизованных) систем управлениями исходным кодом. Операции управлениями версиями мало различаются в разных системах, и я буду приводить по одному примеру на (приложении Java Petstore от фирмы Sun), чтобы показать сценарий разработки в каждой из систем. Мы предложим вашему вниманию некоторые свои наблюдения о каждой из систем, но окончательный выбор будет за вами.

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

Зачем вам нужен контроль версий

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

Системы контроля версий также помогают членам команды работать одновременно над исходниками проекта так, чтобы не портить код друг другу и вообще «не толкаться локтями». Это можно сделать следующими способами:

  • Первый способ — заблокировать файл, когда над ним работает пользователь, так чтобы остальные не могли редактировать его одновременно с первым. Это модель «блокировка-изменение-разблокировка».
  • Противоположный подход позволяет разработчикам редактировать один и тот же файл одновременно, а затем сливать изменения воедино в новую версию, содержащую изменения всех разработчиков. Этот подход, известный как «копирование-изменение-слияние», более распространен в открытых СУВ[2], потому что она больше соответствует подходу разрабочиков открытого ПО.

СУВ также помогают выделить и именовать важные версии/ревизии вашего проекта, например, релизы продукта.

И наконец, СУВ позволяет сделать новую ветвь исходников и работать над ней, не нарушая стабильность изначальной версии. Например, вы можете выпустить стабильную версию продукта, даже если ваша команда параллельно работает над альтернативной версией разработки. А позднее вы сможете интегрировать изменения в основной ствол продукта и выпустить новую стабильную версию.

Пункты сравнения

В следующих разделах мы познакомимся с четырьмя системами управления версиями, способными реализовать все вышеперечисленные функции.

Все эти продукты слишком функциональны и сложны, чтобы можно было бы в рамках одной статьи исчерпывающим образом разобраться с ними. Вместо этого я приведу краткий тест-драйв каждой из этих СУВ на примере приложения «Sun’s Java Petstore».

Первое, что вам обычно нужно сделать при работе с системой управления версиями — это добавить свой исходный код в репозитарий (хранилище). Далее, вам нужно получить копию последней версии проекта из хранилища. Затем вы можете делать правки и обновлять версии, хранимые в репозитории. Если же другой разработчик одновременно правил те же самые файлы, то произойдет одно из двух:

  • система сможет самостоятельно объединить изменения;
  • вам придется разрешить «конфликт слияния» вручную.

Иногда вам может понадобится пометить отдельную версию проекта уникальным ярлыком («меткой», «тегом»), «прикрепленным» к этой версии. Позднее, вы сможете создать свою собственную изолированную ветвь исходников, внести некоторые изменения, а затем «пронести» их в основной ствол.

Обобщая, получаем, что можно оценивать системы управления версиями, исходя из того, как в этих системах происходит импорт и экспорт, правки и обновления, слияния, а также присвоения меток и ветвления. Возможно, вам также будет интересно, какая из них проще в использовании, как у них с производительностью, и поддерживает ли СУВ бинарные файлы и атомарные коммиты (если непонятно что это — я позже разъясню). В следующих разделах вы увидите, как CVS, SVN, BZR и Mercurial выполняют эти операции, что должно дать вам практические представления о том, как использовать каждую из этих систем в «повседневной жизни». При этом вы также почувствуете, какая система управления версиями лучше всего вам подходит.

Мы будем опираться на приложение «Java Petstore 2.0», так что, возможно, вам будет полезно для начала скачать его.

Централизованные СУВ

Введение в CVS

CVS — система контроля версий с открытым кодом, появившаяся в 1980-ых и до сих пор используемая многими организациями.

Она использует клиент-серверную архитектуру, сохраняя файлы на сервере в виде RCS-файлов (а RCS — это «еще более древняя, чем CVS» система контроля версий, на базе которой построена CVS).

Прежде чем сделать что-либо CVS вам необходимо сообщить ей, где находится CVS-сервер.

Обычно это делается определением переменной среды CVSROOT:

$ export CVSROOT=:pserver:john@cvs.mycompany.com:/usr/local/cvs

Далее, следует залогиниться на сервере:

$ cvs login
(Logging in to john@cvs.mycompany.com)
CVS password:

CVS использует очень простую (и несколько ограниченную) систему аутентификации, основанную на серверных учетных записях UNIX-пользователей и групп.

Чтобы добавить свой проект в репозиторий CVS, нужно выполнить команду cvs import:

$ cvs import javapetstore START-REF START -m "Initial import"

Работа с бинарными файлами

Стандартная команда cvs import достаточно проста, но, как правило, недостаточно мощна для любых сколько-нибудь нетривиальных современных Web-проектов.

Основной недостаток CVS — плохая работа с бинарными файлами. CVS была разработана для работы в основном с текстовыми файлами, и по умолчанию предполагается, что все, что вы добавляете, является текстом. Если вы специально не укажете обратное, CVS будет предполагать, что ваши JARы и GIFы — текстовые файлы, и даже попытается объединять изменения разных версий!

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

Обязательно явно объясните CVS, используя опцию «-kb», что Вы импортируете бинарные файлы:

$ cvs add -kb logo.gif

Если Вы реально заинтересовались, заметим, что можно настроить CVS, конфигурируя сервер, чтобы распознавать бинарные файлы по имени, но это несколько сложно, и здесь мы это опустим.


Элементарные операции в управлении версиями

После размещения Вашего проекта под CVS вам надо произвести извлечение (check out) рабочей копии.

Рабочая копия (working copy) — это копия проекта, которую Вы будете использовать в своей ежедневной разработке, а начальный каталог проекта, который вы импортировали в CVS, теперь можно забросить.

Листинг 1. Извлечение (Checking out) локальной копии javapetstore.

$ cvs checkout javapowerstore
cvs checkout: Updating javapetstore
U javapetstore/3RD-PARTY-LICENSE.txt
U javapetstore/bp-project.xml
U javapetstore/build.xml
U javapetstore/index.html
cvs checkout: Updating javapetstore/bp-project
U javapetstore/bp-project/app-client-ant.xml
U javapetstore/bp-project/app-server-ant.xml
...

Как только вы получили локальную копию, с ней можно работать обычным образом. Вы можете использовать команды типа cvs add и cvs rm, чтобы добавить или удалить файлы из репозитория.

Вот типичный cvs commit:

$ cvs commit -m "Fixed bug #123"

Прежде чем зафиксировать свои изменения, нужно правильно выполнить обновление локальной копии, чтобы загрузить из репозитория свежие изменения.

Если другой разработчик изменил один из файлов, который вы тоже изменили, CVS попытается слить (merge) изменения.

Если слить изменения невозможно, вы получите следующее недвусмысленное сообщение:

Листинг 2. Обновление javapetstore в CVS — найдены конфликты!

$ cvs update
cvs update: Updating .
RCS file: /usr/local/cvs/javapetstore/index.html,v
retrieving revision 1.2
retrieving revision 1.3
Merging differences between 1.2 and 1.3 into index.html
rcsmerge: warning: conflicts during merge
cvs update: conflicts found in index.html
C index.html
cvs update: Updating bp-project

В «конфликтом» файле, конфликтующие строки выделяются следующим образом:

Листинг 3. Сообщения CVS об ошибках

<<<<<<< index.html
p.copy {text-align: right}
=======
p.copy {text-align: center}
>>>>>>> 1.3

Когда вы исправите конфликтующие строки, вы можете закоммитить ваши изменения.

Метки и ветки

В системе контроля версий метки (tags, теги/ярлыки) используются для того, чтобы обозначить выделенные версии вашего приложения.

В CVS можно сделать метку для группы файлов командой cvs tag, как показано ниже:

Листинг 4. Установка меток с помощью команды «cvs tag»

$ cvs tag release-1-0
cvs tag: Tagging .
T 3RD-PARTY-LICENSE.txt
T bp-project.xml
T build.xml
T index.html
cvs tag: Tagging bp-project
T bp-project/app-client-ant.xml
T bp-project/app-server-ant.xml
T bp-project/app-server.properties
T bp-project/build.properties
...

Аналогичный подход используется для создания новой ветви разработки. Ветки позволяют продолжить работу без взаимных помех в разных направлениях, например, разработка новой версии (релиза) и исправлении ошибок в текущей версии.

Листинг 5. Ветвление в CVS

$ cvs tag -b release-1-0-patches
cvs tag: Tagging .
T 3RD-PARTY-LICENSE.txt
T bp-project.xml
T build.xml
T index.html
cvs tag: Tagging bp-project
T bp-project/app-client-ant.xml
...

Проблемы архитектуры CVS

Обзор CVS был бы неполным без указания ряда минусов при его использовании. Короткий список того, что признают многие разработчики:

  • Медленные операции пометки и ветвления;
  • Невозможны переименовывание файлов и перемещение каталогов;
  • Коммиты не атомарны;
  • Плохая поддержка бинарных файлов.

Да, в отличие от большинства современных систем управления версиями, коммиты в CVS не атомарны.

В системе управления версиями, которые поддерживают атомарность коммитов, измененные файлы публикуются в репозитории не «по очереди», а группой (обычно именуемой как change set, «набор изменений»).

При этом, если корректное слияние с существующими в репозитории исходниками невозможно, или если передача изменений «обрывается» (например, из-за сбоев сети), то коммит прерывается и репозиторий останется неизменным. Это гарантирует, что исходный код в репозитории всегда остается согласованным и целостным.

А когда вы фиксируете изменения CVS-ом, не гарантируется, что все они будут полностью и корректно внесены в репозиторий. Если по какой-либо причине коммит не удался, исходный код в репозитории может быть в «нестабильном» состоянии. Это основной архитектурный недочет в CVS.

Многие разработчики также испытывают трудности при использовании CVS-тэгов и веток при разработке широкомасштабного проекта.

Обновление всех файлов «по одному» может быть долгим процессом. К примеру, если вам нужно пометить ваш проект, который содержит 1000 файлов, CVS будет метить каждый файл в вашем проекте поочередно, то есть для каждого файла будет отдельное сетевое взаимодействие. Для большого проекта этот процесс может занять часы.

Как и COBOL, CVS — это legacy[3]-система, которая по-прежнему широко применяется. Тем не менее, старомодная архитектура делает CVS плохим решением для современного процесса разработки, где основное значение имеют скорость и гибкость.

Сегодня большинство разработчиков, при наличии выбора, предпочтут более современные СУВ, такие как Subversion, Bazaar или Mercurial.

Subversion

Subversion — это относительно новый продукт, специально разработанный, чтобы исправить известные недостатки CVS.

Этот пакет ПО, который может похвастать таким современным функционалом как атомарные коммиты, быстрое создание ветвей и меток, переименование и перемещение файлов и директорий, поддержка бинарных файлов, легковесные сетевые транзакции и прочее.

Другая приятная особенность Subversion заключается в легкости настройки сервера Apache для предоставления доступа через HTTP (или HTTPS) к вашему репозиторию, так что вы можете просматривать ваш репозиторий, используя обычный веб-браузер.

Subversion также предоставляет более гибкую, чем в CVS модель аутентификации и более тонкое управление правами пользователей.

Одно из принципиальных различий между Subversion и CVS заключается в том, как эти системы отслеживают изменения.

CVS — это файло-ориентировнная система, которая ведет отдельную историю версий для каждого файла.

Subversion же отслеживает целые ревизии.

Ревизию можно рассматривать как снимок каталога проекта со всем его содержимым в определенный момент времени.

Ревизии — это краеугольный камень атомарных обновлений в Subversion.

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

Альтернативный взгляд на этот подход — это то, что любой набор изменений может быть представлен как «отдельная связка», относящаяся к определенному разработчику.

Это также делает более эффективной обработку бинарных файлов и ускоряет создание меток и ветвей.

К сожалению, в CVS отсутствует даже понятие об атомарности обновлений.

И в результате, в CVS, если публикация не удается или будет прервана по какой-либо причине, репозиторий может остаться в нестабильном, нецелостном состоянии: некоторые файлы будут корректно обновленными, тогда как другие останутся в предыдущей версии.

Subversion в действии

Команды Subversion очень похожи на команды CVS, что позволяет пользователям CVS не чувствовать себя полными идиотами, начиная работу с Subversion.

Импорт проекта в репозиторий Subversion выглядит примерно так:

Листинг 6. Импорт в svn

$ svn import . http://svn.mycompany.com -m "Initial import"
Adding         trunk
Adding         trunk/javapetstore
Adding         trunk/javapetstore/setup
Adding         trunk/javapetstore/setup/sql
Adding         trunk/javapetstore/setup/sql/javadb
Adding         trunk/javapetstore/setup/sql/javadb/delete.sql
Adding         trunk/javapetstore/setup/sql/javadb/cities.del
...
Adding         trunk/javapetstore/web/images
Adding  (bin)  trunk/javapetstore/web/images/purple-jellyfish-med.jpg
Adding  (bin)  trunk/javapetstore/web/images/fish3.gif
Adding  (bin)  trunk/javapetstore/web/images/california-desert-tortoise.jpg
Adding  (bin)  trunk/javapetstore/web/images/CIMG9129-s.jpg
...

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

Структура директорий

Наличие каталога «trunk» в приведенном коде указывает на ещё одно важное отличие CVS от Subversion.

По принятому соглашению, все ветки в Subversion (включая главную, так называемый «основной ствол») хранятся в раздельных каталогах, так же, как и метки.

Рекомендуемая структура директорий для Subversion — это три подкаталога в корневой директории проекта:

  • trunk
  • branches
  • tags

На практике вы можете задать структуру каталогов одним из нескольких способов.

Если ваши проекты в значительной степени независимы и вы хотите создавать различные метки и ветви для каждого из проектов, то можно использовать структуру, подобную этой:

  • myproject1
    • trunk
    • branches
    • tags
  • myproject2
    • ...

Если все проекты в репозитории тесно связаны, можно выбрать такую структуру:

  • trunk
    • myproject1
    • myproject2
    • ...
  • branches
  • tags

Регулярные операции программиста

Как и CVS, Subversion предполагает, что пользователь перед началом повседневной работы будет запрашивать рабочую копию файлов:

Листинг 7. Извлечение проекта javapetstore из SVN-репозитария

$ svn checkout http://svn.mycompany.com/trunk javapetstore
A    javapetstore/setup
A    javapetstore/setup/setup.xml
A    javapetstore/setup/sql
A    javapetstore/setup/sql/javadb
...

Обновление файлов в Subversion также весьма похоже на соответствующую CVS-операцию:

Листинг 8. Обновление в SVN

$ svn update
U    javapetstore/main/java/src/UpdatedClass.java
D    javapetstore/main/java/src/DeletedClass.java
A    javapetstore/main/java/src/AddedClass.java
G    javapetstore/main/java/src/MergedClass.java
C    javapetstore/main/java/src/ConflictingClass.java
...
Updated to revision 10.

Когда Subversion обновляет локальные файлы, он выводит аннотацию к выполняемому обновлению.

Перечисляются все файлы в репозитории, которые были изменены, и каждому соответствует код, обозначающий тип изменений:

  • «U» для обновления (Update),
  • «D» при удалении (Deleted),
  • «A» при добавлении (Added),
  • «G» при удачном слиянии (merGed), и
  • «C» в случае конфликта (Conflicting) .

Конфликты в CVS и Subversion обрабатываются сходным образом, но Subversion представляет их в несколько более читабельном виде:

Листинг 9. Сообщение о неудавшемся слиянии

<<<<<<< .mine
  result = useMyFunction();
=======
  result = useBertsFunction();
>>>>>>> .r21

Subversion не позволит вам подтвердить изменения до тех пор, пока вы не устраните конфликт с помощью команды svn resolved:

$ svn resolved javapetstore/main/java/src/ConflictingClass.java

Также можно добавлять, удалять или переименовывать файлы и директории с помощью интуитивно понятных команд, таких как svn add, svn copy, svn delete и svn move.

Наконец, подтверждение изменений выполняется командой svn commit:

Листинг 10. Подтверждение изменений в SVN

$ svn commit -m "These are my changes"
Adding         LocallyAddedFile.java
Deleting       LocallyDeletedFile.java
Sending        ModifiedFile.java
Transmitting file data ..
Committed revision 11.

Создание меток и веток, а также слияние изменений

В Subversion вы можете создавать тэги просто путём копирования определённой ревизии (или последней ревизии основной ветки) в директорию с названием tags, как показано ниже:

$ svn copy -m "Tagging Release 1.0" http://svn.mycompany.com/trunk http://svn.mycompany.com/tags/release-1.0

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

$ svn copy -m "Creating new alpha development branch" . http://svn.mycompany.com/branches/release-1.1-alpha

Для того чтобы переключиться на созданную ветку, вы можете использовать команду svn switch:

$ svn switch http://svn.mycompany.com/branches/release-1.1-alpha

В какой-то момент вы можете произвести слияние ваших изменений с данными, хранящимися в основной ветке, используя команду svn merge.

Слияние изменений — это одна из наиболее сложных операций в Subversion.

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

Это можно сделать с помощью команды svn log с параметром --stop-on-copy (для того чтобы быть уверенным, что журнал изменений захватит только период с момента отцепления вашей ветки):

Листинг 11. Журнал изменений для ветки

$ svn log http://svn.mycompany.com/branches/release-1.1-alpha --stop-on-copy

r54 | john | 2007-07-27 22:00:38 +1200 (Fri, 27 Jul 2007) | 1 line


------------------------------------------------------------------------
r15 | john | 2007-06-03 22:00:31 +1200 (Tue, 03 Jul 2007) | 1 line
.
.
.
r11 | john | 2007-06-03 21:59:08 +1200 (Tue,  03 Jul 2007) | 1 line


Это означает, что изменения в ветке охватывают ревизии с одиннадцатой по пятьдесят четвёртую.

Таким образом, чтобы произвести слияние изменения с основной веткой, вы можете переключиться на основную ветку и запустить выполнение команды svn merge:

Листинг 12. Слияние изменений (обратите внимание на комментарий)

$ svn switch file:///data/svn/dev-repos/javalamp/trunk

$ svn merge -r 11:54 http://svn.mycompany.com/branches/release-1.1-alpha
...

$ svn commit -m "Merged changes from branch release-1.1-alpha into trunk"

Комментирование при фиксации слияния изменений очень важно, поскольку Subversion не отслеживает подробно историю отращивания веток и слияния изменений. (Примечание переводчика: «Отращивание веток» всегда учитывалось с SVN, а начиная с версии 1.5 — отслеживает и хранит историю слияний).

Subversion в целом

В целом, Subversion — хорошо спроектированная и отлично написанная СУВ с поддержкой сложных функций и IDE, обладает ясной документацией и широкой поддержкой сообщества.

Действительно, Subversion вполне справляется с миссией замены CVS как стандарта в открытых СУВ.

С другой стороны, в Subversion не просто отслеживать слияние веток.

Механизм меток Subversion, основанный на копировании, безусловно быстрый, но ему не хватает интуитивности команды типа «svn tag».

Новые идеи в управлении версиями: Распределённые СУВ

Сейчас мы рассмотрим другую пару систем контроля версий, сильно отличающихся от первых двух по своему дизайну и философии.

Несмотря на то, что и CVS, и Subversion основаны на идее центрального репозитория (хотя Subversion поддерживает зеркалирование в режиме «только чтение»), Bazaar и Mercurial основаны на философии распределенных хранилищ без выделенного центрального сервера.

Отсутствие центрального сервера — это и главное достоинство, и основная слабость распределённой системы контроля версий.

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

Если разработчик работает над несколькими ветками, у него будет несколько полных копий репозитория.

Это устраняет зависимость от центрального сервера, и потенциально делает систему более устойчивой.

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

Для крупных проектов через некоторое время распределённая модель может привести к видимым проблемам с объемом используемого дискового пространства и падением производительности.

Mercurial: встречайте нового малыша

Mercurial — это новейшая система управления версиями с открытым исходным кодом, основанная на распределённой модели.

В Mercurial, как в Subversion или CVS, программисты работают с локальной рабочей директорией.

Однако, в отличие от централизованных решений, Mercurial также хранит копию всей истории проекта целиком на машине каждого разработчика.

В этом случае, разработчики могут работать параллельно, даже в отсутствие сетевого соединения.

Как в Subversion, и в отличие от CVS, в Mercurial используется понятие набора изменений (change set).

Каждая ревизия — это срез проекта, полученный в определенный момент времени.

Когда Вы подтверждаете Ваши изменения, Вы создаете новую ревизию.

Как и Subversion, Mercurial может похвастать и быстрым созданием тегов и веток, хорошей поддержкой бинарных файлов и другими преимуществами, обусловленными использованием концепции «наборов изменений».

Но в отличие от Subversion, когда вы коммитите изменения в Mercurial, вы создаёте новую ревизию только в своём локальном репозитории (который, в соответствии с установленной в Mercurial распределённой моделью, считается таким же «правильным» репозиторием, как и у любого другого пользователя).

Давайте уделим минутку на то, чтобы понять как это работает.

Установка вашего собственного репозитория Mercurial

Первое, что мы делаем, когда начинаем работу с проектом хранимым под Mercurial — клонируем локальную копию проекта.

Как и следовало ожидать для распределённой системы управления версиями, вы можете получить доступ к репозиторию Mercurial через HTTP.

В Листинге 13 я создал копию самого проекта Mercurial.

Листинг 13. Клонирование локальной копии репозитория Mercurial.

$ hg clone http://www.selenic.com/repo/hg
destination directory: hg
destination directory: hg
requesting all changes
adding changesets
adding manifests
adding file changes
added 5027 changesets with 9501 changes to 665 files
583 files updated, 0 files merged, 0 files removed, 0 files unresolved

После получения локальной копии, можно править существующие файлы и добавлять новые, используя интуитивно понятные команды, как как hg add, hg remove и hg rename.

Команда hg status, как аналогичные команды Subversion и CVS, позволяют вам увидеть общую картину — что было модифицировано в ваших файлах проекта, в сравнение с вашей локальной копией репозитория.

Листинг 14. Привычные операции в Mercurial

$ hg add LocallyAddedFile.java
$ hg status
M LocallyModifiedFile.java
A LocallyAddedFile.java

Таким же образом вы подтверждаете изменения своей локальной копии репозитория, используя команду hg commit:

$ hg commit -m "Some minor changes"
No username found, using 'wakaleo@taronga' instead

Push, pull, propagate

Заметьте, что команда hg commit обновляет только вашу локальную копию репозитория.

Поскольку не существует центрального сервера для обновлений, только у вас будет новейший репозиторий.

В этом состоит главная разница между распределённой системой управления версиями и централизованной, такой, как CVS или Subversion.

Чтобы обновить другой репозиторий, вы должны распространить ваши изменения на этот репозиторий, используя команду hg push. Либо другой разработчик может извлечь ваши изменения для своей локальной копии репозитория, используя команду hg pull.

Представим, например, что Jill сделала некоторые изменения, которые вы должны интегрировать в свой исходный код.

Чтобы сделать это, вам нужно «вытащить» её изменения из её машины, вот так:

$ hg pull http://jill@jillsmachine

Слияние и ветвление

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

После слияния вам нужно отправить полученный код в ваш локальный репозиторий, как показано ниже:

$ hg merge
$ hg commit

Как и при любом слиянии, могут появиться конфликты.

Когда случается конфликт, Mercurial не пытается соединить два файла (в отличие от Subversion или CVS).

Вместо этого он показывает конфликтующие файлы и предоставляет вам выбрать любимую программу с GUI для слияния.

Если вы не указываете, откуда вы берёте изменения, Mercurial считает, что вы хотите их получить из оригинального репозитория, который вы использовали для клонирования своей локальной копии.

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

Это также относится к команде hg push, как показано здесь:

Листинг 15. Пронос изменений в исходный репозиторий

$ hg push
*pushing to http://buildserver.mycompany.com/mercurial/repo/myproject
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 3 changes to 2 files

Ветвление обрабатывается просто путём клонирования новой копии вашего локального репозитория.

Добротно реализованы и метки: в принципе, это просто ссылки на определённый набор изменений, которые вы можете создать, используя команду hg tag.

Листинг 16. Ветвление и создание тэгов

$ tag "release candidate 1"
$ hg tags
tip                                2:87726d51f171
release candidate 1                1:1d05b948ba76

Mercurial — это молодой инструмент с некоторым свежим функционалом, таким, как простая пометка наборов изменений.

Так же, как и другими распределёнными системами контроля версий, системой Mercurial пользуется гораздо меньше людей, чем такими распространенными инструментами как CVS или Subversion.

Поддержка Mercurial средами разработки также развита меньше, чем для CVS или Subversion, хотя есть плагин для Eclipse, с основными возможностями по интеграции.

Кому нужен Собор, если есть Базар (Bazaar)[4]?

С недавних пор Bazaar стал довольно известен в определённых кругах благодаря тому, что этой системой контроля версий пользуются разработчики Ubuntu Linux. Как и Mercurial, Bazaar использует распределённую модель, основанную на наборах изменений, и хранит локальную копию всего репозитория на каждой машине. Давайте посмотрим, чем Bazаar отличается от Mercurial.

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

$ bzr whoami "John <john@mycompany.com>"
wakaleo@taronga:/bazaar$ bzr whoami
John <john@mycompany.com>

Если вы хотите начать работу над новым проектом, следующим вашим действием должно стать отращивание новой ветки проекта на вашей локальной машине. Это делается с помощью команды bzr branch. Это то же самое, что команда clone в системе Mercurial. Система Bazaar, как и Mercurial и SVN, поддерживает доступ по HTTP.

$ bzr branch http://buildserver.mycompany.com/bazaar/myproject--head/
Branched 24 revision(s).

Это создаст копию определённой ветки (в данном случае самой последней или основной ветки разработки). После этого вы можете делать с этой копией всё, что душе угодно.

А эти команды вполне обычны и совсем не «базарны»[5]

Ну и когда понадобится зафиксировать изменения, Вы увидите, что эти команды тоже относительно просты. Вы можете добавлять новые файлы или переименовывать имеющиеся в вашем локальном репозитории с помощью команд bzr add и bzr mv. Команда bzr add заслуживает отдельного упоминания благодаря своей простоте. Если вы запустите bzr add без параметров, она автоматически добавит в репозиторий все файлы и каталоги, которых там ещё нет, кроме файлов с расширениями, которые вы пометили как игнорируемые (с помощью команды bzr ignore). Если вы когда-либо испытывали сложности с добавлением файлов в CVS или Subversion, эта команда позволит вам вздохнуть с облегчением:

Листинг 17. Массовое добавление файлов с помощью Bazaar

$ bzr add
added LICENCE.txt
added src/NewClass.java
added README.txt
added test
added test/TestClass.java

Фиксация изменений производится командой bzr commit.

Листинг 18. Commit-ы также достаточно просты:

bzr commit -m "Setup new project"
bzr commit -m "Setup new project"
added LICENCE.txt
added README.txt
added src
added src/NewClass.java
added test
added test/TestClass.java
Committed revision 1.

Bazaar также позволяет достаточно удобно удалять файлы. Он автоматически определяет удалённые файлы, так что вам нет необходимости вручную выполнять команду remove.

Листинг 19. Bazaar фиксирует удаление файла

$ rm RedundantClass.java
$ bzr commit -m "Removed unnecessary file"
missing RedundantClass.java
deleted RedundantClass.java
Committed revision 2.

Более того, если вы допустили ошибку, Bazaar позволит вам легко откатить изменения с помощью очень удобной команды bzr uncommit.

Листинг 20. Иногда вам просто нужен uncommit

$ bzr uncommit
    3 John      2007-08-01
      Updated LICENCE text.
The above revision(s) will be removed.
Are you sure [y/N]? y

Миграция изменений в распределённой сети

До сих пор фиксация изменений происходила только в вашем локальном репозитории. В какой-то момент у вас может возникнуть желание обменяться своими изменениями с другими членами вашей команды. Для этого следует использовать команду bzr push. Проблема в том, что вам нужно знать, с кем вы хотите обменяться изменениями, а также куда из следует загрузить, чтобы быть уверенным, что все заинтересованные участники получат ваш последний и самый лучший код. Теоретически можно было бы обновить компьютер каждого разработчика отдельно. Но на практике часто есть некий центральный сервер, на котором все разработчики складывают последние обновления.

Вот пример команды, обновляющей код на таком центральном сервере:

$ bzr push http://buildserver.mycompany.com/bazaar/myproject--head
All changes applied successfully.
Pushed up to revision 2.

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

bzr push http://buildserver.mycompany.com/bazaar/myproject--head
bzr: ERROR: These branches have diverged.  Try using "merge" and then "push".

Автоматическое слияние изменений

Так же, как CVS и Subversion, Bazaar, в отличие от Mercurial, поддерживает автоматическое слияние изменений в файлах. Алгоритм слияния изменений, использующийся в Bazaar, достаточно мощный, однако он не может полностью избавить от конфликтов. Когда возникает конфликт, Bazaar разрешает его так же, как это делает Subversion. Вот так Bazaar сообщает о конфликте при попытке слияния изменений в локальном и удалённом репозиториях:

Листинг 21. Сообщение о конфликте в Bazaar

bzr merge http://buildserver.mycompany.com/bazaar/myproject--head
 M* LICENCE.txt
  * README.txt
  * src/NewClass.java
  * test/TestClass.java
Text conflict in LICENCE.txt
1 conflicts encountered.

Внутри файла также будет оставлено сообщение о конфликте, в формате, похожем на тот, который используется в SVN:

Листинг 22. Конфликт слияния в Bazaar

$ more LICENCE.txt
<<<<<<< TREE
This is a propriatary license. My lawyer says it's best.
=======
Open source rules! This license is GPL.
>>>>>>> MERGE-SOURCE

И когда мы разрешаем такой конфликт, то информируем Bazaar об этом, используя команду resolve:

$ bzr resolve LICENCE.txt

Затем мы должны внести изменения локально, и вставить их в документы, находящиеся на сервере

Листинг 23. Команды commit и push

$ bzr commit -m "Merged updates"
modified LICENCE.txt
modified README.txt
modified src/NewClass.java
modified test/TestClass.java
Committed revision 4.
$ bzr push http://buildserver.mycompany.com/bazaar/myproject--head
Pushed up to revision 4

Тэги и ветви

Bazaar использует ветви и метки (тэги) почти так же, как это делает Mercurial. Несмотря на то, что тэги — относительно новое средство, встроенное в Bazaar, создать новую ветвь очень просто, благодаря применению команды bzr branch, показанной выше. Тэги управляются командами bzr tag и bzr tags, как показано ниже:

Листинг 24. Управление тэгами в Bazaar

$ bzr tag release-1.0
Created tag release-1.0.
$ bzr tags
release-1.0
john@mycompany.com-20070801104450-h5xcg35tyy3xxo19

Bazaar — роскошный, понятный инструмент управления версиями. Он легко изучаем, хорошо документирован и адаптирован для некоторых известных open source проектов, таких как дистрибутив Ubuntu linux. С другой стороны, как и Mercurial, IDE поддерживает Bazaar с ограничениями: на сегодняшний день, для Eclipse существует лишь alpha-версия плагина (Прим. переводчика: уже давно вышел релиз, см. [3]).

Заключение

В данной статье показана общая картина функционала четырех наиболее заметных систем управления версиями с открытыми исходными кодами. Сначала мы обсудили CVS. Будучи хорошим инструментом в свое время, и до сих пор использующаяся многими проектами, CVS не поддерживает бинарные форматы файлов и атомарные коммиты, и ее ужасные системы меток и ветвлений являются «проклятием» для разработчиков больших проектов. Subversion, популярный наследник CVS, лучше адаптирован под нужды большинства современных корпоративных Java проектов. И CVS и SVN имеют отличную поддержку IDE.

Bazaar и Mercurial являются новыми системами, которые реализуют концепцию распределенного управления версиями. Распределенное управление исходными кодами интересно и дает некоторые практические преимущества перед централизованным управлением. Также можно утверждать, что распределенные средства предоставляют более продвинутый набор команд. С другой стороны, распределенные СУВ далеко не так распространены среди пользователей, по сравнению с CVS и Subversion, и также нуждаются в качественной интеграции с IDE, ведь такая интеграция уже стала обыденностью для CVS и Subversion.

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

Ссылки

  1. John Ferguson Smart, JavaWorld.com, 09/18/07, http://www.javaworld.com/javaworld/jw-09-2007/jw-09-versioncontrol.html
  2. СУВ — система управления версиями.
  3. legacy — устаревшая, унаследованная.
  4. Отсылка к известным эссе и книге «Собор и Базар», посвященной идеологии open-source разработки. См. [1], [2].
  5. В оригинале непередаваемая игра слов «Bazaar» → «bzr» → «bizarre» → «странный»…

Репликация: База Знаний «Заказных Информ Систем» → «Subversion или CVS, Bazaar или Mercurial»

Любые правки этой статьи будут перезаписаны при следующем сеансе репликации. Если у вас есть серьезное замечание по тексту статьи, запишите его в раздел «discussion».