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