Command-Line Subversion Tutorial, Part 2: Importing, Branching and Merging

Posted in Programming and Version Control on Wednesday, the 22nd of April, 2009.

Tagged: , , and

In Command-Line Subversion Tutorial, Part 1: The Basics, I covered many of the more common svn commands, and hopefully illustrated a typical SVN workflow.

This time around I'd like to tackle some of the commands involved in managing a project within a Subversion repository, specifically those that you'll need in order to import a project to a repository, and to branch and merge a project's codebase.

Importing Files into Subversion

In Part 1, I intentionally glossed over the step of getting your project into Subversion in the first place, so that we could get on with the business of coding. This time, let's take a step backwards and look at the svn import command. The assumption I'll make this time is that you have a Subversion server up and running. We may return to that assumption in a later instalment.

Let's begin by imagining that that we've started work on a new project - the "Hello World" project which we used as an example last time - and currently have two files, index.html, and hello.jpg. Decide whereabouts you want those to live in your repository (I've chosen /hellosite here, for the sake of simplicity), cd into the directory containing those files, and then import the files using the svn import command as follows:

[simon@vps02 hellosite]$ ls
hello.jpg  index.html
[simon@vps02 hellosite]$ svn import -m 'Initial import' . svn://svnrepo/hellosite
Adding         hello.jpg
Adding         index.html

Committed revision 421.
[simon@vps02 hellosite]$ svn status
svn: warning: '.' is not a working copy

Once again, that -m option allows you to add a brief, helpful commit message. If all is in order, the files are added to Subversion and committed automatically.

That's pretty much all there is to it: your project is now in Subversion! As you can see by typing svn status, the actual files you're working on won't automatically become a version-controlled working copy (svn: warning: '.' is not a working copy), so you will need to move them aside and svn co a working copy from the repository, just like we did in Part 1.

Once that's done, you can work with the project as normal, using the commands which we covered last time.

Branching...

Whilst the commands we've look at so far will give you enough control to do your job 95% of the time, there will be times when you need a little more power. It's very likely that if your team consists of more than one developer, or if you ever have more than one task to do at a time, one issue which you'll come up against fairly promptly is that of "branching".

This may be a good time to introduce the oft-used "tree" metaphor for version control repositories. It's common to describe repositories in terms of a "trunk" and "branches". The trunk is simply the main body of code within version control. If you have code in Subversion, then you have a trunk, and in many cases that's all you'll ever need.

A "branch" is little more than a copy of the trunk within the repository, allowing for a parallel line of development. Branches should be used with caution, but there are a couple of good reasons why you may wish to create one.

A common scenario is that you have a large feature to deliver, which may take some time to finish. You can't halt all other development on the project, or commit (and risk releasing) partial changes, but on the other hand, as you develop the new feature you do want to continue to enjoy the benefits of working on version controlled files and directories, and making regular commits.

In this case it may be helpful to create a branch and develop the new feature within it, later merging it all back to the trunk. This is what's known as a developer branch or feature branch - the precise naming tends to depend on how many developers are involved!

Another common type of branch is known as a release branch. This is typically done when you are in the last few days before a major release of your project. A release branch is created, and this is where all those inevitable last minute tweaks and bug fixes are made. By working in a branch in this way, the rest of the team can continue their day-to-day development work on the trunk, independently of what's happening in the release branch.

Fortunately, creating a branch is remarkably simple. A branch is simply a copy of the trunk, and can easily be made using the svn cp or svn copy command, supplying it with two Subversion repository URLs. The first of these will be the address of the resource to be branched, typically the trunk, and the second will be the desired address of the new branch. (Again, these paths are just examples. In practice it's far more common to have a trunk/ directory and a branches/ directory under a project's root, but how you organise your repository is really up to you).

[simon@vps02 hellosite]$ svn cp -m 'Making test branch' svn://svnrepo/hellosite svn://svnrepo/hellosite2

Committed revision 422.

Simple as that: that's your branch created and automatically committed, ready for you to check out and work with. Working with a branch is no different from working with any versioned resource in Subversion, and the typical workflow from Part 1 applies as well in that situation as it does when working from the trunk.

...and Merging

Once you've created a branch, you will inevitably come to a point where you wish the changes within it to make it back into the trunk. This is known as "merging", and is achieved by use of the appropriately-named svn merge command. This is a little more complex than branching, but it's easy enough if you know how.

Assume that your illustrious colleague Bart has made some extensive changes to index.html. To merge all of those changes at once, you'll need a note of those two Subversion URLs we chose earlier (the URLs of both the trunk and the branch), and you will also need a working copy of the trunk. cd into the directory which holds that working copy, and initiate the merge using svn merge and the following syntax:

[simon@vps02 hellosite]$ svn merge svn://svnrepo/hellosite2 svn://svnrepo/hellosite .
U    index.html
[simon@vps02 hellosite]$ svn status
M      index.html

What we've done there is tell svn merge to take any and all changes made in the hellosite2 branch and merge them into hellosite. This time, nothing is committed automatically: the resulting changes are actually made to the working copy, so that we can review them first, and make sure that we're happy with them. If so, we can svn commit the changes as with any other changes to a working copy.

A slighly more subtle approach to merging is to specify revision numbers. Let's say Bart made some useful changes and committed them as revisions 432 and 433, and that we want those changes to be replicated in the trunk (or in another branch), we can pass those revision numbers to svn merge using the -r option:

[simon@vps02 hellosite]$ svn merge  -r 431:433 svn://svnrepo/hellosite2 .
U    index.html
[simon@vps02 hellosite]$ svn status
M    index.html

We've told SVN to "take all changes made to /hellosite2 between revisions 431 and 433, and copy them all into my current working copy". Again, nothing is committed automatically, and we're free to review the resulting code before we commit it ourselves.

Finally, it's worth noting if we wanted to be even more cautious, we could have used the --dry-run option to svn merge, which would give us an idea what the merge would do to our working copy if we were brave enough to actually run it.

Conflicts and Resolution

In the previous section I've presented a somewhat idealistic view of events, where everything goes according to plan. This may not always be the case: if Bart and ourselves have changed the same line(s) of code in different ways, we'll have created what's known as a conflict, which Subversion will not be able to resolvewithout our intervention. If that were the case, that last merge might look more like so:

[simon@vps02 hellosite]$ svn merge -r 431:433 svn://svnrepo/hellosite2 .
C    index.html
[simon@vps02 hellosite]$ svn status
?      index.html.merge-left.r430
?      index.html.working
?      index.html.merge-right.r433
C      index.html

This looks bad. That C next to index.html stands for "conflicted", and it means that we have a little work to do to sort things out. index.html.working is effectively a backup of our previous working copy, and the other two files are some associated SVN debris that we don't need to worry too much about. To clean the mess up, let's open index.html in an editor:

<html>
<head>
<title>Hello World!</title>
</head>
<body>

<<<<<<< .working
<img src="hello.jpg" alt="Goodbye, Cruel World" />
=======
<img src="hello.jpg" alt="Hello World" />
>>>>>>> .merge-right.r433

</body>
</html>

SVN is telling us that Bart's new version and our own version contradict each other, and here we see why. In his branch, Bart has added alt="Hello World", whilst we, in a less optimistic mood, have added alt="Goodbye, Cruel World". To resolve this, we just need to decide which one we want to keep, and then manually edit index.html until we are happy with it. We can simply delete all the extra lines that Subversion has added to flag up where the conflict occurs.

Once that's done, we can tell Subversion that we're happy with the very latest version of the file by issuing the svn resolved command:

[simon@vps02 hellosite]$ svn status
?      index.html.merge-left.r430
?      index.html.working
?      index.html.merge-right.r433
C      index.html
[simon@vps02 hellosite]$ svn resolved index.html
Resolved conflicted state of 'index.html'
[simon@vps02 hellosite]$ svn status
M      index.html
[simon@vps02 hellosite]$ svn commit -m "Merged Bart's changes from r431 and r432 into trunk"
Sending        index.html
Transmitting file data .
Committed revision 434.
[simon@vps02 hellosite]$ svn status
[simon@vps02 hellosite]$ ls
hello.jpg  index.html
[simon@vps02 hellosite]$

Great, Subversion has deleted all those strange extra files it had created, and our conflict is resolved. We can commit the resulting file, and we can all crack on with our next task!

A Cautionary Note

Branching and merging can be used in many more complex ways that this, and sadly often are. Branching tends to be overused, and can create more problems than it solves - that conflict was only the tip of the iceberg! The examples here only scrape the surface or what can be done, but should still be adequate to cover the vast majority of the cases you'll actually come across.

Experience suggests that if your needs appear to be much more complicated than this, then you're probably doing something wrong in the "bigger picture", and may have larger problems than version control alone can solve.

Further Help with SVN Commands

Parts 1 and 2 of this tutorial series have covered most of the useful SVN commands. I'll end by letting you know about just one more: svn help. Simply typing svn help at the command line will print out a list of all the available Subversion commands. You can then type svn help commandname (for example svn help merge or svn help commit, to find out more about a specific command. The exact nature of that information varies from command to command of course, but will typically include an overview of the command's purpose, the syntax for using it, and a list of the various command-line options which can be passed to it. Think of it as a man page for Subversion.

So finally, having now covered a few more ways to use command-line Subversion, my plan is that the third part of this series will introduce a powerful concept, that of Subversion properties.

Previous Posts in this Series:

Related Reading

Comments

Posted by Jon on Wednesday, the 22nd of September, 2010.

Thank you for these tutorials. Very clear.

How about a link in this tutorial to go on to Part 3?

Enter your comment: