0

A brief note before I start: there is a lot of explanation required to "set the stage", and it may seem like this is more of a design question than a question about a programming problem. The question is actually about SVN branching and merging, so please read to the end.

Scenario:

I have a large Visual Studio solution with quite a few projects. I'm using SVN, so of course the trunk has my production line of development. This consists of a core DLL assembly, a "main" UI user client, and a handful of "plugin" assemblies that operate by implementing interfaces on the core assembly in order to provide functionality within the UI, and also by utilizing a set of service methods which provide common functionality to all of the plugins (such as persistence logic operations, storage operations for a centralized file store architecture, etc.)

enter image description here

There are also external utilities that I have built over time which must duplicate a lot of the business logic in the plugins. I won't go into much detail because it will ultimately distract from my main question, but just picture, for example, a scheduled service on a server that handles centralized maintenance operations related to a particular plugin's data.

When I initially built this application, I (stupidly) didn't anticipate the need for centralized service tiers, so I architected the core assembly (for better or worse), as shown above, to be tightly integrated with the presentation layer of the application. In other words, the UI presentation logic needed to integrate the plugins with the user interface and the business logic needed by the plugins to perform common plugin logic operations is all part of the one "core" assembly. Therefore, much of the "shared" logic that exists between the plugins and the centralized services has resulted in duplicated code.

enter image description here

I decided to undertake the major refactoring initiative to pull out the common logic -- that which is not related to the presentation -- into a "shared" assembly. For this, I created a branch off the trunk. I reorganized common code into a "shared" assembly, and I re-pointed everything in the client application (plugins, etc.) and the external service applications to utilize the shared assembly. In many cases, I also had to rename classes in order to fit their more-general purpose going forward. The core assembly remained in place only to broker presentation-layer responsibilities between the plugins and the UI.

enter image description here

Problem:

Now that I have successfully completed the refactoring, I want to reintegrate the branch back into the trunk. Merging is tricky business even in simple cases, but what I'm facing here is a lot of tree conflicts to put it mildly. Also, in addition to residing in an entirely new project, the folder structure in the "shared" project is quite a bit different from what it was in the "core" project. Classes are, in many cases, located in different places due to the new mechanisms for using the shared assembly.

I want to maintain the version history of every class from its old home in the core assembly to its new home in the shared assembly. Furthermore, I want to guarantee that the merge is successful. That seems obvious, but in testing a miniature version of this whole scenario, I was never able to get the conflicts to resolve in such a way where my branch features remained entirely intact. Furthermore, the fact that I have renamed some of the classes, as I stated earlier, to suit their more-general roles, makes it very tricky to maintain the version history.

I will note that I am using AnkhSVN which helps in "normal" cases when you rename files to repair the moves, but it doesn't seem to work in these major tree-conflict cases. Also, I know there is a difference in how merges work between different versions of SVN -- I believe it's pre-SVN 1.5 and post-SVN 1.5. I'm using SVN 1.9.3.

I have been trying to figure this out for a few weeks now. I've been pouring through the SVN book, TortoiseSVN resources like this, and anything I could find from google searches, like this, this, and this -- among many, many, many others. I feel like I'm going crazy and I think advanced SVN (and Tortoise) are impossible to learn with the traditional teach-yourself, learn-from-the-web-and-books approach. At any rate, I would greatly appreciate any insight that is out there.

What is the proper methodology when you create a feature branch using SVN and plan on making major tree changes and "moves" (i.e. renames) so that you can reintegrate those changes with the trunk without losing anything?

Community
  • 1
  • 1
rory.ap
  • 34,009
  • 10
  • 83
  • 174

1 Answers1

0

Congratulations to stepping on the most "popular" rake in SVN - "Merge Hell after refactoring"!

There are (at least) two simple rules for your case, produced by the bitter experience:

  1. Never perform refactoring in SVN
  2. If you'll ignore rule 1: in the name of all that is holy and good in the world don't touch ANYTHING in trunk during refactoring in branch

If you reject these the righteous covenants you still have a ways to salvation

Pure SVN-way, long and dirty

Merge all and every subtree, which is source of Tree Conflict, determining by hands every source and target like

svn merge NEW_PATH/NEW_NAME old_path/old_name

and finalize this the bloody work by full merge

Tricky Mercurial-way (or Git-way, but I just hate Git)

Preface: such merges aren't problem at all for modern DVCSes, they have "bridges" to SVN-repos, thus - you can delegate this job of merging to external VCS of choice and return results back (with some limitations and warnings)

I'm too lazy to speak about all DVCSes and will explain only about Mercurial (considering that with SVN-background it will be the least painful migration).

With HGSubversion Mercurial can read (pull) and write (push) to Subversion repositories, but - it can't push to Subversion results of it's own merges, thus: it will be multi-stage operation with the substitution of WC of Subversion in the process

A brief synopsis

  1. Install Mercurial (TortoiseHG) and HGSubversion extension
  2. Clone the whole SVN-repository to Mercurial into some temporary location (not current Subversion WC)
  3. Merge branch to mainline (SVN's trunk become default branch), resolve (possible) context-conflicts (not tree)
  4. Test (?) results
  5. Perform the full replacement of Subversion Working Copy (WC of trunk, obviously) by the content of Mercurial Working Directory (beware of .svn and .hg folders respectively)
  6. Commit WC to trunk
  7. For the beauty and compliance with all rules "cheat" mergeinfo data of trunk (committed in step 6 must me known later as mergeset, although it is not true formally)

HTH

PS - migration to Mercurial with HGVS doesn't seems as totally crazy idea for now

Lazy Badger
  • 94,711
  • 9
  • 78
  • 110
  • Thank you for your reply. I wish I understood more about *why* SVN sucks at merging. But, then, I have a *lot* to learn yet about version control in general. – rory.ap Apr 13 '16 at 14:23
  • One problem with this approach is that while Hg tracks file renames, such round-trip of the data through Hg will, I suppose, lose this information. On the other hand, the question of whether keeping it would be relevant, is open -- after all, when you split a file into two, it's a moot point whether either of them should be deemed as an ansestor of the original; or both; or none. In either case it appears that after what you propose any files that got renamed in the right side of the merge won't be recorded as such in the resulting svn commit. – kostix Apr 14 '16 at 19:52
  • @kostix - yes, *at least one* problem. Brave boys can use `hg rebase --svn`, which is pushable to SVN, but I'm afraid of it for long-term branches – Lazy Badger Apr 15 '16 at 02:44