Rev 2777: Support annotating knits with no annotation cache in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Mon Sep 3 00:36:09 BST 2007


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 2777
revision-id: pqm at pqm.ubuntu.com-20070902233606-wb062d366w5c83uc
parent: pqm at pqm.ubuntu.com-20070901160444-hcr66zejwyy0jezc
parent: aaron.bentley at utoronto.ca-20070902223854-vh6jc4c19w94htsm
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Mon 2007-09-03 00:36:06 +0100
message:
  Support annotating knits with no annotation cache
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/annotate.py             annotate.py-20050922133147-7c60541d2614f022
  bzrlib/knit.py                 knit.py-20051212171256-f056ac8f0fbe1bd9
  bzrlib/tests/test_annotate.py  test_annotate.py-20061213215015-sttc9agsxomls7q0-1
  bzrlib/tests/test_versionedfile.py test_versionedfile.py-20060222045249-db45c9ed14a1c2e5
    ------------------------------------------------------------
    revno: 2770.1.10
    merged: aaron.bentley at utoronto.ca-20070902223854-vh6jc4c19w94htsm
    parent: aaron.bentley at utoronto.ca-20070902223333-e0g232qwy63mj4qm
    parent: pqm at pqm.ubuntu.com-20070901160444-hcr66zejwyy0jezc
    committer: Aaron Bentley <aaron.bentley at utoronto.ca>
    branch nick: pack-annotate
    timestamp: Sun 2007-09-02 18:38:54 -0400
    message:
      Merge bzr.dev
    ------------------------------------------------------------
    revno: 2770.1.9
    merged: aaron.bentley at utoronto.ca-20070902223333-e0g232qwy63mj4qm
    parent: aaron.bentley at utoronto.ca-20070902222950-s810wxdatgaap7by
    committer: Aaron Bentley <aaron.bentley at utoronto.ca>
    branch nick: pack-annotate
    timestamp: Sun 2007-09-02 18:33:33 -0400
    message:
      Simplify annotate iterator
    ------------------------------------------------------------
    revno: 2770.1.8
    merged: aaron.bentley at utoronto.ca-20070902222950-s810wxdatgaap7by
    parent: abentley at panoramicfeedback.com-20070831200932-cce1tz9ft6bdfjw5
    committer: Aaron Bentley <aaron.bentley at utoronto.ca>
    branch nick: pack-annotate
    timestamp: Sun 2007-09-02 18:29:50 -0400
    message:
      Use topo-sorted ancestry for generating annotations
    ------------------------------------------------------------
    revno: 2770.1.7
    merged: abentley at panoramicfeedback.com-20070831200932-cce1tz9ft6bdfjw5
    parent: abentley at panoramicfeedback.com-20070831194044-1328krhsdc795cbc
    committer: Aaron Bentley <abentley at panoramicfeedback.com>
    branch nick: pack-annotate
    timestamp: Fri 2007-08-31 16:09:32 -0400
    message:
      Update NEWS
    ------------------------------------------------------------
    revno: 2770.1.6
    merged: abentley at panoramicfeedback.com-20070831194044-1328krhsdc795cbc
    parent: abentley at panoramicfeedback.com-20070831193852-6ip22sbw058yib5u
    committer: Aaron Bentley <abentley at panoramicfeedback.com>
    branch nick: pack-annotate
    timestamp: Fri 2007-08-31 15:40:44 -0400
    message:
      Remove annotate override for normal knits
    ------------------------------------------------------------
    revno: 2770.1.5
    merged: abentley at panoramicfeedback.com-20070831193852-6ip22sbw058yib5u
    parent: aaron.bentley at utoronto.ca-20070831121426-rh5lo64rygkpy3xs
    committer: Aaron Bentley <abentley at panoramicfeedback.com>
    branch nick: pack-annotate
    timestamp: Fri 2007-08-31 15:38:52 -0400
    message:
      Clean up docs, test matching blocks for reannotate
    ------------------------------------------------------------
    revno: 2770.1.4
    merged: aaron.bentley at utoronto.ca-20070831121426-rh5lo64rygkpy3xs
    parent: aaron.bentley at utoronto.ca-20070831035850-9ap3s1a6m0qc03zg
    committer: Aaron Bentley <aaron.bentley at utoronto.ca>
    branch nick: pack-annotate
    timestamp: Fri 2007-08-31 08:14:26 -0400
    message:
      Further optimize annotation, using existing matching blocks
    ------------------------------------------------------------
    revno: 2770.1.3
    merged: aaron.bentley at utoronto.ca-20070831035850-9ap3s1a6m0qc03zg
    parent: aaron.bentley at utoronto.ca-20070831031243-c87mp74f7xvdymi1
    committer: Aaron Bentley <aaron.bentley at utoronto.ca>
    branch nick: pack-annotate
    timestamp: Thu 2007-08-30 23:58:50 -0400
    message:
      Rework knit annotation as stack-based
    ------------------------------------------------------------
    revno: 2770.1.2
    merged: aaron.bentley at utoronto.ca-20070831031243-c87mp74f7xvdymi1
    parent: aaron.bentley at utoronto.ca-20070831025954-bs1ngof5pa3kejrs
    committer: Aaron Bentley <aaron.bentley at utoronto.ca>
    branch nick: pack-annotate
    timestamp: Thu 2007-08-30 23:12:43 -0400
    message:
      Convert to knit-only annotation
    ------------------------------------------------------------
    revno: 2770.1.1
    merged: aaron.bentley at utoronto.ca-20070831025954-bs1ngof5pa3kejrs
    parent: pqm at pqm.ubuntu.com-20070830171447-t9j1u709q4yw63lh
    committer: Aaron Bentley <aaron.bentley at utoronto.ca>
    branch nick: pack-annotate
    timestamp: Thu 2007-08-30 22:59:54 -0400
    message:
      Initial implmentation of plain knit annotation
=== modified file 'NEWS'
--- a/NEWS	2007-09-01 16:04:44 +0000
+++ b/NEWS	2007-09-02 22:38:54 +0000
@@ -171,6 +171,9 @@
       efficiently streaming the knit data for a set of versions over the smart
       protocol.
 
+    * Knits with no annotation cache still produce correct annotations.
+      (Aaron Bentley)
+
   TESTING:
 
     * Use UTF-8 encoded StringIO for log tests to avoid failures on

=== modified file 'bzrlib/annotate.py'
--- a/bzrlib/annotate.py	2007-08-16 18:50:22 +0000
+++ b/bzrlib/annotate.py	2007-08-31 19:38:52 +0000
@@ -137,20 +137,29 @@
         yield (revno_str, author, date_str, origin, text)
 
 
-def reannotate(parents_lines, new_lines, new_revision_id):
+def reannotate(parents_lines, new_lines, new_revision_id,
+               _left_matching_blocks=None):
     """Create a new annotated version from new lines and parent annotations.
     
     :param parents_lines: List of annotated lines for all parents
     :param new_lines: The un-annotated new lines
     :param new_revision_id: The revision-id to associate with new lines
         (will often be CURRENT_REVISION)
+    :param left_matching_blocks: a hint about which areas are common
+        between the text and its left-hand-parent.  The format is
+        the SequenceMatcher.get_matching_blocks format.
     """
-    if len(parents_lines) == 1:
-        for data in _reannotate(parents_lines[0], new_lines, new_revision_id):
+    if len(parents_lines) == 0:
+        for line in new_lines:
+            yield new_revision_id, line
+    elif len(parents_lines) == 1:
+        for data in _reannotate(parents_lines[0], new_lines, new_revision_id,
+                                _left_matching_blocks):
             yield data
     else:
-        reannotations = [list(_reannotate(p, new_lines, new_revision_id)) for
-                         p in parents_lines]
+        block_list = [_left_matching_blocks] + [None] * len(parents_lines)
+        reannotations = [list(_reannotate(p, new_lines, new_revision_id, b))
+                         for p, b in zip(parents_lines, block_list)]
         for annos in zip(*reannotations):
             origins = set(a for a, l in annos)
             line = annos[0][1]
@@ -162,12 +171,15 @@
                 yield new_revision_id, line
 
 
-def _reannotate(parent_lines, new_lines, new_revision_id):
+def _reannotate(parent_lines, new_lines, new_revision_id,
+                matching_blocks=None):
     plain_parent_lines = [l for r, l in parent_lines]
     matcher = patiencediff.PatienceSequenceMatcher(None, plain_parent_lines,
                                                    new_lines)
     new_cur = 0
-    for i, j, n in matcher.get_matching_blocks():
+    if matching_blocks is None:
+        matching_blocks = matcher.get_matching_blocks()
+    for i, j, n in matching_blocks:
         for line in new_lines[new_cur:j]:
             yield new_revision_id, line
         for data in parent_lines[i:i+n]:

=== modified file 'bzrlib/knit.py'
--- a/bzrlib/knit.py	2007-08-31 02:05:10 +0000
+++ b/bzrlib/knit.py	2007-09-02 22:38:54 +0000
@@ -74,6 +74,7 @@
 from bzrlib.lazy_import import lazy_import
 lazy_import(globals(), """
 from bzrlib import (
+    annotate,
     pack,
     trace,
     )
@@ -290,6 +291,10 @@
                        for origin, text in lines)
         return out
 
+    def annotate_iter(self, knit, version_id):
+        content = knit._get_content(version_id)
+        return content.annotate_iter()
+
 
 class KnitPlainFactory(_KnitFactory):
     """Factory for creating plain Content objects."""
@@ -345,6 +350,9 @@
             out.extend([text for origin, text in lines])
         return out
 
+    def annotate_iter(self, knit, version_id):
+        return annotate_knit(knit, version_id)
+
 
 def make_empty_knit(transport, relpath):
     """Construct a empty knit at the specified location."""
@@ -1107,9 +1115,7 @@
     def annotate_iter(self, version_id):
         """See VersionedFile.annotate_iter."""
         version_id = osutils.safe_revision_id(version_id)
-        content = self._get_content(version_id)
-        for origin, text in content.annotate_iter():
-            yield origin, text
+        return self.factory.annotate_iter(self, version_id)
 
     def get_parents(self, version_id):
         """See VersionedFile.get_parents."""
@@ -2522,6 +2528,33 @@
         return besti, bestj, bestsize
 
 
+def annotate_knit(knit, revision_id):
+    """Annotate a knit with no cached annotations.
+
+    This implementation is for knits with no cached annotations.
+    It will work for knits with cached annotations, but this is not
+    recommended.
+    """
+    ancestry = knit.get_ancestry(revision_id)
+    fulltext = dict(zip(ancestry, knit.get_line_list(ancestry)))
+    annotations = {}
+    for candidate in ancestry:
+        if candidate in annotations:
+            continue
+        parents = knit.get_parents(candidate)
+        if len(parents) == 0:
+            blocks = None
+        elif knit._index.get_method(candidate) != 'line-delta':
+            blocks = None
+        else:
+            parent, sha1, noeol, delta = knit.get_delta(candidate)
+            blocks = KnitContent.get_line_delta_blocks(delta,
+                fulltext[parents[0]], fulltext[candidate])
+        annotations[candidate] = list(annotate.reannotate([annotations[p]
+            for p in parents], fulltext[candidate], candidate, blocks))
+    return iter(annotations[revision_id])
+
+
 try:
     from bzrlib._knit_load_data_c import _load_data_c as _load_data
 except ImportError:

=== modified file 'bzrlib/tests/test_annotate.py'
--- a/bzrlib/tests/test_annotate.py	2007-08-15 14:11:32 +0000
+++ b/bzrlib/tests/test_annotate.py	2007-08-31 19:38:52 +0000
@@ -87,6 +87,14 @@
 e
 """.splitlines(True)
 
+expected_1 = annotation("""\
+blahblah a
+blahblah b
+blahblah c
+blahblah d
+blahblah e
+""")
+
 
 new_2 = """\
 a
@@ -365,9 +373,10 @@
 
 class TestReannotate(tests.TestCase):
 
-    def annotateEqual(self, expected, parents, newlines, revision_id):
+    def annotateEqual(self, expected, parents, newlines, revision_id,
+                      blocks=None):
         annotate_list = list(annotate.reannotate(parents, newlines,
-                             revision_id))
+                             revision_id, blocks))
         self.assertEqual(len(expected), len(annotate_list))
         for e, a in zip(expected, annotate_list):
             self.assertEqual(e, a)
@@ -377,3 +386,21 @@
         self.annotateEqual(expected_2_1, [parent_2], new_1, 'blahblah')
         self.annotateEqual(expected_1_2_2, [parent_1, parent_2], new_2, 
                            'blahblah')
+
+    def test_reannotate_no_parents(self):
+        self.annotateEqual(expected_1, [], new_1, 'blahblah')
+
+    def test_reannotate_left_matching_blocks(self):
+        """Ensure that left_matching_blocks has an impact.
+
+        In this case, the annotation is ambiguous, so the hint isn't actually
+        lying.
+        """
+        parent = [('rev1', 'a\n')]
+        new_text = ['a\n', 'a\n']
+        blocks = [(0, 0, 1), (1, 2, 0)]
+        self.annotateEqual([('rev1', 'a\n'), ('rev2', 'a\n')], [parent],
+                           new_text, 'rev2', blocks)
+        blocks = [(0, 1, 1), (1, 2, 0)]
+        self.annotateEqual([('rev2', 'a\n'), ('rev1', 'a\n')], [parent],
+                           new_text, 'rev2', blocks)

=== modified file 'bzrlib/tests/test_versionedfile.py'
--- a/bzrlib/tests/test_versionedfile.py	2007-08-30 08:11:54 +0000
+++ b/bzrlib/tests/test_versionedfile.py	2007-09-02 22:38:54 +0000
@@ -34,8 +34,11 @@
                            RevisionAlreadyPresent,
                            WeaveParentMismatch
                            )
-from bzrlib.knit import KnitVersionedFile, \
-     KnitAnnotateFactory, KnitPlainFactory
+from bzrlib.knit import (
+    KnitVersionedFile,
+    KnitAnnotateFactory,
+    KnitPlainFactory,
+    )
 from bzrlib.tests import TestCaseWithMemoryTransport, TestSkipped
 from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
 from bzrlib.trace import mutter
@@ -881,8 +884,8 @@
 class TestKnit(TestCaseWithMemoryTransport, VersionedFileTestMixIn):
 
     def get_file(self, name='foo'):
-        return KnitVersionedFile(name, get_transport(self.get_url('.')),
-                                 delta=True, create=True)
+        return self.get_factory()(name, get_transport(self.get_url('.')),
+                                  delta=True, create=True)
 
     def get_factory(self):
         return KnitVersionedFile
@@ -894,7 +897,7 @@
         return knit
 
     def reopen_file(self, name='foo', create=False):
-        return KnitVersionedFile(name, get_transport(self.get_url('.')),
+        return self.get_factory()(name, get_transport(self.get_url('.')),
             delta=True,
             create=create)
 
@@ -909,6 +912,19 @@
                           get_transport(self.get_url('.')))
 
 
+class TestPlaintextKnit(TestKnit):
+    """Test a knit with no cached annotations"""
+
+    def _factory(self, name, transport, file_mode=None, access_mode=None,
+                 delta=True, create=False):
+        return KnitVersionedFile(name, transport, file_mode, access_mode,
+                                 KnitPlainFactory(), delta=delta,
+                                 create=create)
+
+    def get_factory(self):
+        return self._factory
+
+
 class InterString(versionedfile.InterVersionedFile):
     """An inter-versionedfile optimised code path for strings.
 




More information about the bazaar-commits mailing list