As we described in the section called “Subversion Working Copies”, the Subversion working copy is a sort of staging area where a user can privately make changes to his or her versioned data and then—when those changes are complete and ready for sharing with others—commit them to the repository. It should come as no surprise, then, that most of the interaction you will have with Subversion will be in the form of asking your Subversion client to do something to one or more items in a local working copy. Even for those operations which don't manipulate the working copy data itself (such as svn log), it's often just easier to use a working copy file or directory as a convenient target for that operation.
Clearly, the typical approach to making changes to your versioned data is via commits from a Subversion working copy. Fortunately, it's not the only way. Users of Subversion who need to make relatively simple changes to their versioned data can do so without the overhead of checking out a working copy. We'll cover some of those supported operations in this section.
The Subversion command-line client supports a number of operations which can be performed directly against repository URLs in order to make simple changes without a working copy. Some of these are described elsewhere in this book, but we provide an exhaustive list of them here for your convenience.
Perhaps the most obvious remote commit-like operation is the svn import command. We describe that command in the section called “Importing Files and Directories” as part of explaining how you can easily get a whole tree of unversioned information into your Subversion repository so you can start doing version-controlled operations on it.
The svn mkdir and svn delete commands, when used with URL targets, are also remote commit-type operations. These allow the user to create one or more new versioned directories or remove (recursively) one or more versioned files or directories, respectively, without the use of a working copy. Each time you issue one of these commands, the client communicates with the server in a way that's similar to how it would describe the commit of a directory added or of an item removed from the working copy. If there's no problem or conflict detected with the requested operation, the server commits the additions or removals in a single new revision.
You can use svn copy or svn move with two URLs—a copy/move source and a destination—to commit a copies and moves of files and directories directly in the repository. These operations tend to be some of the most expensive ones when performed within a working copy, but they complete in constant time when performed remotely using repository URLs. In fact, the svn copy remote operation is commonly used to create branches in Subversion, as we discuss later in the section called “Creating a Branch”.
As with the regular svn commit command,
you can supply a log message with any of these commands we've
discussed so far to describe the changes you're making. Use
the --file (-F)
or --message
(-m)
option, or otherwise allow the client to prompt
you for the log message.
Finally, there are a number of operations related to unversioned revision properties which can be performed directly against the repository. In fact, revision properties are somewhat unique in this context, as they aren't stored in the working copy and therefore must be modified without working copy interaction. See the section called “Properties” for a more detailed description of how to manage properties in Subversion.
One shortcoming of the remote commit operation support
offered in the command-line client is that you are essentially
limited to one operation—or, really, one type of
operation—per commit. For example, it's perfectly
natural and supported to, say, use svn
delete followed by svn mkdir
within a working copy to replace an existing versioned
directory with a brand new one. When you commit the results
of those operations, a single new revision is created in the
repository, and that revision carries the full replacement of
your directory. You can't really do the same thing as remote
operations using the command-line client while still
preserving the it-happened-in-a-single-revision-ness of the
change—svn
delete URL
would create a
new revision that removed the directory; svn
mkdir URL
would generate
a second revision for the directory's re-creation.
Fortunately, Subversion provides a separate tool which exists solely to allow users to string together a set of remote operations and commit them as one atomic change. That tool is the svnmucc tool—the Subversion Multiple URL Command Client:
$ svnmucc --help Subversion multiple URL command client usage: svnmucc ACTION... Perform one or more Subversion repository URL-based ACTIONs, committing the result as a (single) new revision. Actions: cp REV URL1 URL2 : copy URL1@REV to URL2 mkdir URL : create new directory URL mv URL1 URL2 : move URL1 to URL2 rm URL : delete URL put SRC-FILE URL : add or modify file URL with contents copied from SRC-FILE (use "-" to read from standard input) propset NAME VAL URL : set property NAME on URL to value VAL propsetf NAME VAL URL : set property NAME on URL to value from file VAL propdel NAME URL : delete property NAME from URL …
svnmucc has been a part of the Subversion project's source code tree for many years (as mucc for most of that time), but it was only in Subversion 1.8 that it become a fully supported member of the Subversion command-line tool suite.
The svnmucc tool can perform any
transformation on your versioned data that svn
itself can. But unlike svn, the functionality
that svnmucc offers isn't broken up into
subcommands. Rather, you provide a list of actions and
operands in a single command line (or from a file stream, via
the --extra-args (-X)
option). Some of the
actions supported by svnmucc mimic those of
the command-line client. You'll notice in the previous
command output actions such as cp
,
mkdir
, mv
,
and rm
, all of which are very similar to
the commands we mentioned in
the section called “Remote command-line client operations”. But
remember, the key difference here is that you can use any
number of these actions together in a single command
invocation, resulting in a single committed revision in the
repository.
Let's take our previous example of trying to simply replace a remote directory. Using svnmucc, you would accomplish this as follows:
$ svnmucc rm http://svn.example.com/projects/sandbox \ mkdir http://svn.example.com/projects/sandbox \ -m "Replace my old sandbox with a fresh new one." r22 committed by harry at 2013-01-15T21:45:26.442865Z $
As you can see, svnmucc accomplished in a single revision what svn—without the benefit of a working copy—required two revisions to complete.
Another difference between svnmucc and svn is that the former currently will not prompt you for a commit log message if you fail to supply one via the command line. Rather, it will use a stock (that is, relatively valueless) log message.
The svnmucc tool is not limited to merely remixing actions that svn itself can perform. It introduces some additional functionality not found in the command-line client. For example, you can use the put action to add or modify a file in the repository, copying the file's intended new contents from either a file on your local machine or from data piped in via standard input. The tool also offers propset, propsetf, and propdel actions, useful for setting properties on versioned files and directories (explicitly, or by copying the property's value from a local file) and for deleting properties on the same. Those actions are unsupported in the command-line client at this time.
At this point, though, it seems prudent to discuss the difference between what can be done with svnmucc and what should be done. A pair of notable quotes comes to mind:
“To whom much has been given, much will be expected.” |
||
--Jesus |
“With great power comes great responsibility.” |
||
--"Spiderman" Peter Parker's Uncle Ben |
Inherent in modifications without a working copy is the loss of the very conflict detection safeguards which make the use of a working copy so valuable. When using svn in the typical way, changes are committed to the server against a specific base version of a file or directory so that you don't inadvertently overwrite contemporary changes made to the same item by another team member. The server knows what version of the file you had before you changed it, and it knows if other folks have changed that same file since that revision was created. That's all the information the server needs to deny your commit when it would clobber someone else's change, forcing you to integrate their change into your working copy and reconsider your own change. Because there is no working copy in the mix here, svnmucc really gives you the power to bypass those safeguards and to act as if the current state of the repository is precisely the base state against which you are working. But hopefully it is obvious to you that this is not a power you should cavalierly wield.
Fortunately, svnmucc allows you to be
more conservative in the way you use the tool. In order to
provide a safety mechanism similar to what is offered by the
use of a working copy, svnmucc offers
a --revision (-r)
option. With this option,
you can manually specify a base revision for the changes you
are attempting to commit. The base revision you choose
is ideally the most recent revision in your repository of
which you can reasonably claim knowledge.
Users are strongly encouraged to use, and to use
correctly, the --revision (-r)
option
to svnmucc.
Proper use of the svnmucc put action
best demonstrates how this --revision (-r)
option should be used. Say Harry wishes to change the
contents of a versioned README
file
without bothering with a full checkout of a working copy.
(We'll assume that there is no other value in using a working
copy for this operation, such as the presence of scripts Harry
should run in advance of his commit to verify that it's a
reasonable one.) The first decision he has to make is which
revision of the file he wants to work with. Typically, users
wish to modify the most recent version of a file. So Harry
queries the revision in which the file was last modified, and
then uses that revision to fetch the contents of the file into
a temporary local file:
$ svn info http://svn.example.com/projects/sandbox/README Path: README URL: http://svn.example.com/projects/sandbox/README Relative URL: ^/sandbox/README Repository Root: http://svn.example.com/projects Repository UUID: 13f79535-47bb-0310-9956-ffa450edef68 Revision: 22 Node Kind: file Last Changed Author: sally Last Changed Rev: 14 Last Changed Date: 2012-09-02 10:34:09 -0400 (Sun, 02 Sep 2012) $ svn cat -r 14 http://svn.example.com/projects/sandbox/README \ > README.tmpfile $
Harry now has a copy of the README
file as it looked when it it was last modified. He makes the
edits he wishes to make to this copy of the file. Naturally,
when he's finished, he wishes to then commit those changes to
the repository.
Now, if Harry naively uses svnmucc put
…
at this point to replace the contents of
README
in the repository with his locally
modified contents, he has just abused the power
that svnmucc affords. What if, just
microseconds prior to his commit, Sally had also modified
the README
file? As with the
svn program, svnmucc
won't attempt some sort of server-side content merge in order
to preserve both users' changes. Rather,
svnmucc will happily replace the current
latest version of the file with the contents specified. Harry
will be oblivious. Sally will be livid.
$ svnmucc put README.tmpfile \ http://svn.example.com/projects/sandbox/README \ -m "Tweak the README file." r24 committed by harry at 2013-01-21T16:21:23.100133Z $ Message from sally@shell.example.com on pts/2 at 16:26 ... We need to talk. Now. EOF
Harry should instead recall the revision he originally
used as the revision on which to base his changes, supplying
that revision to svnmucc via
the --revision (-r)
option, and thus giving
the server the opportunity to bounce his commit if, by his own
(perhaps ignorant) admission, he's attempting to modify an
out-of-date item:
$ svnmucc -r 14 put README.tmpfile \ http://svn.example.com/projects/sandbox/README \ -m "Tweak the README file." svnmucc: E170004: Item '/sandbox/README' is out of date $
Like other svnmucc options,
the --revision (-r)
option operates at a
scope global to the whole command—every action specified
in that command. This enables you to have the same sort of
safeguards you would have if you had checked out a working
copy of your entire repository (and thus had a working copy
entirely at a single uniform revision), made changes to that
working copy, and then committed all those changes at
once.
As you can see, svnmucc is a handy addition to the Subversion user's tool chest. For a complete reference of this tool's offerings, see svnmucc Reference—Subversion Multiple URL Command Client.