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