Using Branches

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.

Figure 4.2. Starting depot layout

Starting depot layout

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

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.

Figure 4.3. Depot with new copy

Depot with new copy

Working with Your Branch

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.

Figure 4.4. The branching of one file's history

The branching of one file's history

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.

The Key Concepts Behind Branches

There are two important lessons that you should remember from this section.

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

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



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