EOS: Git Module
Table of Contents
(provide 'eos-git)
Git magic with Magit
I use C-x g
everywhere to go directly to Magit.
(use-package magit :ensure t :bind (("C-x g" . magit-status)) :init (add-hook 'magit-mode-hook 'eos/turn-on-hl-line) :config (setq git-commit-summary-max-length 70) (setenv "GIT_PAGER" "") (if (file-exists-p "/usr/local/bin/emacsclient") (setq magit-emacsclient-executable "/usr/local/bin/emacsclient") (setq magit-emacsclient-executable (executable-find "emacsclient"))) (defun eos/magit-browse () "Browse to the project's github URL, if available" (interactive) (let ((url (with-temp-buffer (unless (zerop (call-process-shell-command "git remote -v" nil t)) (error "Failed: 'git remote -v'")) (goto-char (point-min)) (when (re-search-forward "github\\.com[:/]\\(.+?\\)\\.git" nil t) (format "https://github.com/%s" (match-string 1)))))) (unless url (error "Can't find repository URL")) (browse-url url))) (define-key magit-mode-map (kbd "C-c C-b") #'eos/magit-browse) ;; Magit has its own binding, so re-bind them (bind-key "M-1" #'eos/create-or-switch-to-eshell-1 magit-mode-map) (bind-key "M-2" #'eos/create-or-switch-to-eshell-2 magit-mode-map) (bind-key "M-3" #'eos/create-or-switch-to-eshell-3 magit-mode-map) (bind-key "M-4" #'eos/create-or-switch-to-eshell-4 magit-mode-map) ;; Allow gpg signing merge commits (magit-define-popup-option 'magit-merge-popup ?S "Sign using gpg" "--gpg-sign=" #'magit-read-gpg-secret-key))
Viewing Github PRs and Issues
There's also a package called magit-gh-pulls that shows the pull requests when viewing the magit buffer
(use-package magit-gh-pulls :ensure t :init (add-hook 'magit-mode-hook #'turn-on-magit-gh-pulls) :config ;; work around https://github.com/sigma/magit-gh-pulls/issues/83 (setq magit-gh-pulls-pull-detail-limit 50))
As an alternative to magit-gh-pulls, there is magithub
(use-package magithub :ensure t :disabled t :after magit :config (magithub-feature-autoinject t) ;; Workaround for https://github.com/vermiculus/magithub/issues/71 (defun magithub-issue--sort (issues) "Sort ISSUES by issue number." (sort issues (lambda (a b) (> (plist-get a :number) (plist-get b :number))))))
Visualizing git changes in a buffer
Quite useful, as well as the C-x n
and C-x p
bindings.
(use-package git-gutter :ensure t :when window-system :defer t :bind (("C-x P" . git-gutter:popup-hunk) ("C-x p" . git-gutter:previous-hunk) ("C-x n" . git-gutter:next-hunk) ("C-c G" . git-gutter:popup-hunk)) :diminish "" :init (add-hook 'prog-mode-hook #'git-gutter-mode) (add-hook 'text-mode-hook #'git-gutter-mode) :config (use-package git-gutter-fringe :ensure t :init (require 'git-gutter-fringe) (when (fboundp 'define-fringe-bitmap) (define-fringe-bitmap 'git-gutter-fr:added [224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224] nil nil 'center) (define-fringe-bitmap 'git-gutter-fr:modified [224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224 224] nil nil 'center) (define-fringe-bitmap 'git-gutter-fr:deleted [0 0 0 0 0 0 0 0 0 0 0 0 0 128 192 224 240 248] nil nil 'center))))
Git-messenger allows you to look up the commit message for the last commit that touched the line wherever your current cursor is.
(use-package git-messenger :ensure t :commands git-messenger:popup-message :bind (("C-c M" . git-messenger:popup-message)) :config (setq git-messenger:show-detail t))
A nice helper to browse code whenever it remotely may be,
browse-at-remote
(use-package browse-at-remote :ensure t :commands browse-at-remote :bind ("C-c g g" . browse-at-remote))
Another nice thing is looking at the file through different revisions by checking out the same thing, but with git-timemachine.
(use-package git-timemachine :ensure t)
Git-annex integration
I've been back and forth on git-annex, but when I want to use it, I want to have it integrated into Emacs/magit when possible.
(use-package git-annex :ensure t) (use-package magit-annex :ensure t)
Ediff
Ediff is fantastic for looking through diffs, and it's what magit can invoke on merge conflicts.
(use-package ediff :init (setq ;; Always split nicely for wide screens ediff-split-window-function 'split-window-horizontally) (defun ediff-copy-both-to-C () (interactive) (ediff-copy-diff ediff-current-difference nil 'C nil (concat (ediff-get-region-contents ediff-current-difference 'A ediff-control-buffer) (ediff-get-region-contents ediff-current-difference 'B ediff-control-buffer)))) (defun add-d-to-ediff-mode-map () (define-key ediff-mode-map "d" 'ediff-copy-both-to-C)) (add-hook 'ediff-keymap-setup-hook 'add-d-to-ediff-mode-map))
Smerge
Emacs has a nice built-in way of handling git conflicts, smerge-mode
. There's a nice hydra I
borrowed that allows you to run through conflicts pretty easily
(defhydra eos/hydra-smerge (:color red :hint nil :pre (smerge-mode 1)) " ^Move^ ^Keep^ ^Diff^ ^Pair^ ------------------------------------------------------ _n_ext _b_ase _R_efine _<_: base-mine _p_rev _m_ine _E_diff _=_: mine-other ^ ^ _o_ther _C_ombine _>_: base-other ^ ^ _a_ll _r_esolve _q_uit _RET_: current " ("RET" smerge-keep-current) ("C" smerge-combine-with-next) ("E" smerge-ediff) ("R" smerge-refine) ("a" smerge-keep-all) ("b" smerge-keep-base) ("m" smerge-keep-mine) ("n" smerge-next) ("o" smerge-keep-other) ("p" smerge-prev) ("r" smerge-resolve) ("<" smerge-diff-base-mine) ("=" smerge-diff-mine-other) (">" smerge-diff-base-other) ("q" nil :color blue))