Fossil SCM

Oops. pass 5 is not complete. Missed the breaking of internal dependencies, this is done in this pass already. Extended pass _2_ and file revisions with code to save the branchchildren (possible dependencies), and pass 5 and changesets with the proper algorithm. From cvs2svn, works, do not truly like it, as it throws away and recomputes a lot of state after each split of a cset. Could update and reuse the state to perform all splits in one go. Will try that next, for now we have a working form in the code base.

aku 2007-11-10 20:40 trunk
Commit 95af789e1fa51e6eac3e4ee857422844a935149e
--- tools/cvs2fossil/lib/c2f_frev.tcl
+++ tools/cvs2fossil/lib/c2f_frev.tcl
@@ -365,10 +365,19 @@
365365
VALUES ($myid, $fid, $myrevnr, $lod, @P@, @C@, $idb, @DP, @DC, @BP , $op, $mydate, $mystate, $mymetaid, $coff, $clen);
366366
}
367367
368368
state transaction {
369369
state run [string map $map $cmd]
370
+
371
+ # And the branch children as well, for pass 5.
372
+ foreach bc $mybranchchildren {
373
+ set bcid [$bc id]
374
+ state run {
375
+ INSERT INTO revisionbranchchildren (rid, brid)
376
+ VALUES ($myid, $bcid);
377
+ }
378
+ }
370379
}
371380
return
372381
}
373382
374383
# # ## ### ##### ######## #############
375384
--- tools/cvs2fossil/lib/c2f_frev.tcl
+++ tools/cvs2fossil/lib/c2f_frev.tcl
@@ -365,10 +365,19 @@
365 VALUES ($myid, $fid, $myrevnr, $lod, @P@, @C@, $idb, @DP, @DC, @BP , $op, $mydate, $mystate, $mymetaid, $coff, $clen);
366 }
367
368 state transaction {
369 state run [string map $map $cmd]
 
 
 
 
 
 
 
 
 
370 }
371 return
372 }
373
374 # # ## ### ##### ######## #############
375
--- tools/cvs2fossil/lib/c2f_frev.tcl
+++ tools/cvs2fossil/lib/c2f_frev.tcl
@@ -365,10 +365,19 @@
365 VALUES ($myid, $fid, $myrevnr, $lod, @P@, @C@, $idb, @DP, @DC, @BP , $op, $mydate, $mystate, $mymetaid, $coff, $clen);
366 }
367
368 state transaction {
369 state run [string map $map $cmd]
370
371 # And the branch children as well, for pass 5.
372 foreach bc $mybranchchildren {
373 set bcid [$bc id]
374 state run {
375 INSERT INTO revisionbranchchildren (rid, brid)
376 VALUES ($myid, $bcid);
377 }
378 }
379 }
380 return
381 }
382
383 # # ## ### ##### ######## #############
384
--- tools/cvs2fossil/lib/c2f_pcollrev.tcl
+++ tools/cvs2fossil/lib/c2f_pcollrev.tcl
@@ -143,10 +143,11 @@
143143
coff INTEGER NOT NULL,
144144
clen INTEGER NOT NULL,
145145
146146
UNIQUE (fid, rev) -- The DTN is unique within the revision's file.
147147
}
148
+
148149
state writing optype {
149150
oid INTEGER NOT NULL PRIMARY KEY,
150151
name TEXT NOT NULL,
151152
UNIQUE(name)
152153
}
@@ -154,10 +155,22 @@
154155
INSERT INTO optype VALUES (-1,'delete'); -- The opcode names are the
155156
INSERT INTO optype VALUES ( 0,'nothing'); -- fixed pieces, see myopstate
156157
INSERT INTO optype VALUES ( 1,'add'); -- in file::rev. myopcode is
157158
INSERT INTO optype VALUES ( 2,'change'); -- loaded from this.
158159
}
160
+
161
+ state writing revisionbranchchildren {
162
+ -- The non-primary children of a revision, as reachable
163
+ -- through a branch symbol, are listed here. This is
164
+ -- needed by pass 5 to break internal dependencies in a
165
+ -- changeset.
166
+
167
+ rid INTEGER NOT NULL REFERENCES revision,
168
+ brid INTEGER NOT NULL REFERENCES revision,
169
+ UNIQUE(rid,brid)
170
+ }
171
+
159172
state writing tag {
160173
tid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
161174
fid INTEGER NOT NULL REFERENCES file, -- File the item belongs to
162175
lod INTEGER REFERENCES symbol, -- Line of development (NULL => Trunk)
163176
sid INTEGER NOT NULL REFERENCES symbol, -- Symbol capturing the tag
164177
--- tools/cvs2fossil/lib/c2f_pcollrev.tcl
+++ tools/cvs2fossil/lib/c2f_pcollrev.tcl
@@ -143,10 +143,11 @@
143 coff INTEGER NOT NULL,
144 clen INTEGER NOT NULL,
145
146 UNIQUE (fid, rev) -- The DTN is unique within the revision's file.
147 }
 
148 state writing optype {
149 oid INTEGER NOT NULL PRIMARY KEY,
150 name TEXT NOT NULL,
151 UNIQUE(name)
152 }
@@ -154,10 +155,22 @@
154 INSERT INTO optype VALUES (-1,'delete'); -- The opcode names are the
155 INSERT INTO optype VALUES ( 0,'nothing'); -- fixed pieces, see myopstate
156 INSERT INTO optype VALUES ( 1,'add'); -- in file::rev. myopcode is
157 INSERT INTO optype VALUES ( 2,'change'); -- loaded from this.
158 }
 
 
 
 
 
 
 
 
 
 
 
 
159 state writing tag {
160 tid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
161 fid INTEGER NOT NULL REFERENCES file, -- File the item belongs to
162 lod INTEGER REFERENCES symbol, -- Line of development (NULL => Trunk)
163 sid INTEGER NOT NULL REFERENCES symbol, -- Symbol capturing the tag
164
--- tools/cvs2fossil/lib/c2f_pcollrev.tcl
+++ tools/cvs2fossil/lib/c2f_pcollrev.tcl
@@ -143,10 +143,11 @@
143 coff INTEGER NOT NULL,
144 clen INTEGER NOT NULL,
145
146 UNIQUE (fid, rev) -- The DTN is unique within the revision's file.
147 }
148
149 state writing optype {
150 oid INTEGER NOT NULL PRIMARY KEY,
151 name TEXT NOT NULL,
152 UNIQUE(name)
153 }
@@ -154,10 +155,22 @@
155 INSERT INTO optype VALUES (-1,'delete'); -- The opcode names are the
156 INSERT INTO optype VALUES ( 0,'nothing'); -- fixed pieces, see myopstate
157 INSERT INTO optype VALUES ( 1,'add'); -- in file::rev. myopcode is
158 INSERT INTO optype VALUES ( 2,'change'); -- loaded from this.
159 }
160
161 state writing revisionbranchchildren {
162 -- The non-primary children of a revision, as reachable
163 -- through a branch symbol, are listed here. This is
164 -- needed by pass 5 to break internal dependencies in a
165 -- changeset.
166
167 rid INTEGER NOT NULL REFERENCES revision,
168 brid INTEGER NOT NULL REFERENCES revision,
169 UNIQUE(rid,brid)
170 }
171
172 state writing tag {
173 tid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
174 fid INTEGER NOT NULL REFERENCES file, -- File the item belongs to
175 lod INTEGER REFERENCES symbol, -- Line of development (NULL => Trunk)
176 sid INTEGER NOT NULL REFERENCES symbol, -- Symbol capturing the tag
177
--- tools/cvs2fossil/lib/c2f_pinitcsets.tcl
+++ tools/cvs2fossil/lib/c2f_pinitcsets.tcl
@@ -45,10 +45,11 @@
4545
# Define the names and structure of the persistent state of
4646
# this pass.
4747
4848
state reading meta
4949
state reading revision
50
+ state reading revisionbranchchildren
5051
state reading branch
5152
state reading tag
5253
state reading symbol
5354
5455
# Data per changeset, namely the project it belongs to, how it
@@ -109,13 +110,14 @@
109110
# Pass manager interface. Executed to perform the
110111
# functionality of the pass.
111112
112113
set csets {}
113114
state transaction {
114
- CreateRevisionChangesets csets ; # Group file revisions into csets.
115
- CreateSymbolChangesets csets ; # Create csets for tags and branches.
116
- PersistTheChangesets $csets
115
+ CreateRevisionChangesets csets ; # Group file revisions into csets.
116
+ BreakInternalDependencies csets ; # Split the csets based on internal conflicts.
117
+ CreateSymbolChangesets csets ; # Create csets for tags and branches.
118
+ PersistTheChangesets $csets
117119
}
118120
return
119121
}
120122
121123
typemethod discard {} {
@@ -268,19 +270,65 @@
268270
}
269271
270272
log write 4 initcsets "Created [nsp $n {symbol changeset}]"
271273
return
272274
}
275
+
276
+ proc BreakInternalDependencies {cv} {
277
+ upvar 1 $cv csets
278
+
279
+ # This code operates on the revision changesets created by
280
+ # 'CreateRevisionChangesets'. As such it has to follow after
281
+ # it, before the symbol changesets are made. The changesets
282
+ # are inspected for internal conflicts and any such are broken
283
+ # by splitting the problematic changeset into multiple
284
+ # fragments. The results are changesets which have no internal
285
+ # dependencies, only external ones.
286
+
287
+ log write 3 initcsets {Break internal dependencies}
288
+ set n 0
289
+
290
+ foreach cset $csets {
291
+ # The main method for splitting does only one split, which
292
+ # may not be enough. The code here iterates until no more
293
+ # splits can be performed. An iterative algorithm was
294
+ # chosen over a recursive one to prevent running into
295
+ # stack limits.
296
+
297
+ set tosplit [list $cset]
298
+ set at 0
299
+ while {$at < [llength $tosplit]} {
300
+ # Note here how we are __not__ advancing in the list
301
+ # when we were able to break the current
302
+ # changeset into two pieces, causing the loop to
303
+ # immediately check the first of the two pieces
304
+ # again for further break possibilities. The
305
+ # other piece is added at the end, thus processed
306
+ # later.
307
+ while {[[lindex $tosplit $at] breakinternaldependencies tosplit]} {}
308
+ incr at
309
+ }
310
+
311
+ # At last the generated fragments are added to the main
312
+ # list of changesets. The first element is skipped as it
313
+ # is already in the list.
314
+ foreach cset [lrange $tosplit 1 end] { lappend csets $cset ; incr n }
315
+ }
316
+
317
+ log write 4 initcsets "Created [nsp $n {additional revision changeset}]"
318
+ log write 4 initcsets Ok.
319
+ return
320
+ }
273321
274322
proc PersistTheChangesets {csets} {
275
- log write 3 initcsets {Saving the created changesets to the persistent state}
323
+ log write 3 initcsets "Saving [nsp [llength $csets] {initial changeset}] to the persistent state"
276324
277325
foreach cset $csets {
278326
$cset persist
279327
}
280328
281
- log write 4 initcsets {Ok.}
329
+ log write 4 initcsets Ok.
282330
return
283331
}
284332
285333
# # ## ### ##### ######## #############
286334
## Configuration
287335
--- tools/cvs2fossil/lib/c2f_pinitcsets.tcl
+++ tools/cvs2fossil/lib/c2f_pinitcsets.tcl
@@ -45,10 +45,11 @@
45 # Define the names and structure of the persistent state of
46 # this pass.
47
48 state reading meta
49 state reading revision
 
50 state reading branch
51 state reading tag
52 state reading symbol
53
54 # Data per changeset, namely the project it belongs to, how it
@@ -109,13 +110,14 @@
109 # Pass manager interface. Executed to perform the
110 # functionality of the pass.
111
112 set csets {}
113 state transaction {
114 CreateRevisionChangesets csets ; # Group file revisions into csets.
115 CreateSymbolChangesets csets ; # Create csets for tags and branches.
116 PersistTheChangesets $csets
 
117 }
118 return
119 }
120
121 typemethod discard {} {
@@ -268,19 +270,65 @@
268 }
269
270 log write 4 initcsets "Created [nsp $n {symbol changeset}]"
271 return
272 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
274 proc PersistTheChangesets {csets} {
275 log write 3 initcsets {Saving the created changesets to the persistent state}
276
277 foreach cset $csets {
278 $cset persist
279 }
280
281 log write 4 initcsets {Ok.}
282 return
283 }
284
285 # # ## ### ##### ######## #############
286 ## Configuration
287
--- tools/cvs2fossil/lib/c2f_pinitcsets.tcl
+++ tools/cvs2fossil/lib/c2f_pinitcsets.tcl
@@ -45,10 +45,11 @@
45 # Define the names and structure of the persistent state of
46 # this pass.
47
48 state reading meta
49 state reading revision
50 state reading revisionbranchchildren
51 state reading branch
52 state reading tag
53 state reading symbol
54
55 # Data per changeset, namely the project it belongs to, how it
@@ -109,13 +110,14 @@
110 # Pass manager interface. Executed to perform the
111 # functionality of the pass.
112
113 set csets {}
114 state transaction {
115 CreateRevisionChangesets csets ; # Group file revisions into csets.
116 BreakInternalDependencies csets ; # Split the csets based on internal conflicts.
117 CreateSymbolChangesets csets ; # Create csets for tags and branches.
118 PersistTheChangesets $csets
119 }
120 return
121 }
122
123 typemethod discard {} {
@@ -268,19 +270,65 @@
270 }
271
272 log write 4 initcsets "Created [nsp $n {symbol changeset}]"
273 return
274 }
275
276 proc BreakInternalDependencies {cv} {
277 upvar 1 $cv csets
278
279 # This code operates on the revision changesets created by
280 # 'CreateRevisionChangesets'. As such it has to follow after
281 # it, before the symbol changesets are made. The changesets
282 # are inspected for internal conflicts and any such are broken
283 # by splitting the problematic changeset into multiple
284 # fragments. The results are changesets which have no internal
285 # dependencies, only external ones.
286
287 log write 3 initcsets {Break internal dependencies}
288 set n 0
289
290 foreach cset $csets {
291 # The main method for splitting does only one split, which
292 # may not be enough. The code here iterates until no more
293 # splits can be performed. An iterative algorithm was
294 # chosen over a recursive one to prevent running into
295 # stack limits.
296
297 set tosplit [list $cset]
298 set at 0
299 while {$at < [llength $tosplit]} {
300 # Note here how we are __not__ advancing in the list
301 # when we were able to break the current
302 # changeset into two pieces, causing the loop to
303 # immediately check the first of the two pieces
304 # again for further break possibilities. The
305 # other piece is added at the end, thus processed
306 # later.
307 while {[[lindex $tosplit $at] breakinternaldependencies tosplit]} {}
308 incr at
309 }
310
311 # At last the generated fragments are added to the main
312 # list of changesets. The first element is skipped as it
313 # is already in the list.
314 foreach cset [lrange $tosplit 1 end] { lappend csets $cset ; incr n }
315 }
316
317 log write 4 initcsets "Created [nsp $n {additional revision changeset}]"
318 log write 4 initcsets Ok.
319 return
320 }
321
322 proc PersistTheChangesets {csets} {
323 log write 3 initcsets "Saving [nsp [llength $csets] {initial changeset}] to the persistent state"
324
325 foreach cset $csets {
326 $cset persist
327 }
328
329 log write 4 initcsets Ok.
330 return
331 }
332
333 # # ## ### ##### ######## #############
334 ## Configuration
335
--- tools/cvs2fossil/lib/c2f_prev.tcl
+++ tools/cvs2fossil/lib/c2f_prev.tcl
@@ -16,10 +16,11 @@
1616
# # ## ### ##### ######## ############# #####################
1717
## Requirements
1818
1919
package require Tcl 8.4 ; # Required runtime.
2020
package require snit ; # OO system.
21
+package require vc::tools::log ; # User feedback.
2122
package require vc::fossil::import::cvs::state ; # State storage.
2223
2324
# # ## ### ##### ######## ############# #####################
2425
##
2526
@@ -33,10 +34,174 @@
3334
set mytype $cstype
3435
set mysrcid $srcid
3536
set myrevisions $revisions
3637
return
3738
}
39
+
40
+ method id {} { return $myid }
41
+
42
+ method breakinternaldependencies {cv} {
43
+ upvar 2 $cv csets ; # simple-dispatch!
44
+
45
+ # This method inspects the changesets for internal
46
+ # dependencies. Nothing is done if there are no
47
+ # such. Otherwise the changeset is split into a set of
48
+ # fragments without internal dependencies, transforming the
49
+ # internal dependencies into external ones. The new changesets
50
+ # are added to the list of all changesets.
51
+
52
+ # Actually at most one split is performed, resulting in at
53
+ # most one additional fragment. It is the caller's
54
+ # responsibility to spli the resulting fragments further.
55
+
56
+ # The code checks only sucessor dependencies, automatically
57
+ # covering the predecessor dependencies as well (A sucessor
58
+ # dependency a -> b is a predecessor dependency b -> a).
59
+
60
+ # Array of dependencies (parent -> child). This is pulled from
61
+ # the state, and limited to successors within the changeset.
62
+ array set dependencies {}
63
+
64
+ set theset ('[join $myrevisions {','}]')
65
+
66
+ foreach {rid child} [state run "
67
+ SELECT R.rid, R.child
68
+ FROM revision R
69
+ WHERE R.rid IN $theset
70
+ AND R.child IS NOT NULL
71
+ AND R.child IN $theset
72
+ UNION
73
+ SELECT R.rid, R.dbchild
74
+ FROM revision R
75
+ WHERE R.rid IN $theset
76
+ AND R.dbchild IS NOT NULL
77
+ AND R.dbchild IN $theset
78
+ UNION
79
+ SELECT R.rid, B.brid
80
+ FROM revision R, revisionbranchchildren B
81
+ WHERE R.rid IN $theset
82
+ AND R.rid = B.rid
83
+ AND B.brid IN $theset
84
+ "] {
85
+ # Consider moving this to the integrity module.
86
+ if {$rid == $child} {
87
+ trouble internal "Revision $rid depends on itself."
88
+ }
89
+ set dependencies($rid) $child
90
+ }
91
+
92
+ if {![array size dependencies]} {return 0} ; # Nothing to break.
93
+
94
+ # We have internal dependencies to break. We now iterate over
95
+ # all positions in the list (which is chronological, at least
96
+ # as far as the timestamps are correct and unique) and
97
+ # determine the best position for the break, by trying to
98
+ # break as many dependencies as possible in one go.
99
+
100
+ # First we create a map of positions to make it easier to
101
+ # determine whether a dependency cross a particular index.
102
+
103
+ array set pos {}
104
+ array set crossing {}
105
+ set n 0
106
+ foreach rev $myrevisions {
107
+ set pos($rev) $n
108
+ set crossing($n) 0
109
+ incr n
110
+ }
111
+
112
+ # Secondly we count the crossings per position, by iterating
113
+ # over the recorded internal dependencies.
114
+
115
+ foreach {rid child} [array get dependencies] {
116
+ set start $pos($rid)
117
+ set end $pos($child)
118
+
119
+ # Note: If the timestamps are badly out of order it is
120
+ # possible to have a backward successor dependency,
121
+ # i.e. with start > end. We may have to swap the
122
+ # indices to ensure that the following loop runs
123
+ # correctly.
124
+ #
125
+ # Note 2: start == end is not possible. It indicates a
126
+ # self-dependency due to the uniqueness of
127
+ # positions, and that is something we have ruled
128
+ # out already.
129
+
130
+ if {$start > $end} {
131
+ while {$end < $start} { incr crossing($end) ; incr end }
132
+ } else {
133
+ while {$start < $end} { incr crossing($start) ; incr start }
134
+ }
135
+ }
136
+
137
+ # Now we can determine the best break location. First we look
138
+ # for the locations with the maximal number of crossings. If
139
+ # there are several we look for the shortest time interval
140
+ # among them. If we still have multiple possibilities after
141
+ # that we select the smallest index among these.
142
+
143
+ set max -1
144
+ set best {}
145
+
146
+ foreach key [array names crossing] {
147
+ set now $crossing($key)
148
+ if {$now > $max} {
149
+ set max $now
150
+ set best $key
151
+ continue
152
+ } elseif {$now == $max} {
153
+ lappend best $key
154
+ }
155
+ }
156
+
157
+ if {[llength $best] > 1} {
158
+ set min -1
159
+ set newbest {}
160
+ foreach at $best {
161
+ set rat [lindex $myrevisions $at] ; incr at
162
+ set rnext [lindex $myrevisions $at] ; incr at -1
163
+ set tat [lindex [state run {SELECT R.date FROM revision R WHERE R.rid = $rat }] 0]
164
+ set tnext [lindex [state run {SELECT R.date FROM revision R WHERE R.rid = $rnext}] 0]
165
+ set delta [expr {$tnext - $tat}]
166
+ if {($min < 0) || ($delta < $min)} {
167
+ set min $delta
168
+ set newbest $at
169
+ } elseif {$delta == $min} {
170
+ lappend newbest $at
171
+ }
172
+ }
173
+ set best $newbest
174
+ }
175
+
176
+ if {[llength $best] > 1} {
177
+ set best [lindex [lsort -integer -increasing $best] 0]
178
+ }
179
+
180
+ # Now we can split off a fragment.
181
+
182
+ set bnext $best ; incr bnext
183
+ set revbefore [lrange $myrevisions 0 $best]
184
+ set revafter [lrange $myrevisions $bnext end]
185
+
186
+ if {![llength $revbefore]} {
187
+ trouble internal "Tried to split off a zero-length fragment at the beginning"
188
+ }
189
+ if {![llength $revafter]} {
190
+ trouble internal "Tried to split off a zero-length fragment at the end"
191
+ }
192
+
193
+ lappend csets [set new [$type %AUTO% $myproject $mytype $mysrcid $revafter]]
194
+ set myrevisions $revbefore
195
+
196
+ log write 4 csets "Breaking <$myid> @$best, making <[$new id]>, cutting $crossing($best)"
197
+
198
+ #puts "\tKeeping <$revbefore>"
199
+ #puts "\tSplit off <$revafter>"
200
+
201
+ return 1
202
+ }
38203
39204
method persist {} {
40205
set tid $mycstype($mytype)
41206
set pid [$myproject id]
42207
set pos 0
@@ -92,13 +257,15 @@
92257
93258
namespace eval ::vc::fossil::import::cvs::project {
94259
namespace export rev
95260
namespace eval rev {
96261
namespace import ::vc::fossil::import::cvs::state
262
+ namespace import ::vc::tools::log
263
+ log register csets
97264
}
98265
}
99266
100267
# # ## ### ##### ######## ############# #####################
101268
## Ready
102269
103270
package provide vc::fossil::import::cvs::project::rev 1.0
104271
return
105272
--- tools/cvs2fossil/lib/c2f_prev.tcl
+++ tools/cvs2fossil/lib/c2f_prev.tcl
@@ -16,10 +16,11 @@
16 # # ## ### ##### ######## ############# #####################
17 ## Requirements
18
19 package require Tcl 8.4 ; # Required runtime.
20 package require snit ; # OO system.
 
21 package require vc::fossil::import::cvs::state ; # State storage.
22
23 # # ## ### ##### ######## ############# #####################
24 ##
25
@@ -33,10 +34,174 @@
33 set mytype $cstype
34 set mysrcid $srcid
35 set myrevisions $revisions
36 return
37 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
39 method persist {} {
40 set tid $mycstype($mytype)
41 set pid [$myproject id]
42 set pos 0
@@ -92,13 +257,15 @@
92
93 namespace eval ::vc::fossil::import::cvs::project {
94 namespace export rev
95 namespace eval rev {
96 namespace import ::vc::fossil::import::cvs::state
 
 
97 }
98 }
99
100 # # ## ### ##### ######## ############# #####################
101 ## Ready
102
103 package provide vc::fossil::import::cvs::project::rev 1.0
104 return
105
--- tools/cvs2fossil/lib/c2f_prev.tcl
+++ tools/cvs2fossil/lib/c2f_prev.tcl
@@ -16,10 +16,11 @@
16 # # ## ### ##### ######## ############# #####################
17 ## Requirements
18
19 package require Tcl 8.4 ; # Required runtime.
20 package require snit ; # OO system.
21 package require vc::tools::log ; # User feedback.
22 package require vc::fossil::import::cvs::state ; # State storage.
23
24 # # ## ### ##### ######## ############# #####################
25 ##
26
@@ -33,10 +34,174 @@
34 set mytype $cstype
35 set mysrcid $srcid
36 set myrevisions $revisions
37 return
38 }
39
40 method id {} { return $myid }
41
42 method breakinternaldependencies {cv} {
43 upvar 2 $cv csets ; # simple-dispatch!
44
45 # This method inspects the changesets for internal
46 # dependencies. Nothing is done if there are no
47 # such. Otherwise the changeset is split into a set of
48 # fragments without internal dependencies, transforming the
49 # internal dependencies into external ones. The new changesets
50 # are added to the list of all changesets.
51
52 # Actually at most one split is performed, resulting in at
53 # most one additional fragment. It is the caller's
54 # responsibility to spli the resulting fragments further.
55
56 # The code checks only sucessor dependencies, automatically
57 # covering the predecessor dependencies as well (A sucessor
58 # dependency a -> b is a predecessor dependency b -> a).
59
60 # Array of dependencies (parent -> child). This is pulled from
61 # the state, and limited to successors within the changeset.
62 array set dependencies {}
63
64 set theset ('[join $myrevisions {','}]')
65
66 foreach {rid child} [state run "
67 SELECT R.rid, R.child
68 FROM revision R
69 WHERE R.rid IN $theset
70 AND R.child IS NOT NULL
71 AND R.child IN $theset
72 UNION
73 SELECT R.rid, R.dbchild
74 FROM revision R
75 WHERE R.rid IN $theset
76 AND R.dbchild IS NOT NULL
77 AND R.dbchild IN $theset
78 UNION
79 SELECT R.rid, B.brid
80 FROM revision R, revisionbranchchildren B
81 WHERE R.rid IN $theset
82 AND R.rid = B.rid
83 AND B.brid IN $theset
84 "] {
85 # Consider moving this to the integrity module.
86 if {$rid == $child} {
87 trouble internal "Revision $rid depends on itself."
88 }
89 set dependencies($rid) $child
90 }
91
92 if {![array size dependencies]} {return 0} ; # Nothing to break.
93
94 # We have internal dependencies to break. We now iterate over
95 # all positions in the list (which is chronological, at least
96 # as far as the timestamps are correct and unique) and
97 # determine the best position for the break, by trying to
98 # break as many dependencies as possible in one go.
99
100 # First we create a map of positions to make it easier to
101 # determine whether a dependency cross a particular index.
102
103 array set pos {}
104 array set crossing {}
105 set n 0
106 foreach rev $myrevisions {
107 set pos($rev) $n
108 set crossing($n) 0
109 incr n
110 }
111
112 # Secondly we count the crossings per position, by iterating
113 # over the recorded internal dependencies.
114
115 foreach {rid child} [array get dependencies] {
116 set start $pos($rid)
117 set end $pos($child)
118
119 # Note: If the timestamps are badly out of order it is
120 # possible to have a backward successor dependency,
121 # i.e. with start > end. We may have to swap the
122 # indices to ensure that the following loop runs
123 # correctly.
124 #
125 # Note 2: start == end is not possible. It indicates a
126 # self-dependency due to the uniqueness of
127 # positions, and that is something we have ruled
128 # out already.
129
130 if {$start > $end} {
131 while {$end < $start} { incr crossing($end) ; incr end }
132 } else {
133 while {$start < $end} { incr crossing($start) ; incr start }
134 }
135 }
136
137 # Now we can determine the best break location. First we look
138 # for the locations with the maximal number of crossings. If
139 # there are several we look for the shortest time interval
140 # among them. If we still have multiple possibilities after
141 # that we select the smallest index among these.
142
143 set max -1
144 set best {}
145
146 foreach key [array names crossing] {
147 set now $crossing($key)
148 if {$now > $max} {
149 set max $now
150 set best $key
151 continue
152 } elseif {$now == $max} {
153 lappend best $key
154 }
155 }
156
157 if {[llength $best] > 1} {
158 set min -1
159 set newbest {}
160 foreach at $best {
161 set rat [lindex $myrevisions $at] ; incr at
162 set rnext [lindex $myrevisions $at] ; incr at -1
163 set tat [lindex [state run {SELECT R.date FROM revision R WHERE R.rid = $rat }] 0]
164 set tnext [lindex [state run {SELECT R.date FROM revision R WHERE R.rid = $rnext}] 0]
165 set delta [expr {$tnext - $tat}]
166 if {($min < 0) || ($delta < $min)} {
167 set min $delta
168 set newbest $at
169 } elseif {$delta == $min} {
170 lappend newbest $at
171 }
172 }
173 set best $newbest
174 }
175
176 if {[llength $best] > 1} {
177 set best [lindex [lsort -integer -increasing $best] 0]
178 }
179
180 # Now we can split off a fragment.
181
182 set bnext $best ; incr bnext
183 set revbefore [lrange $myrevisions 0 $best]
184 set revafter [lrange $myrevisions $bnext end]
185
186 if {![llength $revbefore]} {
187 trouble internal "Tried to split off a zero-length fragment at the beginning"
188 }
189 if {![llength $revafter]} {
190 trouble internal "Tried to split off a zero-length fragment at the end"
191 }
192
193 lappend csets [set new [$type %AUTO% $myproject $mytype $mysrcid $revafter]]
194 set myrevisions $revbefore
195
196 log write 4 csets "Breaking <$myid> @$best, making <[$new id]>, cutting $crossing($best)"
197
198 #puts "\tKeeping <$revbefore>"
199 #puts "\tSplit off <$revafter>"
200
201 return 1
202 }
203
204 method persist {} {
205 set tid $mycstype($mytype)
206 set pid [$myproject id]
207 set pos 0
@@ -92,13 +257,15 @@
257
258 namespace eval ::vc::fossil::import::cvs::project {
259 namespace export rev
260 namespace eval rev {
261 namespace import ::vc::fossil::import::cvs::state
262 namespace import ::vc::tools::log
263 log register csets
264 }
265 }
266
267 # # ## ### ##### ######## ############# #####################
268 ## Ready
269
270 package provide vc::fossil::import::cvs::project::rev 1.0
271 return
272

Keyboard Shortcuts

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