代码之家  ›  专栏  ›  技术社区  ›  alternative

使用ediff作为git合并工具

  •  49
  • alternative  · 技术社区  · 16 年前

    我希望能够与“git mergetool”一起使用ediff。

    我发现了一些修改源代码的补丁,我不想这样做。相反,我想在.gitconfig中添加对ediff的支持。

    我知道git内置了对emerge的支持,但我更喜欢ediff。

    我试图将以下行添加到我的.gitconfig中:

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

    eval: 1: Syntax error: "(" unexpected
    

    我做错了什么?

    12 回复  |  直到 15 年前
        1
  •  31
  •   hvrauhal tarsius    11 年前

    我使用更复杂的命令。据我记忆所及,我是从这条线得到的 http://kerneltrap.org/mailarchive/git/2007/6/28/250230

    [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\\\"))\"
    

    请注意,我已将其拆分为几行以增加可读性,并使用 \ 所以git-config将其视为一行。

    我通常使用EmacClient编辑,例如提交消息。不幸的是,上面的mergetool配置没有使用EmacClient,当我试图让它与EmacClient一起工作时,遇到了各种问题,包括EmacClient立即返回的事实。

    但是你刚刚提醒了我这个问题,所以我可能很快就会解决这个问题。但是,如果其他人已经找到了一个解决方案,那当然是很好的;-)

        2
  •  13
  •   u-punkt    14 年前

    #!/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
    

    要将其与“git mergetool”一起使用,请将以下内容放入git配置中:

    [merge]
            tool = ediff
    
    [mergetool "ediff"]
            cmd = /path/to/ediff-merge-script $LOCAL $REMOTE $MERGED $BASE
            trustExitCode = true
    

    此外,您应该检查(在脚本中)所用工具的路径,以及穷人的控制台检测是否适合您。

    -a "" )和评估 ediff-merge-files-with-ancestor ediff-merge-files 如果没有基本版本(例如,当合并两个分支时,其中相同的路径/文件已独立创建)。

    当不存在冲突标记时,脚本退出,代码为0,git将认为合并成功。

    设置合并工具选项 trustExitCode 到 true 此外,如果开始,冲突标记的编辑后检查也将不起作用 emacsclient 和 --no-wait 选项

        3
  •  8
  •   harveyt    15 年前

    这是我的设置,它运行得相当好,至少使用了Emacs 23.3。我使用的技巧是在钩子中使用(递归编辑),这样EmacClient在建议的ediff退出钩子调用(退出递归编辑)之前不会退出。

    我使用advistedediff-quit来确保退出递归编辑是最后一件事。

    还有一些钩子可以保存当前帧和窗口状态,并在以后恢复,钩子可以使当前帧填充屏幕。您可能希望修改,但我发现合并全屏是最好的方式。

    放入您的gitconfig:

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

    放入.emacs或等效文件:

    ;;
    ;; 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)
    
        4
  •  6
  •   pajato0    16 年前

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

    请注意双反斜杠字符。我有点理解,他们需要(而不是一个)通过sh/bash引用和emacs启动引用机制。我将把它留给更了解Emacs和shell引用的人来解释血腥的细节。

    -pmr

        5
  •  4
  •   Community Mohan Dere    9 年前

    Viper3369代码中的elisp代码( Using ediff as git mergetool

    (编辑:我想我应该发布修改后的emacs lisp代码:

    ;;
    ;; 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)
    
        6
  •  4
  •   Community Mohan Dere    9 年前

    谢谢,它也适用于xemacs,但是引用 the reply by pmr 似乎不起作用,但我认为所有其他回复中的引用都很好:

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

    我把上面的代码放进去了 ~/.gitconfig

        7
  •  3
  •   tresi    15 年前

    这是tarsius设置的一个变体。它处理祖先文件$BASE不存在的情况,并允许您中止合并,而不破坏git关于冲突的状态(在退出时不自动保存)。它还将换行符反斜杠,以便您可以保留格式。

    [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\\\")))\"
    
        8
  •  2
  •   zbeekman    15 年前

    要使用Subversion的交互式合并工具代替git,请参见 this 张贴一些说明来设置此设置。

        9
  •  2
  •   saint    11 年前

    有一种方法可以在EmacClient中使用ediff合并祖先文件功能。

    退出hook(它) 必须 追赶 ediff清除命令(否则ediff会话未正确终止)将通过shell命令在管道中拍摄角色。

    一个更精致的将使用信号量。

    下面是Unix超级用户。

    然后到达Emacs大师(Stefan Monnier)并告诉您可以打电话

    添加后

    (投球退出)

    在埃迪夫河的尽头。没有命名管道,没有信号量,只有Emacs LISP。简单、优雅,不需要奇怪的测试来避免在不使用管道或信号量时使用它们。

        10
  •  1
  •   Geoff    15 年前

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

    并在下面添加了“(when)”子句,因为我通常更喜欢多帧ediff:

    ;;
    ;; 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) ...
    )
    
        11
  •  1
  •   Johan Claesson    11 年前

    结合上面我最喜欢的想法。此配置使用 EmacClient,因此要求emacs已在运行。

    这也适用于git difftool—它将调用ediff文件。(何时)

    在.gitconfig中:

    [mergetool "ec-merge"]
            prompt = false
            cmd = ec-merge "$LOCAL" "$REMOTE" "$BASE" "$MERGED"
            trustExitCode = true
    [merge]
            tool = ec-merge
    [difftool]
            prompt = false
    

    在~/bin/ec merge中(确保~/bin位于您的路径中):

    #!/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"
    

    在.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))
    

    通常,如果emacs已访问任何相关文件(本地、, 远程、基本或合并)ediff将要求它保存并杀死 缓冲器如果你喜欢我总是想回答“是”,那么添加 这也适用于您的.emacs:

    (setq jcl-save-and-kill-buffers-before-merge t)
    
        12
  •  1
  •   Vadim Kotov First Zero    9 年前

    这是一个很好的关于使用mercurial进行此操作的讨论。看起来他们有一个包装器脚本,可以缓解EmacClient问题: https://www.mercurial-scm.org/wiki/MergingWithEmacs