Update: Subversion fork was removed in favor of Protected Directories and Merge Requests. Get it for free with Assembla Renzoku.
Our release notes contain a description of the new Fork feature, which supports the traditional Git fork and merge workflow, plus Subversion Fork. Subversion doesn't suck, and it is very popular, but it was not designed for a fork and merge workflow. This article describes the problem and our proposed solution.
A fork and merge workflow blasts through the barrier created by your own team's commit privileges. "Contributors" can come along and fork (copy) the code, work on it in their own space, and submit merge requests to the "maintainer" of the original repository. It expands the number of contributors for open source projects. It's also good for trying out new people on a team, or for maintaining customized copies of an app that tracks a source app.
A fork and merge workflow should support these steps
1) Fork / clone / copy the repository
2) Submit a merge request to the parent repository
3) Merge changes from the child fork
4) Update the child fork by merging changes from the parent
The magic ingredient that makes it possible to take changes from a subversion repository that someone else controls, rather than just a branch in your own repository, is called "foreign merge". Subversion will happily accept accept commands to merge from a different repository than the one you checked out. This allows you to run step 3 (merge changes from the child fork).
The problem with subversion comes when you loop back after doing step 4 (merge updates into your fork), and go back to step 2 and 3 (send more changes to the parent). Suppose that the parent added a directory called "parents_new_feature". Then you updated your fork and merged this new directory. Later, when the parent tries to merge your work, Subversion will see that you added a directory called "parents_new_feature", and it will try to create a second copy in the parent.
Subversion has workarounds for this problem. Recent versions of subversion can remember the changesets - using revision numbers - that are already merged with a branch in the same repository. However, if you are doing a foreign merge, the revision numbers will be different in the two repositories, so this workaround doesn't work.
A distributed VCS like git or Mercurial tracks all changesets with unique ID's. So, when they see "parents_new_feature" coming back, it's obvious to them that they already have that changeset. They only merge the new changesets.
This isn't a big problem for most subversion users, because they do not merge back and forth. Most subversion users only merge in one direction. They branch, they make their changes, and they merge changes from the branch to trunk. Then they throw away the branch, and start over. The branch has a short life cycle, and does not need to do much merging from trunk.
So, we designed a worfkflow around that tactic. We added a button to the child fork labeled "Update Fork". This just wipes out your forked version of /trunk (we recommend that you rename your trunk first to save it as a tag) and replaces it with the parent trunk. Then you can start over making new changes. When you submit the new set of changes, the merge will find only your changes. there is no extra changeset where you merged from the parent.
Note that in this system, the Update Fork operation is always performed on the /trunk branch, and the merge instructions assume that you will merge from /trunk. To use the system, you must follow the convention that you put your contributions in /trunk.
If you want to keep your changes and customizations, you will have to merge from your copy into the newly updated trunk. If the parent accepted some of your change requests, you will have to work around the places where your changes were already merged into the parent.
Let's consider some use cases to see where we run into this already-merged problem.
- In the case of a private customization, this won't be a problem, because your customizations are not in the parent.
- If you are working mainly to contribute improvements to the parent, the relevant improvements should be merged with the parent and included in your updated fork, and you won't need to merge your copy.
- If you are combining the customization and contributor roles (probably because you find something that you want to fix), then you can get around the problem by making a new fork for your contribution.
- You will have a problem in the cases where you are working on a an unfinished contribution through several merge cycles - a classic subversion problem, but not a majority case.
In a lot of cases, the contributor does not need to merge. That's important in real life. Merges can get confusing. The maintainer who is accepting merge requests needs to have some sophistication about merging, testing, and reverting. But, the more numerous contributors might be less experienced newbies, translators, graphic designers, etc. This is possibly the very reason that a project is using subversion, rather than a more complicated and egalitarian tool like git, which requires all of its users to merge.
Here's where you find the fork feature ...
There is room for improvement in "Update Fork". For example, the current system requires you to do a client-side operation to rename /trunk, and after that hit the "Update Fork" to get our server-side replacement. It would be more convenient if the "Update Fork" operation did the rename on the server side so you only had one operation. That's a more complicated implementation, but we are working on it.
We thought about some alternate solutions. For example, we could use our Web interface and svn metadata to intelligently generate commands for the svn cherrypick feature. Or, we may just be unaware of how to correctly use the latest merge tracking features in svn 1.5 and 1.6 with foreign merges, or maybe we can make some additional tweaks to subversion itself. This "update fork" solution is simple, and it results in simple merges. However, we will consider alternate workflows if you suggest them.