|
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
|
|