GNU Info

Info Node: (cvsbook.info)Merging Repeatedly Into The Trunk

(cvsbook.info)Merging Repeatedly Into The Trunk


Next: The Dovetail Approach -- Merging In And Out Of The Trunk Prev: Some Principles For Working With Branches Up: Going Out On A Limb (How To Work With Branches And Survive)
Enter node , (file) or (file)node

Merging Repeatedly Into The Trunk
---------------------------------

Let's assume qsmith needs to do development on a branch for a while, to
avoid destabilizing the trunk that he shares with jrandom.  The first
step is to create the branch.  Notice how qsmith creates a regular
(non-branch) tag at the branch point first, and then creates the branch:

     paste$ pwd
     /home/qsmith/myproj
     paste$ cvs tag Root-of-Exotic_Greetings
     cvs tag: Tagging .
     T README.txt
     T foo.gif
     T hello.c
     cvs tag: Tagging a-subdir
     T a-subdir/whatever.c
     cvs tag: Tagging a-subdir/subsubdir
     T a-subdir/subsubdir/fish.c
     cvs tag: Tagging b-subdir
     T b-subdir/random.c
     paste$ cvs tag -b Exotic_Greetings-branch
     cvs tag: Tagging .
     T README.txt
     T foo.gif
     T hello.c
     cvs tag: Tagging a-subdir
     T a-subdir/whatever.c
     cvs tag: Tagging a-subdir/subsubdir
     T a-subdir/subsubdir/fish.c
     cvs tag: Tagging b-subdir
     T b-subdir/random.c
     paste$

The point of tagging the trunk first is that it may be necessary someday
to retrieve the trunk as it was the moment the branch was created.  If
you ever need to do that, you'll have to have a way of referring to the
trunk snapshot without referring to the branch itself.  Obviously, you
can't use the branch tag because that would retrieve the branch, not the
revisions in the trunk that form the root of the branch.  The only way
to do it is to make a regular tag at the same revisions the branch
sprouts from.  (Some people stick to this rule so faithfully that I
considered listing it as "Branching Principle Number 4: Always create a
non-branch tag at the branch point."  However, many sites don't do it,
and they generally seem to do okay, so it's really a matter of taste.)
From here on, I will refer to this non-branch tag as the "branch point
tag".

Notice also that a naming convention is being adhered to: The branch
point tag begins with `Root-of-', then the actual branch name, which
uses underscores instead of hyphens to separate words.  When the actual
branch is created, its tag ends with the suffix `-branch' so that you
can identify it as a branch tag just by looking at the tag name.  (The
branch point tag `Root-of-Exotic_Greetings' does not include the
-branch because it is not a branch tag.)  You don't have to use this
particular naming convention, of course, but you should use some
convention.

Of course, I'm being extra pedantic here.  In smallish projects, where
everyone knows who's doing what and confusion is easy to recover from,
these conventions don't have to be used.  Whether you use a branch point
tag or have a strict naming convention for your tags depends on the
complexity of the project and the branching scheme.  (Also, don't forget
that you can always go back later and update old tags to use new
conventions by retrieving an old tagged version, adding the new tag, and
then deleting the old tag.)

Now, qsmith is ready to start working on the branch:

     paste$ cvs update -r Exotic_Greetings-branch
     cvs update: Updating .
     cvs update: Updating a-subdir
     cvs update: Updating a-subdir/subsubdir
     cvs update: Updating b-subdir
     paste$

He makes some changes to a couple of files and commits them on the
branch:

     paste$ emacs README.txt a-subdir/whatever.c b-subdir/random.c
     ...
     paste$ cvs ci -m "print greeting backwards, etc"
     cvs commit: Examining .
     cvs commit: Examining a-subdir
     cvs commit: Examining a-subdir/subsubdir
     cvs commit: Examining b-subdir
     Checking in README.txt;
     /usr/local/newrepos/myproj/README.txt,v  <--  README.txt
     new revision: 1.14.2.1; previous revision: 1.14
     done
     Checking in a-subdir/whatever.c;
     /usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
     new revision: 1.3.2.1; previous revision: 1.3
     done
     Checking in b-subdir/random.c;
     /usr/local/newrepos/myproj/b-subdir/random.c,v  <--  random.c
     new revision: 1.1.1.1.2.1; previous revision: 1.1.1.1
     done
     paste$

Meanwhile, jrandom is continuing to work on the trunk.  She modifies two
of the three files that qsmith touched.  Just for kicks, we'll have her
make changes that conflict with qsmith's work:

     floss$ emacs README.txt whatever.c
      ...
     floss$ cvs ci -m "some very stable changes indeed"
     cvs commit: Examining .
     cvs commit: Examining a-subdir
     cvs commit: Examining a-subdir/subsubdir
     cvs commit: Examining b-subdir
     Checking in README.txt;
     /usr/local/newrepos/myproj/README.txt,v  <--  README.txt
     new revision: 1.15; previous revision: 1.14
     done
     Checking in a-subdir/whatever.c;
     /usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
     new revision: 1.4; previous revision: 1.3
     done
     floss$

The conflict is not apparent yet, of course, because neither developer
has tried to merge branch and trunk.  Now, jrandom does the merge:

     floss$ cvs update -j Exotic_Greetings-branch
     cvs update: Updating .
     RCS file: /usr/local/newrepos/myproj/README.txt,v
     retrieving revision 1.14
     retrieving revision 1.14.2.1
     Merging differences between 1.14 and 1.14.2.1 into README.txt
     rcsmerge: warning: conflicts during merge
     cvs update: Updating a-subdir
     RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v
     retrieving revision 1.3
     retrieving revision 1.3.2.1
     Merging differences between 1.3 and 1.3.2.1 into whatever.c
     rcsmerge: warning: conflicts during merge
     cvs update: Updating a-subdir/subsubdir
     cvs update: Updating b-subdir
     RCS file: /usr/local/newrepos/myproj/b-subdir/random.c,v
     retrieving revision 1.1.1.1
     retrieving revision 1.1.1.1.2.1
     Merging differences between 1.1.1.1 and 1.1.1.1.2.1 into random.c
     floss$ cvs update
     cvs update: Updating .
     C README.txt
     cvs update: Updating a-subdir
     C a-subdir/whatever.c
     cvs update: Updating a-subdir/subsubdir
     cvs update: Updating b-subdir
     M b-subdir/random.c
     floss$

Two of the files conflict.  No big deal; with her usual savoir-faire,
jrandom resolves the conflicts, commits, and tags the trunk as
successfully merged:

     floss$ emacs README.txt a-subdir/whatever.c
      ...
     floss$ cvs ci -m "merged from Exotic_Greetings-branch (conflicts resolved)"
     cvs commit: Examining .
     cvs commit: Examining a-subdir
     cvs commit: Examining a-subdir/subsubdir
     cvs commit: Examining b-subdir
     Checking in README.txt;
     /usr/local/newrepos/myproj/README.txt,v  <--  README.txt
     new revision: 1.16; previous revision: 1.15
     done
     Checking in a-subdir/whatever.c;
     /usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
     new revision: 1.5; previous revision: 1.4
     done
     Checking in b-subdir/random.c;
     /usr/local/newrepos/myproj/b-subdir/random.c,v  <--  random.c
     new revision: 1.2; previous revision: 1.1
     done
     floss$ cvs tag merged-Exotic_Greetings
     cvs tag: Tagging .
     T README.txt
     T foo.gif
     T hello.c
     cvs tag: Tagging a-subdir
     T a-subdir/whatever.c
     cvs tag: Tagging a-subdir/subsubdir
     T a-subdir/subsubdir/fish.c
     cvs tag: Tagging b-subdir
     T b-subdir/random.c
     floss$

Meanwhile, qsmith needn't wait for the merge to finish before continuing
development, as long as he makes a tag for the batch of changes from
which jrandom merged (later, jrandom will need to know this tag name; in
general, branches depend on frequent and thorough developer
communications):

     paste$ cvs tag Exotic_Greetings-1
     cvs tag: Tagging .
     T README.txt
     T foo.gif
     T hello.c
     cvs tag: Tagging a-subdir
     T a-subdir/whatever.c
     cvs tag: Tagging a-subdir/subsubdir
     T a-subdir/subsubdir/fish.c
     cvs tag: Tagging b-subdir
     T b-subdir/random.c
     paste$ emacs a-subdir/whatever.c
      ...
     paste$ cvs ci -m "print a randomly capitalized greeting"
     cvs commit: Examining .
     cvs commit: Examining a-subdir
     cvs commit: Examining a-subdir/subsubdir
     cvs commit: Examining b-subdir
     Checking in a-subdir/whatever.c;
     /usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
     new revision: 1.3.2.2; previous revision: 1.3.2.1
     done
     paste$

And of course, qsmith should tag those changes once he's done:

     paste$ cvs -q tag Exotic_Greetings-2
     T README.txt
     T foo.gif
     T hello.c
     T a-subdir/whatever.c
     T a-subdir/subsubdir/fish.c
     T b-subdir/random.c
     paste$

While all this is going on, jrandom makes a change in a different file,
one that qsmith hasn't touched in his new batch of edits:

     floss$ emacs README.txt
      ...
     floss$ cvs ci -m "Mention new Exotic Greeting features" README.txt
     Checking in README.txt;
     /usr/local/newrepos/myproj/README.txt,v  <--  README.txt
     new revision: 1.17; previous revision: 1.16
     done
     floss$

At this point, qsmith has committed a new change on the branch, and
jrandom has committed a nonconflicting change in a different file on the
trunk.  Watch what happens when jrandom tries to merge from the branch
again:

     floss$ cvs -q update -j Exotic_Greetings-branch
     RCS file: /usr/local/newrepos/myproj/README.txt,v
     retrieving revision 1.14
     retrieving revision 1.14.2.1
     Merging differences between 1.14 and 1.14.2.1 into README.txt
     rcsmerge: warning: conflicts during merge
     RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v
     retrieving revision 1.3
     retrieving revision 1.3.2.2
     Merging differences between 1.3 and 1.3.2.2 into whatever.c
     rcsmerge: warning: conflicts during merge
     RCS file: /usr/local/newrepos/myproj/b-subdir/random.c,v
     retrieving revision 1.1
     retrieving revision 1.1.1.1.2.1
     Merging differences between 1.1 and 1.1.1.1.2.1 into random.c
     floss$ cvs -q update
     C README.txt
     C a-subdir/whatever.c
     floss$

There are conflicts!  Is that what you expected?

The problem lies in the semantics of merging.  Back in Note: An
Overview of CVS, I explained that when you run

     floss$ cvs update -j BRANCH

in a working copy, CVS merges into the working copy the differences
between BRANCH's root and its tip.  The trouble with that behavior, in
this situation, is that most of those changes had already been
incorporated into the trunk the first time that jrandom did a merge.
When CVS tried to merge them in again (over themselves, as it were), it
naturally registered a conflict.

What jrandom really wanted to do was merge into her working copy the
changes between the branch's most recent merge and its current tip.  You
can do this by using two -j flags to update, as you may recall from
Note: An Overview of CVS, as long as you know what revision to specify
with each flag.  Fortunately, qsmith made a tag at exactly the last
merge point (hurrah for planning ahead!), so this will be no problem.
First, let's have jrandom restore her working copy to a clean state,
from which she can redo the merge:

     floss$ rm README.txt a-subdir/whatever.c
     floss$ cvs -q update
     cvs update: warning: README.txt was lost
     U README.txt
     cvs update: warning: a-subdir/whatever.c was lost
     U a-subdir/whatever.c
     floss$

Now she's ready to do the merge, this time using qsmith's conveniently
placed tag:

     floss$ cvs -q update -j Exotic_Greetings-1 -j Exotic_Greetings-branch
     RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v
     retrieving revision 1.3.2.1
     retrieving revision 1.3.2.2
     Merging differences between 1.3.2.1 and 1.3.2.2 into whatever.c
     floss$ cvs -q update
     M a-subdir/whatever.c
     floss$

Much better.  The change from qsmith has been incorporated into
whatever.c; jrandom can now commit and tag:

     floss$ cvs -q ci -m "merged again from Exotic_Greetings (1)"
     Checking in a-subdir/whatever.c;
     /usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
     new revision: 1.6; previous revision: 1.5
     done
     floss$ cvs -q tag merged-Exotic_Greetings-1
     T README.txt
     T foo.gif
     T hello.c
     T a-subdir/whatever.c
     T a-subdir/subsubdir/fish.c
     T b-subdir/random.c
     floss$

Even if qsmith had forgotten to tag at the merge point, all hope would
not be lost.  If jrandom knew approximately when qsmith's first batch of
changes had been committed, she could try filtering by date:

     floss$ cvs update -j Exotic_Greetings-branch:3pm -j Exotic_Greetings_branch

Although useful as a last resort, filtering by date is less than ideal
because it selects the changes based on people's recollections rather
than dependable developer designations.  If qsmith's first mergeable set
of changes had happened over several commits instead of in one commit,
jrandom may mistakenly choose a date or time that would catch some of
the changes, but not all of them.

There's no reason why each taggable point in qsmith's changes needs to
be sent to the repository in a single commit - it just happens to have
worked out that way in these examples.  In real life, qsmith may make
several commits between tags.  He can work on the branch in isolation,
as he pleases.  The point of the tags is to record successive points on
the branch where he considers the changes to be mergeable into the
trunk.  As long as jrandom always merges using two -j flags and is
careful to use qsmith's merge tags in the right order and only once
each, the trunk should never experience the double-merge problem.
Conflicts may occur, but they will be the unavoidable kind that requires
human resolution - situations in which both branch and trunk made
changes to the same area of code.


automatically generated by info2www version 1.2.2.9