The Problem

The scenario is: a long-lived release branch getting bug fixes, and trunk undergoing development. Bug fixes are merged between the trunk and branch; there are two (at least) ways this can be handled. One is the approach used by the Subversion project: fix bugs on trunk and cherry-pick merge those changes to the release branch. Other organisations do it differently: they fix bugs directly on the branch and do catch-up merges to ensure that the changes get onto trunk.

Both approaches suffer when the trunk development renames files and directories as the rename often indicates major new development that is not suitable for the branch. This means that the merges raise tree conflicts that require user intervention to solve. The big problem is that having solved and committed a solution, the next merge is likely to produce essentially the same conflict requiring the user to go through the same steps to resolve it.

The Aim

We want the merge to automatically apply incoming changes to the moved items. These are not uncommitted local moves, they are moves that have been committed to the repository. So the solution is to identify moves in the repository history and then use those moves to adjust the incoming merge differences so that they apply to the moved item.

The first stage, identifying moves in the repository, is the subject of ongoing work (Record moves in the database, extracting them from the log history, etc.). The second stage involves using the identified moves to avoid conflicts: some work has also been done on getting update to understand local, uncommitted moves, and this can probably be extended.

It is not clear how well automatic move identification will work in practice. Even if it works well there will always be some moves that cannot be identified automatically, if only because the user simply made the move using add/rm without any sort of copy. Another scenario is splitting a file in two and later discarding one half.

The idea here is to allow the user to resolve conflicts by telling Subversion "A moved to B" and storing that information. This could happen before the merge, or during conflict resolution, or at some other time. The information gets stored in a property and committed so that it is available for subsequent merges. In this way the user only has to resolve the conflict once and repeat merges don't conflict.

This doesn't preclude automatic move identification. Initially we require the user to resolve all moves, but as automatic move identification starts to work it can bypass asking (or perhaps suggest the answer).

The Design

What information needs to be stored?

"A moved to B" obviously. "at rN"?

Do we need to store a revision range?

Where is it stored?

In an svn:property of some sort. It may need special handling to merge changes to the property along the lines of svn:mergeinfo.

Do we store the property at the working copy root? In the parent of of the moved item?

Given A/B/C/D on one branch and suppose there have been three renames on the other branch at various times:




giving A/X/Y/Z. We could store all three moves in the root or we could store the B->X in A, C->Y in B and D->Z in C. Storing it in parent would mean that it automatically adjusted as the parent was moved. If this good or bad?

What would root storage look like? There are obviously ordering issues.




How is the information obtained?

During interactive merge resolution?

"svn resolve --moved-to B A"

When is it stored?

Like any property change it can be explicily committed, but generally it would be stored as part of the commit of the merge.

Multiple branches?

Suppose two branches are merging to trunk, with one branch older than the other. The trunk may have made renames between the two branches. So A->B may apply to branch one while X->B may apply to branch two.

Does the conflict resolution stored on the source branch affect how it gets merged to the destination?

Is there transitive behaviour?

RepetitiveResolvingOfTheSameRename (last edited 2011-11-18 12:28:10 by PhilipMartin)