AaronCrane.co.uk

Using git jump with Emacs

Git version 1.7.8, which is shortly to be released at this writing, contains a handy new program git jump. The idea is that you can say things like git jump diff to have your $EDITOR go directly to the lines within your files that contain unstaged changes.

The design of git jump is particularly helpful for Vim users, but what if you prefer Emacs?

The central idea of git jump is that it relies on Vim’s notion of “quickfix” files. Conventional usage of quickfix files is to run a compiler on your code, saving its error messages into a file; then you can say vim -q error-file, and Vim helps you step through the list of errors and fix them. In particular, you needn’t manually open the right files and then jump to the location flagged by the compiler.

So git jump merely generates a file with a list of relevant code locations, in a fairly standard format for compiler error messages, then launches $EDITOR -q on that file. Building on the existing quickfix mechanism in this way is a pretty good design, I think.

Emacs has a feature broadly equivalent to Vim quickfix files, but the other way round. Where Vim expects you to run the compiler outside the editor, saving its error messages to a file, Emacs encourages you to run the compiler inside the editor. The error messages are saved directly into an Emacs compile-mode buffer, whence you can step through them in much the same way as with quickfix locations in Vim.

Using git jump with Emacs, then, is just a matter of finding a way to get compile-mode to treat the relevant quickfix file as compiler output. Fortunately, that’s not too hard. First we’ll need a new Emacs function that treats a named file as if it were compiler output; and then we’ll need to make sure that $EDITOR -q invokes that function appropriately.

Start by adding this Emacs function to your ~/.emacs (or equivalent):

(defun quickfix-open (files)
  (setq-default compilation-directory default-directory)
  (compilation-start
   (concat "cat "
           (mapconcat #'shell-quote-argument files " "))))

That defines a function quickfix-open which takes a list of filenames, and starts a compilation with a command that just runs cat on those filenames.

The script is just slightly more tricky. I’m going to assume that you use emacsclient (which lets you open files in a running Emacs process). The script will therefore run emacsclient directly by default, but if it receives the -q option, it will use emacsclient -e expression to evaluate a suitable Lisp expression in the running Emacs. Since we already have quickfix-open, we’ll just need to invoke it on the relevant files.

There’s one additional wrinkle: when I was testing this, I found that by the time compile-mode launched cat, the temporary quickfix file created by git jump had already been deleted. So instead of passing the quickfix file to Emacs directly, we’ll save the data into a separate file, and then pass that. For simplicity, I’m just going to store the quickfix data in ~/.quickfix; that means you can’t quickfix two separate git jump lists simultaneously, but that doesn’t seem like a problem in practice.

So, a very simple implementation of this script might look like this:

#! /bin/sh

if [ $1 != -q ]; then
    exec emacsclient "$@"
else
    shift
    qf=$HOME/.quickfix
    cat "$@" > "$qf"
    exec emacsclient -e "(quickfix-open '(\"$qf\"))"
fi

Save that as, for example, ~/bin/e, and change your shell configuration to set EDITOR=$HOME/bin/e, and now you should be able to use git jump at will. Share and enjoy!