We make use of the ability to copy, move, rename, and completely replace files and directories on our computers all that 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 that you'd expect when manipulating your unversioned ones. But that flexibility means that across the lifetime of your repository, a given versioned resource might have many paths, and a given path might represent several entirely different versioned resources. And this introduces a certain level of complexity to your interactions with those paths and resources.
Subversion is pretty smart about noticing when an object's version history includes such “changes of address”. For example, if you ask for all the logs 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.
Clearly the thing you deleted and the thing you later added
aren't the same thing, they merely happen to have had the same
path, which we'll call /trunk/object
.
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 lived at that path?
Clearly, Subversion needs a hint about what you are really
asking.
And thanks to moves, versioned resource history can get far
more twisted than that, even. 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.
[37]
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.
When scenarios like these occur, attempting to instruct Subversion to work with these re-used 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 twenty 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 in order to do the right thing.
In version 1.1, Subversion introduced a way for you to tell
it exactly which Main Street you meant. It's called the
peg revision, and it is a revision
provided to Subversion for the sole purpose of identifying a
unique line of history. Because at most one versioned resource
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 refer to 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,
[38]
we can think of “Main Street” as our path and
“Wheaton” as our peg revision. These two pieces of
information identify a unique path which can travelled (north or
south on Main Street), and will keep us from travelling 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
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 twenty thousand 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 if we are asking about how the
current file looked back in revision 1, or
are we 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 either of them. To find out how the
current IDEA
file looked in that old
revision, you run:
$ svn cat -r 1 concept/IDEA subversion/libsvn_client/ra.c:775: (apr_err=20014) 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 command above is shorthand
for a longer notation which explicitly lists a peg revision.
The expanded notation is:
$ svn cat -r 1 concept/IDEA@BASE subversion/libsvn_client/ra.c:775: (apr_err=20014) svn: Unable to find repository location for 'concept/IDEA' in revision 1
And when executed, it has the expected results. Peg revisions
generally default to a value of BASE
(the
revision currently present in the working copy) when applied to
working copy paths, and of HEAD
when applied
to URLs.
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.
This appears to be the right output. The text even mentions
frabbing naggily worts, so this is almost certainly the file
which 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 the 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.