+#!/bin/bash
+set -e
+print_usage () { cat <<'END'
+usage:
+ in repository directory
+ cvs-repomove --move local-repo module hostname remote-repo
+ in working directory
+ cvs-repomove automatically update **/CVS/Root
+END
+}
+
+# We do things in the following order:
+# src dst
+# 0. Check things @/..moving-to none/..moved-to
+# 1. Rename src repo to prevent commits etc. ..moving-to none/..moved-to
+# 2. Make temporary copy at destination ..moving-to ..tmp
+# 3. Install temporary copy at destination ..moving-to @
+# 4. Move aside src repo ..moved-to @
+
+fail () { echo >&2 "error: $1"; exit 8; }
+bad_usage () { echo >&2 "bad usage: $1"; print_usage >&2; exit 12; }
+
+move=false
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --move) move=true ;;
+ --help) print_usage; exit 0 ;;
+ --) ;;
+ -*) bad_usage "unknown option $1" ;;
+ *) break ;;
+ esac
+ shift
+done
+
+mn () { echo " $1"; }
+
+check_module () {
+ case "$module" in
+ *..*) fail \
+ "moving a module with \`..' in its name is not supported" ;;
+ */*) fail \
+ "moving a subdirectory is not supported" ;;
+ -*) fail \
+ "moving a module whose name starts with \`-' is not supported" ;;
+ esac
+}
+
+check_remote_host () {
+ case "$1" in
+ /*|.*|-*) fail "bad hostname $dsthost" ;;
+ esac
+}
+
+check_remote_path () {
+ case "$1" in
+ *[^0-9a-zA-Z/._+,-]*) fail \
+ "remote pathname may not contain metacharacters, sorry" ;;
+ esac
+}
+
+do_move () {
+ check_module
+ check_remote_host "$dsthost"
+ check_remote_path "$dstrepo/$module"
+
+ case "$dstrepo" in
+ /*) ;;
+ *) bad_usage "destination repo path must be absolute" ;;
+ esac
+
+ printf "moving module %s from %s to %s:%s\n" \
+ "$module" "$srcrepo" "$dsthost" "$dstrepo"
+
+ mn "checking existing repository"
+ ls -d -- "$srcrepo/CVSROOT" >/dev/null
+
+ dstrepotrans="$(printf "%s" "$dstrepo" | tr / :)"
+ movingto="moving-to-$dsthost:$dstrepotrans"
+ if test -d "$srcrepo/$module..$movingto"; then
+ echo " resuming previous attempt at a move"
+ resume=true
+ if test -d "$srcrepo/$module"; then
+ fail "but $srcrepo/$module exists too"
+ fi
+ else
+ resume=false
+ ls -d -- "$srcrepo/$module" >/dev/null
+ fi
+
+ set +e
+ previously="$(ls -d -- "$srcrepo/$module..moved-to-"* 2>/dev/null)"
+ set -e
+ if [ "x$previously" != x ]; then
+ echo " btw, module was once before moved away from here"
+ mv -- "$previously" \
+ "${previously/..moved-to-/..previously-$(date +%s)-moved-to-}"
+ fi
+
+ mn "checking dst repository"
+ "$CVS_RSH" "$dsthost" bash -ec "'
+ cd $dstrepo
+ ls -d CVSROOT >/dev/null
+ if test -d $dstrepo/$module; then
+ echo >&2 module already exists in destination repo
+ exit 1
+ fi
+ for f in $module..*; do
+ case \"\$f\" in
+ *..moved-to-*) echo \" btw, module was previously at destn repo\" ;;
+ *..previously-*) ;;
+ *..tmp-*) echo \" nb: possibly-stale temp/partial copy \$f\" ;;
+ *..\*) ;;
+ *) echo >&2 \"error: found unexpected subdir \$f\"; exit 8;;
+ esac
+ done
+ '"
+
+ if ! $resume; then
+ mv -- "$srcrepo/$module" "$srcrepo/$module..$movingto"
+ fi
+
+ mn "transferring repo data"
+ tmpid="tmp-$(uname -n).$(date +%s)"
+ tar -c -C "$srcrepo/$module..$movingto" -f - . | \
+ "$CVS_RSH" "$dsthost" bash -ec "'
+ cd $dstrepo
+ mkdir $module..$tmpid
+ cd $module..$tmpid
+ tar --no-same-owner -xf -
+ '"
+ test "${PIPESTATUS[*]}" = "0 0"
+
+ mn "confirming move at destination repo"
+ "$CVS_RSH" "$dsthost" bash -ec "'
+ cd $dstrepo
+ mv $module..$tmpid $module
+ '"
+
+ mn "confirming move at local repo"
+ mv -- "$srcrepo/$module..$movingto" \
+ "$srcrepo/$module..moved-to-$dsthost:$dstrepotrans"
+ echo "module moved successfully"
+}
+
+compute_fqdn_data () {
+ fqdn_data="$(adnshost -s - "$1" 2>/dev/null || true)"
+}
+
+do_furtle () {
+ module="$(cat CVS/Repository)"
+ oldroot="$(cat CVS/Root)"
+ goose="$oldroot"
+ check_module
+ printf "checking/updating repo for %s\n" "$module"
+ compute_fqdn_data "$(uname -n)"
+ our_fqdn_data="$fqdn_data"
+ searching=true
+ while $searching; do
+ mn "chasing geese at $goose"
+ case "$goose" in
+ *[0-9a-zA-Z]:/*)
+ remotehost="${goose%%:*}"
+ path="${goose#*:}"
+ check_remote_host "$remotehost"
+ check_remote_path "$remotepath/$module"
+ isremote=true
+ compute_fqdn_data "$remotehost"
+ if [ "x$fqdn_data" = "x$our_fqdn_data" -a \
+ "x$fqdn_data" != x ]; then
+ isremote=false
+ goose="$path"
+ fi
+ ;;
+ *:*)
+ fail "unknown remote repository string $goose"
+ ;;
+ /*)
+ isremote=false
+ path="$goose"
+ ;;
+ *)
+ fail "unknown repository string $goose"
+ ;;
+ esac
+ check="
+ cd $path
+ if test -d $module; then echo good; exit 0; fi
+ if ls -d $module..moved-to-* 2>/dev/null; then exit 0; fi
+ echo bad
+"
+ if $isremote; then
+ new_goose_info="$("$CVS_RSH" "$remotehost" \
+ bash -ec "'$check'")"
+ else
+ new_goose_info="$( bash -ec "$check")"
+ fi
+ case "$new_goose_info" in
+ good)
+ searching=false
+ ;;
+ bad)
+ echo >&2 "trail went cold at $goose"
+ exit 4
+ ;;
+ *..moved-to-*)
+ goose="$(printf "%s" \
+ "${new_goose_info#*..moved-to-}" | \
+ tr : / | sed -e 's,/,:,')"
+ ;;
+ esac
+ done
+ if [ "x$goose" = "x$oldroot" ]; then
+ echo 'repo has not moved - nothing to do'
+ exit 0
+ fi
+ mn "found new repo, adjusting working tree"
+ cvs-adjustroot "$oldroot" "$goose"
+ echo 'working tree adjustment completed'
+}
+
+if $move; then
+ [ "$#" = 4 ] || bad_usage "--move needs hostname and path"
+ srcrepo="$1"; module="$2"; dsthost="$3"; dstrepo="$4"
+ do_move
+else
+ [ "$#" = 0 ] || bad_usage "without --move, give no arguments"
+ do_furtle
+fi