Fossil SCM

fossil-scm / tools / cvs2fossil / lib / c2f_pinitcsets.tcl
Blame History Raw 380 lines
1
## -*- tcl -*-
2
# # ## ### ##### ######## ############# #####################
3
## Copyright (c) 2007-2008 Andreas Kupries.
4
#
5
# This software is licensed as described in the file LICENSE, which
6
# you should have received as part of this distribution.
7
#
8
# This software consists of voluntary contributions made by many
9
# individuals. For exact contribution history, see the revision
10
# history and logs, available at http://fossil-scm.hwaci.com/fossil
11
# # ## ### ##### ######## ############# #####################
12
13
## Pass V. This pass creates the initial set of project level
14
## revisions, aka changesets. Later passes will refine them, puts them
15
## into proper order, set their dependencies, etc.
16
17
# # ## ### ##### ######## ############# #####################
18
## Requirements
19
20
package require Tcl 8.4 ; # Required runtime.
21
package require snit ; # OO system.
22
package require vc::tools::misc ; # Text formatting.
23
package require vc::tools::log ; # User feedback.
24
package require vc::tools::mem ; # Memory tracking.
25
package require vc::fossil::import::cvs::repository ; # Repository management.
26
package require vc::fossil::import::cvs::state ; # State storage.
27
package require vc::fossil::import::cvs::integrity ; # State integrity checks.
28
package require vc::fossil::import::cvs::project::rev ; # Project level changesets
29
30
# # ## ### ##### ######## ############# #####################
31
## Register the pass with the management
32
33
vc::fossil::import::cvs::pass define \
34
InitCsets \
35
{Initialize ChangeSets} \
36
::vc::fossil::import::cvs::pass::initcsets
37
38
# # ## ### ##### ######## ############# #####################
39
##
40
41
snit::type ::vc::fossil::import::cvs::pass::initcsets {
42
# # ## ### ##### ######## #############
43
## Public API
44
45
typemethod setup {} {
46
# Define the names and structure of the persistent state of
47
# this pass.
48
49
state use project
50
state use file
51
state use revision
52
state use revisionbranchchildren
53
state use branch
54
state use tag
55
state use symbol
56
state use meta
57
58
# Data per changeset, namely the project it belongs to, how it
59
# was induced (revision or symbol), plus reference to the
60
# primary entry causing it (meta entry or symbol). An adjunct
61
# table translates the type id's into human readable labels.
62
63
state extend changeset {
64
cid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
65
pid INTEGER NOT NULL REFERENCES project,
66
type INTEGER NOT NULL REFERENCES cstype,
67
src INTEGER NOT NULL -- REFERENCES meta|symbol (type dependent)
68
}
69
state extend cstype {
70
tid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
71
name TEXT NOT NULL,
72
UNIQUE (name)
73
}
74
# Note: Keep the labels used here in sync with the names for
75
# singleton helper classes for 'project::rev'. They are
76
# the valid type names for changesets and also hardwired
77
# in some code.
78
state run {
79
INSERT INTO cstype VALUES (0,'rev');
80
INSERT INTO cstype VALUES (1,'sym::tag');
81
INSERT INTO cstype VALUES (2,'sym::branch');
82
}
83
84
# Map from changesets to the (file level) revisions, tags, or
85
# branches they contain. The pos'ition provides an order of
86
# the items within a changeset. They are unique within the
87
# changeset. The items are in principle unique, if we were
88
# looking only at relevant changesets. However as they come
89
# from disparate sources the same id may have different
90
# meaning, be in different changesets and so is formally not
91
# unique. So we can only say that it is unique within the
92
# changeset. The integrity module has stronger checks.
93
94
state extend csitem {
95
cid INTEGER NOT NULL REFERENCES changeset,
96
pos INTEGER NOT NULL,
97
iid INTEGER NOT NULL, -- REFERENCES revision|tag|branch
98
UNIQUE (cid, pos),
99
UNIQUE (cid, iid)
100
} { iid }
101
# Index on: iid (successor/predecessor retrieval)
102
103
project::rev getcstypes
104
return
105
}
106
107
typemethod load {} {
108
# Pass manager interface. Executed to load data computed by
109
# this pass into memory when this pass is skipped instead of
110
# executed.
111
112
state use changeset
113
state use csitem
114
state use cstype
115
116
# Need the types first, the constructor used inside of the
117
# 'load' below uses them to assert the correctness of type
118
# names.
119
project::rev getcstypes
120
project::rev load ::vc::fossil::import::cvs::repository
121
project::rev loadcounter
122
return
123
}
124
125
typemethod run {} {
126
# Pass manager interface. Executed to perform the
127
# functionality of the pass.
128
129
state transaction {
130
CreateRevisionChangesets ; # Group file revisions into
131
# preliminary csets and split
132
# them based on internal
133
# conflicts.
134
CreateSymbolChangesets ; # Create csets for tags and
135
# branches.
136
}
137
138
repository printcsetstatistics
139
integrity changesets
140
141
# Load the changesets for use by the next passes.
142
project::rev load ::vc::fossil::import::cvs::repository
143
project::rev loadcounter
144
return
145
}
146
147
typemethod discard {} {
148
# Pass manager interface. Executed for all passes after the
149
# run passes, to remove all data of this pass from the state,
150
# as being out of date.
151
152
state discard changeset
153
state discard cstype
154
state discard csitem
155
return
156
}
157
158
# # ## ### ##### ######## #############
159
## Internal methods
160
161
proc CreateRevisionChangesets {} {
162
log write 3 initcsets {Create changesets based on revisions}
163
164
# To get the initial of changesets we first group all file
165
# level revisions using the same meta data entry together. As
166
# the meta data encodes not only author and log message, but
167
# also line of development and project we can be sure that
168
# revisions in different project and lines of development are
169
# not grouped together. In contrast to cvs2svn we do __not__
170
# use distance in time between revisions to break them
171
# apart. We have seen CVS repositories (from SF) where a
172
# single commit contained revisions several hours apart,
173
# likely due to trouble on the server hosting the repository.
174
175
# We order the revisions here by time, this will help the
176
# later passes (avoids joins later to get at the ordering
177
# info).
178
179
# The changesets made from these groups are immediately
180
# inspected for internal conflicts and any such are broken by
181
# splitting the problematic changeset into multiple
182
# fragments. The results are changesets which have no internal
183
# dependencies, only external ones.
184
185
set n 0
186
set nx 0
187
188
set lastmeta {}
189
set lastproject {}
190
set revisions {}
191
192
# Note: We could have written this loop to create the csets
193
# early, extending them with all their revisions. This
194
# however would mean lots of (slow) method invokations
195
# on the csets. Doing it like this, late creation, means
196
# less such calls. None, but the creation itself.
197
198
log write 14 initcsets meta_begin
199
mem::mark
200
state foreachrow {
201
SELECT M.mid AS xmid,
202
R.rid AS xrid,
203
M.pid AS xpid
204
FROM revision R,
205
meta M -- R ==> M, using PK index of M.
206
WHERE R.mid = M.mid
207
ORDER BY M.mid, R.date
208
} {
209
log write 14 initcsets meta_next
210
211
if {$lastmeta != $xmid} {
212
if {[llength $revisions]} {
213
incr n
214
set p [repository projectof $lastproject]
215
log write 14 initcsets meta_cset_begin
216
mem::mark
217
set cset [project::rev %AUTO% $p rev $lastmeta $revisions]
218
log write 14 initcsets meta_cset_done
219
set spawned [$cset breakinternaldependencies nx]
220
$cset persist
221
$cset destroy
222
foreach cset $spawned { $cset persist ; $cset destroy }
223
mem::mark
224
set revisions {}
225
}
226
set lastmeta $xmid
227
set lastproject $xpid
228
}
229
lappend revisions $xrid
230
}
231
232
if {[llength $revisions]} {
233
incr n
234
set p [repository projectof $lastproject]
235
log write 14 initcsets meta_cset_begin
236
mem::mark
237
set cset [project::rev %AUTO% $p rev $lastmeta $revisions]
238
log write 14 initcsets meta_cset_done
239
set spawned [$cset breakinternaldependencies nx]
240
$cset persist
241
$cset destroy
242
foreach cset $spawned { $cset persist ; $cset destroy }
243
mem::mark
244
}
245
246
log write 14 initcsets meta_done
247
mem::mark
248
249
log write 4 initcsets "Created and saved [nsp $n {revision changeset}]"
250
log write 4 initcsets "Created and saved [nsp $nx {additional revision changeset}]"
251
252
mem::mark
253
log write 4 initcsets Ok.
254
return
255
}
256
257
proc CreateSymbolChangesets {} {
258
log write 3 initcsets {Create changesets based on symbols}
259
mem::mark
260
261
# Tags and branches induce changesets as well, containing the
262
# revisions they are attached to (tags), or spawned from
263
# (branches).
264
265
set n 0
266
267
# First process the tags, then the branches. We know that
268
# their ids do not overlap with each other.
269
270
set lastsymbol {}
271
set lastproject {}
272
set tags {}
273
274
state foreachrow {
275
SELECT S.sid AS xsid,
276
T.tid AS xtid,
277
S.pid AS xpid
278
FROM tag T,
279
symbol S -- T ==> R/S, using PK indices of R, S.
280
WHERE T.sid = S.sid
281
ORDER BY S.sid, T.tid
282
} {
283
if {$lastsymbol != $xsid} {
284
if {[llength $tags]} {
285
incr n
286
set p [repository projectof $lastproject]
287
set cset [project::rev %AUTO% $p sym::tag $lastsymbol $tags]
288
set tags {}
289
$cset persist
290
$cset destroy
291
}
292
set lastsymbol $xsid
293
set lastproject $xpid
294
}
295
lappend tags $xtid
296
}
297
298
if {[llength $tags]} {
299
incr n
300
set p [repository projectof $lastproject]
301
set cset [project::rev %AUTO% $p sym::tag $lastsymbol $tags]
302
$cset persist
303
$cset destroy
304
}
305
306
set lastsymbol {}
307
set lasproject {}
308
set branches {}
309
310
state foreachrow {
311
SELECT S.sid AS xsid,
312
B.bid AS xbid,
313
S.pid AS xpid
314
FROM branch B,
315
symbol S -- B ==> R/S, using PK indices of R, S.
316
WHERE B.sid = S.sid
317
ORDER BY S.sid, B.bid
318
} {
319
if {$lastsymbol != $xsid} {
320
if {[llength $branches]} {
321
incr n
322
set p [repository projectof $lastproject]
323
set cset [project::rev %AUTO% $p sym::branch $lastsymbol $branches]
324
set branches {}
325
$cset persist
326
$cset destroy
327
}
328
set lastsymbol $xsid
329
set lastproject $xpid
330
}
331
lappend branches $xbid
332
}
333
334
if {[llength $branches]} {
335
incr n
336
set p [repository projectof $lastproject]
337
set cset [project::rev %AUTO% $p sym::branch $lastsymbol $branches]
338
$cset persist
339
$cset destroy
340
}
341
342
log write 4 initcsets "Created and saved [nsp $n {symbol changeset}]"
343
mem::mark
344
return
345
}
346
347
# # ## ### ##### ######## #############
348
## Configuration
349
350
pragma -hasinstances no ; # singleton
351
pragma -hastypeinfo no ; # no introspection
352
pragma -hastypedestroy no ; # immortal
353
354
# # ## ### ##### ######## #############
355
}
356
357
namespace eval ::vc::fossil::import::cvs::pass {
358
namespace export initcsets
359
namespace eval initcsets {
360
namespace import ::vc::fossil::import::cvs::repository
361
namespace import ::vc::fossil::import::cvs::state
362
namespace import ::vc::fossil::import::cvs::integrity
363
namespace eval project {
364
namespace import ::vc::fossil::import::cvs::project::rev
365
}
366
namespace eval mem {
367
namespace import ::vc::tools::mem::mark
368
}
369
namespace import ::vc::tools::misc::*
370
namespace import ::vc::tools::log
371
log register initcsets
372
}
373
}
374
375
# # ## ### ##### ######## ############# #####################
376
## Ready
377
378
package provide vc::fossil::import::cvs::pass::initcsets 1.0
379
return
380

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button