The answer is that CVS does not store a snapshot of each revision, but rather
stores a snapshot of the first revision and the delta's needed to create the
2nd, 3rd, etc revisions. Imagine you have 3 revisions of a file:
a -> b -> c
CVS stores a, but does not store b or c. Instead, CVS stores delta(a,b) and
delta(b,c), which are defined as:
delta(a,b) = b - a
delta(b,c) = c - b
Thus, if you want to check out revision b, cvs constructs it as:
b = a + delta(a,b)
and gives it to you. If you want to check out revision c, cvs constructs it as
c = a + delta(a,b) + delta(b,c)
and gives it to you (actually, this is a little oversimplified - CVS has an
optimized version of what I just said so that it is not continually re-creating
the newest revision from the original and all of the deltas; however, logically
this is what happens).
Now, for a branch example. Suppose you have
a -> b
|
d
and you want to create c on the trunk as the merged result of b and d. Fine.
Then you get:
a -> b -> c
| /
d-------/
Look at this in terms of the deltas:
c = a + delta(a,b) + delta(a,d)
which could also be written as:
c = a + delta(a,c)
where
delta(a,c) = delta(a,b) + delta(b,c)
Now, suppose you do more development on the branch and want to merge again to
the trunk.
a -> b -> c
| /
d--------/ -> e
and you want to create an f on the trunk which is the merged result of c and e.
Well, to cvs, the definition of e is
e = a + delta(a,d) + delta(d,e)
If you checkout c and say "cvs update -j <tag at e>", cvs figures out that c and
e have a as their common ancestor. It then simply combines all of the deltas to
get the result:
f = a + delta(a,c) + delta(a,e)
f = a + { delta(a,b) + delta(a,d) } + { delta(a,d) + delta(d,e) }
So, you can see that we get a double dose of delta(a,d) which is the problem.
The cause of this problem is that CVS does NOT have an automatic way of
remembering the previous merge that created c from b and d. You have to
remember for CVS. You remind CVS about the previous merge by using an
additional "-j" option in the merge command:
cvs update -j <tag at d> -j <tag at e>
which tells CVS not to use any deltas before d on the branch. So, we construct
f as:
f = a + { delta(a,b) + delta(a,d) } + { delta(d,e) }
which is the result you want.
Because merging more than once from a branch to the trunk is a manual affair
(you need must remember without any mistakes where all previous merges
occurred), I recommend merging from a branch no more than once, then forget the
branch ever existed. Create a new branch after the merge if you need one. If
your process insists on merging from a branch multiple times, try (very hard) to
change to process to create new branches, each of which is merged to the trunk
either zero or one times. Another alternative is to postpone merging the branch
to the trunk until no more work will be done on the branch. Hopefully, one of
these alternatives will work for you.
Alan Thompson
P.S. Although it is possible, don't even think about branching from a branch.
If you do, the complexity, number of manual steps, and opportunity for major
error go up like a factorial (n! - even worse than exponentially!).
To: "'[EMAIL PROTECTED]'" <[EMAIL PROTECTED]>
cc: (bcc: Alan Thompson/Orincon)
Subject: Merging and "false" conflicts
I don't entirely understand why if you merge a branch to the trunk twice and
don't use 2 -j args for the second merge, the changes brought in with the
1st merge will sometimes show up as conflicts. I'd expect that CVS would
recognize that for each such change that is already in the trunk, a warning
or something would be printed rather that it showing up sometimes as a
conflict
This actually is a simple case of what I'm experiencing because we have a
rather complicated collection of branches which can (unless we are very
careful) lead to the same set of changes being in both the source and target
of a merge. In this situation we face many "false" conflicts that although
are easy to resolve, we'd like to avoid them entirely.
Any insight for me out the?
Thanks,
ksb