0

My SVN repo is like this:

/trunk
/branches/project1/trunk
/branches/project1/branches

My directory /branches/project1/trunk is a branch of /trunk. I have made a lot of modifications to this branch (new code, new files added, files deleted, etc...). I sometime merge from branch to trunk, and from trunk to branch. But my mistake was to always use "Merge a range of revisions" in all cases. I just understand recently that "Merge a range of revisions" should be used for the merge from trunk to the branch, and "Reintergrate a branch" is for the merge from the branch back to the trunk.

So today, I try to merge the diffs from the branch to the trunk with the right method, and fix a lot of conflicts. But since then, when I merge from branch to trunk and from trunk to branch, SVN wants to "update" some files that had been added one day in the branch. And the update does nothing except increasing the rev number. If I try to merge 2 or 3 times, the same files are "updated" each time (e.g. increasing the rev number only).

Is there a way to fix this? Or should I delete my branch and create a new one, at the same path?

The SVN version is:

TortoiseSVN 1.6.16, Build 21511 - 32 Bit , 2011/06/01 19:00:35
Subversion 1.6.17
4

1 回答 1

2

Your problem here is your workflow. Branches are not designed to be used in the way that you are using them. You can merge changes into a branch from the branch's source (in your example, /trunk), but once you merge the branch changes back into the source you should stop using the branch and delete it. (Side note: technically there is an 'official' way to keep a branch alive after reintegrating it but since it's equivalent to deleting and re-creating the branch and more difficult to do, it's usually not the best route to take)

The problem here is metadata. When you merge changes between branches, Subversion records metadata indicating which revisions were merged in. Merging from branch back to trunk attaches metadata to the trunk indicating that certain revisions from the branch were merged in. The next time that you merge changes from trunk->branch, one of the revisions that gets merged in is the trunk revision representing the branch->trunk merge. At this point the metadata is rather confusing and almost circular, and you can actually end up corrupting your branch by merging in older bits of code that you have modified in the branch since the last merge. Even if the merge finishes successfully, you are increasing the likelihood of conflicts on future merges. In general, it's extremely error-prone and not a use case that Subversion was designed around. For a better explanation of why this causes problems, see the link above.

In general, branches are designed for temporary, short-term development work. What you are doing sounds more like a longer-term fork than a branch, and that's definitely not one of Subversion's typical use cases. You have a couple of options here.

1) When you merge from the branch to the trunk, don't record any metadata about the merge. Either hand-merge the changes (instead of letting Subversion do it), or merge the changes and then revert the metadata changes before committing. This is not a very good solution and introduces opportunities for copy/paste errors, but it might result in fewer problems. You're still operating outside of the standard Subversion workflows, so I expect that you'll still run into issues.

2) Avoid partially merging a branch back into the trunk. If you merge all of the branch's changes, do a --reintegrate merge and then delete the branch (you can always create a new one, even one with the same name). If only a subset of the changes need to be merged, do the initial development in the trunk (not the branch) and then merge the changes from trunk to branch. That way, all of your merges are still going in the same direction, and the end result is the changes are present in both trunk and branch.

3) Consider using git. The workflow that you are describing is more easily done with git and doesn't involve jumping through as many hoops to get it running. You can also run git on top of Subversion, so you can use a git repository on your local machine that pulls from and pushes changes to a Subversion repository.

4) Eliminate the need for the fork by refactoring your code so that a separate branch isn't necessary. Break out the common functionality into a shared module, and hide the differences behind a common interface. If you'd like, you can use compile-time flags to choose which version to build. This would eliminate the need to do merges entirely.

#4 is obviously the best solution. I understand that it may not be possible to do that sort of thing in all cases, especially when the code base is large and the branch makes major architectural changes. When that's not possible, I recommend option #3. I use this method personally. My team uses a Subversion repository, but several developers on the team - including myself - run git on our local machines in order to do some more-advanced things that Subversion doesn't do (or doesn't do easily). We don't maintain any long-term branches like you are doing, but we often use multiple short-term branches and merge changes between them.

For a more detailed description of Subversion's typical use cases regarding branches and merges, see the Subversion book.

于 2012-07-10T17:30:48.547 回答