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 ---