Automated workspace updates with Mercurial
06 Sep 2007, 15:34 by Giorgos KeramidasWhen using a distributed SCM, it is often very useful to be able to automatically sync a “reference” workspace with a remote, “parent” workspace. This way, even when offline, a local clone of the parent workspace is available.
Having a local clone of the “reference tree” is useful in many ways.
On my laptop I keep periodically sync’ed clones of the following Mercurial repositories, for example:
- The main Mercurial repository; http://www.selenic.com/repo/hg/
- The stable Mercurial repository; http://www.selenic.com/repo/hg-stable
- The crew Mercurial repository; http://hg.intevation.org/mercurial/crew
- The stable crew Mercurial repository; http://hg.intevation.org/mercurial/crew-stable
After a while, I got tired of having to type “hg pull -u” in each one of them:
% cd ~/hg/mercurial/main && hg pull % cd ~/hg/mercurial/stable && hg pull % cd ~/hg/mercurial/crew && hg pull % cd ~/hg/mercurial/crew-stable && hg pull
Even with the hg -R repo ...
option of Mercurial itself, it’s boring to have to type things like:
% cd ~/hg/mercurial % for repo in main stable crew crew-stable ; do hg -R "${repo}" pull -u ; done
Instead of having to rely on bash and its wonderful CTRL-R history search feature to repeat this command whenever I needed it, I started writing a shell script to make this process easier to repeat without a lot of opportunity for typos, or other errors being introduced:
#!/usr/bin/ksh -p # Copyright (c) 2007 Giorgos Keramidas <keramida@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. # # Pull changesets in one or more Mercurial clones. # # usage: # hg-pull-mercurial repo [repo ...] # # The repositories where local Mercurial clones live are assumed to be # under ${HOME}/hg/mercurial or under the ${MERCURIALBASE} path. # # -------------------------------------------------------------------- progname=$(basename "$0") # If TMPDIR is not set, use /var/tmp as a safe fallback value. TMPDIR="${TMPDIR:-/var/tmp}" export TMPDIR # -------------------------------------------------------------------- # # err exitval message # Display message to stderr and log to the syslog, and exit with # exitval as the return code of the script. # function err { exitval=$1 shift log "${progname}: ERROR: $*" exit "${exitval:-1}" } # # warn message # Display message to stderr and the log file, if any. # function warn { log "${progname}: WARNING: $*" } # # info message # Display informational message to stdout and to the logfile, # if one is defined. # function info { log "${progname}: INFO: $*" } # # debug message # Output message to stderr if debug_output_enabled is set to # 'yes', 'true' or '1'. Please AVOID calling any shell # subroutine that may recursively call debug(). # function debug { case ${debug_output_enabled} in [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) log "${progname}: DEBUG: $*" ;; esac } # # log message # Display time-stamped message and log it to "${LOGFILE}", # if one is defined. # function log { __timestamp="`date -u '+%Y-%m-%d %H:%M:%S'`" print -u2 "${__timestamp} -- $*" if [ -n "${LOGFILE}" ]; then print "${__timestamp} -- $*" >> "${LOGFILE}" fi } # # msg message # Display message and log it to "${LOGFILE}", if one is defined. # function msg { print -u2 "$*" if [ -n "${LOGFILE}" ]; then print "$*" >> "${LOGFILE}" fi } # -------------------------------------------------------------------- function usage { print -u2 "usage: ${progname} repo [repo ...]" exit 1 } if [ $# -eq 0 ]; then usage exit 1 fi # -------------------------------------------------------------------- # # Local mercurial clones are assumed to be stored in ~/hg/mercurial, # unless MERCURIALBASE points elsewhere. The repositories under the # $MERCURIALBASE tree should have a 'parent' path defined in their # repository-specific .hg/hgrc configuration, pointing at the proper # remote 'parent workspace'. # MERCURIALBASE="${MERCURIALBASE:-${HOME}/hg/mercurial}" EXIT=0 for repo in "$@" ; do repodir="${MERCURIALBASE}/${repo}" debug "Checking for hg workspace at ${repodir}" if [ ! -d "${repodir}/.hg" ]; then err 1 "${repodir} is not a Mercurial workspace" fi debug "Workspace looks ok at ${repodir}" log "Pulling changes into ${repodir}" hg -R "${repodir}" pull -u parent if [ $? -ne 0 ]; then warn "${repodir}: cannot update workspace." EXIT=1 fi log "Successfully updated workspace at ${repodir}" msg "" done exit $EXIT
With the script saved in my ~/cron.d
directory, I can now update one or more of my local Mercurial
clones, by running:
% ~/cron.d/hg-pull.sh main stable crew crew-stable
This is something that will also look good in a cron job, but I’m sticking to the manual invocation for now, until all the warts have been cleaned in the pull script.
Updating one repository is also easy:
% ~/cron.d/hg-pull.ksh main 2007-09-06 15:36:49 -- Pulling changes into /home/keramida/hg/mercurial/main pulling from http://www.selenic.com/repo/hg/ searching for changes no changes found 2007-09-06 15:36:51 -- Successfully updated workspace at /home/keramida/hg/mercurial/main%
Multiple repositories seem to work too:
% ~/cron.d/hg-pull-mercurial.ksh main stable crew crew-stable 2007-09-06 15:37:43 -- Pulling changes into /home/keramida/hg/mercurial/main pulling from http://www.selenic.com/repo/hg/ searching for changes no changes found 2007-09-06 15:37:45 -- Successfully updated workspace at /home/keramida/hg/mercurial/main 2007-09-06 15:37:45 -- Pulling changes into /home/keramida/hg/mercurial/stable pulling from http://www.selenic.com/repo/hg-stable searching for changes no changes found 2007-09-06 15:37:46 -- Successfully updated workspace at /home/keramida/hg/mercurial/stable 2007-09-06 15:37:46 -- Pulling changes into /home/keramida/hg/mercurial/crew pulling from http://hg.intevation.org/mercurial/crew searching for changes no changes found 2007-09-06 15:37:47 -- Successfully updated workspace at /home/keramida/hg/mercurial/crew 2007-09-06 15:37:47 -- Pulling changes into /home/keramida/hg/mercurial/crew-stable pulling from http://hg.intevation.org/mercurial/crew-stable searching for changes no changes found 2007-09-06 15:37:48 -- Successfully updated workspace at /home/keramida/hg/mercurial/crew-stable %
That was nice, and fairly easy to get going :-)