Better Magit Diffs
By default, Git diffs use the perfectly-serviceable, decades-old unified diff format. A drop-in replacement like delta offers various improvements: most relevantly to me, it adds syntax highlighting (not coming to Magit any time soon) and fine-grained differences (possible in Magit, but in a less useful form). When I tried magit-delta a year ago, I eventually had to disable it because of performance issues. Although those haven’t been resolved yet, I just took another stab at it, prompted by diff envy. I re-enabled magit-delta, then added a command to disable it when required:
Emacs Lisp(defun aankh/toggle-magit-delta ()
(interactive)
(magit-delta-mode
(if magit-delta-mode
-1
1))
(magit-refresh))
(transient-append-suffix 'magit-diff '(-1 -1 -1)
'("l" "Toggle magit-delta" aankh/toggle-magit-delta))
Now I could always revert—pardon the pun—to quick, regular diffs by pressing d l in a Magit buffer.
Having come this far, I thought, why not try something even more cutting-edge? difftastic is
a structural diff that understands syntax
, which
serves as a complement to ordinary diffs. I followed Tassilo Horn’s guide to using it with
Magit, allowing # d in a
Magit buffer to pop up an automatic difftastic diff, or # s to do the same for a specific range.
Unfortunately, the builtin colours were hard to read:
The only control offered by the program is --light and --dark, which select dimmer and brighter shades, respectively. I tried giving just the difftastic buffer a dark background, which necessitated creating a new frame entirely:
It was awkward and brittle. b-fuze suggested rewriting the colours in the
output, even simply via a tool like sed. Fortunately, I ultimately didn’t need to resort to
modifying the ANSI colour codes. Instead, I examined the results of the
ansi-color-apply-on-region
function, which creates overlays with anonymous faces, and wrote some
inelegant Emacs Lisp to alter just those colours (which I’ll need to update if difftastic starts
using backgrounds):
Emacs Lisp(defun aankh/recolor-difftastic ()
(let ((ovs (overlays-in (point-min) (point-max))))
(dolist (ov ovs)
(let ((face (overlay-get ov 'face)))
(when (and (not (null face)) (listp face))
(when (plist-get face :foreground)
(plist-put face :foreground (aankh/get-remapped-difftastic-colour (plist-get face :foreground))))
(when-let ((existing (cl-find :foreground face :key (lambda (x) (if (consp x) (car x) nil)))))
(setf face
(cl-subst `(:foreground ,(aankh/get-remapped-difftastic-colour (plist-get existing :foreground)))
:foreground
face
:key (lambda (x) (if (consp x) (car x) nil)))))
(overlay-put ov 'face face))))))
(defun aankh/get-remapped-difftastic-colour (original)
(alist-get original +aankh/difftastic-colour-remapping+ nil nil 'string=))
(defconst +aankh/difftastic-colour-remapping+
`(("red2" . "#a8353e") ;; https://oklch.com/#50,0.15,20,100
("green2" . "#107823")
("yellow2" . "#2f3b97")))
I picked my colours using the OKLCH colour picker & converter
to try to keep them in harmony with each other. When called at the end of
th/magit--with-difftastic
’s sentinel lambda, this function produces a more legible structural
diff:
While I’d like to incorporate these into the regular Magit status view—probably requiring either
unified diff–style output or JSON
output—I’m satisfied for now. As a final step, I
moved the three commands to a new subsection of the magit-diff
transient:
Emacs Lisp;; For some reason, this was being called twice without the guard.
(unless (boundp 'aankh/added-magit-diff-suffixes)
(transient-append-suffix 'magit-diff '(-1 -1)
[("l" "Toggle magit-delta" aankh/toggle-magit-delta)
("D" "Difftastic Diff (dwim)" th/magit-diff-with-difftastic)
("S" "Difftastic Show" th/magit-show-with-difftastic)]))
(setf aankh/added-magit-diff-suffixes t)
For reference, here’s a visual comparison between regular Magit diffs, Magit diffs with hunk refinement, and my new options:
Plus, how difftastic… compares to delta: