| | @@ -3,15 +3,16 @@ |
| 3 | 3 | |
| 4 | 4 | # ----------------------------------------------------------------------------- |
| 5 | 5 | # Requirements |
| 6 | 6 | |
| 7 | 7 | package require Tcl 8.4 |
| 8 | | -package require fileutil ; # Tcllib (traverse directory hierarchy) |
| 9 | | -package require vc::rcs::parser ; # Handling the RCS archive files. |
| 10 | | -package require vc::tools::log ; # User feedback |
| 11 | | -package require vc::cvs::cmd ; # Access to cvs application. |
| 12 | | -package require vc::cvs::ws::files ; # Scan CVS repository for relevant files. |
| 8 | +package require fileutil ; # Tcllib (traverse directory hierarchy) |
| 9 | +package require vc::rcs::parser ; # Handling the RCS archive files. |
| 10 | +package require vc::tools::log ; # User feedback |
| 11 | +package require vc::cvs::cmd ; # Access to cvs application. |
| 12 | +package require vc::cvs::ws::files ; # Scan CVS repository for relevant files. |
| 13 | +package require vc::cvs::ws::timeline ; # Manage timeline of all changes. |
| 13 | 14 | package require struct::tree |
| 14 | 15 | |
| 15 | 16 | namespace eval ::vc::cvs::ws { |
| 16 | 17 | vc::tools::log::system cvs |
| 17 | 18 | namespace import ::vc::tools::log::write |
| | @@ -68,26 +69,18 @@ |
| 68 | 69 | return 1 |
| 69 | 70 | } |
| 70 | 71 | |
| 71 | 72 | proc ::vc::cvs::ws::begin {src} { |
| 72 | 73 | variable project |
| 73 | | - variable base |
| 74 | 74 | |
| 75 | 75 | set src [file normalize $src] |
| 76 | | - if {![check $src msg]} { |
| 77 | | - return -code error $msg |
| 78 | | - } |
| 79 | | - set base $src |
| 80 | | - write 0 cvs "Base: $base" |
| 81 | | - if {$project eq ""} { |
| 82 | | - write 0 cvs "Project: <ALL>" |
| 83 | | - } else { |
| 84 | | - write 0 cvs "Project: $project" |
| 85 | | - } |
| 76 | + if {![check $src msg]} { return -code error $msg } |
| 77 | + |
| 78 | + DefBase $src |
| 79 | + MakeTimeline [ScanArchives [files::find [RootPath]]] |
| 86 | 80 | |
| 87 | 81 | # OLD api calls ... TODO rework for more structure ... |
| 88 | | - scan ; # Gather revision data from the archives |
| 89 | 82 | csets ; # Group changes into sets |
| 90 | 83 | rtree ; # Build revision tree (trunk only right now). |
| 91 | 84 | |
| 92 | 85 | set w [workspace] ; # OLD api ... TODO inline |
| 93 | 86 | if {$project ne ""} { |
| | @@ -139,66 +132,102 @@ |
| 139 | 132 | wssetup $id ; # OLD api ... TODO inline |
| 140 | 133 | } |
| 141 | 134 | |
| 142 | 135 | # ----------------------------------------------------------------------------- |
| 143 | 136 | # Internals - Old API for now. |
| 137 | + |
| 138 | +proc ::vc::cvs::ws::DefBase {path} { |
| 139 | + variable project |
| 140 | + variable base |
| 141 | + |
| 142 | + set base $path |
| 143 | + |
| 144 | + write 0 cvs "Base: $base" |
| 145 | + if {$project eq ""} { |
| 146 | + write 0 cvs "Project: <ALL>" |
| 147 | + } else { |
| 148 | + write 0 cvs "Project: $project" |
| 149 | + } |
| 150 | + return |
| 151 | +} |
| 152 | + |
| 153 | +proc ::vc::cvs::ws::RootPath {} { |
| 154 | + variable project |
| 155 | + variable base |
| 156 | + |
| 157 | + if {$project eq ""} { |
| 158 | + return $base |
| 159 | + } else { |
| 160 | + return $base/$project |
| 161 | + } |
| 162 | +} |
| 144 | 163 | |
| 145 | 164 | # Scan repository, collect archives, parse them, and collect revision |
| 146 | 165 | # information (file, revision -> date, author, commit message) |
| 147 | 166 | |
| 148 | | -proc ::vc::cvs::ws::scan {} { |
| 149 | | - variable project |
| 150 | | - variable base |
| 151 | | - variable timeline |
| 152 | | - |
| 153 | | - set n 0 |
| 154 | | - set d $base ; if {$project ne ""} {append d /$project} |
| 155 | | - |
| 156 | | - set files [::vc::cvs::ws::files::find $d] |
| 157 | | - |
| 167 | +proc ::vc::cvs::ws::ScanArchives {files} { |
| 158 | 168 | write 0 cvs "Scanning archives ..." |
| 169 | + |
| 170 | + set d [RootPath] |
| 171 | + set r {} |
| 172 | + set n 0 |
| 159 | 173 | |
| 160 | 174 | ::foreach {rcs f} $files { |
| 161 | 175 | write 1 cvs "Archive $rcs" |
| 162 | | - |
| 163 | | - # Get the meta data we need (revisions, timeline, messages). |
| 164 | | - set meta [process $d/$rcs] |
| 165 | | - |
| 166 | | - array set p $meta |
| 167 | | - |
| 168 | | - ::foreach {rev ts} $p(date) {_ a} $p(author) {_ cm} $p(commit) {_ st} $p(state) { |
| 169 | | - set op [expr {($rev eq "1.1") ? "A" : "M"}] |
| 170 | | - if {$st eq "dead"} {set op "R"} |
| 171 | | - |
| 172 | | - # A dead-first revision is rev 1.1 with op R. For an |
| 173 | | - # example see the file memchan/DEPENDENCIES. Such a file |
| 174 | | - # seems to exist only! on its branch. The branches |
| 175 | | - # information is set on the revision (extend rcsparser!), |
| 176 | | - # symbols has a tag, refering to a branch, possibly magic. |
| 177 | | - |
| 178 | | - if {($rev eq "1.1") && ($op eq "R")} { |
| 179 | | - write 2 cvs {Dead root revision} |
| 180 | | - } |
| 181 | | - |
| 182 | | - lappend timeline($ts) [list $op $ts $a $rev $f $cm] |
| 183 | | - } |
| 184 | | - |
| 185 | | - #unset p(commit) |
| 186 | | - #parray p |
| 187 | | - |
| 188 | | - incr n |
| 189 | | - } |
| 190 | | - |
| 191 | | - write 0 cvs "Processed $n [expr {($n == 1) ? "file" : "files"}]" |
| 192 | | - return |
| 193 | | -} |
| 194 | | - |
| 195 | | -namespace eval ::vc::cvs::ws { |
| 196 | | - # Timeline: tstamp -> (op, tstamp, author, revision, file, commit message) |
| 197 | | - |
| 198 | | - variable timeline ; array set timeline {} |
| 199 | | -} |
| 176 | + # Get the meta data we need (revisions, timeline, messages). |
| 177 | + lappend r $f [process $d/$rcs] |
| 178 | + incr n |
| 179 | + } |
| 180 | + |
| 181 | + write 0 cvs "Processed [NSIPL $n file]" |
| 182 | + return $r |
| 183 | +} |
| 184 | + |
| 185 | +proc ::vc::cvs::ws::MakeTimeline {meta} { |
| 186 | + write 0 cvs "Generating coalesced timeline ..." |
| 187 | + |
| 188 | + set n 0 |
| 189 | + ::foreach {f meta} $meta { |
| 190 | + array set md $meta |
| 191 | + array set date $md(date) |
| 192 | + array set auth $md(author) |
| 193 | + array set cmsg $md(commit) |
| 194 | + array set stat $md(state) |
| 195 | + |
| 196 | + ::foreach rev [lsort -dict [array names date]] { |
| 197 | + set operation [Operation $rev $stat($rev)] |
| 198 | + NoteDeadRoots $f $rev $operation |
| 199 | + timeline::add $date($rev) $f $rev $operation $auth($rev) $cmsg($rev) |
| 200 | + incr n |
| 201 | + } |
| 202 | + #B Extend branch management |
| 203 | + } |
| 204 | + |
| 205 | + write 0 cvs "Generated [NSIPL $n entry entries]" |
| 206 | + return |
| 207 | +} |
| 208 | + |
| 209 | +proc ::vc::cvs::ws::NoteDeadRoots {f rev operation} { |
| 210 | + # A dead-first revision is rev 1.1 with op R. For an example see |
| 211 | + # the file memchan/DEPENDENCIES. Such a file seems to exist only! |
| 212 | + # on its branch. The branches information is set on the revision |
| 213 | + # (extend rcsparser!), symbols has a tag, refering to a branch, |
| 214 | + # possibly magic. |
| 215 | + |
| 216 | + if {($rev eq "1.1") && ($operation eq "R")} { |
| 217 | + write 2 cvs "Dead root revision: $f" |
| 218 | + } |
| 219 | + return |
| 220 | +} |
| 221 | + |
| 222 | +proc ::vc::cvs::ws::Operation {rev state} { |
| 223 | + if {$state eq "dead"} {return "R"} ; # Removed |
| 224 | + if {$rev eq "1.1"} {return "A"} ; # Added |
| 225 | + return "M" ; # Modified |
| 226 | +} |
| 227 | + |
| 228 | + |
| 200 | 229 | |
| 201 | 230 | # Group single changes into changesets |
| 202 | 231 | |
| 203 | 232 | proc ::vc::cvs::ws::csets {} { |
| 204 | 233 | variable timeline |
| | @@ -208,38 +237,25 @@ |
| 208 | 237 | |
| 209 | 238 | array unset csets * ; array set csets {} |
| 210 | 239 | array unset cmap * ; array set cmap {} |
| 211 | 240 | set ncs 0 |
| 212 | 241 | |
| 213 | | - write 0 cvs "Processing timeline" |
| 214 | | - |
| 215 | | - set n 0 |
| 216 | | - CSClear |
| 217 | | - ::foreach ts [lsort -dict [array names timeline]] { |
| 218 | | - |
| 219 | | - # op tstamp author revision file commit |
| 220 | | - # 0 1 2 3 4 5/end |
| 221 | | - # b c a |
| 222 | | - |
| 223 | | - set entries [lsort -index 2 [lsort -index 0 [lsort -index end $timeline($ts)]]] |
| 224 | | - #puts [join $entries \n] |
| 225 | | - |
| 226 | | - ::foreach entry $entries { |
| 227 | | - if {![CSNone] && [CSNew $entry]} { |
| 228 | | - CSSave |
| 229 | | - CSClear |
| 230 | | - #puts ==\n$reason |
| 231 | | - } |
| 232 | | - CSAdd $entry |
| 233 | | - incr n |
| 234 | | - } |
| 235 | | - } |
| 236 | | - |
| 237 | | - write 0 cvs "Processed $n [expr {($n == 1) ? "entry" : "entries"}]" |
| 238 | | - |
| 239 | | - set n [array size csets] |
| 240 | | - write 0 cvs "Found $n [expr {($n == 1) ? "changeset" : "changesets"}]" |
| 242 | + write 0 cvs "Generating changesets from timeline" |
| 243 | + |
| 244 | + CSClear |
| 245 | + timeline::foreach date file revision operation author cmsg { |
| 246 | + # API adaption |
| 247 | + set entry [list $operation $date $author $revision $file $cmsg] |
| 248 | + |
| 249 | + if {![CSNone] && [CSNew $entry]} { |
| 250 | + CSSave |
| 251 | + CSClear |
| 252 | + } |
| 253 | + CSAdd $entry |
| 254 | + } |
| 255 | + |
| 256 | + write 0 cvs "Found [NSIPL [array size csets] changeset]" |
| 241 | 257 | return |
| 242 | 258 | } |
| 243 | 259 | |
| 244 | 260 | |
| 245 | 261 | namespace eval ::vc::cvs::ws { |
| | @@ -507,10 +523,19 @@ |
| 507 | 523 | ::foreach {o r} $or break |
| 508 | 524 | puts "$b $o $f $r" |
| 509 | 525 | } |
| 510 | 526 | return |
| 511 | 527 | } |
| 528 | + |
| 529 | +proc ::vc::cvs::ws::NSIPL {n singular {plural {}}} { |
| 530 | + return "$n [SIPL $n $singular $plural]" |
| 531 | +} |
| 532 | +proc ::vc::cvs::ws::SIPL {n singular {plural {}}} { |
| 533 | + if {$n == 1} {return $singular} |
| 534 | + if {$plural eq ""} {set plural ${singular}s} |
| 535 | + return $plural |
| 536 | +} |
| 512 | 537 | |
| 513 | 538 | # ----------------------------------------------------------------------------- |
| 514 | 539 | |
| 515 | 540 | namespace eval ::vc::cvs::ws { |
| 516 | 541 | variable base {} ; # Toplevel repository directory |
| 517 | 542 | |