We copy, move, rename, and completely replace files and directories on our computers all the time. And your version control system shouldn't get in the way of your doing these things with your version-controlled files and directories, either. Subversion's file management support is quite liberating, affording almost as much flexibility for versioned files as you'd expect when manipulating your unversioned ones. But that flexibility means that across the lifetime of your repository, a given versioned object might have many paths, and a given path might represent several entirely different versioned objects. This introduces a certain level of complexity to your interactions with those paths and objects.
Subversion is pretty smart about noticing when an object's version history includes such “changes of address.” For example, if you ask for the revision history log of a particular file that was renamed last week, Subversion happily provides all those logs—the revision in which the rename itself happened, plus the logs of relevant revisions both before and after that rename. So, most of the time, you don't even have to think about such things. But occasionally, Subversion needs your help to clear up ambiguities.
The simplest example of this occurs when a directory or file is deleted from
version control, and then a new directory or file is created with the same
name and added to version control. The thing you deleted and the thing you
later added aren't the same thing. They merely happen to have had the same
path—/trunk/object
, for example. What, then,
does it mean to ask Subversion about the history of
/trunk/object
? Are you asking about the thing currently
at that location, or the old thing you deleted from that location? Are you
asking about the operations that have happened to all
the objects that have ever lived at that path? Subversion needs a hint about
what you really want.
And thanks to moves, versioned object history can get far more twisted than
even that. For example, you might have a directory named
concept
, containing some nascent software project
you've been toying with. Eventually, though, that project matures to the
point that the idea seems to actually have some wings, so you do the
unthinkable and decide to give the project a name.[10] Let's say you called your software
Frabnaggilywort. At this point, it makes sense to rename the directory to
reflect the project's new name, so concept
is renamed
to frabnaggilywort
. Life goes on, Frabnaggilywort
releases a 1.0 version and is downloaded and used daily by hordes of people
aiming to improve their lives.
It's a nice story, really, but it doesn't end there. Entrepreneur that you
are, you've already got another think in the tank. So you make a new
directory, concept
, and the cycle begins again. In
fact, the cycle begins again many times over the years, each time starting
with that old concept
directory, then sometimes seeing
that directory renamed as the idea cures, sometimes seeing it deleted when
you scrap the idea. Or, to get really sick, maybe you rename
concept
to something else for a while, but later rename
the thing back to concept
for some reason.
In scenarios like these, attempting to instruct Subversion to work with these reused paths can be a little like instructing a motorist in Chicago's West Suburbs to drive east down Roosevelt Road and turn left onto Main Street. In a mere 20 minutes, you can cross “Main Street” in Wheaton, Glen Ellyn, and Lombard. And no, they aren't the same street. Our motorist—and our Subversion—need a little more detail to do the right thing.
Fortunately, Subversion allow you to tell it exactly which Main Street you
meant. The mechanism used is called a peg revision,
and you provide these to Subversion for the sole purpose of identifying
unique lines of history. Because at most one versioned object may occupy a
path at any given time—or, more precisely, in any one
revision—the combination of a path and a peg revision is all that is
needed to unambiguously identify a specific line of history. Peg revisions
are specified to the Subversion command-line client using at
syntax, so called because the syntax involves appending an
“at sign” (@
) and the peg revision to the
end of the path with which the revision is associated.
But what of the --revision
(-r
) of which
we've spoken so much in this book? That revision (or set of revisions) is
called the operative revision (or
operative revision range). Once a particular line of
history has been identified using a path and peg revision, Subversion
performs the requested operation using the operative revision(s). To map
this to our Chicagoland streets analogy, if we are told to go to 606 N. Main
Street in Wheaton,[11] we can think of
“Main Street” as our path and “Wheaton” as our peg
revision. These two pieces of information identify a unique path that can
be traveled (north or south on Main Street), and they keep us from traveling
up and down the wrong Main Street in search of our destination. Now we
throw in “606 N.” as our operative revision of sorts, and we
know exactly where to go.
Say that long ago we created our repository, and in revision 1 we added our
first concept
directory, plus an
IDEA
file in that directory talking about the concept.
After several revisions in which real code was added and tweaked, we, in
revision 20, renamed this directory to
frabnaggilywort
. By revision 27, we had a new concept,
a new concept
directory to hold it, and a new
IDEA
file to describe it. And then five years and
thousands of revisions flew by, just like they would in any good romance
story.
Now, years later, we wonder what the IDEA
file looked
like back in revision 1. But Subversion needs to know whether we are asking
about how the current file looked back in revision 1,
or whether we are asking for the contents of whatever file lived at
concepts/IDEA
in revision 1. Certainly those questions
have different answers, and because of peg revisions, you can ask those
questions. To find out how the current IDEA
file
looked in that old revision, you run:
$ svn cat -r 1 concept/IDEA svn: Unable to find repository location for 'concept/IDEA' in revision 1
Of course, in this example, the current IDEA
file
didn't exist yet in revision 1, so Subversion gives an error. The previous
command is shorthand for a longer notation which explicitly lists a peg
revision. The expanded notation is:
$ svn cat -r 1 concept/IDEA@BASE svn: Unable to find repository location for 'concept/IDEA' in revision 1
当执行时,它包含期望的结果。
The perceptive reader is probably wondering at this point whether the peg
revision syntax causes problems for working copy paths or URLs that actually
have at signs in them. After all, how does svn know
whether news@11
is the name of a directory in my tree or
just a syntax for “revision 11 of news
”?
Thankfully, while svn will always assume the latter,
there is a trivial workaround. You need only append an at sign to the end
of the path, such as news@11@
. svn
cares only about the last at sign in the argument, and it is not considered
illegal to omit a literal peg revision specifier after that at sign. This
workaround even applies to paths that end in an at sign—you would use
filename@@
to talk about a file named
filename@
.
Let's ask the other question, then—in revision 1, what were the
contents of whatever file occupied the address
concepts/IDEA
at the time? We'll use an explicit peg
revision to help us out.
$ svn cat concept/IDEA@1 The idea behind this project is to come up with a piece of software that can frab a naggily wort. Frabbing naggily worts is tricky business, and doing it incorrectly can have serious ramifications, so we need to employ over-the-top input validation and data verification mechanisms.
Notice that we didn't provide an operative revision this time. That's because when no operative revision is specified, Subversion assumes a default operative revision that's the same as the peg revision.
As you can see, the output from our operation appears to be correct. The
text even mentions frabbing naggily worts, so this is almost certainly the
file that describes the software now called Frabnaggilywort. In fact, we
can verify this using the combination of an explicit peg revision and
explicit operative revision. We know that in HEAD
, the
Frabnaggilywort project is located in the
frabnaggilywort
directory. So we specify that we want
to see how the line of history identified in HEAD
as the
path frabnaggilywort/IDEA
looked in revision 1.
$ svn cat -r 1 frabnaggilywort/IDEA@HEAD The idea behind this project is to come up with a piece of software that can frab a naggily wort. Frabbing naggily worts is tricky business, and doing it incorrectly can have serious ramifications, so we need to employ over-the-top input validation and data verification mechanisms.
And the peg and operative revisions need not be so trivial, either. For
example, say frabnaggilywort
had been deleted from
HEAD
, but we know it existed in revision 20, and we want
to see the diffs for its IDEA
file between revisions 4
and 10. We can use peg revision 20 in conjunction with the URL that would
have held Frabnaggilywort's IDEA
file in revision 20,
and then use 4 and 10 as our operative revision range.
$ svn diff -r 4:10 http://svn.red-bean.com/projects/frabnaggilywort/IDEA@20 Index: frabnaggilywort/IDEA =================================================================== --- frabnaggilywort/IDEA (revision 4) +++ frabnaggilywort/IDEA (revision 10) @@ -1,5 +1,5 @@ -The idea behind this project is to come up with a piece of software -that can frab a naggily wort. Frabbing naggily worts is tricky -business, and doing it incorrectly can have serious ramifications, so -we need to employ over-the-top input validation and data verification -mechanisms. +The idea behind this project is to come up with a piece of +client-server software that can remotely frab a naggily wort. +Frabbing naggily worts is tricky business, and doing it incorrectly +can have serious ramifications, so we need to employ over-the-top +input validation and data verification mechanisms.
Fortunately, most folks aren't faced with such complex situations. But when you are, remember that peg revisions are that extra hint Subversion needs to clear up ambiguity.