chiark / gitweb /
geese-chasing bugfix from liberator; move ..moved-to-* thing away in destination
[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 >&2 \"    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 >&2 \"    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-*)
119    echo \"    btw, module was previously at destn repo\"
120    mv -- \"\$f\" \
121     \"\${f/..moved-to-/..previously-\$(date +%s)-moved-to-}\"
122    ;;
123   *..previously-*) ;;
124   *..tmp-*)
125    echo \"    nb: possibly-stale temp/partial copy \$f\"
126    ;;
127   *..\*)
128    ;;
129   *)
130    echo >&2 \"error: found unexpected subdir \$f\"
131    exit 8
132    ;;
133   esac
134  done
135         '"
136
137         if ! $resume; then
138                 "$CVS_RSH" "$srchost" bash -ec "'
139  mv -- $srcrepo/$module $srcrepo/$module..$movingto
140                 '"
141         fi
142
143         mn "transferring repo data"
144         tmpid="tmp-$(uname -n).$(date +%s)"
145         "$CVS_RSH" "$srchost" bash -ec "'
146  tar -c -C $srcrepo/$module..$movingto -f - .
147         '" | "$CVS_RSH" "$dsthost" bash -ec "'
148                 cd $dstrepo
149                 mkdir $module..$tmpid
150                 cd $module..$tmpid
151                 tar --no-same-owner -xf -
152         '"
153         test "${PIPESTATUS[*]}" = "0 0"
154
155         mn "confirming move at destination repo"
156         "$CVS_RSH" "$dsthost" bash -ec "'
157                 cd $dstrepo
158                 mv $module..$tmpid $module
159         '"
160
161         mn "confirming move at source repo"
162         "$CVS_RSH" "$srchost" bash -ec "'
163  mv -- $srcrepo/$module..$movingto \
164   $srcrepo/$module..moved-to-$dsthost:$dstrepotrans
165         '"
166         echo "module moved successfully"
167 }
168
169 compute_fqdn_data () {
170         fqdn_data="$(adnshost -s - "$1" 2>/dev/null || true)"
171 }
172
173 do_furtle () {
174         module="$(cat CVS/Repository)"
175         oldroot="$(cat CVS/Root)"
176         goose="$oldroot"
177         check_module
178         printf "checking/updating repo for %s\n" "$module"
179         compute_fqdn_data "$(uname -n)"
180         our_fqdn_data="$fqdn_data"
181         searching=true
182         while $searching; do
183                 mn "chasing geese at $goose"
184                 case "$goose" in
185                 *[0-9a-zA-Z]:/*)
186                         remotehost="${goose%%:*}"
187                         path="${goose#*:}"
188                         check_hostname "$remotehost"
189                         check_remote_path "$path/$module"
190                         isremote=true
191                         compute_fqdn_data "$remotehost"
192                         if [ "x$fqdn_data" = "x$our_fqdn_data" -a \
193                                 "x$fqdn_data" != x ]; then
194                                 isremote=false
195                                 goose="$path"
196                         fi
197                         ;;
198                 *:*)
199                         fail "unknown remote repository string $goose"
200                         ;;
201                 /*)
202                         isremote=false
203                         path="$goose"
204                         ;;
205                 *)
206                         fail "unknown repository string $goose"
207                         ;;
208                 esac
209                 check="
210  cd $path
211  if test -d $module; then echo good; exit 0; fi
212  if ls -d $module..moved-to-* 2>/dev/null; then exit 0; fi
213  echo bad
214 "
215                 if $isremote; then
216                         new_goose_info="$("$CVS_RSH" "$remotehost" \
217                                         bash -ec "'$check'")"
218                 else
219                         new_goose_info="$(      bash -ec "$check")"
220                 fi
221                 case "$new_goose_info" in
222                 good)
223                         searching=false
224                         ;;
225                 bad)
226                         echo >&2 "trail went cold at $goose"
227                         exit 4
228                         ;;
229                 *..moved-to-*)
230                         goose="$(printf "%s" \
231                                 "${new_goose_info#*..moved-to-}" | \
232                                 tr : / | sed -e 's,/,:,')"
233                         ;;
234                 esac
235         done
236         if [ "x$goose" = "x$oldroot" ]; then
237                 echo 'repo has not moved - nothing to do'
238                 exit 0
239         fi
240         mn "found new repo, adjusting working tree"
241         cvs-adjustroot "$oldroot" "$goose"
242         echo 'working tree adjustment completed'
243 }
244
245 if $move; then
246         if [ $# = 4 ]; then
247                 srchost="$(hostname -f)"; srcrepo="$1"
248                 module="$2";
249                 dsthost="$3"; dstrepo="$4"
250         elif [ $# = 5 ]; then
251                 srchost="$1"; srcrepo="$2"
252                 module="$3";
253                 dsthost="$4"; dstrepo="$5"
254         else
255                 bad_usage "--move needs hostname(s) and paths"
256         fi
257         do_move
258 else
259         [ "$#" = 0 ] || bad_usage "without --move, give no arguments"
260         do_furtle
261 fi