~bzr-pqm/bzr/bzr.dev

2245.3.2 by John Arbash Meinel
Cleanup according to Wouter's suggestions.
1
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
2
#
1385 by Martin Pool
- simple weave-based annotate code (not complete)
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.
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
7
#
1385 by Martin Pool
- simple weave-based annotate code (not complete)
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.
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
12
#
1385 by Martin Pool
- simple weave-based annotate code (not complete)
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""File annotate based on weave storage"""
18
1185.16.8 by Martin Pool
doc
19
# TODO: Choice of more or less verbose formats:
20
# 
21
# interposed: show more details between blocks of modified lines
22
23
# TODO: Show which revision caused a line to merge into the parent
24
1185.16.57 by Martin Pool
[merge] from aaron
25
# TODO: perhaps abbreviate timescales depending on how recent they are
26
# e.g. "3:12 Tue", "13 Oct", "Oct 2005", etc.  
27
1385 by Martin Pool
- simple weave-based annotate code (not complete)
28
import sys
1185.16.1 by Martin Pool
- update annotate for new branch api
29
import time
1385 by Martin Pool
- simple weave-based annotate code (not complete)
30
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
31
from bzrlib import (
32
    errors,
2593.1.3 by Adeodato Simó
Cope with to_file.encoding being None or not present.
33
    osutils,
1551.9.19 by Aaron Bentley
Merge from bzr.dev
34
    patiencediff,
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
35
    tsort,
36
    )
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
37
from bzrlib.config import extract_email_address
38
39
40
def annotate_file(branch, rev_id, file_id, verbose=False, full=False,
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
41
                  to_file=None, show_ids=False):
3010.1.1 by Robert Collins
Lock the tree's used to test annotate_file, and add a docstring for annotate_file explaining its needs.
42
    """Annotate file_id at revision rev_id in branch.
43
44
    The branch should already be read_locked() when annotate_file is called.
45
46
    :param branch: The branch to look for revision numbers and history from.
47
    :param rev_id: The revision id to annotate.
48
    :param file_id: The file_id to annotate.
49
    :param verbose: Show all details rather than truncating to ensure
50
        reasonable text width.
51
    :param full: XXXX Not sure what this does.
52
    :param to_file: The file to output the annotation to; if None stdout is
53
        used.
54
    :param show_ids: Show revision ids in the annotation output.
55
    """
1385 by Martin Pool
- simple weave-based annotate code (not complete)
56
    if to_file is None:
57
        to_file = sys.stdout
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
58
2831.3.1 by Ian Clatworthy
code cleanups for annotate.py
59
    # Handle the show_ids case
2182.3.9 by John Arbash Meinel
Faster annotate --show-ids. No need to pull the history or the revision info
60
    last_rev_id = None
61
    if show_ids:
2831.3.1 by Ian Clatworthy
code cleanups for annotate.py
62
        annotations = _annotations(branch.repository, file_id, rev_id)
2182.3.9 by John Arbash Meinel
Faster annotate --show-ids. No need to pull the history or the revision info
63
        max_origin_len = max(len(origin) for origin, text in annotations)
64
        for origin, text in annotations:
65
            if full or last_rev_id != origin:
66
                this = origin
67
            else:
68
                this = ''
69
            to_file.write('%*s | %s' % (max_origin_len, this, text))
70
            last_rev_id = origin
71
        return
72
2831.3.1 by Ian Clatworthy
code cleanups for annotate.py
73
    # Calculate the lengths of the various columns
1185.33.39 by Martin Pool
[patch] annotate --long (robey pointer)
74
    annotation = list(_annotate_file(branch, rev_id, file_id))
2027.3.1 by John Arbash Meinel
'bzr annotate' shouldn't fail on an empty file: fix bug #56814
75
    if len(annotation) == 0:
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
76
        max_origin_len = max_revno_len = max_revid_len = 0
2027.3.1 by John Arbash Meinel
'bzr annotate' shouldn't fail on an empty file: fix bug #56814
77
    else:
2182.3.8 by John Arbash Meinel
Some cleanup to make annotate.py < 79 chars wide.
78
        max_origin_len = max(len(x[1]) for x in annotation)
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
79
        max_revno_len = max(len(x[0]) for x in annotation)
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
80
        max_revid_len = max(len(x[3]) for x in annotation)
2182.3.2 by John Arbash Meinel
Use shortened revnos unless --long is supplied
81
    if not verbose:
2182.3.7 by John Arbash Meinel
Cleanup and add blackbox tests for annotate.
82
        max_revno_len = min(max_revno_len, 12)
83
    max_revno_len = max(max_revno_len, 3)
2182.3.2 by John Arbash Meinel
Use shortened revnos unless --long is supplied
84
2831.3.1 by Ian Clatworthy
code cleanups for annotate.py
85
    # Output the annotations
86
    prevanno = ''
87
    encoding = getattr(to_file, 'encoding', None) or \
88
            osutils.get_terminal_encoding()
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
89
    for (revno_str, author, date_str, line_rev_id, text) in annotation:
2182.3.9 by John Arbash Meinel
Faster annotate --show-ids. No need to pull the history or the revision info
90
        if verbose:
91
            anno = '%-*s %-*s %8s ' % (max_revno_len, revno_str,
92
                                       max_origin_len, author, date_str)
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
93
        else:
2182.3.9 by John Arbash Meinel
Faster annotate --show-ids. No need to pull the history or the revision info
94
            if len(revno_str) > max_revno_len:
95
                revno_str = revno_str[:max_revno_len-1] + '>'
96
            anno = "%-*s %-7s " % (max_revno_len, revno_str, author[:7])
2831.3.1 by Ian Clatworthy
code cleanups for annotate.py
97
        if anno.lstrip() == "" and full:
98
            anno = prevanno
2593.1.1 by Adeodato Simó
Improve annotate to prevent unicode exceptions in certain situations.
99
        try:
100
            to_file.write(anno)
101
        except UnicodeEncodeError:
2593.1.4 by Adeodato Simó
Add comment from John to the try/except block.
102
            # cmd_annotate should be passing in an 'exact' object, which means
103
            # we have a direct handle to sys.stdout or equivalent. It may not
104
            # be able to handle the exact Unicode characters, but 'annotate' is
105
            # a user function (non-scripting), so shouldn't die because of
106
            # unrepresentable annotation characters. So encode using 'replace',
107
            # and write them again.
2593.1.3 by Adeodato Simó
Cope with to_file.encoding being None or not present.
108
            to_file.write(anno.encode(encoding, 'replace'))
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
109
        to_file.write('| %s\n' % (text,))
2831.3.1 by Ian Clatworthy
code cleanups for annotate.py
110
        prevanno = anno
111
112
113
def _annotations(repo, file_id, rev_id):
3350.6.10 by Martin Pool
VersionedFiles review cleanups
114
    """Return the list of (origin_revision_id, line_text) for a revision of a file in a repository."""
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
115
    annotations = repo.texts.annotate((file_id, rev_id))
3350.6.10 by Martin Pool
VersionedFiles review cleanups
116
    # 
117
    return [(key[-1], line) for (key, line) in annotations]
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
118
2182.3.10 by John Arbash Meinel
minor cleanup.
119
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
120
def _annotate_file(branch, rev_id, file_id):
2182.3.10 by John Arbash Meinel
minor cleanup.
121
    """Yield the origins for each line of a file.
122
2671.5.3 by Lukáš Lalinsky
Use the author name in annotate.
123
    This includes detailed information, such as the author name, and
2182.3.10 by John Arbash Meinel
minor cleanup.
124
    date string for the commit, rather than just the revision id.
125
    """
2418.5.8 by John Arbash Meinel
Update annotate.py to use the new helper function.
126
    revision_id_to_revno = branch.get_revision_id_to_revno_map()
2831.3.1 by Ian Clatworthy
code cleanups for annotate.py
127
    annotations = _annotations(branch.repository, file_id, rev_id)
1385 by Martin Pool
- simple weave-based annotate code (not complete)
128
    last_origin = None
1551.9.6 by Aaron Bentley
Optimize annotate by retrieving all revisions at once
129
    revision_ids = set(o for o, t in annotations)
130
    revision_ids = [o for o in revision_ids if 
131
                    branch.repository.has_revision(o)]
132
    revisions = dict((r.revision_id, r) for r in 
133
                     branch.repository.get_revisions(revision_ids))
134
    for origin, text in annotations:
1385 by Martin Pool
- simple weave-based annotate code (not complete)
135
        text = text.rstrip('\r\n')
136
        if origin == last_origin:
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
137
            (revno_str, author, date_str) = ('','','')
1385 by Martin Pool
- simple weave-based annotate code (not complete)
138
        else:
139
            last_origin = origin
1551.9.6 by Aaron Bentley
Optimize annotate by retrieving all revisions at once
140
            if origin not in revisions:
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
141
                (revno_str, author, date_str) = ('?','?','?')
1185.16.1 by Martin Pool
- update annotate for new branch api
142
            else:
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
143
                revno_str = '.'.join(str(i) for i in
144
                                            revision_id_to_revno[origin])
1551.9.6 by Aaron Bentley
Optimize annotate by retrieving all revisions at once
145
            rev = revisions[origin]
1185.16.32 by Martin Pool
- add a basic annotate built-in command
146
            tz = rev.timezone or 0
2182.3.8 by John Arbash Meinel
Some cleanup to make annotate.py < 79 chars wide.
147
            date_str = time.strftime('%Y%m%d',
1185.16.32 by Martin Pool
- add a basic annotate built-in command
148
                                     time.gmtime(rev.timestamp + tz))
149
            # a lazy way to get something like the email address
150
            # TODO: Get real email address
2671.5.7 by Lukáš Lalinsky
Rename get_author to get_apparent_author, revert the long log back to displaying the committer.
151
            author = rev.get_apparent_author()
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
152
            try:
153
                author = extract_email_address(author)
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
154
            except errors.NoEmailInUsername:
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
155
                pass        # use the whole name
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
156
        yield (revno_str, author, date_str, origin, text)
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
157
158
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
159
def reannotate(parents_lines, new_lines, new_revision_id,
3224.1.10 by John Arbash Meinel
Introduce the heads_provider for reannotate.
160
               _left_matching_blocks=None,
161
               heads_provider=None):
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
162
    """Create a new annotated version from new lines and parent annotations.
163
    
1551.9.17 by Aaron Bentley
Annotate for working trees across all parents
164
    :param parents_lines: List of annotated lines for all parents
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
165
    :param new_lines: The un-annotated new lines
166
    :param new_revision_id: The revision-id to associate with new lines
167
        (will often be CURRENT_REVISION)
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
168
    :param left_matching_blocks: a hint about which areas are common
169
        between the text and its left-hand-parent.  The format is
3224.1.3 by John Arbash Meinel
Clarify the format of 'matching_blocks'
170
        the SequenceMatcher.get_matching_blocks format
171
        (start_left, start_right, length_of_match).
3224.1.10 by John Arbash Meinel
Introduce the heads_provider for reannotate.
172
    :param heads_provider: An object which provids a .heads() call to resolve
173
        if any revision ids are children of others.
174
        If None, then any ancestry disputes will be resolved with
175
        new_revision_id
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
176
    """
2770.1.1 by Aaron Bentley
Initial implmentation of plain knit annotation
177
    if len(parents_lines) == 0:
3180.2.1 by John Arbash Meinel
Change reannotate to special case the 2-parent case.
178
        lines = [(new_revision_id, line) for line in new_lines]
2770.1.1 by Aaron Bentley
Initial implmentation of plain knit annotation
179
    elif len(parents_lines) == 1:
3180.2.1 by John Arbash Meinel
Change reannotate to special case the 2-parent case.
180
        lines = _reannotate(parents_lines[0], new_lines, new_revision_id,
181
                            _left_matching_blocks)
182
    elif len(parents_lines) == 2:
183
        left = _reannotate(parents_lines[0], new_lines, new_revision_id,
184
                           _left_matching_blocks)
3224.1.9 by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent.
185
        lines = _reannotate_annotated(parents_lines[1], new_lines,
3224.1.10 by John Arbash Meinel
Introduce the heads_provider for reannotate.
186
                                      new_revision_id, left,
187
                                      heads_provider)
1551.9.17 by Aaron Bentley
Annotate for working trees across all parents
188
    else:
3180.2.1 by John Arbash Meinel
Change reannotate to special case the 2-parent case.
189
        reannotations = [_reannotate(parents_lines[0], new_lines,
190
                                     new_revision_id, _left_matching_blocks)]
191
        reannotations.extend(_reannotate(p, new_lines, new_revision_id)
192
                             for p in parents_lines[1:])
193
        lines = []
1551.9.17 by Aaron Bentley
Annotate for working trees across all parents
194
        for annos in zip(*reannotations):
195
            origins = set(a for a, l in annos)
196
            if len(origins) == 1:
3180.2.1 by John Arbash Meinel
Change reannotate to special case the 2-parent case.
197
                # All the parents agree, so just return the first one
198
                lines.append(annos[0])
1551.9.17 by Aaron Bentley
Annotate for working trees across all parents
199
            else:
3180.2.1 by John Arbash Meinel
Change reannotate to special case the 2-parent case.
200
                line = annos[0][1]
201
                if len(origins) == 2 and new_revision_id in origins:
202
                    origins.remove(new_revision_id)
203
                if len(origins) == 1:
3180.2.2 by John Arbash Meinel
Fix typo, (thanks Ian)
204
                    lines.append((origins.pop(), line))
3180.2.1 by John Arbash Meinel
Change reannotate to special case the 2-parent case.
205
                else:
206
                    lines.append((new_revision_id, line))
207
    return lines
1551.9.18 by Aaron Bentley
Updates from review comments
208
1551.9.17 by Aaron Bentley
Annotate for working trees across all parents
209
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
210
def _reannotate(parent_lines, new_lines, new_revision_id,
211
                matching_blocks=None):
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
212
    new_cur = 0
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
213
    if matching_blocks is None:
2831.3.1 by Ian Clatworthy
code cleanups for annotate.py
214
        plain_parent_lines = [l for r, l in parent_lines]
215
        matcher = patiencediff.PatienceSequenceMatcher(None,
216
            plain_parent_lines, new_lines)
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
217
        matching_blocks = matcher.get_matching_blocks()
3180.2.1 by John Arbash Meinel
Change reannotate to special case the 2-parent case.
218
    lines = []
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
219
    for i, j, n in matching_blocks:
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
220
        for line in new_lines[new_cur:j]:
3180.2.1 by John Arbash Meinel
Change reannotate to special case the 2-parent case.
221
            lines.append((new_revision_id, line))
222
        lines.extend(parent_lines[i:i+n])
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
223
        new_cur = j + n
3180.2.1 by John Arbash Meinel
Change reannotate to special case the 2-parent case.
224
    return lines
3224.1.9 by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent.
225
226
3224.1.23 by John Arbash Meinel
Clean up the new function variables.
227
def _get_matching_blocks(old, new):
228
    matcher = patiencediff.PatienceSequenceMatcher(None,
229
        old, new)
230
    return matcher.get_matching_blocks()
231
232
233
def _find_matching_unannotated_lines(output_lines, plain_child_lines,
234
                                     child_lines, start_child, end_child,
235
                                     right_lines, start_right, end_right,
236
                                     heads_provider, revision_id):
237
    """Find lines in plain_right_lines that match the existing lines.
238
239
    :param output_lines: Append final annotated lines to this list
240
    :param plain_child_lines: The unannotated new lines for the child text
241
    :param child_lines: Lines for the child text which have been annotated
242
        for the left parent
243
    :param start_child: Position in plain_child_lines and child_lines to start the
244
        match searching
245
    :param end_child: Last position in plain_child_lines and child_lines to search
246
        for a match
247
    :param right_lines: The annotated lines for the whole text for the right
248
        parent
249
    :param start_right: Position in right_lines to start the match
250
    :param end_right: Last position in right_lines to search for a match
251
    :param heads_provider: When parents disagree on the lineage of a line, we
252
        need to check if one side supersedes the other
253
    :param revision_id: The label to give if a line should be labeled 'tip'
254
    """
255
    output_extend = output_lines.extend
256
    output_append = output_lines.append
257
    # We need to see if any of the unannotated lines match
258
    plain_right_subset = [l for a,l in right_lines[start_right:end_right]]
259
    plain_child_subset = plain_child_lines[start_child:end_child]
260
    match_blocks = _get_matching_blocks(plain_right_subset, plain_child_subset)
261
262
    last_child_idx = 0
263
264
    for right_idx, child_idx, match_len in match_blocks:
265
        # All the lines that don't match are just passed along
266
        if child_idx > last_child_idx:
267
            output_extend(child_lines[start_child + last_child_idx
268
                                      :start_child + child_idx])
269
        for offset in xrange(match_len):
270
            left = child_lines[start_child+child_idx+offset]
271
            right = right_lines[start_right+right_idx+offset]
272
            if left[0] == right[0]:
273
                # The annotations match, just return the left one
274
                output_append(left)
275
            elif left[0] == revision_id:
276
                # The left parent marked this as unmatched, so let the
277
                # right parent claim it
278
                output_append(right)
279
            else:
280
                # Left and Right both claim this line
281
                if heads_provider is None:
282
                    output_append((revision_id, left[1]))
283
                else:
284
                    heads = heads_provider.heads((left[0], right[0]))
285
                    if len(heads) == 1:
286
                        output_append((iter(heads).next(), left[1]))
287
                    else:
288
                        # Both claim different origins
289
                        output_append((revision_id, left[1]))
290
                        # We know that revision_id is the head for
291
                        # left and right, so cache it
292
                        heads_provider.cache(
293
                            (revision_id, left[0]),
294
                            (revision_id,))
295
                        heads_provider.cache(
296
                            (revision_id, right[0]),
297
                            (revision_id,))
298
        last_child_idx = child_idx + match_len
299
300
3224.1.9 by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent.
301
def _reannotate_annotated(right_parent_lines, new_lines, new_revision_id,
3224.1.10 by John Arbash Meinel
Introduce the heads_provider for reannotate.
302
                          annotated_lines, heads_provider):
3224.1.9 by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent.
303
    """Update the annotations for a node based on another parent.
304
305
    :param right_parent_lines: A list of annotated lines for the right-hand
306
        parent.
307
    :param new_lines: The unannotated new lines.
308
    :param new_revision_id: The revision_id to attribute to lines which are not
309
        present in either parent.
310
    :param annotated_lines: A list of annotated lines. This should be the
311
        annotation of new_lines based on parents seen so far.
3224.1.10 by John Arbash Meinel
Introduce the heads_provider for reannotate.
312
    :param heads_provider: When parents disagree on the lineage of a line, we
313
        need to check if one side supersedes the other.
3224.1.9 by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent.
314
    """
3376.2.3 by Martin Pool
Updated info about assertions
315
    if len(new_lines) != len(annotated_lines):
316
        raise AssertionError("mismatched new_lines and annotated_lines")
3224.1.9 by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent.
317
    # First compare the newly annotated lines with the right annotated lines.
318
    # Lines which were not changed in left or right should match. This tends to
319
    # be the bulk of the lines, and they will need no further processing.
320
    lines = []
321
    lines_extend = lines.extend
3224.1.23 by John Arbash Meinel
Clean up the new function variables.
322
    last_right_idx = 0 # The line just after the last match from the right side
323
    last_left_idx = 0
324
    matching_left_and_right = _get_matching_blocks(right_parent_lines,
325
                                                   annotated_lines)
326
    for right_idx, left_idx, match_len in matching_left_and_right:
327
        # annotated lines from last_left_idx to left_idx did not match the lines from
328
        # last_right_idx
329
        # to right_idx, the raw lines should be compared to determine what annotations
3224.1.9 by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent.
330
        # need to be updated
3224.1.23 by John Arbash Meinel
Clean up the new function variables.
331
        if last_right_idx == right_idx or last_left_idx == left_idx:
3224.1.9 by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent.
332
            # One of the sides is empty, so this is a pure insertion
3224.1.23 by John Arbash Meinel
Clean up the new function variables.
333
            lines_extend(annotated_lines[last_left_idx:left_idx])
3224.1.9 by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent.
334
        else:
335
            # We need to see if any of the unannotated lines match
3224.1.23 by John Arbash Meinel
Clean up the new function variables.
336
            _find_matching_unannotated_lines(lines,
337
                                             new_lines, annotated_lines,
338
                                             last_left_idx, left_idx,
339
                                             right_parent_lines,
340
                                             last_right_idx, right_idx,
341
                                             heads_provider,
342
                                             new_revision_id)
343
        last_right_idx = right_idx + match_len
344
        last_left_idx = left_idx + match_len
3224.1.9 by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent.
345
        # If left and right agree on a range, just push that into the output
3224.1.23 by John Arbash Meinel
Clean up the new function variables.
346
        lines_extend(annotated_lines[left_idx:left_idx + match_len])
3224.1.9 by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent.
347
    return lines