Fossil

Check-in [87cf6090]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Created convenience methods to create the human readable repesentation of a changeset and lists of such, and made liberal use of them.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:87cf60902113c7564a2ae43de7ede9e83d23af1c
User & Date: aku 2007-11-24 05:31:30
Context
2007-11-25
02:51
Tweaks of the log output, and reworked internals to expose not only breaking of cycles, but of paths as well. check-in: 54e9b0a1 user: aku tags: trunk
2007-11-24
05:31
Created convenience methods to create the human readable repesentation of a changeset and lists of such, and made liberal use of them. check-in: 87cf6090 user: aku tags: trunk
04:40
Bugfix in changeset class. Documented and fixed the SQL statements pulling the successor and predecessor information out of the state. It mishandled the Trunk <-> NTDB transitions. check-in: 184c5632 user: aku tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to tools/cvs2fossil/lib/c2f_cyclebreaker.tcl.

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
	}

	set dg [struct::graph dg]

	foreach cset $changesets {
	    $dg node insert $cset
	    $dg node set    $cset timerange [$cset timerange]
	    $dg node set    $cset label     [ID $cset]
	    $dg node set    $cset __id__    [$cset id]
	}

	# 2. Find for all relevant changeset their revisions and their
	#    dependencies. Map the latter back to changesets and
	#    construct the corresponding arcs.

................................................................................
		# to be not possible, as pass 5 (InitCsets) takes care
		# to transform internal into external dependencies by
		# breaking the relevant changesets apart. So having
		# one indicates big trouble in pass 5. We report them
		# and dump internal structures to make it easier to
		# trace the links causing the problem.
		if {$succ eq $cset} {
		    trouble fatal "Self-referencing changeset <[$cset id]>"
		    log write 2 cyclebreaker "LOOP changeset <[$cset id]> __________________"
		    array set nmap [$cset nextmap]
		    foreach r [lsort -dict [array names nmap]] {
			foreach succrev $nmap($r) {
			    log write 2 cyclebreaker \
				"LOOP * rev <$r> --> rev <$succrev> --> cs [join [struct::list map [project::rev ofrev $succrev] [myproc ID]] { }]"
			}
		    }
		}
	    }
	}

	# Run the user hook to manipulate the graph before
................................................................................
	if {[log verbosity?] < 10} return
	::variable mybottom
	log write 10 cyclebreaker \
	    "Pending: [struct::list map $mybottom [myproc FormatPendingItem]]"
	return
    }

    proc FormatPendingItem {item} { lreplace $item 0 0 <[[lindex $item 0] id]> }

    proc FindCycle {dg} {
	# This procedure is run if and only the graph is not empty and
	# all nodes have predecessors. This means that each node is
	# either part of a cycle or (indirectly) depending on a node
	# in a cycle. We can start at an arbitrary node, follow its
	# incoming edges to its predecessors until we see a node a
................................................................................
	    # Choose arbitrary predecessor
	    set start [lindex [$dg nodes -in $start] 0]
	}

	return [struct::list reverse [lrange $path $seen($start) end]]
    }

    proc ID {cset} { return "<[$cset id]>" }

    proc BreakCycle {dg cycle} {
	# The cycle we have gotten is broken by breaking apart one or
	# more of the changesets in the cycle. This causes us to
	# create one or more changesets which are to be committed,
	# added to the graph, etc. pp.

	# NOTE/TODO. Move this map operation to project::rev, as typemethod.
	set cprint [join [struct::list map $cycle [myproc ID]] { }]

	lappend cycle [lindex $cycle 0] [lindex $cycle 1]
	set bestlink {}
	set bestnode {}

	foreach \
	    prev [lrange $cycle 0 end-2] \
................................................................................
		    set bestlink $link
		    set bestnode $cset
		} else {
		    $link destroy
		}
	    }

	log write 5 cyclebreaker "Breaking cycle ($cprint) by splitting changeset <[$bestnode id]>"
	set ID [$bestnode id]
	Mark $dg -${ID}-before

	set newcsets [$bestlink break]
	$bestlink destroy

        # At this point the old changeset (BESTNODE) is gone
................................................................................
	set pre [$dg nodes -in $n]

        $dg node delete $n

	foreach cset $replacements {
	    $dg node insert $cset
	    $dg node set    $cset timerange [$cset timerange]
	    $dg node set    $cset label     [ID $cset]
	    $dg node set    $cset __id__    [$cset id]
	}

	foreach cset $replacements {
	    foreach succ [$cset successors] {
		# The new changesets may have dependencies outside of
		# the chosen set. These are ignored
		if {![$dg node exists $succ]} continue
		$dg arc insert $cset $succ
		if {$succ eq $cset} {
		    trouble internal "Self-referencing changeset <[$cset id]>"
		}
	    }
	}
	foreach cset $pre {
	    foreach succ [$cset successors] {
		# Note that the arc may already exist in the graph. If
		# so ignore it. The new changesets may have







|







 







|
|




|







 







|







 







<
<







|







 







|







 







|










|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
...
296
297
298
299
300
301
302


303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
...
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
...
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
	}

	set dg [struct::graph dg]

	foreach cset $changesets {
	    $dg node insert $cset
	    $dg node set    $cset timerange [$cset timerange]
	    $dg node set    $cset label     [$cset str]
	    $dg node set    $cset __id__    [$cset id]
	}

	# 2. Find for all relevant changeset their revisions and their
	#    dependencies. Map the latter back to changesets and
	#    construct the corresponding arcs.

................................................................................
		# to be not possible, as pass 5 (InitCsets) takes care
		# to transform internal into external dependencies by
		# breaking the relevant changesets apart. So having
		# one indicates big trouble in pass 5. We report them
		# and dump internal structures to make it easier to
		# trace the links causing the problem.
		if {$succ eq $cset} {
		    trouble fatal "Self-referencing changeset [$cset str]"
		    log write 2 cyclebreaker "LOOP changeset [$cset str] __________________"
		    array set nmap [$cset nextmap]
		    foreach r [lsort -dict [array names nmap]] {
			foreach succrev $nmap($r) {
			    log write 2 cyclebreaker \
				"LOOP * rev <$r> --> rev <$succrev> --> cs [project::rev strlist [project::rev ofrev $succrev]]"
			}
		    }
		}
	    }
	}

	# Run the user hook to manipulate the graph before
................................................................................
	if {[log verbosity?] < 10} return
	::variable mybottom
	log write 10 cyclebreaker \
	    "Pending: [struct::list map $mybottom [myproc FormatPendingItem]]"
	return
    }

    proc FormatPendingItem {item} { lreplace $item 0 0 [[lindex $item 0] str] }

    proc FindCycle {dg} {
	# This procedure is run if and only the graph is not empty and
	# all nodes have predecessors. This means that each node is
	# either part of a cycle or (indirectly) depending on a node
	# in a cycle. We can start at an arbitrary node, follow its
	# incoming edges to its predecessors until we see a node a
................................................................................
	    # Choose arbitrary predecessor
	    set start [lindex [$dg nodes -in $start] 0]
	}

	return [struct::list reverse [lrange $path $seen($start) end]]
    }



    proc BreakCycle {dg cycle} {
	# The cycle we have gotten is broken by breaking apart one or
	# more of the changesets in the cycle. This causes us to
	# create one or more changesets which are to be committed,
	# added to the graph, etc. pp.

	# NOTE/TODO. Move this map operation to project::rev, as typemethod.
	set cprint [project::rev strlist $cycle]

	lappend cycle [lindex $cycle 0] [lindex $cycle 1]
	set bestlink {}
	set bestnode {}

	foreach \
	    prev [lrange $cycle 0 end-2] \
................................................................................
		    set bestlink $link
		    set bestnode $cset
		} else {
		    $link destroy
		}
	    }

	log write 5 cyclebreaker "Breaking cycle ($cprint) by splitting changeset [$bestnode str]"
	set ID [$bestnode id]
	Mark $dg -${ID}-before

	set newcsets [$bestlink break]
	$bestlink destroy

        # At this point the old changeset (BESTNODE) is gone
................................................................................
	set pre [$dg nodes -in $n]

        $dg node delete $n

	foreach cset $replacements {
	    $dg node insert $cset
	    $dg node set    $cset timerange [$cset timerange]
	    $dg node set    $cset label     [$cset str]
	    $dg node set    $cset __id__    [$cset id]
	}

	foreach cset $replacements {
	    foreach succ [$cset successors] {
		# The new changesets may have dependencies outside of
		# the chosen set. These are ignored
		if {![$dg node exists $succ]} continue
		$dg arc insert $cset $succ
		if {$succ eq $cset} {
		    trouble internal "Self-referencing changeset [$cset str]"
		}
	    }
	}
	foreach cset $pre {
	    foreach succ [$cset successors] {
		# Note that the arc may already exist in the graph. If
		# so ignore it. The new changesets may have

Changes to tools/cvs2fossil/lib/c2f_pbreakacycle.tcl.

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
...
300
301
302
303
304
305
306
307









308
309
310
311
312
313
314
	    CheckAndBreakBackwardBranch $graph $cset
	}
	return
    }

    proc CheckAndBreakBackwardBranch {graph cset} {
	while {[IsABackwardBranch $graph $cset]} {
	    log write 5 breakacycle "Breaking backward branch changeset <[$cset id]>"

	    # 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
	    # induces a new changeset, and the second may still be
................................................................................
	if {![llength $backwardrevisions]} { trouble internal "Set of backward revisions is empty" }
	return
    }


    # # ## ### ##### ######## #############

    proc SaveOrder {graph cset pos} {









    }

    # # ## ### ##### ######## #############

    proc BreakCycle {graph} {
	cyclebreaker break $graph
    }







|







 







|
>
>
>
>
>
>
>
>
>







131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
...
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
	    CheckAndBreakBackwardBranch $graph $cset
	}
	return
    }

    proc CheckAndBreakBackwardBranch {graph cset} {
	while {[IsABackwardBranch $graph $cset]} {
	    log write 5 breakacycle "Breaking backward branch changeset [$cset str]"

	    # 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
	    # induces a new changeset, and the second may still be
................................................................................
	if {![llength $backwardrevisions]} { trouble internal "Set of backward revisions is empty" }
	return
    }


    # # ## ### ##### ######## #############

    proc SaveOrder {graph at cset} {
	set cid [$cset id]

	log write 4 breakacycle "Comitting @ $at: [$cset str]"
	state run {
	    INSERT INTO csorder (cid,  pos)
	    VALUES              ($cid, $at)
	}
	# MAYBE TODO: Write the project level changeset dependencies as well.
	return
    }

    # # ## ### ##### ######## #############

    proc BreakCycle {graph} {
	cyclebreaker break $graph
    }

Changes to tools/cvs2fossil/lib/c2f_pbreakrcycle.tcl.

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
    }

    proc IsByRevision {cset} { $cset byrevision }

    proc SaveOrder {graph at cset} {
	set cid [$cset id]

	log write 4 breakrcycle "Comitting @ $at: <$cid>"
	state run {
	    INSERT INTO csorder (cid,  pos)
	    VALUES              ($cid, $at)
	}
	# TODO: Write the project level changeset dependencies as well.
	return
    }

    # # ## ### ##### ######## #############
    ## Configuration

    pragma -hasinstances   no ; # singleton







|




|







102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
    }

    proc IsByRevision {cset} { $cset byrevision }

    proc SaveOrder {graph at cset} {
	set cid [$cset id]

	log write 4 breakrcycle "Comitting @ $at: [$cset str]"
	state run {
	    INSERT INTO csorder (cid,  pos)
	    VALUES              ($cid, $at)
	}
	# MAYBE TODO: Write the project level changeset dependencies as well.
	return
    }

    # # ## ### ##### ######## #############
    ## Configuration

    pragma -hasinstances   no ; # singleton

Changes to tools/cvs2fossil/lib/c2f_prev.tcl.

48
49
50
51
52
53
54


55
56
57
58
59
60
61
...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
...
354
355
356
357
358
359
360






361
362
363
364
365
366
367
	# mapping from revisions to them.
	lappend mychangesets   $self
	set     myidmap($myid) $self
	foreach r $revisions { lappend myrevmap($r) $self }
	return
    }



    method id        {} { return $myid }
    method revisions {} { return $myrevisions }
    method data      {} { return [list $myproject $mytype $mysrcid] }

    method bysymbol   {} { return [expr {$mytype eq "sym"}] }
    method byrevision {} { return [expr {$mytype eq "rev"}] }

................................................................................
	# Array of dependencies (parent -> child). This is pulled from
	# the state, and limited to successors within the changeset.

	array set dependencies {}
	PullInternalSuccessorRevisions dependencies $myrevisions
	if {![array size dependencies]} {return 0} ; # Nothing to break.

	log write 6 csets ...<$myid>.......................................................

	# We have internal dependencies to break. We now iterate over
	# all positions in the list (which is chronological, at least
	# as far as the timestamps are correct and unique) and
	# determine the best position for the break, by trying to
	# break as many dependencies as possible in one go. When a
	# break was found this is redone for the fragments coming and
................................................................................
	    Border $fragment s e
	    if {$laste != ($s - 1)} {
		trouble internal "Bad fragment border <$laste | $s>, gap or overlap"
	    }

	    set new [$type %AUTO% $myproject $mytype $mysrcid [lrange $myrevisions $s $e]]

            log write 4 csets "Breaking <$myid> @ $laste, new <[$new id]>, cutting $breaks($laste)"

	    set laste $e
	}

	if {$laste != ([llength $myrevisions]-1)} {
	    trouble internal "Bad fragment end @ $laste, gap, or beyond end of the range"
	}
................................................................................
	    }
	    lappend newcsets [$type %AUTO% $project $cstype $cssrc $fragmentrevisions]
	}

	foreach c $newcsets { $c persist }
	return $newcsets
    }







    # # ## ### ##### ######## #############
    ## State

    variable myid        {} ; # Id of the cset for the persistent
			      # state.
    variable myproject   {} ; # Reference of the project object the







>
>







 







|







 







|







 







>
>
>
>
>
>







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
...
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
...
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
	# mapping from revisions to them.
	lappend mychangesets   $self
	set     myidmap($myid) $self
	foreach r $revisions { lappend myrevmap($r) $self }
	return
    }

    method str {} { return "<${myid}>" }

    method id        {} { return $myid }
    method revisions {} { return $myrevisions }
    method data      {} { return [list $myproject $mytype $mysrcid] }

    method bysymbol   {} { return [expr {$mytype eq "sym"}] }
    method byrevision {} { return [expr {$mytype eq "rev"}] }

................................................................................
	# Array of dependencies (parent -> child). This is pulled from
	# the state, and limited to successors within the changeset.

	array set dependencies {}
	PullInternalSuccessorRevisions dependencies $myrevisions
	if {![array size dependencies]} {return 0} ; # Nothing to break.

	log write 6 csets ...[$self str].......................................................

	# We have internal dependencies to break. We now iterate over
	# all positions in the list (which is chronological, at least
	# as far as the timestamps are correct and unique) and
	# determine the best position for the break, by trying to
	# break as many dependencies as possible in one go. When a
	# break was found this is redone for the fragments coming and
................................................................................
	    Border $fragment s e
	    if {$laste != ($s - 1)} {
		trouble internal "Bad fragment border <$laste | $s>, gap or overlap"
	    }

	    set new [$type %AUTO% $myproject $mytype $mysrcid [lrange $myrevisions $s $e]]

            log write 4 csets "Breaking [$self str ] @ $laste, new [$new str], cutting $breaks($laste)"

	    set laste $e
	}

	if {$laste != ([llength $myrevisions]-1)} {
	    trouble internal "Bad fragment end @ $laste, gap, or beyond end of the range"
	}
................................................................................
	    }
	    lappend newcsets [$type %AUTO% $project $cstype $cssrc $fragmentrevisions]
	}

	foreach c $newcsets { $c persist }
	return $newcsets
    }

    typemethod strlist {changesets} {
	return [join [struct::list map $changesets [myproc ID]]]
    }

    proc ID {cset} { $cset str }

    # # ## ### ##### ######## #############
    ## State

    variable myid        {} ; # Id of the cset for the persistent
			      # state.
    variable myproject   {} ; # Reference of the project object the

Changes to tools/cvs2fossil/lib/c2f_prevlink.tcl.

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
	if {$smove < $omove} { return 1 } ; # self is better.

	return 0 ; # Self is worse or equal, i.e. not better.
    }

    method break {} {
	if {![$self breakable]} {
	    trouble internal "Changeset <[$mycset id]> is not breakable."
	}

	# One thing to choose when splitting CSET is where the
	# revision in categories 1 and 2 (none and passthrough
	# respectively) are moved to. This is done using the counters.

	if {!$mycount(prev)} {







|







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
	if {$smove < $omove} { return 1 } ; # self is better.

	return 0 ; # Self is worse or equal, i.e. not better.
    }

    method break {} {
	if {![$self breakable]} {
	    trouble internal "Changeset [$mycset str] is not breakable."
	}

	# One thing to choose when splitting CSET is where the
	# revision in categories 1 and 2 (none and passthrough
	# respectively) are moved to. This is done using the counters.

	if {!$mycount(prev)} {