====================================================================
NEW SECTION FOR CHAPTER 1
====================================================================
A Quick StartSome people have trouble absorbing a new technology by
reading the sort of "top down" approach provided by this book.
This section is a very short introduction to Subversion, and is
designed to give "bottom up" learners a fighting chance. If
you're one of those folks who prefers to learn by
experimentation, the following demonstration will get you up and
running. Along the way, we give links to the relevant chapters
of this book.If you're new to the entire concept of version control or to
the copy-modify-merge model used by both CVS and
Subversion, then you should read
before going any further.The following example assumes that you have
svn, the Subversion commandline client, and
svnadmin, the administrative tool, ready to
go. It also assumes that your svn client has
been compiled against Berkeley DB. To verify this, run
svn --version and make sure the
ra_local module is available. Without this
module, the client cannot access file://
URLs.Subversion stores all versioned data in a central
repository. To begin, create a new repository:
$ svnadmin create /path/to/repos
$ ls /path/to/repos
conf/ dav/ db/ format hooks/ locks/ README.txt
This command creates a new directory
/path/to/repos which contains a Subversion
repository. Make sure that this directory lives on a local disk,
not a network share. This new directory
mainly contains a collection of Berkeley DB database files. You
won't see your versioned files if you peek inside. For more
information about repository creation and maintenance, see .Next, create a tree of files and directories to import into
the repository. For reasons that will be clear later on (see
), your structure should contain three
top-level directories named branches,
tags, and trunk:
/tmp/project/branches/
/tmp/project/tags/
/tmp/project/trunk/
foo.c
bar.c
Makefile
…
Once you have a tree of data ready to go, import the data
into the repository with the svn import
command (see ):
$ svn import /tmp/project file:///path/to/repos -m "initial import"
Adding /tmp/project/branches
Adding /tmp/project/tags
Adding /tmp/project/trunk
Adding /tmp/project/trunk/foo.c
Adding /tmp/project/trunk/bar.c
Adding /tmp/project/trunk/Makefile
…
Committed revision 1.
$
Now the repository contains your tree of data. At this
point, you create a working copy of the
trunk directory. This is where your
actual work will happen:
$ svn checkout file://path/to/repos/trunk project
A project/foo.c
A project/bar.c
A project/Makefile
…
Checked out revision 1.
Now you have a personal copy of part of the repository in a
new directory named project. You can edit
the files in your working copy and then commit those changes
back into the repository.Enter your working copy and edit a
file's contents.Run svn diff to see unified
diff output of your changes.Run svn commit to commit the
new version of your file to the repository.Run svn update to bring your
working copy up-to-date with the repository.
For a full tour of all the things you can do with your
working copy, read .At this point, you have the option of making your repository
available to others over a network. See to learn about the different sorts of
server processes available and how to configure them.
====================================================================
NEW SECTION FOR CHAPTER 4
====================================================================
Noticing or Ignoring AncestryWhen conversing with a Subversion developer, you might
very likely hear reference to the term
ancestry. This word is used to
describe the relationship between two objects in a
repository: if they're related to each other, than one
object is said to be an ancestor of the other.For example, suppose you commit revision 100, which
includes a change to a file foo.c.
Then foo.c@99 is an
ancestor of foo.c@100.
On the other hand, suppose you commit the deletion of
foo.c in revision 101, and then add a
new file by the same name in revision 102. In this case,
foo.c@99 and
foo.c@102 may appear to be related
(they have the same path), but in fact are completely
different objects in the repository. They share no history
or ancestry.The reason for bringing this up is to point out an
important difference between svn diff and
svn merge. The former command ignores
ancestry, while the latter command is quite sensitive to it.
For example, if you asked svn diff to
compare revisions 99 and 102 of foo.c,
you would see line-based diffs; the diff command is blindly
comparing two paths. But if you asked svn
merge to compare the same two objects, it would
notice that they're unrelated and first attempt to delete
the old file, then add the new file; you would see a
D foo.c followed by a A
foo.c.Most merges involve comparing trees that are ancestrally
related to one another, and therefore svn
merge defaults to this behavior. Occasionally,
however, you may want the merge command to compare two
unrelated trees. For example, you may have imported two
source-code trees representing different vendor releases of
a software project (see ).
If you asked svn merge to compare the two
trees, you'd see the entire first tree being deleted,
followed by an add of the entire second tree!In these situations, you'll want svn
merge to do a path-based comparison only, ignoring
any relations between files and directories. Add the
option to your merge
command, and it will behave just like svn
diff. (And conversely, the
option will cause
svn diff to behave like the merge
command.)
====================================================================
NEW SECTION FOR CHAPTER 7
====================================================================
Automatic Property SettingProperties are a powerful feature of Subversion, acting as
key components of many Subversion features discussed elsewhere
in this and other chapters—textual diff and merge
support, keyword substitution, newline translation, etc. But
to get the full benefit of properties, they must be set on the
right files and directories. Unfortunately, that can be a
step easily forgotten in the routine of things, especially
since failing to set a property doesn't usually result in an
obvious error condition (at least compared to, say, failing to
add a file to version control). To help your properties get
applied to the places that need them, Subversion provides a
couple of simple but useful features.Whenever you introduce a file to version control using the
svn add or svn import
commands, Subversion runs a very basic heuristic to determine
if that file consists of human-readable or non-human-readable
content. If the latter is the decision made, Subversion will
automatically set the svn:mime-type
property on that file to
application/octet-stream (the generic
this is a collection of bytes MIME type). Of
course, if Subversion guesses incorrectly, or if you wish to
set the svn:mime-type property to something
more precise—perhaps image/png or
application/x-shockwave-flash—you can
always remove or edit that property.Subversion also provides the auto-props feature, which
allows you to create mappings of filename patterns to property
names and values. These mappings are made in your runtime
configuration area. They again affect adds and imports, and
not only can override any default MIME type decision made by
Subversion during those operations, they can also set
additional Subversion or custom properties, too. For example,
you might create a mapping that says that any time you add
JPEG files—ones that match the pattern
*.jpg—Subversion should automatically
set the svn:mime-type property on those
files to image/jpeg. Or perhaps any files
that match *.cpp should have
svn:eol-style set to
native, and svn:keywords
set to Id. Auto-prop support is perhaps
the handiest property related tool in the Subversion toolbox.
See for more about
configuring that support.
====================================================================
REPLACEMENT SECTION FOR CHAPTER 7
====================================================================
General Vendor Branch Management ProcedureManaging vendor branches generally works like this. You
create a top-level directory (such as
/vendor) to hold the vendor branches.
Then you import the third party code into a subdirectory of
that top-level directory. You then copy that subdirectory
into your main development branch (for example,
/trunk) at the appropriate location. You
always make your local changes in the main development branch.
With each new release of the code you are tracking you bring
it into the vendor branch and merge the changes into
/trunk, resolving whatever conflicts
occur between your local changes and the upstream
changes.Perhaps an example will help to clarify this algorithm.
We'll use a scenario where your development team is creating a
calculator program that links against a third-party complex
number arithmetic library, libcomplex. We'll begin with the
initial creation of the vendor branch, and the import of the
first vendor drop. We'll call our vendor branch directory
libcomplex, and our code drops will go
into a subdirectory of our vendor branch called
current. And since svn
import creates all the intermediate parent
directories it needs, we can actually accomplish both of these
steps with a single command.
$ svn import /path/to/libcomplex-1.0 \
http://svn.example.com/repos/vendor/libcomplex/current \
-m 'importing initial 1.0 vendor drop'
…
We now have the current version of the libcomplex source
code in /vendor/libcomplex/current. Now,
we tag that version (see )
and then copy it into the main development branch. Our copy
will create a new directory called
libcomplex in our existing
calc project directory. It is in this
copied version of the vendor data that we will make our
customizations.
$ svn copy http://svn.example.com/repos/vendor/libcomplex/current \
http://svn.example.com/repos/vendor/libcomplex/1.0 \
-m 'tagging libcomplex-1.0'
…
$ svn copy http://svn.example.com/repos/vendor/libcomplex/1.0 \
http://svn.example.com/repos/calc/libcomplex \
-m 'bringing libcomplex-1.0 into the main branch'
…
We check out our project's main branch—which now
includes a copy of the first vendor drop—and we get to
work customizing the libcomplex code. Before we know it, our
modified version of libcomplex is now completely integrated
into our calculator program.
And entirely bug-free, of course!A few weeks later, the developers of libcomplex release a
new version of their library—version 1.1—which
contains some features and functionality that we really want.
We'd like to upgrade to this new version, but without losing
the customizations we made to the existing version. What we
essentially would like to do is to replace our current
baseline version of libcomplex 1.0 with a copy of libcomplex
1.1, and then re-apply the custom modifications we previously
made to that library to the new version. But we actually
approach the problem from the other direction, applying the
changes made to libcomplex between versions 1.0 and 1.1 to our
modified copy of it.To perform this upgrade, we checkout a copy of our vendor
branch, and replace the code in the
current directory with the new libcomplex
1.1 source code. We quite literally copy new files on top of
existing files, perhaps exploding the libcomplex 1.1 release
tarball atop our existing files and directories. The goal
here is to make our current directory
contain only the libcomplex 1.1 code, and to ensure that all
that code is under version control. Oh, and we want to do
this with as little version control history disturbance as
possible.After replacing the 1.0 code with 1.1 code, svn
status will show files with local modifications as
well as, perhaps, some unversioned or missing files. If we
did what we were supposed to do, the unversioned files are
only those new files introduced in the 1.1 release of
libcomplex—we run svn add on those to
get them under version control. The missing files are files
that were in 1.0 but not in 1.1, and on those paths we run
svn remove. Finally, once our
current working copy contains only the
libcomplex 1.1 code, we commit the changes we made to get it
looking that way.Our current branch now contains the
new vendor drop. We tag the new version (in the same way we
previously tagged the version 1.0 vendor drop), and then merge
the differences between the tag of the previous version and
the new current version into our main development
branch.
$ cd working-copies/calc
$ svn merge http://svn.example.com/repos/vendor/libcomplex/1.0 \
http://svn.example.com/repos/vendor/libcomplex/current \
libcomplex
… # resolve all the conflicts between their changes and our changes
$ svn commit -m 'merging libcomplex-1.1 into the main branch'
…
In the trivial use-case, the new version of our
third-party tool would look, from a files-and-directories
point of view, just like the previous version. None of the
libcomplex source files would have been deleted, renamed or
moved to different locations—the new version would
contain only textual modifications against the previous one.
In a perfect world, our modifications would apply cleanly to
the new version of the library, with absolutely no
complications or conflicts.But things aren't always that simple, and in fact it is
quite common for source files to get moved around between
releases of software. This complicates the process of ensuring
that our modifications are still valid for the new version of
code, and can quickly degrade into a situation where we have to
manually recreate our customizations in the new version. Once
Subversion knows about the history of a given source
file—including all its previous locations—the
process of merging in the new version of the library is pretty
simple. But we are responsible for telling Subversion how the
source file layout changed from vendor drop to vendor
drop.
====================================================================
NEW SECTION FOR CHAPTER 9
====================================================================
svnservesvnserve allows access to Subversion
repositories using the svn network protocol.
You can run svnserve either as a standalone server process, or
you can have another process, such as inetd,
xinetd or sshd, launch it
for you.Once the client has selected a repository by transmitting
its URL, svnserve reads a file named
conf/svnserve.conf in the repository
directory to determine repository-specific settings such as what
authentication database to use and what authorization policies
to apply. See for details of
the svnserve.conf file.svnserve SwitchesUnlike the previous commands we've
described. svnserve has no
subcommands—svnserve is controlled
exclusively by switches. ()Causes svnserve to run in daemon
mode. svnserve backgrounds itself
and accepts and serves TCP/IP connections on the svn
port (3690, by default).=PORTCauses svnserve to listen on
PORT when run in daemon
mode.=HOSTCauses svnserve to listen on the
interface specified by HOST,
which may be either a hostname or an IP address.When used together with , this
switch causes svnserve to stay in the
foreground. This switch is mainly useful for
debugging. ()Causes svnserve to use the
stdin/stdout file descriptors, as is appropriate for a
daemon running out of inetd. ()Displays a usage summary and exits.=ROOT
(=ROOT)Sets the virtual root for repositories served by
svnserve. The pathname in URLs
provided by the client will be interpreted relative to
this root, and will not be allowed to escape this
root. ()Causes svnserve to run in tunnel
mode, which is just like the inetd
mode of operation (serve one connection over
stdin/stdout) except that the connection is considered
to be pre-authenticated with the username of the current
uid. This flag is selected by the client when running
over a tunnel agent such as
ssh. ()When running in daemon mode, causes
svnserve to spawn a thread instead of
a process for each connection. The
svnserve process still backgrounds
itself at startup time. ()Causes svnserve to accept one
connection on the svn port, serve it, and exit. This
option is mainly useful for debugging.