Check-in [bd131addb9]
Not logged in
Overview

SHA1 Hash:bd131addb9a700df2cef390ee558ba8721fe47bb
Date: 2007-10-12 05:56:46
User: aku
Comment:Started to flesh out the handling symbols and revisions coming in from the rcs parser. First fragments of revision object. Type methods for classification of revision numb ers.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified tools/cvs2fossil/lib/c2f_file.tcl from [4bd5111a52] to [a86b5d4424].

@@ -16,10 +16,12 @@
 # # ## ### ##### ######## ############# #####################
 ## Requirements
 
 package require Tcl 8.4                             ; # Required runtime.
 package require snit                                ; # OO system.
+package require struct::set                         ; # Set operations.
+package require vc::fossil::import::cvs::file::rev  ; # CVS per file revisions.
 
 # # ## ### ##### ######## ############# #####################
 ##
 
 snit::type ::vc::fossil::import::cvs::file {
@@ -30,11 +32,12 @@
 	set mypath    $path
 	set myproject $project
 	return
     }
 
-    method path {} { return $mypath }
+    method path    {} { return $mypath }
+    method project {} { return $myproject }
 
     # # ## ### ##### ######## #############
     ## Methods required for the class to be a sink of the rcs parser
 
     method begin     {} {}
@@ -52,11 +55,11 @@
     method extend {rev commitmsg deltarange} {}
 
     #method begin {} {puts begin}
     #method sethead {h} {puts head=$h}
     #method setprincipalbranch {b} {puts pb=$b}
-    #method deftag {s r} {puts $s=$r }
+    #method deftag {s r} {puts $s=$r}
     #method setcomment {c} {puts comment=$c}
     #method admindone {} {puts admindone}
     #method def {rev date author state next branches} {puts "def $rev $date $author $state $next $branches"}
     #method setdesc {d} {puts desc=$d}
     #method extend {rev commitmsg deltarange} {puts "extend $commitmsg $deltarange"}
@@ -69,55 +72,221 @@
     }
 
     # # ## ### ##### ######## #############
     ## Implement the sink
 
-    method begin     {} {}
-    method done      {} {}
-    method admindone {} {}
-
-    method sethead {h} {
-	set myhead $h
+    method begin {} {}
+    method done {} {}
+
+    method admindone {} {
+	# We do nothing at the boundary of admin and revision data
+    }
+
+    method sethead {revnr} {
+	set myhead $revnr
 	return
     }
 
-    method setprincipalbranch {b} {
-	set myprincipal $b
+    method setprincipalbranch {branchnr} {
+	set myprincipal $branchnr
 	return
     }
 
-    method deftag {symbol rev} {
-	# Slice symbols into branches and tags, canonical numbers ...
+    method deftag {name revnr} {
+	# FUTURE: Perform symbol transformation here.
+
+	if {[struct::set contains $mysymbols $name]} {
+	    trouble fatal "Multiple definitions of the symbol '$name' in '$mypath'"
+	    return
+	}
+
+	struct::set add mysymbols $name
+
+	if {[rev isbranchrevnr $revnr -> branchnr]} {
+	    $self AddBranch $name $branchnr
+	} else {
+	    $self AddTag $name $revnr
+	}
+	return
     }
 
     method setcomment {c} {# ignore}
     method setdesc    {d} {# ignore}
 
-    method def {rev date author state next branches} {
-	set myrev($rev) [list $date $author $state $next $branches]
+    method def {revnr date author state next branches} {
+	$self LookForUnlabeledBranches $branches
 	$myproject author $author
+
+	if {[info exists myrev($revnr)]} {
+	    trouble fatal "File $mypath contains duplicate definitions for revision $revnr."
+	    return
+	}
+
+	set myrev($revnr) [rev %AUTO% $date $author $state $self]
+
+	RecordBasicDependencies $revnr $next
 	return
     }
 
-    method extend {rev commitmsg deltarange} {
+    method extend {revnr commitmsg deltarange} {
 	set cm [string trim $commitmsg]
-	lappend myrev($rev) $cm $deltarange
 	$myproject cmessage $cm
+
+	set rev $myrev($revnr)
+
+	if {[$rev hascommitmsg]} {
+	    # Apparently repositories exist in which the delta data
+	    # for revision 1.1 is provided several times, at least
+	    # twice. The actual cause of this duplication is not
+	    # known. Speculation centers on RCS/CVS bugs, or from
+	    # manual edits of the repository which borked the
+	    # internals. Whatever the cause, testing showed that both
+	    # cvs and rcs use the first definition when performing a
+	    # checkout, and we follow their lead. Side notes: 'cvs
+	    # log' fails on such a file, and 'cvs rlog' prints the log
+	    # message from the first delta, ignoring the second.
+
+	    log write 1 file "In file $mypath : Duplicate delta data for revision $revnr"
+	    log write 1 file "Ignoring the duplicate"
+	    return
+	}
+
+	# Extend the revision with the new information. The revision
+	# object uses this to complete its meta data set.
+
+	$rev setcommitmsg $cm
+	$rev settext  $deltarange
+
+	# If this is revision 1.1, we have to determine whether the
+	# file seems to have been created through 'cvs add' instead of
+	# 'cvs import'. This can be done by looking at the un-
+	# adulterated commit message, as CVS uses a hardwired magic
+	# message for the latter, i.e. "Initial revision\n", no
+	# period.  (This fact also helps us when the time comes to
+	# determine whether this file might have had a default branch
+	# in the past.)
+
+	if {$revnr eq ""} {
+	    set myimported [expr {$commitmsg eq "Initial revision\n"}]
+	}
+
+	# Here we also keep track of the order in which the revisions
+	# were added to the file.
+
+	lappend myrevisions $rev
 	return
     }
 
     # # ## ### ##### ######## #############
     ## State
 
-    variable mypath       {} ; # Path of rcs archive
-    variable myproject    {} ; # Project object the file belongs to.
-    variable myrev -array {} ; # All revisions and their connections.
-    variable myhead       {} ; # Head revision    (rev number)
-    variable myprincipal  {} ; # Principal branch (branch number)
+    variable mypath            {} ; # Path of rcs archive
+    variable myproject         {} ; # Project object the file belongs to.
+    variable myrev -array      {} ; # All revisions and their connections.
+    variable myrevisions       {} ; # Same as myrev, but a list, giving us the order
+    #                             ; # of revisions.
+    variable myhead            {} ; # Head revision (revision number)
+    variable myprincipal       {} ; # Principal branch (branch number)
+    #                             ; # Contrary to the name this is the default branch.
+    variable mydependencies    {} ; # Dictionary parent -> child, dependency recorder.
+    variable myimported        0  ; # Boolean flag. Set iff rev 1.1 of the file seemingly
+    #                             ; # was imported instead of added normally.
+    variable myroot            {} ; # Revision number of the root revision. Usually '1.1'.
+    #                             ; # Can be a different number, because of 'cvsadmin -o'.
+    variable mybranches -array {} ; # branch number   -> symbol object handling the branch
+    variable mytags     -array {} ; # revision number -> list of symbol object for the tags
+    #                             ; # associated with the revision.
+    variable mysymbols         {} ; # Set of symbol names found in this file.
+
+    ### TODO ###
+    ### File flag - executable,
+    ### RCS mode info (kb, kkb, ...)
 
     # # ## ### ##### ######## #############
     ## Internal methods
+
+    method LookForUnlabeledBranches {branches} {
+	foreach branchrevnr $branches {
+	    if {[catch {
+		set branch [$self Rev2Branch $branchrevnr]
+	    }]} {
+		set branch [$self AddUnlabeledBranch [rev 2branchnr $branchrevnr]]
+	    }
+	    # TODO $branch child $branchrevnr - when add-unlabeled has sensible return value
+	}
+	return
+    }
+
+    method Rev2Branch {revnr} {
+	if {[rev istrunkrevnr $revnr]} {
+	    trouble internal "Expected a branch revision number"
+	}
+	return $mybranches([rev 2branchnr $revnr])
+    }
+
+    method AddUnlabeledBranch {branchnr} {
+	return [$self AddBranch unlabeled-$branchnr $branchnr]
+    }
+
+    method AddBranch {name branchnr} {
+	if {[info exists mybranches($branchnr)]} {
+	    log write 1 file "In '$mypath': Branch '$branchnr' named '[$mybranches($branchnr) name]'"
+	    log write 1 file "Cannot have second name '$name', ignoring it"
+	    return
+	}
+	set sym ""
+	set branch ""
+	#TODO set sym [$myproject getsymbol $name ]
+	#TODO set tag [sym %AUTO% branch $sym $branchnr]
+	set mybranches($branchnr) $branch
+	return $branch
+    }
+
+    method AddTag {name revnr} {
+	set sym ""
+	set tag ""
+	#TODO set sym [$myproject getsymbol $name ]
+	#TODO set tag [sym %AUTO% tag $sym $revnr]
+	lappend mytags($revnr) $tag
+	return $tag
+    }
+
+    proc RecordBasicDependencies {revnr next} {
+	# Handle the revision dependencies. Record them for now, do
+	# nothing with them yet.
+
+	# On the trunk the 'next' field points to the previous
+	# revision, i.e. the _parent_ of the current one. Example:
+	# 1.6's next is 1.5 (modulo cvs admin -o).
+
+	# Contrarily on a branch the 'next' field points to the
+	# primary _child_ of the current revision. As example,
+	# 1.1.3.2's 'next' will be 1.1.3.3.
+
+	# The 'next' field actually always refers to the revision
+	# containing the delta needed to retrieve that revision.
+
+	# The dependencies needed here are the logical structure,
+	# parent/child, and not the implementation dependent delta
+	# pointers.
+
+	if {$next eq ""} return
+
+	upvar 1 mydependencies mydependencies
+
+	#                          parent -> child
+	if {[rev istrunkrevnr $revnr]} {
+	    lappend mydependencies $next $revnr
+	} else {
+	    lappend mydependencies $revnr $next
+	}
+	return
+    }
+
+
+    # # ## ### ##### ######## #############
+    ## Configuration
 
     pragma -hastypeinfo    no  ; # no type introspection
     pragma -hasinfo        no  ; # no object introspection
     pragma -hastypemethods no  ; # type is not relevant.
     pragma -simpledispatch yes ; # simple fast dispatch
@@ -125,12 +294,16 @@
     # # ## ### ##### ######## #############
 }
 
 namespace eval ::vc::fossil::import::cvs {
     namespace export file
+    namespace eval file {
+	# Import not required, already a child namespace.
+	# namespace import vc::fossil::import::cvs::file::rev
+    }
 }
 
 # # ## ### ##### ######## ############# #####################
 ## Ready
 
 package provide vc::fossil::import::cvs::file 1.0
 return

Modified tools/cvs2fossil/lib/c2f_frev.tcl from [a6284f7501] to [f56084c138].

@@ -23,26 +23,71 @@
 
 snit::type ::vc::fossil::import::cvs::file::rev {
     # # ## ### ##### ######## #############
     ## Public API
 
-    constructor {} {
+    constructor {date author state thefile} {
 	return
     }
 
+    method hascommitmsg {} {
+	# TODO: check that we have the commit message
+	return 0
+    }
+
+    method setcommitmsg {cm} {
+    }
+
+    method settext {text} {
+    }
+
+    # # ## ### ##### ######## #############
+    ## Type API
+
+    typemethod istrunkrevnr {revnr} {
+	return [expr {[llength [split $revnr .]] == 1}]
+    }
+
+    typemethod 2branchnr {revnr} {
+	# Input is a branch revision number, i.e. a revision number
+	# with an even number of components; for example '2.9.2.1'
+	# (never '2.9.2' nor '2.9.0.2').  The return value is the
+	# branch number (for example, '2.9.2').  For trunk revisions,
+	# like '3.4', we return the empty string.
+
+	if {[$type istrunkrevnr $revnr]} {
+	    return ""
+	}
+	return [join [lrange [split $revnr .] 0 end-1] .]
+    }
+
+    typemethod isbranchrevnr {revnr _ bv} {
+	if {[regexp $mybranchpattern $revnr -> head tail]} {
+	    upvar 1 $bv branchnr
+	    set branchnr ${head}.$tail
+	    return 1
+	}
+	return 0
+    }
+
     # # ## ### ##### ######## #############
     ## State
+
+    typevariable mybranchpattern {^((?:\d+\.\d+\.)+)(?:0\.)?(\d+)$}
+    # First a nonzero even number of digit groups with trailing dot
+    # CVS then sticks an extra 0 in here; RCS does not.
+    # And the last digit group.
 
     # # ## ### ##### ######## #############
     ## Internal methods
 
     # # ## ### ##### ######## #############
     ## Configuration
 
     pragma -hastypeinfo    no  ; # no type introspection
     pragma -hasinfo        no  ; # no object introspection
-    pragma -hastypemethods no  ; # type is not relevant.
+    #pragma -hastypemethods no  ; # type is not relevant.
     pragma -simpledispatch yes ; # simple fast dispatch
 
     # # ## ### ##### ######## #############
 }