Fossil SCM
Completed pass 6, wrote the code performing the breaking of cycles. Done by analysing each triple of changesets in the cycle at the file dependency level to see which revisions can be sorted apart. Added some additional utility routines. Extended the changeset class with the accessors required by the cycle breaker.
Commit
94c39d63758d78dc26b3ad5c2fdd285ea37047d1
Parent
67600f777bb412f…
5 files changed
+75
-15
+74
-13
+180
+23
-2
+1
| --- tools/cvs2fossil/lib/c2f_pbreakrcycle.tcl | ||
| +++ tools/cvs2fossil/lib/c2f_pbreakrcycle.tcl | ||
| @@ -15,17 +15,18 @@ | ||
| 15 | 15 | ## dependency tree. |
| 16 | 16 | |
| 17 | 17 | # # ## ### ##### ######## ############# ##################### |
| 18 | 18 | ## Requirements |
| 19 | 19 | |
| 20 | -package require Tcl 8.4 ; # Required runtime. | |
| 21 | -package require snit ; # OO system. | |
| 22 | -package require struct::graph ; # Graph handling. | |
| 23 | -package require struct::list ; # Higher order list operations. | |
| 24 | -package require vc::tools::log ; # User feedback. | |
| 25 | -package require vc::fossil::import::cvs::state ; # State storage. | |
| 26 | -package require vc::fossil::import::cvs::project::rev ; # Project level changesets | |
| 20 | +package require Tcl 8.4 ; # Required runtime. | |
| 21 | +package require snit ; # OO system. | |
| 22 | +package require struct::graph ; # Graph handling. | |
| 23 | +package require struct::list ; # Higher order list operations. | |
| 24 | +package require vc::tools::log ; # User feedback. | |
| 25 | +package require vc::fossil::import::cvs::state ; # State storage. | |
| 26 | +package require vc::fossil::import::cvs::project::rev ; # Project level changesets | |
| 27 | +package require vc::fossil::import::cvs::project::revlink ; # Cycle links. | |
| 27 | 28 | |
| 28 | 29 | # # ## ### ##### ######## ############# ##################### |
| 29 | 30 | ## Register the pass with the management |
| 30 | 31 | |
| 31 | 32 | vc::fossil::import::cvs::pass define \ |
| @@ -65,20 +66,22 @@ | ||
| 65 | 66 | typemethod run {} { |
| 66 | 67 | # Pass manager interface. Executed to perform the |
| 67 | 68 | # functionality of the pass. |
| 68 | 69 | |
| 69 | 70 | state reading revision |
| 71 | + state reading changeset | |
| 72 | + state reading csrevision | |
| 70 | 73 | |
| 71 | 74 | # We create a graph of the revision changesets, using the file |
| 72 | 75 | # level dependencies to construct a first approximation of |
| 73 | 76 | # them at the project level. Then look for cycles in that |
| 74 | 77 | # graph and break them. |
| 75 | 78 | |
| 76 | 79 | # 1. Create nodes for all relevant changesets and a mapping |
| 77 | 80 | # from the revisions to their changesets/nodes. |
| 78 | 81 | |
| 79 | - log write 3 brkrcycle {Creating changeset graph, filling with nodes} | |
| 82 | + log write 3 breakrcycle {Creating changeset graph, filling with nodes} | |
| 80 | 83 | |
| 81 | 84 | set dg [struct::graph dg] |
| 82 | 85 | |
| 83 | 86 | state transaction { |
| 84 | 87 | foreach cset [project::rev all] { |
| @@ -90,11 +93,11 @@ | ||
| 90 | 93 | |
| 91 | 94 | # 2. Find for all relevant changeset their revisions and their |
| 92 | 95 | # dependencies. Map the latter back to changesets and |
| 93 | 96 | # construct the corresponding arcs. |
| 94 | 97 | |
| 95 | - log write 3 brkrcycle {Setting up node dependencies} | |
| 98 | + log write 3 breakrcycle {Setting up node dependencies} | |
| 96 | 99 | |
| 97 | 100 | state transaction { |
| 98 | 101 | foreach cset [project::rev all] { |
| 99 | 102 | if {[$cset bysymbol]} continue |
| 100 | 103 | foreach succ [$cset successors] { |
| @@ -107,21 +110,21 @@ | ||
| 107 | 110 | # the nodes which have no predecessors, in order from |
| 108 | 111 | # oldest to youngest, saving and removing dependencies. If |
| 109 | 112 | # we find no nodes without predecessors we have a cycle, |
| 110 | 113 | # and work on breaking it. |
| 111 | 114 | |
| 112 | - log write 3 brkrcycle {Computing changeset order, breaking cycles} | |
| 115 | + log write 3 breakrcycle {Computing changeset order, breaking cycles} | |
| 113 | 116 | |
| 114 | 117 | InitializeCandidates $dg |
| 115 | 118 | state transaction { |
| 116 | 119 | while {1} { |
| 117 | 120 | while {[WithoutPredecessor $dg n]} { |
| 118 | 121 | SaveAndRemove $dg $n |
| 119 | 122 | } |
| 120 | 123 | if {![llength [dg nodes]]} break |
| 121 | - set cycle [FindCycle $dg] | |
| 122 | - BreakCycle $dg $cycle | |
| 124 | + BreakCycle $dg [FindCycle $dg] | |
| 125 | + InitializeCandidates $dg | |
| 123 | 126 | } |
| 124 | 127 | } |
| 125 | 128 | |
| 126 | 129 | return |
| 127 | 130 | } |
| @@ -224,20 +227,76 @@ | ||
| 224 | 227 | while {1} { |
| 225 | 228 | # Stop searching when we have seen the current node |
| 226 | 229 | # already, the circle has been closed. |
| 227 | 230 | if {[info exists seen($start)]} break |
| 228 | 231 | lappend path $start |
| 229 | - set seen($start) [llength $path] | |
| 232 | + set seen($start) [expr {[llength $path]-1}] | |
| 230 | 233 | # Choose arbitrary predecessor |
| 231 | 234 | set start [lindex [$dg nodes -in $start] 0] |
| 232 | 235 | } |
| 233 | 236 | |
| 234 | 237 | return [struct::list reverse [lrange $path $seen($start) end]] |
| 235 | 238 | } |
| 236 | 239 | |
| 240 | + proc ID {cset} { return "<[$cset id]>" } | |
| 241 | + | |
| 237 | 242 | proc BreakCycle {dg cycle} { |
| 238 | - trouble internal "Break cycle <$cycle>" | |
| 243 | + # The cycle we have gotten is broken by breaking apart one or | |
| 244 | + # more of the changesets in the cycle. This causes us to | |
| 245 | + # create one or more changesets which are to be committed, | |
| 246 | + # added to the graph, etc. pp. | |
| 247 | + | |
| 248 | + set cprint [join [struct::list map $cycle [myproc ID]] { }] | |
| 249 | + | |
| 250 | + lappend cycle [lindex $cycle 0] [lindex $cycle 1] | |
| 251 | + set bestlink {} | |
| 252 | + set bestnode {} | |
| 253 | + | |
| 254 | + foreach \ | |
| 255 | + prev [lrange $cycle 0 end-2] \ | |
| 256 | + cset [lrange $cycle 1 end-1] \ | |
| 257 | + next [lrange $cycle 2 end] { | |
| 258 | + | |
| 259 | + # Each triple PREV -> CSET -> NEXT of changesets, a | |
| 260 | + # 'link' in the cycle, is analysed and the best | |
| 261 | + # location where to at least weaken the cycle is | |
| 262 | + # chosen for further processing. | |
| 263 | + | |
| 264 | + set link [project::revlink %AUTO% $prev $cset $next] | |
| 265 | + if {$bestlink eq ""} { | |
| 266 | + set bestlink $link | |
| 267 | + set bestnode $cset | |
| 268 | + } elseif {[$link betterthan $bestlink]} { | |
| 269 | + $bestlink destroy | |
| 270 | + set bestlink $link | |
| 271 | + set bestnode $cset | |
| 272 | + } else { | |
| 273 | + $link destroy | |
| 274 | + } | |
| 275 | + } | |
| 276 | + | |
| 277 | + log write 5 breakrcycle "Breaking cycle ($cprint) by splitting changeset <[$bestnode id]>" | |
| 278 | + | |
| 279 | + set newcsets [$bestlink break] | |
| 280 | + $bestlink destroy | |
| 281 | + | |
| 282 | + # At this point the old changeset (BESTNODE) is gone | |
| 283 | + # already. We remove it from the graph as well and then enter | |
| 284 | + # the fragments generated for it. | |
| 285 | + | |
| 286 | + $dg node delete $bestnode | |
| 287 | + | |
| 288 | + foreach cset $newcsets { | |
| 289 | + dg node insert $cset | |
| 290 | + dg node set $cset timerange [$cset timerange] | |
| 291 | + } | |
| 292 | + | |
| 293 | + foreach cset $newcsets { | |
| 294 | + foreach succ [$cset successors] { | |
| 295 | + dg arc insert $cset $succ | |
| 296 | + } | |
| 297 | + } | |
| 239 | 298 | return |
| 240 | 299 | } |
| 241 | 300 | |
| 242 | 301 | typevariable at 0 ; # Counter for commit ids for the changesets. |
| 243 | 302 | typevariable bottom {} ; # List of candidate nodes for committing. |
| @@ -256,16 +315,17 @@ | ||
| 256 | 315 | namespace export breakrcycle |
| 257 | 316 | namespace eval breakrcycle { |
| 258 | 317 | namespace import ::vc::fossil::import::cvs::state |
| 259 | 318 | namespace eval project { |
| 260 | 319 | namespace import ::vc::fossil::import::cvs::project::rev |
| 320 | + namespace import ::vc::fossil::import::cvs::project::revlink | |
| 261 | 321 | } |
| 262 | 322 | namespace import ::vc::tools::log |
| 263 | - log register brkrcycle | |
| 323 | + log register breakrcycle | |
| 264 | 324 | } |
| 265 | 325 | } |
| 266 | 326 | |
| 267 | 327 | # # ## ### ##### ######## ############# ##################### |
| 268 | 328 | ## Ready |
| 269 | 329 | |
| 270 | 330 | package provide vc::fossil::import::cvs::pass::breakrcycle 1.0 |
| 271 | 331 | return |
| 272 | 332 |
| --- tools/cvs2fossil/lib/c2f_pbreakrcycle.tcl | |
| +++ tools/cvs2fossil/lib/c2f_pbreakrcycle.tcl | |
| @@ -15,17 +15,18 @@ | |
| 15 | ## dependency tree. |
| 16 | |
| 17 | # # ## ### ##### ######## ############# ##################### |
| 18 | ## Requirements |
| 19 | |
| 20 | package require Tcl 8.4 ; # Required runtime. |
| 21 | package require snit ; # OO system. |
| 22 | package require struct::graph ; # Graph handling. |
| 23 | package require struct::list ; # Higher order list operations. |
| 24 | package require vc::tools::log ; # User feedback. |
| 25 | package require vc::fossil::import::cvs::state ; # State storage. |
| 26 | package require vc::fossil::import::cvs::project::rev ; # Project level changesets |
| 27 | |
| 28 | # # ## ### ##### ######## ############# ##################### |
| 29 | ## Register the pass with the management |
| 30 | |
| 31 | vc::fossil::import::cvs::pass define \ |
| @@ -65,20 +66,22 @@ | |
| 65 | typemethod run {} { |
| 66 | # Pass manager interface. Executed to perform the |
| 67 | # functionality of the pass. |
| 68 | |
| 69 | state reading revision |
| 70 | |
| 71 | # We create a graph of the revision changesets, using the file |
| 72 | # level dependencies to construct a first approximation of |
| 73 | # them at the project level. Then look for cycles in that |
| 74 | # graph and break them. |
| 75 | |
| 76 | # 1. Create nodes for all relevant changesets and a mapping |
| 77 | # from the revisions to their changesets/nodes. |
| 78 | |
| 79 | log write 3 brkrcycle {Creating changeset graph, filling with nodes} |
| 80 | |
| 81 | set dg [struct::graph dg] |
| 82 | |
| 83 | state transaction { |
| 84 | foreach cset [project::rev all] { |
| @@ -90,11 +93,11 @@ | |
| 90 | |
| 91 | # 2. Find for all relevant changeset their revisions and their |
| 92 | # dependencies. Map the latter back to changesets and |
| 93 | # construct the corresponding arcs. |
| 94 | |
| 95 | log write 3 brkrcycle {Setting up node dependencies} |
| 96 | |
| 97 | state transaction { |
| 98 | foreach cset [project::rev all] { |
| 99 | if {[$cset bysymbol]} continue |
| 100 | foreach succ [$cset successors] { |
| @@ -107,21 +110,21 @@ | |
| 107 | # the nodes which have no predecessors, in order from |
| 108 | # oldest to youngest, saving and removing dependencies. If |
| 109 | # we find no nodes without predecessors we have a cycle, |
| 110 | # and work on breaking it. |
| 111 | |
| 112 | log write 3 brkrcycle {Computing changeset order, breaking cycles} |
| 113 | |
| 114 | InitializeCandidates $dg |
| 115 | state transaction { |
| 116 | while {1} { |
| 117 | while {[WithoutPredecessor $dg n]} { |
| 118 | SaveAndRemove $dg $n |
| 119 | } |
| 120 | if {![llength [dg nodes]]} break |
| 121 | set cycle [FindCycle $dg] |
| 122 | BreakCycle $dg $cycle |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | return |
| 127 | } |
| @@ -224,20 +227,76 @@ | |
| 224 | while {1} { |
| 225 | # Stop searching when we have seen the current node |
| 226 | # already, the circle has been closed. |
| 227 | if {[info exists seen($start)]} break |
| 228 | lappend path $start |
| 229 | set seen($start) [llength $path] |
| 230 | # Choose arbitrary predecessor |
| 231 | set start [lindex [$dg nodes -in $start] 0] |
| 232 | } |
| 233 | |
| 234 | return [struct::list reverse [lrange $path $seen($start) end]] |
| 235 | } |
| 236 | |
| 237 | proc BreakCycle {dg cycle} { |
| 238 | trouble internal "Break cycle <$cycle>" |
| 239 | return |
| 240 | } |
| 241 | |
| 242 | typevariable at 0 ; # Counter for commit ids for the changesets. |
| 243 | typevariable bottom {} ; # List of candidate nodes for committing. |
| @@ -256,16 +315,17 @@ | |
| 256 | namespace export breakrcycle |
| 257 | namespace eval breakrcycle { |
| 258 | namespace import ::vc::fossil::import::cvs::state |
| 259 | namespace eval project { |
| 260 | namespace import ::vc::fossil::import::cvs::project::rev |
| 261 | } |
| 262 | namespace import ::vc::tools::log |
| 263 | log register brkrcycle |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | # # ## ### ##### ######## ############# ##################### |
| 268 | ## Ready |
| 269 | |
| 270 | package provide vc::fossil::import::cvs::pass::breakrcycle 1.0 |
| 271 | return |
| 272 |
| --- tools/cvs2fossil/lib/c2f_pbreakrcycle.tcl | |
| +++ tools/cvs2fossil/lib/c2f_pbreakrcycle.tcl | |
| @@ -15,17 +15,18 @@ | |
| 15 | ## dependency tree. |
| 16 | |
| 17 | # # ## ### ##### ######## ############# ##################### |
| 18 | ## Requirements |
| 19 | |
| 20 | package require Tcl 8.4 ; # Required runtime. |
| 21 | package require snit ; # OO system. |
| 22 | package require struct::graph ; # Graph handling. |
| 23 | package require struct::list ; # Higher order list operations. |
| 24 | package require vc::tools::log ; # User feedback. |
| 25 | package require vc::fossil::import::cvs::state ; # State storage. |
| 26 | package require vc::fossil::import::cvs::project::rev ; # Project level changesets |
| 27 | package require vc::fossil::import::cvs::project::revlink ; # Cycle links. |
| 28 | |
| 29 | # # ## ### ##### ######## ############# ##################### |
| 30 | ## Register the pass with the management |
| 31 | |
| 32 | vc::fossil::import::cvs::pass define \ |
| @@ -65,20 +66,22 @@ | |
| 66 | typemethod run {} { |
| 67 | # Pass manager interface. Executed to perform the |
| 68 | # functionality of the pass. |
| 69 | |
| 70 | state reading revision |
| 71 | state reading changeset |
| 72 | state reading csrevision |
| 73 | |
| 74 | # We create a graph of the revision changesets, using the file |
| 75 | # level dependencies to construct a first approximation of |
| 76 | # them at the project level. Then look for cycles in that |
| 77 | # graph and break them. |
| 78 | |
| 79 | # 1. Create nodes for all relevant changesets and a mapping |
| 80 | # from the revisions to their changesets/nodes. |
| 81 | |
| 82 | log write 3 breakrcycle {Creating changeset graph, filling with nodes} |
| 83 | |
| 84 | set dg [struct::graph dg] |
| 85 | |
| 86 | state transaction { |
| 87 | foreach cset [project::rev all] { |
| @@ -90,11 +93,11 @@ | |
| 93 | |
| 94 | # 2. Find for all relevant changeset their revisions and their |
| 95 | # dependencies. Map the latter back to changesets and |
| 96 | # construct the corresponding arcs. |
| 97 | |
| 98 | log write 3 breakrcycle {Setting up node dependencies} |
| 99 | |
| 100 | state transaction { |
| 101 | foreach cset [project::rev all] { |
| 102 | if {[$cset bysymbol]} continue |
| 103 | foreach succ [$cset successors] { |
| @@ -107,21 +110,21 @@ | |
| 110 | # the nodes which have no predecessors, in order from |
| 111 | # oldest to youngest, saving and removing dependencies. If |
| 112 | # we find no nodes without predecessors we have a cycle, |
| 113 | # and work on breaking it. |
| 114 | |
| 115 | log write 3 breakrcycle {Computing changeset order, breaking cycles} |
| 116 | |
| 117 | InitializeCandidates $dg |
| 118 | state transaction { |
| 119 | while {1} { |
| 120 | while {[WithoutPredecessor $dg n]} { |
| 121 | SaveAndRemove $dg $n |
| 122 | } |
| 123 | if {![llength [dg nodes]]} break |
| 124 | BreakCycle $dg [FindCycle $dg] |
| 125 | InitializeCandidates $dg |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | return |
| 130 | } |
| @@ -224,20 +227,76 @@ | |
| 227 | while {1} { |
| 228 | # Stop searching when we have seen the current node |
| 229 | # already, the circle has been closed. |
| 230 | if {[info exists seen($start)]} break |
| 231 | lappend path $start |
| 232 | set seen($start) [expr {[llength $path]-1}] |
| 233 | # Choose arbitrary predecessor |
| 234 | set start [lindex [$dg nodes -in $start] 0] |
| 235 | } |
| 236 | |
| 237 | return [struct::list reverse [lrange $path $seen($start) end]] |
| 238 | } |
| 239 | |
| 240 | proc ID {cset} { return "<[$cset id]>" } |
| 241 | |
| 242 | proc BreakCycle {dg cycle} { |
| 243 | # The cycle we have gotten is broken by breaking apart one or |
| 244 | # more of the changesets in the cycle. This causes us to |
| 245 | # create one or more changesets which are to be committed, |
| 246 | # added to the graph, etc. pp. |
| 247 | |
| 248 | set cprint [join [struct::list map $cycle [myproc ID]] { }] |
| 249 | |
| 250 | lappend cycle [lindex $cycle 0] [lindex $cycle 1] |
| 251 | set bestlink {} |
| 252 | set bestnode {} |
| 253 | |
| 254 | foreach \ |
| 255 | prev [lrange $cycle 0 end-2] \ |
| 256 | cset [lrange $cycle 1 end-1] \ |
| 257 | next [lrange $cycle 2 end] { |
| 258 | |
| 259 | # Each triple PREV -> CSET -> NEXT of changesets, a |
| 260 | # 'link' in the cycle, is analysed and the best |
| 261 | # location where to at least weaken the cycle is |
| 262 | # chosen for further processing. |
| 263 | |
| 264 | set link [project::revlink %AUTO% $prev $cset $next] |
| 265 | if {$bestlink eq ""} { |
| 266 | set bestlink $link |
| 267 | set bestnode $cset |
| 268 | } elseif {[$link betterthan $bestlink]} { |
| 269 | $bestlink destroy |
| 270 | set bestlink $link |
| 271 | set bestnode $cset |
| 272 | } else { |
| 273 | $link destroy |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | log write 5 breakrcycle "Breaking cycle ($cprint) by splitting changeset <[$bestnode id]>" |
| 278 | |
| 279 | set newcsets [$bestlink break] |
| 280 | $bestlink destroy |
| 281 | |
| 282 | # At this point the old changeset (BESTNODE) is gone |
| 283 | # already. We remove it from the graph as well and then enter |
| 284 | # the fragments generated for it. |
| 285 | |
| 286 | $dg node delete $bestnode |
| 287 | |
| 288 | foreach cset $newcsets { |
| 289 | dg node insert $cset |
| 290 | dg node set $cset timerange [$cset timerange] |
| 291 | } |
| 292 | |
| 293 | foreach cset $newcsets { |
| 294 | foreach succ [$cset successors] { |
| 295 | dg arc insert $cset $succ |
| 296 | } |
| 297 | } |
| 298 | return |
| 299 | } |
| 300 | |
| 301 | typevariable at 0 ; # Counter for commit ids for the changesets. |
| 302 | typevariable bottom {} ; # List of candidate nodes for committing. |
| @@ -256,16 +315,17 @@ | |
| 315 | namespace export breakrcycle |
| 316 | namespace eval breakrcycle { |
| 317 | namespace import ::vc::fossil::import::cvs::state |
| 318 | namespace eval project { |
| 319 | namespace import ::vc::fossil::import::cvs::project::rev |
| 320 | namespace import ::vc::fossil::import::cvs::project::revlink |
| 321 | } |
| 322 | namespace import ::vc::tools::log |
| 323 | log register breakrcycle |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | # # ## ### ##### ######## ############# ##################### |
| 328 | ## Ready |
| 329 | |
| 330 | package provide vc::fossil::import::cvs::pass::breakrcycle 1.0 |
| 331 | return |
| 332 |
+74
-13
| --- tools/cvs2fossil/lib/c2f_prev.tcl | ||
| +++ tools/cvs2fossil/lib/c2f_prev.tcl | ||
| @@ -44,26 +44,34 @@ | ||
| 44 | 44 | return |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | 47 | method id {} { return $myid } |
| 48 | 48 | method revisions {} { return $myrevisions } |
| 49 | + method data {} { return [list $myproject $mytype $mysrcid] } | |
| 49 | 50 | |
| 50 | 51 | method setid {id} { set myid $id ; return } |
| 51 | 52 | |
| 52 | 53 | method bysymbol {} { return [expr {$mytype eq "sym"}] } |
| 53 | 54 | method byrevision {} { return [expr {$mytype eq "rev"}] } |
| 54 | 55 | |
| 55 | 56 | method successors {} { |
| 56 | 57 | # NOTE / FUTURE: Possible bottleneck. |
| 57 | - array set dependencies {} | |
| 58 | - PullSuccessorRevisions dependencies $myrevisions | |
| 59 | 58 | set csets {} |
| 60 | - foreach {_ child} [array get dependencies] { | |
| 61 | - lappend csets $myrevmap($child) | |
| 59 | + foreach {_ children} [$self nextmap] { | |
| 60 | + foreach child $children { | |
| 61 | + lappend csets $myrevmap($child) | |
| 62 | + } | |
| 62 | 63 | } |
| 63 | 64 | return [lsort -unique $csets] |
| 64 | 65 | } |
| 66 | + | |
| 67 | + method nextmap {} { | |
| 68 | + if {[llength $mynextmap]} { return $mynextmap } | |
| 69 | + PullSuccessorRevisions tmp $myrevisions | |
| 70 | + set mynextmap [array get tmp] | |
| 71 | + return $mynextmap | |
| 72 | + } | |
| 65 | 73 | |
| 66 | 74 | method breakinternaldependencies {} { |
| 67 | 75 | # This method inspects the changesets for internal |
| 68 | 76 | # dependencies. Nothing is done if there are no |
| 69 | 77 | # such. Otherwise the changeset is split into a set of |
| @@ -84,11 +92,11 @@ | ||
| 84 | 92 | |
| 85 | 93 | # Array of dependencies (parent -> child). This is pulled from |
| 86 | 94 | # the state, and limited to successors within the changeset. |
| 87 | 95 | |
| 88 | 96 | array set dependencies {} |
| 89 | - PullSuccessorRevisions dependencies $myrevisions | |
| 97 | + PullInternalSuccessorRevisions dependencies $myrevisions | |
| 90 | 98 | if {![array size dependencies]} {return 0} ; # Nothing to break. |
| 91 | 99 | |
| 92 | 100 | log write 6 csets ...<$myid>....................................................... |
| 93 | 101 | |
| 94 | 102 | # We have internal dependencies to break. We now iterate over |
| @@ -231,19 +239,41 @@ | ||
| 231 | 239 | SELECT MIN(R.date), MAX(R.date) |
| 232 | 240 | FROM revision R |
| 233 | 241 | WHERE R.rid IN $theset |
| 234 | 242 | "] |
| 235 | 243 | } |
| 244 | + | |
| 245 | + method drop {} { | |
| 246 | + state transaction { | |
| 247 | + state run { | |
| 248 | + DELETE FROM changeset WHERE cid = $myid; | |
| 249 | + DELETE FROM csrevision WHERE cid = $myid; | |
| 250 | + } | |
| 251 | + } | |
| 252 | + foreach r $myrevisions { unset myrevmap($r) } | |
| 253 | + set pos [lsearch -exact $mychangesets $self] | |
| 254 | + set mychangesets [lreplace $mychangesets $pos $pos] | |
| 255 | + return | |
| 256 | + } | |
| 236 | 257 | |
| 237 | 258 | # # ## ### ##### ######## ############# |
| 238 | 259 | ## State |
| 239 | 260 | |
| 240 | - variable myid ; # Id of the cset for the persistent state. | |
| 241 | - variable myproject ; # Reference of the project object the changeset belongs to. | |
| 242 | - variable mytype ; # rev or sym, where the cset originated from. | |
| 243 | - variable mysrcid ; # id of the metadata or symbol the cset is based on. | |
| 244 | - variable myrevisions ; # List of the file level revisions in the cset. | |
| 261 | + variable myid {} ; # Id of the cset for the persistent | |
| 262 | + # state. | |
| 263 | + variable myproject {} ; # Reference of the project object the | |
| 264 | + # changeset belongs to. | |
| 265 | + variable mytype {} ; # rev or sym, where the cset originated | |
| 266 | + # from. | |
| 267 | + variable mysrcid {} ; # Id of the metadata or symbol the cset | |
| 268 | + # is based on. | |
| 269 | + variable myrevisions {} ; # List of the file level revisions in | |
| 270 | + # the cset. | |
| 271 | + variable mynextmap {} ; # Dictionary mapping from the revisions | |
| 272 | + # to their successors. Cache to avoid | |
| 273 | + # loading this from the state more than | |
| 274 | + # once. | |
| 245 | 275 | |
| 246 | 276 | # # ## ### ##### ######## ############# |
| 247 | 277 | ## Internal methods |
| 248 | 278 | |
| 249 | 279 | typevariable mycounter 0 ; # Id counter for csets. |
| @@ -254,11 +284,11 @@ | ||
| 254 | 284 | SELECT tid, name FROM cstype; |
| 255 | 285 | }] { set mycstype($name) $tid } |
| 256 | 286 | return |
| 257 | 287 | } |
| 258 | 288 | |
| 259 | - proc PullSuccessorRevisions {dv revisions} { | |
| 289 | + proc PullInternalSuccessorRevisions {dv revisions} { | |
| 260 | 290 | upvar 1 $dv dependencies |
| 261 | 291 | set theset ('[join $revisions {','}]') |
| 262 | 292 | |
| 263 | 293 | foreach {rid child} [state run " |
| 264 | 294 | -- Primary children |
| @@ -284,11 +314,42 @@ | ||
| 284 | 314 | "] { |
| 285 | 315 | # Consider moving this to the integrity module. |
| 286 | 316 | if {$rid == $child} { |
| 287 | 317 | trouble internal "Revision $rid depends on itself." |
| 288 | 318 | } |
| 289 | - set dependencies($rid) $child | |
| 319 | + lappend dependencies($rid) $child | |
| 320 | + } | |
| 321 | + } | |
| 322 | + | |
| 323 | + proc PullSuccessorRevisions {dv revisions} { | |
| 324 | + upvar 1 $dv dependencies | |
| 325 | + set theset ('[join $revisions {','}]') | |
| 326 | + | |
| 327 | + foreach {rid child} [state run " | |
| 328 | + -- Primary children | |
| 329 | + SELECT R.rid, R.child | |
| 330 | + FROM revision R | |
| 331 | + WHERE R.rid IN $theset | |
| 332 | + AND R.child IS NOT NULL | |
| 333 | + UNION | |
| 334 | + -- Transition NTDB to trunk | |
| 335 | + SELECT R.rid, R.dbchild | |
| 336 | + FROM revision R | |
| 337 | + WHERE R.rid IN $theset | |
| 338 | + AND R.dbchild IS NOT NULL | |
| 339 | + UNION | |
| 340 | + -- Secondary (branch) children | |
| 341 | + SELECT R.rid, B.brid | |
| 342 | + FROM revision R, revisionbranchchildren B | |
| 343 | + WHERE R.rid IN $theset | |
| 344 | + AND R.rid = B.rid | |
| 345 | + "] { | |
| 346 | + # Consider moving this to the integrity module. | |
| 347 | + if {$rid == $child} { | |
| 348 | + trouble internal "Revision $rid depends on itself." | |
| 349 | + } | |
| 350 | + lappend dependencies($rid) $child | |
| 290 | 351 | } |
| 291 | 352 | } |
| 292 | 353 | |
| 293 | 354 | proc InitializeBreakState {revisions} { |
| 294 | 355 | upvar 1 pos pos cross cross range range depc depc delta delta \ |
| @@ -318,11 +379,11 @@ | ||
| 318 | 379 | # to ensure that the following loop runs correctly. |
| 319 | 380 | # |
| 320 | 381 | # Note 2: start == end is not possible. It indicates a |
| 321 | 382 | # self-dependency due to the uniqueness of positions, |
| 322 | 383 | # and that is something we have ruled out already, see |
| 323 | - # PullSuccessorRevisions. | |
| 384 | + # PullInternalSuccessorRevisions. | |
| 324 | 385 | |
| 325 | 386 | foreach {rid child} [array get dependencies] { |
| 326 | 387 | set dkey [list $rid $child] |
| 327 | 388 | set start $pos($rid) |
| 328 | 389 | set end $pos($child) |
| 329 | 390 | |
| 330 | 391 | ADDED tools/cvs2fossil/lib/c2f_prevlink.tcl |
| --- tools/cvs2fossil/lib/c2f_prev.tcl | |
| +++ tools/cvs2fossil/lib/c2f_prev.tcl | |
| @@ -44,26 +44,34 @@ | |
| 44 | return |
| 45 | } |
| 46 | |
| 47 | method id {} { return $myid } |
| 48 | method revisions {} { return $myrevisions } |
| 49 | |
| 50 | method setid {id} { set myid $id ; return } |
| 51 | |
| 52 | method bysymbol {} { return [expr {$mytype eq "sym"}] } |
| 53 | method byrevision {} { return [expr {$mytype eq "rev"}] } |
| 54 | |
| 55 | method successors {} { |
| 56 | # NOTE / FUTURE: Possible bottleneck. |
| 57 | array set dependencies {} |
| 58 | PullSuccessorRevisions dependencies $myrevisions |
| 59 | set csets {} |
| 60 | foreach {_ child} [array get dependencies] { |
| 61 | lappend csets $myrevmap($child) |
| 62 | } |
| 63 | return [lsort -unique $csets] |
| 64 | } |
| 65 | |
| 66 | method breakinternaldependencies {} { |
| 67 | # This method inspects the changesets for internal |
| 68 | # dependencies. Nothing is done if there are no |
| 69 | # such. Otherwise the changeset is split into a set of |
| @@ -84,11 +92,11 @@ | |
| 84 | |
| 85 | # Array of dependencies (parent -> child). This is pulled from |
| 86 | # the state, and limited to successors within the changeset. |
| 87 | |
| 88 | array set dependencies {} |
| 89 | PullSuccessorRevisions dependencies $myrevisions |
| 90 | if {![array size dependencies]} {return 0} ; # Nothing to break. |
| 91 | |
| 92 | log write 6 csets ...<$myid>....................................................... |
| 93 | |
| 94 | # We have internal dependencies to break. We now iterate over |
| @@ -231,19 +239,41 @@ | |
| 231 | SELECT MIN(R.date), MAX(R.date) |
| 232 | FROM revision R |
| 233 | WHERE R.rid IN $theset |
| 234 | "] |
| 235 | } |
| 236 | |
| 237 | # # ## ### ##### ######## ############# |
| 238 | ## State |
| 239 | |
| 240 | variable myid ; # Id of the cset for the persistent state. |
| 241 | variable myproject ; # Reference of the project object the changeset belongs to. |
| 242 | variable mytype ; # rev or sym, where the cset originated from. |
| 243 | variable mysrcid ; # id of the metadata or symbol the cset is based on. |
| 244 | variable myrevisions ; # List of the file level revisions in the cset. |
| 245 | |
| 246 | # # ## ### ##### ######## ############# |
| 247 | ## Internal methods |
| 248 | |
| 249 | typevariable mycounter 0 ; # Id counter for csets. |
| @@ -254,11 +284,11 @@ | |
| 254 | SELECT tid, name FROM cstype; |
| 255 | }] { set mycstype($name) $tid } |
| 256 | return |
| 257 | } |
| 258 | |
| 259 | proc PullSuccessorRevisions {dv revisions} { |
| 260 | upvar 1 $dv dependencies |
| 261 | set theset ('[join $revisions {','}]') |
| 262 | |
| 263 | foreach {rid child} [state run " |
| 264 | -- Primary children |
| @@ -284,11 +314,42 @@ | |
| 284 | "] { |
| 285 | # Consider moving this to the integrity module. |
| 286 | if {$rid == $child} { |
| 287 | trouble internal "Revision $rid depends on itself." |
| 288 | } |
| 289 | set dependencies($rid) $child |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | proc InitializeBreakState {revisions} { |
| 294 | upvar 1 pos pos cross cross range range depc depc delta delta \ |
| @@ -318,11 +379,11 @@ | |
| 318 | # to ensure that the following loop runs correctly. |
| 319 | # |
| 320 | # Note 2: start == end is not possible. It indicates a |
| 321 | # self-dependency due to the uniqueness of positions, |
| 322 | # and that is something we have ruled out already, see |
| 323 | # PullSuccessorRevisions. |
| 324 | |
| 325 | foreach {rid child} [array get dependencies] { |
| 326 | set dkey [list $rid $child] |
| 327 | set start $pos($rid) |
| 328 | set end $pos($child) |
| 329 | |
| 330 | DDED tools/cvs2fossil/lib/c2f_prevlink.tcl |
| --- tools/cvs2fossil/lib/c2f_prev.tcl | |
| +++ tools/cvs2fossil/lib/c2f_prev.tcl | |
| @@ -44,26 +44,34 @@ | |
| 44 | return |
| 45 | } |
| 46 | |
| 47 | method id {} { return $myid } |
| 48 | method revisions {} { return $myrevisions } |
| 49 | method data {} { return [list $myproject $mytype $mysrcid] } |
| 50 | |
| 51 | method setid {id} { set myid $id ; return } |
| 52 | |
| 53 | method bysymbol {} { return [expr {$mytype eq "sym"}] } |
| 54 | method byrevision {} { return [expr {$mytype eq "rev"}] } |
| 55 | |
| 56 | method successors {} { |
| 57 | # NOTE / FUTURE: Possible bottleneck. |
| 58 | set csets {} |
| 59 | foreach {_ children} [$self nextmap] { |
| 60 | foreach child $children { |
| 61 | lappend csets $myrevmap($child) |
| 62 | } |
| 63 | } |
| 64 | return [lsort -unique $csets] |
| 65 | } |
| 66 | |
| 67 | method nextmap {} { |
| 68 | if {[llength $mynextmap]} { return $mynextmap } |
| 69 | PullSuccessorRevisions tmp $myrevisions |
| 70 | set mynextmap [array get tmp] |
| 71 | return $mynextmap |
| 72 | } |
| 73 | |
| 74 | method breakinternaldependencies {} { |
| 75 | # This method inspects the changesets for internal |
| 76 | # dependencies. Nothing is done if there are no |
| 77 | # such. Otherwise the changeset is split into a set of |
| @@ -84,11 +92,11 @@ | |
| 92 | |
| 93 | # Array of dependencies (parent -> child). This is pulled from |
| 94 | # the state, and limited to successors within the changeset. |
| 95 | |
| 96 | array set dependencies {} |
| 97 | PullInternalSuccessorRevisions dependencies $myrevisions |
| 98 | if {![array size dependencies]} {return 0} ; # Nothing to break. |
| 99 | |
| 100 | log write 6 csets ...<$myid>....................................................... |
| 101 | |
| 102 | # We have internal dependencies to break. We now iterate over |
| @@ -231,19 +239,41 @@ | |
| 239 | SELECT MIN(R.date), MAX(R.date) |
| 240 | FROM revision R |
| 241 | WHERE R.rid IN $theset |
| 242 | "] |
| 243 | } |
| 244 | |
| 245 | method drop {} { |
| 246 | state transaction { |
| 247 | state run { |
| 248 | DELETE FROM changeset WHERE cid = $myid; |
| 249 | DELETE FROM csrevision WHERE cid = $myid; |
| 250 | } |
| 251 | } |
| 252 | foreach r $myrevisions { unset myrevmap($r) } |
| 253 | set pos [lsearch -exact $mychangesets $self] |
| 254 | set mychangesets [lreplace $mychangesets $pos $pos] |
| 255 | return |
| 256 | } |
| 257 | |
| 258 | # # ## ### ##### ######## ############# |
| 259 | ## State |
| 260 | |
| 261 | variable myid {} ; # Id of the cset for the persistent |
| 262 | # state. |
| 263 | variable myproject {} ; # Reference of the project object the |
| 264 | # changeset belongs to. |
| 265 | variable mytype {} ; # rev or sym, where the cset originated |
| 266 | # from. |
| 267 | variable mysrcid {} ; # Id of the metadata or symbol the cset |
| 268 | # is based on. |
| 269 | variable myrevisions {} ; # List of the file level revisions in |
| 270 | # the cset. |
| 271 | variable mynextmap {} ; # Dictionary mapping from the revisions |
| 272 | # to their successors. Cache to avoid |
| 273 | # loading this from the state more than |
| 274 | # once. |
| 275 | |
| 276 | # # ## ### ##### ######## ############# |
| 277 | ## Internal methods |
| 278 | |
| 279 | typevariable mycounter 0 ; # Id counter for csets. |
| @@ -254,11 +284,11 @@ | |
| 284 | SELECT tid, name FROM cstype; |
| 285 | }] { set mycstype($name) $tid } |
| 286 | return |
| 287 | } |
| 288 | |
| 289 | proc PullInternalSuccessorRevisions {dv revisions} { |
| 290 | upvar 1 $dv dependencies |
| 291 | set theset ('[join $revisions {','}]') |
| 292 | |
| 293 | foreach {rid child} [state run " |
| 294 | -- Primary children |
| @@ -284,11 +314,42 @@ | |
| 314 | "] { |
| 315 | # Consider moving this to the integrity module. |
| 316 | if {$rid == $child} { |
| 317 | trouble internal "Revision $rid depends on itself." |
| 318 | } |
| 319 | lappend dependencies($rid) $child |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | proc PullSuccessorRevisions {dv revisions} { |
| 324 | upvar 1 $dv dependencies |
| 325 | set theset ('[join $revisions {','}]') |
| 326 | |
| 327 | foreach {rid child} [state run " |
| 328 | -- Primary children |
| 329 | SELECT R.rid, R.child |
| 330 | FROM revision R |
| 331 | WHERE R.rid IN $theset |
| 332 | AND R.child IS NOT NULL |
| 333 | UNION |
| 334 | -- Transition NTDB to trunk |
| 335 | SELECT R.rid, R.dbchild |
| 336 | FROM revision R |
| 337 | WHERE R.rid IN $theset |
| 338 | AND R.dbchild IS NOT NULL |
| 339 | UNION |
| 340 | -- Secondary (branch) children |
| 341 | SELECT R.rid, B.brid |
| 342 | FROM revision R, revisionbranchchildren B |
| 343 | WHERE R.rid IN $theset |
| 344 | AND R.rid = B.rid |
| 345 | "] { |
| 346 | # Consider moving this to the integrity module. |
| 347 | if {$rid == $child} { |
| 348 | trouble internal "Revision $rid depends on itself." |
| 349 | } |
| 350 | lappend dependencies($rid) $child |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | proc InitializeBreakState {revisions} { |
| 355 | upvar 1 pos pos cross cross range range depc depc delta delta \ |
| @@ -318,11 +379,11 @@ | |
| 379 | # to ensure that the following loop runs correctly. |
| 380 | # |
| 381 | # Note 2: start == end is not possible. It indicates a |
| 382 | # self-dependency due to the uniqueness of positions, |
| 383 | # and that is something we have ruled out already, see |
| 384 | # PullInternalSuccessorRevisions. |
| 385 | |
| 386 | foreach {rid child} [array get dependencies] { |
| 387 | set dkey [list $rid $child] |
| 388 | set start $pos($rid) |
| 389 | set end $pos($child) |
| 390 | |
| 391 | DDED tools/cvs2fossil/lib/c2f_prevlink.tcl |
| --- a/tools/cvs2fossil/lib/c2f_prevlink.tcl | ||
| +++ b/tools/cvs2fossil/lib/c2f_prevlink.tcl | ||
| @@ -0,0 +1,180 @@ | ||
| 1 | +## -*- tcl -*- | |
| 2 | +# # ## ### ##### ## -*- tcl -*- | |
| 3 | +# # ## ### ##### ######## ############# ##################### | |
| 4 | +## Copyright (c) 2007 Andreas Kupries. | |
| 5 | +# | |
| 6 | +# This software is licensed as described in the file LICENSE, which | |
| 7 | +# you should have received as part of this distribution. | |
| 8 | +# | |
| 9 | +# This software consists of voluntary contributions made by many | |
| 10 | +# individuals. For exact contribution history, see the revision | |
| 11 | +# history and logs, available at http://fossil-scm.hwaci.com/fossil | |
| 12 | +# # ## ### ##### ######## ############# ##################### | |
| 13 | + | |
| 14 | +## Helper class for the pass 6 cycle breaker. Each instance refers to | |
| 15 | +## three changesets A, B, and C, with A a predecessor of B, and B | |
| 16 | +## predecessor of C, and the whole part of a dependency cycle. | |
| 17 | + | |
| 18 | +## Instances analyse the file level dependencies which gave rise to | |
| 19 | +## the changeset dependencies of A, B, and C, with the results used ####### ############# ##################### | |
| 20 | +## Copyright (c) 2007 Andreas Kupries. | |
| 21 | +# | |
| 22 | +# This software is licensed as described in the file LICENSE, which | |
| 23 | +# you should have received as part of this distribution. | |
| 24 | +# | |
| 25 | +# This software consists of voluntary contributions made by many | |
| 26 | +# individuals. For exact contribution history, see the revision | |
| 27 | +# history and logs, available at http://fossil-scm.hwaci.com/fossil | |
| 28 | +# # ## ### ##### ######## ############# ##################### | |
| 29 | + | |
| 30 | +## Helper class for the pass 6 cycle breaker. Each instance refers to | |
| 31 | +## three changesets A, B, and C, with A a predecessor of B, and B | |
| 32 | +## predecessort best fully break the cycle. | |
| 33 | + | |
| 34 | +# # ## ### ##### ######## ############# ##################### | |
| 35 | +## Requirements | |
| 36 | + | |
| 37 | +package require Tcl 8.4 ; # Required runtime. | |
| 38 | +package require snit ; # OO system. | |
| 39 | +package require vc::tools::misc ; # Text formatting | |
| 40 | +package require vc::tools::trouble ; # Error reporting. | |
| 41 | +package require vc::tools::log ; # User feedback. | |
| 42 | +package require vc::fossil::import::cvs::state ; # State storage. | |
| 43 | +package require vc::fossil::import::cvs::integrity ; # State integrity checks. | |
| 44 | +package require vc::fossil::import::cvs::project::rev ; # Project level changesets | |
| 45 | + | |
| 46 | +# # ## ### ##### ######## ############# ##################### | |
| 47 | +## | |
| 48 | + | |
| 49 | +snit::type ::vc::fossil::import::cvs::project::revlink { | |
| 50 | + # # ## ### ##### ######## ############# | |
| 51 | + ## Public API | |
| 52 | + | |
| 53 | + constructor {prev cset next} { | |
| 54 | + set myprev $prev | |
| 55 | + set mycset $cset | |
| 56 | + set mynext $next | |
| 57 | + | |
| 58 | + # We perform the bulk of the analysis during construction. The | |
| 59 | + # file revisions held by the changeset CSET can be sorted into | |
| 60 | + # four categories. | |
| 61 | + | |
| 62 | + # 1. Revisions whose predecessors are not in PREV, nor are | |
| 63 | + # their successors found in NEXT. These revisions do not | |
| 64 | + # count, as they did not induce any of the two dependencies | |
| 65 | + # under consideration. Thf {!(prev) $mycount(next | |
| 66 | + trouble internal "than {other} {" | |
| 67 | + | |
| 68 | + set sbreak [$self breakable] | |
| 69 | + set obreak [$other breakable] | |
| 70 | + | |
| 71 | + if {$sbreak && !$obreak} { return 1 } ; # self is better. | |
| 72 | + if {!$sbreak && $obreak} { return 0 } ; # self is worse. | |
| 73 | + | |
| 74 | + # Equality. Look at the counters. | |
| 75 | + # - Whichever has the lesser number of passthrough revisions | |
| 76 | + # is better, as more can be split off, weakening the cycle | |
| 77 | + # more. | |
| 78 | + # - Whichever has less links to move is better. | |
| 79 | + | |
| 80 | + set opass [$other passcount] | |
| 81 | + if {$mycount(pass) < $opass} { return 1 } ; # self is better. | |
| 82 | + if {$mycount(pass) > $opass} { return 0 } ; # self is worse. | |
| 83 | + | |
| 84 | + set smove [$self linkstomove] | |
| 85 | + set omove [$other linkstomove] | |
| 86 | + | |
| 87 | + if {$smove < $omove} { return 1 } ; # self is better. | |
| 88 | + | |
| 89 | + return 0 ; # Self is worse or equal, i.e. not better. | |
| 90 | + } | |
| 91 | + | |
| 92 | + method bre<[$mycset id]>split in the mycategory(prev|nehole part of a dependency cycle. | |
| 93 | + | |
| 94 | +## Instances analyse the file level dependencies which gave rise to | |
| 95 | +## the changeset dependencies of A, B, and C, with the results used ####### ############# ##################### | |
| 96 | +## Copyright (c) 2007 Andreas Kupries. | |
| 97 | +# | |
| 98 | +# This software is licensed as described in the file LICENSE, which | |
| 99 | +# you should have received as part of this distribution. | |
| 100 | +# | |
| 101 | +# This software consists of voluntary contributions made by many | |
| 102 | +# individuals. For exact contribution history, see the revision | |
| 103 | +# history and logs, available at http://fossil-scm.hwaci.com/fossil | |
| 104 | +# # ## ### ##### ######## ############# ##################### | |
| 105 | + | |
| 106 | +## Helper class for the pass 6 cycle breaker. Each instance refers to | |
| 107 | +## three changesets A, B, and C, with A a predecessor of B, and B | |
| 108 | +## predecessort best fully break the cycle. | |
| 109 | + | |
| 110 | +# # ## ### ##### ######## ############# ##################### | |
| 111 | +## Requirements | |
| 112 | + | |
| 113 | +package require Tcl 8.4 ; # Required runtime. | |
| 114 | +package require snit ; # OO system. | |
| 115 | +package require vc::tools::misc ; # Text formatting | |
| 116 | +package require vc::tools::trouble ; # Error reporting. | |
| 117 | +package require vc::tools::log ; # User feedback. | |
| 118 | +package require vc::fossil::import::cvs::state ; # State storage. | |
| 119 | +package require vc::fossil::import::cvs::integrity ; # State integrity checks. | |
| 120 | +package require vc::fossil::import::cvs::project::rev ; # Project level changesets | |
| 121 | + | |
| 122 | +# # ## ### ##### ######## ############# ##################### | |
| 123 | +## | |
| 124 | + | |
| 125 | +snit::type ::vc::fossil::import::cvs::project::revlink { | |
| 126 | + # # ## ### ##### ######## ############# | |
| 127 | + ## Public API | |
| 128 | + | |
| 129 | + constructor {prev cset next} { | |
| 130 | + set myprev $prev | |
| 131 | + set mycset $cset | |
| 132 | + set mynext $next | |
| 133 | + | |
| 134 | + # We perform the bulk of the analysis during construction. The | |
| 135 | + # file revisions held by the changeset CSET can be sorted into | |
| 136 | + # four categories. | |
| 137 | + | |
| 138 | + # 1. Revisions whose predecessors are not in PREV, nor are | |
| 139 | + # their successors found in NEXT. These revisions do not | |
| 140 | + # count, as they did not induce any of the two dependencies | |
| 141 | + # under consideration. Thf {!(prev) $mycount(next | |
| 142 | + trouble internal "than {other} {" | |
| 143 | + | |
| 144 | + set sbreak [$self breakable] | |
| 145 | + set obreak [$other breakable] | |
| 146 | + | |
| 147 | + if {$sbreak && !$obreak} { return 1 } ; # self is better. | |
| 148 | + if {!$sbreak && $obreak} { return 0 } ; # self is worse. | |
| 149 | + | |
| 150 | + # Equality. Look at the counters. | |
| 151 | + # - Whichever has the lesser number of passthrough revisions | |
| 152 | + # is better, as more can be split off, weakening the cycle | |
| 153 | + # more. | |
| 154 | + # - Whichever has less links to move is better. | |
| 155 | + | |
| 156 | + set opass [$other passcount] | |
| 157 | + if {$mycount(pass) < $opass} { return 1 } ; # self is better. | |
| 158 | + if {$mycount(pass) > $opass} { return 0 } ; # self is worse. | |
| 159 | + | |
| 160 | + set smove [$self linkstomove] | |
| 161 | + set omove [$other linkstomove] | |
| 162 | + | |
| 163 | + if {$smove < $omove} { return 1 } ; # self is better. | |
| 164 | + | |
| 165 | + return 0 ; # Self is worse or equal, i.e. not better. | |
| 166 | + } | |
| 167 | + | |
| 168 | + method bre<[$mycset id]>new changesets the | |
| 169 | + # old one is dropped from all databases, in and out of memory, | |
| 170 | + # and then destroyed. | |
| 171 | + | |
| 172 | + struct::list assign [$mycset data] project cstype cssrc | |
| 173 | + $mycset drop | |
| 174 | + $mycset destroy | |
| 175 | + | |
| 176 | + set newcsets {} | |
| 177 | + lappend newcsets [project::rev %AUTO% $project $cstype $cssrc $mycategory(prev)] | |
| 178 | + lappend newcsets [project::rev %AUext)] | |
| 179 | + | |
| 180 | + foreach c $newcsets { $c p |
| --- a/tools/cvs2fossil/lib/c2f_prevlink.tcl | |
| +++ b/tools/cvs2fossil/lib/c2f_prevlink.tcl | |
| @@ -0,0 +1,180 @@ | |
| --- a/tools/cvs2fossil/lib/c2f_prevlink.tcl | |
| +++ b/tools/cvs2fossil/lib/c2f_prevlink.tcl | |
| @@ -0,0 +1,180 @@ | |
| 1 | ## -*- tcl -*- |
| 2 | # # ## ### ##### ## -*- tcl -*- |
| 3 | # # ## ### ##### ######## ############# ##################### |
| 4 | ## Copyright (c) 2007 Andreas Kupries. |
| 5 | # |
| 6 | # This software is licensed as described in the file LICENSE, which |
| 7 | # you should have received as part of this distribution. |
| 8 | # |
| 9 | # This software consists of voluntary contributions made by many |
| 10 | # individuals. For exact contribution history, see the revision |
| 11 | # history and logs, available at http://fossil-scm.hwaci.com/fossil |
| 12 | # # ## ### ##### ######## ############# ##################### |
| 13 | |
| 14 | ## Helper class for the pass 6 cycle breaker. Each instance refers to |
| 15 | ## three changesets A, B, and C, with A a predecessor of B, and B |
| 16 | ## predecessor of C, and the whole part of a dependency cycle. |
| 17 | |
| 18 | ## Instances analyse the file level dependencies which gave rise to |
| 19 | ## the changeset dependencies of A, B, and C, with the results used ####### ############# ##################### |
| 20 | ## Copyright (c) 2007 Andreas Kupries. |
| 21 | # |
| 22 | # This software is licensed as described in the file LICENSE, which |
| 23 | # you should have received as part of this distribution. |
| 24 | # |
| 25 | # This software consists of voluntary contributions made by many |
| 26 | # individuals. For exact contribution history, see the revision |
| 27 | # history and logs, available at http://fossil-scm.hwaci.com/fossil |
| 28 | # # ## ### ##### ######## ############# ##################### |
| 29 | |
| 30 | ## Helper class for the pass 6 cycle breaker. Each instance refers to |
| 31 | ## three changesets A, B, and C, with A a predecessor of B, and B |
| 32 | ## predecessort best fully break the cycle. |
| 33 | |
| 34 | # # ## ### ##### ######## ############# ##################### |
| 35 | ## Requirements |
| 36 | |
| 37 | package require Tcl 8.4 ; # Required runtime. |
| 38 | package require snit ; # OO system. |
| 39 | package require vc::tools::misc ; # Text formatting |
| 40 | package require vc::tools::trouble ; # Error reporting. |
| 41 | package require vc::tools::log ; # User feedback. |
| 42 | package require vc::fossil::import::cvs::state ; # State storage. |
| 43 | package require vc::fossil::import::cvs::integrity ; # State integrity checks. |
| 44 | package require vc::fossil::import::cvs::project::rev ; # Project level changesets |
| 45 | |
| 46 | # # ## ### ##### ######## ############# ##################### |
| 47 | ## |
| 48 | |
| 49 | snit::type ::vc::fossil::import::cvs::project::revlink { |
| 50 | # # ## ### ##### ######## ############# |
| 51 | ## Public API |
| 52 | |
| 53 | constructor {prev cset next} { |
| 54 | set myprev $prev |
| 55 | set mycset $cset |
| 56 | set mynext $next |
| 57 | |
| 58 | # We perform the bulk of the analysis during construction. The |
| 59 | # file revisions held by the changeset CSET can be sorted into |
| 60 | # four categories. |
| 61 | |
| 62 | # 1. Revisions whose predecessors are not in PREV, nor are |
| 63 | # their successors found in NEXT. These revisions do not |
| 64 | # count, as they did not induce any of the two dependencies |
| 65 | # under consideration. Thf {!(prev) $mycount(next |
| 66 | trouble internal "than {other} {" |
| 67 | |
| 68 | set sbreak [$self breakable] |
| 69 | set obreak [$other breakable] |
| 70 | |
| 71 | if {$sbreak && !$obreak} { return 1 } ; # self is better. |
| 72 | if {!$sbreak && $obreak} { return 0 } ; # self is worse. |
| 73 | |
| 74 | # Equality. Look at the counters. |
| 75 | # - Whichever has the lesser number of passthrough revisions |
| 76 | # is better, as more can be split off, weakening the cycle |
| 77 | # more. |
| 78 | # - Whichever has less links to move is better. |
| 79 | |
| 80 | set opass [$other passcount] |
| 81 | if {$mycount(pass) < $opass} { return 1 } ; # self is better. |
| 82 | if {$mycount(pass) > $opass} { return 0 } ; # self is worse. |
| 83 | |
| 84 | set smove [$self linkstomove] |
| 85 | set omove [$other linkstomove] |
| 86 | |
| 87 | if {$smove < $omove} { return 1 } ; # self is better. |
| 88 | |
| 89 | return 0 ; # Self is worse or equal, i.e. not better. |
| 90 | } |
| 91 | |
| 92 | method bre<[$mycset id]>split in the mycategory(prev|nehole part of a dependency cycle. |
| 93 | |
| 94 | ## Instances analyse the file level dependencies which gave rise to |
| 95 | ## the changeset dependencies of A, B, and C, with the results used ####### ############# ##################### |
| 96 | ## Copyright (c) 2007 Andreas Kupries. |
| 97 | # |
| 98 | # This software is licensed as described in the file LICENSE, which |
| 99 | # you should have received as part of this distribution. |
| 100 | # |
| 101 | # This software consists of voluntary contributions made by many |
| 102 | # individuals. For exact contribution history, see the revision |
| 103 | # history and logs, available at http://fossil-scm.hwaci.com/fossil |
| 104 | # # ## ### ##### ######## ############# ##################### |
| 105 | |
| 106 | ## Helper class for the pass 6 cycle breaker. Each instance refers to |
| 107 | ## three changesets A, B, and C, with A a predecessor of B, and B |
| 108 | ## predecessort best fully break the cycle. |
| 109 | |
| 110 | # # ## ### ##### ######## ############# ##################### |
| 111 | ## Requirements |
| 112 | |
| 113 | package require Tcl 8.4 ; # Required runtime. |
| 114 | package require snit ; # OO system. |
| 115 | package require vc::tools::misc ; # Text formatting |
| 116 | package require vc::tools::trouble ; # Error reporting. |
| 117 | package require vc::tools::log ; # User feedback. |
| 118 | package require vc::fossil::import::cvs::state ; # State storage. |
| 119 | package require vc::fossil::import::cvs::integrity ; # State integrity checks. |
| 120 | package require vc::fossil::import::cvs::project::rev ; # Project level changesets |
| 121 | |
| 122 | # # ## ### ##### ######## ############# ##################### |
| 123 | ## |
| 124 | |
| 125 | snit::type ::vc::fossil::import::cvs::project::revlink { |
| 126 | # # ## ### ##### ######## ############# |
| 127 | ## Public API |
| 128 | |
| 129 | constructor {prev cset next} { |
| 130 | set myprev $prev |
| 131 | set mycset $cset |
| 132 | set mynext $next |
| 133 | |
| 134 | # We perform the bulk of the analysis during construction. The |
| 135 | # file revisions held by the changeset CSET can be sorted into |
| 136 | # four categories. |
| 137 | |
| 138 | # 1. Revisions whose predecessors are not in PREV, nor are |
| 139 | # their successors found in NEXT. These revisions do not |
| 140 | # count, as they did not induce any of the two dependencies |
| 141 | # under consideration. Thf {!(prev) $mycount(next |
| 142 | trouble internal "than {other} {" |
| 143 | |
| 144 | set sbreak [$self breakable] |
| 145 | set obreak [$other breakable] |
| 146 | |
| 147 | if {$sbreak && !$obreak} { return 1 } ; # self is better. |
| 148 | if {!$sbreak && $obreak} { return 0 } ; # self is worse. |
| 149 | |
| 150 | # Equality. Look at the counters. |
| 151 | # - Whichever has the lesser number of passthrough revisions |
| 152 | # is better, as more can be split off, weakening the cycle |
| 153 | # more. |
| 154 | # - Whichever has less links to move is better. |
| 155 | |
| 156 | set opass [$other passcount] |
| 157 | if {$mycount(pass) < $opass} { return 1 } ; # self is better. |
| 158 | if {$mycount(pass) > $opass} { return 0 } ; # self is worse. |
| 159 | |
| 160 | set smove [$self linkstomove] |
| 161 | set omove [$other linkstomove] |
| 162 | |
| 163 | if {$smove < $omove} { return 1 } ; # self is better. |
| 164 | |
| 165 | return 0 ; # Self is worse or equal, i.e. not better. |
| 166 | } |
| 167 | |
| 168 | method bre<[$mycset id]>new changesets the |
| 169 | # old one is dropped from all databases, in and out of memory, |
| 170 | # and then destroyed. |
| 171 | |
| 172 | struct::list assign [$mycset data] project cstype cssrc |
| 173 | $mycset drop |
| 174 | $mycset destroy |
| 175 | |
| 176 | set newcsets {} |
| 177 | lappend newcsets [project::rev %AUTO% $project $cstype $cssrc $mycategory(prev)] |
| 178 | lappend newcsets [project::rev %AUext)] |
| 179 | |
| 180 | foreach c $newcsets { $c p |
+23
-2
| --- tools/cvs2fossil/lib/misc.tcl | ||
| +++ tools/cvs2fossil/lib/misc.tcl | ||
| @@ -37,20 +37,41 @@ | ||
| 37 | 37 | |
| 38 | 38 | proc nsp {n singular {plural {}}} { |
| 39 | 39 | return "$n [sp $n $singular $plural]" |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | - # Find maximum in a list. | |
| 42 | + # Find maximum/minimum in a list. | |
| 43 | 43 | |
| 44 | 44 | proc max {list} { |
| 45 | 45 | set max -1 |
| 46 | 46 | foreach e $list { |
| 47 | 47 | if {$e < $max} continue |
| 48 | 48 | set max $e |
| 49 | 49 | } |
| 50 | 50 | return $max |
| 51 | 51 | } |
| 52 | + | |
| 53 | + proc min {list} { | |
| 54 | + set min {} | |
| 55 | + foreach e $list { | |
| 56 | + if {$min == {}} { | |
| 57 | + set min $e | |
| 58 | + } elseif {$e > $min} continue | |
| 59 | + set min $e | |
| 60 | + } | |
| 61 | + return $min | |
| 62 | + } | |
| 63 | + | |
| 64 | + proc max2 {a b} { | |
| 65 | + if {$a > $b} { return $a } | |
| 66 | + return $b | |
| 67 | + } | |
| 68 | + | |
| 69 | + proc min2 {a b} { | |
| 70 | + if {$a < $b} { return $a } | |
| 71 | + return $b | |
| 72 | + } | |
| 52 | 73 | |
| 53 | 74 | proc ldelete {lv item} { |
| 54 | 75 | upvar 1 $lv list |
| 55 | 76 | set pos [lsearch -exact $list $item] |
| 56 | 77 | if {$pos < 0} return |
| @@ -67,13 +88,13 @@ | ||
| 67 | 88 | |
| 68 | 89 | # # ## ### ##### ######## ############# |
| 69 | 90 | } |
| 70 | 91 | |
| 71 | 92 | namespace eval ::vc::tools::misc { |
| 72 | - namespace export sp nsp max ldelete striptrailingslash | |
| 93 | + namespace export sp nsp max min max2 min2 ldelete striptrailingslash | |
| 73 | 94 | } |
| 74 | 95 | |
| 75 | 96 | # ----------------------------------------------------------------------------- |
| 76 | 97 | # Ready |
| 77 | 98 | |
| 78 | 99 | package provide vc::tools::misc 1.0 |
| 79 | 100 | return |
| 80 | 101 |
| --- tools/cvs2fossil/lib/misc.tcl | |
| +++ tools/cvs2fossil/lib/misc.tcl | |
| @@ -37,20 +37,41 @@ | |
| 37 | |
| 38 | proc nsp {n singular {plural {}}} { |
| 39 | return "$n [sp $n $singular $plural]" |
| 40 | } |
| 41 | |
| 42 | # Find maximum in a list. |
| 43 | |
| 44 | proc max {list} { |
| 45 | set max -1 |
| 46 | foreach e $list { |
| 47 | if {$e < $max} continue |
| 48 | set max $e |
| 49 | } |
| 50 | return $max |
| 51 | } |
| 52 | |
| 53 | proc ldelete {lv item} { |
| 54 | upvar 1 $lv list |
| 55 | set pos [lsearch -exact $list $item] |
| 56 | if {$pos < 0} return |
| @@ -67,13 +88,13 @@ | |
| 67 | |
| 68 | # # ## ### ##### ######## ############# |
| 69 | } |
| 70 | |
| 71 | namespace eval ::vc::tools::misc { |
| 72 | namespace export sp nsp max ldelete striptrailingslash |
| 73 | } |
| 74 | |
| 75 | # ----------------------------------------------------------------------------- |
| 76 | # Ready |
| 77 | |
| 78 | package provide vc::tools::misc 1.0 |
| 79 | return |
| 80 |
| --- tools/cvs2fossil/lib/misc.tcl | |
| +++ tools/cvs2fossil/lib/misc.tcl | |
| @@ -37,20 +37,41 @@ | |
| 37 | |
| 38 | proc nsp {n singular {plural {}}} { |
| 39 | return "$n [sp $n $singular $plural]" |
| 40 | } |
| 41 | |
| 42 | # Find maximum/minimum in a list. |
| 43 | |
| 44 | proc max {list} { |
| 45 | set max -1 |
| 46 | foreach e $list { |
| 47 | if {$e < $max} continue |
| 48 | set max $e |
| 49 | } |
| 50 | return $max |
| 51 | } |
| 52 | |
| 53 | proc min {list} { |
| 54 | set min {} |
| 55 | foreach e $list { |
| 56 | if {$min == {}} { |
| 57 | set min $e |
| 58 | } elseif {$e > $min} continue |
| 59 | set min $e |
| 60 | } |
| 61 | return $min |
| 62 | } |
| 63 | |
| 64 | proc max2 {a b} { |
| 65 | if {$a > $b} { return $a } |
| 66 | return $b |
| 67 | } |
| 68 | |
| 69 | proc min2 {a b} { |
| 70 | if {$a < $b} { return $a } |
| 71 | return $b |
| 72 | } |
| 73 | |
| 74 | proc ldelete {lv item} { |
| 75 | upvar 1 $lv list |
| 76 | set pos [lsearch -exact $list $item] |
| 77 | if {$pos < 0} return |
| @@ -67,13 +88,13 @@ | |
| 88 | |
| 89 | # # ## ### ##### ######## ############# |
| 90 | } |
| 91 | |
| 92 | namespace eval ::vc::tools::misc { |
| 93 | namespace export sp nsp max min max2 min2 ldelete striptrailingslash |
| 94 | } |
| 95 | |
| 96 | # ----------------------------------------------------------------------------- |
| 97 | # Ready |
| 98 | |
| 99 | package provide vc::tools::misc 1.0 |
| 100 | return |
| 101 |
| --- tools/cvs2fossil/lib/pkgIndex.tcl | ||
| +++ tools/cvs2fossil/lib/pkgIndex.tcl | ||
| @@ -19,10 +19,11 @@ | ||
| 19 | 19 | package ifneeded vc::fossil::import::cvs::pass::initcsets 1.0 [list source [file join $dir c2f_pinitcsets.tcl]] |
| 20 | 20 | package ifneeded vc::fossil::import::cvs::pass::breakrcycle 1.0 [list source [file join $dir c2f_pbreakrcycle.tcl]] |
| 21 | 21 | package ifneeded vc::fossil::import::cvs::project 1.0 [list source [file join $dir c2f_project.tcl]] |
| 22 | 22 | package ifneeded vc::fossil::import::cvs::project::lodmgr 1.0 [list source [file join $dir c2f_plodmgr.tcl]] |
| 23 | 23 | package ifneeded vc::fossil::import::cvs::project::rev 1.0 [list source [file join $dir c2f_prev.tcl]] |
| 24 | +package ifneeded vc::fossil::import::cvs::project::revlink 1.0 [list source [file join $dir c2f_prevlink.tcl]] | |
| 24 | 25 | package ifneeded vc::fossil::import::cvs::project::sym 1.0 [list source [file join $dir c2f_psym.tcl]] |
| 25 | 26 | package ifneeded vc::fossil::import::cvs::project::trunk 1.0 [list source [file join $dir c2f_ptrunk.tcl]] |
| 26 | 27 | package ifneeded vc::fossil::import::cvs::repository 1.0 [list source [file join $dir c2f_repository.tcl]] |
| 27 | 28 | package ifneeded vc::fossil::import::cvs::state 1.0 [list source [file join $dir c2f_state.tcl]] |
| 28 | 29 | package ifneeded vc::rcs::parser 1.0 [list source [file join $dir rcsparser.tcl]] |
| 29 | 30 |
| --- tools/cvs2fossil/lib/pkgIndex.tcl | |
| +++ tools/cvs2fossil/lib/pkgIndex.tcl | |
| @@ -19,10 +19,11 @@ | |
| 19 | package ifneeded vc::fossil::import::cvs::pass::initcsets 1.0 [list source [file join $dir c2f_pinitcsets.tcl]] |
| 20 | package ifneeded vc::fossil::import::cvs::pass::breakrcycle 1.0 [list source [file join $dir c2f_pbreakrcycle.tcl]] |
| 21 | package ifneeded vc::fossil::import::cvs::project 1.0 [list source [file join $dir c2f_project.tcl]] |
| 22 | package ifneeded vc::fossil::import::cvs::project::lodmgr 1.0 [list source [file join $dir c2f_plodmgr.tcl]] |
| 23 | package ifneeded vc::fossil::import::cvs::project::rev 1.0 [list source [file join $dir c2f_prev.tcl]] |
| 24 | package ifneeded vc::fossil::import::cvs::project::sym 1.0 [list source [file join $dir c2f_psym.tcl]] |
| 25 | package ifneeded vc::fossil::import::cvs::project::trunk 1.0 [list source [file join $dir c2f_ptrunk.tcl]] |
| 26 | package ifneeded vc::fossil::import::cvs::repository 1.0 [list source [file join $dir c2f_repository.tcl]] |
| 27 | package ifneeded vc::fossil::import::cvs::state 1.0 [list source [file join $dir c2f_state.tcl]] |
| 28 | package ifneeded vc::rcs::parser 1.0 [list source [file join $dir rcsparser.tcl]] |
| 29 |
| --- tools/cvs2fossil/lib/pkgIndex.tcl | |
| +++ tools/cvs2fossil/lib/pkgIndex.tcl | |
| @@ -19,10 +19,11 @@ | |
| 19 | package ifneeded vc::fossil::import::cvs::pass::initcsets 1.0 [list source [file join $dir c2f_pinitcsets.tcl]] |
| 20 | package ifneeded vc::fossil::import::cvs::pass::breakrcycle 1.0 [list source [file join $dir c2f_pbreakrcycle.tcl]] |
| 21 | package ifneeded vc::fossil::import::cvs::project 1.0 [list source [file join $dir c2f_project.tcl]] |
| 22 | package ifneeded vc::fossil::import::cvs::project::lodmgr 1.0 [list source [file join $dir c2f_plodmgr.tcl]] |
| 23 | package ifneeded vc::fossil::import::cvs::project::rev 1.0 [list source [file join $dir c2f_prev.tcl]] |
| 24 | package ifneeded vc::fossil::import::cvs::project::revlink 1.0 [list source [file join $dir c2f_prevlink.tcl]] |
| 25 | package ifneeded vc::fossil::import::cvs::project::sym 1.0 [list source [file join $dir c2f_psym.tcl]] |
| 26 | package ifneeded vc::fossil::import::cvs::project::trunk 1.0 [list source [file join $dir c2f_ptrunk.tcl]] |
| 27 | package ifneeded vc::fossil::import::cvs::repository 1.0 [list source [file join $dir c2f_repository.tcl]] |
| 28 | package ifneeded vc::fossil::import::cvs::state 1.0 [list source [file join $dir c2f_state.tcl]] |
| 29 | package ifneeded vc::rcs::parser 1.0 [list source [file join $dir rcsparser.tcl]] |
| 30 |