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