point to more example scripts
[shellsnippets/shellsnippets.git] / mksh / svn2cvs
1 #!/usr/bin/env mksh
2 id='$MirOS: contrib/hosted/tg/svn2cvs.sh,v 1.3 2009/05/17 13:10:42 tg Exp $'
3 #-
4 # Copyright (c) 2008
5 #       Thorsten Glaser <tg@mirbsd.org>
6 #
7 # Provided that these terms and disclaimer and all copyright notices
8 # are retained or reproduced in an accompanying document, permission
9 # is granted to deal in this work without restriction, including un-
10 # limited rights to use, publicly perform, distribute, sell, modify,
11 # merge, give away, or sublicence.
12 #
13 # This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
14 # the utmost extent permitted by applicable law, neither express nor
15 # implied; without malicious intent or gross negligence. In no event
16 # may a licensor, author or contributor be held liable for indirect,
17 # direct, other damage, loss, or other issues arising in any way out
18 # of dealing in the work, even if advised of the possibility of such
19 # damage or existence of a defect, except proven that it results out
20 # of said person's immediate fault when using the work as intended.
21
22 export LC_ALL=C TZ=UTC RCSINIT='-x,v -z'
23 unset LANGUAGE
24 nl='
25 '
26 saveIFS=$IFS
27 id=${id#\$}
28 id=${id%\$}
29 be='\e[1;31m'
30 bm='\e[1;32m'
31 bi='\e[1m'
32 bo='\e[0m'
33
34 print -n $bo\\r
35 i=0
36 if ! whence -p cmp >&-; then
37         print -u2 ${be}Error${bo}: you must install cmp to continue.
38         i=1
39 fi
40 if ! whence -p cvs >&-; then
41         print -u2 ${be}Error${bo}: you must install GNU CVS to continue.
42         i=1
43 fi
44 if ! whence -p rcs >&- || ! whence -p ci >&- || ! whence -p co >&-; then
45         print -u2 ${be}Error${bo}: you must install GNU RCS to continue.
46         i=1
47 fi
48 if ! whence -p svn >&-; then
49         print -u2 ${be}Error${bo}: you must install Subversion to continue.
50         i=1
51 fi
52 (( i )) && exit 1
53
54
55 function die {
56         print -u2 -- "${be}$*${bo}"
57         exit 1
58 }
59
60 function usage {
61         print -u2 "${bi}Syntax${bo}:\tmksh svn2cvs.sh [-ht] [-B baserev] [-H headrev]"
62         print -u2 "\t[-m module] -r repo -s svnurl"
63         print -u2 "Normal conversion goes from baserev (1) to headrev (HEAD)"
64         print -u2 "WARNING: This script is not yet whitespace-in-pathname safe!"
65         print -u2 "\nExample:"
66         print -u2 \$ mksh svn2cvs -t -r /cvs -s \
67             svn+ssh://www.FreeWRT.org/svn/trunk
68         print -u2 \$ mksh svn2cvs -B 10000 -r /cvs -m OpenWrt/packages/utils/mksh \\
69         print -u2 \\t-s https://svn.openwrt.org/openwrt/packages/utils/mksh
70         print -u2 \$ mksh svn2cvs -r /cvs -m BSDanywhere -s \
71             svn://svn.startek.ch/BSDanywhere/trunk
72         exit ${1:-1}
73 }
74
75
76 flag_t=0
77 baserev=1
78 headrev=HEAD
79 module=
80 repo=
81 url=
82
83 while getopts "B:H:hm:r:s:t" ch; do
84         case $ch {
85         (B)     [[ $OPTARG = +([0-9]) ]] || die base revision "'$OPTARG'" \
86                     not numeric
87                 baserev=$OPTARG
88                 ;;
89         (H)     [[ $OPTARG = +([0-9]) ]] || die head revision "'$OPTARG'" \
90                     not numeric
91                 headrev=$OPTARG
92                 ;;
93         (h)     usage 0
94                 ;;
95         (m)     module=${OPTARG##*(/)}
96                 ;;
97         (r)     repo=${OPTARG%%*(/)}
98                 ;;
99         (s)     url=${OPTARG%%*(/)}
100                 ;;
101         (t)     flag_t=1
102                 ;;
103         (*)     usage
104                 ;;
105         }
106 done
107 shift $((OPTIND - 1))
108
109 [[ -z $repo || -z $url ]] && usage
110
111 [[ $repo = :* ]] && die remote repositories are not allowed
112 [[ $repo = /* ]] || repo=$(pwd)/$repo
113
114 if [[ ! -d $repo/CVSROOT/. ]]; then
115         print -u2 ${bi}=== Initialising CVS repository "'$repo'"${bo}
116         mkdir -p "$repo" || die cannot create CVS repository at "'$repo'"
117         cvs -d "$repo" init || die cannot initialise CVS repository \
118             at "'$repo'"
119 fi
120
121 [[ -n $module ]] || module=${url##*/}
122 R=$repo/$module
123 [[ -d $R/. ]] || mkdir -p "$R" || die cannot create module "'$R'"
124 rp=${R%/*}
125 bp=${R##*/}
126
127 [[ $url = -* ]] && die SVN repository URL cannot begin with a dash
128 [[ $module = -* ]] && die CVS module cannot begin with a dash
129 [[ $bp = -* ]] && die last component of CVS module cannot begin with a dash
130
131 T=$(mktemp -d ${TMPDIR:-/tmp}/svn2cvs.XXXXXXXXXXXX) || die cannot create \
132     temporary directory
133 trap "cd /; rm -rf $T; exit 0" 0
134 trap "cd /; rm -rf $T; exit 1" 1 2 3 5 13 15
135
136 cd "$T"
137 mkdir c s
138
139 print -u2 ${bi}=== Preparing CVS module "'$R'" for operation${bo}
140 find "$R" -name '*,v' >f.rcs
141 if [[ -s f.rcs ]]; then
142         xargs chmod ug=r,o-x <f.rcs
143         xargs rcs -kb <f.rcs
144 fi
145
146 print -u2 ${bi}=== Initial checkout of CVS "$R"${bo}
147 (cd c; cvs -qd "$repo" co -PA -d "$bp" "$module") || die cannot checkout sources
148
149 svn log -r $baserev:$headrev "$url" >log
150 if [[ ! -s f.rcs && $(wc -l <log) -lt 2 ]]; then
151         i=$(svn info "$url" | sed -n 's/Last Changed Rev: //p')
152         if [[ $headrev = HEAD ]] || (( (i < headrev) && (i > baserev) )); then
153                 print -u2 "${be}*** Fabricating commit for r$i as" \
154                     r${baserev}:${headrev} are empty and no ,v files$bo
155                 baserev=$i
156         fi
157         svn log -r $baserev:$headrev "$url" >log
158 fi
159
160 cat log |&
161 i=0
162 while IFS= read -pr line; do
163         if [[ $i != 0 && $line = ------------------------------------------------------------------------ ]]; then
164                 logmsg=${logmsg%%*($nl)}
165                 print -u2 ${bi}=== Got log message for revision $i by $author \
166                     on $ymd $hms:${bo}
167                 print -r -- "$logmsg" | sed "s/^/${bm}>>>${bo} /" >&2
168
169                 if [[ -d s/$bp/. ]]; then
170                         print -u2 ${bi}=== Updating SVN checkout to \
171                             revision $i${bo}
172                         (cd "s/$bp"; svn up -r$i) || die cannot update sources
173                 else
174                         print -u2 ${bi}=== Initial checkout of SVN \
175                             "${url}@$baserev"${bo}
176                         (cd s; svn co -r $i "$url" "$bp") || die cannot \
177                             checkout sources
178                 fi
179
180                 print -u2 ${bi}=== Generating list of files${bo}
181                 rm -f f.*
182                 for dir in c s; do
183                         (cd $dir; find "$bp" -type f) | \
184                             fgrep -v -e /CVS/ -e /.svn/ | \
185                             sort >f.$dir
186                 done
187
188                 IFS=$nl
189                 set -A cfiles -- $(<f.c)
190                 set -A sfiles -- $(<f.s)
191                 IFS=$saveIFS
192
193                 ic=0; nc=${#cfiles[*]}
194                 is=0; ns=${#sfiles[*]}
195                 while (( (ic < nc) || (is < ns) )); do
196                         if (( ic == nc )); then
197                                 print -r -- "${sfiles[is++]}" >>f.add
198                         elif (( is == ns )); then
199                                 print -r -- "${cfiles[ic++]}" >>f.del
200                         elif [[ ${cfiles[ic]} = ${sfiles[is]} ]]; then
201                                 cmp -s "c/${cfiles[ic]}" "s/${sfiles[is]}" || \
202                                     print -r -- "${cfiles[ic]}" >>f.chg
203                                 let ++ic ++is
204                         elif [[ ${cfiles[ic]} < ${sfiles[is]} ]]; then
205                                 print -r -- "${cfiles[ic++]}" >>f.del
206                         else
207                                 print -r -- "${sfiles[is++]}" >>f.add
208                         fi
209                 done
210                 # clear the possibly huge arrays
211                 set -A cfiles
212                 set -A sfiles
213
214                 [[ -e f.del ]] && print -u2 ${bi}=== Handling file deletions${bo}
215                 [[ -e f.del ]] && (cd "$rp"; while IFS= read -r fn; do
216                         co -l "$fn"
217                         ci -f -d"${ymd} ${hms}+00" -m"$logmsg" -T -w"$author" \
218                             -sdead "$fn"
219                         [[ -d ${fn%/*}/Attic/. ]] || mkdir -p "${fn%/*}/Attic"
220                         mv "${fn},v" "${fn%/*}/Attic/"
221                 done) <f.del
222
223                 [[ -e f.add ]] && print -u2 ${bi}=== Preparing file additions${bo}
224                 [[ -e f.add ]] && (cd "$rp"; while IFS= read -r fn; do
225                         if [[ -e ${fn%/*}/Attic/${fn##*/},v ]]; then
226                                 mv "${fn%/*}/Attic/${fn##*/},v" "${fn%/*}/"
227                         else
228                                 [[ -d ${fn%/*}/. ]] || mkdir -p "${fn%/*}"
229                                 rcs -i -kb -L \
230                                     -t-"autoconverted by $id from ${url}/${fn#$bp/}" \
231                                     -T "$fn"
232                         fi
233                 done) <f.add
234
235                 [[ -e f.add ]] && cat f.add >>f.chg
236                 if [[ -e f.chg ]]; then
237                         print -u2 ${bi}=== Handling content changes${bo}
238                         (cd "$rp"; xargs co -l) <f.chg
239                         while IFS= read -r fn; do
240                                 cat "s/$fn" >"$rp/$fn"
241                         done <f.chg
242                         (cd "$rp"; xargs ci -f -d"${ymd} ${hms}+00" \
243                             -m"$logmsg" -T -w"$author" -sr$i) <f.chg
244                 fi
245
246                 print -u2 ${bi}=== Updating CVS checkout to HEAD${bo}
247                 (cd "c/$bp"; CVSREADONLYFS=1 cvs -q up -PAd) || die cannot \
248                     update sources
249
250                 if (( flag_t )); then
251                         print -u2 ${bi}=== Tagging deposited revision $i${bo}
252                         (cd "c/$bp"; cvs -q tag -F From_SVN_r$i)
253                 fi
254         fi
255
256         if [[ $line = ------------------------------------------------------------------------ ]]; then
257                 read -pr i x author y ymd hms line || break
258                 i=${i#r}
259                 logmsg=
260                 print -u2 ${bi}=== Begin parsing revision $i${bo}
261                 read -pr line || break
262         else
263                 logmsg=$logmsg${logmsg:+$nl}$line
264         fi
265 done
266
267 print -u2 ${bi}=== Fixing up properties${bo}
268 if [[ -d s/$bp/. ]]; then
269         (cd "s/$bp"; svn proplist -v -R .; print Properties end) |&
270         rm -f binlist
271         while read -p x y z line; do
272                 if [[ $x = Properties ]]; then
273                         fn=${z#\'}
274                         fn=${fn%\':}
275                         [[ -f $R/${fn},v ]] || fn=
276                 elif [[ -z $fn ]]; then
277                         continue
278                 elif [[ $x = svn:executable ]]; then
279                         chmod +x "$R/${fn},v"
280                 elif [[ $x = svn:mime-type && $z = application/* ]]; then
281                         print -r -- "$bp/${fn},v" >>binlist
282                 fi
283         done
284 fi
285 (cd "$rp"; find "$bp" -name '*,v') >f.rcs
286 [[ -s f.rcs ]] && (cd "$rp"; xargs rcs -kkv) <f.rcs
287 [[ -e binlist ]] && (cd "$rp"; xargs rcs -kb) <binlist
288
289 print -u2 ${bm}=== All done.${bo}
290 cd /
291 rm -rf "$T"
292 exit 0