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