An interactive, iterative ‘git blame’ mode for Emacs

I’m an active user of Magit for all my Git needs within Emacs. One thing I’ve always found missing was nice support for ‘git blame’ — and by ‘nice’ I mean some more comfort than the simple display of its output.

When I’m hunting bugs one thing I often need is not only the information who modified a certain line but also who modified the line before that. Also what I’d like is having both the source code (including syntax highlighting, of course) and the blame information shown side-by-side with quick access to the log message for particular commits.

That’s what ‘mo-git-blame’ tries to solve. It is a standalone mode that can be used with any of the various Git version control modes. Here are a couple of its features:

  • Shows the output of ‘git blame’ and the file content side-by-side with syntax highlighting for the content
  • Show the log messages for the current revision or the revision in the current line
  • Show the diff (via ‘git show’) for the current revision or the revision in the current line
  • Show the file content for the revision in the current line
  • Call ‘git blame’ for the same file for the revision in the current line

Here’s what it looks like:

Screenshot of Emacs with mo-git-blame-mode

Screenshot of Emacs with mo-git-blame-mode

You can download the sources from my Git repository or directly with

git clone git://github.com/mbunkus/mo-git-blame.git

Installation and usage instructions are included both in the file itself and its README, but here’s the short version:

(add-to-list 'load-path "~/.emacs.d/mo-git-blame")
(autoload 'mo-git-blame-file "mo-git-blame" nil t)
(autoload 'mo-git-blame-current "mo-git-blame" nil t)

16 thoughts on “An interactive, iterative ‘git blame’ mode for Emacs

  1. shjk

    How does this differ from vc-annotate (which does support git in recent emacs versions)?

    1. mosu Post author

      As magit doesn’t have a full-blown file manager (meaning it only shows modified files) such an integration would be limited to the files it actually lists as modified. That isn’t hard to do; basically a function that checks whether or not point is at a file and use that file’s name for mo-git-blame. I’ll try to write that up next week.

      Or did you have something else in mind?

  2. phil

    To be honest, I doubt it. It doesn’t quite fit. I’d be happy to stick a recommendation for it in the manual though.

    1. mosu Post author

      That’s what I thought as well and why I didn’t try to integrate it before — even with such a simple method as I outlined in the comment above.

  3. nico

    This looks great.

    But is it possible that it does not cope with hard symlinks? Because of several reasons we have to link to our git repo this way (chroot). Then when I try mo-git-blame-current the following error is shown:

    fatal: Not a valid object name d7912b8:../../../../../home/nick/src/modules/Affiliate/Actions/CRUD.pm

    But Magit itself shows no problems.

  4. Dave Abrahams

    Not workin’ for me on MacOSX with Emacs 23.2; it hangs emacs waiting for the git blame command to finish, and if I kill the git command Emacs remains hung so I have to kill it too. Interestingly, the git command it hangs on works just fine from the command-line!

  5. DaveAbrahams

    The MacOSX problems are fixed, but frankly I’m a little perplexed as to how to use this. I ran it, and I wanted to find out who (and which commit) was responsible for a given line of source, but I didn’t see a way to do that. What am I missing?

    1. mosu Post author

      First, visit the file you want to blame. Then execute `mo-git-blame-current’. This works best if you have only one window shown.

      MGB will split that window into two: the left one will show the output of “git blame –interactive”, the right one will contain the file content at the revision you’re blaming for. Initially this is the same revision your HEAD is at, but you can re-blame for other revisions by pressing some of the keys listed in the help for `mo-git-blame-current’ (or the mode’s key map).

      Whenever you scroll either of the two windows the other will be scrolled by the same amount.

      Is this enough to get you started?

      1. DaveAbrahams

        Well, it’s still not working for me. The latest problem is, when I visit the file and invoke mo-git-blame-current, I get this:

        fatal: ‘../../../../../../../Users/dave/src/llvm/tools/clang/lib/Sema/SemaExpr.cpp’ is outside repository.

        Oh, weird. There’s a directory symlink in the path to the file **above the repository directory** and that confuses mo-git-blame. If I open it by its canonical name, it works. Maybe you could make mo-git-blame more resilient to this

  6. DaveAbrahams

    One further thing: I have diff-mode-read-only set, and once I hit `RET’ in the buffer with the revision numbers, I can’t do it again until I kill the diff buffer.

Comments are closed.