Make Emacs faster than Vim in "git mergetool"

  |   Source

My article Emacs is the best merge tool for Git explains how to combine git mergetool with ediff-mode in Emacs.

Harrison McCullough suggested the work flow can be faster if emacs is replaced with emacsclient.

I did some research and found a perfect solution. It's even faster than Vim.

Initial solution

Please note emacsclient is only use for resolving conflicts.

Step 1, start emacs server by running emacs -Q --daemon --eval "(setq startup-now t)" -l "/home/my-username/.emacs.d/init.el" --eval "(progn (require 'server) (server-start))" in shell.

Step 2, insert below code into ~/.emacs.d/init.el (see the comment why this advice is required):

(defadvice server-save-buffers-kill-terminal (after server-save-buffers-kill-terminal-after-hack activate)
  ;; kill all buffers, so new ediff panel is re-created and `ediff-startup-hook-setup' is called again
  ;; besides, remove the buffers whose binding files are alredy merged in `buffer-list'
  (mapc 'kill-buffer (buffer-list)))

Step 3, insert below code into ~/.gitconfig:

[mergetool.ediff]
cmd = emacsclient -nw --eval \"(progn (setq ediff-quit-hook 'kill-emacs) (if (file-readable-p \\\"$BASE\\\") (ediff-merge-files-with-ancestor \\\"$LOCAL\\\" \\\"$REMOTE\\\" \\\"$BASE\\\" nil \\\"$MERGED\\\") (ediff-merge-files \\\"$LOCAL\\\" \\\"$REMOTE\\\" nil \\\"$MERGED\\\")))\"

My real world solution

It's similar to initial solution. But some scripts are created for automation.

Step 1, read Using Emacs as a Server in the manual and create ~/.config/systemd/user/emacs.service for Systemd:

[Unit]
Description=Emacs text editor
Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/

[Service]
Type=forking
ExecStart=emacs -Q --daemon --eval "(setq startup-now t)" -l "/home/my-username/.emacs.d/init.el" --eval "(progn (require 'server) (server-start))" 
ExecStop=emacsclient --eval "(kill-emacs)"
Environment=SSH_AUTH_SOCK=%t/keyring/ssh
Restart=on-failure

[Install]
WantedBy=default.target

Step 2, set up in ~/.gitconfig:

[mergetool.emacs]
    cmd = ediff.sh "$LOCAL" "$REMOTE" "$BASE" "$MERGED"
[mergetool.emacsclient]
    cmd = MYEMACSCLIENT=emacsclient ediff.sh "$LOCAL" "$REMOTE" "$BASE" "$MERGED"

Step 3, create ediff.sh:

#!/bin/sh
[ -z "$MYEMACSCLIENT" ] && MYEMACSCLIENT="emacs"
# emacsclient won't work in git mergetool
# $1=$LOCAL $2=$REMOTE $3=$BASE $4=$MERGED
if [ "$MYEMACSCLIENT" = "emacs" ]; then
    $MYEMACSCLIENT -nw -Q --eval "(setq startup-now t)" -l "$HOME/.emacs.d/init.el" --eval "(progn (setq ediff-quit-hook 'kill-emacs) (if (file-readable-p \"$3\") (ediff-merge-files-with-ancestor \"$1\" \"$2\" \"$3\" nil \"$4\") (ediff-merge-files \"$1\" \"$2\" nil \"$4\")))"
else
    $MYEMACSCLIENT -nw --eval "(progn (setq ediff-quit-hook 'kill-emacs) (if (file-readable-p \"$3\") (ediff-merge-files-with-ancestor \"$1\" \"$2\" \"$3\" nil \"$4\") (ediff-merge-files \"$1\" \"$2\" nil \"$4\")))"
fi

Step 4, run git mergetool -t emacsclient to resolve conflicts.

My init-ediff.el in emacs.d.

Comments powered by Disqus