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): |
|
114 |
"""Return the list of (origin,text) for a revision of a file in a repository."""
|
|
115 |
w = repo.weave_store.get_weave(file_id, repo.get_transaction()) |
|
3316.2.13
by Robert Collins
* ``VersionedFile.annotate_iter`` is deprecated. While in principal this |
116 |
return w.annotate(rev_id) |
1185.16.53
by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests |
117 |
|
2182.3.10
by John Arbash Meinel
minor cleanup. |
118 |
|
2245.3.1
by John Arbash Meinel
bzr annotate should use Branch's dotted revnos. |
119 |
def _annotate_file(branch, rev_id, file_id): |
2182.3.10
by John Arbash Meinel
minor cleanup. |
120 |
"""Yield the origins for each line of a file.
|
121 |
||
2671.5.3
by Lukáš Lalinsky
Use the author name in annotate. |
122 |
This includes detailed information, such as the author name, and
|
2182.3.10
by John Arbash Meinel
minor cleanup. |
123 |
date string for the commit, rather than just the revision id.
|
124 |
"""
|
|
2418.5.8
by John Arbash Meinel
Update annotate.py to use the new helper function. |
125 |
revision_id_to_revno = branch.get_revision_id_to_revno_map() |
2831.3.1
by Ian Clatworthy
code cleanups for annotate.py |
126 |
annotations = _annotations(branch.repository, file_id, rev_id) |
1385
by Martin Pool
- simple weave-based annotate code (not complete) |
127 |
last_origin = None |
1551.9.6
by Aaron Bentley
Optimize annotate by retrieving all revisions at once |
128 |
revision_ids = set(o for o, t in annotations) |
129 |
revision_ids = [o for o in revision_ids if |
|
130 |
branch.repository.has_revision(o)] |
|
131 |
revisions = dict((r.revision_id, r) for r in |
|
132 |
branch.repository.get_revisions(revision_ids)) |
|
133 |
for origin, text in annotations: |
|
1385
by Martin Pool
- simple weave-based annotate code (not complete) |
134 |
text = text.rstrip('\r\n') |
135 |
if origin == last_origin: |
|
1185.16.53
by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests |
136 |
(revno_str, author, date_str) = ('','','') |
1385
by Martin Pool
- simple weave-based annotate code (not complete) |
137 |
else: |
138 |
last_origin = origin |
|
1551.9.6
by Aaron Bentley
Optimize annotate by retrieving all revisions at once |
139 |
if origin not in revisions: |
1185.16.53
by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests |
140 |
(revno_str, author, date_str) = ('?','?','?') |
1185.16.1
by Martin Pool
- update annotate for new branch api |
141 |
else: |
2182.3.1
by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos. |
142 |
revno_str = '.'.join(str(i) for i in |
143 |
revision_id_to_revno[origin]) |
|
1551.9.6
by Aaron Bentley
Optimize annotate by retrieving all revisions at once |
144 |
rev = revisions[origin] |
1185.16.32
by Martin Pool
- add a basic annotate built-in command |
145 |
tz = rev.timezone or 0 |
2182.3.8
by John Arbash Meinel
Some cleanup to make annotate.py < 79 chars wide. |
146 |
date_str = time.strftime('%Y%m%d', |
1185.16.32
by Martin Pool
- add a basic annotate built-in command |
147 |
time.gmtime(rev.timestamp + tz)) |
148 |
# a lazy way to get something like the email address
|
|
149 |
# 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. |
150 |
author = rev.get_apparent_author() |
1185.16.53
by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests |
151 |
try: |
152 |
author = extract_email_address(author) |
|
2182.3.1
by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos. |
153 |
except errors.NoEmailInUsername: |
1185.16.53
by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests |
154 |
pass # use the whole name |
1563.2.1
by Robert Collins
Merge in a variation of the versionedfile api from versioned-file. |
155 |
yield (revno_str, author, date_str, origin, text) |
1551.9.16
by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree |
156 |
|
157 |
||
2770.1.5
by Aaron Bentley
Clean up docs, test matching blocks for reannotate |
158 |
def reannotate(parents_lines, new_lines, new_revision_id, |
3224.1.10
by John Arbash Meinel
Introduce the heads_provider for reannotate. |
159 |
_left_matching_blocks=None, |
160 |
heads_provider=None): |
|
1551.9.16
by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree |
161 |
"""Create a new annotated version from new lines and parent annotations.
|
162 |
|
|
1551.9.17
by Aaron Bentley
Annotate for working trees across all parents |
163 |
:param parents_lines: List of annotated lines for all parents
|
1551.9.16
by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree |
164 |
:param new_lines: The un-annotated new lines
|
165 |
:param new_revision_id: The revision-id to associate with new lines
|
|
166 |
(will often be CURRENT_REVISION)
|
|
2770.1.5
by Aaron Bentley
Clean up docs, test matching blocks for reannotate |
167 |
:param left_matching_blocks: a hint about which areas are common
|
168 |
between the text and its left-hand-parent. The format is
|
|
3224.1.3
by John Arbash Meinel
Clarify the format of 'matching_blocks' |
169 |
the SequenceMatcher.get_matching_blocks format
|
170 |
(start_left, start_right, length_of_match).
|
|
3224.1.10
by John Arbash Meinel
Introduce the heads_provider for reannotate. |
171 |
:param heads_provider: An object which provids a .heads() call to resolve
|
172 |
if any revision ids are children of others.
|
|
173 |
If None, then any ancestry disputes will be resolved with
|
|
174 |
new_revision_id
|
|
1551.9.16
by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree |
175 |
"""
|
2770.1.1
by Aaron Bentley
Initial implmentation of plain knit annotation |
176 |
if len(parents_lines) == 0: |
3180.2.1
by John Arbash Meinel
Change reannotate to special case the 2-parent case. |
177 |
lines = [(new_revision_id, line) for line in new_lines] |
2770.1.1
by Aaron Bentley
Initial implmentation of plain knit annotation |
178 |
elif len(parents_lines) == 1: |
3180.2.1
by John Arbash Meinel
Change reannotate to special case the 2-parent case. |
179 |
lines = _reannotate(parents_lines[0], new_lines, new_revision_id, |
180 |
_left_matching_blocks) |
|
181 |
elif len(parents_lines) == 2: |
|
182 |
left = _reannotate(parents_lines[0], new_lines, new_revision_id, |
|
183 |
_left_matching_blocks) |
|
3224.1.9
by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent. |
184 |
lines = _reannotate_annotated(parents_lines[1], new_lines, |
3224.1.10
by John Arbash Meinel
Introduce the heads_provider for reannotate. |
185 |
new_revision_id, left, |
186 |
heads_provider) |
|
1551.9.17
by Aaron Bentley
Annotate for working trees across all parents |
187 |
else: |
3180.2.1
by John Arbash Meinel
Change reannotate to special case the 2-parent case. |
188 |
reannotations = [_reannotate(parents_lines[0], new_lines, |
189 |
new_revision_id, _left_matching_blocks)] |
|
190 |
reannotations.extend(_reannotate(p, new_lines, new_revision_id) |
|
191 |
for p in parents_lines[1:]) |
|
192 |
lines = [] |
|
1551.9.17
by Aaron Bentley
Annotate for working trees across all parents |
193 |
for annos in zip(*reannotations): |
194 |
origins = set(a for a, l in annos) |
|
195 |
if len(origins) == 1: |
|
3180.2.1
by John Arbash Meinel
Change reannotate to special case the 2-parent case. |
196 |
# All the parents agree, so just return the first one
|
197 |
lines.append(annos[0]) |
|
1551.9.17
by Aaron Bentley
Annotate for working trees across all parents |
198 |
else: |
3180.2.1
by John Arbash Meinel
Change reannotate to special case the 2-parent case. |
199 |
line = annos[0][1] |
200 |
if len(origins) == 2 and new_revision_id in origins: |
|
201 |
origins.remove(new_revision_id) |
|
202 |
if len(origins) == 1: |
|
3180.2.2
by John Arbash Meinel
Fix typo, (thanks Ian) |
203 |
lines.append((origins.pop(), line)) |
3180.2.1
by John Arbash Meinel
Change reannotate to special case the 2-parent case. |
204 |
else: |
205 |
lines.append((new_revision_id, line)) |
|
206 |
return lines |
|
1551.9.18
by Aaron Bentley
Updates from review comments |
207 |
|
1551.9.17
by Aaron Bentley
Annotate for working trees across all parents |
208 |
|
2770.1.5
by Aaron Bentley
Clean up docs, test matching blocks for reannotate |
209 |
def _reannotate(parent_lines, new_lines, new_revision_id, |
210 |
matching_blocks=None): |
|
1551.9.16
by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree |
211 |
new_cur = 0 |
2770.1.5
by Aaron Bentley
Clean up docs, test matching blocks for reannotate |
212 |
if matching_blocks is None: |
2831.3.1
by Ian Clatworthy
code cleanups for annotate.py |
213 |
plain_parent_lines = [l for r, l in parent_lines] |
214 |
matcher = patiencediff.PatienceSequenceMatcher(None, |
|
215 |
plain_parent_lines, new_lines) |
|
2770.1.5
by Aaron Bentley
Clean up docs, test matching blocks for reannotate |
216 |
matching_blocks = matcher.get_matching_blocks() |
3180.2.1
by John Arbash Meinel
Change reannotate to special case the 2-parent case. |
217 |
lines = [] |
2770.1.5
by Aaron Bentley
Clean up docs, test matching blocks for reannotate |
218 |
for i, j, n in matching_blocks: |
1551.9.16
by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree |
219 |
for line in new_lines[new_cur:j]: |
3180.2.1
by John Arbash Meinel
Change reannotate to special case the 2-parent case. |
220 |
lines.append((new_revision_id, line)) |
221 |
lines.extend(parent_lines[i:i+n]) |
|
1551.9.16
by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree |
222 |
new_cur = j + n |
3180.2.1
by John Arbash Meinel
Change reannotate to special case the 2-parent case. |
223 |
return lines |
3224.1.9
by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent. |
224 |
|
225 |
||
3224.1.23
by John Arbash Meinel
Clean up the new function variables. |
226 |
def _get_matching_blocks(old, new): |
227 |
matcher = patiencediff.PatienceSequenceMatcher(None, |
|
228 |
old, new) |
|
229 |
return matcher.get_matching_blocks() |
|
230 |
||
231 |
||
232 |
def _find_matching_unannotated_lines(output_lines, plain_child_lines, |
|
233 |
child_lines, start_child, end_child, |
|
234 |
right_lines, start_right, end_right, |
|
235 |
heads_provider, revision_id): |
|
236 |
"""Find lines in plain_right_lines that match the existing lines.
|
|
237 |
||
238 |
:param output_lines: Append final annotated lines to this list
|
|
239 |
:param plain_child_lines: The unannotated new lines for the child text
|
|
240 |
:param child_lines: Lines for the child text which have been annotated
|
|
241 |
for the left parent
|
|
242 |
:param start_child: Position in plain_child_lines and child_lines to start the
|
|
243 |
match searching
|
|
244 |
:param end_child: Last position in plain_child_lines and child_lines to search
|
|
245 |
for a match
|
|
246 |
:param right_lines: The annotated lines for the whole text for the right
|
|
247 |
parent
|
|
248 |
:param start_right: Position in right_lines to start the match
|
|
249 |
:param end_right: Last position in right_lines to search for a match
|
|
250 |
:param heads_provider: When parents disagree on the lineage of a line, we
|
|
251 |
need to check if one side supersedes the other
|
|
252 |
:param revision_id: The label to give if a line should be labeled 'tip'
|
|
253 |
"""
|
|
254 |
output_extend = output_lines.extend |
|
255 |
output_append = output_lines.append |
|
256 |
# We need to see if any of the unannotated lines match
|
|
257 |
plain_right_subset = [l for a,l in right_lines[start_right:end_right]] |
|
258 |
plain_child_subset = plain_child_lines[start_child:end_child] |
|
259 |
match_blocks = _get_matching_blocks(plain_right_subset, plain_child_subset) |
|
260 |
||
261 |
last_child_idx = 0 |
|
262 |
||
263 |
for right_idx, child_idx, match_len in match_blocks: |
|
264 |
# All the lines that don't match are just passed along
|
|
265 |
if child_idx > last_child_idx: |
|
266 |
output_extend(child_lines[start_child + last_child_idx |
|
267 |
:start_child + child_idx]) |
|
268 |
for offset in xrange(match_len): |
|
269 |
left = child_lines[start_child+child_idx+offset] |
|
270 |
right = right_lines[start_right+right_idx+offset] |
|
271 |
if left[0] == right[0]: |
|
272 |
# The annotations match, just return the left one
|
|
273 |
output_append(left) |
|
274 |
elif left[0] == revision_id: |
|
275 |
# The left parent marked this as unmatched, so let the
|
|
276 |
# right parent claim it
|
|
277 |
output_append(right) |
|
278 |
else: |
|
279 |
# Left and Right both claim this line
|
|
280 |
if heads_provider is None: |
|
281 |
output_append((revision_id, left[1])) |
|
282 |
else: |
|
283 |
heads = heads_provider.heads((left[0], right[0])) |
|
284 |
if len(heads) == 1: |
|
285 |
output_append((iter(heads).next(), left[1])) |
|
286 |
else: |
|
287 |
# Both claim different origins
|
|
288 |
output_append((revision_id, left[1])) |
|
289 |
# We know that revision_id is the head for
|
|
290 |
# left and right, so cache it
|
|
291 |
heads_provider.cache( |
|
292 |
(revision_id, left[0]), |
|
293 |
(revision_id,)) |
|
294 |
heads_provider.cache( |
|
295 |
(revision_id, right[0]), |
|
296 |
(revision_id,)) |
|
297 |
last_child_idx = child_idx + match_len |
|
298 |
||
299 |
||
3224.1.9
by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent. |
300 |
def _reannotate_annotated(right_parent_lines, new_lines, new_revision_id, |
3224.1.10
by John Arbash Meinel
Introduce the heads_provider for reannotate. |
301 |
annotated_lines, heads_provider): |
3224.1.9
by John Arbash Meinel
Introduce _reannotate_annotated, which updates an annotated text based on a new parent. |
302 |
"""Update the annotations for a node based on another parent.
|
303 |
||
304 |
:param right_parent_lines: A list of annotated lines for the right-hand
|
|
305 |
parent.
|
|
306 |
:param new_lines: The unannotated new lines.
|
|
307 |
:param new_revision_id: The revision_id to attribute to lines which are not
|
|
308 |
present in either parent.
|
|
309 |
:param annotated_lines: A list of annotated lines. This should be the
|
|
310 |
annotation of new_lines based on parents seen so far.
|
|
3224.1.10
by John Arbash Meinel
Introduce the heads_provider for reannotate. |
311 |
:param heads_provider: When parents disagree on the lineage of a line, we
|
312 |
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. |
313 |
"""
|
314 |
assert len(new_lines) == len(annotated_lines) |
|
315 |
# First compare the newly annotated lines with the right annotated lines.
|
|
316 |
# Lines which were not changed in left or right should match. This tends to
|
|
317 |
# be the bulk of the lines, and they will need no further processing.
|
|
318 |
lines = [] |
|
319 |
lines_extend = lines.extend |
|
3224.1.23
by John Arbash Meinel
Clean up the new function variables. |
320 |
last_right_idx = 0 # The line just after the last match from the right side |
321 |
last_left_idx = 0 |
|
322 |
matching_left_and_right = _get_matching_blocks(right_parent_lines, |
|
323 |
annotated_lines) |
|
324 |
for right_idx, left_idx, match_len in matching_left_and_right: |
|
325 |
# annotated lines from last_left_idx to left_idx did not match the lines from
|
|
326 |
# last_right_idx
|
|
327 |
# 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. |
328 |
# need to be updated
|
3224.1.23
by John Arbash Meinel
Clean up the new function variables. |
329 |
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. |
330 |
# 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. |
331 |
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. |
332 |
else: |
333 |
# We need to see if any of the unannotated lines match
|
|
3224.1.23
by John Arbash Meinel
Clean up the new function variables. |
334 |
_find_matching_unannotated_lines(lines, |
335 |
new_lines, annotated_lines, |
|
336 |
last_left_idx, left_idx, |
|
337 |
right_parent_lines, |
|
338 |
last_right_idx, right_idx, |
|
339 |
heads_provider, |
|
340 |
new_revision_id) |
|
341 |
last_right_idx = right_idx + match_len |
|
342 |
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. |
343 |
# 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. |
344 |
assert len(lines) == left_idx |
345 |
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. |
346 |
return lines |