#!/bin/bash set -e print_usage () { cat <<'END' usage: to move repository directory, cd to anywhere and cvs-repomove --move local-repo module hostname remote-repo cvs-repomove --move src-hostname src-repo module dst-hostname dst-repo to adjust CVS/Root afterwards, in each 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_hostname () { case "$1" in /*|.*|-*) fail "bad hostname $dsthost" ;; esac } check_remote_path () { case "$1" in *[^0-9a-zA-Z/._+,-]*) fail \ "pathname may not contain metacharacters, sorry" ;; esac } do_move () { check_module check_hostname "$srchost" check_hostname "$dsthost" check_remote_path "$srcrepo/$module" check_remote_path "$dstrepo/$module" case "$dstrepo" in /*) ;; *) bad_usage "destination repo path must be absolute" ;; esac printf "moving module %s from %s:%s to %s:%s\n" \ "$module" "$srchost" "$srcrepo" "$dsthost" "$dstrepo" mn "checking existing repository" "$CVS_RSH" "$srchost" bash -ec "' ls -d -- $srcrepo/CVSROOT >/dev/null '" dstrepotrans="$(printf "%s" "$dstrepo" | tr / :)" movingto="moving-to-$dsthost:$dstrepotrans" resume="$("$CVS_RSH" "$srchost" bash -ec "' if test -d $srcrepo/$module..$movingto; then echo \" resuming previous attempt at a move\" resume=true if test -d $srcrepo/$module; then echo >&2 \"but $srcrepo/$module exists too\" exit 1 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 echo \$resume '")" 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 "$CVS_RSH" "$srchost" bash -ec "' mv -- $srcrepo/$module $srcrepo/$module..$movingto '" fi mn "transferring repo data" tmpid="tmp-$(uname -n).$(date +%s)" "$CVS_RSH" "$srchost" bash -ec "' 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 source repo" "$CVS_RSH" "$srchost" bash -ec "' 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_hostname "$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 if [ $# = 4 ]; then srchost="$(hostname -f)"; srcrepo="$1" module="$2"; dsthost="$3"; dstrepo="$4" elif [ $# = 5 ]; then srchost="$1"; srcrepo="$2" module="$3"; dsthost="$4"; dstrepo="$5" else bad_usage "--move needs hostname(s) and paths" fi do_move else [ "$#" = 0 ] || bad_usage "without --move, give no arguments" do_furtle fi