Saturday, April 17, 2010

See a visual diff of work done in a git branch with git-branch-diff

I have developed a Ruby script called git-branch-diff that allows you to use a graphical directory compare tool to compare the working tree of a branch with the state of the tree from which it was originally branched. For download, setup, and usage instructons, see:
Background - My Workflow
I don't know about you, but a big part of my development workflow involves constantly diff'ing the changes I have made and am in the progress of making, as a way to constantly keep in touch with the state of what I am working on, and even as a reminder of what I am supposed to be doing. I like to write notes in contextual comments that I never intend to push out to the master branch, to help remind me of TODOs that need to be done before calling a feature sprint done.

In my world of constant multi-tasking and task-switching, I frequently have numerous topic branches in progress on the same repository. I like to end the day with no uncommitted changes, and even no un-pushed changes (i.e.: let github handle backups). So, after a task-switch, it's an integral part of my workflow to be able to see a good diff of the cumulative work I've done in a branch that hasn't been merged back in the master yet. This is usually pretty easy when we're talking about uncommitted changes. But, when I want to see the set of changes in that branch that would be merged if I were to do it now, things get a little more difficult.

The Problem
Git is a powerful tool with a crazy number of features. I probably only know 1% of them...and everywhere I searched, I could not find a way to do a visual directory compare of what has changed in my branch. Perhaps I've been spoiled by a great line of tools from syntevo GmbH that I've been using for years. I started with SmartCVS, then moved on to SmartSVN, and now to SmartGit. I am, always have been, and always will be a command-line guy. I'll admit that most of my cvs/svn/git usage happens from the keyboard. But I have come to rely on these GUI tools as an excellent addition to the command-line when it comes to visualizing my working tree status, performing directory and file diffs, editing line-by-line diffs, 3-way-merge conflict resolution, and easy selective add/commit/revert operations. Over the years I have come to rely on them as a key part of my routine - especially the directory diff view of all the changes in my working tree.

SmartGit has that capability for comparing the index, HEAD, and working tree, but not (yet) for arbitrary commits...or what I want most: comparing the HEAD of a topic-branch to the commit from which it was branched. The folks at syntevo assure me this will be included in a forthcoming point release, but what am I to do in the meantime?

Attempted Solution
I can get the data I want with a command like:
git checkout my_topic_branch
git diff `git merge-base master HEAD`
That shows me exactly the diffs I am interested in, but in a primitive textual (albeit colorized) context diff that I have to navigate with less.

Well, the folks at syntevo have another awesome tool called SmartSynchronize. This is their directory/file diff/sync/merge tool from their source control products as a standalone diff tool. So...why not just pump this diff data into that? To do that, you need to edit your ~/.gitconfig file to specify the custom difftool. For example, on Mac OS X, these additions look like:

The problem with this is that git invokes the difftool once per file being diff'ed. That's no good. I don't want to open my external visual diff tool 100 times. I want to open it once with a list of all the files added/modified/removed and be able to click on them individually to see their diffs, and ideally even be able to edit, synchronize, revert, etc. SmartSynchronize gives me all this...but git doesn't give it two directories to compare, it gives it two files to compare, one file at a time.

The Solution
So, I developed a tool called git-branch-diff that does all this for me. It works by finding the names and statuses of the differing files and constructing two temporary directories that match the original directory structure with the old and new versions, respectively. Then it passes those directories to SmartSynchronize, and voila! I've got a beautiful directory compare that represents the state of my topic-branch:

It only takes a few seconds to download and setup, and using it is as simple as:
git checkout my_topic_branch
git branch-diff
This script would be pretty easy to modify to handle comparing arbitrary commits...but once syntevo adds this to SmartGit, I won't need this anymore anyway! But, for those of you that won't be using SmartGit, this script can still be useful to you, and you can hook it up to whatever directory compare tool you want (e.g.: opendiff or BeyondCompare).

If you think this tool would be a valuable addition to your workflow, you can get the code and instructions for this tool at:
It can easily plug in any directory compare tool, but I highly recommend you give SmartSynchronize a spin with its 30-day free trial.

NOTE: One current limitation in this script is that after making and saving modifications in SmartSynchronize, this script does not apply those back to your working tree. I'll probably implement that soon, so if you're interested, stay tuned...

1 comment:

eugene said...

It's weird no one has actually needed to use this :)

Thx a lot, such a headache gone now!