chiark / gitweb /
hexbin-term wip
[chiark-utils.git] / scripts / cvs-repomove
1 #!/bin/bash
2 set -e
3 print_usage () { cat <<'END'
4 usage:
5   to move repository directory, cd to anywhere and
6     cvs-repomove --move local-repo module hostname remote-repo
7     cvs-repomove --move src-hostname src-repo module dst-hostname dst-repo
8   to adjust CVS/Root afterwards, in each working directory
9     cvs-repomove                automatically update **/CVS/Root
10 END
11 }
12
13 # We do things in the following order:
14 #                                               src             dst
15 #  0. Check things                              @/..moving-to   none/..moved-to
16 #  1. Rename src repo to prevent commits etc.   ..moving-to     none/..moved-to
17 #  2. Make temporary copy at destination        ..moving-to     ..tmp
18 #  3. Install temporary copy at destination     ..moving-to     @
19 #  4. Move aside src repo                       ..moved-to      @
20
21 fail () { echo >&2 "error: $1"; exit 8; }
22 bad_usage () { echo >&2 "bad usage: $1"; print_usage >&2; exit 12; }
23
24 move=false
25
26 while [ $# -gt 0 ]; do
27         case "$1" in
28         --move)         move=true ;;
29         --help)         print_usage; exit 0 ;;
30         --)             ;;
31         -*)             bad_usage "unknown option $1" ;;
32         *)              break ;;
33         esac
34         shift
35 done
36
37 mn () { echo " $1"; }
38
39 check_module () {
40         case "$module" in
41         *..*)           fail \
42  "moving a module with \`..' in its name is not supported" ;;
43         */*)            fail \
44  "moving a subdirectory is not supported" ;;
45         -*)             fail \
46  "moving a module whose name starts with \`-' is not supported" ;;
47         esac
48 }
49
50 check_hostname () {
51         case "$1" in
52         /*|.*|-*)       fail "bad hostname $dsthost" ;;
53         esac
54 }
55
56 check_remote_path () {
57         case "$1" in
58         *[^0-9a-zA-Z/._+,-]*) fail \
59                 "pathname may not contain metacharacters, sorry" ;;
60         esac
61 }
62
63 do_move () {
64         check_module
65         check_hostname "$srchost"
66         check_hostname "$dsthost"
67         check_remote_path "$srcrepo/$module"
68         check_remote_path "$dstrepo/$module"
69
70         case "$dstrepo" in
71         /*)     ;;
72         *)      bad_usage "destination repo path must be absolute" ;;
73         esac
74
75         printf "moving module %s from %s:%s to %s:%s\n" \
76                 "$module" "$srchost" "$srcrepo" "$dsthost" "$dstrepo"
77
78         mn "checking existing repository"
79         "$CVS_RSH" "$srchost" bash -ec "'
80  ls -d -- $srcrepo/CVSROOT >/dev/null
81         '"
82
83         dstrepotrans="$(printf "%s" "$dstrepo" | tr / :)"
84         movingto="moving-to-$dsthost:$dstrepotrans"
85         resume="$("$CVS_RSH" "$srchost" bash -ec "'
86  if test -d $srcrepo/$module..$movingto; then
87   echo \"    resuming previous attempt at a move\"
88   resume=true
89   if test -d $srcrepo/$module; then
90    echo >&2 \"but $srcrepo/$module exists too\"
91    exit 1
92   fi
93  else
94   resume=false
95   ls -d -- $srcrepo/$module >/dev/null
96  fi
97  set +e
98  previously=\"$(ls -d -- $srcrepo/$module..moved-to-* 2>/dev/null)\"
99  set -e
100  if [ \"x$previously\" != x ]; then
101   echo \"    btw, module was once before moved away from here\"
102   mv -- \"$previously\" \
103    \"${previously/..moved-to-/..previously-\$(date +%s)-moved-to-}\"
104  fi
105  echo \$resume
106         '")"
107
108         mn "checking dst repository"
109         "$CVS_RSH" "$dsthost" bash -ec "'
110  cd $dstrepo
111  ls -d CVSROOT >/dev/null
112  if test -d $dstrepo/$module; then
113   echo >&2 module already exists in destination repo
114   exit 1
115  fi
116  for f in $module..*; do
117   case \"\$f\" in
118   *..moved-to-*)   echo \"    btw, module was previously at destn repo\" ;;
119   *..previously-*) ;;
120   *..tmp-*)        echo \"    nb: possibly-stale temp/partial copy \$f\" ;;
121   *..\*)           ;;
122   *)               echo >&2 \"error: found unexpected subdir \$f\"; exit 8;;
123   esac
124  done
125         '"
126
127         if ! $resume; then
128                 "$CVS_RSH" "$srchost" bash -ec "'
129  mv -- $srcrepo/$module $srcrepo/$module..$movingto
130                 '"
131         fi
132
133         mn "transferring repo data"
134         tmpid="tmp-$(uname -n).$(date +%s)"
135         "$CVS_RSH" "$srchost" bash -ec "'
136  tar -c -C $srcrepo/$module..$movingto -f - .
137         '" | "$CVS_RSH" "$dsthost" bash -ec "'
138                 cd $dstrepo
139                 mkdir $module..$tmpid
140                 cd $module..$tmpid
141                 tar --no-same-owner -xf -
142         '"
143         test "${PIPESTATUS[*]}" = "0 0"
144
145         mn "confirming move at destination repo"
146         "$CVS_RSH" "$dsthost" bash -ec "'
147                 cd $dstrepo
148                 mv $module..$tmpid $module
149         '"
150
151         mn "confirming move at source repo"
152         "$CVS_RSH" "$srchost" bash -ec "'
153  mv -- $srcrepo/$module..$movingto \
154   $srcrepo/$module..moved-to-$dsthost:$dstrepotrans
155         '"
156         echo "module moved successfully"
157 }
158
159 compute_fqdn_data () {
160         fqdn_data="$(adnshost -s - "$1" 2>/dev/null || true)"
161 }
162
163 do_furtle () {
164         module="$(cat CVS/Repository)"
165         oldroot="$(cat CVS/Root)"
166         goose="$oldroot"
167         check_module
168         printf "checking/updating repo for %s\n" "$module"
169         compute_fqdn_data "$(uname -n)"
170         our_fqdn_data="$fqdn_data"
171         searching=true
172         while $searching; do
173                 mn "chasing geese at $goose"
174                 case "$goose" in
175                 *[0-9a-zA-Z]:/*)
176                         remotehost="${goose%%:*}"
177                         path="${goose#*:}"
178                         check_hostname "$remotehost"
179                         check_remote_path "$remotepath/$module"
180                         isremote=true
181                         compute_fqdn_data "$remotehost"
182                         if [ "x$fqdn_data" = "x$our_fqdn_data" -a \
183                                 "x$fqdn_data" != x ]; then
184                                 isremote=false
185                                 goose="$path"
186                         fi
187                         ;;
188                 *:*)
189                         fail "unknown remote repository string $goose"
190                         ;;
191                 /*)
192                         isremote=false
193                         path="$goose"
194                         ;;
195                 *)
196                         fail "unknown repository string $goose"
197                         ;;
198                 esac
199                 check="
200  cd $path
201  if test -d $module; then echo good; exit 0; fi
202  if ls -d $module..moved-to-* 2>/dev/null; then exit 0; fi
203  echo bad
204 "
205                 if $isremote; then
206                         new_goose_info="$("$CVS_RSH" "$remotehost" \
207                                         bash -ec "'$check'")"
208                 else
209                         new_goose_info="$(      bash -ec "$check")"
210                 fi
211                 case "$new_goose_info" in
212                 good)
213                         searching=false
214                         ;;
215                 bad)
216                         echo >&2 "trail went cold at $goose"
217                         exit 4
218                         ;;
219                 *..moved-to-*)
220                         goose="$(printf "%s" \
221                                 "${new_goose_info#*..moved-to-}" | \
222                                 tr : / | sed -e 's,/,:,')"
223                         ;;
224                 esac
225         done
226         if [ "x$goose" = "x$oldroot" ]; then
227                 echo 'repo has not moved - nothing to do'
228                 exit 0
229         fi
230         mn "found new repo, adjusting working tree"
231         cvs-adjustroot "$oldroot" "$goose"
232         echo 'working tree adjustment completed'
233 }
234
235 if $move; then
236         if [ $# = 4 ]; then
237                 srchost="$(hostname -f)"; srcrepo="$1"
238                 module="$2";
239                 dsthost="$3"; dstrepo="$4"
240         elif [ $# = 5 ]; then
241                 srchost="$1"; srcrepo="$2"
242                 module="$3";
243                 dsthost="$4"; dstrepo="$5"
244         else
245                 bad_usage "--move needs hostname(s) and paths"
246         fi
247         do_move
248 else
249         [ "$#" = 0 ] || bad_usage "without --move, give no arguments"
250         do_furtle
251 fi