Diff
Not logged in

Differences From:

File tools/lib/cvs.tcl part of check-in [b504674c5f] - Fixed problems with the untested statistics module. Moved cset <-> uuid map out of cvs to control layer, separate package. Currently not really useful, will be needed when handling cvs branches. Moved some user feedback around, and the import control too. by aku on 2007-09-15 03:18:31. [view]

To:

File tools/lib/cvs.tcl part of check-in [d8c18fc148] - 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. by aku on 2007-09-17 00:56:40. [view]

@@ -20,25 +20,133 @@
 
 # -----------------------------------------------------------------------------
 # 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
@@ -45,15 +153,17 @@
 
     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
 	}
 
@@ -60,10 +170,17 @@
 	# 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.
 
@@ -85,16 +202,16 @@
 	    }
 	}
 
 	# 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
@@ -147,9 +264,9 @@
     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
@@ -156,9 +273,9 @@
 
 	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
@@ -204,10 +321,10 @@
 
     # 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
@@ -244,38 +361,25 @@
     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.
@@ -300,16 +404,17 @@
 	} 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.
 
@@ -363,22 +468,8 @@
     }
     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 {} {
@@ -403,9 +494,9 @@
     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}
 
@@ -431,10 +522,10 @@
 
     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
@@ -445,9 +536,9 @@
 
 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
@@ -458,25 +549,29 @@
 }
 
 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