merging with hg(1)
Giorgos Keramidas
keramida at ceid.upatras.gr
Tue Jan 22 19:13:24 EET 2008
Καλησπέρα,
όσοι χρησιμοποιείται το Mercurial για merges με το hg.hellug.gr μπορεί
να βρείτε χρήσιμο το παρακάτω script. Το χρησιμοποιώ εδώ και καιρό εγώ
βάζοντας στο ``~/.hgrc'' μου κάτι σαν:
[ui]
merge = /home/keramida/bin/hg-merge
Μια σημαντική αλλαγή που έκανα πρόσφατα είναι ότι αν σε ένα commit
υπάρχουν merges/revisions που είναι 100% πανομοιότυπα, π.χ.:
[ancestor] --> [keramida]
\
`---> [manolis]
Τότε το merge κάνει αυτόματα 'resolve', θεωρώντας ότι το να είναι ίδια
τα αρχεία στο [keramida] και [manolis] είναι αρκετά καλή ένδειξη ότι δεν
χρειάζεται να γίνει merge τίποτα, π.χ.:
- Δημιουργία ενός `master' με ένα `foo' file:
% cd /tmp
% mkdir hgdemo
% cd hgdemo
% hg init master
% cd master
% echo foo > foo
% hg add
adding foo
% hg ci -m 'add foo'
% cd ..
% ls -la
total 6
drwxrwxr-x 3 keramida wheel - 512 Jan 22 19:01 .
drwxrwxrwt 12 root wheel - 512 Jan 22 19:01 ..
drwxrwxr-x 3 keramida wheel - 512 Jan 22 19:01 master
- Δημιουργία 2 clones με ακριβώς την ίδια αλλαγή, αλλά όχι το ίδιο
changeset id:
% hg clone master keramida
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
% hg clone master manolis
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
% cd manolis
% echo foobar > foo
% hg ci -m 'modify foo'
% cd /tmp/hgdemo/keramida/
% echo foobar > foo
% hg ci -m 'modify foo (duplicate)'
% hg heads --template '{rev}:{node|short} {tags}| {author|email} | {desc|strip|firstline}\n'
1:cd54f17c35c3 tip| keramida at ceid.upatras.gr | modify foo (duplicate)
% hg pull ../manolis
pulling from ../manolis
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 0 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
% hg heads --template '{rev}:{node|short} {tags}| {author|email} | {desc|strip|firstline}\n'
2:038803e77c9f tip| keramida at ceid.upatras.gr | modify foo
1:cd54f17c35c3 | keramida at ceid.upatras.gr | modify foo (duplicate)
Επειδή οι *content* αλλαγές είναι ακριβώς οι ίδιες, στα δύο heads (1)
και (2), το επόμενο βήμα δεν ξεκινάει κάποιο editor για manual merge:
% hg up --clean 1
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
% hg merge
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
% hg st
% hg parent --template '{rev}:{node|short} {tags}| {author|email} | {desc|strip|firstline}\n'
1:cd54f17c35c3 | keramida at ceid.upatras.gr | modify foo (duplicate)
2:038803e77c9f tip| keramida at ceid.upatras.gr | modify foo
% hg ci -m 'merge from manolis (no conflicts)'
%
Αλλά το merge έχει γίνει κανονικά:
% hg glog
@ changeset: 3:45fe20157a6f
|\ tag: tip
| | parent: 1:cd54f17c35c3
| | parent: 2:038803e77c9f
| | user: Giorgos Keramidas <keramida at ceid.upatras.gr>
| | date: Tue Jan 22 19:03:19 2008 +0200
| | summary: merge from manolis (no conflicts)
| |
| o changeset: 2:038803e77c9f
| | parent: 0:30961d810b03
| | user: Giorgos Keramidas <keramida at ceid.upatras.gr>
| | date: Tue Jan 22 19:02:04 2008 +0200
| | summary: modify foo
| |
o | changeset: 1:cd54f17c35c3
|/ user: Giorgos Keramidas <keramida at ceid.upatras.gr>
| date: Tue Jan 22 19:02:21 2008 +0200
| summary: modify foo (duplicate)
|
o changeset: 0:30961d810b03
user: Giorgos Keramidas <keramida at ceid.upatras.gr>
date: Tue Jan 22 19:01:36 2008 +0200
summary: add foo
%
Το `hg-merge' script που έχω τοπικά εγώ τώρα είναι:
----- start of script -----------------------------------------------------
#!/bin/sh
#
# hg-merge -- merge wrapper for the Mercurial version-control system
#
# Copyright (c) 2006 Giorgos Keramidas <keramida at FreeBSD.org>
# 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 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() {
# 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} 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 is *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
}
err() {
errcode=$1
shift
echo >&2 "`basename $0`: error: $*"
if test -z "${CLEANUP}" ; then
cleanup
fi
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 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}/hgmerge-XXXXXX"`
if test $? -ne 0 ; then
err 1 "Cannot create backup file at ${TMPDIR}/hgmerge-XXXXXX"
fi
LABEL=`basename "${LOCAL}"`
# First try to add conflict markers around the areas that need special
# attention in the ${LOCAL} file.
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}" ; then
success
exit 0
fi
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 and we should now edit this file.
#
# 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).
#
# When the editor exits successfully, there should be no conflict markers
# in the ${LOCAL} file, otherwise we consider this merge failed.
${EDITOR} "${LOCAL}"
if test $? -ne 0 ; then
err 1 "merge error 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
if grep '^>>>>>>>' "${LOCAL}" >/dev/null 2>&1 ; then
err 1 "'^>>>>>>>' conflict markers" \
"still found in the working-copy." \
"Merge aborted for ${LOCAL}"
fi
success
exit 0
----- end of script -------------------------------------------------------
More information about the Freebsd-doc-el
mailing list