Check-in [1ea319fb67]
Not logged in
Overview

SHA1 Hash:1ea319fb6786f7ebc42a1518f158d42818e7380c
Date: 2007-11-25 07:44:24
User: aku
Comment:Another helper, textual, write changeset data to stdout.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Added tools/cvs2fossil/changeset version [60a9ddc157]

@@ -1,1 +1,119 @@
+#!/bin/sh
+## -*- tcl -*- \
+exec tclsh "$0" ${1+"$@"}
+
+# # ## ### ##### ######## ############# #####################
+## Copyright (c) 2007 Andreas Kupries.
+#
+# This software is licensed as described in the file LICENSE, which
+# you should have received as part of this distribution.
+#
+# This software consists of voluntary contributions made by many
+# individuals.  For exact contribution history, see the revision
+# history and logs, available at http://fossil-scm.hwaci.com/fossil
+# # ## ### ##### ######## ############# #####################
+
+## Helper application, debugging of cvs2fossil. This application
+## extracts all information about a changeset and writes it nicely
+## formatted to stdout. The changeset is specified by its internal
+## numerical id.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements, extended package management for local packages.
+
+lappend auto_path [file join [file dirname [info script]] lib]
+
+package require Tcl 8.4                               ; # Required runtime.
+package require struct::graph                         ; # Graph handling.
+package require struct::list                          ; # Higher order list ops.
+package require vc::fossil::import::cvs::project::rev ; # Changesets
+package require vc::fossil::import::cvs::state        ; # State storage.
+package require vc::tools::misc                       ; # Min/max.
+
+namespace import ::vc::fossil::import::cvs::state
+namespace import ::vc::fossil::import::cvs::project::rev
+namespace import ::vc::tools::misc::*
+namespace import ::vc::tools::log
+log verbosity 0
+
+# Process the command line, i.e. get the database to access, and file
+# of interest. The latter can be specified by name, id, or indirectly
+# through the id of one of the revisions it contains.
+
+state use [lindex $argv 0]
+state reading changeset
+state reading cstype
+state reading csrevision
+state reading project
+state reading revision
+state reading file
+state reading symbol
+state reading meta
+state reading author
+state reading cmessage
+
+set cid [lindex $argv 1]
+
+struct::list assign [state run {
+    SELECT C.cid, C.pid, C.src, P.name, CT.name
+    FROM changeset C, project P, cstype CT
+    WHERE C.cid = $cid
+    AND   P.pid = C.pid
+    AND   CT.tid = C.type
+}] cid pid src pname tname
+
+puts ""
+puts "Changeset <$tname $cid> in project \"$pname\" ($pid)"
+
+if {$tname eq "sym"} {
+    puts "Symbol \"[state run {
+	SELECT name
+	FROM symbol
+	WHERE sid = $src
+    }]\""
+} else {
+    struct::list assign [state run {
+	SELECT P.name, S.name, A.name, L.text
+	FROM meta M, project P, symbol S, author A, cmessage L
+	WHERE M.mid = $src
+	AND P.pid = M.pid
+	AND S.sid = M.bid
+	AND A.aid = M.aid
+	AND L.cid = M.cid
+    }] project lod author cmessage
+    puts "Meta: Project = \"$project\""
+    puts "Meta: LOD     = \"$lod\""
+    puts "Meta: Author  = \"$author\""
+    puts "Meta: Log     |[join [split $cmessage \n] "\"\nMeta: Log     |"]"
+}
+
+array set rev {}
+foreach {rid date pos fname frev default} [state run {
+    SELECT R.rid, R.date, C.pos, F.name, R.rev, R.isdefault
+    FROM csrevision C, revision R, file F
+    WHERE C.cid = $cid
+    AND   R.rid = C.rid
+    AND   F.fid = R.fid
+    ORDER BY C.pos, R.date
+}] {
+    set rev($rid) [list $pos $date $fname $frev $default]
+    puts "File: [expr {$default?"D":" "}] [clock format $date] [format %3d $pos]/[format %6d $rid] ${frev}::$fname"
+}
+
+
+::vc::fossil::import::cvs::project::rev::PullPredecessorRevisions pdep [array names rev]
+::vc::fossil::import::cvs::project::rev::PullSuccessorRevisions   sdep [array names rev]
+
+
+
+puts ""
+exit
+
+
+
+
+
+
+
 
+exit

Modified tools/cvs2fossil/lib/c2f_pbreakacycle.tcl from [456c81fca5] to [2eea5c9c18].

@@ -20,10 +20,11 @@
 ## Requirements
 
 package require Tcl 8.4                                   ; # Required runtime.
 package require snit                                      ; # OO system.
 package require struct::list                              ; # Higher order list operations.
+package require struct::set                               ; # Set operations.
 package require vc::tools::misc                           ; # Min, max.
 package require vc::tools::log                            ; # User feedback.
 package require vc::tools::trouble                        ; # Error reporting.
 package require vc::fossil::import::cvs::repository       ; # Repository management.
 package require vc::fossil::import::cvs::cyclebreaker     ; # Breaking dependency cycles.
@@ -62,11 +63,17 @@
 
     typemethod run {} {
 	# Pass manager interface. Executed to perform the
 	# functionality of the pass.
 
+	set len [string length [project::rev num]]
+	set myatfmt %${len}s
+	incr len 6
+	set mycsfmt %${len}s
+
 	cyclebreaker precmd   [myproc BreakBackwardBranches]
+	cyclebreaker savecmd  [myproc KeepOrder]
 	cyclebreaker breakcmd [myproc BreakCycle]
 
 	state transaction {
 	    LoadCommitOrder
 	    cyclebreaker run break-all [myproc Changesets]
@@ -88,16 +95,18 @@
 
     proc Changesets {} { project::rev all }
 
     proc LoadCommitOrder {} {
 	::variable mycset
+	::variable myrevisionchangesets
 
 	state transaction {
 	    foreach {cid pos} [state run { SELECT cid, pos FROM csorder }] {
 		set cset [project::rev of $cid]
 		$cset setpos $pos
 		set mycset($pos) $cset
+		lappend myrevisionchangesets $cset
 	    }
 	    # Remove the order information now that we have it in
 	    # memory, so that we can save it once more, for all
 	    # changesets, while breaking the remaining cycles.
 	    state run { DELETE FROM csorder }
@@ -301,12 +310,187 @@
     }
 
 
     # # ## ### ##### ######## #############
 
+    proc KeepOrder {graph at cset} {
+	set cid [$cset id]
+
+	log write 4 breakacycle "Changeset @ [format $myatfmt $at]: [format $mycsfmt [$cset str]] <<[FormatTR $graph $cset]>>"
+
+	# We see here a mixture of symbol and revision changesets.
+	# The symbol changesets are ignored as irrelevant.
+
+	if {[$cset pos] eq ""} return
+
+	# For the revision changesets we are sure that they are
+	# consumed in the same order as generated by pass 7
+	# (RevTopologicalSort). Per the code in cvs2svn.
+
+	# NOTE: I cannot see that. Assume cs A and cs B, not dependent
+	#       on each other in the set of revisions, now B after A
+	#       simply means that B has a later time or depends on
+	#       something wit a later time than A. In the full graph A
+	#       may now have dependencies which shift it after B,
+	#       violating the above assumption.
+	#
+	# Well, it seems to work if I do not make the NTDB root a
+	# successor of the regular root. Doing so seems to tangle the
+	# changesets into a knots regarding time vs dependencies and
+	# trigger such shifts. Keeping these two roots separate OTOH
+	# disappears the tangle. So, for now I accept that, and for
+	# paranoia I add code which checks this assumption.
+
+	struct::set exclude myrevisionchangesets $cset
+
+	::variable mylastpos
+	set new [$cset pos]
+
+	if {$new != ($mylastpos + 1)} {
+	    if {$mylastpos < 0} {
+		set old "<NONE>"
+	    } else {
+		::variable mycset
+		set old [$mycset($mylastpos) str]@$mylastpos
+	    }
+
+	    trouble internal "Ordering of revision changesets violated, [$cset str]@$new is not immediately after $old"
+	}
+
+	set mylastpos $new
+	return
+    }
+
+    proc FormatTR {graph cset} {
+	return [join [struct::list map [$graph node set $cset timerange] {clock format}] { -- }]
+    }
+
+    typevariable mylastpos            -1 ; # Position of last revision changeset saved.
+    typevariable myrevisionchangesets {} ; # Set of revision changesets
+
+    typevariable myatfmt ; # Format for log output to gain better alignment of the various columns.
+    typevariable mycsfmt ; # Ditto for the changesets.
+
+    # # ## ### ##### ######## #############
+
     proc BreakCycle {graph} {
+	# In this pass the cycle breaking can be made a bit more
+	# targeted, hence this custom callback.
+	#
+	# First we use the data remembered by 'SaveOrder', about the
+	# last commit position it handled, to deduce the next revision
+	# changeset it would encounter. Then we look for the shortest
+	# predecessor path from it to all other revision changesets
+	# and break this path. Without such a path we fall back to the
+	# generic cycle breaker.
+
+	::variable mylastpos
+	::variable mycset
+	::variable myrevisionchangesets
+
+	set nextpos [expr {$mylastpos + 1}]
+	set next    $mycset($nextpos)
+
+	puts "** Last: $mylastpos = [$mycset($mylastpos) str] @ [$mycset($mylastpos) pos]"
+	puts "** Next: $nextpos = [$next str] @ [$next pos]"
+
+	set path [SearchForPath $graph $next $myrevisionchangesets]
+	if {[llength $path]} {
+	    cyclebreaker break-segment $graph $path
+	    return
+	}
+
+	# We were unable to find an ordered changeset in the reachable
+	# predecessors, fall back to the generic code for breaking the
+	# found cycle.
+
 	cyclebreaker break $graph
+    }
+
+    proc SearchForPath {graph n stopnodes} {
+	# Search for paths to prerequisites of N.
+	#
+	# Try to find the shortest dependency path that causes the
+	# changeset N to depend (directly or indirectly) on one of the
+	# changesets contained in STOPNODES.
+	#
+	# We consider direct and indirect dependencies in the sense
+	# that the changeset can be reached by following a chain of
+	# predecessor nodes.
+	#
+	# When one of the csets in STOPNODES is found, we terminate
+	# the search and return the path from that cset to N.  If no
+	# path is found to a node in STOP_SET, we return the empty
+	# list/path.
+
+	# This is in essence a multi-destination Dijkstra starting at
+	# N which stops when one of the destinations in STOPNODES has
+	# been reached, traversing the predecessor arcs.
+
+	# REACHABLE :: array (NODE -> list (STEPS, PREVIOUS))
+	#
+	# Semantics: NODE can be reached from N in STEPS steps, and
+	# PREVIOUS is the previous node in the path which reached it,
+	# allowing us at the end to construct the full path by
+	# following these backlinks from the found destination. N is
+	# only included as a key if there is a loop leading back to
+	# it.
+
+	# PENDING :: list (list (NODE, STEPS))
+	#
+	# Semantics: A list of possibilities that still have to be
+	# investigated, where STEPS is the number of steps to get to
+	# NODE.
+
+	array set reachable {}
+	set pending [list [list $n 0]]
+	set at 0
+
+	puts "** Searching shortest path ..."
+
+	while {$at < [llength $pending]} {
+	    struct::list assign [lindex $pending $at] current steps
+
+	    #puts "** [lindex $pending $at] ** [$current str] **"
+	    incr at
+
+	    # Process the possibility. This is a breadth-first traversal.
+	    incr steps
+	    foreach pre [$graph nodes -in $current] {
+	        # Since the search is breadth-first, we only have to #
+	        # set nodes that don't already exist. If they do they
+	        # have been reached already on a shorter path.
+
+		if {[info exists reachable($pre)]} continue
+
+		set reachable($pre) [list $steps $current]
+		lappend pending [list $pre $steps]
+
+		# Continue the search while have not reached any of
+		# our destinations?
+		if {![struct::set contain $pre $stopnodes]} continue
+
+		# We have arrived, PRE is one of the destination; now
+		# construct and return the path to it from N by
+		# following the backlinks in the search state.
+		set path [list $pre]
+		while {1} {
+		    set pre [lindex $reachable($pre) 1]
+		    if {$pre eq $n} break
+		    lappend path $pre
+		}
+		lappend path $n
+
+		puts "** Searching shortest path ... Found ([project rev strlist $path])"
+		return $path
+	    }
+	}
+
+	puts "** Searching shortest path ... Not found"
+
+	# No path found.
+	return {}
     }
 
     # # ## ### ##### ######## #############
 
     typevariable mycset -array {} ; # Map from commit positions to the