development:emacs:ediff
Ediff Mode
Because Ediff is a very useful Emacs mode (in particular for VC) i wrote a shell script to start it from the command line.
#!/bin/bash # # Emacs EDIFF shell script. # NOTE: This script was tested with bash, dash (Debian 12) and zsh (macOS 26+). # # Usage: ediff.sh [--client] LOCAL REMOTE [MERGED [BASE]] # # Copyright (C) 2013-2026 Ralf Hoppe <ralf@rho62.de> # abspath () { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" } # if this is really a MERGE MERGE=1 if [ $# -gt 0 ] && [ "$1" = "--client" ] ; then LOCAL=$(abspath "$2") REMOTE=$(abspath "$3") MERGED=$(abspath "$4") BASE=$(abspath "$5") EMACS_CMD="emacsclient --create-frame" # NOTE: Empty list `vc-handled-backends' is a workaround for Emacs 24 # bug #18788, regarding vc-git mode-line. ELISP_COMMON=\ "(setq rho/vc-handled-backends-backup vc-handled-backends \ rho/sh-ediff-buf-local (get-file-buffer \"${LOCAL}\") \ rho/sh-ediff-buf-remote (get-file-buffer \"${REMOTE}\") \ rho/sh-ediff-frame (selected-frame) \ cscope-initial-directory nil \ vc-handled-backends ()) \ (defun rho/sh-ediff-quit-hook () \ (setq vc-handled-backends rho/vc-handled-backends-backup) \ (ignore-errors \ (unless rho/sh-ediff-buf-local \ (kill-buffer (get-file-buffer \"${LOCAL}\"))) \ (unless rho/sh-ediff-buf-remote \ (kill-buffer (get-file-buffer \"${REMOTE}\")))) \ (remove-hook 'ediff-quit-hook 'rho/sh-ediff-quit-hook) \ (when (frame-live-p rho/sh-ediff-frame) \ (delete-frame rho/sh-ediff-frame))) \ (add-hook 'ediff-quit-hook 'rho/sh-ediff-quit-hook t)" if [ -z "$(lsof -u $USER -a -c '/[Ee]macs/' 2>/dev/null | grep '/server')" ] ; then emacs --rho-minimal & sleep 2 fi sleep 1 shift else LOCAL=$(abspath "$1") REMOTE=$(abspath "$2") MERGED=$(abspath "$3") BASE=$(abspath "$4") EMACS_CMD="emacs --rho-minimal" ELISP_COMMON=\ "(setq ediff-quit-hook 'kill-emacs \ vc-handled-backends ())" fi if [ $# -lt 2 ]; then echo "Usage: $(basename $0) [--client] LOCAL REMOTE [MERGED [BASE]]" echo " (see also GIT-DIFFTOOL(1) and GIT-MERGETOOL(1))" exit 1 fi echo -e "$# files:\nLOCAL=$LOCAL\nREMOTE=$REMOTE" if [ $# -eq 4 ] ; then echo -e "MERGED=$MERGED\nBASE=$BASE" $EMACS_CMD --eval \ "(progn $ELISP_COMMON (setq-default ediff-show-clashes-only t) (ediff-merge-files-with-ancestor \"${LOCAL}\" \"${REMOTE}\" \"${BASE}\" nil \"${MERGED}\"))" > /dev/null elif [ $# -eq 2 -o "${REMOTE}" = "${MERGED}" ] ; then $EMACS_CMD --eval \ "(progn $ELISP_COMMON (ediff-files \"$LOCAL\" \"$REMOTE\"))" > /dev/null MERGE=0 else echo "MERGED=$MERGED" $EMACS_CMD --eval \ "(progn $ELISP_COMMON (setq-default ediff-show-clashes-only t) (ediff-merge-files \"${LOCAL}\" \"${REMOTE}\" nil \"${MERGED}\"))" > /dev/null fi if [ $MERGE -ne 0 ]; then EGREPHITS="$(egrep -c '^(<<<<<<<|>>>>>>>)' $MERGED)" if [ $EGREPHITS -ne 0 ]; then TMPFILE="$(mktemp --tmpdir $(basename $MERGED).XXXXXXXXXX)" cp -p "$MERGED" "$TMPFILE" echo "Conflicts -> see file $TMPFILE" exit 1 fi fi exit 0
development/emacs/ediff.txt · Last modified: by Ralf Hoppe
