goose
Vladimir Marek
Vladimir.Marek at Sun.COM
Sat Nov 24 08:13:53 UTC 2007
Hi,
I often find myself in a situation, where I do have patch against some
program, but can't or don't want to push the patch back to upstream
repository. At the same time I would like to keep my patch updated with
every upstream change, so that the updates are as small as possible. I
used mercurial queues plus this my wrapper to automate the task. It's
made to fulfill my needs, but I'm open to discussion.
Enjoy
--
Vlad
-------------- next part --------------
#!/usr/bin/ksh
# goose
# =====
#
# Made by: Vladimir.Marek at Sun.COM
#
# So far this script is made to help me in may day to day work, but if there
# will be interest, I'll create it's own support alias and homepage. I do
# welcome any comments.
#
# Script used to keep your patches up to date against upstream changes.
#
# Whole thing is based on Mercurial and Mercurial queues. For the script
# operation we need two Mercurial workspaces:
#
# incoming_ws: This is where the upstream changes are converted to Mercurial
# patches_ws: Clone of incoming_ws, here we keep our patches in mq
#
# The work is done in several steps:
# 1) bring incoming_ws up to date and convert it to Mercurial
# 2) synchronize patches_ws with incoming_ws and refresh all patches
# 3) after successful refresh, optionally run some command (can be some test,
# package build, etc.)
#
# If anything fails, you are notified about what failed. Goose can send emails,
# so you can run it from cron. The intended usage is to have goose silently
# merge your patches with upstream as long as possible, only disturbing you
# when the merge (or the optional build or test) fails. Also any potential
# change to your patches due to automatic merging is hg qsaved. This means that
# you will not loose any work because of silent mismerge and moreover you will
# be able to easilly find which change in incoming_ws provoked modification to
# your patches.
#
# ad 1)
# -----
# If incoming_ws is svn, cvs, hg or teamware, goose can itself update the
# workspace and convert the changes to Mercurial. Best is hg, as there is no
# work, just "hg up". Second is svn, where goose can convert each "svn commit"
# to single "hg commit". Cvs and TeamWare are bad, as I do something like "cvs
# up; hg commit --addremove".
# You can also take care of this step yourself, just make sure you keep
# incoming_ws up-to-date and managed by hg. In such case pass option
# '-nocontrol', which tels goose to skip on any updating of incoming_ws.
# When you pass any directory as incoming_ws, but is not managed by Mercurial,
# goose will convert it. Again, goose knows few other systems (svn, cvs,
# TeamWare) and do the right thing. If you want to do it by hand, just convert
# it before running mercurial first time.
#
# ad 2)
# -----
# Goose tries to apply your mq patches to every single incoming_ws changeset.
# This ensures that the merging is as small as possible. If automatic merging
# fails, you are notified about it with instructions what to do now. It is
# recommended to use 'nodate' style patches, since otherwise mq qrefresh
# creates different patches every time, even if there are no real changes. When
# goose detects that you are not using 'nodate' patches, it will try to use
# external utility "sterilize_qrefresh", which cleans up the unnecessary
# differences.
#
# ad 3)
# -----
# The command specified in -after has to return with code 0, otherwise it's
# treated as some sort of error and reported to you.
#
# For commandline and examples run "goose --help"
# TODO
# - better incoming sync (done for svn)
# - use hg convert to manage repo conversion ?
# - proper status in .hg/goose/...
# - don't start two processes in parallel (user/cron)
# - easilly find out which state the repository is at (needs merging, etc.)
# - run -after script only if patches_ws changed
set -u
alias debug=''
alias error=report_error_screen
ORIGINAL_PWD=$(pwd)
GOOSE_REFRESH_MQ=''
GOOSE_USAGE=''
GOOSE_NO_CONTROL=''
GOOSE_AFTER_COMMAND=''
VERSION=4
set -A SETUP_VARIABLE_NAME
set -A SETUP_VARIABLE_VALUE
trap '[ -n "$GLOBAL_GOOSE_TEMP" ] && rm -rf "$GLOBAL_GOOSE_TEMP"' EXIT INT TERM
GLOBAL_GOOSE_TEMP=$( mktemp -d )
export GLOBAL_GOOSE_TEMP
while [ $# -gt 0 ]; do
case "$1" in
-h|-help|--help)
GOOSE_USAGE='yes'
;;
-m|--mail)
alias error=report_error_mail
;;
-mq)
GOOSE_REFRESH_MQ='yes'
;;
-after)
GOOSE_AFTER_COMMAND="$2"
shift
;;
-nocontrol)
GOOSE_NO_CONTROL='yes'
;;
-d*)
alias debug="set -x"
file=${1#??}
[ -n "$file" ] && exec 2>"$file"
;;
*)
break;
;;
esac
shift
done
debug
#-----
##
## @function add_setup_variable
##
## @desc Registers variable to goose. This complex evnironment variable
## handling is here, so that later we can display all variable values in --help
##
## @param $1 - variable name
## @param $2 - default value
##
#-----
function setup_variable
{
debug
SETUP_VARIABLE_NAME[${#SETUP_VARIABLE_NAME[*]}]="$1"
SETUP_VARIABLE_VALUE[${#SETUP_VARIABLE_VALUE[*]}]="$2"
}
#-----
##
## @function environment_display
##
## @desc Display all registered variables value and default value
##
#-----
function environment_display
{
debug
local CNT=0
while [ "${#SETUP_VARIABLE_NAME[*]}" -ne "$CNT" ]; do
local NAME=${SETUP_VARIABLE_NAME[$CNT]}
eval "echo $NAME = \$$NAME"
let CNT=$CNT+1
done
}
#-----
##
## @function environment_setup
##
## @desc Go thgrough all registered vriables and fill them with default if they are not set
##
#-----
function environment_setup
{
debug
local CNT=0
while [ "${#SETUP_VARIABLE_NAME[*]}" -ne "$CNT" ]; do
local NAME=${SETUP_VARIABLE_NAME[$CNT]}
local VAL=${SETUP_VARIABLE_VALUE[$CNT]}
eval "$NAME=\"\${$NAME-\"$VAL\"}\""
let CNT=$CNT+1
done
}
setup_variable GOOSE_HG hg
setup_variable GOOSE_CVS cvs
setup_variable GOOSE_SVN svn
setup_variable STERILIZE_QREFRESH sterilize_qrefresh.pl
setup_variable GOOSE_EMAIL ''
environment_setup
#-----
##
## @function report_error_mail
##
## @desc Report error to email and exit
##
## @param $1 - Short error description
## @param STDIN - error text
##
#-----
function report_error_mail
{
debug
local TEMP=$GLOBAL_GOOSE_TEMP/mail
cat > "$TEMP"
local SUBJECT="goose - empty subject"
[ -n "$1" ] && SUBJECT="$1"
cat "$TEMP" | mailx -r "$GOOSE_EMAIL" -s "$1" "$GOOSE_EMAIL"
echo "Goose Error (sent by mail to $GOOSE_EMAIL): $1"
cat "$TEMP"
exit
}
#-----
##
## @function report_error_screen
##
## @desc Report error to screen and exit
##
## @param $1 - Short error description
## @param STDIN - error text
##
#-----
function report_error_screen
{
(
debug
echo "Goose Error: $1"
cat
)
exit
}
#-----
##
## @function error_short
##
## @desc Report error and exit. No error text on stdin (as oposed to error only)
##
## @param $1 - Error text
##
#-----
function error_short
{
debug
/bin/true | error "$1"
}
#-----
##
## Identify what repository type is at given path
##
## @param path (when not present, CWD is used)
##
#-----
function get_repo_type
{
debug
local REPO="${1-"$(pwd)"}/"
if [ -d "${REPO}CVS" ]; then
echo cvs
return
fi
if [ -d "${REPO}Codemgr_wsdata" ]; then
echo teamware
return
fi
if [ -d "${REPO}.svn" ]; then
echo svn
return
fi
if [ -d "${REPO}.hg" ]; then
echo hg
return
fi
echo none
}
#-----
##
## @function teamware_update
##
## @desc Do teamware update in current directory; bringover
##
#-----
function teamware_update
{
debug
$GOOSE_CVS -q up -dR
}
#-----
##
## @function cvs_update
##
## @desc Do cvs update in current directory
##
#-----
function cvs_update
{
debug
$GOOSE_CVS -q up -dR
}
#-----
##
## @function svn_update
##
## @desc Do svn update in current directory
##
#-----
function svn_update
{
debug
$GOOSE_SVN up
}
#-----
##
## @function hg_update
##
## @desc Do hg update in current directory
##
#-----
function hg_update
{
debug
$GOOSE_HG pull -u
}
#-----
##
## @function hg_none_update
##
## @desc Update hg managed directory. Convert it to hg if it's the first time
##
#-----
function hg_none_update
{
debug
set -e
init_incoming_repository
$GOOSE_HG addremove
$GOOSE_HG ci -m "* update $(date +'%Y-%m-%d %H-%M-%S')" >/dev/null
}
#-----
##
## @function hg_cvs_update
##
## @desc Update hg manged cvs repo in current path. Convert it to hg if it's
## the first time
##
#-----
function hg_cvs_update
{
debug
set -e
init_incoming_repository
cvs_update
$GOOSE_HG addremove
$GOOSE_HG ci -m "* cvs up $(date +'%Y-%m-%d %H-%M-%S')" >/dev/null
}
#-----
##
## @function hg_svn_update
##
## @desc Update hg manged svn repo in current path. Convert it to hg if it's
## the first time
##
#-----
function hg_svn_update
{
debug
set -e
init_incoming_repository
local SRC_REV=$( svn info . | grep ^Revision: | sed -e 's/.* //' )
svn_update
local DST_REV=$( svn info . | grep ^Revision: | sed -e 's/.* //' )
if [ "$SRC_REV" -eq "$DST_REV" ]; then
return
fi
local TEMP=$GLOBAL_GOOSE_TEMP/svn_update
local MSG=$GLOBAL_GOOSE_TEMP/msg
svn log --xml -r $SRC_REV:HEAD |
perl -e '$_=join "", <>; while (m/<logentry\s+revision="(\d+)"/sg) { print "$1\n" }' |
while read LINE; do
set -e
[ "$LINE" = "$SRC_REV" ] && continue
svn up -r "$LINE"
svn log -r "$LINE" > $TEMP
local AUTHOR=$( cat $TEMP | tail +2 | head -1 | sed -e 's/^[^|]*| //' -e 's/ .*//' )
[ -z "$AUTHOR" ] && error_short "hg_svn_update: no author found"
local DATE=$( cat $TEMP | tail +2 | head -1 | sed -e 's/^[^|]*|[^|]*| //' -e 's/ (.*//' )
[ -z "$DATE" ] && error_short "hg_svn_update: no date found"
cat $TEMP | perl -e '
@x=<>;
@x=splice (@x, 3, -1); # remove everything apart message
while ($x[-1]=~m/^\r?\n$/s) { pop @x }; # remove empty lines at the end
print @x' > $MSG
echo "\nOriginal svn revision: $LINE" >> $MSG
$GOOSE_HG addremove
$GOOSE_HG ci -u $AUTHOR -d "$DATE" -l $MSG
done || error_short "Error in hg_svn_update"
}
#-----
##
## @function hg_teamware_update
##
## @desc Update hg manged teamware repo in current path. Convert it to hg if
## it's the first time
##
#-----
function hg_teamware_update
{
debug
set -e
init_incoming_repository
teamware_update
$GOOSE_HG addremove
$GOOSE_HG ci -m "* bringover $(date +'%Y-%m-%d %H-%M-%S')" >/dev/null
}
#-----
##
## @function hg_hg_update
##
## @desc Update hg repo in current path.
##
#-----
function hg_hg_update
{
debug
set -e
if [ -n "$GOOSE_NO_CONTROL" ]; then
$GOOSE_HG addremove
$GOOSE_HG ci -m "* goose update $(date +'%Y-%m-%d %H-%M-%S')" >/dev/null
return
fi
if ! $GOOSE_HG showconfig | grep paths.default > /dev/null; then
# No parent defined
return
fi
$GOOSE_HG pull -u
}
#-----
##
## @function update_incoming
##
## @desc Update incoming repository at given path
##
## @param $1 - incoming repository path
##
#-----
function update_incoming
{
debug
cd "$1" &&
local TYPE=$(get_repo_type)
if [ -n "$TYPE" ]; then
eval hg_${TYPE}_update
fi
}
#-----
##
## @function is_mq_clean
##
## @desc Checks that mercurial patches queue is clean. If there is no mq, it
## returns succesfully
##
## @param $1 - temporary file
##
#-----
function is_mq_clean
{
debug
if [ -d ".hg/patches" ]; then
cd .hg/patches &&
is_workspace_clean "$1" &&
cd ../.. &&
return
fi
}
#-----
##
## Checks if CWD mercurial workspace is 'clean'. That means no local changes
##
## @param $1 - temporary file
##
#-----
function is_workspace_clean
{
debug
if [ -z "$1" ]; then
error_short "is_workspace_clean: Error, no directory specified"
fi
local CLEAN_TEMP="$1"
$GOOSE_HG status >"$CLEAN_TEMP" 2>&1 &&
# Exit if there are some conflicts
if [ $( cat "$CLEAN_TEMP" | wc -l ) != "0" ]; then
echo "hg status not clean"
return 1
fi
if [ -e ".hg/patches.1" ]; then
echo ".hg/patches.1 exists"
return 1
fi
local SIZE=$( find .hg/patches/series -size 0 2>/dev/null )
if [ -n "$SIZE" ]; then
# hg qpush returns error when there are no patches
return 0
fi
$GOOSE_HG qpop -a >"$CLEAN_TEMP" 2>&1
if ! $GOOSE_HG qpush -a >"$CLEAN_TEMP" 2>&1; then
echo "qpush -a does not work"
return 1
fi
return 0
}
#-----
##
## @param $1 - incoming repository path
##
## a) Create apropriate .hgignore
## b) Do first commit
##
#-----
function init_incoming_repository
{
debug
set -e
[ -d .hg ] && return
if [ -n "$GOOSE_NO_CONTROL" ]; then
$GOOSE_HG init .
$GOOSE_HG addremove
$GOOSE_HG ci -m "Initial goose Hg setup"
return
fi
if [ -d CVS ]; then
echo 'syntax: regexp' > .hgignore
echo '(^|/)CVS($|/)' >> .hgignore
$GOOSE_HG init .
$GOOSE_HG addremove
$GOOSE_HG ci -m "Initial goose Hg setup for CVS"
return
fi
if [ -d .svn ]; then
echo 'syntax: regexp' > .hgignore
echo '(^|/).svn($|/)' >> .hgignore
local REV=$( svn info . | grep ^Revision: | sed -e 's/.* //' )
local DATE=$( svn log -r $REV | tail +2 | head -1 | sed -e 's/^[^|]*|[^|]*| //' -e 's/ (.*//' )
$GOOSE_HG init .
$GOOSE_HG addremove
$GOOSE_HG ci -d "$DATE" -m "Initial goose Hg setup for SVN; revision = $REV"
return
fi
if [ -d Codemgr_wsdata ]; then
echo 'syntax: regexp' > .hgignore
echo '(^|/)Codemgr_wsdata($|/)' >> .hgignore
echo '(^|/)SCCS($|/)' >> .hgignore
$GOOSE_HG init .
$GOOSE_HG addremove
$GOOSE_HG ci -m "Initial goose Hg setup for TeamWare"
return
fi
error_short "Unknown repository type at $(pwd). Perhaps you need -nocontrol parameter ?"
}
#-----
##
## @function commit_patches
##
## @desc commits queue state
##
## @param $1 - patches repository path
##
#-----
function commit_patches
{
debug
cd "$1"
$GOOSE_HG qpop -a
local TIP=$( $GOOSE_HG tip --template '{rev}\n')
$GOOSE_HG qcommit --addremove -m "$TIP $(date +'%Y-%m-%d %H-%M-%S')"
}
#-----
##
## @function refresh_patches
##
## @desc Goes through all patches in queue and does refresh only when necessary
##
## @param $1 - patches repostory path
##
#-----
function refresh_patches
{
debug
set -e
local TEMP=$GLOBAL_GOOSE_TEMP/refresh_patches
cd "$1"
$GOOSE_HG qpop -a >> "$TEMP" 2>&1
while $GOOSE_HG qpush >> "$TEMP" 2>&1; do
$GOOSE_HG qrefresh >> "$TEMP" 2>&1
done
cd "$1/.hg/patches"
if ! $GOOSE_HG showconfig | grep ^diff.nodates &> /dev/null
then
echo "diff.nodates is not set in hgrc, I'm running sterilize_qrefresh ($STERILIZE_QREFRESH)"
$GOOSE_HG diff | $STERILIZE_QREFRESH 2>&1 >> "$TEMP"
fi
# Now check if we still apply cleanly
cd "$1"
$GOOSE_HG qpop -a 2>> "$TEMP" 2>&1
$GOOSE_HG qpush -a >> "$TEMP" 2>&1
is_workspace_clean "$TEMP" && return
( echo "Refreshing patches in "`pwd`" did not work as expected.\n\n"; cat "$TEMP" ) | error "Workspace '$1' mq refresh failed"
}
#-----
##
## @function create_patches_repository
##
## @desc create patches repository if there is not already one
##
## @param $1 - incoming repository path
## @param $2 - patches repository path
##
#-----
function create_patches_repository
{
debug
set -e
cd "$ORIGINAL_PWD"
if ! [ -e "$2" ]; then
# If we are asked to sync with non-existing clone, create it first
$GOOSE_HG clone "$1" "$2" &&
$GOOSE_HG --cwd "$2" qinit -c &&
return
error_short "Creating patches repo failed"
fi
}
#-----
##
## @function update_patches_setup
##
## @desc update patches repository from incoming repository. Start merging process also
##
## @param $1 - patches repository path
## @param $2 - do microstep update if nonempty string
##
#-----
function update_patches_setup
{
debug
local TEMP=$GLOBAL_GOOSE_TEMP/update_patches_setup_1
local TEMP2=$GLOBAL_GOOSE_TEMP/update_patches_setup_2
cd "$1" &&
# Exit if there are some conflicts
local REASON
REASON=$( is_workspace_clean "$TEMP" ) ||
( echo "Workspace "`pwd`" is not in clean state:$REASON"; cat "$TEMP" ) | error "Workspace '$1' is not clean"
REASON=$( is_mq_clean "$TEMP" ) ||
( echo "Workspace's MQ at "`pwd`" is not in clean state:$REASON"; cat "$TEMP" ) | error "Workspace '$1' is not clean"
$GOOSE_HG qpop -a >> "$TEMP" 2>&1
if $GOOSE_HG incoming | grep "no changes found" > /dev/null; then
# No changes
return
fi
$GOOSE_HG qpush -a >> "$TEMP" 2>&1
$GOOSE_HG qsave -e -c >> "$TEMP" 2>&1
if [ -z "$2" ]; then
# whole change
$GOOSE_HG pull >> "$TEMP" 2>&1
else
# microstep
local REV
REV=$( $GOOSE_HG incoming --newest-first --template '{rev}\n' | tail -1 )
if [ -z "$REV" ]; then
error_short "Can't get microstep revision"
fi
$GOOSE_HG pull --rev "$REV" >> "$TEMP" 2>&1
fi
$GOOSE_HG update -C tip >> "$TEMP" 2>&1
touch "$1/.hg/goose_in_merging"
}
#-----
##
## @function update_patches_run
##
## @desc Run the actual merging work. If merge failed, this is the point where to continue
##
## @param $1 - patches repository
##
#-----
function update_patches_run
{
debug
local TEMP=$GLOBAL_GOOSE_TEMP/update_patches_run_1
cd "$1"
HGMERGE=/bin/false $GOOSE_HG qpush -m -a >> "$TEMP" && return
(
echo "Workspace "`pwd`" Needs manual intervention:\n\n";
cat "$TEMP";
echo "\n\nTO MANUALLY MERGE:\ncd $1\n$GOOSE_HG qpush -m\n... resolve conflicts\n$0 -mq '' $1\n"
) | error "Workspace '$1' needs merging"
}
#-----
##
## @function update_patches_finish
##
## @desc Cleanup after mq merging
##
## @param $1 - patches repository
##
#-----
function update_patches_finish
{
debug
local TEMP=$GLOBAL_GOOSE_TEMP/UPDATE_PATCHES_FINISH
cd "$1"
if [ $( $GOOSE_HG qunapplied | wc -l ) != "0" ]; then
( echo "Workspace "`pwd`": not all patches pushed\n\n"; cat "$TEMP" ) | error "Workspace '$1': not all patches pushed"
fi
set -e
$GOOSE_HG qpop -a >> "$TEMP"
$GOOSE_HG qpop -a -n patches.1 >> "$TEMP"
rm -rf ".hg/patches.1"
rm ".hg/goose_in_merging"
}
#-----
##
## @function full_path
##
## @desc Return absolute path of a directory for the given relative path
## If $1 is empty, return empty
##
## @param $1 - directory path
##
#-----
function full_path
{
debug
[ -z "$1" ] && return
cd "$1" || error_short "full_path: '$1' does not exist or is not directory"
pwd
}
#-----
##
## @function have_outstanding_changes
##
## @desc returns true if hg pull will get some changes from parent
##
## @param $1 - hg repository path to be considered updating
##
#-----
function have_outstanding_changes
{
debug
cd "$1"
$GOOSE_HG incoming --template '{rev}\n' | grep "no changes found" >/dev/null && return 1
return 0
}
#-----
##
## @function do_update_incoming
##
## @desc Update incoming repository. If patches is empty, don't convert it to hg
##
## @param $1 - incoming repository path
## @param $2 - patches repository path
##
#-----
function do_update_incoming
{
debug
local INCOMING="$1"
# If INCOMING is empty, don't update from official gate
if [ -n "$INCOMING" ]; then
if [ $# = 1 ]; then
# Simple update
export TYPE=$(get_repo_type "$INCOMING")
[ -n "$TYPE" ] && (
cd "$INCOMING"
eval ${TYPE}_update
)
return
fi
update_incoming "$INCOMING"
create_patches_repository "$@"
fi
}
#-----
##
## @function do_update_patches
##
## @desc Update patches repository. Main work is done here
##
## @param $1 - path to patches repository
##
#-----
function do_update_patches
{
debug
[ -z "$1" ] && return
local PATCHES
PATCHES=$(full_path "$1")
if [ -e "$PATCHES/.hg/goose" ]; then
( echo "Workspace "`pwd`" is locked\n$PATCHES/.hg/goose exists\n" ) | error "Workspace '$1' is locked"
fi
while [ -e "$PATCHES/.hg/goose_in_merging" ] || have_outstanding_changes "$PATCHES" || [ -n "$GOOSE_REFRESH_MQ" ]; do
if [ -n "$PATCHES" ]; then
if [ -z $GOOSE_REFRESH_MQ ]; then
if ! [ -e "$PATCHES/.hg/goose_in_merging" ]; then
# update_patches_setup has to be run only once, even that several microsteps will be processed
# Also don't run setup if we are just merging (-mq)
update_patches_setup "$PATCHES" 'microstep'
fi
fi
update_patches_run "$PATCHES"
update_patches_finish "$PATCHES"
refresh_patches "$PATCHES"
commit_patches "$PATCHES"
if [ -n "$GOOSE_REFRESH_MQ" ]; then
# We have been just merging, don't loop over more work and return
return
fi
fi
done
}
#-----
##
## @function do_update_after
##
## @desc Run specified command after all updating is done
##
## @param $1 - incoming_ws
## @param $2 - patches_ws
##
#-----
function do_update_after
{
debug
[ -z "$GOOSE_AFTER_COMMAND" ] && return
local TEMP=$GLOBAL_GOOSE_TEMP/after
local INCOMING="$1"
local PATCHES=""
if [ $# = 1 ]; then
# Just incoming is defined
[ -z "$INCOMING" ] && error "-after: incoming_ws has to be defined if patches_ws is not"
cd "$INCOMING"
else
[ -z "$2" ] && error "-after: patches_ws can not be empty when defined"
PATCHES="$(full_path "$2")"
cd "$PATCHES"
$GOOSE_HG qpush -a
fi
set +e
( $GOOSE_AFTER_COMMAND "$INCOMING" "$PATCHES" 2>&1 ) 2>&1 > "$TEMP"
RET=$?
set -e
if ! [ $RET = 0 ]; then
cat "$TEMP" | error "Error in -after script for workspace $(pwd)"
fi
}
#-----
##
## @param $1 - incoming repository
## @param $2 - patch repository (optional)
##
## When $2 is missing, we just update incoming repository (like cvs up)
##
## Otherwise we
## a) Update incoming repository
## b) Update Hg for incoming repo. (unless incoming repo. is already Hg)
## c) If patches repo is not clean, exit
## d) Update patches repository
## e) If there are conflicts, help resolve them
##
#-----
function do_update
{
debug
set -e
# Replace $1 by it's full path
local INCOMING
INCOMING="$(full_path "$1")"
shift
if [ $# = 0 ]; then
set -- "$INCOMING"
else
set -- "$INCOMING" "$@"
fi
do_update_incoming "$@"
do_update_patches "${2-}"
do_update_after "$@"
}
#-----
##
## @function usage
##
## @desc Usage
##
#-----
function usage
{
name=$(basename "$0")
cat <<EOT
$name [options] incoming_ws [patches_ws]
Version: $VERSION
options:
-h|--help - this help
-m|--mail - report errors by email
-d - produce debug output
-dfile - produce debug output and store it to file
-mq - just refresh mq
-nocontrol - incoming repository is not under version control
-after cmd - run following command after successfull merge
- the command is run from patches_ws dir
- if patches_ws is empty string, after command is not run
- if patches_ws is not defined, after command is run from incoming_ws
- all mq patches are applied first
$name incoming - update only incoming directory
$name incoming patches - update incoming, patches, and sync your changes
environment variables:
EOT
environment_display
cat <<EOT
Examples:
=========
Detect versioning system and do update:
$name incoming_ws
Detect versioning system, do update and convert to hg:
$name incoming_ws ''
Detect versionoing system, do update, convert to hg and update patches:
$name incoming_ws patches_ws
Don't let goose update incoming repository
cd incoming_ws
p4 sync # do whatever you need to bring the incoming up to date
convert_to_hg.sh # do whatever you need to convert the changes to hg
# now ask $name to skip any management of incoming repository
$name -nocontrol incoming_ws patches_ws
If updating patches resulted in colision, merge it by hand:
cd patches_ws # enter the workspace
hg qpush -m # initiate merge. External tool will be launched
$name -mq '' . # finish the merge work by commiting mq state
# now continue in automatic patches updating to process
# any remaining changes
$name incoming_ws patches_ws
EOT
}
#-----
##
## @function main
##
## @param $1 - incoming repository
## @param $2 - patches repository
##
#-----
local CNT=0
if [ -n "$GOOSE_USAGE" ] || [ $# = 0 ]; then
usage
exit
fi
do_update "$@"
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 185 bytes
Desc: not available
URL: <http://lists.mercurial-scm.org/pipermail/mercurial/attachments/20071124/3ab0804b/attachment-0001.asc>
More information about the Mercurial
mailing list