Handling Binary Files in a Merge

posted by Ryan Wilcox on November 30, 2009

Say you’re building a website and you keep your images in the Mercurial repository for the site. Eventually two people are going to change that file at the same time and have a merge conflict.

Setting up Mercurial for Different Merge Tools

In your ~/.hgrc file, you should add the following section:

[merge-tools]
diff_images.args = $output $other

[merge-patterns]
**.png = diff_images

Now, create a diff_images shell script, set it to executable, and put it on your $PATH. Mine is:

open -a GraphicConverter $1 $2 -W 
# opens the first and second parameter with GraphicConverter, waiting
# until same is quit before letting the shell continue.

Now a merge that contains .png files that conflict will open up GraphicConverter. There will be two images that open up: one as it exists in the your source tree now, and one from Mercurial. You can tell the difference because the one from Mercurial will have a lot of random-seeming letters after the file extension. For example, if your conflicting file is image.png, GraphicConverter will open up two windows: image.png and image.png~other.bQkQxd.

Manually move your changes from the new file into the original file (image.png), saving the original when done and quitting GraphicConverter. These are the changes that will be committed.

How this Setup Works

First, diff_images.args sets up two parameters to pass into the graphics tool:

There are other options that the .args parameter provides:

You could use these to set up a three way merge, if you desired, or enhance diff_images to show the user the contents of the file before this merge mess happened.

Next, the [merge-patterns] section associates what merge tool to use when a file name matches a particular pattern.

Without the merge-pattern section, diff_images would be considered for a diff candidate, but skipped over because Mercurial will assume that diff_images can’t handle diffing binary files. Adding lines to [merge-pattern] will tell Mercurial that diff_images can handle the file types specified.

Instead of putting in a merge-patterns section, you could add a line, like diff_images.args, that tells Mercurial that diff_images can handle binary documents: diff_images.binary = True

However, this will make diff_images a candidate for all binary differences, which GraphicConverter may not be up to (for example, diffing mp3s would not be a good task for GraphicConverter).

When Things Go Terribly Wrong: Aborting the Merge

Merges can not be aborted via the standard hg revert mechanism. Try it and you’ll get:

$ hg revert --all
abort: uncommitted merge - please provide a specific revision

But you just want to blow away your merge work and start from scratch. Use hg update --clean for this.

References

Mostly we deal with text, source code etc. But sometimes we put binary files into our repository and dealing with the inevitable merge conflict is not as easy as source code.