Внешние зависимости

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

Чтобы этого избежать у Subversion есть поддержка внешних зависимостей. Внешняя зависимость является сопоставлением локального каталога к URL другого версионированного каталога (или к его конкретной правке). Групповое объявление внешних зависимостей делается в Subversion при помощи свойства svn:externals. Установка и редактирование этого свойства выполняется с помощью команд svn propset и svn propedit (см. «Использование свойств»). Свойство может быть установлено для любого версионированного каталога, значение свойства представляет собой таблицу с путями к подкаталогам (относительно того каталога, для которого это свойство устанавливается) и полными абсолютными URL в Subversion-хранилище.

$ svn propget svn:externals calc
third-party/sounds             http://sounds.red-bean.com/repos
third-party/skins              http://skins.red-bean.com/repositories/skinproj
third-party/skins/toolkit -r21 http://svn.red-bean.com/repos/skin-maker

Удобство свойства svn:external заключается в том, что после его установки для версионированного каталога, всё кто будет создавать рабочую копию с этим каталогом получат возможность пользоваться преимуществами внешней зависимости. Другими словами, после того как кто-то из участников проекта обозначил необходимую структуру рабочей копии, больше никому об этом беспокоиться не придется — при создании рабочей копии, Subversion, кроме оригинальных данных, сделает копии данных, определенных как внешние зависимости.

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

$ svn checkout http://svn.example.com/repos/calc
A  calc
A  calc/Makefile
A  calc/integer.c
A  calc/button.c
Checked out revision 148.

Fetching external item into calc/third-party/sounds
A  calc/third-party/sounds/ding.ogg
A  calc/third-party/sounds/dong.ogg
A  calc/third-party/sounds/clang.ogg
…
A  calc/third-party/sounds/bang.ogg
A  calc/third-party/sounds/twang.ogg
Checked out revision 14.

Fetching external item into calc/third-party/skins
…

Если необходимо изменить внешние зависимости, сделать это можно с помощью обычных команд редактирования свойств. При фиксации изменений свойства svn:externals, при следующем запуске svn update Subversion синхронизирует существующие копии элементов, в соответствии с внесенными во внешние зависимости изменениями. Тоже самое произойдет и когда другие участники проекта обновят свои рабочие копии и получат изменения во внешних зависимостях.

Подсказка

Учитывая, что свойство svn:externals имеет многострочное значение, крайне рекомендуется вместо команды svn propset использовать svn propedit.

Подсказка

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

Команда svn status умеет определять внешние зависимости, показывая код статуса X для подкатологов, в которых находятся внешние зависимости и рекурсивно проходит по этим подкаталогам, показывая статус самих внешних элементов.

Существующая сегодня в Subversion поддержка внешних зависимостей может вводить в заблуждение. Во первых, внешние зависимости могут указывать только на папки, но не на файлы. Во вторых, внешние зависимости не могут указывать на относительные пути (например, подобные ../../skins/myskin). В третьих, рабочие копии созданные через внешние зависимости являются оторванными от первичной рабочей копии (от того каталога, для которого установлено свойство svn:externals). А Subversion полноценно работает только на не отсоединенных рабочих копиях. Это означает, что если вы захотите зафиксировать изменения, сделанные в одной или нескольких таких рабочих копиях, вам придется принудительно выполнять команду svn commit для этих рабочих копий — фиксирование первичной рабочей копии не распространяется на внешние зависимости.

Кроме того, так как зависимости используют абсолютные URL, перемещение или копирование папки к которой они присоединены не будет влиять на то, что будет вытаскиваться из хранилища в виде внешней зависимости (при этом, локальные подкаталоги, назначенные как целевые для внешних зависимостей, при переименовании родительского каталога будут, естественно, перемещены вместе с ним). В определенных ситуациях это может сбивать с толку и запутывать. Например, у вас есть корневой каталог my-project и для одного из его подкаталогов (my-project/some-dir) вы назначаете внешнюю зависимость, отслеживающую изменения другого подкаталога (my-project/external-dir).

$ svn co http://svn.example.com/projects .
A    my-project
A    my-project/some-dir
A    my-project/external-dir
…
Fetching external item into 'my-project/some-dir/subdir'
Checked out external at revision 11.

Checked out revision 11.
$ svn pget svn:externals my-project/some-dir
subdir http://svn.example.com/projects/my-project/external-dir

$

Переименуем с помощью команды svn move каталог my-project. Теперь внешние зависимости продолжают продолжают указывать на путь в каталоге my-project, а самого этого каталога уже не существует.

$ svn mv -q my-project renamed-project
$ svn ci -m "Rename my-project to renamed-project."
Deleting       my-project
Adding         my-renamed-project

Committed revision 12.
$ svn up

Fetching external item into 'renamed-project/some-dir/subdir'
svn: Target path does not exist
$

Тот факт, что внешние зависимости используют абсолютные URL может вызвать проблемы при работе с хранилищами, доступными через несколько URL-схем. Интересная проблема может возникнуть если, например, Subversion сервер позволяет любому пользователю создать рабочую копию подключившись через http:// или https://, а фиксации позволяет выполнять только через https://. Если внешние зависимости используют http:// вариант URL хранилища, то для рабочих копий, созданных для этих внешних зависимостей, нельзя будет выполнить фиксацию изменений. С другой стороны, если использовался https:// вариант URL, то пользователи которые создают рабочую копию через http:// по тому, что их клиент не поддерживает https://, не смогут получить внешние элементы. Обратите внимание и на то, что при переопределении рабочей копии (с помощью команды svn --relocate), внешние зависимости не будут переопределены.

Наконец, могут буть ситуации в которых предпочтительно, чтобы подкоманды svn не идентифицировали и не оперировали над рабочими копиями, созданными как внешние зависимости. Для таких случаев, при вызове подкоманды можно использовать параметр --ignore-externals.