Bookmark and Share

Introduction to merging with SVN

Posted: Friday, April 3rd, 2009 at 4:56 pmUpdated: Saturday, April 4th, 2009 at 9:04 am

One of the “problem” in using SVN with branches is that you have to figure out which revisions to apply. In most cases, one could not simply take the HEAD version and simply merge that to a branch or TRUNK.

On this page, I’ll attempt to explain why we can’t simply merge the HEAD version. On page 2, we’ll see what we can do manually. Finally on page 3, we’ll use a wonderful tool svnmerge.py to automatically merge the changes.

Why you can’t merge HEAD revision.

One thing that you need to understand about SVN is that SVN doesn’t save the whole file on each revisions; rather it’ll just save the changes. To illustrate this point, suppose you have a file checked-in in revision 1 SVN like below:

function my_add($num) {
	$add_by = 1;
	return $num + $add_by;
}

Suppose you make changes to my_add function like below and commit it to SVN as revision 2.

function my_add($num) {
	$add_by = 5;
	return $num + $add_by;
}

On revision 2, SVN doesn’t save all 4 lines of the file; rather, it’ll just save the diff between the 2 files. So as far as SVN concerns, it will save only the information that the file was changed on line 2 from a to b similar to the information below. The exact format of what SVN saves I’m not sure but the concept is the same.

@@ -2 +2 @@
-	$add_by = 1;
+	$add_by = 5;

So basically what it’s saying is that line 2 from the previous version is deleted and replaced by line that started with plus (+) sign.

Let’s suppose that you make another change to the file like below and commit it as revision 3.

function my_add($num) {
	$add_by = 5;
	return ($num + $add_by) * -1;
}

Just like when saving revision 2, SVN will only take the changes between this version and the previous version. So at revision 3, SVN will save something like below (again, I’m not saying the exact thing is saved in SVN but something like it):

@@ -3 +3 @@
-	return $num + $add_by;
+	return ($num + $add_by) * -1;

Now imagine that you have a branch somewhere that has the same copy of the file as revision 1. When you merge head (revision 3), then the only change that will be applied is at line 3. So the merged file will become like below:

function my_add($num) {
	$add_by = 1;
	return ($num + $add_by) * -1;
}

Ooops … now we have a problem. When you merge only revision 3, changes on revision 2 is not applied. As the result, line 2 is inconsistent. Let me illustrate it with the actual SVN session.

user@dev:~/work$ svn mkdir http://localhost/svn/tutorial/TRUNK -m \
"Creating TRUNK"
user@dev:~/work$ svn checkout http://localhost/svn/tutorial/TRUNK
user@dev:~/work$ cd TRUNK/
user@dev:~/work$ vi testfile.php
user@dev:~/work/TRUNK$ svn add testfile.php
A         testfile.php
user@dev:~/work/TRUNK$ svn commit testfile.php -m "Initial checkin"
Adding         testfile.php
Transmitting file data .
Committed revision 2.
user@dev:~/work/TRUNK$

At this point, we’ve setup our SVN, added new file and checked it in for the first time. Next, we’ll create a branch out of the initial check-in.

user@dev:~/work/TRUNK$ svn copy http://localhost/svn/tutorial/TRUNK/ \
http://localhost/svn/tutorial/mine -m "Creating my branch"
user@dev:~/work/TRUNK$

Then we’ll continue making changes in TRUNK like the example above.

user@dev:~/work/TRUNK$ svn commit -m "Changed line 2"
Sending        testfile.php
Transmitting file data .
Committed revision 4.
user@dev:~/work/TRUNK$ svn commit -m "Changed line 3"
Sending        testfile.php
Transmitting file data .
Committed revision 5.
user@dev:~/work/TRUNK$ cat testfile.php 
<?php
function my_add($num) {
        $add_by = 5;
        return ($num + $add_by) * -1;
}
user@dev:~/work/TRUNK$

Now that we’ve got the changes above, let’s try working on the branch and try to merge only the HEAD. Right now, we are on revision 5. So merging head is basically merging revision 4 to 5. (1 revision before up to HEAD).

user@dev:~/work$ svn checkout http://localhost/svn/tutorial/mine/
A    mine/testfile.php
Checked out revision 5.
user@dev:~/work$ cd mine/
user@dev:~/work/mine$ cat testfile.php 
<?php
function my_add($num) {
        $add_by = 1;
        return $num + $add_by;
}
user@dev:~/work/mine$ svn merge -r 4:5 \
http://localhost/svn/tutorial/TRUNK/testfile.php
U    testfile.php
user@dev:~/work/mine$ cat testfile.php 
<?php
function my_add($num) {
        $add_by = 1;
        return ($num + $add_by) * -1;
}
user@dev:~/work/mine$

As you can see from the merged result, we only have the changes for line 3 and the other line remains unchanged. I hope this illustrates why you can’t simply merge head. You’ll loose some previous changes if you do that.

So how do we do it then? Well read on to page 2.

Pages: 1 2 3

2 Responses to “Introduction to merging with SVN”

  1. Gabriele Vivinetto Says:

    I think this is not needed woth svn 1.6.x, because it saves merge history in the svn:property mergeinfo.
    Am I wrong ?

  2. Maresa Says:

    Yes … SVN 1.6.x does support merging .. hence we don’t have to use svnmerge.py anymore. There are plenty of cases where distributions doesn’t have SVN 1.6 yet. In particular Ubuntu Hardy (the distribution I’m using). Even if you’re using SVN 1.6, it doesn’t mean that you can’t still use svnmerge.py though. I have a feeling that I would still be using svnmerge.py even though I’m using SVN 1.6 just because I’m already familiar with it. Maybe once svnmerge.py is no longer maintained, I’d be forced to use SVN 1.6 built in merging feature :-)

Leave a Reply