Sunday, February 10, 2013

Public service announcement: patdiff is awesome

This is just a word of note: patdiff is an amazing diff tool from Jane Street that’s totally blowing my mind. I’m very, very thankful to Jake McArthur (a Jane St. employee) for showing me this tool. He was missing it at home too!

It uses Bram Cohen’s patience diff algorithm for computing differences between two files. But the real benefit of patdiff is the beautiful output besides that: only relevant, changed terms are highlighted, and insertion/deletion/modification markers only appear on the side.

It’s been difficult training myself to the new output of patdiff since it’s quite different from most other formulations of patience diff. Git itself has patience diff (but it wouldn’t really make a difference in the picture, ha ha!1) So does bzr, and probably hg. However, I really like this tool a lot already, even though I’ve only used it for a day or two.

I think the most important aspect of patience diff is that it matches lines which actually change much more accurately, like in Bram’s example in his post I linked to. As a programmer, you often make changes to code like this:

 void func1() {
     x += 1
 }

+void functhreehalves() {
+    x += 1.5
+}
+
 void func2() {
     x += 2
 }

Which is completely reasonable. But a lot of tools interpret this as:

 void func1() {
     x += 1
+}
+
+void functhreehalves() {
+    x += 1.5
 }
 
 void func2() {
     x += 2
 }
But I love this color on my 80x24
terminals! Source.

Which is completely annoying.

So, patience diff is a lot more accurate. patdiff takes this to 11, and is very particular about what it highlights on the line too - this is the real thing that sets it apart from other tools.

Notice in the picture that it only highlights the exact words that were inserted or deleted from my .cabal file. At a glance, you might think “well, that makes it hard to visually distinguish those two inserted and deleted blocks.” That is, it’s hard to see the difference between the ‘bulk’ changes without looking to the left-hand side. And yes, you’re right - git with color, on the other hand, makes the contrast between insertion blocks and deletion blocks very clear. But the contrast is clear much in the same way it’s clear on your friends green and black PT cruiser.

Considering the extra accuracy, I think retraining my eyes on reading diffs a little is worth it.

Getting patdiff

Briefly, you need to install opam. If you’re on OS X and use Homebrew, that’s as easy as 1-2-3:

$ brew update && brew install opam

If you’re on Linux, you can use the installer script to install it into $HOME. You’ll need the PCRE development libraries and the OCaml compiler, however. On Ubuntu, you can get these by doing:

$ sudo apt-get install libpcre3-dev
$ sudo apt-get install ocaml

Then, install opam using the automated installer:

$ wget http://www.ocamlpro.com/pub/opam_installer.sh
$ sh ./opam_installer.sh $HOME/bin

After you install it, initialize the opam environment:

$ opam init

Finally, add the following line to your $HOME/.profile:

$ (which opam > /dev/null) && eval $(opam config -env)

And run it in any existing, open shells.

Finally, just update and install:

$ opam update
$ opam install patdiff

Adding a git patdiff alias

You’ll notice in the picture above I configured git to have a patdiff alias that shows things all beautiful and ocamlized. It’s pretty easy, luckily:

$ git config --global alias.patdiff 'difftool -y -x patdiff'

And now you use git patdiff to show differences, with the syntax:

$ git patdiff OLD NEW

Note that because of this syntax between old and new, if you want to see the diff of the latest commit, you must do:

$ git patdiff HEAD~ HEAD

and not do:

$ git patdiff HEAD HEAD~

The latter will show you a ‘reverse’ diff instead.

I’m looking into making patdiff the default for all traditional diff operations. It seems that git difftool does support this, but only for a handful of extra diff tools. It’s likely doable with some shell script nastiness. I also need to see if I can get it working with mercurial, which is what we use at work.

Update: Using patdiff with git diff

You can use patdiff with regular git diff too! Do this:

$ cat > $HOME/bin/git-external_patdiff.sh
#!/bin/sh
patdiff $2 $5 -alt-old a/$5 -alt-new b/$5 | cat
^D
$ chmod +x $HOME/bin/git-external_patdiff.sh

That will put a simple git wrapper for patdiff in your $HOME/bin. Next, configure git to use it:

$ git config --global diff.external $HOME/bin/git-external_patdiff.sh

Then git diff will use patdiff as well.

Update: meta blog diffs

Here’s another example of using patdiff while editing this very blog post on my OSX machine. Isn’t that diff just so much better?

Americans: do not confuse wibbles and tribbles. They’re unrelated.

  1. Before you ask: no, I do not do stand up comedy.

6 comments:

  1. FWIW, Jake is a current Jane Street employee, unless something untoward has happened over the weekend.

    ReplyDelete
  2. Oh, I'm an idiot. I should have asked Jake first (People in #haskell tend to shift around jobs from time to time, so I likely got him completely confused for someone else. Fixed!)

    ReplyDelete
  3. with standard git diff, "--word-diff=color" has some of the same effect.

    (installing patdiff now anyway :)

    ReplyDelete
  4. I love meld tool for a long time now, and I remember when I worked with mercurial one cool thing was that it made meld usable by `meld extdiff p- meld` command, which has options to show a particular commit diff or a branch diff easily. Don't remember all it can do exactly, but thought that might be helpful.

    ReplyDelete
  5. FYI, OS X folks might have to do a `brew install pcre` before `opam install patdiff`

    ReplyDelete

  6. Computer repair service in delhi at doorstep in a reasonable price

    http://www.mkpcdoctor.in

    ReplyDelete