Size: 4548
Comment: Someone please review this edit; it's my first. I haven't actually tested this either.
|
Size: 4832
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 3: | Line 3: |
[[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]]. |
[[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 merge program with Mercurial. |
Line 9: | Line 8: |
Add the following to your ~/.hgrc file (see MergeToolConfiguration): | |
Line 10: | Line 10: |
Add the following to your ~/.hgrc file (see MergeToolConfiguration): | |
Line 16: | Line 15: |
emacs.args = -q --eval "(ediff-merge-with-ancestor \"$local\" \"$other\" \"$base\" nil \"$output\")" | 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)" |
Line 19: | Line 23: |
If you want to use the plain emerge tool in emacs, the following line works instead in the merge-tools section: | A more elaborate version that puts the ediff control window inside the main frame: |
Line 21: | Line 26: |
emacs.args = -q --eval "(emerge-files-with-ancestor nil \"$local\" \"$other\" \"$base\" \"$output\" nil 'kill-emacs)" | emacs.args = -q --eval "(require 'ediff)" --eval "(setq ediff-window-setup-function 'ediff-setup-windows-plain)" --eval "(add-hook 'ediff-quit-hook 'save-buffers-kill-emacs)" --eval "(ediff-merge-with-ancestor \""$local"\" \""$other"\" \""$base"\" nil \""$output"\")" |
Line 25: | Line 30: |
Dump the following content into a file in your PATH (don't forget to turn on the execute bit): | |
Line 26: | Line 32: |
Dump the following content into a file in your PATH (don't forget to turn on the execute bit): | |
Line 28: | Line 33: |
#!/bin/sh | #!/bin/sh |
Line 30: | Line 35: |
set -e # bail out quickly on failure | set -e # bail out quickly on failure |
Line 32: | Line 37: |
LOCAL="$1" BASE="$2" OTHER="$3" |
LOCAL="$1" BASE="$2" OTHER="$3" |
Line 36: | Line 41: |
BACKUP="$LOCAL.orig" | BACKUP="$LOCAL.orig" |
Line 38: | Line 43: |
Restore () { cp "$BACKUP" "$LOCAL" } |
Restore () { cp "$BACKUP" "$LOCAL" } |
Line 43: | Line 48: |
ExitOK () { exit $? } |
ExitOK () { exit $? } |
Line 48: | Line 53: |
# Back up our file cp "$LOCAL" "$BACKUP" |
# Back up our file cp "$LOCAL" "$BACKUP" |
Line 51: | Line 56: |
# 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 |
# 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! |
Line 70: | Line 62: |
Restore elif which diff3 > /dev/null 2>&1 ; then if diff3 -m "$BACKUP" "$BASE" "$OTHER" > "$LOCAL" ; then # success ExitOK fi Restore fi |
|
Line 71: | Line 71: |
echo "emacs-merge: failed to merge files" exit 1 |
if emacs -q --no-site-file --eval "(ediff-merge-with-ancestor \"$BACKUP\" \"$OTHER\" \"$BASE\" nil \"$LOCAL\")" then ExitOK fi |
Line 74: | Line 76: |
# End of file | echo "emacs-merge: failed to merge files" exit 1 # End of file |
Line 77: | Line 82: |
''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.'' |
''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.'' |
Line 81: | Line 85: |
Line 85: | Line 88: |
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 86: | Line 90: |
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 88: | Line 91: |
[ui] merge = emacs-merge |
[ui] merge = emacs-merge |
Line 91: | Line 94: |
Line 93: | Line 95: |
Line 96: | Line 97: |
Until the trunk was patched (on 02 Oct 2010), emacsclient did not relay errors from the server process properly (it always exited with status 0), so hg had no way to know if an error occurred, '''and would happily commit the merge, possibly without you ever noticing something is amiss'''. |
Until the trunk was patched (on 02 Oct 2010), emacsclient did not relay errors from the server process properly (it always exited with status 0), so hg had no way to know if an error occurred, '''and would happily commit the merge, possibly without you ever noticing something is amiss'''. |
Line 100: | Line 100: |
([[http://debbugs.gnu.org/cgi/bugreport.cgi?bug=6963|Emacs Bug #6963]] has been resolved in the trunk on 02 Oct 2010, so you'll need Emacs 24, at least. (Check etc/NEWS for "If emacsclient shuts down as ... its exit status is 1") | . ([[http://debbugs.gnu.org/cgi/bugreport.cgi?bug=6963|Emacs Bug #6963]] has been resolved in the trunk on 02 Oct 2010, so you'll need Emacs 24, at least. (Check etc/NEWS for "If emacsclient shuts down as ... its exit status is 1") |
Line 105: | Line 106: |
[ui] merge = emacsclient-merge |
[ui] merge = emacsclient-merge |
Line 109: | Line 110: |
{{{ #!/bin/bash |
|
Line 110: | Line 113: |
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` A (July, 2013): The issue appears to be with how the arguments are passed to the merge. I found that the following `.hgrc` stanza fixed the issue: |
|
Line 111: | Line 134: |
#!/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 |
[ui] merge = emacsclient-merge [merge-tools] emacsclient-merge.args=$local $other $base $output |
Line 130: | Line 139: |
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 A (Nov 2011): Then upgrade to a newer version of emacs. This is fixed in Emacs 24, which has been in pretesting for months, and a stable release is expected in early 2012. |
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.
Contents
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)"
A more elaborate version that puts the ediff control window inside the main frame:
emacs.args = -q --eval "(require 'ediff)" --eval "(setq ediff-window-setup-function 'ediff-setup-windows-plain)" --eval "(add-hook 'ediff-quit-hook 'save-buffers-kill-emacs)" --eval "(ediff-merge-with-ancestor \""$local"\" \""$other"\" \""$base"\" nil \""$output"\")"
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.
Until the trunk was patched (on 02 Oct 2010), emacsclient did not relay errors from the server process properly (it always exited with status 0), so hg had no way to know if an error occurred, and would happily commit the merge, possibly without you ever noticing something is amiss.
As a workaround if the fix isn't in the version of emacs you use, you'll need a wrapper script like the one below to supply the error handling.
(Emacs Bug #6963 has been resolved in the trunk on 02 Oct 2010, so you'll need Emacs 24, at least. (Check etc/NEWS for "If emacsclient shuts down as ... its exit status is 1")
(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
A (July, 2013): The issue appears to be with how the arguments are passed to the merge. I found that the following .hgrc stanza fixed the issue:
[ui] merge = emacsclient-merge [merge-tools] emacsclient-merge.args=$local $other $base $output