| | @@ -9,10 +9,11 @@ |
| 9 | 9 | package require vc::rcs::parser ; # Handling the RCS archive files. |
| 10 | 10 | package require vc::tools::log ; # User feedback |
| 11 | 11 | package require vc::cvs::cmd ; # Access to cvs application. |
| 12 | 12 | package require vc::cvs::ws::files ; # Scan CVS repository for relevant files. |
| 13 | 13 | package require vc::cvs::ws::timeline ; # Manage timeline of all changes. |
| 14 | +package require vc::cvs::ws::csets ; # Manage the changesets found in the timeline |
| 14 | 15 | package require struct::tree |
| 15 | 16 | |
| 16 | 17 | namespace eval ::vc::cvs::ws { |
| 17 | 18 | vc::tools::log::system cvs |
| 18 | 19 | namespace import ::vc::tools::log::write |
| | @@ -72,13 +73,13 @@ |
| 72 | 73 | proc ::vc::cvs::ws::begin {src} { |
| 73 | 74 | if {![check $src msg]} { return -code error $msg } |
| 74 | 75 | |
| 75 | 76 | DefBase $src |
| 76 | 77 | MakeTimeline [ScanArchives [files::find [RootPath]]] |
| 78 | + MakeChangesets |
| 77 | 79 | |
| 78 | 80 | # OLD api calls ... TODO rework for more structure ... |
| 79 | | - csets ; # Group changes into sets |
| 80 | 81 | rtree ; # Build revision tree (trunk only right now). |
| 81 | 82 | |
| 82 | 83 | return [MakeWorkspace] |
| 83 | 84 | } |
| 84 | 85 | |
| | @@ -92,11 +93,10 @@ |
| 92 | 93 | # OLD api ... TODO inline |
| 93 | 94 | uplevel 1 [list ::vc::cvs::ws::foreach_cset $cv 0 $script] |
| 94 | 95 | } |
| 95 | 96 | |
| 96 | 97 | proc ::vc::cvs::ws::ncsets {args} { |
| 97 | | - variable ncs |
| 98 | 98 | variable ntrunk |
| 99 | 99 | |
| 100 | 100 | if {[llength $args] > 1} { |
| 101 | 101 | return -code error "wrong#args: Expected ?-import?" |
| 102 | 102 | } elseif {[llength $args] == 1} { |
| | @@ -105,11 +105,11 @@ |
| 105 | 105 | } else { |
| 106 | 106 | return $ntrunk |
| 107 | 107 | } |
| 108 | 108 | } |
| 109 | 109 | |
| 110 | | - return $ncs |
| 110 | + return [csets::num] |
| 111 | 111 | } |
| 112 | 112 | |
| 113 | 113 | proc ::vc::cvs::ws::isadmin {path} { |
| 114 | 114 | # Check if path is a CVS admin file. |
| 115 | 115 | if {[string match CVS/* $path]} {return 1} |
| | @@ -116,16 +116,30 @@ |
| 116 | 116 | if {[string match */CVS/* $path]} {return 1} |
| 117 | 117 | return 0 |
| 118 | 118 | } |
| 119 | 119 | |
| 120 | 120 | proc ::vc::cvs::ws::checkout {id} { |
| 121 | | - variable workspace ; cd $workspace |
| 122 | | - wssetup $id ; # OLD api ... TODO inline |
| 121 | + variable workspace |
| 122 | + cd $workspace |
| 123 | + |
| 124 | + array set cs [csets::get $id] |
| 125 | + |
| 126 | + write 1 cvs "@ $cs(date)" |
| 127 | + ::foreach l [split [string trim $cs(cmsg)] \n] { |
| 128 | + write 1 cvs "| $l" |
| 129 | + } |
| 130 | + |
| 131 | + ::foreach {f r} $cs(removed) { write 2 cvs "R $f $r" ; Remove $f $r } |
| 132 | + ::foreach {f r} $cs(added) { write 2 cvs "A $f $r" ; Checkout $f $r } |
| 133 | + ::foreach {f r} $cs(changed) { write 2 cvs "M $f $r" ; Checkout $f $r } |
| 134 | + |
| 135 | + # Provide metadata about the changeset the backend may wish to have |
| 136 | + return [list $cs(author) $cs(date) $cs(cmsg)] |
| 123 | 137 | } |
| 124 | 138 | |
| 125 | 139 | # ----------------------------------------------------------------------------- |
| 126 | | -# Internals - Old API for now. |
| 140 | +# Internals |
| 127 | 141 | |
| 128 | 142 | proc ::vc::cvs::ws::DefBase {path} { |
| 129 | 143 | variable project |
| 130 | 144 | variable base |
| 131 | 145 | |
| | @@ -149,13 +163,10 @@ |
| 149 | 163 | } else { |
| 150 | 164 | return $base/$project |
| 151 | 165 | } |
| 152 | 166 | } |
| 153 | 167 | |
| 154 | | -# Scan repository, collect archives, parse them, and collect revision |
| 155 | | -# information (file, revision -> date, author, commit message) |
| 156 | | - |
| 157 | 168 | proc ::vc::cvs::ws::ScanArchives {files} { |
| 158 | 169 | write 0 cvs "Scanning archives ..." |
| 159 | 170 | |
| 160 | 171 | set d [RootPath] |
| 161 | 172 | set r {} |
| | @@ -196,11 +207,11 @@ |
| 196 | 207 | unset auth |
| 197 | 208 | unset cmsg |
| 198 | 209 | unset stat |
| 199 | 210 | } |
| 200 | 211 | |
| 201 | | - write 0 cvs "Generated [NSIPL $n entry entries]" |
| 212 | + write 0 cvs "Timeline has [NSIPL $n entry entries]" |
| 202 | 213 | return |
| 203 | 214 | } |
| 204 | 215 | |
| 205 | 216 | proc ::vc::cvs::ws::NoteDeadRoots {f rev operation} { |
| 206 | 217 | # A dead-first revision is rev 1.1 with op R. For an example see |
| | @@ -218,10 +229,23 @@ |
| 218 | 229 | proc ::vc::cvs::ws::Operation {rev state} { |
| 219 | 230 | if {$state eq "dead"} {return "R"} ; # Removed |
| 220 | 231 | if {$rev eq "1.1"} {return "A"} ; # Added |
| 221 | 232 | return "M" ; # Modified |
| 222 | 233 | } |
| 234 | + |
| 235 | +proc ::vc::cvs::ws::MakeChangesets {} { |
| 236 | + write 0 cvs "Generating changesets from timeline" |
| 237 | + |
| 238 | + csets::init |
| 239 | + timeline::foreach date file revision operation author cmsg { |
| 240 | + csets::add $date $file $revision $operation $author $cmsg |
| 241 | + } |
| 242 | + csets::done |
| 243 | + |
| 244 | + write 0 cvs "Found [NSIPL [csets::num] changeset]" |
| 245 | + return |
| 246 | +} |
| 223 | 247 | |
| 224 | 248 | proc ::vc::cvs::ws::MakeWorkspace {} { |
| 225 | 249 | variable project |
| 226 | 250 | variable workspace [fileutil::tempfile importF_cvs_ws_] |
| 227 | 251 | |
| | @@ -233,57 +257,15 @@ |
| 233 | 257 | |
| 234 | 258 | write 0 cvs "Workspace: $workspace" |
| 235 | 259 | return $w |
| 236 | 260 | } |
| 237 | 261 | |
| 238 | | - |
| 239 | | -# Group single changes into changesets |
| 240 | | - |
| 241 | | -proc ::vc::cvs::ws::csets {} { |
| 242 | | - variable timeline |
| 243 | | - variable csets |
| 244 | | - variable ncs |
| 245 | | - variable cmap |
| 246 | | - |
| 247 | | - array unset csets * ; array set csets {} |
| 248 | | - array unset cmap * ; array set cmap {} |
| 249 | | - set ncs 0 |
| 250 | | - |
| 251 | | - write 0 cvs "Generating changesets from timeline" |
| 252 | | - |
| 253 | | - CSClear |
| 254 | | - timeline::foreach date file revision operation author cmsg { |
| 255 | | - # API adaption |
| 256 | | - set entry [list $operation $date $author $revision $file $cmsg] |
| 257 | | - |
| 258 | | - if {![CSNone] && [CSNew $entry]} { |
| 259 | | - CSSave |
| 260 | | - CSClear |
| 261 | | - } |
| 262 | | - CSAdd $entry |
| 263 | | - } |
| 264 | | - |
| 265 | | - write 0 cvs "Found [NSIPL [array size csets] changeset]" |
| 266 | | - return |
| 267 | | -} |
| 268 | | - |
| 269 | | - |
| 270 | | -namespace eval ::vc::cvs::ws { |
| 271 | | - # Changeset data: |
| 272 | | - # ncs: Counter-based id generation |
| 273 | | - # csets: id -> (user commit start end depth (file -> (op rev))) |
| 274 | | - |
| 275 | | - variable ncs ; set ncs 0 ; # Counter for changesets |
| 276 | | - variable csets ; array set csets {} ; # Changeset data |
| 277 | | -} |
| 278 | | - |
| 279 | 262 | # Building the revision tree from the changesets. |
| 280 | 263 | # Limitation: Currently only trunk csets is handled. |
| 281 | 264 | # Limitation: Dead files are not removed, i.e. no 'R' actions right now. |
| 282 | 265 | |
| 283 | 266 | proc ::vc::cvs::ws::rtree {} { |
| 284 | | - variable csets |
| 285 | 267 | variable rtree {} |
| 286 | 268 | variable ntrunk 0 |
| 287 | 269 | |
| 288 | 270 | write 0 cvs "Extracting the trunk" |
| 289 | 271 | |
| | @@ -294,15 +276,14 @@ |
| 294 | 276 | set b 0 ; # No branch csets found yet. |
| 295 | 277 | |
| 296 | 278 | # Extracting the trunk is easy, simply by looking at the involved |
| 297 | 279 | # version numbers. |
| 298 | 280 | |
| 299 | | - ::foreach c [lrange [lsort -integer [array names csets]] 1 end] { |
| 300 | | - ::foreach {u cm s e rd f} $csets($c) break |
| 301 | | - |
| 281 | + for {set c 1} {$c < [csets::num]} {incr c} { |
| 282 | + array set cs [csets::get $c] |
| 302 | 283 | # Ignore branch changes, just count them for the statistics. |
| 303 | | - if {$rd != 2} { |
| 284 | + if {$cs(lastd) != 2} { |
| 304 | 285 | incr b |
| 305 | 286 | continue |
| 306 | 287 | } |
| 307 | 288 | |
| 308 | 289 | # Trunk revision, connect to, and update the head. |
| | @@ -322,78 +303,10 @@ |
| 322 | 303 | |
| 323 | 304 | variable rtree {} |
| 324 | 305 | variable ntrunk 0 |
| 325 | 306 | } |
| 326 | 307 | |
| 327 | | -proc ::vc::cvs::ws::wssetup {c} { |
| 328 | | - variable csets |
| 329 | | - variable base |
| 330 | | - variable project |
| 331 | | - |
| 332 | | - # pwd = workspace |
| 333 | | - |
| 334 | | - ::foreach {u cm s e rd fs} $csets($c) break |
| 335 | | - |
| 336 | | - write 1 cvs "@ $s" |
| 337 | | - |
| 338 | | - ::foreach l [split [string trim $cm] \n] { |
| 339 | | - write 1 cvs "| $l" |
| 340 | | - } |
| 341 | | - |
| 342 | | - ::foreach {f or} $fs { |
| 343 | | - ::foreach {op r} $or break |
| 344 | | - write 2 cvs "$op $f $r" |
| 345 | | - |
| 346 | | - if {$op eq "R"} { |
| 347 | | - # Remove file from workspace. Prune empty directories. |
| 348 | | - # |
| 349 | | - # NOTE: A dead-first file (rev 1.1 dead) will never have |
| 350 | | - # existed. |
| 351 | | - # |
| 352 | | - # NOTE: Logically empty directories still physically |
| 353 | | - # contain the CVS admin directory, hence the check for == |
| 354 | | - # 1, not == 0. There might also be hidden files, we count |
| 355 | | - # them as well. Always hidden are . and .. and they do not |
| 356 | | - # count as user file. |
| 357 | | - |
| 358 | | - file delete $f |
| 359 | | - set fd [file dirname $f] |
| 360 | | - if { |
| 361 | | - ([llength [glob -nocomplain -directory $fd *]] == 1) && |
| 362 | | - ([llength [glob -nocomplain -directory -type hidden $fd *]] == 2) |
| 363 | | - } { |
| 364 | | - file delete -force $fd |
| 365 | | - } |
| 366 | | - } else { |
| 367 | | - # Added or modified, put the requested version of the file |
| 368 | | - # into the workspace. |
| 369 | | - |
| 370 | | - if {$project ne ""} {set f $project/$f} |
| 371 | | - if {[catch { |
| 372 | | - dova -d $base co -r $r $f |
| 373 | | - } msg]} { |
| 374 | | - if {[string match {*invalid change text*} $msg]} { |
| 375 | | - # The archive of the file is corrupted and the |
| 376 | | - # chosen version not accessible due to that. We |
| 377 | | - # report the problem, but otherwise ignore it. As |
| 378 | | - # a consequence the destination repository will not |
| 379 | | - # contain the full history of the named file. By |
| 380 | | - # ignoring the problem we however get as much as |
| 381 | | - # is possible. |
| 382 | | - |
| 383 | | - write 0 cvs "EE Corrupted archive file. Inaccessible revision." |
| 384 | | - continue |
| 385 | | - } |
| 386 | | - return -code error $msg |
| 387 | | - } |
| 388 | | - } |
| 389 | | - } |
| 390 | | - |
| 391 | | - # Provide metadata about the changeset the backend may wish to have |
| 392 | | - return [list $u $s $cm] |
| 393 | | -} |
| 394 | | - |
| 395 | 308 | proc ::vc::cvs::ws::foreach_cset {cv node script} { |
| 396 | 309 | upvar 1 $cv c |
| 397 | 310 | variable rtree |
| 398 | 311 | |
| 399 | 312 | set c $node |
| | @@ -421,98 +334,58 @@ |
| 421 | 334 | set c [lindex [$rtree children $c] 0] |
| 422 | 335 | } |
| 423 | 336 | return |
| 424 | 337 | } |
| 425 | 338 | |
| 426 | | -# ----------------------------------------------------------------------------- |
| 427 | | -# Internal helper commands: Changeset inspection and construction. |
| 428 | | - |
| 429 | | -proc ::vc::cvs::ws::CSClear {} { |
| 430 | | - upvar 1 start start end end cm cm user user files files lastd lastd |
| 431 | | - |
| 432 | | - set start {} |
| 433 | | - set end {} |
| 434 | | - set cm {} |
| 435 | | - set user {} |
| 436 | | - set lastd {} |
| 437 | | - array unset files * |
| 438 | | - array set files {} |
| 439 | | - return |
| 440 | | -} |
| 441 | | - |
| 442 | | -proc ::vc::cvs::ws::CSNone {} { |
| 443 | | - upvar 1 start start |
| 444 | | - return [expr {$start eq ""}] |
| 445 | | -} |
| 446 | | - |
| 447 | | -proc ::vc::cvs::ws::CSNew {entry} { |
| 448 | | - upvar 1 start start end end cm cm user user files files lastd lastd reason reason |
| 449 | | - |
| 450 | | - #puts -nonewline stdout . ; flush stdout |
| 451 | | - |
| 452 | | - ::foreach {op ts a rev f ecm} $entry break |
| 453 | | - |
| 454 | | - # User change |
| 455 | | - if {$a ne $user} {set reason user ; return 1} |
| 456 | | - |
| 457 | | - # File already in current cset |
| 458 | | - if {[info exists files($f)]} {set reason file ; return 1} |
| 459 | | - |
| 460 | | - # Current cset trunk/branch different from entry. |
| 461 | | - set depth [llength [split $rev .]] |
| 462 | | - if {($lastd == 2) != ($depth == 2)} {set reason depth/$lastd/$depth/($rev)/$f ; return 1} |
| 463 | | - |
| 464 | | - # Commit message changed |
| 465 | | - if {$cm ne $ecm} {set reason cmsg\ <<$ecm>> ; return 1} |
| 466 | | - |
| 467 | | - # Everything is good, still the same cset |
| 468 | | - return 0 |
| 469 | | -} |
| 470 | | - |
| 471 | | -proc ::vc::cvs::ws::CSSave {} { |
| 472 | | - variable cmap |
| 473 | | - variable csets |
| 474 | | - variable ncs |
| 475 | | - upvar 1 start start end end cm cm user user files files lastd lastd |
| 476 | | - |
| 477 | | - set csets($ncs) [list $user $cm $start $end $lastd [array get files]] |
| 478 | | - |
| 479 | | - # Record which revisions of a file are in what csets |
| 480 | | - ::foreach {f or} [array get files] { |
| 481 | | - ::foreach {_ rev} $or break |
| 482 | | - set cmap([list $f $rev]) $ncs |
| 483 | | - } |
| 484 | | - |
| 485 | | - #CSDump $ncs |
| 486 | | - |
| 487 | | - incr ncs |
| 488 | | - return |
| 489 | | -} |
| 490 | | - |
| 491 | | -proc ::vc::cvs::ws::CSAdd {entry} { |
| 492 | | - upvar 1 start start end end cm cm user user files files lastd lastd |
| 493 | | - |
| 494 | | - ::foreach {op ts a rev f ecm} $entry break |
| 495 | | - |
| 496 | | - if {$start eq ""} {set start $ts} |
| 497 | | - set end $ts |
| 498 | | - set cm $ecm |
| 499 | | - set user $a |
| 500 | | - set files($f) [list $op $rev] |
| 501 | | - set lastd [llength [split $rev .]] |
| 502 | | - return |
| 503 | | -} |
| 504 | | - |
| 505 | | -proc ::vc::cvs::ws::CSDump {c} { |
| 506 | | - variable csets |
| 507 | | - ::foreach {u cm s e rd f} $csets($c) break |
| 508 | | - |
| 509 | | - puts "$u $s"; regsub -all {.} $u { } b |
| 510 | | - puts "$b $e" |
| 511 | | - ::foreach {f or} $f { |
| 512 | | - ::foreach {o r} $or break |
| 513 | | - puts "$b $o $f $r" |
| 339 | +proc ::vc::cvs::ws::Checkout {f r} { |
| 340 | + variable base |
| 341 | + variable project |
| 342 | + |
| 343 | + # Added or modified, put the requested version of the file into |
| 344 | + # the workspace. |
| 345 | + |
| 346 | + if {$project ne ""} {set f $project/$f} |
| 347 | + if {[catch { |
| 348 | + dova -d $base co -r $r $f |
| 349 | + } msg]} { |
| 350 | + if {[string match {*invalid change text*} $msg]} { |
| 351 | + |
| 352 | + # The archive of the file is corrupted and the chosen |
| 353 | + # version not accessible due to that. We report the |
| 354 | + # problem, but otherwise ignore it. As a consequence the |
| 355 | + # destination repository will not contain the full history |
| 356 | + # of the named file. By ignoring the problem we however |
| 357 | + # get as much as is possible. |
| 358 | + |
| 359 | + write 0 cvs "EE Corrupted archive file. Inaccessible revision." |
| 360 | + return |
| 361 | + } |
| 362 | + return -code error $msg |
| 363 | + } |
| 364 | + return |
| 365 | +} |
| 366 | + |
| 367 | +proc ::vc::cvs::ws::Remove {f r} { |
| 368 | + # Remove file from workspace. Prune empty directories. |
| 369 | + # NOTE: A dead-first file (rev 1.1 dead) will never have existed. |
| 370 | + |
| 371 | + file delete $f |
| 372 | + Prune [file dirname $f] |
| 373 | + return |
| 374 | +} |
| 375 | + |
| 376 | +proc ::vc::cvs::ws::Prune {path} { |
| 377 | + # NOTE: Logically empty directories still physically contain the |
| 378 | + # CVS admin directory, hence the check for == 1, not == 0. There |
| 379 | + # might also be hidden files, we count them as well. Always hidden |
| 380 | + # are . and .. and they do not count as user file. |
| 381 | + |
| 382 | + if { |
| 383 | + ([llength [glob -nocomplain -directory $path *]] == 1) && |
| 384 | + ([llength [glob -nocomplain -directory -type hidden $path *]] == 2) |
| 385 | + } { |
| 386 | + file delete -force $path |
| 514 | 387 | } |
| 515 | 388 | return |
| 516 | 389 | } |
| 517 | 390 | |
| 518 | 391 | proc ::vc::cvs::ws::NSIPL {n singular {plural {}}} { |
| 519 | 392 | |
| 520 | 393 | ADDED tools/lib/cvs_csets.tcl |