bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
| 1658.1.9
by Martin Pool Give an error for bzr diff on an nonexistent file (Malone #3619) | 1 | # Copyright (C) 2004, 2005, 2006 Canonical Ltd.
 | 
| 1711.2.54
by John Arbash Meinel Use mkstemp instead of NamedTemporary file for external diff. | 2 | #
 | 
| 1
by mbp at sourcefrog import from baz patch-364 | 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.
 | |
| 1711.2.54
by John Arbash Meinel Use mkstemp instead of NamedTemporary file for external diff. | 7 | #
 | 
| 1
by mbp at sourcefrog import from baz patch-364 | 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.
 | |
| 1711.2.54
by John Arbash Meinel Use mkstemp instead of NamedTemporary file for external diff. | 12 | #
 | 
| 1
by mbp at sourcefrog import from baz patch-364 | 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 | ||
| 2520.4.140
by Aaron Bentley Use matching blocks from mpdiff for knit delta creation | 17 | import difflib | 
| 1711.2.54
by John Arbash Meinel Use mkstemp instead of NamedTemporary file for external diff. | 18 | import os | 
| 1899.1.5
by John Arbash Meinel Always buffer the output of diff, so we can check if retcode==2 is because of Binary files | 19 | import re | 
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 20 | import shutil | 
| 1996.3.9
by John Arbash Meinel lazy_import diff.py | 21 | import sys | 
| 22 | ||
| 23 | from bzrlib.lazy_import import lazy_import | |
| 24 | lazy_import(globals(), """ | |
| 25 | import errno
 | |
| 1692.8.7
by James Henstridge changes suggested by John Meinel | 26 | import subprocess
 | 
| 1711.2.54
by John Arbash Meinel Use mkstemp instead of NamedTemporary file for external diff. | 27 | import tempfile
 | 
| 1740.2.5
by Aaron Bentley Merge from bzr.dev | 28 | import time
 | 
| 29 | ||
| 1955.2.10
by John Arbash Meinel Unset a few other LANG type variables when spawning diff | 30 | from bzrlib import (
 | 
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 31 |     bzrdir,
 | 
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 32 |     commands,
 | 
| 1955.2.10
by John Arbash Meinel Unset a few other LANG type variables when spawning diff | 33 |     errors,
 | 
| 34 |     osutils,
 | |
| 1996.3.9
by John Arbash Meinel lazy_import diff.py | 35 |     patiencediff,
 | 
| 36 |     textfile,
 | |
| 1551.12.29
by Aaron Bentley Copy and extend patch date formatting code, add patch-date parsing | 37 |     timestamp,
 | 
| 1955.2.10
by John Arbash Meinel Unset a few other LANG type variables when spawning diff | 38 |     )
 | 
| 1996.3.9
by John Arbash Meinel lazy_import diff.py | 39 | """) | 
| 40 | ||
| 41 | from bzrlib.symbol_versioning import ( | |
| 42 | deprecated_function, | |
| 3123.1.1
by John Arbash Meinel Update from deprecating 0.93 to 1.0, and add a 1.1 deprecation. | 43 | one_zero, | 
| 1996.3.9
by John Arbash Meinel lazy_import diff.py | 44 |         )
 | 
| 1711.2.54
by John Arbash Meinel Use mkstemp instead of NamedTemporary file for external diff. | 45 | from bzrlib.trace import mutter, warning | 
| 1
by mbp at sourcefrog import from baz patch-364 | 46 | |
| 1711.2.24
by John Arbash Meinel Late bind to PatienceSequenceMatcher to allow plugin to override. | 47 | |
| 767
by Martin Pool - files are only reported as modified if their name or parent has changed, | 48 | # TODO: Rather than building a changeset object, we should probably
 | 
| 49 | # invoke callbacks on an object.  That object can either accumulate a
 | |
| 50 | # list, write them out directly, etc etc.
 | |
| 51 | ||
| 2520.4.140
by Aaron Bentley Use matching blocks from mpdiff for knit delta creation | 52 | |
| 53 | class _PrematchedMatcher(difflib.SequenceMatcher): | |
| 54 | """Allow SequenceMatcher operations to use predetermined blocks""" | |
| 55 | ||
| 56 | def __init__(self, matching_blocks): | |
| 57 | difflib.SequenceMatcher(self, None, None) | |
| 58 | self.matching_blocks = matching_blocks | |
| 59 | self.opcodes = None | |
| 60 | ||
| 61 | ||
| 1558.15.11
by Aaron Bentley Apply merge review suggestions | 62 | def internal_diff(old_filename, oldlines, new_filename, newlines, to_file, | 
| 1711.2.30
by John Arbash Meinel Fix bug in internal_diff handling of unicode paths | 63 | allow_binary=False, sequence_matcher=None, | 
| 64 | path_encoding='utf8'): | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 65 |     # FIXME: difflib is wrong if there is no trailing newline.
 | 
| 66 |     # The syntax used by patch seems to be "\ No newline at
 | |
| 67 |     # end of file" following the last diff line from that
 | |
| 68 |     # file.  This is not trivial to insert into the
 | |
| 69 |     # unified_diff output and it might be better to just fix
 | |
| 70 |     # or replace that function.
 | |
| 71 | ||
| 72 |     # In the meantime we at least make sure the patch isn't
 | |
| 73 |     # mangled.
 | |
| 74 | ||
| 75 | ||
| 76 |     # Special workaround for Python2.3, where difflib fails if
 | |
| 77 |     # both sequences are empty.
 | |
| 78 | if not oldlines and not newlines: | |
| 79 |         return
 | |
| 1558.15.2
by Aaron Bentley Implemented binary file handling for diff | 80 | |
| 1558.15.11
by Aaron Bentley Apply merge review suggestions | 81 | if allow_binary is False: | 
| 1996.3.9
by John Arbash Meinel lazy_import diff.py | 82 | textfile.check_text_lines(oldlines) | 
| 83 | textfile.check_text_lines(newlines) | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 84 | |
| 1185.81.8
by John Arbash Meinel Updating unified_diff to take a factory, using the new diff algorithm in the code. | 85 | if sequence_matcher is None: | 
| 1996.3.9
by John Arbash Meinel lazy_import diff.py | 86 | sequence_matcher = patiencediff.PatienceSequenceMatcher | 
| 87 | ud = patiencediff.unified_diff(oldlines, newlines, | |
| 1740.2.5
by Aaron Bentley Merge from bzr.dev | 88 | fromfile=old_filename.encode(path_encoding), | 
| 89 | tofile=new_filename.encode(path_encoding), | |
| 1185.81.8
by John Arbash Meinel Updating unified_diff to take a factory, using the new diff algorithm in the code. | 90 | sequencematcher=sequence_matcher) | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 91 | |
| 1092.1.50
by Robert Collins make diff lsdiff/filterdiff friendly | 92 | ud = list(ud) | 
| 3085.1.1
by John Arbash Meinel Fix internal_diff to not fail when the texts are identical. | 93 | if len(ud) == 0: # Identical contents, nothing to do | 
| 94 |         return
 | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 95 |     # work-around for difflib being too smart for its own good
 | 
| 96 |     # if /dev/null is "1,0", patch won't recognize it as /dev/null
 | |
| 97 | if not oldlines: | |
| 98 | ud[2] = ud[2].replace('-1,0', '-0,0') | |
| 99 | elif not newlines: | |
| 100 | ud[2] = ud[2].replace('+1,0', '+0,0') | |
| 1092.1.50
by Robert Collins make diff lsdiff/filterdiff friendly | 101 |     # work around for difflib emitting random spaces after the label
 | 
| 102 | ud[0] = ud[0][:-2] + '\n' | |
| 103 | ud[1] = ud[1][:-2] + '\n' | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 104 | |
| 804
by Martin Pool Patch from John: | 105 | for line in ud: | 
| 106 | to_file.write(line) | |
| 974.1.5
by Aaron Bentley Fixed handling of missing newlines in udiffs | 107 | if not line.endswith('\n'): | 
| 108 | to_file.write("\n\\ No newline at end of file\n") | |
| 2911.6.1
by Blake Winton Change 'print >> f,'s to 'f.write('s. | 109 | to_file.write('\n') | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 110 | |
| 111 | ||
| 1920.1.1
by John Arbash Meinel fix bug #56307, handle binary files even when LANG is not english | 112 | def _spawn_external_diff(diffcmd, capture_errors=True): | 
| 113 | """Spawn the externall diff process, and return the child handle. | |
| 114 | ||
| 115 |     :param diffcmd: The command list to spawn
 | |
| 2138.1.1
by Wouter van Heyst Robuster external diff output handling. | 116 |     :param capture_errors: Capture stderr as well as setting LANG=C
 | 
| 117 |         and LC_ALL=C. This lets us read and understand the output of diff,
 | |
| 118 |         and respond to any errors.
 | |
| 1920.1.1
by John Arbash Meinel fix bug #56307, handle binary files even when LANG is not english | 119 |     :return: A Popen object.
 | 
| 120 |     """
 | |
| 121 | if capture_errors: | |
| 2321.2.2
by Alexander Belchenko win32 fixes for test_external_diff_binary (gettext on win32 rely on $LANGUAGE) | 122 |         # construct minimal environment
 | 
| 123 | env = {} | |
| 124 | path = os.environ.get('PATH') | |
| 125 | if path is not None: | |
| 126 | env['PATH'] = path | |
| 2321.2.5
by Alexander Belchenko external diff: no need for special code path for win32 (suggested by John Meinel) | 127 | env['LANGUAGE'] = 'C' # on win32 only LANGUAGE has effect | 
| 128 | env['LANG'] = 'C' | |
| 129 | env['LC_ALL'] = 'C' | |
| 1920.1.1
by John Arbash Meinel fix bug #56307, handle binary files even when LANG is not english | 130 | stderr = subprocess.PIPE | 
| 131 | else: | |
| 2321.2.2
by Alexander Belchenko win32 fixes for test_external_diff_binary (gettext on win32 rely on $LANGUAGE) | 132 | env = None | 
| 1920.1.1
by John Arbash Meinel fix bug #56307, handle binary files even when LANG is not english | 133 | stderr = None | 
| 134 | ||
| 135 | try: | |
| 136 | pipe = subprocess.Popen(diffcmd, | |
| 137 | stdin=subprocess.PIPE, | |
| 138 | stdout=subprocess.PIPE, | |
| 139 | stderr=stderr, | |
| 2321.2.2
by Alexander Belchenko win32 fixes for test_external_diff_binary (gettext on win32 rely on $LANGUAGE) | 140 | env=env) | 
| 1920.1.1
by John Arbash Meinel fix bug #56307, handle binary files even when LANG is not english | 141 | except OSError, e: | 
| 142 | if e.errno == errno.ENOENT: | |
| 143 | raise errors.NoDiff(str(e)) | |
| 144 |         raise
 | |
| 145 | ||
| 146 | return pipe | |
| 147 | ||
| 148 | ||
| 1185.35.29
by Aaron Bentley Support whitespace in diff filenames | 149 | def external_diff(old_filename, oldlines, new_filename, newlines, to_file, | 
| 571
by Martin Pool - new --diff-options to pass options through to external | 150 | diff_opts): | 
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 151 | """Display a diff by calling out to the external diff program.""" | 
| 581
by Martin Pool - make sure any bzr output is flushed before | 152 |     # make sure our own output is properly ordered before the diff
 | 
| 153 | to_file.flush() | |
| 154 | ||
| 1711.2.54
by John Arbash Meinel Use mkstemp instead of NamedTemporary file for external diff. | 155 | oldtmp_fd, old_abspath = tempfile.mkstemp(prefix='bzr-diff-old-') | 
| 156 | newtmp_fd, new_abspath = tempfile.mkstemp(prefix='bzr-diff-new-') | |
| 157 | oldtmpf = os.fdopen(oldtmp_fd, 'wb') | |
| 158 | newtmpf = os.fdopen(newtmp_fd, 'wb') | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 159 | |
| 160 | try: | |
| 161 |         # TODO: perhaps a special case for comparing to or from the empty
 | |
| 162 |         # sequence; can just use /dev/null on Unix
 | |
| 163 | ||
| 164 |         # TODO: if either of the files being compared already exists as a
 | |
| 165 |         # regular named file (e.g. in the working directory) then we can
 | |
| 166 |         # compare directly to that, rather than copying it.
 | |
| 167 | ||
| 168 | oldtmpf.writelines(oldlines) | |
| 169 | newtmpf.writelines(newlines) | |
| 170 | ||
| 1711.2.54
by John Arbash Meinel Use mkstemp instead of NamedTemporary file for external diff. | 171 | oldtmpf.close() | 
| 172 | newtmpf.close() | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 173 | |
| 571
by Martin Pool - new --diff-options to pass options through to external | 174 | if not diff_opts: | 
| 175 | diff_opts = [] | |
| 176 | diffcmd = ['diff', | |
| 1740.2.5
by Aaron Bentley Merge from bzr.dev | 177 | '--label', old_filename, | 
| 1711.2.54
by John Arbash Meinel Use mkstemp instead of NamedTemporary file for external diff. | 178 | old_abspath, | 
| 1740.2.5
by Aaron Bentley Merge from bzr.dev | 179 | '--label', new_filename, | 
| 1711.2.56
by John Arbash Meinel Raise NoDiff if 'diff' not present. | 180 | new_abspath, | 
| 181 | '--binary', | |
| 182 |                   ]
 | |
| 571
by Martin Pool - new --diff-options to pass options through to external | 183 | |
| 184 |         # diff only allows one style to be specified; they don't override.
 | |
| 185 |         # note that some of these take optargs, and the optargs can be
 | |
| 186 |         # directly appended to the options.
 | |
| 187 |         # this is only an approximate parser; it doesn't properly understand
 | |
| 188 |         # the grammar.
 | |
| 189 | for s in ['-c', '-u', '-C', '-U', | |
| 190 | '-e', '--ed', | |
| 191 | '-q', '--brief', | |
| 192 | '--normal', | |
| 193 | '-n', '--rcs', | |
| 194 | '-y', '--side-by-side', | |
| 195 | '-D', '--ifdef']: | |
| 196 | for j in diff_opts: | |
| 197 | if j.startswith(s): | |
| 198 |                     break
 | |
| 199 | else: | |
| 200 |                 continue
 | |
| 201 |             break
 | |
| 202 | else: | |
| 203 | diffcmd.append('-u') | |
| 204 | ||
| 205 | if diff_opts: | |
| 206 | diffcmd.extend(diff_opts) | |
| 207 | ||
| 1920.1.1
by John Arbash Meinel fix bug #56307, handle binary files even when LANG is not english | 208 | pipe = _spawn_external_diff(diffcmd, capture_errors=True) | 
| 209 | out,err = pipe.communicate() | |
| 210 | rc = pipe.returncode | |
| 571
by Martin Pool - new --diff-options to pass options through to external | 211 | |
| 1920.1.1
by John Arbash Meinel fix bug #56307, handle binary files even when LANG is not english | 212 |         # internal_diff() adds a trailing newline, add one here for consistency
 | 
| 213 | out += '\n' | |
| 1899.1.5
by John Arbash Meinel Always buffer the output of diff, so we can check if retcode==2 is because of Binary files | 214 | if rc == 2: | 
| 215 |             # 'diff' gives retcode == 2 for all sorts of errors
 | |
| 216 |             # one of those is 'Binary files differ'.
 | |
| 217 |             # Bad options could also be the problem.
 | |
| 1904.1.4
by Marien Zwart Make external diff in binary mode work with recent versions of diffutils. | 218 |             # 'Binary files' is not a real error, so we suppress that error.
 | 
| 1920.1.1
by John Arbash Meinel fix bug #56307, handle binary files even when LANG is not english | 219 | lang_c_out = out | 
| 220 | ||
| 221 |             # Since we got here, we want to make sure to give an i18n error
 | |
| 222 | pipe = _spawn_external_diff(diffcmd, capture_errors=False) | |
| 223 | out, err = pipe.communicate() | |
| 224 | ||
| 225 |             # Write out the new i18n diff response
 | |
| 226 | to_file.write(out+'\n') | |
| 227 | if pipe.returncode != 2: | |
| 1996.3.9
by John Arbash Meinel lazy_import diff.py | 228 | raise errors.BzrError( | 
| 229 |                                'external diff failed with exit code 2'
 | |
| 2138.1.1
by Wouter van Heyst Robuster external diff output handling. | 230 |                                ' when run with LANG=C and LC_ALL=C,'
 | 
| 231 | ' but not when run natively: %r' % (diffcmd,)) | |
| 1920.1.1
by John Arbash Meinel fix bug #56307, handle binary files even when LANG is not english | 232 | |
| 233 | first_line = lang_c_out.split('\n', 1)[0] | |
| 1904.1.4
by Marien Zwart Make external diff in binary mode work with recent versions of diffutils. | 234 |             # Starting with diffutils 2.8.4 the word "binary" was dropped.
 | 
| 235 | m = re.match('^(binary )?files.*differ$', first_line, re.I) | |
| 1920.1.1
by John Arbash Meinel fix bug #56307, handle binary files even when LANG is not english | 236 | if m is None: | 
| 1996.3.9
by John Arbash Meinel lazy_import diff.py | 237 | raise errors.BzrError('external diff failed with exit code 2;' | 
| 238 | ' command: %r' % (diffcmd,)) | |
| 1920.1.1
by John Arbash Meinel fix bug #56307, handle binary files even when LANG is not english | 239 | else: | 
| 240 |                 # Binary files differ, just return
 | |
| 241 |                 return
 | |
| 242 | ||
| 243 |         # If we got to here, we haven't written out the output of diff
 | |
| 244 |         # do so now
 | |
| 245 | to_file.write(out) | |
| 246 | if rc not in (0, 1): | |
| 571
by Martin Pool - new --diff-options to pass options through to external | 247 |             # returns 1 if files differ; that's OK
 | 
| 248 | if rc < 0: | |
| 249 | msg = 'signal %d' % (-rc) | |
| 250 | else: | |
| 251 | msg = 'exit code %d' % rc | |
| 252 | ||
| 1996.3.9
by John Arbash Meinel lazy_import diff.py | 253 | raise errors.BzrError('external diff failed with %s; command: %r' | 
| 254 | % (rc, diffcmd)) | |
| 1899.1.6
by John Arbash Meinel internal_diff always adds a trailing \n, make sure external_diff does too | 255 | |
| 256 | ||
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 257 | finally: | 
| 258 | oldtmpf.close() # and delete | |
| 259 | newtmpf.close() | |
| 1711.2.54
by John Arbash Meinel Use mkstemp instead of NamedTemporary file for external diff. | 260 |         # Clean up. Warn in case the files couldn't be deleted
 | 
| 261 |         # (in case windows still holds the file open, but not
 | |
| 262 |         # if the files have already been deleted)
 | |
| 263 | try: | |
| 264 | os.remove(old_abspath) | |
| 265 | except OSError, e: | |
| 266 | if e.errno not in (errno.ENOENT,): | |
| 267 | warning('Failed to delete temporary file: %s %s', | |
| 268 | old_abspath, e) | |
| 269 | try: | |
| 270 | os.remove(new_abspath) | |
| 271 | except OSError: | |
| 272 | if e.errno not in (errno.ENOENT,): | |
| 273 | warning('Failed to delete temporary file: %s %s', | |
| 274 | new_abspath, e) | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 275 | |
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 276 | |
| 3123.1.1
by John Arbash Meinel Update from deprecating 0.93 to 1.0, and add a 1.1 deprecation. | 277 | @deprecated_function(one_zero) | 
| 1551.2.15
by Aaron Bentley Rename cmd_show_diff to diff_cmd_helper | 278 | def diff_cmd_helper(tree, specific_files, external_diff_options, | 
| 1684.1.6
by Martin Pool (patch) --diff-prefix option (goffredo, alexander) | 279 | old_revision_spec=None, new_revision_spec=None, | 
| 2197.2.1
by Martin Pool Refactor cmd_diff | 280 | revision_specs=None, | 
| 1684.1.6
by Martin Pool (patch) --diff-prefix option (goffredo, alexander) | 281 | old_label='a/', new_label='b/'): | 
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 282 | """Helper for cmd_diff. | 
| 283 | ||
| 2197.2.1
by Martin Pool Refactor cmd_diff | 284 |     :param tree:
 | 
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 285 |         A WorkingTree
 | 
| 286 | ||
| 2197.2.1
by Martin Pool Refactor cmd_diff | 287 |     :param specific_files:
 | 
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 288 |         The specific files to compare, or None
 | 
| 289 | ||
| 2197.2.1
by Martin Pool Refactor cmd_diff | 290 |     :param external_diff_options:
 | 
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 291 |         If non-None, run an external diff, and pass it these options
 | 
| 292 | ||
| 2197.2.1
by Martin Pool Refactor cmd_diff | 293 |     :param old_revision_spec:
 | 
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 294 |         If None, use basis tree as old revision, otherwise use the tree for
 | 
| 295 |         the specified revision. 
 | |
| 296 | ||
| 2197.2.1
by Martin Pool Refactor cmd_diff | 297 |     :param new_revision_spec:
 | 
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 298 |         If None, use working tree as new revision, otherwise use the tree for
 | 
| 299 |         the specified revision.
 | |
| 300 |     
 | |
| 2197.2.1
by Martin Pool Refactor cmd_diff | 301 |     :param revision_specs: 
 | 
| 302 |         Zero, one or two RevisionSpecs from the command line, saying what revisions 
 | |
| 303 |         to compare.  This can be passed as an alternative to the old_revision_spec 
 | |
| 304 |         and new_revision_spec parameters.
 | |
| 305 | ||
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 306 |     The more general form is show_diff_trees(), where the caller
 | 
| 307 |     supplies any two trees.
 | |
| 308 |     """
 | |
| 2197.2.2
by Martin Pool merge bzr.dev, reconcile with option changes | 309 | |
| 310 |     # TODO: perhaps remove the old parameters old_revision_spec and
 | |
| 311 |     # new_revision_spec, since this is only really for use from cmd_diff and
 | |
| 312 |     # it now always passes through a sequence of revision_specs -- mbp
 | |
| 313 |     # 20061221
 | |
| 314 | ||
| 1551.2.14
by Aaron Bentley Updated argument names, DRY fixes. | 315 | def spec_tree(spec): | 
| 1732.3.1
by Matthieu Moy Implementation of -r revno:N:/path/to/branch | 316 | if tree: | 
| 317 | revision = spec.in_store(tree.branch) | |
| 318 | else: | |
| 319 | revision = spec.in_store(None) | |
| 320 | revision_id = revision.rev_id | |
| 321 | branch = revision.branch | |
| 322 | return branch.repository.revision_tree(revision_id) | |
| 2197.2.1
by Martin Pool Refactor cmd_diff | 323 | |
| 324 | if revision_specs is not None: | |
| 325 | assert (old_revision_spec is None | |
| 326 | and new_revision_spec is None) | |
| 327 | if len(revision_specs) > 0: | |
| 328 | old_revision_spec = revision_specs[0] | |
| 329 | if len(revision_specs) > 1: | |
| 330 | new_revision_spec = revision_specs[1] | |
| 331 | ||
| 1551.2.14
by Aaron Bentley Updated argument names, DRY fixes. | 332 | if old_revision_spec is None: | 
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 333 | old_tree = tree.basis_tree() | 
| 334 | else: | |
| 1551.2.14
by Aaron Bentley Updated argument names, DRY fixes. | 335 | old_tree = spec_tree(old_revision_spec) | 
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 336 | |
| 2197.2.1
by Martin Pool Refactor cmd_diff | 337 | if (new_revision_spec is None | 
| 338 | or new_revision_spec.spec is None): | |
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 339 | new_tree = tree | 
| 340 | else: | |
| 1551.2.14
by Aaron Bentley Updated argument names, DRY fixes. | 341 | new_tree = spec_tree(new_revision_spec) | 
| 2197.2.1
by Martin Pool Refactor cmd_diff | 342 | |
| 1551.7.19
by Aaron Bentley Always include working tree when calculating file ids for diff | 343 | if new_tree is not tree: | 
| 344 | extra_trees = (tree,) | |
| 345 | else: | |
| 346 | extra_trees = None | |
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 347 | |
| 1551.2.14
by Aaron Bentley Updated argument names, DRY fixes. | 348 | return show_diff_trees(old_tree, new_tree, sys.stdout, specific_files, | 
| 1684.1.6
by Martin Pool (patch) --diff-prefix option (goffredo, alexander) | 349 | external_diff_options, | 
| 1551.7.19
by Aaron Bentley Always include working tree when calculating file ids for diff | 350 | old_label=old_label, new_label=new_label, | 
| 351 | extra_trees=extra_trees) | |
| 1551.2.13
by Aaron Bentley Got diff working properly with checkouts | 352 | |
| 571
by Martin Pool - new --diff-options to pass options through to external | 353 | |
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 354 | def _get_trees_to_diff(path_list, revision_specs, old_url, new_url): | 
| 355 | """Get the trees and specific files to diff given a list of paths. | |
| 356 | ||
| 357 |     This method works out the trees to be diff'ed and the files of
 | |
| 358 |     interest within those trees.
 | |
| 359 | ||
| 360 |     :param path_list:
 | |
| 361 |         the list of arguments passed to the diff command
 | |
| 362 |     :param revision_specs:
 | |
| 363 |         Zero, one or two RevisionSpecs from the diff command line,
 | |
| 364 |         saying what revisions to compare.
 | |
| 365 |     :param old_url:
 | |
| 366 |         The url of the old branch or tree. If None, the tree to use is
 | |
| 367 |         taken from the first path, if any, or the current working tree.
 | |
| 368 |     :param new_url:
 | |
| 369 |         The url of the new branch or tree. If None, the tree to use is
 | |
| 370 |         taken from the first path, if any, or the current working tree.
 | |
| 371 |     :returns:
 | |
| 372 |         a tuple of (old_tree, new_tree, specific_files, extra_trees) where
 | |
| 373 |         extra_trees is a sequence of additional trees to search in for
 | |
| 374 |         file-ids.
 | |
| 375 |     """
 | |
| 376 |     # Get the old and new revision specs
 | |
| 377 | old_revision_spec = None | |
| 378 | new_revision_spec = None | |
| 379 | if revision_specs is not None: | |
| 380 | if len(revision_specs) > 0: | |
| 381 | old_revision_spec = revision_specs[0] | |
| 3072.1.5
by Ian Clatworthy more good ideas from abentley | 382 | if old_url is None: | 
| 383 | old_url = old_revision_spec.get_branch() | |
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 384 | if len(revision_specs) > 1: | 
| 385 | new_revision_spec = revision_specs[1] | |
| 3072.1.5
by Ian Clatworthy more good ideas from abentley | 386 | if new_url is None: | 
| 387 | new_url = new_revision_spec.get_branch() | |
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 388 | |
| 3072.1.5
by Ian Clatworthy more good ideas from abentley | 389 | other_paths = [] | 
| 390 | make_paths_wt_relative = True | |
| 3164.1.1
by Ian Clatworthy diff without arguments means the current tree, not the current directory | 391 | consider_relpath = True | 
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 392 | if path_list is None or len(path_list) == 0: | 
| 3164.1.1
by Ian Clatworthy diff without arguments means the current tree, not the current directory | 393 |         # If no path is given, the current working tree is used
 | 
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 394 | default_location = u'.' | 
| 3164.1.1
by Ian Clatworthy diff without arguments means the current tree, not the current directory | 395 | consider_relpath = False | 
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 396 | elif old_url is not None and new_url is not None: | 
| 397 | other_paths = path_list | |
| 3072.1.5
by Ian Clatworthy more good ideas from abentley | 398 | make_paths_wt_relative = False | 
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 399 | else: | 
| 400 | default_location = path_list[0] | |
| 401 | other_paths = path_list[1:] | |
| 402 | ||
| 403 |     # Get the old location
 | |
| 3072.1.2
by Ian Clatworthy Test various --old and --new combinations | 404 | specific_files = [] | 
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 405 | if old_url is None: | 
| 406 | old_url = default_location | |
| 407 | working_tree, branch, relpath = \ | |
| 408 | bzrdir.BzrDir.open_containing_tree_or_branch(old_url) | |
| 3164.1.1
by Ian Clatworthy diff without arguments means the current tree, not the current directory | 409 | if consider_relpath and relpath != '': | 
| 3072.1.2
by Ian Clatworthy Test various --old and --new combinations | 410 | specific_files.append(relpath) | 
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 411 | old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch) | 
| 412 | ||
| 413 |     # Get the new location
 | |
| 414 | if new_url is None: | |
| 415 | new_url = default_location | |
| 416 | if new_url != old_url: | |
| 417 | working_tree, branch, relpath = \ | |
| 418 | bzrdir.BzrDir.open_containing_tree_or_branch(new_url) | |
| 3164.1.1
by Ian Clatworthy diff without arguments means the current tree, not the current directory | 419 | if consider_relpath and relpath != '': | 
| 3072.1.2
by Ian Clatworthy Test various --old and --new combinations | 420 | specific_files.append(relpath) | 
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 421 | new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch, | 
| 422 | basis_is_default=working_tree is None) | |
| 423 | ||
| 3072.1.2
by Ian Clatworthy Test various --old and --new combinations | 424 |     # Get the specific files (all files is None, no files is [])
 | 
| 3072.1.5
by Ian Clatworthy more good ideas from abentley | 425 | if make_paths_wt_relative and working_tree is not None: | 
| 3072.1.2
by Ian Clatworthy Test various --old and --new combinations | 426 | other_paths = _relative_paths_in_tree(working_tree, other_paths) | 
| 427 | specific_files.extend(other_paths) | |
| 428 | if len(specific_files) == 0: | |
| 429 | specific_files = None | |
| 430 | ||
| 431 |     # Get extra trees that ought to be searched for file-ids
 | |
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 432 | extra_trees = None | 
| 3072.1.5
by Ian Clatworthy more good ideas from abentley | 433 | if working_tree is not None and working_tree not in (old_tree, new_tree): | 
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 434 | extra_trees = (working_tree,) | 
| 435 | return old_tree, new_tree, specific_files, extra_trees | |
| 436 | ||
| 437 | ||
| 438 | def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True): | |
| 439 | if branch is None and tree is not None: | |
| 440 | branch = tree.branch | |
| 441 | if spec is None or spec.spec is None: | |
| 442 | if basis_is_default: | |
| 3072.1.5
by Ian Clatworthy more good ideas from abentley | 443 | if tree is not None: | 
| 444 | return tree.basis_tree() | |
| 445 | else: | |
| 446 | return branch.basis_tree() | |
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 447 | else: | 
| 448 | return tree | |
| 449 | revision = spec.in_store(branch) | |
| 450 | revision_id = revision.rev_id | |
| 451 | rev_branch = revision.branch | |
| 452 | return rev_branch.repository.revision_tree(revision_id) | |
| 453 | ||
| 454 | ||
| 3072.1.2
by Ian Clatworthy Test various --old and --new combinations | 455 | def _relative_paths_in_tree(tree, paths): | 
| 456 | """Get the relative paths within a working tree. | |
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 457 | |
| 3072.1.6
by Ian Clatworthy Fix docstring for _relative_paths_in_tree | 458 |     Each path may be either an absolute path or a path relative to the
 | 
| 459 |     current working directory.
 | |
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 460 |     """
 | 
| 3072.1.2
by Ian Clatworthy Test various --old and --new combinations | 461 | result = [] | 
| 462 | for filename in paths: | |
| 463 | try: | |
| 464 | result.append(tree.relpath(osutils.dereference_path(filename))) | |
| 465 | except errors.PathNotChild: | |
| 466 | raise errors.BzrCommandError("Files are in different branches") | |
| 467 | return result | |
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 468 | |
| 469 | ||
| 571
by Martin Pool - new --diff-options to pass options through to external | 470 | def show_diff_trees(old_tree, new_tree, to_file, specific_files=None, | 
| 1684.1.6
by Martin Pool (patch) --diff-prefix option (goffredo, alexander) | 471 | external_diff_options=None, | 
| 1551.7.17
by Aaron Bentley Switch to PathsNotVersioned, accept extra_trees | 472 | old_label='a/', new_label='b/', | 
| 2598.6.12
by ghigo Move the encoding of the commit message at the command line level | 473 | extra_trees=None, | 
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 474 | path_encoding='utf8', | 
| 475 | using=None): | |
| 550
by Martin Pool - Refactor diff code into one that works purely on | 476 | """Show in text form the changes from one tree to another. | 
| 477 | ||
| 3072.1.1
by Ian Clatworthy Improved diff based on feedback from abentley | 478 |     to_file
 | 
| 479 |         The output stream.
 | |
| 480 | ||
| 481 |     specific_files
 | |
| 482 |         Include only changes to these files - None for all changes.
 | |
| 571
by Martin Pool - new --diff-options to pass options through to external | 483 | |
| 484 |     external_diff_options
 | |
| 485 |         If set, use an external GNU diff and pass these options.
 | |
| 1551.7.18
by Aaron Bentley Indentation and documentation fixes | 486 | |
| 487 |     extra_trees
 | |
| 488 |         If set, more Trees to use for looking up file ids
 | |
| 2598.6.12
by ghigo Move the encoding of the commit message at the command line level | 489 | |
| 490 |     path_encoding
 | |
| 2598.6.24
by ghigo update on the basis of Aaron suggestions | 491 |         If set, the path will be encoded as specified, otherwise is supposed
 | 
| 492 |         to be utf8
 | |
| 550
by Martin Pool - Refactor diff code into one that works purely on | 493 |     """
 | 
| 1543.1.1
by Denys Duchier lock operations for trees - use them for diff | 494 | old_tree.lock_read() | 
| 495 | try: | |
| 2255.7.38
by John Arbash Meinel show_diff_trees() should lock any extra trees it is passed. | 496 | if extra_trees is not None: | 
| 497 | for tree in extra_trees: | |
| 498 | tree.lock_read() | |
| 1543.1.1
by Denys Duchier lock operations for trees - use them for diff | 499 | new_tree.lock_read() | 
| 500 | try: | |
| 3009.2.22
by Aaron Bentley Update names & docstring | 501 | differ = DiffTree.from_trees_options(old_tree, new_tree, to_file, | 
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 502 | path_encoding, | 
| 503 | external_diff_options, | |
| 504 | old_label, new_label, using) | |
| 3009.2.12
by Aaron Bentley Associate labels with text diffing only | 505 | return differ.show_diff(specific_files, extra_trees) | 
| 1543.1.1
by Denys Duchier lock operations for trees - use them for diff | 506 | finally: | 
| 507 | new_tree.unlock() | |
| 2255.7.38
by John Arbash Meinel show_diff_trees() should lock any extra trees it is passed. | 508 | if extra_trees is not None: | 
| 509 | for tree in extra_trees: | |
| 510 | tree.unlock() | |
| 1543.1.1
by Denys Duchier lock operations for trees - use them for diff | 511 | finally: | 
| 512 | old_tree.unlock() | |
| 513 | ||
| 514 | ||
| 1740.2.5
by Aaron Bentley Merge from bzr.dev | 515 | def _patch_header_date(tree, file_id, path): | 
| 516 | """Returns a timestamp suitable for use in a patch header.""" | |
| 2405.1.2
by John Arbash Meinel Fix bug #103870 by passing None instead of a (sometimes wrong) path | 517 | mtime = tree.get_file_mtime(file_id, path) | 
| 518 | assert mtime is not None, \ | |
| 519 | "got an mtime of None for file-id %s, path %s in tree %s" % ( | |
| 520 | file_id, path, tree) | |
| 521 | return timestamp.format_patch_date(mtime) | |
| 1740.2.5
by Aaron Bentley Merge from bzr.dev | 522 | |
| 523 | ||
| 1662.1.9
by Martin Pool Give a clear error for bzr status of an unversioned, nonexistent file. (Malone #3619) | 524 | def _raise_if_nonexistent(paths, old_tree, new_tree): | 
| 525 | """Complain if paths are not in either inventory or tree. | |
| 526 | ||
| 527 |     It's OK with the files exist in either tree's inventory, or 
 | |
| 528 |     if they exist in the tree but are not versioned.
 | |
| 529 |     
 | |
| 530 |     This can be used by operations such as bzr status that can accept
 | |
| 531 |     unknown or ignored files.
 | |
| 532 |     """
 | |
| 533 | mutter("check paths: %r", paths) | |
| 534 | if not paths: | |
| 535 |         return
 | |
| 536 | s = old_tree.filter_unversioned_files(paths) | |
| 537 | s = new_tree.filter_unversioned_files(s) | |
| 538 | s = [path for path in s if not new_tree.has_filename(path)] | |
| 539 | if s: | |
| 540 | raise errors.PathsDoNotExist(sorted(s)) | |
| 541 | ||
| 542 | ||
| 1398
by Robert Collins integrate in Gustavos x-bit patch | 543 | def get_prop_change(meta_modified): | 
| 544 | if meta_modified: | |
| 545 | return " (properties changed)" | |
| 546 | else: | |
| 547 | return "" | |
| 548 | ||
| 549 | ||
| 3009.2.22
by Aaron Bentley Update names & docstring | 550 | class DiffPath(object): | 
| 3009.2.14
by Aaron Bentley Update return type handling | 551 | """Base type for command object that compare files""" | 
| 3009.2.17
by Aaron Bentley Update docs | 552 | |
| 3009.2.14
by Aaron Bentley Update return type handling | 553 |     # The type or contents of the file were unsuitable for diffing
 | 
| 3009.2.29
by Aaron Bentley Change constants to strings | 554 | CANNOT_DIFF = 'CANNOT_DIFF' | 
| 3009.2.14
by Aaron Bentley Update return type handling | 555 |     # The file has changed in a semantic way
 | 
| 3009.2.29
by Aaron Bentley Change constants to strings | 556 | CHANGED = 'CHANGED' | 
| 557 |     # The file content may have changed, but there is no semantic change
 | |
| 558 | UNCHANGED = 'UNCHANGED' | |
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 559 | |
| 3009.2.13
by Aaron Bentley Refactor differ to support registering differ factories | 560 | def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8'): | 
| 3009.2.17
by Aaron Bentley Update docs | 561 | """Constructor. | 
| 562 | ||
| 563 |         :param old_tree: The tree to show as the old tree in the comparison
 | |
| 564 |         :param new_tree: The tree to show as new in the comparison
 | |
| 565 |         :param to_file: The file to write comparison data to
 | |
| 566 |         :param path_encoding: The character encoding to write paths in
 | |
| 567 |         """
 | |
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 568 | self.old_tree = old_tree | 
| 569 | self.new_tree = new_tree | |
| 570 | self.to_file = to_file | |
| 3009.2.13
by Aaron Bentley Refactor differ to support registering differ factories | 571 | self.path_encoding = path_encoding | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 572 | |
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 573 | def finish(self): | 
| 574 |         pass
 | |
| 575 | ||
| 3009.2.28
by Aaron Bentley Add from_diff_tree factories | 576 |     @classmethod
 | 
| 577 | def from_diff_tree(klass, diff_tree): | |
| 578 | return klass(diff_tree.old_tree, diff_tree.new_tree, | |
| 579 | diff_tree.to_file, diff_tree.path_encoding) | |
| 580 | ||
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 581 |     @staticmethod
 | 
| 582 | def _diff_many(differs, file_id, old_path, new_path, old_kind, new_kind): | |
| 583 | for file_differ in differs: | |
| 584 | result = file_differ.diff(file_id, old_path, new_path, old_kind, | |
| 585 | new_kind) | |
| 3009.2.22
by Aaron Bentley Update names & docstring | 586 | if result is not DiffPath.CANNOT_DIFF: | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 587 | return result | 
| 588 | else: | |
| 3009.2.22
by Aaron Bentley Update names & docstring | 589 | return DiffPath.CANNOT_DIFF | 
| 590 | ||
| 591 | ||
| 592 | class DiffKindChange(object): | |
| 3009.2.17
by Aaron Bentley Update docs | 593 | """Special differ for file kind changes. | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 594 | |
| 3009.2.17
by Aaron Bentley Update docs | 595 |     Represents kind change as deletion + creation.  Uses the other differs
 | 
| 596 |     to do this.
 | |
| 597 |     """
 | |
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 598 | def __init__(self, differs): | 
| 599 | self.differs = differs | |
| 600 | ||
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 601 | def finish(self): | 
| 602 |         pass
 | |
| 603 | ||
| 3009.2.28
by Aaron Bentley Add from_diff_tree factories | 604 |     @classmethod
 | 
| 605 | def from_diff_tree(klass, diff_tree): | |
| 606 | return klass(diff_tree.differs) | |
| 607 | ||
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 608 | def diff(self, file_id, old_path, new_path, old_kind, new_kind): | 
| 3009.2.17
by Aaron Bentley Update docs | 609 | """Perform comparison | 
| 610 | ||
| 611 |         :param file_id: The file_id of the file to compare
 | |
| 612 |         :param old_path: Path of the file in the old tree
 | |
| 613 |         :param new_path: Path of the file in the new tree
 | |
| 614 |         :param old_kind: Old file-kind of the file
 | |
| 615 |         :param new_kind: New file-kind of the file
 | |
| 616 |         """
 | |
| 3009.2.18
by Aaron Bentley Change KindChangeDiffer's anti-recursion to avoid kind pairs with None | 617 | if None in (old_kind, new_kind): | 
| 3009.2.22
by Aaron Bentley Update names & docstring | 618 | return DiffPath.CANNOT_DIFF | 
| 619 | result = DiffPath._diff_many(self.differs, file_id, old_path, | |
| 3009.2.18
by Aaron Bentley Change KindChangeDiffer's anti-recursion to avoid kind pairs with None | 620 | new_path, old_kind, None) | 
| 3009.2.22
by Aaron Bentley Update names & docstring | 621 | if result is DiffPath.CANNOT_DIFF: | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 622 | return result | 
| 3009.2.22
by Aaron Bentley Update names & docstring | 623 | return DiffPath._diff_many(self.differs, file_id, old_path, new_path, | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 624 | None, new_kind) | 
| 625 | ||
| 626 | ||
| 3009.2.22
by Aaron Bentley Update names & docstring | 627 | class DiffDirectory(DiffPath): | 
| 3009.2.19
by Aaron Bentley Implement directory diffing | 628 | |
| 629 | def diff(self, file_id, old_path, new_path, old_kind, new_kind): | |
| 630 | """Perform comparison between two directories. (dummy) | |
| 631 | ||
| 632 |         """
 | |
| 633 | if 'directory' not in (old_kind, new_kind): | |
| 634 | return self.CANNOT_DIFF | |
| 635 | if old_kind not in ('directory', None): | |
| 636 | return self.CANNOT_DIFF | |
| 637 | if new_kind not in ('directory', None): | |
| 638 | return self.CANNOT_DIFF | |
| 639 | return self.CHANGED | |
| 640 | ||
| 3009.2.20
by Aaron Bentley PEP8 | 641 | |
| 3009.2.22
by Aaron Bentley Update names & docstring | 642 | class DiffSymlink(DiffPath): | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 643 | |
| 644 | def diff(self, file_id, old_path, new_path, old_kind, new_kind): | |
| 3009.2.17
by Aaron Bentley Update docs | 645 | """Perform comparison between two symlinks | 
| 646 | ||
| 647 |         :param file_id: The file_id of the file to compare
 | |
| 648 |         :param old_path: Path of the file in the old tree
 | |
| 649 |         :param new_path: Path of the file in the new tree
 | |
| 650 |         :param old_kind: Old file-kind of the file
 | |
| 651 |         :param new_kind: New file-kind of the file
 | |
| 652 |         """
 | |
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 653 | if 'symlink' not in (old_kind, new_kind): | 
| 3009.2.14
by Aaron Bentley Update return type handling | 654 | return self.CANNOT_DIFF | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 655 | if old_kind == 'symlink': | 
| 656 | old_target = self.old_tree.get_symlink_target(file_id) | |
| 657 | elif old_kind is None: | |
| 658 | old_target = None | |
| 659 | else: | |
| 3009.2.14
by Aaron Bentley Update return type handling | 660 | return self.CANNOT_DIFF | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 661 | if new_kind == 'symlink': | 
| 662 | new_target = self.new_tree.get_symlink_target(file_id) | |
| 663 | elif new_kind is None: | |
| 664 | new_target = None | |
| 665 | else: | |
| 3009.2.14
by Aaron Bentley Update return type handling | 666 | return self.CANNOT_DIFF | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 667 | return self.diff_symlink(old_target, new_target) | 
| 668 | ||
| 669 | def diff_symlink(self, old_target, new_target): | |
| 670 | if old_target is None: | |
| 671 | self.to_file.write('=== target is %r\n' % new_target) | |
| 672 | elif new_target is None: | |
| 673 | self.to_file.write('=== target was %r\n' % old_target) | |
| 674 | else: | |
| 675 | self.to_file.write('=== target changed %r => %r\n' % | |
| 676 | (old_target, new_target)) | |
| 3009.2.14
by Aaron Bentley Update return type handling | 677 | return self.CHANGED | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 678 | |
| 679 | ||
| 3009.2.22
by Aaron Bentley Update names & docstring | 680 | class DiffText(DiffPath): | 
| 3009.2.2
by Aaron Bentley Implement Differ object for abstracting diffing | 681 | |
| 3009.2.7
by Aaron Bentley Move responsibility for generating diff labels into Differ.diff | 682 |     # GNU Patch uses the epoch date to detect files that are being added
 | 
| 683 |     # or removed in a diff.
 | |
| 684 | EPOCH_DATE = '1970-01-01 00:00:00 +0000' | |
| 685 | ||
| 3009.2.13
by Aaron Bentley Refactor differ to support registering differ factories | 686 | def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8', | 
| 687 | old_label='', new_label='', text_differ=internal_diff): | |
| 3009.2.22
by Aaron Bentley Update names & docstring | 688 | DiffPath.__init__(self, old_tree, new_tree, to_file, path_encoding) | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 689 | self.text_differ = text_differ | 
| 690 | self.old_label = old_label | |
| 691 | self.new_label = new_label | |
| 3009.2.12
by Aaron Bentley Associate labels with text diffing only | 692 | self.path_encoding = path_encoding | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 693 | |
| 694 | def diff(self, file_id, old_path, new_path, old_kind, new_kind): | |
| 3009.2.17
by Aaron Bentley Update docs | 695 | """Compare two files in unified diff format | 
| 696 | ||
| 697 |         :param file_id: The file_id of the file to compare
 | |
| 698 |         :param old_path: Path of the file in the old tree
 | |
| 699 |         :param new_path: Path of the file in the new tree
 | |
| 700 |         :param old_kind: Old file-kind of the file
 | |
| 701 |         :param new_kind: New file-kind of the file
 | |
| 702 |         """
 | |
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 703 | if 'file' not in (old_kind, new_kind): | 
| 3009.2.14
by Aaron Bentley Update return type handling | 704 | return self.CANNOT_DIFF | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 705 | from_file_id = to_file_id = file_id | 
| 706 | if old_kind == 'file': | |
| 707 | old_date = _patch_header_date(self.old_tree, file_id, old_path) | |
| 708 | elif old_kind is None: | |
| 709 | old_date = self.EPOCH_DATE | |
| 3009.2.12
by Aaron Bentley Associate labels with text diffing only | 710 | from_file_id = None | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 711 | else: | 
| 3009.2.14
by Aaron Bentley Update return type handling | 712 | return self.CANNOT_DIFF | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 713 | if new_kind == 'file': | 
| 714 | new_date = _patch_header_date(self.new_tree, file_id, new_path) | |
| 715 | elif new_kind is None: | |
| 716 | new_date = self.EPOCH_DATE | |
| 717 | to_file_id = None | |
| 718 | else: | |
| 3009.2.14
by Aaron Bentley Update return type handling | 719 | return self.CANNOT_DIFF | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 720 | from_label = '%s%s\t%s' % (self.old_label, old_path, old_date) | 
| 721 | to_label = '%s%s\t%s' % (self.new_label, new_path, new_date) | |
| 722 | return self.diff_text(from_file_id, to_file_id, from_label, to_label) | |
| 723 | ||
| 724 | def diff_text(self, from_file_id, to_file_id, from_label, to_label): | |
| 725 | """Diff the content of given files in two trees | |
| 726 | ||
| 727 |         :param from_file_id: The id of the file in the from tree.  If None,
 | |
| 728 |             the file is not present in the from tree.
 | |
| 729 |         :param to_file_id: The id of the file in the to tree.  This may refer
 | |
| 730 |             to a different file from from_file_id.  If None,
 | |
| 731 |             the file is not present in the to tree.
 | |
| 732 |         """
 | |
| 733 | def _get_text(tree, file_id): | |
| 734 | if file_id is not None: | |
| 735 | return tree.get_file(file_id).readlines() | |
| 736 | else: | |
| 737 | return [] | |
| 738 | try: | |
| 739 | from_text = _get_text(self.old_tree, from_file_id) | |
| 740 | to_text = _get_text(self.new_tree, to_file_id) | |
| 741 | self.text_differ(from_label, from_text, to_label, to_text, | |
| 742 | self.to_file) | |
| 743 | except errors.BinaryFile: | |
| 744 | self.to_file.write( | |
| 745 | ("Binary files %s and %s differ\n" % | |
| 746 | (from_label, to_label)).encode(self.path_encoding)) | |
| 3009.2.14
by Aaron Bentley Update return type handling | 747 | return self.CHANGED | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 748 | |
| 749 | ||
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 750 | class DiffFromTool(DiffPath): | 
| 751 | ||
| 752 | def __init__(self, command_template, old_tree, new_tree, to_file, | |
| 753 | path_encoding='utf-8'): | |
| 754 | DiffPath.__init__(self, old_tree, new_tree, to_file, path_encoding) | |
| 755 | self.command_template = command_template | |
| 756 | self._root = tempfile.mkdtemp(prefix='bzr-diff-') | |
| 757 | ||
| 758 |     @classmethod
 | |
| 759 | def from_string(klass, command_string, old_tree, new_tree, to_file, | |
| 760 | path_encoding='utf-8'): | |
| 761 | command_template = commands.shlex_split_unicode(command_string) | |
| 762 | command_template.extend(['%(old_path)s', '%(new_path)s']) | |
| 763 | return klass(command_template, old_tree, new_tree, to_file, | |
| 764 | path_encoding) | |
| 765 | ||
| 766 |     @classmethod
 | |
| 767 | def make_from_diff_tree(klass, command_string): | |
| 768 | def from_diff_tree(diff_tree): | |
| 769 | return klass.from_string(command_string, diff_tree.old_tree, | |
| 770 | diff_tree.new_tree, diff_tree.to_file) | |
| 771 | return from_diff_tree | |
| 772 | ||
| 773 | def _get_command(self, old_path, new_path): | |
| 774 | my_map = {'old_path': old_path, 'new_path': new_path} | |
| 775 | return [t % my_map for t in self.command_template] | |
| 776 | ||
| 777 | def _execute(self, old_path, new_path): | |
| 3145.1.1
by Aaron Bentley Handle missing tools gracefully in diff --using | 778 | command = self._get_command(old_path, new_path) | 
| 779 | try: | |
| 780 | proc = subprocess.Popen(command, stdout=subprocess.PIPE, | |
| 781 | cwd=self._root) | |
| 782 | except OSError, e: | |
| 783 | if e.errno == errno.ENOENT: | |
| 784 | raise errors.ExecutableMissing(command[0]) | |
| 3145.1.2
by Aaron Bentley Don't swallow other OSErrors | 785 | else: | 
| 786 |                 raise
 | |
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 787 | self.to_file.write(proc.stdout.read()) | 
| 788 | return proc.wait() | |
| 789 | ||
| 3123.6.5
by Aaron Bentley Symlink to real files if possible | 790 | def _try_symlink_root(self, tree, prefix): | 
| 791 | if not (getattr(tree, 'abspath', None) is not None | |
| 792 | and osutils.has_symlinks()): | |
| 793 | return False | |
| 794 | try: | |
| 795 | os.symlink(tree.abspath(''), osutils.pathjoin(self._root, prefix)) | |
| 796 | except OSError, e: | |
| 797 | if e.errno != errno.EEXIST: | |
| 798 |                 raise
 | |
| 799 | return True | |
| 800 | ||
| 3123.6.4
by Aaron Bentley Set mtime (and atime) on files for --using | 801 | def _write_file(self, file_id, tree, prefix, relpath): | 
| 802 | full_path = osutils.pathjoin(self._root, prefix, relpath) | |
| 3123.6.5
by Aaron Bentley Symlink to real files if possible | 803 | if self._try_symlink_root(tree, prefix): | 
| 804 | return full_path | |
| 3123.6.4
by Aaron Bentley Set mtime (and atime) on files for --using | 805 | parent_dir = osutils.dirname(full_path) | 
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 806 | try: | 
| 807 | os.makedirs(parent_dir) | |
| 808 | except OSError, e: | |
| 809 | if e.errno != errno.EEXIST: | |
| 810 |                 raise
 | |
| 3123.6.6
by Aaron Bentley Use relpath for get_file | 811 | source = tree.get_file(file_id, relpath) | 
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 812 | try: | 
| 3123.6.4
by Aaron Bentley Set mtime (and atime) on files for --using | 813 | target = open(full_path, 'wb') | 
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 814 | try: | 
| 815 | osutils.pumpfile(source, target) | |
| 816 | finally: | |
| 817 | target.close() | |
| 818 | finally: | |
| 819 | source.close() | |
| 3123.6.7
by Aaron Bentley Mark temporary files read-only | 820 | osutils.make_readonly(full_path) | 
| 3123.6.4
by Aaron Bentley Set mtime (and atime) on files for --using | 821 | mtime = tree.get_file_mtime(file_id) | 
| 822 | os.utime(full_path, (mtime, mtime)) | |
| 823 | return full_path | |
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 824 | |
| 825 | def _prepare_files(self, file_id, old_path, new_path): | |
| 826 | old_disk_path = self._write_file(file_id, self.old_tree, 'old', | |
| 827 | old_path) | |
| 828 | new_disk_path = self._write_file(file_id, self.new_tree, 'new', | |
| 829 | new_path) | |
| 830 | return old_disk_path, new_disk_path | |
| 831 | ||
| 832 | def finish(self): | |
| 3123.6.8
by Aaron Bentley Us osutils.rmtree instead of shutil.rmtree, now that some files are readonly | 833 | osutils.rmtree(self._root) | 
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 834 | |
| 835 | def diff(self, file_id, old_path, new_path, old_kind, new_kind): | |
| 836 | if (old_kind, new_kind) != ('file', 'file'): | |
| 837 | return DiffPath.CANNOT_DIFF | |
| 838 | self._prepare_files(file_id, old_path, new_path) | |
| 839 | self._execute(osutils.pathjoin('old', old_path), | |
| 840 | osutils.pathjoin('new', new_path)) | |
| 841 | ||
| 842 | ||
| 3009.2.22
by Aaron Bentley Update names & docstring | 843 | class DiffTree(object): | 
| 844 | """Provides textual representations of the difference between two trees. | |
| 845 | ||
| 846 |     A DiffTree examines two trees and where a file-id has altered
 | |
| 847 |     between them, generates a textual representation of the difference.
 | |
| 848 |     DiffTree uses a sequence of DiffPath objects which are each
 | |
| 849 |     given the opportunity to handle a given altered fileid. The list
 | |
| 850 |     of DiffPath objects can be extended globally by appending to
 | |
| 851 |     DiffTree.diff_factories, or for a specific diff operation by
 | |
| 3009.2.27
by Aaron Bentley Use extra_factories instead of extra_diffs | 852 |     supplying the extra_factories option to the appropriate method.
 | 
| 3009.2.22
by Aaron Bentley Update names & docstring | 853 |     """
 | 
| 854 | ||
| 855 |     # list of factories that can provide instances of DiffPath objects
 | |
| 3009.2.17
by Aaron Bentley Update docs | 856 |     # may be extended by plugins.
 | 
| 3009.2.28
by Aaron Bentley Add from_diff_tree factories | 857 | diff_factories = [DiffSymlink.from_diff_tree, | 
| 858 | DiffDirectory.from_diff_tree] | |
| 3009.2.13
by Aaron Bentley Refactor differ to support registering differ factories | 859 | |
| 3009.2.12
by Aaron Bentley Associate labels with text diffing only | 860 | def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8', | 
| 3009.2.28
by Aaron Bentley Add from_diff_tree factories | 861 | diff_text=None, extra_factories=None): | 
| 3009.2.17
by Aaron Bentley Update docs | 862 | """Constructor | 
| 863 | ||
| 864 |         :param old_tree: Tree to show as old in the comparison
 | |
| 865 |         :param new_tree: Tree to show as new in the comparison
 | |
| 866 |         :param to_file: File to write comparision to
 | |
| 867 |         :param path_encoding: Character encoding to write paths in
 | |
| 3009.2.28
by Aaron Bentley Add from_diff_tree factories | 868 |         :param diff_text: DiffPath-type object to use as a last resort for
 | 
| 3009.2.17
by Aaron Bentley Update docs | 869 |             diffing text files.
 | 
| 3009.2.27
by Aaron Bentley Use extra_factories instead of extra_diffs | 870 |         :param extra_factories: Factories of DiffPaths to try before any other
 | 
| 871 |             DiffPaths"""
 | |
| 3009.2.28
by Aaron Bentley Add from_diff_tree factories | 872 | if diff_text is None: | 
| 873 | diff_text = DiffText(old_tree, new_tree, to_file, path_encoding, | |
| 874 | '', '', internal_diff) | |
| 3009.2.4
by Aaron Bentley Make old_tree/new_tree construction parameters of Differ | 875 | self.old_tree = old_tree | 
| 876 | self.new_tree = new_tree | |
| 3009.2.2
by Aaron Bentley Implement Differ object for abstracting diffing | 877 | self.to_file = to_file | 
| 3009.2.28
by Aaron Bentley Add from_diff_tree factories | 878 | self.path_encoding = path_encoding | 
| 3009.2.13
by Aaron Bentley Refactor differ to support registering differ factories | 879 | self.differs = [] | 
| 3009.2.27
by Aaron Bentley Use extra_factories instead of extra_diffs | 880 | if extra_factories is not None: | 
| 3009.2.28
by Aaron Bentley Add from_diff_tree factories | 881 | self.differs.extend(f(self) for f in extra_factories) | 
| 882 | self.differs.extend(f(self) for f in self.diff_factories) | |
| 883 | self.differs.extend([diff_text, DiffKindChange.from_diff_tree(self)]) | |
| 3009.2.6
by Aaron Bentley Convert show_diff_trees into a Differ method | 884 | |
| 885 |     @classmethod
 | |
| 886 | def from_trees_options(klass, old_tree, new_tree, to_file, | |
| 3009.2.17
by Aaron Bentley Update docs | 887 | path_encoding, external_diff_options, old_label, | 
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 888 | new_label, using): | 
| 3009.2.22
by Aaron Bentley Update names & docstring | 889 | """Factory for producing a DiffTree. | 
| 3009.2.17
by Aaron Bentley Update docs | 890 | |
| 891 |         Designed to accept options used by show_diff_trees.
 | |
| 892 |         :param old_tree: The tree to show as old in the comparison
 | |
| 893 |         :param new_tree: The tree to show as new in the comparison
 | |
| 894 |         :param to_file: File to write comparisons to
 | |
| 895 |         :param path_encoding: Character encoding to use for writing paths
 | |
| 896 |         :param external_diff_options: If supplied, use the installed diff
 | |
| 897 |             binary to perform file comparison, using supplied options.
 | |
| 898 |         :param old_label: Prefix to use for old file labels
 | |
| 899 |         :param new_label: Prefix to use for new file labels
 | |
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 900 |         :param using: Commandline to use to invoke an external diff tool
 | 
| 3009.2.17
by Aaron Bentley Update docs | 901 |         """
 | 
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 902 | if using is not None: | 
| 903 | extra_factories = [DiffFromTool.make_from_diff_tree(using)] | |
| 904 | else: | |
| 905 | extra_factories = [] | |
| 3009.2.6
by Aaron Bentley Convert show_diff_trees into a Differ method | 906 | if external_diff_options: | 
| 907 | assert isinstance(external_diff_options, basestring) | |
| 908 | opts = external_diff_options.split() | |
| 909 | def diff_file(olab, olines, nlab, nlines, to_file): | |
| 910 | external_diff(olab, olines, nlab, nlines, to_file, opts) | |
| 911 | else: | |
| 912 | diff_file = internal_diff | |
| 3009.2.28
by Aaron Bentley Add from_diff_tree factories | 913 | diff_text = DiffText(old_tree, new_tree, to_file, path_encoding, | 
| 914 | old_label, new_label, diff_file) | |
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 915 | return klass(old_tree, new_tree, to_file, path_encoding, diff_text, | 
| 916 | extra_factories) | |
| 3009.2.6
by Aaron Bentley Convert show_diff_trees into a Differ method | 917 | |
| 3009.2.12
by Aaron Bentley Associate labels with text diffing only | 918 | def show_diff(self, specific_files, extra_trees=None): | 
| 3009.2.17
by Aaron Bentley Update docs | 919 | """Write tree diff to self.to_file | 
| 920 | ||
| 921 |         :param sepecific_files: the specific files to compare (recursive)
 | |
| 922 |         :param extra_trees: extra trees to use for mapping paths to file_ids
 | |
| 923 |         """
 | |
| 3123.6.2
by Aaron Bentley Implement diff --using natively | 924 | try: | 
| 925 | return self._show_diff(specific_files, extra_trees) | |
| 926 | finally: | |
| 927 | for differ in self.differs: | |
| 928 | differ.finish() | |
| 929 | ||
| 930 | def _show_diff(self, specific_files, extra_trees): | |
| 3009.2.6
by Aaron Bentley Convert show_diff_trees into a Differ method | 931 |         # TODO: Generation of pseudo-diffs for added/deleted files could
 | 
| 932 |         # be usefully made into a much faster special case.
 | |
| 3123.4.1
by Aaron Bentley Diff sorts files in alphabetical order | 933 | iterator = self.new_tree._iter_changes(self.old_tree, | 
| 934 | specific_files=specific_files, | |
| 935 | extra_trees=extra_trees, | |
| 936 | require_versioned=True) | |
| 3009.2.6
by Aaron Bentley Convert show_diff_trees into a Differ method | 937 | has_changes = 0 | 
| 3123.4.1
by Aaron Bentley Diff sorts files in alphabetical order | 938 | def changes_key(change): | 
| 939 | old_path, new_path = change[1] | |
| 940 | path = new_path | |
| 941 | if path is None: | |
| 942 | path = old_path | |
| 943 | return path | |
| 944 | def get_encoded_path(path): | |
| 945 | if path is not None: | |
| 946 | return path.encode(self.path_encoding, "replace") | |
| 947 | for (file_id, paths, changed_content, versioned, parent, name, kind, | |
| 948 | executable) in sorted(iterator, key=changes_key): | |
| 3123.4.3
by Aaron Bentley Tweak path handling | 949 | if parent == (None, None): | 
| 950 |                 continue
 | |
| 951 | oldpath, newpath = paths | |
| 3123.4.1
by Aaron Bentley Diff sorts files in alphabetical order | 952 | oldpath_encoded = get_encoded_path(paths[0]) | 
| 953 | newpath_encoded = get_encoded_path(paths[1]) | |
| 954 | old_present = (kind[0] is not None and versioned[0]) | |
| 955 | new_present = (kind[1] is not None and versioned[1]) | |
| 956 | renamed = (parent[0], name[0]) != (parent[1], name[1]) | |
| 957 | prop_str = get_prop_change(executable[0] != executable[1]) | |
| 958 | if (old_present, new_present) == (True, False): | |
| 959 | self.to_file.write("=== removed %s '%s'\n" % | |
| 960 | (kind[0], oldpath_encoded)) | |
| 3123.4.3
by Aaron Bentley Tweak path handling | 961 | newpath = oldpath | 
| 3123.4.1
by Aaron Bentley Diff sorts files in alphabetical order | 962 | elif (old_present, new_present) == (False, True): | 
| 963 | self.to_file.write("=== added %s '%s'\n" % | |
| 964 | (kind[1], newpath_encoded)) | |
| 3123.4.3
by Aaron Bentley Tweak path handling | 965 | oldpath = newpath | 
| 3123.4.1
by Aaron Bentley Diff sorts files in alphabetical order | 966 | elif renamed: | 
| 967 | self.to_file.write("=== renamed %s '%s' => '%s'%s\n" % | |
| 968 | (kind[0], oldpath_encoded, newpath_encoded, prop_str)) | |
| 3123.4.2
by Aaron Bentley Handle diff with property change correctly | 969 | else: | 
| 970 |                 # if it was produced by _iter_changes, it must be
 | |
| 971 |                 # modified *somehow*, either content or execute bit.
 | |
| 3123.4.1
by Aaron Bentley Diff sorts files in alphabetical order | 972 | self.to_file.write("=== modified %s '%s'%s\n" % (kind[0], | 
| 973 | newpath_encoded, prop_str)) | |
| 974 | if changed_content: | |
| 3123.4.3
by Aaron Bentley Tweak path handling | 975 | self.diff(file_id, oldpath, newpath) | 
| 3123.4.1
by Aaron Bentley Diff sorts files in alphabetical order | 976 | has_changes = 1 | 
| 977 | if renamed: | |
| 978 | has_changes = 1 | |
| 3009.2.6
by Aaron Bentley Convert show_diff_trees into a Differ method | 979 | return has_changes | 
| 3009.2.2
by Aaron Bentley Implement Differ object for abstracting diffing | 980 | |
| 3009.2.12
by Aaron Bentley Associate labels with text diffing only | 981 | def diff(self, file_id, old_path, new_path): | 
| 3009.2.17
by Aaron Bentley Update docs | 982 | """Perform a diff of a single file | 
| 983 | ||
| 984 |         :param file_id: file-id of the file
 | |
| 985 |         :param old_path: The path of the file in the old tree
 | |
| 986 |         :param new_path: The path of the file in the new tree
 | |
| 987 |         """
 | |
| 3009.2.2
by Aaron Bentley Implement Differ object for abstracting diffing | 988 | try: | 
| 3009.2.8
by Aaron Bentley Support diffing without indirecting through inventory entries | 989 | old_kind = self.old_tree.kind(file_id) | 
| 3087.1.1
by Aaron Bentley Diff handles missing files correctly, with no tracebacks | 990 | except (errors.NoSuchId, errors.NoSuchFile): | 
| 3009.2.8
by Aaron Bentley Support diffing without indirecting through inventory entries | 991 | old_kind = None | 
| 3009.2.3
by Aaron Bentley Detect missing files from inv operation | 992 | try: | 
| 3009.2.8
by Aaron Bentley Support diffing without indirecting through inventory entries | 993 | new_kind = self.new_tree.kind(file_id) | 
| 3087.1.1
by Aaron Bentley Diff handles missing files correctly, with no tracebacks | 994 | except (errors.NoSuchId, errors.NoSuchFile): | 
| 3009.2.8
by Aaron Bentley Support diffing without indirecting through inventory entries | 995 | new_kind = None | 
| 3009.2.7
by Aaron Bentley Move responsibility for generating diff labels into Differ.diff | 996 | |
| 3009.2.22
by Aaron Bentley Update names & docstring | 997 | result = DiffPath._diff_many(self.differs, file_id, old_path, | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 998 | new_path, old_kind, new_kind) | 
| 3009.2.22
by Aaron Bentley Update names & docstring | 999 | if result is DiffPath.CANNOT_DIFF: | 
| 3009.2.11
by Aaron Bentley Refactor diff to be more pluggable | 1000 | error_path = new_path | 
| 1001 | if error_path is None: | |
| 1002 | error_path = old_path | |
| 3009.2.22
by Aaron Bentley Update names & docstring | 1003 | raise errors.NoDiffFound(error_path) |