15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
# # ## ### ##### ######## ############# #####################
## 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.
package require vc::fossil::import::cvs::file::sym ; # CVS per file symbols.
package require vc::fossil::import::cvs::state ; # State storage.
package require vc::fossil::import::cvs::integrity ; # State integrity checks.
package require vc::tools::trouble ; # Error reporting.
package require vc::tools::log ; # User feedback
package require vc::tools::misc ; # Text formatting
# # ## ### ##### ######## ############# #####################
##
|
>
>
|
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
# # ## ### ##### ######## ############# #####################
## Requirements
package require Tcl 8.4 ; # Required runtime.
package require snit ; # OO system.
package require struct::set ; # Set operations.
package require struct::list ; # Higher order operations.
package require vc::fossil::import::cvs::file::rev ; # CVS per file revisions.
package require vc::fossil::import::cvs::file::sym ; # CVS per file symbols.
package require vc::fossil::import::cvs::state ; # State storage.
package require vc::fossil::import::cvs::integrity ; # State integrity checks.
package require vc::fossil::import::cvs::gtcore ; # Graph traversal core.
package require vc::tools::trouble ; # Error reporting.
package require vc::tools::log ; # User feedback
package require vc::tools::misc ; # Text formatting
# # ## ### ##### ######## ############# #####################
##
|
247
248
249
250
251
252
253
254
255
256
257
258
259
260
|
if {[$myproject trunkonly]} {
$self ExcludeNonTrunkInformation
}
$self AggregateSymbolData
return
}
# # ## ### ##### ######## #############
## State
variable myid {} ; # File id in the persistent state.
variable mypath {} ; # Path of the file's rcs archive.
variable myusrpath {} ; # Path of the file as seen by users.
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
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
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
|
if {[$myproject trunkonly]} {
$self ExcludeNonTrunkInformation
}
$self AggregateSymbolData
return
}
# # ## ### ##### ######## #############
## Pass XII (Import).
method pushto {repository} {
set ws [$repository workspace]
struct::list assign [$self Expand $ws] filemap revmap
# filemap = dict (path -> uuid)
# revmap = dict (path -> rid)
array set idmap [$repository importfiles $filemap]
# Wipe workspace clean of the imported files.
foreach x [glob -directory $ws r*] { file delete $x }
foreach {path rid} $revmap {
set uuid $idmap($path)
state run {
INSERT INTO revuuid (rid, uuid)
VALUES ($rid, $uuid)
}
}
return
}
method Expand {dir} {
set ex [struct::graph ex] ; # Expansion graph.
set zp [struct::graph zp] ; # Zip/Import graph.
close [open $dir/r__empty__ w];# Base for detached roots on branches.
# Phase I: Pull the revisions from memory and fill the graphs
# with them...
set earcs {} ; # Arcs for expansion graph
set zarcs {} ; # Arcs for zip graph
set revmap {} ; # path -> rid map to later merge uuid information
foreach {rid revnr parent child coff clen} [state run {
SELECT R.rid, R.rev, R.parent, R.child, R.coff, R.clen
FROM revision R
WHERE R.fid = $myid
}] {
lappend revmap r$revnr $rid
$zp node insert $rid
$zp node set $rid revnr $revnr
$zp node set $rid label <$revnr>
if {$child ne ""} {
lappend zarcs $child $rid
}
$ex node insert $rid
$ex node set $rid text [list $coff $clen]
$ex node set $rid revnr $revnr
$ex node set $rid label <$revnr>
if {[rev istrunkrevnr $revnr]} {
# On the trunk, this revision is a delta based on the
# child. That makes the child our predecessor.
if {$child eq ""} continue
lappend earcs $child $rid
} else {
# On a branch this revision is a delta based on the
# parent. That makes the parent our predecessor.
if {$parent eq ""} {
# Detached branch root, this is a patch based on
# the empty string.
$ex node set $rid __base__ r__empty__
continue
}
lappend earcs $parent $rid
}
}
# Phase II: Insert the accumulated dependencies
foreach {from to} $earcs { $ex arc insert $from $to }
foreach {from to} $zarcs { $zp arc insert $from $to }
# Phase III: Traverse the graphs, expand the file, and
# generate import instructions.
set archive [file join [$myproject fullpath] $mypath]
set ac [open $archive r]
fconfigure $ac -translation binary
# First traverse the expansion graph, this gives us the
# revisions in the order we have to expand them, which we do.
gtcore datacmd [mymethod ExpandData]
gtcore formatcmd [mymethod ExpandFormat]
gtcore sortcmd [mymethod ExpandSort]
gtcore savecmd [mymethod Expand1 $ac $dir]
gtcore traverse $ex ; # The graph is gone after the call
close $ac
# Now traverse the import graph, this builds the instruction
# map for the fossil deltas.
gtcore datacmd [mymethod ExpandData]
gtcore formatcmd [mymethod ExpandFormat]
gtcore sortcmd [mymethod ExpandSort]
gtcore savecmd [mymethod Expand2]
set myimport {}
gtcore traverse $zp ; # The graph is gone after the call
set filemap $myimport
unset myimport
# And back to import control
return [list $filemap $revmap]
}
method ExpandData {graph node} { return [$graph node get $node revnr] }
method ExpandFormat {graph item} { return <[lindex $item 1]> } ; # revnr
method ExpandSort {graph candidates} {
# candidates = list(item), item = list(node revnr)
# Sort by node and revnr -> Trunk revisions come first.
return [lsort -index 1 -dict [lsort -index 0 -dict $candidates]]
}
method Expand1 {chan dir graph node} {
set revnr [$graph node get $node revnr]
set fname r$revnr
struct::list assign [$graph node get $node text] offset length
seek $chan $offset start
set data [string map {@@ @} [read $chan $length]]
if {![$graph node keyexists $node __base__]} {
# Full text node. Get the data, decode it, and save.
log write 2 file {Expanding <$revnr>, full text}
fileutil::writeFile -translation binary $dir/$fname $data
} else {
# Delta node. __base__ is the name of the file containing
# the baseline. The patch is at the specified location of
# the archive file.
set fbase [$graph node get $node __base__]
log write 2 file {Expanding <$revnr>, is delta of <$fbase>}
set base [fileutil::cat -translation binary $dir/$fbase]
# Writing the patch to disk is just for better
# debugging. It is not used otherwise.
fileutil::writeFile $dir/rpatch $data
fileutil::writeFile -translation binary $dir/$fname \
[Apply $base $data]
}
# Post to all successors that the just generated file is their
# baseline.
foreach out [$graph nodes -out $node] {
$graph node set $out __base__ $fname
}
return
}
proc Apply {base delta} {
# base = base text.
# delta = delta in rcs format.
#
# Both strings are unencoded, i.e. things like @@, etc. have
# already been replaced with their proper characters.
#
# Return value is the patched text.
set base [split $base \n]
set blen [llength $base]
set ooff 0
set res ""
set lines [split $delta \n]
set nlines [llength $lines]
for {set i 0} {$i < $nlines} {} {
if {![regexp {^([ad])(\d+)\s(\d+)$} [lindex $lines $i] -> cmd sl cn]} {
trouble internal "Bad ed command '[lindex $lines $i]'"
}
incr i
set el [expr {$sl + $cn}]
switch -exact -- $cmd {
d {
incr sl -1
incr el -1
if {$sl < $ooff} { trouble internal {Deletion before last edit} }
if {$sl > $blen} { trouble internal {Deletion past file end} }
if {$el > $blen} { trouble internal {Deletion beyond file end} }
foreach x [lrange $base $ooff $sl] { lappend res $x }
set ooff $el
}
a {
if {$sl < $ooff} { trouble internal {Insert before last edit} }
if {$sl > $blen} { trouble internal {Insert past file end} }
foreach x [lrange $base $ooff $sl] { lappend res $x }
foreach x [lrange $lines $i [expr {$i + $cn}]] { lappend res $x }
set ooff $sl
incr i $cn
}
}
}
foreach x [lrange $base $ooff end] { lappend res $x }
return [join $res \n]
}
method Expand2 {graph node} {
set revnr [$graph node get $node revnr]
# First import the file.
lappend myimport [list A r$revnr {}]
if {[$graph node keyexists $node __base__]} {
# Delta node. __base__ is the name of the file containing
# the baseline. Generate instruction to make the delta as
# well.
set fbase [$graph node get $node __base__]
lappend myimport [list D r$revnr r$fbase]
}
# Post to all successors that the just generated file is their
# baseline. Exception: Those which ave already a baseline set.
# Together with the sorting of trunk revisions first the trunk
# should one uninterupted line, with branch roots _not_ delta
# compressed per their branches.
foreach out [$graph nodes -out $node] {
if {[$graph node keyexists $out __base__]} continue
$graph node set $out __base__ $revnr
}
return
}
variable myimport
# # ## ### ##### ######## #############
## State
variable myid {} ; # File id in the persistent state.
variable mypath {} ; # Path of the file's rcs archive.
variable myusrpath {} ; # Path of the file as seen by users.
|
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
|
# namespace import ::vc::fossil::import::cvs::file::rev
# namespace import ::vc::fossil::import::cvs::file::sym
namespace import ::vc::tools::misc::*
namespace import ::vc::tools::trouble
namespace import ::vc::tools::log
namespace import ::vc::fossil::import::cvs::state
namespace import ::vc::fossil::import::cvs::integrity
}
}
# # ## ### ##### ######## ############# #####################
## Ready
package provide vc::fossil::import::cvs::file 1.0
return
|
>
|
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
|
# namespace import ::vc::fossil::import::cvs::file::rev
# namespace import ::vc::fossil::import::cvs::file::sym
namespace import ::vc::tools::misc::*
namespace import ::vc::tools::trouble
namespace import ::vc::tools::log
namespace import ::vc::fossil::import::cvs::state
namespace import ::vc::fossil::import::cvs::integrity
namespace import ::vc::fossil::import::cvs::gtcore
}
}
# # ## ### ##### ######## ############# #####################
## Ready
package provide vc::fossil::import::cvs::file 1.0
return
|