Fossil SCM

fossil-scm / tools / cvs2fossil / lib / c2f_psym.tcl
Blame History Raw 423 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
## Symbols (Tags, Branches) per project.
14
15
# # ## ### ##### ######## ############# #####################
16
## Requirements
17
18
package require Tcl 8.4 ; # Required runtime.
19
package require snit ; # OO system.
20
package require vc::tools::trouble ; # Error reporting.
21
package require vc::tools::log ; # User feedback.
22
package require vc::tools::misc ; # Text formatting.
23
package require vc::fossil::import::cvs::state ; # State storage.
24
package require struct::set ; # Set handling.
25
26
# # ## ### ##### ######## ############# #####################
27
##
28
29
snit::type ::vc::fossil::import::cvs::project::sym {
30
# # ## ### ##### ######## #############
31
## Public API
32
33
constructor {name id project} {
34
set myname $name
35
set myid $id
36
set myproject $project
37
38
# Count total number of symbols.
39
incr mynum
40
return
41
}
42
43
method name {} { return $myname }
44
method id {} { return $myid }
45
46
method istrunk {} { return 0 }
47
48
method parent {} {
49
return [$myproject getsymbol [state run {
50
SELECT S.name
51
FROM preferedparent P, symbol S
52
WHERE P.sid = $myid
53
AND S.sid = P.pid
54
}]]
55
}
56
57
# # ## ### ##### ######## #############
58
## Symbol type
59
60
method determinetype {} {
61
# This is done by a fixed heuristics, with guidance by the
62
# user in edge-cases. Contrary to cvs2svn which uses a big
63
# honking streagy class and rule objects. Keep it simple, we
64
# can expand later when we actually need all the complexity
65
# for configurability.
66
67
# The following guidelines are applied:
68
# - Is usage unambigous ?
69
# - Was there ever a commit on the symbol ?
70
# - More used as tag, or more used as branch ?
71
# - At last, what has the user told us about it ?
72
# - Fail
73
74
foreach rule {
75
UserConfig
76
Unambigous
77
HasCommits
78
VoteCounts
79
} {
80
set chosen [$self $rule]
81
if {$chosen eq $myundef} continue
82
$self MarkAs $rule $chosen
83
return
84
}
85
86
# None of the above was able to decide which type to assign to
87
# the symbol. This is a fatal error preventing the execution
88
# of the passes after 'CollateSymbols'.
89
90
incr myrulecount(Undecided_)
91
trouble fatal "Unable to decide how to convert symbol '$myname'"
92
return
93
}
94
95
method markthetrunk {} { $self MarkAs IsTheTrunk $mybranch ; return }
96
97
# # ## ### ##### ######## #############
98
## Symbol statistics
99
100
method defcounts {tc bc cc} {
101
set mybranchcount $tc
102
set mytagcount $bc
103
set mycommitcount $cc
104
return
105
}
106
107
method countasbranch {} { incr mybranchcount ; return }
108
method countastag {} { incr mytagcount ; return }
109
method countacommit {} { incr mycommitcount ; return }
110
111
method blockedby {symbol} {
112
# Remember the symbol as preventing the removal of this
113
# symbol. Ot is a tag or branch that spawned from a revision
114
# on this symbol.
115
116
struct::set include myblockers $symbol
117
return
118
}
119
120
method possibleparent {symbol} {
121
log write 9 symbol "Possible parent ($myname) = [$symbol name]"
122
123
if {[info exists mypparent($symbol)]} {
124
incr mypparent($symbol)
125
} else {
126
set mypparent($symbol) 1
127
}
128
return
129
}
130
131
method isghost {} {
132
# Checks if this symbol (as line of development) never
133
# existed.
134
135
if {$mycommitcount > 0} { return 0 }
136
if {[llength $myblockers]} { return 0 }
137
if {[array size mypparent] > 0} { return 0 }
138
139
return 1
140
}
141
142
# # ## ### ##### ######## #############
143
144
method persistrev {} {
145
set pid [$myproject id]
146
147
state transaction {
148
state run {
149
INSERT INTO symbol ( sid, pid, name, type, tag_count, branch_count, commit_count)
150
VALUES ($myid, $pid, $myname, $myundef, $mytagcount, $mybranchcount, $mycommitcount);
151
}
152
foreach symbol $myblockers {
153
set bid [$symbol id]
154
state run {
155
INSERT INTO blocker (sid, bid)
156
VALUES ($myid, $bid);
157
}
158
}
159
foreach {symbol count} [array get mypparent] {
160
set pid [$symbol id]
161
state run {
162
INSERT INTO parent (sid, pid, n)
163
VALUES ($myid, $pid, $count);
164
}
165
}
166
}
167
return
168
}
169
170
# # ## ### ##### ######## #############
171
## State
172
173
variable myproject {} ; # Reference to the project object
174
# containing the symbol.
175
variable myname {} ; # The symbol's name
176
variable myid {} ; # Repository wide numeric id of the
177
# symbol. This implicitly encodes the
178
# project as well.
179
180
variable mybranchcount 0 ; # Count how many uses as branch.
181
variable mytagcount 0 ; # Count how many uses as tag.
182
variable mycommitcount 0 ; # Count how many files did a commit on the symbol.
183
184
variable myblockers {} ; # List (Set) of the symbols which block
185
# the exclusion of this symbol.
186
187
variable mypparent -array {} ; # Maps from symbols to the number
188
# of files in which it could have
189
# been a parent of this symbol.
190
191
variable mytype {} ; # The type chosen for the symbol to use in
192
# the conversion.
193
194
# # ## ### ##### ######## #############
195
196
typemethod exclude {pattern} {
197
# Store the pattern in memory for use by the code doing type
198
# determination.
199
200
lappend myexcludepattern [ProcessPattern $pattern exclusion]
201
return
202
}
203
204
typemethod forcetag {pattern} {
205
# Store the pattern in memory for use by the code doing type
206
# determination.
207
208
lappend myforcepattern [ProcessPattern $pattern force-tag] $mytag
209
return
210
}
211
212
typemethod forcebranch {pattern} {
213
# Store the pattern in memory for use by the code doing type
214
# determination.
215
216
lappend myforcepattern [ProcessPattern $pattern force-branch] $mybranch
217
return
218
}
219
220
proc ProcessPattern {pattern label} {
221
if {[string match *:*:* $pattern]} {
222
# Bad syntax for the pattern, using multiple colons.
223
224
trouble fatal "Bad $label pattern '$pattern'"
225
} elseif {![string match *:* $pattern]} {
226
# When only a symbol pattern is specified it applies to
227
# all projects.
228
229
return [list * $pattern]
230
} else {
231
# Both project and symbol patterns are present, we split
232
# them apart now for storage and easier extraction later.
233
234
return [split $pattern :]
235
}
236
}
237
238
typevariable myexcludepattern {} ; # List of patterns specifying
239
# the symbols to exclude from
240
# conversion. Tags and/or
241
# branches.
242
243
typevariable myforcepattern {} ; # List of patterns and types
244
# specifying which symbols to
245
# force to specific types.
246
247
typemethod getsymtypes {} {
248
state foreachrow {
249
SELECT tid, name FROM symtype;
250
} { set mysymtype($tid) $name }
251
return
252
}
253
254
# Keep the codes below in sync with 'pass::collrev/setup('symtype').
255
typevariable myexcluded 0 ; # Code for symbols which are excluded.
256
typevariable mytag 1 ; # Code for symbols which are tags.
257
typevariable mybranch 2 ; # Code for symbols which are branches.
258
typevariable myundef 3 ; # Code for symbols of unknown type.
259
typevariable mysymtype -array {} ; # Map from type code to label for the log.
260
261
typemethod undef {} { return $myundef }
262
typemethod excluded {} { return $myexcluded }
263
typemethod tag {} { return $mytag }
264
typemethod branch {} { return $mybranch }
265
266
typemethod printrulestatistics {} {
267
log write 2 symbol "Rule usage statistics:"
268
269
set fmt %[string length $mynum]s
270
set all 0
271
272
foreach key [lsort [array names myrulecount]] {
273
log write 2 symbol "* [format $fmt $myrulecount($key)] $key"
274
incr all $myrulecount($key)
275
}
276
277
log write 2 symbol "= [format $fmt $all] total"
278
return
279
}
280
281
# Statistics on how often each 'rule' was used to decide on the
282
# type of a symbol.
283
typevariable myrulecount -array {
284
HasCommits 0
285
IsTheTrunk 0
286
Unambigous 0
287
Undecided_ 0
288
UserConfig 0
289
VoteCounts 0
290
}
291
292
typemethod printtypestatistics {} {
293
log write 2 symbol "Symbol type statistics:"
294
295
set fmt %[string length $mynum]s
296
set all 0
297
298
state foreachrow {
299
SELECT T.name AS stype,
300
T.plural AS splural,
301
COUNT (s.sid) AS n
302
FROM symbol S, symtype T
303
WHERE S.type = T.tid
304
GROUP BY T.name
305
ORDER BY T.name
306
} {
307
log write 2 symbol "* [format $fmt $n] [sp $n $stype $splural]"
308
incr all $n
309
}
310
311
log write 2 symbol "= [format $fmt $all] total"
312
return
313
}
314
315
typevariable mynum 0
316
317
# # ## ### ##### ######## #############
318
## Internal methods
319
320
method UserConfig {} {
321
set project [$myproject base]
322
323
# First check if the user requested the exclusion of the
324
# symbol from conversion.
325
326
foreach ex $myexcludepattern {
327
struct::list assign $ex pp sp
328
if {![string match $pp $project]} continue
329
if {![string match $sp $myname]} continue
330
return $myexcluded
331
}
332
333
# If the symbol is not excluded further check if the user
334
# forces its conversion as a specific type.
335
336
foreach {ex stype} $myforcepattern {
337
struct::list assign $ex pp sp
338
if {![string match $pp $project]} continue
339
if {![string match $sp $myname]} continue
340
return $stype
341
}
342
343
# Nothing is forced, have the main system hand the symbol over
344
# to the regular heuristics.
345
346
return $myundef
347
}
348
349
method Unambigous {} {
350
# If a symbol is used unambiguously as a tag/branch, convert
351
# it as such.
352
353
set istag [expr {$mytagcount > 0}]
354
set isbranch [expr {$mybranchcount > 0 || $mycommitcount > 0}]
355
356
if {$istag && $isbranch} { return $myundef }
357
if {$istag} { return $mytag }
358
if {$isbranch} { return $mybranch }
359
360
# Symbol was not used at all.
361
return $myundef
362
}
363
364
method HasCommits {} {
365
# If there was ever a commit on the symbol, convert it as a
366
# branch.
367
368
if {$mycommitcount > 0} { return $mybranch }
369
return $myundef
370
}
371
372
method VoteCounts {} {
373
# Convert the symbol based on how often it was used as a
374
# branch/tag. Whichever happened more often determines how the
375
# symbol is converted.
376
377
if {$mytagcount > $mybranchcount} { return $mytag }
378
if {$mytagcount < $mybranchcount} { return $mybranch }
379
return $myundef
380
}
381
382
method MarkAs {label chosen} {
383
log write 3 symbol {\[$label\] Converting symbol '$myname' as $mysymtype($chosen)}
384
385
set mytype $chosen
386
incr myrulecount($label)
387
388
# This is stored directly into the database.
389
state run {
390
UPDATE symbol
391
SET type = $chosen
392
WHERE sid = $myid
393
}
394
return
395
}
396
397
# # ## ### ##### ######## #############
398
## Configuration
399
400
pragma -hastypeinfo no ; # no type introspection
401
pragma -hasinfo no ; # no object introspection
402
pragma -simpledispatch yes ; # simple fast dispatch
403
404
# # ## ### ##### ######## #############
405
}
406
407
namespace eval ::vc::fossil::import::cvs::project {
408
namespace export sym
409
namespace eval sym {
410
namespace import ::vc::fossil::import::cvs::state
411
namespace import ::vc::tools::misc::*
412
namespace import ::vc::tools::trouble
413
namespace import ::vc::tools::log
414
log register symbol
415
}
416
}
417
418
# # ## ### ##### ######## ############# #####################
419
## Ready
420
421
package provide vc::fossil::import::cvs::project::sym 1.0
422
return
423

Keyboard Shortcuts

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