|
1
|
## -*- tcl -*- |
|
2
|
# # ## ### ##### ######## ############# ##################### |
|
3
|
## Copyright (c) 2007 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 III. This pass divides the symbols collected by the previous |
|
14
|
## pass into branches, tags, and excludes. The latter are also |
|
15
|
## partially deleted by this pass, not only marked. It is the next |
|
16
|
## pass however, 'FilterSym', which performs the full deletion. |
|
17
|
|
|
18
|
# # ## ### ##### ######## ############# ##################### |
|
19
|
## Requirements |
|
20
|
|
|
21
|
package require Tcl 8.4 ; # Required runtime. |
|
22
|
package require snit ; # OO system. |
|
23
|
package require vc::tools::trouble ; # Error reporting. |
|
24
|
package require vc::tools::log ; # User feedback. |
|
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::project::sym ; # Project level symbols |
|
28
|
|
|
29
|
# # ## ### ##### ######## ############# ##################### |
|
30
|
## Register the pass with the management |
|
31
|
|
|
32
|
vc::fossil::import::cvs::pass define \ |
|
33
|
CollateSymbols \ |
|
34
|
{Collate symbols} \ |
|
35
|
::vc::fossil::import::cvs::pass::collsym |
|
36
|
|
|
37
|
# # ## ### ##### ######## ############# ##################### |
|
38
|
## |
|
39
|
|
|
40
|
snit::type ::vc::fossil::import::cvs::pass::collsym { |
|
41
|
# # ## ### ##### ######## ############# |
|
42
|
## Public API |
|
43
|
|
|
44
|
typemethod setup {} { |
|
45
|
# Define names and structure of the persistent state of this |
|
46
|
# pass. |
|
47
|
|
|
48
|
state use project |
|
49
|
state use symbol |
|
50
|
state use symtype |
|
51
|
state use blocker |
|
52
|
state use parent |
|
53
|
|
|
54
|
state extend preferedparent { |
|
55
|
-- For each symbol the prefered parent. This describes the |
|
56
|
-- tree of the found lines of development. Actually a |
|
57
|
-- forest in case of multiple projects, with one tree per |
|
58
|
-- project. |
|
59
|
|
|
60
|
sid INTEGER NOT NULL PRIMARY KEY REFERENCES symbol, |
|
61
|
pid INTEGER NOT NULL REFERENCES symbol |
|
62
|
} { pid } |
|
63
|
# Index on: pid (branch successors) |
|
64
|
return |
|
65
|
} |
|
66
|
|
|
67
|
typemethod load {} { |
|
68
|
# Pass manager interface. Executed to load data computed by |
|
69
|
# this pass into memory when this pass is skipped instead of |
|
70
|
# executed. |
|
71
|
|
|
72
|
# The results of this pass are fully in the persistent state, |
|
73
|
# there is nothing to load for the next one. |
|
74
|
return |
|
75
|
} |
|
76
|
|
|
77
|
typemethod run {} { |
|
78
|
# Pass manager interface. Executed to perform the |
|
79
|
# functionality of the pass. |
|
80
|
|
|
81
|
state transaction { |
|
82
|
repository determinesymboltypes |
|
83
|
|
|
84
|
project::sym printrulestatistics |
|
85
|
project::sym printtypestatistics |
|
86
|
} |
|
87
|
|
|
88
|
if {![trouble ?]} { |
|
89
|
UnconvertedSymbols |
|
90
|
BadSymbolTypes |
|
91
|
BlockedExcludes |
|
92
|
InvalidTags |
|
93
|
} |
|
94
|
|
|
95
|
if {![trouble ?]} { |
|
96
|
DropExcludedSymbolsFromReferences |
|
97
|
DeterminePreferedParents |
|
98
|
} |
|
99
|
|
|
100
|
log write 1 collsym "Collation completed" |
|
101
|
return |
|
102
|
} |
|
103
|
|
|
104
|
typemethod discard {} { |
|
105
|
# Pass manager interface. Executed for all passes after the |
|
106
|
# run passes, to remove all data of this pass from the state, |
|
107
|
# as being out of date. |
|
108
|
|
|
109
|
state discard preferedparent |
|
110
|
return |
|
111
|
} |
|
112
|
|
|
113
|
# # ## ### ##### ######## ############# |
|
114
|
## Internal methods |
|
115
|
|
|
116
|
## TODO: Move UnconvertedSymbols, BadSymbolTypes, BlockedIncludes, |
|
117
|
## TODO: InvalidTags to the integrity module? |
|
118
|
|
|
119
|
proc UnconvertedSymbols {} { |
|
120
|
# Paranoia - Have we left symbols without conversion |
|
121
|
# information (i.e. with type 'undefined') ? |
|
122
|
|
|
123
|
set undef [project::sym undef] |
|
124
|
|
|
125
|
state foreachrow { |
|
126
|
SELECT P.name AS pname, S.name AS sname |
|
127
|
FROM symbol S, project P |
|
128
|
WHERE S.type = $undef -- Restrict to undefined symbols |
|
129
|
AND P.pid = S.pid -- Get project for symbol |
|
130
|
} { |
|
131
|
trouble fatal "$pname : The symbol '$sname' was left undefined" |
|
132
|
} |
|
133
|
return |
|
134
|
} |
|
135
|
|
|
136
|
proc BadSymbolTypes {} { |
|
137
|
# Paranoia - Have we left symbols with bogus conversion |
|
138
|
# information (type out of the valid range (excluded, branch, |
|
139
|
# tag)) ? |
|
140
|
|
|
141
|
state foreachrow { |
|
142
|
SELECT P.name AS pname, S.name AS sname |
|
143
|
FROM symbol S, project P |
|
144
|
WHERE S.type NOT IN (0,1,2) -- Restrict to symbols with bogus type codes |
|
145
|
AND P.pid = S.pid -- Get project of symbol |
|
146
|
} { |
|
147
|
trouble fatal "$pname : The symbol '$sname' has no proper conversion type" |
|
148
|
} |
|
149
|
return |
|
150
|
} |
|
151
|
|
|
152
|
proc BlockedExcludes {} { |
|
153
|
# Paranoia - Have we scheduled symbols for exclusion without |
|
154
|
# also excluding their dependent symbols ? |
|
155
|
|
|
156
|
set excl [project::sym excluded] |
|
157
|
|
|
158
|
state foreachrow { |
|
159
|
SELECT P.name AS pname, S.name AS sname, SB.name AS bname |
|
160
|
FROM symbol S, blocker B, symbol SB, project P |
|
161
|
WHERE S.type = $excl -- Restrict to excluded symbols |
|
162
|
AND S.sid = B.sid -- Get symbols blocking them |
|
163
|
AND B.bid = SB.sid -- and |
|
164
|
AND SB.type != $excl -- which are not excluded themselves |
|
165
|
AND P.pid = S.pid -- Get project of symbol |
|
166
|
} { |
|
167
|
trouble fatal "$pname : The symbol '$sname' cannot be excluded as the unexcluded symbol '$bname' depends on it." |
|
168
|
} |
|
169
|
return |
|
170
|
} |
|
171
|
|
|
172
|
proc InvalidTags {} { |
|
173
|
# Paranoia - Have we scheduled symbols for conversion as tags |
|
174
|
# which absolutely cannot be converted as tags due to commits |
|
175
|
# made on them ? |
|
176
|
|
|
177
|
# In other words, this checks finds out if the user has asked |
|
178
|
# nonsensical conversions of symbols, which should have been |
|
179
|
# left to the heuristics, most specifically |
|
180
|
# 'project::sym.HasCommits()'. |
|
181
|
|
|
182
|
set tag [project::sym tag] |
|
183
|
|
|
184
|
state foreachrow { |
|
185
|
SELECT P.name AS pname, S.name AS sname |
|
186
|
FROM project P, symbol S |
|
187
|
WHERE S.type = $tag -- Restrict to tag symbols |
|
188
|
AND S.commit_count > 0 -- which have revisions committed to them |
|
189
|
AND P.pid = S.pid -- Get project of symbol |
|
190
|
} { |
|
191
|
trouble fatal "$pname : The symbol '$sname' cannot be forced to be converted as tag because it has commits." |
|
192
|
} |
|
193
|
return |
|
194
|
} |
|
195
|
|
|
196
|
proc DropExcludedSymbolsFromReferences {} { |
|
197
|
# The excluded symbols cann be used as blockers nor as |
|
198
|
# possible parent for other symbols. We now drop the relevant |
|
199
|
# entries to prevent them from causing confusion later on. |
|
200
|
|
|
201
|
set excl [project::sym excluded] |
|
202
|
|
|
203
|
state run { |
|
204
|
DELETE FROM blocker |
|
205
|
WHERE bid IN (SELECT sid |
|
206
|
FROM symbol |
|
207
|
WhERE type = $excl); -- Get excluded symbols |
|
208
|
DELETE FROM parent |
|
209
|
WHERE pid IN (SELECT sid |
|
210
|
FROM symbol |
|
211
|
WhERE type = $excl); -- Get excluded symbols |
|
212
|
} |
|
213
|
return |
|
214
|
} |
|
215
|
|
|
216
|
proc DeterminePreferedParents {} { |
|
217
|
array set prefered {} |
|
218
|
|
|
219
|
set excl [project::sym excluded] |
|
220
|
|
|
221
|
# Phase I: Pull the possible parents, using sorting to put the |
|
222
|
# prefered parent of each symbol last among all |
|
223
|
# candidates, allowing us get the prefered one by |
|
224
|
# each candidate overwriting all previous |
|
225
|
# selections. Note that we ignore excluded symbols, |
|
226
|
# we do not care about their prefered parents and do |
|
227
|
# not attempt to compute them. |
|
228
|
|
|
229
|
state foreachrow { |
|
230
|
SELECT S.sid AS xs, |
|
231
|
P.pid AS xp, |
|
232
|
S.name AS sname, |
|
233
|
SB.name AS pname, |
|
234
|
PR.name AS prname, |
|
235
|
P.n AS votes |
|
236
|
FROM symbol S, parent P, symbol SB, project PR |
|
237
|
WHERE S.type != $excl -- Restrict to wanted symbols |
|
238
|
AND S.sid = P.sid -- Get possible parents of symbol |
|
239
|
AND P.pid = SB.sid -- and |
|
240
|
AND S.pid = PR.pid -- the project of the symbol |
|
241
|
ORDER BY P.n ASC, P.pid DESC -- Sorting, see below |
|
242
|
-- |
|
243
|
-- Higher votes and smaller ids (= earlier branches) last |
|
244
|
-- We simply keep the last possible parent for each |
|
245
|
-- symbol. This parent will have the max number of votes |
|
246
|
-- for its symbol and will be the earliest created branch |
|
247
|
-- possible among all with many votes. |
|
248
|
} { |
|
249
|
log write 9 pcollsym "Voting $votes for Parent($sname) = $pname" |
|
250
|
|
|
251
|
set prefered($xs) [list $xp $sname $pname $prname] |
|
252
|
} |
|
253
|
|
|
254
|
# Phase II: Write the found preferences back into the table |
|
255
|
# this pass defined for it. |
|
256
|
|
|
257
|
foreach {s x} [array get prefered] { |
|
258
|
struct::list assign $x p sname pname prname |
|
259
|
state run { |
|
260
|
INSERT INTO preferedparent (sid, pid) |
|
261
|
VALUES ($s, $p); |
|
262
|
} |
|
263
|
|
|
264
|
log write 3 pcollsym "$prname : '$sname's prefered parent is '$pname'" |
|
265
|
} |
|
266
|
|
|
267
|
# Phase III: Check the result that all symbols except for |
|
268
|
# trunks have a prefered parent. We also ignore |
|
269
|
# excluded symbols, as we intentionally did not |
|
270
|
# compute a prefered parent for them, see phase I. |
|
271
|
|
|
272
|
state foreachrow { |
|
273
|
SELECT PR.name AS pname, S.name AS sname |
|
274
|
FROM symbol S LEFT OUTER JOIN preferedparent P |
|
275
|
ON S.sid = P.sid, -- From symbol to prefered parent |
|
276
|
project PR |
|
277
|
WHERE P.pid IS NULL -- restrict to symbols without a preference |
|
278
|
AND S.type != $excl -- which are not excluded |
|
279
|
AND S.name != ':trunk:' -- and are not a trunk |
|
280
|
AND S.pid = PR.pid -- get project of symbol |
|
281
|
} { |
|
282
|
trouble fatal "$pname : '$sname' has no prefered parent." |
|
283
|
} |
|
284
|
|
|
285
|
# The reverse, having prefered parents for unknown symbols |
|
286
|
# cannot occur. |
|
287
|
return |
|
288
|
} |
|
289
|
|
|
290
|
# # ## ### ##### ######## ############# |
|
291
|
## Configuration |
|
292
|
|
|
293
|
pragma -hasinstances no ; # singleton |
|
294
|
pragma -hastypeinfo no ; # no introspection |
|
295
|
pragma -hastypedestroy no ; # immortal |
|
296
|
|
|
297
|
# # ## ### ##### ######## ############# |
|
298
|
} |
|
299
|
|
|
300
|
namespace eval ::vc::fossil::import::cvs::pass { |
|
301
|
namespace export collsym |
|
302
|
namespace eval collsym { |
|
303
|
namespace import ::vc::fossil::import::cvs::repository |
|
304
|
namespace import ::vc::fossil::import::cvs::state |
|
305
|
namespace eval project { |
|
306
|
namespace import ::vc::fossil::import::cvs::project::sym |
|
307
|
} |
|
308
|
namespace import ::vc::tools::trouble |
|
309
|
namespace import ::vc::tools::log |
|
310
|
log register collsym |
|
311
|
} |
|
312
|
} |
|
313
|
|
|
314
|
# # ## ### ##### ######## ############# ##################### |
|
315
|
## Ready |
|
316
|
|
|
317
|
package provide vc::fossil::import::cvs::pass::collsym 1.0 |
|
318
|
return |
|
319
|
|