Copyright © 2005 Michael L.H. Brouwer, Russell Brown
(TBA)
Table of Contents
List of Figures
After playing with Subversion and SVK for a long time, without really being able to use either for real work since our main repository was still using CVS, the author convinced his management to let him switch their SCM over to SVK directly instead of moving to Subversion which would have been at best a step sideways from CVS.
The transistion went smoothly and after the CVS repository was converted to Subversion using cvs2svn, we mirrored it to a SVK depot and setup a quick and easy bootstrap proccess for everyone to use. Almost all of the developers started using SVK immediately without any problems. A few of them asked the question:
Q: You expect me to use a piece of software without documentation for mission critical work?
A: It has documentation, look at the built in help or go to the svk wiki.
Of course I started to realize that without hanging out in the #svk irc channel, the help and the wiki were really not sufficient to help get someone going in using SVK. So I started writing a guide to using SVK day by day on our internal wiki.
After a few days of manually updating the wiki and keeping the table of contents in sync with the actual content, I started to realize that a wiki isn't the best way to write a book, which is what this guide was turning into. I also watched people coming to #svk on a daily basis asking very similar questions about SVK which should have been answered in a document somewhere.
After some encouragement from clkao in irc I decided to start with a copy of the Subversion book's docbook XML sources and write a book about SVK. The idea was to collect information from the wiki, FAQs, from #svk and things I was putting on our internal wiki that really applied to SVK in general into one place.
This book is that one place.
— , San Jose, 30 August, 2005
Table of Contents
“##TODO witty open quote here” —##TODO Witty Quote Author
In the world of open-source software, the Concurrent Versions System (CVS) has long been the tool of choice for version control. And rightly so. CVS itself is free software, and its non-restrictive modus operandi and support for networked operation—which allow dozens of geographically dispersed programmers to share their work—fits the collaborative nature of the open-source world very well. CVS and its semi-chaotic development model have become cornerstones of open-source culture.
Like many tools that have lasted 25 years, CVS is starting to show its age. Subversion is a relatively new version control system designed to be the successor to CVS. The designers set out to win the hearts of CVS users in two ways: by creating an open-source system with a design (and “look and feel”) similar to CVS, and by attempting to fix most of CVS's noticeable flaws. While the result isn't necessarily the next great evolution in version control design, Subversion is very powerful, very usable, and very flexible.
For some people, a plain successor to CVS wasn't good enough. One of those people was Chia-liang Kao. He took a year off his regular work to sit down and write a version control system that would help raise his own productivity once he got back to doing paid work. The result of his labor, and more recently that of an entire community of users and developers is SVK. While Subversion set out take over CVS's user base, SVK attempts to provide an answer for many others - including people who had already defected to another version control system and users who had never before used version control. SVK is written in Perl and uses the underlying revision-tracking filesystem built by the Subversion project.
This book documents SVK version 1.04. We have made every attempt to be thorough in our coverage. However, SVK has a thriving and energetic development community — a number of features and improvements planned for future versions of SVK may change some of the commands and specific notes in this book.
This book is written for computer-literate folk who want to use SVK to manage their data. While SVK runs on a number of different operating systems, its primary user interface is command-line based. It is that command-line tool (svk) which is discussed and used in this book. For consistency, the examples in this book assume the reader is using a Unix-like operating system, and is relatively comfortable with Unix and command-line interfaces.
That said, the svk program also runs on
non-Unix platforms like Microsoft Windows. With a few minor
exceptions, such as the use of backward slashes
(\) instead of forward slashes
(/) for path separators, the input to and
output from this tool when run on Windows are identical to its
Unix counterpart. However, Windows users may find more success
by running the examples inside the Cygwin Unix emulation
environment.
Most readers are probably programmers or sysadmins who need to track changes to source code. This is the most common use for SVK, and therefore it is the scenario underlying all of the book's examples. But SVK can be used to manage changes to any sort of information: images, music, databases, documentation, and so on. To SVK, all data is just data.
While this book is written with the assumption that the reader has never used version control, we've also tried to make it easy for users of CVS or Subversion to make a painless leap into SVK. Special sidebars may discuss CVS or Subversion from time to time, and a special appendix summarizes most of the differences between CVS, Subversion and SVK.
This book aims to be useful to people of widely different backgrounds—from people with no previous experience in version control to experienced sysadmins. Depending on your own background, certain chapters may be more or less important to you. The following can be considered a “recommended reading list” for various types of readers:
The assumption here is that you've probably used CVS before, and are dying to get a Subversion server up and running ASAP. Chapters 5 and 6 will show you how to create your first repository and make it available over the network. After that's done, chapter 3 and appendix A are the fastest routes to learning the SVK client while drawing on your CVS or Subversion experience.
Your administrator has probably set up Subversion already, and you need to learn how to use SVK as a client. If you've never used a version control system (like CVS or Subversion), then chapters 2 and 3 are a vital introduction. If you're already an old hand at CVS or Subversion, chapter 3 and appendix A are the best place to start.
Whether you're a user or administrator, eventually your project will grow larger. You're going to want to learn how to do more advanced things with SVK, such as how to use branches and perform merges (chapter 4), how to use SVK's property support, how to configure runtime options (chapter 7), and other things. Chapters 4 and 7 aren't vital at first, but be sure to read them once you're comfortable with the basics.
Presumably, you're already familiar with SVK, and now want to either extend it or add new tests or fixes to it. Chapter 8 is just for you.
The book ends with reference material—chapter 9 is a reference guide for all SVK commands, and the appendices cover a number of useful topics. These are the chapters you're mostly likely to come back to after you've finished the book.
This section covers the various conventions used in this book.
Used for commands, command output, and switches
Constant width
italicUsed for replaceable items in code and text
ItalicUsed for file and directory names
This icon designates a note relating to the surrounding text.
This icon designates a helpful tip relating to the surrounding text.
This icon designates a warning relating to the surrounding text.
Note that the source code examples are just that—examples. While they will compile with the proper compiler incantations, they are intended to illustrate the problem at hand, not necessarily serve as examples of good programming style.
The chapters that follow and their contents are listed here:
Covers the history of SVK as well as its features, architecture, components, and install methods. Also includes a quick-start guide.
Explains the basics of version control and different versioning models, along with SVK's depot, working copies and revisions.
Walks you through a day in the life of a SVK user. It demonstrates how to use SVK to obtain, modify, and commit data.
Discusses branches, merges, and tagging, including best practices for branching and merging, common use cases, how to undo changes, and how to easily swing from one branch to the next.
Describes the basics of the SVK depot, how to create, configure and maintain a depot, how to setup a shared repository and the tool you can use to do all this.
Explains how to configure a Subversion server for
use with SVK, and the three ways to access your
repository: HTTP, the
svn protocol, and local access. It
also covers the details of authentication, authorization
and anonymous access.
Explores the SVK client environment variables, file
and directory properties, how to
ignore files in your working copy,
and lastly how to handle vendor branches.
Describes the internals of SVK, the $SVKROOT administrative areas from a programmer's point of view. Shows how to write new tests for SVK and most importantly, how to contribute to the development of SVK.
Explains in great detail every subcommand of svk with plenty of examples for the whole family!
Covers the similarities and differences between SVK and Subversion.
Addresses common problems and difficulties using and building SVK.
Discusses tools that support or use Subversion, including alternative client programs, repository browser tools, and so on.
This book started out as a branch of the Version Control With Subversion book. Over time is was morphed into an SVK specific book by the authors. As such, it has always been under a free license. (See Appendix D, Copyright.) In fact, the book was written in the public eye, as a part of SVK. This means two things:
You will always find the latest version of this book in
the book's own Subversion repository at svn://svn.clkao.org/svkbook/trunk.
You can distribute and make changes to this book however you wish—it's under a free license. Of course, rather than distribute your own private version of this book, we'd much rather you send feedback and patches to the SVK developer community. See the section called “” to learn about joining this community.
A relatively recent online version of this book can be found
at http://svkbook.elixus.org.
This book would not be possible (nor very useful) if SVK did not exist. For that, the authors would like to thank Chia-liang Kao for having the vision of writing an Open Source version control system with the power, speed and interoperability that SVK has.
In addition this book wouldn't have existed, or at least would have taken much longer to write if it wasn't for the Subversion book written by Ben Collins-Sussman, Brian W. Fitzpatrick and C. Michael Pilato
The original book on which this book is based is titled Version Control With Subversion and is Copyright 2002, 2003, 2004, 2005 by Ben Collins-Sussman, Brian W. Fitzpatrick and C. Michael Pilato
We would also like to thank the countless people who contributed to the SVK book with reviews, suggestions and fixes: While this is undoubtedly not a complete list, this book would be incomplete and incorrect without the help of: Gary Hoo, Jesse Vincent, Michael Hendricks, Chris Russo and the entire SVK community.
Finally we would like to thank the countless people who contributed to the original subversion book with informal reviews, suggestions, and fixes: While this is undoubtedly not a complete list, this book would be incomplete and incorrect without the help of: Jani Averbach, Ryan Barrett, Francois Beausoleil, Jennifer Bevan, Matt Blais, Zack Brown, Martin Buchholz, Brane Cibej, John R. Daily, Peter Davis, Olivier Davy, Robert P. J. Day, Mo DeJong, Brian Denny, Joe Drew, Nick Duffek, Ben Elliston, Justin Erenkrantz, Shlomi Fish, Julian Foad, Chris Foote, Martin Furter, Dave Gilbert, Eric Gillespie, Matthew Gregan, Art Haas, Greg Hudson, Alexis Huxley, Jens B. Jorgensen, Tez Kamihira, David Kimdon, Mark Benedetto King, Andreas J. Koenig, Nuutti Kotivuori, Matt Kraai, Scott Lamb, Vincent Lefevre, Morten Ludvigsen, Paul Lussier, Bruce A. Mah, Philip Martin, Feliciano Matias, Patrick Mayweg, Gareth McCaughan, Jon Middleton, Tim Moloney, Mats Nilsson, Joe Orton, Amy Lyn Pilato, Kevin Pilch-Bisson, Dmitriy Popkov, Michael Price, Mark Proctor, Steffen Prohaska, Daniel Rall, Tobias Ringstrom, Garrett Rooney, Joel Rosdahl, Christian Sauer, Larry Shatzer, Russell Steicke, Sander Striker, Erik Sjoelund, Johan Sundstroem, John Szakmeister, Mason Thomas, Eric Wadsworth, Colin Watson, Alex Waugh, Chad Whitacre, Josef Wolf, Blair Zajac, and the entire Subversion community.
Table of Contents
Version control is the art of managing changes to information. It has long been a critical tool for programmers, who typically spend their time making small changes to software and then undoing those changes the next day. But the usefulness of version control software extends far beyond the bounds of the software development world. Anywhere you can find people using computers to manage information that changes often, there is room for version control. And that's where SVK comes into play.
This chapter contains a high-level introduction to SVK—what it is; what it does; how to get it.
SVK is a free/open-source version control system. That is, SVK manages files and directories over time. A tree of files is placed into a depot on the user's machine. The depot remembers every change ever made to your files and directories, and also to other files and directories that you mirror from other places. This allows you to recover older versions of your data, or examine the history of how your data changed. In this regard, many people think of a version control system as a sort of “time machine”.
Also central to many uses of Version Control is the concept of a repository. A repository is like a filesystem that can either be hosted on a remote server or locally on the same machine as the user's depot. While SVK doesn't currently provide a repository server, it has been designed to be able to work with repositories created by other Version Control systems; in particular Subversion repositories.
SVK can access repositories across networks, which allows them to be used by people on different computers. This is convenient to those who like a centralized repository model, and SVK supports this fully. Others prefer to use other more detached or distributed models, and again SVK is at home in these environments too. Whichever model is used, because the work is versioned, can always track back through the changes that have been made, and undo them if required.
Some version control systems are also software configuration management (SCM) systems. These systems are specifically tailored to manage trees of source code, and have many features that are specific to software development—such as natively understanding programming languages, or supplying tools for building software. SVK, however, is not one of these systems. It is a general system that can be used to manage any collection of files. For you, those files might be source code—for others, anything from grocery shopping lists to digital video mix-downs and beyond.
The idea of source control management has been around for many years. Early on the centralized repository scheme became the standard for many developer teams. In more recent times, distributed source control has become part of the normal work flow process. CVS is a widely known and used standard for Source Control Management. Because of some shortcomings a team of developers decided to create an improved version of CVS called Subversion (svn). While Subversion, is still a centralized repository system, because it was a new design, it was developed to be more flexible than its predecessor. Because of this fact, developers are able to take key components and connect them together in ways that the original designers never dreamt of. SVK is one of those dreams.
This particular dream came from Chia-liang Kao, who took a year out from work to develop SVK.###TODO: Expand this some more.
While Subversion aimed to take the basic Source Control model already provided by CVS and improve on its design and implementation, SVK aims to open up other Source Control techniques and features offered by other Source Control Systems and models.
Because some of these features are very different to those offered by fully-centralized systems such as CVS and Subversion, there may be a number of techniques or terms that are unfamiliar to you. These will be explained later.
Below is a list of some of the key features provided by SVK
SVK provides:
If a repository is being used, all read-only operations are available without a connection to the repository.
It is possible to use SVK without any connection to a repository whatsoever.
Merges between branches are tracked automatically, and therefore do not need manual lists of revision numbers to be specified.
SVK performs very well when compared with other version control systems.###TODO Need more here.
SVK can mirror repositories created by a number of Source Control Systems other than Subversion. ###TODO: List some here
Subversion expresses file differences using a binary differencing algorithm, which works identically on both text (human-readable) and binary (human-unreadable) files. Both types of files are stored equally compressed in the repository, and differences are transmitted in both directions across the network.
Figure 1.1, “SVK's Architecture” illustrates what one might call a “mile-high” view of SVK's design.
Because of the extremely flexible nature of SVK, it's very difficult to pin down one definitive way of describing its architecture; either in words or graphically. The way in which it all works depends largely on the source control model that you're currently using, and that gets even more complicated when you consider that you could be using more than one model with multiple projects in the same svk installation.
On one end is a Subversion repository that holds all of your versioned data. On the other end is your Subversion client program, which manages local reflections of portions of that versioned data (called “working copies”). Between these extremes are multiple routes through various Repository Access (RA) layers. Some of these routes go across computer networks and through network servers which then access the repository. Others bypass the network altogether and access the repository directly.
SVK is a set of Perl 5 modules written on top of Subversion's Perl bindings. In order to run SVK you will need to install Subversion with Perl bindings. Subversion itself is built on a portability layer called APR (Apache Portable Runtime library). This means SVK should work on any operating system that supports Perl 5 and the Apache httpd server runs on: Windows, Linux, all flavors of BSD, Mac OS X, Netware, and others.
The easiest way to get SVK is to download a binary
package built for your operating system. SVK's website
(http://svk.elixus.org) often has
these packages available for download, posted by volunteers.
The site usually contains graphical installer packages for users
of Apple and Microsoft operating systems. If you run a Unix-like
operating system, you can use your system's native package
distribution system (RPMs, DEBs, the ports tree, etc.) to get
SVK.
Alternately, you can build SVK directly from source
code. Below are instructions from the SVK wiki at http://svk.elixus.org/index.php?BuildingSvkInYourHomeDirectory,
on how to build SVK in your home directory without root
access:
#!/bin/sh
# Install Perl
curl -O http://www.cpan.org/src/stable.tar.gz
tar xzf stable.tar.gz
cd perl-5*
sh Configure -ders -Dprefix=$HOME/local
make
make test # I didn't really do this step :P
make install
cd ..
export PATH=$HOME/local/bin:$PATH
# Install CPAN Modules
rm -f $HOME/local/lib/perl5/5.8.6/CPAN/Config.pm
rm -fr .cpan
perl -e 'print "\n\n\n\n\n\n\n\nfollow\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n5\n4\n1\n\nq\n"' > answers
perl -MCPAN -eshell < answers
rm answers
cpan Bundle::CPAN < /dev/null
cpan LWP < /dev/null
cpan ExtUtils::AutoInstall < /dev/null
cpan Module::Build < /dev/null
cpan Module::Install < /dev/null
cpan Module::Signature < /dev/null
cpan SVN::Simple::Edit < /dev/null
cpan version < /dev/null
cpan Sort::amic < /dev/null
cpan PerlIO::via::symlink < /dev/null
cpan IO::Digest < /dev/null
cpan Date::Parse < /dev/null
cpan File::Type < /dev/null
cpan PerlIO::eol < /dev/null
cpan Locale::Maketext::Simple < /dev/null
cpan Locale::Maketext::Lexicon < /dev/null
cpan FreezeThaw < /dev/null
cpan HTML::Element < /dev/null
cpan IPC::Run3 < /dev/null
cpan Pod::HTML_Elements < /dev/null
cpan Text::Diff < /dev/null
cpan XML::ValidWriter < /dev/null
cpan VCP::Dest::svk < /dev/null
# Install Apache 2.0
wget http://apache.mirrors.pair.com/httpd/httpd-2.0.52.tar.gz
tar xzf httpd-2.0.52.tar.gz
./configure --prefix=$HOME/local/apache2 --enable-mods-shared='headers rewrite dav ssl'
make
make install
cd ..
# XXX Apache configuration/startup juju goes here...
# Install SWIG
wget http://optusnet.dl.sourceforge.net/sourceforge/swig/swig-1.3.19.tar.gz
tar xzf swig-1.3.24.tar.gz
cd SWIG-1.3.24/
./configure --with-perl5=$HOME/local/bin/perl5.8.6 --prefix=$HOME/local
make
make install
cd ..
# Install Subversion
wget http://subversion.tigris.org/tarballs/subversion-1.2.1.tar.gz
tar xzf subversion-1.2.1.tar.gz
cd subversion-1.2.1
./configure \
SWIG=$HOME/local/bin/swig \
PERL=$HOME/local/bin/perl5.8.6 \
--prefix=$HOME/local \
--with-apxs=$HOME/local/apache2/bin/apxs
make
make swig-pl
make check-swig-pl
make install
make install-swig-pl
cd ..
# Install SVN-Mirror
svn co svn://svn.clkao.org/member/clkao/modules/SVN-Mirror/ SVN-Mirror
cd SVN-Mirror
perl Makefile.PL
make
make test
make install
cd ..
# Install VCP
wget http://search.cpan.org/CPAN/authors/id/A/AU/AUTRIJUS/VCP-autrijus-snapshot-0.9-20050110.tar.gz
tar xzf VCP-autrijus-snapshot-0.9-20050110.tar.gz
cd VCP-autrijus-snapshot-0.9-20050110
perl Makefile.PL
make
make test
make install
cd ..
# Install SVK
svn co svn://svn.clkao.org/svk/trunk svk
cd svk/
perl Makefile.PL
make
make test
make install
cd ..
rm -fr SVN-Mirror SWIG-1.3.24 perl-5.8.6 stable.tar.gz \
subversion-1.2.1* svk swig-1.3.24.tar.gz
svk help commands
# Done!
SVK, once installed, has a number of different pieces. The following is a quick overview of what you get. Don't be alarmed if the brief descriptions leave you scratching your head—there are plenty more pages in this book devoted to alleviating that confusion.
The command-line client program.
A tool for creating, tweaking or repairing a Subversion repository—this is technically part of subversion, however svk need it to be installed in order to create depots..
Assuming you have SVK installed correctly, you should be ready to start. The next two chapters will walk you through the use of svk, SVK's command-line client program.
Some 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 SVK, 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, Subversion and SVK, then you should read Chapter 2, Basic Concepts before going any further.
The following example assumes that you have svk, the SVK command-line client, and svnadmin, the administrative tool which is part of Subversion, ready to go. It also assumes you are using Subversion 1.2 or later (run svnadmin --version to check.) and SVK-1.00 or later (run svk --version to check.
SVK stores all versioned data in a depot. To begin, create
the default depot (make sure to answer
y<return> to the question svk asks
you):
$ svk depotmap --init Repository /Users/sally/.svk/local does not exist, create? (y/n)y $ ls ~/.svk cache config local
This command creates a new directory
~/.svk[1] which contains SVK's administrative
files, and a Subversion repository called local.
SVK has no concept of a “project”. The depot is just a virtual versioned filesystem, a large tree that can hold anything you wish. Some people prefer to store only one project in a depot, and others prefer to store multiple projects in a depot by placing them into separate directories. The merits of each approach are discussed in the section called “”. Either way, the depot only manages files and directories, so it's up to humans to interpret particular directories as “projects”.
In this example, we assume that you already have some sort
of project (a collection of files and directories) that you wish
to import into your newly created SVK depot. Begin
by organizing them into a single directory
called myproject (or whatever you wish).
For reasons that will be clear later on (see
Chapter 4, Branching and Merging), your project's tree
structure should contain three top-level directories
named branches,
tags, and
trunk. The trunk
directory should contain all of your data,
while branches
and tags directories are empty:
/tmp/myproject/branches/
/tmp/myproject/tags/
/tmp/myproject/trunk/
foo.c
bar.c
Makefile
…
Once you have your tree of data ready to go, import it into the repository with the svk import command (see the section called “svk import”):
$ svk import --message "initial import" /tmp/myproject //myproject Committed revision 1. Import path //myproject initialized. Committed revision 2. Directory /tmp/myproject imported to depotpath //myproject as revision 2.
Now the depot contains this tree of data. As mentioned
earlier, you won't see your files by directly peeking into the
depot; they're all stored within a database. But the
depot's imaginary filesystem now contains a top-level
directory named myproject, which in turn
contains your data.
Note that the original /tmp/myproject
directory is unchanged; SVK is unaware of it. (In fact,
you can even delete that directory if you wish.) In order to
start manipulating repository data, you need to create a new
“working copy” of the data, a sort of private
workspace. Ask SVK to “check out” a working
copy of the myproject/trunk directory in
the depot:
$ svk checkout //myproject/trunk myproject Syncing //myproject/trunk(/myproject/trunk) in /Users/sally/myproject to 2. A myproject/foo.c A myproject/bar.c A myproject/Makefile …
Now you have a editable copy of part of the depot in a
new directory named myproject. You can edit
the files in your working copy and then commit those changes
back into the depot.
Enter your working copy and edit a file's contents.
Run svk diff to see unified diff output of your changes.
Run svk commit to commit the new version of your file to the depot.
Run svk update to bring your working copy “up-to-date” with the depot.
For a full tour of all the things you can do with your working copy, read Chapter 3, Guided Tour.
At this point, you have the option of making your depot available to others over a network. See Chapter 6, Server Configuration to learn about the different sorts of server processes available and how to configure them.
Table of Contents
This chapter is a short, casual introduction to SVK. If you're new to version control, this chapter is definitely for you. We begin with a discussion of general version control concepts, work our way into the specific ideas behind SVK, and show some simple examples of SVK in use.
Even though the examples in this chapter show people sharing collections of program source code, keep in mind that SVK can manage any sort of file collection—it's not limited to helping computer programmers.
SVK is a system for tracking history. At its core is a depot[2], which is a central store of data. The depot stores information in the form of a filesystem tree—a typical hierarchy of files and directories.
So why is this interesting? So far, this sounds like the definition of a typical file system. And indeed, the depot is a kind of file system but it's not your usual breed. What makes the SVK depot special is that it remembers every change ever written to it: every change to every file, and even changes to the directory tree itself, such as the addition, deletion, and rearrangement of files and directories.
When SVK reads data from the depot, it normally sees only the latest version of the filesystem tree. But it also has the ability to view previous states of the filesystem. For example, you can ask SVK historical questions like, “What did this directory contain last Wednesday?” or “Who was the last person to change this file, and what changes did they make?” These are the sorts of questions that are at the heart of any version control system: systems that are designed to record and track changes to data over time.
The core mission of a version control system is to enable collaborative editing and sharing of data. But different systems use different strategies to achieve this.
All version control systems have to solve the same fundamental problem: how will the system allow users to share information, but prevent them from accidentally stepping on each other's feet? It's all too easy for users to accidentally overwrite each other's changes in the depot.
Consider the scenario shown in Figure 2.1, “The problem to avoid”. Suppose we have two co-workers, Harry and Sally. They each decide to edit the same repository file at the same time. If Harry saves his changes to the repository first, then it's possible that (a few moments later) Sally could accidentally overwrite them with her own new version of the file. While Harry's version of the file won't be lost forever (because the system remembers every change), any changes Harry made won't be present in Sally's newer version of the file, because she never saw Harry's changes to begin with. Harry's work is still effectively lost—or at least missing from the latest version of the file—and probably by accident. This is definitely a situation we want to avoid!
Many version control systems use a lock-modify-unlock model to address this problem. In such a system, the repository allows only one person to change a file at a time. First Harry must “lock” the file before he can begin making changes to it. Locking a file is a lot like borrowing a book from the library; if Harry has locked a file, then Sally cannot make any changes to it. If she tries to lock the file, the repository will deny the request. All she can do is read the file, and wait for Harry to finish his changes and release his lock. After Harry unlocks the file, his turn is over, and now Sally can take her turn by locking and editing. Figure 2.2, “The lock-modify-unlock solution” demonstrates this simple solution.
The problem with the lock-modify-unlock model is that it's a bit restrictive, and often becomes a roadblock for users:
Locking may cause administrative problems. Sometimes Harry will lock a file and then forget about it. Meanwhile, because Sally is still waiting to edit the file, her hands are tied. And then Harry goes on vacation. Now Sally has to get an administrator to release Harry's lock. The situation ends up causing a lot of unnecessary delay and wasted time.
Locking may cause unnecessary serialization. What if Harry is editing the beginning of a text file, and Sally simply wants to edit the end of the same file? These changes don't overlap at all. They could easily edit the file simultaneously, and no great harm would come, assuming the changes were properly merged together. There's no need for them to take turns in this situation.
Locking may create a false sense of security. Pretend that Harry locks and edits file A, while Sally simultaneously locks and edits file B. But suppose that A and B depend on one another, and the changes made to each are semantically incompatible. Suddenly A and B don't work together anymore. The locking system was powerless to prevent the problem—yet it somehow provided a false sense of security. It's easy for Harry and Sally to imagine that by locking files, each is beginning a safe, insulated task, and thus not bother discussing their incompatible changes early on.
SVK, Subversion, CVS, and other version control systems use a copy-modify-merge model as an alternative to locking. In this model, each user's client contacts the project repository and creates a personal working copy—a local reflection of the repository's files and directories. Users then work in parallel, modifying their private copies. Finally, the private copies are merged together into a new, final version. The version control system often assists with the merging, but ultimately a human being is responsible for making it happen correctly.
Here's an example. Say that Harry and Sally each create working copies of the same project, copied from the repository. They work concurrently, and make changes to the same file A within their copies. Sally saves her changes to the repository first. When Harry attempts to save his changes later, the repository informs him that his file A is out-of-date. In other words, that file A in the repository has somehow changed since he last copied it. So Harry asks his client to merge any new changes from the repository into his working copy of file A. Chances are that Sally's changes don't overlap with his own; so once he has both sets of changes integrated, he saves his working copy back to the repository. Figure 2.3, “The copy-modify-merge solution” and Figure 2.4, “The copy-modify-merge solution (continued)” show this process.
But what if Sally's changes do overlap with Harry's changes? What then? This situation is called a conflict, and it's usually not much of a problem. When Harry asks his client to merge the latest repository changes into his working copy, his copy of file A is somehow flagged as being in a state of conflict: he'll be able to see both sets of conflicting changes, and manually choose between them. Note that software can't automatically resolve conflicts; only humans are capable of understanding and making the necessary intelligent choices. Once Harry has manually resolved the overlapping changes—perhaps after a discussion with Sally—he can safely save the merged file back to the repository.
The copy-modify-merge model may sound a bit chaotic, but in practice, it runs extremely smoothly. Users can work in parallel, never waiting for one another. When they work on the same files, it turns out that most of their concurrent changes don't overlap at all; conflicts are infrequent. And the amount of time it takes to resolve conflicts is far less than the time lost by a locking system.
In the end, it all comes down to one critical factor: user communication. When users communicate poorly, both syntactic and semantic conflicts increase. No system can force users to communicate perfectly, and no system can detect semantic conflicts. So there's no point in being lulled into a false promise that a locking system will somehow prevent conflicts; in practice, locking seems to inhibit productivity more than anything else.
It's time to move from the abstract to the concrete. In this section, we'll show real examples of SVK being used.
You've already read about working copies; now we'll demonstrate how the SVK client creates and uses them.
A SVK working copy is an ordinary directory tree on your local system, containing a collection of files. You can edit these files however you wish, and if they're source code files, you can compile your program from them in the usual way. Your working copy is your own private work area: SVK will never incorporate changes from the depot, nor publish your own changes to the depot, until you explicitly tell it to do so.
After you've made some changes to the files in your working copy and verified that they work properly, SVK provides you with commands to “publish” your changes to the depot. If the depot contained changes not yet in your working copy[3], SVK provides you with commands to merge those changes into your working directory (by reading from the depot).
A SVK working copy doesn't contain any extra files, unlike
Subversion and CVS working copies. Instead SVK keeps track of
the state of your working copy in a subdirectory of your home
directory named .svk.
A typical SVK depot often holds the files (or source code) for several projects; usually, each project is a subdirectory in the depot's filesystem tree. In this arrangement, a user's working copy will usually correspond to a particular subtree of the depot.
For example, suppose you have two software projects,
paint and calc, for
which you wish to start keeping a history.
To add these 2 projects to your depot you would run the following commands:
$ svk import --message "Initial import of calc." calc //calc Repository /Users/sally/.svk/local does not exist, create? (y/n)y Committed revision 1. Import path //calc initialized. Committed revision 2. Directory /Users/sally/calc imported to depotpath //calc as revision 2.
Then repeat the same steps for the paint project. Now you have 2 projects in the depot. Each project lives in its own top-level subdirectory, as shown in Figure 2.5, “The depot's filesystem”.
Since the calc and paint projects have been imported into the depot, it's safe to remove the directories we imported from, so lets run:
$ rm -rf calc $ rm -rf paint
Now in order to get a working copy, you must
check out some subtree of the
depot[4].
(The term “check out” may sound like it has
something to do with locking or reserving resources, but it
doesn't; it simply creates a private copy of the project for
you.) For example, if you check out
/calc, you will get a working copy like
this:
$ svk checkout //calc Syncing //calc(/calc) in /Users/sally/calc to 2. A calc/button.c A calc/Makefile A calc/integer.c $ ls -A calc Makefile button.c integer.c
The list of letter A's indicates that SVK is adding
a number of items to your working copy. You now have a
editable copy of the depot's /calc
directory.
Suppose you make changes to button.c.
Since SVK knows the revision that the file in your working
copy was based on, SVK can tell that you've changed the file.
However, SVK does not make your changes public until you
explicitly tell it to. The act of publishing your changes is
more commonly known as committing (or
checking in) changes to the
depot.
To publish your changes to others, you can use SVK's commit command:
$ svk commit button.c Committed revision 57.
Now your changes to button.c have
been committed to the depot; if another user checks out a
working copy of /calc, they will see
your changes in the latest version of the file.
Suppose you have a collaborator, Sally, who checked out a
working copy of /calc at the same time
you did. When you commit your change to
button.c, Sally's working copy is left
unchanged; SVK only modifies working copies at the
user's request.
To bring her project up to date, Sally can ask SVK to update her working copy, by using the SVK update command. This will incorporate your changes into her working copy, as well as any others that have been committed since she checked it out.
$ pwd /Users/sally/calc $ ls -A Makefile integer.c button.c $ svk update U button.c
The output from the svk update command
indicates that SVK updated the contents of
button.c. Note that Sally didn't need to
specify which files to update; SVK uses the information
about the working copy stored inside
~/.svk/config, and further information from
the depot, to decide which files need to be brought up to
date.
A svk commit operation can publish changes to any number of files and directories as a single atomic transaction. In your working copy, you can change files' contents, create, delete, rename and copy files and directories, and then commit the complete set of changes as a unit.
In the depot, each commit is treated as an atomic transaction: either all the commit's changes take place, or none of them take place. SVK tries to retain this atomicity in the face of program crashes, system crashes, network problems, and other users' actions.
Each time the depot accepts a commit, this creates a new state of the filesystem tree, called a revision. Each revision is assigned a unique natural number, one greater than the number of the previous revision. The initial revision of a freshly created depot is numbered zero, and consists of nothing but an empty root directory.
Figure 2.6, “The depot” illustrates a nice way to visualize the depot. Imagine an array of revision numbers, starting at 0, stretching from left to right. Each revision number has a filesystem tree hanging below it, and each tree is a “snapshot” of the way the depot looked after a commit.
It's important to note that working copies do not always correspond to any single revision in the depot; they may contain files from several different revisions. For example, suppose you check out a working copy from a depot whose most recent revision is 4:
calc/Makefile:4
integer.c:4
button.c:4
At the moment, this working directory corresponds exactly
to revision 4 in the repository. However, suppose you make a
change to button.c, and commit that
change. Assuming no other commits have taken place, your
commit will create revision 5 of the depot, and your
working copy will now look like this:
calc/Makefile:4
integer.c:4
button.c:5
Suppose that, at this point, Sally commits a change to
integer.c, creating revision 6. If you
use svk update to bring your working copy
up to date, then it will look like this:
calc/Makefile:6
integer.c:6
button.c:6
Sally's change to integer.c will
appear in your working copy, and your change will still be
present in button.c. In this example,
the text of Makefile is identical in
revisions 4, 5, and 6, but SVK will mark your working
copy of Makefile with revision 6 to
indicate that it is still current. So, after you do a clean
update at the top of your working copy, it will generally
correspond to exactly one revision in the repository.
For each working copy, SVK records
three essential pieces of information in the
hash: subsection of the
checkout: section in the
~/.svk/config file:
An absolute path to the working copy, or a subdirectory thereof, and
what revision your working copy is based on (this is called the working copy's working revision), and
the encoding in which the filenames are stored by the filesystem.
Given this information, by consulting the depot, SVK can tell which of the following four states a working file is in:
The file is unchanged in the working directory, and no changes to that file have been committed to the depot since its working revision. An svk commit of the file will do nothing, and an svk update of the file will do nothing.
The file has been changed in the working directory, and no changes to that file have been committed to the depot since its base revision. There are local changes that have not been committed to the depot, thus an svk commit of the file will succeed in publishing your changes, and an svk update of the file will do nothing.
The file has not been changed in the working directory, but it has been changed in the depot. The file should eventually be updated, to make it current with the depot revision. An svk commit of the file will do nothing, and an svk update of the file will fold the latest changes into your working copy.
The file has been changed both in the working directory, and in the depot. An svk commit of the file will fail with an “out-of-date” error. The file should be updated first; an svk update command will attempt to merge the public changes with the local changes. If SVK can't complete the merge in a plausible way automatically, it will ask the user how to resolve the conflict.
This may sound like a lot to keep track of, but the svk status command will show you the state of any item in your working copy. For more information on that command, see the section called “svk status”.
As a general principle, Subversion tries to be as flexible as possible. One special kind of flexibility is the ability to have a working copy containing files and directories with a mix of different working revision numbers. Unfortunately, this flexibility tends to confuse a number of new users. If the earlier example showing mixed revisions perplexed you, here's a primer on both why the feature exists and how to make use of it.
One of the fundamental rules of Subversion is that a “push” action does not cause a “pull”, nor the other way around. Just because you're ready to submit new changes to the repository doesn't mean you're ready to receive changes from other people. And if you have new changes still in progress, then svn update should gracefully merge repository changes into your own, rather than forcing you to publish them.
The main side-effect of this rule is that it means a working copy has to do extra bookkeeping to track mixed revisions, and be tolerant of the mixture as well. It's made more complicated by the fact that directories themselves are versioned.
For example, suppose you have a working copy entirely at
revision 10. You edit the
file foo.html and then perform
an svn commit, which creates revision 15
in the repository. After the commit succeeds, many new
users would expect the working copy to be entirely at
revision 15, but that's not the case! Any number of changes
might have happened in the repository between revisions 10
and 15. It would be a lie to claim that we had a working
copy of revision 15, the client simply doesn't know. If, on
the other hand, svn commit were to
automatically download the newest changes, then it would be
possible to set the entire working copy to revision
15—but then we'd be breaking the fundamental rule
of “push” and “pull” remaining
separate actions. Therefore the only safe thing the
Subversion client can do is mark the one
file—foo.html—as being at
revision 15. The rest of the working copy remains at
revision 10. Only by running svn update
can the latest changes be downloaded, and the whole working
copy be marked as revision 15.
The fact is, every time you run svn commit, your working copy ends up with some mixture of revisions. The things you just committed are marked as having larger working revisions than everything else. After several commits (with no updates in-between) your working copy will contain a whole mixture of revisions. Even if you're the only person using the repository, you will still see this phenomenon. To examine your mixture of working revisions, use the svn status --verbose command (see the section called “svk status” for more information.)
Often, new users are completely unaware that their working copy contains mixed revisions. This can be confusing, because many client commands are sensitive to the working revision of the item they're examining. For example, the svn log command is used to display the history of changes to a file or directory (see the section called “svk log”). When the user invokes this command on a working copy object, they expect to see the entire history of the object. But if the object's working revision is quite old (often because svn update hasn't been run in a long time), then the history of the older version of the object is shown.
If your project is sufficiently complex, you'll discover that it's sometimes nice to forcibly “backdate” portions of your working copy to an earlier revision; you'll learn how to do that in Chapter 3, Guided Tour. Perhaps you'd like to test an earlier version of a sub-module contained in a subdirectory, or perhaps you'd like to figure out when a bug first came into existence in a specific file. This is the “time machine” aspect of a version control system — the feature which allows you to move any portion of your working copy forward and backward in history.
However you make use of mixed revisions in your working copy, there are limitations to this flexibility.
First, you cannot commit the deletion of a file or directory which isn't fully up-to-date. If a newer version of the item exists in the repository, your attempt to delete will be rejected, to prevent you from accidentally destroying changes you've not yet seen.
Second, you cannot commit a metadata change to a directory unless it's fully up-to-date. You'll learn about attaching “properties” to items in Chapter 7, Advanced Topics A directory's working revision defines a specific set of entries and properties, and thus committing a property change to an out-of-date directory may destroy properties you've not yet seen.
We've covered a number of fundamental Subversion concepts in this chapter:
We've introduced the notions of the central repository, the client working copy, and the array of repository revision trees.
We've seen some simple examples of how two collaborators can use Subversion to publish and receive changes from one another, using the “copy-modify-merge” model.
We've talked a bit about the way Subversion tracks and manages information in a working copy.
At this point, you should have a good idea of how Subversion works in the most general sense. Armed with this knowledge, you should now be ready to jump into the next chapter, which is a detailed tour of Subversion's commands and features.
[2] Technically you can have more than one depot, but well talk about that later.
[3] More on how this can happen in chapter 3
[4] Actually we could have caused the
directory from which we imported to become a working copy
simply by adding the --to-checkout switch to
the import command.
[5] If your home directory is on a networked filesystem this is not strictly true, and it is in fact possible to share a depot amongst different users by having your depot on a shared volume. However in chapter 3 we will show better ways of working together on the same projects.
[6] Yes you can have more than one depot, but don't worry about it for now. Chances are you won't ever need more than one.
Table of Contents
Now we will go into the details of using SVK. By the time you reach the end of this chapter, you will be able to perform almost all the tasks you need to use SVK in a normal day's work. You'll start with an initial checkout of your code, and walk through making changes and examining those changes. You'll also see how to bring changes made by others into your working copy, examine them, and work through any conflicts that might arise.
Note that this chapter is not meant to be an exhaustive list of all SVK's commands—rather, it's a conversational introduction to the most common SVK tasks you'll encounter. This chapter assumes that you've read and understood Chapter 2, Basic Concepts and are familiar with the general model of SVK. For a complete reference of all commands, see Chapter 9, SVK Complete Reference.
Before reading on, here is the most important command you'll ever need when using SVK: svk help. The SVK command-line client is self-documenting—at any time, a quick svk help <subcommand> will describe the syntax, switches, and behavior of the subcommand.
You use svk import to import a new project into a SVK depot. While this is most likely the very first thing you will do when you set up your depot, it's not something that happens very often. For a detailed description of import, see the section called “svk import” later in this chapter.
You use svk mirror to mirror a remote Subversion repository into a SVK depot[7]. SVK will then allow you to check out working copies from your local mirror as well as commit changes back to the mirrored repository. Basically this makes SVK a Subversion client with a local cache, which as it turns out makes SVK orders of magnitude faster than the basic Subversion client. In addition this opens up the possibility to create local branches and track changes between those and the mirror in both directions, for more information on working with local branches, see Chapter 4, Branching and Merging.
Before we go on, you should know a bit about how to identify a particular revision in your depot. As you learned in the section called “Revisions”, a revision is a “snapshot” of the depot at a particular moment in time. As you continue to commit and grow your depot, you need a mechanism for identifying these snapshots.
You specify these revisions by using the
--revision (-r) switch plus
the revision you want (svk <subcommand>
--revision REV) or you can specify a range by
separating two revisions with a colon (svk
<subcommand> --revision REV1:REV2). And SVK
lets you refer to these revisions by number, keyword, or
date.
When you create a new SVK depot, it begins its life at revision zero and each successive commit increases the revision number by one. After your commit completes, the SVK client informs you of the new revision number:
$ svk commit --message "Corrected number of cheese slices." Committed revision 3.
If at any point in the future you want to refer to that revision (we'll see how and why we might want to do that later in this chapter), you can refer to it as “3”.
If, however, you were committing from a working copy that was a direct checkout of a mirrored depotpath, things would be a little more complicated. This is because the mirrored repository has its own idea of revision numbers which is distinct from the local depots idea of revision numbers.
$ svk commit --message "Corrected number of cheese slices." Commit into mirrored path: merging back directly. Merging back to mirror source http://svn.sally.org/calc. Merge back committed as revision 45. Syncing http://svn.sally.org/calc Retrieving log information from 45 to 45 Committed revision 15 from revision 45.
What happened here is that SVK first committed the change you made to the original mirrored repository. After that it automatically performs a svk sync to download the change on the mirror to the local depot again. This ensures that you can never commit anything to a mirrored path that isn't also on the mirror itself.
Above we showed you how to use the
--revision switch to refer to a specific
revision number. By default the revision numbers you specify
are the ones in your depot. Sometimes you want to refer to a
particular revision of the mirrored repository instead.
Luckily SVK keeps a mapping from local to remote revision
numbers and allows you to specify both local depot revision
numbers or revision numbers in the mirrored repository when
performing operations. To do so you only need to add a
@ right after the revision number. To get
the log message for the revision we just committed above you
would use:
$ svk log --revision 45@ ---------------------------------------------------------------------- r15 (orig r45): sally | 2005-07-23 14:49:11 -0700 Corrected number of cheese slices. ----------------------------------------------------------------------
Notice how the log output shows both the local depots
r15, and mirrored repositories
r45 revision numbers. This can be a useful
aid in certain situations as well.
The SVK client understands a number of
revision keywords. These keywords
can be used instead of integer arguments to the
--revision switch, and are resolved into
specific revision numbers by SVK:
For every file and directory in your working copy SVK keeps track of the revision you last updated to. You can refer to this as the “BASE” revision.
The latest revision in the depot.
The last revision an item in a working copy was updated to.
BASE, can be used to refer to local
paths, but not to DEPOTPATHs.
Here are some examples of revision keywords in action. Don't worry if the commands don't make sense yet; we'll be explaining these commands as we go through the chapter:
$ svk diff --revision BASE:HEAD foo.c
# shows the changes in the depot not yet in your working copy.
$ svk log --revision HEAD
# shows log message for the latest depot commit
$ svk diff --revision HEAD
# compares your working file (with local mods) to the latest version
# in the depot.
$ svk diff --revision BASE:HEAD foo.c
# compares your “pristine” foo.c (no local mods) with the
# latest version in the depot
$ svk log --revision BASE:HEAD
# shows all commit logs since you last updated
These keywords allow you to perform many common (and helpful) operations without having to look up specific revision numbers or remember the exact revision of your working copy.
Anywhere that you specify a revision number or revision keyword, you can also specify a date inside curly braces “{}”. You can even access a range of changes in the depot using both dates and revisions together!
Here are examples of the date formats that SVK accepts. Remember to use quotes around any date that contains spaces.
$ svk checkout --revision {2002-02-17}
$ svk checkout --revision {15:30}
$ svk checkout --revision {15:30:00.200000}
$ svk checkout --revision {"2002-02-17 15:30"}
$ svk checkout --revision {"2002-02-17 15:30 0230"}
$ svk checkout --revision {2002-02-17T15:30}
$ svk checkout --revision {2002-02-17T15:30Z}
$ svk checkout --revision {2002-02-17T15:30-04:00}
$ svk checkout --revision {20020217T1530}
$ svk checkout --revision {20020217T1530Z}
$ svk checkout --revision {20020217T1530-0500}
…
When you specify a date as a revision, SVK finds the most recent revision of the depot as of that date:
$ svk log --revision {2005-07-23}
----------------------------------------------------------------------
r12 (orig r41): sally | 2005-07-22 10:06:17 -0700
…
You can also use a range of dates. SVK will find all revisions between both dates, inclusive:
$ svk log --revision {2005-07-20}:{2005-07-29}
…
As we pointed out, you can also mix dates and revisions:
$ svk log --revision {2005-07-20}:4040
Users should be aware of a subtlety that can become quite a stumbling-block when dealing with dates in SVK. Since the timestamp of a revision is stored as a property of the revision—an unversioned, modifiable property—revision timestamps can be changed to represent complete falsifications of true chronology, or even removed altogether. This will wreak havoc on the internal date-to-revision conversion that SVK performs.
Most of the time, you will want to start using SVK by creating a mirror of a remote Subversion repository containing your project. Mirroring a repository creates a copy of it on your local machine. This copy contains as much of the original history of the Subversion repository as you want. For this example let's mirror everything:
$ svk mirror svn://svn.clkao.org/svk //mirror/svk Committed revision 1. $ svk sync //mirror/svk Retrieving log information from 1 to 1281 Committed revision 2 from revision 1. Committed revision 3 from revision 2. … Committed revision 1282 from revision 1281.
When mirroring a project you will have the choice between mirroring just trunk or the branch you are interested in as opposed to the entire project. In most cases you will want to mirror the entire project tree including the trunk, branches and tags directories. Since branches and tags are cheap copies on the remote server the mirror will also store them as cheap copies and thus not use significantly more disk space.
If you choose not to do this and mirror just a single branch or trunk, and you later decide you need access to other branches or tags in the remote repository, separately mirroring those will cause you local depot to contain disjoint mirrors of the individual branches and tags, and copies that were originally cheap copies in the remote repository will become full blown copies in your local depot. In addition you might run into problems with merge tracking across remote branches.
Creating a mirror of a large Subversion repository can take quite a while. However rest assured that once you have the mirror keeping it up to date is very fast. We will also discuss several ways to speed up getting an initial mirror on a large number of machines, see ##TODO.
Now that we have a mirror of the Subversion repository in our depot we are ready to create a working copy:
$ svk checkout //mirror/svk/trunk Syncing //mirror/svk/trunk(/mirror/svk/trunk) in /Users/sally/trunk to 1282. A trunk/utils A trunk/utils/extract-docs A trunk/utils/extract-message-catalog A trunk/utils/svk-ediff.el A trunk/SIGNATURE A trunk/pkg …
Although the above example checks out the trunk directory, you can just as easily check out any deep subdirectory of a depot by specifying the subdirectory in the checkout DEPOTPATH:
$ svk checkout //mirror/svk/trunk/lib/SVK/Command Syncing //mirror/svk/trunk(/mirror/svk/trunk) in /Users/sally/Command to 1282. A Command/Propdel.pm A Command/Checkout.pm A Command/Revert.pm A Command/Cat.pm …
Since SVK uses a “copy-modify-merge” model instead of “lock-modify-unlock” (see Chapter 2, Basic Concepts), you're already able to start making changes to the files and directories in your working copy. Your working copy is just like any other collection of files and directories on your system. You can edit and change them, move them around, you can even delete the entire working copy and forget about it—though you should run svk checkout --detach to let svk know you removed it.
While your working copy is “just like any other collection of files and directories on your system”, you need to let SVK know if you're going to be rearranging anything inside of your working copy. If you want to copy or move an item in a working copy, you should use svk copy or svk move instead of the copy and move commands provided by your operating system. We'll talk more about them later in this chapter.
Unless you're ready to commit a new file or directory, or changes to existing ones, there's no need to further notify the Subversion server you mirrored from that you've done anything.
While you can certainly check out a working copy with the DEPOTPATH as the only argument, you can also specify a directory after your DEPOTPATH. This places your working copy in the new directory that you name. For example:
$ svk checkout //mirror/svk/trunk svk Syncing //mirror/svk/trunk(/mirror/svk/trunk) in /Users/sally/svk to 1282. A svk/utils A svk/utils/extract-docs A svk/utils/extract-message-catalog A svk/utils/svk-ediff.el A svk/SIGNATURE …
That will place your working copy in a directory named
svk instead of a directory named
trunk as we did previously.
SVK has numerous features, options, bells and whistles, but on a day-to-day basis, odds are that you will only use a few of them. In this section we'll run through the most common things that you might find yourself doing with SVK in the course of a day's work.
The typical work cycle looks like this:
Update your working copy
svk sync
svk update
Make changes
svk add
svk delete
svk copy
svk move
Examine your changes
svk status
svk diff
svk revert
Merge others' changes into your working copy
svk update
svk resolved
Commit your changes
svk commit
When working on a project with a team, you'll want to update your mirror to receive any changes made by other developers on the project since your last update.
$ svk sync //mirror/project Retrieving log information from 500 to 502 Committed revision 503 from revision 500. Committed revision 504 from revision 501. Committed revision 505 from revision 502.
Then you'll want to bring those changes into your working copy. Use svk update to bring your working copy into sync with the latest revision in your depot.
$ svk update Syncing //mirror/project/trunk(/mirror/project/trunk) in /Users/sally/project to 505. U foo.c U bar.c
You can combine the first 2 steps by passing the
--sync (-s) switch to
svk update
In this case, someone else checked in modifications to
both foo.c and bar.c
since the last time you updated, and SVK has updated
your working copy to include those changes.
Let's examine the output of svk update a bit more. When the server sends changes to your working copy, a letter code is displayed next to each item to let you know what actions SVK performed to bring your working copy up-to-date:
U fooFile foo was
Updated (received changes
from the server).
A fooFile or directory foo was
Added to your working
copy.
D fooFile or directory foo was
Deleted from your working
copy.
R fooFile or directory foo was
Replaced in your working
copy; that is, foo was deleted, and a
new item with the same name was added. While they may have
the same name, the depot considers them to be distinct
objects with distinct histories.
G fooFile foo received new changes
from the depot, but your working copy version of the
file had your modifications. The changes did not
intersect, so SVK has successfully
merGed the depot's
changes into the file without a problem.
g fooFile foo received new changes
from the depot, but your working copy version of the
file had your modifications. However the changes were
exactly the same as your local modifications, so SVK has
successfully merged the
depot's changes into the file without changing a
thing.
C fooFile foo received
Conflicting changes from
the server. The changes from the server directly overlap
your own changes to the file. No need to panic, though.
This overlap needs to be resolved by a human (you); we
discuss this situation later in this chapter.
Now you can get to work and make changes in your working copy. It's usually most convenient to decide on a particular change (or set of changes) to make, such as writing a new feature, fixing a bug, etc. The SVK commands that you will use here are svk add, svk delete, svk copy, and svk move. However, if you are merely editing files that are already in SVK, you may not need to use any of these commands until you commit. Changes you can make to your working copy:
This is the simplest sort of change. You don't need to tell SVK that you intend to change a file; just make your changes. SVK will be able to automatically detect which files have been changed.
You can ask SVK to “mark” files and directories for scheduled removal, addition, copying, or moving. While these changes may take place immediately in your working copy, no additions or removals will happen in the depot until you commit them.
To make file changes, use your text editor, word processor, graphics program, or whatever tool you would normally use. SVK handles binary files just as easily as it handles text files—and just as efficiently too.
Here is an overview of the four SVK subcommands that you'll use most often to make tree changes (we'll cover svk import and svk mkdir later).
While you can edit your files with whatever tool you like, you shouldn't change the structure of your working copy without letting SVK know what you're doing. Use the svk copy, svk delete, and svk move commands to change the structure of your working copy, and use the svk add command to place new files and directories under version control.
Schedule file, directory, or symbolic link
foo to be added to the depot.
When you next commit, foo will
become a child of its parent directory. Note that if
foo is a directory, everything
underneath foo will be scheduled
for addition. If you only want to add
foo itself, pass the
--non-recursive (-N)
switch.
Schedule file, directory, or symbolic link
foo to be deleted from the
depot. If foo is a file or
link, it is immediately deleted from your working copy.
If foo is a directory, it is not
deleted, but SVK schedules it for deletion. When
you commit your changes, foo will
be removed from your working copy and the depot.
[8]
Create a new item bar as a
duplicate of foo.
bar is automatically scheduled for
addition. When bar is added to the
depot on the next commit, its copy history is
recorded (as having originally come from
foo). svk copy
does not create intermediate directories unless you pass
it the --parent switch.
This command is exactly the same as running
svk copy foo bar; svk delete foo.
That is, bar is scheduled for
addition as a copy of foo, and
foo is scheduled for removal.
svk move does not create intermediate
directories unless you pass it the
--parent switch.
Once you've finished making changes, you need to commit them to the depot, but before you do so, it's usually a good idea to take a look at exactly what you've changed. By examining your changes before you commit, you can make a more accurate log message. You may also discover that you've inadvertently changed a file, and this gives you a chance to revert those changes before committing. Additionally, this is a good opportunity to review and scrutinize changes before publishing them. You can see exactly what changes you've made by using svk status, svk diff, and svk revert. You will usually use the first two commands to find out what files have changed in your working copy, and then perhaps the third to revert some (or all) of those changes.
You'll probably use the svk status command more than any other SVK command.
If you run svk status at the top of
your working copy with no arguments, it will detect all file
and tree changes you've made. Below are examples of
the different status codes that svk
status can return. (Note that the text following
# is not
actually printed by svk status.)
M bar.c # the content in bar.c has local modifications M baz.c # baz.c has property but no content modifications ? foo.o # svk doesn't manage foo.o ! some_dir # svk manages this, but it's missing or incomplete ~ qux # versioned as file/dir/link, but type has changed I .screenrc # svk doesn't manage this, and is set to ignore it A moved_dir # added with history of where it came from M moved_dir/README # added with history and has local modifications D stuff/fish.c # file is scheduled for deletion A stuff/loot/bloo.h # file is scheduled for addition C stuff/loot/lump.c # file has textual conflicts from an update C stuff/loot/glub.c # file has property conflicts from an update R xyz.c # file is scheduled for replacement
In this output format svk status prints three columns of characters, followed by a whitespace character, followed by a file or directory name. The first column tells the status of a file or directory and/or its contents. The codes printed here are:
A itemThe file, directory, or symbolic link
item has been scheduled for
addition into the depot.
C itemThe file item is in a state
of conflict. That is, changes received from the
server during an update overlap with local changes
that you have in your working copy. You must resolve
this conflict before committing your changes to the
depot.
D itemThe file, directory, or symbolic link
item has been scheduled for
deletion from the depot.
M itemThe contents of the file item
have been modified.
R itemThe file, directory, or symbolic link
item has been scheduled to
replace item in the depot.
This means that the object is first deleted, then
another object of the same name is added, all within a
single revision.
? itemThe file, directory, or symbolic link
item is not under version
control. You can silence the question marks by either
passing the --quiet
(-q) switch to svk
status, or by setting the
svn:ignore property on the parent
directory. For more information on ignored files, see
the section called “”.
! itemThe file, directory, or symbolic link
item is under version control but
is missing or somehow incomplete. The item can be
missing if it's removed using a non-SVK command. In
the case of a directory, it can be incomplete if you
happened to interrupt a checkout or update. A quick
svk revert --recursive item
will refetch the directory from the depot, or
svk revert item will restore a
missing file.
~ itemThe file, directory, or symbolic link
item is in the depot as one
kind of object, but what's actually in your working
copy is some other kind. For example, SVK
might have a file in the depot, but you removed
the file and created a directory in its place, without
using the svk delete or
svk add command.
I itemThe file, directory, or symbolic link
item is not under version control,
and SVK is configured to ignore it during
svk add, svk import
and svk status operations. For more
information on ignored files, see the section called “”. Note that this
symbol only shows up if you pass the
--no-ignore switch to svk
status—otherwise the file would be
ignored and not listed at all!
The second column tells the status of a file or
directory's properties (see the section called “” for more information on
properties). If an M
appears in the second column, then the properties have been
modified, otherwise a whitespace will be printed.
The third column will only show whitespace or a
which means that the file
or directory is scheduled to be added or modified with
additional attached history. This typically happens when you
svk move or svk copy a file
or directory. If you see
A , this means
the item is scheduled for addition-with-history. It could be
a file, or the root of a copied directory.
means the item is part of a subtree scheduled for
addition-with-history, i.e. some parent got copied, and it's
just coming along for the ride.
M means the item
is part of a subtree scheduled for addition-with-history,
and it has local modifications. When you
commit, first the parent will be added-with-history (copied),
which means this file will automatically exist in the copy.
Then the local modifications will be uploaded into the
copy.
If you pass a specific path to svk status, it gives you information about that item alone:
$ svk status stuff/fish.c D stuff/fish.c
svk status also has a
--verbose (-v) switch,
which will show you the status of every
item in your working copy, even if it has not been
changed:
$ svk status --verbose
M 44 23 sally README
44 30 sally INSTALL
M 44 20 harry bar.c
44 18 ira stuff
44 35 harry stuff/trout.c
D 44 19 ira stuff/fish.c
44 21 sally stuff/things
A 0 ? ? stuff/things/bloo.h
44 36 harry stuff/things/gloo.c
This is the “long form” output of svk status. The first column remains the same, but the second column shows the working-revision of the item. The third and fourth columns show the revision in which the item last changed, and who changed it.
None of the above invocations to svk
status show you changes in the depot that your
working copy does not yet have. To do this you need to use
the svk update with the
--check-only (-C) switch,
which will show what would happen if you actually ran
svk update:
$ svk update --check-only Syncing //mirror/fish/trunk(/mirror/fish/trunk) in /Users/sally/svk to 35. C README U stuff/trout.c 1 conflict found.
Notice the two entries.
svk update at this point, you would
receive changes to README
and trout.c. This tells you some very
useful information—you'll need to update and get the
server changes on README before you
commit, or the depot will reject your commit for being
out-of-date. (More on this subject later.)
Another way to examine your changes is with the svk diff command. You can find out exactly how you've modified things by running svk diff with no arguments, which prints out file changes in unified diff format:[9]
$ svk diff
=== bar.c
==================================================================
--- bar.c (revision 3)
bar.c (local)
@@ -1,7 1,12 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
- printf("Sixty-four slices of American Cheese...\n");
printf("Sixty-five slices of American Cheese...\n");
return 0;
}
=== README
==================================================================
--- README (revision 3)
README (local)
@@ -193,3 193,4 @@
Note to self: pick up laundry.
=== stuff/fish.c
==================================================================
--- stuff/fish.c (revision 1)
stuff/fish.c (local)
-Welcome to the file known as 'fish'.
-Information on fish will be here soon.
=== stuff/things/bloo.h
==================================================================
--- stuff/things/bloo.h (revision 8)
stuff/things/bloo.h (local)
Here is a new file to describe
things about bloo.
The svk diff command produces this
output by comparing your working files against the
BASE revision in the depot. Files
scheduled for addition are displayed as all added-text, and
files scheduled for deletion are displayed as all deleted
text.
Output is displayed in unified diff
format. That is, removed lines are prefaced
with a - and added lines are prefaced
with a . svk diff
also prints filename and offset information useful to the
patch program, so you can generate
“patches” by redirecting the diff output to a
file:
$ svk diff > patchfile
You could, for example, email the patch file to another developer for review or testing prior to commit.
However SVK has a much better way to create patches,
which is by using the --patch switch to
svk commit or almost any other command
that modifies the depot. Some notable advantages of this
method over using svk diff include that
patches contain log messages, and tree changes, as well as
binary file changes, while remaining safe to be sent
though email.
$ svk commit --message '' --patch - > patchfile
Now suppose you see the above diff output, and realize
that your changes to README are a
mistake; perhaps you accidentally typed that text into the
wrong file in your editor.
This is a perfect opportunity to use svk revert.
$ svk revert README Reverted README
SVK reverts the file to its pre-modified state by overwriting it with the version your working copy was based on. But also note that svk revert can undo any scheduled operations—for example, you might decide that you don't want to add a new file after all:
$ svk status foo ? foo $ svk add foo A foo $ svk revert foo Reverted foo $ svk status foo ? foo
Or perhaps you mistakenly removed a file from version control:
$ svk status README $ svk delete README D README $ svk revert README Reverted README $ svk status README
We've already seen how svk update --check-only can predict conflicts. Suppose you run svk update and some interesting things occur:
$ svk update Syncing //mirror/fish/trunk(/mirror/fish/trunk) in /Users/sally/svk to 46. U INSTALL G README g foo.c
The U,
G and
g codes are no cause for
concern; those files cleanly absorbed changes from the
depot. The files marked with
U contained no local changes
but were Updated with changes
from the repository. The G
stands for merGed, which
means that the file had local changes to begin with, but the
changes coming from the depot didn't overlap with the local
changes. Finally the g
stands for merged, but in
this case your local copy already contained all the changes
from the depot, so the update operation did not modify the
file.
When a file has local changes, and the changes coming from the depot overlap with the local changes, that file is considered to have a conflict. When this happens SVK gives you a number of choices:
$ svk update Syncing //mirror/fish/trunk(/mirror/fish/trunk) in /Users/sally/svk to 47. Conflict found in bar.c: e)dit, d)iff, m)erge, s)kip, t)heirs, y)ours, h)elp? [e]
At this point you have a number of options.
If SVK doesn't consider the file to be of a mergeable type
then only the t and y
options below are available. (SVK uses the
svn:mime-type property to decide if a
file is capable of contextual, line-based merging. See
the section called “” to
learn more.)
e—editAfter SVK places conflict markers—special strings of text which delimit the “sides” of the conflict—into the file to visibly demonstrate the overlapping areas, the file is opened in an editor window.
If after editing the file all the conflict markers
have been removed SVK will now give you the option to
accept (a) the changes you have made.
All the other options remain available as well, so you
can now view the diffs after your edits, re-edit the
file, etc.
d—diffDiff your file against the merged file.
This runs the internal diff tool or that specified by the
$SVKDIFF environment variable.
dm—diff mergedDiff the base revision of the file against
the merged file.
This runs the internal diff tool or that specified by the
$SVKDIFF environment variable.
dt—diff theirsDiff the base revision of the file against
the revision of the file from the depot.
This runs the internal diff tool or that specified by the
$SVKDIFF environment variable.
dy—diff yoursDiff the base revision of the file against
the local version of the file (or when merging directly to
a depot against revision of the file being merged to).
This runs the internal diff tool or that specified by the
$SVKDIFF environment variable.
m—mergeInvokes a 3 way merging tool or your choice and lets you resolve the conflicts using that. If you have more than one supported merging tool installed SVK will prompt you to pick one like so:
e)dit, d)iff, m)erge, s)kip, t)heirs, y)ours, h)elp? [a] m Multiple merge tools found, choose one: (to skip this question, set the SVKMERGE environment variable to one of them) 1)FileMerge, 2)Vim, q)uit? 2
The merge tools listed will vary based on which ones
you have installed. Like the message says you can set
your $SVKMERGE environment variable
to avoid having to choose a merge tool each time you
select the merge (m) option.
Currently SVK supports the following merge tools:
AraxisMerge, Emacs, FileMerge, GVim, GtkDiff, Guiffy,
KDiff3, Meld, P4WinMerge, TkDiff, TortoiseMerge, Vim,
XXDiff.
Entering q will return you to the
previous menu and let you choose any of the other
options again.
This option is very similar to the edit
(e) option in that, after merging the
file SVK will give you the option to accept
(a) the changes you have made. All
the other options remain available as well, so you can
now view the diffs after your edits, edit the file,
re-merge it, etc.
s[10]—skipSVK places conflict markers—special strings of text which delimit the “sides” of the conflict—into the file to visibly demonstrate the overlapping areas.
The file in question remains in conflict and running
svk status on it will show it as
being in state C or
conflicting. You will need to manually edit the file to
remove the conflicts, and run svk
resolved once you have done so to let SVK know
the file is no longer in conflict.
t—theirsKeep the changes between the base and depot version of the file. This discards the local changes you made to the file (or when merging directly to a depot, discards the changes between the merge base and the revision of the file being merged to).
y—yoursKeep the local version of the file (or when merging directly to a depot keep the revision of the file being merged to). This discards the changes made between the base and the depot version of the file.
h—helpDisplays help explaining all the different options presented.
For example, Sally makes changes to the file
sandwich.txt in the depot. Harry has
just changed the file in his working copy and checked it in.
Sally updates her working copy before checking in and she gets
a conflict. She decides to skip doing the merge and do it by
hand after the fact:
$ svk update Syncing //bread/trunk(/bread/trunk) in /Users/sally/bread to 70. Conflict found in sandwich.txt: e)dit, d)iff, m)erge, s)kip, t)heirs, y)ours, h)elp? [e] s C sandwich.txt 1 conflict found.
At this point, SVK will not
allow you to commit the file sandwich.txt
until the conflict has been resolved.
$ svk commit --message "Add a few more things" 1 conflict detected. Use 'svk resolved' after resolving them.
If you get a conflict, you need to do one of three things:
Merge the conflicted text “by hand” (by examining and editing the conflict markers within the file).
Run svk revert <filename> to throw away all of your local changes.
Once you've resolved the conflict, you need to let SVK know by running svk resolved. After this SVK no longer considers the file to be in a state of conflict.
$ svk resolved sandwich.txt /Users/sally/bread/sandwich.txt marked as resolved.
Merging conflicts by hand can be quite intimidating the first time you attempt it, but with a little practice, it can become as easy as falling off a bike.
Here's an example. Due to a miscommunication, you and
Sally, your collaborator, both edit the file
sandwich.txt at the same time. Sally
commits her changes, and when you go to update your working
copy, you get a conflict and we're going to have to edit
sandwich.txt to resolve the conflicts.
First, let's take a look at the file:
$ cat sandwich.txt Top piece of bread Mayonnaise Lettuce Tomato >>>> YOUR VERSION sandwich.txt 112646405792039 Salami Mortadella Prosciutto Djon Mustard ==== ORIGINAL VERSION sandwich.txt 112646405792039 Provolone Creole Mustard ==== THEIR VERSION sandwich.txt 112646405792039 Provolone Sauerkraut Grilled Chicken Creole Mustard <<<< 112646405792039 Bottom piece of bread
The lines ending in 112646405792039[11] are conflict markers, and are not part of the actual data in conflict. You generally want to ensure that those are removed from the file before your next commit. The text between the first two sets of markers is composed of the changes you made in the conflicting area:
>>>> YOUR VERSION sandwich.txt 112646405792039 Salami Mortadella Prosciutto Djon Mustard ==== ORIGINAL VERSION sandwich.txt 112646405792039
The text between the second and third sets of conflict markers (if any) is the text from the original revision of the file that both your working copy and Sally's version were based on:
==== ORIGINAL VERSION sandwich.txt 112646405792039 Provolone Creole Mustard ==== THEIR VERSION sandwich.txt 112646405792039
The text between the third and fourth sets of conflict markers is the text from Sally's commit:
==== THEIR VERSION sandwich.txt 112646405792039 Provolone Sauerkraut Grilled Chicken Creole Mustard <<<< 112646405792039
Usually you won't want to just delete the conflict markers and Sally's changes—she's going to be awfully surprised when the sandwich arrives and it's not what she wanted. So this is where you pick up the phone or walk across the office and explain to Sally that you can't get sauerkraut from an Italian deli.[12] Once you've agreed on the changes you will check in, edit your file and remove the conflict markers.
Top piece of bread Mayonnaise Lettuce Tomato Provolone Salami Mortadella Prosciutto Grilled Chicken Djon Mustard Bottom piece of bread
Now run svk resolved, and you're ready to commit your changes:
$ svk resolved sandwich.txt $ svk commit -m "Go ahead and use my sandwich, discarding Sally's Sauerkraut."
If you get a conflict and you want to resolve it right
away you can choose the edit option to
edit the file in conflict. SVK will launch your selected
editor with a buffer containing the exact same contents you
would have gotten if you used the skip
option above. Once you finish editing the file and all the
conflict markers have been removed SVK will allow you to
accept the changes you made:
$ svk update Syncing //bread/trunk(/bread/trunk) in /Users/sally/bread to 70. Conflict found in sandwich.txt: e)dit, d)iff, m)erge, s)kip, t)heirs, y)ours, h)elp? [e] e Waiting for editor... Merged sandwich.txt: a)ccept, e)dit, d)iff, m)erge, s)kip, t)heirs, y)ours, h)elp? [a] a G sandwich.txt
If you get a conflict and decide that you want to throw
out your changes, you can merely select the
theirs option when ask to resolve the
conflict:
$ svk update Syncing //bread/trunk(/bread/trunk) in /Users/sally/bread to 70. Conflict found in sandwich.txt: e)dit, d)iff, m)erge, s)kip, t)heirs, y)ours, h)elp? [e] t G sandwich.txt
If you get a conflict and decide that you want to discard
the changes someone else has made to a file you have
modified, you can merely select the
yours option when ask to resolve the
conflict:
$ svk update Syncing //bread/trunk(/bread/trunk) in /Users/sally/bread to 70. Conflict found in sandwich.txt: e)dit, d)iff, m)erge, s)kip, t)heirs, y)ours, h)elp? [e] y G sandwich.txt
It's rare that you would do this blindly (since you
don't know what the changes are that you are discarding) so
in most cases you'd want to look at the changes first. To
do so you would use dt to show their
changes:
$ svk update
Syncing //bread/trunk(/bread/trunk) in /Users/sally/bread to 70.
Conflict found in sandwich.txt:
e)dit, d)iff, m)erge, s)kip, t)heirs, y)ours, h)elp? [e] dt
--- sandwich.txt (BASE)
sandwich.txt (THEIRS)
@@ -3,5 3,7 @@
Lettuce
Tomato
Provolone
Sauerkraut
Grilled Chicken
Creole Mustard
Bottom piece of bread
Conflict found in sandwich.txt:
e)dit, d)iff, m)erge, s)kip, t)heirs, y)ours, h)elp? [e] y
G sandwich.txt
If you get a conflict, and upon examination decide that you want to throw out your changes and start your edits again, just revert your changes:
$ svk revert sandwich.txt Reverted 'sandwich.txt'
Note that when you revert a conflicted file, you don't have to run svk resolved.
Now you're ready to check in your changes. Note that svk resolved, unlike most of the other commands we've dealt with in this chapter, requires an argument. In any case, you want to be careful and only run svk resolved when you're certain that you've fixed the conflict in your file—once you have run svk resolved, SVK will let you commit the file even if it still contains conflict markers.
Finally! Your edits are finished, you've merged all changes from the server, and you're ready to commit your changes to the depot.
The svk commit command sends all of
your changes to the depot. When you commit a change, you
need to supply a log message,
describing your change. Your log message will be attached to
the new revision you create. If your log message is brief,
you may wish to supply it on the command line using the
--message (or -m)
switch:
$ svk commit --message "Corrected number of cheese slices." Committed revision 71.
However, if you've been composing your log message as you
work, you may want to tell SVK to get the message from
a file by passing the filename with the
--file switch:
$ svk commit --file logmsg Committed revision 71.
If you fail to specify either the
--message or --file switch,
then SVK will automatically launch your favorite editor (see
the $SVN_EDITOR section in the section called “”) for composing a
log message.
If you're in your editor writing a commit message and decide that you want to cancel your commit, you can just quit your editor without saving changes. If you've already saved your commit message, simply delete the text and save again.
$ svk commit Waiting for editor... Log message not modified: a)bort, e)dit, c)ommit?a Aborted. $
The depot doesn't know or care if your changes make any sense as a whole; it only checks to make sure that nobody else has changed any of the same files that you did when you weren't looking. If somebody has done that, the entire commit will fail with a message informing you that one or more of your files is out-of-date:
$ svk commit --message "Add more cheese" Transaction is out of date: Out of date: '/bread/trunk/sandwich.txt' in transaction '71-1' Please update checkout first.
At this point, you need to run svk update, deal with any merges or conflicts that result, and attempt your commit again.
That covers the basic work cycle for using SVK. There are many other features in SVK that you can use to manage your repository and working copy, but you can get by quite easily using only the commands that we've discussed so far in this chapter.
As we mentioned earlier, the depot is like a time machine. It keeps a record of every change ever committed, and allows you to explore this history by examining previous versions of files and directories as well as the metadata that accompanies them. With a single SVK command, you can check out the depot (or restore an existing working copy) exactly as it was at any date or revision number in the past. However, sometimes you just want to peer into the past instead of going into the past.
There are several commands that can provide you with historical data from the depot:
Shows you broad information: log messages attached to revisions, and which paths changed in each revision.
Shows you the specific details of how a file changed over time.
This is used to retrieve any file as it existed in a particular revision number and display it on your screen.
Displays the files in a directory for any given revision.
To find information about the history of a file or directory, use the svk log command. svk log will provide you with a record of who made changes to a file or directory, at what revision it changed, the time and date of that revision, and, if it was provided, the log message that accompanied the commit.
$ svk log ---------------------------------------------------------------------- r71: sally | 2005-09-11 12:10:39 -0700 Corrected number of cheese slices. ---------------------------------------------------------------------- r70: sally | 2005-09-11 09:02:15 -0700 Added some Sauerkraut and Grilled Chicken. ---------------------------------------------------------------------- r69: sally | 2005-09-11 09:00:59 -0700 Made a sandwich. ---------------------------------------------------------------------- r68: sally | 2005-09-11 09:00:50 -0700 Directory for svk import. ----------------------------------------------------------------------
Note that the log messages are printed in
reverse chronological order by default.
If you wish to see a different range of revisions in a
particular order, or just a single revision, pass the
--revision (-r)
switch:
$ svk log --revision 5:19 # shows logs 5 through 19 in chronological order $ svk log -r 19:5 # shows logs 5 through 19 in reverse order $ svk log -r 8 # shows log for revision 8
You can also examine the log history of a single file or directory. For example:
$ svk log foo.c … $ svk log //trunk/code/foo.c …
These will display log messages only for those revisions in which the working file (or DEPOTPATH) changed.
If you want even more information about a file or
directory, svk log also takes a
--verbose (-v) switch.
Because SVK allows you to move and copy files and
directories, it is important to be able to track path changes
in the filesystem, so in verbose mode, svk
log will include a list of changed paths in a
revision in its output:
$ svk log -r8 -v // ---------------------------------------------------------------------- r8: sally | 2005-07-20 14:37:21 -0700 Changed paths: A /calc/button.c (from /calc/button2.c:7) D /calc/button2.c Journaled about trip to New York. ----------------------------------------------------------------------
We've already seen svk diff before—it displays file differences in unified diff format; it was used to show the local modifications made to our working copy before committing to the repository.
In fact, it turns out that there are three distinct uses of svk diff:
Examine local changes
Compare your working copy to the depot
Compare depot to depot
As we've seen, invoking svk diff with no switches will compare your working files to the revision from the depot that your working copy is based on:
$ svk diff
=== rules.txt
==================================================================
--- rules.txt (revision 3)
rules.txt (local)
@@ -1,4 1,5 @@
Be kind to others
Freedom = Responsibility
Everything in moderation
-Chew with your mouth open
Chew with your mouth closed
Listen when others are speaking
$
If a single --revision
(-r) number is passed, then your
working copy is compared to the specified revision in the
repository.
$ svk diff --revision 3 rules.txt
=== rules.txt
==================================================================
--- rules.txt (revision 3)
rules.txt (local)
@@ -1,4 1,5 @@
Be kind to others
Freedom = Responsibility
Everything in moderation
-Chew with your mouth open
Chew with your mouth closed
Listen when others are speaking
$
If two revision numbers, separated by a colon, are
passed via --revision
(-r), then the two revisions are directly
compared.
$ svk diff --revision 2:3 rules.txt
=== rules.txt
==================================================================
--- rules.txt (revision 2)
rules.txt (revision 3)
@@ -1,4 1,4 @@
Be kind to others
-Freedom = Chocolate Ice Cream
Freedom = Responsibility
Everything in moderation
Chew with your mouth open
$
Not only can you use svk diff to compare files in your working copy to the depot, but if you supply a depotpath argument, you can examine the differences between items in the depot without even having a working copy. This is especially useful if you wish to inspect changes in a file when you don't have a working copy on your local machine:
$ svk diff --revision 4:5 //example/trunk/text/rules.txt … $
If you want to examine an earlier version of a file and not necessarily the differences between two files, you can use svk cat:
$ svk cat --revision 2 rules.txt Be kind to others Freedom = Chocolate Ice Cream Everything in moderation Chew with your mouth open $
You can also redirect the output directly into a file:
$ svk cat --revision 2 rules.txt > rules.txt.v2 $
You're probably wondering why we don't just use svk update --revision to update the file to the older revision. There are a few reasons why we might prefer to use svk cat.
First, you may want to see the differences between two revisions of a file using an external diff program (perhaps a graphical one, or perhaps your file is in such a format that the output of unified diff is nonsensical). In this case, you'll need to grab a copy of the old revision, redirect it to a file, and pass both that and the file in your working copy to your external diff program.
Sometimes it's easier to look at an older version of a file in its entirety as opposed to just the differences between it and another revision.
The svk list command shows you what files are in a repository directory without actually downloading the files to your local machine:
$ svk list // README bread/ calc/ mirror/ paint/ tags/
If you want a more detailed listing, pass the
--verbose (-v) flag to get
output like this:
$ svk list --verbose //
48 harry 1331 Jul 28 02:07 README
71 sally Sep 11 12:10 bread/
63 sally Aug 25 08:21 calc/
46 svm Jul 23 15:16 mirror/
4 sally Jul 20 09:41 paint/
31 sally Jul 22 09:33 tags/
The columns tell you the revision at which the file or directory was last modified, the user who modified it, the size if it is a file, the date it was last modified, and the item's name.
In addition to all of the above commands, you can use
svk update and svk
checkout with the --revision switch
to take an entire working copy “back in time”
[13]:
$ svk checkout --revision 1729 # Checks out a new working copy at r1729 … $ svk update --revision 1729 # Updates an existing working copy to r1729 …
While not as frequently used as the commands previously discussed in this chapter, you will occasionally need these commands.
When SVK modifies your working copy, it tries to do so as safely as possible. Before changing the working copy, SVK writes its intentions to a log file. Next it executes the commands in the log file to apply the requested change. Finally, SVK removes the log file. Architecturally, this is similar to a journaled filesystem. If a SVK operation is interrupted (if the process is killed, or if the machine crashes, for example), the log files remain on disk. By re-executing the log files, SVK can complete the previously started operation, and your working copy can get itself back into a consistent state.
And this is exactly what svk cleanup does: it searches your working copy and runs any leftover logs, removing locks in the process. If SVK ever tells you that some part of your working copy is “locked”, then this is the command that you should run:
$ svk status /Users/sally/bread/sandwich.txt already locked, use 'svk cleanup' if lock is stalled $ svk cleanup $ svk status M sandwich.txt
The svk import command is a quick way to copy an unversioned tree of files into a depot, creating intermediate directories as necessary.
$ svk import mytree //some/project --message "Initial import" Committed revision 1. Directory /Users/sally/mytree imported to depotpath //some/project as revision 1.
The previous example copied the contents of directory
mytree under the directory
some/project in the default depot:
$ svk list //some/project bar.c foo.c subdir/
Note that after the import is finished, the original tree is not converted into a working copy. To start working, you still need to svk checkout a fresh working copy of the tree.
If you wish to convert the directory from which the import
is done to a working copy right away, simply pass the
--to-checkout (t) switch to
the svk import command:
$ svk import mytree //some/project --message "Initial import" --to-checkout
Committed revision 1.
Directory /Users/sally/mytree imported to depotpath //some/project as revision 1.
$ svk status --verbose mytree
1 1 sally mytree/bar.c
1 1 sally mytree/foo.c
1 1 sally mytree/subdir/quux.h
1 1 sally mytree/subdir
1 1 sally mytree
Now we've covered most of the SVK client commands. Notable exceptions are those dealing with branching and merging (see Chapter 4, Branching and Merging) and properties (see the section called “”). However, you may want to take a moment to skim through Chapter 9, SVK Complete Reference to get an idea of all the many different commands that SVK has—and how you can use them to make your work easier.
[7] SVK can even mirror other types of repositories such as CVS and perforce by using vcp. However as of now this is a read only mirror.
[8] Of course, nothing is ever totally
deleted from the depot—just from the
HEAD of the depot. You can get
back anything you delete by checking out (or updating
your working copy) a revision earlier than the one in
which you deleted it.
[9] SVK uses its internal diff engine,
which produces unified diff format, by default. If you want
diff output in a different format, specify an external diff
program with any options you wish to pass to it by setting
the $SVKDIFF environment variable. For
example, to see local differences in file
foo.c in context output format while
ignoring whitespace changes, you might run
SVKDIFF="diff -bc" svk diff foo.c.
[10] This option is not available when merging directly to a depot
[11] This number is generated at by SVK for each conflict.
[12] And if you ask them for it, they may very well ride you out of town on a rail.
[13] See? We told you that SVK was a time machine.
Table of Contents
Branching, tagging, and merging are concepts common to almost all version control systems. If you're not familiar with these ideas, we provide a good introduction in this chapter. If you are familiar, then hopefully you'll find it interesting to see how SVK implements these ideas.
Branching is a fundamental part of version control. If you're going to allow SVK to manage your data, then this is a feature you'll eventually come to depend on. This chapter assumes that you're already familiar with SVK's basic concepts (Chapter 2, Basic Concepts).
Suppose it's your job to maintain a document for a division in your company, a handbook of some sort. One day a different division asks you for the same handbook, but with a few parts “tweaked” for them, since they do things slightly differently.
What do you do in this situation? You do the obvious thing: you make a second copy of your document, and begin maintaining the two copies separately. As each department asks you to make small changes, you incorporate them into one copy or the other.
You often want to make the same change to both copies. For example, if you discover a typo in the first copy, it's very likely that the same typo exists in the second copy. The two documents are almost the same, after all; they only differ in small, specific ways.
This is the basic concept of a branch—namely, a line of development that exists independently of another line, yet still shares a common history if you look far enough back in time. A branch always begins life as a copy of something, and moves on from there, generating its own history (see Figure 4.1, “Branches of development”).
SVK has commands to help you maintain parallel branches of your files and directories. It allows you to create branches by copying your data, and remembers that the copies are related to one another. It also helps you duplicate changes from one branch to another. Finally, it keep track of which changes are present on each branch so that you only need to resolve conflicting changes once.
At this point, you should understand how each commit creates an entire new filesystem tree (called a “revision”) in the depot. If not, go back and read about revisions in the section called “Revisions”.
For this chapter, we'll go back to the same example from
Chapter 2. Remember that you and your collaborator, Sally, are
sharing a depot that contains two projects,
paint and calc.
Notice that in Figure 4.2, “Starting depot layout”, however, each
project directory now contains subdirectories named
trunk and branches.
The reason for this will soon become clear.
As before, assume that Sally and you both have working
copies of the “calc” project. Specifically, you
each have a working copy of /calc/trunk.
All the files for the project are in this subdirectory rather
than in /calc itself, because your team has
decided that /calc/trunk is where the
“main line” of development is going to take
place.
Let's say that you've been given the task of performing a
radical reorganization of the project. It will take a long time
to write, and will affect all the files in the project. The
problem here is that you don't want to interfere with Sally, who
is in the process of fixing small bugs here and there. She's
depending on the fact that the latest version of the project (in
/calc/trunk) is always usable. If you
start committing your changes bit-by-bit, you'll surely break
things for Sally.
One strategy is to crawl into a hole: you and Sally can stop
sharing information for a week or two. That is, start gutting
and reorganizing all the files in your working copy, but don't
commit or update until you're completely finished with the task.
There are a number of problems with this, though. First, it's
not very safe. Most people like to save their work to the
depot frequently, should something bad accidentally happen
to their working copy. Second, it's not very flexible. If you
do your work on different computers (perhaps you have a working
copy of /calc/trunk on two different
machines), you'll need to manually copy your changes back and
forth, or just do all the work on a single computer. By that
same token, it's difficult to share your changes-in-progress
with anyone else. A common software development “best
practice” is to allow your peers to review your work as you
go. If nobody sees your intermediate commits, you lose
potential feedback. Finally, when you're finished with all your
changes, you might find it very difficult to re-merge your final
work with the rest of the company's main body of code. Sally
(or others) may have made many other changes in the depot
that are difficult to incorporate into your working
copy—especially if you run svk update
after weeks of isolation.
The better solution is to create your own branch, or line of development, in the depot. This allows you to save your half-broken work frequently without interfering with others, yet you can still selectively share information with your collaborators. You'll see exactly how this works later on.
Creating a branch is very simple—you make a copy of
the project in the depot using the svk
copy command. SVK is not only able to copy
single files, but whole directories as well. In this case,
you want to make a copy of the
/calc/trunk directory. Where should the
new copy live? Wherever you wish—it's a matter of
project policy. Let's say that your team has a policy of
creating branches in the /calc/branches
area of the repository, and you want to name your branch
my-calc-branch. You'll want to create a
new directory,
/calc/branches/my-calc-branch, which
begins its life as a copy of
/calc/trunk.
There are two different ways to make a copy. We'll
demonstrate the messy way first, just to make the concept
clear. To begin, check out a working copy of the project's
root directory, /calc:
$ svk checkout //calc bigwc Syncing //calc(/calc) in /Users/sally/bigwc to 78. A bigwc/trunk A bigwc/trunk/button.c A bigwc/trunk/Makefile A bigwc/trunk/integer.c A bigwc/branches
Making a copy is now simply a matter of passing two working-copy paths to the svk copy command:
$ cd bigwc $ svk copy trunk branches/my-calc-branch A branches/my-calc-branch A branches/my-calc-branch/button.c A branches/my-calc-branch/Makefile A branches/my-calc-branch/integer.c $ svk status A branches/my-calc-branch
In this case, the svk copy command
recursively copies the trunk working
directory to a new working directory,
branches/my-calc-branch. As you can see
from the svk status command, the new
directory is now scheduled for addition to the repository.
But also notice the “ ” sign in the third column.
This indicates that the scheduled addition is a
copy of something, not something new.
When you commit your changes, SVK will create
/calc/branches/my-calc-branch in the
depot by copying /calc/trunk:
$ svk commit --message "Creating a private branch of /calc/trunk." Committed revision 79.
And now the easier method of creating a branch, which we should have told you about in the first place: svk copy is able to operate directly on two DEPOTPATHs.
$ svk copy //calc/trunk //calc/branches/my-calc-branch \
--message "Creating a private branch of /calc/trunk."
Committed revision 79.
There's really no difference between these two methods.
Both procedures create a new directory in revision 79, and
the new directory is a copy of
/calc/trunk. This is shown in Figure 4.3, “Depot with new copy”. Notice that the second method,
however, performs an immediate commit.
[14]
It's an easier procedure, because it doesn't require you to
check out a large mirror of the depot. In fact, this
technique doesn't even require you to have a working copy at
all.
Now that you've created a branch of the project, you can check out a new working copy to start using it:
$ svk checkout //calc/branches/my-calc-branch Syncing //calc/branches/my-calc-branch(/calc/branches/my-calc-branch) in /Users/sally/my-calc-branch to 79. A my-calc-branch/button.c A my-calc-branch/Makefile A my-calc-branch/integer.c U my-calc-branch
There's nothing special about this working copy; it simply
mirrors a different directory in the depot. When you
commit changes, however, Sally won't ever see them when she
updates. Her working copy is of
/calc/trunk. (Be sure to read the section called “Switching a Working Copy” later in this chapter: the
svk switch command is an alternate way of
creating a working copy of a branch.)
Let's pretend that a week goes by, and the following commits happen:
You make a change to
/calc/branches/my-calc-branch/integer.c,
which creates revision 80.
You make a change to
/calc/branches/my-calc-branch/button.c,
which creates revision 81.
Sally makes a change to
/calc/trunk/integer.c, which creates
revision 82.
There are now two independent lines of development, shown
in Figure 4.4, “The branching of one file's history”, happening on
integer.c.
Things get interesting when you look at the history of
changes made to your copy of
integer.c:
$ pwd /home/user/my-calc-branch $ svk log --verbose integer.c ---------------------------------------------------------------------- r80: sally | 2005-09-11 13:12:31 -0700 Changed paths: M /calc/branches/my-calc-branch/integer.c frozzled the wazjub. ---------------------------------------------------------------------- r79: sally | 2005-09-11 13:04:13 -0700 Changed paths: A /calc/branches/my-calc-branch (from /calc/trunk:78) Creating a private branch of /calc/trunk. ----------------------------------------------------------------------
Notice that SVK is only tracing the history of your
branch's integer.c to the point where it
was copied. It shows the creation of the branch as an event in the
history, because integer.c was implicitly
copied when all of /calc/trunk/ was
copied. If you want to see the history prior to the copy you
need to supply the --cross
(-x) switch to the svk log
command:
$ svk log --verbose --cross integer.c ---------------------------------------------------------------------- r80: harry | 2005-09-11 13:12:31 -0700 Changed paths: M /calc/branches/my-calc-branch/integer.c frozzled the wazjub. ---------------------------------------------------------------------- r79: harry | 2005-09-11 13:04:13 -0700 Changed paths: A /calc/branches/my-calc-branch (from /calc/trunk:78) Creating a private branch of /calc/trunk. ---------------------------------------------------------------------- r45: sally | 2005-07-23 14:49:11 -0700 Changed paths: M /calc/trunk/integer.c * integer.c: changed a docstring. ---------------------------------------------------------------------- r2: sally | 2005-07-20 09:40:50 -0700 Changed paths: A /calc/trunk/Makefile A /calc/trunk/button.c A /calc/trunk/integer.c Initial import of calc. ----------------------------------------------------------------------
Now look what happens when Sally runs the same command on her copy of the file:
$ pwd /home/sally/calc $ svk log --verbose integer.c ---------------------------------------------------------------------- r82: sally | 2005-09-11 14:03:03 -0700 Changed paths: M /calc/trunk/integer.c * integer.c: fix a bunch of spelling errors. ---------------------------------------------------------------------- r45: sally | 2005-07-23 14:49:11 -0700 Changed paths: M /calc/trunk/integer.c * integer.c: changed a docstring. ---------------------------------------------------------------------- r2: sally | 2005-07-20 09:40:50 -0700 Changed paths: A /calc/trunk/Makefile A /calc/trunk/button.c A /calc/trunk/integer.c Initial import of calc. ----------------------------------------------------------------------
Sally sees her own revision 82 change, but not the change you made in revision 80. As far as SVK is concerned, these two commits affected different files in different depot locations. However, SVK does show that the two files share a common history. Before the branch-copy was made in revision 79, they used to be the same file. That's why you and Sally both see the changes made in revisions 45 and 2.
There are two important lessons that you should remember from this section.
Unlike many other version control systems, SVK's branches exist as normal filesystem directories in the depot, not in an extra dimension. These directories just happen to carry some extra historical information.
SVK has no internal concept of a branch—only copies. When you copy a directory, the resulting directory is only a “branch” because you attach that meaning to it. You may think of the directory differently, or treat it differently, but to SVK it's just an ordinary directory that happens to have been created by copying.
Now you and Sally are working on parallel branches of the project: you're working on a private branch, and Sally is working on the trunk, or main line of development.
For projects that have a large number of contributors, it's common for most people to have working copies of the trunk. Whenever someone needs to make a long-running change that is likely to disrupt the trunk, a standard procedure is to create a private branch and commit changes there until all the work is complete.
So, the good news is that you and Sally aren't interfering with each other. The bad news is that it's very easy to drift too far apart. Remember that one of the problems with the “crawl in a hole” strategy is that by the time you're finished with your branch, it may be near-impossible to merge your changes back into the trunk without a huge number of conflicts.
Instead, you and Sally might continue to share changes as you work. It's up to you to decide which changes are worth sharing; SVK gives you the ability to selectively “copy” changes between branches. And when you're completely finished with your branch, your entire set of branch changes can be copied back into the trunk.
In the previous section, we mentioned that both you and
Sally made changes to integer.c on
different branches. If you look at Sally's log message for
revision 344, you can see that she fixed some spelling errors.
No doubt, your copy of the same file still has the same spelling
errors. It's likely that your future changes to this file will
be affecting the same areas that have the spelling errors, so
you're in for some potential conflicts when you merge your
branch someday. It's better, then, to receive Sally's change
now, before you start working too heavily
in the same places.
It's time to use the svk merge command. This command, it turns out, is a very close cousin to the svk diff command (which you read about in Chapter 3). Both commands are able to compare any two objects in the repository and describe the differences. For example, you can ask svk diff to show you the exact change made by Sally in revision 82:
$ svk diff -r 81:82 //calc/trunk
=== integer.c
===================================================================
--- integer.c (revision 81)
integer.c (revision 82)
@@ -147,7 147,7 @@
case 6: sprintf(info->operating_system, "HPFS (OS/2 or NT)"); break;
case 7: sprintf(info->operating_system, "Macintosh"); break;
case 8: sprintf(info->operating_system, "Z-System"); break;
- case 9: sprintf(info->operating_system, "CPM"); break;
case 9: sprintf(info->operating_system, "CP/M"); break;
case 10: sprintf(info->operating_system, "TOPS-20"); break;
case 11: sprintf(info->operating_system, "NTFS (Windows NT)"); break;
case 12: sprintf(info->operating_system, "QDOS"); break;
@@ -164,7 164,7 @@
low = (unsigned short) read_byte(gzfile); /* read LSB */
high = (unsigned short) read_byte(gzfile); /* read MSB */
high = high << 8; /* interpret MSB correctly */
- total = low high; /* add them togethe for correct total */
total = low high; /* add them together for correct total */
info->extra_header = (unsigned char *) my_malloc(total);
fread(info->extra_header, total, 1, gzfile);
@@ -241,7 241,7 @@
Store the offset with ftell() ! */
if ((info->data_offset = ftell(gzfile))== -1) {
- printf("error: ftell() retturned -1.\n");
printf("error: ftell() returned -1.\n");
exit(1);
}
@@ -249,7 249,7 @@
printf("I believe start of compressed data is %u\n", info->data_offset);
#endif
- /* Set postion eight bytes from the end of the file. */
/* Set position eight bytes from the end of the file. */
if (fseek(gzfile, -8, SEEK_END)) {
printf("error: fseek() returned non-zero\n");
The svk merge command is almost exactly the same. Instead of printing the differences to your terminal, however, it applies them directly to your working copy as local modifications:
$ svk merge -r 81:82 //calc/trunk U integer.c $ svk status M integer.c
The output of svk merge shows that your
copy of integer.c was patched. It now
contains Sally's change—the change has been
“copied” from the trunk to your working copy of
your private branch, and now exists as a local modification.
At this point, it's up to you to review the local modification
and make sure it works correctly.
In another scenario, it's possible that things may not have
gone so well, and that integer.c may have
entered a conflicted state. You might need to resolve the
conflict using standard procedures (see Chapter 3), or if you
decide that the merge was a bad idea altogether, simply give up
and svk revert the local change.
But assuming that you've reviewed the merged change, you can svk commit the change as usual. At that point, the change has been merged into your repository branch. In version control terminology, this act of copying changes between branches is commonly called porting changes.
When you commit the local modification, make sure your log message mentions that you're porting a specific change from one branch to another. For example:
$ svk commit -m "integer.c: ported r344 (spelling fixes) from trunk." Sending integer.c Transmitting file data . Committed revision 360.
As you'll see in the next sections, this is a very important “best practice” to follow.
A word of warning: while svk diff and svk merge are very similar in concept, they do have different syntax in many cases. Be sure to read about them in Chapter 9 for details, or ask svk help. For example, svk merge requires a working-copy path as a target, i.e. a place where it should apply the tree-changes. If the target isn't specified, it assumes you are trying to perform one of the following common operations:
You want to merge directory changes into your current working directory.
You want to merge the changes in a specific file into a file by the same name which exists in your current working directory.
If you are merging a directory and haven't specified a target path, svk merge assumes the first case above and tries to apply the changes into your current directory. If you are merging a file, and that file (or a file by the same name) exists in your current working directory, svk merge assumes the second case and tries to apply the changes to a local file with the same name.
If you want changes applied somewhere else, you'll need to say so. For example, if you're sitting in the parent directory of your working copy, you'll have to specify the target directory to receive the changes:
$ svk merge -r 343:344 http://svk.example.com/repos/calc/trunk my-calc-branch U my-calc-branch/integer.c
You've now seen an example of the svk merge command, and you're about to see several more. If you're feeling confused about exactly how merging works, you're not alone. Many users (especially those new to version control) are initially perplexed about the proper syntax of the command, and about how and when the feature should be used. But fear not, this command is actually much simpler than you think! There's a very easy technique for understanding exactly how svk merge behaves.
The main source of confusion is the name of the command. The term “merge” somehow denotes that branches are combined together, or that there's some sort of mysterious blending of data going on. That's not the case. A better name for the command might have been svk diff-and-apply, because that's all that happens: two repository trees are compared, and the differences are applied to a working copy.
The command takes three arguments:
An initial repository tree (often called the left side of the comparison),
An final repository tree (often called the right side of the comparison),
A working copy to accept the differences as local changes (often called the target of the merge).
Once these three arguments are specified, the two trees are compared, and the resulting differences are applied to the target working copy as local modifications. When the command is done, the results are no different than if you had hand-edited the files, or run various svk add or svk delete commands yourself. If you like the results, you can commit them. If you don't like the results, you can simply svk revert all of the changes.
The syntax of svk merge allows you to specify the three necessary arguments rather flexibly. Here are some examples:
$ svk merge http://svk.example.com/repos/branch1@150 \
http://svk.example.com/repos/branch2@212 \
my-working-copy
$ svk merge -r 100:200 http://svk.example.com/repos/trunk my-working-copy
$ svk merge -r 100:200 http://svk.example.com/repos/trunk
The first syntax lays out all three arguments explictly, naming each tree in the form URL@REV and naming the working copy target. The second syntax can be used as a shorthand for situations when you're comparing two different revisions of the same URL. The last syntax shows how the working-copy argument is optional; if omitted, it defaults to the current directory.
Merging changes sounds simple enough, but in practice it can become a headache. The problem is that if you repeatedly merge changes from one branch to another, you might accidentally merge the same change twice. When this happens, sometimes things will work fine. When patching a file, Subversion typically notices if the file already has the change, and does nothing. But if the already-existing change has been modified in any way, you'll get a conflict.
Ideally, your version control system should prevent the double-application of changes to a branch. It should automatically remember which changes a branch has already received, and be able to list them for you. It should use this information to help automate merges as much as possible.
Unfortunately, Subversion is not such a system. Like CVS, Subversion does not yet record any information about merge operations. When you commit local modifications, the repository has no idea whether those changes came from running svk merge, or from just hand-editing the files.
What does this mean to you, the user? It means that until the day Subversion grows this feature, you'll have to track merge information yourself. The best place to do this is in the commit log-message. As demonstrated in the earlier example, it's recommended that your log-message mention a specific revision number (or range of revisions) that are being merged into your branch. Later on, you can run svk log to review which changes your branch already contains. This will allow you to carefully construct a subsequent svk merge command that won't be redundant with previously ported changes.
In the next section, we'll show some examples of this technique in action.
Because merging only results in local modifications, it's not usually a high-risk operation. If you get the merge wrong the first time, simply svk revert the changes and try again.
It's possible, however, that your working copy might already have local modifications. The changes applied by a merge will be mixed with your pre-existing ones, and running svk revert is no longer an option. The two sets of changes may be impossible to separate.
In cases like this, people take comfort in being able to
predict or examine merges before they happen. One simple
way to do that is to run svk diff with
the same arguments you plan to pass to svk
merge, as we already showed in our first example
of merging. Another method of previewing is to pass the
--dry-run option to the merge
command:
$ svk merge --dry-run -r 343:344 http://svk.example.com/repos/calc/trunk U integer.c $ svk status # nothing printed, working copy is still unchanged.
The --dry-run option doesn't actually
apply any local changes to the working copy. It only shows
status codes that would be printed in a
real merge. It's useful for getting a “high
level” preview of the potential merge, for those
times when running svk diff gives too
much detail.
Just like the svk update command, svk merge applies changes to your working copy. And therefore it's also capable of creating conflicts. The conflicts produced by svk merge, however, are sometimes different, and this section explains those differences.
To begin with, assume that your working copy has no local edits. When you svk update to a particular revision, the changes sent by the server will always apply “cleanly” to your working copy. The server produces the delta by comparing two trees: a virtual snapshot of your working copy, and the revision tree you're interested in. Because the left-hand side of the comparison is exactly equal to what you already have, the delta is guaranteed to correctly convert your working copy into the right-hand tree.
But svk merge has no such guarantees and can be much more chaotic: the user can ask the server to compare any two trees at all, even ones that are unrelated to the working copy! This means there's large potential for human error. Users will sometimes compare the wrong two trees, creating a delta that doesn't apply cleanly. svk merge will do its best to apply as much of the delta as possible, but some parts may be impossible. Just like the Unix patch command sometimes complains about “failed hunks”, svk merge will complain about “skipped targets”:
$ svk merge -r 1288:1351 http://svk.example.com/repos/branch U foo.c U bar.c Skipped missing target: 'baz.c' U glub.c C glorb.h $
In the previous example it might be the case that
baz.c exists in both snapshots of the
branch being compared, and the resulting delta wants to
change the file's contents, but the file doesn't exist in
the working copy. Whatever the case, the
“skipped” message means that the user is most
likely comparing the wrong two trees; they're the classic
sign of driver error. When this happens, it's easy to
recursively revert all the changes created by the merge
(svk revert --recursive), delete any
unversioned files or directories left behind after the
revert, and re-run svk merge with
different arguments.
Also notice that the previous example shows a conflict
happening on glorb.h. We already
stated that the working copy has no local edits: how can a
conflict possibly happen? Again, because the user can use
svk merge to define and apply any old
delta to the working copy, that delta may contain textual
changes that don't cleanly apply to a working file, even if
the file has no local modifications.
Another small difference between svk
update and svk merge are the
names of the full-text files created when a conflict
happens. In the section called “Resolve Conflicts (Merging Others' Changes)”, we saw
that an update produces files named
filename.mine,
filename.rOLDREV, and
filename.rNEWREV. When svk
merge produces a conflict, though, it creates
three files named filename.working,
filename.left, and
filename.right. In this case, the
terms “left” and “right” are
describing which side of the double-tree comparison the file
came from. In any case, these differing names will help you
distinguish between conflicts that happened as a result of an
update versus ones that happened as a result of a
merge.
When 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, then 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 svk diff and
svk merge. The former command ignores
ancestry, while the latter command is quite sensitive to it.
For example, if you asked svk 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 svk
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 svk 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 the section called “”). If you asked svk 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 svk
merge to do a path-based comparison only, ignoring
any relations between files and directories. Add the
--ignore-ancestry option to your merge
command, and it will behave just like svk
diff. (And conversely, the
--notice-ancestry option will cause
svk diff to behave like the merge
command.)
There are many different uses for branching and svk merge, and this section describes the most common ones you're likely to run into.
To complete our running example, we'll move forward in time. Suppose several days have passed, and many changes have happened on both the trunk and your private branch. Suppose that you've finished working on your private branch; the feature or bug fix is finally complete, and now you want to merge all of your branch changes back into the trunk for others to enjoy.
So how do we use svk merge in this
scenario? Remember that this command compares two trees, and
applies the differences to a working copy. So to receive the
changes, you need to have a working copy of the trunk. We'll
assume that either you still have your original one lying
around (fully updated), or that you recently checked out a
fresh working copy of /calc/trunk.
But which two trees should be compared? At first glance, the answer may seem obvious: just compare the latest trunk tree with your latest branch tree. But beware—this assumption is wrong, and has burned many a new user! Since svk merge operates like svk diff, comparing the latest trunk and branch trees will not merely describe the set of changes you made to your branch. Such a comparison shows too many changes: it would not only show the addition of your branch changes, but also the removal of trunk changes that never happened on your branch.
To express only the changes that happened on your branch,
you need to compare the initial state of your branch to its
final state. Using svk log on your branch,
you can see that your branch was created in revision 341. And
the final state of your branch is simply a matter of using the
HEAD revision. That means you want to
compare revisions 341 and HEAD of your
branch directory, and apply those differences to a working
copy of the trunk.
A nice way of finding the revision in which a branch was
created (the “base” of the branch) is to use the
--stop-on-copy option to svk
log. The log subcommand will normally show every
change ever made to the branch, including tracing back
through the copy which created the branch. So normally,
you'll see history from the trunk as well. The
--stop-on-copy will halt log output as soon
as svk log detects that its target was
copied or renamed.
So in our continuing example,
$ svk log --verbose --stop-on-copy \
http://svk.example.com/repos/calc/branches/my-calc-branch
…
------------------------------------------------------------------------
r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
A /calc/branches/my-calc-branch (from /calc/trunk:340)
$
As expected, the final revision printed by this command
is the revision in which my-calc-branch
was created by copying.
Here's the final merging procedure, then:
$ cd calc/trunk $ svk update At revision 405. $ svk merge -r 341:405 http://svk.example.com/repos/calc/branches/my-calc-branch U integer.c U button.c U Makefile $ svk status M integer.c M button.c M Makefile # ...examine the diffs, compile, test, etc... $ svk commit -m "Merged my-calc-branch changes r341:405 into the trunk." Sending integer.c Sending button.c Sending Makefile Transmitting file data ... Committed revision 406.
Again, notice that the commit log message very specifically mentions the range of changes that was merged into the trunk. Always remember to do this, because it's critical information you'll need later on.
For example, suppose you decide to keep working on your
branch for another week, in order to complete an enhancement
to your original feature or bug fix. The repository's
HEAD revision is now 480, and you're ready
to do another merge from your private branch to the trunk.
But as discussed in the section called “Best Practices for Merging”, you
don't want to merge the changes you've already merged before;
you only want to merge everything “new” on your
branch since the last time you merged. The trick is to figure
out what's new.
The first step is to run svk log on the trunk, and look for a log message about the last time you merged from the branch:
$ cd calc/trunk $ svk log … ------------------------------------------------------------------------ r406 | user | 2004-02-08 11:17:26 -0600 (Sun, 08 Feb 2004) | 1 line Merged my-calc-branch changes r341:405 into the trunk. ------------------------------------------------------------------------ …
Aha! Since all branch-changes that happened between
revisions 341 and 405 were previously merged to the trunk as
revision 406, you now know that you want to merge only the
branch changes after that—by comparing revisions 406 and
HEAD.
$ cd calc/trunk $ svk update At revision 480. # We notice that HEAD is currently 480, so we use it to do the merge: $ svk merge -r 406:480 http://svk.example.com/repos/calc/branches/my-calc-branch U integer.c U button.c U Makefile $ svk commit -m "Merged my-calc-branch changes r406:480 into the trunk." Sending integer.c Sending button.c Sending Makefile Transmitting file data ... Committed revision 481.
Now the trunk contains the complete second wave of changes made to the branch. At this point, you can either delete your branch (we'll discuss this later on), or continue working on your branch and repeat this procedure for subsequent merges.
Another common use for svk merge is to
roll back a change that has already been committed. Suppose
you're working away happily on a working copy of
/calc/trunk, and you discover that the
change made way back in revision 303, which changed
integer.c, is completely wrong. It never
should have been committed. You can use svk
merge to “undo” the change in your
working copy, and then commit the local modification to the
repository. All you need to do is to specify a
reverse difference:
$ svk merge -r 303:302 http://svk.example.com/repos/calc/trunk U integer.c $ svk status M integer.c $ svk diff … # verify that the change is removed … $ svk commit -m "Undoing change committed in r303." Sending integer.c Transmitting file data . Committed revision 350.
One way to think about a repository revision is as a
specific group of changes (some version control systems call
these changesets). By using the
-r switch, you can ask svk
merge to apply a changeset, or whole range of
changesets, to your working copy. In our case of undoing a
change, we're asking svk merge to apply
changeset #303 to our working copy
backwards.
Keep in mind that rolling back a change like this is just
like any other svk merge operation, so you
should use svk status and svk
diff to confirm that your work is in the state you
want it to be in, and then use svk commit
to send the final version to the repository. After
committing, this particular changeset is no longer reflected
in the HEAD revision.
Again, you may be thinking: well, that really didn't undo
the commit, did it? The change still exists in revision 303.
If somebody checks out a version of the
calc project between revisions 303 and
349, they'll still see the bad change, right?
Yes, that's true. When we talk about
“removing” a change, we're really talking about
removing it from HEAD. The original change
still exists in the repository's history. For most
situations, this is good enough. Most people are only
interested in tracking the HEAD of a
project anyway. There are special cases, however, where you
really might want to destroy all evidence of the commit.
(Perhaps somebody accidentally committed a confidential
document.) This isn't so easy, it turns out, because
Subversion was deliberately designed to never lose
information. Revisions are immutable trees which build upon
one another. Removing a revision from history would cause a
domino effect, creating chaos in all subsequent revisions and
possibly invalidating all working copies.
[16]
The great thing about version control systems is that
information is never lost. Even when you delete a file or
directory, it may be gone from the HEAD
revision, but the object still exists in earlier revisions.
One of the most common questions new users ask is, “How
do I get my old file or directory back?”
The first step is to define exactly which item you're trying to resurrect. Here's a useful metaphor: you can think of every object in the repository as existing in a sort of two-dimensional coordinate system. The first coordinate is a particular revision tree, and the second coordinate is a path within that tree. So every version of your file or directory can be defined by a specific coordinate pair.
Subversion has no Attic directory
like CVS does,
[17]
so you need to use svk
log to discover the exact coordinate pair you wish
to resurrect. A good strategy is to run svk log
--verbose in a directory which used to contain your
deleted item. The --verbose option shows a
list of all changed items in each revision; all you need to do
is find the revision in which you deleted the file or
directory. You can do this visually, or by using another tool
to examine the log output (via grep, or
perhaps via an incremental search in an editor).
$ cd parent-dir $ svk log --verbose … ------------------------------------------------------------------------ r808 | joe | 2003-12-26 14:29:40 -0600 (Fri, 26 Dec 2003) | 3 lines Changed paths: D /calc/trunk/real.c M /calc/trunk/integer.c Added fast fourier transform functions to integer.c. Removed real.c because code now in double.c. …
In the example, we're assuming that you're looking for a
deleted file real.c. By looking through
the logs of a parent directory, you've spotted that this file
was deleted in revision 808. Therefore, the last version of
the file to exist was in the revision right before that.
Conclusion: you want to resurrect the path
/calc/trunk/real.c from revision
807.
That was the hard part—the research. Now that you know what you want to restore, you have two different choices.
One option is to use svk merge to apply
revision 808 “in reverse”. (We've already
discussed how to undo changes, see the section called “Undoing Changes”.) This would have the effect of
re-adding real.c as a local modification.
The file would be scheduled for addition, and after a commit,
the file would again exist in HEAD.
In this particular example, however, this is probably not
the best strategy. Reverse-applying revision 808 would not
only schedule real.c for addition, but
the log message indicates that it would also undo certain
changes to integer.c, which you don't
want. Certainly, you could reverse-merge revision 808 and
then svk revert the local modifications to
integer.c, but this technique doesn't
scale well. What if there were 90 files changed in revision
808?
A second, more targeted strategy is not to use svk merge at all, but rather the svk copy command. Simply copy the exact revision and path “coordinate pair” from the repository to your working copy:
$ svk copy --revision 807 \
http://svk.example.com/repos/calc/trunk/real.c ./real.c
$ svk status
A real.c
$ svk commit -m "Resurrected real.c from revision 807, /calc/trunk/real.c."
Adding real.c
Transmitting file data .
Committed revision 1390.
The plus sign in the status output indicates that the item
isn't merely scheduled for addition, but scheduled for
addition “with history”. Subversion remembers
where it was copied from. In the future, running svk
log on this file will traverse back through the
file's resurrection and through all the history it had prior
to revision 807. In other words, this new
real.c isn't really new; it's a direct
descendant of the original, deleted file.
Although our example shows us resurrecting a file, note that these same techniques work just as well for resurrecting deleted directories.
Version control is most often used for software development, so here's a quick peek at two of the most common branching/merging patterns used by teams of programmers. If you're not using Subversion for software development, feel free to skip this section. If you're a software developer using version control for the first time, pay close attention, as these patterns are often considered best practices by experienced folk. These processes aren't specific to Subversion; they're applicable to any version control system. Still, it may help to see them described in Subversion terms.
Most software has a typical lifecycle: code, test, release, repeat. There are two problems with this process. First, developers need to keep writing new features while quality-assurance teams take time to test supposedly-stable versions of the software. New work cannot halt while the software is tested. Second, the team almost always needs to support older, released versions of software; if a bug is discovered in the latest code, it most likely exists in released versions as well, and customers will want to get that bugfix without having to wait for a major new release.
Here's where version control can help. The typical procedure looks like this:
Developers commit all new work to the
trunk.
Day-to-day changes are committed to
/trunk: new features, bugfixes, and
so on.
The trunk is copied to a
“release” branch.
When the team thinks the software is ready for release
(say, a 1.0 release), then /trunk
might be copied to
/branches/1.0.
Teams continue to work in parallel.
One team begins rigorous testing of the release branch,
while another team continues new work (say, for version
2.0) on /trunk. If bugs are
discovered in either location, fixes are ported back and
forth as necessary. At some point, however, even that
process stops. The branch is “frozen” for
final testing right before a release.
The branch is tagged and released.
When testing is complete,
/branches/1.0 is copied to
/tags/1.0.0 as a reference
snapshot. The tag is packaged and released to
customers.
The branch is maintained over time.
While work continues on /trunk for
version 2.0, bugfixes continue to be ported from
/trunk to
/branches/1.0. When enough
bugfixes have accumulated, management may decide to do a
1.0.1 release: /branches/1.0 is
copied to /tags/1.0.1, and the tag
is packaged and released.
This entire process repeats as the software matures: when the 2.0 work is complete, a new 2.0 release branch is created, tested, tagged, and eventually released. After some years, the repository ends up with a number of release branches in “maintenance” mode, and a number of tags representing final shipped versions.
A feature branch is the sort of
branch that's been the dominant example in this chapter, the
one you've been working on while Sally continues to work on
/trunk. It's a temporary branch
created to work on a complex change without interfering with
the stability of /trunk. Unlike
release branches (which may need to be supported forever),
feature branches are born, used for a while, merged back to
the trunk, then ultimately deleted. They have a finite span
of usefulness.
Again, project policies vary widely concerning exactly
when it's appropriate to create a feature branch. Some
projects never use feature branches at all: commits to
/trunk are a free-for-all. The
advantage to this system is that it's simple—nobody
needs to learn about branching or merging. The disadvantage
is that the trunk code is often unstable or unusable. Other
projects use branches to an extreme: no change is
ever committed to the trunk directly.
Even the most trivial changes are created on a short-lived
branch, carefully reviewed and merged to the trunk. Then
the branch is deleted. This system guarantees an
exceptionally stable and usable trunk at all times, but at
the cost of tremendous process overhead.
Most projects take a middle-of-the-road approach. They
commonly insist that /trunk compile and
pass regression tests at all times. A feature branch is
only required when a change requires a large number of
destabilizing commits. A good rule of thumb is to ask this
question: if the developer worked for days in isolation and
then committed the large change all at once (so that
/trunk were never destabilized), would
it be too large a change to review? If the answer to that
question is “yes”, then the change should be
developed on a feature branch. As the developer commits
incremental changes to the branch, they can be easily
reviewed by peers.
Finally, there's the issue of how to best keep a feature branch in “sync” with the trunk as work progresses. As we mentioned earlier, there's a great risk to working on a branch for weeks or months; trunk changes may continue to pour in, to the point where the two lines of development differ so greatly that it may become a nightmare trying to merge the branch back to the trunk.
This situation is best avoided by regularly merging trunk changes to the branch. Make up a policy: once a week, merge the last week's worth of trunk changes to the branch. Take care when doing this; the merging needs to be hand-tracked to avoid the problem of repeated merges (as described in the section called “Tracking Merges Manually”). You'll need to write careful log messages detailing exactly which revision ranges have been been merged already (as demonstrated in the section called “Merging a Whole Branch to Another”). It may sound intimidating, but it's actually pretty easy to do.
At some point, you'll be ready to merge the “synchronized” feature branch back to the trunk. To do this, begin by doing a final merge of the latest trunk changes to the branch. When that's done, the latest versions of branch and trunk will be absolutely identical except for your branch changes. So in this special case, you would merge by comparing the branch with the trunk:
$ cd trunk-working-copy
$ svk update
At revision 1910.
$ svk merge http://svk.example.com/repos/calc/trunk@1910 \
http://svk.example.com/repos/calc/branches/mybranch@1910
U real.c
U integer.c
A newdirectory
A newdirectory/newfile
…
By comparing the HEAD revision of the
trunk with the HEAD revision of the
branch, you're defining a delta that describes only the
changes you made to the branch; both lines of development
already have all of the trunk changes.
Another way of thinking about this pattern is that your weekly sync of trunk to branch is analogous to running svk update in a working copy, while the final merge step is analogous to running svk commit from a working copy. After all, what else is a working copy but a very shallow private branch? It's a branch that's only capable of storing one change at a time.
The svk switch command transforms an
existing working copy into a different branch. While this
command isn't strictly necessary for working with branches, it
provides a nice shortcut to users. In our earlier example,
after creating your private branch, you checked out a fresh
working copy of the new repository directory. Instead, you can
simply ask Subversion to change your working copy of
/calc/trunk to mirror the new branch
location:
$ cd calc $ svk info | grep URL URL: http://svk.example.com/repos/calc/trunk $ svk switch http://svk.example.com/repos/calc/branches/my-calc-branch U integer.c U button.c U Makefile Updated to revision 341. $ svk info | grep URL URL: http://svk.example.com/repos/calc/branches/my-calc-branch
After “switching” to the branch, your working copy is no different than what you would get from doing a fresh checkout of the directory. And it's usually more efficient to use this command, because often branches only differ by a small degree. The server sends only the minimal set of changes necessary to make your working copy reflect the branch directory.
The svk switch command also takes a
--revision (-r) option, so you
need not always move your working copy to the “tip”
of the branch.
Of course, most projects are more complicated than our
calc example, containing multiple
subdirectories. Subversion users often follow a specific
algorithm when using branches:
Copy the project's entire “trunk” to a new branch directory.
Switch only part of the trunk working copy to mirror the branch.
In other words, if a user knows that the branch-work only needs to happen on a specific subdirectory, they use svk switch to move only that subdirectory to the branch. (Or sometimes users will switch just a single working file to the branch!) That way, they can continue to receive normal “trunk” updates to most of their working copy, but the switched portions will remain immune (unless someone commits a change to their branch). This feature adds a whole new dimension to the concept of a “mixed working copy”—not only can working copies contain a mixture of working revisions, but a mixture of repository locations as well.
If your working copy contains a number of switched subtrees from different repository locations, it continues to function as normal. When you update, you'll receive patches to each subtree as appropriate. When you commit, your local changes will still be applied as a single, atomic change to the repository.
Note that while it's okay for your working copy to reflect a mixture of repository locations, these locations must all be within the same repository. Subversion repositories aren't yet able to communicate with one another; that's a feature planned beyond Subversion 1.0.[18]
Because svk switch is essentially a variant of svk update, it shares the same behaviors; any local modifications in your working copy are preserved when new data arrives from the repository. This allows you to perform all sorts of clever tricks.
For example, suppose you have a working copy of
/calc/trunk and make a number of changes to
it. Then you suddenly realize that you meant to make the
changes to a branch instead. No problem! When you svk
switch your working copy to the branch, the local
changes will remain. You can then test and commit them to the
branch.
Another common version control concept is a tag. A tag is just a “snapshot” of a project in time. In Subversion, this idea already seems to be everywhere. Each repository revision is exactly that—a snapshot of the filesystem after each commit.
However, people often want to give more human-friendly names
to tags, like release-1.0. And they want to
make snapshots of smaller subdirectories of the filesystem.
After all, it's not so easy to remember that release-1.0 of a
piece of software is a particular subdirectory of revision
4822.
Once again, svk copy comes to the
rescue. If you want to create a snapshot of
/calc/trunk exactly as it looks in the
HEAD revision, then make a copy of it:
$ svk copy http://svk.example.com/repos/calc/trunk \
http://svk.example.com/repos/calc/tags/release-1.0 \
-m "Tagging the 1.0 release of the 'calc' project."
Committed revision 351.
This example assumes that a
/calc/tags directory already exists. (If it
doesn't, see svk mkdir).
After the copy completes, the new
release-1.0 directory is forever a
snapshot of how the project looked in the
HEAD revision at the time you made the
copy. Of course you might want to be more precise about
exactly which revision you copy, in case somebody else may
have committed changes to the project when you weren't
looking. So if you know that revision 350 of
/calc/trunk is exactly the snapshot you
want, you can specify it by passing -r 350 to
the svk copy command.
But wait a moment: isn't this tag-creation procedure the same procedure we used to create a branch? Yes, in fact, it is. In Subversion, there's no difference between a tag and a branch. Both are just ordinary directories that are created by copying. Just as with branches, the only reason a copied directory is a “tag” is because humans have decided to treat it that way: as long as nobody ever commits to the directory, it forever remains a snapshot. If people start committing to it, it becomes a branch.
If you are administering a repository, there are two approaches you can take to managing tags. The first approach is “hands off”: as a matter of project policy, decide where your tags will live, and make sure all users know how to treat the directories they copy in there. (That is, make sure they know not to commit to them.) The second approach is more paranoid: you can use one of the access-control scripts provided with Subversion to prevent anyone from doing anything but creating new copies in the tags-area (See Chapter 6, Server Configuration.) The paranoid approach, however, isn't usually necessary. If a user accidentally commits a change to a tag-directory, you can simply undo the change as discussed in the previous section. This is version control, after all.
Sometimes you may want your “snapshot” to be more complicated than a single directory at a single revision.
For example, pretend your project is much larger than our
calc example: suppose it contains a
number of subdirectories and many more files. In the course
of your work, you may decide that you need to create a working
copy that is designed to have specific features and bug fixes.
You can accomplish this by selectively backdating files or
directories to particular revisions (using svk update
-r liberally), or by switching files and directories
to particular branches (making use of svk
switch). When you're done, your working copy is a
hodgepodge of repository locations from different revisions.
But after testing, you know it's the precise combination of
data you need.
Time to make a snapshot. Copying one URL to another won't work here. In this case, you want to make a snapshot of your exact working copy arrangement and store it in the repository. Luckily, svk copy actually has four different uses (which you can read about in Chapter 9), including the ability to copy a working-copy tree to the repository:
$ ls my-working-copy/ $ svk copy my-working-copy http://svk.example.com/repos/calc/tags/mytag Committed revision 352.
Now there is a new directory in the repository,
/calc/tags/mytag, which is an exact
snapshot of your working copy—mixed revisions, URLs,
and all.
Other users have found interesting uses for this feature. Sometimes there are situations where you have a bunch of local changes made to your working copy, and you'd like a collaborator to see them. Instead of running svk diff and sending a patch file (which won't capture tree changes), you can instead use svk copy to “upload” your working copy to a private area of the repository. Your collaborator can then either checkout a verbatim copy of your working copy, or use svk merge to receive your exact changes.
You may have noticed by now that Subversion is extremely flexible. Because it implements branches and tags with the same underlying mechanism (directory copies), and because branches and tags appear in normal filesystem space, many people find Subversion intimidating. It's almost too flexible. In this section, we'll offer some suggestions for arranging and managing your data over time.
There are some standard, recommended ways to organize a
repository. Most people create a trunk
directory to hold the “main line” of development,
a branches directory to contain branch
copies, and a tags directory to contain
tag copies. If a repository holds only one project, then
often people create these top-level directories:
/trunk /branches /tags
If a repository contains multiple projects, admins typically index their layout by project (see the section called “” to read more about “project roots”):
/paint/trunk /paint/branches /paint/tags /calc/trunk /calc/branches /calc/tags
Of course, you're free to ignore these common layouts. You can create any sort of variation, whatever works best for you or your team. Remember that whatever you choose, it's not a permanent commitment. You can reorganize your repository at any time. Because branches and tags are ordinary directories, the svk move command can move or rename them however you wish. Switching from one layout to another is just a matter of issuing a series of server-side moves; if you don't like the way things are organized in the repository, just juggle the directories around.
Remember, though, that while moving directories may be easy to do, you need to be considerate of your users as well. Your juggling can be disorienting to users with existing working copies. If a user has a working copy of a particular repository directory, your svk move operation might remove the path from the latest revision. When the user next runs svk update, they'll be told that their working copy represents a path that no longer exists, and the user will be forced to svk switch to the new location.
Another nice feature of Subversion's model is that
branches and tags can have finite lifetimes, just like any
other versioned item. For example, suppose you eventually
finish all your work on your personal branch of the
calc project. After merging all of your
changes back into /calc/trunk, there's
no need for your private branch directory to stick around
anymore:
$ svk delete http://svk.example.com/repos/calc/branches/my-calc-branch \
-m "Removing obsolete branch of calc project."
Committed revision 375.
And now your branch is gone. Of course it's not really
gone: the directory is simply missing from the
HEAD revision, no longer distracting
anyone. If you use svk checkout,
svk switch, or svk list
to examine an earlier revision, you'll still be able to see
your old branch.
If browsing your deleted directory isn't enough, you can
always bring it back. Resurrecting data is very easy in
Subversion. If there's a deleted directory (or file) that
you'd like to bring back into HEAD, simply
use svk copy -r to copy it from the old
revision:
$ svk copy -r 374 http://svk.example.com/repos/calc/branches/my-calc-branch \
http://svk.example.com/repos/calc/branches/my-calc-branch
Committed revision 376.
In our example, your personal branch had a relatively
short lifetime: you may have created it to fix a bug or
implement a new feature. When your task is done, so is the
branch. In software development, though, it's also common to
have two “main” branches running side-by-side for
very long periods. For example, suppose it's time to release
a stable calc project to the public, and
you know it's going to take a couple of months to shake bugs
out of the software. You don't want people to add new
features to the project, but you don't want to tell all
developers to stop programming either. So instead, you create
a “stable” branch of the software that won't
change much:
$ svk copy http://svk.example.com/repos/calc/trunk \
http://svk.example.com/repos/calc/branches/stable-1.0 \
-m "Creating stable branch of calc project."
Committed revision 377.
And now developers are free to continue adding
cutting-edge (or experimental) features to
/calc/trunk, and you can declare a
project policy that only bug fixes are to be committed to
/calc/branches/stable-1.0. That is, as
people continue to work on the trunk, a human selectively
ports bug fixes over to the stable branch. Even after the
stable branch has shipped, you'll probably continue to
maintain the branch for a long time—that is, as long
as you continue to support that release for customers.
We've covered a lot of ground in this chapter. We've discussed the concepts of tags and branches, and demonstrated how Subversion implements these concepts by copying directories with the svk copy command. We've shown how to use svk merge to copy changes from one branch to another, or roll back bad changes. We've gone over the use of svk switch to create mixed-location working copies. And we've talked about how one might manage the organization and lifetimes of branches in a repository.
Remember the Subversion mantra: branches and tags are cheap. So use them liberally!
[14] SVK does not support cross-depot copying. When using DEPOTPATHs with svk copy or svk move, you can only copy items within the same depot.
[15] In the future, the Subversion project plans to use (or invent) an expanded patch format that describes tree-changes.
[16] The Subversion project has plans, however, to someday implement an svnadmin obliterate command that would accomplish the task of permanently deleting information. In the meantime, see the section called “” for a possible workaround.
[17] Because CVS doesn't version trees, it creates an
Attic area within each repository
directory as a way of remembering deleted files.
[18] You can, however, use
svk switch with the
--relocate switch if the URL of your server
changes and you don't want to abandon an existing working copy.
See the svk switch section in Chapter 9, SVK Complete Reference for more information and an example.
Table of Contents
This chapter is intended to be a complete reference to using the command svk line client and all its subcommands.
To use the command line client, you type
svk, the subcommand you wish to use
[19], and any switches or targets that
you wish to operate on—the switches always come after the
subcommand that they apply to. There are no global switches to
svk, with the exception of the
--help (-h) switch, which will
give you help for a particular subcommand.
To get help on how to use a command you could run any of:
$ svk -h status $ svk --help status $ svk status -h $ svk status --help $ svk help status
One notable exception to the svk subcommand switches rule is the admin subcommand. This command takes it's own set of subcommands. It's described below in the the section called “svk admin” section.
You can find examples of how to use most client commands in Chapter 3, Guided Tour and commands for managing properties in the section called “”.
While SVK has different switches for its
subcommands, all switches are global[20]—that is, each switch is
guaranteed to mean the same thing regardless of the subcommand
you use it with. For example, --verbose
(-v) always means “verbose
output”, regardless of the subcommand you use it
with.
You can specify a command line switch anywhere on the command line after the name of the subcommand itself. So it's OK to mix switches and options. SVK knows what is what.
--all (-a)Causes SVK to operate on all instances of a particular kind. It depends on the subcommand what exactly this means.
--auto (-a)Requests svk uses the previous merge point as the starting point for this cmerge or merge operation.
--base (-b)
REV[@]Use REV[@] as the merge base.
See the --revision for an explanation
of valid values for REV[@].
--baseless (-B)Use the youngest revision as the merge base.
--change (-c)
[-]REV[@]Act on the change made by revision
[-]REV[@]. Specifying an
@ after the revision number will
cause SVK to operate on the mirrored repository's
revision number rather than the local depots revision
number if the path being operated on is a mirrored path.
Specifying REV is the same
as --revision REV-1:REV, whereas
specifying -REV is equivalent
to --revision REV:REV-1.
--check-only (-C)Goes through all the motions of running a command, but makes no actual changes—either on disk or in the depot.
--cross (-x)Causes a SVK subcommand which is traversing the history of a versioned resource to continue harvesting that historical information when a copy—that is, a location in history where that resource was copied from another location in the repository—is encountered.
--depot
DEPOTNAMEApply the svk patch operation to
the depot specified by
DEPOTNAME.
--depth (-d)
LEVELRecurse at most LEVEL
levels deep. This is used together with the
--recursive switch to svk
list.
--detach (-d)
[DEPOTPATH | PATH]Request that SVK forget about a depot, mirror or working copy.
--directCommit directly even if the path is mirrored.
Don't ever use this option unless you know what you are doing. Better yet don't use this option unless your IRC handle is clkao.
--encoding ENCTells SVK that your commit message is encoded in the charset provided. The default is your operating system's native locale, and you should specify the encoding if your commit message is in any other encoding.
--exportExport mode, checkout a detached copy.
--file (-F)
FILENAMEUses the contents of the file passed as an argument to this switch for the specified subcommand.
--from (-f)Merges or pushes from the specified
PATH.
--from-checkout (-f)Allows a svk import to be run on a working copy.
--full-path (-f)Shows the full path of listed files rather than showing files and directories as a tree.
--help (-h or
-?)
If used with one or more subcommands, shows the built-in help text for each subcommand. If used alone, it displays the general client help text.
--host HOSTUse HOST as the hostname
shown in the merge log.
--importImport mode. Automatically add any new nodes and delete any missing ones. This switch is used with the commit command. An example of how it could be used to track a third party product is:
$ svk checkout --non-recursive //thirdparty/trunk thirdparty-1.2.3 $ tar xvf thirdparty-1.2.3.tar $ svk commit --import thirdparty-1.2.3
--incremental (-I)Applies each change individually.
--init (-i)Initialize the default depot.
--keep-local (-K)Prevents svk from removing a file from a working copy when it is scheduled for deletion via svk delete.
--limit (-l)
NUMShow only the first NUM
log messages.
--list (-l)Show a list of pairs. Shows depotpath, working copy pairs with svk checkout, depotpath, mirror source pairs with svk mirror and depotname, repository path pairs with svk depotmap.
--log (-l)Generate a commit log message consisting of all the log messages of the revision being merged.
--lump (-l)When causes the svk push and svk pull commands to lump all the changes together in a single commit, rather than commit each change incrementally when calling svk smerge.
--merge (-m)If the depotpath for the working copy being updated is a copy of another depotpath—i.e. a branch, perform a svk smerge --log --message '' from the original to the copy before executing the actual command.
--message (-m)
MESSAGE
Indicates that you will specify a commit message on the command line, following this switch. For example:
$ svk commit --message "They don't make Sunday."
--parent (-p)Recursively create intermediate directories in the depot as required.
--patch (-P)
NAMERather than committing a change SVK will generate a
patch named NAME. The name
- will cause svk to output the
patch the standard output rather than storing it.
--non-recursive (-N)Stops a subcommand from recursing into subdirectories. Most subcommands recurse by default, but some subcommands—usually those that have the potential to remove or undo your local modifications—do not.
--no-ignoreDo not ignore files that would normally be ignored.
--no-ticketIndicates that you do not with this merge point to be recorded for merge tracking purposes.
--quiet (-q)Requests that the client print only essential information while performing an operation.
--recoverRecover the state of a mirrored depot path.
--recursive (-R)Makes a subcommand recurse into subdirectories. Most subcommands recurse by default.
--relocate [FROM] TO
Used with the svk mirror subcommand, changes the location of the remote repository that the mirror in a local depot references. This is useful if the location of your repository changes and you have an existing mirror that you'd like to continue to use. See svk mirror for an example.
Used with the svk checkout to move a working copy to a different location.
Used with the svk depotmap to move a depot to a different location.
--remoterevMakes a generated merge log use revision numbers from the remote mirror rather than those in the depot.
--revision (-r)
REV[@]:[REV2[@]]
Indicates that you're going to supply a revision (or range of revisions) for a particular operation. You can provide revision numbers, revision keywords or dates (in curly braces), as arguments to the revision switch. If you wish to provide a range of revisions, you can provide two revisions separated by a colon. For example:
$ svk log --revision 1729
$ svk log --revision 1729:HEAD
$ svk log --revision 1729:1744
$ svk log --revision {2001-12-04}:{2002-02-17}
$ svk log --revision 1729:{2002-02-17}
If the path for which you specified
REV is a mirrored path, you may
append an @ to then end of the
REV to refer to the revision
number in the remote mirror rather than the one in the
local depot. For example:
$ svk log --revision 1729@ //mirrors/test $ svk log --revision 1729@:1744@
See the section called “Revision Keywords” for more information.
--revpropOperates on a revision property instead of a
property specific to a file or directory. This switch
requires that you also pass a revision with the
--revision (-r)
switch. See the section called “”
for more details on unversioned properties.
--sign (-S)Causes SVK to use sign the change being committed.
--skipto (-s) REMOTEREVDuring svk sync will start
mirroring from the remote repositories revision
REMOTEREV, rather than the
first not yet mirrored revision.
--strictCauses SVK to use strict semantics, a notion which is rather vague unless talking about specific subcommands.
--summarize (-s)Causes svk diff to show a status like summary of changes rather than the actual diffs.
--sync (-s)Synchronize with mirrored sources before starting the operation.
--templateUsed together with the --message
(-m) or the --file
(-F) switch, it uses the specified
message as a template to edit rather than as the actual
commit message.
--to (-t)Merges to the specified
PATH.
--to-checkout (-t)Request that a tree on which svk import is run be turned into a working copy immediately
--torev (-t) REMOTEREVDuring svk sync will mirror up to
the specified remote repositories revision
REMOTEREV, rather than
HEAD.
--track-renameRequests that SVK track changes made to renamed nodes and do the right thing when this occurs.
--unlockForcibly remove a stale lock on a mirror.
--upgradeUpgrade a mirror to the latest version.
--verbatimProduces a verbatim merge log without any indents or headers.
--verbose (-v)Requests that the client print out as much information as it can while running any subcommand. This may result in SVK printing out additional fields, detailed information about every file, or additional information regarding its actions.
--versionPrints the client version info.
svk add — Add files, directories, or symbolic links.
Add files, directories, or symbolic links to your working copy and schedule them for addition to the repository. They will be uploaded and added to the repository on your next commit. If you add something and change your mind before committing, you can unschedule the addition using svk revert.
To add a file to your working copy:
$ svk add foo.c A foo.c
When adding a directory, the default behavior of svk add is to recurse:
$ svk add testdir A testdir A testdir/a A testdir/b A testdir/c A testdir/d
You can add a directory without adding its contents:
$ svk add --non-recursive otherdir A otherdir
The command svk add * will even recurse down into directories that are already under version control, looking for unversioned objects deeper down in the tree.
$ svk add * A foo.c A somedir/bar.c A otherdir/docs/baz.doc …
svk annotate — Show author and revision information in-line for the specified files or URLs.
Show author and revision information in-line for the specified files or URLs. Each line of text is annotated at the beginning with the author (username) and the revision number for the last change to that line.
If you want to see annotated source for
readme.txt in your test
depot:
$ svk ann //calc/integer.c
Annotations for /calc/integer.c (3 active revisions):
****************
2 ( sally 2005-07-20): /* integer.c */
10 ( sally 2005-07-21):
10 ( sally 2005-07-21): #define ZERO 0
10 ( sally 2005-07-21): #define ONE 1
By default svk annotate will not
look further back than the closest copy. If you wish to
see the entire history of the file including who changed
what before the copy use the --cross
switch. In particular if you are running annotate on a
branched or tagged revision of a file you might want to
see it history back beyond the copy:
$ svk annotate //tags/calc-1.0/integer.c
Annotations for /tags/calc-1.0/integer.c (1 active revisions):
****************
31 ( sally 2005-07-22): /* integer.c */
31 ( sally 2005-07-22):
31 ( sally 2005-07-22): #define ZERO 0
31 ( sally 2005-07-22): #define ONE 1
$ svk annotate --cross //tags/calc-1.0/integer.c
Annotations for /tags/calc-1.0/integer.c (4 active revisions):
****************
2 ( sally 2005-07-20): /* integer.c */
10 ( sally 2005-07-21):
10 ( sally 2005-07-21): #define ZERO 0
10 ( sally 2005-07-21): #define ONE 1
svk cat — Output the contents of the specified files or URLs.
Output the contents of the specified files or URLs. For listing the contents of directories, see svk list.
If you want to view readme.txt in your depot without checking it out:
$ svk cat //test/readme.txt This is a README file. You should read this.
If your working copy is out of date (or you have
local modifications) and you want to see the
HEAD revision of a file in your
working copy, svk cat will
automatically fetch the HEAD revision
when you give it a path:
$ cat foo.c This file is in my local working copy and has changes that I've made. $ svk cat foo.c Latest revision fresh from the repository!
svk checkout — Check out a working copy from a repository.
svk checkout DEPOTPATH[@REV[@]] [PATH]
svk checkout URL [PATH]
svk checkout --list
svk checkout --relocate DEPOTPATH|PATH PATH
svk checkout --detach [DEPOTPATH|PATH]
svk checkout --purge
Check out a working copy from a repository. If
PATH is omitted, the
basename of the DEPOTPATH will be used as the
destination.
Using the second form, svk will setup a mirror of the specified URL if you don't have one already, sync the mirror and checkout a working copy from the mirror.
Unlike Subversion or CVS, SVK working copies do not
contain any administrative files or directories. This
means things like diff -r and
find work great on SVK working
copies. However because of this it's not possible to
simply rename an svk working copy or remove it without
telling svk about it. SVK keeps the metadata
associated with a working copy inside your
$SVKROOT (which defaults to
~/.svk). Because of this you need to
use the commands listed here to manage working
copies.
With the --list switch, output one
line per checked out working copy. Each line consists of
the depotpath followed by an absolute path to the working
copy.
If you wish to change the location of a checked out
working copy you need to use the svk
checkout with the --relocate.
Give it both the old and the new path to the working copy.
Either before or after that you should move the working
copy to it's new location so SVK can find it.
Once you are done using a working copy you should tell
svk this since it keeps track of all checked out working
copies. To do so you would run svk
checkout with the --detach
switch. This lets SVK know it no longer needs to keep
track of this particular working copy. This command does
not actually remove the working copy from disk. That's
still your job.
If you want to create a working copy that svk
doesn't track at all you can use the
--export switch to the svk
checkout command.
If you accidentally remove a working copy or working
copies that svk was tracking for you, running svk
checkout with the --purge
switch will prompt svk to forget about each working copy
it can no longer find on disk.
--revision (-r) REV[@] --non-recursive (-N) --list (-l) --detach (-d) [DEPOTPATH|PATH] --quiet (-q) --export --relocate DEPOTPATH|PATH PATH --purge
Check out a working copy into a directory called
mine:
$ svk checkout //test mine Syncing //test(/test) in /Users/sally/mine to 2. A mine/a A mine/b $ ls mine
List which working copies you have checked out:
$ svk checkout --list Depot Path Path ======================================================================== //test /Users/sally/mine
Change the location of the checked out working copy:
$ mkdir test $ svk checkout --relocate mine test/mine Checkout '/Users/sally/mine' relocated to '/Users/sally/test/mine'. $ svk checkout --list Depot Path Path ======================================================================== //test /Users/sally/test/mine
Get rid of the working copy since we don't need it anymore:
$ svk checkout --detach test/mine Checkout path '/Users/sally/test/mine' detached. $ svk checkout --list Depot Path Path ======================================================================== $ rm -rf test/mine
Using the --purge switch after you
have removed a working copy:
$ svk checkout --quiet //test mine Syncing //test(/test) in /Users/sally/mine to 2. $ rm -rf mine $ svk checkout --purge Purge checkout of //test to non-existing directory /Users/sally/mine? (y/n) y Checkout path '/Users/sally/mine' detached.
svk cleanup — Remove stale locks left behind by an aborted svk sync of a mirror.
If, for some reason, an svk commit fails due to a network problem or otherwise. You can run this command to remove the lock left behind.
Well, there's not much to the examples here as
svk cleanup generates no output. If
you pass no PATH, “.” is
used.
$ svk commit # Do a kill -9 of svk in a different shell window $ svk update /Users/sally/mine already locked, use 'svk cleanup' if lock is stalled $ svk cleanup Cleaned up stalled lock on /Users/sally/mine.
svk cmerge — Cherry pick specific changes and merge them
Merge the changes specified by
REVRANGE in DEPOTPATH to
TARGET, or .
if no TARGET is specified. If
TARGET is a depotpath, commit
the result directly to that depot.
The range REVRANGE can
be specified either as
--revision REV1[@]:REV2[@] or
--change [-]REV[@].
This command is currently deprecated, pending improvements to the Subversion API. In the meantime, use svk merge --change to obtain similar functionality.
Working copy if TARGET is a
working copy or not specified. Depot if
TARGET is a depotpath, and
mirrored repository if it's a mirrored depotpath.
svk commit — Send changes from your working copy to the repository.
Send changes from your working copy to the
repository. If you do not supply a log message with your
commit by using either the --file or
--message switch,
svk will launch your editor for you
to compose a commit message. See the
editor-cmd section in the section called “”.
If you begin a commit and SVK launches your editor to compose the commit message, you can still abort without committing your changes. If you want to cancel your commit, just quit your editor without saving your commit message and Subversion will prompt you to either abort the commit, continue with no message, or edit the message again.
--import --message (-m) --file (-F) --template --encoding ENC --patch (-P) NAME --sign (-S) --check-only (-C) --non-recursive (-N) --direct
Commit a simple modification to a file with the
commit message on the command line and an implicit
target of your current directory
(“.”):
$ svk commit --message "added howto section." Sending a Transmitting file data . Committed revision 3.
Commit a modification to the file
foo.c (explicitly specified on the
command line) with the commit message in a file named
msg:
$ svk commit --file msg foo.c Committed revision 5.
To commit a file scheduled for deletion:
$ svk commit --message "removed file 'c'." Committed revision 7.
To recursively commit all changes to the working copy you run:
$ svk commit
Waiting for editor...
<svk will launch your favorite editor here, with a buffer open
that looks something like this:>
=== Targets to commit (you may delete items from it) ===
M /Users/sally/calc/integer.c
A /Users/sally/calc/foo.c
Sometimes you may wish only to commit a subset of
the changes in a working copy. For example in the case
above let's assume you wish to only commit the change to
integer.c. You would edit the buffer
to look like:
Fixed typo in documentation. === Targets to commit (you may delete items from it) === M /Users/sally/calc/integer.c
When you save the commit message and exit the
editor, SVK will only commit the files still listed
below the === Targets to commit (you may delete
items from it) === line in the commit
message.
svk copy — Copy a file or directory in a working copy or in a depot.
Copy a file or directory in a working copy or in the
repository. src and
DST can each be either a
working copy (WC) path or a depot path. In addition
src can also be a URL:
Copy and schedule an item for addition (with history).
Immediately commit a copy of WC to DEPOTPATH.
Although svk supports having mixed revision
working copies, for example by backreving
certain files or directories with a svk
update --revision, creating a copy of
such a mixed revision working copy using this
form of the svk copy command
will not create a mixed revision tag or branch.
Instead it creates a tag or branch which is a
copy of the same revision that the
WC path specified is
at without regard to the revisions of any
subdirectories or files.
If the current directory is inside a working copy, check out DEPOTPATH into the current directory, and schedule it for addition. Otherwise prompt for a destination depot path to copy to.
Check out DEPOTPATH into WC, and schedule it for addition.
Complete in depot copy. This is usually used to branch and tag.
Interactively sets up a mirror of URL if you
do not have one already. After the optional setup of
the mirror, creates a local copy of the mirror in
DEPOTPATH, finally checks out that local branch in
a new working copy using the basename of the local
copy. See svk mirror
for more information on valid
URL values.
Interactively sets up a mirror of URL if you do not have one already. After the optional setup of the mirror, prompts for a DEPOTPATH to create a local copy of the mirror in, finally checks out that local branch in a new working copy called PATH.
Interactively sets up a mirror of URL if you do not have one already. After the optional setup of the mirror, creates a local copy of the mirror in DEPOTPATH.
You can only copy files within a single depot. SVK does not support cross-depot copying.
It is possible to copy files from one mirrored repository to another, if both mirrors live inside the same depot. This allows you to copy with history between different mirrored repositories.
Depot if destination is a DEPOT or source is a URL.
Working copy if destination is a WC path.
Creates a working copy if source is a URL.
If source or destination is in the depot, or if needed to look up the source revision number.
If destination is in the depot at a mirrored path or if the source is a URL which isn't yet being mirrored.
--revision (-r) REV[@] --parent (-p) --quiet (-q) --message (-m) --file (-F) --template --encoding ENC --patch (-P) NAME --sign (-S) --check-only (-C) --direct
Copy an item within your working copy (just schedules the copy—nothing goes into the depot until you commit):
$ svk copy foo.txt bar.txt A bar.txt $ svk status A bar.txt
Copy an item in your working copy to a PATH in the depot (an immediate commit, so you must supply a commit message):
$ svk copy --message "Remote copy." near.txt //test/far-away.txt Committed revision 8.
Copy an item from the depot to your working copy (just schedules the copy—nothing goes into the depot until you commit):
This is the recommended way to resurrect a dead file in your depot!
$ svk copy //test/far-away near-here A near-here
And finally, copying between two DEPOTPATHS:
$ svk copy --message "remote copy." //test/far-away //test/over-there Committed revision 9.
This is the easiest way to “tag” a
revision in your depot—just svk
copy that revision (usually
HEAD) into your tags directory.
$ svk copy --message "tag tree" //test/trunk //test/tags/0.6.32-prerelease Committed revision 12.
And don't worry if you forgot to tag—you can always specify an older revision and tag anytime:
$ svk copy --revision 11 --message "Forgot to tag at rev 11" //test/trunk //test/tags/0.6.32-prerelease Committed revision 13.
svk delete — Delete an item from a working copy or a depot.
Items specified by PATH are
scheduled for deletion upon the next commit. Files (and
directories that have not been committed) are immediately
removed from the working copy. The command will not
remove any modified items or items scheduled for addition;
use the svk revert first for
those.
Items specified by DEPOTPATH are deleted from the repository via an immediate commit. Multiple DEPOTPATHS are currently not supported.
Working copy if operating on files, Depot if operating on a DEPOTPATH, Mirrored repository if operation on a mirrored DEPOTPATH
--keep-local (-K) --message (-m) --file (-F) --template --encoding ENC --patch (-P) NAME --sign (-S) --check-only (-C) --direct
Using svk to delete a file from your working copy merely schedules it to be deleted. When you commit, the file is deleted in the depot.
$ svk delete myfile D myfile $ svk commit --message "Deleted file 'myfile'." Committed revision 14.
Deleting a DEPOTPATH, however, is immediate, so you have to supply a log message:
$ svk delete --message "Deleting file 'yourfile'" //test/yourfile Committed revision 15.
Here's an example of how to delete a file that has local mods:
$ svk delete over-there over-there is modified, use 'svk revert' first. $ svk revert over-there Reverted over-there $ svk delete over-there D over-there
Here's an example of how to delete a file which was scheduled for addition but not yet committed:
$ svk delete new-file new-file is scheduled, use 'svk revert'. $ svk revert new-file Reverted new-file $ rm new-file
svk depotmap — Create or edit the depot mapping configuration.
depotmap
depotmap DEPOTNAME REPOPATH
depotmap --list
depotmap --detach [DEPOTNAME|REPOPATH]
depotmap --relocate [DEPOTNAME|REPOPATH] REPOPATH2
The first form will launch you preferred editor and
show you a list of DEPOTNAME:
REPOPATH mappings. You can add or remove
new mappings in your editor, rename depots, or move where
the repository for a depot is—it won't move it but
you can update the location in the depotmap after you have
moved the repository yourself. After you save the depotmap
and exit the editor svk will ask you to create
repositories for each of the new depot entries you have
made.
The second form will display a list of depotname and corresponding repository location pairs.
The third form will remove the specified depot from the depotname to repository path mappings. However it will not delete the repository that the depot referred to.
The fourth form allows you to let svk know you moved the repository for a depot to a new location.
--init (-i) --list (-l) --detach (-d) [DEPOTNAME|REPOPATH] --relocate [DEPOTNAME|REPOPATH] REPOPATH2
Create the default depot the first time you use svk:
$ svk depotmap --init Repository /Users/sally/.svk/local does not exist, create? (y/n)y
Add a depot named tmp in /tmp/tmp— this isn't a good place to store a real depot but works for this example:
$ svk depotmap /tmp/ /tmp/tmp New depot map saved. Repository /tmp/tmp does not exist, create? (y/n)y
Show a listing of depots and their corresponding Subversion repositories:
$ svk depotmap --list Depot Path ============================================================ // /Users/sally/.svk/local /tmp/ /tmp/tmp
Move the repository for the /tmp/ depot we just created to a different location:
$ mv /tmp/tmp /tmp/tmp-repository $ svk depotmap --relocate /tmp/ /tmp/tmp-repository Depot 'tmp' relocated to '/tmp/tmp-repository'.
Get rid of the /tmp/ depot and the repository for it:
$ svk depotmap --detach /tmp/ Depot 'tmp' detached. $ rm -rf /tmp/tmp-repository
Note that in all the examples above you could use
plain tmp instead of
/tmp/ to refer to the depot as
well.
svk describe — Describe a change
Given DEPOT is the root of
the depot containing TARGET or
the depot for the current working copy if
TARGET isn't specified. This
command is equivalent to running
svk log --revision REV DEPOT followed by
svk diff --revision REV-1:REV DEPOT
Show what exactly changed and why at revision 10 in the depot for the current working copy:
$ svk describe 10 integer.c
----------------------------------------------------------------------
r10: sally | 2005-07-20 21:46:51 -0700
Added defines.
----------------------------------------------------------------------
=== integer.c
==================================================================
--- integer.c (revision 9)
integer.c (revision 10)
@@ -1 1,4 @@
/* integer.c */
#define ZERO 0
#define ONE 1
svk diff — Display the differences between two paths.
diff [--revision REV[@]] [PATH...]
diff --revision REV1[@][:REV2[@]]] DEPOTPATH|PATH...
diff DEPOTPATH1 DEPOTPATH2
diff DEPOTPATH [PATH]
Display the differences between two paths. The four different ways you can use svk diff are:
svk diff [--revision REV[@]] [PATH...]
displays the differences between each file or directory
specified in the working copy, if none are specified
. is implied, with the
BASE or
REV (if it's specified)
revision of that file in the depot
svk diff --revision REV1[@]:REV2[2] DEPOTPATH|PATH...
displays the differences between
REV1 and
REV2 of the DEPOTPATH
specified, or of each file or directory specified in the
working copy.
svk diff DEPOTPATH1 DEPOTPATH2 shows the differences between 2 depot paths—this is useful for displaying the difference between branches or tags.
svk diff DEPOTPATH [PATH]
shows the differences between the current working copy (or
the PATH directory and the
contents of DEPOTPATH.
Running svk diff ignores the ancestry of files and merely compares the contents of the two files being compared.
Compare BASE and your working copy
(one of the most popular uses of svk
diff):
$ echo 'Added some stuff.' >> file.txt
$ svk diff file.txt
=== file.txt
==================================================================
--- file.txt (revision 15)
file.txt (local)
@@ -0,0 1 @@
Added some stuff.
See how your working copy's modifications compare against an older revision:
$ svk diff --revision 14 file.txt
=== file.txt
==================================================================
--- file.txt (revision 14)
file.txt (local)
Compare revision 12 to revision 15 using range notation:
$ svk diff --revision 12:15 //trunk/file.txt
=== file.txt
==================================================================
--- file.txt (revision 12)
file.txt (revision 15)
Compare revision 3000 to revision 3500 of all files in
trunk using range notation:
$ svk diff --revision 3000:3500 //test/trunk
If you have a working copy, you can obtain the differences without typing in the long URLs:
$ svk diff --revision 12:15 file.txt
=== file.txt
==================================================================
--- file.txt (revision 12)
file.txt (revision 15)
Set the $SVKDIFF environment
variable to run and external diff program instead of the
built in diff.
$ SVKDIFF="/usr/bin/diff -i -b" svk diff file.txt 1,2c1 < This is a README file. < You should read this. --- > Added some stuff.
svk help — Help!
svk import — Recursively commit a copy of PATH to DEPOTPATH.
Recursively commit a copy of
PATH to
DEPOTPATH. If
PATH is omitted
“.” is assumed. Parent
directories are created in the repository as
necessary.
--from-checkout (-f) --to-checkout (-t) --message (-m) --file (-F) --template --encoding ENC --patch (-P) NAME --sign (-S) --check-only (-C) --non-recursive (-N) --direct
This imports the local directory
myproj into the root of your
depot:
$ svk import --message "New import" myproj // Committed revision 25. Directory /Users/sally/myproj imported to depotpath // as revision 25.
This imports the local directory myproj
into trunk/misc in your depot. The
directory trunk/misc need not exist before
you import into it—svk import will
recursively create directories for you:
$ svk import --message "New import" myproj //trunk/misc/myproj Committed revision 26. Import path //trunk/misc/myproj initialized. Committed revision 27. Directory /Users/sally/myproj imported to depotpath //trunk/misc/myproj as revision 27.
After importing data, note that the original tree is
not under version control. If you
wish to use the original tree as a working copy right away
you need to specify the --to-checkout
switch to import. Alternatively you can
svk checkout a fresh working copy of
the tree.
By default svk will not let you import from a tree
that is already a working copy. If you wish to do so
anyway you need to specify the
--from-checkout switch.
svk info — Print information about PATHs.
Print information about both working copy paths and DEPOTPATHS, including:
Checkout Path
Depot Path
Revision
Last Changed Rev.
Mirrored From:
Copied From:
Merged From:
svk info will show you all the useful information that it has for your working copy.
$ svk info Checkout Path: /Users/sally/myproj Depot Path: //trunk/myproj Revision: 24254 Last Changed Rev.: 24253 Mirrored From: http://server.sally.com/, Rev. 24751 Merged From: //branches/branch-a, Rev. 24179 Merged From: //branches/branch-b, Rev. 23849
svk info also acts on DEPOTPATHS
$ svk info //trunk/myproj Depot Path: //trunk/myproj Revision: 24254 Last Changed Rev.: 24253 Mirrored From: http://server.sally.com/, Rev. 24751 Merged From: //branches/branch-a, Rev. 24179 Merged From: //branches/branch-b, Rev. 23849
svk list — List directory entries in the depot.
List each TARGET file and
the contents of each TARGET
directory as they exist in the repository. If
TARGET is a working copy path,
the corresponding repository URL will be used.
The default TARGET is
“.”, meaning the
depotpath of the current working copy directory.
With --verbose, the following fields show
the status of the item:
Revision number of the last commit
Author of the last commit
Size (in bytes)
Date and time of the last commit
svk list is most useful if you want to see what files a depot has without checking out a working copy:
$ svk list //test/support README.txt INSTALL examples/ …
You can pass the --verbose switch for
additional information, rather like the UNIX command
ls -l:
$ svk list --verbose //test/support
16 sally 28361 Jan 16 23:18 README.txt
27 sally 0 Jan 18 15:27 INSTALL
24 harry Jan 18 11:27 examples/
…
For further details, see the section called “svk list”.
svk log — Display commit log messages.
The default target is the path of your current
directory. If no arguments are supplied, svk
log shows the log messages for all files and
directories inside of (and including) the current working
directory of your working copy. You can refine the
results by specifying a path, one or more revisions, or
any combination of the two. The default revision range
for a local path is BASE:1.
If you specify a DEPOTPATH alone, then it prints log
messages for everything that the DEPOTPATH contains. The
default revision range for a URL is
HEAD:1.
With --verbose, svk log
will also print all affected paths with each log message.
With --quiet, svk log
will not print the log message body itself (this is
compatible with --verbose).
Logs do not follow copy history by default. Use
--cross to enable following of copy
history.
You can see the log messages for all the paths that changed in your working copy by running svk log from the top:
$ svk log ---------------------------------------------------------------------- r20: harry | 2005-07-19 10:43:54 -0700 Tweak. ---------------------------------------------------------------------- r17: sally | 2005-07-19 10:35:41 -0700 …
Examine all log messages for a particular file in your working copy:
$ svk log foo.c ---------------------------------------------------------------------- r32: sally | 2005-07-19 11:43:13 -0700 Added defines. ---------------------------------------------------------------------- r28: sally | 2005-07-19 11:48:33 -0700 …
If you don't have a working copy handy, you can log a DEPOTPATH:
$ svk log //test/foo.c ---------------------------------------------------------------------- r32: sally | 2005-07-19 11:43:13 -0700 Added defines. ---------------------------------------------------------------------- r28: sally | 2005-07-19 11:48:33 -0700 …
If you run svk log on a specific path and provide a specific revision and get no output at all
$ svk log --revision 20 //untouched.txt ----------------------------------------------------------------------
That just means that the path was not modified in that revision. If you log from the top of the repository, or know the file that changed in that revision, you can specify it explicitly:
$ svk log --revision 20 touched.txt ---------------------------------------------------------------------- r20: harry | 2005-07-19 10:43:54 -0700 Tweak. ----------------------------------------------------------------------
Sometimes you might not care about the log messages
themselves, but just about the meta information. To only
display a one line header for each revision use the
--quiet (-q) switch to
svk log:
$ svk log --quiet //test/foo.c ---------------------------------------------------------------------- r32: sally | 2005-07-19 11:43:13 -0700 ---------------------------------------------------------------------- r28: sally | 2005-07-19 11:48:33 -0700 ---------------------------------------------------------------------- …
svk merge — Apply differences between two sources.
svk merge --revision N:M DEPOTPATH [WCPATH]
svk merge --revision DEPOTPATH1 DEPOTPATH2
svk merge --revision N:M [--to|--from] [WCPATH]
TODO ## In the first and second forms, the source paths (URLs
in the first form, working copy paths in the second) are
specified at revisions N and
M. These are the two sources
to be compared. The revisions default to
HEAD if omitted.
In the third form, SOURCE
can be a URL or working copy item, in which case the
corresponding URL is used. This URL, at revisions
N and
M, defines the two sources to
be compared.
WCPATH is the working copy
path that will receive the changes. If
WCPATH is omitted, a default
value of “.” is assumed,
unless the sources have identical basenames that match a
file within “.”: in which
case, the differences will be applied to that file.
Unlike svk diff, the merge command takes the ancestry of a file into consideration when performing a merge operation. This is very important when you're merging changes from one branch into another and you've renamed a file on one branch but not the other.
Working copy if merging to a working copy. Depot if merging to a depotpath, and mirrored repository if the destination is a mirrored depotpath.
--revision (-r) REV --change (-c) REV --incremental (-I) --auto (-a) --log (-l) --sync (-s) --to (-t) --from (-f) --verbatim --no-ticket --track-rename --message (-m) --file (-F) --template --encoding ENC --patch (-P) NAME --sign (-S) --check-only (-C) --direct
Merge a branch back into the trunk (assuming that you have a working copy of the trunk, and that the branch was created in revision 250):
$ svk merge --revision 250:HEAD //branches/my-branch U myproj/tiny.txt U myproj/thhgttg.txt U myproj/win.txt U myproj/flo.txt
If you branched at revision 23, and you want to merge changes on trunk into your branch, you could do this from inside the working copy of your branch:
$ svk merge --revision 23:30 //trunk/vendors U myproj/thhgttg.txt …
To merge changes to a single file:
$ cd myproj $ svk merge --revision 30:31 thhgttg.txt U thhgttg.txt
svk mirror — Manage mirrors
mirror DEPOTPATH URL
mirror --list [DEPOTNAME...]
mirror --relocate DEPOTPATH URL
mirror --detach DEPOTPATH
mirror --recover DEPOTPATH
mirror --upgrade /DEPOTNAME/
The first form sets up a new mirror of the repository at URL in DEPOTPATH. DEPOTPATH must not yet exist, nor can any of it's parent directories already be a mirror. The URL can either be a Subversion repository URL, or something that vcp understands if you have that installed. Subversion repository mirrors are read/write, vcp based mirrors (such as a mirror of a CVS repository) are read only. For more details on valid URL values, see ##TODO.
The second form lists all the mirrors in the depots
listed by DEPOTNAME, or all
mirrors if no DEPOTNAME is
given.
The third form detaches the specified path from it's mirrored URL, causing that path and it's children to be treated as regular DEPOTPATHs from now one. The contents of DEPOTPATH remain unchanged, so any revisions retrieved into the mirror prior to the detaching remain available.
To completely remove a mirror you can use svk delete on the mirrored path. However when doing so be careful not to delete the parent of an attached mirror since doing so will confuse svk greatly right now.
If you accidentally do an SVN commit onto a mirrored
path—because you used the svn
client to commit directly to the depot's repository, or
the --direct switch to
svk, running svk mirror
--recover can undo it (reverse apply the
commit(s), and set the appropriate revprop), to get the
mirror working again.
The final form is used to upgrade the mirrors inside a
given DEPOTNAME to the current
standard way of tracking mirrored revisions. This command
is only relevant if you are upgrading from an older
version of SVK. ##TODO—How old? Which
version?
To mirror the svkbook sources in the /mirror/svkbook directory of the default depot (the mirror directory will get created automatically if it doesn't yet exist), run:
$ svk mirror svn://svn.clkao.org/svkbook //mirror/svkbook Committed revision 12.
Setting up a mirrored path doesn't actually mirror anything yet. To do so you need to run svk sync.
To view which repositories you have set up to mirror and where they are being mirrored to you would use:
$ svk mirror --list Path Source ============================================================ //mirror/svkbook svn://svn.clkao.org/svkbook
Sometimes an administrator might change the “base location” of the repository you are mirroring—in other words, the contents of the repository doesn't change, but the main URL used to reach the root of the repository does. For example, the hostname may change, the URL scheme, or any part of the URL which leads to the repository itself. Rather than create a new mirror, you can use the svn mirror --relocate command to point the depot to the new location of the mirrored repository.
For the sake of this example let's assume that the
book repository we mirrored, switched from using the
svnprotocol to http
instead, and that the hostname changed from svn to
svkbook, and that the svkbook virtual host has the repos
at it's root. To change our depot to refer to the mirrored
repository by it's new URL we would run:
$ svk mirror --relocate //mirror/svkbook http://svkbook.clkao.org/ Committed revision 13.
SVK won't let you relocate a mirror unless the new mirrored repository has the same UUID as the original mirror had. This prevents you from accidentally using relocate to point a mirror at something that isn't really the same repository after all.
svk mkdir — Create a new directory under version control.
Create a directory with a name given by the final
component of the PATH or
DEPOTPATH. A directory
specified by a working copy
PATH is scheduled for addition
in the working copy. A directory specified by a
DEPOTPATH is created in the
depot via an immediate commit. In both cases all the
intermediate directories must already exist, unless the
--parent (-p) switch is
specified.
Working copy, depot if operating on a DEPOTPATH, Mirrored repository if the DEPOTPATH is a mirrored path.
svk move — Move a file or directory.
This command moves a file or directory in your working copy or in the repository.
This command is equivalent to an svk copy followed by svk delete.
SVK does not support moving between working copies and DEPOTs. In addition, you can only move files within a single depot—SVK does not support cross-depot moving. However if you have two mirrored repositories in the same depot, you can move files and directories between them.
Move and schedule a file or directory for addition (with history).
Complete in depot rename.
Working copy, depot if operating on a DEPOTPATH, Mirrored repository if either DEPOTPATH is a mirrored path.
svk patch — Manage patches.
svk patch --list|--ls
svk patch --cat|--view PATCHNAME
svk patch --regenerate|--regen PATCHNAME
svk patch --update|--up PATCHNAME
svk patch --apply PATCHNAME [TARGET] [-- MERGEOPTIONS]
svk patch --delete|--rm PATCHNAME
Let say you want to create a patch for something rather than actually commit it. To do so you would:
$ touch foo $ svk add foo $ svk commit --message "Added foo" --patch bug1 Patching locally against mirror source file:///Users/sally/.svk/local/calc. Patch bug1 created.
Currently SVK doesn't track which depot a patch is
for. If you are using more than one depot and a patch
applies to a depot other than the default, or
// depot you need to specify the
--depot
DEPOTNAME switch to let SVK
know which depot the patch is meant for.
To view the contents of a patch you would run:
$ svk patch --cat bug1 ==== Patch <bug1> level 1 Source: [No source] Target: f5a4d37e-57fc-0310-86b3-8bf2ff90d511:/calc:53 [local] Log: Added foo === foo ==================================================================
Now let's revert the uncommitted change and apply the patch to our wc:
$ svk revert foo $ rm foo $ svk patch --apply bug1 A foo
We could also apply the patch directly to the depot—this could be on a different machine, like the one we are mirroring from:
$ svk patch --apply bug1 //calc A foo Committed revision 54.
If we no longer need a patch we can delete it:
$ svk patch --delete bug1
svk propdel — Remove a property from an item.
svk propdel PROPNAME [DEPOTPATH|PATH...]
svk propdel PROPNAME --revprop --revision REV[@] [DEPOTPATH]
This removes properties from files, directories, or revisions. The first form removes versioned properties, while the second removes unversioned properties on a depot revision.
Working copy, depot only if operating on a DEPOTPATH, mirrored repository if the depotpath is mirrored.
svk propedit — Edit the property of one or more items under version control.
svk propedit PROPNAME DEPOTPATH|PATH...
svk propedit PROPNAME --revprop --revision REV[@] [DEPOTPATH]
Edit one or more properties on files, directories, or revisions. The first form edits versioned properties, while the second edits unversioned properties on a depot revision.
Working copy, depot only if operating on a DEPOTPATH, mirrored repository if the depotpath is mirrored.
--recursive (-R) --revision (-r) REV --revprop --message (-m) --file (-F) --template --encoding ENC --patch (-P) NAME --sign (-S) --check-only (-C) --direct
svk propedit makes it easy to modify properties that have multiple values:
$ svk propedit svn:keywords foo.c
Waiting for editor...
<svk will launch your favorite editor here, with a buffer open
containing the current contents of the svn:keywords property. You
can add multiple values to a property easily here by entering one
value per line.>
M foo.c
svk propget — Print the value of a property.
svk propget PROPNAME [TARGET[@REV]...]
svk propget PROPNAME --revprop --revision REV[@] [DEPOTPATH]
Print the value of a property on files, directories, or revisions. The first form prints the versioned property of an item or items in your working copy, while the second prints unversioned remote property on a depot revision. See the section called “” for more information on properties.
svk proplist — List all properties.
List all properties on files, directories, or revisions. The first form lists versioned properties in your working copy, while the second lists unversioned properties on a depot revision.
You can use proplist to see the properties on an item in your working copy:
$ svk proplist foo.c Properties on foo.c: svn:mime-type svn:keywords owner
But with the --verbose flag, svn
proplist is extremely handy as it also shows you the
values for the properties:
$ svk proplist --verbose foo.c Properties on foo.c: svn:mime-type : text/plain svn:keywords : Author Date Rev owner : sally
svk propset — Set PROPNAME to PROPVAL on files, directories, or revisions.
svn propset PROPNAME PROPVAL [DEPOTPATH|PATH...]
svn propset PROPNAME --revprop --revision REV@ PROPVAL [DEPOTPATH|PATH]
Set PROPNAME to
PROPVAL on files, directories,
or revisions. The first example creates a versioned, local
property change in the working copy, or directly in the
depot if a depotpath is specified. If neither are
specified it set the property on ..
The second creates an unversioned, property change on a
depot revision, using the DEPOT specified by
DEPOTPATH or
PATH or the depot associated
with ..
SVK has a number of “special” properties that affect its behavior. See the section called “” for more on these properties.
Working copy, depot only if operating on a DEPOTPATH, mirrored repository if the depotpath is mirrored.
--revision (-r) REV --recursive (-R) --revprop --message (-m) --file (-F) --template --encoding ENC --patch (-P) NAME --sign (-S) --check-only (-C) --quiet (-q) --direct
Set the mimetype on a file:
$ svk propset svn:mime-type image/jpeg foo.jpg M foo.jpg
On a UNIX system, if you want a file to have the executable permission set:
$ svk propset svn:executable ON script.sh M script.sh
Perhaps you have an internal policy to set certain properties for the benefit of your coworkers:
$ svk propset owner sally foo.c M foo.c
If you made a mistake in a log message for a
particular revision and want to change it, use
--revprop and set svn:log
to the new log message:
$ svk propset --revprop --revision 25 svn:log "Journaled about trip to New York." Property 'svn:log' set on repository revision 25.
Or, if you don't have a working copy, you can provide a DEPOTPATH.
$ svn propset --revprop --revision 26 svn:log "Document nap." // property 'svn:log' set on repository revision 26.
If you are working with a mirrored repository changes to revision properties will only affect your own depot. Also if you were to make changes to revision properties in the Subversion repository you are mirroring from, other people would only see the changes you make if they hadn't already synced up to the revision you are changing. This is because changing revision properties does not commit a change to the repository so the mirroring code has no way of knowing that anything changed.
svk resolved — Remove “conflicted” state on working copy files or directories.
Remove “conflicted” state on working copy files or directories. This routine does not semantically resolve conflict markers; it merely removes conflict-related artifact files and allows PATH to be committed again; that is, it tells Subversion that the conflicts have been “resolved”. See the section called “Resolve Conflicts (Merging Others' Changes)” for an in-depth look at resolving conflicts.
If you get a conflict on an update, and you decide to skip resolving it at update time, your file will have conflict markers in it and it will be marked as in conflict by svk:
$ svk update
Syncing //calc(/calc) in /Users/sally/calc to 11.
Conflict found in integer.c:
e)dit, d)iff, m)erge, s)kip, t)heirs, y)ours, h)elp? [e]
You type s<return>
C integer.c
$ cat integer.c
/* integer.c */
>>>> YOUR VERSION integer.c 112192333646711
#include <conflict.h>
==== ORIGINAL VERSION integer.c 112192333646711
==== THEIR VERSION integer.c 112192333646711
#define ZERO 0
#define ONE 1
<<<< 112192333646711
Once you've resolved the conflict and
integer.c is ready to be committed,
run svk resolved integer.c to let SVK
know you've taken care of everything.
svk revert — Undo all local edits.
Reverts any local changes to a file or directory and resolves any conflicted states. svk revert will not only revert the contents of an item in your working copy, but also any property changes. Finally, you can use it to undo any scheduling operations that you may have done (e.g. files scheduled for addition or deletion can be “unscheduled”).
Discard changes to a file:
$ svk revert integer.c Reverted integer.c
If you want to revert a whole directory of files,
use the --recursive flag:
$ svk revert --recursive . Reverted newdir/afile Reverted foo.c Reverted bar.txt
Lastly, you can undo any scheduling operations:
$ svk add mistake.txt whoops A mistake.txt A whoops A whoops/oopsie.c $ svk revert mistake.txt whoops Reverted mistake.txt Reverted whoops $ svk status ? mistake.txt ? whoops
If you provide no targets to svk
revert, it will do nothing—to protect
you from accidentally losing changes in your working
copy, svk revert requires you to
provide at least one target. ##TODO Sadly currently this
is not true and the svk revert actually
defaults to . if no path is
given.
You can use svk revert --recursive together with svk checkout --non-recursive to create a sparse working copy tree. Lets say you have a project called multi with 4 subdirectories and you only need the p1 and p2 subdirectories. To do so you could run:
$ svk checkout --non-recursive //multi Syncing //multi(/multi) in /Users/sally/multi to 11. U multi $ cd multi $ svk revert -R p1 p2 Reverted p1 Reverted p1/foo.c Reverted p2 Reverted p2/bar.c …
svk smerge — Automatically merge all changes between branches.
svk smerge DEPOTPATH [PATH]
svk smerge DEPOTPATH1 DEPOTPATH2
svk smerge --from [TARGET]
svk smerge --to [TARGET]
The first form merges changes from the specified
DEPOTPATH to the working copy
specified by PATH or
. if PATH is
omitted.
The second form performs a direct in depot merge from
DEPOTPATH1 to
DEPOTPATH2.
The third form will merge changes from the closest
copy anchor in the depot of the specified
TARGET—which must be a
depotpath that is a copy of something else, or a working
copy who's depotpath is a copy of something. Directly in
the depot to the source from which TARGET was copied.
Essentially this pushes the changes since the last smerge
on the specified branch to it's parent.
The fourth form will merge changes directly in the
depot to the closest copy anchor in the depot specified by
TARGET—which must be a
depotpath that is a copy of something else, or a working
copy who's depotpath is a copy of something, from the
source of the original upstream copy. Essentially this
pulls the parents changes since the last smerge into the
specified branch.
Working copy if merging to a working copy. Depot if merging to a depotpath, and mirrored repository if the destination is a mirrored depotpath.
--incremental (-I) --log (-l) --baseless (-B) --base (-b) REV[@] --sync (-s) --to (-t) --from (-f) --verbatim --no-ticket --track-rename --host HOST --remoterev --message (-m) --file (-F) --template --encoding ENC --patch (-P) NAME --sign (-S) --check-only (-C) --direct
Let's say you have worked on a project locally in your
svk depot until now, and you've decided it's time to
publish your project and it's history to a remote
Subversion repository. Furthermore let's assume you have
already mirrored the repository in question and there are
empty trunk tags and branches folders in the mirror.
Let's say locally your project is at
//project and the new mirror of the
Subversion reository you wish to publish to is at
//mirrors/project. To publish the
entire history of you local project you would run:
$ svk smerge --baseless --incremental --verbatim //project //mirrors/project/trunk Auto-merging (0, 4) /project to /mirrors/project/trunk (base /:0). ===> Auto-merging (0, 1) /project to /mirrors/project/trunk (base /:0). Merging back to mirror source svn ssh://localhost/Users/sally/project-repo. Empty merge. ===> Auto-merging (1, 2) /project to /mirrors/project/trunk (base /:0). Merging back to mirror source svn ssh://localhost/Users/sally/project-repo. A README New merge ticket: 8b976603-c150-4cc8-864b-160347647051:/project:2 Merge back committed as revision 2. yncing svn ssh://localhost/Users/sally/project-repo Retrieving log information from 2 to 2 Committed revision 7 from revision 2. …
The --baseless switch makes the
smerge assume there is no common ancestor (which there
isn't yet). The --incremental switch
will make svk merge each commit to your local branch as a
separate commit to the upstream repository. And finally
the --verbatim switch will cause the log
messages of the commits to the upstream repository to be
identical to the original ones in your local svk
depot.
svk status — Print the status of working copy files and directories.
Print the status of working copy files and directories. With no arguments, it prints only locally modified items (no repository access).
The first three columns in the output are each one character wide, and each column gives you information about different aspects of each working copy item.
The first column indicates that an item was added, deleted, or otherwise changed.
No modifications.
Item is scheduled for Addition.
Item is scheduled for Deletion.
Item has been Modified.
Item has been Replaced in your working copy.
The contents (as opposed to the properties) of the item conflict with updates received from the depot.
Item is being ignored (e.g. with the
svn:ignore property).
Item is not under version control—not
displayed if --quiet switch is
present.
Item is missing (e.g. you moved or deleted it without using svk). This also indicates that a directory is incomplete (a checkout or update was interrupted).
Item is versioned as one kind of object (file, directory, link), but has been replaced by different kind of object.
The second column tells the status of a file's or directory's properties.
No modifications.
Properties for this item have been modified.
Properties for this item are in conflict with property updates received from the repository.
The third column is populated only if the item is scheduled for addition-with-history.
No history scheduled with commit.
History scheduled with commit.
Remaining fields are variable width and delimited by spaces: The working revision (with -v) The last committed revision and last committed author (with -v) The working copy path is always the final field, so it can include spaces.
do not display files not under version control
disregard default and svn:ignore property ignores
do not descend recursively
print full revision information on every item
This is the easiest way to find out what changes you have made to your working copy:
$ svk status wc M wc/bar.c A wc/qax.c
If you want to find out what files in your working copy are out-of-date, you should use svk update --check-only instead of svk status, see svk update.
For many more examples of svk status, see the section called “svk status”.
svk switch — Update working copy to a different DEPOTPATH.
This subcommand updates your working copy to mirror a new DEPOTPATH—this must be a DEPOTPATH which shares a common ancestor with your working copy. This is the svk way to move a working copy to a new branch. See the section called “Switching a Working Copy” for an in-depth look at switching.
If you're currently inside the directory
vendors which was branched to
vendors-with-fix and you'd like to
switch your working copy to that branch:
$ svk switch //branches/vendors-with-fix Syncing //trunk(/trunk) in /Users/sally/vendors to 31. U myproj/foo.txt U myproj/bar.txt U myproj/baz.c U myproj/qux.c
And to switch back, just provide the DEPOTPATH to the location in the repository from which you originally checked out your working copy:
$ svk switch //trunk Syncing //branches/vendors-with-fix(/branches/vendors-with-fix) in /Users/sally/vendors to 31. U myproj/foo.txt U myproj/bar.txt U myproj/baz.c U myproj/qux.c
It's not possible to switch part of your working copy to a branch. Currently SVK doesn't support mixed depot path working copies.
svk sync — Download upstream changes into mirrored DEPOTPATHs.
The first form downloads upstream changes to the mirror anchored at the specified DEPOTPATH, optionally limited to the specified revision range.. The second form downloads all upstream changes to all mirrors in the specified depots (which can be specified by name or a path in the depot).
To mirror the svkbook sources and just mirror the current HEAD rather than the entire history run:
$ svk mirror svn://svn.clkao.org/svkbook //mirror/svkbook Committed revision 12. $ svk sync --skipto HEAD //mirror/svkbook Retrieving log information from 58 to 58 Committed revision 13 from revision 58.
To download all changes you don't yet have for all your mirrors run:
$ svk sync --all Starting to synchronize //mirror/svkbook Syncing svn://svn.clkao.org/svkbook Retrieving log information from 59 to 60 Committed revision 14 from revision 59. Committed revision 15 from revision 60.
Running svk sync will never modify any working copies, nor will it ever cause any conflicts or merges to happen. This is because it's the only way a mirrored depotpath will ever get written to[21]. So it's safe (and recommended) to run svk sync whenever you have connectivity. Even committing to a mirrored path using svk commit or any other command the does a direct modification of the depot will not actually change the mirrored path directly. Instead it commits to the mirror first and then runs an implicit svk sync for you.
[21] With the exception of the property changes at the mirrors anchor made by svk mirror --relocate.
svk update — Update your working copy.
svk update brings changes from the
depot into your working copy. If no revision given,
it brings your working copy up-to-date with the
HEAD revision. Otherwise, it
synchronizes the working copy to the revision given by the
--revision switch.
For each updated item a line will start with a character reporting the action taken. These characters have the following meaning:
Added
Deleted
Updated
Conflict
Merged
Merged, but the file in your working copy already had the changes being merged so the file is unchanged.
A character in the first column signifies an update to the actual file, while updates to the file's properties are shown in the second column.
--revision (-r) REV --non-recursive (-N) --check-only (-C) --sync (-s) --merge (-m) --quiet (-q)
Pick up repository changes that have happened since your last update:
$ svk update Syncing //myproj(/myproj) in /Users/sally/myproj to 31. A newdir/toggle.c A newdir/disclose.c A newdir/launch.c D newdir/README
You can also update your working copy to an older revision (SVK doesn't have the concept of “sticky” files like CVS does; see Appendix A, SVK for CVS Users):
$ svn update --revision 30 Syncing //myproj(/myproj) in /Users/sally/myproj to 30. A newdir/README D newdir/toggle.c D newdir/disclose.c D newdir/launch.c U foo.c
If you want to examine an older revision of a single file, you may want to use svk cat.
To see what would happen if you ran svk
update without actually modifying your working
copy use the --check-only switch:
$ svk update ---check-only Syncing //myproj(/myproj) in /Users/sally/myproj to 31. A newdir/toggle.c A newdir/disclose.c A newdir/launch.c D newdir/README
svk verify — Verify change signatures.
svk admin is the administrative tool for monitoring and repairing your svk depots. For detailed information, see the section called “”.
Since svk admin works via direct repository access (and thus can only be used on the machine that holds the repository), it refers to the repository with a path, not a URL.
--bdb-log-keep(Berkeley DB specific) Disable automatic log removal of database log files.
--bdb-txn-nosync(Berkeley DB specific) Disables fsync when committing database transactions.
--bypass-hooksBypass the repository hook system.
--clean-logsRemoves unused Berkeley DB logs.
--force-uuidBy default, when loading data into repository that
already contains revisions, svk admin
will ignore the UUID from the dump
stream. This switch will cause the repository's
UUID to be set to the
UUID from the stream.
--ignore-uuidBy default, when loading an empty repository,
svk admin will use the
UUID from the dump stream. This
switch will cause that UUID to be ignored.
--incrementalDump a revision only as a diff against the previous revision, instead of the usual fulltext.
--parent-dir
DIRWhen loading a dump file, root paths at
DIR instead of
/.
--revision (-r)
ARGSpecify a particular revision to operate on.
--quietDo not show normal progress—show only errors.
--use-post-commit-hookWhen loading a dump file, run the repository's post-commit hook after finalizing each newly loaded revision.
--use-pre-commit-hookWhen loading a dump file, run the repository's pre-commit hook before finalizing each newly loaded revision. If the hook fails, abort the commit and terminate the load process.
svk admin create — Create a new, empty repository.
Create a new, empty repository at the path provided.
If the provided directory does not exist, it will be
created for you.[22] As of
Subversion 1.2, svk admin creates new
repositories with the fsfs filesystem
backend by default.
Creating a new repository is just this easy:
$ svk admin create /usr/local/svn/repos
In Subversion 1.0, a Berkeley DB repository is always
created. In Subversion 1.1, a Berkeley DB repository is
the default repository type, but an FSFS repository can be
created using the --fs-type
switch:
$ svk admin create /usr/local/svn/repos --fs-type fsfs
svk admin deltify — Deltify changed paths in a revision range.
svk admin deltify only exists in 1.0.x due to historical reasons. This command is deprecated and no longer needed.
It dates from a time when Subversion offered administrators greater control over compression strategies in the repository. This turned out to be a lot of complexity for very little gain, and this “feature” was deprecated.
svk admin dump — Dump the contents of filesystem to stdout.
Dump the contents of filesystem to stdout in a
“dumpfile” portable format, sending feedback
to stderr. Dump revisions
LOWER rev through
UPPER rev. If no revisions are
given, dump all revision trees. If only
LOWER is given, dump that one
revision tree. See the section called “”
for a practical use.
If the size of your Subversion dumpfile is a concern,
you can use the --deltas switch to (in
some cases drastically) reduce the size of the dumpfile
that svk admin creates. With this
switch, instead of writing the full content of each
revision to the dumpfile, svk admin dump
will only emit the differences from one revision to the
next. There are, however, disadvantages to creating
deltified dumpfiles—it's more CPU intensive to
create them, svndumpfilter can't
operate on them, and non-deltified dumpfiles tend to
compress better.
Dump your whole repository:
$ svk admin dump /usr/local/svn/repos SVN-fs-dump-format-version: 1 Revision-number: 0 * Dumped revision 0. Prop-content-length: 56 Content-length: 56 …
Incrementally dump a single transaction from your repository:
$ svk admin dump /usr/local/svn/repos --revision 21 --incremental * Dumped revision 21. SVN-fs-dump-format-version: 1 Revision-number: 21 Prop-content-length: 101 Content-length: 101 …
svk admin help
svk admin hotcopy — Make a hot copy of a repository.
This subcommand makes a full “hot” backup
of your repository, including all hooks, configuration
files, and, of course, database files. If you pass the
--clean-logs switch,
svk admin will perform a hotcopy of your
repository, and then remove unused Berkeley DB logs from
the original repository. You can run this command at any
time and make a safe copy of the repository, regardless of
whether other processes are using the repository.
svk admin list-dblogs — Ask
Berkeley DB which log files exist for a given Subversion
repository (applies only to repositories using the
bdb backend).
Berkeley DB creates logs of all changes to the
repository, which allow it to recover in the face of
catastrophe. Unless you enable
DB_LOGS_AUTOREMOVE, the log files
accumulate, although most are no longer used and can be
deleted to reclaim disk space. See the section called “” for more
information.
svk admin list-unused-dblogs — Ask Berkeley DB which log files can be safely
deleted (applies only to repositories using the
bdb backend).
Berkeley DB creates logs of all changes to the
repository, which allow it to recover in the face of
catastrophe. Unless you enable
DB_LOGS_AUTOREMOVE, the log files
accumulate, although most are no longer used and can be
deleted to reclaim disk space. See the section called “” for more
information.
svk admin load — Read a “dumpfile”-formatted stream from stdin.
Read a “dumpfile”-formatted stream from stdin, committing new revisions into the repository's filesystem. Send progress feedback to stdout.
--quiet (-q) --ignore-uuid --force-uuid --use-pre-commit-hook --use-post-commit-hook --parent-dir
This shows the beginning of loading a repository from a backup file (made, of course, with svk admin dump):
$ svk admin load /usr/local/svn/restored < repos-backup
<<< Started new txn, based on original revision 1
* adding path : test ... done.
* adding path : test/a ... done.
…
Or if you want to load into a subdirectory:
$ svk admin load --parent-dir new/subdir/for/project /usr/local/svn/restored < repos-backup
<<< Started new txn, based on original revision 1
* adding path : test ... done.
* adding path : test/a ... done.
…
svk admin lslocks — Print descriptions of all locks.
This lists the one locked file in the repository at
/svn/repos
$ svk admin lslocks /svn/repos Path: /tree.jpg UUID Token: opaquelocktoken:ab00ddf0-6afb-0310-9cd0-dda813329753 Owner: harry Created: 2005-07-08 17:27:36 -0500 (Fri, 08 Jul 2005) Expires: Comment (1 line): Rework the uppermost branches on the bald cypress in the foreground.
svk admin lstxns — Print the names of all uncommitted transactions.
Print the names of all uncommitted transactions. See the section called “” for information on how uncommitted transactions are created and what you should do with them.
svk admin recover — Bring a repository database back into a
consistent state (applies only to repositories using the
bdb backend). In addition, if
repos/conf/passwd does not exist, it
will create a default password file .
Run this command if you get an error indicating that your repository needs to be recovered.
Recover a hung repository:
$ svk admin recover /usr/local/svn/repos/ Repository lock acquired. Please wait; recovering the repository may take some time... Recovery completed. The latest repos revision is 34.
Recovering the database requires an exclusive lock on the repository. If another process is accessing the repository, then svk admin recover will error:
$ svk admin recover /usr/local/svn/repos svn: Failed to get exclusive repository access; perhaps another process such as httpd, svnserve or svn has it open? $
The --wait switch, however, will
cause svk admin recover to wait
indefinitely for other processes to disconnect:
$ svk admin recover /usr/local/svn/repos --wait Waiting on repository lock; perhaps another process has it open? ### time goes by... Repository lock acquired. Please wait; recovering the repository may take some time... Recovery completed. The latest repos revision is 34.
svk admin rmtxns — Delete transactions from a repository.
Delete outstanding transactions from a repository. This is covered in detail in the section called “”.
Remove named transactions:
$ svk admin rmtxns /usr/local/svn/repos/ 1w 1x
Fortunately, the output of lstxns works great as the input for rmtxns:
$ svk admin rmtxns /usr/local/svn/repos/ `svk admin lstxns /usr/local/svn/repos/`
Which will remove all uncommitted transactions from your repository.
svk admin setlog — Set the log-message on a revision.
Set the log-message on revision REVISION to the contents of FILE.
This is similar to using svn propset
--revprop to set the svn:log property
on a revision, except that you can also use the option
--bypass-hooks to avoid running any pre- or
post-commit hooks, which is useful if the modification of
revision properties has not been enabled in the pre-revprop-change
hook.
Revision properties are not under version control, so this command will permanently overwrite the previous log message.
svk admin verify — Verify the data stored in the repository.
svk admin rmcache — Purge the inode/mtime/size cache for working copies.
Use this command to remove SVK's cache of inode, mtime, size tuples for files for all working copies. Having the cache around dramatically speeds up most working copy related SVK commands, however SVK might sometimes leave unused cache entries around—for example entries from since detached working copies. Running this command purges the cache and frees up some disk space.
[19] Yes, yes, you don't need a subcommand to use the
--version switch, but we'll get to that in just
a minute.
[20] While this true for the long versions of all switches there is some aliasing going on with the short (one letter versions) of some.
[21] With the exception of the property changes at the mirrors anchor made by svk mirror --relocate.
[22] Remember, svk admin works only with local paths, not URLs.
Table of Contents
This chapter goes into greater depth about the depot. It explains what depots are, and how you manage them and how you can best make use of them.
The primary focus of this chapter is the depotmap command, and is best read with reference to svk depotmap.
As you will already know, a depot is SVK's primary storage area. It is the place from which all of your code is checked out, and to where you commit your changes and mirror other repositories. Up to this point this book has intentionally kept the inner workings of the depot away from the reader, as they are not essential to using SVK for most tasks, and would undoubtedly confuse the new user unnecessarily.
However, knowing more about how depots work and how to manage them can be of use to all users of SVK in terms of flexibility, and can help the reader gain a deeper understanding of SVK; beyond the basics.
As was explained in the section called “The Depot”
depot is simply a Subversion repository, which can be located
anywhere in the filesystem. The Subversion file system used for
this repository is defined by the $SVNFSTYPE
environment variable. If $SVNFSTYPE is not
set, the file system is determined by the version of the
Subversion client installed (fsfs for version 1.1 and newer, bdb
for older versions of Subversion).
Outside of this Subversion repository, SVK also keeps
higher-level information about the depot in the
$SVKROOT/config file. The two key pieces of
information stored about a depot are:
DEPOTNAMEThe DEPOTNAME is a symbolic
name give to the depot which makes up a part of the
DEPOTPATHs that you pass to SVK commands.
REPOPATHThe REPOPATH is the full
absolute path to the Subversion repository itself.
Is is possible to have multiple depots in a single user's SVK configuration. Of course, you need to have at least one depot in order to use SVK, and after SVK was first installed you created this depot before doing anything else. This is the depot that you have been using throughout the book up to this point, and is called the Default Depot.
The default depot is special because it has no
DEPOTNAME. Or rather, its
DEPOTNAME is the empty string. By
default its REPOPATH is
$SVKROOT/local.
The Default Depot is created using the
depotmap --init command. This is the only
time that the --init switch is used with
the depotmap command
For many users, the default depot is all that is needed. However, some users find it useful to be able to create additional named depots.
You can create as many additional named depots as you like. Depots are created using svk depotmap command.
For example, to create a new depot with
DEPOTNAME 'test'
and REPOPATH
/tmp/test-depot, you can use this
command:
$ svk depotmap test /tmp/test-depot New depot map saved. Repository /tmp/test-depot does not exist, create? (y/n)y
Note that depotmap prompts you to
create the REPOPATH if it does not
already exist. This allows you to create more than one
DEPOTNAME pointing at the same
REPOPATH if you want:
$ svk depotmap test2 /tmp/test-depot New depot map saved.
In Depot Paths
At this point you may be wondering why you'd want to use multiple depots. After all, a depot is just a Subversion repository, and that means you are free to structure your depot in any way you wish, with the root folder perhaps containing a folder for each project you're working on, with each project possibly containing a mirror for that project.
Indeed, many people do use SVK just like this: with everything placed in the default depot, and often never even realizing that it is possible to have more than one depot.
There are however some good reasons why you might want to use more than one depot:
Imagine that you are mirroring two repositories: an Open Source project and also a project for your employer. You might want to keep the two mirrors in separate depots so that the two projects can't affect each other.
You are free to create depots anywhere you have permission to. You may want to create a depot on a partition that has a lot of space, for example if you are mirroring a particularly large repository.
A project in its own depot can easily be thrown away completely without having any effect on other projects. If the projects were in the same depot you could of course delete the relevant paths in the depot, but the history of the deleted paths would remain, which may be precisely what you are trying to remove.
You may want to use different depots for different purposes. For example, you might have one depot for mirrors, and another for your personal depot containing your local repository that only you commit to.
If you are using the fsfs Subversion filesystem for your depot, it is possible to host a depot on a remote filesystem mounted via NFS. This allows you to share the same depot with other developers.
While using multiple depots has many advantages, there are a few things that you should consider before using them. Different depots really are completely separate entities: you can't create a branch in one depot that is a copy of a directory in another, and you can't do merging between them.
Most of the time depots just sit there and are used by the other SVK commands that you use in general operation, but occasionally depots needs to be operated on directly. This section describes how to do the more general depot administration operations.
For reference documentation on the depotmap command that is used throughout this section, see svk depotmap.
When dealing with depots it's often useful to be able to
find out what depots you have on your system. This is
accomplished using the depotmap command
with the --list option. For example:
$ svk depotmap --list Depot Path ============================================================ // /home/me/.svk/local /my_project/ /home/me/.svk/my_project /my_company/ /var/share/depot
The left column lists the depot path, while the right column lists the actual path to the depot on your filesystem. In this case we see that two of the depots this user has mapped to are located in their home directory, while the other is located in a shared place in the filesystem. The first depot listed is the default depot.
If you reach a point where you no longer want to treat
a depot as a depot (for example if you want to use it as a
repository or you want to delete it completely) you can
tell SVK to forget about it using the
--detach option. For example:
$ svk depotmap --detach /my_project Depot 'my_project' detached.
Use the --list to see that the depot
is no longer known by SVK
$ svk depotmap --list Depot Path ============================================================ // /home/me/.svk/local /my_company/ /var/share/depot
Note that if you do this by mistake you can undo the
operation by mapping the depot again. For example, to
undo the --detach operation above do
this:
$ svk depotmap my_project /var/share/depot New depot map saved. $ svk depotmap --list Depot Path ============================================================ // /home/me/.svk/local /my_project/ /home/me/.svk/my_project /my_company/ /var/share/depot
The --list command shows that the depot
is once again known about by SVK.
Copyright (c) 2002-2005
Ben Collins-Sussman, Brian W. Fitzpatrick, C. Michael Pilato.
This work is licensed under the Creative Commons Attribution License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by/2.0/ or send a letter to
Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305,
USA.
A summary of the license is given below, followed by the full legal
text.
--------------------------------------------------------------------
You are free:
* to copy, distribute, display, and perform the work
* to make derivative works
* to make commercial use of the work
Under the following conditions:
Attribution. You must give the original author credit.
* For any reuse or distribution, you must make clear to others the
license terms of this work.
* Any of these conditions can be waived if you get permission from
the author.
Your fair use and other rights are in no way affected by the above.
The above is a summary of the full license below.
====================================================================
Creative Commons Legal Code
Attribution 2.0
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
DAMAGES RESULTING FROM ITS USE.
License
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS
PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE
WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS
PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND
AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS
YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF
SUCH TERMS AND CONDITIONS.
1. Definitions
a. "Collective Work" means a work, such as a periodical issue,
anthology or encyclopedia, in which the Work in its entirety in
unmodified form, along with a number of other contributions,
constituting separate and independent works in themselves, are
assembled into a collective whole. A work that constitutes a
Collective Work will not be considered a Derivative Work (as
defined below) for the purposes of this License.
b. "Derivative Work" means a work based upon the Work or upon the
Work and other pre-existing works, such as a translation,
musical arrangement, dramatization, fictionalization, motion
picture version, sound recording, art reproduction, abridgment,
condensation, or any other form in which the Work may be recast,
transformed, or adapted, except that a work that constitutes a
Collective Work will not be considered a Derivative Work for the
purpose of this License. For the avoidance of doubt, where the
Work is a musical composition or sound recording, the
synchronization of the Work in timed-relation with a moving
image ("synching") will be considered a Derivative Work for the
purpose of this License.
c. "Licensor" means the individual or entity that offers the Work
under the terms of this License.
d. "Original Author" means the individual or entity who created the Work.
e. "Work" means the copyrightable work of authorship offered under
the terms of this License.
f. "You" means an individual or entity exercising rights under this
License who has not previously violated the terms of this
License with respect to the Work, or who has received express
permission from the Licensor to exercise rights under this
License despite a previous violation.
2. Fair Use Rights. Nothing in this license is intended to reduce,
limit, or restrict any rights arising from fair use, first sale or
other limitations on the exclusive rights of the copyright owner
under copyright law or other applicable laws.
3. License Grant. Subject to the terms and conditions of this License,
Licensor hereby grants You a worldwide, royalty-free,
non-exclusive, perpetual (for the duration of the applicable
copyright) license to exercise the rights in the Work as stated
below:
a. to reproduce the Work, to incorporate the Work into one or more
Collective Works, and to reproduce the Work as incorporated in
the Collective Works;
b. to create and reproduce Derivative Works;
c. to distribute copies or phonorecords of, display publicly,
perform publicly, and perform publicly by means of a digital
audio transmission the Work including as incorporated in
Collective Works;
d. to distribute copies or phonorecords of, display publicly,
perform publicly, and perform publicly by means of a digital
audio transmission Derivative Works.
e.
For the avoidance of doubt, where the work is a musical composition:
i. Performance Royalties Under Blanket Licenses. Licensor
waives the exclusive right to collect, whether
individually or via a performance rights society
(e.g. ASCAP, BMI, SESAC), royalties for the public
performance or public digital performance (e.g. webcast)
of the Work.
ii. Mechanical Rights and Statutory Royalties. Licensor waives
the exclusive right to collect, whether individually or
via a music rights agency or designated agent (e.g. Harry
Fox Agency), royalties for any phonorecord You create from
the Work ("cover version") and distribute, subject to the
compulsory license created by 17 USC Section 115 of the US
Copyright Act (or the equivalent in other jurisdictions).
f. Webcasting Rights and Statutory Royalties. For the avoidance of
doubt, where the Work is a sound recording, Licensor waives the
exclusive right to collect, whether individually or via a
performance-rights society (e.g. SoundExchange), royalties for
the public digital performance (e.g. webcast) of the Work,
subject to the compulsory license created by 17 USC Section 114
of the US Copyright Act (or the equivalent in other
jurisdictions).
The above rights may be exercised in all media and formats whether now
known or hereafter devised. The above rights include the right to make
such modifications as are technically necessary to exercise the rights
in other media and formats. All rights not expressly granted by
Licensor are hereby reserved.
4. Restrictions.The license granted in Section 3 above is expressly
made subject to and limited by the following restrictions:
a. You may distribute, publicly display, publicly perform, or
publicly digitally perform the Work only under the terms of this
License, and You must include a copy of, or the Uniform Resource
Identifier for, this License with every copy or phonorecord of
the Work You distribute, publicly display, publicly perform, or
publicly digitally perform. You may not offer or impose any
terms on the Work that alter or restrict the terms of this
License or the recipients' exercise of the rights granted
hereunder. You may not sublicense the Work. You must keep intact
all notices that refer to this License and to the disclaimer of
warranties. You may not distribute, publicly display, publicly
perform, or publicly digitally perform the Work with any
technological measures that control access or use of the Work in
a manner inconsistent with the terms of this License
Agreement. The above applies to the Work as incorporated in a
Collective Work, but this does not require the Collective Work
apart from the Work itself to be made subject to the terms of
this License. If You create a Collective Work, upon notice from
any Licensor You must, to the extent practicable, remove from
the Collective Work any reference to such Licensor or the
Original Author, as requested. If You create a Derivative Work,
upon notice from any Licensor You must, to the extent
practicable, remove from the Derivative Work any reference to
such Licensor or the Original Author, as requested.
b. If you distribute, publicly display, publicly perform, or
publicly digitally perform the Work or any Derivative Works or
Collective Works, You must keep intact all copyright notices for
the Work and give the Original Author credit reasonable to the
medium or means You are utilizing by conveying the name (or
pseudonym if applicable) of the Original Author if supplied; the
title of the Work if supplied; to the extent reasonably
practicable, the Uniform Resource Identifier, if any, that
Licensor specifies to be associated with the Work, unless such
URI does not refer to the copyright notice or licensing
information for the Work; and in the case of a Derivative Work,
a credit identifying the use of the Work in the Derivative Work
(e.g., "French translation of the Work by Original Author," or
"Screenplay based on original Work by Original Author"). Such
credit may be implemented in any reasonable manner; provided,
however, that in the case of a Derivative Work or Collective
Work, at a minimum such credit will appear where any other
comparable authorship credit appears and in a manner at least as
prominent as such other comparable authorship credit.
5. Representations, Warranties and Disclaimer
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING,
LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR
WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED,
STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF
TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE,
NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY,
OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED
WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY
APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY
LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE
OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE
WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
7. Termination
a. This License and the rights granted hereunder will terminate
automatically upon any breach by You of the terms of this
License. Individuals or entities who have received Derivative
Works or Collective Works from You under this License, however,
will not have their licenses terminated provided such
individuals or entities remain in full compliance with those
licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any
termination of this License.
b. Subject to the above terms and conditions, the license granted
here is perpetual (for the duration of the applicable copyright
in the Work). Notwithstanding the above, Licensor reserves the
right to release the Work under different license terms or to
stop distributing the Work at any time; provided, however that
any such election will not serve to withdraw this License (or
any other license that has been, or is required to be, granted
under the terms of this License), and this License will continue
in full force and effect unless terminated as stated above.
8. Miscellaneous
a. Each time You distribute or publicly digitally perform the Work
or a Collective Work, the Licensor offers to the recipient a
license to the Work on the same terms and conditions as the
license granted to You under this License.
b. Each time You distribute or publicly digitally perform a
Derivative Work, Licensor offers to the recipient a license to
the original Work on the same terms and conditions as the
license granted to You under this License.
c. If any provision of this License is invalid or unenforceable
under applicable law, it shall not affect the validity or
enforceability of the remainder of the terms of this License,
and without further action by the parties to this agreement,
such provision shall be reformed to the minimum extent necessary
to make such provision valid and enforceable.
d. No term or provision of this License shall be deemed waived and
no breach consented to unless such waiver or consent shall be in
writing and signed by the party to be charged with such waiver
or consent.
e. This License constitutes the entire agreement between the
parties with respect to the Work licensed here. There are no
understandings, agreements or representations with respect to
the Work not specified here. Licensor shall not be bound by any
additional provisions that may appear in any communication from
You. This License may not be modified without the mutual written
agreement of the Licensor and You.
Creative Commons is not a party to this License, and makes no warranty
whatsoever in connection with the Work. Creative Commons will not be
liable to You or any party on any legal theory for any damages
whatsoever, including without limitation any general, special,
incidental or consequential damages arising in connection to this
license. Notwithstanding the foregoing two (2) sentences, if Creative
Commons has expressly identified itself as the Licensor hereunder, it
shall have all rights and obligations of Licensor.
Except for the limited purpose of indicating to the public that the
Work is licensed under the CCPL, neither party will use the trademark
"Creative Commons" or any related trademark or logo of Creative
Commons without the prior written consent of Creative Commons. Any
permitted use will be in compliance with Creative Commons'
then-current trademark usage guidelines, as may be published on its
website or otherwise made available upon request from time to time.
Creative Commons may be contacted at http://creativecommons.org/.
====================================================================