Emacs: Run git-blame on the current line

Published on 2014-05-10
Tagged: emacs

View All Posts

Here's a handy function I wrote that you can add to your .emacs file. This function runs git blame on the line the cursor is on. It prints the short commit id, author, and commit date into the minibuffer. It also copies the short commit id to the kill ring so you can paste it somewhere else if you need to.

Emacs's various git integration modes probably provide something more elaborate, but this works very well for me.

(defun git-blame-line ()
  "Runs `git blame` on the current line and
   adds the commit id to the kill ring"
  (interactive)
  (let* ((line-number (save-excursion
                        (goto-char (point-at-bol))
                        (+ 1 (count-lines 1 (point)))))
         (line-arg (format "%d,%d" line-number line-number))
         (commit-buf (generate-new-buffer "*git-blame-line-commit*")))
    (call-process "git" nil commit-buf nil 
                  "blame" (buffer-file-name) "-L" line-arg)
    (let* ((commit-id (with-current-buffer commit-buf
                        (buffer-substring 1 9)))
           (log-buf (generate-new-buffer "*git-blame-line-log*")))
      (kill-new commit-id)
      (call-process "git" nil log-buf nil 
                    "log" "-1" "--pretty=%h   %an   %s" commit-id)
      (with-current-buffer log-buf
        (message "Line %d: %s" line-number (buffer-string)))
      (kill-buffer log-buf))
    (kill-buffer commit-buf)))

In case you don't read elisp, here's a translation of what's happening.

let line-number be the current line
let commit-buf be a new buffer
call `git blame file-name -L line-number,line-number` 
    and write the result into commit-buf
let commit-id be the short commit id parsed from commit-buf
let log-buf be a new buffer
add commit-id to the kill ring
call `git log -1 commit-id --pretty='%h   %an   %s'`
    and write the result into log-buf
write the line number and the contents of log-buf to the minibuffer
kill log-buf and commit-buf