Differences between revisions 1 and 19 (spanning 18 versions)
Revision 1 as of 2005-08-26 00:58:13
Size: 2269
Editor: waste
Comment:
Revision 19 as of 2011-06-28 12:40:29
Size: 4159
Editor: hh
Comment: emacsclient script not working.
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
== Using Emacs as a merger program == #pragma section-numbers 3
== Using Emacs as a merge program ==
Line 3: Line 4:
[http://www.gnu.org/software/emacs/ Emacs] is bundled with an elisp program called Ediff which purpose is to help developers to visually apply patches. One of the various ediff commands is well suited to three way merging and can be used as a merger program with ["Mercurial"]. [[http://www.gnu.org/software/emacs/|Emacs]] is bundled with an elisp program called Ediff whose purpose is to help developers to visually apply patches. One of the ediff commands is well-suited to three-way merging and can be used as a [[MergeProgram|merge program]] with [[Mercurial]].
Line 5: Line 6:
=== Wrapping Emacs+Ediff call in a script === <<TableOfContents>>
Line 7: Line 8:
So to use [http://www.gnu.org/software/emacs/ Emacs] as ["Mercurial"] merger program, dump the following content into a file in your PATH (don't forget to turn on the execute bit): === Mercurial 1.0 and later ===

Add the following to your ~/.hgrc file (see MergeToolConfiguration):
{{{
[ui]
merge = emacs

[merge-tools]
emacs.args = -q --eval "(ediff-merge-with-ancestor \"$local\" \"$other\" \"$base\" nil \"$output\")"
}}}

If you want to use the plain emerge tool in emacs, the following line works instead in the merge-tools section:
{{{
emacs.args = -q --eval "(emerge-files-with-ancestor nil \"$local\" \"$other\" \"$base\" \"$output\" nil 'kill-emacs)"
}}}

=== Mercurial 0.9.5 and earlier: Wrap Emacs+Ediff call in a script ===

Dump the following content into a file in your PATH (don't forget to turn on the execute bit):
Line 13: Line 32:
   LOCAL=$1
   BASE=$2
   OTHER=$3
}}}
   LOCAL="$1"
   BASE="$2"
   OTHER="$3"
Line 18: Line 36:
{{{
BACKUP=$LOCAL.orig
   BACKUP="$LOCAL.orig"
Line 21: Line 38:
   Rm ()
   {
       if [ -f $BACKUP ]; then
           rm -f $BACKUP
       else
           :
       fi
   }
}}}

{{{
Line 34: Line 40:
       cp $BACKUP $LOCAL        cp "$BACKUP" "$LOCAL"
Line 36: Line 42:
}}}
Line 38: Line 43:
{{{
Line 41: Line 45:
       Rm
       E
xit $?
       exit $?
Line 44: Line 47:
}}}
Line 46: Line 48:
{{{
Line 48: Line 49:
   cp $LOCAL $BACKUP
}}}
   cp "$LOCAL" "$BACKUP"
Line 51: Line 51:
{{{
Line 54: Line 53:
       if merge $LOCAL $BASE $OTHER 2> /dev/null; then        if merge "$LOCAL" "$BASE" "$OTHER" 2> /dev/null; then
Line 60: Line 59:
       if diff3 -m $BACKUP $BASE $OTHER > $LOCAL ; then        if diff3 -m "$BACKUP" "$BASE" "$OTHER" > "$LOCAL" ; then
Line 66: Line 65:
}}}
Line 68: Line 66:
{{{
Line 73: Line 70:
}}}
Line 75: Line 71:
{{{
Line 78: Line 73:
}}}
Line 80: Line 74:
{{{
Line 84: Line 77:
=== How the script works === ''Considering Mercurial is now able to premerge before running the merge tool and even does it by default (see hgrc(5)), I feel that attempts of merge
with merge and diff3 are not needed anymore.''

===== How the script works =====
Line 88: Line 84:
=== Enabling the script usage === ===== Enabling the script usage =====
Line 90: Line 86:
Don't forget to add an entry in your hgrc file (either ~/.hgrc or the local working copy .hg/hgrc) to point ["Mercurial"] at your merge command (let's call it emacs-merge) Don't forget to add an entry in your hgrc file (either ~/.hgrc or the local working copy .hg/hgrc) to point [[Mercurial]] at your merge command (let's call it emacs-merge)
Line 94: Line 90:
}}}

=== All Mercurial versions: using emacsclient ===

You may want to use 'emacsclient' to reuse an already running Emacs session.
However, emacsclient does not relay errors from the server process properly (it always exits with status 0),
so hg has no way to know if an error occured, '''and will happily commit the merge, possibly without you ever noticing something is amiss'''.

As a workaround until [[http://debbugs.gnu.org/cgi/bugreport.cgi?bug=6963|Emacs Bug #6963]] is resolved,
you'll need a wrapper script like the one below to supply the error handling.

(save it as e. g. 'emacsclient-merge' and configure it like this)

{{{
   [ui]
   merge = emacsclient-merge
}}}


{{{
    #!/bin/bash

    if [ $# -lt 1 ]; then
      echo 1>&2 "Usage: $0 local other base output"
      exit 1
    fi

    local=$1
    other=$2
    base=$3
    output=$4

    OUTPUT=`emacsclient --no-wait --eval "(ediff-merge-with-ancestor \"$local\" \"$other\" \"$base\" nil \"$output\")" 2>&1`
    echo $OUTPUT | grep -v "Ediff Control Panel"

    if echo "$OUTPUT" | grep -q '^*ERROR*'; then
        exit 1
    fi
}}}

Q (28 Jun, 2011): This currently does not work. Seems that others are having the same problem (https://identi.ca/notice/71698204). The error message is: *ERROR*: The merge buffer file ~/path must not be a directory

Using Emacs as a merge program

Emacs is bundled with an elisp program called Ediff whose purpose is to help developers to visually apply patches. One of the ediff commands is well-suited to three-way merging and can be used as a merge program with Mercurial.

Mercurial 1.0 and later

Add the following to your ~/.hgrc file (see MergeToolConfiguration):

[ui]
merge = emacs

[merge-tools]
emacs.args = -q --eval "(ediff-merge-with-ancestor \"$local\" \"$other\" \"$base\" nil \"$output\")"

If you want to use the plain emerge tool in emacs, the following line works instead in the merge-tools section:

emacs.args = -q --eval "(emerge-files-with-ancestor nil \"$local\" \"$other\" \"$base\" \"$output\" nil 'kill-emacs)"

Mercurial 0.9.5 and earlier: Wrap Emacs+Ediff call in a script

Dump the following content into a file in your PATH (don't forget to turn on the execute bit):

   #!/bin/sh
   
   set -e # bail out quickly on failure
   
   LOCAL="$1"
   BASE="$2"
   OTHER="$3"

   BACKUP="$LOCAL.orig"
   
   Restore ()
   {
       cp "$BACKUP" "$LOCAL" 
   }

   ExitOK ()
   {
       exit $?
   }

   # Back up our file
   cp "$LOCAL" "$BACKUP"

   # Attempt to do a non-interactive merge
   if which merge > /dev/null 2>&1 ; then
       if merge "$LOCAL" "$BASE" "$OTHER" 2> /dev/null; then
           # success!
           ExitOK 
       fi
       Restore
   elif which diff3 > /dev/null 2>&1 ; then
       if diff3 -m "$BACKUP" "$BASE" "$OTHER" > "$LOCAL" ; then
           # success
           ExitOK
       fi
       Restore
   fi

   if emacs -q --no-site-file --eval "(ediff-merge-with-ancestor \"$BACKUP\" \"$OTHER\" \"$BASE\" nil \"$LOCAL\")" 
   then       
       ExitOK
   fi

   echo "emacs-merge: failed to merge files"
   exit 1

   # End of file

Considering Mercurial is now able to premerge before running the merge tool and even does it by default (see hgrc(5)), I feel that attempts of merge with merge and diff3 are not needed anymore.

0.1. How the script works

This script tries first to automatically merge the files using the RCS merge program or the diff3 program. If the automatic merger fails merging the files because of a conflict or, neither merge nor diff3 are available on the system, then emacs is launched to let the developer resolve the conflicts.

0.2. Enabling the script usage

Don't forget to add an entry in your hgrc file (either ~/.hgrc or the local working copy .hg/hgrc) to point Mercurial at your merge command (let's call it emacs-merge)

   [ui]
   merge = emacs-merge

All Mercurial versions: using emacsclient

You may want to use 'emacsclient' to reuse an already running Emacs session. However, emacsclient does not relay errors from the server process properly (it always exits with status 0), so hg has no way to know if an error occured, and will happily commit the merge, possibly without you ever noticing something is amiss.

As a workaround until Emacs Bug #6963 is resolved, you'll need a wrapper script like the one below to supply the error handling.

(save it as e. g. 'emacsclient-merge' and configure it like this)

   [ui]
   merge = emacsclient-merge

    #!/bin/bash

    if [ $# -lt 1 ]; then
      echo 1>&2 "Usage: $0 local other base output"
      exit 1
    fi

    local=$1
    other=$2
    base=$3
    output=$4

    OUTPUT=`emacsclient --no-wait --eval "(ediff-merge-with-ancestor \"$local\" \"$other\" \"$base\" nil \"$output\")" 2>&1`
    echo $OUTPUT | grep -v "Ediff Control Panel"

    if echo "$OUTPUT" | grep -q '^*ERROR*'; then
        exit 1
    fi

Q (28 Jun, 2011): This currently does not work. Seems that others are having the same problem (https://identi.ca/notice/71698204). The error message is: *ERROR*: The merge buffer file ~/path must not be a directory

MergingWithEmacs (last edited 2015-08-18 22:49:33 by mpm)