# Branching and Merging

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 Subversion implements these ideas.

Branching is a fundamental part of version control. If you're going to allow Subversion 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 Subversion's basic concepts ().

## What's a Branch?

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

### Branches of development

Subversion 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 can make portions of your working copy reflect different branches, so that you can mix and match different lines of development in your daily work.

## Using Branches

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 .

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 that in , however, each project directory now contains subdirectories named `trunk` and `branches`. The reason for this will soon become clear.

### Starting repository 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 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 `/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 repository that are difficult to incorporate into your working copy—especially if you run `svn update` after weeks of isolation.

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

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 `/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`:

    $ svn checkout http://svn.example.com/repos/calc bigwc
    A  bigwc/trunk/
    A  bigwc/trunk/Makefile
    A  bigwc/trunk/integer.c
    A  bigwc/trunk/button.c
    A  bigwc/branches/
    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 branches/my-calc-branch
    $ svn status
    A  +   branches/my-calc-branch

In this case, the `svn copy` command recursively copies the `trunk` working directory to a new working directory, `branches/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 `/calc/branches/my-calc-branch` in the repository by copying `/calc/trunk`, rather than resending all of the working copy data over the network:

    $ svn commit -m "Creating a private branch of /calc/trunk."
    Adding         branches/my-calc-branch
    Committed revision 341.

And now the easier method of creating a branch, which we should have told you about in the first place: `svn copy` is able to operate directly on two URLs.

    $ svn copy http://svn.example.com/repos/calc/trunk \
               http://svn.example.com/repos/calc/branches/my-calc-branch \
          -m "Creating a private branch of /calc/trunk."

    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 `/calc/trunk`. This is shown in . Notice that the second method, however, performs an immediate commit. Subversion does not support cross-repository copying. When using URLs with `svn copy` or `svn move`, you can only copy items within the same repository. 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.

#### Repository with new copy

#### Cheap Copies

Subversion's repository has a special design. When you copy a directory, you don't need to worry about the repository growing huge—Subversion doesn't actually duplicate any data. Instead, it creates a new directory entry that points to an existing tree. If you're a Unix user, this is the same concept as a hard-link. From there, the copy is said to be lazy. That is, if you commit a change to one file within the copied directory, then only that file changes—the rest of the files continue to exist as links to the original files in the original directory.

This is why you'll often hear Subversion users talk about cheap copies. It doesn't matter how large the directory is—it takes a very tiny, constant amount of time to make a copy of it. In fact, this feature is the basis of how commits work in Subversion: each revision is a cheap copy of the previous revision, with a few items lazily changed within. (To read more about this, visit Subversion's website and read about the bubble up method in Subversion's design documents.)

Of course, these internal mechanics of copying and sharing data are hidden from the user, who simply sees copies of trees. The main point here is that copies are cheap, both in time and space. Make branches as often as you want.

### 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:

    $ svn checkout http://svn.example.com/repos/calc/branches/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 directory in the repository. 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 later in this chapter: the `svn 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/button.c`, which creates revision 342.

- You make a change to `/calc/branches/my-calc-branch/integer.c`, which creates revision 343.

- Sally makes a change to `/calc/trunk/integer.c`, which creates revision 344.

There are now two independent lines of development, shown in , happening on `integer.c`.

#### 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

    $ svn log --verbose integer.c
    ------------------------------------------------------------------------
    r343 | user | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
    Changed paths:
       M /calc/branches/my-calc-branch/integer.c

    * integer.c:  frozzled the wazjub.

    ------------------------------------------------------------------------
    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)

    Creating a private branch of /calc/trunk.

    ------------------------------------------------------------------------
    r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
    Changed paths:
       M /calc/trunk/integer.c

    * integer.c:  changed a docstring.

    ------------------------------------------------------------------------
    r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
    Changed paths:
       M /calc/trunk/integer.c

    * integer.c:  adding this file to the project.

    ------------------------------------------------------------------------

Notice that Subversion is tracing the history of your branch's `integer.c` all the way back through time, even traversing 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. Now look what happens when Sally runs the same command on her copy of the file:

    $ pwd
    /home/sally/calc

    $ svn log --verbose integer.c
    ------------------------------------------------------------------------
    r344 | sally | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
    Changed paths:
       M /calc/trunk/integer.c

    * integer.c:  fix a bunch of spelling errors.

    ------------------------------------------------------------------------
    r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
    Changed paths:
       M /calc/trunk/integer.c

    * integer.c:  changed a docstring.

    ------------------------------------------------------------------------
    r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
    Changed paths:
       M /calc/trunk/integer.c

    * 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 the changes made in revisions 303 and 98.

### The Key Concepts Behind Branches

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

1. Unlike many other version control systems, Subversion's branches exist as normal filesystem directories in the repository, not in an extra dimension. These directories just happen to carry some extra historical information.

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

## Copying Changes Between Branches

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; Subversion 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.

### Copying Specific Changes

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 `svn merge` command. This command, it turns out, is a very close cousin to the `svn 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 `svn diff` to show you the exact change made by Sally in revision 344:

    $ svn diff -r 343:344 http://svn.example.com/repos/calc/trunk

    Index: integer.c
    ===================================================================
    --- integer.c	(revision 343)
    +++ integer.c	(revision 344)
    @@ -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 `svn merge` 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:

    $ svn merge -r 343:344 http://svn.example.com/repos/calc/trunk
    U  integer.c

    $ svn status
    M  integer.c

The output of `svn 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 `svn revert` the local change.

But assuming that you've reviewed the merged change, you can `svn 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:

    $ svn 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.

#### Why Not Use Patches Instead?

A question may be on your mind, especially if you're a Unix user: why bother to use `svn merge` at all? Why not simply use the operating system's `patch` command to accomplish the same job? For example:

    $ svn diff -r 343:344 http://svn.example.com/repos/calc/trunk > patchfile
    $ patch -p0  < patchfile
    Patching file integer.c using Plan A...
    Hunk #1 succeeded at 147.
    Hunk #2 succeeded at 164.
    Hunk #3 succeeded at 241.
    Hunk #4 succeeded at 249.
    done

In this particular case, yes, there really is no difference. But `svn merge` has special abilities that surpass the `patch` program. The file format used by `patch` is quite limited; it's only able to tweak file contents. There's no way to represent changes to trees, such as the addition, removal, or renaming of files and directories. If Sally's change had, say, added a new directory, the output of `svn diff` wouldn't have mentioned it at all. `svn diff` only outputs the limited patch-format, so there are some ideas it simply can't express. In the future, the Subversion project plans to use (or invent) an expanded patch format that describes tree-changes. The `svn merge` command, however, can express tree-changes by directly applying them to your working copy.

A word of warning: while `svn diff` and `svn 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 `svn help`. For example, `svn 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:

1. You want to merge directory changes into your current working directory.

2. 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, `svn 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, `svn 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:

    $ svn merge -r 343:344 http://svn.example.com/repos/calc/trunk my-calc-branch
    U   my-calc-branch/integer.c

### Best Practices for Merging

#### Tracking Merges Manually

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 1.0 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 `svn 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 `svn log` to review which changes your branch already contains. This will allow you to carefully construct a subsequent `svn 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.

#### Previewing Merges

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 `svn 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 `svn 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 `svn diff` with the same arguments you plan to pass to `svn 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:

    $ svn merge --dry-run -r 343:344 http://svn.example.com/repos/calc/trunk
    U  integer.c

    $ svn 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 `svn diff` gives too much detail.

#### Subversion and Changesets

Everyone seems to have a slightly different definition of changeset, or a least a different expectation of what it means for a version control system to have changeset features. For our purpose, let's say that a changeset is just a collection of changes with a unique name. The changes might include textual edits to file contents, modifications to tree structure, or tweaks to metadata. In more common speak, a changeset is just a patch with a name you can refer to.

In Subversion, a global revision number N names a tree in the repository: it's the way the repository looked after the Nth commit. It's also the name of an implicit changeset: if you compare tree N with tree N-1, you can derive the exact patch that was committed. For this reason, it's easy to think of revision N as not just a tree, but a changeset as well. If you use an issue tracker to manage bugs, you can use the revision numbers to refer to particular patches that fix bugs—for example, this issue was fixed by revision 9238.. Somebody can then run `svn log -r9238` to read about the exact changeset which fixed the bug, and run `svn diff -r9237:9238` to see the patch itself. And Subversion's merge command also uses revision numbers. You can merge specific changesets from one branch to another by naming them in the merge arguments: `svn merge -r9237:9238` would merge changeset #9238 into your working copy.

#### Noticing or Ignoring Ancestry

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, than 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 `svn diff` and `svn merge`. The former command ignores ancestry, while the latter command is quite sensitive to it. For example, if you asked `svn 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 `svn 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 `svn 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 ). If you asked `svn 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 `svn 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 `svn diff`. (And conversely, the `--notice-ancestry` option will cause `svn diff` to behave like the merge command.)

## Common Use-Cases for Merging

There are many different uses for `svn merge`, and this section describes the most common ones you're likely to run into.

### Merging a Whole Branch to Another

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 bugfix 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 `svn 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 `svn merge` operates like `svn 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 `svn 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 `svn 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 `svn log` detects that its target was copied or renamed.

So in our continuing example,

    $ svn log --verbose --stop-on-copy \
              http://svn.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
    $ svn update
    At revision 405.

    $ svn merge -r 341:HEAD http://svn.example.com/repos/calc/branches/my-calc-branch
    U   integer.c
    U   button.c
    U   Makefile

    $ svn status
    M   integer.c
    M   button.c
    M   Makefile

    # ...examine the diffs, compile, test, etc...

    $ svn 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 bugfix. 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 , 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 `svn log` on the trunk, and look for a log message about the last time you merged from the branch:

    $ cd calc/trunk
    $ svn 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
    $ svn update
    At revision 480.

    # We notice that HEAD is currently 480, so we use it to do the merge:

    $ svn merge -r 406:480 http://svn.example.com/repos/calc/branches/my-calc-branch
    U   integer.c
    U   button.c
    U   Makefile

    $ svn 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.

### Undoing Changes

Another common use for `svn 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 `svn 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:

    $ svn merge -r 303:302 http://svn.example.com/repos/calc/trunk
    U  integer.c

    $ svn status
    M  integer.c

    $ svn diff
    …
    # verify that the change is removed
    …

    $ svn 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 `svn merge` to apply a changeset, or whole range of changesets, to your working copy. In our case of undoing a change, we're asking `svn 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 `svn merge` operation, so you should use `svn status` and `svn diff` to confirm that your work is in the state you want it to be in, and then use `svn 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. 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 for a possible workaround.

### Resurrecting Deleted Items

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, Because CVS doesn't version trees, it creates an `Attic` area within each repository directory as a way of remembering deleted files. so you need to use `svn log` to discover the exact coordinate pair you wish to resurrect. A good strategy is to run `svn 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
    $ svn 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 `svn merge` to apply revision 808 in reverse. (We've already discussed how to undo changes, see .) 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 `svn 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 `svn merge` at all, but rather the `svn copy` command. Simply copy the exact revision and path coordinate pair from the repository to your working copy:

    $ svn copy --revision 807 \
               http://svn.example.com/repos/calc/trunk/real.c ./real.c

    $ svn status
    A  +   real.c

    $ svn 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 `svn 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.

## Switching a Working Copy

The `svn 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

    $ svn info | grep URL
    URL: http://svn.example.com/repos/calc/trunk

    $ svn switch http://svn.example.com/repos/calc/branches/my-calc-branch
    U   integer.c
    U   button.c
    U   Makefile
    Updated to revision 341.

    $ svn info | grep URL
    URL: http://svn.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 `svn 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:

1. Copy the project's entire 'trunk' to a new branch directory.

2. 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 `svn 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. You can, however, use `svn 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 `svn switch` section in for more information and an example.

### Switches and Updates

Have you noticed that the output of `svn switch` and `svn update` look the same? The switch command is actually a superset of the update command.

When you run `svn update`, you're asking the repository to compare two trees. The repository does so, and then sends a description of the differences back to the client. The only difference between `svn switch` and `svn update` is that the update command always compares two identical paths.

That is, if your working copy is a mirror of `/calc/trunk`, then `svn update` will automatically compare your working copy of `/calc/trunk` to `/calc/trunk` in the `HEAD` revision. If you're switching your working copy to a branch, then `svn switch` will compare your working copy of `/calc/trunk` to some other branch-directory in the `HEAD` revision.

In other words, an update moves your working copy through time. A switch moves your working copy through time and space.

Because `svn switch` is essentially a variant of `svn 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 `svn switch` your working copy to the branch, the local changes will remain. You can then test and commit them to the branch.

## Tags

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.

### Creating a Simple Tag

Once again, `svn 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:

    $ svn copy http://svn.example.com/repos/calc/trunk \
               http://svn.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 ). 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 `svn 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 .) 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.

### Creating a Complex Tag

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 bugfixes. You can accomplish this by selectively backdating files or directories to particular revisions (using `svn update -r` liberally), or by switching files and directories to particular branches (making use of `svn 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, `svn 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/

    $ svn copy my-working-copy http://svn.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 `svn diff` and sending a patchfile (which won't capture tree changes), you can instead use `svn 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 `svn merge` to receive your exact changes.

## Branch Maintenance

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.

### Repository Layout

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 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 `svn 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 `svn move` operation might remove the path from the latest revision. When the user next runs `svn update`, they'll be told that their working copy represents a path that no longer exists, and the user will be forced to `svn switch` to the new location.

### Data Lifetimes

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:

    $ svn delete http://svn.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 `svn checkout`, `svn switch`, or `svn 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 `svn copy -r` to copy it from the old revision:

    $ svn copy -r 374 http://svn.example.com/repos/calc/branches/my-calc-branch \
                      http://svn.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:

    $ svn copy http://svn.example.com/repos/calc/trunk \
             http://svn.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 bugfixes are to be committed to `/calc/branches/stable-1.0`. That is, as people continue to work on the trunk, a human selectively ports bugfixes 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.

## Summary

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 `svn copy` command. We've shown how to use `svn merge` to copy changes from one branch to another, or roll back bad changes. We've gone over the use of `svn 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!