4889
Comment: Add a new diff3-based merge script (similar to [MergingWithEmacs])
|
6449
|
Deletions are marked like this. | Additions are marked like this. |
Line 2: | Line 2: |
Some people prefer editing conflicts within an editor, manually merging the areas that contain conflicts. To do this with [''Mercurial''], you can use a small wrapper-script around the GNU diff(3) utility that is described here. | Some people prefer editing conflicts within an editor, manually merging the areas that contain conflicts. To do this with ["Mercurial"], you can use a small wrapper-script around the GNU diff(3) utility that is described here. |
Line 5: | Line 5: |
The GNU diff3(1) utility can accept a list of three files, similar to the one [''Mercurial''] passed to a [:MergeProgram:merger program] and generate a copy of the local file with [:Conflict:conflict] markers. Typically, the output of diff3(1) looks like this: | The GNU diff3(1) utility can accept a list of three files, similar to the one ["Mercurial"] passed to a [:MergeProgram:merger program] and generate a copy of the local file with [:Conflict:conflict] markers. Typically, the output of diff3(1) looks like this: |
Line 134: | Line 134: |
=== A Simpler Version === {{{ #! /bin/sh # hgmerge.sh - invoke diff3 to merge files, save conflicts in mine.hgmerge # # author: Noel Burton-Krahn # created: Feb 28, 2007 # exit on error or undefined variables set -eu # command-line args mine="$1" orig="$2" theirs="$3" # where to save merged file merged="$mine".hgmerge rm -f "$merged" if diff3 -L mine -L original -L theirs -E -m "$mine" "$orig" "$theirs" > "$merged" then mv "$merged" "$mine" echo Merged "$mine" else echo Conflict saved in "'""$merged""'". Rename to $(basename "$mine") when fixed. exit 1 fi }}} === Version run under Windows 2000/XP === This script like pervious. It work with diff3 from !GnuWin32-0.6.19. It may be wrong if diff3 do not understand windows style path (for exemple on MSYS-1.0.10 and may be same on Cygwin). {{{ @echo off IF "%3"=="" GOTO err_arg_cnt IF NOT "%4"=="" GOTO err_arg_cnt IF NOT EXIST %1 GOTO err_file_not_exist IF NOT EXIST %2 GOTO err_file_not_exist IF NOT EXIST %3 GOTO err_file_not_exist SET my=%1 SET orig=%2 SET your=%3 SET merged=%my%.hgmerge IF EXIST "%merged%" (del /q "%merged%") diff3 -L my -L orig -L your -E -m "%my%" "%orig%" "%your%" > "%merged%" IF ERRORLEVEL 1 ( echo C %my% EXIT 1 ) ELSE ( echo M %my% move /y "%merged%" "%my%" ) EXIT 0 :err_arg_cnt @echo Wrong arg count! @echo You run: @echo ^> %0 %* @echo Must: @echo ^> %0 my orig your EXIT 1 :err_file_not_exist @echo One of '%*' files not exist. EXIT 1 }}} |
Merging Manually Using Your Favorite Editor
Some people prefer editing conflicts within an editor, manually merging the areas that contain conflicts. To do this with ["Mercurial"], you can use a small wrapper-script around the GNU diff(3) utility that is described here.
Wrapping GNU diff3(1)
The GNU diff3(1) utility can accept a list of three files, similar to the one ["Mercurial"] passed to a [:MergeProgram:merger program] and generate a copy of the local file with [:Conflict:conflict] markers. Typically, the output of diff3(1) looks like this:
A <<<<<<< local B - my local changes ||||||| base B ======= B - changes made by others >>>>>>> other C
To make diff3(1) generate a copy of the merged file with this sort of conflict markers and fire up an editor on this copy, you can save the following to a file in your PATH and turn on its execute bit:
if test $# -ne 3 ; then echo >&2 "usage: `basename $0` MYFILE OLDFILE YOURFILE" exit 1 fi # Keep a local copy of the filenames involved in the merge. LOCAL="$1" BASE="$2" OTHER="$3" cleanup() { if test -n "${TMPDIR}" && test -n "${WC}" ; then B=`dirname "${WC}"` TMPDIRPATH=`cd "${TMPDIR}"; pwd` WCPATH=`cd "${B}"; pwd` if test X"${B}" = X"${TMPDIR}" ; then /bin/rm -f "${WC}" fi fi } success() { if test -z "${WC}" || test -z "${LOCAL}" ; then err 1 "internal merge script error." fi # The merge was successful. Copy back the merged file on top of ${LOCAL} cp "${WC}" "${LOCAL}" && /bin/rm "${WC}" if test $? -ne 0 ; then err 1 "Failed to save merged file at ${LOCAL}" fi } err() { errcode=$1 shift echo >&2 "`basename $0`: error: $*" cleanup exit $errcode } # 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:-vi}" 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 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. WC=`mktemp "${TMPDIR}/hgmerge-XXXXXX"` if test $? -ne 0 ; then err 1 "Cannot create temporary file at ${TMPDIR}/hgmerge-XXXXXX" fi # 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 # First try to add conflict markers around the areas that need special # attention in the ${LOCAL} file. The output is not saved directly over the # file that is currently in-conflict, but is saved in the ${WC} temporary file # to allow editing of the conflict regions without diff3 -m "${LOCAL}" "${BASE}" "${OTHER}" > "${WC}" 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 merhge ${LOCAL}" fi # In all other cases, diff3(1) has found conflicts, added the proper conflict # markers to the ${WC} file and we should now edit this file. Fire up an # editor with the ${WC} file and let the user manually resolve the conflicts. # When the editor exits successfully, there should be no conflict markers in # the ${WC} file, otherwise we consider this merge failed. ${EDITOR} "${WC}" if test $? -ne 0 ; then err 1 "merge error for ${LOCAL}" fi if grep '^<<<<<<<' "${WC}" >/dev/null 2>&1 || grep '^|||||||' "${WC}" >/dev/null 2>&1 || grep '^=======' "${WC}" >/dev/null 2>&1 || grep '^>>>>>>>' "${WC}" >/dev/null 2>&1 ; then err 1 "conflict markers still found in the working-copy. Merge aborted for ${LOCAL}" fi success exit 0
How the script works
This script tries first to automatically merge the files using the GNU diff3(1) utility program. If the automatic merge fails because there are conflicts, then an editor is launched on the output of diff3(1) to let you manually resolve the conflicts. When the editor is done modifying the resolved file, the script checks again to make sure that all conflict markers have been removed now. If there are still some conflict markers, the merge fails. Re-running the merge should be ok.
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 hg-merge):
[ui] merge = hg-merge
A Simpler Version
# hgmerge.sh - invoke diff3 to merge files, save conflicts in mine.hgmerge # # author: Noel Burton-Krahn # created: Feb 28, 2007 # exit on error or undefined variables set -eu # command-line args mine="$1" orig="$2" theirs="$3" # where to save merged file merged="$mine".hgmerge rm -f "$merged" if diff3 -L mine -L original -L theirs -E -m "$mine" "$orig" "$theirs" > "$merged" then mv "$merged" "$mine" echo Merged "$mine" else echo Conflict saved in "'""$merged""'". Rename to $(basename "$mine") when fixed. exit 1 fi
Version run under Windows 2000/XP
This script like pervious. It work with diff3 from GnuWin32-0.6.19. It may be wrong if diff3 do not understand windows style path (for exemple on MSYS-1.0.10 and may be same on Cygwin).
@echo off IF "%3"=="" GOTO err_arg_cnt IF NOT "%4"=="" GOTO err_arg_cnt IF NOT EXIST %1 GOTO err_file_not_exist IF NOT EXIST %2 GOTO err_file_not_exist IF NOT EXIST %3 GOTO err_file_not_exist SET my=%1 SET orig=%2 SET your=%3 SET merged=%my%.hgmerge IF EXIST "%merged%" (del /q "%merged%") diff3 -L my -L orig -L your -E -m "%my%" "%orig%" "%your%" > "%merged%" IF ERRORLEVEL 1 ( echo C %my% EXIT 1 ) ELSE ( echo M %my% move /y "%merged%" "%my%" ) EXIT 0 :err_arg_cnt @echo Wrong arg count! @echo You run: @echo ^> %0 %* @echo Must: @echo ^> %0 my orig your EXIT 1 :err_file_not_exist @echo One of '%*' files not exist. EXIT 1