-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcvs2git
247 lines (232 loc) · 8.14 KB
/
cvs2git
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#!/bin/mksh
# -*- mode: sh -*-
#-
# Copyright © 2014, 2015, 2017, 2020
# mirabilos <[email protected]>
#
# Provided that these terms and disclaimer and all copyright notices
# are retained or reproduced in an accompanying document, permission
# is granted to deal in this work without restriction, including un‐
# limited rights to use, publicly perform, distribute, sell, modify,
# merge, give away, or sublicence.
#
# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
# the utmost extent permitted by applicable law, neither express nor
# implied; without malicious intent or gross negligence. In no event
# may a licensor, author or contributor be held liable for indirect,
# direct, other damage, loss, or other issues arising in any way out
# of dealing in the work, even if advised of the possibility of such
# damage or existence of a defect, except proven that it results out
# of said person’s immediate fault when using the work as intended.
#-
# Depends: cvs (>= 2:1.12.13+real-18), cvs2svn (>= 2.4.0-4)
# needs mksh R50 2014/06/10
set -o pipefail
mydir=$(dirname "$(realpath "$0")")
cfg=${1%%.cfg}.cfg
for tool in cvs cvs2git git; do
var=$(whence -p $tool) && [[ -n $var && -x $var ]] && continue
print -ru2 "E: tool $tool missing"
exit 1
done
cd "$mydir"
if [[ -z $cfg || $cfg = */* || ! -s $cfg ]]; then
print -ru2 "E: configuration file '$cfg' not found"
exit 1
fi
set -A pprm_branches
set -A pprm_tags
set -A torm_branches
set -A torm_tags
set -A extra_grafts
set -A remote
unset domain source prepend cvs2git_username
do_precollect() { :; }
do_extrafetch() { :; } # make this set do_grafts=1 if used
. "$mydir/$cfg"
for var in source remote; do
eval '[[ -n $'$var' ]]' && continue
print -ru2 "E: config file '$cfg' var $var not set"
exit 1
done
if [[ ! -d $source/. ]]; then
print -ru2 "E: config file '$cfg' source '$source' does not exist"
exit 1
fi
[[ -n $cvs2git_username ]] || if [[ -n $domain ]]; then
cvs2git_username=root
else
cvs2git_username=cvs2git
fi
# not in /tmp due to sheer size of tmp files
if ! T=$(mktemp -d /var/tmp/cvs2git.XXXXXXXXXX); then
print -u2 "E: mkdtemp failed"
exit 255
fi
print -ru2 "I: Entering working area '$T'"
cd "$T"
function cleanup_and_die {
cd /
#print -ru2 \
rm -rf "$T"
exit ${1:-1}
}
trap cleanup_and_die 1 2 3 5 13 15
function die {
print -ru2 "E: $*"
cleanup_and_die
}
(do_precollect) || die do_precollect failed
cat >cvswrapper <<-EOF
#!/bin/mksh
cvs "\$@" 2>${T@Q}/cvswrapped; rv=\$?
grep -E -v \
-e 'Danger: Granting read access to incompatible repository!' \
-e 'CVSROOT/config: found keyword .(tag|umask|DisableXProg). in repository' \
<${T@Q}/cvswrapped >&2
exit \$rv
EOF
chmod +x cvswrapper || die "chmod failed with errorlevel $?"
print -u2 "I: $SECONDS seconds elapsed for setup"
cvs2git --blobfile=blobfile --dumpfile=dumpfile \
--encoding=utf-8 --fallback-encoding=cp1252 \
--username="$cvs2git_username" --keywords-off --keep-cvsignore \
--use-cvs --cvs="$T"/cvswrapper \
--force-keyword-mode=kept ${domain:+--cvs-domain="$domain"} \
"$source" || die "cvs2git failed with errorlevel $?"
print -u2 "I: $SECONDS seconds elapsed for cvs2git"
git='git -c advice.graftFileDeprecated=false'
$git init --bare dst || die "git init failed with errorlevel $?"
cd dst
$git fast-import --export-marks=../cvs2git-tmp/git-marks.dat <../blobfile || \
die "git fast-import of blobfile failed with errorlevel $?"
print -u2 "I: $SECONDS seconds elapsed for blob import"
$git fast-import --import-marks=../cvs2git-tmp/git-marks.dat <../dumpfile || \
die "git fast-import of dumpfile failed with errorlevel $?"
print -u2 "I: $SECONDS seconds elapsed for dump import"
for rem in "${remote[@]}"; do
[[ $rem = /* && ! -d $rem/. ]] || continue
mkdir -p "$rem" || \
die "remote '$rem' not found and could not be created (with errorlevel $?) either"
$git init --bare "$rem" || die "git init '$rem' failed with errorlevel $?"
done
for var in "${torm_branches[@]}"; do
$git branch -D "$var" || die "git branch failed with errorlevel $?"
done
for var in "${torm_tags[@]}"; do
$git tag -d "$var" || die "git tag failed with errorlevel $?"
done
print -u2 "I: $SECONDS seconds elapsed for janitorial work"
do_grafts=0
if [[ -n $prepend ]]; then
# Urahn means ancestor… it’s hard to find terms to use that
# do not already have meaning in (D)VCS context…
$git remote add urahn "$prepend" || \
die "git remote failed with errorlevel $?"
# mixin tags
$git ls-remote --tags urahn | while read -r hash ref; do
if $git show-ref --verify --quiet "$ref"; then
# tag already exists
$git show-ref --verify "$ref" |&
read -pr exhash exref
print -ru2 "I: pull existing tag '$ref' ($hash vs $exhash)"
if [[ $hash = "$exhash" ]]; then
# they are the same, though, uniquify
continue
fi
# rename older tag, at most once
new=$ref@$hash
if $git show-ref --verify --quiet "$new"; then
# renamed tag also already exists
$git show-ref --verify "$new" |&
read -pr exhash exref
[[ $hash = "$exhash" ]] && continue
die "new ref '$new' already exists"
fi
else
# fetch the existing tag
print -ru2 "I: pull new tag '$ref' ($hash)"
new=$ref
fi
$git fetch -n urahn "$ref:$new" || \
die "git fetch failed with errorlevel $?"
done || die "git ls-remote failed with errorlevel $?"
# mixin branches
$git ls-remote --heads urahn | while read -r hash ref; do
if ! $git show-ref --verify --quiet "$ref"; then
# branch does not yet exist, simply pull it
print -ru2 "I: pull new branch '$ref' ($hash)"
$git fetch -n urahn "$ref:$ref" || \
die "git fetch failed with errorlevel $?"
continue
fi
print -ru2 "I: pull existing branch '$ref'"
# fetch objects belonging to the branch (old)
$git fetch -n urahn "$ref" || \
die "git fetch failed with errorlevel $?"
# find out the root commit of the branch (new)
exhash=$($git rev-list "$ref" | tail -n 1) || \
die "git rev-list | tail failed with errorlevel $?"
# create a graft point
print -r -- "$exhash" "$hash" >>info/grafts
print -ru2 "N: graft $exhash $hash"
done || die "git ls-remote failed with errorlevel $?"
# remove branches/tags that are not necessary to merge
for var in "${pprm_branches[@]}"; do
$git branch -D "$var" || die "git branch failed with errorlevel $?"
done
for var in "${pprm_tags[@]}"; do
$git tag -d "$var" || die "git tag failed with errorlevel $?"
done
do_grafts=1
fi
do_extrafetch
if (( do_grafts )); then
for var in "${extra_grafts[@]}"; do
print -r -- $var >>info/grafts
print -ru2 "N: extra graft" $var
done
if [[ -n $cvs2git_graft_debugging ]]; then
print -ru2 "!! Spawning a shell for graft debugging"
print -ru2 "N: Type 'exit' to continue conversion!"
mksh -l 0<>/dev/tty >&0 2>&0
fi
# drop extra remotes before doing this, for speed
for var in $($git remote); do
$git remote remove "$var" || \
die "git remote failed with errorlevel $?"
done
# make graft permanent
if [[ -s info/grafts ]]; then
FILTER_BRANCH_SQUELCH_WARNING=1 \
$git filter-branch --tag-name-filter cat -- --all || \
die "git filter-branch failed with errorlevel $?"
rm -f info/grafts
fi
# nuke junk
$git for-each-ref --format="%(refname)" refs/original/ | \
while IFS= read -r ref; do
$git update-ref -d "$ref" || \
die "git update-ref failed with errorlevel $?"
done || die "git for-each-ref failed with errorlevel $?"
print -u2 "I: $SECONDS seconds elapsed for history prepending"
fi
$git reflog expire --expire=all --all || die "git reflog failed with errorlevel $?"
$git gc --aggressive --prune=now || die "git gc failed with errorlevel $?"
print -u2 "I: $SECONDS seconds elapsed for garbage collection"
for rem in "${remote[@]}"; do
print -ru2 "D: pushing to $rem"
$git remote add target "$rem" || die "git remote failed with errorlevel $?"
$git push --mirror target || die "git push failed with errorlevel $?"
$git remote remove target || die "git remote failed with errorlevel $?"
done
print -u2 "I: $SECONDS seconds elapsed after pushing"
for rem in "${remote[@]}"; do
[[ $rem = /* ]] || continue
print -ru2 "D: gc’ing $rem"
cd "$rem" && $git gc
done
print -u2 "I: $SECONDS seconds elapsed after gc’ing"
cd /
rm -rf "$T"
exit 0