CVS--Concurrent Versions System v1.12.12.1: Branching and merging

This document was originally released under the GNU General Public License (GPL). All changes to it must also be released under the GPL, as well as the GNU Free Documentation License (which all contributions to this Wiki are released under).

| < | | > |   | << | | Up | | >> |         |Top| |Contents| |Index| | ? |

Branching and merging

CVS allows you to isolate changes onto a separate

line of development, known as a branch. When you change files on a branch, those changes do not appear on the main trunk or other branches.

Later you can move changes from one branch to another

branch (or the main trunk) by merging. Merging involves first running cvs update -j, to merge the changes into the working directory. You can then commit that revision, and thus effectively copy the changes onto another branch.


| < | | > |   | << | | Up | | >> |         |Top| |Contents| |Index| | ? |

What branches are good for

Incremental/Bug Fix Branch

Suppose that release 1.0 of tc has been made. You are continuing to develop tc, planning to create release 1.1 in a couple of months. After a while your customers start to complain about a fatal bug. You check out release 1.0 (see section Tags-Symbolic revisions) and find the bug (which turns out to have a trivial fix). However, the current revision of the sources are in a state of flux and are not expected to be stable for at least another month. There is no way to make a bug fix release based on the newest sources.

The thing to do in a situation like this is to create a branch on

the revision trees for all the files that make up release 1.0 of tc. You can then make modifications to the branch without disturbing the main trunk. When the modifications are finished you can elect to either incorporate them on the main trunk, or leave them on the branch.

Experimental/Development Branch

Suppose that tc was developed based on library X, but new library Y may (or may not) offer better performance.

Porting tc to use library Y will require modification of many files. Until the work is done, you will not be sure that you want to use library Y going forward.

You create a branch on which the port to library Y will be done. The main trunk will be untouched by this work.

If the decision is made to switch to library Y, the changes will be merged to the trunk.

Sequential Releases

Branches are also often done for developing consecutive releases without stopping work on one to work on the other.

For example, It is possible to work on version 2.1 before version 2.0 is finished. Desired 2.0 changes can be merged to the branch for 2.1 and vice versa.

Simultaneous Releases

Branches are also used for developing for simultaneous releases. For example, you may want to create branches for multiple platforms or languages.

Code changes can be merged from one platform to another, or not if there are incompatibilities beyond the scope of an ifdef.


| < | | > |   | << | | Up | | >> |         |Top| |Contents| |Index| | ? |

Creating a branch

You can create a branch with tag -b; for

example, assuming you're in a working copy:

 
<nowiki>$ cvs tag -b rel-1-0-patches
</nowiki>

This splits off a branch based on the currently committed files in the repository (not the working copy), assigning that branch the name

`rel-1-0-patches'.

It is important to understand that branches get created

in the repository, not in the working copy. Creating a branch based on current revisions, as the above example does, will not automatically switch the working copy to be on the new branch. For information on how to do that, see Accessing branches.

You can also create a branch without reference to any

working copy, by using rtag:

 
<nowiki>$ cvs rtag -b -r rel-1-0 rel-1-0-patches tc
</nowiki>

`-r rel-1-0' says that this branch should be

rooted at the revision that corresponds to the tag `rel-1-0'. It need not be the most recent revision - it's often useful to split a branch off an old revision (for example, when fixing a bug in a past release otherwise known to be stable).

As with `tag', the `-b' flag tells

rtag to create a branch (rather than just a symbolic revision name). Note that the numeric revision number that matches `rel-1-0' will probably be different from file to file.

So, the full effect of the command is to create a new

branch - named `rel-1-0-patches' - in module `tc', rooted in the revision tree at the point tagged by `rel-1-0'.


| < | | > |   | << | | Up | | >> |         |Top| |Contents| |Index| | ? |

Accessing branches

You can retrieve a branch in one of two ways: by

checking it out fresh from the repository, or by switching an existing working copy over to the branch.

To check out a branch from the repository, invoke

`checkout' with the `-r' flag, followed by the tag name of the branch (see section Creating a branch):

 
<nowiki>$ cvs checkout -r rel-1-0-patches tc
</nowiki>

Or, if you already have a working copy, you can switch

it to a given branch with `update -r':

 
<nowiki>$ cvs update -r rel-1-0-patches tc
</nowiki>

or equivalently:

 
<nowiki>$ cd tc
$ cvs update -r rel-1-0-patches
</nowiki>

It does not matter if the working copy was originally

on the main trunk or on some other branch - the above command will switch it to the named branch. And similarly to a regular `update' command, `update -r' merges any changes you have made, notifying you of conflicts where they occur.

Once you have a working copy tied to a particular

branch, it remains there until you tell it otherwise. This means that changes checked in from the working copy will add new revisions on that branch, while leaving the main trunk and other branches unaffected.

To find out what branch a working copy is on, you can

use the `status' command. In its output, look for the field named `Sticky tag' (see section Sticky tags) - that's CVS's way of telling you the branch, if any, of the current working files:

 
<nowiki>$ cvs status -v driver.c backend.c
===================================================================
File: driver.c          Status: Up-to-date

    Version:            1.7     Sat Dec  5 18:25:54 1992
    RCS Version:        1.7     /u/cvsroot/yoyodyne/tc/driver.c,v
    Sticky Tag:         rel-1-0-patches (branch: 1.7.2)
    Sticky Date:        (none)
    Sticky Options:     (none)

    Existing Tags:
        rel-1-0-patches             (branch: 1.7.2)
        rel-1-0                     (revision: 1.7)

===================================================================
File: backend.c         Status: Up-to-date

    Version:            1.4     Tue Dec  1 14:39:01 1992
    RCS Version:        1.4     /u/cvsroot/yoyodyne/tc/backend.c,v
    Sticky Tag:         rel-1-0-patches (branch: 1.4.2)
    Sticky Date:        (none)
    Sticky Options:     (none)

    Existing Tags:
        rel-1-0-patches             (branch: 1.4.2)
        rel-1-0                     (revision: 1.4)
        rel-0-4                     (revision: 1.4)

</nowiki>

Don't be confused by the fact that the branch numbers

for each file are different (`1.7.2' and `1.4.2' respectively). The branch tag is the same, `rel-1-0-patches', and the files are indeed on the same branch. The numbers simply reflect the point in each file's revision history at which the branch was made. In the above example, one can deduce that `driver.c' had been through more changes than `backend.c' before this branch was created.

See Branches and revisions for details about how

branch numbers are constructed.


| < | | > |   | << | | Up | | >> |         |Top| |Contents| |Index| | ? |

Branches and revisions

Ordinarily, a file's revision history is a linear

series of increments (see section Revision numbers):

 
<nowiki>       +-----+    +-----+    +-----+    +-----+    +-----+
       ! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 !
       +-----+    +-----+    +-----+    +-----+    +-----+
</nowiki>

However, CVS is not limited to linear development. The

revision tree can be split into branches, where each branch is a self-maintained line of development. Changes made on one branch can easily be moved back to the main trunk.

Each branch has a branch number, consisting of an

odd number of period-separated decimal integers. The branch number is created by appending an integer to the revision number where the corresponding branch forked off. Having branch numbers allows more than one branch to be forked off from a certain revision.

All revisions on a branch have revision numbers formed

by appending an ordinal number to the branch number. The following figure illustrates branching with an example.

 
<nowiki>                                                      +-------------+
                           Branch 1.2.2.3.2 ->        ! 1.2.2.3.2.1 !
                                                    / +-------------+
                                                   /
                                                  /
                 +---------+    +---------+    +---------+
Branch 1.2.2 -> _! 1.2.2.1 !----! 1.2.2.2 !----! 1.2.2.3 !
               / +---------+    +---------+    +---------+
              /
             /
+-----+    +-----+    +-----+    +-----+    +-----+
! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 !  <- The main trunk
+-----+    +-----+    +-----+    +-----+    +-----+
                !
                !
                !   +---------+    +---------+    +---------+
Branch 1.2.4 -> +---! 1.2.4.1 !----! 1.2.4.2 !----! 1.2.4.3 !
                    +---------+    +---------+    +---------+

</nowiki>

The exact details of how the branch number is

constructed is not something you normally need to be concerned about, but here is how it works: When CVS creates a branch number it picks the first unused even integer, starting with 2. So when you want to create a branch from revision 6.4 it will be numbered 6.4.2. All branch numbers ending in a zero (such as 6.4.0) are used internally by CVS (see section Magic branch numbers). The branch 1.1.1 has a special meaning. See section Tracking third-party sources.


| < | | > |   | << | | Up | | >> |         |Top| |Contents| |Index| | ? |

Magic branch numbers

This section describes a CVS feature called

magic branches. For most purposes, you need not worry about magic branches; CVS handles them for you. However, they are visible to you in certain circumstances, so it may be useful to have some idea of how it works.

Externally, branch numbers consist of an odd number of

dot-separated decimal integers. See section Revision numbers. That is not the whole truth, however. For efficiency reasons CVS sometimes inserts an extra 0 in the second rightmost position (1.2.4 becomes 1.2.0.4, 8.9.10.11.12 becomes 8.9.10.11.0.12 and so on).

CVS does a pretty good job at hiding these so

called magic branches, but in a few places the hiding is incomplete:

  • The magic branch number appears in the output from cvs log.

  • You cannot specify a symbolic branch name to cvs admin.

You can use the admin command to reassign a

symbolic name to a branch the way RCS expects it to be. If R4patches is assigned to the branch 1.4.2 (magic branch number 1.4.0.2) in file `numbers.c' you can do this:

 
<nowiki>$ cvs admin -NR4patches:1.4.2 numbers.c
</nowiki>

It only works if at least one revision is already

committed on the branch. Be very careful so that you do not assign the tag to the wrong number. (There is no way to see how the tag was assigned yesterday).


| < | | > |   | << | | Up | | >> |         |Top| |Contents| |Index| | ? |

Merging an entire branch

You can merge changes made on a branch into your working copy by giving

the `-j branchname' flag to the update subcommand. With one `-j branchname' option the update subcommand merges the changes made between the greatest common ancestor (GCA) of the branch and the destination revision (in the simple case below the GCA is the point where the branch forked) and the newest revision on that branch into your working copy.

Note that any directories created on the branch you are merging from will not be created on the branch you are merging to. See See section Merging can add or remove files for a method to add the missing directories.

The `-j' stands for "join".

Consider this revision tree:

 
<nowiki>+-----+    +-----+    +-----+    +-----+
! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !      <- The main trunk
+-----+    +-----+    +-----+    +-----+
                !
                !
                !   +---------+    +---------+
Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 !
                    +---------+    +---------+
</nowiki>

The branch 1.2.2 has been given the tag (symbolic name) `R1fix'. The

following example assumes that the module `mod' contains only one file, `m.c'.

 
<nowiki>$ cvs checkout mod               # Retrieve the latest revision, 1.4

$ cvs update -j R1fix m.c        # Merge all changes made on the branch,
                                 # i.e. the changes between revision 1.2
                                 # and 1.2.2.2, into your working copy
                                 # of the file.

$ cvs commit -m "Included R1fix" # Create revision 1.5.
</nowiki>

A conflict can result from a merge operation. If that

happens, you should resolve it before committing the new revision. See section Conflicts example.

If your source files contain keywords (see section Keyword substitution),

you might be getting more conflicts than strictly necessary. See Merging and keywords, for information on how to avoid this.

The checkout command also supports the `-j branchname' flag. The

same effect as above could be achieved with this:

 
<nowiki>$ cvs checkout -j R1fix mod
$ cvs commit -m "Included R1fix"
</nowiki>

It should be noted that update -j tagname will also work but may

not produce the desired result. See section Merging can add or remove files, for more.


| < | | > |   | << | | Up | | >> |         |Top| |Contents| |Index| | ? |

Merging from a branch several times

Continuing our example, the revision tree now looks

like this:

 
<nowiki>+-----+    +-----+    +-----+    +-----+    +-----+
! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 !   <- The main trunk
+-----+    +-----+    +-----+    +-----+    +-----+
                !                           *
                !                          *
                !   +---------+    +---------+
Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 !
                    +---------+    +---------+
</nowiki>

where the starred line represents the merge from the

`R1fix' branch to the main trunk, as just discussed.

Now suppose that development continues on the

`R1fix' branch:

 
<nowiki>+-----+    +-----+    +-----+    +-----+    +-----+
! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 !   <- The main trunk
+-----+    +-----+    +-----+    +-----+    +-----+
                !                           *
                !                          *
                !   +---------+    +---------+    +---------+
Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 !----! 1.2.2.3 !
                    +---------+    +---------+    +---------+
</nowiki>

and then you want to merge those new changes onto the

main trunk. If you just use the cvs update -j R1fix m.c command again, CVS will attempt to merge again the changes which you have already merged, which can have undesirable side effects.

So instead you need to specify that you only want to

merge the changes on the branch which have not yet been merged into the trunk. To do that you specify two `-j' options, and CVS merges the changes from the first revision to the second revision. For example, in this case the simplest way would be

 
<nowiki>cvs update -j 1.2.2.2 -j R1fix m.c    # Merge changes from 1.2.2.2 to the
                                      # head of the R1fix branch
</nowiki>

The problem with this is that you need to specify the

1.2.2.2 revision manually. A slightly better approach might be to use the date the last merge was done:

 
<nowiki>cvs update -j R1fix:yesterday -j R1fix m.c
</nowiki>

Better yet, tag the R1fix branch after every merge into

the trunk, and then use that tag for subsequent merges:

 
<nowiki>cvs update -j merged_from_R1fix_to_trunk -j R1fix m.c
</nowiki>


| < | | > |   | << | | Up | | >> |         |Top| |Contents| |Index| | ? |

Merging differences between any two revisions

With two `-j revision' flags, the update

(and checkout) command can merge the differences between any two revisions into your working file.

 
<nowiki>$ cvs update -j 1.5 -j 1.3 backend.c
</nowiki>

will undo all changes made between revision

1.3 and 1.5. Note the order of the revisions!

If you try to use this option when operating on

multiple files, remember that the numeric revisions will probably be very different between the various files. You almost always use symbolic tags rather than revision numbers when operating on multiple files.

Specifying two `-j' options can also undo file

removals or additions. For example, suppose you have a file named `file1' which existed as revision 1.1, and you then removed it (thus adding a dead revision 1.2). Now suppose you want to add it again, with the same contents it had previously. Here is how to do it:

 
<nowiki>$ cvs update -j 1.2 -j 1.1 file1
U file1
$ cvs commit -m test
Checking in file1;
/tmp/cvs-sanity/cvsroot/first-dir/file1,v  <--  file1
new revision: 1.3; previous revision: 1.2
done
$
</nowiki>


| < | | > |   | << | | Up | | >> |         |Top| |Contents| |Index| | ? |

Merging can add or remove files

If the changes which you are merging involve removing

or adding some files, update -j will reflect such additions or removals.

For example:

 
<nowiki>cvs update -A
touch a b c
cvs add a b c ; cvs ci -m "added" a b c
cvs tag -b branchtag
cvs update -r branchtag
touch d ; cvs add d
rm a ; cvs rm a
cvs ci -m "added d, removed a"
cvs update -A
cvs update -jbranchtag
</nowiki>

After these commands are executed and a `cvs commit' is done,

file `a' will be removed and file `d' added in the main branch.

Note that using a single static tag (`-j tagname')

rather than a dynamic tag (`-j branchname') to merge changes from a branch will usually not remove files which were removed on the branch since CVS does not automatically add static tags to dead revisions. The exception to this rule occurs when a static tag has been attached to a dead revision manually. Use the branch tag to merge all changes from the branch or use two static tags as merge endpoints to be sure that all intended changes are propagated in the merge.

Note that if you create a directory in the branch, it will not be merged into the main branch. For example:

 
<nowiki>cvs update -A
touch a b c
cvs add a b c ; cvs ci -m "added" a b c
cvs tag -b branchtag
cvs update -r branchtag
mkdir d ; cvs add d
cd d ; touch e ; cvs add e
cvs ci -m "added directory d, added d/e"
cvs update -A
cvs update -jbranchtag
</nowiki>

will not create directory d in the main branch. To find the missing directories, you can use the following steps. Note that you need to have both the main and the branch directories. We will use the directories "main" and "branch", respectively.

 
<nowiki>
cd branch
find . -type d | sort > /tmp/merge-from
cd main
find . -type d | sort > /tmp/merge-to
diff /tmp/merge-from /tmp/merge-to | grep -v 'CVS$'
</nowiki>

The above steps will list all the directories that were created in the branch but do not exist in the main branch. For each directory listed, we will need to do an update as follows:

 
<nowiki>
cvs update -j branchtag -d newdirectory
</nowiki>


| < | | > |   | << | | Up | | >> |         |Top| |Contents| |Index| | ? |

Merging and keywords

If you merge files containing keywords (see section Keyword substitution), you will normally get numerous

conflicts during the merge, because the keywords are expanded differently in the revisions which you are merging.

Therefore, you will often want to specify the

`-kk' (see section Substitution modes) switch to the merge command line. By substituting just the name of the keyword, not the expanded value of that keyword, this option ensures that the revisions which you are merging will be the same as each other, and avoid spurious conflicts.

For example, suppose you have a file like this:

 
<nowiki>       +---------+
      _! 1.1.2.1 !   <-  br1
     / +---------+
    /
   /
+-----+    +-----+
! 1.1 !----! 1.2 !
+-----+    +-----+
</nowiki>

and your working directory is currently on the trunk

(revision 1.2). Then you might get the following results from a merge:

 
<nowiki>$ cat file1
key $<i>Revision: 1.2 $
. . .
$ cvs update -j br1
U file1
RCS file: /cvsroot/first-dir/file1,v
retrieving revision 1.1
retrieving revision 1.1.2.1
Merging differences between 1.1 and 1.1.2.1 into file1
rcsmerge: warning: conflicts during merge
$ cat file1
<<<<<<< file1
key $<i>Revision: 1.2 $
=======
key $<i>Revision: 1.1.2.1 $
>>>>>>> 1.1.2.1
. . .
</nowiki>

What happened was that the merge tried to merge the

differences between 1.1 and 1.1.2.1 into your working directory. So, since the keyword changed from Revision: 1.1 to Revision: 1.1.2.1, CVS tried to merge that change into your working directory, which conflicted with the fact that your working directory had contained Revision: 1.2.

Here is what happens if you had used `-kk':

 
<nowiki>$ cat file1
key $<i>Revision: 1.2 $
. . .
$ cvs update -kk -j br1
U file1
RCS file: /cvsroot/first-dir/file1,v
retrieving revision 1.1
retrieving revision 1.1.2.1
Merging differences between 1.1 and 1.1.2.1 into file1
$ cat file1
key $<i>Revision$
. . .
</nowiki>

What is going on here is that revision 1.1 and 1.1.2.1

both expand as plain Revision, and therefore merging the changes between them into the working directory need not change anything. Therefore, there is no conflict.

WARNING: In versions of CVS prior to 1.12.2, there was a

major problem with using `-kk' on merges. Namely, `-kk' overrode any default keyword expansion mode set in the archive file in the repository. This could, unfortunately for some users, cause data corruption in binary files (with a default keyword expansion mode set to `-kb'). Therefore, when a repository contained binary files, conflicts had to be dealt with manually rather than using `-kk' in a merge command.

In CVS version 1.12.2 and later, the keyword expansion mode

provided on the command line to any CVS command no longer overrides the `-kb' keyword expansion mode setting for binary files, though it will still override other default keyword expansion modes. You can now safely merge using `-kk' to avoid spurious conflicts on lines containing RCS keywords, even when your repository contains binary files.


| << | | >> |           |Top| |Contents| |Index| | ? |

This document was generated by Derek Price on July, 21 2005 using texi2html 1.77.