Использование веток

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

В этой главе мы воспользуемся тем же примером, что и в Глава 1, Фундаментальные понятия. Как вы помните, вы и ваш соразработчик Салли совместно используете хранилище, содержащее два проекта, paint и calc. Как отмечалось в Рисунок 4.2, «Начальная структура хранилища», каждый каталог проекта содержит подкаталоги с именами trunk и branches. Назначение этих каталогов вскоре станет понятно.

Рисунок 4.2. Начальная структура хранилища

Начальная структура хранилища

Как и раньше, предположим, что и Салли, и вы имеете рабочие копии проекта «calc». Точнее, каждый из вас имеет рабочую копию /calc/trunk. Все файлы, относящиеся к проекту, находятся в этом подкаталоге, а не прямо в /calc, потому что ваша команда решила размещать «главную линию» разработки в /calc/trunk.

Допустим, перед вами была поставлена задача коренной реорганизации проекта. Это займет много времени и затронет все файлы проекта. Проблема заключается в том, что вы не хотите мешать Салли, которая прямо сейчас занимается исправлением небольших ошибок. Ее работа зависит от постоянной доступности последней версии проекта (каталога /calc/trunk). Если вы начнете пошагово фиксировать свои изменения, вы конечно же смешаете Салли все карты.

Одним из вариантов является временная изоляция: вы и Салли перестаете делиться информацией на неделю или две. В это время вы начинаете перелопачивать и реорганизовывать файлы рабочей копии, но не фиксируете и не обновляете ее до завершения работы над задачей. Однако, в этом случае появляется несколько проблем. Во-первых, это не очень надежно. Большинство людей предпочитают часто сохранять свою работу в хранилище на случай, если с рабочей копией вдруг случится что-то плохое. Во-вторых, это не достаточно гибко. Если вы работаете на разных компьютерах (к примеру, если рабочая копия /calc/trunk есть у вас на двух разных машинах), вам придется вручную копировать изменения туда и обратно, либо делать всю работу на одном компьютере. С другой стороны, вам трудно делиться вносимыми изменениями с кем-то еще. А предоставление возможности знакомиться с проделанной вами работой по мере ее продвижения считается «наилучшей практикой» при разработке любого программного обеспечения. Если никто не будет видеть ваших промежуточных фиксаций, вы теряете потенциал обратной связи. Наконец, когда вы закончите свои изменения, может выясниться, что слить проделанную вами работу с остальным программным кодом компании чрезвычайно трудно. Салли (или кто-то другой) могла внести такие изменения в хранилище, которые трудно совместить с вашей рабочей копией — особенно, если вы выполните svn update впервые после нескольких недель изоляции.

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

Создание ветки

Создать ветку очень просто — при помощи команды svn copy в хранилище создается копия проекта. Subversion может копировать не только отдельные файлы, но и целые каталоги. Итак, вам нужно сделать копию каталога /calc/trunk. Где должна лежать эта новая копия? Где угодно — этот вопрос определяется правилами проекта. Допустим, что по правилам вашей команды ветки создаются в каталоге /calc/branches хранилища, и вы хотите назвать свою ветку my-calc-branch. Тогда вам следует создать новый каталог /calc/branches/my-calc-branch, который будет копией /calc/trunk.

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

$ svn checkout http://svn.example.com/repos/calc bigwc
A  bigwc/trunk/
A  bigwc/trunk/Makefile
A  bigwc/trunk/integer.c
A  bigwc/trunk/button.c
A  bigwc/branches/
Checked out revision 340.

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

$ cd bigwc
$ svn copy trunk branches/my-calc-branch
$ svn status
A  +   branches/my-calc-branch

В результате команда svn copy рекурсивно копирует рабочий каталог trunk в новый рабочий каталог branches/my-calc-branch. Теперь команда svn status покажет, что новый каталог запланирован для добавления в хранилище. Обратите внимание на знак «+» после буквы А. Он означает, что запланированное для добавления представляет собой какую-то копию, а не что-то новое. При следующей фиксации Subversion создаст в хранилище каталог /calc/branches/my-calc-branch, скопировав его из /calc/trunk вместо того, чтобы повторно отправлять по сети всю информацию рабочей копии:

$ svn commit -m "Creating a private branch of /calc/trunk."
Adding         branches/my-calc-branch
Committed revision 341.

А теперь покажем простой способ создания ветки, о котором мы упоминали раньше: команда svn copy может оперировать двумя URL-адресами напрямую.

$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/branches/my-calc-branch \
      -m "Creating a private branch of /calc/trunk."

Committed revision 341.

В сущности, между этими двумя методами нет разницы. Оба варианта создают в правке 341 новый каталог, и этой новый каталог является копией /calc/trunk. Это показывает Рисунок 4.3, «Хранилище, содержащее новую копию». Обратите внимание на то, что второй метод, кроме всего прочего, выполняет немедленную фиксацию. [24]Эта процедура более проста в использовании, так как нет необходимости в выгрузке в рабочую копию значительного объема данных из хранилища. По сути, в этом случае можно вовсе не иметь рабочей копии.

Рисунок 4.3. Хранилище, содержащее новую копию

Хранилище, содержащее новую копию

Работа с веткой

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

$ svn checkout http://svn.example.com/repos/calc/branches/my-calc-branch
A  my-calc-branch/Makefile
A  my-calc-branch/integer.c
A  my-calc-branch/button.c
Checked out revision 341.

В этой рабочей копии нет ничего особенного; это просто зеркало другого каталога хранилища. Однако, если Салли обновит свою рабочую копию, она не увидит там ваших изменений. Рабочая копия Салли создана из каталога /calc/trunk. (Смотрите далее в этой главе раздел «Переключение рабочей копии»: команда svn switch является альтернативным способом создания рабочей копии ветки.)

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

  • Вы внесли изменения в /calc/branches/my-calc-branch/button.c, создав таким образом правку 342.

  • Вы внесли изменения в /calc/branches/my-calc-branch/integer.c, создав правку 343.

  • Салли внесла изменения в /calc/trunk/integer.c, создав правку 344.

Теперь у файла integer.c есть два независимых направления разработки, что демонстрирует Рисунок 4.4, «История ветвления для одного файла».

Рисунок 4.4. История ветвления для одного файла

История ветвления для одного файла

Если посмотреть историю изменений, сделанных в вашей копии integer.c, можно увидеть интересные вещи:

$ pwd
/home/user/my-calc-branch

$ svn log --verbose integer.c
------------------------------------------------------------------------
r343 | user | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   M /calc/branches/my-calc-branch/integer.c

* integer.c:  frozzled the wazjub.

------------------------------------------------------------------------
r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   A /calc/branches/my-calc-branch (from /calc/trunk:340)

Creating a private branch of /calc/trunk.

------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  changed a docstring.

------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  adding this file to the project.

------------------------------------------------------------------------

Обратите внимание на то, что Subversion полностью прослеживает всю историю ветки integer.c во времени, в том числе пересекая точку создания копии. Создание ветки показано как событие в истории, потому что файл integer.c был неявно скопирован при копировании всего каталога /calc/trunk/. Теперь давайте посмотрим, какой результат выдаст такая же команда для Салли:

$ pwd
/home/sally/calc

$ svn log --verbose integer.c
------------------------------------------------------------------------
r344 | sally | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  fix a bunch of spelling errors.

------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  changed a docstring.

------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  adding this file to the project.

------------------------------------------------------------------------

Салли увидит свои собственные изменения в правке 344, а ваши, сделанные в правке 343 — нет. Subversion позаботилась о том, чтобы эти две фиксации затронули разные файлы, имеющие разное расположение в хранилище. Тем не менее, Subversion будет показывать то, что два файла имеют общую историю. До создания ветки-копии в правке 341 это был один файл. Поэтому и вы, и Салли видите изменения, сделанные в правках 303 и 98.

Ключевые идеи, стоящие за ветками

Из этого раздела вы должны запомнить две вещи.

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

  2. Subversion не имеет такого понятия как ветка — есть только копии. Копия каталога становится «веткой» только потому, что вы рассматриваете ее таким образом. Вы можете по-разному думать о каталоге, по разному его трактовать, но для Subversion это не более чем обычный каталог, созданный в результате копирования.



[24] Subversion не поддерживает возможность копирования между хранилищами. При использовании в командах svn copy или svn move URL-адресов можно копировать только элементы из одного и того же хранилища.