[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: unexpected tree conflict on merge for same source file

From: Stefan Sperling <stsp_at_elego.de>
Date: Wed, 17 Oct 2012 19:12:17 +0200

On Wed, Oct 17, 2012 at 05:30:14PM +0200, Sven Uhlig wrote:
> > Ideally, you'd reintegrate prj1 into trunk first and then merge the
> > changes into prj2 from trunk, using a sequence such as:
>
> I guess this merge was a bad example and wrong as well. There could be
> changes that are commited to prj1 that are requried in prj2 because of
> e.g. some shared code.
>
> I would do this with the following command:
> svn merge -r A:B "^/branches/prj1"
>
> Reintegration is not an option because the development will continue on
> both branches in parallel.

This approach sounds very chaotic to me.

Usually you'd isolate branches from another precisely because each
branch should *not* see code developed on the other cousin branches.
But at the same time you want these branches to share some code?
That's a contradicition. And it makes merging harder than it needs to be.

Every time you cherry-pick changes between cousins you run a high
risk of merge conflicts later when the cousins are merged into other
branches.

I mean, what you're doing is:

  svn copy ^/trunk ^/branches/branch1
  svn copy ^/trunk ^/branches/branch2

Usually, branch1 should never see code from branch2 and vice versa.
Unless one of the branches gets reintegrated into trunk, in which
case changes are transferred between the branches indirectly via
merges to and from trunk. That's the whole point of having two
distinct branches.

In your case, why are the branches which need to share common code not
branches off a branch which contains the shared code? Something like:

  svn copy ^/trunk ^/branches/shared-base-branch
  svn copy ^/branches/shared-base-branch ^/branches/branch1
  svn copy ^/branches/shared-base-branch ^/branches/branch2

Now you can develop common code on shared-base-branch, and changes
specific to branch1 and branch2 on each of those branches, respectively.
The flow of change is straightforward: Branch1 and branch2 only sync to
the shared-base-branch, which in turn syncs itself to trunk.
The shared-base-branch can be reintegrated into trunk once both branch1
and branch2 have been reintegrated into it.
This eliminates cherry-picking, thereby reducing the risk of merge
conflicts, and it also makes it easier to figure out which changes
have already been merged where.

If the above won't work for some reason, maybe you shouldn't be using
branching/merging in the first place. Instead, you could treat the
shared code as an external dependency which your build system will
pull into the build.
Pushing the problem out of the version control problem space into the
build system problem space can make sharing code a lot easier to manage.
You could also consider using an svn:external to pull the same shared
code into a subdirectory of each branch, instead of cherry-picking
changes between cousin branches.

> >> svn switch "^/branches/testing"
> >> svn merge "^/branches/prj1"
> >> svn commit -m "prj1 into testing"
> >>
> >> svn merge "^/branches/prj2"
> >
> > Again, you hit a similar problem here.
>
> I do not understand why there is a conflict in "second.txt".
>
> As far as I can see it the following files are the same:
> - /branch/prj1/second.txt
> - /branch/prj2/second.txt
>
> /branch/prj1/second.txt was added and never changed
> > svn switch "^/branches/prj1"
> > svn add second.txt
> > svn commit -m "2nd"
>
> /branch/prj2/second.txt was also added and never changed
> > svn switch "^/branches/prj2"
> > svn merge "^/branches/prj1"
> > svn commit -m "prj1 into prj2"
>
> With svn:mergeinfo the SVN should have enough information to see that
> the files are the same. There was no change and the original source is
> the same (/branches/prj1/)

You created second.txt on the prj1 branch. Let's assume you did so
in revision 40, so that the change within prj1_at_40 looks like this:

  A second.txt

("add a file called second.txt")

You then cherry-picked this revision from prj1 into prj2, causing a commit
that added second.txt to prj2. Let's call this changeset 'prj2_at_50':

  A second.txt (copied from, say, prj1_at_49)
  Mergeinfo addition: Merged prj1:40-49

Now you merge prj1 into testing, let's say in r61. The common ancestor of
testing and prj1 is trunk, which does not contain second.txt.
So among other changes you are merging:

  A second.txt (copied from, say, prj1_at_60)
  Mergeinfo addition: Merged prj1:40-60

Next, you merge prj2 into testing, let's say in r71. The common ancestor
of prj2 and testing is trunk, which does not contain second.txt.
So the changeset being merged looks something like this:

  A second.txt (copied from, say, prj2_at_70)
  Mergeinfo addition: Merged prj1:61-70
  Mergeinfo addition: Merged prj2:50-70

This results in the add vs. add tree conflict.

How is Subversion supposed to tell that prj1:40-70 contains the same
semantic change as prj2:50-70, the change which we cherry-picked from
prj1 to prj2? In terms of mergeinfo the addition of second.txt happens
in two distinct changesets: prj1_at_40 and prj2_at_50
Mergeinfo doesn't tell us that, semantically, prj1_at_40 and prj2_at_50 contain
the same change.

And neither does tracing back history to the common ancestor of prj2 and
testing tell us that these 'second.txt' files are related.
The common ancestor is trunk, which has never seen 'second.txt'.

The fact that this is a spurious conflict is evident to us because we
know how the second.txt addition to prj2 came about. But what if we had
added a different 'second.txt' file to prj2, which just happens to have
the same name but is otherwise unrelated to the existing file in testing
and prj1? Subversion cannot tell the difference between this scenario
and the one above, so it needs to flag a tree conflict to alert us of
the possibility that there is a genuine tree conflict.

One key aspect here is that Subversion treats addition as a first-class
operation. It doesn't care what the content of a newly added file is.
If the change you are merging contains an 'A foo' change, that means
"add foo", regardless of foo's content.
Received on 2012-10-17 19:12:56 CEST

This is an archived mail posted to the Subversion Users mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.