Overview
SHA1 Hash: | b42cff97e319c2087208f98065f6c809ed560da4 |
---|---|
Date: | 2007-11-30 03:57:19 |
User: | aku |
Comment: | Replaced the checks for self-referential changesets in the cycle breaker with a scheme in the changeset class doing checks when splitting a changeset, which is also called by the general changeset integrity code, after each pass. Extended log output at high verbosity levels. Thorough checking of the fragments a changeset is to be split into. |
Timelines: | ancestors | descendants | both | trunk |
Other Links: | files | ZIP archive | manifest |
Tags And Properties
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
Changes
[hide diffs]Modified tools/cvs2fossil/lib/c2f_cyclebreaker.tcl from [df041b5731] to [10b6b95ceb].
@@ -197,31 +197,10 @@ foreach succ [$cset successors] { # Changesets may have dependencies outside of the # chosen set. These are ignored if {![$dg node exists $succ]} continue $dg arc insert $cset $succ - - # Check for changesets referencing themselves. Such a - # loop shows that the changeset in question has - # internal dependencies. Something which is supposed - # to be not possible, as pass 5 (InitCsets) takes care - # to transform internal into external dependencies by - # breaking the relevant changesets apart. So having - # one indicates big trouble in pass 5. We report them - # and dump internal structures to make it easier to - # trace the links causing the problem. - if {$succ eq $cset} { - log write 2 cyclebreaker "LOOP changeset [$cset str] __________________" - array set nmap [$cset nextmap] - foreach r [lsort -dict [array names nmap]] { - foreach succrev $nmap($r) { - log write 2 cyclebreaker \ - "LOOP * <$r> --> <$succrev> --> cs [[project::rev ofitem $succrev] str]" - } - } - trouble fatal "Self-referencing changeset [$cset str]" - } } } if {$log} { log write 3 cyclebreaker "Has [nsp [llength [$dg arcs]] dependency dependencies]" @@ -232,13 +211,10 @@ if {$log} { Mark $dg -start } MarkWatch $dg PreHook $dg MarkWatch $dg - - # This kills the application if loops (see above) were found. - trouble abort? return $dg } # Instead of searching the whole graph for the degree-0 nodes in # each iteration we compute the list once to start, and then only @@ -440,21 +416,10 @@ foreach succ [$cset successors] { # The new changesets may have dependencies outside of # the chosen set. These are ignored if {![$dg node exists $succ]} continue $dg arc insert $cset $succ - if {$succ eq $cset} { - log write 2 cyclebreaker "LOOP changeset [$cset str] __________________" - array set nmap [$cset nextmap] - foreach r [lsort -dict [array names nmap]] { - foreach succrev $nmap($r) { - log write 2 cyclebreaker \ - "LOOP * <$r> --> <$succrev> --> cs [[project::rev ofitem $succrev] str]" - } - } - trouble internal "Self-referencing changeset [$cset str]" - } } } foreach cset $pre { foreach succ [$cset successors] { # Note that the arc may already exist in the graph. If
Modified tools/cvs2fossil/lib/c2f_integrity.tcl from [6d1e02083a] to [ea08d30250].
@@ -51,17 +51,18 @@ set n 0 AllButMeta return } - typemethod changesets {} { + typemethod changesets {csets} { log write 4 integrity {Check database consistency} set n 0 RevisionChangesets TagChangesets BranchChangesets + Selfreferentiality $csets return } # # ## ### ##### ######## ############# ## Internal methods @@ -732,10 +733,21 @@ GROUP BY V.cid) AS VV WHERE VV.cid = UU.cid AND UU.fcount < VV.rcount) AND T.tid = C.type } + return + } + + proc Selfreferentiality {csets} { + log write 4 integrity {Checking changesets for self-references} + + foreach cset $csets { + if {[$cset selfreferential]} { + trouble fatal "[$cset str] depends on itself" + } + } return } proc ___UnusedChangesetChecks___ {} { # This code performs a number of paranoid checks of the
Modified tools/cvs2fossil/lib/c2f_pbreakacycle.tcl from [da5cab23e3] to [2c3ba3a1ce].
@@ -80,11 +80,11 @@ LoadCommitOrder cyclebreaker run break-all [myproc Changesets] } repository printcsetstatistics - integrity changesets + integrity changesets [project::rev all] return } typemethod discard {} { # Pass manager interface. Executed for all passes after the
Modified tools/cvs2fossil/lib/c2f_pbreakrcycle.tcl from [4ea6d5327b] to [0b1951c1de].
@@ -69,11 +69,11 @@ state transaction { cyclebreaker run break-rev [myproc Changesets] } repository printcsetstatistics - integrity changesets + integrity changesets [project::rev all] return } typemethod discard {} { # Pass manager interface. Executed for all passes after the
Modified tools/cvs2fossil/lib/c2f_pbreakscycle.tcl from [aa112553ed] to [427766d2fb].
@@ -68,11 +68,11 @@ state transaction { cyclebreaker run break-sym [myproc Changesets] } repository printcsetstatistics - integrity changesets + integrity changesets [project::rev all] return } typemethod discard {} { # Pass manager interface. Executed for all passes after the
Modified tools/cvs2fossil/lib/c2f_pinitcsets.tcl from [b1c3a78eda] to [39a67db2c5].
@@ -142,11 +142,11 @@ CreateSymbolChangesets ; # Create csets for tags and branches. PersistTheChangesets } repository printcsetstatistics - integrity changesets + integrity changesets [project::rev all] return } typemethod discard {} { # Pass manager interface. Executed for all passes after the
Modified tools/cvs2fossil/lib/c2f_prev.tcl from [1c3ee2d518] to [6ad5e03750].
@@ -16,10 +16,11 @@ # # ## ### ##### ######## ############# ##################### ## Requirements package require Tcl 8.4 ; # Required runtime. package require snit ; # OO system. +package require struct::set ; # Set operations. package require vc::tools::misc ; # Text formatting package require vc::tools::trouble ; # Error reporting. package require vc::tools::log ; # User feedback. package require vc::fossil::import::cvs::state ; # State storage. package require vc::fossil::import::cvs::integrity ; # State integrity checks. @@ -36,11 +37,13 @@ set myid $theid } else { set myid [incr mycounter] } - integrity assert {[info exists mycstype($cstype)]} {Bad changeset type '$cstype'.} + integrity assert { + [info exists mycstype($cstype)] + } {Bad changeset type '$cstype'.} set myproject $project set mytype $cstype set mytypeobj ::vc::fossil::import::cvs::project::rev::${cstype} set mysrcid $srcid @@ -53,10 +56,11 @@ set myidmap($myid) $self foreach iid $items { set key [list $cstype $iid] set myitemmap($key) $self lappend mytitems $key + log write 8 csets {MAP+ item <$key> $self = [$self str]} } return } method str {} { @@ -243,10 +247,11 @@ # thus never more than one reference in the list. foreach iid $myitems { set key [list $mytype $iid] unset myitemmap($key) + log write 8 csets {MAP- item <$key> $self = [$self str]} } # Create changesets for the fragments, reusing the current one # for the first fragment. We sort them in order to allow # checking for gaps and nice messages. @@ -283,10 +288,11 @@ set myitems [lrange $myitems 0 $firste] foreach iid $myitems { set key [list $mytype $iid] set myitemmap($key) $self + log write 8 csets {MAP+ item <$key> $self = [$self str]} } return 1 } @@ -313,23 +319,56 @@ } method timerange {} { return [$mytypeobj timerange $myitems] } method drop {} { + log write 8 csets {Dropping $self = [$self str]} + state transaction { state run { DELETE FROM changeset WHERE cid = $myid; DELETE FROM csitem WHERE cid = $myid; } } foreach iid $myitems { set key [list $mytype $iid] unset myitemmap($key) + log write 8 csets {MAP- item <$key> $self = [$self str]} } set pos [lsearch -exact $mychangesets $self] set mychangesets [lreplace $mychangesets $pos $pos] return + } + + method selfreferential {} { + log write 9 csets {Checking [$self str] /[llength $myitems]} + + if {![struct::set contains [$self successors] $self]} { + return 0 + } + if {[log verbosity?] < 8} { return 1 } + + # Print the detailed successor structure of the self- + # referential changeset, if the verbosity of the log is dialed + # high enough. + + log write 8 csets [set hdr {Self-referential changeset [$self str] __________________}] + array set nmap [$self nextmap] + foreach item [lsort -dict [array names nmap]] { + foreach succitem $nmap($item) { + set succcs $myitemmap($succitem) + set hint [expr {($succcs eq $self) + ? "LOOP" + : " "}] + set i "<$item [$type itemstr $item]>" + set s "<$succitem [$type itemstr $succitem]>" + set scs [$succcs str] + log write 8 csets {$hint * $i --> $s --> cs $scs} + } + } + log write 8 csets [regsub -all {[^ ]} $hdr {_}] + return 1 } typemethod split {cset args} { # As part of the creation of the new changesets specified in # ARGS as sets of items, all subsets of CSET's item set, CSET @@ -338,25 +377,63 @@ # # Note: The item lists found in args are tagged items. They # have to have the same type as the changeset, being subsets # of its items. This is checked in Untag1. + # Constraints: No fragment must be empty. All fragments have + # to be subsets of the cset. The union has to cover the + # original. All pairwise intersections have to be empty. + + log write 8 csets {OLD: [lsort [$cset items]]} + + set cover {} + foreach fragmentitems $args { + log write 8 csets {NEW: [lsort $fragmentitems]} + + integrity assert { + ![struct::set empty $fragmentitems] + } {changeset fragment is empty} + integrity assert { + [struct::set subsetof $fragmentitems [$cset items]] + } {changeset fragment is not a subset} + struct::set add cover $fragmentitems + } + integrity assert { + [struct::set equal $cover [$cset items]] + } {The fragments do not cover the original changeset} + set i 1 + foreach fia $args { + foreach fib [lrange $args $i end] { + integrity assert { + [struct::set empty [struct::set intersect $fia $fib]] + } {The fragments <$fia> and <$fib> overlap} + } + incr i + } + + # All checks pass, actually perform the split. + struct::list assign [$cset data] project cstype cssrc $cset drop $cset destroy set newcsets {} foreach fragmentitems $args { - integrity assert { - [llength $fragmentitems] - } {Attempted to create an empty changeset, i.e. without items} - lappend newcsets [$type %AUTO% $project $cstype $cssrc \ - [Untag $fragmentitems $cstype]] - } - - foreach c $newcsets { $c persist } + log write 8 csets {MAKE: [lsort $fragmentitems]} + + set fragment [$type %AUTO% $project $cstype $cssrc \ + [Untag $fragmentitems $cstype]] + lappend newcsets $fragment + $fragment persist + + if {[$fragment selfreferential]} { + trouble fatal "[$fragment str] depends on itself" + } + } + + trouble abort? return $newcsets } typemethod strlist {changesets} { return [join [struct::list map $changesets [myproc ID]]] @@ -370,10 +447,15 @@ proc Untag1 {cstype theitem} { struct::list assign $theitem t i integrity assert {$cstype eq $t} {Item $i's type is '$t', expected '$cstype'} return $i + } + + typemethod itemstr {item} { + struct::list assign $item itype iid + return [$itype str $iid] } # # ## ### ##### ######## ############# ## State @@ -674,10 +756,21 @@ typemethod byrevision {} { return 1 } typemethod bysymbol {} { return 0 } typemethod istag {} { return 0 } typemethod isbranch {} { return 0 } + typemethod str {revision} { + struct::list assign [state run { + SELECT R.rev, F.name, P.name + FROM revision R, file F, project P + WHERE R.rid = $revision + AND F.fid = R.fid + AND P.pid = F.pid + }] revnr fname pname + return "$pname/${revnr}::$fname" + } + # result = list (mintime, maxtime) typemethod timerange {items} { set theset ('[join $items {','}]') return [state run " SELECT MIN(R.date), MAX(R.date) @@ -918,10 +1011,22 @@ typemethod byrevision {} { return 0 } typemethod bysymbol {} { return 1 } typemethod istag {} { return 1 } typemethod isbranch {} { return 0 } + typemethod str {tag} { + struct::list assign [state run { + SELECT S.name, F.name, P.name + FROM tag T, symbol S, file F, project P + WHERE T.tid = $tag + AND F.fid = T.fid + AND P.pid = F.pid + AND S.sid = T.sid + }] sname fname pname + return "$pname/T'${sname}'::$fname" + } + # result = list (mintime, maxtime) typemethod timerange {tags} { # The range is defined as the range of the revisions the tags # are attached to. @@ -985,10 +1090,22 @@ snit::type ::vc::fossil::import::cvs::project::rev::sym::branch { typemethod byrevision {} { return 0 } typemethod bysymbol {} { return 1 } typemethod istag {} { return 0 } typemethod isbranch {} { return 1 } + + typemethod str {branch} { + struct::list assign [state run { + SELECT S.name, F.name, P.name + FROM branch B, symbol S, file F, project P + WHERE B.bid = $branch + AND F.fid = B.fid + AND P.pid = F.pid + AND S.sid = B.sid + }] sname fname pname + return "$pname/B'${sname}'::$fname" + } # result = list (mintime, maxtime) typemethod timerange {branches} { # The range of a branch is defined as the range of the # revisions the branches are spawned by. NOTE however that the