Categories
Software development

Image diffs with git

Git is an awesome version control tool. Currently I use it exclusively for projects where I get to decide the VCS. I mostly use the command line interface and gitk and since I’m quite fluent with these the work flow is most of the time quite quick.

One of the occasions where gitk doesn’t really do well is showing differences in image files. For example, when there is a .png image checked in to the git repository and it changes gitk (nor bare git) tells nothing about what changed visually in the image.

Since git is quite configurable and extendable I wanted to search for a way to amend it somehow in order to really see how the images in my git repositories have changed – visually.

First off course I searched from the net what kind of solutions others might have for this problem. I got some ideas but not really a satisfying complete solution.

From [1] I found the tip of using .gitattributes for specifying how to handle diffs for images differently from other file types. From [2] I got encouragement to use ImageMagick for showing the diffs. I have used ImageMagick before for many things so it wasn’t a new tool for me. I might have used it anyway.

These propositions were still lacking in my opinion so I needed to improve them.

My solution for image diffs in git

Here’s a step-by-step guide how I currently do my image diffs with git.

Telling git that images are images

First I needed to tell git that it should handle image files specifically. This was achieved with a .gitattributes file. Since in my opinion images are images regardless of the project or git repository I’m using, I did this globally. It’s possible to do this per project too if you so wish.

First I specified a global git setting for a global .gitattributes file:

[~]$ git config --global core.attributesfile '~/.gitattributes'

Then in the file specified above I added some specifications that tell git that some file name extensions should be considered as image files:

[~]$ cat .gitattributes 
*.gif diff=image
*.jpg diff=image
*.png diff=image

More file name extensions can be off course added as needed.

Telling git how to show diffs for images

After the previous step git knows what are image files but it still wouldn’t show their diffs any differently from other binary files. That needed to be specified next.

First I added another global setting to git that specifies a command that is used for diffs of files of type “image”:

[~]$ git config --global diff.image.command '~/bin/git-imgdiff'

The script specified in that setting needs to off course exist. Here’s the contents of the script that I currently have:

[~]$ cat ~/bin/git-imgdiff
#!/bin/sh
compare $2 $1 png:- | montage -geometry +4+4 $2 - $1 png:- | display -title "$1" -

All of the commands used in the script (compare, montage and display) belong to the ImageMagick suite.

That’s all

And that’s it so far. There’s off course a lot of place for improvement but this is my current solution.

Here’s an example screenshot what the result of all this might look like:

The starting point of the diff is on the left, the end point of the diff is on the right and the differences of the images are in the middle. The changes in the image are shown with a bright red colour.

References

[1] http://lars-tesmer.com/blog/2010/09/20/git—how-to-get-better-diffs-for-images/
[2] http://ospublish.constantvzw.org/blog/tools/diff-git-imagemagick

16 replies on “Image diffs with git”

On my system, the following works without altering configuration or .gitattributes:

git difftool -x git-imgdiff -y *.png

The downside of configuring a “default” diff tool is that even git diff –binary will use the diff tool, and some of my scripts rely on it.

Have you found a way to treat images with different dimensions?

Good point. Basically the git-imgdiff script can be used to compare any two images. There’s no need for git to be involved at all (maybe I should rename the script to just imgdiff…). I’m sure there are even more possibilities in git where this script could be attached to.

One nice thing with the way I did it (i.e. telling git to handle image files in a special way) is that when you have done some local changes to many files (including images and text files for example) you can just call ‘git diff’. The text file differences are shown in the console and the image diffs are shown with the helper script. This might not off course suit everybody but I found it useful.

Unfortunately I’ve got nothing for images with different dimensions 🙁

I had some trouble with your script when not standing in any of the branches I’m diffing. After debuging for a while I found that a more correct line would be:

#!/bin/sh
compare $2 $5 png:- | montage -geometry +4+4 $2 – $5 png:- | display -title “$1” –

When running the command git diff master…feature
$1: path to the file in the repository
$2: path to temporary checkout of file from master
$3: hash for file in master
$4: mode (privileges) for file in master
$5: path to temporary checkout of file from feature
$6: hash for file in feature
$7: mode (privileges) for file in feature

Thanks for a good how-to though

And another improvement: Add -fuzz to compare to ignore differences in jpeg-artifacts. 4000 is a value I arrived at through experimentation but can be overridden through the variable $GIT_IMAGE_DIFF_FUZZ.

#!/bin/sh
if [ -z “$GIT_IMAGE_DIFF_FUZZ” ]
then
GIT_IMAGE_DIFF_FUZZ=4000
fi
compare -fuzz $GIT_IMAGE_DIFF_FUZZ $2 $5 png:- | montage -geometry +4+4 $2 – $5 png:- | display -title “$1” –

Hi Aki,
Very interesting read. As a production artist in games I’ve grown accustomed to using GIT. However, I was curious as to whether it makes sense to use it for tracking the progress in image files from a storage perspective. Specifically for image files such as tiffs, do you know whether GIT stores only changes in the files, or does it store a complete copy?

Thanks!

Hi Aki,
Very interesting read. I was curious whether GIT stores whole copies of these image files, or only their deltas. I imagine it may vary by filetype, but what about say, a .tiff file?

Hi! When I run this on Windows, I have to use imdisplay instead of display. However, all I get is one image, not a comparison between two.

Here are the commands I’m using:

compare $2 $1 png:- | montage -geometry +4+4 $2 – $1 png:- | imdisplay -title “$1” –

Leave a Reply

Your email address will not be published.