Sometimes it is useful to construct a working copy that is made out of a number of different checkouts. For example, you may want different subdirectories to come from different locations in a repository or perhaps from different repositories altogether. You could certainly set up such a scenario by hand—using svn checkout to create the sort of nested working copy structure you are trying to achieve. But if this layout is important for everyone who uses your repository, every other user will need to perform the same checkout operations that you did.
Fortunately, Subversion provides support for externals
definitions. An externals definition is a mapping of a local
directory to the URL—and ideally a particular revision—of a versioned directory. In
Subversion, you declare externals definitions in groups using the svn:externals
property. You can create or modify
this property using svn propset or
svn propedit (see Manipulating Properties). It can be set on any versioned
directory, and its value describes both the external repository location
and the client-side directory to which that location should be checked
out.
The convenience of the svn:externals
property is that once it is set on a versioned directory, everyone
who checks out a working copy with that directory also gets the benefit of
the externals definition. In other words, once one person has made the
effort to define the nested working copy structure, no one else has to
bother—Subversion will, after checking out the original working copy,
automatically also check out the external working copies.
The relative target subdirectories of externals definitions must not already exist on your or other users’ systems—Subversion will create them when it checks out the external working copy.
You also get in the externals definition design all the regular
benefits of Subversion properties. The definitions are versioned. If you
need to change an externals definition, you can do so using the regular
property modification subcommands. When you commit a change to the
svn:externals
property, Subversion will
synchronize the checked-out items against the changed externals definition
when you next run svn update. The same thing will
happen when others update their working copies and receive your changes to
the externals definition.
Because the svn:externals
property has a multiline value, we strongly recommend that you use
svn propedit instead of svn propset.
Subversion releases prior to 1.5 honor an externals definition format that is a multiline table of subdirectories (relative to the versioned directory on which the property is set), optional revision flags, and fully qualified, absolute Subversion repository URLs. An example of this might looks as follows:
$ svn propget svn:externals calc third-party/sounds http://svn.example.com/repos/sounds third-party/skins -r148 http://svn.example.com/skinproj third-party/skins/toolkit -r21 http://svn.example.com/skin-maker
When someone checks out a working copy of the calc directory referred to in the previous example, Subversion also continues to check out the items found in its externals definition:
$ 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 ...
As of Subversion 1.5, however, a new format of the svn:externals
property is supported. Externals
definitions are still multiline, but the order and format of the various
pieces of information have changed. The new syntax more closely mimics the
order of arguments you might pass to svn
checkout: the optional revision flags come first, then the
external Subversion repository URL, and finally the relative local
subdirectory. Notice, though, that this time we didn’t say “fully
qualified, absolute Subversion repository URLs.” That’s because the
new format supports relative URLs and URLs that carry peg revisions. The
previous example of an externals definition might, in Subversion 1.5, look
like the following:
$ svn propget svn:externals calc http://svn.example.com/repos/sounds third-party/sounds -r148 http://svn.example.com/skinproj third-party/skins -r21 http://svn.example.com/skin-maker third-party/skins/toolkit
Or, making use of the peg revision syntax (which we describe in detail in Peg and Operative Revisions), it might appear as:
$ svn propget svn:externals calc http://svn.example.com/repos/sounds third-party/sounds http://svn.example.com/skinproj@148 third-party/skins http://svn.example.com/skin-maker@21 third-party/skins/toolkit
You should seriously consider using explicit revision numbers in all of your externals definitions. Doing so means that you get to decide when to pull down a different snapshot of external information, and exactly which snapshot to pull. Besides avoiding the surprise of getting changes to third-party repositories that you might not have any control over, using explicit revision numbers also means that as you backdate your working copy to a previous revision, your externals definitions will also revert to the way they looked in that previous revision, which in turn means that the external working copies will be updated to match the way they looked back when your repository was at that previous revision. For software projects, this could be the difference between a successful and a failed build of an older snapshot of your complex codebase.
For most repositories, these three ways of formatting the externals definitions have the same ultimate effect. They all bring the same benefits. Unfortunately, they all bring the same annoyances, too. Since the definitions shown use absolute URLs, moving or copying a directory to which they are attached will not affect what gets checked out as an external (though the relative local target subdirectory will, of course, move with the renamed directory). This can be confusing—even frustrating—in certain situations. For example, say you have a top-level directory named my-project, and you’ve created an externals definition on one of its subdirectories (my-project/some-dir) that tracks the latest revision of another of its subdirectories (my-project/external-dir):
$ svn checkout 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 propget svn:externals my-project/some-dir subdir http://svn.example.com/projects/my-project/external-dir $
Now you use svn move to rename the my-project directory. At this point, your externals definition will still refer to a path under the my-project directory, even though that directory no longer exists:
$ svn move -q my-project renamed-project $ svn commit -m "Rename my-project to renamed-project." Deleting my-project Adding renamed-project Committed revision 12. $ svn update Fetching external item into 'renamed-project/some-dir/subdir' svn: Target path does not exist $
Also, absolute URLs can cause problems with repositories that are
available via multiple URL schemes. For example, if your Subversion server
is configured to allow everyone to check out the repository over http://
or https://
, but to allow commits to come in only
via https://
, you have an interesting
problem on your hands. If your externals definitions use the http://
form of the repository URLs, you won’t
be able to commit anything from the working copies created by those
externals. On the other hand, if they use the https://
form of the URLs, anyone who might be
checking out via http://
because his
client doesn’t support https://
will be
unable to fetch the external items. Be aware, too, that if you need to
reparent your working copy (using svn switch
with the --relocate
option), externals definitions will
not also be reparented.
Subversion 1.5 takes a huge step forward in relieving these frustrations. As mentioned earlier, the URLs used in the new externals definition format can be relative, and Subversion provides syntax magic for specifying multiple flavors of URL relativity:
../
Relative to the URL of the directory on which the svn:externals
property is set
^/
Relative to the root of the repository in which the svn:externals
property is versioned
//
Relative to the scheme of the URL of the directory on which
the svn:externals
property is
set
/
Relative to the root URL of the server on which the svn:externals
property is versioned
So, looking a fourth time at our previous externals definition example, and making use of the new absolute URL syntax in various ways, we might now see:
$ svn propget svn:externals calc ^/sounds third-party/sounds /skinproj@148 third-party/skins //svn.example.com/skin-maker@21 third-party/skins/toolkit
However, the support that exists for externals definitions in
Subversion remains less than ideal. An externals definition can point only
to directories, not to files. Also, the local subdirectory part of the
definition cannot contain ..
parent
directory indicators (such as ../../skins/myskin). Perhaps most
disappointingly, the working copies created via the externals definition
support are still disconnected from the primary working copy (on whose
versioned directories the svn:externals
property was actually set). And Subversion still truly operates only on
nondisjoint working copies. So, for example, if you want to commit changes
that you’ve made in one or more of those external working copies, you must
run svn commit explicitly on those
working copies—committing on the primary working copy will not recurse
into any external ones.
We’ve already mentioned some of the additional shortcomings of the
old svn:externals
format and how the new Subversion 1.5 format improves upon it. But be
careful when making use of the new format that you don’t inadvertently
cause problems for other folks accessing your repository who are using
older Subversion clients. While Subversion 1.5 clients will continue to
recognize and support the original externals definition format, older
clients will not be able to correctly parse the new
format.
Besides the svn checkout,
svn update, svn switch, and svn
export commands which actually manage the
disjoint (or disconnected) subdirectories into
which externals are checked out, the svn
status command also recognizes externals definitions. It
displays a status code of X
for the
disjoint external subdirectories, and then recurses into those
subdirectories to display the status of the external items themselves. You
can pass the --ignore-externals
option to any of
these subcommands to disable externals definition processing.