subversion-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Stefan Sperling <>
Subject Re: Fixing merge - Subtree, Cyclic, and Tree Change cases
Date Wed, 20 Jul 2011 12:47:21 GMT
On Wed, Jul 20, 2011 at 01:13:09PM +0100, Julian Foad wrote:
> There are (broadly speaking) two ways we could perform a "re-integrate"
> merge.  The "general" way is to do it by using a sufficiently clever
> general-purpose merge algorithm to merge all the changes that are unique
> to the branch, onto the trunk.  The "special" way is to recognize that,
> if the branch is up to date with trunk, then the desired state of the
> trunk is exactly the current state of the branch, so all we need to do
> is make the trunk look like the branch, which is theoretically a trivial
> operation.
> Both ways achieve the basic desired result when re-integrating a feature
> branch to trunk, and their differences, while significant, are
> relatively minor in context of the whole field of merging.  The
> Subversion project implemented the "special" way, and I would guess that
> was mainly because the merge tracking was not clever enough to make the
> "general" way work, while recognizing that it's often helpful to have a
> simple concept on which to base a well-defined work flow so the loss of
> generality was not a big problem in many cases.

I think the above description simplifies things a bit too much
for the purpose of this discussion.

To avoid potential misunderstandings I'd like to provide a more
detailed explanation of how reintegrate works.
Andy also said earlier he didn't understand why --reintegrate is
necessary and I hope I can answer this question.

A re-integrate does not simply swap out the trunk with the branch.
It runs a two-URL merge with parameters constructed in such a
way that the delta being merged contains exactly the changes
committed for the feature, and no unrelated changes.

Quoting 'svn help merge' output from Subversion 1.7:

     A feature has been developed on a branch called "feature".
     The feature branch started as a copy of trunk@W. Work on the
     feature has completed and it should be merged back into the trunk.
     The feature branch was last synced with its immediate ancestor,
     the trunk, in revision X. So the difference between trunk@X and
     feature@HEAD contains the complete set of changes that implement
     the feature, and no other changes. These changes are applied to
     the trunk.

                 feature  +-------------------------------R
                         /                               . \
                        /                  ..............   \
                       /                  .                  v
         trunk ------+--------------------L------------------o
                    rW                   rX

     In the diagram above, L marks the left side of the merge (trunk@X),
     and R marks the right side of the merge (feature@HEAD). The difference
     between the left and right side is merged into the target.

Essentially, all merges in Subversion are 2-URL merges.
svn merge generates some delta and applies that to a working copy.
There is no other way, right now, that a merge could work.

A two-URL merge needs the following parameters:

  PATH1, REV1 (left side of the delta)
  PATH2, REV2 (right side of the delta)
  TARGET (working copy of some PATH3, up-to-date enough for commit)

The reintegrate merge automatically fills in all required parameters 
expect PATH2, and we get:

  PATH1 = trunk, REV1 = rX
  PATH2 = feature, REV2 = HEAD
  TARGET = working copy of trunk (generally speaking @HEAD)

A sync merge can fill in the all parameters as well, except PATH2.
However, it needs to do so in a different way. With a sync merge
PATH1 and PATH2 are the same so the delta between trunk@REV1 and
trunk@REV2 is merged into a working copy of the feature branch
In case of reintegrate PATH1 and PATH2 are not the same!

So svn merge needs the special --reintegrate option because just
'svn merge ^/branch' would be ambiguous.

> Note that a practical benefit of the work flow using the "special"
> re-integrate is it ensures the reintegration will not generate any merge
> conflicts - neither physical, nor semantic (since we assume the branch
> has been reviewed and tested).

It does not generate conflicts because of the way REV1 is chosen,
and because of the way PATH1 and PATH2 are chosen.

In some cases, you can actually get a clean merge by reintegrating
a branch like this: svn merge ^/feature
This results in the delta between feature@W and feature@HEAD
being applied to a trunk working copy. This merge can be conflict-free
but is not guaranteed to be conflict-free.

> The "special" kind of re-integrate that Subversion performs has a
> logical pre-condition: that the branch must be up to date with the
> "trunk".  In concrete terms, the required check is that the total set of
> mergeinfo on the branch must indicate that all changes from the trunk
> are included in full.

No. Using the above diagram as a reference, it only requires that
there are no gaps in the branch's mergeinfo for merges from trunk
between rW and rX. It does not require that changes between rX and
HEAD have already been merged from the trunk to the branch.
It just requires that a continous chunk of history has been merged,
i.e. no cherry-picking from trunk to the branch between rW and rX,
you need everything in that range.

> The implementation of this check has been overly simple and pessimistic
> in the past.  It is possible for the same merge history to be recorded
> in different ways.  In some cases where mergeinfo is recorded on a
> subtree, the very same merge history could equally well be recorded in a
> single svn:mergeinfo on the root of the branch.  The original
> "re-integrate" merge did not tolerate mergeinfo properties on subtrees
> at all, but that limitation was soon relaxed and I believe it has since
> been improved to logical completion so that now mergeinfo is allowed on
> subtrees as long as the total merge history described is logically
> correct.

Again, the question is about gaps in the subtree mergeinfo,
not about whether everything has already been merged.

View raw message