2021-08-17 11:49:19 +00:00
|
|
|
;;; init-git.el --- VCS/Git Configuration File -*- lexical-binding: t -*-
|
|
|
|
;;; Commentary:
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
(use-package ediff
|
|
|
|
:custom
|
|
|
|
(ediff-setup-windows-plain 'ediff-setup-windows-plain))
|
|
|
|
|
|
|
|
(use-package diff-hl
|
|
|
|
:hook
|
2023-01-17 13:22:42 +00:00
|
|
|
(emacs-startup . global-diff-hl-mode)
|
2021-08-17 11:49:19 +00:00
|
|
|
(dired-mode . diff-hl-dired-mode)
|
|
|
|
(magit-post-refresh . diff-hl-magit-post-refresh))
|
|
|
|
|
2022-08-30 15:13:33 +00:00
|
|
|
(use-package gitconfig)
|
|
|
|
(use-package git-modes)
|
2022-08-30 11:53:46 +00:00
|
|
|
|
2022-08-13 10:36:35 +00:00
|
|
|
(use-package git-timemachine
|
|
|
|
:bind
|
|
|
|
("C-x v t" . git-timemachine-toggle))
|
2021-08-17 11:49:19 +00:00
|
|
|
|
2022-10-12 12:29:53 +00:00
|
|
|
(use-package vc
|
|
|
|
:bind
|
|
|
|
(("C-x v C-r" . my/vc-refresh-state)
|
|
|
|
("C-x v C-m" . my/update-git-master))
|
|
|
|
:custom (vc-follow-symlinks nil)
|
|
|
|
:config
|
|
|
|
(defun my/vc-refresh-state ()
|
|
|
|
(interactive)
|
|
|
|
(when-let ((root-dir (vc-root-dir)))
|
|
|
|
(dolist (buf (buffer-list))
|
|
|
|
(when (and (not (buffer-modified-p buf))
|
|
|
|
(buffer-file-name buf)
|
|
|
|
(file-exists-p (buffer-file-name buf))
|
|
|
|
(file-in-directory-p (buffer-file-name buf) root-dir))
|
|
|
|
(with-current-buffer buf
|
|
|
|
(vc-refresh-state))))))
|
|
|
|
|
|
|
|
;; [alias]
|
|
|
|
;; update-master = !git fetch origin master:master
|
|
|
|
;; update-main = !git fetch origin main:main
|
|
|
|
(defun my/update-git-master ()
|
|
|
|
"Update git master or main branch."
|
|
|
|
(interactive)
|
|
|
|
(if-let ((root (vc-root-dir)))
|
|
|
|
(let* ((branches (vc-git-branches))
|
|
|
|
(main-p (member "main" branches))
|
|
|
|
(master-p (member "master" branches))
|
|
|
|
(current-branch (car branches))
|
|
|
|
(on-master-p (member current-branch '("master" "main")))
|
|
|
|
(command (if main-p "update-main" "update-master"))
|
|
|
|
(buffer "*vc-update-master*"))
|
|
|
|
(if on-master-p
|
|
|
|
(vc-pull)
|
|
|
|
;; based on vc-git--pushpull
|
|
|
|
(require 'vc-dispatcher)
|
|
|
|
(apply #'vc-do-async-command buffer root vc-git-program command nil)
|
|
|
|
(with-current-buffer buffer
|
|
|
|
(vc-run-delayed
|
|
|
|
(vc-compilation-mode 'git)
|
|
|
|
(setq-local compile-command
|
|
|
|
(concat vc-git-program " " command))
|
|
|
|
(setq-local compilation-directory root)
|
|
|
|
(setq-local compilation-arguments
|
|
|
|
(list compile-command nil
|
|
|
|
(lambda (_name-of-mode) buffer)
|
|
|
|
nil))))
|
|
|
|
(vc-set-async-update buffer)))
|
|
|
|
(message "not a git repository"))))
|
|
|
|
|
2021-08-17 11:49:19 +00:00
|
|
|
(use-package magit
|
|
|
|
:bind
|
2022-05-19 06:45:07 +00:00
|
|
|
("C-c g g" . magit-dispatch) ;; magit-file-dispatch is C-c M-g
|
|
|
|
("C-c g u" . my/magit-set-upstream)
|
|
|
|
("C-c g r" . my/magit-refresh-state)
|
2023-03-11 13:24:14 +00:00
|
|
|
("C-c g m" . my/magit-update-master)
|
2021-08-17 11:49:19 +00:00
|
|
|
:config
|
2021-12-22 12:57:21 +00:00
|
|
|
;; Requires the following gitconfig:
|
|
|
|
;; [alias]
|
|
|
|
;; upstream = !git push -u origin HEAD
|
|
|
|
;; TODO - this is useful after setting push remote, but is there a better way?
|
2021-08-17 11:49:19 +00:00
|
|
|
(defun my/magit-set-upstream ()
|
|
|
|
(interactive)
|
|
|
|
(magit-shell-command-topdir "git upstream"))
|
2022-10-12 12:29:53 +00:00
|
|
|
|
2021-08-17 11:49:19 +00:00
|
|
|
;; update stale git info on the modeline (based on code removed from doom modeline)
|
|
|
|
(defun my/magit-refresh-state ()
|
2022-10-12 12:29:53 +00:00
|
|
|
"Update modeline git branch information."
|
2021-08-17 11:49:19 +00:00
|
|
|
(interactive)
|
|
|
|
(dolist (buf (buffer-list))
|
|
|
|
(when (and (not (buffer-modified-p buf))
|
|
|
|
(buffer-file-name buf)
|
|
|
|
(file-exists-p (buffer-file-name buf))
|
|
|
|
(file-in-directory-p (buffer-file-name buf) (magit-toplevel)))
|
|
|
|
(with-current-buffer buf
|
|
|
|
(vc-refresh-state)))))
|
2022-10-12 12:29:53 +00:00
|
|
|
|
|
|
|
;; [alias]
|
|
|
|
;; update-master = !git fetch origin master:master
|
|
|
|
;; update-main = !git fetch origin main:main
|
|
|
|
(defun my/magit-update-master ()
|
|
|
|
"Update git master or main branch."
|
|
|
|
(interactive)
|
|
|
|
(if (magit-toplevel)
|
|
|
|
(let* ((branches (vc-git-branches))
|
|
|
|
(main-p (member "main" branches))
|
|
|
|
(current-branch (car branches))
|
|
|
|
(on-master-p (member current-branch '("master" "main")))
|
|
|
|
(command (concat "git " (if main-p "update-main" "update-master"))))
|
|
|
|
(if on-master-p
|
|
|
|
(vc-pull)
|
|
|
|
(magit-shell-command-topdir command)))
|
|
|
|
(message "Not a git repository")))
|
|
|
|
|
2022-10-12 15:26:42 +00:00
|
|
|
;; difftastic code copied from https://tsdh.org/posts/2022-08-01-difftastic-diffing-with-magit.html
|
|
|
|
(defun my/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))))))))))))
|
|
|
|
|
|
|
|
(defun my/magit-show-with-difftastic (rev)
|
|
|
|
"Show the result of \"git show REV\" with GIT_EXTERNAL_DIFF=difft."
|
|
|
|
(interactive
|
|
|
|
(list (or
|
|
|
|
;; If REV is given, just use it.
|
|
|
|
(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")
|
|
|
|
(my/magit--with-difftastic
|
|
|
|
(get-buffer-create (concat "*git show difftastic " rev "*"))
|
|
|
|
(list "git" "--no-pager" "show" "--ext-diff" rev))))
|
|
|
|
|
|
|
|
(defun my/magit-diff-with-difftastic (arg)
|
|
|
|
"Show the result of \"git diff ARG\" with GIT_EXTERNAL_DIFF=difft."
|
|
|
|
(interactive
|
|
|
|
(list (or
|
|
|
|
;; 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) "")
|
|
|
|
"*")))
|
|
|
|
(my/magit--with-difftastic
|
|
|
|
(get-buffer-create name)
|
|
|
|
`("git" "--no-pager" "diff" "--ext-diff" ,@(when arg (list arg))))))
|
|
|
|
|
2022-11-01 16:39:50 +00:00
|
|
|
(require 'ansi-color)
|
|
|
|
;; https://tsdh.org/posts/2022-07-20-using-eldoc-with-magit-async.html
|
|
|
|
;; https://tsdh.org/posts/2021-06-21-using-eldoc-with-magit.html
|
|
|
|
(defvar my/eldoc-git-show-stat--process nil)
|
|
|
|
(defun my/eldoc-git-show-stat (callback commit)
|
|
|
|
"Compute diffstat for COMMIT asynchronously, then call CALLBACK with it."
|
|
|
|
;; Kill the possibly still running old process and its buffer.
|
|
|
|
(when (processp my/eldoc-git-show-stat--process)
|
|
|
|
(let ((buf (process-buffer my/eldoc-git-show-stat--process)))
|
|
|
|
(when (process-live-p my/eldoc-git-show-stat--process)
|
|
|
|
(let (confirm-kill-processes)
|
|
|
|
(kill-process my/eldoc-git-show-stat--process)))
|
|
|
|
(when (buffer-live-p buf)
|
|
|
|
(kill-buffer buf))))
|
|
|
|
|
|
|
|
;; Spawn a new "git show" process.
|
|
|
|
(let* ((cmd (list "git" "--no-pager" "show"
|
|
|
|
"--no-color"
|
|
|
|
;; Author Name <author@email.com>, <date-and-time>
|
|
|
|
"--format=format:%an <%ae>, %aD"
|
|
|
|
"--stat=80"
|
|
|
|
commit)))
|
|
|
|
;; An async eldoc-documentation-function must also return a non-nil,
|
|
|
|
;; non-string result if it's applicable for computing a documentation
|
|
|
|
;; string, so we set and return the new process here.
|
|
|
|
(setq my/eldoc-git-show-stat--process
|
|
|
|
(make-process
|
|
|
|
:name "eldoc-git-show"
|
|
|
|
:buffer (generate-new-buffer " *git-show-stat*")
|
|
|
|
:noquery t
|
|
|
|
:command cmd
|
|
|
|
:sentinel (lambda (proc event)
|
|
|
|
(when (eq (process-status proc) 'exit)
|
|
|
|
(with-current-buffer (process-buffer proc)
|
|
|
|
(goto-char (point-min))
|
|
|
|
(put-text-property (point-min)
|
|
|
|
(line-end-position)
|
|
|
|
'face 'bold)
|
|
|
|
(funcall callback (buffer-string)))))))))
|
|
|
|
|
|
|
|
(defvar my/magit-eldoc-last-commit nil)
|
|
|
|
(defun my/magit-eldoc-for-commit (callback)
|
|
|
|
(let ((commit (magit-commit-at-point)))
|
|
|
|
(when (and commit
|
|
|
|
(not (equal commit my/magit-eldoc-last-commit)))
|
|
|
|
(setq my/magit-eldoc-last-commit commit)
|
|
|
|
(my/eldoc-git-show-stat callback commit))))
|
|
|
|
|
|
|
|
(defun my/magit-eldoc-setup ()
|
|
|
|
(add-hook 'eldoc-documentation-functions
|
|
|
|
#'my/magit-eldoc-for-commit nil t))
|
|
|
|
|
|
|
|
(add-hook 'magit-status-mode-hook #'my/magit-eldoc-setup)
|
|
|
|
(add-hook 'magit-log-mode-hook #'my/magit-eldoc-setup)
|
|
|
|
|
|
|
|
(eldoc-add-command 'magit-next-line)
|
|
|
|
(eldoc-add-command 'magit-previous-line)
|
|
|
|
|
2022-10-12 12:29:53 +00:00
|
|
|
;; Based on https://tsdh.org/posts/2022-08-01-difftastic-diffing-with-magit.html
|
|
|
|
(transient-define-prefix my/magit-extra-commands ()
|
|
|
|
"Extra magit commands."
|
|
|
|
["Extra commands"
|
|
|
|
("u" "Set upstream" my/magit-set-upstream)
|
|
|
|
("r" "Refresh state (update modeline)" my/magit-refresh-state)
|
2022-10-12 15:26:42 +00:00
|
|
|
("m" "Update master/main" my/magit-update-master)
|
|
|
|
("d" "Difftastic Diff (dwim)" my/magit-diff-with-difftastic)
|
2022-11-18 12:11:28 +00:00
|
|
|
("s" "Difftastic Show" my/magit-show-with-difftastic)
|
|
|
|
("D" "Toggle magit-delta-mode" my/toggle-delta-mode)])
|
2022-10-12 12:29:53 +00:00
|
|
|
(transient-append-suffix 'magit-dispatch "!"
|
|
|
|
'("#" "Extra Magit Cmds" my/magit-extra-commands))
|
|
|
|
(define-key magit-status-mode-map (kbd "#") #'my/magit-extra-commands)
|
|
|
|
|
2021-08-17 11:49:19 +00:00
|
|
|
:custom
|
|
|
|
(magit-diff-refine-hunk 'all)
|
2021-08-18 14:00:22 +00:00
|
|
|
(magit-diff-paint-whitespace-lines 'all)
|
2021-08-19 13:45:17 +00:00
|
|
|
(magit-diff-refine-ignore-whitespace nil)
|
2021-08-18 14:00:22 +00:00
|
|
|
(magit-diff-highlight-trailing t))
|
2021-08-17 11:49:19 +00:00
|
|
|
|
2022-11-18 12:11:28 +00:00
|
|
|
(use-package magit-delta
|
2022-11-21 14:10:41 +00:00
|
|
|
:after magit
|
|
|
|
:demand t
|
2022-11-18 12:11:28 +00:00
|
|
|
:config
|
|
|
|
(defun my/toggle-delta-mode ()
|
|
|
|
(interactive)
|
|
|
|
(call-interactively #'magit-delta-mode)
|
|
|
|
(magit-refresh)))
|
|
|
|
|
2021-08-17 11:49:19 +00:00
|
|
|
(use-package forge
|
|
|
|
:after magit)
|
|
|
|
|
2023-03-11 13:24:29 +00:00
|
|
|
(use-package git-link
|
|
|
|
:config
|
|
|
|
(defun git-link-on-branch ()
|
|
|
|
(interactive)
|
|
|
|
(let ((git-link-use-commit nil))
|
|
|
|
(call-interactively 'git-link)))
|
|
|
|
:custom (git-link-use-commit t)
|
|
|
|
:bind
|
|
|
|
("C-c g s" . git-link)
|
|
|
|
("C-c g S" . git-link-on-branch)
|
|
|
|
("C-c g c" . git-link-commit))
|
|
|
|
|
2021-08-17 11:49:19 +00:00
|
|
|
(provide 'init-git)
|
|
|
|
;;; init-git.el ends here
|