Overview
SHA1 Hash: | d8c18fc148f545ddcb8769389177a892a18666e8 |
---|---|
Date: | 2007-09-17 00:56:40 |
User: | aku |
Comment: | Reworked the CVS handling code to have a simpler API, more like the reworked Fossil API. The API now has a form where adding the handling of branches should not require complex changes in the import controller any longer. Extended the system to allow the user to restrict the importing to a sub-directory of the chosen repository, via the new switch --project. This is required to pull a SF CVS repository apart into the various projects it may have. Example: Under Tcl we have 3 projects, namely Tcl itself, sampleextension, and Thread. |
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/import-cvs.tcl from [c75797ae19] to [8655038b63].
@@ -42,10 +42,11 @@ # Requirements package require Tcl 8.4 package require vc::tools::log ; # User Feedback package require vc::fossil::import::cvs ; # Importer Control +package require vc::cvs::ws ; # CVS frontend namespace eval ::import { namespace import ::vc::fossil::import::cvs::* } @@ -69,10 +70,11 @@ while {[string match "-*" [set opt [this]]]} { switch -exact -- $opt { --breakat { next ; import::configure -breakat [this] } --nosign { import::configure -nosign 1 } --saveto { next ; import::configure -saveto [file normalize [this]] } + --project { next ; import::configure -project [this] } -v { incr verbosity ; ::vc::tools::log::verbosity $verbosity } -h - default usage } next @@ -80,16 +82,12 @@ remainder if {[llength $argv] != 2} usage foreach {cvs fossil} $argv break - if { - ![file exists $cvs] || - ![file readable $cvs] || - ![file isdirectory $cvs] - } { - usage "CVS directory missing, not readable, or not a directory." + if {![::vc::cvs::ws::check $cvs msg]} { + usage $msg } elseif {[file exists $fossil]} { usage "Fossil destination repository exists already." } return @@ -124,10 +122,11 @@ global argv0 puts stderr "Usage: $argv0 ?-v? ?--nosign? ?--breakat id? ?--saveto path? cvs-repository fossil-repository" if {$text eq ""} { puts stderr " --nosign: Do not sign the imported changesets." puts stderr " --breakat: Stop just before committing the identified changeset." + puts stderr " --project: Path in the CVS repository to limit the import to." puts stderr " --saveto: Save commit command to the specified file." puts stderr " -v: Increase log verbosity. Can be used multiple times." } else { puts stderr " $text" }
Modified tools/lib/cvs.tcl from [a015f1382f] to [11e2bbce8e].
@@ -19,52 +19,169 @@ } # ----------------------------------------------------------------------------- # API -# Define repository directory. - -proc ::vc::cvs::ws::at {path} { - variable base [file normalize $path] - write 0 cvs "Base: $base" +# vc::cvs::ws::configure key value - Configure the subsystem. +# vc::cvs::ws::check src mv - Check if src is a CVS repository directory. +# vc::cvs::ws::begin src - Start new workspace and return the top- +# most directory co'd files are put into. +# vc::cvs::ws::ncsets ?-import? - Retrieve number of csets (all/to import) +# vc::cvs::ws::foreach csvar script - Run the script for each changeset, the +# id of the current changeset stored in +# the variable named by csvar. +# vc::cvs::ws::done - Close workspace and delete it. +# vc::cvs::ws::isadmin path - Check if path is an admin file of CVS +# vc::cvs::ws::checkout id - Have workspace contain the changeset id. +# +# Configuration keys: +# +# -project path - Sub directory under 'src' to limit the import to. + +# ----------------------------------------------------------------------------- +# API Implementation + +proc ::vc::cvs::ws::configure {key value} { + variable project + + switch -exact -- $key { + -project { set project $value } + default { + return -code error "Unknown switch $key, expected \ + -project" + } + } + return +} + +proc ::vc::cvs::ws::check {src mv} { + variable project + upvar 1 $mv msg + if { + ![fileutil::test $src erd msg "CVS Repository"] || + ![fileutil::test $src/CVSROOT erd msg "CVS Admin directory"] || + (($project ne "") && + ![fileutil::test $src/$project erd msg "Project directory"]) + } { + return 0 + } + return 1 +} + +proc ::vc::cvs::ws::begin {src} { + variable project + variable base + + set src [file normalize $src] + if {![check $src msg]} { + return -code error $msg + } + set base $src + write 0 cvs "Base: $base" + if {$project eq ""} { + write 0 cvs "Project: <ALL>" + } else { + write 0 cvs "Project: $project" + } + + # OLD api calls ... TODO rework for more structure ... + scan ; # Gather revision data from the archives + csets ; # Group changes into sets + rtree ; # Build revision tree (trunk only right now). + + set w [workspace] ; # OLD api ... TODO inline + if {$project ne ""} { + set w $w/$project + file mkdir $w + } + return $w +} + +proc ::vc::cvs::ws::done {} { + variable cwd + variable workspace + cd $cwd + file delete -force $workspace return } -namespace eval ::vc::cvs::ws { - # Toplevel repository directory - variable base {} -} +proc ::vc::cvs::ws::foreach {cv script} { + # OLD api ... TODO inline + uplevel 1 [list ::vc::cvs::ws::foreach_cset $cv 0 $script] +} + +proc ::vc::cvs::ws::ncsets {args} { + variable ncs + variable ntrunk + + if {[llength $args] > 1} { + return -code error "wrong#args: Expected ?-import?" + } elseif {[llength $args] == 1} { + if {[set k [lindex $args 0]] ne "-import"} { + return -code "Unknown switch $k, expected -import" + } else { + return $ntrunk + } + } + + return $ncs +} + +proc ::vc::cvs::ws::isadmin {path} { + # Check if path is a CVS admin file. + if {[string match CVS/* $path]} {return 1} + if {[string match */CVS/* $path]} {return 1} + return 0 +} + +proc ::vc::cvs::ws::checkout {id} { + variable workspace ; cd $workspace + wssetup $id ; # OLD api ... TODO inline +} + +# ----------------------------------------------------------------------------- +# Internals - Old API for now. # Scan repository, collect archives, parse them, and collect revision # information (file, revision -> date, author, commit message) proc ::vc::cvs::ws::scan {} { + variable project variable base variable npaths variable rpaths variable timeline write 0 cvs {Scanning directory hierarchy} set n 0 - foreach rcs [fileutil::findByPattern $base -glob *,v] { - set rcs [fileutil::stripPath $base $rcs] - # Now rcs is relative to base + set d $base ; if {$project ne ""} {append d /$project} + + ::foreach rcs [fileutil::findByPattern $d -glob *,v] { + set rcs [fileutil::stripPath $d $rcs] + # Now rcs is relative to base/project write 1 cvs "Archive $rcs" - if {[string match CVSROOT* $rcs]} { + if {[string match CVSROOT/* $rcs]} { write 2 cvs {Ignored. Administrative file} continue } # Derive the regular path from the rcs path. Meaning: Chop of # the ",v" suffix, and remove a possible "Attic". set f [string range $rcs 0 end-2] if {"Attic" eq [lindex [file split $rcs] end-1]} { - set f [file join [file dirname [file dirname $f]] [file tail $f]] - if {[file exists $base/$f,v]} { + + # The construction below ensures that Attic/X maps to X + # instead of ./X. Otherwise, Y/Attic/X maps to Y/X. + + set fx [file dirname [file dirname $f]] + set f [file tail $f] + if {$fx ne "."} { set f [file join $fx $f] } + + if {[file exists $d/$f,v]} { # We have a regular archive and an Attic archive # refering to the same user visible file. Ignore the # file in the Attic. write 2 cvs "Ignored. Attic superceded by regular archive" @@ -84,18 +201,18 @@ continue } } # Get the meta data we need (revisions, timeline, messages). - set meta [process $base/$rcs] + set meta [process $d/$rcs] set npaths($rcs) $f set rpaths($f) $rcs array set p $meta - foreach {rev ts} $p(date) {_ a} $p(author) {_ cm} $p(commit) {_ st} $p(state) { + ::foreach {rev ts} $p(date) {_ a} $p(author) {_ cm} $p(commit) {_ st} $p(state) { set op [expr {($rev eq "1.1") ? "A" : "M"}] if {$st eq "dead"} {set op "R"} # A dead-first revision is rev 1.1 with op R. For an # example see the file memchan/DEPENDENCIES. Such a file @@ -146,20 +263,20 @@ write 0 cvs "Processing timeline" set n 0 CSClear - foreach ts [lsort -dict [array names timeline]] { + ::foreach ts [lsort -dict [array names timeline]] { # op tstamp author revision file commit # 0 1 2 3 4 5/end # b c a set entries [lsort -index 2 [lsort -index 0 [lsort -index end $timeline($ts)]]] #puts [join $entries \n] - foreach entry $entries { + ::foreach entry $entries { if {![CSNone] && [CSNew $entry]} { CSSave CSClear #puts ==\n$reason } @@ -203,12 +320,12 @@ set b 0 ; # No branch csets found yet. # Extracting the trunk is easy, simply by looking at the involved # version numbers. - foreach c [lrange [lsort -integer [array names csets]] 1 end] { - foreach {u cm s e rd f} $csets($c) break + ::foreach c [lrange [lsort -integer [array names csets]] 1 end] { + ::foreach {u cm s e rd f} $csets($c) break # Ignore branch changes, just count them for the statistics. if {$rd != 2} { incr b continue @@ -243,40 +360,27 @@ cd $workspace ; # Checkouts go here. return $workspace } -proc ::vc::cvs::ws::wsignore {path} { - # Ignore CVS admin files. - if {[string match */CVS/* $path]} {return 1} - return 0 -} - -proc ::vc::cvs::ws::wsclear {} { - variable cwd - variable workspace - cd $cwd - file delete -force $workspace - return -} - proc ::vc::cvs::ws::wssetup {c} { variable csets variable base + variable project # pwd = workspace - foreach {u cm s e rd fs} $csets($c) break + ::foreach {u cm s e rd fs} $csets($c) break write 1 cvs "@ $s" - foreach l [split [string trim $cm] \n] { + ::foreach l [split [string trim $cm] \n] { write 1 cvs "| $l" } - foreach {f or} $fs { - foreach {op r} $or break + ::foreach {f or} $fs { + ::foreach {op r} $or break write 2 cvs "$op $f $r" if {$op eq "R"} { # Remove file from workspace. Prune empty directories. # @@ -299,18 +403,19 @@ } } else { # Added or modified, put the requested version of the file # into the workspace. + if {$project ne ""} {set f $project/$f} if {[catch { dova -d $base co -r $r $f } msg]} { if {[string match {*invalid change text*} $msg]} { # The archive of the file is corrupted and the # chosen version not accessible due to that. We # report the problem, but otherwise ignore it. As - # a consequence the fossil repository will not + # a consequence the destination repository will not # contain the full history of the named file. By # ignoring the problem we however get as much as # is possible. write 0 cvs "EE Corrupted archive file. Inaccessible revision." @@ -362,24 +467,10 @@ set c [lindex [$rtree children $c] 0] } return } -proc ::vc::cvs::ws::root {} { - return 0 -} - -proc ::vc::cvs::ws::ntrunk {} { - variable ntrunk - return $ntrunk -} - -proc ::vc::cvs::ws::ncsets {} { - variable ncs - return $ncs -} - # ----------------------------------------------------------------------------- # Internal helper commands: Changeset inspection and construction. proc ::vc::cvs::ws::CSClear {} { upvar 1 start start end end cm cm user user files files lastd lastd @@ -402,11 +493,11 @@ proc ::vc::cvs::ws::CSNew {entry} { upvar 1 start start end end cm cm user user files files lastd lastd reason reason #puts -nonewline stdout . ; flush stdout - foreach {op ts a rev f ecm} $entry break + ::foreach {op ts a rev f ecm} $entry break # User change if {$a ne $user} {set reason user ; return 1} # File already in current cset @@ -430,12 +521,12 @@ upvar 1 start start end end cm cm user user files files lastd lastd set csets($ncs) [list $user $cm $start $end $lastd [array get files]] # Record which revisions of a file are in what csets - foreach {f or} [array get files] { - foreach {_ rev} $or break + ::foreach {f or} [array get files] { + ::foreach {_ rev} $or break set cmap([list $f $rev]) $ncs } #CSDump $ncs @@ -444,11 +535,11 @@ } proc ::vc::cvs::ws::CSAdd {entry} { upvar 1 start start end end cm cm user user files files lastd lastd - foreach {op ts a rev f ecm} $entry break + ::foreach {op ts a rev f ecm} $entry break if {$start eq ""} {set start $ts} set end $ts set cm $ecm set user $a @@ -457,26 +548,30 @@ return } proc ::vc::cvs::ws::CSDump {c} { variable csets - foreach {u cm s e rd f} $csets($c) break + ::foreach {u cm s e rd f} $csets($c) break puts "$u $s"; regsub -all {.} $u { } b puts "$b $e" - foreach {f or} $f { - foreach {o r} $or break + ::foreach {f or} $f { + ::foreach {o r} $or break puts "$b $o $f $r" } return } +# ----------------------------------------------------------------------------- + namespace eval ::vc::cvs::ws { - namespace export at scan csets rtree workspace wsignore wsclear wssetup \ - foreach_cset root ntrunk ncsets + variable base {} ; # Toplevel repository directory + variable project {} ; # Sub directory to limit the import to. + + namespace export configure begin done foreach ncsets checkout } # ----------------------------------------------------------------------------- # Ready package provide vc::cvs::ws 1.0 return
Modified tools/lib/fossil.tcl from [a0b0c0df17] to [a6c49a2ab2].
@@ -62,13 +62,15 @@ } return } proc ::vc::fossil::ws::begin {origin} { - variable rp [file normalize [fileutil::tempfile import2_fsl_rp_]] + variable base [file normalize $origin] + variable rp [file normalize [fileutil::tempfile import2_fsl_rp_]] cd $origin + dova new $rp ; # create and ... dova open $rp ; # ... connect write 0 fossil "Repository: $rp" return @@ -81,10 +83,13 @@ return } proc ::vc::fossil::ws::commit {cset user timestamp message} { variable lastuuid + variable base + + cd $base # Commit the current state of the workspace. Scan for new and # removed files and issue the appropriate fossil add/rm commands # before actually comitting. @@ -200,10 +205,11 @@ variable breakat {} ; # Do not stop variable saveto {} ; # Do not save commit message variable appname {} ; # Name of importer application using the package. variable ignore {} ; # No files to ignore. + variable base {} ; # Workspace directory variable rp {} ; # Repository the package works on. variable lastuuid {} ; # Uuid of last imported changeset. namespace export configure begin done commit }
Modified tools/lib/importcvs.tcl from [4409d3ed45] to [8476a3bcc6].
@@ -18,23 +18,24 @@ namespace eval fossil { namespace import ::vc::fossil::ws::* } namespace eval stats { namespace import ::vc::fossil::import::stats::* } namespace eval map { namespace import ::vc::fossil::import::map::* } fossil::configure -appname cvs2fossil - fossil::configure -ignore ::vc::cvs::ws::wsignore + fossil::configure -ignore ::vc::cvs::ws::isadmin } # ----------------------------------------------------------------------------- # API # Configuration # # vc::fossil::import::cvs::configure key value - Set configuration # -# Legal keys: -nosign <bool>, default false -# -breakat <int>, default :none: -# -saveto <path>, default :none: +# Legal keys: -nosign <bool>, default false +# -breakat <int>, default :none: +# -saveto <path>, default :none: +# -limit <path>, default :none: # # Functionality # # vc::fossil::import::cvs::run src dst - Perform an import. @@ -45,10 +46,11 @@ # The options are simply passed through to the fossil importer # backend. switch -exact -- $key { -breakat { fossil::configure -breakat $value } -nosign { fossil::configure -nosign $value } + -project { cvs::configure -project $value } -saveto { fossil::configure -saveto $value } default { return -code error "Unknown switch $key, expected one of \ -breakat, -nosign, or -saveto" } @@ -58,30 +60,26 @@ # Import the CVS repository found at directory 'src' into the new # fossil repository at 'dst'. proc ::vc::fossil::import::cvs::run {src dst} { - cvs::at $src ; # Define location of CVS repository - cvs::scan ; # Gather revision data from the archives - cvs::csets ; # Group changes into sets - cvs::rtree ; # Build revision tree (trunk only right now). - - write 0 import {Begin conversion} - write 0 import {Setting up workspaces} - #B map::set {} {} - cvs::workspace ; # cd's to workspace - fossil::begin [pwd] ; # Uses cwd as workspace to connect to. - stats::setup [cvs::ntrunk] [cvs::ncsets] - - cvs::foreach_cset cset [cvs::root] { + + set src [file normalize $src] + set dst [file normalize $dst] + + set ws [cvs::begin $src] + fossil::begin $ws + stats::setup [cvs::ncsets -import] [cvs::ncsets] + + cvs::foreach cset { Import1 $cset } stats::done - cvs::wsclear fossil::done $dst + cvs::done write 0 import Ok. return } @@ -98,11 +96,11 @@ return } proc ::vc::fossil::import::cvs::ImportCS {cset} { #B fossil::setup [map::get [cvs::parentOf $cset]] - lassign [cvs::wssetup $cset] user timestamp message + lassign [cvs::checkout $cset] user timestamp message lassign [fossil::commit $cset $user $timestamp $message] uuid ad rm ch write 2 import "== +${ad}-${rm}*${ch}" map::set $cset $uuid return }