chiark / gitweb /
git-cache-proxy: housekeeping: use rm -rf, not remove_tree (which is weirdly broken)
[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 # Copyright 2004-2006 Ian Jackson <ian@chiark.greenend.org.uk>
14 #
15 # This script and its documentation (if any) are free software; you
16 # can redistribute it and/or modify them under the terms of the GNU
17 # General Public License as published by the Free Software Foundation;
18 # either version 3, or (at your option) any later version.
19
20 # chiark-named-conf and its manpage are distributed in the hope that
21 # it will be useful, but WITHOUT ANY WARRANTY; without even the
22 # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
23 # PURPOSE.  See the GNU General Public License for more details.
24
25 # You should have received a copy of the GNU General Public License along
26 # with this program; if not, consult the Free Software Foundation's
27 # website at www.fsf.org, or the GNU Project website at www.gnu.org.
28
29
30 # We do things in the following order:
31 #                                               src             dst
32 #  0. Check things                              @/..moving-to   none/..moved-to
33 #  1. Rename src repo to prevent commits etc.   ..moving-to     none/..moved-to
34 #  2. Make temporary copy at destination        ..moving-to     ..tmp
35 #  3. Install temporary copy at destination     ..moving-to     @
36 #  4. Move aside src repo                       ..moved-to      @
37
38 fail () { echo >&2 "error: $1"; exit 8; }
39 bad_usage () { echo >&2 "bad usage: $1"; print_usage >&2; exit 12; }
40
41 move=false
42
43 while [ $# -gt 0 ]; do
44         case "$1" in
45         --move)         move=true ;;
46         --help)         print_usage; exit 0 ;;
47         --)             ;;
48         -*)             bad_usage "unknown option $1" ;;
49         *)              break ;;
50         esac
51         shift
52 done
53
54 mn () { echo " $1"; }
55
56 check_module () {
57         case "$module" in
58         *..*)           fail \
59  "moving a module with \`..' in its name is not supported" ;;
60         */*)            fail \
61  "moving a subdirectory is not supported" ;;
62         -*)             fail \
63  "moving a module whose name starts with \`-' is not supported" ;;
64         esac
65 }
66
67 check_hostname () {
68         case "$1" in
69         /*|.*|-*)       fail "bad hostname $dsthost" ;;
70         esac
71 }
72
73 check_remote_path () {
74         case "$1" in
75         *[^0-9a-zA-Z/._+,-]*) fail \
76                 "pathname may not contain metacharacters, sorry" ;;
77         esac
78 }
79
80 do_move () {
81         check_module
82         check_hostname "$srchost"
83         check_hostname "$dsthost"
84         check_remote_path "$srcrepo/$module"
85         check_remote_path "$dstrepo/$module"
86
87         case "$dstrepo" in
88         /*)     ;;
89         *)      bad_usage "destination repo path must be absolute" ;;
90         esac
91
92         printf "moving module %s from %s:%s to %s:%s\n" \
93                 "$module" "$srchost" "$srcrepo" "$dsthost" "$dstrepo"
94
95         mn "checking existing repository"
96         "$CVS_RSH" "$srchost" bash -ec "'
97  ls -d -- $srcrepo/CVSROOT >/dev/null
98         '"
99
100         dstrepotrans="$(printf '%s\n' "$dstrepo" | tr / :)"
101         movingto="moving-to-$dsthost:$dstrepotrans"
102         resume="$("$CVS_RSH" "$srchost" bash -ec "'
103  if test -d $srcrepo/$module..$movingto; then
104   echo >&2 \"    resuming previous attempt at a move\"
105   resume=true
106   if test -d $srcrepo/$module; then
107    echo >&2 \"but $srcrepo/$module exists too\"
108    exit 1
109   fi
110  else
111   resume=false
112   ls -d -- $srcrepo/$module >/dev/null
113  fi
114  set +e
115  previously=\"$(ls -d -- $srcrepo/$module..moved-to-* 2>/dev/null)\"
116  set -e
117  if [ \"x\$previously\" != x ]; then
118   echo >&2 \"    btw, module was once before moved away from here\"
119   mv -- \"\$previously\" \
120    \"\${previously/..moved-to-/..previously-\$(date +%s)-moved-to-}\"
121  fi
122  echo \$resume
123         '")"
124
125         mn "checking dst repository"
126         "$CVS_RSH" "$dsthost" bash -ec "'
127  cd $dstrepo
128  ls -d CVSROOT >/dev/null
129  if test -d $dstrepo/$module; then
130   echo >&2 module already exists in destination repo
131   exit 1
132  fi
133  for f in $module..*; do
134   case \"\$f\" in
135   *..moved-to-*)
136    echo \"    btw, module was previously at destn repo\"
137    mv -- \"\$f\" \
138     \"\${f/..moved-to-/..previously-\$(date +%s)-moved-to-}\"
139    ;;
140   *..previously-*) ;;
141   *..tmp-*)
142    echo \"    nb: possibly-stale temp/partial copy \$f\"
143    ;;
144   *..\*)
145    ;;
146   *)
147    echo >&2 \"error: found unexpected subdir \$f\"
148    exit 8
149    ;;
150   esac
151  done
152         '"
153
154         if ! $resume; then
155                 "$CVS_RSH" "$srchost" bash -ec "'
156  mv -- $srcrepo/$module $srcrepo/$module..$movingto
157                 '"
158         fi
159
160         mn "transferring repo data"
161         tmpid="tmp-$(uname -n).$(date +%s)"
162         "$CVS_RSH" "$srchost" bash -ec "'
163  tar -c -C $srcrepo/$module..$movingto -f - .
164         '" | "$CVS_RSH" "$dsthost" bash -ec "'
165                 cd $dstrepo
166                 mkdir $module..$tmpid
167                 cd $module..$tmpid
168                 tar --no-same-owner -xf -
169         '"
170         test "${PIPESTATUS[*]}" = "0 0"
171
172         mn "confirming move at destination repo"
173         "$CVS_RSH" "$dsthost" bash -ec "'
174                 cd $dstrepo
175                 mv $module..$tmpid $module
176         '"
177
178         mn "confirming move at source repo"
179         "$CVS_RSH" "$srchost" bash -ec "'
180  mv -- $srcrepo/$module..$movingto \
181   $srcrepo/$module..moved-to-$dsthost:$dstrepotrans
182         '"
183         echo "module moved successfully"
184 }
185
186 compute_fqdn_data () {
187         fqdn_data="$(adnshost -s - "$1" 2>/dev/null || true)"
188 }
189
190 do_furtle () {
191         module="$(cat CVS/Repository)"
192         oldroot="$(cat CVS/Root)"
193         goose="$oldroot"
194         check_module
195         printf "checking/updating repo for %s\n" "$module"
196         compute_fqdn_data "$(uname -n)"
197         our_fqdn_data="$fqdn_data"
198         searching=true
199         while $searching; do
200                 mn "chasing geese at $goose"
201                 case "$goose" in
202                 *[0-9a-zA-Z]:/*)
203                         remotehost="${goose%%:*}"
204                         path="${goose#*:}"
205                         check_hostname "$remotehost"
206                         check_remote_path "$path/$module"
207                         isremote=true
208                         compute_fqdn_data "$remotehost"
209                         if [ "x$fqdn_data" = "x$our_fqdn_data" -a \
210                                 "x$fqdn_data" != x ]; then
211                                 isremote=false
212                                 goose="$path"
213                         fi
214                         ;;
215                 *:*)
216                         fail "unknown remote repository string $goose"
217                         ;;
218                 /*)
219                         isremote=false
220                         path="$goose"
221                         ;;
222                 *)
223                         fail "unknown repository string $goose"
224                         ;;
225                 esac
226                 check="
227  cd $path
228  if test -d $module; then echo good; exit 0; fi
229  if ls -d $module..moved-to-* 2>/dev/null; then exit 0; fi
230  echo bad
231 "
232                 if $isremote; then
233                         new_goose_info="$("$CVS_RSH" "$remotehost" \
234                                         bash -ec "'$check'")"
235                 else
236                         new_goose_info="$(      bash -ec "$check")"
237                 fi
238                 case "$new_goose_info" in
239                 good)
240                         searching=false
241                         ;;
242                 bad)
243                         echo >&2 "trail went cold at $goose"
244                         exit 4
245                         ;;
246                 *..moved-to-*)
247                         goose="$(printf '%s\n' \
248                                 "${new_goose_info#*..moved-to-}" | \
249                                 tr : / | sed -e 's,/,:,')"
250                         ;;
251                 esac
252         done
253         if [ "x$goose" = "x$oldroot" ]; then
254                 echo 'repo has not moved - nothing to do'
255                 exit 0
256         fi
257         mn "found new repo, adjusting working tree"
258         cvs-adjustroot "$oldroot" "$goose"
259         echo 'working tree adjustment completed'
260 }
261
262 if $move; then
263         if [ $# = 4 ]; then
264                 srchost="$(hostname -f)"; srcrepo="$1"
265                 module="$2";
266                 dsthost="$3"; dstrepo="$4"
267         elif [ $# = 5 ]; then
268                 srchost="$1"; srcrepo="$2"
269                 module="$3";
270                 dsthost="$4"; dstrepo="$5"
271         else
272                 bad_usage "--move needs hostname(s) and paths"
273         fi
274         do_move
275 else
276         [ "$#" = 0 ] || bad_usage "without --move, give no arguments"
277         do_furtle
278 fi