Rev 3471: start factoring the reannotation code into a policy class, which can then be subclassed. in http://bzr.arbash-meinel.com/branches/bzr/1.6-dev/annotation

John Arbash Meinel john at arbash-meinel.com
Wed Jun 4 23:38:00 BST 2008


At http://bzr.arbash-meinel.com/branches/bzr/1.6-dev/annotation

------------------------------------------------------------
revno: 3471
revision-id: john at arbash-meinel.com-20080604223747-vf0mbkj5goxkyc3w
parent: pqm at pqm.ubuntu.com-20080604174215-d0m8mjs939ek9ed7
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: annotation
timestamp: Wed 2008-06-04 17:37:47 -0500
message:
  start factoring the reannotation code into a policy class, which can then be subclassed.
modified:
  bzrlib/annotate.py             annotate.py-20050922133147-7c60541d2614f022
  bzrlib/tests/test_annotate.py  test_annotate.py-20061213215015-sttc9agsxomls7q0-1
-------------- next part --------------
=== modified file 'bzrlib/annotate.py'
--- a/bzrlib/annotate.py	2008-04-24 07:22:53 +0000
+++ b/bzrlib/annotate.py	2008-06-04 22:37:47 +0000
@@ -37,6 +37,102 @@
 from bzrlib.config import extract_email_address
 
 
+class AnnotationPolicy(object):
+    """This class represents a specific method for performing annotations.
+
+    Mostly, this class determines how to resolve ambiguous lines. (Lines which
+    both sides claim different last-modified revisions.)
+    """
+
+    def __init__(self):
+        self._sequence_matcher = patiencediff.PatienceSequenceMatcher
+
+    def _get_matching_blocks(self, old, new):
+        matcher = self._sequence_matcher(None, old, new)
+        return matcher.get_matching_blocks()
+
+    def process_unmatched_lines(self, output_lines, plain_child_lines,
+                                annotated_child_lines, start_child, end_child,
+                                annotated_right_lines, start_right, end_right,
+                                heads_provider, revision_id):
+        """Find lines in plain right_lines that match plain_child_lines
+
+        :param output_lines: Append final annotated lines to this list
+        :param plain_child_lines: The unannotated new lines for the child text
+        :param annotated_child_lines: Lines for the child text which have been
+            annotated from other parents
+        :param start_child: Position in plain_child_lines and
+            annotated_child_lines to start the match searching
+        :param end_child: Last position in plain_child_lines and
+            annotated_child_lines to search for a match
+        :param annotated_right_lines: The annotated lines for the whole text
+            for the right parent
+        :param start_right: Position in annotated_right_lines to start the
+            match
+        :param end_right: Last position in annotated_right_lines to search for
+            a match
+        :param heads_provider: When parents disagree on the lineage of a line,
+            we need to check if one side supersedes the other
+        :param revision_id: The label to give if a line should be labeled 'tip'
+        """
+        output_extend = output_lines.extend
+        output_append = output_lines.append
+        # We need to see if any of the unannotated lines match
+        plain_right_subset = [l for a,l in
+            annotated_right_lines[start_right:end_right]]
+        plain_child_subset = plain_child_lines[start_child:end_child]
+        match_blocks = self._get_matching_blocks(plain_right_subset,
+                                                 plain_child_subset)
+
+        last_child_idx = 0
+
+        self._heads_provider = heads_provider
+
+        for right_idx, child_idx, match_len in match_blocks:
+            # All the lines that don't match are just passed along
+            if child_idx > last_child_idx:
+                output_extend(annotated_child_lines[
+                    start_child + last_child_idx:start_child + child_idx])
+            for offset in xrange(match_len):
+                left = annotated_child_lines[start_child+child_idx+offset]
+                right = annotated_right_lines[start_right+right_idx+offset]
+                if left[0] == right[0]:
+                    # The annotations match, just return the left one
+                    output_append(left)
+                elif left[0] == revision_id:
+                    # The left parent marked this as unmatched, so let the
+                    # right parent claim it
+                    output_append(right)
+                else:
+                    # Left and Right both claim this line
+                    output_append(self._resolve_ambiguous(revision_id, left,
+                                                          right))
+            last_child_idx = child_idx + match_len
+
+    def _resolve_ambiguous(self, revision_id, left, right):
+        """We have an ambiguous line between two texts.
+        
+        Resolve this somehow.
+        """
+        if self._heads_provider is not None:
+            heads = self._heads_provider.heads((left[0], right[0]))
+            if len(heads) == 1:
+                return (iter(heads).next(), left[1])
+            else:
+                # Both claim different origins
+                # We know that revision_id is the head for
+                # left and right, so cache it
+                self._heads_provider.cache(
+                    (revision_id, left[0]),
+                    (revision_id,))
+                self._heads_provider.cache(
+                    (revision_id, right[0]),
+                    (revision_id,))
+                return (revision_id, left[1])
+        # Without a heads provider, we just always pick 'tip'
+        return (revision_id, left[1])
+
+
 def annotate_file(branch, rev_id, file_id, verbose=False, full=False,
                   to_file=None, show_ids=False):
     """Annotate file_id at revision rev_id in branch.
@@ -229,74 +325,6 @@
     return matcher.get_matching_blocks()
 
 
-def _find_matching_unannotated_lines(output_lines, plain_child_lines,
-                                     child_lines, start_child, end_child,
-                                     right_lines, start_right, end_right,
-                                     heads_provider, revision_id):
-    """Find lines in plain_right_lines that match the existing lines.
-
-    :param output_lines: Append final annotated lines to this list
-    :param plain_child_lines: The unannotated new lines for the child text
-    :param child_lines: Lines for the child text which have been annotated
-        for the left parent
-    :param start_child: Position in plain_child_lines and child_lines to start the
-        match searching
-    :param end_child: Last position in plain_child_lines and child_lines to search
-        for a match
-    :param right_lines: The annotated lines for the whole text for the right
-        parent
-    :param start_right: Position in right_lines to start the match
-    :param end_right: Last position in right_lines to search for a match
-    :param heads_provider: When parents disagree on the lineage of a line, we
-        need to check if one side supersedes the other
-    :param revision_id: The label to give if a line should be labeled 'tip'
-    """
-    output_extend = output_lines.extend
-    output_append = output_lines.append
-    # We need to see if any of the unannotated lines match
-    plain_right_subset = [l for a,l in right_lines[start_right:end_right]]
-    plain_child_subset = plain_child_lines[start_child:end_child]
-    match_blocks = _get_matching_blocks(plain_right_subset, plain_child_subset)
-
-    last_child_idx = 0
-
-    for right_idx, child_idx, match_len in match_blocks:
-        # All the lines that don't match are just passed along
-        if child_idx > last_child_idx:
-            output_extend(child_lines[start_child + last_child_idx
-                                      :start_child + child_idx])
-        for offset in xrange(match_len):
-            left = child_lines[start_child+child_idx+offset]
-            right = right_lines[start_right+right_idx+offset]
-            if left[0] == right[0]:
-                # The annotations match, just return the left one
-                output_append(left)
-            elif left[0] == revision_id:
-                # The left parent marked this as unmatched, so let the
-                # right parent claim it
-                output_append(right)
-            else:
-                # Left and Right both claim this line
-                if heads_provider is None:
-                    output_append((revision_id, left[1]))
-                else:
-                    heads = heads_provider.heads((left[0], right[0]))
-                    if len(heads) == 1:
-                        output_append((iter(heads).next(), left[1]))
-                    else:
-                        # Both claim different origins
-                        output_append((revision_id, left[1]))
-                        # We know that revision_id is the head for
-                        # left and right, so cache it
-                        heads_provider.cache(
-                            (revision_id, left[0]),
-                            (revision_id,))
-                        heads_provider.cache(
-                            (revision_id, right[0]),
-                            (revision_id,))
-        last_child_idx = child_idx + match_len
-
-
 def _reannotate_annotated(right_parent_lines, new_lines, new_revision_id,
                           annotated_lines, heads_provider):
     """Update the annotations for a node based on another parent.
@@ -320,6 +348,8 @@
     lines_extend = lines.extend
     last_right_idx = 0 # The line just after the last match from the right side
     last_left_idx = 0
+
+    policy = AnnotationPolicy()
     matching_left_and_right = _get_matching_blocks(right_parent_lines,
                                                    annotated_lines)
     for right_idx, left_idx, match_len in matching_left_and_right:
@@ -332,13 +362,13 @@
             lines_extend(annotated_lines[last_left_idx:left_idx])
         else:
             # We need to see if any of the unannotated lines match
-            _find_matching_unannotated_lines(lines,
-                                             new_lines, annotated_lines,
-                                             last_left_idx, left_idx,
-                                             right_parent_lines,
-                                             last_right_idx, right_idx,
-                                             heads_provider,
-                                             new_revision_id)
+            policy.process_unmatched_lines(lines,
+                                           new_lines, annotated_lines,
+                                           last_left_idx, left_idx,
+                                           right_parent_lines,
+                                           last_right_idx, right_idx,
+                                           heads_provider,
+                                           new_revision_id)
         last_right_idx = right_idx + match_len
         last_left_idx = left_idx + match_len
         # If left and right agree on a range, just push that into the output

=== modified file 'bzrlib/tests/test_annotate.py'
--- a/bzrlib/tests/test_annotate.py	2008-02-18 22:19:41 +0000
+++ b/bzrlib/tests/test_annotate.py	2008-06-04 22:37:47 +0000
@@ -184,7 +184,7 @@
                      committer="joe at foo.com",
                      timestamp=1166046000.00, timezone=0)
 
-        tree2 = tree1.bzrdir.clone('tree2').open_workingtree()
+        tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
 
         self.build_tree_contents([('tree1/a', 'first\nsecond\n')])
         tree1.commit('b', rev_id='rev-2',
@@ -235,7 +235,7 @@
         tree1, tree2 = self.create_merged_trees()
         tree1.unlock()
 
-        tree3 = tree2.bzrdir.clone('tree3').open_workingtree()
+        tree3 = tree2.bzrdir.sprout('tree3').open_workingtree()
 
         tree2.commit('noop', rev_id='rev-1_1_2')
         self.assertEqual(0, tree1.merge_from_branch(tree2.branch))
@@ -246,7 +246,7 @@
                      committer='jerry at foo.com',
                      timestamp=1166046003.00, timezone=0)
 
-        tree4 = tree3.bzrdir.clone('tree4').open_workingtree()
+        tree4 = tree3.bzrdir.sprout('tree4').open_workingtree()
 
         tree3.commit('noop', rev_id='rev-1_2_2',
                      committer='jerry at foo.com',
@@ -275,7 +275,7 @@
         self.build_tree_contents([('tree1/file', base_text)])
         tree1.add(['file'], ['file-id'])
         tree1.commit('base', rev_id='rev-base')
-        tree2 = tree1.bzrdir.clone('tree2').open_workingtree()
+        tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
 
         self.build_tree_contents([('tree1/file', a_text),
                                   ('tree2/file', b_text)])



More information about the bazaar-commits mailing list