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 the section called “Manipulating Properties”). It can be set on any versioned directory, and its value is
a multi-line table of subdirectories (relative to the versioned
directory on which the property is set), optional revision
flags, and fully qualified, absolute Subversion repository
URLs.
$ 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
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 those nested
working copy checkouts, no one else has to
bother—Subversion will, upon checkout of the original
working copy, 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.
Note the previous externals definition example. When
someone checks out a working copy of the
calc
directory, 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 …
If you need to change the 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.
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 they 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.
The svn status command also recognizes
externals definitions, displaying a status code of
X
for the disjoint subdirectories into which
externals are checked out, and then recursing into those
subdirectories to display the status of the external items
themselves.
The support that exists for externals definitions in
Subversion is less than ideal, though. First, an
externals definition can only point to directories, not files.
Second, the externals definition cannot point to relative paths
(paths like ../../skins/myskin
). Third, 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 only truly
operates on non-disjoint 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.
Also, since the definitions themselves 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
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
) which 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 my-renamed-project Committed revision 12. $ svn update Fetching external item into 'renamed-project/some-dir/subdir' svn: Target path does not exist $
Also, the absolute URLs that externals definitions use
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
only allow commits to come in 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 their client doesn't support
https://
will be unable to fetch the external
items. Be aware, too, that if you need to re-parent your
working copy (using svn switch --relocate),
externals definitions will not also be
re-parented.
Finally, there might be times when you would prefer that
svn subcommands would not recognize, or
otherwise operate upon, the external working copies. In those instances,
you can pass the --ignore-externals
option to
the subcommand.