Check-in [38b967dcf5]
Not logged in
Overview

SHA1 Hash:38b967dcf5613a918fcddcde269df452ce378752
Date: 2007-11-17 00:29:42
User: drh
Comment:Merge aku's CVS import changes into the main line. Fix a small bug in diff.c.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Added art/CollRev1.dia version [186c7bb7a8]

@@ -1,1 +1,793 @@
-
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true">
+    <dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O0">
+          <dia:attribute name="obj_pos">
+            <dia:point val="15,13"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="14.95,12.95;20.05,15.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="15,13"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="5"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O1">
+          <dia:attribute name="obj_pos">
+            <dia:point val="16.2733,14.064"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="16.2733,13.5215;18.7096,14.4615"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Revision#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="16.2733,14.064"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O2">
+          <dia:attribute name="obj_pos">
+            <dia:point val="15,7"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="14.95,6.95;20.05,9.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="15,7"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="5"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O3">
+          <dia:attribute name="obj_pos">
+            <dia:point val="17.0039,8.06397"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="17.0039,7.52147;17.979,8.46147"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#File#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="17.0039,8.06397"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O4">
+          <dia:attribute name="obj_pos">
+            <dia:point val="15,1"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="14.95,0.95;20.05,3.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="15,1"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="5"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O5">
+          <dia:attribute name="obj_pos">
+            <dia:point val="16.4942,2.06397"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="16.4942,1.52147;18.4887,2.46147"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Project#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="16.4942,2.06397"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O6">
+        <dia:attribute name="obj_pos">
+          <dia:point val="17.5,13"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.45,8.92929;17.55,13"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="17.5,13"/>
+          <dia:point val="17.5,13"/>
+          <dia:point val="17.5,9"/>
+          <dia:point val="17.5,9"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+          <dia:enum val="0"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O7">
+        <dia:attribute name="obj_pos">
+          <dia:point val="17.5,7"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.45,2.92929;17.55,7"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="17.5,7"/>
+          <dia:point val="17.5,7"/>
+          <dia:point val="17.5,3"/>
+          <dia:point val="17.5,3"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+          <dia:enum val="0"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O8">
+          <dia:attribute name="obj_pos">
+            <dia:point val="15,19"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="14.95,18.95;20.05,21.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="15,19"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="5"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O9">
+          <dia:attribute name="obj_pos">
+            <dia:point val="16.7775,20.064"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="16.7775,19.5215;18.2225,20.4615"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Meta#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="16.7775,20.064"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - PolyLine" version="0" id="O10">
+        <dia:attribute name="obj_pos">
+          <dia:point val="15,20"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.5,14.95;15.05,20.05"/>
+        </dia:attribute>
+        <dia:attribute name="poly_points">
+          <dia:point val="15,20"/>
+          <dia:point val="8,20"/>
+          <dia:point val="8,15"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O11">
+        <dia:attribute name="obj_pos">
+          <dia:point val="15,14"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="10.95,13.5;15.05,14.5"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="15,14"/>
+          <dia:point val="11,14"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O12">
+        <dia:attribute name="obj_pos">
+          <dia:point val="20,20"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="19.95,1.5;27.05,20.05"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="20,20"/>
+          <dia:point val="27,20"/>
+          <dia:point val="27,2"/>
+          <dia:point val="20,2"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+          <dia:enum val="0"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O13">
+        <dia:attribute name="obj_pos">
+          <dia:point val="17.5,15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17,14.95;18,19.05"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="17.5,15"/>
+          <dia:point val="17.5,19"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - PolyLine" version="0" id="O14">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,13"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.95,1.5;15.05,13.05"/>
+        </dia:attribute>
+        <dia:attribute name="poly_points">
+          <dia:point val="8,13"/>
+          <dia:point val="8,2"/>
+          <dia:point val="15,2"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O15">
+          <dia:attribute name="obj_pos">
+            <dia:point val="5,13"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="4.95,12.95;11.05,15.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="5,13"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O16">
+          <dia:attribute name="obj_pos">
+            <dia:point val="6.91375,14.0725"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="6.91375,13.53;9.08625,14.47"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Symbol#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="6.91375,14.0725"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Text" version="1" id="O17">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18,12"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18,11.4575;21.06,12.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#belongs to#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="18,12"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O18">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18,6"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18,5.4575;21.06,6.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#belongs to#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="18,6"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O19">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18,16"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18,15.4575;19.03,16.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#has#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="18,16"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O20">
+        <dia:attribute name="obj_pos">
+          <dia:point val="21,20"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="21,19.4575;24.06,20.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#belongs to#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="21,20"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O21">
+        <dia:attribute name="obj_pos">
+          <dia:point val="11,20"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="11,19.4575;14.06,20.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#belongs to#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="11,20"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O22">
+        <dia:attribute name="obj_pos">
+          <dia:point val="12,15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="12,14.4575;15.06,15.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#belongs to#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="12,15"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O23">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9,12"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9,11.4575;12.06,12.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#belongs to#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="9,12"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O24">
+        <dia:attribute name="obj_pos">
+          <dia:point val="1,13"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="1,12.4575;10.5175,13.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#(Line of Development  /  Branch)#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="1,13"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+  </dia:layer>
+</dia:diagram>

Added art/CollRev2.dia version [59b1c331a5]

@@ -1,1 +1,576 @@
-
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true">
+    <dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O0">
+          <dia:attribute name="obj_pos">
+            <dia:point val="13,1"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.95,0.95;20.05,3.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="13,1"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="7"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O1">
+          <dia:attribute name="obj_pos">
+            <dia:point val="15.2818,2.06397"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="15.2818,1.52147;17.7182,2.46147"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Revision#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="15.2818,2.06397"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O2">
+          <dia:attribute name="obj_pos">
+            <dia:point val="13,7"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.95,6.95;20.05,9.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="13,7"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="7"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O3">
+          <dia:attribute name="obj_pos">
+            <dia:point val="14.1456,8.06397"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="14.1456,7.52147;18.8544,8.46147"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Revision' (Child)#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="14.1456,8.06397"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O4">
+          <dia:attribute name="obj_pos">
+            <dia:point val="2,2"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="1.95,1.95;9.05,4.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="2,2"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="7"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O5">
+          <dia:attribute name="obj_pos">
+            <dia:point val="5.00393,3.06397"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="5.00393,2.52147;5.97901,3.46147"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#File#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="5.00393,3.06397"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Line" version="0" id="O6">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13,8"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.83023,6.5028;13.0606,8.06063"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="13,8"/>
+          <dia:point val="9,7"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O7">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13,2"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.83023,1.93937;13.0606,3.4972"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="13,2"/>
+          <dia:point val="9,3"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O8">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13,2"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.57833,1.92972;13.0703,7.35139"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="13,2"/>
+          <dia:point val="9,7"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O9">
+        <dia:attribute name="obj_pos">
+          <dia:point val="13,8"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.57833,2.64861;13.0703,8.07028"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="13,8"/>
+          <dia:point val="9,3"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O10">
+          <dia:attribute name="obj_pos">
+            <dia:point val="2,6"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="1.95,5.95;9.05,8.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="2,6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="7"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O11">
+          <dia:attribute name="obj_pos">
+            <dia:point val="3.53147,6.6725"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="3.53147,6.13;7.45147,7.87"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Line of
+Development#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="3.53147,6.6725"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Text" version="1" id="O12">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18,6"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18,5.4575;20.0947,6.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string># parent#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="18,6"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O13">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18,7"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.5,2.95;18.5,7.05"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="18,7"/>
+          <dia:point val="18,3"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Text" version="1" id="O14">
+          <dia:attribute name="obj_pos">
+            <dia:point val="22,7"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="22,6.4575;28.4122,8.1975"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#branch parent symbol
+       (NULL)#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="22,7"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Line" version="0" id="O15">
+          <dia:attribute name="obj_pos">
+            <dia:point val="20,8"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="19.95,7.5;22.05,8.5"/>
+          </dia:attribute>
+          <dia:attribute name="conn_endpoints">
+            <dia:point val="20,8"/>
+            <dia:point val="22,8"/>
+          </dia:attribute>
+          <dia:attribute name="numcp">
+            <dia:int val="1"/>
+          </dia:attribute>
+          <dia:attribute name="end_arrow">
+            <dia:enum val="22"/>
+          </dia:attribute>
+          <dia:attribute name="end_arrow_length">
+            <dia:real val="0.5"/>
+          </dia:attribute>
+          <dia:attribute name="end_arrow_width">
+            <dia:real val="0.5"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Polygon" version="0" id="O16">
+          <dia:attribute name="obj_pos">
+            <dia:point val="23,7"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="21.9293,6.87929;23.05,9.12071"/>
+          </dia:attribute>
+          <dia:attribute name="poly_points">
+            <dia:point val="23,7"/>
+            <dia:point val="22,8"/>
+            <dia:point val="23,9"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Text" version="1" id="O17">
+        <dia:attribute name="obj_pos">
+          <dia:point val="15,4"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="15,3.4575;16.57,4.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string># child#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="15,4"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O18">
+        <dia:attribute name="obj_pos">
+          <dia:point val="15,3"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="14.5,2.95;15.5,7.05"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="15,3"/>
+          <dia:point val="15,7"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+  </dia:layer>
+</dia:diagram>

Added art/CollRev3.dia version [d14a7b35e0]

@@ -1,1 +1,980 @@
-
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true">
+    <dia:group>
+      <dia:object type="Standard - Text" version="1" id="O0">
+        <dia:attribute name="obj_pos">
+          <dia:point val="7,15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7,14.6;7,15.8"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>##</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="7,15"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O1">
+          <dia:attribute name="obj_pos">
+            <dia:point val="13,1"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.95,0.95;19.05,3.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="13,1"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O2">
+          <dia:attribute name="obj_pos">
+            <dia:point val="14.04,1.66397"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="14.04,1.12147;17.96,2.86147"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Line of
+Development#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="14.04,1.66397"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O3">
+          <dia:attribute name="obj_pos">
+            <dia:point val="3,1"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="2.95,0.95;9.05,3.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="3,1"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O4">
+          <dia:attribute name="obj_pos">
+            <dia:point val="4.04,1.66397"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="4.04,1.12147;7.96,2.86147"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Line' of
+Development#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="4.04,1.66397"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O5">
+          <dia:attribute name="obj_pos">
+            <dia:point val="3,13"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="2.95,12.95;9.05,15.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="3,13"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O6">
+          <dia:attribute name="obj_pos">
+            <dia:point val="3.94,13.664"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="3.94,13.1215;8.06,14.8615"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Revision''
+(Branch Start)#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="3.94,13.664"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O7">
+          <dia:attribute name="obj_pos">
+            <dia:point val="13,7"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.95,6.95;19.05,9.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="13,7"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O8">
+          <dia:attribute name="obj_pos">
+            <dia:point val="14.7733,8.0725"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="14.7733,7.53;17.2096,8.47"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Revision#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="14.7733,8.0725"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O9">
+          <dia:attribute name="obj_pos">
+            <dia:point val="23,13"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="22.95,12.95;29.05,15.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="23,13"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O10">
+          <dia:attribute name="obj_pos">
+            <dia:point val="25.5039,14.0725"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="25.5039,13.53;26.479,14.47"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#File#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="25.5039,14.0725"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O11">
+          <dia:attribute name="obj_pos">
+            <dia:point val="13,13"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.95,12.95;19.05,15.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="13,13"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O12">
+          <dia:attribute name="obj_pos">
+            <dia:point val="14.6933,13.6725"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="14.6933,13.13;17.2896,14.87"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Revision'
+(Child)#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="14.6933,13.6725"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Line" version="0" id="O13">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19,14"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18.95,13.5;23.05,14.5"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="19,14"/>
+          <dia:point val="23,14"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O14">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6,13"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="5.5,2.95;6.5,13.05"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="6,13"/>
+          <dia:point val="6,3"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O15">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18,9"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="17.5,8.95;18.5,13.05"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="18,9"/>
+          <dia:point val="18,13"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O16">
+        <dia:attribute name="obj_pos">
+          <dia:point val="16,7"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="15.5,2.95;16.5,7.05"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="16,7"/>
+          <dia:point val="16,3"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O17">
+        <dia:attribute name="obj_pos">
+          <dia:point val="14,13"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="13.5,8.95;14.5,13.05"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="14,13"/>
+          <dia:point val="14,9"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O18">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9,14"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.95,7.5;13.05,14.05"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="9,14"/>
+          <dia:point val="11,14"/>
+          <dia:point val="11,8"/>
+          <dia:point val="13,8"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+          <dia:enum val="0"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O19">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6,15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="5.95,14.95;26.5,17.05"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="6,15"/>
+          <dia:point val="6,17"/>
+          <dia:point val="26,17"/>
+          <dia:point val="26,15"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="1"/>
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O20">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19,8"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18.95,7.95;26.5,13.05"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="19,8"/>
+          <dia:point val="26,8"/>
+          <dia:point val="26,13"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O21">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6,12"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="6,11.4575;8.2125,13.1975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string># branch
+ parent#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="6,12"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O22">
+        <dia:attribute name="obj_pos">
+          <dia:point val="14,12"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="14,11.4575;16.0947,12.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string># parent#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="14,12"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O23">
+        <dia:attribute name="obj_pos">
+          <dia:point val="18,10"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="18,9.4575;19.57,10.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string># child#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="18,10"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O24">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19,7"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="19,6.52875;22.245,8.39875"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#
+ belongs to#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="19,7"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O25">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6,15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="6,14.4575;9.245,16.1975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#
+ belongs to#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="6,15"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O26">
+        <dia:attribute name="obj_pos">
+          <dia:point val="19,13"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="19,12.5288;22.245,14.3987"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#
+ belongs to#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="19,13"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O27">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9,14"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9,13.5288;11.0947,15.3987"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#
+ parent#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="9,14"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - ZigZagLine" version="1" id="O28">
+        <dia:attribute name="obj_pos">
+          <dia:point val="3,14"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="0.95,1.5;3.05,14.05"/>
+        </dia:attribute>
+        <dia:attribute name="orth_points">
+          <dia:point val="3,14"/>
+          <dia:point val="1,14"/>
+          <dia:point val="1,2"/>
+          <dia:point val="3,2"/>
+        </dia:attribute>
+        <dia:attribute name="orth_orient">
+          <dia:enum val="0"/>
+          <dia:enum val="1"/>
+          <dia:enum val="0"/>
+        </dia:attribute>
+        <dia:attribute name="autorouting">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O29">
+        <dia:attribute name="obj_pos">
+          <dia:point val="1,12"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="1,11.4575;4.245,12.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string># belongs to#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="1,12"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O30">
+        <dia:attribute name="obj_pos">
+          <dia:point val="10,2.12153"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="10,1.10403;12.205,2.86403"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#=/=#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="1.5"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="10,2.12153"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+  </dia:layer>
+</dia:diagram>

Added art/CollRev4.dia version [be6e84c213]

@@ -1,1 +1,640 @@
-
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+  <dia:diagramdata>
+    <dia:attribute name="background">
+      <dia:color val="#ffffff"/>
+    </dia:attribute>
+    <dia:attribute name="pagebreak">
+      <dia:color val="#000099"/>
+    </dia:attribute>
+    <dia:attribute name="paper">
+      <dia:composite type="paper">
+        <dia:attribute name="name">
+          <dia:string>#Letter#</dia:string>
+        </dia:attribute>
+        <dia:attribute name="tmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="bmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="lmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="rmargin">
+          <dia:real val="2.5399999618530273"/>
+        </dia:attribute>
+        <dia:attribute name="is_portrait">
+          <dia:boolean val="true"/>
+        </dia:attribute>
+        <dia:attribute name="scaling">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="fitto">
+          <dia:boolean val="false"/>
+        </dia:attribute>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="grid">
+      <dia:composite type="grid">
+        <dia:attribute name="width_x">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="width_y">
+          <dia:real val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_x">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="visible_y">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:composite type="color"/>
+      </dia:composite>
+    </dia:attribute>
+    <dia:attribute name="color">
+      <dia:color val="#d8e5e5"/>
+    </dia:attribute>
+    <dia:attribute name="guides">
+      <dia:composite type="guides">
+        <dia:attribute name="hguides"/>
+        <dia:attribute name="vguides"/>
+      </dia:composite>
+    </dia:attribute>
+  </dia:diagramdata>
+  <dia:layer name="Background" visible="true">
+    <dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O0">
+          <dia:attribute name="obj_pos">
+            <dia:point val="3,8"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="2.95,7.95;9.05,10.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="3,8"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O1">
+          <dia:attribute name="obj_pos">
+            <dia:point val="4.11647,8.66397"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="4.11647,8.12147;7.86647,9.86147"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Revision'
+(Child NTDB)#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="4.11647,8.66397"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O2">
+          <dia:attribute name="obj_pos">
+            <dia:point val="5,1"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="4.95,0.95;11.05,3.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="5,1"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O3">
+          <dia:attribute name="obj_pos">
+            <dia:point val="6.78184,1.6725"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="6.78184,1.13;9.21816,2.87"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Revision
+(NTDB)#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="6.78184,1.6725"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O4">
+          <dia:attribute name="obj_pos">
+            <dia:point val="1,15"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="0.95,14.95;7.05,17.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="1,15"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O5">
+          <dia:attribute name="obj_pos">
+            <dia:point val="2.11647,15.664"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="2.11647,15.1215;5.86647,16.8615"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Revision''
+(Child NTDB)#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="2.11647,15.664"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:group>
+        <dia:object type="Standard - Box" version="0" id="O6">
+          <dia:attribute name="obj_pos">
+            <dia:point val="13,13"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="12.95,12.95;19.05,15.05"/>
+          </dia:attribute>
+          <dia:attribute name="elem_corner">
+            <dia:point val="13,13"/>
+          </dia:attribute>
+          <dia:attribute name="elem_width">
+            <dia:real val="6"/>
+          </dia:attribute>
+          <dia:attribute name="elem_height">
+            <dia:real val="2"/>
+          </dia:attribute>
+          <dia:attribute name="show_background">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+        </dia:object>
+        <dia:object type="Standard - Text" version="1" id="O7">
+          <dia:attribute name="obj_pos">
+            <dia:point val="14.2915,13.664"/>
+          </dia:attribute>
+          <dia:attribute name="obj_bb">
+            <dia:rectangle val="14.2915,13.1215;17.6915,14.8615"/>
+          </dia:attribute>
+          <dia:attribute name="text">
+            <dia:composite type="text">
+              <dia:attribute name="string">
+                <dia:string>#Revision""
+(non-NTDB)#</dia:string>
+              </dia:attribute>
+              <dia:attribute name="font">
+                <dia:font family="sans" style="0" name="Helvetica"/>
+              </dia:attribute>
+              <dia:attribute name="height">
+                <dia:real val="0.80000000000000004"/>
+              </dia:attribute>
+              <dia:attribute name="pos">
+                <dia:point val="14.2915,13.664"/>
+              </dia:attribute>
+              <dia:attribute name="color">
+                <dia:color val="#000000"/>
+              </dia:attribute>
+              <dia:attribute name="alignment">
+                <dia:enum val="0"/>
+              </dia:attribute>
+            </dia:composite>
+          </dia:attribute>
+          <dia:attribute name="valign">
+            <dia:enum val="3"/>
+          </dia:attribute>
+        </dia:object>
+      </dia:group>
+      <dia:object type="Standard - Line" version="0" id="O8">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,8"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="7.93501,2.76788;10.4828,8.06499"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="8,8"/>
+          <dia:point val="10,3"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O9">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6,3"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="3.51719,2.93501;6.06499,8.23212"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="6,3"/>
+          <dia:point val="4,8"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O10">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,10"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="1.51719,9.93501;4.06499,15.2321"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="4,10"/>
+          <dia:point val="2,15"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O11">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6,15"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="5.93501,9.76788;8.48281,15.065"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="6,15"/>
+          <dia:point val="8,10"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O12">
+        <dia:attribute name="obj_pos">
+          <dia:point val="16,13"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8.70852,8.54107;16.0682,13.0682"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="16,13"/>
+          <dia:point val="9,9"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Line" version="0" id="O13">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6,10"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="5.93178,9.93178;13.2915,14.4589"/>
+        </dia:attribute>
+        <dia:attribute name="conn_endpoints">
+          <dia:point val="6,10"/>
+          <dia:point val="13,14"/>
+        </dia:attribute>
+        <dia:attribute name="numcp">
+          <dia:int val="1"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow">
+          <dia:enum val="22"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_length">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+        <dia:attribute name="end_arrow_width">
+          <dia:real val="0.5"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O14">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6,14"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="6,13.5288;8.27969,15.3987"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#
+  parent#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="6,14"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O15">
+        <dia:attribute name="obj_pos">
+          <dia:point val="6,3"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="6,2.4575;7.385,4.1975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#
+child#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="6,3"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O16">
+        <dia:attribute name="obj_pos">
+          <dia:point val="4,10"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="4,9.4575;5.385,11.1975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#
+child#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="4,10"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O17">
+        <dia:attribute name="obj_pos">
+          <dia:point val="15,12"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="15,11.5288;18.3897,13.3987"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#
+    dbparent#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="15,12"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O18">
+        <dia:attribute name="obj_pos">
+          <dia:point val="9,12"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="9,11.4575;11.865,12.3975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#    dbchild#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="9,12"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+      <dia:object type="Standard - Text" version="1" id="O19">
+        <dia:attribute name="obj_pos">
+          <dia:point val="8,7"/>
+        </dia:attribute>
+        <dia:attribute name="obj_bb">
+          <dia:rectangle val="8,6.4575;10.2797,8.1975"/>
+        </dia:attribute>
+        <dia:attribute name="text">
+          <dia:composite type="text">
+            <dia:attribute name="string">
+              <dia:string>#
+  parent#</dia:string>
+            </dia:attribute>
+            <dia:attribute name="font">
+              <dia:font family="sans" style="0" name="Helvetica"/>
+            </dia:attribute>
+            <dia:attribute name="height">
+              <dia:real val="0.80000000000000004"/>
+            </dia:attribute>
+            <dia:attribute name="pos">
+              <dia:point val="8,7"/>
+            </dia:attribute>
+            <dia:attribute name="color">
+              <dia:color val="#000000"/>
+            </dia:attribute>
+            <dia:attribute name="alignment">
+              <dia:enum val="0"/>
+            </dia:attribute>
+          </dia:composite>
+        </dia:attribute>
+        <dia:attribute name="valign">
+          <dia:enum val="3"/>
+        </dia:attribute>
+      </dia:object>
+    </dia:group>
+  </dia:layer>
+</dia:diagram>

Modified src/diff.c from [017b424914] to [fe83b4caec].

@@ -23,11 +23,11 @@
 **
 ** This file contains code used to compute a "diff" between two
 ** text files.
 */
 #include "config.h"
-#include "diff2.h"
+#include "diff.h"
 #include <assert.h>
 
 
 #if 0
 #define DEBUG(X) X

Added tools/cvs2fossil/cvs2fossil version [df73a69477]

@@ -1,1 +1,32 @@
+#!/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
+# # ## ### ##### ######## ############# #####################
+
+## Command line application wrapped around the import packages.
+
+# # ## ### ##### ######## ############# #####################
+## 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 vc::fossil::import::cvs ; # Main functionality.
+
+# # ## ### ##### ######## ############# #####################
+## Execution
+
+vc::fossil::import::cvs run $argv
+exit 0
 
+# # ## ### ##### ######## ############# #####################

Added tools/cvs2fossil/doc/LICENSE version [aede671429]

@@ -1,1 +1,19 @@
+This code is under the same license as fossil itself.
+
+- - -- --- ----- ---------
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public
+License version 2 as published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public
+License along with this library; if not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA  02111-1307, USA.
 
+- - -- --- ----- ---------

Added tools/cvs2fossil/doc/README version [6d655c3f82]

@@ -1,1 +1,7 @@
 
+[Acknowledge the work done by the creators of and submitters to the
+cvs2svn project/application. Needed their documentation, notes, and
+code as guide for this implementation.]
+
+[Determine if their license allows me to copy their notes here for
+reference.]

Added tools/cvs2fossil/lib/c2f_cyclebreaker.tcl version [41093323e5]

@@ -1,1 +1,291 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## This file provides a helper package for the passes 6 and 7 which
+## contains the common code of the cycle breaking algorithm.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                                   ; # Required runtime.
+package require snit                                      ; # OO system.
+package require struct::graph                             ; # Graph handling.
+package require struct::list                              ; # Higher order list operations.
+package require vc::tools::log                            ; # User feedback.
+package require vc::tools::misc                           ; # Text formatting.
+package require vc::fossil::import::cvs::project::rev     ; # Project level changesets
+package require vc::fossil::import::cvs::project::revlink ; # Cycle links.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::cyclebreaker {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod run {changesets {savecmd {}}} {
+	::variable save $savecmd
+	::variable at   0
+
+	# We create a graph of the revision changesets, using the file
+	# level dependencies to construct a first approximation of the
+	# dependencies at the project level. Then we look for cycles
+	# in that graph and break them.
+
+	# 1. Create nodes for all relevant changesets and a mapping
+	#    from the revisions to their changesets/nodes.
+
+	log write 3 cyclebreaker "Creating changeset graph, filling with nodes"
+	log write 3 cyclebreaker "Adding [nsp [llength $changesets] node]"
+
+	set dg [struct::graph dg]
+
+	foreach cset $changesets {
+	    dg node insert $cset
+	    dg node set    $cset timerange [$cset timerange]
+	}
+
+	# 2. Find for all relevant changeset their revisions and their
+	#    dependencies. Map the latter back to changesets and
+	#    construct the corresponding arcs.
+
+	log write 3 cyclebreaker {Setting up node dependencies}
+
+	foreach cset $changesets {
+	    foreach succ [$cset successors] {
+		# Changesets may have dependencies outside of the
+		# chosen set. These are ignored
+		if {![dg node exists $succ]} continue
+		dg arc insert $cset $succ
+	    }
+	}
+
+	# 3. Lastly we iterate the graph topologically. We mark off
+	#    the nodes which have no predecessors, in order from
+	#    oldest to youngest, saving and removing dependencies. If
+	#    we find no nodes without predecessors we have a cycle,
+	#    and work on breaking it.
+
+	log write 3 cyclebreaker {Now sorting the changesets, breaking cycles}
+
+	InitializeCandidates $dg
+	while {1} {
+	    while {[WithoutPredecessor $dg n]} {
+		SaveAndRemove $dg $n
+	    }
+	    if {![llength [dg nodes]]} break
+	    BreakCycle $dg [FindCycle $dg]
+	    InitializeCandidates $dg
+	}
+
+	dg destroy
+
+	log write 3 cyclebreaker Done.
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    # Instead of searching the whole graph for the degree-0 nodes in
+    # each iteration we compute the list once to start, and then only
+    # update it incrementally based on the outgoing neighbours of the
+    # node chosen for commit.
+
+    proc InitializeCandidates {dg} {
+	# bottom = list (list (node, range min, range max))
+	::variable bottom
+	foreach n [$dg nodes] {
+	    if {[$dg node degree -in $n]} continue
+	    lappend bottom [linsert [$dg node get $n timerange] 0 $n]
+	}
+	set bottom [lsort -index 1 -integer [lsort -index 2 -integer $bottom]]
+	return
+    }
+
+    proc WithoutPredecessor {dg nv} {
+	::variable bottom
+
+	upvar 1 $nv n
+	if {![llength $bottom]} { return 0 }
+
+	set n [lindex [lindex $bottom 0] 0]
+	set bottom [lrange $bottom 1 end]
+	set changed 0
+
+	# Update list of nodes without predecessor, based on the
+	# outgoing neighbours of the chosen node. This should be
+	# faster than iterating of the whole set of nodes, finding all
+	# without predecessors, sorting them by time, etc. pp.
+	foreach out [$dg nodes -out $n] {
+	    if {[$dg node degree -in $out] > 1} continue
+	    # Degree-1 neighbour, will have no predecessors after the
+	    # removal of n. Put on the list.
+	    lappend bottom [linsert [$dg node get $out timerange] 0 $out]
+	    set changed 1
+	}
+	if {$changed} {
+	    set bottom [lsort -index 1 -integer [lsort -index 2 -integer $bottom]]
+	}
+
+	# We do not delete the node immediately, to allow the Save
+	# procedure to save the dependencies as well (encoded in the
+	# arcs).
+	return 1
+    }
+
+    proc SaveAndRemove {dg n} {
+	::variable at
+	::variable save
+
+	# Give the user of the cycle breaker the opportunity to work
+	# with the changeset before it is removed from the graph.
+
+	if {[llength $save]} {
+	    uplevel #0 [linsert $save end $at $n]
+	}
+
+	incr at
+	$dg node delete $n
+	return
+    }
+
+    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
+	# second time. That node closes the cycle and the beginning is
+	# its first occurence. Note that we can choose an arbitrary
+	# predecessor of each node as well, we do not have to search.
+
+	# We record for each node the index of the first appearance in
+	# the path, making it easy at the end to cut the cycle from
+	# it.
+
+	# Choose arbitrary node to start our search at.
+	set start [lindex [$dg nodes] 0]
+
+	# Initialize state, path of seen nodes, and when seen.
+	set       path {}
+	array set seen {}
+
+	while {1} {
+	    # Stop searching when we have seen the current node
+	    # already, the circle has been closed.
+	    if {[info exists seen($start)]} break
+	    lappend path $start
+	    set seen($start) [expr {[llength $path]-1}]
+	    # 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.
+
+	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] \
+	    cset [lrange $cycle 1 end-1] \
+	    next [lrange $cycle 2 end] {
+
+		# Each triple PREV -> CSET -> NEXT of changesets, a
+		# 'link' in the cycle, is analysed and the best
+		# location where to at least weaken the cycle is
+		# chosen for further processing.
+
+		set link [project::revlink %AUTO% $prev $cset $next]
+		if {$bestlink eq ""} {
+		    set bestlink $link
+		    set bestnode $cset
+		} elseif {[$link betterthan $bestlink]} {
+		    $bestlink destroy
+		    set bestlink $link
+		    set bestnode $cset
+		} else {
+		    $link destroy
+		}
+	    }
+
+	log write 5 breakrcycle "Breaking cycle ($cprint) by splitting changeset <[$bestnode id]>"
+
+	set newcsets [$bestlink break]
+	$bestlink destroy
+
+        # At this point the old changeset (BESTNODE) is gone
+        # already. We remove it from the graph as well and then enter
+        # the fragments generated for it.
+
+        $dg node delete $bestnode
+
+	foreach cset $newcsets {
+	    $dg node insert $cset
+	    $dg node set    $cset timerange [$cset timerange]
+	}
+
+	foreach cset $newcsets {
+	    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
+	    }
+	}
+	return
+    }
+
+    typevariable at      0 ; # Counter for commit ids for the changesets.
+    typevariable bottom {} ; # List of candidate nodes for committing.
+    typevariable save   {} ; # The command to call for each processed node
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs {
+    namespace export cyclebreaker
+    namespace eval cyclebreaker {
+	namespace eval project {
+	    namespace import ::vc::fossil::import::cvs::project::rev
+	    namespace import ::vc::fossil::import::cvs::project::revlink
+	}
+	namespace import ::vc::tools::misc::*
+	namespace import ::vc::tools::log
+	log register cyclebreaker
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::cyclebreaker 1.0
+return

Added tools/cvs2fossil/lib/c2f_file.tcl version [a0a48d681e]

@@ -1,1 +1,1113 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## File, part of a project, part of a CVS repository. Multiple
+## instances are possible.
+
+# # ## ### ##### ######## ############# #####################
+## 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::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 {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    constructor {id path usrpath executable project} {
+	set myid         $id
+	set mypath       $path
+	set myusrpath    $usrpath
+	set myexecutable $executable
+	set myproject    $project
+	set mytrunk      [$myproject trunk]
+	return
+    }
+
+    method setid {id} {
+	if {$myid ne ""} { trouble internal "File '$mypath' already has an id, '$myid'" }
+	set myid $id
+	return
+    }
+
+    method id      {} { return $myid }
+    method path    {} { return $mypath }
+    method usrpath {} { return $myusrpath }
+    method project {} { return $myproject }
+
+    delegate method commitmessageof to myproject
+
+    # # ## ### ##### ######## #############
+    ## Methods required for the class to be a sink of the rcs parser
+
+    #method begin {} {puts begin}
+    #method sethead {h} {puts head=$h}
+    #method setprincipalbranch {b} {puts pb=$b}
+    #method deftag {s r} {puts $s=$r}
+    #method setcomment {c} {puts comment=$c}
+    #method admindone {} {puts admindone}
+    #method def {rev date author state next branches} {puts "def $rev $date $author $state $next $branches"}
+    #method defdone {} {puts def-done}
+    #method setdesc {d} {puts desc=$d}
+    #method extend {rev commitmsg deltarange} {puts "extend $commitmsg $deltarange"}
+    #method done {} {puts done}
+
+    # # ## ### ##### ######## #############
+    ## Persistence (pass II)
+
+    method persist {} {
+	# First collect the reachable revisions and symbols, then
+	# assign id's to all. They are sorted so that we will have ids
+	# which sort in order of creation. Then we can save them. This
+	# is done bottom up. Revisions, then symbols. __NOTE__ This
+	# works only because sqlite is not checking foreign key
+	# references during insert. This allows to have dangling
+	# references which are fixed later. The longest dangling
+	# references are for the project level symbols, these we do
+	# not save here, but at the end of the pass. What we need are
+	# the ids, hence the two phases.
+
+	struct::list assign [$self Active] revisions symbols
+	foreach rev $revisions { $rev defid }
+	foreach sym $symbols   { $sym defid }
+
+	state transaction {
+	    foreach rev $revisions { $rev persist }
+	    foreach sym $symbols   { $sym persist }
+	}
+	return
+    }
+
+    method drop {} {
+	foreach {_ rev}    [array get myrev]      { $rev destroy }
+	foreach {_ branch} [array get mybranches] { $branch destroy }
+	foreach {_ taglist} [array get mytags] {
+	    foreach tag $taglist { $tag destroy }
+	}
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Implement the sink
+
+    method begin {} {#ignore}
+
+    method sethead {revnr} {
+	set myheadrevnr $revnr
+	return
+    }
+
+    method setprincipalbranch {branchnr} {
+	set myprincipal $branchnr
+	return
+    }
+
+    method deftag {name revnr} {
+	# FUTURE: Perform symbol transformation here.
+
+	if {[struct::set contains $mysymbols $name]} {
+	    trouble fatal "Multiple definitions of the symbol '$name' in '$mypath'"
+	    return
+	}
+
+	struct::set add mysymbols $name
+
+	if {[rev isbranchrevnr $revnr -> branchnr]} {
+	    $self AddBranch $name $branchnr
+	} else {
+	    $self AddTag $name $revnr
+	}
+	return
+    }
+
+    method setcomment {c} {# ignore}
+
+    method admindone {} {
+	# We do nothing at the boundary of admin and revision data
+    }
+
+    method def {revnr date author state next branches} {
+	$self RecordBranchCommits $branches
+
+	if {[info exists myrev($revnr)]} {
+	    trouble fatal "File $mypath contains duplicate definitions for revision $revnr."
+	    return
+	}
+
+	set myaid($revnr) [$myproject defauthor $author]
+	set myrev($revnr) [rev %AUTO% $revnr $date $state $self]
+
+	$self RecordBasicDependencies $revnr $next
+	return
+    }
+
+    method defdone {} {
+	# This is all done after the revision tree has been extracted
+	# from the file, before the commit mesages and delta texts are
+	# processed.
+
+	$self ProcessPrimaryDependencies
+	$self ProcessBranchDependencies
+	$self SortBranches
+	$self ProcessTagDependencies
+	$self DetermineTheRootRevision
+	return
+    }
+
+    method setdesc {d} {# ignore}
+
+    method extend {revnr commitmsg textrange} {
+	set cmid [$myproject defcmessage [string trim $commitmsg]]
+
+	set rev $myrev($revnr)
+
+	if {[$rev hasmeta]} {
+	    # Apparently repositories exist in which the delta data
+	    # for revision 1.1 is provided several times, at least
+	    # twice. The actual cause of this duplication is not
+	    # known. Speculation centers on RCS/CVS bugs, or from
+	    # manual edits of the repository which borked the
+	    # internals. Whatever the cause, testing showed that both
+	    # cvs and rcs use the first definition when performing a
+	    # checkout, and we follow their lead. Side notes: 'cvs
+	    # log' fails on such a file, and 'cvs rlog' prints the log
+	    # message from the first delta, ignoring the second.
+
+	    log write 1 file "In file $mypath : Duplicate delta data for revision $revnr"
+	    log write 1 file "Ignoring the duplicate"
+	    return
+	}
+
+	# Determine the line of development for the revision (project
+	# level). This gives us the branchid too, required for the
+	# meta data group the revision is in. (Note: By putting both
+	# branch/lod and project information into the group we ensure
+	# that any cross-project and cross-branch commits are
+	# separated into multiple commits, one in each of the projects
+	# and/or branches).
+
+	set lod [$self GetLOD $revnr]
+
+	$rev setmeta [$myproject defmeta [$lod id] $myaid($revnr) $cmid]
+	$rev settext $textrange
+	$rev setlod  $lod
+
+	# If this is revision 1.1, we have to determine whether the
+	# file seems to have been created through 'cvs add' instead of
+	# '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, possibly excluding
+	# non-trunk data, and collecting aggregate symbol statistics.
+
+	$self DetermineRevisionOperations
+	$self DetermineLinesOfDevelopment
+	$self HandleNonTrunkDefaultBranch
+	$self RemoveIrrelevantDeletions
+	$self RemoveInitialBranchDeletions
+
+	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.
+    variable myexecutable      0  ; # Boolean flag 'file executable'.
+    variable myproject         {} ; # Reference to the project object
+				    # the file belongs to.
+    variable myrev -array      {} ; # Maps revision number to the
+				    # associated revision object.
+    variable myrevisions       {} ; # Same as myrev, but a list,
+				    # giving us the order of
+				    # revisions.
+    variable myaid      -array {} ; # Map revision numbers to the id
+				    # of the author who committed
+				    # it. This is later aggregated
+				    # with commit message, branch name
+				    # and project id for a meta id.
+    variable myheadrevnr       {} ; # Head revision (revision number)
+    variable myprincipal       {} ; # Principal branch (branch number).
+				    # Contrary to the name this is the
+				    # default branch.
+    variable mydependencies    {} ; # Dictionary parent -> child,
+				    # records primary dependencies.
+    variable myimported        0  ; # Boolean flag. Set if and only if
+				    # rev 1.1 of the file seemingly
+				    # was imported instead of added
+				    # normally.
+    variable myroot            {} ; # Reference to the revision object
+				    # holding the root revision.  Its
+				    # number usually is '1.1'. Can be
+				    # a different number, because of
+				    # gaps created via 'cvsadmin -o'.
+    variable mybranches -array {} ; # Maps branch number to the symbol
+				    # object handling the branch.
+    variable mytags     -array {} ; # Maps revision number to the list
+				    # of symbol objects for the tags
+				    # associated with the revision.
+    variable mysymbols         {} ; # Set of the symbol names found in
+				    # this file.
+
+    variable mybranchcnt 0 ; # Counter for branches, to record their
+			     # 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 {
+		set branch [$self Rev2Branch $branchrevnr]
+	    }]} {
+		set branch [$self AddUnlabeledBranch [rev 2branchnr $branchrevnr]]
+	    }
+
+	    # Record the commit, just as revision number for
+	    # now. ProcesBranchDependencies will extend that ito a
+	    # proper object reference.
+
+	    $branch setchildrevnr $branchrevnr
+	}
+	return
+    }
+
+    method Rev2Branch {revnr} {
+	if {[rev istrunkrevnr $revnr]} {
+	    trouble internal "Expected a branch revision number"
+	}
+	return $mybranches([rev 2branchnr $revnr])
+    }
+
+    method AddUnlabeledBranch {branchnr} {
+	return [$self AddBranch unlabeled-$branchnr $branchnr]
+    }
+
+    method AddBranch {name branchnr} {
+	if {[info exists mybranches($branchnr)]} {
+	    log write 1 file "In '$mypath': Branch '$branchnr' named '[$mybranches($branchnr) name]'"
+	    log write 1 file "Cannot have second name '$name', ignoring it"
+	    return
+	}
+	set branch [sym %AUTO% branch $branchnr [$myproject getsymbol $name] $self]
+	$branch setposition [incr mybranchcnt]
+	set mybranches($branchnr) $branch
+	return $branch
+    }
+
+    method AddTag {name revnr} {
+	set tag [sym %AUTO% tag $revnr [$myproject getsymbol $name] $self]
+	lappend mytags($revnr) $tag
+	return $tag
+    }
+
+    method RecordBasicDependencies {revnr next} {
+	# Handle the revision dependencies. Record them for now, do
+	# nothing with them yet.
+
+	# On the trunk the 'next' field points to the previous
+	# revision, i.e. the _parent_ of the current one. Example:
+	# 1.6's next is 1.5 (modulo cvs admin -o).
+
+	# Contrarily on a branch the 'next' field points to the
+	# primary _child_ of the current revision. As example,
+	# 1.1.3.2's 'next' will be 1.1.3.3.
+
+	# The 'next' field actually always refers to the revision
+	# containing the delta needed to retrieve that revision.
+
+	# The dependencies needed here are the logical structure,
+	# parent/child, and not the implementation dependent delta
+	# pointers.
+
+	if {$next eq ""} return
+	#                          parent -> child
+	if {[rev istrunkrevnr $revnr]} {
+	    lappend mydependencies $next $revnr
+	} else {
+	    lappend mydependencies $revnr $next
+	}
+	return
+    }
+
+    method ProcessPrimaryDependencies {} {
+	foreach {parentrevnr childrevnr} $mydependencies {
+	    set parent $myrev($parentrevnr)
+	    set child  $myrev($childrevnr)
+	    $parent setchild $child
+	    $child setparent $parent
+	}
+	return
+    }
+
+    method ProcessBranchDependencies {} {
+	foreach {branchnr branch} [array get mybranches] {
+	    set revnr [$branch parentrevnr]
+
+	    if {![info exists myrev($revnr)]} {
+		log write 1 file "In '$mypath': The branch '[$branch name]' references"
+		log write 1 file "the bogus revision '$revnr' and will be ignored."
+		$branch destroy
+		unset mybranches($branchnr)
+	    } else {
+		set rev $myrev($revnr)
+		$rev addbranch $branch
+		$branch setparent $rev
+
+		# If revisions were committed on the branch we store a
+		# 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 haschildrev]} {
+		    set childrevnr [$branch childrevnr]
+		    set child $myrev($childrevnr)
+		    $branch setchild $child
+
+		    $child setparentbranch $branch
+		    $child setparent       $rev
+		    $rev addchildonbranch $child
+		}
+	    }
+	}
+	return
+    }
+
+    method SortBranches {} {
+	foreach {revnr rev} [array get myrev] { $rev sortbranches }
+	return
+    }
+
+    method ProcessTagDependencies {} {
+	foreach {revnr taglist} [array get mytags] {
+	    if {![info exists myrev($revnr)]} {
+		set n [llength $taglist]
+		log write 1 file "In '$mypath': The following [nsp $n tag] reference"
+		log write 1 file "the bogus revision '$revnr' and will be ignored."
+		foreach tag $taglist {
+		    log write 1 file "    [$tag name]"
+		    $tag destroy
+		}
+		unset mytags($revnr)
+	    } else {
+		set rev $myrev($revnr)
+		foreach tag $taglist {
+		    $rev addtag    $tag
+		    $tag settagrev $rev
+		}
+	    }
+	}
+	return
+    }
+
+    method DetermineTheRootRevision {} {
+	# The root is the one revision which has no parent. By
+	# checking all revisions we ensure that we can detect and
+	# report the case of multiple roots. Without that we could
+	# simply take one revision and follow the parent links to
+	# 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
+    }
+
+    method DetermineRevisionOperations {} {
+	foreach rev $myrevisions { $rev determineoperation }
+	return
+    }
+
+    method DetermineLinesOfDevelopment {} {
+	# For revisions this has been done already, in 'extend'. Now
+	# we do this for the branches and tags.
+
+	foreach {_ branch} [array get mybranches] {
+	    $branch setlod [$self GetLOD [$branch parentrevnr]]
+	}
+
+	foreach {_ taglist} [array get mytags] {
+	    foreach tag $taglist {
+		$tag setlod [$self GetLOD [$tag tagrevnr]]
+	    }
+	}
+	return
+    }
+
+    method GetLOD {revnr} {
+	if {[rev istrunkrevnr $revnr]} {
+	    return $mytrunk
+	} else {
+	    return [$self Rev2Branch $revnr]
+	}
+    }
+
+    method HandleNonTrunkDefaultBranch {} {
+	set revlist [$self NonTrunkDefaultRevisions]
+	if {![llength $revlist]} return
+
+	$self AdjustNonTrunkDefaultBranch $revlist
+	$self CheckLODs
+	return
+    }
+
+    method 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.
+
+	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 {}
+	}
+    }
+
+    # General note: In the following methods we only modify the links
+    # between revisions and symbols to restructure the revision
+    # tree. We do __not__ destroy the objects. Given the complex links
+    # GC is difficult at this level. It is much easier to drop
+    # everything when we we are done. This happens in 'drop', using
+    # the state variable 'myrev', 'mybranches', and 'mytags'. What we
+    # have to persist, performed by 'persist', we know will be
+    # reachable through the revisions listed in 'myroots' and their
+    # children and symbols.
+
+    method AdjustNonTrunkDefaultBranch {revlist} {
+	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.
+
+	    $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} {
+		$rev11 removebranch        $vendor
+		$rev11 removechildonbranch $first
+		$vendor cutchild
+		$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 setondefaultbranch 1
+	}
+
+	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
+    }
+
+    method CheckLODs {} {
+	foreach {_ branch}  [array get mybranches] { $branch checklod }
+	foreach {_ taglist} [array get mytags] {
+	    foreach tag $taglist { $tag checklod }
+	}
+	return
+    }
+
+    method RemoveIrrelevantDeletions {} {
+	# From cvs2svn: If a file is added on a branch, then a trunk
+	# revision is added at the same time in the 'Dead' state.
+	# This revision doesn't do anything useful, so delete it.
+
+	foreach root $myroots {
+	    if {[$root isneeded]} continue
+	    log write 2 file "Removing unnecessary dead revision [$root revnr]"
+
+	    # Remove as root, make its child new root after
+	    # disconnecting it from the revision just going away.
+
+	    ldelete myroots $root
+	    if {[$root haschild]} {
+		set child [$root child]
+		$child cutfromparent
+		lappend myroots $child
+	    }
+
+	    # Cut out the branches spawned by the revision to be
+	    # deleted. If the branch has revisions they should already
+	    # use operation 'add', no need to change that. The first
+	    # revision on each branch becomes a new and disconnected
+	    # root.
+
+	    foreach branch [$root branches] {
+		if {![$branch haschild]} continue
+		set first [$branch child]
+		$first cutfromparentbranch
+		$first cutfromparent
+		$branch cutchild
+		lappend myroots $first
+	    }
+	    $root removeallbranches
+
+	    # Tagging a dead revision doesn't do anything, so remove
+	    # any tags that were set on it.
+
+	    $root removealltags
+
+	    # This can only happen once per file, and we might have
+	    # just changed myroots, so end the loop
+	    break
+	}
+	return
+    }
+
+    method RemoveInitialBranchDeletions {} {
+	# From cvs2svn: If the first revision on a branch is an
+	# unnecessary delete, remove it.
+	#
+	# If a file is added on a branch (whether or not it already
+	# existed on trunk), then new versions of CVS add a first
+	# branch revision in the 'dead' state (to indicate that the
+	# file did not exist on the branch when the branch was
+	# created) followed by the second branch revision, which is an
+	# add.  When we encounter this situation, we sever the branch
+	# from trunk and delete the first branch revision.
+
+	# At this point we may have already multiple roots in myroots,
+	# we have to process them all.
+
+	foreach root [$self LinesOfDevelopment] {
+	    if {[$root isneededbranchdel]} continue
+	    log write 2 file "Removing unnecessary initial branch delete [$root revnr]"
+
+	    set branch [$root parentbranch]
+	    set parent [$root parent]
+	    set child  [$root child]
+
+	    ldelete myroots $root
+	    lappend myroots $child
+
+	    $branch cutchild
+	    $child  cutfromparent
+
+	    $parent removebranch        $branch
+	    $parent removechildonbranch $root
+	}
+	return
+    }
+
+    method LinesOfDevelopment {} {
+	# Determine all lines of development for the file. This are
+	# the known roots, and the root of all branches found on the
+	# line of primary children.
+
+	set lodroots {}
+	foreach root $myroots {
+	    $self AddBranchedLinesOfDevelopment lodroots $root
+	    lappend lodroots $root
+	}
+	return $lodroots
+    }
+
+    method AddBranchedLinesOfDevelopment {lv root} {
+	upvar 1 $lv lodroots
+	while {$root ne ""} {
+	    foreach branch [$root branches] {
+		if {![$branch haschild]} continue
+		set child [$branch child]
+		# Recurse into the branch for deeper branches.
+		$self AddBranchedLinesOfDevelopment lodroots $child
+		lappend lodroots $child
+	    }
+	    set root [$root child]
+	}
+	return
+    }
+
+    method ExcludeNonTrunkInformation {} {
+	# Remove all non-trunk branches, revisions, and tags. We do
+	# keep the tags which are on the trunk.
+
+	set ntdbroot ""
+	foreach root [$self LinesOfDevelopment] {
+	    # Note: Here the order of the roots is important,
+	    # i.e. that we get them in depth first order. This ensures
+	    # that the removal of a branch happens only after the
+	    # branches spawned from it were removed. Otherwise the
+	    # system might try to access deleted objects.
+
+	    # Do not exclude the trunk.
+	    if {[[$root lod] istrunk]} continue
+	    $self ExcludeBranch $root ntdbroot
+	}
+
+	if {$ntdbroot ne ""} {
+	    $self GraftNTDB2Trunk $ntdbroot
+	}
+	return
+    }
+
+    method ExcludeBranch {root nv} {
+	# Exclude the branch/lod starting at root, a revision.
+	#
+	# If the LOD starts with non-trunk default branch revisions,
+	# we leave them in place and do not delete the branch. In that
+	# case the command sets the variable in NV so that we can
+	# later rework these revisons to be purely trunk.
+
+	if {[$root isondefaultbranch]} {
+	    # Handling a NTDB. This branch may consists not only of
+	    # NTDB revisions, but also some non-NTDB. The latter are
+	    # truly on a branch and have to be excluded. The following
+	    # loop determines if there are such revisions.
+
+	    upvar 1 $nv ntdbroot
+	    set ntdbroot $root
+	    $root cutfromparentbranch
+
+	    set rev $root
+	    while {$rev ne ""} {
+		$rev removeallbranches
+		# See note [x].
+
+		if {[$rev isondefaultbranch]} {
+		    set rev [$rev child]
+		} else {
+		    break
+		}
+	    }
+
+	    # rev now contains the first non-NTDB revision after the
+	    # NTDB, or is empty if there is no such. If we have some
+	    # they have to removed.
+
+	    if {$rev ne ""}  {
+		set lastntdb [$rev parent]
+		$lastntdb cutfromchild
+		while {$rev ne ""} {
+		    $rev removealltags
+		    $rev removeallbranches
+		    # Note [x]: We may still have had branches on the
+		    # revision. Branches without revisions committed
+		    # on them do not show up in the list of roots aka
+		    # lines of development.
+		    set rev [$rev child]
+		}
+	    }
+	    return
+	}
+
+	# No NTDB stuff to deal with. First delete the branch object
+	# itself, after cutting all the various connections.
+
+	set branch [$root parentbranch]
+	if {$branch ne ""} {
+	    set branchparent [$branch parent]
+	    $branchparent removebranch        $branch
+	    $branchparent removechildonbranch $root
+	}
+
+	# The root is no such any longer either.
+	ldelete myroots $root
+
+	# Now go through the line and remove all its revisions.
+
+	while {$root ne ""} {
+	    $root removealltags
+	    $root removeallbranches
+	    # Note: See the note [x].
+
+	    # From cvs2svn: If this is the last default revision on a
+	    # non-trunk default branch followed by a 1.2 revision,
+	    # then the 1.2 revision depends on this one.  FIXME: It is
+	    # questionable whether this handling is correct, since the
+	    # non-trunk default branch revisions affect trunk and
+	    # should therefore not just be discarded even if
+	    # --trunk-only.
+
+	    if {[$root hasdefaultbranchchild]} {
+		set ntdbchild [$root defaultbranchchild]
+		if {[$ntdbchild defaultbranchparent] ne $ntdbchild} {
+		    trouble internal "ntdb - trunk linkage broken"
+		}
+		$ntdbchild cutdefaultbranchparent
+		if {[$ntdbchild hasparent]} {
+		    lappend myroots [$ntdbchild parent]
+		}
+	    }
+
+	    set root [$root child]
+	}
+
+	return
+    }
+
+    method GraftNTDB2Trunk {root} {
+	# We can now graft the non-trunk default branch revisions to
+	# trunk. They should already be alone on a CVSBranch-less
+	# branch.
+
+	if {[$root hasparentbranch]} { trouble internal "NTDB root still has its branch symbol" }
+	if {[$root hasbranches]}     { trouble internal "NTDB root still has spawned branches" }
+
+	set last $root
+	while {[$last haschild]} {set last [$last child]}
+
+	if {[$last hasdefaultbranchchild]} {
+
+	    set rev12 [$last defaultbranchchild]
+	    $rev12 cutdefaultbranchparent
+	    $last  cutdefaultbranchchild
+
+	    $rev12 changeparent $last
+	    $last  changechild $rev12
+
+	    ldelete myroots $rev12
+
+	    # Note and remember that the type of rev12 was already
+	    # adjusted by AdjustNonTrunkDefaultBranch, so we don't
+	    # have to change its type here.
+	}
+
+	while {$root ne ""} {
+	    $root setondefaultbranch 0
+	    $root setlod $mytrunk
+	    foreach tag [$root tags] {
+		$tag setlod $mytrunk
+	    }
+	    set root [$root child]
+	}
+
+        return
+    }
+
+    method Active {} {
+	set revisions {}
+	set symbols   {}
+
+	foreach root [$self LinesOfDevelopment] {
+	    if {[$root hasparentbranch]} { lappend symbols [$root parentbranch] }
+	    while {$root ne ""} {
+		lappend revisions $root
+		foreach tag    [$root tags]     { lappend symbols $tag    }
+		foreach branch [$root branches] { lappend symbols $branch }
+		set lod [$root lod]
+		if {![$lod istrunk]} { lappend symbols $lod }
+		set root [$root child]
+	    }
+	}
+
+	return [list [lsort -unique -dict $revisions] [lsort -unique -dict $symbols]]
+    }
+
+
+    method AggregateSymbolData {} {
+	# Now that the exact set of revisions (and through that
+	# branches and tags) is known we can update the aggregate
+	# symbol statistics.
+
+	foreach root [$self LinesOfDevelopment] {
+	    set lod [$root lod]
+
+	    # Note: If the LOD is the trunk the count*, etc. methods
+	    # will do nothing, as it is always present (cannot be
+	    # excluded), and is always a branch too.
+
+	    # Lines of development count as branches and have a commit
+	    # on them (root). If they are still attached to a tree we
+	    # have to compute and register possible parents.
+
+	    $lod countasbranch
+	    $lod countacommit
+
+	    if {[$root hasparentbranch]} {
+		# Note lod == [$root parentbranch]
+		$lod possibleparents
+	    }
+
+	    # For the revisions in the line we register their branches
+	    # and tags as blockers for the lod, and update the type
+	    # counters as well. As branch symbols without commits on
+	    # them are not listed as lines of development, we have to
+	    # count them here as well, as plain branches. At last we
+	    # have to compute and register the possible parents of the
+	    # tags, in case they are later converted as branches.
+
+	    while {$root ne ""} {
+		foreach branch [$root branches] {
+		    $lod blockedby $branch
+		    $branch possibleparents
+		    if {[$branch haschild]} continue
+		    $branch countasbranch
+		}
+
+		foreach tag [$root tags] {
+		    $lod blockedby $tag
+		    $tag possibleparents
+		    $tag countastag
+		}
+
+		set root [$root child]
+	    }
+	}
+
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hastypeinfo    no  ; # no type introspection
+    pragma -hasinfo        no  ; # no object introspection
+    pragma -hastypemethods no  ; # type is not relevant.
+
+    # # ## ### ##### ######## #############
+}
+
+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
+	namespace import ::vc::fossil::import::cvs::state
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::file 1.0
+return

Added tools/cvs2fossil/lib/c2f_flodmgr.tcl version [755aedadae]

@@ -1,1 +1,57 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Lines of Development in a file (Symbols, and the trunk).
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                             ; # Required runtime.
+package require snit                                ; # OO system.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::file::lodmgr {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    constructor {} {
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    # # ## ### ##### ######## #############
+    ## 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::file {
+    namespace export lodmgr
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::file::lodmgr 1.0
+return

Added tools/cvs2fossil/lib/c2f_frev.tcl version [b685fd71ec]

@@ -1,1 +1,531 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Revisions per file.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                             ; # Required runtime.
+package require snit                                ; # OO system.
+package require vc::tools::misc                     ; # Text formatting
+package require vc::fossil::import::cvs::state      ; # State storage.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::file::rev {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    constructor {revnr date state thefile} {
+	set myrevnr    $revnr
+	set mydate     $date
+	set myorigdate $date
+	set mystate    $state
+	set myfile     $thefile
+	return
+    }
+
+    method defid {} {
+	set myid [incr myidcounter]
+	return
+    }
+
+    method id {} { return $myid }
+
+    # Basic pieces ________________________
+
+    method hasmeta {} { return [expr {$mymetaid ne ""}] }
+    method hastext {} {
+	struct::list assign $mytext s e
+	return [expr {$s <= $e}]
+    }
+
+    method setmeta {meta} { set mymetaid $meta ; return }
+    method settext {text} { set mytext   $text ; return }
+    method setlod  {lod}  { set mylod    $lod  ; return }
+
+    method revnr {} { return $myrevnr }
+    method state {} { return $mystate }
+    method lod   {} { return $mylod   }
+    method date  {} { return $mydate  }
+
+    method isneeded {} {
+	if {$myoperation ne "nothing"}         {return 1}
+	if {$myrevnr ne "1.1"}                 {return 1}
+	if {![$mylod istrunk]}                 {return 1}
+	if {![llength $mybranches]}            {return 1}
+	set firstbranch [lindex $mybranches 0]
+	if {![$firstbranch haschild]}          {return 1}
+	if {$myisondefaultbranch}              {return 1}
+
+	# FIX: This message will not match if the RCS file was renamed
+	# manually after it was created.
+
+	set gen "file [file tail [$myfile usrpath]] was initially added on branch [$firstbranch name]."
+	set log [$myfile commitmessageof $mymetaid]
+
+	return [expr {$log ne $gen}]
+    }
+
+    method isneededbranchdel {} {
+	if {$myparentbranch eq ""}           {return 1} ; # not first on a branch, needed
+	set base [$myparentbranch parent]
+	if {$base           eq ""}           {return 1} ; # branch has parent lod, needed
+	if {[$self LODLength] < 2}           {return 1} ; # our lod contains only ourselves, needed.
+	if {$myoperation ne "delete"}        {return 1} ; # Not a deletion, needed
+	if {[llength $mytags]}               {return 1} ; # Have tags, needed
+	if {[llength $mybranches]}           {return 1} ; # Have other branches, needed
+	if {abs($mydate - [$base date]) > 2} {return 1} ; # Next rev > 2 seconds apart, needed
+
+        # FIXME: This message will not match if the RCS file was
+        # renamed manually after it was created.
+
+	set qfile [string map {
+	    .  \\.  ?  \\?  *  \\*  \\ \\\\ +  \\+  ^ \\^ $ \\$
+	    \[ \\\[ \] \\\] (  \\(   ) \\)  \{ \\\{ \} \\\}
+	} [file tail [$myfile usrpath]]]
+	set pattern "file $qfile was added on branch .* on \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}( \[+-\]\\d{4})?"
+	set log     [$myfile commitmessageof $mymetaid]
+
+	# Not the special message, needed
+	if {![regexp -- $pattern $log]} {return 1}
+
+	# This is an unneeded initial branch delete.
+	return 0
+    }
+
+    method LODLength {} {
+	set n 1 ; # count self
+	set rev $mychild
+	while {$rev ne ""} {
+	    incr n
+	    set rev [$rev child]
+	}
+	return $n
+    }
+
+    # Basic parent/child linkage __________
+
+    method hasparent {} { return [expr {$myparent ne ""}] }
+    method haschild  {} { return [expr {$mychild  ne ""}] }
+
+    method setparent {parent} {
+	if {$myparent ne ""} { trouble internal "Parent already defined" }
+	set myparent $parent
+	return
+    }
+
+    method cutfromparent {} { set myparent "" ; return }
+    method cutfromchild  {} { set mychild  "" ; return }
+
+    method setchild {child} {
+	if {$mychild ne ""} { trouble internal "Child already defined" }
+	set mychild $child
+	return
+    }
+
+    method changeparent {parent} { set myparent $parent ; return }
+    method changechild  {child}  { set mychild  $child  ; return }
+
+    method parent {} { return $myparent }
+    method child  {} { return $mychild  }
+
+    # Branch linkage ______________________
+
+    method setparentbranch {branch} {
+	if {$myparentbranch ne ""} { trouble internal "Branch parent already defined" }
+	set myparentbranch $branch
+	return
+    }
+
+    method hasparentbranch {} { return [expr {$myparentbranch ne ""}] }
+    method hasbranches     {} { return [llength $mybranches] }
+
+    method parentbranch {} { return $myparentbranch }
+    method branches     {} { return $mybranches }
+
+    method addbranch {branch} {
+	lappend mybranches $branch
+	return
+    }
+
+    method addchildonbranch {child} {
+	lappend mybranchchildren $child
+	return
+    }
+
+    method cutfromparentbranch {} { set myparentbranch "" ; return }
+
+    method removebranch {branch} {
+	ldelete mybranches $branch
+	return
+    }
+
+    method removechildonbranch {rev} {
+	ldelete mybranchchildren $rev
+	return
+    }
+
+    method sortbranches {} {
+	# Pass 2: CollectRev
+
+	if {[llength $mybranches] < 2} return
+
+	# Sort the branches spawned by this revision in creation
+	# order. To help in this our file gave all branches a position
+	# id, in order of their definition by the RCS archive.
+	#
+	# The creation order is (apparently) the reverse of the
+	# definition order. (If a branch is created then deleted, a
+	# later branch can be assigned the recycled branch number;
+	# therefore branch numbers are not an indication of creation
+	# order.)
+
+	set tmp {}
+	foreach branch $mybranches {
+	    lappend tmp [list $branch [$branch position]]
+	}
+
+	set mybranches {}
+	foreach item [lsort -index 1 -decreasing $tmp] {
+	    struct::list assign $item branch position
+	    lappend mybranches $branch
+	}
+	return
+    }
+
+    method movebranchesto {rev} {
+	set revlod [$rev lod]
+	foreach branch $mybranches {
+	    $rev addbranch $branch
+	    $branch setparent $rev
+	    $branch setlod $revlod
+	}
+	foreach branchrev $mybranchchildren {
+	    $rev addchildonbranch $branchrev
+	    $branchrev cutfromparent
+	    $branchrev setparent $rev
+	}
+	set mybranches       {}
+	set mybranchchildren {}
+	return
+    }
+
+    method removeallbranches {} {
+	set mybranches       {}
+	set mybranchchildren {}
+	return
+    }
+
+    # Tag linkage _________________________
+
+    method addtag {tag} {
+	lappend mytags $tag
+	return
+    }
+
+    method tags {} { return $mytags }
+
+    method removealltags {} {
+	set mytags {}
+	return
+    }
+
+    method movetagsto {rev} {
+	set revlod [$rev lod]
+	foreach tag $mytags {
+	    $rev addtag $tag
+	    $tag settagrev $rev
+	    $tag setlod $revlod
+	}
+	set mytags {}
+	return
+    }
+
+    # general symbol operations ___________
+
+    method movesymbolsto {rev} {
+	# Move the tags and branches attached to this revision to the
+	# destination and fix all pointers.
+
+	$self movetagsto     $rev
+	$self movebranchesto $rev
+	return
+    }
+
+    # Derived stuff _______________________
+
+    method determineoperation {} {
+	# Look at the state of both this revision and its parent to
+	# determine the type opf operation which was performed (add,
+	# modify, delete, none).
+	#
+	# The important information is dead vs not-dead for both,
+	# giving rise to four possible types.
+
+	set sdead [expr {$mystate eq "dead"}]
+	set pdead [expr {$myparent eq "" || [$myparent state] eq "dead"}]
+
+	set myoperation $myopstate([list $pdead $sdead])
+	return
+    }
+
+    method operation {} { return $myoperation }
+    method retype {x} { set myoperation $x ; return }
+
+    method isondefaultbranch    {} { return $myisondefaultbranch }
+
+    method setondefaultbranch   {x} { set myisondefaultbranch $x ; return }
+
+    method setdefaultbranchchild  {rev} { set mydbchild $rev ; return }
+    method setdefaultbranchparent {rev} {
+	set mydbparent $rev
+
+	# Retype the revision (may change from 'add' to 'change').
+
+	set sdead [expr {$myoperation     ne "change"}]
+	set pdead [expr {[$rev operation] ne "change"}]
+	set myoperation $myopstate([list $pdead $sdead])
+	return
+    }
+
+    method cutdefaultbranchparent {} { set mydbparent "" ; return }
+    method cutdefaultbranchchild  {} { set mydbchild  "" ; return }
+
+    method defaultbranchchild  {} { return $mydbchild }
+    method defaultbranchparent {} { return $mydbparent }
+
+    method hasdefaultbranchchild  {} { return [expr {$mydbchild  ne ""}] }
+    method hasdefaultbranchparent {} { return [expr {$mydbparent ne ""}] }
+
+    # # ## ### ##### ######## #############
+    ## Type API
+
+    typemethod istrunkrevnr {revnr} {
+	return [expr {[llength [split $revnr .]] == 2}]
+    }
+
+    typemethod isbranchrevnr {revnr _ bv} {
+	if {[regexp $mybranchpattern $revnr -> head tail]} {
+	    upvar 1 $bv branchnr
+	    set branchnr ${head}$tail
+	    return 1
+	}
+	return 0
+    }
+
+    typemethod 2branchnr {revnr} {
+	# Input is a branch revision number, i.e. a revision number
+	# with an even number of components; for example '2.9.2.1'
+	# (never '2.9.2' nor '2.9.0.2').  The return value is the
+	# branch number (for example, '2.9.2').  For trunk revisions,
+	# like '3.4', we return the empty string.
+
+	if {[$type istrunkrevnr $revnr]} {
+	    return ""
+	}
+	return [join [lrange [split $revnr .] 0 end-1] .]
+    }
+
+    typemethod 2branchparentrevnr {branchnr} {
+	# Chop the last segment off
+	return [join [lrange [split $branchnr .] 0 end-1] .]
+    }
+
+    # # ## ### ##### ######## #############
+
+    method persist {} {
+	set fid [$myfile id]
+	set lod [$mylod id]
+	set op  $myopcode($myoperation)
+	set idb $myisondefaultbranch
+
+	struct::list assign $mytext coff end
+	set clen [expr {$end - $coff}]
+
+	lappend map @P@ [expr { ($myparent       eq "") ? "NULL" : [$myparent       id] }]
+	lappend map @C@ [expr { ($mychild        eq "") ? "NULL" : [$mychild        id] }]
+	lappend map @DP [expr { ($mydbparent     eq "") ? "NULL" : [$mydbparent     id] }]
+	lappend map @DC [expr { ($mydbchild      eq "") ? "NULL" : [$mydbchild      id] }]
+	lappend map @BP [expr { ($myparentbranch eq "") ? "NULL" : [$myparentbranch id] }]
+
+	set cmd {
+	    INSERT INTO revision ( rid,   fid,  rev,      lod, parent, child,  isdefault, dbparent, dbchild, bparent,  op,  date,    state,    mid,       coff,  clen)
+	    VALUES               ($myid, $fid, $myrevnr, $lod, @P@,    @C@,   $idb,       @DP,      @DC,     @BP    , $op, $mydate, $mystate, $mymetaid, $coff, $clen);
+	}
+
+	state transaction {
+	    state run [string map $map $cmd]
+
+	    # And the branch children as well, for pass 5.
+	    foreach bc $mybranchchildren {
+		set bcid [$bc id]
+		state run {
+		    INSERT INTO revisionbranchchildren (rid,   brid)
+		    VALUES                             ($myid, $bcid);
+		}
+	    }
+	}
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    # Persistent: myid                - revision.rid
+    #             myfile              - revision.fid
+    #             mylod               - revision.lod
+    #             myrevnr             - revision.rev
+    #             mydate              - revision.date
+    #             mystate             - revision.state
+    #             mymetaid            - revision.mid
+    #             mytext              - revision.{cs,cl}
+    #             myparent            - revision.parent
+    #             mychild             - revision.child
+    #             myparentbranch      - revision.bparent
+    #             myoperation         - revision.op
+    #             myisondefaultbranch - revision.isdefault
+    #             mydbparent          - revision.dbparent
+    #             mydbchild           - revision.dbchild
+
+
+    typevariable mybranchpattern {^((?:\d+\.\d+\.)+)(?:0\.)?(\d+)$}
+    # First a nonzero even number of digit groups with trailing dot
+    # CVS then sticks an extra 0 in here; RCS does not.
+    # And the last digit group.
+
+    typevariable myidcounter 0 ; # Counter for revision ids.
+    variable myid           {} ; # Revision id.
+
+    variable myrevnr     {} ; # Revision number of the revision.
+    variable mydate      {} ; # Timestamp of the revision, seconds since epoch
+    variable myorigdate  {} ; # Original unmodified timestamp.
+    variable mystate     {} ; # State of the revision.
+    variable myfile      {} ; # Ref to the file object the revision belongs to.
+    variable mytext      {} ; # Range of the (delta) text for this revision in the file.
+    variable mymetaid    {} ; # Id of the meta data group the revision
+			      # belongs to. This is later used to put
+			      # the file revisions into preliminary
+			      # changesets (aka project revisions).
+			      # This id encodes 4 pieces of data,
+			      # namely: the project and branch the
+			      # revision was committed to, the author
+			      # who did the commit, and the message
+			      # used.
+    variable mylod       {} ; # Reference to the line-of-development
+			      # object the revision belongs to. An
+			      # alternative idiom would be to call it
+			      # the branch the revision is on. This
+			      # reference is to a project-level object
+			      # (symbol or trunk).
+
+    # Basic parent/child linkage (lines of development)
+
+    variable myparent {} ; # Ref to parent revision object. Link required because of
+    #                    ; # 'cvsadmin -o', which can create arbitrary gaps in the
+    #                    ; # numbering sequence. This is in the same line of development
+    #                    ; # Note: For the first revision on a branch the revision
+    #                    ; # it was spawned from is the parent. Only the root revision
+    #                    ; # of myfile's revision tree has nothing set here.
+    #                    ; #
+
+    variable mychild  {} ; # Ref to the primary child revision object, i.e. the next
+    #                    ; # revision in the same line of development.
+
+    # Branch linkage ____________________
+
+    variable mybranches     {} ; # List of the branches (objs) spawned by this revision.
+    variable myparentbranch {} ; # For the first revision on a branch the relevant
+    #                          ; # branch object. This also allows us to determine if
+    #                          ; # myparent is in the same LOD, or the revision the
+    #                          ; # branch spawned from.
+
+    # List of the revision objects of the first commits on any
+    # branches spawned by this revision on which commits occurred.
+    # This dependency is kept explicitly because otherwise a
+    # revision-only topological sort would miss the dependency that
+    # exists via -> mybranches.
+
+    variable mybranchchildren {} ; # List of the revisions (objs) which are the first
+    #                            ; # commits on any of the branches spawned from this
+    #                            ; # revision. The dependency is kept explicitly to
+    #                            ; # ensure that a revision-only topological sort will
+    #                            ; # not miss it, as it otherwise exists only via
+    #                            ; # mybranches.
+
+    # Tag linkage ________________________
+
+    variable mytags {} ; # List of tags (objs) associated with this revision.
+
+    # More derived data
+
+    variable myoperation        {} ; # One of 'add', 'change', 'delete', or
+			             # 'nothing'. Derived from our and
+			             # its parent's state.
+    variable myisondefaultbranch 0 ; # Boolean flag, set if the
+				     # revision is on the non-trunk
+				     # default branch, aka vendor
+				     # branch.
+    variable mydbparent         {} ; # Reference to the last revision
+				     # on the vendor branch if this is
+				     # the primary child of the
+				     # regular root.
+    variable mydbchild          {} ; # Reference to the primary child
+				     # of the regular root if this is
+				     # the last revision on the vendor
+				     # branch.
+
+    # dead(self) x dead(parent) -> operation
+    typevariable myopstate -array {
+	{0 0} change
+	{0 1} delete
+	{1 0} add
+	{1 1} nothing
+    }
+
+    typemethod getopcodes {} {
+	foreach {id name} [state run {
+	    SELECT oid, name FROM optype;
+	}] { set myopcode($name) $id }
+	return
+    }
+
+    typevariable myopcode -array {}
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hastypeinfo    no  ; # no type introspection
+    pragma -hasinfo        no  ; # no object introspection
+    pragma -simpledispatch yes ; # simple fast dispatch
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::file {
+    namespace export rev
+    namespace eval rev {
+	namespace import ::vc::tools::misc::*
+	namespace import ::vc::fossil::import::cvs::state
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::file::rev 1.0
+return

Added tools/cvs2fossil/lib/c2f_fsym.tcl version [31fcf57a52]

@@ -1,1 +1,297 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Symbols (Tags, Branches) per file.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                             ; # Required runtime.
+package require snit                                ; # OO system.
+package require vc::tools::trouble                  ; # Error reporting.
+package require vc::fossil::import::cvs::file::rev  ; # CVS per file revisions.
+package require vc::fossil::import::cvs::state      ; # State storage.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::file::sym {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    constructor {symtype nr symbol file} {
+	set myfile   $file
+	set mytype   $symtype
+	set mynr     $nr
+	set mysymbol $symbol
+
+	switch -exact -- $mytype {
+	    branch  { SetupBranch }
+	    tag     { }
+	    default { trouble internal "Bad symbol type '$mytype'" }
+	}
+	return
+    }
+
+    method defid {} {
+	set myid [incr myidcounter]
+	return
+    }
+
+    method fid    {} { return $myid     }
+    method symbol {} { return $mysymbol }
+
+    # Symbol acessor methods.
+
+    delegate method name to mysymbol
+    delegate method id   to mysymbol
+
+    # Symbol aggregation methods
+
+    delegate method countasbranch to mysymbol
+    delegate method countastag    to mysymbol
+    delegate method countacommit  to mysymbol
+
+    method blockedby {fsymbol} {
+	$mysymbol blockedby [$fsymbol symbol]
+	return
+    }
+
+    method possibleparents {} {
+	switch -exact -- $mytype {
+	    branch { $self BranchParents }
+	    tag    { $self TagParents    }
+	}
+	return
+    }
+
+    method BranchParents {} {
+	# The "obvious" parent of a branch is the branch holding the
+	# revision spawning the branch. Any other branches that are
+	# rooted at the same revision and were committed earlier than
+	# the branch are also possible parents.
+
+	$mysymbol possibleparent [[$mybranchparent lod] symbol]
+
+	foreach branch [$mybranchparent branches] {
+	    # A branch cannot be its own parent. Nor can a branch
+	    # created after this one be its parent. This means that we
+	    # can abort the loop when we have reached ourselves in the
+	    # list of branches. Here the order of file::rev.mybranches
+	    # comes into play, as created by file::rev::sortbranches.
+
+	    if {$branch eq $self} break
+	    $mysymbol possibleparent [$branch symbol]
+	}
+	return
+    }
+
+    method TagParents {} {
+	# The "obvious" parent of a tag is the branch holding the
+	# revision spawning the tag. Branches that are spawned by the
+	# same revision are also possible parents.
+
+	$mysymbol possibleparent [[$mytagrev lod] symbol]
+
+	foreach branch [$mytagrev branches] {
+	    $mysymbol possibleparent [$branch symbol]
+	}
+	return
+    }
+
+    #
+
+    method istrunk {} { return 0 }
+
+    # Branch acessor methods.
+
+    method setchildrevnr  {revnr} {
+	if {$mybranchchildrevnr ne ""} { trouble internal "Child already defined" }
+	set mybranchchildrevnr $revnr
+	return
+    }
+
+    method setposition {n}   { set mybranchposition $n ; return }
+    method setparent   {rev} { set mybranchparent $rev ; return }
+    method setchild    {rev} { set mybranchchild  $rev ; return }
+    method cutchild    {}    { set mybranchchild  ""   ; return }
+
+    method branchnr    {} { return $mynr }
+    method parentrevnr {} { return $mybranchparentrevnr }
+    method childrevnr  {} { return $mybranchchildrevnr }
+    method haschildrev {} { return [expr {$mybranchchildrevnr ne ""}] }
+    method haschild    {} { return [expr {$mybranchchild ne ""}] }
+    method parent      {} { return $mybranchparent }
+    method child       {} { return $mybranchchild }
+    method position    {} { return $mybranchposition }
+
+    # Tag acessor methods.
+
+    method tagrevnr  {}    { return $mynr }
+    method settagrev {rev} {set mytagrev $rev ; return }
+
+    # Derived information
+
+    method lod {} { return $mylod }
+
+    method setlod {lod} {
+	set mylod $lod
+	$self checklod
+	return
+    }
+
+    method checklod {} {
+	# Consistency check. The symbol's line-of-development has to
+	# be same as the line-of-development of its source (parent
+	# revision of a branch, revision of a tag itself).
+
+	switch -exact -- $mytype {
+	    branch  { set slod [$mybranchparent lod] }
+	    tag     { set slod [$mytagrev       lod] }
+	}
+
+	if {$mylod ne $slod} {
+	    trouble fatal "For $mytype [$mysymbol name]: LOD conflict with source, '[$mylod name]' vs. '[$slod name]'"
+	    return
+	}
+	return
+    }
+
+    # # ## ### ##### ######## #############
+
+    method persist {} {
+	# Save the information we need after the collection pass.
+
+	set fid [$myfile   id]
+	set sid [$mysymbol id]
+	set lod [$mylod    id]
+
+	switch -exact -- $mytype {
+	    tag {
+		set rid [$mytagrev id]
+		state transaction {
+		    state run {
+			INSERT INTO tag ( tid,   fid,  lod,  sid,  rev)
+			VALUES          ($myid, $fid, $lod, $sid, $rid);
+		    }
+		}
+	    }
+	    branch {
+		lappend map @F@ [expr { ($mybranchchild eq "") ? "NULL" : [$mybranchchild id] }]
+
+		set rid [$mybranchparent id]
+		set cmd {
+		    INSERT INTO branch ( bid,   fid,  lod,  sid,  root, first, bra,  pos              )
+		    VALUES             ($myid, $fid, $lod, $sid, $rid,  @F@,  $mynr, $mybranchposition);
+		}
+		state transaction {
+		    state run [string map $map $cmd]
+		}
+	    }
+	}
+
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    # Persistent:
+    #        Tag: myid           - tag.tid
+    #             myfile         - tag.fid
+    #             mylod          - tag.lod
+    #             mysymbol       - tag.sid
+    #             mytagrev       - tag.rev
+    #
+    #     Branch: myid           - branch.bid
+    #		  myfile         - branch.fid
+    #		  mylod          - branch.lod
+    #             mysymbol       - branch.sid
+    #             mybranchparent - branch.root
+    #             mybranchchild  - branch.first
+    #             mynr           - branch.bra
+
+    typevariable myidcounter 0 ; # Counter for symbol ids.
+    variable myid           {} ; # Symbol id.
+
+    ## Basic, all symbols _________________
+
+    variable myfile   {} ; # Reference to the file the symbol is in.
+    variable mytype   {} ; # Symbol type, 'tag', or 'branch'.
+    variable mynr     {} ; # Revision number of a 'tag', branch number
+			   # of a 'branch'.
+    variable mysymbol {} ; # Reference to the symbol object of this
+			   # symbol at the project level.
+    variable mylod    {} ; # Reference to the line-of-development
+			   # object the symbol belongs to. An
+			   # alternative idiom would be to call it the
+			   # branch the symbol is on. This reference
+			   # is to a project-level object (symbol or
+			   # trunk).
+
+    ## Branch symbols _____________________
+
+    variable mybranchparentrevnr {} ; # The number of the parent
+				      # revision, derived from our
+				      # branch number (mynr).
+    variable mybranchparent      {} ; # Reference to the revision
+				      # (object) which spawns the
+				      # branch.
+    variable mybranchchildrevnr  {} ; # Number of the first revision
+				      # committed on this branch.
+    variable mybranchchild       {} ; # Reference to the revision
+				      # (object) first committed on
+				      # this branch.
+    variable mybranchposition    {} ; # Relative id of the branch in
+				      # the file, to sort into
+				      # creation order.
+
+    ## Tag symbols ________________________
+
+    variable mytagrev {} ; # Reference to the revision object the tag
+			   # is on, identified by 'mynr'.
+
+    # ... nothing special ... (only mynr, see basic)
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    proc SetupBranch {} {
+	upvar 1 mybranchparentrevnr mybranchparentrevnr mynr mynr
+	set mybranchparentrevnr [rev 2branchparentrevnr  $mynr]
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hastypeinfo    no  ; # no type introspection
+    pragma -hasinfo        no  ; # no object introspection
+    pragma -hastypemethods no  ; # type is not relevant.
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::file {
+    namespace export sym
+    namespace eval sym {
+	namespace import ::vc::fossil::import::cvs::file::rev
+	namespace import ::vc::fossil::import::cvs::state
+	namespace import ::vc::tools::trouble
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::file::sym 1.0
+return

Added tools/cvs2fossil/lib/c2f_ftrunk.tcl version [4dd74e0101]

@@ -1,1 +1,57 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Trunk, the special main line of development in a file.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                             ; # Required runtime.
+package require snit                                ; # OO system.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::file::trunk {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    constructor {} {
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    # # ## ### ##### ######## #############
+    ## 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::file {
+    namespace export trunk
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::file::trunk 1.0
+return

Added tools/cvs2fossil/lib/c2f_integrity.tcl version [f5cf3c5637]

@@ -1,1 +1,321 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## This package holds a number of integrity checks done on the
+## persistent state. This is used by the passes II and IV.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                               ; # Required runtime.
+package require snit                                  ; # OO system.
+package require vc::tools::trouble                    ; # Error reporting.
+package require vc::tools::log                        ; # User feedback.
+package require vc::fossil::import::cvs::state        ; # State storage.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::integrity {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod strict {} {
+	set n 0
+	AllButMeta
+	Meta
+	return
+    }
+
+    typemethod metarelaxed {} {
+	set n 0
+	AllButMeta
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    proc AllButMeta {} {
+	# This code performs a number of paranoid checks of the
+	# database, searching for inconsistent cross-references.
+	log write 4 integrity {Check database consistency}
+
+	upvar 1 n n ; # Counter for the checks (we print an id before
+		      # the main label).
+
+	# Find all revisions which disagree with their line of
+	# development about the project they are owned by.
+	Check \
+	    {Revisions and their LODs have to be in the same project} \
+	    {disagrees with its LOD about owning project} {
+		SELECT F.name, R.rev
+		FROM revision R, file F, symbol S
+		WHERE R.fid = F.fid
+		AND   R.lod = S.sid
+		AND   F.pid != S.pid
+		;
+	    }
+	# Find all revisions which disgree with their meta data about
+	# the project they are owned by.
+	Check \
+	    {Revisions and their meta data have to be in the same project} \
+	    {disagrees with its meta data about owning project} {
+		SELECT F.name, R.rev
+		FROM revision R, file F, meta M
+		WHERE R.fid = F.fid
+		AND   R.mid = M.mid
+		AND   F.pid != M.pid
+		;
+	    }
+	# Find all revisions with a primary child which disagrees
+	# about the file they belong to.
+	Check \
+	    {Revisions and their primary children have to be in the same file} \
+	    {disagrees with its primary child about the owning file} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.child IS NOT NULL
+		AND   R.child = C.rid
+		AND   C.fid != R.fid
+		;
+	    }
+
+	# Find all revisions with a branch parent symbol whose parent
+	# disagrees about the file they belong to.
+	Check \
+	    {Revisions and their branch children have to be in the same file} \
+	    {at the beginning of its branch and its parent disagree about the owning file} {
+		SELECT F.name, R.rev
+		FROM revision R, revision P, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NOT NULL
+		AND   R.parent = P.rid
+		AND   R.fid != P.fid
+		;
+	    }
+	# Find all revisions with a non-NTDB child which disagrees
+	# about the file they belong to.
+	Check \
+	    {Revisions and their non-NTDB children have to be in the same file} \
+	    {disagrees with its non-NTDB child about the owning file} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.dbchild IS NOT NULL
+		AND   R.dbchild = C.rid
+		AND   C.fid != R.fid
+		;
+	    }
+	# Find all revisions which have a primary child, but the child
+	# does not have them as parent.
+	Check \
+	    {Revisions have to be parents of their primary children} \
+	    {is not the parent of its primary child} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.child IS NOT NULL
+		AND   R.child = C.rid
+		AND   C.parent != R.rid
+		;
+	    }
+	# Find all revisions which have a primrary child, but the
+	# child has a branch parent symbol making them brach starters.
+	Check \
+	    {Primary children of revisions must not start branches} \
+	    {is parent of a primary child which is the beginning of a branch} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.child IS NOT NULL
+		AND   R.child = C.rid
+		AND   C.bparent IS NOT NULL
+		;
+	    }
+	# Find all revisions without branch parent symbol which have a
+	# parent, but the parent does not have them as primary child.
+	Check \
+	    {Revisions have to be primary children of their parents, if any} \
+	    {is not the child of its parent} {
+		SELECT F.name, R.rev
+		FROM revision R, revision P, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NULL
+		AND   R.parent IS NOT NULL
+		AND   R.parent = P.rid
+		AND   P.child != R.rid
+		;
+	    }
+	# Find all revisions with a branch parent symbol which do not
+	# have a parent.
+	Check \
+	    {Branch starting revisions have to have a parent} \
+	    {at the beginning of its branch has no parent} {
+		SELECT F.name, R.rev
+		FROM revision R, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NOT NULL
+		AND   R.parent IS NULL
+		;
+	    }
+	# Find all revisions with a branch parent symbol whose parent
+	# has them as primary child.
+	Check \
+	    {Branch starting revisions must not be primary children of their parents} \
+	    {at the beginning of its branch is the primary child of its parent} {
+		SELECT F.name, R.rev
+		FROM revision R, revision P, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NOT NULL
+		AND   R.parent IS NOT NULL
+		AND   R.parent = P.rid
+		AND   P.child = R.rid
+		;
+	    }
+	# Find all revisions with a non-NTDB child which are not on
+	# the NTDB.
+	Check \
+	    {NTDB to trunk transition has to begin on NTDB} \
+	    {has a non-NTDB child, yet is not on the NTDB} {
+		SELECT F.name, R.rev
+		FROM revision R, file F
+		WHERE R.fid = F.fid
+		AND   R.dbchild IS NOT NULL
+		AND   NOT R.isdefault
+		;
+	    }
+	# Find all revisions with a NTDB parent which are on the NTDB.
+	Check \
+	    {NTDB to trunk transition has to end on non-NTDB} \
+	    {has a NTDB parent, yet is on the NTDB} {
+		SELECT F.name, R.rev
+		FROM revision R, file F
+		WHERE R.fid = F.fid
+		AND   R.dbparent IS NOT NULL
+		AND   R.isdefault
+		;
+	    }
+	# Find all revisions with a child which disagrees about the
+	# line of development they belong to.
+	Check \
+	    {Revisions and their primary children have to be in the same LOD} \
+	    {and its primary child disagree about their LOD} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.child IS NOT NULL
+		AND   R.child = C.rid
+		AND   C.lod != R.lod
+		;
+	    }
+	# Find all revisions with a non-NTDB child which agrees about
+	# the line of development they belong to.
+	Check \
+	    {NTDB and trunk revisions have to be in different LODs} \
+	    {on NTDB and its non-NTDB child wrongly agree about their LOD} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.dbchild IS NOT NULL
+		AND   R.dbchild = C.rid
+		AND   C.lod = R.lod
+		;
+	    }
+	# Find all revisions with a branch parent symbol which is not
+	# their LOD.
+	Check \
+	    {Branch starting revisions have to have their LOD as branch parent symbol} \
+	    {at the beginning of its branch does not have the branch symbol as its LOD} {
+		SELECT F.name, R.rev
+		FROM revision R, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NOT NULL
+		AND   R.lod != R.bparent
+		;
+	    }
+	# Find all revisions with a branch parent symbol whose parent
+	# is in the same line of development.
+	Check \
+	    {Revisions and their branch children have to be in different LODs} \
+	    {at the beginning of its branch and its parent wrongly agree about their LOD} {
+		SELECT F.name, R.rev
+		FROM revision R, revision P, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NOT NULL
+		AND   R.parent = P.rid
+		AND   R.lod = P.lod
+		;
+	    }
+	return
+    }
+
+    proc Meta {} {
+	# This code performs a number of paranoid checks of the
+	# database, searching for inconsistent cross-references.
+	log write 4 integrity {Check database consistency}
+
+	upvar 1 n n ; # Counter for the checks (we print an id before
+		      # the main label).
+
+	# Find all revisions which disgree with their meta data about
+	# the branch/line of development they belong to.
+	Check \
+	    {Revisions and their meta data have to be in the same LOD} \
+	    {disagrees with its meta data about owning LOD} {
+		SELECT F.name, R.rev
+		FROM revision R, meta M, file F
+		WHERE R.mid = M.mid
+		AND   R.lod != M.bid
+		AND   R.fid = F.fid
+		;
+	    }
+	return
+    }
+
+    proc Check {header label sql} {
+	upvar 1 n n
+	set ok 1
+	foreach {fname revnr} [state run $sql] {
+	    set ok 0
+	    trouble fatal "$fname <$revnr> $label"
+	}
+	log write 5 integrity "\[[format %02d [incr n]]\] [expr {$ok ? "Ok    " : "Failed"}] ... $header"
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs {
+    namespace export integrity
+    namespace eval integrity {
+	namespace import ::vc::fossil::import::cvs::state
+	namespace import ::vc::tools::trouble
+	namespace import ::vc::tools::log
+	log register integrity
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::integrity 1.0
+return

Added tools/cvs2fossil/lib/c2f_option.tcl version [d7008b5e1a]

@@ -1,1 +1,230 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Option database, processes the command line. Note that not all of
+## the option information is stored here. Parts are propagated to
+## other pieces of the system and handled there, via option
+## delegation
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                               ; # Required runtime.
+package require snit                                  ; # OO system.
+package require vc::tools::trouble                    ; # Error reporting.
+package require vc::tools::log                        ; # User feedback.
+package require vc::tools::misc                       ; # Misc. path reformatting.
+package require vc::fossil::import::cvs::pass         ; # Pass management
+package require vc::fossil::import::cvs::pass::collar ; # Pass I.
+package require vc::fossil::import::cvs::repository   ; # Repository management
+package require vc::fossil::import::cvs::state        ; # State storage
+package require vc::fossil::import::cvs::project::sym ; # Project level symbols
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::option {
+    # # ## ### ##### ######## #############
+    ## Public API, Options.
+
+    # --help, --help-passes, -h
+    # --version
+    # -p, --pass, --passes
+    # --ignore-conflicting-attics
+    # --project
+    # -v, --verbose
+    # -q, --quiet
+    # --state (conversion status, ala config.cache)
+    # --trunk-only
+    # --exclude, --force-tag, --force-branch
+
+    # -o, --output
+    # --dry-run
+    # --force-branch RE
+    # --force-tag RE
+    # --symbol-transform RE:XX
+
+    # # ## ### ##### ######## #############
+    ## Public API, Methods
+
+    typemethod process {arguments} {
+	# Syntax of arguments: ?option ?value?...? /path/to/cvs/repository
+
+	while {[IsOption arguments -> option]} {
+	    switch -exact -- $option {
+		-h                          -
+		--help                      { PrintHelp    ; exit 0 }
+		--help-passes               { pass help    ; exit 0 }
+		--version                   { PrintVersion ; exit 0 }
+		-p                          -
+		--pass                      -
+		--passes                    { pass select [Value arguments] }
+		--ignore-conflicting-attics { collar ignore_conflicting_attics }
+		--project                   { repository add [Value arguments] }
+		-v                          -
+		--verbose                   { log verbose }
+		-q                          -
+		--quiet                     { log quiet }
+		--state                     { state use [Value arguments] }
+		--trunk-only                { repository trunkonly! }
+		--exclude                   { project::sym exclude     [Value arguments] }
+		--force-tag                 { project::sym forcetag    [Value arguments] }
+		--force-branch              { project::sym forcebranch [Value arguments] }
+		default {
+		    Usage $badoption$option\n$gethelp
+		}
+	    }
+	}
+
+	if {[llength $arguments] > 1} Usage
+	if {[llength $arguments] < 1} { Usage $nocvs }
+	repository base [striptrailingslash [lindex $arguments 0]]
+
+	Validate
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods, printing information.
+
+    proc PrintHelp {} {
+	global argv0
+	trouble info "Usage: $argv0 $usage"
+	trouble info ""
+	trouble info "  Information options"
+	trouble info ""
+	trouble info "    -h, --help    Print this message and exit with success"
+	trouble info "    --help-passes Print list of passes and exit with success"
+	trouble info "    --version     Print version number of $argv0"
+	trouble info "    -v, --verbose Increase application's verbosity"
+	trouble info "    -q, --quiet   Decrease application's verbosity"
+	trouble info ""
+	trouble info "  Conversion control options"
+	trouble info ""
+	trouble info "    -p, --pass PASS            Run only the specified conversion pass"
+	trouble info "    -p, --passes ?START?:?END? Run only the passes START through END,"
+	trouble info "                               inclusive."
+	trouble info ""
+	trouble info "                               Passes are specified by name."
+	trouble info ""
+	trouble info "    --ignore-conflicting-attics"
+	trouble info "                               Prevent abort when conflicting archives"
+	trouble info "                               were found in both regular and Attic."
+	trouble info ""
+	trouble info "    --state PATH               Save state to the specified file, and"
+	trouble info "                               load state of previous runs from it too."
+	trouble info ""
+	trouble info "    --exclude ?PROJECT:?SYMBOL Exclude the named symbol from all or"
+	trouble info "                               just the specified project. Both project"
+	trouble info "                               and symbol names are glob patterns."
+	trouble info ""
+	trouble info "    --force-tag ?PROJECT:?SYMBOL"
+	trouble info "                               Force the named symbol from all or just"
+	trouble info "                               the specified project to be converted as"
+	trouble info "                               tag. Both project and symbol names are"
+	trouble info "                               glob patterns."
+	trouble info ""
+	trouble info "    --force-branch ?PROJECT:?SYMBOL"
+	trouble info "                               Force the named symbol from all or just"
+	trouble info "                               the specified project to be converted as"
+	trouble info "                               branch. Both project and symbol names"
+	trouble info "                               are glob patterns."
+	trouble info ""
+
+	# --project, --cache
+	# ...
+	return
+    }
+
+    proc PrintVersion {} {
+	global argv0
+	set v [package require vc::fossil::import::cvs]
+	trouble info "$argv0 v$v"
+	return
+    }
+
+    proc Usage {{text {}}} {
+	global argv0
+	trouble fatal "Usage: $argv0 $usage"
+	if {$text ne ""} { trouble fatal "$text" }
+	exit 1
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods, command line processing
+
+    typevariable usage     "?option ?value?...? cvs-repository-path"
+    typevariable nocvs     "       The cvs-repository-path is missing."
+    typevariable badoption "       Bad option "
+    typevariable gethelp   "       Use --help to get help."
+
+    proc IsOption {av _ ov} {
+	upvar 1 $av arguments $ov option
+	set candidate [lindex $arguments 0]
+	if {![string match -* $candidate]} {return 0}
+	set option    $candidate
+	set arguments [lrange $arguments 1 end]
+	return 1
+    }
+
+    proc Value {av} {
+	upvar 1 $av arguments
+	set v         [lindex $arguments 0]
+	set arguments [lrange $arguments 1 end]
+	return $v
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods, state validation
+
+    proc Validate {} {
+	# Prevent in-depth validation if the options were already bad.
+	trouble abort?
+
+	repository validate
+	state      setup
+
+	trouble abort?
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs {
+    namespace export option
+    namespace eval option {
+	namespace import ::vc::tools::misc::striptrailingslash
+	namespace import ::vc::fossil::import::cvs::pass
+	namespace import ::vc::fossil::import::cvs::pass::collar
+	namespace import ::vc::fossil::import::cvs::repository
+	namespace import ::vc::fossil::import::cvs::state
+	namespace eval project {
+	    namespace import ::vc::fossil::import::cvs::project::sym
+	}
+	namespace import ::vc::tools::trouble
+	namespace import ::vc::tools::log
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::option 1.0
+return

Added tools/cvs2fossil/lib/c2f_pass.tcl version [6faa3b2682]

@@ -1,1 +1,213 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Pass manager. All passes register here, with code, description, and
+## callbacks (... setup, run, finalize). Option processing and help
+## query this manager to dynamically create the relevant texts.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                         ; # Required runtime.
+package require snit                            ; # OO system.
+package require vc::fossil::import::cvs::state  ; # State storage
+package require vc::tools::misc                 ; # Text formatting
+package require vc::tools::trouble              ; # Error reporting.
+package require vc::tools::log                  ; # User feedback.
+package require struct::list                    ; # Portable lassign
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::pass {
+    # # ## ### ##### ######## #############
+    ## Public API, Methods (Setup, query)
+
+    typemethod define {name description command} {
+	if {[info exists mydesc($name)]} {
+	    trouble internal "Multiple definitions for pass code '$name'"
+	}
+	lappend mypasses $name
+	set mydesc($name) $description
+	set mycmd($name)  $command
+	return
+    }
+
+    typemethod help {} {
+	trouble info ""
+	trouble info "Conversion passes:"
+	trouble info ""
+	set n 0
+
+	set clen [max [struct::list map $mypasses {string length}]]
+	set cfmt %-${clen}s
+	set nfmt %[string length [llength $mypasses]]s
+
+	foreach code $mypasses {
+	    trouble info "  [format $nfmt $n]: [format $cfmt $code] : $mydesc($code)"
+	    incr n
+	}
+	trouble info ""
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Public API, Methods (Execution)
+
+    typemethod select {passdef} {
+	set pl [split $passdef :]
+	if {[llength $pl] > 2} {
+	    trouble fatal "Bad pass definition '$passdef'"
+	    trouble fatal "Expected at most one ':'"
+	} elseif {[llength $pl] == 2} {
+	    struct::list assign $pl start end
+
+	    if {($start eq "") && ($end eq "")} {
+		trouble fatal "Specify at least one of start- or end-pass"
+		set ok 0
+	    } else {
+		set ok 1
+		Ok? $start start ok
+		Ok? $end   end   ok
+	    }
+
+	    if {$ok} {
+		set mystart [Convert $start 0]
+		set myend   [Convert $end end]
+		if {$mystart > $myend} {
+		    trouble fatal "Start pass is after end pass"
+		}
+	    }
+	} elseif {[llength $pl] < 2} {
+	    set start [lindex $pl 0]
+	    Ok? $start "" __dummy__ 0
+	    set mystart [Id $start]
+	    set myend   $mystart
+	}
+    }
+
+    typemethod run {} {
+	if {$mystart < 0} {set mystart 0}
+	if {$myend   < 0} {set myend [expr {[llength $mypasses] - 1}]}
+
+	set skipped [lrange $mypasses 0 [expr {$mystart - 1}]]
+	set run     [lrange $mypasses $mystart $myend]
+	set defered [lrange $mypasses [expr {$myend + 1}] end]
+
+	foreach p $skipped {
+	    log write 0 pass "Skip  $p"
+	    Call $p load
+	}
+	foreach p $run {
+	    log write 0 pass "Setup $p"
+	    Call $p setup
+	}
+	foreach p $run {
+	    log write 0 pass "Begin $p"
+	    Time $p [lindex [time {Call $p run} 1] 0]
+	    log write 0 pass "Done  $p"
+	    trouble abort?
+	}
+	foreach p $defered {
+	    log write 0 pass "Defer $p"
+	    Call $p discard
+	}
+
+	state release
+	ShowTimes
+	return
+    }
+
+    proc Time {pass useconds} {
+	::variable mytime
+	lappend    mytime $pass $useconds
+	return
+    }
+
+    proc ShowTimes {} {
+	::variable mytime
+	foreach {pass useconds} $mytime {
+	    set sec [format %8.2f [expr {double($useconds)/1e6}]]
+	    log write 0 pass "$sec sec/$pass"
+	}
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    proc Ok? {code label ov {emptyok 1}} {
+	upvar 1 $ov ok
+	::variable mydesc
+	if {$emptyok && ($code eq "")} return
+	if {[info exists mydesc($code)]} return
+	if {$label ne ""} {append label " "}
+	trouble fatal "Bad ${label}pass code $code"
+	set ok 0
+	return
+    }
+
+    proc Convert {code default} {
+	::variable mypasses
+	return [expr {($code eq "") ? $default : [Id $code]}]
+    }
+
+    proc Id {code} {
+	::variable mypasses
+	return [lsearch -exact $mypasses $code]
+    }
+
+    proc Call {code args} {
+	::variable mycmd
+	set cmd $mycmd($code)
+	foreach a $args { lappend cmd $a }
+	eval $cmd
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal, state
+
+    typevariable mypasses      {} ; # List of registered passes (codes).
+    typevariable mydesc -array {} ; # Pass descriptions (one line).
+    typevariable mycmd  -array {} ; # Pass callback command.
+
+    typevariable mystart -1
+    typevariable myend   -1
+    typevariable mytime  {} ; # Timing data for each executed pass.
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs {
+    namespace export pass
+    namespace eval pass {
+	namespace import ::vc::fossil::import::cvs::state
+	namespace import ::vc::tools::misc::*
+	namespace import ::vc::tools::trouble
+	namespace import ::vc::tools::log
+	log register pass
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::pass 1.0
+return

Added tools/cvs2fossil/lib/c2f_pbreakacycle.tcl version [9a96d44941]

@@ -1,1 +1,101 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Pass VIII. This is the final pass for breaking changeset dependency
+## cycles. The two previous passes broke cycles covering revision and
+## symbol changesets, respectively. This pass now breaks any remaining
+## cycles each of which has to contain at least one revision and at
+## least one symbol changeset.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                                   ; # Required runtime.
+package require snit                                      ; # OO system.
+package require struct::list                              ; # Higher order list operations.
+package require vc::tools::log                            ; # User feedback.
+package require vc::fossil::import::cvs::cyclebreaker     ; # Breaking dependency cycles.
+package require vc::fossil::import::cvs::state            ; # State storage.
+package require vc::fossil::import::cvs::project::rev     ; # Project level changesets
+
+# # ## ### ##### ######## ############# #####################
+## Register the pass with the management
+
+vc::fossil::import::cvs::pass define \
+    BreakAllCsetCycles \
+    {Break Remaining ChangeSet Dependency Cycles} \
+    ::vc::fossil::import::cvs::pass::breakacycle
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::pass::breakacycle {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod setup {} {
+	# Define the names and structure of the persistent state of
+	# this pass.
+	return
+    }
+
+    typemethod load {} {
+	# Pass manager interface. Executed to load data computed by
+	# this pass into memory when this pass is skipped instead of
+	# executed.
+	return
+    }
+
+    typemethod run {} {
+	# Pass manager interface. Executed to perform the
+	# functionality of the pass.
+	return
+    }
+
+    typemethod discard {} {
+	# Pass manager interface. Executed for all passes after the
+	# run passes, to remove all data of this pass from the state,
+	# as being out of date.
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::pass {
+    namespace export breakacycle
+    namespace eval breakacycle {
+	namespace import ::vc::fossil::import::cvs::cyclebreaker
+	namespace import ::vc::fossil::import::cvs::state
+	namespace eval project {
+	    namespace import ::vc::fossil::import::cvs::project::rev
+	}
+	namespace import ::vc::tools::log
+	log register breakacycle
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::pass::breakacycle 1.0
+return

Added tools/cvs2fossil/lib/c2f_pbreakrcycle.tcl version [030100362a]

@@ -1,1 +1,141 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Pass VI. This pass goes over the set of revision based changesets
+## and breaks all dependency cycles they may be in. We need a
+## dependency tree. Identical to pass VII, except for the selection of
+## the changesets.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                                   ; # Required runtime.
+package require snit                                      ; # OO system.
+package require struct::list                              ; # Higher order list operations.
+package require vc::tools::log                            ; # User feedback.
+package require vc::fossil::import::cvs::repository       ; # Repository management.
+package require vc::fossil::import::cvs::cyclebreaker     ; # Breaking dependency cycles.
+package require vc::fossil::import::cvs::state            ; # State storage.
+package require vc::fossil::import::cvs::project::rev     ; # Project level changesets
+
+# # ## ### ##### ######## ############# #####################
+## Register the pass with the management
+
+vc::fossil::import::cvs::pass define \
+    BreakRevCsetCycles \
+    {Break Revision ChangeSet Dependency Cycles} \
+    ::vc::fossil::import::cvs::pass::breakrcycle
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::pass::breakrcycle {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod setup {} {
+	# Define the names and structure of the persistent state of
+	# this pass.
+
+	state reading revision
+	state reading changeset
+	state reading csrevision
+
+	state writing csorder {
+	    -- Commit order of changesets based on their dependencies
+	    cid INTEGER  NOT NULL  REFERENCES changeset,
+	    pos INTEGER  NOT NULL,
+	    UNIQUE (cid),
+	    UNIQUE (pos)
+	}
+	return
+    }
+
+    typemethod load {} {
+	# Pass manager interface. Executed to load data computed by
+	# this pass into memory when this pass is skipped instead of
+	# executed.
+
+	state reading changeset
+	project::rev loadcounter
+	return
+    }
+
+    typemethod run {} {
+	# Pass manager interface. Executed to perform the
+	# functionality of the pass.
+
+	state transaction {
+	    cyclebreaker run [struct::list filter [project::rev all] \
+				  [myproc IsByRevision]] \
+		[myproc SaveOrder]
+	}
+
+	repository printcsetstatistics
+	return
+    }
+
+    typemethod discard {} {
+	# Pass manager interface. Executed for all passes after the
+	# run passes, to remove all data of this pass from the state,
+	# as being out of date.
+
+	state discard csorder
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    proc IsByRevision {cset} { $cset byrevision }
+
+    proc SaveOrder {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
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::pass {
+    namespace export breakrcycle
+    namespace eval breakrcycle {
+	namespace import ::vc::fossil::import::cvs::cyclebreaker
+	namespace import ::vc::fossil::import::cvs::repository
+	namespace import ::vc::fossil::import::cvs::state
+	namespace eval project {
+	    namespace import ::vc::fossil::import::cvs::project::rev
+	}
+	namespace import ::vc::tools::log
+	log register breakrcycle
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::pass::breakrcycle 1.0
+return

Added tools/cvs2fossil/lib/c2f_pbreakscycle.tcl version [a1d5600281]

@@ -1,1 +1,112 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Pass VII. This pass goes over the set of symbol based changesets
+## and breaks all dependency cycles they may be in. We need a
+## dependency tree. Identical to pass VI, except for the selection of
+## the changesets.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                                   ; # Required runtime.
+package require snit                                      ; # OO system.
+package require struct::list                              ; # Higher order list operations.
+package require vc::fossil::import::cvs::cyclebreaker     ; # Breaking dependency cycles.
+package require vc::fossil::import::cvs::repository       ; # Repository management.
+package require vc::fossil::import::cvs::state            ; # State storage.
+package require vc::fossil::import::cvs::project::rev     ; # Project level changesets
+
+# # ## ### ##### ######## ############# #####################
+## Register the pass with the management
+
+vc::fossil::import::cvs::pass define \
+    BreakSymCsetCycles \
+    {Break Symbol ChangeSet Dependency Cycles} \
+    ::vc::fossil::import::cvs::pass::breakscycle
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::pass::breakscycle {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod setup {} {
+	# Define the names and structure of the persistent state of
+	# this pass.
+
+	state reading revision
+	state reading changeset
+	state reading csrevision
+	return
+    }
+
+    typemethod load {} {
+	# Pass manager interface. Executed to load data computed by
+	# this pass into memory when this pass is skipped instead of
+	# executed.
+	return
+    }
+
+    typemethod run {} {
+	# Pass manager interface. Executed to perform the
+	# functionality of the pass.
+
+	state transaction {
+	    cyclebreaker run [struct::list filter [project::rev all] \
+				  [myproc IsBySymbol]]
+	}
+
+	repository printcsetstatistics
+	return
+    }
+
+    typemethod discard {} {
+	# Pass manager interface. Executed for all passes after the
+	# run passes, to remove all data of this pass from the state,
+	# as being out of date.
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    proc IsBySymbol {cset} { $cset bysymbol }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::pass {
+    namespace export breakscycle
+    namespace eval breakscycle {
+	namespace import ::vc::fossil::import::cvs::cyclebreaker
+	namespace import ::vc::fossil::import::cvs::repository
+	namespace import ::vc::fossil::import::cvs::state
+	namespace eval project {
+	    namespace import ::vc::fossil::import::cvs::project::rev
+	}
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::pass::breakscycle 1.0
+return

Added tools/cvs2fossil/lib/c2f_pcollar.tcl version [32b24a90eb]

@@ -1,1 +1,266 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Pass I. This pass scans the repository to import for RCS archives,
+## and sorts and filters them into the declared projects, if any
+## Without declared projects the whole repository is treated as a
+## single project.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                             ; # Required runtime.
+package require snit                                ; # OO system.
+package require fileutil::traverse                  ; # Directory traversal.
+package require fileutil                            ; # File & path utilities.
+package require vc::tools::trouble                  ; # Error reporting.
+package require vc::tools::log                      ; # User feedback.
+package require vc::fossil::import::cvs::pass       ; # Pass management.
+package require vc::fossil::import::cvs::repository ; # Repository management.
+package require vc::fossil::import::cvs::state      ; # State storage
+
+# # ## ### ##### ######## ############# #####################
+## Register the pass with the management
+
+vc::fossil::import::cvs::pass define \
+    CollectAr \
+    {Collect archives in repository} \
+    ::vc::fossil::import::cvs::pass::collar
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::pass::collar {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod setup {} {
+	# Define names and structure of the persistent state of this
+	# pass.
+
+	# We deal with repository projects, and the rcs archive files
+	# in the projects.
+
+	# For the first, projects, we keep their names, which are
+	# their paths relative to the base directory of the whole
+	# repository. These have to be globally unique, i.e. no two
+	# projects can have the same name.
+
+	# For the files we keep their names, which are their paths
+	# relative to the base directory of the whole project! These
+	# have to be unique within a project, however globally this
+	# does not hold, a name may occur several times, in different
+	# projects. We further store the user visible file name
+	# associated with the rcs archive.
+
+	# Both projects and files are identified by globally unique
+	# integer ids, automatically assigned by the database.
+
+	state writing project {
+	    pid  INTEGER  NOT NULL  PRIMARY KEY AUTOINCREMENT,
+	    name TEXT     NOT NULL  UNIQUE
+	}
+	state writing file {
+	    fid     INTEGER  NOT NULL  PRIMARY KEY AUTOINCREMENT,
+	    pid     INTEGER  NOT NULL  REFERENCES project,       -- project the file belongs to
+	    name    TEXT     NOT NULL,
+	    visible TEXT     NOT NULL,
+	    exec    INTEGER  NOT NULL, -- boolean, 'file executable'.
+	    UNIQUE (pid, name)         -- file names are unique within a project
+	}
+	return
+    }
+
+    typemethod load {} {
+	# Pass manager interface. Executed for all passes before the
+	# run passes, to load all data of their pass from the state,
+	# as if it had been computed by the pass itself.
+
+	state reading project
+	state reading file
+
+	repository load
+	return
+    }
+
+    typemethod run {} {
+	# Pass manager interface. Executed to perform the
+	# functionality of the pass.
+
+	set rbase [repository base?]
+	foreach project [repository projects] {
+	    set base [file join $rbase [$project base]]
+	    log write 1 collar "Scan $base"
+
+	    set traverse [fileutil::traverse %AUTO% $base \
+			      -prefilter [myproc FilterAtticSubdir $base]]
+	    set n 0
+	    set r {}
+
+	    $traverse foreach path {
+		set rcs [fileutil::stripPath $base $path]
+		if {[IsCVSAdmin    $rcs]}  continue
+		if {![IsRCSArchive $path]} continue
+
+		set usr [UserPath $rcs isattic]
+		if {[IsSuperceded $base $rcs $usr $isattic]} continue
+
+		if {
+		    [file exists      $base/$usr] &&
+		    [file isdirectory $base/$usr]
+		} {
+		    trouble fatal "Directory name conflicts with filename."
+		    trouble fatal "Please remove or rename one of the following:"
+		    trouble fatal "    $base/$usr"
+		    trouble fatal "    $base/$rcs"
+		    continue
+		}
+
+		log write 4 collar "Found   $rcs"
+		$project addfile $rcs $usr [file executable $rcs]
+
+		incr n
+		if {[log verbosity?] < 4} {
+		    log progress 0 collar $n {}
+		}
+	    }
+
+	    $traverse destroy
+	}
+
+	repository printstatistics
+	repository persist
+
+	log write 1 collar "Scan completed"
+	return
+    }
+
+    typemethod discard {} {
+	# Pass manager interface. Executed for all passes after the
+	# run passes, to remove all data of this pass from the state,
+	# as being out of date.
+
+	state discard project
+	state discard file
+	return
+    }
+
+    typemethod ignore_conflicting_attics {} {
+	set myignore 1
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    typevariable myignore 0
+
+    proc FilterAtticSubdir {base path} {
+	# This command is used by the traverser to prevent it from
+	# scanning into subdirectories of an Attic. We get away with
+	# checking the immediate parent directory of the current path
+	# as our rejection means that deeper path do not occur.
+
+	if {[file tail [file dirname $path]] eq "Attic"} {
+	    set ad [fileutil::stripPath $base $path]
+	    log write 1 collar "Directory $ad found in Attic, ignoring."
+	    return 0
+	}
+	return 1
+    }
+
+    proc IsRCSArchive {path} {
+	if {![string match *,v $path]}     {return 0}
+	if {[fileutil::test $path fr msg]} {return 1}
+	trouble warn $msg
+	return 0
+    }
+
+    proc IsCVSAdmin {rcs} {
+	if {![string match CVSROOT/* $rcs]} {return 0}
+	log write 4 collar "Ignored $rcs, administrative archive"
+	return 1
+    }
+
+    proc UserPath {rcs iav} {
+	upvar 1 $iav isattic
+
+	# Derive the user-visible path from the rcs path. Meaning:
+	# Chop off the ",v" suffix, and remove a possible "Attic".
+
+	set f [string range $rcs 0 end-2]
+
+	if {"Attic" eq [lindex [file split $rcs] end-1]} {
+
+	    # The construction below ensures that Attic/X maps to X
+	    # instead of ./X. Otherwise, Y/Attic/X maps to Y/X.
+
+	    set fx [file dirname [file dirname $f]]
+	    set f  [file tail $f]
+	    if {$fx ne "."} { set f [file join $fx $f] }
+
+	    set isattic 1
+	} else {
+	    set isattic 0
+	}
+
+	return $f
+    }
+
+    proc IsSuperceded {base rcs usr isattic} {
+	::variable myignore
+
+	if {!$isattic}                   {return 0}
+	if {![file exists $base/$usr,v]} {return 0}
+
+	# We have a regular archive and an Attic archive refering to
+	# the same user visible file. Ignore the file in the Attic.
+	#
+	# By default this is a problem causing an abort after the pass
+	# has completed. The user can however force us to ignore it.
+	# In that case the warning is still printed, but will not
+	# induce an abort any longer.
+
+	if {$myignore} {
+	    log write 2 collar "Ignored $rcs, superceded archive"
+	} else {
+	    trouble warn       "Ignored $rcs, superceded archive"
+	}
+	return 1
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::pass {
+    namespace export collar
+    namespace eval collar {
+	namespace import ::vc::fossil::import::cvs::repository
+	namespace import ::vc::fossil::import::cvs::state
+	namespace import ::vc::tools::trouble
+	namespace import ::vc::tools::log
+	log register collar
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::pass::collar 1.0
+return

Added tools/cvs2fossil/lib/c2f_pcollrev.tcl version [e9c8eea44f]

@@ -1,1 +1,644 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Pass II. This pass parses the collected rcs archives and extracts
+## all the information they contain (revisions, and symbols).
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                               ; # Required runtime.
+package require snit                                  ; # OO system.
+package require vc::tools::trouble                    ; # Error reporting.
+package require vc::tools::log                        ; # User feedback.
+package require vc::fossil::import::cvs::pass         ; # Pass management.
+package require vc::fossil::import::cvs::repository   ; # Repository management.
+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::project::sym ; # Project level symbols.
+package require vc::fossil::import::cvs::file::rev    ; # File level revisions.
+package require vc::rcs::parser                       ; # Rcs archive data extraction.
+
+# # ## ### ##### ######## ############# #####################
+## Register the pass with the management
+
+vc::fossil::import::cvs::pass define \
+    CollectRev \
+    {Collect revisions and symbols} \
+    ::vc::fossil::import::cvs::pass::collrev
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::pass::collrev {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod setup {} {
+	# Define names and structure of the persistent state of this
+	# pass.
+
+	state reading project
+	state reading file
+
+	# We deal with per project and per file data, the first
+	# collated from the second.
+
+	# Per file we have general information, ..., and then
+	# revisions and symbols. The latter can be further separated
+	# into tags and branches. At project level the per-file
+	# symbols information is merged.
+
+	# File level ...
+	#	Revisions, Branches, Tags
+	#
+	# Pseudo class hierarchy
+	#	Tag      <- Symbol <- Event
+	#	Branch   <- Symbol <- Event
+	#	Revision           <- Event
+
+	state writing revision {
+	    -- Revisions. Identified by a global numeric id each
+	    -- belongs to a single file, identified by its id. It
+	    -- further has a dotted revision number (DTN).
+	    --
+	    -- Constraint: The dotted revision number is unique within
+            --             the file. See end of definition.
+
+	    rid  INTEGER  NOT NULL  PRIMARY KEY AUTOINCREMENT,
+	    fid  INTEGER  NOT NULL  REFERENCES file,   -- File owning revision.
+	    rev  TEXT     NOT NULL,                    -- Dotted Rev Number.
+
+	    -- All revisions belong to a line-of-development,
+	    -- identified by a symbol (project level). During data
+	    -- collection it was a file-level branch symbol.
+	    --
+	    -- Constraint: All the LOD symbols are in the same project
+	    --             as the file itself. This cannot be
+	    --             expressed in CREATE TABLE syntax.
+
+	    lod  INTEGER  NOT NULL  REFERENCES symbol, -- Line of development
+
+	    -- The revisions in a file are organized in a forest of
+	    -- trees, with the main lines defined through the parent /
+	    -- child references. A revision without a parent is the
+	    -- root of a tree, and a revision without a child is a
+	    -- leaf.
+
+	    -- Constraints: All revisions coupled through parent/child
+	    --              refer to the same LOD symbol. The parent
+	    --              of a child of X is X. The child of a
+	    --              parent of X is X.
+
+	    parent  INTEGER            REFERENCES revision,
+	    child   INTEGER            REFERENCES revision,
+
+	    -- The representation of a branch in a tree is the
+	    -- exception to the three constraints above.
+
+	    -- The beginning of a branch is represented by a non-NULL
+	    -- bparent of a revision. This revision B is the first on
+	    -- the branch. Its parent P is the revision the branch is
+	    -- rooted in, and it is not the child of P. B and P refer
+	    -- to different LOD symbols. The bparent of B is also its
+	    -- LOD, and the LOD of its children.
+
+	    bparent INTEGER            REFERENCES symbol,
+
+	    -- Lastly we keep information is about non-trunk default
+	    -- branches (NTDB) in the revisions.
+
+	    -- All revisions on the NTDB have 'isdefault' TRUE,
+	    -- everyone else FALSE. The last revision X on the NTDB
+	    -- which is still considered to be on the trunk as well
+	    -- has a non-NULL 'dbchild' which refers to the root of
+	    -- the trunk. The root also has a non-NULL dbparent
+	    -- refering to X.
+
+	    isdefault INTEGER  NOT NULL,
+	    dbparent  INTEGER            REFERENCES revision,
+	    dbchild   INTEGER            REFERENCES revision,
+
+	    -- The main payload of the revision are the date/time it
+	    -- was entered, its state, operation (= type/class), text
+	    -- content, and meta data (author, log message, branch,
+	    -- project). The last is encoded as single id, see table
+	    -- 'meta'. The date/time is given in seconds since the
+	    -- epoch, for easy comparison. The text content is an
+	    -- (offset,length) pair into the rcs archive.
+
+	    op    INTEGER  NOT NULL  REFERENCES optype,
+	    date  INTEGER  NOT NULL,
+	    state TEXT     NOT NULL,
+	    mid   INTEGER  NOT NULL  REFERENCES meta,
+	    coff  INTEGER  NOT NULL,
+	    clen  INTEGER  NOT NULL,
+
+	    UNIQUE (fid, rev) -- The DTN is unique within the revision's file.
+	}
+
+	state writing optype {
+	    oid   INTEGER  NOT NULL  PRIMARY KEY,
+	    name  TEXT     NOT NULL,
+	    UNIQUE(name)
+	}
+	state run {
+	    INSERT INTO optype VALUES (-1,'delete');  -- The opcode names are the
+	    INSERT INTO optype VALUES ( 0,'nothing'); -- fixed pieces, see myopstate
+	    INSERT INTO optype VALUES ( 1,'add');     -- in file::rev. myopcode is
+	    INSERT INTO optype VALUES ( 2,'change');  -- loaded from this.
+	}
+
+	state writing revisionbranchchildren {
+	    -- The non-primary children of a revision, as reachable
+	    -- through a branch symbol, are listed here. This is
+	    -- needed by pass 5 to break internal dependencies in a
+	    -- changeset.
+
+	    rid   INTEGER  NOT NULL  REFERENCES revision,
+	    brid  INTEGER  NOT NULL  REFERENCES revision,
+	    UNIQUE(rid,brid)
+	}
+
+	state writing tag {
+	    tid  INTEGER  NOT NULL  PRIMARY KEY AUTOINCREMENT,
+	    fid  INTEGER  NOT NULL  REFERENCES file,     -- File the item belongs to
+	    lod  INTEGER            REFERENCES symbol,   -- Line of development (NULL => Trunk)
+	    sid  INTEGER  NOT NULL  REFERENCES symbol,   -- Symbol capturing the tag
+
+	    rev  INTEGER  NOT NULL  REFERENCES revision  -- The revision being tagged.
+	}
+
+	state writing branch {
+	    bid   INTEGER  NOT NULL  PRIMARY KEY AUTOINCREMENT,
+	    fid   INTEGER  NOT NULL  REFERENCES file,     -- File the item belongs to
+	    lod   INTEGER            REFERENCES symbol,   -- Line of development (NULL => Trunk)
+	    sid   INTEGER  NOT NULL  REFERENCES symbol,   -- Symbol capturing the branch
+
+	    root  INTEGER  NOT NULL  REFERENCES revision, -- Revision the branch sprouts from
+	    first INTEGER            REFERENCES revision, -- First revision committed to the branch
+	    bra   TEXT     NOT NULL,                      -- branch number
+	    pos   INTEGER  NOT NULL                       -- creation order in root.
+	}
+
+	# Project level ...
+	#	pLineOfDevelopment, pSymbol, pBranch, pTag, pTrunk
+	#
+	#	pTrunk  <- pLineOfDevelopment
+	#	pBranch <- pSymbol, pLineOfDevelopment
+	#	pTag    <- pSymbol, pLineOfDevelopment
+
+	state writing symbol {
+	    sid  INTEGER  NOT NULL  PRIMARY KEY AUTOINCREMENT,
+	    pid  INTEGER  NOT NULL  REFERENCES project,  -- Project the symbol belongs to
+	    name TEXT     NOT NULL,
+	    type INTEGER  NOT NULL  REFERENCES symtype,  -- enum { excluded = 0, tag, branch, undefined }
+
+	    tag_count    INTEGER  NOT NULL, -- How often the symbol is used as tag.
+	    branch_count INTEGER  NOT NULL, -- How often the symbol is used as branch
+	    commit_count INTEGER  NOT NULL, -- How often a file was committed on the symbol
+
+	    UNIQUE (pid, name) -- Symbols are unique within the project
+	}
+
+	state writing blocker {
+	    -- For each symbol we save which other symbols are
+	    -- blocking its removal (if the user asks for it).
+
+	    sid INTEGER  NOT NULL  REFERENCES symbol, --
+	    bid INTEGER  NOT NULL  REFERENCES symbol, -- Sprouted from sid, blocks it.
+	    UNIQUE (sid, bid)
+	}
+
+	state writing parent {
+	    -- For each symbol we save which other symbols can act as
+	    -- a possible parent in some file, and how often.
+
+	    sid INTEGER  NOT NULL  REFERENCES symbol, --
+	    pid INTEGER  NOT NULL  REFERENCES symbol, -- Possible parent of sid
+	    n   INTEGER  NOT NULL,                    -- How often pid can act as parent.
+	    UNIQUE (sid, pid)
+	}
+
+	state writing symtype {
+	    tid    INTEGER  NOT NULL  PRIMARY KEY,
+	    name   TEXT     NOT NULL,
+	    plural TEXT     NOT NULL,
+	    UNIQUE (name)
+	    UNIQUE (plural)
+	}
+	state run {
+	    INSERT INTO symtype VALUES (0,'excluded', 'excluded');
+	    INSERT INTO symtype VALUES (1,'tag',      'tags');
+	    INSERT INTO symtype VALUES (2,'branch',   'branches');
+	    INSERT INTO symtype VALUES (3,'undefined','undefined');
+	}
+
+	state writing meta {
+	    -- Meta data of revisions. See revision.mid for the
+	    -- reference. Many revisions can share meta data. This is
+	    -- actually one of the criterions used to sort revisions
+	    -- into changesets.
+
+	    mid INTEGER  NOT NULL  PRIMARY KEY  AUTOINCREMENT,
+
+	    -- Meta data belongs to a specific project, stronger, to a
+	    -- branch in that project. It further has a log message,
+	    -- and its author. This is unique with the project and
+	    -- branch.
+
+	    pid INTEGER  NOT NULL  REFERENCES project,  --
+	    bid INTEGER  NOT NULL  REFERENCES symbol,   --
+	    aid INTEGER  NOT NULL  REFERENCES author,   --
+	    cid INTEGER  NOT NULL  REFERENCES cmessage, --
+
+	    UNIQUE (pid, bid, aid, cid)
+
+	    -- Constraints: The project of the meta data of a revision
+	    --              X is the same as the project of X itself.
+	    --
+	    -- ............ The branch of the meta data of a revision
+	    --              X is the same as the line of development
+	    --              of X itself.
+	}
+
+	# Authors and commit messages are fully global, i.e. per
+	# repository.
+
+	state writing author {
+	    aid  INTEGER  NOT NULL  PRIMARY KEY  AUTOINCREMENT,
+	    name TEXT     NOT NULL  UNIQUE
+	}
+
+	state writing cmessage {
+	    cid  INTEGER  NOT NULL  PRIMARY KEY  AUTOINCREMENT,
+	    text TEXT     NOT NULL  UNIQUE
+	}
+
+	project::sym getsymtypes
+	file::rev    getopcodes
+	return
+    }
+
+    typemethod load {} {
+	state reading symbol
+	state reading symtype
+	state reading optype
+
+	project::sym getsymtypes
+	file::rev    getopcodes
+	repository   loadsymbols
+	return
+    }
+
+    typemethod run {} {
+	# Pass manager interface. Executed to perform the
+	# functionality of the pass.
+
+	set rbase [repository base?]
+	foreach project [repository projects] {
+	    set base [file join $rbase [$project base]]
+	    log write 1 collrev "Processing $base"
+
+	    foreach file [$project files] {
+		set path [$file path]
+		log write 2 collrev "Parsing $path"
+		if {[catch {
+		    parser process [file join $base $path] $file
+		} msg]} {
+		    global errorCode
+		    if {$errorCode eq "vc::rcs::parser"} {
+			trouble fatal "$path is not a valid RCS archive ($msg)"
+		    } else {
+			global errorInfo
+			trouble internal $errorInfo
+		    }
+		} else {
+		    # We persist the core of the data collected about
+		    # each file immediately after it has been parsed
+		    # and wrangled into shape, and then drop it from
+		    # memory. This is done to keep the amount of
+		    # required memory within sensible limits. Without
+		    # doing it this way we would easily gobble up 1G
+		    # of RAM or more with all the objects (revisions
+		    # and file-level symbols).
+
+		    $file persist
+		}
+
+		$file drop
+	    }
+
+	    $project purgeghostsymbols
+	}
+
+	repository persistrev
+	repository printrevstatistics
+	integrity  strict
+
+	log write 1 collrev "Scan completed"
+	return
+    }
+
+    typemethod discard {} {
+	# Pass manager interface. Executed for all passes after the
+	# run passes, to remove all data of this pass from the state,
+	# as being out of date.
+
+	state discard revision
+	state discard tag
+	state discard branch
+	state discard symbol
+	state discard blocker
+	state discard parent
+	state discard symtype
+	state discard meta
+	state discard author
+	state discard cmessage
+	return
+    }
+
+    proc Paranoia {} {
+	# This code performs a number of paranoid checks of the
+	# database, searching for inconsistent cross-references.
+	log write 4 collrev {Check database consistency}
+
+	set n 0 ; # Counter for the checks (we print an id before the
+		  # main label).
+
+	# Find all revisions which disagree with their line of
+	# development about the project they are owned by.
+	Check \
+	    {Revisions and their LODs have to be in the same project} \
+	    {disagrees with its LOD about owning project} {
+		SELECT F.name, R.rev
+		FROM revision R, file F, symbol S
+		WHERE R.fid = F.fid
+		AND   R.lod = S.sid
+		AND   F.pid != S.pid
+		;
+	    }
+	# Find all revisions which disgree with their meta data about
+	# the project they are owned by.
+	Check \
+	    {Revisions and their meta data have to be in the same project} \
+	    {disagrees with its meta data about owning project} {
+		SELECT F.name, R.rev
+		FROM revision R, file F, meta M
+		WHERE R.fid = F.fid
+		AND   R.mid = M.mid
+		AND   F.pid != M.pid
+		;
+	    }
+	# Find all revisions which disgree with their meta data about
+	# the branch/line of development they belong to.
+	Check \
+	    {Revisions and their meta data have to be in the same LOD} \
+	    {disagrees with its meta data about owning LOD} {
+		SELECT F.name, R.rev
+		FROM revision R, meta M, file F
+		WHERE R.mid = M.mid
+		AND   R.lod != M.bid
+		AND   R.fid = F.fid
+		;
+	    }
+	# Find all revisions with a primary child which disagrees
+	# about the file they belong to.
+	Check \
+	    {Revisions and their primary children have to be in the same file} \
+	    {disagrees with its primary child about the owning file} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.child IS NOT NULL
+		AND   R.child = C.rid
+		AND   C.fid != R.fid
+		;
+	    }
+
+	# Find all revisions with a branch parent symbol whose parent
+	# disagrees about the file they belong to.
+	Check \
+	    {Revisions and their branch children have to be in the same file} \
+	    {at the beginning of its branch and its parent disagree about the owning file} {
+		SELECT F.name, R.rev
+		FROM revision R, revision P, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NOT NULL
+		AND   R.parent = P.rid
+		AND   R.fid != P.fid
+		;
+	    }
+	# Find all revisions with a non-NTDB child which disagrees
+	# about the file they belong to.
+	Check \
+	    {Revisions and their non-NTDB children have to be in the same file} \
+	    {disagrees with its non-NTDB child about the owning file} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.dbchild IS NOT NULL
+		AND   R.dbchild = C.rid
+		AND   C.fid != R.fid
+		;
+	    }
+	# Find all revisions which have a primary child, but the child
+	# does not have them as parent.
+	Check \
+	    {Revisions have to be parents of their primary children} \
+	    {is not the parent of its primary child} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.child IS NOT NULL
+		AND   R.child = C.rid
+		AND   C.parent != R.rid
+		;
+	    }
+	# Find all revisions which have a primrary child, but the
+	# child has a branch parent symbol making them brach starters.
+	Check \
+	    {Primary children of revisions must not start branches} \
+	    {is parent of a primary child which is the beginning of a branch} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.child IS NOT NULL
+		AND   R.child = C.rid
+		AND   C.bparent IS NOT NULL
+		;
+	    }
+	# Find all revisions without branch parent symbol which have a
+	# parent, but the parent does not have them as primary child.
+	Check \
+	    {Revisions have to be primary children of their parents, if any} \
+	    {is not the child of its parent} {
+		SELECT F.name, R.rev
+		FROM revision R, revision P, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NULL
+		AND   R.parent IS NOT NULL
+		AND   R.parent = P.rid
+		AND   P.child != R.rid
+		;
+	    }
+	# Find all revisions with a branch parent symbol which do not
+	# have a parent.
+	Check \
+	    {Branch starting revisions have to have a parent} \
+	    {at the beginning of its branch has no parent} {
+		SELECT F.name, R.rev
+		FROM revision R, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NOT NULL
+		AND   R.parent IS NULL
+		;
+	    }
+	# Find all revisions with a branch parent symbol whose parent
+	# has them as primary child.
+	Check \
+	    {Branch starting revisions must not be primary children of their parents} \
+	    {at the beginning of its branch is the primary child of its parent} {
+		SELECT F.name, R.rev
+		FROM revision R, revision P, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NOT NULL
+		AND   R.parent IS NOT NULL
+		AND   R.parent = P.rid
+		AND   P.child = R.rid
+		;
+	    }
+	# Find all revisions with a non-NTDB child which are not on
+	# the NTDB.
+	Check \
+	    {NTDB to trunk transition has to begin on NTDB} \
+	    {has a non-NTDB child, yet is not on the NTDB} {
+		SELECT F.name, R.rev
+		FROM revision R, file F
+		WHERE R.fid = F.fid
+		AND   R.dbchild IS NOT NULL
+		AND   NOT R.isdefault
+		;
+	    }
+	# Find all revisions with a NTDB parent which are on the NTDB.
+	Check \
+	    {NTDB to trunk transition has to end on non-NTDB} \
+	    {has a NTDB parent, yet is on the NTDB} {
+		SELECT F.name, R.rev
+		FROM revision R, file F
+		WHERE R.fid = F.fid
+		AND   R.dbparent IS NOT NULL
+		AND   R.isdefault
+		;
+	    }
+	# Find all revisions with a child which disagrees about the
+	# line of development they belong to.
+	Check \
+	    {Revisions and their primary children have to be in the same LOD} \
+	    {and its primary child disagree about their LOD} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.child IS NOT NULL
+		AND   R.child = C.rid
+		AND   C.lod != R.lod
+		;
+	    }
+	# Find all revisions with a non-NTDB child which agrees about
+	# the line of development they belong to.
+	Check \
+	    {NTDB and trunk revisions have to be in different LODs} \
+	    {on NTDB and its non-NTDB child wrongly agree about their LOD} {
+		SELECT F.name, R.rev
+		FROM revision R, revision C, file F
+		WHERE R.fid = F.fid
+		AND   R.dbchild IS NOT NULL
+		AND   R.dbchild = C.rid
+		AND   C.lod = R.lod
+		;
+	    }
+	# Find all revisions with a branch parent symbol which is not
+	# their LOD.
+	Check \
+	    {Branch starting revisions have to have their LOD as branch parent symbol} \
+	    {at the beginning of its branch does not have the branch symbol as its LOD} {
+		SELECT F.name, R.rev
+		FROM revision R, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NOT NULL
+		AND   R.lod != R.bparent
+		;
+	    }
+	# Find all revisions with a branch parent symbol whose parent
+	# is in the same line of development.
+	Check \
+	    {Revisions and their branch children have to be in different LODs} \
+	    {at the beginning of its branch and its parent wrongly agree about their LOD} {
+		SELECT F.name, R.rev
+		FROM revision R, revision P, file F
+		WHERE R.fid = F.fid
+		AND   R.bparent IS NOT NULL
+		AND   R.parent = P.rid
+		AND   R.lod = P.lod
+		;
+	    }
+	return
+    }
+
+    proc Check {header label sql} {
+	upvar 1 n n
+	set ok 1
+	foreach {fname revnr} [state run $sql] {
+	    set ok 0
+	    trouble fatal "$fname <$revnr> $label"
+	}
+	log write 5 collrev "\[[format %02d [incr n]]\] [expr {$ok ? "Ok    " : "Failed"}] ... $header"
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::pass {
+    namespace export collrev
+    namespace eval collrev {
+	namespace import ::vc::rcs::parser
+	namespace import ::vc::fossil::import::cvs::repository
+	namespace import ::vc::fossil::import::cvs::state
+	namespace import ::vc::fossil::import::cvs::integrity
+	namespace eval project {
+	    namespace import ::vc::fossil::import::cvs::project::sym
+	}
+	namespace eval file {
+	    namespace import ::vc::fossil::import::cvs::file::rev
+	}
+	namespace import ::vc::tools::trouble
+	namespace import ::vc::tools::log
+	log register collrev
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::pass::collrev 1.0
+return

Added tools/cvs2fossil/lib/c2f_pcollsym.tcl version [7553c6538a]

@@ -1,1 +1,303 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Pass III. This pass divides the symbols collected by the previous
+## pass into branches, tags, and excludes. The latter are also
+## partially deleted by this pass, not only marked. It is the next
+## pass however, 'FilterSym', which performs the full deletion.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                               ; # Required runtime.
+package require snit                                  ; # OO system.
+package require vc::tools::trouble                    ; # Error reporting.
+package require vc::tools::log                        ; # User feedback.
+package require vc::fossil::import::cvs::repository   ; # Repository management.
+package require vc::fossil::import::cvs::state        ; # State storage.
+package require vc::fossil::import::cvs::project::sym ; # Project level symbols
+
+# # ## ### ##### ######## ############# #####################
+## Register the pass with the management
+
+vc::fossil::import::cvs::pass define \
+    CollateSymbols \
+    {Collate symbols} \
+    ::vc::fossil::import::cvs::pass::collsym
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::pass::collsym {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod setup {} {
+	# Define names and structure of the persistent state of this
+	# pass.
+
+	state reading symbol
+	state reading blocker
+	state reading parent
+
+	state writing preferedparent {
+	    -- For each symbol the prefered parent. This describes the
+	    -- tree of the found lines of development. Actually a
+	    -- forest in case of multiple projects, with one tree per
+	    -- project.
+
+	    sid INTEGER  NOT NULL  PRIMARY KEY  REFERENCES symbol,
+	    pid INTEGER  NOT NULL               REFERENCES symbol
+	}
+	return
+    }
+
+    typemethod load {} {
+	# Pass manager interface. Executed to load data computed by
+	# this pass into memory when this pass is skipped instead of
+	# executed.
+
+	# The results of this pass are fully in the persistent state,
+	# there is nothing to load for the next one.
+	return
+    }
+
+    typemethod run {} {
+	# Pass manager interface. Executed to perform the
+	# functionality of the pass.
+
+	state transaction {
+	    repository   determinesymboltypes
+
+	    project::sym printrulestatistics
+	    project::sym printtypestatistics
+	}
+
+	if {![trouble ?]} {
+	    UnconvertedSymbols
+	    BadSymbolTypes
+	    BlockedExcludes
+	    InvalidTags
+	}
+
+	if {![trouble ?]} {
+	    DropExcludedSymbolsFromReferences
+	    DeterminePreferedParents
+	}
+
+	log write 1 collsym "Collation completed"
+	return
+    }
+
+    typemethod discard {} {
+	# Pass manager interface. Executed for all passes after the
+	# run passes, to remove all data of this pass from the state,
+	# as being out of date.
+
+	state discard preferedparent
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    proc UnconvertedSymbols {} {
+	# Paranoia - Have we left symbols without conversion
+	# information (i.e. with type 'undefined') ?
+
+	set undef [project::sym undef]
+
+	foreach {pname sname} [state run {
+	    SELECT P.name, S.name
+	    FROM   project P, symbol S
+	    WHERE  P.pid = S.pid
+	    AND    S.type = $undef
+	}] {
+	    trouble fatal "$pname : The symbol '$sname' was left undefined"
+	}
+	return
+    }
+
+    proc BadSymbolTypes {} {
+	# Paranoia - Have we left symbols with bogus conversion
+	# information (type out of the valid range (excluded, branch,
+	# tag)) ?
+
+	foreach {pname sname} [state run {
+	    SELECT P.name, S.name
+	    FROM   project P, symbol S
+	    WHERE  P.pid = S.pid
+	    AND    S.type NOT IN (0,1,2)
+	}] {
+	    trouble fatal "$pname : The symbol '$sname' has no proper conversion type"
+	}
+	return
+    }
+
+    proc BlockedExcludes {} {
+	# Paranoia - Have we scheduled symbols for exclusion without
+	# also excluding their dependent symbols ?
+
+	set excl [project::sym excluded]
+
+	foreach {pname sname bname} [state run {
+	    SELECT P.name, S.name, SB.name
+	    FROM   project P, symbol S, blocker B, symbol SB
+	    WHERE  P.pid = S.pid
+	    AND    S.type = $excl
+	    AND    S.sid = B.sid
+	    AND    B.bid = SB.sid
+	    AND    SB.type != $excl
+	}] {
+	    trouble fatal "$pname : The symbol '$sname' cannot be excluded as the unexcluded symbol '$bname' depends on it."
+	}
+	return
+    }
+
+    proc InvalidTags {} {
+	# Paranoia - Have we scheduled symbols for conversion as tags
+	# which absolutely cannot be converted as tags due to commits
+	# made on them ?
+
+	# In other words, this checks finds out if the user has asked
+	# nonsensical conversions of symbols, which should have been
+	# left to the heuristics, most specifically
+	# 'project::sym.HasCommits()'.
+
+	set tag [project::sym tag]
+
+	foreach {pname sname} [state run {
+	    SELECT P.name, S.name
+	    FROM   project P, symbol S
+	    WHERE  P.pid = S.pid
+	    AND    S.type = $tag
+	    AND    S.commit_count > 0
+	}] {
+	    trouble fatal "$pname : The symbol '$sname' cannot be forced to be converted as tag because it has commits."
+	}
+	return
+    }
+
+    proc DropExcludedSymbolsFromReferences {} {
+	# The excluded symbols cann be used as blockers nor as
+	# possible parent for other symbols. We now drop the relevant
+	# entries to prevent them from causing confusion later on.
+
+	set excl [project::sym excluded]
+
+	state run {
+	    DELETE FROM blocker
+	    WHERE bid IN (SELECT sid
+			  FROM   symbol
+			  WhERE  type = $excl);
+	    DELETE FROM parent
+	    WHERE pid IN (SELECT sid
+			  FROM   symbol
+			  WhERE  type = $excl);
+	}
+	return
+    }
+
+    proc DeterminePreferedParents {} {
+	array set prefered {}
+
+	set excl [project::sym excluded]
+
+	# Phase I: Pull the possible parents, using sorting to put the
+	#          prefered parent of each symbol last among all
+	#          candidates, allowing us get the prefered one by
+	#          each candidate overwriting all previous
+	#          selections. Note that we ignore excluded symbol, we
+	#          do not care about their prefered parents and do not
+	#          attempt to compute them.
+
+	foreach {s p sname pname prname} [state run {
+	    SELECT   S.sid, P.pid, S.name, SB.name, PR.name
+	    FROM     symbol S, parent P, symbol SB, project PR
+	    WHERE    S.sid = P.sid
+	    AND      P.pid = SB.sid
+	    AND      S.pid = PR.pid
+	    AND      S.type != $excl
+	    ORDER BY P.n ASC, P.pid DESC
+	    -- Higher votes and smaller ids (= earlier branches) last
+	    -- We simply keep the last possible parent for each
+	    -- symbol.  This parent will have the max number of votes
+	    -- for its symbol and will be the earliest created branch
+	    -- possible among all with many votes.
+	}] {
+	    set prefered($s) [list $p $sname $pname $prname]
+	}
+
+	# Phase II: Write the found preferences back into the table
+	#           this pass defined for it.
+
+	foreach {s x} [array get prefered] {
+	    struct::list assign $x p sname pname prname
+	    state run {
+		INSERT INTO preferedparent (sid, pid)
+		VALUES                     ($s,  $p);
+	    }
+
+	    log write 3 pcollsym "$prname : '$sname's prefered parent is '$pname'"
+	}
+
+	# Phase III: Check the result that all symbols except for
+	#            trunks have a prefered parent. We also ignore
+	#            excluded symbols, as we intentionally did not
+	#            compute a prefered parent for them, see phase I.
+
+	foreach {pname sname} [state run {
+	    SELECT PR.name, S.name
+	    FROM   project PR, symbol S LEFT OUTER JOIN preferedparent P
+	    ON     S.sid = P.sid
+	    WHERE  P.pid IS NULL
+	    AND    S.name != ':trunk:'
+	    AND    S.pid = PR.pid
+	    AND    S.type != $excl
+	}] {
+	    trouble fatal "$pname : '$sname' has no prefered parent."
+	}
+
+	# The reverse, having prefered parents for unknown symbols
+	# cannot occur.
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::pass {
+    namespace export collsym
+    namespace eval collsym {
+	namespace import ::vc::fossil::import::cvs::repository
+	namespace import ::vc::fossil::import::cvs::state
+	namespace eval project {
+	    namespace import ::vc::fossil::import::cvs::project::sym
+	}
+	namespace import ::vc::tools::trouble
+	namespace import ::vc::tools::log
+	log register collsym
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::pass::collsym 1.0
+return

Added tools/cvs2fossil/lib/c2f_pfiltersym.tcl version [29f48baf16]

@@ -1,1 +1,522 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Pass IV. Coming after the symbol collation pass this pass now
+## removes all revisions and symbols referencing any of the excluded
+## symbols from the persistent database.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                               ; # Required runtime.
+package require snit                                  ; # OO system.
+package require vc::tools::misc                       ; # Text formatting.
+package require vc::tools::log                        ; # User feedback.
+package require vc::fossil::import::cvs::repository   ; # Repository management.
+package require vc::fossil::import::cvs::state        ; # State storage.
+package require vc::fossil::import::cvs::integrity    ; # State storage integrity checks.
+package require vc::fossil::import::cvs::project::sym ; # Project level symbols
+
+# # ## ### ##### ######## ############# #####################
+## Register the pass with the management
+
+vc::fossil::import::cvs::pass define \
+    FilterSymbols \
+    {Filter symbols, remove all excluded pieces} \
+    ::vc::fossil::import::cvs::pass::filtersym
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::pass::filtersym {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod setup {} {
+	# Define names and structure of the persistent state of this
+	# pass.
+
+	state reading symbol
+	state reading blocker
+	state reading parent
+	state reading preferedparent
+	state reading revision
+	state reading revisionbranchchildren
+	state reading branch
+	state reading tag
+
+	state writing noop {
+	    id    INTEGER NOT NULL  PRIMARY KEY, -- tag/branch reference
+	    noop  INTEGER NOT NULL
+	}
+	return
+    }
+
+    typemethod load {} {
+	# Pass manager interface. Executed to load data computed by
+	# this pass into memory when this pass is skipped instead of
+	# executed.
+
+	# The results of this pass are fully in the persistent state,
+	# there is nothing to load for the next one.
+	return
+    }
+
+    typemethod run {} {
+	# Pass manager interface. Executed to perform the
+	# functionality of the pass.
+
+	# The removal of excluded symbols and everything referencing
+	# to them is done completely in the database.
+
+	state transaction {
+	    FilterExcludedSymbols
+	    MutateSymbols
+	    AdjustParents
+	    RefineSymbols
+
+	    repository printrevstatistics
+
+	    # Strict integrity enforces that all meta entries are in
+	    # the same LOD as the revision using them. At this point
+	    # this may not be true any longer. If a NTDB was excluded
+	    # then all revisions it shared with the trunk were moved
+	    # to the trunk LOD, however their meta entries will still
+	    # refer to the now gone LOD symbol. This is fine however,
+	    # it will not affect our ability to use the meta entries
+	    # to distinguish and group revisions into changesets. It
+	    # should be noted that we cannot simply switch the meta
+	    # entries over to the trunk either, as that may cause the
+	    # modified entries to violate the unique-ness constrain
+	    # set on that table.
+	    integrity metarelaxed
+	}
+
+	log write 1 filtersym "Filtering completed"
+	return
+    }
+
+    typemethod discard {} {
+	# Pass manager interface. Executed for all passes after the
+	# run passes, to remove all data of this pass from the state,
+	# as being out of date.
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    proc FilterExcludedSymbols {} {
+	log write 3 filtersym "Remove the excluded symbols and their users"
+
+	# We pull all the excluded symbols together into a table for
+	# easy reference by the upcoming DELETE and other statements.
+	# ('x IN table' clauses).
+
+	set excl [project::sym excluded]
+
+	state run {
+	    CREATE TEMPORARY TABLE excludedsymbols AS
+	    SELECT sid
+	    FROM   symbol
+	    WHERE  type = $excl
+	}
+
+	# First we have to handle the possibility of an excluded
+	# NTDB. This is a special special case there we have to
+	# regraft the revisions which are shared between the NTDB and
+	# Trunk onto the trunk, preventing their deletion later. We
+	# have code for that in 'file', however that operated on the
+	# in-memory revision objects, which we do not have here. We do
+	# the same now without object, by directly manipulating the
+	# links in the database.
+
+	array set ntdb {}
+	array set link {}
+
+	foreach {id parent transfer} [state run {
+	    SELECT R.rid, R.parent, R.dbchild
+	    FROM  revision R, symbol S
+	    WHERE R.lod = S.sid
+	    AND   S.sid IN excludedsymbols
+	    AND   R.isdefault
+	}] {
+	    set ntdb($id) $parent
+	    if {$transfer eq ""} continue
+	    set link($id) $transfer
+	}
+
+	foreach joint [array names link] {
+	    # The joints are the highest NTDB revisions which are
+	    # shared with their respective trunk. We disconnect from
+	    # their NTDB children, and make them parents of their
+	    # 'dbchild'. The associated 'dbparent' is squashed
+	    # instead. All parents of the joints are moved to the
+	    # trunk as well.
+
+	    set tjoint $link($joint)
+	    set tlod [state one {
+		SELECT lod FROM revision WHERE rid = $tjoint
+	    }]
+
+	    # Covnert db/parent/child into regular parent/child links.
+	    state run {
+		UPDATE revision SET dbparent = NULL, parent = $joint  WHERE rid = $tjoint ;
+		UPDATE revision SET dbchild  = NULL, child  = $tjoint WHERE rid = $joint  ;
+	    }
+	    while {1} {
+		# Move the NTDB trunk revisions to trunk.
+		state run {
+		    UPDATE revision SET lod = $tlod, isdefault = 0 WHERE rid = $joint
+		}
+		set last $joint
+		set joint $ntdb($joint)
+		if {![info exists ntdb($joint)]} break
+	    }
+
+	    # Reached the NTDB basis in the trunk. Finalize the
+	    # parent/child linkage and squash the branch parent symbol
+	    # reference.
+
+	    state run {
+		UPDATE revision SET child   = $last WHERE rid = $joint ;
+		UPDATE revision SET bparent = NULL  WHERE rid = $last  ;
+	    }
+	}
+
+	# Now that the special case is done we can simply kill all the
+	# revisions, tags, and branches referencing any of the
+	# excluded symbols in some way. This is easy as we do not have
+	# to select them again and again from the base tables any
+	# longer.
+
+	state run {
+	    CREATE TEMPORARY TABLE excludedrevisions AS
+	    SELECT rid FROM revision WHERE lod IN excludedsymbols;
+
+	    DELETE FROM revision WHERE lod IN excludedsymbols;
+	    DELETE FROM tag      WHERE lod IN excludedsymbols;
+	    DELETE FROM tag      WHERE sid IN excludedsymbols;
+	    DELETE FROM branch   WHERE lod IN excludedsymbols;
+	    DELETE FROM branch   WHERE sid IN excludedsymbols;
+
+	    DELETE FROM revisionbranchchildren WHERE rid  IN excludedrevisions;
+	    DELETE FROM revisionbranchchildren WHERE brid IN excludedrevisions;
+
+	    DROP TABLE excludedrevisions;
+	    DROP TABLE excludedsymbols;
+	}
+	return
+    }
+
+    proc MutateSymbols {} {
+	# Next, now that we know which symbols are what we look for
+	# file level tags which are actually converted as branches
+	# (project level, and vice versa), and move them to the
+	# correct tables.
+
+	# # ## ### ##### ######## #############
+
+	log write 3 filtersym "Mutate symbols, preparation"
+
+	set branch [project::sym branch]
+	set tag    [project::sym tag]
+
+	set tagstomutate [state run {
+	    SELECT T.tid, T.fid, T.lod, T.sid, T.rev
+	    FROM tag T, symbol S
+	    WHERE T.sid = S.sid
+	    AND S.type = $branch
+	}]
+
+	set branchestomutate [state run {
+	    SELECT B.bid, B.fid, B.lod, B.sid, B.root, B.first, B.bra
+	    FROM branch B, symbol S
+	    WHERE B.sid = S.sid
+	    AND S.type = $tag
+	}]
+
+	log write 4 filtersym "Changing [nsp [expr {[llength $tagstomutate]/5}] tag] into branches"
+	log write 4 filtersym "Changing [nsp [expr {[llength $branchestomutate]/7}] branch branches] into tags"
+
+	# # ## ### ##### ######## #############
+
+	log write 3 filtersym "Mutate tags to branches"
+
+	foreach {id fid lod sid rev} $tagstomutate {
+	    state run {
+		DELETE FROM tag WHERE tid = $id ;
+		INSERT INTO branch (bid, fid,  lod,  sid,  root, first, bra, pos)
+		VALUES             ($id, $fid, $lod, $sid, $rev, NULL,  '',  -1);
+	    }
+	}
+
+	log write 3 filtersym "Ok."
+
+	# # ## ### ##### ######## #############
+
+	log write 3 filtersym "Mutate branches to tags"
+
+	foreach {id fid lod sid root first bra} $branchestomutate {
+	    state run {
+		DELETE FROM branch WHERE bid = $id ;
+		INSERT INTO tag (tid, fid,  lod,  sid,  rev)
+		VALUES          ($id, $fid, $lod, $sid, $root);
+	    }
+	}
+
+	log write 3 filtersym "Ok."
+
+	# # ## ### ##### ######## #############
+	return
+    }
+
+    # Adjust the parents of symbols to their preferred parents.
+
+    # If a file level ymbol has a preferred parent that is different
+    # than its current parent, and if the preferred parent is an
+    # allowed parent of the symbol in this file, then we graft the
+    # aSymbol onto its preferred parent.
+
+    proc AdjustParents {} {
+	log write 3 filtersym "Adjust parents, loading data in preparation"
+
+	# We pull important maps once into memory so that we do quick
+	# hash lookup later when processing the graft candidates.
+
+	# Tag/Branch names ...
+	array set sn [state run { SELECT T.tid, S.name FROM tag T,    symbol S WHERE T.sid = S.sid }]
+	array set sn [state run { SELECT B.bid, S.name FROM branch B, symbol S WHERE B.sid = S.sid }]
+	# Symbol names ...
+	array set sx [state run { SELECT L.sid, L.name FROM symbol L }]
+	# Files and projects.
+	array set fpn {}
+	foreach {id fn pn} [state run {
+		SELECT F.fid, F.name, P.name
+		FROM   file F, project P
+		WHERE  F.pid = P.pid
+	}] { set fpn($id) [list $fn $pn] }
+
+	set tagstoadjust [state run {
+	    SELECT T.tid, T.fid, T.lod, P.pid, S.name, R.rev, R.rid
+	    FROM tag T, preferedparent P, symbol S, revision R
+	    WHERE T.sid = P.sid
+	    AND   T.lod != P.pid
+	    AND   P.pid = S.sid
+	    AND   S.name != ':trunk:'
+	    AND   T.rev = R.rid
+	}]
+
+	set branchestoadjust [state run {
+	    SELECT B.bid, B.fid, B.lod, B.pos, P.pid, S.name, R.rev, R.rid
+	    FROM branch B, preferedparent P, symbol S, revision R
+	    WHERE B.sid = P.sid
+	    AND   B.lod != P.pid
+	    AND   P.pid = S.sid
+	    AND   S.name != ':trunk:'
+	    AND   B.root = R.rid
+	}]
+
+	set tmax [expr {[llength $tagstoadjust] / 7}]
+	set bmax [expr {[llength $branchestoadjust] / 8}]
+
+	log write 4 filtersym "Reparenting at most [nsp $tmax tag]"
+	log write 4 filtersym "Reparenting at most [nsp $bmax branch branches]"
+
+	log write 3 filtersym "Adjust tag parents"
+
+	# Find the tags whose current parent (lod) is not the prefered
+	# parent, the prefered parent is not the trunk, and the
+	# prefered parent is a possible parent per the tag's revision.
+
+	set fmt %[string length $tmax]s
+	set mxs [format $fmt $tmax]
+
+	set n 0
+	foreach {id fid lod pid preferedname revnr rid} $tagstoadjust {
+
+	    # BOTTLE-NECK ...
+	    #
+	    # The check if the candidate (pid) is truly viable is
+	    # based finding the branch as possible parent, and done
+	    # now instead of as part of the already complex join.
+	    #
+	    # ... AND P.pid IN (SELECT B.sid
+	    #                   FROM branch B
+	    #                   WHERE B.root = R.rid)
+
+	    if {![state one {
+		SELECT COUNT(*)
+		FROM branch B
+		WHERE  B.sid  = $pid
+		AND    B.root = $rid
+	    }]} {
+		incr tmax -1
+		set  mxs [format $fmt $tmax]
+		continue
+	    }
+
+	    #
+	    # BOTTLE-NECK ...
+
+	    # The names for use in the log output are retrieved
+	    # separately, to keep the join selecting the adjustable
+	    # tags small, not burdened with the dereferencing of links
+	    # to name.
+
+	    set tagname $sn($id)
+	    set oldname $sx($lod)
+	    struct::list assign $fpn($fid) fname prname
+
+	    # Do the grafting.
+
+	    log write 4 filtersym "\[[format $fmt $n]/$mxs\] $prname : Grafting tag '$tagname' on $fname/$revnr from '$oldname' onto '$preferedname'"
+	    state run { UPDATE tag SET lod = $pid WHERE tid = $id ; }
+	    incr n
+	}
+
+	log write 3 filtersym "Reparented [nsp $n tag]"
+
+	log write 3 filtersym "Adjust branch parents"
+
+	# Find the branches whose current parent (lod) is not the
+	# prefered parent, the prefered parent is not the trunk, and
+	# the prefered parent is a possible parent per the branch's
+	# revision.
+
+	set fmt %[string length $bmax]s
+	set mxs [format $fmt $bmax]
+
+	set n 0
+	foreach {id fid lod pos pid preferedname revnr rid} $branchestoadjust {
+
+	    # BOTTLE-NECK ...
+	    #
+	    # The check if the candidate (pid) is truly viable is
+	    # based on the branch positions in the spawning revision,
+	    # and done now instead of as part of the already complex
+	    # join.
+	    #
+	    # ... AND P.pid IN (SELECT BX.sid
+	    #                   FROM branch BX
+	    #                   WHERE BX.root = R.rid
+	    #                   AND   BX.pos > B.pos)
+
+	    if {![state one {
+		SELECT COUNT(*)
+		FROM branch B
+		WHERE  B.sid  = $pid
+		AND    B.root = $rid
+		AND    B.pos  > $pos
+	    }]} {
+		incr bmax -1
+		set  mxs [format $fmt $bmax]
+		continue
+	    }
+
+	    #
+	    # BOTTLE-NECK ...
+
+	    # The names for use in the log output are retrieved
+	    # separately, to keep the join selecting the adjustable
+	    # tags small, not burdened with the dereferencing of links
+	    # to name.
+
+	    set braname $sn($id)
+	    set oldname $sx($lod)
+	    struct::list assign $fpn($fid) fname prname
+
+	    # Do the grafting.
+
+	    log write 4 filtersym "\[[format $fmt $n]/$mxs\] $prname : Grafting branch '$braname' on $fname/$revnr from '$oldname' onto '$preferedname'"
+	    state run { UPDATE tag SET lod = $pid WHERE tid = $id ; }
+	    incr n
+	}
+
+	log write 3 filtersym "Reparented [nsp $n branch branches]"
+	return
+    }
+
+    proc RefineSymbols {} {
+	# Tags and branches are marked as normal/noop based on the op
+	# of their revision.
+
+	log write 3 filtersym "Refine symbols (no-op or not?)"
+
+	log write 4 filtersym "    Regular tags"
+	state run {
+	    INSERT INTO noop
+	    SELECT T.tid, 0
+	    FROM tag T, revision R
+	    WHERE T.rev  = R.rid
+	    AND   R.op  != 0 -- 0 == nothing
+	}
+
+	log write 4 filtersym "    No-op tags"
+	state run {
+	    INSERT INTO noop
+	    SELECT T.tid, 1
+	    FROM tag T, revision R
+	    WHERE T.rev  = R.rid
+	    AND   R.op   = 0 -- nothing
+	}
+
+	log write 4 filtersym "    Regular branches"
+	state run {
+	    INSERT INTO noop
+	    SELECT B.bid, 0
+	    FROM branch B, revision R
+	    WHERE B.root = R.rid
+	    AND   R.op  != 0 -- nothing
+	}
+
+	log write 4 filtersym "    No-op branches"
+	state run {
+	    INSERT INTO noop
+	    SELECT B.bid, 1
+	    FROM branch B, revision R
+	    WHERE B.root = R.rid
+	    AND   R.op   = 0 -- nothing
+	}
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::pass {
+    namespace export filtersym
+    namespace eval filtersym {
+	namespace import ::vc::fossil::import::cvs::repository
+	namespace import ::vc::fossil::import::cvs::state
+	namespace import ::vc::fossil::import::cvs::integrity
+	namespace eval project {
+	    namespace import ::vc::fossil::import::cvs::project::sym
+	}
+	namespace import ::vc::tools::misc::nsp
+	namespace import ::vc::tools::log
+	log register filtersym
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::pass::filtersym 1.0
+return

Added tools/cvs2fossil/lib/c2f_pinitcsets.tcl version [10710518c2]

@@ -1,1 +1,352 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Pass V. This pass creates the initial set of project level
+## revisions, aka changesets. Later passes will refine them, puts them
+## into proper order, set their dependencies, etc.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                               ; # Required runtime.
+package require snit                                  ; # OO system.
+package require vc::tools::misc                       ; # Text formatting.
+package require vc::tools::log                        ; # User feedback.
+package require vc::fossil::import::cvs::repository   ; # Repository management.
+package require vc::fossil::import::cvs::state        ; # State storage.
+package require vc::fossil::import::cvs::project::sym ; # Project level symbols
+package require vc::fossil::import::cvs::project::rev ; # Project level changesets
+
+# # ## ### ##### ######## ############# #####################
+## Register the pass with the management
+
+vc::fossil::import::cvs::pass define \
+    InitCsets \
+    {Initialize ChangeSets} \
+    ::vc::fossil::import::cvs::pass::initcsets
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::pass::initcsets {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod setup {} {
+	# Define the names and structure of the persistent state of
+	# this pass.
+
+	state reading meta
+	state reading revision
+	state reading revisionbranchchildren
+	state reading branch
+	state reading tag
+	state reading symbol
+
+	# Data per changeset, namely the project it belongs to, how it
+	# was induced (revision or symbol), plus reference to the
+	# primary entry causing it (meta entry or symbol). An adjunct
+	# table translates the type id's into human readable labels.
+
+	state writing changeset {
+	    cid   INTEGER  NOT NULL  PRIMARY KEY  AUTOINCREMENT,
+	    pid   INTEGER  NOT NULL  REFERENCES project,
+	    type  INTEGER  NOT NULL  REFERENCES cstype,
+	    src   INTEGER  NOT NULL -- REFERENCES meta|symbol (type dependent)
+	}
+	state writing cstype {
+	    tid   INTEGER  NOT NULL  PRIMARY KEY  AUTOINCREMENT,
+	    name  TEXT     NOT NULL,
+	    UNIQUE (name)
+	}
+	state run {
+	    INSERT INTO cstype VALUES (0,'rev');
+	    INSERT INTO cstype VALUES (1,'sym');
+	}
+
+	# Map from changesets to the (file level) revisions they
+	# contain. The pos'ition provides an order of the revisions
+	# within a changeset. They are unique within the changeset.
+	# The revisions are in principle unique, if we were looking
+	# only at revision changesets. However a revision can appear
+	# in both revision and symbol changesets, and in multiple
+	# symbol changesets as well. So we can only say that it is
+	# unique within the changeset.
+	#
+	# TODO: Check if integrity checks are possible.
+
+	state writing csrevision {
+	    cid  INTEGER  NOT NULL  REFERENCES changeset,
+	    pos  INTEGER  NOT NULL,
+	    rid  INTEGER  NOT NULL  REFERENCES revision,
+	    UNIQUE (cid, pos),
+	    UNIQUE (cid, rid)
+	}
+
+	project::rev getcstypes
+	return
+    }
+
+    typemethod load {} {
+	# Pass manager interface. Executed to load data computed by
+	# this pass into memory when this pass is skipped instead of
+	# executed.
+
+	state reading changeset
+	state reading csrevision
+	state reading cstype
+
+	foreach {id pid cstype srcid} [state run {
+	    SELECT C.cid, C.pid, CS.name, C.src
+	    FROM   changeset C, cstype CS
+	    WHERE  C.type = CS.tid
+	    ORDER BY C.cid
+	}] {
+	    set r [project::rev %AUTO% [repository projectof $pid] $cstype $srcid [state run {
+		SELECT C.rid
+		FROM   csrevision C
+		WHERE  C.cid = $id
+		ORDER  BY C.pos
+	    }]]
+	    $r setid $id
+	}
+
+	project::rev getcstypes
+	return
+    }
+
+    typemethod run {} {
+	# Pass manager interface. Executed to perform the
+	# functionality of the pass.
+
+	state transaction {
+	    CreateRevisionChangesets  ; # Group file revisions into csets.
+	    BreakInternalDependencies ; # Split the csets based on internal conflicts.
+	    CreateSymbolChangesets    ; # Create csets for tags and branches.
+	    PersistTheChangesets
+
+	    repository printcsetstatistics
+	}
+	return
+    }
+
+    typemethod discard {} {
+	# Pass manager interface. Executed for all passes after the
+	# run passes, to remove all data of this pass from the state,
+	# as being out of date.
+
+	state discard changeset
+	state discard cstype
+	state discard csrevision
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    proc CreateRevisionChangesets {} {
+	log write 3 initcsets {Create changesets based on revisions}
+
+	# To get the initial of changesets we first group all file
+	# level revisions using the same meta data entry together. As
+	# the meta data encodes not only author and log message, but
+	# also line of development and project we can be sure that
+	# revisions in different project and lines of development are
+	# not grouped together. In contrast to cvs2svn we do __not__
+	# use distance in time between revisions to break them
+	# apart. We have seen CVS repositories (from SF) where a
+	# single commit contained revisions several hours apart,
+	# likely due to trouble on the server hosting the repository.
+
+	# We order the revisions here by time, this will help the
+	# later passes (avoids joins later to get at the ordering
+	# info).
+
+	set n 0
+
+	set lastmeta    {}
+	set lastproject {}
+	set revisions   {}
+
+	# Note: We could have written this loop to create the csets
+	#       early, extending them with all their revisions. This
+	#       however would mean lots of (slow) method invokations
+	#       on the csets. Doing it like this, late creation, means
+	#       less such calls. None, but the creation itself.
+
+	foreach {mid rid pid} [state run {
+	    SELECT M.mid, R.rid, M.pid
+	    FROM   revision R, meta M   -- R ==> M, using PK index of M.
+	    WHERE  R.mid = M.mid
+	    ORDER  BY M.mid, R.date
+	}] {
+	    if {$lastmeta != $mid} {
+		if {[llength $revisions]} {
+		    incr n
+		    set  p [repository projectof $lastproject]
+		    project::rev %AUTO% $p rev $lastmeta $revisions
+		    set revisions {}
+		}
+		set lastmeta    $mid
+		set lastproject $pid
+	    }
+	    lappend revisions $rid
+	}
+
+	if {[llength $revisions]} {
+	    incr n
+	    set  p [repository projectof $lastproject]
+	    project::rev %AUTO% $p rev $lastmeta $revisions
+	}
+
+	log write 4 initcsets "Created [nsp $n {revision changeset}]"
+	return
+    }
+
+    proc CreateSymbolChangesets {} {
+	log write 3 initcsets {Create changesets based on symbols}
+
+	# Tags and branches induce changesets as well, containing the
+	# revisions they are attached to (tags), or spawned from
+	# (branches).
+
+	set n 0
+
+	# First process the tags, then the branches. We know that
+	# their ids do not overlap with each other.
+
+	set lastsymbol  {}
+	set lastproject {}
+	set revisions   {}
+
+	foreach {sid rid pid} [state run {
+	    SELECT S.sid, R.rid, S.pid
+	    FROM  tag T, revision R, symbol S     -- T ==> R/S, using PK indices of R, S.
+	    WHERE T.rev = R.rid
+	    AND   T.sid = S.sid
+	    ORDER BY S.sid, R.date
+	}] {
+	    if {$lastsymbol != $sid} {
+		if {[llength $revisions]} {
+		    incr n
+		    set  p [repository projectof $lastproject]
+		    project::rev %AUTO% $p sym $lastsymbol $revisions
+		    set revisions {}
+		}
+		set lastsymbol  $sid
+		set lastproject $pid
+	    }
+	    lappend revisions $rid
+	}
+
+	if {[llength $revisions]} {
+	    incr n
+	    set  p [repository projectof $lastproject]
+	    project::rev %AUTO% $p sym $lastsymbol $revisions
+	}
+
+	set lastsymbol {}
+	set lasproject {}
+	set revisions  {}
+
+	foreach {sid rid pid} [state run {
+	    SELECT S.sid, R.rid, S.pid
+	    FROM  branch B, revision R, symbol S  -- B ==> R/S, using PK indices of R, S.
+	    WHERE B.root = R.rid
+	    AND   B.sid  = S.sid
+	    ORDER BY S.sid, R.date
+	}] {
+	    if {$lastsymbol != $sid} {
+		if {[llength $revisions]} {
+		    incr n
+		    set  p [repository projectof $lastproject]
+		    project::rev %AUTO% $p sym $lastsymbol $revisions
+		    set revisions {}
+		}
+		set lastsymbol  $sid
+		set lastproject $pid
+	    }
+	    lappend revisions $rid
+	}
+
+	if {[llength $revisions]} {
+	    incr n
+	    set  p [repository projectof $lastproject]
+	    project::rev %AUTO% $p sym $lastsymbol $revisions
+	}
+
+	log write 4 initcsets "Created [nsp $n {symbol changeset}]"
+	return
+    }
+
+    proc BreakInternalDependencies {} {
+	# This code operates on the revision changesets created by
+	# 'CreateRevisionChangesets'. As such it has to follow after
+	# it, before the symbol changesets are made. The changesets
+	# are inspected for internal conflicts and any such are broken
+	# by splitting the problematic changeset into multiple
+	# fragments. The results are changesets which have no internal
+	# dependencies, only external ones.
+
+	log write 3 initcsets {Break internal dependencies}
+	set old [llength [project::rev all]]
+
+	foreach cset [project::rev all] {
+	    $cset breakinternaldependencies
+	}
+
+	set n [expr {[llength [project::rev all]] - $old}]
+	log write 4 initcsets "Created [nsp $n {additional revision changeset}]"
+	log write 4 initcsets Ok.
+	return
+    }
+
+    proc PersistTheChangesets {} {
+	log write 3 initcsets "Saving [nsp [llength [project::rev all]] {initial changeset}] to the persistent state"
+
+	foreach cset [project::rev all] {
+	    $cset persist
+	}
+
+	log write 4 initcsets Ok.
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::pass {
+    namespace export initcsets
+    namespace eval initcsets {
+	namespace import ::vc::fossil::import::cvs::repository
+	namespace import ::vc::fossil::import::cvs::state
+	namespace eval project {
+	    namespace import ::vc::fossil::import::cvs::project::rev
+	}
+	namespace import ::vc::tools::misc::*
+	namespace import ::vc::tools::log
+	log register initcsets
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::pass::initcsets 1.0
+return

Added tools/cvs2fossil/lib/c2f_plodmgr.tcl version [cb6fc6ef5f]

@@ -1,1 +1,57 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Lines of Development in a project (Symbols, and the trunk).
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                             ; # Required runtime.
+package require snit                                ; # OO system.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::project::lodmgr {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    constructor {} {
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    # # ## ### ##### ######## #############
+    ## 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::project {
+    namespace export lodmgr
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::project::lodmgr 1.0
+return

Added tools/cvs2fossil/lib/c2f_prev.tcl version [458c8956e4]

@@ -1,1 +1,601 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Revisions per project, aka Changesets. These objects are first used
+## in pass 5, which creates the initial set covering the repository.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                               ; # Required runtime.
+package require snit                                  ; # OO system.
+package require vc::tools::misc                       ; # Text formatting
+package require vc::tools::trouble                    ; # Error reporting.
+package require vc::tools::log                        ; # User feedback.
+package require vc::fossil::import::cvs::state        ; # State storage.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::project::rev {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    constructor {project cstype srcid revisions} {
+	set myid        [incr mycounter]
+	set myproject   $project
+	set mytype      $cstype
+	set mysrcid	$srcid
+	set myrevisions $revisions
+
+	# Keep track of the generated changesets and of the inverse
+	# mapping from revisions to them.
+	lappend mychangesets $self
+	foreach r $revisions { set myrevmap($r) $self }
+	return
+    }
+
+    method id        {} { return $myid }
+    method revisions {} { return $myrevisions }
+    method data      {} { return [list $myproject $mytype $mysrcid] }
+
+    method setid {id} { set myid $id ; return }
+
+    method bysymbol   {} { return [expr {$mytype eq "sym"}] }
+    method byrevision {} { return [expr {$mytype eq "rev"}] }
+
+    method successors {} {
+	# NOTE / FUTURE: Possible bottleneck.
+	set csets {}
+	foreach {_ children} [$self nextmap] {
+	    foreach child $children {
+		lappend csets $myrevmap($child)
+	    }
+	}
+	return [lsort -unique $csets]
+    }
+
+    method nextmap {} {
+	if {[llength $mynextmap]} { return $mynextmap }
+	PullSuccessorRevisions tmp $myrevisions
+	set mynextmap [array get tmp]
+	return $mynextmap
+    }
+
+    method breakinternaldependencies {} {
+	# This method inspects the changesets for internal
+	# dependencies. Nothing is done if there are no
+	# such. Otherwise the changeset is split into a set of
+	# fragments without internal dependencies, transforming the
+	# internal dependencies into external ones. The new changesets
+	# are added to the list of all changesets.
+
+	# We perform all necessary splits in one go, instead of only
+	# one. The previous algorithm, adapted from cvs2svn, computed
+	# a lot of state which was thrown away and then computed again
+	# for each of the fragments. It should be easier to update and
+	# reuse that state.
+
+	# The code checks only sucessor dependencies, as this
+	# automatically covers the predecessor dependencies as well (A
+	# successor dependency a -> b is also a predecessor dependency
+	# b -> a).
+
+	# 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
+	# after, after upding the crossing information.
+
+	# Data structures:
+	# Map:  POS   revision id      -> position in list.
+	#       CROSS position in list -> number of dependencies crossing it
+	#       DEPC  dependency       -> positions it crosses
+	# List: RANGE Of the positions itself.
+	# A dependency is a single-element map parent -> child
+
+	InitializeBreakState $myrevisions
+
+	set fragments {}
+	set pending   [list $range]
+	set at        0
+	array set breaks {}
+
+	while {$at < [llength $pending]} {
+	    set current [lindex $pending $at]
+
+	    log write 6 csets ". . .. ... ..... ........ ............."
+	    log write 6 csets "Scheduled   [join [PRs [lrange $pending $at end]] { }]"
+	    log write 6 csets "Considering [PR $current] \[$at/[llength $pending]\]"
+
+	    set best [FindBestBreak $current]
+
+	    if {$best < 0} {
+		# The inspected range has no internal
+		# dependencies. This is a complete fragment.
+		lappend fragments $current
+
+		log write 6 csets "No breaks, final"
+	    } else {
+		# Split the range and schedule the resulting fragments
+		# for further inspection. Remember the number of
+		# dependencies cut before we remove them from
+		# consideration, for documentation later.
+
+		set breaks($best) $cross($best)
+
+		log write 6 csets "Best break @ $best, cutting [nsp $cross($best) dependency dependencies]"
+
+		# Note: The value of best is an abolute location in
+		# myrevisions. Use the start of current to make it an
+		# index absolute to current.
+
+		set brel [expr {$best - [lindex $current 0]}]
+		set bnext $brel ; incr bnext
+		set fragbefore [lrange $current 0 $brel]
+		set fragafter  [lrange $current $bnext end]
+
+		log write 6 csets "New pieces  [PR $fragbefore] [PR $fragafter]"
+
+		if {![llength $fragbefore]} {
+		    trouble internal "Tried to split off a zero-length fragment at the beginning"
+		}
+		if {![llength $fragafter]} {
+		    trouble internal "Tried to split off a zero-length fragment at the end"
+		}
+
+		lappend pending $fragbefore $fragafter
+		CutAt $best
+	    }
+
+	    incr at
+	}
+
+	log write 6 csets ". . .. ... ..... ........ ............."
+
+	# Create changesets for the fragments, reusing the current one
+	# for the first fragment. We sort them in order to allow
+	# checking for gaps and nice messages.
+
+	set fragments [lsort -index 0 -integer $fragments]
+
+	#puts \t.[join [PRs $fragments] .\n\t.].
+
+	Border [lindex $fragments 0] firsts firste
+
+	if {$firsts != 0} {
+	    trouble internal "Bad fragment start @ $firsts, gap, or before beginning of the range"
+	}
+
+	set laste $firste
+	foreach fragment [lrange $fragments 1 end] {
+	    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"
+	}
+
+	# Put the first fragment into the current changeset.
+	set myrevisions [lrange $myrevisions 0 $firste]
+
+	return 1
+    }
+
+    method persist {} {
+	set tid $mycstype($mytype)
+	set pid [$myproject id]
+	set pos 0
+
+	state transaction {
+	    state run {
+		INSERT INTO changeset (cid,   pid,  type, src)
+		VALUES                ($myid, $pid, $tid, $mysrcid);
+	    }
+
+	    foreach rid $myrevisions {
+		state run {
+		    INSERT INTO csrevision (cid,   pos,  rid)
+		    VALUES                 ($myid, $pos, $rid);
+		}
+		incr pos
+	    }
+	}
+	return
+    }
+
+    method timerange {} {
+	set theset ('[join $myrevisions {','}]')
+	return [state run "
+	    SELECT MIN(R.date), MAX(R.date)
+	    FROM revision R
+	    WHERE R.rid IN $theset
+	"]
+    }
+
+    method drop {} {
+	state transaction {
+	    state run {
+		DELETE FROM changeset  WHERE cid = $myid;
+		DELETE FROM csrevision WHERE cid = $myid;
+	    }
+	}
+	foreach r $myrevisions { unset myrevmap($r) }
+	set pos          [lsearch -exact $mychangesets $self]
+	set mychangesets [lreplace $mychangesets $pos $pos]
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    variable myid        {} ; # Id of the cset for the persistent
+			      # state.
+    variable myproject   {} ; # Reference of the project object the
+			      # changeset belongs to.
+    variable mytype      {} ; # rev or sym, where the cset originated
+			      # from.
+    variable mysrcid     {} ; # Id of the metadata or symbol the cset
+			      # is based on.
+    variable myrevisions {} ; # List of the file level revisions in
+			      # the cset.
+    variable mynextmap   {} ; # Dictionary mapping from the revisions
+			      # to their successors. Cache to avoid
+			      # loading this from the state more than
+			      # once.
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    typevariable mycounter        0 ; # Id counter for csets. Last id used.
+    typevariable mycstype -array {} ; # Map cstypes to persistent ids.
+
+    typemethod getcstypes {} {
+	foreach {tid name} [state run {
+	    SELECT tid, name FROM cstype;
+	}] { set mycstype($name) $tid }
+	return
+    }
+
+    typemethod loadcounter {} {
+	# Initialize the counter from the state
+	set mycounter [state one { SELECT MAX(cid) FROM changeset }]
+	return
+    }
+
+    proc PullInternalSuccessorRevisions {dv revisions} {
+	upvar 1 $dv dependencies
+	set theset ('[join $revisions {','}]')
+
+	foreach {rid child} [state run "
+   -- Primary children
+	    SELECT R.rid, R.child
+	    FROM   revision R
+	    WHERE  R.rid   IN $theset
+	    AND    R.child IS NOT NULL
+	    AND    R.child IN $theset
+    UNION
+    -- Transition NTDB to trunk
+	    SELECT R.rid, R.dbchild
+	    FROM   revision R
+	    WHERE  R.rid   IN $theset
+	    AND    R.dbchild IS NOT NULL
+	    AND    R.dbchild IN $theset
+    UNION
+    -- Secondary (branch) children
+	    SELECT R.rid, B.brid
+	    FROM   revision R, revisionbranchchildren B
+	    WHERE  R.rid   IN $theset
+	    AND    R.rid = B.rid
+	    AND    B.brid IN $theset
+	"] {
+	    # Consider moving this to the integrity module.
+	    if {$rid == $child} {
+		trouble internal "Revision $rid depends on itself."
+	    }
+	    lappend dependencies($rid) $child
+	}
+    }
+
+    proc PullSuccessorRevisions {dv revisions} {
+	upvar 1 $dv dependencies
+	set theset ('[join $revisions {','}]')
+
+	foreach {rid child} [state run "
+   -- Primary children
+	    SELECT R.rid, R.child
+	    FROM   revision R
+	    WHERE  R.rid   IN $theset
+	    AND    R.child IS NOT NULL
+    UNION
+    -- Transition NTDB to trunk
+	    SELECT R.rid, R.dbchild
+	    FROM   revision R
+	    WHERE  R.rid   IN $theset
+	    AND    R.dbchild IS NOT NULL
+    UNION
+    -- Secondary (branch) children
+	    SELECT R.rid, B.brid
+	    FROM   revision R, revisionbranchchildren B
+	    WHERE  R.rid   IN $theset
+	    AND    R.rid = B.rid
+	"] {
+	    # Consider moving this to the integrity module.
+	    if {$rid == $child} {
+		trouble internal "Revision $rid depends on itself."
+	    }
+	    lappend dependencies($rid) $child
+	}
+    }
+
+    proc InitializeBreakState {revisions} {
+	upvar 1 pos pos cross cross range range depc depc delta delta \
+	    dependencies dependencies
+
+	# First we create a map of positions to make it easier to
+	# determine whether a dependency crosses a particular index.
+
+	array set pos   {}
+	array set cross {}
+	array set depc  {}
+	set range       {}
+	set n 0
+	foreach rev $revisions {
+	    lappend range $n
+	    set pos($rev) $n
+	    set cross($n) 0
+	    incr n
+	}
+
+	# Secondly we count the crossings per position, by iterating
+	# over the recorded internal dependencies.
+
+	# Note: If the timestamps are badly out of order it is
+	#       possible to have a backward successor dependency,
+	#       i.e. with start > end. We may have to swap the indices
+	#       to ensure that the following loop runs correctly.
+	#
+	# Note 2: start == end is not possible. It indicates a
+	#         self-dependency due to the uniqueness of positions,
+	#         and that is something we have ruled out already, see
+	#         PullInternalSuccessorRevisions.
+
+	foreach {rid child} [array get dependencies] {
+	    set dkey    [list $rid $child]
+	    set start   $pos($rid)
+	    set end     $pos($child)
+	    set crosses {}
+
+	    if {$start > $end} {
+		while {$end < $start} {
+		    lappend crosses $end
+		    incr cross($end)
+		    incr end
+		}
+	    } else {
+		while {$start < $end} {
+		    lappend crosses $start
+		    incr cross($start)
+		    incr start
+		}
+	    }
+	    set depc($dkey) $crosses
+	}
+
+	InitializeDeltas $revisions
+	return
+    }
+
+    proc InitializeDeltas {revisions} {
+	upvar 1 delta delta
+
+	# Pull the timestamps for all revisions in the changesets and
+	# compute their deltas for use by the break finder.
+
+	array set delta {}
+	array set stamp {}
+
+	set theset ('[join $revisions {','}]')
+	foreach {rid time} [state run "
+	    SELECT R.rid, R.date
+	    FROM revision R
+	    WHERE R.rid IN $theset
+	"] {
+	    set stamp($rid) $time
+	}
+
+	set n 0
+	foreach rid [lrange $revisions 0 end-1] rnext [lrange $revisions 1 end] {
+	    set delta($n) [expr {$stamp($rnext) - $stamp($rid)}]
+	    incr n
+	}
+	return
+    }
+
+    proc FindBestBreak {range} {
+	upvar 1 cross cross delta delta
+
+	# Determine the best break location in the given range of
+	# positions. First we look for the locations with the maximal
+	# number of crossings. If there are several we look for the
+	# shortest time interval among them. If we still have multiple
+	# possibilities after that we select the earliest location
+	# among these.
+
+	# Note: If the maximal number of crossings is 0 then the range
+	#       has no internal dependencies, and no break location at
+	#       all. This possibility is signaled via result -1.
+
+	# Note: A range of length 1 or less cannot have internal
+	#       dependencies, as that needs at least two revisions in
+	#       the range.
+
+	if {[llength $range] < 2} { return -1 }
+
+	set max -1
+	set best {}
+
+	foreach location $range {
+	    set crossings $cross($location)
+	    if {$crossings > $max} {
+		set max  $crossings
+		set best [list $location]
+		continue
+	    } elseif {$crossings == $max} {
+		lappend best $location
+	    }
+	}
+
+	if {$max == 0}            { return -1 }
+	if {[llength $best] == 1} { return [lindex $best 0] }
+
+	set locations $best
+	set best {}
+	set min -1
+
+	foreach location $locations {
+	    set interval $delta($location)
+	    if {($min < 0) || ($interval < $min)} {
+		set min  $interval
+		set best [list $location]
+	    } elseif {$interval == $min} {
+		lappend best $location
+	    }
+	}
+
+	if {[llength $best] == 1} { return [lindex $best 0] }
+
+	return [lindex [lsort -integer -increasing $best] 0]
+    }
+
+    proc CutAt {location} {
+	upvar 1 cross cross depc depc
+
+	# It was decided to split the changeset at the given
+	# location. This cuts a number of dependencies. Here we update
+	# the cross information so that the break finder has accurate
+	# data when we look at the generated fragments.
+
+	set six [log visible? 6]
+
+	foreach {dep range} [array get depc] {
+	    # Check all dependencies still known, take their range and
+	    # see if the break location falls within.
+
+	    Border $range s e
+	    if {$location < $s} continue ; # break before range, ignore
+	    if {$location > $e} continue ; # break after range, ignore.
+
+	    # This dependency crosses the break location. We remove it
+	    # from the crossings counters, and then also from the set
+	    # of known dependencies, as we are done with it.
+
+	    foreach loc $depc($dep) { incr cross($loc) -1 }
+	    unset depc($dep)
+
+	    if {!$six} continue
+
+	    struct::list assign $dep parent child
+	    log write 6 csets "Broke dependency [PD $parent] --> [PD $child]"
+	}
+
+	return
+    }
+
+    # Print identifying data for a revision (project, file, dotted rev
+    # number), for high verbosity log output.
+
+    proc PD {id} {
+	foreach {p f r} [state run {
+		SELECT P.name , F.name, R.rev
+		FROM revision R, file F, project P
+		WHERE R.rid = $id
+		AND   R.fid = F.fid
+		AND   F.pid = P.pid
+	}] break
+	return "'$p : $f/$r'"
+    }
+
+    # Printing one or more ranges, formatted, and only their border to
+    # keep the strings short.
+
+    proc PRs {ranges} {
+	return [struct::list map $ranges [myproc PR]]
+    }
+
+    proc PR {range} {
+	Border $range s e
+	return <${s}...${e}>
+    }
+
+    proc Border {range sv ev} {
+	upvar 1 $sv s $ev e
+	set s [lindex $range 0]
+	set e [lindex $range end]
+	return
+    }
+
+    # # ## ### ##### ######## #############
+
+    typevariable mychangesets    {} ; # List of all known changesets.
+    typevariable myrevmap -array {} ; # Map from revisions to their changeset.
+
+    typemethod all {} {
+	return $mychangesets
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hastypeinfo    no  ; # no type introspection
+    pragma -hasinfo        no  ; # no object introspection
+    pragma -simpledispatch yes ; # simple fast dispatch
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::project {
+    namespace export rev
+    namespace eval rev {
+	namespace import ::vc::fossil::import::cvs::state
+	namespace import ::vc::tools::misc::*
+	namespace import ::vc::tools::trouble
+	namespace import ::vc::tools::log
+	log register csets
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::project::rev 1.0
+return

Added tools/cvs2fossil/lib/c2f_prevlink.tcl version [01734176b9]

@@ -1,1 +1,248 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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 class for the pass 6 cycle breaker. Each instance refers to
+## three changesets A, B, and C, with A a predecessor of B, and B
+## predecessor of C, and the whole part of a dependency cycle.
+
+## Instances analyse the file level dependencies which gave rise to
+## the changeset dependencies of A, B, and C, with the results used by
+## the cycle breaker algorithm to find a good location where to at
+## least weaken and at best fully break the cycle.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                               ; # Required runtime.
+package require snit                                  ; # OO system.
+package require vc::tools::misc                       ; # Text formatting
+package require vc::tools::trouble                    ; # Error reporting.
+package require vc::tools::log                        ; # User feedback.
+package require vc::fossil::import::cvs::state        ; # State storage.
+package require vc::fossil::import::cvs::project::rev ; # Project level changesets
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::project::revlink {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    constructor {prev cset next} {
+	set myprev $prev
+	set mycset $cset
+	set mynext $next
+
+	# We perform the bulk of the analysis during construction. The
+	# file revisions held by the changeset CSET can be sorted into
+	# four categories.
+
+	# 1. Revisions whose predecessors are not in PREV, nor are
+	#    their successors found in NEXT. These revisions do not
+	#    count, as they did not induce any of the two dependencies
+	#    under consideration. They can be ignored.
+
+	# 2. Revisions which have predecessors in PREV and sucessors
+	#    in NEXT. They are called 'passthrough' in cvs2svn. They
+	#    induce both dependencies under consideration and are thus
+	#    critical in the creation of the cycle. As such they are
+	#    also unbreakable :(
+
+	# 3. Revisions which have predecessor in PREVE, but no
+	#    successors in NEXT. As such they induced the incoming
+	#    dependency, but not the outgoing one.
+
+	# 4. Revisions which have no predecessors in PREVE, but their
+	#    successors are in NEXT. As such they induced the outgoing
+	#    dependency, but not the incoming one.
+
+	# If we have no passthrough revisions then splitting the
+	# changeset between categories 3 and 4, with category 1 going
+	# wherever, will break the cycle. If category 2 revisions are
+	# present we can still perform the split, this will however
+	# not break the cycle, only weaken it.
+
+	array set csetprevmap [Invert [$myprev nextmap]]
+	array set csetnextmap [$mycset nextmap]
+
+	set prevrev [$myprev revisions]
+	set nextrev [$mynext revisions]
+
+	foreach r [$mycset revisions] {
+	    set rt [RT $r]
+	    incr    mycount($rt)
+	    lappend mycategory($rt) $r
+	}
+	return
+    }
+
+    # Result is TRUE if and only breaking myset will do some good.
+    method breakable {} { expr  {$mycount(prev) || $mycount(next)} }
+    method passcount {} { return $mycount(pass) }
+
+    method linkstomove {} {
+	# Return the number of revisions that would be moved should we
+	# split the changeset.
+
+	set n [min2 $mycount(prev) $mycount(next)]
+	if {$n > 0 } { return $n }
+	return [max2 $mycount(prev) $mycount(next)]
+    }
+
+    method betterthan {other} {
+	set sbreak [$self  breakable]
+	set obreak [$other breakable]
+
+	if {$sbreak && !$obreak} { return 1 } ; # self is better.
+	if {!$sbreak && $obreak} { return 0 } ; # self is worse.
+
+	# Equality. Look at the counters.
+	# - Whichever has the lesser number of passthrough revisions
+	#   is better, as more can be split off, weakening the cycle
+	#   more.
+	# - Whichever has less links to move is better.
+
+	set opass [$other passcount]
+	if {$mycount(pass) < $opass} { return 1 } ; # self is better.
+	if {$mycount(pass) > $opass} { return 0 } ; # self is worse.
+
+	set smove [$self  linkstomove]
+	set omove [$other linkstomove]
+
+	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)} {
+	    # Nothing in category 3 => 1,2 go there, 4 the other.
+	    set mycategory(prev) [concat $mycategory(none) $mycategory(pass)]
+	} elseif {!$mycount(next)} {
+	    # Nothing in category 4 => 1,2 go there, 3 the other.
+	    set mycategory(next) [concat $mycategory(none) $mycategory(pass)]
+	} elseif {$mycount(prev) < $mycount(next)} {
+	    # Less predecessors than successors => 1,2 go to the
+	    # sucessors.
+	    set mycategory(next) [concat $mycategory(next) $mycategory(none) \
+				      $mycategory(pass)]
+	} else {
+	    # Less successors than predecessors => 1,2 go to the
+	    # predecessors.
+	    set mycategory(next) [concat $mycategory(next) $mycategory(none) \
+				      $mycategory(pass)]
+	}
+
+	# We now have the split in the mycategory(prev|next)
+	# elements. As part of the creation of the new changesets the
+	# old one is dropped from all databases, in and out of memory,
+	# and then destroyed.
+
+	struct::list assign [$mycset data] project cstype cssrc
+	$mycset drop
+	$mycset destroy
+
+	set newcsets {}
+	lappend newcsets [project::rev %AUTO% $project $cstype $cssrc $mycategory(prev)]
+	lappend newcsets [project::rev %AUTO% $project $cstype $cssrc $mycategory(next)]
+
+	foreach c $newcsets { $c persist }
+
+	return $newcsets
+    }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    variable myprev {} ; # Reference to predecessor changeset in the link.
+    variable mycset {} ; # Reference to the main changeset of the link.
+    variable mynext {} ; # Reference to the successor changeset in the link.
+
+    # Counters for the revision categories.
+    variable mycount -array {
+	none 0
+	prev 0
+	next 0
+	pass 0
+    }
+    # Lists of revisions for the various categories
+    variable mycategory -array {
+	none {}
+	prev {}
+	next {}
+	pass {}
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    proc RT {r} {
+	upvar 1 csetprevmap csetprevmap csetnextmap csetnextmap prevrev prevrev nextrev nextrev
+
+	set inc	[expr {[info exists csetprevmap($r)]
+		       ? [struct::set size [struct::set intersect $csetprevmap($r) $prevrev]]
+		       : 0}]
+	set out [expr {[info exists csetnextmap($r)]
+		       ? [struct::set size [struct::set intersect $csetnextmap($r) $nextrev]]
+		       : 0}]
+
+	if {$inc && $out} { return pass }
+	if {$inc}         { return prev }
+	if {$out}         { return next }
+	return none
+    }
+
+    proc Invert {dict} {
+	array set tmp {}
+	foreach {k values} $dict {
+	    foreach v $values { lappend tmp($v) $k }
+	}
+	return [array get tmp]
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hastypeinfo    no  ; # no type introspection
+    pragma -hasinfo        no  ; # no object introspection
+    pragma -simpledispatch yes ; # simple fast dispatch
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::project {
+    namespace export revlink
+    namespace eval revlink {
+	namespace import ::vc::fossil::import::cvs::state
+	namespace import ::vc::tools::misc::*
+	namespace import ::vc::tools::trouble
+	namespace eval project {
+	    namespace import ::vc::fossil::import::cvs::project::rev
+	}
+	namespace import ::vc::tools::log
+	log register csets
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::project::revlink 1.0
+return

Added tools/cvs2fossil/lib/c2f_project.tcl version [39d1bb0e92]

@@ -1,1 +1,222 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Project, part of a CVS repository. Multiple instances are possible.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                                 ; # Required runtime.
+package require snit                                    ; # OO system.
+package require vc::fossil::import::cvs::file           ; # CVS archive file.
+package require vc::fossil::import::cvs::state          ; # State storage.
+package require vc::fossil::import::cvs::project::sym   ; # Per project symbols.
+package require vc::fossil::import::cvs::project::trunk ; # Per project trunk, main lod
+package require vc::tools::log                          ; # User feedback
+package require struct::list                            ; # Advanced list operations..
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::project {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    constructor {path r} {
+	set mybase       $path
+	set myrepository $r
+	set mytrunk      [trunk %AUTO% $self]
+	set mysymbol([$mytrunk name]) $mytrunk
+	return
+    }
+
+    method base  {} { return $mybase  }
+    method trunk {} { return $mytrunk }
+
+    method printbase {} {
+	if {$mybase eq ""} {return <Repository>}
+	return $mybase
+    }
+
+    method id    {}   { return $myid }
+    method setid {id} { set myid $id ; return }
+
+    method addfile {rcs usr executable {fid {}}} {
+	set myfiles($rcs) [list $usr $executable $fid]
+	return
+    }
+
+    method filenames {} {
+	return [lsort -dict [array names myfiles]]
+    }
+
+    method files {} {
+	return [TheFiles]
+    }
+
+    delegate method defauthor       to myrepository
+    delegate method defcmessage     to myrepository
+    delegate method trunkonly       to myrepository
+    delegate method commitmessageof to myrepository
+
+    method defmeta {bid aid cid} {
+	return [$myrepository defmeta $myid $bid $aid $cid]
+    }
+
+    method getsymbol {name} {
+	if {![info exists mysymbol($name)]} {
+	    set mysymbol($name) \
+		[sym %AUTO% $name [$myrepository defsymbol $myid $name] $self]
+	}
+	return $mysymbol($name)
+    }
+
+    method hassymbol {name} {
+	return [info exists mysymbol($name)]
+    }
+
+    method purgeghostsymbols {} {
+	set changes 1
+	while {$changes} {
+	    set changes 0
+	    foreach {name symbol} [array get mysymbol] {
+		if {![$symbol isghost]} continue
+		log write 3 project "$mybase: Deleting ghost symbol '$name'"
+		$symbol destroy
+		unset mysymbol($name)
+		set changes 1
+	    }
+	}
+	return
+    }
+
+    method determinesymboltypes {} {
+	foreach {name symbol} [array get mysymbol] {
+	    $symbol determinetype
+	}
+	return
+    }
+
+    # pass I persistence
+    method persist {} {
+	TheFiles ; # Force id assignment.
+
+	state transaction {
+	    # Project data first. Required so that we have its id
+	    # ready for the files.
+
+	    state run {
+		INSERT INTO project (pid,  name)
+		VALUES              (NULL, $mybase);
+	    }
+	    set myid [state id]
+
+	    # Then all files, with proper backreference to their
+	    # project.
+
+	    foreach rcs [lsort -dict [array names myfiles]] {
+		struct::list assign $myfiles($rcs) usr executable _fid_
+		state run {
+		    INSERT INTO file (fid,  pid,   name, visible, exec)
+		    VALUES           (NULL, $myid, $rcs, $usr,    $executable);
+		}
+		$myfmap($rcs) setid [state id]
+	    }
+	}
+	return
+    }
+
+    # pass II persistence
+    method persistrev {} {
+	# Note: The per file information (incl. revisions and symbols)
+	# has already been saved and dropped. This was done
+	# immediately after processing it, i.e. as part of the main
+	# segment of the pass, to keep out use of memory under
+	# control.
+	#
+	# The repository level information has been saved as well too,
+	# just before saving the projects started. So now we just have
+	# to save the remaining project level parts to fix the
+	# left-over dangling references, which are the symbols.
+
+	state transaction {
+	    foreach {name symbol} [array get mysymbol] {
+		$symbol persistrev
+	    }
+	}
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    variable mybase           {} ; # Project directory.
+    variable myid             {} ; # Project id in the persistent state.
+    variable mytrunk          {} ; # Reference to the main line of
+				   # development for the project.
+    variable myfiles   -array {} ; # Maps the rcs archive paths to
+				   # their user-visible files.
+    variable myfobj           {} ; # File objects for the rcs archives
+    variable myfmap    -array {} ; # Map rcs archive to their object.
+    variable myrepository     {} ; # Repository the prject belongs to.
+    variable mysymbol  -array {} ; # Map symbol names to project-level
+				   # symbol objects.
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    proc TheFiles {} {
+	upvar 1 myfiles myfiles myfobj myfobj self self myfmap myfmap
+	if {![llength $myfobj]} {
+	    set myfobj [EmptyFiles myfiles]
+	}
+	return $myfobj
+    }
+
+    proc EmptyFiles {fv} {
+	upvar 1 $fv myfiles self self myfmap myfmap
+	set res {}
+	foreach rcs [lsort -dict [array names myfiles]] {
+	    struct::list assign $myfiles($rcs) f executable fid
+	    set file [file %AUTO% $fid $rcs $f $executable $self]
+	    lappend res $file
+	    set myfmap($rcs) $file
+	}
+	return $res
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hastypeinfo    no  ; # no type introspection
+    pragma -hasinfo        no  ; # no object introspection
+    pragma -hastypemethods no  ; # type is not relevant.
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs {
+    namespace export project
+    namespace eval project {
+	namespace import ::vc::tools::log
+	namespace import ::vc::fossil::import::cvs::file
+	namespace import ::vc::fossil::import::cvs::state
+	# Import not required, already a child namespace.
+	# namespace import ::vc::fossil::import::cvs::project::sym
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::project 1.0
+return

Added tools/cvs2fossil/lib/c2f_psym.tcl version [e97d1e5410]

@@ -1,1 +1,409 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Symbols (Tags, Branches) per project.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                               ; # Required runtime.
+package require snit                                  ; # OO system.
+package require vc::tools::trouble                    ; # Error reporting.
+package require vc::tools::log                        ; # User feedback.
+package require vc::tools::misc                       ; # Text formatting.
+package require vc::fossil::import::cvs::state        ; # State storage.
+package require struct::set                           ; # Set handling.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::project::sym {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    constructor {name id project} {
+	set myname    $name
+	set myid      $id
+	set myproject $project
+
+	# Count total number of symbols.
+	incr mynum
+	return
+    }
+
+    method name {} { return $myname }
+    method id   {} { return $myid   }
+
+    # # ## ### ##### ######## #############
+    ## Symbol type
+
+    method determinetype {} {
+	# This is done by a fixed heuristics, with guidance by the
+	# user in edge-cases. Contrary to cvs2svn which uses a big
+	# honking streagy class and rule objects. Keep it simple, we
+	# can expand later when we actually need all the complexity
+	# for configurability.
+
+	# The following guidelines are applied:
+	# - Is usage unambigous ?
+	# - Was there ever a commit on the symbol ?
+	# - More used as tag, or more used as branch ?
+	# - At last, what has the user told us about it ?
+	# - Fail
+
+	foreach rule {
+	    UserConfig
+	    Unambigous
+	    HasCommits
+	    VoteCounts
+	} {
+	   set chosen [$self $rule]
+	   if {$chosen eq $myundef} continue
+	   $self MarkAs $rule $chosen
+	   return
+	}
+
+	# None of the above was able to decide which type to assign to
+	# the symbol. This is a fatal error preventing the execution
+	# of the passes after 'CollateSymbols'.
+
+	incr myrulecount(Undecided_)
+	trouble fatal "Unable to decide how to convert symbol '$myname'"
+	return
+    }
+
+    method markthetrunk {} { $self MarkAs IsTheTrunk $mybranch ; return }
+
+    # # ## ### ##### ######## #############
+    ## Symbol statistics
+
+    method defcounts {tc bc cc} {
+	set mybranchcount $tc
+	set mytagcount    $bc
+	set mycommitcount $cc
+	return
+    }
+
+    method countasbranch {} { incr mybranchcount ; return }
+    method countastag    {} { incr mytagcount    ; return }
+    method countacommit  {} { incr mycommitcount ; return }
+
+    method blockedby {symbol} {
+	# Remember the symbol as preventing the removal of this
+	# symbol. Ot is a tag or branch that spawned from a revision
+	# on this symbol.
+
+	struct::set include myblockers $symbol
+	return
+    }
+
+    method possibleparent {symbol} {
+	if {[info exists mypparent($symbol)]} {
+	    incr mypparent($symbol)
+	} else {
+	    set  mypparent($symbol) 1
+	}
+	return
+    }
+
+    method isghost {} {
+	# Checks if this symbol (as line of development) never
+	# existed.
+
+	if {$mycommitcount > 0}         { return 0 }
+	if {[llength $myblockers]}      { return 0 }
+	if {[array size mypparent] > 0} { return 0 }
+
+	return 1
+    }
+
+    # # ## ### ##### ######## #############
+
+    method persistrev {} {
+	set pid [$myproject id]
+
+	state transaction {
+	    state run {
+		INSERT INTO symbol ( sid,   pid,  name,   type,     tag_count,   branch_count,   commit_count)
+		VALUES             ($myid, $pid, $myname, $myundef, $mytagcount, $mybranchcount, $mycommitcount);
+	    }
+	    foreach symbol $myblockers {
+		set bid [$symbol id]
+		state run {
+		    INSERT INTO blocker (sid,   bid)
+		    VALUES              ($myid, $bid);
+		}
+	    }
+	    foreach {symbol count} [array get mypparent] {
+		set pid [$symbol id]
+		state run {
+		    INSERT INTO parent (sid,   pid,  n)
+		    VALUES             ($myid, $pid, $count);
+		}
+	    }
+	}
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    variable myproject {} ; # Reference to the project object
+			    # containing the symbol.
+    variable myname    {} ; # The symbol's name
+    variable myid      {} ; # Repository wide numeric id of the
+			    # symbol. This implicitly encodes the
+			    # project as well.
+
+    variable mybranchcount 0 ; # Count how many uses as branch.
+    variable mytagcount    0 ; # Count how many uses as tag.
+    variable mycommitcount 0 ; # Count how many files did a commit on the symbol.
+
+    variable myblockers   {} ; # List (Set) of the symbols which block
+			       # the exclusion of this symbol.
+
+    variable mypparent -array {} ; # Maps from symbols to the number
+				   # of files in which it could have
+				   # been a parent of this symbol.
+
+    variable mytype {} ; # The type chosen for the symbol to use in
+			 # the conversion.
+
+    # # ## ### ##### ######## #############
+
+    typemethod exclude {pattern} {
+	# Store the pattern in memory for use by the code doing type
+	# determination.
+
+	lappend myexcludepattern [ProcessPattern $pattern exclusion]
+	return
+    }
+
+    typemethod forcetag {pattern} {
+	# Store the pattern in memory for use by the code doing type
+	# determination.
+
+	lappend myforcepattern [ProcessPattern $pattern force-tag] $mytag
+	return
+    }
+
+    typemethod forcebranch {pattern} {
+	# Store the pattern in memory for use by the code doing type
+	# determination.
+
+	lappend myforcepattern [ProcessPattern $pattern force-branch] $mybranch
+	return
+    }
+
+    proc ProcessPattern {pattern label} {
+	if {[string match *:*:* $pattern]} {
+	    # Bad syntax for the pattern, using multiple colons.
+
+	    trouble fatal "Bad $label pattern '$pattern'"
+	} elseif {![string match *:* $pattern]} {
+	    # When only a symbol pattern is specified it applies to
+	    # all projects.
+
+	    return [list * $pattern]
+	} else {
+	    # Both project and symbol patterns are present, we split
+	    # them apart now for storage and easier extraction later.
+
+	    return [split $pattern :]
+	}
+    }
+
+    typevariable myexcludepattern {} ; # List of patterns specifying
+				       # the symbols to exclude from
+				       # conversion. Tags and/or
+				       # branches.
+
+    typevariable myforcepattern {} ; # List of patterns and types
+				     # specifying which symbols to
+				     # force to specific types.
+
+    typemethod getsymtypes {} {
+	foreach {tid name} [state run {
+	    SELECT tid, name FROM symtype;
+	}] { set mysymtype($tid) $name }
+	return
+    }
+
+    # Keep the codes below in sync with 'pass::collrev/setup('symtype').
+    typevariable myexcluded        0 ; # Code for symbols which are excluded.
+    typevariable mytag             1 ; # Code for symbols which are tags.
+    typevariable mybranch          2 ; # Code for symbols which are branches.
+    typevariable myundef           3 ; # Code for symbols of unknown type.
+    typevariable mysymtype -array {} ; # Map from type code to label for the log.
+
+    typemethod undef    {} { return $myundef    }
+    typemethod excluded {} { return $myexcluded }
+    typemethod tag      {} { return $mytag      }
+    typemethod branch   {} { return $mybranch   }
+
+    typemethod printrulestatistics {} {
+	log write 2 symbol "Rule usage statistics:"
+
+	set fmt %[string length $mynum]s
+	set all 0
+
+	foreach key [lsort [array names myrulecount]] {
+	    log write 2 symbol "* [format $fmt $myrulecount($key)] $key"
+	    incr all $myrulecount($key)
+	}
+
+	log write 2 symbol "= [format $fmt $all] total"
+	return
+    }
+
+    # Statistics on how often each 'rule' was used to decide on the
+    # type of a symbol.
+    typevariable myrulecount -array {
+	HasCommits 0
+	IsTheTrunk 0
+	Unambigous 0
+	Undecided_ 0
+	UserConfig 0
+	VoteCounts 0
+    }
+
+    typemethod printtypestatistics {} {
+	log write 2 symbol "Symbol type statistics:"
+
+	set fmt %[string length $mynum]s
+	set all 0
+
+	foreach {stype splural n} [state run {
+	    SELECT T.name, T.plural, COUNT (s.sid)
+	    FROM symbol S, symtype T
+	    WHERE S.type = T.tid
+	    GROUP BY T.name
+	    ORDER BY T.name
+	    ;
+	}] {
+	    log write 2 symbol "* [format $fmt $n] [sp $n $stype $splural]"
+	    incr all $n
+	}
+
+	log write 2 symbol "= [format $fmt $all] total"
+	return
+    }
+
+    typevariable mynum 0
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    method UserConfig {} {
+	set project [$myproject base]
+
+	# First check if the user requested the exclusion of the
+	# symbol from conversion.
+
+	foreach ex $myexcludepattern {
+	    struct::list assign $ex pp sp
+	    if {![string match $pp $project]} continue
+	    if {![string match $sp $myname]}  continue
+	    return $myexcluded
+	}
+
+	# If the symbol is not excluded further check if the user
+	# forces its conversion as a specific type.
+
+	foreach {ex stype} $myforcepattern {
+	    struct::list assign $ex pp sp
+	    if {![string match $pp $project]} continue
+	    if {![string match $sp $myname]}  continue
+	    return $stype
+	}
+
+	# Nothing is forced, have the main system hand the symbol over
+	# to the regular heuristics.
+
+	return $myundef
+    }
+
+    method Unambigous {} {
+	# If a symbol is used unambiguously as a tag/branch, convert
+	# it as such.
+
+	set istag    [expr {$mytagcount    > 0}]
+	set isbranch [expr {$mybranchcount > 0 || $mycommitcount > 0}]
+
+	if {$istag && $isbranch} { return $myundef  }
+	if {$istag}              { return $mytag    }
+	if {$isbranch}           { return $mybranch }
+
+	# Symbol was not used at all.
+	return $myundef
+    }
+
+    method HasCommits {} {
+	# If there was ever a commit on the symbol, convert it as a
+	# branch.
+
+	if {$mycommitcount > 0} { return $mybranch }
+	return $myundef
+    }
+
+    method VoteCounts {} {
+	# Convert the symbol based on how often it was used as a
+	# branch/tag. Whichever happened more often determines how the
+	# symbol is converted.
+
+	if {$mytagcount > $mybranchcount} { return $mytag }
+	if {$mytagcount < $mybranchcount} { return $mybranch }
+	return $myundef
+    }
+
+    method MarkAs {label chosen} {
+	log write 3 symbol "\[$label\] Converting symbol '$myname' as $mysymtype($chosen)"
+
+	set mytype $chosen
+	incr myrulecount($label)
+
+	# This is stored directly into the database.
+	state run {
+	    UPDATE symbol
+	    SET type = $chosen
+	    WHERE sid = $myid
+	    ;
+	}
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hastypeinfo    no  ; # no type introspection
+    pragma -hasinfo        no  ; # no object introspection
+    pragma -simpledispatch yes ; # simple fast dispatch
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::project {
+    namespace export sym
+    namespace eval sym {
+	namespace import ::vc::fossil::import::cvs::state
+	namespace import ::vc::tools::misc::*
+	namespace import ::vc::tools::trouble
+	namespace import ::vc::tools::log
+	log register symbol
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::project::sym 1.0
+return

Added tools/cvs2fossil/lib/c2f_ptrunk.tcl version [b20f9bb07d]

@@ -1,1 +1,88 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Trunk, the special main line of development in a project.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                             ; # Required runtime.
+package require snit                                ; # OO system.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::project::trunk {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    constructor {project} {
+	set mysymbol [$project getsymbol $myname]
+	set myid     [$mysymbol id]
+	return
+    }
+
+    destructor {
+	$mysymbol destroy
+    }
+
+    method name    {} { return $myname }
+    method id      {} { return $myid   }
+    method istrunk {} { return 1 }
+    method symbol  {} { return $self }
+
+    method forceid {id} { set myid $id ; return }
+
+    method defcounts {tc bc cc} {}
+
+    method countasbranch {} {}
+    method countastag    {} {}
+    method countacommit  {} {}
+
+    method blockedby      {symbol} {}
+    method possibleparent {symbol} {}
+
+    method isghost {} { return 0 }
+
+    delegate method persistrev to mysymbol
+
+    method determinetype {} { $mysymbol markthetrunk }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    typevariable myname   :trunk: ; # Name shared by all trunk symbols.
+    variable     myid     {}      ; # The trunk's symbol id.
+    variable     mysymbol {}      ; # The symbol underneath the trunk.
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hastypeinfo    no  ; # no type introspection
+    pragma -hasinfo        no  ; # no object introspection
+    pragma -hastypemethods no  ; # type is not relevant.
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs::project {
+    namespace export trunk
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::project::trunk 1.0
+return

Added tools/cvs2fossil/lib/c2f_repository.tcl version [6cbcf7b826]

@@ -1,1 +1,472 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Repository manager. Keeps projects and their files around.
+
+package provide vc::fossil::import::cvs::repository 1.0
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                          ; # Required runtime.
+package require snit                             ; # OO system.
+package require vc::tools::trouble               ; # Error reporting.
+package require vc::tools::log                   ; # User feedback.
+package require vc::tools::misc                  ; # Text formatting.
+package require vc::tools::id                    ; # Indexing and id generation.
+package require vc::fossil::import::cvs::project ; # CVS projects.
+package require vc::fossil::import::cvs::state   ; # State storage.
+package require struct::list                     ; # List operations.
+package require fileutil                         ; # File operations.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::repository {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod base {path} {
+	# Could be checked, easier to defer to the overall validation.
+	set mybase $path
+	return
+    }
+
+    typemethod add {path} {
+	# Most things cannot be checked immediately, as the base is
+	# not known while projects are added. We can and do check for
+	# uniqueness. We accept multiple occurences of a name, and
+	# treat them as a single project.
+
+	if {[lsearch -exact $myprojpaths $path] >= 0} return
+	lappend myprojpaths $path
+	return
+    }
+
+    typemethod trunkonly! {} { set mytrunkonly 1 ; return }
+    typemethod trunkonly  {} { return $mytrunkonly }
+
+    typemethod projects {} {
+	return [TheProjects]
+    }
+
+    typemethod base? {} { return $mybase }
+
+    typemethod validate {} {
+	if {![IsRepositoryBase $mybase msg]} {
+	    trouble fatal $msg
+	    # Without a good base directory checking any projects is
+	    # wasted time, so we leave now.
+	    return
+	}
+	foreach pp $myprojpaths {
+	    if {![IsProjectBase $mybase/$pp $mybase/CVSROOT msg]} {
+		trouble fatal $msg
+	    }
+	}
+	return
+    }
+
+    typemethod defauthor   {a}               { $myauthor put $a }
+    typemethod defcmessage {cm}              { $mycmsg   put $cm }
+    typemethod defsymbol   {pid name}        { $mysymbol put [list $pid $name] }
+    typemethod defmeta     {pid bid aid cid} { $mymeta   put [list $pid $bid $aid $cid] }
+
+    typemethod commitmessageof {mid} {
+	struct::list assign [$mymeta keyof $mid] pid bid aid cid
+	return [$mycmsg keyof $cid]
+    }
+
+    # pass I results
+    typemethod printstatistics {} {
+	set prlist [TheProjects]
+	set npr [llength $prlist]
+
+	log write 2 repository "Statistics: Scanned [nsp $npr project]"
+
+	if {$npr > 1} {
+	    set  bmax [max [struct::list map $prlist [myproc .BaseLength]]]
+	    incr bmax 2
+	    set  bfmt %-${bmax}s
+
+	    set  nmax [max [struct::list map $prlist [myproc .NFileLength]]]
+	    set  nfmt %${nmax}s
+	} else {
+	    set bfmt %s
+	    set nfmt %s
+	}
+
+	set keep {}
+	foreach p $prlist {
+	    set nfiles [llength [$p filenames]]
+	    set line "Statistics: Project [format $bfmt \"[$p printbase]\"] : [format $nfmt $nfiles] [sp $nfiles file]"
+	    if {$nfiles < 1} {
+		append line ", dropped"
+	    } else {
+		lappend keep $p
+	    }
+	    log write 2 repository $line
+	}
+
+	if {![llength $keep]} {
+	    trouble warn "Dropped all projects"
+	} elseif {$npr == [llength $keep]} {
+	    log write 2 repository "Keeping all projects"
+	} else {
+	    log write 2 repository "Keeping [nsp [llength $keep] project]"
+	    trouble warn "Dropped [nsp [expr {$npr - [llength $keep]}] {empty project}]"
+	}
+
+	# Keep reduced set of projects.
+	set projects $keep
+	return
+    }
+
+    # pass I persistence
+    typemethod persist {} {
+	::variable myprojmap
+	state transaction {
+	    foreach p [TheProjects] {
+		$p persist
+		set myprojmap([$p id]) $p
+	    }
+	}
+	return
+    }
+
+    typemethod load {} {
+	state transaction {
+	    foreach   {pid  name} [state run {
+		SELECT pid, name FROM project ;
+	    }] {
+		set project [project %AUTO% $name $type]
+
+		lappend myprojpaths $name
+		lappend myprojects  $project
+		set myprojmap($pid) $project
+		$project setid $pid
+	    }
+	    foreach   {fid  pid  name  visible  exec} [state run {
+		SELECT fid, pid, name, visible, exec FROM file ;
+	    }] {
+		$myprojmap($pid) addfile $name $visible $exec $fid
+	    }
+	}
+	return
+    }
+
+    # pass II results
+    typemethod printrevstatistics {} {
+	log write 2 repository "Revision statistics"
+	# number of revisions, symbols, repository wide, and per project ...
+
+	set rcount [state one { SELECT COUNT (*) FROM revision }]
+	set tcount [state one { SELECT COUNT (*) FROM tag      }]
+	set bcount [state one { SELECT COUNT (*) FROM branch   }]
+	set scount [state one { SELECT COUNT (*) FROM symbol   }]
+	set acount [state one { SELECT COUNT (*) FROM author   }]
+	set ccount [state one { SELECT COUNT (*) FROM cmessage }]
+	set fmt %[string length [max [list $rcount $tcount $bcount $scount $acount $ccount]]]s
+
+	log write 2 repository "Statistics: [format $fmt $rcount] [sp $rcount revision]"
+	log write 2 repository "Statistics: [format $fmt $tcount] [sp $tcount tag]"
+	log write 2 repository "Statistics: [format $fmt $bcount] [sp $bcount branch branches]"
+	log write 2 repository "Statistics: [format $fmt $scount] [sp $scount symbol]"
+	log write 2 repository "Statistics: [format $fmt $acount] [sp $acount author]"
+	log write 2 repository "Statistics: [format $fmt $ccount] [sp $ccount {log message}]"
+
+	set prlist [TheProjects]
+	set npr [llength $prlist]
+
+	if {$npr > 1} {
+	    set  bmax [max [struct::list map $prlist [myproc .BaseLength]]]
+	    incr bmax 2
+	    set  bfmt %-${bmax}s
+	} else {
+	    set bfmt %s
+	}
+
+	foreach p $prlist {
+	    set pid [$p id]
+	    set prefix "Statistics: Project [format $bfmt \"[$p printbase]\"]"
+	    regsub -all {[^	]} $prefix { } blanks
+	    set sep " : "
+
+	    set rcount [state one { SELECT COUNT (*) FROM revision R, file F WHERE R.fid = F.fid AND F.pid = $pid }]
+	    set tcount [state one { SELECT COUNT (*) FROM tag T,      file F WHERE T.fid = F.fid AND F.pid = $pid }]
+	    set bcount [state one { SELECT COUNT (*) FROM branch B,   file F WHERE B.fid = F.fid AND F.pid = $pid }]
+	    set scount [state one { SELECT COUNT (*) FROM symbol             WHERE pid = $pid                     }]
+	    set acount [state one { SELECT COUNT (*) FROM author   WHERE aid IN (SELECT DISTINCT aid FROM meta WHERE pid = $pid) }]
+	    set ccount [state one { SELECT COUNT (*) FROM cmessage WHERE cid IN (SELECT DISTINCT cid FROM meta WHERE pid = $pid) }]
+
+	    log write 2 repository "$prefix$sep[format $fmt $rcount] [sp $rcount revision]"
+	    log write 2 repository "$blanks$sep[format $fmt $tcount] [sp $tcount tag]"
+	    log write 2 repository "$blanks$sep[format $fmt $bcount] [sp $bcount branch branches]"
+	    log write 2 repository "$blanks$sep[format $fmt $scount] [sp $scount symbol]"
+	    log write 2 repository "$blanks$sep[format $fmt $acount] [sp $acount author]"
+	    log write 2 repository "$blanks$sep[format $fmt $ccount] [sp $ccount {log message}]"
+	}
+	return
+    }
+
+    # pass II persistence
+    typemethod persistrev {} {
+	state transaction {
+	    SaveAuthors
+	    SaveCommitMessages
+	    # TODO: Save symbols of all projects (before the revisions
+	    # in the projects, as they are referenced by the meta
+	    # tuples)
+	    SaveMeta
+	    foreach p [TheProjects] { $p persistrev }
+	}
+	return
+    }
+
+    typemethod loadsymbols {} {
+	state transaction {
+	    # We load the symbol ids at large to have the mapping
+	    # right from the beginning.
+
+	    foreach {sid pid name tc bc cc} [state run {
+		SELECT sid, pid, name, tag_count, branch_count, commit_count
+		FROM symbol
+		;
+	    }] {
+		$mysymbol map $sid [list $pid $name]
+		set project $myprojmap($pid)
+
+		set force  [$project hassymbol $name]
+		set symbol [$project getsymbol $name]
+
+		# Forcing happens only for the trunks.
+		if {$force} { $symbol forceid $sid }
+
+		# Set the loaded counts.
+		$symbol defcounts $tc $bc $cc
+
+		# Note: The type is neither retrieved nor set, for
+		# this is used to load the pass II data, which means
+		# that everything is 'undefined' at this point anyway.
+
+		# future: $symbol load (blockers, and parents)
+	    }
+	}
+	return
+    }
+
+    typemethod determinesymboltypes {} {
+	foreach project [TheProjects] {
+	    $project determinesymboltypes
+	}
+	return
+    }
+
+    typemethod projectof {pid} {
+	return $myprojmap($pid)
+    }
+
+
+    # pass IV+ results
+    typemethod printcsetstatistics {} {
+	log write 2 repository "Changeset statistics"
+	# number of revisions, symbols, repository wide, and per project ...
+
+	set ccount [state one { SELECT COUNT (*) FROM changeset                }]
+	set rcount [state one { SELECT COUNT (*) FROM changeset WHERE type = 0 }]
+	set scount [state one { SELECT COUNT (*) FROM changeset WHERE type = 1 }]
+	set fmt %[string length [max [list $ccount $rcount $scount]]]s
+
+	log write 2 repository "Statistics: [format $fmt $ccount] [sp $ccount changeset]"
+	log write 2 repository "Statistics: [format $fmt $rcount] [sp $rcount {revision changeset}]"
+	log write 2 repository "Statistics: [format $fmt $scount] [sp $scount {symbol changeset}]"
+
+	set prlist [TheProjects]
+	set npr [llength $prlist]
+
+	if {$npr > 1} {
+	    set  bmax [max [struct::list map $prlist [myproc .BaseLength]]]
+	    incr bmax 2
+	    set  bfmt %-${bmax}s
+	} else {
+	    set bfmt %s
+	}
+
+	foreach p $prlist {
+	    set pid [$p id]
+	    set prefix "Statistics: Project [format $bfmt \"[$p printbase]\"]"
+	    regsub -all {[^	]} $prefix { } blanks
+	    set sep " : "
+
+	    set ccount [state one { SELECT COUNT (*) FROM changeset WHERE pid = $pid              }]
+	    set rcount [state one { SELECT COUNT (*) FROM changeset WHERE pid = $pid AND type = 0 }]
+	    set scount [state one { SELECT COUNT (*) FROM changeset WHERE pid = $pid AND type = 1 }]
+
+	    log write 2 repository "$prefix$sep[format $fmt $ccount] [sp $ccount changeset]"
+	    log write 2 repository "$blanks$sep[format $fmt $rcount] [sp $rcount {revision changeset}]"
+	    log write 2 repository "$blanks$sep[format $fmt $scount] [sp $scount {symbol changeset}]"
+	}
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    typevariable mybase           {} ; # Base path to CVS repository.
+    typevariable myprojpaths      {} ; # List of paths to all declared
+				       # projects, relative to mybase.
+    typevariable myprojects       {} ; # List of objects for all
+				       # declared projects.
+    typevariable myprojmap -array {} ; # Map from project ids to their
+				       # objects.
+    typevariable myauthor         {} ; # Names of all authors found,
+				       # maps to their ids.
+    typevariable mycmsg           {} ; # All commit messages found,
+				       # maps to their ids.
+    typevariable mymeta           {} ; # Maps all meta data tuples
+				       # (project, branch, author,
+				       # cmessage) to their ids.
+    typevariable mysymbol         {} ; # Map symbols identified by
+				       # project and name to their
+				       # id. This information is not
+				       # saved directly.
+    typevariable mytrunkonly      0  ; # Boolean flag. Set by option
+				       # processing when the user
+				       # requested a trunk-only import
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+    typeconstructor {
+	set myauthor [vc::tools::id %AUTO%]
+	set mycmsg   [vc::tools::id %AUTO%]
+	set mymeta   [vc::tools::id %AUTO%]
+	set mysymbol [vc::tools::id %AUTO%]
+	return
+    }
+
+    proc .BaseLength {p} {
+	return [string length [$p printbase]]
+    }
+
+    proc .NFileLength {p} {
+	return [string length [llength [$p filenames]]]
+    }
+
+    proc IsRepositoryBase {path mv} {
+	::variable mybase
+	upvar 1 $mv msg
+	if {![fileutil::test $mybase         edr msg {CVS Repository}]}      {return 0}
+	if {![fileutil::test $mybase/CVSROOT edr msg {CVS Admin Directory}]} {return 0}
+	return 1
+    }
+
+    proc IsProjectBase {path admin mv} {
+	upvar 1 $mv msg
+	if {![fileutil::test $path edr msg Project]} {return 0}
+	if {
+	    ($path eq $admin) ||
+	    [string match $admin/* $path]
+	} {
+	    set msg "Administrative subdirectory $path cannot be a project"
+	    return 0
+	}
+	return 1
+    }
+
+    proc TheProjects {} {
+	upvar 1 type type
+	::variable myprojects
+	::variable myprojpaths
+
+	if {![llength $myprojects]} {
+	    set myprojects [EmptyProjects $myprojpaths]
+	}
+	return $myprojects
+    }
+
+    proc EmptyProjects {projpaths} {
+	::variable mybase
+	upvar 1 type type
+	set res {}
+	if {[llength $projpaths]} {
+	    foreach pp $projpaths {
+		lappend res [project %AUTO% $pp $type]
+	    }
+	} else {
+	    # Base is the single project.
+	    lappend res [project %AUTO% "" $type]
+	}
+	return $res
+    }
+
+    proc SaveAuthors {} {
+	::variable myauthor
+	foreach {name aid} [$myauthor get] {
+	    state run {
+		INSERT INTO author ( aid,  name)
+		VALUES             ($aid, $name);
+	    }
+	}
+	return
+    }
+
+    proc SaveCommitMessages {} {
+	::variable mycmsg
+	foreach {text cid} [$mycmsg get] {
+	    state run {
+		INSERT INTO cmessage ( cid,  text)
+		VALUES               ($cid, $text);
+	    }
+	}
+	return
+    }
+
+    proc SaveMeta {} {
+	::variable mymeta
+	foreach {key mid} [$mymeta get] {
+	    struct::list assign $key pid bid aid cid
+	    state run {
+		INSERT INTO meta ( mid,  pid,  bid,  aid,  cid)
+		VALUES           ($mid, $pid, $bid, $aid, $cid);
+	    }
+	}
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs {
+    namespace export repository
+    namespace eval repository {
+	namespace import ::vc::fossil::import::cvs::project
+	namespace import ::vc::fossil::import::cvs::state
+	namespace import ::vc::tools::misc::*
+	namespace import ::vc::tools::id
+	namespace import ::vc::tools::trouble
+	namespace import ::vc::tools::log
+	log register repository
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+return

Added tools/cvs2fossil/lib/c2f_state.tcl version [5356f2147d]

@@ -1,1 +1,195 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## State manager. Maintains the sqlite database used by all the other
+## parts of the system, especially the passes and their support code,
+## to persist and restore their state across invokations.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                          ; # Required runtime.
+package require snit                             ; # OO system.
+package require fileutil                         ; # File operations.
+package require sqlite3                          ; # Database access.
+package require vc::tools::trouble               ; # Error reporting.
+package require vc::tools::log                   ; # User feedback.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs::state {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod use {path} {
+	# Immediate validation. There are are two possibilities to
+	# consider. The path exists or it doesn't.
+
+	# In the first case it has to be a readable and writable file,
+	# and it has to be a proper sqlite database. Further checks
+	# regarding the required tables will be done later, by the
+	# passes, during their setup.
+
+	# In the second case we have to be able to create the file,
+	# and check that. This is done by opening it, sqlite will then
+	# try to create it, and may fail.
+
+	if {[file exists $path]} {
+	    if {![fileutil::test $path frw msg {cvs2fossil state}]} {
+		trouble fatal $msg
+		return
+	    }
+	}
+
+	if {[catch {
+	    sqlite3 ${type}::TEMP $path
+	} res]} {
+	    trouble fatal $res
+	    return
+	}
+
+	# A previously defined state database is closed before
+	# committing to the new definition. We do not store the path
+	# itself, this ensures that the file is _not_ cleaned up after
+	# a run.
+
+	set mystate ${type}::STATE
+	set mypath  {}
+
+	catch { $mystate close }
+	rename  ${type}::TEMP $mystate
+
+	log write 2 state "is $path"
+	return
+    }
+
+    typemethod setup {} {
+	# If, and only if no state database was defined by the user
+	# then it is now the time to create our own using a tempfile.
+
+	if {$mystate ne ""} return
+
+	set mypath  [fileutil::tempfile cvs2fossil_state_]
+	set mystate ${type}::STATE
+	sqlite3 $mystate $mypath
+
+	log write 2 state "using $mypath"
+	return
+    }
+
+    typemethod release {} {
+	log write 2 state release
+	${type}::STATE close
+	if {$mypath eq ""} return
+	file delete $mypath
+	return
+    }
+
+    typemethod writing {name definition} {
+	# Method for a user to declare a table its needs for storing
+	# persistent state, and the expected structure. A possibly
+	# previously existing definition is dropped.
+
+	log write 0 state "writing $name" ; # TODO move to level 5 or so
+
+	$mystate transaction {
+	    catch { $mystate eval "DROP TABLE $name" }
+	    $mystate eval "CREATE TABLE $name ( $definition )"
+	}
+	return
+    }
+
+    typemethod reading {name} {
+	log write 0 state "reading $name" ; # TODO move to level 5 or so
+
+	# Method for a user to declare a table it wishes to read
+	# from. A missing table is an internal error causing an
+	# immediate exit.
+
+	set found [llength [$mystate eval {
+	    SELECT name
+	    FROM sqlite_master
+	    WHERE type = 'table'
+	    AND   name = $name
+	    ;
+	}]]
+
+	if {$found} return
+
+	trouble internal "The required table \"$name\" is not defined."
+	# Not reached
+	return
+    }
+
+    typemethod discard {name} {
+	# Method for a user to remove outdated information from the
+	# persistent state, table by table.
+
+	log write 0 state "discard $name" ; # TODO move to level 5 or so
+
+	$mystate transaction {
+	    catch { $mystate eval "DROP TABLE $name" }
+	}
+	return
+    }
+
+    typemethod run {args} {
+	return [uplevel 1 [linsert $args 0 $mystate eval]]
+    }
+
+    typemethod one {args} {
+	return [lindex [uplevel 1 [linsert $args 0 $mystate eval]] 0]
+    }
+
+    typemethod transaction {script} {
+	return [uplevel 1 [list $mystate transaction $script]]
+    }
+
+    typemethod id {} {
+	return [$mystate last_insert_rowid]
+    }
+
+    # # ## ### ##### ######## #############
+    ## State
+
+    typevariable mystate {} ; # Sqlite database (command) holding the converter state.
+    typevariable mypath  {} ; # Path to the database, for cleanup of a temp database.
+
+    # # ## ### ##### ######## #############
+    ## Internal methods
+
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::fossil::import::cvs {
+    namespace export state
+    namespace eval state {
+	namespace import ::vc::tools::trouble
+	namespace import ::vc::tools::log
+	log register state
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs::state 1.0
+return

Added tools/cvs2fossil/lib/cvs2fossil.tcl version [516ad35538]

@@ -1,1 +1,88 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Main package of the cvs conversion/import facility. Loads the
+## required pieces and controls their interaction.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                         ; # Required runtime.
+package require snit                            ; # OO system
+
+# # ## ### ##### ######## ############# #####################
+## Passes. The order in which the various passes are loaded is
+##         important. It is the same order in which they will
+##         register, and then be run in.
+
+package require vc::fossil::import::cvs::pass::collar      ; # Coll'ect Ar'chives.
+package require vc::fossil::import::cvs::pass::collrev     ; # Coll'ect Rev'isions.
+package require vc::fossil::import::cvs::pass::collsym     ; # Coll'ate Sym'bols
+package require vc::fossil::import::cvs::pass::filtersym   ; # Filter'  Sym'bols
+
+# Note: cvs2svn's SortRevisionSummaryPass and SortSymbolSummaryPass
+#       are not implemented by us. They are irrelevant due to our use
+#       of a relational database proper for the persistent state,
+#       allowing us to sort the data on the fly as we need it.
+
+package require vc::fossil::import::cvs::pass::initcsets   ; # Init'ialize C'hange'Sets
+package require vc::fossil::import::cvs::pass::breakrcycle ; # Break' R'evision Cycle's
+
+# Note: cvs2svn's RevisionTopologicalSortPass is not a separate pass,
+#       but was subsumed by the previous pass, by immediately saving
+#       the order of consumed graph nodes to 'csorder'.
+
+package require vc::fossil::import::cvs::pass::breakscycle ; # Break' S'ymbol Cycle's
+package require vc::fossil::import::cvs::pass::breakacycle ; # Break' A'll Cycle's
+
+# # ## ### ##### ######## ############# #####################
+## Support for passes etc.
+
+package require vc::fossil::import::cvs::option ; # Cmd line parsing & database
+package require vc::fossil::import::cvs::pass   ; # Pass management
+package require vc::tools::log                  ; # User feedback
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::fossil::import::cvs {
+    # # ## ### ##### ######## #############
+    ## Public API, Methods
+
+    typemethod run {arguments} {
+	# Run a series of passes over the cvs repository to extract,
+	# filter, and order its historical information. Which passes
+	# are actually run is determined through the specified options
+	# and their defaults.
+
+	option process $arguments
+	pass run
+
+	vc::tools::log write 0 cvs2fossil Done
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::fossil::import::cvs 1.0
+return

Added tools/cvs2fossil/lib/id.tcl version [ad0171bebe]

@@ -1,1 +1,64 @@
+# # ## ### ##### ######## #############
+
+## A simple class for handling an in-memory index mapping from
+## arbitrary strings to a small numeric id. Can be queried in reverse
+## too, returning the string for the id.
+
+## Id's are starting from 1.
+
+# # ## ### ##### ######## #############
+## Requirements.
+
+package require Tcl  ; # Runtime.
+package require snit ; # OO runtime.
+
+# # ## ### ##### ######## #############
+## Implementation.
+
+snit::type ::vc::tools::id {
+    # # ## ### ##### ######## #############
+
+    constructor {} {}
+
+    # # ## ### ##### ######## #############
+    ## Public API.
+    ## - Put data into the index, incl. query for id of key.
+    ## - Lookup data for id.
+
+    method put {key} {
+	if {[info exists mydata($key)]} { return $mydata($key) }
+	incr mycounter
+
+	set mydata($key)   $mycounter
+	set myinvert($mycounter) $key
+
+	return $mycounter
+    }
+
+    # Explicitly load the database with a mapping.
+    method map {id key} {
+	set mydata($key)   $id
+	set myinvert($id) $key
+    }
+
+    method keyof {id} { return $myinvert($id) }
+    method get   {}   { return [array get mydata] }
+
+    # # ## ### ##### ######## #############
+    ## Internal. State.
+
+    variable mydata   -array {} ; # Map data -> id
+    variable myinvert -array {} ; # Map id -> data
+    variable mycounter        0 ; # Counter for id generation.
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::tools {
+    namespace export id
+}
+
+# # ## ### ##### ######## #############
+## Ready.
 
+package provide vc::tools::id 1.0

Added tools/cvs2fossil/lib/log.tcl version [2d2937d11b]

@@ -1,1 +1,168 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Utility package, basic user feedback
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4 ; # Required runtime
+package require snit    ; # OO system.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::tools::log {
+    # # ## ### ##### ######## #############
+    ## Public API, Methods
+
+    # Write the message 'text' to log, for the named 'system'. The
+    # message is written if and only if the message verbosity is less
+    # or equal the chosen verbosity. A message of verbosity 0 cannot
+    # be blocked.
+
+    typemethod write {verbosity system text} {
+	if {$verbosity > $myloglevel} return
+	uplevel #0 [linsert $mylogcmd end write [System $system] $text]
+	return
+    }
+
+    # Similar to write, especially in the handling of the verbosity,
+    # to drive progress displays. It signals that for some long
+    # running operation we are at tick 'n' of at most 'max' ticks. An
+    # empty 'max' indicates an infinite progress display.
+
+    typemethod progress {verbosity system n max} {
+	if {$verbosity > $myloglevel} return
+	uplevel #0 [linsert $mylogcmd end progress [System $system] $n $max]
+	return
+    }
+
+    typemethod visible? {verbosity} {
+	return [expr {$verbosity <= $myloglevel}]
+    }
+
+    # # ## ### ##### ######## #############
+    # Public API, Administrative methods
+
+    # Set verbosity to the chosen 'level'. Only messages with a level
+    # less or equal to this one will be shown.
+
+    typemethod verbosity {level} {
+	if {$level < 1} {set level 0}
+	set myloglevel $level
+	return
+    }
+
+    typemethod verbose {} {
+	incr myloglevel
+	return
+    }
+
+    typemethod quiet {} {
+	if {$myloglevel < 1} return
+	incr myloglevel -1
+	return
+    }
+
+    # Query the currently set verbosity.
+
+    typemethod verbosity? {} {
+	return  $myloglevel
+    }
+
+    # Set the log callback handling the actual output of messages going
+    # through the package.
+
+    typemethod command {cmdprefix} {
+	variable mylogcmd $cmdprefix
+	return
+    }
+
+    # Register a system name, to enable tabular formatting. This is
+    # done by setting up a format specifier with a proper width. This
+    # is handled in the generation command, before the output callback
+    # is invoked.
+
+    typemethod register {name} {
+	set nlen [string length $name]
+	if {$nlen < $mysyslen} return
+	set mysyslen $nlen
+	set mysysfmt %-${mysyslen}s
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal, state
+
+    typevariable myloglevel 2                     ; # Some verbosity, not too much
+    typevariable mylogcmd   ::vc::tools::log::OUT ; # Standard output to stdout.
+    typevariable mysysfmt %s                      ; # Non-tabular formatting.
+    typevariable mysyslen 0                       ; # Ditto.
+
+    # # ## ### ##### ######## #############
+    ## Internal, helper methods (formatting, dispatch)
+
+    proc System {s} {
+	::variable mysysfmt
+	return [format $mysysfmt $s]
+    }
+
+    # # ## ### ##### ######## #############
+    ## Standard output callback, module internal
+
+    # Dispatch to the handlers of the possible operations.
+
+    proc OUT {op args} {
+	eval [linsert $args 0 ::vc::tools::log::OUT/$op]
+	return
+    }
+
+    # Write handler. Each message is a line.
+
+    proc OUT/write {system text} {
+	puts "$system $text"
+	return
+    }
+
+    # Progress handler. Uses \r to return to the beginning of the
+    # current line without advancing.
+
+    proc OUT/progress {system n max} {
+	if {$max eq {}} {
+	    puts -nonewline "$system $n\r"
+	} else {
+	    puts -nonewline "$system [format %[string length $max]s $n]/$max\r"
+	}
+	flush stdout
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::tools {
+    namespace export log
+}
+
+# -----------------------------------------------------------------------------
+# Ready
 
+package provide vc::tools::log 1.0
+return

Added tools/cvs2fossil/lib/misc.tcl version [d837793c99]

@@ -1,1 +1,100 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Utilities for various things: text formatting, max, ...
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4 ; # Required runtime
+
+# # ## ### ##### ######## ############# #####################
+##
+
+namespace eval ::vc::tools::misc {
+    # # ## ### ##### ######## #############
+    ## Public API, Methods
+
+    # Choose singular vs plural forms of a word based on a number.
+
+    proc sp {n singular {plural {}}} {
+	if {$n == 1} {return $singular}
+	if {$plural eq ""} {set plural ${singular}s}
+	return $plural
+    }
+
+    # As above, with the number automatically put in front of the
+    # string.
+
+    proc nsp {n singular {plural {}}} {
+	return "$n [sp $n $singular $plural]"
+    }
+
+    # Find maximum/minimum in a list.
+
+    proc max {list} {
+	set max -1
+	foreach e $list {
+	    if {$e < $max} continue
+	    set max $e
+	}
+	return $max
+    }
+
+    proc min {list} {
+	set min {}
+	foreach e $list {
+	    if {$min == {}} {
+		set min $e
+	    } elseif {$e > $min} continue
+	    set min $e
+	}
+	return $min
+    }
+
+    proc max2 {a b} {
+	if {$a > $b}  { return $a }
+	return $b
+    }
+
+    proc min2 {a b} {
+	if {$a < $b}  { return $a }
+	return $b
+    }
+
+    proc ldelete {lv item} {
+	upvar 1 $lv list
+	set pos [lsearch -exact $list $item]
+	if {$pos < 0} return
+	set list [lreplace $list $pos $pos]
+	return
+    }
+
+    # Delete item from list by name
+
+    proc striptrailingslash {path} {
+	# split and rejoin gets rid of a traling / character.
+	return [eval [linsert [file split $path] 0 file join]]
+    }
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::tools::misc {
+    namespace export sp nsp max min max2 min2 ldelete striptrailingslash
+}
+
+# -----------------------------------------------------------------------------
+# Ready
 
+package provide vc::tools::misc 1.0
+return

Added tools/cvs2fossil/lib/pkgIndex.tcl version [326b42f768]

@@ -1,1 +1,36 @@
-
+# # ## ### ##### ######## ############# #####################
+## Package management.
+## Index of the local packages required by cvs2fossil
+# # ## ### ##### ######## ############# #####################
+if {![package vsatisfies [package require Tcl] 8.4]} return
+package ifneeded vc::fossil::import::cvs                    1.0 [list source [file join $dir cvs2fossil.tcl]]
+package ifneeded vc::fossil::import::cvs::file              1.0 [list source [file join $dir c2f_file.tcl]]
+package ifneeded vc::fossil::import::cvs::file::lodmgr      1.0 [list source [file join $dir c2f_flodmgr.tcl]]
+package ifneeded vc::fossil::import::cvs::file::rev         1.0 [list source [file join $dir c2f_frev.tcl]]
+package ifneeded vc::fossil::import::cvs::file::sym         1.0 [list source [file join $dir c2f_fsym.tcl]]
+package ifneeded vc::fossil::import::cvs::file::trunk       1.0 [list source [file join $dir c2f_ftrunk.tcl]]
+package ifneeded vc::fossil::import::cvs::option            1.0 [list source [file join $dir c2f_option.tcl]]
+package ifneeded vc::fossil::import::cvs::integrity         1.0 [list source [file join $dir c2f_integrity.tcl]]
+package ifneeded vc::fossil::import::cvs::pass              1.0 [list source [file join $dir c2f_pass.tcl]]
+package ifneeded vc::fossil::import::cvs::pass::collar      1.0 [list source [file join $dir c2f_pcollar.tcl]]
+package ifneeded vc::fossil::import::cvs::pass::collrev     1.0 [list source [file join $dir c2f_pcollrev.tcl]]
+package ifneeded vc::fossil::import::cvs::pass::collsym     1.0 [list source [file join $dir c2f_pcollsym.tcl]]
+package ifneeded vc::fossil::import::cvs::pass::filtersym   1.0 [list source [file join $dir c2f_pfiltersym.tcl]]
+package ifneeded vc::fossil::import::cvs::pass::initcsets   1.0 [list source [file join $dir c2f_pinitcsets.tcl]]
+package ifneeded vc::fossil::import::cvs::pass::breakrcycle 1.0 [list source [file join $dir c2f_pbreakrcycle.tcl]]
+package ifneeded vc::fossil::import::cvs::pass::breakscycle 1.0 [list source [file join $dir c2f_pbreakscycle.tcl]]
+package ifneeded vc::fossil::import::cvs::pass::breakacycle 1.0 [list source [file join $dir c2f_pbreakacycle.tcl]]
+package ifneeded vc::fossil::import::cvs::cyclebreaker      1.0 [list source [file join $dir c2f_cyclebreaker.tcl]]
+package ifneeded vc::fossil::import::cvs::project           1.0 [list source [file join $dir c2f_project.tcl]]
+package ifneeded vc::fossil::import::cvs::project::lodmgr   1.0 [list source [file join $dir c2f_plodmgr.tcl]]
+package ifneeded vc::fossil::import::cvs::project::rev      1.0 [list source [file join $dir c2f_prev.tcl]]
+package ifneeded vc::fossil::import::cvs::project::revlink  1.0 [list source [file join $dir c2f_prevlink.tcl]]
+package ifneeded vc::fossil::import::cvs::project::sym      1.0 [list source [file join $dir c2f_psym.tcl]]
+package ifneeded vc::fossil::import::cvs::project::trunk    1.0 [list source [file join $dir c2f_ptrunk.tcl]]
+package ifneeded vc::fossil::import::cvs::repository        1.0 [list source [file join $dir c2f_repository.tcl]]
+package ifneeded vc::fossil::import::cvs::state             1.0 [list source [file join $dir c2f_state.tcl]]
+package ifneeded vc::rcs::parser                            1.0 [list source [file join $dir rcsparser.tcl]]
+package ifneeded vc::tools::log                             1.0 [list source [file join $dir log.tcl]]
+package ifneeded vc::tools::misc                            1.0 [list source [file join $dir misc.tcl]]
+package ifneeded vc::tools::trouble                         1.0 [list source [file join $dir trouble.tcl]]
+package ifneeded vc::tools::id                              1.0 [list source [file join $dir id.tcl]]

Added tools/cvs2fossil/lib/rcsparser.tcl version [93e4a1e5a5]

@@ -1,1 +1,485 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+# A tool package, provides a parser for RCS archive files. This parser
+# is implemented via recursive descent. It is not only given a file to
+# process, but also a 'sink', an object it calls out to at important
+# places of the parsing process to either signal an event and/or
+# convey gathered information to it. The sink is responsible for the
+# actual processing of the data in whatever way it desires.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4                             ; # Required runtime.
+package require snit                                ; # OO system.
+package require fileutil                            ; # File utilities.
+package require vc::tools::log                      ; # User feedback.
+package require struct::list                        ; # Advanced list ops.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::rcs::parser {
+    # # ## ### ##### ######## #############
+    ## Public API
+
+    typemethod process {path sink} {
+	Initialize $path $sink
+	Call begin
+	Admin ; Deltas ; Description ; DeltaTexts
+	Call done
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods, recursive descent, syntactical processing
+
+    proc Admin {} {
+	Head ; PrincipalBranch ; Access ; Symbols
+	Locks ; Strictness ; FileComment ; Expand
+	Call admindone
+	return
+    }
+
+    # # ## ### ##### ######## #############
+
+    proc Head {} {
+	RequiredLiteral head
+	RequiredNumber -> head
+	Semicolon
+	Call sethead $head
+	return
+    }
+
+    proc PrincipalBranch {} {
+	if {![OptionalLiteral branch]} return
+	RequiredNumber -> branch
+	Semicolon
+	Call setprincipalbranch $branch
+	return
+    }
+
+    proc Access {} {
+	RequiredLiteral access ;
+	Semicolon
+	return
+    }
+
+    proc Symbols {} {
+	RequiredLiteral symbols
+	while {[Ident -> symbol]} {
+	    if {
+		![regexp {^\d*[^,.:;@$]([^,.:;@$]*\d*)*$} $symbol] ||
+		[string match */ $symbol]
+	    } {
+		Rewind
+		Bad {symbol name}
+	    }
+	    RequiredNumber -> rev
+	    Call deftag $symbol $rev
+	}
+	Semicolon
+	return
+    }
+
+    proc Locks {} {
+	# Not saving locks.
+	RequiredLiteral locks
+	while {[Ident -> symbol]} {
+	    RequiredNumber -> l
+	}
+	Semicolon
+	return
+    }
+
+    proc Strictness {} {
+	# Not saving strictness
+	if {![OptionalLiteral strict]} return
+	Semicolon
+	return
+    }
+
+    proc FileComment {} {
+	if {![OptionalLiteral comment]} return
+	if {![OptionalString -> c]} return
+	Semicolon
+	Call setcomment $c
+	return
+    }
+
+    proc Expand {} {
+	# Not saving expanded keywords
+	if {![OptionalLiteral expand]} return
+	if {![OptionalString -> dummy]} return
+	Semicolon
+	return
+    }
+
+    # # ## ### ##### ######## #############
+
+    proc Deltas {} {
+	set ok [OptionalNumber -> rev]
+	while {$ok} {
+	    Date     -> d
+	    Author   -> a
+	    State    -> s
+	    Branches -> b
+	    NextRev  -> n
+	    Call def $rev $d $a $s $n $b
+
+	    # Check if this is followed by a revision number or the
+	    # literal 'desc'. If neither we consume whatever is there
+	    # until the next semicolon, as it has to be a 'new
+	    # phrase'. Otherwise, for a revision number we loop back
+	    # and consume that revision, and lastly for 'desc' we stop
+	    # completely as this signals the end of the revision tree
+	    # and the beginning of the deltas.
+
+	    while {1} {
+		set ok [OptionalNumber -> rev]
+		if {$ok} break
+
+		if {[LiteralPeek desc]} {
+		    set ok 0
+		    break
+		}
+
+		Anything -> dummy
+		Semicolon
+	    }
+	}
+	Call defdone
+	return
+    }
+
+    # # ## ### ##### ######## #############
+
+    proc Date {_ dv} {
+	upvar 1 $dv d
+	RequiredLiteral date
+	RequiredNumber -> d
+	Semicolon
+
+	struct::list assign [split $d .] year month day hour min sec
+	if {$year < 100} {incr year 1900}
+	set d [clock scan "${year}-${month}-${day} ${hour}:${min}:${sec}"]
+	return
+    }
+
+    proc Author {_ av} {
+	upvar 1 $av a
+	RequiredLiteral author
+	Anything -> a
+	Semicolon
+	return
+    }
+
+    proc State {_ sv} {
+	upvar 1 $sv s
+	RequiredLiteral state
+	Anything -> s
+	Semicolon
+	return
+    }
+
+    proc Branches {_ bv} {
+	upvar 1 $bv b
+	RequiredLiteral branches
+	Anything -> b
+	Semicolon
+	return
+    }
+
+    proc NextRev {_ nv} {
+	upvar 1 $nv n
+	RequiredLiteral next
+	Anything -> n
+	Semicolon
+	return
+    }
+
+    # # ## ### ##### ######## #############
+
+    proc Description {} {
+	upvar 1 data data res res
+	RequiredLiteral desc
+	RequiredString -> d
+	Call setdesc $d
+	return
+    }
+
+    # # ## ### ##### ######## #############
+
+    proc DeltaTexts {} {
+	while {[OptionalNumber -> rev]} {
+	    RequiredLiteral log
+	    RequiredString      -> cmsg
+	    if {[regexp {[\000-\010\013\014\016-\037]} $cmsg]} {
+		#Rewind
+		#Bad "log message for $rev contains at least one control character"
+	    }
+
+	    RequiredLiteral text
+	    RequiredStringRange -> delta
+	    Call extend $rev $cmsg $delta
+	}
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods, lexiographical processing
+
+    proc Semicolon {} {
+	::variable mydata
+	::variable mypos
+
+	set ok [regexp -start $mypos -indices -- {\A\s*;\s*} $mydata match]
+	if {!$ok} { Expected ';' }
+
+	SkipOver match
+	return
+    }
+
+    proc RequiredLiteral {name} {
+	::variable mydata
+	::variable mypos
+
+	set pattern "\\A\\s*$name\\s*"
+	set ok [regexp -start $mypos -indices -- $pattern $mydata match]
+	if {!$ok} { Expected '$name' }
+
+	SkipOver match
+	return
+    }
+
+    proc OptionalLiteral {name} {
+	::variable mydata
+	::variable mypos
+
+	set pattern "\\A\\s*$name\\s*"
+	set ok [regexp -start $mypos -indices -- $pattern $mydata match]
+	if {!$ok} { return 0 }
+
+	SkipOver match
+	return 1
+    }
+
+    proc LiteralPeek {name} {
+	::variable mydata
+	::variable mypos
+
+	set pattern "\\A\\s*$name\\s*"
+	set ok [regexp -start $mypos -indices -- $pattern $mydata match]
+	if {!$ok} { return 0 }
+
+	# NO - SkipOver match - Only looking ahead here.
+	return 1
+    }
+
+    proc RequiredNumber {_ v} {
+	upvar 1 $v value
+	::variable mydata
+	::variable mypos
+
+	set pattern {\A\s*((\d|\.)+)\s*}
+	set ok [regexp -start $mypos -indices -- $pattern $mydata match v]
+	if {!$ok} { Expected id }
+
+	Extract $v -> value
+	SkipOver match
+	return
+    }
+
+    proc OptionalNumber {_ v} {
+	upvar 1 $v value
+	::variable mydata
+	::variable mypos
+
+	set pattern {\A\s*((\d|\.)+)\s*}
+	set ok [regexp -start $mypos -indices -- $pattern $mydata match v]
+	if {!$ok} { return 0 }
+
+	Extract $v -> value
+	SkipOver match
+	return 1
+    }
+
+    proc RequiredString {_ v} {
+	upvar 1 $v value
+	::variable mydata
+	::variable mypos
+
+	set ok [regexp -start $mypos -indices -- {\A\s*@(([^@]*(@@)*)*)@\s*} $mydata match v]
+	if {!$ok} { Expected string }
+
+	Extract $v -> value
+	set value [string map {@@ @} $value]
+	SkipOver match
+	return
+    }
+
+    proc RequiredStringRange {_ v} {
+	upvar 1 $v value
+	::variable mydata
+	::variable mypos
+
+	set ok [regexp -start $mypos -indices -- {\A\s*@(([^@]*(@@)*)*)@\s*} $mydata match value]
+	if {!$ok} { Expected string }
+
+	SkipOver match
+	return
+    }
+
+    proc OptionalString {_ v} {
+	upvar 1 $v value
+	::variable mydata
+	::variable mypos
+
+	set ok [regexp -start $mypos -indices -- {\A\s*@(([^@]*(@@)*)*)@\s*} $mydata match v]
+	if {!$ok} { return 0 }
+
+	Extract $v -> value
+	set value [string map {@@ @} $value]
+	SkipOver match
+	return 1
+    }
+
+    proc Ident {_ v} {
+	upvar 1 $v value
+	::variable mydata
+	::variable mypos
+
+	set ok [regexp -start $mypos -indices -- {\A\s*;\s*} $mydata]
+	if {$ok} { return 0 }
+
+	set ok [regexp -start $mypos -indices -- {\A\s*([^:]*)\s*:\s*} $mydata match v]
+	if {!$ok} { return 0 }
+
+	Extract $v -> value
+	SkipOver match
+	return 1
+    }
+
+    proc Anything {_ v} {
+	upvar 1 $v value
+	::variable mydata
+	::variable mypos
+
+	regexp -start $mypos -indices -- {\A\s*([^;]*)\s*} $mydata match v
+
+	Extract $v -> value
+	SkipOver match
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal methods, input handling
+
+    proc Extract {range _ v} {
+	upvar 1 $v value
+	::variable mydata
+	struct::list assign $range s e
+	set value [string range $mydata $s $e]
+	return
+    }
+
+    proc SkipOver {mv} {
+	# Note: The indices are absolute!, not relative to the start
+	# location.
+	upvar 1 $mv match
+	::variable mypos
+	::variable mysize
+	::variable mylastpos
+
+	struct::list assign $match s e
+	#puts "<$s $e> [info level -1]"
+
+	set  mylastpos $mypos
+	set  mypos $e
+	incr mypos
+
+	log progress 2 rcs $mypos $mysize
+	#puts $mypos/$mysize
+	return
+    }
+
+    proc Rewind {} {
+	::variable mypos
+	::variable mylastpos
+
+	set  mypos $mylastpos
+	return
+    }
+
+    proc Expected {x} {
+	::variable mydata
+	::variable mypos
+	set e $mypos ; incr e 30
+	return -code error -errorcode vc::rcs::parser \
+	    "Expected $x @ '[string range $mydata $mypos $e]...'"
+    }
+
+    proc Bad {x} {
+	::variable mydata
+	::variable mypos
+	set e $mypos ; incr e 30
+	return -code error -errorcode vc::rcs::parser \
+	    "Bad $x @ '[string range $mydata $mypos $e]...'"
+    }
+
+    # # ## ### ##### ######## #############
+    ## Setup, callbacks.
+
+    proc Initialize {path sink} {
+	::variable mypos  0
+	::variable mydata [fileutil::cat -encoding binary $path]
+	::variable mysize [file size $path]
+	::variable mysink $sink
+	return
+    }
+
+    proc Call {args} {
+	::variable mysink
+	set cmd $mysink
+	foreach a $args { lappend cmd $a }
+	eval $cmd
+	return
+    }
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    typevariable mydata {} ; # Rcs archive contents to process
+    typevariable mysize 0  ; # Length of contents
+    typevariable mysink {} ; # Sink to report to
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+namespace eval ::vc::rcs {
+    namespace export parser
+    namespace eval parser {
+	namespace import ::vc::tools::log
+	log register rcs
+    }
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::rcs::parser 1.0
+return

Added tools/cvs2fossil/lib/trouble.tcl version [f653273970]

@@ -1,1 +1,124 @@
+## -*- tcl -*-
+# # ## ### ##### ######## ############# #####################
+## 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
+# # ## ### ##### ######## ############# #####################
+
+## Utility package, error reporting on top of the log package.
+
+# # ## ### ##### ######## ############# #####################
+## Requirements
+
+package require Tcl 8.4        ; # Required runtime.
+package require vc::tools::log ; # Basic log generation.
+package require snit           ; # OO system.
+
+# # ## ### ##### ######## ############# #####################
+##
+
+snit::type ::vc::tools::trouble {
+    # # ## ### ##### ######## #############
+    ## Public API, Methods
+
+    typemethod internal {text} {
+	foreach line [split $text \n] { $type fatal "INTERNAL ERROR! $line" }
+	exit 1
+    }
+
+    typemethod fatal {text} {
+	lappend myfatal $text
+	return
+    }
+
+    typemethod warn {text} {
+	lappend mywarn $text
+	log write 0 trouble $text
+	return
+    }
+
+    typemethod info {text} {
+	lappend myinfo $text
+	return
+    }
+
+    typemethod show {} {
+	foreach m $myinfo  { log write 0 ""      $m }
+	foreach m $mywarn  { log write 0 warning $m }
+	foreach m $myfatal { log write 0 fatal   $m }
+	return
+    }
+
+    typemethod ? {} {
+	return [expr {
+	    [llength $myinfo] ||
+	    [llength $mywarn] ||
+	    [llength $myfatal]
+	}]
+    }
+
+    typemethod abort? {} {
+	if {
+	    ![llength $myinfo] &&
+	    ![llength $mywarn] &&
+	    ![llength $myfatal]
+	} return
+
+	# Frame the pending messages to make them more clear as the
+	# cause of the abort.
+
+	set     myinfo [linsert $myinfo 0 "" "Encountered problems." ""]
+	lappend myfatal "Stopped due to problems."
+
+	# We have error messages to print, so stop now.
+	exit 1
+    }
+
+    # # ## ### ##### ######## #############
+    ## Internal, state
+
+    typevariable myinfo  {}
+    typevariable mywarn  {}
+    typevariable myfatal {}
+
+    # # ## ### ##### ######## #############
+    ## Configuration
+
+    pragma -hasinstances   no ; # singleton
+    pragma -hastypeinfo    no ; # no introspection
+    pragma -hastypedestroy no ; # immortal
+
+    # # ## ### ##### ######## #############
+}
+
+# # ## ### ##### ######## ############# #####################
+## Internal. Special. Set up a hook into the application exit, to show
+## the remembered messages, before passing through the regular command.
+
+rename ::exit ::vc::tools::trouble::EXIT
+proc   ::exit {{status 0}} {
+    ::vc::tools::trouble show
+    ::vc::tools::trouble::EXIT $status
+    # Not reached.
+    return
+}
+
+namespace eval ::vc::tools {
+    namespace eval trouble {namespace import ::vc::tools::log }
+    trouble::log register ""
+    trouble::log register fatal
+    trouble::log register trouble
+    trouble::log register warning
+    namespace export trouble
+}
+
+# # ## ### ##### ######## ############# #####################
+## Ready
 
+package provide vc::tools::trouble 1.0
+return

Deleted tools/import-cvs.tcl version [8655038b63]

Deleted tools/lib/cvs.tcl version [e947a38eb8]

Deleted tools/lib/cvs_cmd.tcl version [47fea6fc5d]

Deleted tools/lib/cvs_csets.tcl version [c6beb0fbbb]

Deleted tools/lib/cvs_files.tcl version [24c1271ded]

Deleted tools/lib/cvs_timeline.tcl version [09c55e80b2]

Deleted tools/lib/fossil.tcl version [6aae29bf14]

Deleted tools/lib/fossil_cmd.tcl version [49d37a1b8b]

Deleted tools/lib/import_map.tcl version [44f7107846]

Deleted tools/lib/import_statistics.tcl version [afdce6cc23]

Deleted tools/lib/importcvs.tcl version [d5f1e97a32]

Deleted tools/lib/log.tcl version [edfb9cfb1e]

Deleted tools/lib/pkgIndex.tcl version [48d829569b]

Deleted tools/lib/rcsparser.tcl version [564a99c1a7]

Deleted tools/lib/trouble.tcl version [a4a0b8a72b]

Added www/CollRev1.gif version [3ca46d7257]

cannot compute difference between binary files

Added www/CollRev2.gif version [581afba0a0]

cannot compute difference between binary files

Added www/CollRev3.gif version [996e26f2d6]

cannot compute difference between binary files

Added www/CollRev4.gif version [2344026a4c]

cannot compute difference between binary files