From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Giorgos Keramidas Newsgroups: gmane.emacs.help Subject: Re: Ediff and merge Date: Sat, 16 May 2009 07:30:07 +0300 Organization: SunSITE.dk - Supporting Open source Message-ID: <87bppt3c1c.fsf@kobe.laptop> References: NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: ger.gmane.org 1242456117 6024 80.91.229.12 (16 May 2009 06:41:57 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sat, 16 May 2009 06:41:57 +0000 (UTC) To: help-gnu-emacs@gnu.org Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Sat May 16 08:41:51 2009 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1M5Daf-0000p8-OU for geh-help-gnu-emacs@m.gmane.org; Sat, 16 May 2009 08:41:50 +0200 Original-Received: from localhost ([127.0.0.1]:47436 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1M5Daf-0002az-6z for geh-help-gnu-emacs@m.gmane.org; Sat, 16 May 2009 02:41:49 -0400 Original-Path: news.stanford.edu!headwall.stanford.edu!newsfeed.esat.net!kanaga.switch.ch!switch.ch!news.tele.dk!news.tele.dk!small.news.tele.dk!feed118.news.tele.dk!dotsrc.org!filter.dotsrc.org!news.dotsrc.org!not-for-mail Original-Newsgroups: gnu.emacs.help User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.0.93 (berkeley-unix) Cancel-Lock: sha1:SL+XkUkvJzIa5Vox81dpJFOZ+AY= Original-Lines: 241 Original-NNTP-Posting-Host: 77.49.53.220 Original-X-Trace: news.sunsite.dk DXC=f7TD53Sb1AlE`KPf6Y4mAkYSB=nbEKnkkl2^feVC_<6fL^MjgbH?lJH1Tkjl_:54PbLAJhc0de?i\E:7>a Original-X-Complaints-To: staff@sunsite.dk Original-Xref: news.stanford.edu gnu.emacs.help:169220 X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.help:64484 Archived-At: On Fri, 15 May 2009 21:34:23 +0200, Rasmus Pank Roulund wrote: > Hello, > > I have a simple and probably simple question on ediff and merge. I > use SVN. Sometimes conflicts happens. I try to resolve them with > Ediff. I use the 3files mode and use "a" and "b" to select whatever I > want to keep. ediff is a fine tool for merging conflicts in 3-way mode. I use it all the time for subversion, mercurial and perforce merges, and it has slowly become one of the tools I consider indispensable when working with version-controlled files. The recent releases of Subversion support a config option to integrate your own 3-way merge tool with the `svn merge' command. That's what I use locally to integrate ediff with subversion merging. You first have to set the following option in the `config' file of your `~/.subversion' directory. Open the file and look for the section called [helpers]. You can add the `merge-tool-cmd' option at that section: [helpers] merge-tool-cmd = svn-ediff-merge Then save the following script as `svn-ediff-merge', make it executable and save it somewhere in your PATH so svn(1) can find it: ,----------------------------------------------------------------------- | #!/bin/sh | # | # svn-merge -- merge wrapper around ediff-mode for Subversion | # | # Copyright (c) 2006-2009 Giorgos Keramidas | # All rights reserved. | # | # Redistribution and use in source and binary forms, with or without | # modification, are permitted provided that the following conditions | # are met: | # 1. Redistributions of source code must retain the above copyright | # notice, this list of conditions and the following disclaimer. | # 2. Redistributions in binary form must reproduce the above copyright | # notice, this list of conditions and the following disclaimer in the | # documentation and/or other materials provided with the distribution. | # | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | # SUCH DAMAGE. | | if test $# -ne 4 ; then | echo >&2 "usage: `basename $0` BASE OTHER LOCAL MERGED" | exit 1 | fi | | # | # Subversion calls the program specified as `merge-tool-cmd' with four | # filename arguments: | # | # BASE = base revision; the common ancestor of $other and $local | # OTHER = parent branch version | # LOCAL = locally modified version | # WC = working copy; the file where merge results should be saved | # | # We have to take care to save these filename arguments, and then run | # ediff-mode with the right options. When the merge is finished, the | # working copy of the file at ${WC} should contain the merge results. | # | | BASE="$1" | OTHER="$2" | LOCAL="$3" | WC="$4" | | cleanup() { | # We failed. If ${RESTORE} is set, then we are supposed to have | # the pathname of a ${BACKUP} copy, and we should restore ${BACKUP} | # to ${LOCAL} and ${LOCAL} to ${WC} before dying. | | CLEANUP=true # Make sure we don't recurse forever. | | test -z "${RESTORE}" && return | test X"${RESTORE}" = X'yes' || return | | if test -z "${BACKUP}" || test -z "${LOCAL}" ; then | err 1 "internal merge script error." | fi | | cat "${BACKUP}" > "${LOCAL}" && rm "${BACKUP}" | if test $? -ne 0 ; then | err 1 "Cannot restore ${LOCAL} -- workspace may be *unclean*" | fi | | return 0 | } | | success() { | if test -z "${BACKUP}" || test -z "${LOCAL}" ; then | err 1 "internal merge script error." | fi | | # The merge was successful. Remove the backup copy of ${LOCAL}. | if test -n "${BACKUP}" ; then | /bin/rm -f "${BACKUP}" | fi | | # When the merge is successful, ediff has left the merge results | # into $LOCAL. Copy them over $WC to let `svn merge' know that | # we are done. | cat "${LOCAL}" > "${WC}" | if test $? -ne 0 ; then | err 1 "Cannot restore ${WC} -- workspace is *unclean*" | fi | } | | err() { | errcode=$1 | shift | echo >&2 "`basename $0`: error: $*" | if test -z "${CLEANUP}" ; then | cleanup | fi | exit $errcode | } | | # Set $EMACS_PROGRAM in the environment of the merge script to the name of | # the Emacs binary you want to use. If unset, it defaults to `emacs' and it | # also affects the default $EDITOR below. | EMACS_PROGRAM="${EMACS_PROGRAM:-emacs}" | export EMACS_PROGRAM | | # Since this script depends on manual edits being performed to the files being | # merged, make sure that ${EDITOR} is truly set to something, even if this is | # just plain good ol' vi(1). | EDITOR="${EDITOR:-${EMACS_PROGRAM}}" | export EDITOR | | # First make sure $TMPDIR points to a meaningful directory. We will be using | # this shell variable further down, so it's a good idea to make sure it isn't | # empty later on. | TMPDIR="${TMPDIR:-/var/tmp}" | export TMPDIR | | # We depend on diff3(1) being available to do the first pass of the merge, | # adding conflict markers around the areas that should be edited. | which diff3 >/dev/null 2>&1 | if test $? -ne 0 ; then | err 1 "No diff3(1) utility found in the current PATH." | fi | | # We will be using a temporary file with the diff3(1) output as the merge | # buffer, until either the merge removes all conflict markers from the working | # copy of the file or we fail somehow to complete the merge. | BACKUP=`mktemp "${TMPDIR}/svnmerge.$$.XXXXXX"` | if test $? -ne 0 ; then | err 1 "Cannot create backup file at ${TMPDIR}/hgmerge.$$.XXXXXX" | fi | | # Save a backup copy of the $LOCAL file version. | cp "${LOCAL}" "${BACKUP}" && RESTORE='yes' | rc=$? | if test $rc -ne 0 ; then | err 1 "Cannot create backup file at ${BACKUP}" | fi | | # If the remote and the local file have no differences, then there's | # nothing to merge. Accept both :) | if cmp "${LOCAL}" "${OTHER}" > /dev/null 2>&1 ; then | success | exit 0 | fi | | LABEL=`basename "${LOCAL}"` | diff3 -m \ | -L "${LABEL}" -L "${LABEL}.base" -L "${LABEL}.other" \ | "${BACKUP}" "${BASE}" "${OTHER}" > "${LOCAL}" | rc=$? | if test $rc -eq 0 ; then | # No conflicts found. Merge done. | success | exit 0 | elif test $rc -gt 1 ; then | err 1 "serious diff3 error, while trying to merge ${LOCAL}" | fi | | # In all other cases, diff3(1) has found conflicts, added the proper | # conflict markers to the ${LOCAL} file. Revert from the ${BACKUP} | # copy of the file, and spawn an emacs+ediff merge session. | # | # Editing the ${LOCAL} file "pollutes" the workspace area, but the filename | # shown in the editor buffer _really_ reflects the workspace path of the | # ${LOCAL} file, which is very helpful with editors (i.e. it lets the | # editor autopick the right 'mode' for editing the file, and so on). | cat "${BACKUP}" > "${LOCAL}" && \ | ${EMACS_PROGRAM} --eval "(ediff-merge-with-ancestor \"$BACKUP\" \"$OTHER\" \"$BASE\" nil \"$LOCAL\")" | if test $? -ne 0 ; then | err 1 "merge error for ${LOCAL}" | fi | | # When the editor exits, there should be no conflict markers in the | # ${LOCAL} copy of the filefile, otherwise we consider the merge failed. | if grep '^>>>> ORIGINAL' "${LOCAL}" >/dev/null 2>&1 ; then | err 1 "'^>>>> ORIGINAL' conflict markers" \ | "still found in the working-copy." \ | "Merge aborted for ${LOCAL}" | fi | if grep '^==== THEIRS' "${LOCAL}" >/dev/null 2>&1 ; then | err 1 "'^==== THEIRS' conflict markers" \ | "still found in the working-copy." \ | "Merge aborted for ${LOCAL}" | fi | if grep '^==== YOURS' "${LOCAL}" >/dev/null 2>&1 ; then | err 1 "'^==== YOURS' conflict markers" \ | "still found in the working-copy." \ | "Merge aborted for ${LOCAL}" | fi | if grep '^<<<<' "${LOCAL}" >/dev/null 2>&1 ; then | err 1 "'^<<<<' conflict markers" \ | "still found in the working-copy." \ | "Merge aborted for ${LOCAL}" | fi | | success # The merge has completed successfully. | exit 0 `----------------------------------------------------------------------- Now whenever `svn merge' prompts you for a conflict, you should be able to type `l' (the shortcut for `launch external merge tool') to fire up an Emacs instance in ediff's 3-way merge mode. When you save the merged buffer, the script will take care of saving the merged file in your workspace. Then you can type `r' (resolved) at the svn merge prompt, and you are done.