At this point, you should understand how each commit creates an entire new filesystem tree (called a "revision") in the repository. 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 repository that contains two projects, paint and calc. Notice, however, that this time somebody has created two new top-level directories in the repository, called trunk and branches. The projects themselves are subdirectories of trunk, which we'll explain later on.
As before, assume that you and Sally both have working copies of the /trunk/calc project.
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 calc, 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 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 repository 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 /trunk/calc 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. Finally, when you're finished, you might find it very difficult to commit your changes. Sally (or others) may have made many other changes in the repository that are difficult to merge into your working copy—especially all at once.
The better solution is to create your own branch, or line of development, in the repository. 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 repository using the svn copy command. Subversion is not only able to copy single files, but whole directories as well. In this case, you want to make a copy of the /trunk/calc 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 /branches/calc area of the repository, and you want to name your branch "my-calc-branch". You'll want to create a new directory, /branches/calc/my-calc-branch, which starts as a copy of /trunk/calc.
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 root (/) of the repository:
$ svn checkout http://svn.example.com/repos bigwc A bigwc/branches/ A bigwc/branches/calc A bigwc/branches/paint A bigwc/trunk/ A bigwc/trunk/calc A bigwc/trunk/calc/Makefile A bigwc/trunk/calc/integer.c A bigwc/trunk/calc/button.c A bigwc/trunk/paint A bigwc/trunk/paint/Makefile A bigwc/trunk/paint/canvas.c A bigwc/trunk/paint/brush.c Checked out revision 340.
Making a copy is now simply a matter of passing two working-copy paths to the svn copy command:
$ cd bigwc $ svn copy trunk/calc branches/calc/my-calc-branch $ svn status A + branches/calc/my-calc-branch
In this case, the svn copy command recursively copies the trunk/calc working directory to a new working directory, branches/calc/my-calc-branch. As you can see from the svn status command, the new directory is now scheduled for addition to the repository. But also notice the + sign next to the letter A. This indicates that the scheduled addition is a copy of something, not something new. When you commit your changes, Subversion will create /branches/calc/my-calc-branch in the repository by copying /trunk/calc, rather than resending all of the working copy data over the network:
$ svn commit -m "Creating a private branch of /trunk/calc." Adding branches/calc/my-calc-branch Committed revision 341.
And now the easier method of creating a branch, which we should have told you about in first place: svn copy is able to operate on two URLs.
$ svn copy http://svn.example.com/repos/trunk/calc \ http://svn.example.com/repos/branches/calc/my-calc-branch \ -m "Creating a private branch of /trunk/calc" Committed revision 341.
There's really no difference between these two methods. Both procedures create a new directory in revision 341, and the new directory is a copy of /trunk/calc. Notice that the second method, however, performs an immediate commit. It's an easier procedure, because it doesn't require you to check out a large mirror of the repository. In fact, this technique doesn't even require you to have a working copy at all.
Now that you've created a new branch of the project, you can check out a new working copy to start using it:
$ svn checkout http://svn.example.com/repos/branches/calc/my-calc-branch A my-calc-branch/Makefile A my-calc-branch/integer.c A my-calc-branch/button.c Checked out revision 341.
There's nothing special about this working copy; it simply mirrors a different location of the repository. When you commit changes, however, Sally won't ever see them when she updates. Her working copy is of /trunk/calc.
Let's pretend that a week goes by, and the following commits happen:
You make a change to /branches/calc/my-calc-branch/button.c, which creates revision 342.
You make a change to /branches/calc/my-calc-branch/integer.c, which creates revision 343.
Sally makes a change to /trunk/calc/integer.c, which creates revision 344.
There are now two independent lines of development 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 $ svn log integer.c ------------------------------------------------------------------------ rev 343: user | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines * integer.c: frozzled the wazjub. ------------------------------------------------------------------------ rev 303: sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines * integer.c: changed a docstring. ------------------------------------------------------------------------ rev 98: sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines * integer.c: adding this file to the project. ------------------------------------------------------------------------
Notice that Subversion is tracing the history of your integer.c all the way back through time, traversing the point where it was copied. (Remember that your branch was created in revision 341.) Now look what happens when Sally runs the same command on her copy of the file:
$ pwd /home/sally/calc $ svn log integer.c ------------------------------------------------------------------------ rev 344: sally | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines * integer.c: fix a bunch of spelling errors. ------------------------------------------------------------------------ rev 303: sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines * integer.c: changed a docstring. ------------------------------------------------------------------------ rev 98: sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines * integer.c: adding this file to the project. ------------------------------------------------------------------------
Sally sees her own revision 344 change, but not the change you made in revision 343. As far as Subversion is concerned, these two commits affected different files in different repository locations. However, Subversion does show that the two files share a common history. Before the branch-copy was made in revision 341, they used to be the same file. That's why you and Sally both see revisions 303 and 98.
There are two important lessons that you should remember from this section.
Unlike many other version control systems, Subversion's branches exist as normal filesystem directories in the repository, not in some extra dimension.
Subversion 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 Subversion it's just an ordinary directory that happens to have been created by copying.