Bind SPC g d to difftastic
As I don't see a new binding in the Magit arena that makes sense.
This commit is contained in:
parent
89446f001b
commit
3b8cfa1f73
1 changed files with 128 additions and 0 deletions
128
ha-config.org
128
ha-config.org
|
@ -1464,6 +1464,134 @@ I also need to append the following to my [[file:~/.gitconfig][~/.gitconfig]] fi
|
||||||
plus-emph-style = syntax "#009000"
|
plus-emph-style = syntax "#009000"
|
||||||
plus-empty-line-marker-style = normal "#006800"
|
plus-empty-line-marker-style = normal "#006800"
|
||||||
#+end_src
|
#+end_src
|
||||||
|
*** Git with Difftastic
|
||||||
|
I’m stealing the code for this section from [[https://tsdh.org/posts/2022-08-01-difftastic-diffing-with-magit.html][this essay]] by Tassilo Horn, and in fact, I’m going to lift a lot of his explanation too, as I may need to remind myself how this works. The idea is based on using Wilfred’s excellent [[https://github.com/Wilfred/difftastic][difftastic]] tool to do a structural/syntax comparison of code changes in git. To begin, install the binary:
|
||||||
|
#+begin_src sh
|
||||||
|
brew install difftastic # and the equivalent on Linux
|
||||||
|
#+end_src
|
||||||
|
Next, we can do this, to use this as a diff tool for everything.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(setenv "GIT_EXTERNAL_DIFF" "difft")
|
||||||
|
#+end_src
|
||||||
|
But perhaps integrating it into Magit and selectively calling it (as it is slow). Tassilo suggests making the call to =difft= optional by first creating a helper function to set the =GIT_EXTERNAL_DIFF= to =difft=:
|
||||||
|
#+begin_src emacs-lisp :tangle no
|
||||||
|
(defun th/magit--with-difftastic (buffer command)
|
||||||
|
"Run COMMAND with GIT_EXTERNAL_DIFF=difft then show result in BUFFER."
|
||||||
|
(let ((process-environment
|
||||||
|
(cons (concat "GIT_EXTERNAL_DIFF=difft --width="
|
||||||
|
(number-to-string (frame-width)))
|
||||||
|
process-environment)))
|
||||||
|
;; Clear the result buffer (we might regenerate a diff, e.g., for
|
||||||
|
;; the current changes in our working directory).
|
||||||
|
(with-current-buffer buffer
|
||||||
|
(setq buffer-read-only nil)
|
||||||
|
(erase-buffer))
|
||||||
|
;; Now spawn a process calling the git COMMAND.
|
||||||
|
(make-process
|
||||||
|
:name (buffer-name buffer)
|
||||||
|
:buffer buffer
|
||||||
|
:command command
|
||||||
|
;; Don't query for running processes when emacs is quit.
|
||||||
|
:noquery t
|
||||||
|
;; Show the result buffer once the process has finished.
|
||||||
|
:sentinel (lambda (proc event)
|
||||||
|
(when (eq (process-status proc) 'exit)
|
||||||
|
(with-current-buffer (process-buffer proc)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(ansi-color-apply-on-region (point-min) (point-max))
|
||||||
|
(setq buffer-read-only t)
|
||||||
|
(view-mode)
|
||||||
|
(end-of-line)
|
||||||
|
;; difftastic diffs are usually 2-column side-by-side,
|
||||||
|
;; so ensure our window is wide enough.
|
||||||
|
(let ((width (current-column)))
|
||||||
|
(while (zerop (forward-line 1))
|
||||||
|
(end-of-line)
|
||||||
|
(setq width (max (current-column) width)))
|
||||||
|
;; Add column size of fringes
|
||||||
|
(setq width (+ width
|
||||||
|
(fringe-columns 'left)
|
||||||
|
(fringe-columns 'right)))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(pop-to-buffer
|
||||||
|
(current-buffer)
|
||||||
|
`(;; If the buffer is that wide that splitting the frame in
|
||||||
|
;; two side-by-side windows would result in less than
|
||||||
|
;; 80 columns left, ensure it's shown at the bottom.
|
||||||
|
,(when (> 80 (- (frame-width) width))
|
||||||
|
#'display-buffer-at-bottom)
|
||||||
|
(window-width . ,(min width (frame-width))))))))))))
|
||||||
|
#+end_src
|
||||||
|
The crucial parts of this helper function are that we "wash" the result using =ansi-color-apply-on-region= so that the function can transform the difftastic highlighting using shell escape codes to Emacs faces. Also, note the need to possibly change the width, as difftastic makes a side-by-side comparison.
|
||||||
|
|
||||||
|
The functions below depend on [[help:magit-thing-at-point][magit-thing-at-point]], and this depends on the [[https://sr.ht/~pkal/compat/][compat]] library, so let’s grab that stuff:
|
||||||
|
#+begin_src emacs-lisp :tangle no
|
||||||
|
(use-package compat
|
||||||
|
:straight (:host github :repo "emacs-straight/compat"))
|
||||||
|
|
||||||
|
(use-package magit-section
|
||||||
|
:commands magit-thing-at-point)
|
||||||
|
#+end_src
|
||||||
|
Next, let's define our first command basically doing a =git show= for some revision which defaults to the commit or branch at point or queries the user if there's none.
|
||||||
|
#+begin_src emacs-lisp :tangle no
|
||||||
|
(defun th/magit-show-with-difftastic (rev)
|
||||||
|
"Show the result of \"git show REV\" with GIT_EXTERNAL_DIFF=difft."
|
||||||
|
(interactive
|
||||||
|
(list (or
|
||||||
|
;; Use if given the REV variable:
|
||||||
|
(when (boundp 'rev) rev)
|
||||||
|
;; If not invoked with prefix arg, try to guess the REV from
|
||||||
|
;; point's position.
|
||||||
|
(and (not current-prefix-arg)
|
||||||
|
(or (magit-thing-at-point 'git-revision t)
|
||||||
|
(magit-branch-or-commit-at-point)))
|
||||||
|
;; Otherwise, query the user.
|
||||||
|
(magit-read-branch-or-commit "Revision"))))
|
||||||
|
(if (not rev)
|
||||||
|
(error "No revision specified")
|
||||||
|
(th/magit--with-difftastic
|
||||||
|
(get-buffer-create (concat "*git show difftastic " rev "*"))
|
||||||
|
(list "git" "--no-pager" "show" "--ext-diff" rev))))
|
||||||
|
#+end_src
|
||||||
|
And here the second command which basically does a =git diff=. It tries to guess what one wants to diff, e.g., when point is on the Staged changes section in a magit buffer, it will run =git diff --cached= to show a diff of all staged changes. If it can not guess the context, it'll query the user for a range or commit for diffing.
|
||||||
|
#+begin_src emacs-lisp :tangle no
|
||||||
|
(defun th/magit-diff-with-difftastic (arg)
|
||||||
|
"Show the result of \"git diff ARG\" with GIT_EXTERNAL_DIFF=difft."
|
||||||
|
(interactive
|
||||||
|
(list (or
|
||||||
|
;; Use If RANGE is given, just use it.
|
||||||
|
(when (boundp 'range) range)
|
||||||
|
;; If prefix arg is given, query the user.
|
||||||
|
(and current-prefix-arg
|
||||||
|
(magit-diff-read-range-or-commit "Range"))
|
||||||
|
;; Otherwise, auto-guess based on position of point, e.g., based on
|
||||||
|
;; if we are in the Staged or Unstaged section.
|
||||||
|
(pcase (magit-diff--dwim)
|
||||||
|
('unmerged (error "unmerged is not yet implemented"))
|
||||||
|
('unstaged nil)
|
||||||
|
('staged "--cached")
|
||||||
|
(`(stash . ,value) (error "stash is not yet implemented"))
|
||||||
|
(`(commit . ,value) (format "%s^..%s" value value))
|
||||||
|
((and range (pred stringp)) range)
|
||||||
|
(_ (magit-diff-read-range-or-commit "Range/Commit"))))))
|
||||||
|
(let ((name (concat "*git diff difftastic"
|
||||||
|
(if arg (concat " " arg) "")
|
||||||
|
"*")))
|
||||||
|
(th/magit--with-difftastic
|
||||||
|
(get-buffer-create name)
|
||||||
|
`("git" "--no-pager" "diff" "--ext-diff" ,@(when arg (list arg))))))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
What's left is integrating the new show and diff commands in Magit. For that purpose, Tasillo created a new transient prefix for all personal commands. Intriguing, but I have a hack that I can use on a leader:
|
||||||
|
#+begin_src emacs-lisp :tangle no
|
||||||
|
(defun ha-difftastic-here ()
|
||||||
|
(interactive)
|
||||||
|
(call-interactively
|
||||||
|
(if (eq major-mode 'magit-log-mode)
|
||||||
|
'th/magit-show-with-difftastic
|
||||||
|
'th/magit-diff-with-difftastic)))
|
||||||
|
|
||||||
|
(ha-leader "g d" '("difftastic" . ha-difftastic-here))
|
||||||
#+end_src
|
#+end_src
|
||||||
*** Time Machine
|
*** Time Machine
|
||||||
The [[https://github.com/emacsmirror/git-timemachine][git-timemachine]] project visually shows how a code file changes with each iteration:
|
The [[https://github.com/emacsmirror/git-timemachine][git-timemachine]] project visually shows how a code file changes with each iteration:
|
||||||
|
|
Loading…
Reference in a new issue