bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
| 1
by mbp at sourcefrog import from baz patch-364 | 1 | #! /usr/bin/env python
 | 
| 2 | # -*- coding: UTF-8 -*-
 | |
| 3 | ||
| 4 | # This program is free software; you can redistribute it and/or modify
 | |
| 5 | # it under the terms of the GNU General Public License as published by
 | |
| 6 | # the Free Software Foundation; either version 2 of the License, or
 | |
| 7 | # (at your option) any later version.
 | |
| 8 | ||
| 9 | # This program is distributed in the hope that it will be useful,
 | |
| 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| 12 | # GNU General Public License for more details.
 | |
| 13 | ||
| 14 | # You should have received a copy of the GNU General Public License
 | |
| 15 | # along with this program; if not, write to the Free Software
 | |
| 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
| 17 | ||
| 18 | from trace import mutter | |
| 356
by Martin Pool - pychecker fixes in bzrlib.diff | 19 | from errors import BzrError | 
| 1
by mbp at sourcefrog import from baz patch-364 | 20 | |
| 21 | ||
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 22 | def internal_diff(old_label, oldlines, new_label, newlines, to_file): | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 23 | import difflib | 
| 24 | ||
| 25 |     # FIXME: difflib is wrong if there is no trailing newline.
 | |
| 26 |     # The syntax used by patch seems to be "\ No newline at
 | |
| 27 |     # end of file" following the last diff line from that
 | |
| 28 |     # file.  This is not trivial to insert into the
 | |
| 29 |     # unified_diff output and it might be better to just fix
 | |
| 30 |     # or replace that function.
 | |
| 31 | ||
| 32 |     # In the meantime we at least make sure the patch isn't
 | |
| 33 |     # mangled.
 | |
| 34 | ||
| 35 | ||
| 36 |     # Special workaround for Python2.3, where difflib fails if
 | |
| 37 |     # both sequences are empty.
 | |
| 38 | if not oldlines and not newlines: | |
| 39 |         return
 | |
| 40 | ||
| 41 | nonl = False | |
| 42 | ||
| 43 | if oldlines and (oldlines[-1][-1] != '\n'): | |
| 44 | oldlines[-1] += '\n' | |
| 45 | nonl = True | |
| 46 | if newlines and (newlines[-1][-1] != '\n'): | |
| 47 | newlines[-1] += '\n' | |
| 48 | nonl = True | |
| 49 | ||
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 50 | ud = difflib.unified_diff(oldlines, newlines, | 
| 51 | fromfile=old_label, tofile=new_label) | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 52 | |
| 53 |     # work-around for difflib being too smart for its own good
 | |
| 54 |     # if /dev/null is "1,0", patch won't recognize it as /dev/null
 | |
| 55 | if not oldlines: | |
| 56 | ud = list(ud) | |
| 57 | ud[2] = ud[2].replace('-1,0', '-0,0') | |
| 58 | elif not newlines: | |
| 59 | ud = list(ud) | |
| 60 | ud[2] = ud[2].replace('+1,0', '+0,0') | |
| 61 | ||
| 62 | to_file.writelines(ud) | |
| 63 | if nonl: | |
| 64 | print >>to_file, "\\ No newline at end of file" | |
| 65 | print >>to_file | |
| 66 | ||
| 67 | ||
| 550
by Martin Pool - Refactor diff code into one that works purely on | 68 | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 69 | |
| 571
by Martin Pool - new --diff-options to pass options through to external | 70 | def external_diff(old_label, oldlines, new_label, newlines, to_file, | 
| 71 | diff_opts): | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 72 | """Display a diff by calling out to the external diff program.""" | 
| 73 | import sys | |
| 74 | ||
| 75 | if to_file != sys.stdout: | |
| 76 | raise NotImplementedError("sorry, can't send external diff other than to stdout yet", | |
| 77 | to_file) | |
| 78 | ||
| 581
by Martin Pool - make sure any bzr output is flushed before | 79 |     # make sure our own output is properly ordered before the diff
 | 
| 80 | to_file.flush() | |
| 81 | ||
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 82 | from tempfile import NamedTemporaryFile | 
| 571
by Martin Pool - new --diff-options to pass options through to external | 83 | import os | 
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 84 | |
| 85 | oldtmpf = NamedTemporaryFile() | |
| 86 | newtmpf = NamedTemporaryFile() | |
| 87 | ||
| 88 | try: | |
| 89 |         # TODO: perhaps a special case for comparing to or from the empty
 | |
| 90 |         # sequence; can just use /dev/null on Unix
 | |
| 91 | ||
| 92 |         # TODO: if either of the files being compared already exists as a
 | |
| 93 |         # regular named file (e.g. in the working directory) then we can
 | |
| 94 |         # compare directly to that, rather than copying it.
 | |
| 95 | ||
| 96 | oldtmpf.writelines(oldlines) | |
| 97 | newtmpf.writelines(newlines) | |
| 98 | ||
| 99 | oldtmpf.flush() | |
| 100 | newtmpf.flush() | |
| 101 | ||
| 571
by Martin Pool - new --diff-options to pass options through to external | 102 | if not diff_opts: | 
| 103 | diff_opts = [] | |
| 104 | diffcmd = ['diff', | |
| 105 | '--label', old_label, | |
| 106 | oldtmpf.name, | |
| 107 | '--label', new_label, | |
| 108 | newtmpf.name] | |
| 109 | ||
| 110 |         # diff only allows one style to be specified; they don't override.
 | |
| 111 |         # note that some of these take optargs, and the optargs can be
 | |
| 112 |         # directly appended to the options.
 | |
| 113 |         # this is only an approximate parser; it doesn't properly understand
 | |
| 114 |         # the grammar.
 | |
| 115 | for s in ['-c', '-u', '-C', '-U', | |
| 116 | '-e', '--ed', | |
| 117 | '-q', '--brief', | |
| 118 | '--normal', | |
| 119 | '-n', '--rcs', | |
| 120 | '-y', '--side-by-side', | |
| 121 | '-D', '--ifdef']: | |
| 122 | for j in diff_opts: | |
| 123 | if j.startswith(s): | |
| 124 |                     break
 | |
| 125 | else: | |
| 126 |                 continue
 | |
| 127 |             break
 | |
| 128 | else: | |
| 129 | diffcmd.append('-u') | |
| 130 | ||
| 131 | if diff_opts: | |
| 132 | diffcmd.extend(diff_opts) | |
| 133 | ||
| 134 | rc = os.spawnvp(os.P_WAIT, 'diff', diffcmd) | |
| 135 | ||
| 136 | if rc != 0 and rc != 1: | |
| 137 |             # returns 1 if files differ; that's OK
 | |
| 138 | if rc < 0: | |
| 139 | msg = 'signal %d' % (-rc) | |
| 140 | else: | |
| 141 | msg = 'exit code %d' % rc | |
| 142 | ||
| 143 | raise BzrError('external diff failed with %s; command: %r' % (rc, diffcmd)) | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 144 | finally: | 
| 145 | oldtmpf.close() # and delete | |
| 146 | newtmpf.close() | |
| 147 | ||
| 148 | ||
| 149 | ||
| 571
by Martin Pool - new --diff-options to pass options through to external | 150 | def show_diff(b, revision, specific_files, external_diff_options=None): | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 151 | import sys | 
| 152 | ||
| 329
by Martin Pool - refactor command functions into command classes | 153 | if revision == None: | 
| 154 | old_tree = b.basis_tree() | |
| 155 | else: | |
| 156 | old_tree = b.revision_tree(b.lookup_revision(revision)) | |
| 157 | ||
| 158 | new_tree = b.working_tree() | |
| 159 | ||
| 571
by Martin Pool - new --diff-options to pass options through to external | 160 | show_diff_trees(old_tree, new_tree, sys.stdout, specific_files, | 
| 161 | external_diff_options) | |
| 162 | ||
| 163 | ||
| 164 | ||
| 165 | def show_diff_trees(old_tree, new_tree, to_file, specific_files=None, | |
| 166 | external_diff_options=None): | |
| 550
by Martin Pool - Refactor diff code into one that works purely on | 167 | """Show in text form the changes from one tree to another. | 
| 168 | ||
| 169 |     to_files
 | |
| 170 |         If set, include only changes to these files.
 | |
| 571
by Martin Pool - new --diff-options to pass options through to external | 171 | |
| 172 |     external_diff_options
 | |
| 173 |         If set, use an external GNU diff and pass these options.
 | |
| 550
by Martin Pool - Refactor diff code into one that works purely on | 174 |     """
 | 
| 175 | ||
| 329
by Martin Pool - refactor command functions into command classes | 176 |     # TODO: Options to control putting on a prefix or suffix, perhaps as a format string
 | 
| 177 | old_label = '' | |
| 178 | new_label = '' | |
| 179 | ||
| 180 | DEVNULL = '/dev/null' | |
| 181 |     # Windows users, don't panic about this filename -- it is a
 | |
| 182 |     # special signal to GNU patch that the file should be created or
 | |
| 183 |     # deleted respectively.
 | |
| 184 | ||
| 185 |     # TODO: Generation of pseudo-diffs for added/deleted files could
 | |
| 186 |     # be usefully made into a much faster special case.
 | |
| 187 | ||
| 571
by Martin Pool - new --diff-options to pass options through to external | 188 | if external_diff_options: | 
| 189 | assert isinstance(external_diff_options, basestring) | |
| 190 | opts = external_diff_options.split() | |
| 191 | def diff_file(olab, olines, nlab, nlines, to_file): | |
| 192 | external_diff(olab, olines, nlab, nlines, to_file, opts) | |
| 193 | else: | |
| 194 | diff_file = internal_diff | |
| 195 | ||
| 196 | ||
| 478
by Martin Pool - put back support for running diff or status on | 197 | delta = compare_trees(old_tree, new_tree, want_unchanged=False, | 
| 483
by Martin Pool - change 'file_list' to more explanatory 'specific_files' | 198 | specific_files=specific_files) | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 199 | |
| 200 | for path, file_id, kind in delta.removed: | |
| 201 | print '*** removed %s %r' % (kind, path) | |
| 202 | if kind == 'file': | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 203 | diff_file(old_label + path, | 
| 204 | old_tree.get_file(file_id).readlines(), | |
| 205 | DEVNULL, | |
| 206 |                       [],
 | |
| 207 | to_file) | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 208 | |
| 209 | for path, file_id, kind in delta.added: | |
| 210 | print '*** added %s %r' % (kind, path) | |
| 211 | if kind == 'file': | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 212 | diff_file(DEVNULL, | 
| 213 |                       [],
 | |
| 214 | new_label + path, | |
| 215 | new_tree.get_file(file_id).readlines(), | |
| 216 | to_file) | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 217 | |
| 218 | for old_path, new_path, file_id, kind, text_modified in delta.renamed: | |
| 219 | print '*** renamed %s %r => %r' % (kind, old_path, new_path) | |
| 220 | if text_modified: | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 221 | diff_file(old_label + old_path, | 
| 222 | old_tree.get_file(file_id).readlines(), | |
| 223 | new_label + new_path, | |
| 224 | new_tree.get_file(file_id).readlines(), | |
| 225 | to_file) | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 226 | |
| 227 | for path, file_id, kind in delta.modified: | |
| 228 | print '*** modified %s %r' % (kind, path) | |
| 229 | if kind == 'file': | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 230 | diff_file(old_label + path, | 
| 231 | old_tree.get_file(file_id).readlines(), | |
| 232 | new_label + path, | |
| 233 | new_tree.get_file(file_id).readlines(), | |
| 234 | to_file) | |
| 329
by Martin Pool - refactor command functions into command classes | 235 | |
| 236 | ||
| 379
by Martin Pool - Simpler compare_inventories() to possibly replace diff_trees | 237 | |
| 558
by Martin Pool - All top-level classes inherit from object | 238 | class TreeDelta(object): | 
| 379
by Martin Pool - Simpler compare_inventories() to possibly replace diff_trees | 239 | """Describes changes from one tree to another. | 
| 240 | ||
| 241 |     Contains four lists:
 | |
| 242 | ||
| 243 |     added
 | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 244 |         (path, id, kind)
 | 
| 379
by Martin Pool - Simpler compare_inventories() to possibly replace diff_trees | 245 |     removed
 | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 246 |         (path, id, kind)
 | 
| 379
by Martin Pool - Simpler compare_inventories() to possibly replace diff_trees | 247 |     renamed
 | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 248 |         (oldpath, newpath, id, kind, text_modified)
 | 
| 379
by Martin Pool - Simpler compare_inventories() to possibly replace diff_trees | 249 |     modified
 | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 250 |         (path, id, kind)
 | 
| 463
by Martin Pool - compare_trees() also reports unchanged files | 251 |     unchanged
 | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 252 |         (path, id, kind)
 | 
| 379
by Martin Pool - Simpler compare_inventories() to possibly replace diff_trees | 253 | |
| 460
by Martin Pool - new testing command compare-trees | 254 |     Each id is listed only once.
 | 
| 379
by Martin Pool - Simpler compare_inventories() to possibly replace diff_trees | 255 | |
| 460
by Martin Pool - new testing command compare-trees | 256 |     Files that are both modified and renamed are listed only in
 | 
| 257 |     renamed, with the text_modified flag true.
 | |
| 463
by Martin Pool - compare_trees() also reports unchanged files | 258 | |
| 259 |     The lists are normally sorted when the delta is created.
 | |
| 379
by Martin Pool - Simpler compare_inventories() to possibly replace diff_trees | 260 |     """
 | 
| 261 | def __init__(self): | |
| 262 | self.added = [] | |
| 263 | self.removed = [] | |
| 264 | self.renamed = [] | |
| 265 | self.modified = [] | |
| 463
by Martin Pool - compare_trees() also reports unchanged files | 266 | self.unchanged = [] | 
| 379
by Martin Pool - Simpler compare_inventories() to possibly replace diff_trees | 267 | |
| 531
by Martin Pool - new utility TreeDelta.touches_file_id | 268 | |
| 269 | def touches_file_id(self, file_id): | |
| 270 | """Return True if file_id is modified by this delta.""" | |
| 271 | for l in self.added, self.removed, self.modified: | |
| 272 | for v in l: | |
| 273 | if v[1] == file_id: | |
| 274 | return True | |
| 275 | for v in self.renamed: | |
| 276 | if v[2] == file_id: | |
| 277 | return True | |
| 278 | return False | |
| 279 | ||
| 280 | ||
| 465
by Martin Pool - Move show_status() out of Branch into a new function in | 281 | def show(self, to_file, show_ids=False, show_unchanged=False): | 
| 282 | def show_list(files): | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 283 | for path, fid, kind in files: | 
| 284 | if kind == 'directory': | |
| 285 | path += '/' | |
| 286 | elif kind == 'symlink': | |
| 287 | path += '@' | |
| 288 | ||
| 465
by Martin Pool - Move show_status() out of Branch into a new function in | 289 | if show_ids: | 
| 290 | print >>to_file, ' %-30s %s' % (path, fid) | |
| 291 | else: | |
| 292 | print >>to_file, ' ', path | |
| 293 | ||
| 460
by Martin Pool - new testing command compare-trees | 294 | if self.removed: | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 295 | print >>to_file, 'removed:' | 
| 465
by Martin Pool - Move show_status() out of Branch into a new function in | 296 | show_list(self.removed) | 
| 297 | ||
| 460
by Martin Pool - new testing command compare-trees | 298 | if self.added: | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 299 | print >>to_file, 'added:' | 
| 465
by Martin Pool - Move show_status() out of Branch into a new function in | 300 | show_list(self.added) | 
| 301 | ||
| 460
by Martin Pool - new testing command compare-trees | 302 | if self.renamed: | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 303 | print >>to_file, 'renamed:' | 
| 304 | for oldpath, newpath, fid, kind, text_modified in self.renamed: | |
| 460
by Martin Pool - new testing command compare-trees | 305 | if show_ids: | 
| 306 | print >>to_file, ' %s => %s %s' % (oldpath, newpath, fid) | |
| 307 | else: | |
| 308 | print >>to_file, ' %s => %s' % (oldpath, newpath) | |
| 465
by Martin Pool - Move show_status() out of Branch into a new function in | 309 | |
| 460
by Martin Pool - new testing command compare-trees | 310 | if self.modified: | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 311 | print >>to_file, 'modified:' | 
| 465
by Martin Pool - Move show_status() out of Branch into a new function in | 312 | show_list(self.modified) | 
| 313 | ||
| 314 | if show_unchanged and self.unchanged: | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 315 | print >>to_file, 'unchanged:' | 
| 465
by Martin Pool - Move show_status() out of Branch into a new function in | 316 | show_list(self.unchanged) | 
| 460
by Martin Pool - new testing command compare-trees | 317 | |
| 318 | ||
| 319 | ||
| 483
by Martin Pool - change 'file_list' to more explanatory 'specific_files' | 320 | def compare_trees(old_tree, new_tree, want_unchanged, specific_files=None): | 
| 478
by Martin Pool - put back support for running diff or status on | 321 | """Describe changes from one tree to another. | 
| 322 | ||
| 323 |     Returns a TreeDelta with details of added, modified, renamed, and
 | |
| 324 |     deleted entries.
 | |
| 325 | ||
| 326 |     The root entry is specifically exempt.
 | |
| 327 | ||
| 328 |     This only considers versioned files.
 | |
| 329 | ||
| 330 |     want_unchanged
 | |
| 485
by Martin Pool - move commit code into its own module | 331 |         If true, also list files unchanged from one version to
 | 
| 332 |         the next.
 | |
| 478
by Martin Pool - put back support for running diff or status on | 333 | |
| 483
by Martin Pool - change 'file_list' to more explanatory 'specific_files' | 334 |     specific_files
 | 
| 485
by Martin Pool - move commit code into its own module | 335 |         If true, only check for changes to specified names or
 | 
| 336 |         files within them.
 | |
| 478
by Martin Pool - put back support for running diff or status on | 337 |     """
 | 
| 485
by Martin Pool - move commit code into its own module | 338 | |
| 339 | from osutils import is_inside_any | |
| 340 | ||
| 460
by Martin Pool - new testing command compare-trees | 341 | old_inv = old_tree.inventory | 
| 342 | new_inv = new_tree.inventory | |
| 343 | delta = TreeDelta() | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 344 | mutter('start compare_trees') | 
| 478
by Martin Pool - put back support for running diff or status on | 345 | |
| 485
by Martin Pool - move commit code into its own module | 346 |     # TODO: match for specific files can be rather smarter by finding
 | 
| 347 |     # the IDs of those files up front and then considering only that.
 | |
| 478
by Martin Pool - put back support for running diff or status on | 348 | |
| 462
by Martin Pool - New form 'file_id in tree' to check if the file is present | 349 | for file_id in old_tree: | 
| 350 | if file_id in new_tree: | |
| 460
by Martin Pool - new testing command compare-trees | 351 | kind = old_inv.get_file_kind(file_id) | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 352 | assert kind == new_inv.get_file_kind(file_id) | 
| 353 | ||
| 460
by Martin Pool - new testing command compare-trees | 354 | assert kind in ('file', 'directory', 'symlink', 'root_directory'), \ | 
| 355 | 'invalid file kind %r' % kind | |
| 477
by Martin Pool - fix header for listing of unknown files | 356 | |
| 357 | if kind == 'root_directory': | |
| 358 |                 continue
 | |
| 359 | ||
| 360 | old_path = old_inv.id2path(file_id) | |
| 361 | new_path = new_inv.id2path(file_id) | |
| 362 | ||
| 483
by Martin Pool - change 'file_list' to more explanatory 'specific_files' | 363 | if specific_files: | 
| 485
by Martin Pool - move commit code into its own module | 364 | if (not is_inside_any(specific_files, old_path) | 
| 365 | and not is_inside_any(specific_files, new_path)): | |
| 478
by Martin Pool - put back support for running diff or status on | 366 |                     continue
 | 
| 367 | ||
| 460
by Martin Pool - new testing command compare-trees | 368 | if kind == 'file': | 
| 369 | old_sha1 = old_tree.get_file_sha1(file_id) | |
| 370 | new_sha1 = new_tree.get_file_sha1(file_id) | |
| 371 | text_modified = (old_sha1 != new_sha1) | |
| 372 | else: | |
| 373 |                 ## mutter("no text to check for %r %r" % (file_id, kind))
 | |
| 374 | text_modified = False | |
| 471
by Martin Pool - actually avoid reporting unchanged files if not required | 375 | |
| 376 |             # TODO: Can possibly avoid calculating path strings if the
 | |
| 377 |             # two files are unchanged and their names and parents are
 | |
| 378 |             # the same and the parents are unchanged all the way up.
 | |
| 379 |             # May not be worthwhile.
 | |
| 460
by Martin Pool - new testing command compare-trees | 380 | |
| 381 | if old_path != new_path: | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 382 | delta.renamed.append((old_path, new_path, file_id, kind, | 
| 383 | text_modified)) | |
| 460
by Martin Pool - new testing command compare-trees | 384 | elif text_modified: | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 385 | delta.modified.append((new_path, file_id, kind)) | 
| 471
by Martin Pool - actually avoid reporting unchanged files if not required | 386 | elif want_unchanged: | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 387 | delta.unchanged.append((new_path, file_id, kind)) | 
| 460
by Martin Pool - new testing command compare-trees | 388 | else: | 
| 566
by Martin Pool - fix bug in reporting diffs between trees where files have | 389 | kind = old_inv.get_file_kind(file_id) | 
| 485
by Martin Pool - move commit code into its own module | 390 | old_path = old_inv.id2path(file_id) | 
| 391 | if specific_files: | |
| 392 | if not is_inside_any(specific_files, old_path): | |
| 393 |                     continue
 | |
| 394 | delta.removed.append((old_path, file_id, kind)) | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 395 | |
| 396 | mutter('start looking for new files') | |
| 460
by Martin Pool - new testing command compare-trees | 397 | for file_id in new_inv: | 
| 398 | if file_id in old_inv: | |
| 399 |             continue
 | |
| 478
by Martin Pool - put back support for running diff or status on | 400 | new_path = new_inv.id2path(file_id) | 
| 483
by Martin Pool - change 'file_list' to more explanatory 'specific_files' | 401 | if specific_files: | 
| 485
by Martin Pool - move commit code into its own module | 402 | if not is_inside_any(specific_files, new_path): | 
| 478
by Martin Pool - put back support for running diff or status on | 403 |                 continue
 | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 404 | kind = new_inv.get_file_kind(file_id) | 
| 478
by Martin Pool - put back support for running diff or status on | 405 | delta.added.append((new_path, file_id, kind)) | 
| 460
by Martin Pool - new testing command compare-trees | 406 | |
| 407 | delta.removed.sort() | |
| 408 | delta.added.sort() | |
| 409 | delta.renamed.sort() | |
| 410 | delta.modified.sort() | |
| 474
by Martin Pool - sort unchanged files | 411 | delta.unchanged.sort() | 
| 460
by Martin Pool - new testing command compare-trees | 412 | |
| 413 | return delta |