;;; tools/magit/config.el -*- lexical-binding: t; -*- ;; ;;; Packages (use-package! magit :commands magit-file-delete :defer-incrementally (dash f s with-editor git-commit package eieio lv transient) :init (setq magit-auto-revert-mode nil) ; we do this ourselves ;; Must be set early to prevent ~/.emacs.d/transient from being created (setq transient-levels-file (concat doom-etc-dir "transient/levels") transient-values-file (concat doom-etc-dir "transient/values") transient-history-file (concat doom-etc-dir "transient/history")) :config (setq transient-default-level 5 magit-revision-show-gravatars '("^Author: " . "^Commit: ") magit-diff-refine-hunk t ; show granular diffs in selected hunk ;; Don't autosave repo buffers. This is too magical, and saving can ;; trigger a bunch of unwanted side-effects, like save hooks and ;; formatters. Trust us to know what we're doing. magit-save-repository-buffers nil) (defadvice! +magit-invalidate-projectile-cache-a (&rest _args) ;; We ignore the args to `magit-checkout'. :after '(magit-checkout magit-branch-and-checkout) (projectile-invalidate-cache nil)) ;; The default location for git-credential-cache is in ;; ~/.cache/git/credential. However, if ~/.git-credential-cache/ exists, then ;; it is used instead. Magit seems to be hardcoded to use the latter, so here ;; we override it to have more correct behavior. (unless (file-exists-p "~/.git-credential-cache/") (setq magit-credential-cache-daemon-socket (doom-glob (or (getenv "XDG_CACHE_HOME") "~/.cache/") "git/credential/socket"))) ;; Magit uses `magit-display-buffer-traditional' to display windows, by ;; default, which is a little primitive. `+magit-display-buffer' marries ;; `magit-display-buffer-fullcolumn-most-v1' with ;; `magit-display-buffer-same-window-except-diff-v1', except: ;; ;; 1. Magit sub-buffers (like `magit-log') that aren't spawned from a status ;; screen are opened as popups. ;; 2. The status screen isn't buried when viewing diffs or logs from the ;; status screen. (setq transient-display-buffer-action '(display-buffer-below-selected) magit-display-buffer-function #'+magit-display-buffer-fn) (set-popup-rule! "^\\(?:\\*magit\\|magit:\\| \\*transient\\*\\)" :ignore t) (add-hook 'magit-popup-mode-hook #'hide-mode-line-mode) ;; Add --tags switch (transient-append-suffix 'magit-fetch "-p" '("-t" "Fetch all tags" ("-t" "--tags"))) (transient-append-suffix 'magit-pull "-r" '("-a" "Autostash" "--autostash")) ;; so magit buffers can be switched to (except for process buffers) (add-hook! 'doom-real-buffer-functions (defun +magit-buffer-p (buf) (with-current-buffer buf (and (derived-mode-p 'magit-mode) (not (eq major-mode 'magit-process-mode)))))) ;; properly kill leftover magit buffers on quit (define-key magit-status-mode-map [remap magit-mode-bury-buffer] #'+magit/quit) ;; Close transient with ESC (define-key transient-map [escape] #'transient-quit-one)) (use-package! forge ;; We defer loading even further because forge's dependencies will try to ;; compile emacsql, which is a slow and blocking operation. :after-call magit-status :commands forge-create-pullreq forge-create-issue :init (setq forge-database-file (concat doom-etc-dir "forge/forge-database.sqlite")) :config ;; All forge list modes are derived from `forge-topic-list-mode' (map! :map forge-topic-list-mode-map :n "q" #'kill-current-buffer) (set-popup-rule! "^\\*?[0-9]+:\\(?:new-\\|[0-9]+$\\)" :size 0.45 :modeline t :ttl 0 :quit nil) (set-popup-rule! "^\\*\\(?:[^/]+/[^ ]+ #[0-9]+\\*$\\|Issues\\|Pull-Requests\\|forge\\)" :ignore t) (defadvice! +magit--forge-get-repository-lazily-a (&rest _) "Make `forge-get-repository' return nil if the binary isn't built yet. This prevents emacsql getting compiled, which appears to come out of the blue and blocks Emacs for a short while." :before-while #'forge-get-repository (file-executable-p emacsql-sqlite-executable)) (defadvice! +magit--forge-build-binary-lazily-a (&rest _) "Make `forge-dispatch' only build emacsql if necessary. Annoyingly, the binary gets built as soon as Forge is loaded. Since we've disabled that in `+magit--forge-get-repository-lazily-a', we must manually ensure it is built when we actually use Forge." :before #'forge-dispatch (unless (file-executable-p emacsql-sqlite-executable) (emacsql-sqlite-compile 2) (if (not (file-executable-p emacsql-sqlite-executable)) (message (concat "Failed to build emacsql; forge may not work correctly.\n" "See *Compile-Log* buffer for details")) ;; HACK Due to changes upstream, forge doesn't initialize completely if ;; it doesn't find `emacsql-sqlite-executable', so we have to do it ;; manually after installing it. (setq forge--sqlite-available-p t) (magit-add-section-hook 'magit-status-sections-hook 'forge-insert-pullreqs nil t) (magit-add-section-hook 'magit-status-sections-hook 'forge-insert-issues nil t) (after! forge-topic (dolist (hook forge-bug-reference-hooks) (add-hook hook #'forge-bug-reference-setup))))))) (use-package! github-review :after magit :config (transient-append-suffix 'magit-merge "i" '("y" "Review pull request" +magit/start-github-review)) (after! forge (transient-append-suffix 'forge-dispatch "c u" '("c r" "Review pull request" +magit/start-github-review)))) (use-package! magit-todos :after magit :config (setq magit-todos-keyword-suffix "\\(?:([^)]+)\\)?:?") ; make colon optional (define-key magit-todos-section-map "j" nil) ;; Warns that jT isn't bound. Well, yeah, you don't need to tell me, that was ;; on purpose ya goose. (advice-add #'magit-todos-mode :around #'doom-shut-up-a)) (use-package! magit-gitflow :hook (magit-mode . turn-on-magit-gitflow)) (use-package! evil-magit :when (featurep! :editor evil +everywhere) :after magit :init (setq evil-magit-state 'normal evil-magit-use-z-for-folds t) :config (unmap! magit-mode-map ;; Replaced by z1, z2, z3, etc "M-1" "M-2" "M-3" "M-4" "1" "2" "3" "4" "0") ; moved to g= (evil-define-key* 'normal magit-status-mode-map [escape] nil) ; q is enough (evil-define-key* '(normal visual) magit-mode-map "%" #'magit-gitflow-popup "zz" #'evil-scroll-line-to-center "g=" #'magit-diff-default-context) (define-key! 'normal (magit-status-mode-map magit-stash-mode-map magit-revision-mode-map magit-diff-mode-map) [tab] #'magit-section-toggle) (after! git-rebase (dolist (key '(("M-k" . "gk") ("M-j" . "gj"))) (when-let (desc (assoc (car key) evil-magit-rebase-commands-w-descriptions)) (setcar desc (cdr key)))) (evil-define-key* evil-magit-state git-rebase-mode-map "gj" #'git-rebase-move-line-down "gk" #'git-rebase-move-line-up)))