Reviewing Merges in Mercurial
In the brave new world of distributed version control, history is no longer linear. It can branch freely, under no central control. But what is perhaps more interesting is that you can merge disparate branches back together. This makes it much easier to do long-term feature work, because it is possible to track a moving target. For the ActionMonkey project, we are following a procedure where all changes are reviewed before they are pushed to the actionmonkey branch. This means that we won’t have a huge blob of changes that need review before they are integrated into mozilla-central.
This creates a new class of problem: how do you perform merges? Is it necessary to review merge changesets with the same rigor as regular changes? How would one actually go about reviewing a merge changeset?
Today, I wanted to update ActionMonkey with the latest changes from mozilla-central. There is enough divergence between ActionMonkey and Mozilla 1.9 that this is not always a simple task: fortunately for me, the only conflicts that required any real merging were fairly simple. I went ahead an performed the merge and did some basic testing. But I really want Jason Orendorff to review my changes for sanity. There is no simple way to see what I actually did:
It isn’t hard to diff this changeset against its mozilla-central parent, or against its actionmonkey parent. But neither of these diffs give you a sense of the real work involved in the merge. What I really want to give to Jason is a static view of a three-way merge as it already happened, highlighting the source locations where I made conflict resolutions or manual changes. Does anyone know if such a tool exists for Mercurial, or for any other distributed version control system?
Because I don’t know of a tool like this, I did the next-best thing: in my checkin comment, I carefully listed every file and function where I made a conflict resolution or manual change. This will at least make it clear in the future where I may have goofed.
April 17th, 2008 at 7:01 pm
There are some issues in the Mercurial bug tracking system, mostly from the NetBeans guys (Jesse Glick) on improving this kind of thing.
April 18th, 2008 at 4:07 am
OK, so I know this is unhelpful as I don’t even know whether this works, but it seems as if it should be easy to do in CVS:
cvs co -r ACTIONMONKEY_BASE mozilla/js
cvs up -j ACTIONMONKEY_BRANCH mozilla/js
cvs up -A mozilla/js
April 18th, 2008 at 7:42 am
Good idea.
As far as I know there is no tool in any DVCSs to do this. It requires a certain amount of integration with the machinery that drives and evaluates merges, though it would not require redoing past merges in a “recordable” state. Just command-level awareness of what a past merge would logically have done on its own — pre-intervention — when attempted. Call the command “hg review_merge”.
The idea would be that the tool attempts to re-perform the merge anew in memory, using an auto-merge strategy (classical 3-way, in most of these tools), and notes every place the auto-merge strategy fails (either file-by-file or, if you want to mask out even more likely noise, hunk-by-bunk). It would then calculate what happened to that file-or-hunk between the output of its auto-merge attempt and the file you produced during the manual merge intervention.
Some of the 3-way mergers (kdiff3 for example) seem to be slightly smarter than diff3 when merging, so you might want to shell out the auto-merge task to it, particularly if *its* auto-merge was the basis for your manual merge. To do that properly you would need to be able to force it to leave conflict markers in the file, and you’d need to parse them on the way out. I’m not sure it has such a mode, but it’s certainly possible. Decay to diff3 if in doubt, as it has no choice but to leave conflict markers: it’s non-interactive!
I could probably write it, but it’d be a little hairy. It depends how well-integrated the auto-merge machinery is in the tool already.
August 14th, 2008 at 2:02 pm
What you can do with other systems and probably hg, is to make the automatic merge, and _ignore_ the conflicts and check in the purely automatic merge into the repo. So, you have a bad revision. (You may have to override the VCS’ safeguards which protect you from checking in conflicts.) Then, you go and fix the merge conflicts and check in the merge fixes as a new revision.
That’s what I did in the past and it works – you see the merge fixes as separate revision, which in a way it is – it’s a change on its own.