Check-in [6b520e7d97]
Not logged in
Overview

SHA1 Hash:6b520e7d97c3d610a2282685fa37f83f1c0dd774
Date: 2007-11-27 09:07:37
User: aku
Comment:Modified to break all backward symbols, not only branches, removed the other custom circle breaking code, should not be needed any longer (See comments for proof).
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified tools/cvs2fossil/lib/c2f_pbreakacycle.tcl from [642a0a9f2e] to [7ef64f5370].

@@ -66,16 +66,15 @@
 	# 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
+	incr len 12
 	set mycsfmt %${len}s
 
-	cyclebreaker precmd   [myproc BreakBackwardBranches]
-	cyclebreaker savecmd  [myproc KeepOrder]
-	cyclebreaker breakcmd [myproc BreakCycle]
+	cyclebreaker precmd   [myproc BreakBackward]
+	cyclebreaker savecmd  [myproc KeepOrder]
 
 	state transaction {
 	    LoadCommitOrder
 	    cyclebreaker run break-all [myproc Changesets]
 	}
@@ -106,21 +105,17 @@
 		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 }
 	}
 	return
     }
 
     # # ## ### ##### ######## #############
 
-    proc BreakBackwardBranches {graph} {
+    proc BreakBackward {graph} {
 	# We go over all branch changesets, i.e. the changesets
 	# created by the symbols which are translated as branches, and
 	# break any which are 'backward', which means that they have
 	# at least one incoming revision changeset which is committed
 	# after at least one of the outgoing revision changesets, per
@@ -135,20 +130,18 @@
 	#
 	# NOTE 2: Might we even be able to select the backward branch
 	# changesets too ?
 
 	foreach cset [$graph nodes] {
-	    if {![$cset isbranch]} continue
-	    CheckAndBreakBackwardBranch $graph $cset
+	    if {![$cset bysymbol]} continue
+	    CheckAndBreakBackward $graph $cset
 	}
 	return
     }
 
-    proc CheckAndBreakBackwardBranch {graph cset} {
-	while {[IsABackwardBranch $graph $cset]} {
-	    log write 5 breakacycle "Breaking backward branch changeset [$cset str]"
-
+    proc CheckAndBreakBackward {graph cset} {
+	while {[IsBackward $graph $cset]} {
 	    # Knowing that the branch is backward we now look at the
 	    # individual revisions in the changeset and determine
 	    # which of them are responsible for the overlap. This
 	    # allows us to split them into two sets, one of
 	    # non-overlapping revisions, and of overlapping ones. Each
@@ -165,11 +158,11 @@
 
 	    # limits : dict (revision -> list (max predecessor commit, min sucessor commit))
 
 	    ComputeLimits $cset limits border
 
-	    log write 6 breakacycle "Using commit position $border as border"
+	    log write 5 breakacycle "Breaking backward changeset [$cset str] with commit position $border as border"
 
 	    # Then we sort the file level items based on there they
 	    # sit relative to the border into before and after the
 	    # border.
 
@@ -181,18 +174,20 @@
 	    # At last check that the normal frament is indeed not
 	    # backward, and iterate over the possibly still backward
 	    # second fragment.
 
 	    struct::list assign $replacements normal backward
-	    if {[IsABackwardBranch $graph $normal]} { trouble internal "The normal fragment is unexpectedly a backward branch" }
+	    if {[IsBackward $graph $normal]} {
+		trouble internal "The normal fragment is unexpectedly backward"
+	    }
 
 	    set cset $backward
 	}
 	return
     }
 
-    proc IsABackwardBranch {dg cset} {
+    proc IsBackward {dg cset} {
 	# A branch is "backward" if it has at least one incoming
 	# revision changeset which is committed after at least one of
 	# the outgoing revision changesets, per the order computed in
 	# pass 6.
 
@@ -313,13 +308,16 @@
 
 
     # # ## ### ##### ######## #############
 
     proc KeepOrder {graph at cset} {
+	::variable myatfmt
+	::variable mycsfmt
+
 	set cid [$cset id]
 
-	log write 4 breakacycle "Changeset @ [format $myatfmt $at]: [format $mycsfmt [$cset str]] <<[FormatTR $graph $cset]>>"
+	log write 8 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
@@ -326,23 +324,41 @@
 
 	# 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.
+	# IMHO this will work if and only if none of the symbol
+	# changesets are "backwards", which explains the breaking of
+	# the backward changesets first, in the pre-hook. A difference
+	# to cvs2svn however is that we are breaking all backward
+	# symbol changesets, both branch and tag. cvs2svn can
+	# apparently assume here that tag symbol changesets are not
+	# backwards, ever. We can't, apparently. It is unclear to me
+	# where the difference is.
+
+	# An interesting thing IMHO, is that after breaking backward
+	# symbol changesets we should not have any circles any
+	# longer. Each circle which was still present had to involve a
+	# backward symbol, and that we split.
+
+	# Proof: Let us assume we that have a circle
+	# 	C: R1 -> ... -> Rx -> S -> Ry -> ... -> Rn -> R1
+	# Let us further assume that S is not backward. That means
+	# ORD(Rx) < ORD(Ry).  The earlier topological sorting without
+	# symbols now forces this relationship through to be
+	#	ORD(Rx) < ORD(R1) < ORD(Rx).
+	# We have reached an impossibility, a paradox. Our initial
+	# assumption of S not being backward cannot hold.
 	#
-	# 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.
+	# Alternate, direct, reasoning: Without S the chain of
+	# dependencies is Ry -> .. -> R1 -> .. -> Rx, therefore
+	# ORD(Ry) < ORD(Rx) holds, and this means S is backward.
+
+	# NOTE. Even with the backward symbols broken, it is not clear
+	# to me yet what this means in terms of tagging revisions
+	# later, as we now have more than one place where the symbol
+	# occurs on the relevant LOD.
 
 	struct::set exclude myrevisionchangesets $cset
 
 	::variable mylastpos
 	set new [$cset pos]
@@ -369,131 +385,10 @@
     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
 				    # changeset (object ref) at that