6731044 wx2hg should deal better with nested repositories

   1 #! /usr/bin/ksh
   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License (the "License").
   7 # You may not use this file except in compliance with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 
  23 #
  24 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  25 # Use is subject to license terms.
  26 #
  27 # ident "@(#)wx2hg.sh   1.4     08/07/29 SMI"
  28 #
  29 
  30 #
  31 # Convert a wx-based workspace to Mercurial.
  32 #
  33 
  34 usage="wx2hg [-u] [-r hg_rev] [-t hg_ws] codemgr_ws"
  35 
  36 #
  37 # If "yes", then give some hints about cleanup and rerunning after a
  38 # failure.
  39 #
  40 can_retry=no
  41 tail=/usr/xpg4/bin/tail
  42 
  43 function has_hg_twin {
  44         [[ -n "$primary_twin" ]]
  45 }
  46 
  47 function warn {
  48         print -u2 wx2hg: warning: "$@"
  49 }
  50 
  51 function note {
  52         print -u2 wx2hg: note: "$@"
  53 }
  54 
  55 function fail {
  56         print -u2 wx2hg: "$@"
  57         if [[ "$can_retry" = yes ]]; then
  58                 print -u2 "Please run"
  59                 print -u2 "  hg --cwd $hg_ws update -C"
  60                 print -u2 "before retrying."
  61         fi
  62         exit 1
  63 }
  64 
  65 function clone_twins {
  66         ws="$1"
  67         rev="$2"
  68 
  69         echo "Cloning $primary_twin"
  70         echo "to $ws"
  71         set -x
  72         hg clone -r $rev "$primary_twin" "$ws"
  73         set +x
  74 
  75         rev_warning=n
  76         for dir in $nested_twins; do
  77                 (cd "$primary_twin"/$dir ; \
  78                     hg log -l 1 -r $hg_rev > /dev/null 2>1)
  79                 if  (( $? != 0 )); then
  80                         warn "Unable to clone $primary_twin/$dir"
  81                         rev_warning=y
  82                         continue
  83                 fi
  84                 echo "Cloning from $primary_twin/$dir"
  85                 echo "to $ws/$dir"
  86                 set -x
  87                 hg clone "$primary_twin"/$dir "$ws"/$dir
  88                 set +x
  89         done
  90 
  91         [[ $rev_warning = "n" ]] || fail \
  92 "revision $hgrev was not present in all workspaces.\n" \
  93 "When using -r with nested repositories, you should specify a tag\n" \
  94 "name that is valid in each workspace."
  95 }
  96 
  97 #
  98 # Command-line processing, sanity checks, and setup.
  99 #
 100 
 101 [[ -n $(whence workspace) ]] || 
 102     fail "workspace command not found; please check PATH."
 103 
 104 # do a wx update?
 105 do_update=yes
 106 
 107 #
 108 # Mercurial workspace to populate.  Default is to create, in the same
 109 # directory as the Teamware workspace, a new Mercurial workspace cloned
 110 # from the hg_twin of the Teamware parent.
 111 #
 112 hg_ws=""
 113 
 114 #
 115 # Revision in the Mercurial workspace to apply the changes to.
 116 # Default is to get the most recent revision (tip), thus avoiding
 117 # the need for a merge unless overridden by the caller using -r.
 118 #
 119 hg_rev="tip"
 120 
 121 while getopts r:t:u opt; do
 122         case $opt in
 123         r)      hg_rev="$OPTARG";;
 124         t)      hg_ws="$OPTARG";;
 125         u)      do_update=no;;
 126         ?)      print -u2 "usage: $usage"; exit 1;;
 127         esac
 128 done
 129 shift $(($OPTIND - 1))
 130 
 131 if [[ $# -ne 1 ]]; then
 132         print -u2 "usage: $usage"
 133         exit 1
 134 fi
 135 
 136 CODEMGR_WS="$1"
 137 [[ "$CODEMGR_WS" = /* ]] || CODEMGR_WS="$(pwd)/$CODEMGR_WS"
 138 export CODEMGR_WS
 139 
 140 if [[ -n "$hg_ws" ]]; then
 141         if [[ ! -d "$hg_ws" || ! -d "$hg_ws/.hg" ]]; then
 142                 fail "$hg_ws is not a Mercurial workspace."
 143         fi
 144         [[ "$hg_ws" = /* ]] || hg_ws="$(pwd)/$hg_ws"
 145 fi
 146 
 147 [[ -d "$CODEMGR_WS" ]] || fail "$CODEMGR_WS does not exist."
 148 cd "$CODEMGR_WS"
 149 
 150 codemgr_parent=$(workspace parent)
 151 [[ -n "$codemgr_parent" ]] || \
 152     fail "$CODEMGR_WS is not a Teamware workspace or does not have a parent."
 153 [[ -d "$codemgr_parent" ]] || fail "parent ($codemgr_parent) doesn't exist."
 154 
 155 primary_twin=""
 156 nested_twins=""
 157 twinfile="$codemgr_parent"/Codemgr_wsdata/hg_twin
 158 if [[ -f $twinfile ]]; then
 159         primary_twin=$(head -1 $twinfile)
 160         nested_twins=$($tail -n +2 $twinfile | sort -r)
 161 fi
 162 
 163 if has_hg_twin; then
 164         echo "Teamware parent $codemgr_parent has twin $primary_twin"
 165         [[ -n "$nested_twins" ]] &&
 166             echo "and nested twins $nested_twins"
 167 fi
 168 
 169 #
 170 # Do this check before time-consuming operations like creating
 171 # the target repo.
 172 #
 173 his=$(find Codemgr_wsdata -name history -mtime -1)
 174 if [[ -z "$his" ]]; then
 175         warn "history file is more than one day old; do you need to" \
 176             "bringover from $codemgr_parent?"
 177 fi
 178 
 179 # Less time-consuming than cloning
 180 
 181 if [[ ! -d wx ]]; then
 182         print "Initializing wx..."
 183         wx init -ft
 184 else
 185         if [[ "$do_update" = yes ]]; then
 186                 print "Updating wx state..."
 187                 wx update
 188         fi
 189 fi
 190 
 191 wx outchk
 192 
 193 out_files=$(wx out)
 194 active_files=$(wx list)
 195 
 196 if [[ ! -z "$out_files" ]]; then
 197         fail "wx2hg will only migrate checked-in files;" \
 198             "please check in these files with wx ci and try again"
 199 fi
 200 
 201 # more time-consuming than wx update and wx outchk
 202 
 203 if [[ -z "$hg_ws" ]]; then
 204         ws=$(basename $(pwd))
 205         hg_ws=$(dirname $(pwd))/"$ws-hg"
 206 fi
 207 
 208 if [[ -d "$hg_ws" ]]; then
 209         echo "Updating preexisting Mercurial workspace $hg_ws to $hg_rev\n"
 210         (cd "$hg_ws"; hg update -C $hg_rev) ||
 211             fail "hg update $hg_rev failed for $hg_ws"
 212         if [[ -n "$nested_twins" ]]; then
 213                 update_warning=n
 214                 for dir in $nested_twins; do
 215                         if [[ ! -d "$hg_ws/$dir" ]]; then
 216                                 warn "$hw_ws/$dir does not exist"
 217                                 update_warning=y
 218                         fi
 219                         echo "Updating preexisting nested workspace " \
 220                             "$hg_ws/$dir to $hg_rev\n"
 221                         (cd "$hg_ws"/$dir ; hg update -C $hg_rev)
 222                         if (( $? != 0 )); then
 223                                 warn "hg update $hg_rev failed for $hg_ws/$dir"
 224                                 update_warning=y
 225                                 continue
 226                         fi
 227                 done
 228 
 229                 [[ $update_warning = "n" ]] ||
 230                     fail "When using an existing Mercurial workspace with\n" \
 231                         "nested repositories, all nested repositories must\n" \
 232                         "already exist in the existing workspace.  If also\n" \
 233                         "specifying -r, then the specified hg_rev must be\n" \
 234                         "valid in all nested repositories."
 235         fi
 236 else
 237         if has_hg_twin; then
 238                 clone_twins "$hg_ws" $hg_rev
 239         else
 240                 fail "$codemgr_parent is not recognized as a gate;" \
 241                     "please provide a Mercurial workspace (-t hg_ws)" \
 242                     "that matches it."
 243         fi
 244 fi
 245 
 246 can_retry=yes
 247 
 248 # Make sure hg_ws is an absolute path
 249 [[ "$hg_ws" = /* ]] || hg_ws="$(pwd)/$hg_ws"
 250 
 251 
 252 # usage: which_repo filename
 253 function which_repo {
 254         typeset f=$1
 255 
 256         for r in $nested_twins; do
 257                 if [ ${f##$r/} != $f ]; then
 258                         echo ${f##$r/} $r
 259                         return
 260                 fi
 261         done
 262 
 263         echo $f "."
 264 }
 265 
 266 #
 267 # Do renames first, because they'll be listed with the new name by "wx
 268 # list".  There's a conflict if the new name already exists or if the
 269 # old name does not exist.  We can theoretically recover from the
 270 # former (move the existing file out of the way, or pick a different
 271 # new name), but not the latter.  For now, just error out and let the
 272 # user fix up the workspace so that there isn't a conflict.
 273 #
 274 
 275 renamelist=/tmp/wxrename$$
 276 wx renamed > "$renamelist"
 277 
 278 # usage: do_rename oldname newname
 279 function do_rename {
 280         typeset old_file old_repo new_file new_repo
 281 
 282         which_repo $1 | read old_file old_repo
 283         which_repo $2 | read new_file new_repo
 284 
 285         typeset old=$old_repo/$old_file
 286         typeset new=$new_repo/$new_file
 287 
 288         [[ -f "$old" ]] || fail "can't rename: $old doesn't exist."
 289         [[ ! -f "$new" ]] || fail "can't rename: $new already exists."
 290 
 291         dir=$(dirname "$new")
 292         base=$(basename "$new")
 293         [[ -d "$dir" ]] || mkdir -p "$dir" || fail "mkdir failed"
 294 
 295         if [ $old_repo = $new_repo ]; then
 296                 print "rename $old -> $new"
 297                 set -x
 298                 ( cd $old_repo; hg mv $old_file $new_file ) || \
 299                     fail "rename failed."
 300                 set +x
 301         else
 302                 print "moving $old_file from repository $old_repo"
 303                 print "to $new_file in repository $new_repo"
 304                 cp $old $new
 305                 set -x
 306                 ( cd $old_repo; hg rm $old_file ) || fail "hg rm failed"
 307                 ( cd $new_repo; hg add $new_file ) || fail "hg add failed"
 308                 set +x
 309         fi
 310 }
 311 
 312 if [[ -s "$renamelist" ]]; then
 313         cat "$renamelist" | (
 314                 cd "$hg_ws"
 315                 while :; do
 316                         read newname oldname
 317                         [[ -n "$newname" ]] || break
 318                         do_rename "$oldname" "$newname"
 319                 done
 320         ) || exit 1
 321 fi
 322 
 323 #
 324 # usage: name_in_parent fname
 325 # If fname had been renamed, echo the old name.  Otherwise echo the
 326 # given name.
 327 #
 328 function name_in_parent {
 329         typeset new old
 330 
 331         if [[ -s "$renamelist" ]]; then
 332                 cat "$renamelist" | while :; do
 333                         read new old
 334                         [[ -n "$new" ]] || break
 335                         if [[ "$1" = "$new" ]]; then
 336                                 print "$old"
 337                                 return
 338                         fi
 339                 done
 340         fi
 341         print "$1"
 342 }
 343 
 344 #
 345 # Now do content changes.  There's a likely conflict if the file in
 346 # Mercurial is different from the file in the Teamware parent.
 347 #
 348 
 349 parentfile=/tmp/parent$$
 350 patchfile=/tmp/patch$$
 351 childfile=/tmp/child$$
 352 
 353 [[ -n "$active_files" ]] || warn "no files in active list."
 354 
 355 for f in $active_files; do
 356         #
 357         # Get the name that the file appears in the parent as.
 358         #
 359         oldname=$(name_in_parent "$f")
 360 
 361         # We need unexpanded SCCS keywords for both parent and child
 362         sccs get -skp "$f" > "$childfile"
 363 
 364         if [[ -f "$codemgr_parent/$oldname" ]]; then
 365                 (cd $codemgr_parent; sccs get -skp "$oldname" > "$parentfile")
 366         else
 367                 rm -f $parentfile
 368         fi
 369 
 370         if [[ ! -r "$parentfile" ]]; then
 371                 print "new file: $f"
 372                 [[ ! -f "$hg_ws/$f" ]] || fail "$f already exists in $hg_ws."
 373                 dir=$(dirname "$hg_ws/$f")
 374                 base=$(basename "$hg_ws/$f")
 375                 [[ -d "$dir" ]] || mkdir -p "$dir" || fail "mkdir failed"
 376                 cp "$childfile" "$hg_ws/$f" || fail "copy failed"
 377                 set -x
 378                 (cd "$dir" && hg add "$base") || fail "hg add failed."
 379                 set +x
 380         elif diff "$parentfile" "$hg_ws/$f" > /dev/null 2>&1; then
 381                 if diff -u "$parentfile" "$childfile" > "$patchfile"; then
 382                         print "skipping $f (unchanged)."
 383                         continue
 384                 fi
 385                 (cd "$hg_ws"; gpatch -F0 $f < "$patchfile")
 386                 [[ $? -eq 0 ]] || fail "$f: patch failed."
 387         else
 388                 diff -u "$parentfile" "$hg_ws/$f"
 389                 echo ""
 390 
 391                 fail "For file:\n\n\t$f\n\nthe teamware parent:" \
 392                     "\n\n\t$codemgr_parent" \
 393                     "\n\ndoesn't match its mercurial twin;" \
 394                         "specify the matching revision in mercurial\nwith" \
 395                         "-r hg_rev, or resynchronize them.\n"
 396         fi
 397 done
 398 
 399 note "remember to commit your changes:"
 400 echo "in primary repository ${hg_ws}:"
 401 ( cd $hg_ws ; hg status -mard )
 402 for n in $nested_twins; do
 403         echo "in nested repository ${n}:"
 404         ( cd $hg_ws/$n ; hg status -mard )
 405 done
 406 
 407 if [[ "$hg_rev" != "tip" ]]; then
 408         note "before you integrate your changes, $hg_ws must be merged to tip"
 409 fi
 410 
 411 rm -f "$parentfile" "$patchfile" "$renamelist" "$childfile"
 412 
 413 exit 0
--- EOF ---