bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
| 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 | 
| 3603.4.3
by Robert Collins Review feedback. | 38 | from bzrlib.repository import _strip_NULL_ghosts | 
| 3603.4.1
by Robert Collins Implement lookups into the current working tree for bzr annotate, fixing bug 3439. | 39 | from bzrlib.revision import CURRENT_REVISION, Revision | 
| 1185.16.53
by Martin Pool - annotate improvements from Goffreddo, with extra bug fixes and tests | 40 | |
| 41 | ||
| 42 | 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. | 43 | 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. | 44 | """Annotate file_id at revision rev_id in branch. | 
| 45 | ||
| 46 |     The branch should already be read_locked() when annotate_file is called.
 | |
| 47 | ||
| 48 |     :param branch: The branch to look for revision numbers and history from.
 | |
| 49 |     :param rev_id: The revision id to annotate.
 | |
| 50 |     :param file_id: The file_id to annotate.
 | |
| 51 |     :param verbose: Show all details rather than truncating to ensure
 | |
| 52 |         reasonable text width.
 | |
| 53 |     :param full: XXXX Not sure what this does.
 | |
| 54 |     :param to_file: The file to output the annotation to; if None stdout is
 | |
| 55 |         used.
 | |
| 56 |     :param show_ids: Show revision ids in the annotation output.
 | |
| 57 |     """
 | |
| 1385
by Martin Pool - simple weave-based annotate code (not complete) | 58 | if to_file is None: | 
| 59 | to_file = sys.stdout | |
| 1185.16.53
by Martin Pool - annotate improvements from Goffreddo, with extra bug fixes and tests | 60 | |
| 2831.3.1
by Ian Clatworthy code cleanups for annotate.py | 61 |     # Handle the show_ids case
 | 
| 3603.4.1
by Robert Collins Implement lookups into the current working tree for bzr annotate, fixing bug 3439. | 62 | annotations = _annotations(branch.repository, file_id, rev_id) | 
| 63 | if show_ids: | |
| 64 | return _show_id_annotations(annotations, to_file, full) | |
| 65 | ||
| 66 |     # Calculate the lengths of the various columns
 | |
| 67 | annotation = list(_expand_annotations(annotations, branch)) | |
| 68 | _print_annotations(annotation, verbose, to_file, full) | |
| 69 | ||
| 70 | ||
| 71 | def annotate_file_tree(tree, file_id, to_file, verbose=False, full=False, | |
| 72 | show_ids=False): | |
| 73 | """Annotate file_id in a tree. | |
| 74 | ||
| 75 |     The tree should already be read_locked() when annotate_file_tree is called.
 | |
| 76 | ||
| 77 |     :param tree: The tree to look for revision numbers and history from.
 | |
| 78 |     :param file_id: The file_id to annotate.
 | |
| 79 |     :param to_file: The file to output the annotation to.
 | |
| 80 |     :param verbose: Show all details rather than truncating to ensure
 | |
| 81 |         reasonable text width.
 | |
| 82 |     :param full: XXXX Not sure what this does.
 | |
| 83 |     :param show_ids: Show revision ids in the annotation output.
 | |
| 84 |     """
 | |
| 85 | rev_id = tree.last_revision() | |
| 86 | branch = tree.branch | |
| 87 | ||
| 88 |     # Handle the show_ids case
 | |
| 89 | annotations = list(tree.annotate_iter(file_id)) | |
| 90 | if show_ids: | |
| 91 | return _show_id_annotations(annotations, to_file, full) | |
| 92 | ||
| 3603.4.3
by Robert Collins Review feedback. | 93 |     # Create a virtual revision to represent the current tree state.
 | 
| 94 |     # Should get some more pending commit attributes, like pending tags,
 | |
| 95 |     # bugfixes etc.
 | |
| 3603.4.1
by Robert Collins Implement lookups into the current working tree for bzr annotate, fixing bug 3439. | 96 | current_rev = Revision(CURRENT_REVISION) | 
| 97 | current_rev.parent_ids = tree.get_parent_ids() | |
| 98 | current_rev.committer = tree.branch.get_config().username() | |
| 99 | current_rev.message = "?" | |
| 100 | current_rev.timestamp = round(time.time(), 3) | |
| 101 | current_rev.timezone = osutils.local_time_offset() | |
| 102 | annotation = list(_expand_annotations(annotations, tree.branch, | |
| 103 | current_rev)) | |
| 104 | _print_annotations(annotation, verbose, to_file, full) | |
| 105 | ||
| 106 | ||
| 107 | def _print_annotations(annotation, verbose, to_file, full): | |
| 108 | """Print annotations to to_file. | |
| 109 | ||
| 110 |     :param to_file: The file to output the annotation to.
 | |
| 111 |     :param verbose: Show all details rather than truncating to ensure
 | |
| 112 |         reasonable text width.
 | |
| 113 |     :param full: XXXX Not sure what this does.
 | |
| 114 |     """
 | |
| 2027.3.1
by John Arbash Meinel 'bzr annotate' shouldn't fail on an empty file: fix bug #56814 | 115 | if len(annotation) == 0: | 
| 2182.3.4
by John Arbash Meinel add show-ids and test that nearby areas are collapsed without full | 116 | 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 | 117 | else: | 
| 2182.3.8
by John Arbash Meinel Some cleanup to make annotate.py < 79 chars wide. | 118 | 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. | 119 | 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 | 120 | 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 | 121 | if not verbose: | 
| 2182.3.7
by John Arbash Meinel Cleanup and add blackbox tests for annotate. | 122 | max_revno_len = min(max_revno_len, 12) | 
| 123 | max_revno_len = max(max_revno_len, 3) | |
| 2182.3.2
by John Arbash Meinel Use shortened revnos unless --long is supplied | 124 | |
| 2831.3.1
by Ian Clatworthy code cleanups for annotate.py | 125 |     # Output the annotations
 | 
| 126 | prevanno = '' | |
| 127 | encoding = getattr(to_file, 'encoding', None) or \ | |
| 128 | osutils.get_terminal_encoding() | |
| 2182.3.4
by John Arbash Meinel add show-ids and test that nearby areas are collapsed without full | 129 | 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 | 130 | if verbose: | 
| 131 | anno = '%-*s %-*s %8s ' % (max_revno_len, revno_str, | |
| 132 | max_origin_len, author, date_str) | |
| 1185.16.53
by Martin Pool - annotate improvements from Goffreddo, with extra bug fixes and tests | 133 | else: | 
| 2182.3.9
by John Arbash Meinel Faster annotate --show-ids. No need to pull the history or the revision info | 134 | if len(revno_str) > max_revno_len: | 
| 135 | revno_str = revno_str[:max_revno_len-1] + '>' | |
| 136 | anno = "%-*s %-7s " % (max_revno_len, revno_str, author[:7]) | |
| 2831.3.1
by Ian Clatworthy code cleanups for annotate.py | 137 | if anno.lstrip() == "" and full: | 
| 138 | anno = prevanno | |
| 2593.1.1
by Adeodato Simó Improve annotate to prevent unicode exceptions in certain situations. | 139 | try: | 
| 140 | to_file.write(anno) | |
| 141 | except UnicodeEncodeError: | |
| 2593.1.4
by Adeodato Simó Add comment from John to the try/except block. | 142 |             # cmd_annotate should be passing in an 'exact' object, which means
 | 
| 143 |             # we have a direct handle to sys.stdout or equivalent. It may not
 | |
| 144 |             # be able to handle the exact Unicode characters, but 'annotate' is
 | |
| 145 |             # a user function (non-scripting), so shouldn't die because of
 | |
| 146 |             # unrepresentable annotation characters. So encode using 'replace',
 | |
| 147 |             # and write them again.
 | |
| 2593.1.3
by Adeodato Simó Cope with to_file.encoding being None or not present. | 148 | to_file.write(anno.encode(encoding, 'replace')) | 
| 2911.6.1
by Blake Winton Change 'print >> f,'s to 'f.write('s. | 149 | to_file.write('| %s\n' % (text,)) | 
| 2831.3.1
by Ian Clatworthy code cleanups for annotate.py | 150 | prevanno = anno | 
| 151 | ||
| 152 | ||
| 3603.4.1
by Robert Collins Implement lookups into the current working tree for bzr annotate, fixing bug 3439. | 153 | def _show_id_annotations(annotations, to_file, full): | 
| 154 | last_rev_id = None | |
| 155 | max_origin_len = max(len(origin) for origin, text in annotations) | |
| 156 | for origin, text in annotations: | |
| 157 | if full or last_rev_id != origin: | |
| 158 | this = origin | |
| 159 | else: | |
| 160 | this = '' | |
| 161 | to_file.write('%*s | %s' % (max_origin_len, this, text)) | |
| 162 | last_rev_id = origin | |
| 163 |     return
 | |
| 164 | ||
| 165 | ||
| 2831.3.1
by Ian Clatworthy code cleanups for annotate.py | 166 | def _annotations(repo, file_id, rev_id): | 
| 3350.6.10
by Martin Pool VersionedFiles review cleanups | 167 | """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. | 168 | annotations = repo.texts.annotate((file_id, rev_id)) | 
| 3350.6.10
by Martin Pool VersionedFiles review cleanups | 169 |     # 
 | 
| 170 | 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 | 171 | |
| 2182.3.10
by John Arbash Meinel minor cleanup. | 172 | |
| 3603.4.1
by Robert Collins Implement lookups into the current working tree for bzr annotate, fixing bug 3439. | 173 | def _expand_annotations(annotations, branch, current_rev=None): | 
| 174 | """Expand a a files annotations into command line UI ready tuples. | |
| 175 | ||
| 176 |     Each tuple includes detailed information, such as the author name, and date
 | |
| 177 |     string for the commit, rather than just the revision id.
 | |
| 178 | ||
| 179 |     :param annotations: The annotations to expand.
 | |
| 180 |     :param revision_id_to_revno: A map from id to revision numbers.
 | |
| 181 |     :param branch: A locked branch to query for revision details.
 | |
| 2182.3.10
by John Arbash Meinel minor cleanup. | 182 |     """
 | 
| 3603.4.1
by Robert Collins Implement lookups into the current working tree for bzr annotate, fixing bug 3439. | 183 | repository = branch.repository | 
| 3603.4.3
by Robert Collins Review feedback. | 184 | if current_rev is not None: | 
| 185 |         # This can probably become a function on MutableTree, get_revno_map there,
 | |
| 186 |         # or something.
 | |
| 187 | last_revision = current_rev.revision_id | |
| 188 |         # XXX: Partially Cloned from branch, uses the old_get_graph, eep.
 | |
| 189 | graph = repository.get_graph() | |
| 190 | revision_graph = dict(((key, value) for key, value in | |
| 191 | graph.iter_ancestry(current_rev.parent_ids) if value is not None)) | |
| 192 | revision_graph = _strip_NULL_ghosts(revision_graph) | |
| 193 | revision_graph[last_revision] = current_rev.parent_ids | |
| 194 | merge_sorted_revisions = tsort.merge_sort( | |
| 195 | revision_graph, | |
| 196 | last_revision, | |
| 197 | None, | |
| 198 | generate_revno=True) | |
| 199 | revision_id_to_revno = dict((rev_id, revno) | |
| 200 | for seq_num, rev_id, depth, revno, end_of_merge in | |
| 201 | merge_sorted_revisions) | |
| 202 | else: | |
| 203 | revision_id_to_revno = branch.get_revision_id_to_revno_map() | |
| 1385
by Martin Pool - simple weave-based annotate code (not complete) | 204 | last_origin = None | 
| 1551.9.6
by Aaron Bentley Optimize annotate by retrieving all revisions at once | 205 | revision_ids = set(o for o, t in annotations) | 
| 3603.4.1
by Robert Collins Implement lookups into the current working tree for bzr annotate, fixing bug 3439. | 206 | revisions = {} | 
| 207 | if CURRENT_REVISION in revision_ids: | |
| 208 | revision_id_to_revno[CURRENT_REVISION] = ( | |
| 209 | "%d?" % (branch.revno() + 1),) | |
| 210 | revisions[CURRENT_REVISION] = current_rev | |
| 1551.9.6
by Aaron Bentley Optimize annotate by retrieving all revisions at once | 211 | revision_ids = [o for o in revision_ids if | 
| 3603.4.1
by Robert Collins Implement lookups into the current working tree for bzr annotate, fixing bug 3439. | 212 | repository.has_revision(o)] | 
| 213 | revisions.update((r.revision_id, r) for r in | |
| 214 | repository.get_revisions(revision_ids)) | |
| 1551.9.6
by Aaron Bentley Optimize annotate by retrieving all revisions at once | 215 | for origin, text in annotations: | 
| 1385
by Martin Pool - simple weave-based annotate code (not complete) | 216 | text = text.rstrip('\r\n') | 
| 217 | if origin == last_origin: | |
| 1185.16.53
by Martin Pool - annotate improvements from Goffreddo, with extra bug fixes and tests | 218 | (revno_str, author, date_str) = ('','','') | 
| 1385
by Martin Pool - simple weave-based annotate code (not complete) | 219 | else: | 
| 220 | last_origin = origin | |
| 1551.9.6
by Aaron Bentley Optimize annotate by retrieving all revisions at once | 221 | if origin not in revisions: | 
| 1185.16.53
by Martin Pool - annotate improvements from Goffreddo, with extra bug fixes and tests | 222 | (revno_str, author, date_str) = ('?','?','?') | 
| 1185.16.1
by Martin Pool - update annotate for new branch api | 223 | else: | 
| 2182.3.1
by John Arbash Meinel Annotate now shows dotted revnos instead of plain revnos. | 224 | revno_str = '.'.join(str(i) for i in | 
| 225 | revision_id_to_revno[origin]) | |
| 1551.9.6
by Aaron Bentley Optimize annotate by retrieving all revisions at once | 226 | rev = revisions[origin] | 
| 1185.16.32
by Martin Pool - add a basic annotate built-in command | 227 | tz = rev.timezone or 0 | 
| 2182.3.8
by John Arbash Meinel Some cleanup to make annotate.py < 79 chars wide. | 228 | date_str = time.strftime('%Y%m%d', | 
| 1185.16.32
by Martin Pool - add a basic annotate built-in command | 229 | time.gmtime(rev.timestamp + tz)) | 
| 230 |             # a lazy way to get something like the email address
 | |
| 231 |             # 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. | 232 | author = rev.get_apparent_author() | 
| 1185.16.53
by Martin Pool - annotate improvements from Goffreddo, with extra bug fixes and tests | 233 | try: | 
| 234 | author = extract_email_address(author) | |
| 2182.3.1
by John Arbash Meinel Annotate now shows dotted revnos instead of plain revnos. | 235 | except errors.NoEmailInUsername: | 
| 1185.16.53
by Martin Pool - annotate improvements from Goffreddo, with extra bug fixes and tests | 236 | pass # use the whole name | 
| 1563.2.1
by Robert Collins Merge in a variation of the versionedfile api from versioned-file. | 237 | yield (revno_str, author, date_str, origin, text) | 
| 1551.9.16
by Aaron Bentley Implement Tree.annotate_iter for RevisionTree and WorkingTree | 238 | |
| 239 | ||
| 2770.1.5
by Aaron Bentley Clean up docs, test matching blocks for reannotate | 240 | def reannotate(parents_lines, new_lines, new_revision_id, | 
| 3224.1.10
by John Arbash Meinel Introduce the heads_provider for reannotate. | 241 | _left_matching_blocks=None, | 
| 242 | heads_provider=None): | |
| 1551.9.16
by Aaron Bentley Implement Tree.annotate_iter for RevisionTree and WorkingTree | 243 | """Create a new annotated version from new lines and parent annotations. | 
| 244 |     
 | |
| 1551.9.17
by Aaron Bentley Annotate for working trees across all parents | 245 |     :param parents_lines: List of annotated lines for all parents
 | 
| 1551.9.16
by Aaron Bentley Implement Tree.annotate_iter for RevisionTree and WorkingTree | 246 |     :param new_lines: The un-annotated new lines
 | 
| 247 |     :param new_revision_id: The revision-id to associate with new lines
 | |
| 248 |         (will often be CURRENT_REVISION)
 | |
| 2770.1.5
by Aaron Bentley Clean up docs, test matching blocks for reannotate | 249 |     :param left_matching_blocks: a hint about which areas are common
 | 
| 250 |         between the text and its left-hand-parent.  The format is
 | |
| 3224.1.3
by John Arbash Meinel Clarify the format of 'matching_blocks' | 251 |         the SequenceMatcher.get_matching_blocks format
 | 
| 252 |         (start_left, start_right, length_of_match).
 | |
| 3224.1.10
by John Arbash Meinel Introduce the heads_provider for reannotate. | 253 |     :param heads_provider: An object which provids a .heads() call to resolve
 | 
| 254 |         if any revision ids are children of others.
 | |
| 255 |         If None, then any ancestry disputes will be resolved with
 | |
| 256 |         new_revision_id
 | |
| 1551.9.16
by Aaron Bentley Implement Tree.annotate_iter for RevisionTree and WorkingTree | 257 |     """
 | 
| 2770.1.1
by Aaron Bentley Initial implmentation of plain knit annotation | 258 | if len(parents_lines) == 0: | 
| 3180.2.1
by John Arbash Meinel Change reannotate to special case the 2-parent case. | 259 | lines = [(new_revision_id, line) for line in new_lines] | 
| 2770.1.1
by Aaron Bentley Initial implmentation of plain knit annotation | 260 | elif len(parents_lines) == 1: | 
| 3180.2.1
by John Arbash Meinel Change reannotate to special case the 2-parent case. | 261 | lines = _reannotate(parents_lines[0], new_lines, new_revision_id, | 
| 262 | _left_matching_blocks) | |
| 263 | elif len(parents_lines) == 2: | |
| 264 | left = _reannotate(parents_lines[0], new_lines, new_revision_id, | |
| 265 | _left_matching_blocks) | |
| 3224.1.9
by John Arbash Meinel Introduce _reannotate_annotated, which updates an annotated text based on a new parent. | 266 | lines = _reannotate_annotated(parents_lines[1], new_lines, | 
| 3224.1.10
by John Arbash Meinel Introduce the heads_provider for reannotate. | 267 | new_revision_id, left, | 
| 268 | heads_provider) | |
| 1551.9.17
by Aaron Bentley Annotate for working trees across all parents | 269 | else: | 
| 3180.2.1
by John Arbash Meinel Change reannotate to special case the 2-parent case. | 270 | reannotations = [_reannotate(parents_lines[0], new_lines, | 
| 271 | new_revision_id, _left_matching_blocks)] | |
| 272 | reannotations.extend(_reannotate(p, new_lines, new_revision_id) | |
| 273 | for p in parents_lines[1:]) | |
| 274 | lines = [] | |
| 1551.9.17
by Aaron Bentley Annotate for working trees across all parents | 275 | for annos in zip(*reannotations): | 
| 276 | origins = set(a for a, l in annos) | |
| 277 | if len(origins) == 1: | |
| 3180.2.1
by John Arbash Meinel Change reannotate to special case the 2-parent case. | 278 |                 # All the parents agree, so just return the first one
 | 
| 279 | lines.append(annos[0]) | |
| 1551.9.17
by Aaron Bentley Annotate for working trees across all parents | 280 | else: | 
| 3180.2.1
by John Arbash Meinel Change reannotate to special case the 2-parent case. | 281 | line = annos[0][1] | 
| 282 | if len(origins) == 2 and new_revision_id in origins: | |
| 283 | origins.remove(new_revision_id) | |
| 284 | if len(origins) == 1: | |
| 3180.2.2
by John Arbash Meinel Fix typo, (thanks Ian) | 285 | lines.append((origins.pop(), line)) | 
| 3180.2.1
by John Arbash Meinel Change reannotate to special case the 2-parent case. | 286 | else: | 
| 287 | lines.append((new_revision_id, line)) | |
| 288 | return lines | |
| 1551.9.18
by Aaron Bentley Updates from review comments | 289 | |
| 1551.9.17
by Aaron Bentley Annotate for working trees across all parents | 290 | |
| 2770.1.5
by Aaron Bentley Clean up docs, test matching blocks for reannotate | 291 | def _reannotate(parent_lines, new_lines, new_revision_id, | 
| 292 | matching_blocks=None): | |
| 1551.9.16
by Aaron Bentley Implement Tree.annotate_iter for RevisionTree and WorkingTree | 293 | new_cur = 0 | 
| 2770.1.5
by Aaron Bentley Clean up docs, test matching blocks for reannotate | 294 | if matching_blocks is None: | 
| 2831.3.1
by Ian Clatworthy code cleanups for annotate.py | 295 | plain_parent_lines = [l for r, l in parent_lines] | 
| 296 | matcher = patiencediff.PatienceSequenceMatcher(None, | |
| 297 | plain_parent_lines, new_lines) | |
| 2770.1.5
by Aaron Bentley Clean up docs, test matching blocks for reannotate | 298 | matching_blocks = matcher.get_matching_blocks() | 
| 3180.2.1
by John Arbash Meinel Change reannotate to special case the 2-parent case. | 299 | lines = [] | 
| 2770.1.5
by Aaron Bentley Clean up docs, test matching blocks for reannotate | 300 | for i, j, n in matching_blocks: | 
| 1551.9.16
by Aaron Bentley Implement Tree.annotate_iter for RevisionTree and WorkingTree | 301 | for line in new_lines[new_cur:j]: | 
| 3180.2.1
by John Arbash Meinel Change reannotate to special case the 2-parent case. | 302 | lines.append((new_revision_id, line)) | 
| 303 | lines.extend(parent_lines[i:i+n]) | |
| 1551.9.16
by Aaron Bentley Implement Tree.annotate_iter for RevisionTree and WorkingTree | 304 | new_cur = j + n | 
| 3180.2.1
by John Arbash Meinel Change reannotate to special case the 2-parent case. | 305 | return lines | 
| 3224.1.9
by John Arbash Meinel Introduce _reannotate_annotated, which updates an annotated text based on a new parent. | 306 | |
| 307 | ||
| 3224.1.23
by John Arbash Meinel Clean up the new function variables. | 308 | def _get_matching_blocks(old, new): | 
| 309 | matcher = patiencediff.PatienceSequenceMatcher(None, | |
| 310 | old, new) | |
| 311 | return matcher.get_matching_blocks() | |
| 312 | ||
| 313 | ||
| 314 | def _find_matching_unannotated_lines(output_lines, plain_child_lines, | |
| 315 | child_lines, start_child, end_child, | |
| 316 | right_lines, start_right, end_right, | |
| 317 | heads_provider, revision_id): | |
| 318 | """Find lines in plain_right_lines that match the existing lines. | |
| 319 | ||
| 320 |     :param output_lines: Append final annotated lines to this list
 | |
| 321 |     :param plain_child_lines: The unannotated new lines for the child text
 | |
| 322 |     :param child_lines: Lines for the child text which have been annotated
 | |
| 323 |         for the left parent
 | |
| 324 |     :param start_child: Position in plain_child_lines and child_lines to start the
 | |
| 325 |         match searching
 | |
| 326 |     :param end_child: Last position in plain_child_lines and child_lines to search
 | |
| 327 |         for a match
 | |
| 328 |     :param right_lines: The annotated lines for the whole text for the right
 | |
| 329 |         parent
 | |
| 330 |     :param start_right: Position in right_lines to start the match
 | |
| 331 |     :param end_right: Last position in right_lines to search for a match
 | |
| 332 |     :param heads_provider: When parents disagree on the lineage of a line, we
 | |
| 333 |         need to check if one side supersedes the other
 | |
| 334 |     :param revision_id: The label to give if a line should be labeled 'tip'
 | |
| 335 |     """
 | |
| 336 | output_extend = output_lines.extend | |
| 337 | output_append = output_lines.append | |
| 338 |     # We need to see if any of the unannotated lines match
 | |
| 339 | plain_right_subset = [l for a,l in right_lines[start_right:end_right]] | |
| 340 | plain_child_subset = plain_child_lines[start_child:end_child] | |
| 341 | match_blocks = _get_matching_blocks(plain_right_subset, plain_child_subset) | |
| 342 | ||
| 343 | last_child_idx = 0 | |
| 344 | ||
| 345 | for right_idx, child_idx, match_len in match_blocks: | |
| 346 |         # All the lines that don't match are just passed along
 | |
| 347 | if child_idx > last_child_idx: | |
| 348 | output_extend(child_lines[start_child + last_child_idx | |
| 349 | :start_child + child_idx]) | |
| 350 | for offset in xrange(match_len): | |
| 351 | left = child_lines[start_child+child_idx+offset] | |
| 352 | right = right_lines[start_right+right_idx+offset] | |
| 353 | if left[0] == right[0]: | |
| 354 |                 # The annotations match, just return the left one
 | |
| 355 | output_append(left) | |
| 356 | elif left[0] == revision_id: | |
| 357 |                 # The left parent marked this as unmatched, so let the
 | |
| 358 |                 # right parent claim it
 | |
| 359 | output_append(right) | |
| 360 | else: | |
| 361 |                 # Left and Right both claim this line
 | |
| 362 | if heads_provider is None: | |
| 363 | output_append((revision_id, left[1])) | |
| 364 | else: | |
| 365 | heads = heads_provider.heads((left[0], right[0])) | |
| 366 | if len(heads) == 1: | |
| 367 | output_append((iter(heads).next(), left[1])) | |
| 368 | else: | |
| 3588.3.1
by John Arbash Meinel Simple patch to the annotate logic to handle bug #232188 | 369 |                         # Both claim different origins, sort lexicographically
 | 
| 370 |                         # so that we always get a stable result.
 | |
| 371 | output_append(sorted([left, right])[0]) | |
| 3224.1.23
by John Arbash Meinel Clean up the new function variables. | 372 | last_child_idx = child_idx + match_len | 
| 373 | ||
| 374 | ||
| 3224.1.9
by John Arbash Meinel Introduce _reannotate_annotated, which updates an annotated text based on a new parent. | 375 | def _reannotate_annotated(right_parent_lines, new_lines, new_revision_id, | 
| 3224.1.10
by John Arbash Meinel Introduce the heads_provider for reannotate. | 376 | annotated_lines, heads_provider): | 
| 3224.1.9
by John Arbash Meinel Introduce _reannotate_annotated, which updates an annotated text based on a new parent. | 377 | """Update the annotations for a node based on another parent. | 
| 378 | ||
| 379 |     :param right_parent_lines: A list of annotated lines for the right-hand
 | |
| 380 |         parent.
 | |
| 381 |     :param new_lines: The unannotated new lines.
 | |
| 382 |     :param new_revision_id: The revision_id to attribute to lines which are not
 | |
| 383 |         present in either parent.
 | |
| 384 |     :param annotated_lines: A list of annotated lines. This should be the
 | |
| 385 |         annotation of new_lines based on parents seen so far.
 | |
| 3224.1.10
by John Arbash Meinel Introduce the heads_provider for reannotate. | 386 |     :param heads_provider: When parents disagree on the lineage of a line, we
 | 
| 387 |         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. | 388 |     """
 | 
| 3376.2.3
by Martin Pool Updated info about assertions | 389 | if len(new_lines) != len(annotated_lines): | 
| 390 | 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. | 391 |     # First compare the newly annotated lines with the right annotated lines.
 | 
| 392 |     # Lines which were not changed in left or right should match. This tends to
 | |
| 393 |     # be the bulk of the lines, and they will need no further processing.
 | |
| 394 | lines = [] | |
| 395 | lines_extend = lines.extend | |
| 3224.1.23
by John Arbash Meinel Clean up the new function variables. | 396 | last_right_idx = 0 # The line just after the last match from the right side | 
| 397 | last_left_idx = 0 | |
| 398 | matching_left_and_right = _get_matching_blocks(right_parent_lines, | |
| 399 | annotated_lines) | |
| 400 | for right_idx, left_idx, match_len in matching_left_and_right: | |
| 401 |         # annotated lines from last_left_idx to left_idx did not match the lines from
 | |
| 402 |         # last_right_idx
 | |
| 403 |         # 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. | 404 |         # need to be updated
 | 
| 3224.1.23
by John Arbash Meinel Clean up the new function variables. | 405 | 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. | 406 |             # 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. | 407 | 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. | 408 | else: | 
| 409 |             # We need to see if any of the unannotated lines match
 | |
| 3224.1.23
by John Arbash Meinel Clean up the new function variables. | 410 | _find_matching_unannotated_lines(lines, | 
| 411 | new_lines, annotated_lines, | |
| 412 | last_left_idx, left_idx, | |
| 413 | right_parent_lines, | |
| 414 | last_right_idx, right_idx, | |
| 415 | heads_provider, | |
| 416 | new_revision_id) | |
| 417 | last_right_idx = right_idx + match_len | |
| 418 | 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. | 419 |         # 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. | 420 | 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. | 421 | return lines |