xref: /onnv-gate/usr/src/tools/scripts/wx2hg.sh (revision 8786:0ce587c60878)
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