Skip to main content

Forcing a correct Subversion branch merge despite tree conflicts

Let's say your development team is following the common pattern for maintaining a "clean" releasable trunk in Subversion. All your development is done in "feature branches", which you merge into a "development integration" branch. From there code is merged into a QA branch for further testing, and is eventually merged into trunk and released.

So your bog-standard SVN repository would look something like this:

/trunk
/branches
    /DEVELOPMENT
    /QA
    /feature1
    /feature2
/tags
   /v1.0.0
   /v1.1.1

Now, what frequently happens in this situation is issues are merged from /branches/DEVELOPMENT to /branches/QA out of order. That is, sometimes you have a bug-fix that you want to move rapidly into production, so it gets plucked from /branches/DEVELOPMENT by itself without merging some of the commits that came before it.

This "cherry picking" of code can be problematic in almost any version control system (including Git), but in Subversion it can result in merge conflicts or even the dreaded tree conflict because you are pulling in issues out of order. If some files or folders were refactored or renamed in /branches/DEVELOPMENT, and the "later" bug-fix commit has a dependency on that rename, you have to do some manual conflict resolution to get things to merge into /branches/QA in a working fashion.

Now if you have dozens of developers working on the same application, with features and bug-fixes being committed to /branches/DEVELOPMENT every few minutes, problems escalate rather quickly. You often get into a case where almost every merge from /branches/DEVELOPMENT to /branches/QA generates multiple conflicts of one form or another. The branches have basically "diverged" significantly due to code being merged out-of-order and doing manual conflict resolution.

The only real solution is to periodically "catch up" /branches/QA to /branches/DEVELOPMENT somehow.

Over the years, I have seen developers pull out file comparison tools and "manually" do this, which actually causes a gap in the internal Subversion history as renamed files "appear" in the history instead of being copied from /branches/DEVELOPMENT. This seems to work intially, but generates more merge conflicts in the future, because the relationship between certain files in the two branches is lost, and ultimately doesn't really fix anything.

I've also seen developers delete /branches/QA entirely and re-branch it from /branches/DEVELOPMENT. This approach ultimately forces them to delete and re-create /trunk as a branch of /branches/QA so merging can still take place! Obviously this leaves a big "discontinuity" in the history, and makes many repository maintenance and conversion tools break horribly. Just try using the git-svn integration tool on such a repository.

Here's the correct solution:



  1. Roll back the history of /branches/QA to it's very "beginning" in a working copy. That is, go back to the commit where the branch was initially created. This is easy in TortoiseSVN using "Revert to this revision" from the GUI log viewer. You can also do it from the command line with a reverse merge. Do not commit just yet. This is a fast-rewind and should never generate any conflicts; it also rolls back the svn:mergeinfo to the same state as when the branch was created
  2. Now, merge all commits from /branches/DEVELOPMENT to /branches/QA in that same working copy. You may get warnings about there being local modifications in your working copy before doing your second merge. Don't worry, that is what we want! This second merge updates all the files to be exactly the same as they are in /branches/DEVELOPMENT, as well as updating the critical svn:mergeinfo properties correctly. Assuming /branches/QA was initially created from /branches/DEVELOPMENT, this is a "fast-forward" merge and will never generate conflicts.
  3. Now, before committing, use diff tools to examine the status of svn:mergeinfo as well as the files. You'll see that the resulting "diff" which will be committed is just the changes that make your old-and-busted /branches/QA identical to /branches/DEVELOPMENT, with the additional merge information that "we are now totally up to date with /branches/DEVELOPMENT". You can also do a double-check file compare between this working copy and a fresh checkout of /branches/DEVELOPMENT if you want to be sure (ignore the .svn directory during the file compare).
  4. Commit! You now have a fixed /branches/QA with a single small catch-up delta commit that can also be merged cleanly into /trunk


Comments

Popular posts from this blog

Fixing slow NFS performance between VMware and Windows 2008 R2

I've seen hundreds of reports of slow NFS performance between VMware ESX/ESXi and Windows Server 2008 (with or without R2) out there on the internet, mixed in with a few reports of it performing fabulously.
We use the storage on our big Windows file servers periodically for one-off dev/test VMware virutal machines, and have  been struggling with this quite a bit recently. It used to be fast. Now it was very slow, like less than 3 MB/s for a copy of a VMDK. It made no sense.
We chased a lot of ideas. Started with the Windows and WMware logs of course, but nothing significant showed up. The Windows Server performance counters showed low CPU utilization and queue depth, low disk queue depth, less than 1 ms average IO service time, and a paltry 30 Mbps network utilization on bonded GbE links.
So where was the bottleneck? I ran across this Microsoft article about slow NFS performance when user name mapping wasn't set up, but it only seemed to apply to Windows 2003. Surely the patch me…

Google's public NTP servers?

I was struggling with finding a good set of low-ping NTP servers for use as upstream sources in the office. Using pool.ntp.org is great and all, but the rotating DNS entries aren't fabulous for Windows NTP clients (or really any NTP software except the reference ntpd implementation).

ntpd resolves a server hostname to an IP once at startup, and then sticks with that IP forever. Most other NTP clients honor DNS TTLs, and will follow the rotation of addresses returned by pool.ntp.org. This means Windows NTP client using the built-in Windows Time Service will actually be trying to sync to a moving set of target servers when pointed at a pool.ntp.org source. Fine for most client, but not great for servers trying to maintain stable timing for security and logging purposes.

I stumbled across this link referencing Google's ntp servers at hostname time[1-4].google.com. These servers support IPv4 and IPv6, and seem to be anycast just like Google's public DNS servers at 8.8.8.8. time…

Presets versus quality in x264 encoding

I'm scoping a project that will require re-encoding a large training video library into HTML5 and Flash-compatible formats. As of today, this means using H.264-based video for best compatability and quality (although WebM might become an option in a year or two).
The open source x264 is widely considered the state of the art in H.264 encoders. Given the large amount of source video we need to convert as part of the project, finding the optimal trade-off between encoding speed and quality with x264-based encoders (x264 itself, FFmpeg, MEencoder, HandBrake, etc.) is important.
So I created a 720p video comprised of several popular video test sequences concatenated together. All of these sequences are from lossless original sources, so we are not re-compressing the artifacts of another video codec. The sequences are designed to torture video codecs: scenes include splashing water, flames, slow pans, detailed backgrounds and fast motion. I did several two-pass 2500 kbps encodings using …