18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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::tools::trouble ; # Error reporting.
package require vc::tools::misc ; # Text formatting
# # ## ### ##### ######## ############# #####################
##
snit::type ::vc::fossil::import::cvs::file {
# # ## ### ##### ######## #############
|
>
|
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
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::tools::trouble ; # Error reporting.
package require vc::tools::log ; # User feedback
package require vc::tools::misc ; # Text formatting
# # ## ### ##### ######## ############# #####################
##
snit::type ::vc::fossil::import::cvs::file {
# # ## ### ##### ######## #############
|
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
# '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
}
method done {} {
# Complete the revisions, branches, and tags. This includes
# looking for a non-trunk default branch, marking its members
# and linking them into the trunk.
DetermineRevisionOperations
DetermineLinesOfDevelopment
# list of roots ... first only one, later can become more.
return
}
# # ## ### ##### ######## #############
## State
variable mypath {} ; # Path of the file's rcs archive.
|
|
>
>
>
<
>
>
>
|
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
|
# '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 "1.1"} {
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
}
method done {} {
# Complete the revisions, branches, and tags. This includes
# looking for a non-trunk default branch, marking its members
# and linking them into the trunk.
DetermineRevisionOperations
DetermineLinesOfDevelopment
HandleNonTrunkDefaultBranch
RemoveIrrelevantDeletions
RemoveInitialBranchDeletions
if {[$myproject trunkonly]} {
ExcludeNonTrunkInformation
}
return
}
# # ## ### ##### ######## #############
## State
variable mypath {} ; # Path of the file's rcs archive.
|
244
245
246
247
248
249
250
251
252
253
254
255
256
257
|
# order of definition. This also defines
# their order of creation, which is the
# reverse of definition. I.e. a smaller
# number means 'Defined earlier', means
# 'Created later'.
variable mytrunk {} ; # Direct reference to myproject -> trunk.
# # ## ### ##### ######## #############
## Internal methods
method RecordBranchCommits {branches} {
foreach branchrevnr $branches {
if {[catch {
|
>
>
>
>
|
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
|
# order of definition. This also defines
# their order of creation, which is the
# reverse of definition. I.e. a smaller
# number means 'Defined earlier', means
# 'Created later'.
variable mytrunk {} ; # Direct reference to myproject -> trunk.
variable myroots {} ; # List of roots in the forest of
# lod's. Object references to revisions and
# branches. The latter can appear when they
# are severed from their parent.
# # ## ### ##### ######## #############
## Internal methods
method RecordBranchCommits {branches} {
foreach branchrevnr $branches {
if {[catch {
|
362
363
364
365
366
367
368
369
370
371
372
373
374
375
|
# reference to the branch there, and further declare
# the first child's parent to be branch's parent, and
# list this child in the parent revision.
if {[$branch haschild]} {
set childrevnr [$branch childrevnr]
set child $myrev($childrevnr)
$child setparentbranch $branch
$child setparent $rev
$rev addchildonbranch $child
}
}
}
|
>
|
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
|
# reference to the branch there, and further declare
# the first child's parent to be branch's parent, and
# list this child in the parent revision.
if {[$branch haschild]} {
set childrevnr [$branch childrevnr]
set child $myrev($childrevnr)
$branch setchild $child
$child setparentbranch $branch
$child setparent $rev
$rev addchildonbranch $child
}
}
}
|
419
420
421
422
423
424
425
426
427
428
429
430
431
432
|
# their root (sic!).
foreach {revnr rev} [array get myrev] {
if {[$rev hasparent]} continue
if {$myroot ne ""} { trouble internal "Multiple root revisions found" }
set myroot $rev
}
return
}
proc DetermineRevisionOperations {} {
upvar 1 myrevisions myrevisions
foreach rev $myrevisions { $rev determineoperation }
return
|
>
>
>
>
|
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
|
# their root (sic!).
foreach {revnr rev} [array get myrev] {
if {[$rev hasparent]} continue
if {$myroot ne ""} { trouble internal "Multiple root revisions found" }
set myroot $rev
}
# In the future we also need a list, as branches can become
# severed from their parent, making them their own root.
set myroots [list $myroot]
return
}
proc DetermineRevisionOperations {} {
upvar 1 myrevisions myrevisions
foreach rev $myrevisions { $rev determineoperation }
return
|
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
|
upvar 1 mytrunk mytrunk
return $mytrunk
} else {
upvar 1 self self
return [$self Rev2Branch $revnr]
}
}
# # ## ### ##### ######## #############
## 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
# # ## ### ##### ######## #############
}
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
# namespace import ::vc::fossil::import::cvs::file::sym
namespace import ::vc::tools::misc::*
namespace import ::vc::tools::trouble
}
}
# # ## ### ##### ######## ############# #####################
## Ready
package provide vc::fossil::import::cvs::file 1.0
return
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
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
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
|
upvar 1 mytrunk mytrunk
return $mytrunk
} else {
upvar 1 self self
return [$self Rev2Branch $revnr]
}
}
proc HandleNonTrunkDefaultBranch {} {
upvar 1 myprincipal myprincipal myroot myroot mybranches mybranches myimported myimported myroots myroots myrev myrev
set revlist [NonTrunkDefaultRevisions]
if {![llength $revlist]} return
AdjustNonTrunkDefaultBranch $revlist
CheckLODs
return
}
proc NonTrunkDefaultRevisions {} {
# From cvs2svn the following explanation (with modifications
# for our algorithm):
# Determine whether there are any non-trunk default branch
# revisions.
# If a non-trunk default branch is determined to have existed,
# return a list of objects for all revisions that were once
# non-trunk default revisions, in dependency order (i.e. root
# first).
# There are two cases to handle:
# One case is simple. The RCS file lists a default branch
# explicitly in its header, such as '1.1.1'. In this case, we
# know that every revision on the vendor branch is to be
# treated as head of trunk at that point in time.
# But there's also a degenerate case. The RCS file does not
# currently have a default branch, yet we can deduce that for
# some period in the past it probably *did* have one. For
# example, the file has vendor revisions 1.1.1.1 -> 1.1.1.96,
# all of which are dated before 1.2, and then it has 1.1.1.97
# -> 1.1.1.100 dated after 1.2. In this case, we should
# record 1.1.1.96 as the last vendor revision to have been the
# head of the default branch.
upvar 1 myprincipal myprincipal myroot myroot mybranches mybranches myimported myimported
if {$myprincipal ne ""} {
# There is still a default branch; that means that all
# revisions on that branch get marked.
log write 5 file "Found explicitly marked NTDB"
set rnext [$myroot child]
if {$rnext ne ""} {
trouble fatal "File with default branch $myprincipal also has revision [$rnext revnr]"
return
}
set rev [$mybranches($myprincipal) child]
set res {}
while {$rev ne ""} {
lappend res $rev
set rev [$rev child]
}
return $res
} elseif {$myimported} {
# No default branch, but the file appears to have been
# imported. So our educated guess is that all revisions
# on the '1.1.1' branch with timestamps prior to the
# timestamp of '1.2' were non-trunk default branch
# revisions.
# This really only processes standard '1.1.1.*'-style
# vendor revisions. One could conceivably have a file
# whose default branch is 1.1.3 or whatever, or was that
# at some point in time, with vendor revisions 1.1.3.1,
# 1.1.3.2, etc. But with the default branch gone now,
# we'd have no basis for assuming that the non-standard
# vendor branch had ever been the default branch anyway.
# Note that we rely on comparisons between the timestamps
# of the revisions on the vendor branch and that of
# revision 1.2, even though the timestamps might be
# incorrect due to clock skew. We could do a slightly
# better job if we used the changeset timestamps, as it is
# possible that the dependencies that went into
# determining those timestamps are more accurate. But
# that would require an extra pass or two.
if {![info exists mybranches(1.1.1)]} { return {} }
log write 5 file "Deduced existence of NTDB"
set rev [$mybranches(1.1.1) child]
set res {}
set stop [$myroot child]
if {$stop eq ""} {
# Get everything on the branch
while {$rev ne ""} {
lappend res $rev
set rev [$rev child]
}
} else {
# Collect everything on the branch which seems to have
# been committed before the first primary child of the
# root revision.
set stopdate [$stop date]
while {$rev ne ""} {
if {[$rev date] >= $stopdate} break
lappend res $rev
set rev [$rev child]
}
}
return $res
} else {
return {}
}
}
proc AdjustNonTrunkDefaultBranch {revlist} {
upvar 1 myroot myroot myimported myimported myroots myroots myrev myrev mybranches mybranches
set stop [$myroot child] ;# rev '1.2'
log write 5 file "Adjusting NTDB containing [nsp [llength $revlist] revision]"
# From cvs2svn the following explanation (with modifications
# for our algorithm):
# Adjust the non-trunk default branch revisions found in the
# 'revlist'.
# 'myimported' is a boolean flag indicating whether this file
# appears to have been imported, which also means that
# revision 1.1 has a generated log message that need not be
# preserved. 'revlist' is a list of object references for the
# revisions that have been determined to be non-trunk default
# branch revisions.
# Note that the first revision on the default branch is
# handled strangely by CVS. If a file is imported (as opposed
# to being added), CVS creates a 1.1 revision, then creates a
# vendor branch 1.1.1 based on 1.1, then creates a 1.1.1.1
# revision that is identical to the 1.1 revision (i.e., its
# deltatext is empty). The log message that the user typed
# when importing is stored with the 1.1.1.1 revision. The 1.1
# revision always contains a standard, generated log message,
# 'Initial revision\n'.
# When we detect a straightforward import like this, we want
# to handle it by deleting the 1.1 revision (which doesn't
# contain any useful information) and making 1.1.1.1 into an
# independent root in the file's dependency tree. In SVN,
# 1.1.1.1 will be added directly to the vendor branch with its
# initial content. Then in a special 'post-commit', the
# 1.1.1.1 revision is copied back to trunk.
# If the user imports again to the same vendor branch, then CVS
# creates revisions 1.1.1.2, 1.1.1.3, etc. on the vendor branch,
# *without* counterparts in trunk (even though these revisions
# effectively play the role of trunk revisions). So after we add
# such revisions to the vendor branch, we also copy them back to
# trunk in post-commits.
# We mark the revisions found in 'revlist' as default branch
# revisions. Also, if the root revision has a primary child
# we set that revision to depend on the last non-trunk default
# branch revision and possibly adjust its type accordingly.
set first [lindex $revlist 0]
log write 6 file "<[$first revnr]> [expr {$myimported ? "imported" : "not imported"}], [$first operation], [expr {[$first hastext] ? "has text" : "no text"}]"
if {$myimported &&
[$first revnr] eq "1.1.1.1" &&
[$first operation] eq "change" &&
![$first hastext]} {
set rev11 [$first parent] ; # Assert: Should be myroot.
log write 3 file "Removing irrelevant revision [$rev11 revnr]"
# Cut out the old myroot revision.
ldelete myroots $rev11 ; # Not a root any longer.
unset myrev([$rev11 revnr])
$first cutfromparent ; # Sever revision from parent revision.
if {$stop ne ""} {
$stop cutfromparent
lappend myroots $stop ; # New root, after vendor branch
}
# Cut out the vendor branch symbol
set vendor [$first parentbranch]
if {$vendor eq ""} { trouble internal "First NTDB revision has no branch" }
if {[$vendor parent] eq $rev11} {
unset mybranches([$vendor branchnr])
$rev11 removebranch $vendor
$rev11 removechildonbranch $first
$first cutfromparentbranch
lappend myroots $first
}
# Change the type of first (typically from Change to Add):
$first retype add
# Move any tags and branches from the old to the new root.
$rev11 movesymbolsto $first
}
# Mark all the special revisions as such
foreach rev $revlist {
log write 3 file "Revision on default branch: [$rev revnr]"
$rev isondefaultbranch
}
if {$stop ne ""} {
# Revision 1.2 logically follows the imported revisions,
# not 1.1. Accordingly, connect it to the last NTDBR and
# possibly change its type.
set last [lindex $revlist end]
$stop setdefaultbranchparent $last ; # Retypes the revision too.
$last setdefaultbranchchild $stop
}
return
}
proc CheckLODs {} {
upvar 1 mybranches mybranches mytags mytags
foreach {_ branch} [array get mybranches] { $branch checklod }
foreach {_ taglist} [array get mytags] {
foreach tag $taglist { $tag checklod }
}
return
}
proc RemoveIrrelevantDeletions {} {
}
proc RemoveInitialBranchDeletions {} {
}
proc ExcludeNonTrunkInformation {} {
}
# # ## ### ##### ######## #############
## 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
# # ## ### ##### ######## #############
}
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
# namespace import ::vc::fossil::import::cvs::file::sym
namespace import ::vc::tools::misc::*
namespace import ::vc::tools::trouble
namespace import ::vc::tools::log
}
}
# # ## ### ##### ######## ############# #####################
## Ready
package provide vc::fossil::import::cvs::file 1.0
return
|