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 "%Z%%M% %I%     %E% 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                 hg clone "$primary_twin"/$dir "$ws"/$dir

  87         done
  88 
  89         [[ $rev_warning = "n" ]] || fail \
  90 "revision $hgrev was not present in all workspaces.\n" \
  91 "When using -r with nested repositories, you should specify a tag\n" \
  92 "name that is valid in each workspace."
  93 }
  94 
  95 #
  96 # Command-line processing, sanity checks, and setup.
  97 #
  98 
  99 [[ -n $(whence workspace) ]] || 
 100     fail "workspace command not found; please check PATH."
 101 
 102 # do a wx update?
 103 do_update=yes
 104 
 105 #
 106 # Mercurial workspace to populate.  Default is to create, in the same
 107 # directory as the Teamware workspace, a new Mercurial workspace cloned
 108 # from the hg_twin of the Teamware parent.
 109 #
 110 hg_ws=""
 111 
 112 #
 113 # Revision in the Mercurial workspace to apply the changes to.
 114 # Default is to get the most recent revision (tip), thus avoiding
 115 # the need for a merge unless overridden by the caller using -r.
 116 #
 117 hg_rev="tip"
 118 
 119 while getopts r:t:u opt; do
 120         case $opt in
 121         r)      hg_rev="$OPTARG";;
 122         t)      hg_ws="$OPTARG";;
 123         u)      do_update=no;;
 124         ?)      print -u2 "usage: $usage"; exit 1;;
 125         esac
 126 done
 127 shift $(($OPTIND - 1))
 128 
 129 if [[ $# -ne 1 ]]; then
 130         print -u2 "usage: $usage"
 131         exit 1
 132 fi
 133 
 134 CODEMGR_WS="$1"
 135 [[ "$CODEMGR_WS" = /* ]] || CODEMGR_WS="$(pwd)/$CODEMGR_WS"
 136 export CODEMGR_WS
 137 
 138 if [[ -n "$hg_ws" ]]; then
 139         if [[ ! -d "$hg_ws" || ! -d "$hg_ws/.hg" ]]; then
 140                 fail "$hg_ws is not a Mercurial workspace."
 141         fi
 142         [[ "$hg_ws" = /* ]] || hg_ws="$(pwd)/$hg_ws"
 143 fi
 144 
 145 [[ -d "$CODEMGR_WS" ]] || fail "$CODEMGR_WS does not exist."
 146 cd "$CODEMGR_WS"
 147 
 148 codemgr_parent=$(workspace parent)
 149 [[ -n "$codemgr_parent" ]] || \
 150     fail "$CODEMGR_WS is not a Teamware workspace or does not have a parent."
 151 [[ -d "$codemgr_parent" ]] || fail "parent ($codemgr_parent) doesn't exist."
 152 
 153 primary_twin=""
 154 nested_twins=""
 155 twinfile="$codemgr_parent"/Codemgr_wsdata/hg_twin
 156 if [[ -f $twinfile ]]; then
 157         primary_twin=$(head -1 $twinfile)
 158         nested_twins=$($tail -n +2 $twinfile)
 159 fi
 160 
 161 if has_hg_twin; then
 162         echo "Teamware parent $codemgr_parent has twin $primary_twin"
 163         [[ -n "$nested_twins" ]] &&
 164             echo "and nested twins $nested_twins"
 165 fi
 166 
 167 #
 168 # Do this check before time-consuming operations like creating
 169 # the target repo.
 170 #
 171 his=$(find Codemgr_wsdata -name history -mtime -1)
 172 if [[ -z "$his" ]]; then
 173         warn "history file is more than one day old; do you need to" \
 174             "bringover from $codemgr_parent?"
 175 fi
 176 
 177 # Less time-consuming than cloning
 178 
 179 if [[ ! -d wx ]]; then
 180         print "Initializing wx..."
 181         wx init -ft
 182 else
 183         if [[ "$do_update" = yes ]]; then
 184                 print "Updating wx state..."
 185                 wx update
 186         fi
 187 fi
 188 
 189 wx outchk
 190 
 191 out_files=$(wx out)
 192 active_files=$(wx list)
 193 
 194 if [[ ! -z "$out_files" ]]; then
 195         fail "wx2hg will only migrate checked-in files;" \
 196             "please check in these files with wx ci and try again"
 197 fi
 198 
 199 # more time-consuming than wx update and wx outchk
 200 
 201 if [[ -z "$hg_ws" ]]; then
 202         ws=$(basename $(pwd))
 203         hg_ws=$(dirname $(pwd))/"$ws-hg"
 204 fi
 205 
 206 if [[ -d "$hg_ws" ]]; then
 207         echo "Updating preexisting Mercurial workspace $hg_ws to $hg_rev\n"
 208         (cd "$hg_ws"; hg update -C $hg_rev) ||
 209             fail "hg update $hg_rev failed for $hg_ws"
 210         if [[ -n "$nested_twins" ]]; then
 211                 update_warning=n
 212                 for dir in $nested_twins; do
 213                         if [[ ! -d "$hg_ws/$dir" ]]; then
 214                                 warn "$hw_ws/$dir does not exist"
 215                                 update_warning=y
 216                         fi
 217                         echo "Updating preexisting nested workspace " \
 218                             "$hg_ws/$dir to $hg_rev\n"
 219                         (cd "$hg_ws"/$dir ; hg update -C $hg_rev)
 220                         if (( $? != 0 )); then
 221                                 warn "hg update $hg_rev failed for $hg_ws/$dir"
 222                                 update_warning=y
 223                                 continue
 224                         fi
 225                 done
 226 
 227                 [[ $update_warning = "n" ]] ||
 228                     fail "When using an existing Mercurial workspace with\n" \
 229                         "nested repositories, all nested repositories must\n" \
 230                         "already exist in the existing workspace.  If also\n" \
 231                         "specifying -r, then the specified hg_rev must be\n" \
 232                         "valid in all nested repositories."
 233         fi
 234 else
 235         if has_hg_twin; then
 236                 clone_twins "$hg_ws" $hg_rev
 237         else
 238                 fail "$codemgr_parent is not recognized as a gate;" \
 239                     "please provide a Mercurial workspace (-t hg_ws)" \
 240                     "that matches it."
 241         fi
 242 fi
 243 
 244 can_retry=yes
 245 
 246 # Make sure hg_ws is an absolute path
 247 [[ "$hg_ws" = /* ]] || hg_ws="$(pwd)/$hg_ws"
 248 
 249 
 250 # usage: which_repo filename
 251 function which_repo {
 252         typeset f=$1
 253 
 254         for r in $nested_twins; do
 255                 if [ ${f##$r/} != $f ]; then
 256                         echo ${f##$r/} $r
 257                         return
 258                 fi
 259         done
 260 
 261         echo $f "."
 262 }
 263 
 264 #
 265 # Do renames first, because they'll be listed with the new name by "wx
 266 # list".  There's a conflict if the new name already exists or if the
 267 # old name does not exist.  We can theoretically recover from the
 268 # former (move the existing file out of the way, or pick a different
 269 # new name), but not the latter.  For now, just error out and let the
 270 # user fix up the workspace so that there isn't a conflict.
 271 #
 272 
 273 renamelist=/tmp/wxrename$$
 274 wx renamed > "$renamelist"
 275 
 276 # usage: do_rename oldname newname
 277 function do_rename {
 278         typeset old_file old_repo new_file new_repo
 279 
 280         which_repo $1 | read old_file old_repo
 281         which_repo $2 | read new_file new_repo
 282 
 283         typeset old=$old_repo/$old_file
 284         typeset new=$new_repo/$new_file
 285 
 286         [[ -f "$old" ]] || fail "can't rename: $old doesn't exist."
 287         [[ ! -f "$new" ]] || fail "can't rename: $new already exists."
 288 
 289         dir=$(dirname "$new")
 290         base=$(basename "$new")
 291         [[ -d "$dir" ]] || mkdir -p "$dir" || fail "mkdir failed"
 292 
 293         if [ $old_repo = $new_repo ]; then
 294                 print "rename $old -> $new"
 295                 set -x
 296                 ( cd $old_repo; hg mv $old_file $new_file ) || \
 297                     fail "rename failed."
 298                 set +x
 299         else
 300                 print "moving $old_file from repository $old_repo"
 301                 print "to $new_file in repository $new_repo"
 302                 cp $old $new
 303                 set -x
 304                 ( cd $old_repo; hg rm $old_file ) || fail "hg rm failed"
 305                 ( cd $new_repo; hg add $new_file ) || fail "hg add failed"
 306                 set +x
 307         fi
 308 }
 309 
 310 if [[ -s "$renamelist" ]]; then
 311         cat "$renamelist" | (
 312                 cd "$hg_ws"
 313                 while :; do
 314                         read newname oldname
 315                         [[ -n "$newname" ]] || break
 316                         do_rename "$oldname" "$newname"
 317                 done
 318         ) || exit 1
 319 fi
 320 
 321 #
 322 # usage: name_in_parent fname
 323 # If fname had been renamed, echo the old name.  Otherwise echo the
 324 # given name.
 325 #
 326 function name_in_parent {
 327         typeset new old
 328 
 329         if [[ -s "$renamelist" ]]; then
 330                 cat "$renamelist" | while :; do
 331                         read new old
 332                         [[ -n "$new" ]] || break
 333                         if [[ "$1" = "$new" ]]; then
 334                                 print "$old"
 335                                 return
 336                         fi
 337                 done
 338         fi
 339         print "$1"
 340 }
 341 
 342 #
 343 # Now do content changes.  There's a likely conflict if the file in
 344 # Mercurial is different from the file in the Teamware parent.
 345 #
 346 
 347 parentfile=/tmp/parent$$
 348 patchfile=/tmp/patch$$
 349 childfile=/tmp/child$$
 350 
 351 [[ -n "$active_files" ]] || warn "no files in active list."
 352 
 353 for f in $active_files; do
 354         #
 355         # Get the name that the file appears in the parent as.
 356         #
 357         oldname=$(name_in_parent "$f")
 358 
 359         # We need unexpanded SCCS keywords for both parent and child
 360         sccs get -skp "$f" > "$childfile"
 361 
 362         if [[ -f "$codemgr_parent/$oldname" ]]; then
 363                 (cd $codemgr_parent; sccs get -skp "$oldname" > "$parentfile")
 364         else
 365                 rm -f $parentfile
 366         fi
 367 
 368         if [[ ! -r "$parentfile" ]]; then
 369                 print "new file: $f"
 370                 [[ ! -f "$hg_ws/$f" ]] || fail "$f already exists in $hg_ws."
 371                 dir=$(dirname "$hg_ws/$f")
 372                 base=$(basename "$hg_ws/$f")
 373                 [[ -d "$dir" ]] || mkdir -p "$dir" || fail "mkdir failed"
 374                 cp "$childfile" "$hg_ws/$f" || fail "copy failed"
 375                 set -x
 376                 (cd "$dir" && hg add "$base") || fail "hg add failed."
 377                 set +x
 378         elif diff "$parentfile" "$hg_ws/$f" > /dev/null 2>&1; then
 379                 if diff -u "$parentfile" "$childfile" > "$patchfile"; then
 380                         print "skipping $f (unchanged)."
 381                         continue
 382                 fi
 383                 (cd "$hg_ws"; gpatch -F0 $f < "$patchfile")
 384                 [[ $? -eq 0 ]] || fail "$f: patch failed."
 385         else
 386                 diff -u "$parentfile" "$hg_ws/$f"
 387                 echo ""
 388 
 389                 fail "For file:\n\n\t$f\n\nthe teamware parent:" \
 390                     "\n\n\t$codemgr_parent" \
 391                     "\n\ndoesn't match its mercurial twin;" \
 392                         "specify the matching revision in mercurial\nwith" \
 393                         "-r hg_rev, or resynchronize them.\n"
 394         fi
 395 done
 396 
 397 note "remember to commit your changes:"
 398 echo "in primary repository ${hg_ws}:"
 399 (  cd $hg_ws ; hg status -mard )
 400 for n in $nested_twins; do
 401         echo "in nested repository ${n}:"
 402         ( cd $hg_ws/$n ; hg status -mard )
 403 done
 404 
 405 if [[ "$hg_rev" != "tip" ]]; then
 406         note "before you integrate your changes, $hg_ws must be merged to tip"
 407 fi
 408 
 409 rm -f "$parentfile" "$patchfile" "$renamelist" "$childfile"
 410 
 411 exit 0
--- EOF ---