/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4454.3.1 by John Arbash Meinel
Initial api for Annotator.
1
# Copyright (C) 2009 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
"""Functionality for doing annotations in the 'optimal' way"""
18
19
from bzrlib import (
20
    annotate,
21
    errors,
22
    graph as _mod_graph,
23
    osutils,
4454.3.3 by John Arbash Meinel
Start implementing the reannotation functionality directly.
24
    patiencediff,
4454.3.1 by John Arbash Meinel
Initial api for Annotator.
25
    )
26
27
28
class AnnotatorPolicy(object):
29
    """Variables that define annotations."""
30
31
32
class Annotator(object):
33
    """Class that drives performing annotations."""
34
35
    def __init__(self, vf):
36
        """Create a new Annotator from a VersionedFile."""
37
        self._vf = vf
4454.3.2 by John Arbash Meinel
Start moving bits into helper functions. Add tests for multiple revs.
38
        self._parent_map = {}
4454.3.3 by John Arbash Meinel
Start implementing the reannotation functionality directly.
39
        self._lines_cache = {}
40
        self._annotations_cache = {}
4454.3.2 by John Arbash Meinel
Start moving bits into helper functions. Add tests for multiple revs.
41
        self._heads_provider = None
4454.3.1 by John Arbash Meinel
Initial api for Annotator.
42
4454.3.2 by John Arbash Meinel
Start moving bits into helper functions. Add tests for multiple revs.
43
    def _get_needed_texts(self, key):
4454.3.1 by John Arbash Meinel
Initial api for Annotator.
44
        graph = _mod_graph.Graph(self._vf)
45
        parent_map = dict((k, v) for k, v in graph.iter_ancestry([key])
46
                          if v is not None)
4454.3.2 by John Arbash Meinel
Start moving bits into helper functions. Add tests for multiple revs.
47
        self._parent_map.update(parent_map)
4454.3.1 by John Arbash Meinel
Initial api for Annotator.
48
        keys = parent_map.keys()
4454.3.2 by John Arbash Meinel
Start moving bits into helper functions. Add tests for multiple revs.
49
        return keys
50
51
    def _get_heads_provider(self):
52
        if self._heads_provider is None:
53
            self._heads_provider = _mod_graph.KnownGraph(self._parent_map)
54
        return self._heads_provider
55
4454.3.4 by John Arbash Meinel
New work on how to resolve conflict lines.
56
    def _get_parent_annotations_and_matches(self, lines, parent_key):
4454.3.3 by John Arbash Meinel
Start implementing the reannotation functionality directly.
57
        parent_lines = self._lines_cache[parent_key]
58
        parent_annotations = self._annotations_cache[parent_key]
59
        # PatienceSequenceMatcher should probably be part of Policy
60
        matcher = patiencediff.PatienceSequenceMatcher(None,
61
            parent_lines, lines)
62
        matching_blocks = matcher.get_matching_blocks()
4454.3.4 by John Arbash Meinel
New work on how to resolve conflict lines.
63
        return parent_annotations, matching_blocks
64
4454.3.7 by John Arbash Meinel
Some minor changes
65
    def _reannotate_one_parent(self, annotations, lines, parent_key):
4454.3.4 by John Arbash Meinel
New work on how to resolve conflict lines.
66
        """Reannotate this text relative to its first parent."""
67
        parent_annotations, matching_blocks = self._get_parent_annotations_and_matches(
68
            lines, parent_key)
4454.3.3 by John Arbash Meinel
Start implementing the reannotation functionality directly.
69
70
        for parent_idx, lines_idx, match_len in matching_blocks:
71
            # For all matching regions we copy across the parent annotations
72
            annotations[lines_idx:lines_idx + match_len] = \
73
                parent_annotations[parent_idx:parent_idx + match_len]
74
4454.3.7 by John Arbash Meinel
Some minor changes
75
    def _reannotate_other_parents(self, annotations, lines, this_annotation,
76
                                  parent_key):
4454.3.4 by John Arbash Meinel
New work on how to resolve conflict lines.
77
        """Reannotate this text relative to a second (or more) parent."""
78
        parent_annotations, matching_blocks = self._get_parent_annotations_and_matches(
79
            lines, parent_key)
80
4454.3.6 by John Arbash Meinel
Adding a trivial 'last_entry' cache drops the time from 56s down to 40s
81
        last_ann = None
82
        last_parent = None
83
        last_res = None
4454.3.7 by John Arbash Meinel
Some minor changes
84
        # TODO: consider making all annotations unique and then using 'is'
85
        #       everywhere. Current results claim that isn't any faster,
86
        #       because of the time spent deduping
4454.3.4 by John Arbash Meinel
New work on how to resolve conflict lines.
87
        for parent_idx, lines_idx, match_len in matching_blocks:
88
            # For lines which match this parent, we will now resolve whether
89
            # this parent wins over the current annotation
90
            for idx in xrange(match_len):
91
                ann_idx = lines_idx + idx
92
                ann = annotations[ann_idx]
93
                par_ann = parent_annotations[parent_idx + idx]
94
                if ann == par_ann:
95
                    # Nothing to change
96
                    continue
4454.3.7 by John Arbash Meinel
Some minor changes
97
                if ann == this_annotation:
4454.3.4 by John Arbash Meinel
New work on how to resolve conflict lines.
98
                    # Originally claimed 'this', but it was really in this
99
                    # parent
100
                    annotations[ann_idx] = par_ann
101
                    continue
4454.3.7 by John Arbash Meinel
Some minor changes
102
                # Resolve the fact that both sides have a different value for
103
                # last modified
4454.3.6 by John Arbash Meinel
Adding a trivial 'last_entry' cache drops the time from 56s down to 40s
104
                if ann == last_ann and par_ann == last_parent:
105
                    annotations[ann_idx] = last_res
106
                else:
107
                    new_ann = set(ann)
108
                    new_ann.update(par_ann)
109
                    new_ann = tuple(sorted(new_ann))
110
                    annotations[ann_idx] = new_ann
111
                    last_ann = ann
112
                    last_parent = par_ann
113
                    last_res = new_ann
4454.3.4 by John Arbash Meinel
New work on how to resolve conflict lines.
114
4454.3.2 by John Arbash Meinel
Start moving bits into helper functions. Add tests for multiple revs.
115
    def annotate(self, key):
116
        """Return annotated fulltext for the given key."""
117
        keys = self._get_needed_texts(key)
118
        heads_provider = self._get_heads_provider
4454.3.1 by John Arbash Meinel
Initial api for Annotator.
119
        for record in self._vf.get_record_stream(keys, 'topological', True):
4454.3.3 by John Arbash Meinel
Start implementing the reannotation functionality directly.
120
            this_key = record.key
4454.3.2 by John Arbash Meinel
Start moving bits into helper functions. Add tests for multiple revs.
121
            lines = osutils.chunks_to_lines(record.get_bytes_as('chunked'))
4454.3.7 by John Arbash Meinel
Some minor changes
122
            this_annotation = (this_key,)
123
            annotations = [this_annotation]*len(lines)
4454.3.3 by John Arbash Meinel
Start implementing the reannotation functionality directly.
124
            self._lines_cache[this_key] = lines
125
            self._annotations_cache[this_key] = annotations
126
127
            parents = self._parent_map[this_key]
128
            if not parents:
129
                continue
4454.3.7 by John Arbash Meinel
Some minor changes
130
            self._reannotate_one_parent(annotations, lines, parents[0])
4454.3.4 by John Arbash Meinel
New work on how to resolve conflict lines.
131
            for parent in parents[1:]:
4454.3.7 by John Arbash Meinel
Some minor changes
132
                self._reannotate_other_parents(annotations, lines,
133
                                               this_annotation, parent)
4454.3.1 by John Arbash Meinel
Initial api for Annotator.
134
        try:
4454.3.3 by John Arbash Meinel
Start implementing the reannotation functionality directly.
135
            annotations = self._annotations_cache[key]
136
        except KeyError:
4454.3.1 by John Arbash Meinel
Initial api for Annotator.
137
            raise errors.RevisionNotPresent(key, self._vf)
4454.3.3 by John Arbash Meinel
Start implementing the reannotation functionality directly.
138
        return annotations, self._lines_cache[key]