Utilisation d'ediff comme git mergetool

je voudrais pouvoir utiliser ediff avec"git mergetool".

j'ai trouvé des patches qui modifient le code source, ce que je ne veux pas faire. Au lieu de cela ,je voudrais 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'essaie d'exécuter ceci avec " gitmergetool --outil=ediff", j'obtiens ceci:

eval: 1: Syntax error: "(" unexpected

Qu'est-ce que je fais de mal?

49
demandé sur peSHIr 2009-11-30 03:53:49

12 réponses

j'utilise une commande plus compliquée. Pour autant que je me souvienne je l'ai eu à partir de ce fil http://kerneltrap.org/mailarchive/git/2007/6/28/250230 (probablement le même que ce à quoi 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é ceci en plusieurs lignes pour augmenter la lisibilité et j'ai échappé à la nouvelle ligne avec \ alors git config le considère comme une seule ligne.

j'utilise habituellement emacsclient pour éditer par exemple des messages de commit. La configuration de mergetool ci-dessus n'utilise malheureusement pas emacsclient, et lorsque j'ai essayé de la faire fonctionner avec emacsclient, j'ai rencontré divers problèmes, y compris le fait qu'emacsclient soit retourné immédiatement.

mais vous venez de me rappeler de cette question, donc je pourrais travailler sur la réparation de ce problème bientôt. Cependant si quelqu'un d'autre a déjà trouvé une solution qui serait grande bien sûr; -)

28
répondu tarsius 2015-05-13 14:09:55

j'utilise le script suivant comme mergetool qui fonctionne assez bien.

#!/bin/bash

# test args
if [ ! ${#} -ge 3 ]; then
    echo 1>&2 "Usage: "151900920" 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=
_REMOTE=
_MERGED=
if [ -r  ] ; then
    _BASE=
    _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

en outre, vous devez 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).

après que le client emacs a terminé le fichier fusionné est vérifié pour les marqueurs de conflit. Si ceux-ci sont trouvés, votre travail sera sauvegardé dans un fichier temporaire, le script sortira avec le code 1 et git restaurera le contenu pré-mergetool du fichier fusionné.

en l'absence de conflit les marqueurs présents, le script sort avec le code 0 et git considérera la fusion comme réussie.

Important: définir l'option mergetool trustExitCode à true ainsi que la vérification après-édition pour les marqueurs de conflit ne fonctionnera pas si vous commencez emacsclient avec l'option --no-wait .

13
répondu u-punkt 2011-11-23 14:59:12

Voici mon installation, qui fonctionne assez bien, en utilisant au moins Emacs 23.3. Le truc que j'ai utilisé était d'utiliser (recursive-edit) dans un crochet tel qu'emacsclient ne sort pas avant un appel ediff-quit (exit-recursive-edit) conseillé.

j'ai utilisé une ediff-quit conseillée pour s'assurer que la exit-recursive-edit est la toute dernière chose faite.

Il ya aussi des crochets pour sauver le cadre actuel et l'état de la fenêtre et de le restaurer après, et le crochet fait le cadre courant remplir l'écran. Vous pouvez vouloir modifier cela, mais je trouve que la fusion plein écran est la meilleure façon.

Je n'ai pas résolu le problème de l'annulation de l'ediff et de faire d'emacsclient return une sortie non nulle.

mettez dans votre gitconfig:

[mergetool "ediff"]
       cmd = emacsclient --eval \"(git-mergetool-emacsclient-ediff \\"$LOCAL\\" \\"$REMOTE\\" \\"$BASE\\" \\"$MERGED\\")\"
       trustExitCode = true
[mergetool]
    prompt = false
[merge]
    tool = ediff

mettez dans votre .emacs ou l'é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)
8
répondu harveyt 2010-12-22 19:04:36

mis à part la question de git vs bzr que j'ai identifiée dans mon commentaire ci-dessus, j'ai été en mesure de confirmer que vous devez échapper à la parens comme dans

 cmd = emacs --eval "\(ediff-merge-files-with-ancestor \"$LOCAL\" \"$REMOTE\" \"$BASE\" nil \"$MERGED\"\)"

notez les caractères de double antislash. Je comprends en quelque sorte qu'ils sont nécessaires (plutôt qu'un seul) pour passer à travers les mécanismes de citation sh/bash et de démarrage d'emacs. Je laisse ça à quelqu'un avec une meilleure compréhension D'Emacs et de shell citant pour expliquer les détails sanglants.

- pmr

5
répondu pajato0 2009-11-30 03:52:32

le code elisp du code Viper3369 ( utilisant ediff comme git mergetool ) utilise une fonction" display-usable-bounds " qui n'existe pas. Puisque les crochets font beaucoup plus qu'il n'est strictement nécessaire, supprimer simplement toutes les références à "display-usable-bounds" est suffisant pour le faire fonctionner pour moi. Bon travail! ;)

(Edit: je pense que je devrais poster la modification emacs-lisp code:

;;
;; 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)
4
répondu TauPan 2017-05-23 12:18:13

Merci, cela fonctionne aussi dans xemacs, cependant 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 très bien:

[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 dans ~/.gitconfig .

4
répondu Nei 2017-05-23 11:47:06

voici une variante de la configuration de tarsius. Il gère lorsque le fichier ancêtre $BASE n'existe pas, et il vous permet d'annuler la fusion sans détruire l'état de git sur le conflit (en ne sauvegardant pas automatiquement sur exit). Il a également newlines backslashed de sorte que vous pouvez garder le formatage.

[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\\")))\"
3
répondu tresi 2010-10-25 21:24:35

pour l'utilisation de L'outil de fusion interactif de Subversion au lieu de git, voir ce pour des instructions sur la configuration.

2
répondu zbeekman 2011-04-29 19:21:57

il y a une façon d'utiliser la fonction ediff-merge-files-with-ancestor avec emacsclient.

le plus simple (pour L'utilisateur GNU/Linux) est de faire une lecture de shell à partir d'un tuyau après l'appel emacsclient. Un crochet ajouté dans append à ediff-quitter-crochet ( doit être exécuté après l' ediff-cleanup-mess sinon la session ediff n'est pas terminée correctement) va tirer un caractère dans le tuyau à travers la commande shell.

un plus raffiné utilisera un sémaphore.

et voici L'utilisateur D'Unix power.

arrive alors le gourou Emacs (Stefan Monnier) et vous dit que vous pouvez appeler

emacsclient --eval '(progn (ediff-fusion-fichiers-esprit.......) (édition récursive)) "

après

(sortie throw)

quelque part au bout de ediff-quit-hook. Pas de tuyau nommé, non des sémaphores, juste Emacs LISP. Simple, élégant et ne nécessite pas de tests bizarres pour éviter d'utiliser des pipes ou des sémaphores quand ils ne sont pas utilisés.

Merci Stefan!

2
répondu saint 2014-10-27 13:47:06

c'était une précieuse trouvaille pour moi. J'ai un petit plus, depuis que j'utilise emacs bureau-enregistrer en mode:

[mergetool "ediff"]
cmd = emacs --no-desktop -eval \"(ediff-merge-files-with-ancestor \\"$PWD/$LOCAL\\" \\"$PWD/$REMOTE\\" \\"$PWD/$BASE\\" nil \\"$PWD/$MERGED\\")\"

et a ajouté la clause "(quand" ci-dessous, parce que je préfère un ediff multi-cadres 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) ...
)
1
répondu Geoff 2011-04-06 23:52:46

combinant mes idées préférées d'en haut. Cette configuration utilise emacsclient et exigent donc qu'un emacs soit déjà en cours d'exécution.

cela fonctionne aussi pour Git difftool - il va invoquer des fichiers ediff. (Lorsque git difftool appelle alors l'ancêtre sera égal au fusionné.)

In .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 sur votre chemin):

#!/bin/bash

set -e

LOCAL=$(readlink -f "")
REMOTE=$(readlink -f "")
BASE=$(readlink -f "")
MERGED=$(readlink -f "")

emacsclient --eval "(jcl-git-merge \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\")"

! egrep -q '^(<<<<<<<|=======|>>>>>>>|####### Ancestor)' "$MERGED"

In .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, remote, base ou fusionné) ediff va lui demander de sauvegarder et de tuer le tampon. Si vous m'aimez toujours vouloir répondre oui à ceci alors ajouter aussi présent pour votre .emacs:

(setq jcl-save-and-kill-buffers-before-merge t)
1
répondu Johan Claesson 2015-03-01 19:08:22

C'est une belle discussion à ce sujet usuing mercurial. Il semble qu'ils aient un script d'enrubannage qui allège la question d'emacsclient: https://www.mercurial-scm.org/wiki/MergingWithEmacs

1
répondu zbeekman 2017-06-19 13:30:45