SVK in Action

It's time to move from the abstract to the concrete. In this section, we'll show real examples of SVK being used.

Working Copies

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”.

Figure 2.5. The depot's filesystem

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.

Revisions

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.

Figure 2.6. The depot

The depot

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.

How Working Copies Track the Depot

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:

Unchanged, and current

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.

Locally changed, and current

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.

Unchanged, and out-of-date

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.

Locally changed, and out-of-date

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.

Mixed Revision Working Copies

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.

Updates and Commits are Separate

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.

Mixed revisions are normal

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.

Mixed revisions are useful

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.

Mixed revisions have limitations

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.



[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.