Je voudrais pouvoir utiliser ediff avec "git mergetool".
J'ai trouvé des correctifs qui altèrent le code source, ce que je ne veux pas faire. Au lieu de cela, j'aimerais ajouter le support ediff avec mon .gitconfig.
Je sais que git a un support intégré pour emerge, mais je préfère ediff.
J'ai essayé d'ajouter ces lignes à mon .gitconfig:
[mergetool "ediff"]
cmd = emacs --eval "(ediff-merge-files-with-ancestor \"$LOCAL\" \"$REMOTE\" \"$BASE\" nil \"$MERGED\")"
Mais quand j'essaye d'exécuter ceci avec "git mergetool --tool = ediff", j'obtiens ceci:
eval: 1: Syntax error: "(" unexpected
Qu'est-ce que je fais mal?
J'utilise une commande plus compliquée. Pour autant que je m'en souvienne, je l'ai obtenu de ce fil http://kerneltrap.org/mailarchive/git/2007/6/28/2502 (probablement le même que celui auquel vous faites référence).
[mergetool.ediff]
cmd = emacs --eval \"\
(progn\
(defun ediff-write-merge-buffer ()\
(let ((file ediff-merge-store-file))\
(set-buffer ediff-buffer-C)\
(write-region (point-min) (point-max) file)\
(message \\\"Merge buffer saved in: %s\\\" file)\
(set-buffer-modified-p nil)\
(sit-for 1)))\
(setq ediff-quit-hook 'kill-emacs\
ediff-quit-merge-hook 'ediff-write-merge-buffer)\
(ediff-merge-files-with-ancestor \\\"$LOCAL\\\" \\\"$REMOTE\\\"\
\\\"$BASE\\\" nil \\\"$MERGED\\\"))\"
Notez que j'ai divisé cela sur plusieurs lignes pour augmenter la lisibilité et échappé à la nouvelle ligne avec \
donc git config le considère comme une seule ligne.
J'utilise habituellement emacsclient pour éditer par exemple valider les messages. La configuration de mergetool ci-dessus n'utilise malheureusement pas emacsclient, et lorsque j'ai essayé de le faire fonctionner avec emacsclient, j'ai rencontré divers problèmes, notamment le fait que emacsclient est retourné immédiatement.
Mais vous venez de me rappeler ce problème, donc je pourrais peut-être travailler à résoudre ce problème bientôt. Cependant, si quelqu'un d'autre a déjà trouvé une solution, ce serait bien sûr ;-)
J'utilise le script suivant comme mergetool qui fonctionne assez bien.
#!/bin/bash
# test args
if [ ! ${#} -ge 3 ]; then
echo 1>&2 "Usage: ${0} LOCAL REMOTE MERGED BASE"
echo 1>&2 " (LOCAL, REMOTE, MERGED, BASE can be provided by \`git mergetool'.)"
exit 1
fi
# tools
_EMACSCLIENT=/usr/local/bin/emacsclient
_BASENAME=/bin/basename
_CP=/bin/cp
_EGREP=/bin/egrep
_MKTEMP=/bin/mktemp
# args
_LOCAL=${1}
_REMOTE=${2}
_MERGED=${3}
if [ -r ${4} ] ; then
_BASE=${4}
_EDIFF=ediff-merge-files-with-ancestor
_EVAL="${_EDIFF} \"${_LOCAL}\" \"${_REMOTE}\" \"${_BASE}\" nil \"${_MERGED}\""
else
_EDIFF=ediff-merge-files
_EVAL="${_EDIFF} \"${_LOCAL}\" \"${_REMOTE}\" nil \"${_MERGED}\""
fi
# console vs. X
if [ "${TERM}" = "linux" ]; then
unset DISPLAY
_EMACSCLIENTOPTS="-t"
else
_EMACSCLIENTOPTS="-c"
fi
# run emacsclient
${_EMACSCLIENT} ${_EMACSCLIENTOPTS} -a "" -e "(${_EVAL})" 2>&1
# check modified file
if [ ! $(egrep -c '^(<<<<<<<|=======|>>>>>>>|####### Ancestor)' ${_MERGED}) = 0 ]; then
_MERGEDSAVE=$(${_MKTEMP} --tmpdir `${_BASENAME} ${_MERGED}`.XXXXXXXXXX)
${_CP} ${_MERGED} ${_MERGEDSAVE}
echo 1>&2 "Oops! Conflict markers detected in $_MERGED."
echo 1>&2 "Saved your changes to ${_MERGEDSAVE}"
echo 1>&2 "Exiting with code 1."
exit 1
fi
exit 0
Pour l'utiliser avec `git mergetool ', mettez ce qui suit dans votre configuration git:
[merge]
tool = ediff
[mergetool "ediff"]
cmd = /path/to/ediff-merge-script $LOCAL $REMOTE $MERGED $BASE
trustExitCode = true
De plus, vous devriez vérifier (dans le script) les chemins des outils utilisés et si la détection de la console du pauvre fonctionne pour vous.
Le script lui-même démarre un client emacs (ou emacs suivi d'un client emacs, -a ""
) et evals soit ediff-merge-files-with-ancestor
ou ediff-merge-files
s'il n'y a pas de version de base (par exemple lors de la fusion de deux branches où le même chemin/fichier a été créé indépendamment).
Une fois le client emacs terminé, le fichier fusionné est vérifié pour les marqueurs de conflit. Si ceux-ci sont trouvés, votre travail sera enregistré dans un fichier temporaire, le script se terminera avec le code 1 et git restaurera le contenu pré-fusionné du fichier fusionné.
Lorsqu'il n'y a aucun marqueur de conflit présent, le script se termine avec le code 0 et git considérera la fusion comme réussie.
Important: La définition de l'option mergetool trustExitCode
sur true
ainsi que la vérification post-modification des marqueurs de conflit ne fonctionneront pas si vous démarrez emacsclient
avec les --no-wait
option.
Voici ma configuration, qui fonctionne assez bien, en utilisant au moins Emacs 23.3. L'astuce que j'ai utilisée consistait à utiliser (recursive-edit) dans un hook de telle sorte qu'emacsclient ne se ferme pas avant qu'un hook ediff-quit conseillé n'appelle (exit-recursive-edit).
J'ai utilisé un ediff-quit conseillé pour m'assurer que la sortie-recursive-edit est la toute dernière chose faite.
Il existe également des crochets pour enregistrer l'état actuel du cadre et de la fenêtre et le restaurer ensuite, et le crochet fait que le cadre actuel remplit l'écran. Vous voudrez peut-être modifier cela, mais je trouve que la fusion en plein écran est la meilleure façon.
Je n'ai pas résolu le problème de l'abandon de l'ediff et de faire en sorte qu'emacsclient retourne une sortie non nulle.
Mettez votre gitconfig:
[mergetool "ediff"]
cmd = emacsclient --eval \"(git-mergetool-emacsclient-ediff \\\"$LOCAL\\\" \\\"$REMOTE\\\" \\\"$BASE\\\" \\\"$MERGED\\\")\"
trustExitCode = true
[mergetool]
Prompt = false
[merge]
tool = ediff
Mettez votre .emacs ou équivalent:
;;
;; Setup for ediff.
;;
(require 'ediff)
(defvar ediff-after-quit-hooks nil
"* Hooks to run after ediff or emerge is quit.")
(defadvice ediff-quit (after edit-after-quit-hooks activate)
(run-hooks 'ediff-after-quit-hooks))
(setq git-mergetool-emacsclient-ediff-active nil)
(defun local-ediff-frame-maximize ()
(let* ((bounds (display-usable-bounds))
(x (nth 0 bounds))
(y (nth 1 bounds))
(width (/ (nth 2 bounds) (frame-char-width)))
(height (/ (nth 3 bounds) (frame-char-height))))
(set-frame-width (selected-frame) width)
(set-frame-height (selected-frame) height)
(set-frame-position (selected-frame) x y)))
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
(setq ediff-split-window-function 'split-window-horizontally)
(defun local-ediff-before-setup-hook ()
(setq local-ediff-saved-frame-configuration (current-frame-configuration))
(setq local-ediff-saved-window-configuration (current-window-configuration))
(local-ediff-frame-maximize)
(if git-mergetool-emacsclient-ediff-active
(raise-frame)))
(defun local-ediff-quit-hook ()
(set-frame-configuration local-ediff-saved-frame-configuration)
(set-window-configuration local-ediff-saved-window-configuration))
(defun local-ediff-suspend-hook ()
(set-frame-configuration local-ediff-saved-frame-configuration)
(set-window-configuration local-ediff-saved-window-configuration))
(add-hook 'ediff-before-setup-hook 'local-ediff-before-setup-hook)
(add-hook 'ediff-quit-hook 'local-ediff-quit-hook 'append)
(add-hook 'ediff-suspend-hook 'local-ediff-suspend-hook 'append)
;; Useful for ediff merge from emacsclient.
(defun git-mergetool-emacsclient-ediff (local remote base merged)
(setq git-mergetool-emacsclient-ediff-active t)
(if (file-readable-p base)
(ediff-merge-files-with-ancestor local remote base nil merged)
(ediff-merge-files local remote nil merged))
(recursive-edit))
(defun git-mergetool-emacsclient-ediff-after-quit-hook ()
(exit-recursive-edit))
(add-hook 'ediff-after-quit-hooks 'git-mergetool-emacsclient-ediff-after-quit-hook 'append)
Mis à part le problème git vs bzr que j'ai identifié dans mon commentaire ci-dessus, j'ai pu confirmer que vous devez échapper aux parens comme dans
cmd = emacs --eval "\\(ediff-merge-files-with-ancestor \"$LOCAL\" \"$REMOTE\" \"$BASE\" nil \"$MERGED\"\\)"
Notez les caractères de double barre oblique inverse. Je comprends en quelque sorte qu'ils sont nécessaires (plutôt qu'un seul) pour passer à la fois par les mécanismes de citation sh/bash ET les mécanismes de citation de démarrage emacs. Je laisse le soin à quelqu'un ayant une meilleure compréhension d'Emacs et de Shell de citer les détails sanglants.
-pmr
Le code elisp dans le code de Viper3369 ( tiliser ediff comme git mergetool ) utilise une fonction "display-usable-bounds" qui n'existe pas. Étant donné que les crochets font beaucoup plus que ce qui est strictement nécessaire, il suffit de supprimer toutes les références aux "limites d'affichage utilisables" pour que cela fonctionne pour moi. Bon travail! ;)
(Edit: je pense que je devrais poster le code emacs-LISP modifié:
;;
;; Setup for ediff.
;;
(require 'ediff)
(defvar ediff-after-quit-hooks nil
"* Hooks to run after ediff or emerge is quit.")
(defadvice ediff-quit (after edit-after-quit-hooks activate)
(run-hooks 'ediff-after-quit-hooks))
(setq git-mergetool-emacsclient-ediff-active nil)
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
(setq ediff-split-window-function 'split-window-horizontally)
(defun local-ediff-before-setup-hook ()
(setq local-ediff-saved-frame-configuration (current-frame-configuration))
(setq local-ediff-saved-window-configuration (current-window-configuration))
;; (local-ediff-frame-maximize)
(if git-mergetool-emacsclient-ediff-active
(raise-frame)))
(defun local-ediff-quit-hook ()
(set-frame-configuration local-ediff-saved-frame-configuration)
(set-window-configuration local-ediff-saved-window-configuration))
(defun local-ediff-suspend-hook ()
(set-frame-configuration local-ediff-saved-frame-configuration)
(set-window-configuration local-ediff-saved-window-configuration))
(add-hook 'ediff-before-setup-hook 'local-ediff-before-setup-hook)
(add-hook 'ediff-quit-hook 'local-ediff-quit-hook 'append)
(add-hook 'ediff-suspend-hook 'local-ediff-suspend-hook 'append)
;; Useful for ediff merge from emacsclient.
(defun git-mergetool-emacsclient-ediff (local remote base merged)
(setq git-mergetool-emacsclient-ediff-active t)
(if (file-readable-p base)
(ediff-merge-files-with-ancestor local remote base nil merged)
(ediff-merge-files local remote nil merged))
(recursive-edit))
(defun git-mergetool-emacsclient-ediff-after-quit-hook ()
(exit-recursive-edit))
(add-hook 'ediff-after-quit-hooks 'git-mergetool-emacsclient-ediff-after-quit-hook 'append)
Merci, cela fonctionne également dans xemacs, mais la citation comme dans la réponse de pmr ne semble pas fonctionner alors que je pense que la citation dans toutes les autres réponses est correcte:
[mergetool "ediff"]
cmd = xemacs -eval \"(ediff-merge-files-with-ancestor \\\"$PWD/$LOCAL\\\" \\\"$PWD/$REMOTE\\\" \\\"$PWD/$BASE\\\" nil \\\"$PWD/$MERGED\\\")\"
[merge]
tool = ediff
J'ai mis ce code ci-dessus dans ~/.gitconfig
.
Voici une variante de la configuration de tarsius. Il gère quand le fichier ancêtre $ BASE n'existe pas, et il vous permet d'interrompre la fusion sans mettre à la corbeille l'état de git sur le conflit (en n'enregistrant pas automatiquement à la sortie). Il a également des retours à la ligne backslashed afin que vous puissiez conserver la mise en forme.
[mergetool.ediff]
cmd = emacs --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\\\")))\"
Il existe un moyen d'utiliser la fonction ediff-merge-files-with-ancestor avec emacsclient.
La plus simple (pour l'utilisateur GNU/Linux) est de faire une lecture Shell depuis un tube après l'appel emacsclient. Un hook ajouté dans append à ediff-quit-hook (il doit être exécuté après ediff-cleanup-mess sinon la session ediff ne se termine pas correctement) tirera un personnage dans le pipe via Shell-commande.
Un plus raffiné utilisera un sémaphore.
Et ici arrive l'utilisateur avancé Unix.
Arrive ensuite le gourou Emacs (Stefan Monnier) et vous dit que vous pouvez appeler
emacsclient --eval '(progn (ediff-merge-files-wit .......) (édition récursive))'
après avoir ajouté
(lancer la sortie)
quelque part à la fin de ediff-quit-hook. Pas de pipe nommée, pas de sémaphores, juste Emacs LISP. Simple, élégant et ne nécessite pas de tests étranges pour éviter d'utiliser des tuyaux ou des sémaphores lorsqu'ils ne sont pas utilisés.
Merci Stefan!
Pour utiliser l'outil de fusion interactif de Subversion au lieu de git, voir this post pour quelques instructions pour configurer cela.
Combiner mes idées préférées d'en haut. Cette configuration utilise emacsclient et nécessite donc qu'un emacs soit déjà en cours d'exécution.
Cela fonctionne également pour git difftool - il invoquera les fichiers ediff. (Lorsque git difftool appelle, l'ancêtre sera égal à la fusion.)
Dans .gitconfig:
[mergetool "ec-merge"]
Prompt = false
cmd = ec-merge "$LOCAL" "$REMOTE" "$BASE" "$MERGED"
trustExitCode = true
[merge]
tool = ec-merge
[difftool]
Prompt = false
Dans ~/bin/ec-merge (assurez-vous que ~/bin est dans votre PATH):
#!/bin/bash
set -e
LOCAL=$(readlink -f "$1")
REMOTE=$(readlink -f "$2")
BASE=$(readlink -f "$3")
MERGED=$(readlink -f "$4")
emacsclient --eval "(jcl-git-merge \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\")"
! egrep -q '^(<<<<<<<|=======|>>>>>>>|####### Ancestor)' "$MERGED"
Dans .emacs:
(server-start)
(defvar jcl-save-and-kill-buffers-before-merge nil
"Normally if emacs already visits any of the concerned files (local,
remote, base or merged) ediff will ask it shall save and kill the
buffer. If you always want to answer yes to this then set this
to non-nil.")
(defun jcl-git-merge (local remote ancestor merged)
(when jcl-save-and-kill-buffers-before-merge
(dolist (file (list local remote ancestor merged))
(setq file (file-truename file))
(let ((old-buffer (and file (find-buffer-visiting file))))
(when old-buffer
(with-current-buffer old-buffer
(save-buffer))
(kill-buffer old-buffer)))))
(prog1
(if (string-equal ancestor merged)
(progn
(ediff-files local remote (list 'jcl-exit-recursive-edit-at-quit))
(format "ediff compared %s and %s" local remote))
(if ancestor
(ediff-merge-files-with-ancestor local remote ancestor
(list 'jcl-exit-recursive-edit-at-quit)
merged)
(ediff-merge-files local remote (list 'jcl-exit-recursive-edit-at-quit merged)))
(format "ediff merged %s" merged))
(recursive-edit)))
(defun jcl-exit-recursive-edit-at-quit ()
(add-hook 'ediff-quit-hook (lambda () (throw 'exit nil)) t t))
Normalement, si emacs visite déjà l'un des fichiers concernés (local, distant, de base ou fusionné), ediff lui demandera de sauvegarder et de tuer le tampon. Si vous m'aimez toujours vouloir répondre oui à cela, ajoutez également ceci à votre .emacs:
(setq jcl-save-and-kill-buffers-before-merge t)
Il s'agit d'une discussion intéressante sur la façon de procéder en utilisant Mercurial. Il semble qu'ils aient un script wrapper qui atténue le problème emacsclient: https://www.Mercurial-scm.org/wiki/MergingWithEmacs
Ce fut une découverte précieuse pour moi. J'ai un petit ajout, car j'utilise le mode de sauvegarde de bureau emacs:
[mergetool "ediff"]
cmd = emacs --no-desktop -eval \"(ediff-merge-files-with-ancestor \\\"$PWD/$LOCAL\\\" \\\"$PWD/$REMOTE\\\" \\\"$PWD/$BASE\\\" nil \\\"$PWD/$MERGED\\\")\"
et ajouté la clause "(quand" ci-dessous, car je préfère un ediff multi-images normalement:
;;
;; Setup for ediff.
;;
(require 'ediff)
(when (or (not desktop-save-mode) (member "--no-desktop" command-line-args))
(defvar ediff-after-quit-hooks nil
... (rest of TauPan's code here) ...
)