/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
6597.2.1 by Richard Wilbur
Split diff format option parser into a separate function, update to include all format options for GNU diff v3.2, and test parser.
1
# Copyright (C) 2005-2014 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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1 by mbp at sourcefrog
import from baz patch-364
16
6379.6.1 by Jelmer Vernooij
Import absolute_import in a few places.
17
from __future__ import absolute_import
18
2520.4.140 by Aaron Bentley
Use matching blocks from mpdiff for knit delta creation
19
import difflib
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
20
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
21
import re
4603.1.20 by Aaron Bentley
Use string.Template substitution with @ as delimiter.
22
import string
1996.3.9 by John Arbash Meinel
lazy_import diff.py
23
import sys
24
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
25
from .lazy_import import lazy_import
1996.3.9 by John Arbash Meinel
lazy_import diff.py
26
lazy_import(globals(), """
27
import errno
7290.14.1 by Jelmer Vernooij
Use external patiencediff.
28
import patiencediff
1692.8.7 by James Henstridge
changes suggested by John Meinel
29
import subprocess
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
30
import tempfile
1740.2.5 by Aaron Bentley
Merge from bzr.dev
31
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
32
from breezy import (
4913.5.24 by Gordon Tyler
Added cmdline.split function, which replaces commands.shlex_split_unicode.
33
    cmdline,
6207.3.3 by jelmer at samba
Fix tests and the like.
34
    controldir,
1955.2.10 by John Arbash Meinel
Unset a few other LANG type variables when spawning diff
35
    errors,
36
    osutils,
1996.3.9 by John Arbash Meinel
lazy_import diff.py
37
    textfile,
1551.12.29 by Aaron Bentley
Copy and extend patch date formatting code, add patch-date parsing
38
    timestamp,
3586.1.21 by Ian Clatworthy
enhance diff to support views
39
    views,
1955.2.10 by John Arbash Meinel
Unset a few other LANG type variables when spawning diff
40
    )
4845.2.1 by Gary van der Merwe
When launching an external diff app, don't write temporary files for a working tree.
41
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
42
from breezy.workingtree import WorkingTree
43
from breezy.i18n import gettext
1996.3.9 by John Arbash Meinel
lazy_import diff.py
44
""")
45
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
46
from .registry import (
5131.1.1 by Jelmer Vernooij
Add --format option to 'bzr diff'.
47
    Registry,
48
    )
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
49
from .sixish import text_type
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
50
from .trace import mutter, note, warning
6731.1.1 by Jelmer Vernooij
Move FileTimestampUnavailable to breezy.tree.
51
from .tree import FileTimestampUnavailable
52
1 by mbp at sourcefrog
import from baz patch-364
53
6524.5.8 by Paul Nixon
Changed "default_context_amount" to be "DEFAULT_CONTEXT_AMOUNT" to be
54
DEFAULT_CONTEXT_AMOUNT = 3
1711.2.24 by John Arbash Meinel
Late bind to PatienceSequenceMatcher to allow plugin to override.
55
7143.15.2 by Jelmer Vernooij
Run autopep8.
56
4603.1.20 by Aaron Bentley
Use string.Template substitution with @ as delimiter.
57
class AtTemplate(string.Template):
58
    """Templating class that uses @ instead of $."""
59
60
    delimiter = '@'
61
62
767 by Martin Pool
- files are only reported as modified if their name or parent has changed,
63
# TODO: Rather than building a changeset object, we should probably
64
# invoke callbacks on an object.  That object can either accumulate a
65
# list, write them out directly, etc etc.
66
2520.4.140 by Aaron Bentley
Use matching blocks from mpdiff for knit delta creation
67
68
class _PrematchedMatcher(difflib.SequenceMatcher):
69
    """Allow SequenceMatcher operations to use predetermined blocks"""
70
71
    def __init__(self, matching_blocks):
72
        difflib.SequenceMatcher(self, None, None)
73
        self.matching_blocks = matching_blocks
74
        self.opcodes = None
75
76
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
77
def internal_diff(old_label, oldlines, new_label, newlines, to_file,
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
78
                  allow_binary=False, sequence_matcher=None,
6524.5.8 by Paul Nixon
Changed "default_context_amount" to be "DEFAULT_CONTEXT_AMOUNT" to be
79
                  path_encoding='utf8', context_lines=DEFAULT_CONTEXT_AMOUNT):
475 by Martin Pool
- rewrite diff using compare_trees()
80
    # FIXME: difflib is wrong if there is no trailing newline.
81
    # The syntax used by patch seems to be "\ No newline at
82
    # end of file" following the last diff line from that
83
    # file.  This is not trivial to insert into the
84
    # unified_diff output and it might be better to just fix
85
    # or replace that function.
86
87
    # In the meantime we at least make sure the patch isn't
88
    # mangled.
89
1558.15.11 by Aaron Bentley
Apply merge review suggestions
90
    if allow_binary is False:
1996.3.9 by John Arbash Meinel
lazy_import diff.py
91
        textfile.check_text_lines(oldlines)
92
        textfile.check_text_lines(newlines)
475 by Martin Pool
- rewrite diff using compare_trees()
93
1185.81.8 by John Arbash Meinel
Updating unified_diff to take a factory, using the new diff algorithm in the code.
94
    if sequence_matcher is None:
1996.3.9 by John Arbash Meinel
lazy_import diff.py
95
        sequence_matcher = patiencediff.PatienceSequenceMatcher
7290.14.1 by Jelmer Vernooij
Use external patiencediff.
96
    ud = unified_diff_bytes(
7290.14.5 by Jelmer Vernooij
Fix formatting.
97
        oldlines, newlines,
98
        fromfile=old_label.encode(path_encoding, 'replace'),
99
        tofile=new_label.encode(path_encoding, 'replace'),
100
        n=context_lines, sequencematcher=sequence_matcher)
475 by Martin Pool
- rewrite diff using compare_trees()
101
1092.1.50 by Robert Collins
make diff lsdiff/filterdiff friendly
102
    ud = list(ud)
7143.15.2 by Jelmer Vernooij
Run autopep8.
103
    if len(ud) == 0:  # Identical contents, nothing to do
3085.1.1 by John Arbash Meinel
Fix internal_diff to not fail when the texts are identical.
104
        return
475 by Martin Pool
- rewrite diff using compare_trees()
105
    # work-around for difflib being too smart for its own good
106
    # if /dev/null is "1,0", patch won't recognize it as /dev/null
107
    if not oldlines:
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
108
        ud[2] = ud[2].replace(b'-1,0', b'-0,0')
475 by Martin Pool
- rewrite diff using compare_trees()
109
    elif not newlines:
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
110
        ud[2] = ud[2].replace(b'+1,0', b'+0,0')
475 by Martin Pool
- rewrite diff using compare_trees()
111
804 by Martin Pool
Patch from John:
112
    for line in ud:
113
        to_file.write(line)
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
114
        if not line.endswith(b'\n'):
115
            to_file.write(b"\n\\ No newline at end of file\n")
116
    to_file.write(b'\n')
475 by Martin Pool
- rewrite diff using compare_trees()
117
118
7290.14.1 by Jelmer Vernooij
Use external patiencediff.
119
def unified_diff_bytes(a, b, fromfile=b'', tofile=b'', fromfiledate=b'',
120
                       tofiledate=b'', n=3, lineterm=b'\n', sequencematcher=None):
121
    r"""
122
    Compare two sequences of lines; generate the delta as a unified diff.
123
124
    Unified diffs are a compact way of showing line changes and a few
125
    lines of context.  The number of context lines is set by 'n' which
126
    defaults to three.
127
128
    By default, the diff control lines (those with ---, +++, or @@) are
129
    created with a trailing newline.  This is helpful so that inputs
130
    created from file.readlines() result in diffs that are suitable for
131
    file.writelines() since both the inputs and outputs have trailing
132
    newlines.
133
134
    For inputs that do not have trailing newlines, set the lineterm
135
    argument to "" so that the output will be uniformly newline free.
136
137
    The unidiff format normally has a header for filenames and modification
138
    times.  Any or all of these may be specified using strings for
139
    'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.  The modification
140
    times are normally expressed in the format returned by time.ctime().
141
142
    Example:
143
144
    >>> for line in bytes_unified_diff(b'one two three four'.split(),
145
    ...             b'zero one tree four'.split(), b'Original', b'Current',
146
    ...             b'Sat Jan 26 23:30:50 1991', b'Fri Jun 06 10:20:52 2003',
147
    ...             lineterm=b''):
148
    ...     print line
149
    --- Original Sat Jan 26 23:30:50 1991
150
    +++ Current Fri Jun 06 10:20:52 2003
151
    @@ -1,4 +1,4 @@
152
    +zero
153
     one
154
    -two
155
    -three
156
    +tree
157
     four
158
    """
159
    if sequencematcher is None:
160
        sequencematcher = difflib.SequenceMatcher
161
162
    if fromfiledate:
163
        fromfiledate = b'\t' + bytes(fromfiledate)
164
    if tofiledate:
165
        tofiledate = b'\t' + bytes(tofiledate)
166
167
    started = False
168
    for group in sequencematcher(None, a, b).get_grouped_opcodes(n):
169
        if not started:
170
            yield b'--- %s%s%s' % (fromfile, fromfiledate, lineterm)
171
            yield b'+++ %s%s%s' % (tofile, tofiledate, lineterm)
172
            started = True
173
        i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4]
174
        yield b"@@ -%d,%d +%d,%d @@%s" % (i1 + 1, i2 - i1, j1 + 1, j2 - j1, lineterm)
175
        for tag, i1, i2, j1, j2 in group:
176
            if tag == 'equal':
177
                for line in a[i1:i2]:
178
                    yield b' ' + line
179
                continue
180
            if tag == 'replace' or tag == 'delete':
181
                for line in a[i1:i2]:
182
                    yield b'-' + line
183
            if tag == 'replace' or tag == 'insert':
184
                for line in b[j1:j2]:
185
                    yield b'+' + line
186
187
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
188
def _spawn_external_diff(diffcmd, capture_errors=True):
6597.2.1 by Richard Wilbur
Split diff format option parser into a separate function, update to include all format options for GNU diff v3.2, and test parser.
189
    """Spawn the external diff process, and return the child handle.
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
190
191
    :param diffcmd: The command list to spawn
2138.1.1 by Wouter van Heyst
Robuster external diff output handling.
192
    :param capture_errors: Capture stderr as well as setting LANG=C
193
        and LC_ALL=C. This lets us read and understand the output of diff,
194
        and respond to any errors.
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
195
    :return: A Popen object.
196
    """
197
    if capture_errors:
2321.2.2 by Alexander Belchenko
win32 fixes for test_external_diff_binary (gettext on win32 rely on $LANGUAGE)
198
        # construct minimal environment
199
        env = {}
200
        path = os.environ.get('PATH')
201
        if path is not None:
202
            env['PATH'] = path
2321.2.5 by Alexander Belchenko
external diff: no need for special code path for win32 (suggested by John Meinel)
203
        env['LANGUAGE'] = 'C'   # on win32 only LANGUAGE has effect
204
        env['LANG'] = 'C'
205
        env['LC_ALL'] = 'C'
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
206
        stderr = subprocess.PIPE
207
    else:
2321.2.2 by Alexander Belchenko
win32 fixes for test_external_diff_binary (gettext on win32 rely on $LANGUAGE)
208
        env = None
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
209
        stderr = None
210
211
    try:
212
        pipe = subprocess.Popen(diffcmd,
213
                                stdin=subprocess.PIPE,
214
                                stdout=subprocess.PIPE,
215
                                stderr=stderr,
2321.2.2 by Alexander Belchenko
win32 fixes for test_external_diff_binary (gettext on win32 rely on $LANGUAGE)
216
                                env=env)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
217
    except OSError as e:
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
218
        if e.errno == errno.ENOENT:
219
            raise errors.NoDiff(str(e))
220
        raise
221
222
    return pipe
223
7143.15.2 by Jelmer Vernooij
Run autopep8.
224
6597.2.1 by Richard Wilbur
Split diff format option parser into a separate function, update to include all format options for GNU diff v3.2, and test parser.
225
# diff style options as of GNU diff v3.2
226
style_option_list = ['-c', '-C', '--context',
227
                     '-e', '--ed',
6597.2.4 by Vincent Ladeuil
Doh, this is *still* supported by diff 3.3 (just not documented in the man page nor the online help anymore).
228
                     '-f', '--forward-ed',
6597.2.1 by Richard Wilbur
Split diff format option parser into a separate function, update to include all format options for GNU diff v3.2, and test parser.
229
                     '-q', '--brief',
230
                     '--normal',
231
                     '-n', '--rcs',
232
                     '-u', '-U', '--unified',
233
                     '-y', '--side-by-side',
234
                     '-D', '--ifdef']
235
7143.15.2 by Jelmer Vernooij
Run autopep8.
236
6597.2.1 by Richard Wilbur
Split diff format option parser into a separate function, update to include all format options for GNU diff v3.2, and test parser.
237
def default_style_unified(diff_opts):
238
    """Default to unified diff style if alternative not specified in diff_opts.
239
240
        diff only allows one style to be specified; they don't override.
241
        Note that some of these take optargs, and the optargs can be
242
        directly appended to the options.
243
        This is only an approximate parser; it doesn't properly understand
244
        the grammar.
245
246
    :param diff_opts: List of options for external (GNU) diff.
247
    :return: List of options with default style=='unified'.
248
    """
249
    for s in style_option_list:
250
        for j in diff_opts:
251
            if j.startswith(s):
252
                break
253
        else:
254
            continue
255
        break
256
    else:
257
        diff_opts.append('-u')
258
    return diff_opts
259
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
260
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
261
def external_diff(old_label, oldlines, new_label, newlines, to_file,
571 by Martin Pool
- new --diff-options to pass options through to external
262
                  diff_opts):
568 by Martin Pool
- start adding support for showing diffs by calling out to
263
    """Display a diff by calling out to the external diff program."""
581 by Martin Pool
- make sure any bzr output is flushed before
264
    # make sure our own output is properly ordered before the diff
265
    to_file.flush()
266
6681.2.4 by Jelmer Vernooij
More renames.
267
    oldtmp_fd, old_abspath = tempfile.mkstemp(prefix='brz-diff-old-')
268
    newtmp_fd, new_abspath = tempfile.mkstemp(prefix='brz-diff-new-')
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
269
    oldtmpf = os.fdopen(oldtmp_fd, 'wb')
270
    newtmpf = os.fdopen(newtmp_fd, 'wb')
568 by Martin Pool
- start adding support for showing diffs by calling out to
271
272
    try:
273
        # TODO: perhaps a special case for comparing to or from the empty
274
        # sequence; can just use /dev/null on Unix
275
276
        # TODO: if either of the files being compared already exists as a
277
        # regular named file (e.g. in the working directory) then we can
278
        # compare directly to that, rather than copying it.
279
280
        oldtmpf.writelines(oldlines)
281
        newtmpf.writelines(newlines)
282
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
283
        oldtmpf.close()
284
        newtmpf.close()
568 by Martin Pool
- start adding support for showing diffs by calling out to
285
571 by Martin Pool
- new --diff-options to pass options through to external
286
        if not diff_opts:
287
            diff_opts = []
4422.1.1 by John Arbash Meinel
Possibly fix for bug #382709 handling non-ascii external filenames.
288
        if sys.platform == 'win32':
289
            # Popen doesn't do the proper encoding for external commands
290
            # Since we are dealing with an ANSI api, use mbcs encoding
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
291
            old_label = old_label.encode('mbcs')
292
            new_label = new_label.encode('mbcs')
571 by Martin Pool
- new --diff-options to pass options through to external
293
        diffcmd = ['diff',
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
294
                   '--label', old_label,
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
295
                   old_abspath,
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
296
                   '--label', new_label,
1711.2.56 by John Arbash Meinel
Raise NoDiff if 'diff' not present.
297
                   new_abspath,
298
                   '--binary',
7143.15.2 by Jelmer Vernooij
Run autopep8.
299
                   ]
571 by Martin Pool
- new --diff-options to pass options through to external
300
6597.2.1 by Richard Wilbur
Split diff format option parser into a separate function, update to include all format options for GNU diff v3.2, and test parser.
301
        diff_opts = default_style_unified(diff_opts)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
302
571 by Martin Pool
- new --diff-options to pass options through to external
303
        if diff_opts:
304
            diffcmd.extend(diff_opts)
305
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
306
        pipe = _spawn_external_diff(diffcmd, capture_errors=True)
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
307
        out, err = pipe.communicate()
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
308
        rc = pipe.returncode
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
309
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
310
        # internal_diff() adds a trailing newline, add one here for consistency
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
311
        out += b'\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
312
        if rc == 2:
313
            # 'diff' gives retcode == 2 for all sorts of errors
314
            # one of those is 'Binary files differ'.
315
            # 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.
316
            # '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
317
            lang_c_out = out
318
319
            # Since we got here, we want to make sure to give an i18n error
320
            pipe = _spawn_external_diff(diffcmd, capture_errors=False)
321
            out, err = pipe.communicate()
322
323
            # Write out the new i18n diff response
7143.15.2 by Jelmer Vernooij
Run autopep8.
324
            to_file.write(out + b'\n')
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
325
            if pipe.returncode != 2:
1996.3.9 by John Arbash Meinel
lazy_import diff.py
326
                raise errors.BzrError(
7143.15.2 by Jelmer Vernooij
Run autopep8.
327
                    'external diff failed with exit code 2'
328
                    ' when run with LANG=C and LC_ALL=C,'
329
                    ' 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
330
7031.1.4 by Jelmer Vernooij
Fix tests.
331
            first_line = lang_c_out.split(b'\n', 1)[0]
1904.1.4 by Marien Zwart
Make external diff in binary mode work with recent versions of diffutils.
332
            # Starting with diffutils 2.8.4 the word "binary" was dropped.
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
333
            m = re.match(b'^(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
334
            if m is None:
1996.3.9 by John Arbash Meinel
lazy_import diff.py
335
                raise errors.BzrError('external diff failed with exit code 2;'
336
                                      ' command: %r' % (diffcmd,))
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
337
            else:
338
                # Binary files differ, just return
339
                return
340
341
        # If we got to here, we haven't written out the output of diff
342
        # do so now
343
        to_file.write(out)
344
        if rc not in (0, 1):
571 by Martin Pool
- new --diff-options to pass options through to external
345
            # returns 1 if files differ; that's OK
346
            if rc < 0:
347
                msg = 'signal %d' % (-rc)
348
            else:
349
                msg = 'exit code %d' % rc
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
350
351
            raise errors.BzrError('external diff failed with %s; command: %r'
6597.2.1 by Richard Wilbur
Split diff format option parser into a separate function, update to include all format options for GNU diff v3.2, and test parser.
352
                                  % (msg, diffcmd))
1899.1.6 by John Arbash Meinel
internal_diff always adds a trailing \n, make sure external_diff does too
353
568 by Martin Pool
- start adding support for showing diffs by calling out to
354
    finally:
355
        oldtmpf.close()                 # and delete
356
        newtmpf.close()
6597.2.3 by Vincent Ladeuil
Remove duplication in cleanups. Note that --forward-ed is not in diff 3.3 and may need to be removed from the supported options.
357
358
        def cleanup(path):
359
            # Warn in case the file couldn't be deleted (in case windows still
360
            # holds the file open, but not if the files have already been
361
            # deleted)
362
            try:
363
                os.remove(path)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
364
            except OSError as e:
6597.2.3 by Vincent Ladeuil
Remove duplication in cleanups. Note that --forward-ed is not in diff 3.3 and may need to be removed from the supported options.
365
                if e.errno not in (errno.ENOENT,):
366
                    warning('Failed to delete temporary file: %s %s', path, e)
367
368
        cleanup(old_abspath)
369
        cleanup(new_abspath)
568 by Martin Pool
- start adding support for showing diffs by calling out to
370
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
371
5147.3.3 by Andrew Bennetts
Add get_trees_and_branches_to_diff_locked, leave get_trees_and_branches_to_diff unchanged for qbzr.
372
def get_trees_and_branches_to_diff_locked(
7143.15.2 by Jelmer Vernooij
Run autopep8.
373
        path_list, revision_specs, old_url, new_url, add_cleanup, apply_view=True):
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
374
    """Get the trees and specific files to diff given a list of paths.
375
376
    This method works out the trees to be diff'ed and the files of
377
    interest within those trees.
378
379
    :param path_list:
380
        the list of arguments passed to the diff command
381
    :param revision_specs:
382
        Zero, one or two RevisionSpecs from the diff command line,
383
        saying what revisions to compare.
384
    :param old_url:
385
        The url of the old branch or tree. If None, the tree to use is
386
        taken from the first path, if any, or the current working tree.
387
    :param new_url:
388
        The url of the new branch or tree. If None, the tree to use is
389
        taken from the first path, if any, or the current working tree.
5147.3.1 by Andrew Bennetts
Avoid 6 branch/repo relocks in cmd_diff.
390
    :param add_cleanup:
391
        a callable like Command.add_cleanup.  get_trees_and_branches_to_diff
392
        will register cleanups that must be run to unlock the trees, etc.
3586.1.21 by Ian Clatworthy
enhance diff to support views
393
    :param apply_view:
394
        if True and a view is set, apply the view or check that the paths
395
        are within it
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
396
    :returns:
4739.3.1 by Jonathan Lange
Fix the docstring for get_trees_and_branches_to_diff.
397
        a tuple of (old_tree, new_tree, old_branch, new_branch,
398
        specific_files, extra_trees) where extra_trees is a sequence of
5147.3.1 by Andrew Bennetts
Avoid 6 branch/repo relocks in cmd_diff.
399
        additional trees to search in for file-ids.  The trees and branches
400
        will be read-locked until the cleanups registered via the add_cleanup
401
        param are run.
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
402
    """
403
    # Get the old and new revision specs
404
    old_revision_spec = None
405
    new_revision_spec = None
406
    if revision_specs is not None:
407
        if len(revision_specs) > 0:
408
            old_revision_spec = revision_specs[0]
3072.1.5 by Ian Clatworthy
more good ideas from abentley
409
            if old_url is None:
410
                old_url = old_revision_spec.get_branch()
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
411
        if len(revision_specs) > 1:
412
            new_revision_spec = revision_specs[1]
3072.1.5 by Ian Clatworthy
more good ideas from abentley
413
            if new_url is None:
414
                new_url = new_revision_spec.get_branch()
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
415
3072.1.5 by Ian Clatworthy
more good ideas from abentley
416
    other_paths = []
417
    make_paths_wt_relative = True
3164.1.1 by Ian Clatworthy
diff without arguments means the current tree, not the current directory
418
    consider_relpath = True
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
419
    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
420
        # If no path is given, the current working tree is used
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
421
        default_location = u'.'
3164.1.1 by Ian Clatworthy
diff without arguments means the current tree, not the current directory
422
        consider_relpath = False
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
423
    elif old_url is not None and new_url is not None:
424
        other_paths = path_list
3072.1.5 by Ian Clatworthy
more good ideas from abentley
425
        make_paths_wt_relative = False
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
426
    else:
427
        default_location = path_list[0]
428
        other_paths = path_list[1:]
429
5147.3.1 by Andrew Bennetts
Avoid 6 branch/repo relocks in cmd_diff.
430
    def lock_tree_or_branch(wt, br):
431
        if wt is not None:
432
            wt.lock_read()
433
            add_cleanup(wt.unlock)
434
        elif br is not None:
435
            br.lock_read()
436
            add_cleanup(br.unlock)
437
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
438
    # Get the old location
3072.1.2 by Ian Clatworthy
Test various --old and --new combinations
439
    specific_files = []
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
440
    if old_url is None:
441
        old_url = default_location
442
    working_tree, branch, relpath = \
6207.3.3 by jelmer at samba
Fix tests and the like.
443
        controldir.ControlDir.open_containing_tree_or_branch(old_url)
5147.3.1 by Andrew Bennetts
Avoid 6 branch/repo relocks in cmd_diff.
444
    lock_tree_or_branch(working_tree, branch)
3164.1.1 by Ian Clatworthy
diff without arguments means the current tree, not the current directory
445
    if consider_relpath and relpath != '':
3586.1.21 by Ian Clatworthy
enhance diff to support views
446
        if working_tree is not None and apply_view:
4032.4.1 by Eduardo Padoan
Moved diff._check_path_in_view() to views.check_path_in_view()
447
            views.check_path_in_view(working_tree, relpath)
3072.1.2 by Ian Clatworthy
Test various --old and --new combinations
448
        specific_files.append(relpath)
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
449
    old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
4705.1.1 by Gary van der Merwe
Change _get_trees_to_diff to get_trees_and_branches_to_diff.
450
    old_branch = branch
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
451
452
    # Get the new location
453
    if new_url is None:
454
        new_url = default_location
455
    if new_url != old_url:
456
        working_tree, branch, relpath = \
6207.3.3 by jelmer at samba
Fix tests and the like.
457
            controldir.ControlDir.open_containing_tree_or_branch(new_url)
5147.3.1 by Andrew Bennetts
Avoid 6 branch/repo relocks in cmd_diff.
458
        lock_tree_or_branch(working_tree, branch)
3164.1.1 by Ian Clatworthy
diff without arguments means the current tree, not the current directory
459
        if consider_relpath and relpath != '':
3586.1.21 by Ian Clatworthy
enhance diff to support views
460
            if working_tree is not None and apply_view:
4032.4.1 by Eduardo Padoan
Moved diff._check_path_in_view() to views.check_path_in_view()
461
                views.check_path_in_view(working_tree, relpath)
3072.1.2 by Ian Clatworthy
Test various --old and --new combinations
462
            specific_files.append(relpath)
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
463
    new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
7143.15.2 by Jelmer Vernooij
Run autopep8.
464
                                 basis_is_default=working_tree is None)
4705.1.1 by Gary van der Merwe
Change _get_trees_to_diff to get_trees_and_branches_to_diff.
465
    new_branch = branch
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
466
3072.1.2 by Ian Clatworthy
Test various --old and --new combinations
467
    # Get the specific files (all files is None, no files is [])
3072.1.5 by Ian Clatworthy
more good ideas from abentley
468
    if make_paths_wt_relative and working_tree is not None:
5346.4.3 by Martin Pool
PathNotChild should not give a traceback.
469
        other_paths = working_tree.safe_relpath_files(
470
            other_paths,
471
            apply_view=apply_view)
3072.1.2 by Ian Clatworthy
Test various --old and --new combinations
472
    specific_files.extend(other_paths)
473
    if len(specific_files) == 0:
474
        specific_files = None
7143.15.2 by Jelmer Vernooij
Run autopep8.
475
        if (working_tree is not None and working_tree.supports_views() and
476
                apply_view):
3586.1.21 by Ian Clatworthy
enhance diff to support views
477
            view_files = working_tree.views.lookup_view()
478
            if view_files:
479
                specific_files = view_files
480
                view_str = views.view_display_str(view_files)
6138.3.4 by Jonathan Riddell
add gettext() to uses of trace.note()
481
                note(gettext("*** Ignoring files outside view. View is %s") % view_str)
3072.1.2 by Ian Clatworthy
Test various --old and --new combinations
482
483
    # Get extra trees that ought to be searched for file-ids
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
484
    extra_trees = None
3072.1.5 by Ian Clatworthy
more good ideas from abentley
485
    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
486
        extra_trees = (working_tree,)
6027.1.4 by Vincent Ladeuil
Remove ``diff.get_trees_and_branches_to_diff`` deprecated in 2.2.0 and the corrsponding tests.
487
    return (old_tree, new_tree, old_branch, new_branch,
488
            specific_files, extra_trees)
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
489
4739.3.1 by Jonathan Lange
Fix the docstring for get_trees_and_branches_to_diff.
490
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
491
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
492
    if branch is None and tree is not None:
493
        branch = tree.branch
494
    if spec is None or spec.spec is None:
495
        if basis_is_default:
3072.1.5 by Ian Clatworthy
more good ideas from abentley
496
            if tree is not None:
497
                return tree.basis_tree()
498
            else:
499
                return branch.basis_tree()
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
500
        else:
501
            return tree
3655.3.1 by Lukáš Lalinský
Fix `bzr st -rbranch:PATH_TO_BRANCH`
502
    return spec.as_tree(branch)
3072.1.1 by Ian Clatworthy
Improved diff based on feedback from abentley
503
504
571 by Martin Pool
- new --diff-options to pass options through to external
505
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)
506
                    external_diff_options=None,
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
507
                    old_label='a/', new_label='b/',
2598.6.12 by ghigo
Move the encoding of the commit message at the command line level
508
                    extra_trees=None,
3123.6.2 by Aaron Bentley
Implement diff --using natively
509
                    path_encoding='utf8',
5131.1.1 by Jelmer Vernooij
Add --format option to 'bzr diff'.
510
                    using=None,
6524.5.1 by pauljnixon at gmail
Made diffs more configurable
511
                    format_cls=None,
6524.5.8 by Paul Nixon
Changed "default_context_amount" to be "DEFAULT_CONTEXT_AMOUNT" to be
512
                    context=DEFAULT_CONTEXT_AMOUNT):
550 by Martin Pool
- Refactor diff code into one that works purely on
513
    """Show in text form the changes from one tree to another.
514
5131.1.1 by Jelmer Vernooij
Add --format option to 'bzr diff'.
515
    :param to_file: The output stream.
5891.1.3 by Andrew Bennetts
Move docstring formatting fixes.
516
    :param specific_files: Include only changes to these files - None for all
5131.1.1 by Jelmer Vernooij
Add --format option to 'bzr diff'.
517
        changes.
7195.5.1 by Martin
Fix remaining whitespace lint in codebase
518
    :param external_diff_options: If set, use an external GNU diff and pass
5131.1.1 by Jelmer Vernooij
Add --format option to 'bzr diff'.
519
        these options.
520
    :param extra_trees: If set, more Trees to use for looking up file ids
7195.5.1 by Martin
Fix remaining whitespace lint in codebase
521
    :param path_encoding: If set, the path will be encoded as specified,
5131.1.1 by Jelmer Vernooij
Add --format option to 'bzr diff'.
522
        otherwise is supposed to be utf8
523
    :param format_cls: Formatter class (DiffTree subclass)
550 by Martin Pool
- Refactor diff code into one that works purely on
524
    """
6524.5.4 by Paul Nixon
Clearing up default context options
525
    if context is None:
6524.5.8 by Paul Nixon
Changed "default_context_amount" to be "DEFAULT_CONTEXT_AMOUNT" to be
526
        context = DEFAULT_CONTEXT_AMOUNT
5131.1.1 by Jelmer Vernooij
Add --format option to 'bzr diff'.
527
    if format_cls is None:
528
        format_cls = DiffTree
6754.8.4 by Jelmer Vernooij
Use new context stuff.
529
    with old_tree.lock_read():
2255.7.38 by John Arbash Meinel
show_diff_trees() should lock any extra trees it is passed.
530
        if extra_trees is not None:
531
            for tree in extra_trees:
532
                tree.lock_read()
1543.1.1 by Denys Duchier
lock operations for trees - use them for diff
533
        new_tree.lock_read()
534
        try:
5131.1.1 by Jelmer Vernooij
Add --format option to 'bzr diff'.
535
            differ = format_cls.from_trees_options(old_tree, new_tree, to_file,
536
                                                   path_encoding,
537
                                                   external_diff_options,
6524.5.1 by pauljnixon at gmail
Made diffs more configurable
538
                                                   old_label, new_label, using,
539
                                                   context_lines=context)
3009.2.12 by Aaron Bentley
Associate labels with text diffing only
540
            return differ.show_diff(specific_files, extra_trees)
1543.1.1 by Denys Duchier
lock operations for trees - use them for diff
541
        finally:
542
            new_tree.unlock()
2255.7.38 by John Arbash Meinel
show_diff_trees() should lock any extra trees it is passed.
543
            if extra_trees is not None:
544
                for tree in extra_trees:
545
                    tree.unlock()
1543.1.1 by Denys Duchier
lock operations for trees - use them for diff
546
547
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
548
def _patch_header_date(tree, path):
1740.2.5 by Aaron Bentley
Merge from bzr.dev
549
    """Returns a timestamp suitable for use in a patch header."""
4976.1.3 by Jelmer Vernooij
Cope with ghosts in 'bzr diff'
550
    try:
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
551
        mtime = tree.get_file_mtime(path)
6731.1.1 by Jelmer Vernooij
Move FileTimestampUnavailable to breezy.tree.
552
    except FileTimestampUnavailable:
4976.1.3 by Jelmer Vernooij
Cope with ghosts in 'bzr diff'
553
        mtime = 0
2405.1.2 by John Arbash Meinel
Fix bug #103870 by passing None instead of a (sometimes wrong) path
554
    return timestamp.format_patch_date(mtime)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
555
556
3268.1.1 by C Miller
Describe the property changes in diffs. Currently, this is the executable-bit
557
def get_executable_change(old_is_x, new_is_x):
7143.15.2 by Jelmer Vernooij
Run autopep8.
558
    descr = {True: b"+x", False: b"-x", None: b"??"}
3268.1.1 by C Miller
Describe the property changes in diffs. Currently, this is the executable-bit
559
    if old_is_x != new_is_x:
7029.4.2 by Jelmer Vernooij
Fix more merge tests.
560
        return [b"%s to %s" % (descr[old_is_x], descr[new_is_x],)]
3268.1.1 by C Miller
Describe the property changes in diffs. Currently, this is the executable-bit
561
    else:
562
        return []
563
1398 by Robert Collins
integrate in Gustavos x-bit patch
564
3009.2.22 by Aaron Bentley
Update names & docstring
565
class DiffPath(object):
3009.2.14 by Aaron Bentley
Update return type handling
566
    """Base type for command object that compare files"""
3009.2.17 by Aaron Bentley
Update docs
567
3009.2.14 by Aaron Bentley
Update return type handling
568
    # The type or contents of the file were unsuitable for diffing
3009.2.29 by Aaron Bentley
Change constants to strings
569
    CANNOT_DIFF = 'CANNOT_DIFF'
3009.2.14 by Aaron Bentley
Update return type handling
570
    # The file has changed in a semantic way
3009.2.29 by Aaron Bentley
Change constants to strings
571
    CHANGED = 'CHANGED'
572
    # The file content may have changed, but there is no semantic change
573
    UNCHANGED = 'UNCHANGED'
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
574
3009.2.13 by Aaron Bentley
Refactor differ to support registering differ factories
575
    def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8'):
3009.2.17 by Aaron Bentley
Update docs
576
        """Constructor.
577
578
        :param old_tree: The tree to show as the old tree in the comparison
579
        :param new_tree: The tree to show as new in the comparison
580
        :param to_file: The file to write comparison data to
581
        :param path_encoding: The character encoding to write paths in
582
        """
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
583
        self.old_tree = old_tree
584
        self.new_tree = new_tree
585
        self.to_file = to_file
3009.2.13 by Aaron Bentley
Refactor differ to support registering differ factories
586
        self.path_encoding = path_encoding
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
587
3123.6.2 by Aaron Bentley
Implement diff --using natively
588
    def finish(self):
589
        pass
590
3009.2.28 by Aaron Bentley
Add from_diff_tree factories
591
    @classmethod
592
    def from_diff_tree(klass, diff_tree):
593
        return klass(diff_tree.old_tree, diff_tree.new_tree,
594
                     diff_tree.to_file, diff_tree.path_encoding)
595
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
596
    @staticmethod
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
597
    def _diff_many(differs, old_path, new_path, old_kind, new_kind):
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
598
        for file_differ in differs:
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
599
            result = file_differ.diff(old_path, new_path, old_kind, new_kind)
3009.2.22 by Aaron Bentley
Update names & docstring
600
            if result is not DiffPath.CANNOT_DIFF:
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
601
                return result
602
        else:
3009.2.22 by Aaron Bentley
Update names & docstring
603
            return DiffPath.CANNOT_DIFF
604
605
606
class DiffKindChange(object):
3009.2.17 by Aaron Bentley
Update docs
607
    """Special differ for file kind changes.
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
608
3009.2.17 by Aaron Bentley
Update docs
609
    Represents kind change as deletion + creation.  Uses the other differs
610
    to do this.
611
    """
7143.15.2 by Jelmer Vernooij
Run autopep8.
612
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
613
    def __init__(self, differs):
614
        self.differs = differs
615
3123.6.2 by Aaron Bentley
Implement diff --using natively
616
    def finish(self):
617
        pass
618
3009.2.28 by Aaron Bentley
Add from_diff_tree factories
619
    @classmethod
620
    def from_diff_tree(klass, diff_tree):
621
        return klass(diff_tree.differs)
622
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
623
    def diff(self, old_path, new_path, old_kind, new_kind):
3009.2.17 by Aaron Bentley
Update docs
624
        """Perform comparison
625
626
        :param old_path: Path of the file in the old tree
627
        :param new_path: Path of the file in the new tree
628
        :param old_kind: Old file-kind of the file
629
        :param new_kind: New file-kind of the file
630
        """
3009.2.18 by Aaron Bentley
Change KindChangeDiffer's anti-recursion to avoid kind pairs with None
631
        if None in (old_kind, new_kind):
3009.2.22 by Aaron Bentley
Update names & docstring
632
            return DiffPath.CANNOT_DIFF
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
633
        result = DiffPath._diff_many(
634
            self.differs, old_path, new_path, old_kind, None)
3009.2.22 by Aaron Bentley
Update names & docstring
635
        if result is DiffPath.CANNOT_DIFF:
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
636
            return result
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
637
        return DiffPath._diff_many(
638
            self.differs, old_path, new_path, None, new_kind)
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
639
640
3009.2.22 by Aaron Bentley
Update names & docstring
641
class DiffDirectory(DiffPath):
3009.2.19 by Aaron Bentley
Implement directory diffing
642
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
643
    def diff(self, old_path, new_path, old_kind, new_kind):
3009.2.19 by Aaron Bentley
Implement directory diffing
644
        """Perform comparison between two directories.  (dummy)
645
646
        """
647
        if 'directory' not in (old_kind, new_kind):
648
            return self.CANNOT_DIFF
649
        if old_kind not in ('directory', None):
650
            return self.CANNOT_DIFF
651
        if new_kind not in ('directory', None):
652
            return self.CANNOT_DIFF
653
        return self.CHANGED
654
3009.2.20 by Aaron Bentley
PEP8
655
3009.2.22 by Aaron Bentley
Update names & docstring
656
class DiffSymlink(DiffPath):
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
657
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
658
    def diff(self, old_path, new_path, old_kind, new_kind):
3009.2.17 by Aaron Bentley
Update docs
659
        """Perform comparison between two symlinks
660
661
        :param old_path: Path of the file in the old tree
662
        :param new_path: Path of the file in the new tree
663
        :param old_kind: Old file-kind of the file
664
        :param new_kind: New file-kind of the file
665
        """
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
666
        if 'symlink' not in (old_kind, new_kind):
3009.2.14 by Aaron Bentley
Update return type handling
667
            return self.CANNOT_DIFF
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
668
        if old_kind == 'symlink':
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
669
            old_target = self.old_tree.get_symlink_target(old_path)
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
670
        elif old_kind is None:
671
            old_target = None
672
        else:
3009.2.14 by Aaron Bentley
Update return type handling
673
            return self.CANNOT_DIFF
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
674
        if new_kind == 'symlink':
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
675
            new_target = self.new_tree.get_symlink_target(new_path)
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
676
        elif new_kind is None:
677
            new_target = None
678
        else:
3009.2.14 by Aaron Bentley
Update return type handling
679
            return self.CANNOT_DIFF
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
680
        return self.diff_symlink(old_target, new_target)
681
682
    def diff_symlink(self, old_target, new_target):
683
        if old_target is None:
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
684
            self.to_file.write(b'=== target is \'%s\'\n' %
7143.15.2 by Jelmer Vernooij
Run autopep8.
685
                               new_target.encode(self.path_encoding, 'replace'))
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
686
        elif new_target is None:
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
687
            self.to_file.write(b'=== target was \'%s\'\n' %
7143.15.2 by Jelmer Vernooij
Run autopep8.
688
                               old_target.encode(self.path_encoding, 'replace'))
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
689
        else:
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
690
            self.to_file.write(b'=== target changed \'%s\' => \'%s\'\n' %
7143.15.2 by Jelmer Vernooij
Run autopep8.
691
                               (old_target.encode(self.path_encoding, 'replace'),
692
                                new_target.encode(self.path_encoding, 'replace')))
3009.2.14 by Aaron Bentley
Update return type handling
693
        return self.CHANGED
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
694
695
3009.2.22 by Aaron Bentley
Update names & docstring
696
class DiffText(DiffPath):
3009.2.2 by Aaron Bentley
Implement Differ object for abstracting diffing
697
3009.2.7 by Aaron Bentley
Move responsibility for generating diff labels into Differ.diff
698
    # GNU Patch uses the epoch date to detect files that are being added
699
    # or removed in a diff.
700
    EPOCH_DATE = '1970-01-01 00:00:00 +0000'
701
7143.15.2 by Jelmer Vernooij
Run autopep8.
702
    def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8',
703
                 old_label='', new_label='', text_differ=internal_diff,
6524.5.8 by Paul Nixon
Changed "default_context_amount" to be "DEFAULT_CONTEXT_AMOUNT" to be
704
                 context_lines=DEFAULT_CONTEXT_AMOUNT):
3009.2.22 by Aaron Bentley
Update names & docstring
705
        DiffPath.__init__(self, old_tree, new_tree, to_file, path_encoding)
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
706
        self.text_differ = text_differ
707
        self.old_label = old_label
708
        self.new_label = new_label
3009.2.12 by Aaron Bentley
Associate labels with text diffing only
709
        self.path_encoding = path_encoding
6524.5.1 by pauljnixon at gmail
Made diffs more configurable
710
        self.context_lines = context_lines
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
711
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
712
    def diff(self, old_path, new_path, old_kind, new_kind):
3009.2.17 by Aaron Bentley
Update docs
713
        """Compare two files in unified diff format
714
715
        :param old_path: Path of the file in the old tree
716
        :param new_path: Path of the file in the new tree
717
        :param old_kind: Old file-kind of the file
718
        :param new_kind: New file-kind of the file
719
        """
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
720
        if 'file' not in (old_kind, new_kind):
3009.2.14 by Aaron Bentley
Update return type handling
721
            return self.CANNOT_DIFF
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
722
        if old_kind == 'file':
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
723
            old_date = _patch_header_date(self.old_tree, old_path)
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
724
        elif old_kind is None:
725
            old_date = self.EPOCH_DATE
726
        else:
3009.2.14 by Aaron Bentley
Update return type handling
727
            return self.CANNOT_DIFF
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
728
        if new_kind == 'file':
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
729
            new_date = _patch_header_date(self.new_tree, new_path)
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
730
        elif new_kind is None:
731
            new_date = self.EPOCH_DATE
732
        else:
3009.2.14 by Aaron Bentley
Update return type handling
733
            return self.CANNOT_DIFF
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
734
        from_label = '%s%s\t%s' % (self.old_label, old_path,
7143.15.2 by Jelmer Vernooij
Run autopep8.
735
                                   old_date)
7031.1.1 by Jelmer Vernooij
Fix breezy.tests.test_diff.
736
        to_label = '%s%s\t%s' % (self.new_label, new_path,
7143.15.2 by Jelmer Vernooij
Run autopep8.
737
                                 new_date)
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
738
        return self.diff_text(old_path, new_path, from_label, to_label)
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
739
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
740
    def diff_text(self, from_path, to_path, from_label, to_label):
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
741
        """Diff the content of given files in two trees
742
6809.4.9 by Jelmer Vernooij
Fix some more tests.
743
        :param from_path: The path in the from tree. If None,
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
744
            the file is not present in the from tree.
6809.4.9 by Jelmer Vernooij
Fix some more tests.
745
        :param to_path: The path in the to tree. This may refer
746
            to a different file from from_path.  If None,
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
747
            the file is not present in the to tree.
748
        """
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
749
        def _get_text(tree, path):
750
            if path is None:
751
                return []
752
            try:
753
                return tree.get_file_lines(path)
754
            except errors.NoSuchFile:
755
                return []
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
756
        try:
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
757
            from_text = _get_text(self.old_tree, from_path)
758
            to_text = _get_text(self.new_tree, to_path)
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
759
            self.text_differ(from_label, from_text, to_label, to_text,
6524.5.1 by pauljnixon at gmail
Made diffs more configurable
760
                             self.to_file, path_encoding=self.path_encoding,
761
                             context_lines=self.context_lines)
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
762
        except errors.BinaryFile:
763
            self.to_file.write(
7143.15.2 by Jelmer Vernooij
Run autopep8.
764
                ("Binary files %s and %s differ\n" %
765
                 (from_label, to_label)).encode(self.path_encoding, 'replace'))
3009.2.14 by Aaron Bentley
Update return type handling
766
        return self.CHANGED
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
767
768
3123.6.2 by Aaron Bentley
Implement diff --using natively
769
class DiffFromTool(DiffPath):
770
771
    def __init__(self, command_template, old_tree, new_tree, to_file,
772
                 path_encoding='utf-8'):
773
        DiffPath.__init__(self, old_tree, new_tree, to_file, path_encoding)
774
        self.command_template = command_template
6681.2.4 by Jelmer Vernooij
More renames.
775
        self._root = osutils.mkdtemp(prefix='brz-diff-')
3123.6.2 by Aaron Bentley
Implement diff --using natively
776
777
    @classmethod
778
    def from_string(klass, command_string, old_tree, new_tree, to_file,
779
                    path_encoding='utf-8'):
4913.5.24 by Gordon Tyler
Added cmdline.split function, which replaces commands.shlex_split_unicode.
780
        command_template = cmdline.split(command_string)
4603.1.20 by Aaron Bentley
Use string.Template substitution with @ as delimiter.
781
        if '@' not in command_string:
782
            command_template.extend(['@old_path', '@new_path'])
3123.6.2 by Aaron Bentley
Implement diff --using natively
783
        return klass(command_template, old_tree, new_tree, to_file,
784
                     path_encoding)
785
786
    @classmethod
5349.1.4 by Matthäus G. Chajdas
Allow both --using and --diff-options.
787
    def make_from_diff_tree(klass, command_string, external_diff_options=None):
3123.6.2 by Aaron Bentley
Implement diff --using natively
788
        def from_diff_tree(diff_tree):
5349.1.4 by Matthäus G. Chajdas
Allow both --using and --diff-options.
789
            full_command_string = [command_string]
790
            if external_diff_options is not None:
791
                full_command_string += ' ' + external_diff_options
792
            return klass.from_string(full_command_string, diff_tree.old_tree,
3123.6.2 by Aaron Bentley
Implement diff --using natively
793
                                     diff_tree.new_tree, diff_tree.to_file)
794
        return from_diff_tree
795
796
    def _get_command(self, old_path, new_path):
797
        my_map = {'old_path': old_path, 'new_path': new_path}
5074.5.1 by INADA Naoki
merge #523746 fix from lp:~songofacandy/bzr/fix-523746-2
798
        command = [AtTemplate(t).substitute(my_map) for t in
799
                   self.command_template]
7143.15.2 by Jelmer Vernooij
Run autopep8.
800
        if sys.platform == 'win32':  # Popen doesn't accept unicode on win32
4634.171.2 by INADA Naoki
Make temporary filename more friendly for non ascii filename.
801
            command_encoded = []
802
            for c in command:
6973.6.2 by Jelmer Vernooij
Fix more tests.
803
                if isinstance(c, text_type):
4634.171.2 by INADA Naoki
Make temporary filename more friendly for non ascii filename.
804
                    command_encoded.append(c.encode('mbcs'))
805
                else:
806
                    command_encoded.append(c)
807
            return command_encoded
808
        else:
809
            return command
3123.6.2 by Aaron Bentley
Implement diff --using natively
810
811
    def _execute(self, old_path, new_path):
3145.1.1 by Aaron Bentley
Handle missing tools gracefully in diff --using
812
        command = self._get_command(old_path, new_path)
813
        try:
814
            proc = subprocess.Popen(command, stdout=subprocess.PIPE,
815
                                    cwd=self._root)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
816
        except OSError as e:
3145.1.1 by Aaron Bentley
Handle missing tools gracefully in diff --using
817
            if e.errno == errno.ENOENT:
818
                raise errors.ExecutableMissing(command[0])
3145.1.2 by Aaron Bentley
Don't swallow other OSErrors
819
            else:
820
                raise
3123.6.2 by Aaron Bentley
Implement diff --using natively
821
        self.to_file.write(proc.stdout.read())
7027.4.11 by Jelmer Vernooij
Fix some tests that fail with -Werror.
822
        proc.stdout.close()
3123.6.2 by Aaron Bentley
Implement diff --using natively
823
        return proc.wait()
824
3123.6.5 by Aaron Bentley
Symlink to real files if possible
825
    def _try_symlink_root(self, tree, prefix):
7143.15.2 by Jelmer Vernooij
Run autopep8.
826
        if (getattr(tree, 'abspath', None) is None or
827
                not osutils.host_os_dereferences_symlinks()):
3123.6.5 by Aaron Bentley
Symlink to real files if possible
828
            return False
829
        try:
830
            os.symlink(tree.abspath(''), osutils.pathjoin(self._root, prefix))
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
831
        except OSError as e:
3123.6.5 by Aaron Bentley
Symlink to real files if possible
832
            if e.errno != errno.EEXIST:
833
                raise
834
        return True
835
5074.5.8 by INADA Naoki
Use tempfile when filepath in tree is not be able to encode with fsencoding.
836
    @staticmethod
837
    def _fenc():
5074.5.9 by INADA Naoki
Make additional comments to clarify
838
        """Returns safe encoding for passing file path to diff tool"""
5074.5.8 by INADA Naoki
Use tempfile when filepath in tree is not be able to encode with fsencoding.
839
        if sys.platform == 'win32':
840
            return 'mbcs'
841
        else:
842
            # Don't fallback to 'utf-8' because subprocess may not be able to
843
            # handle utf-8 correctly when locale is not utf-8.
844
            return sys.getfilesystemencoding() or 'ascii'
845
846
    def _is_safepath(self, path):
847
        """Return true if `path` may be able to pass to subprocess."""
848
        fenc = self._fenc()
849
        try:
850
            return path == path.encode(fenc).decode(fenc)
851
        except UnicodeError:
852
            return False
853
5074.5.7 by INADA Naoki
Test for filename encoding can't test subprocess execution because
854
    def _safe_filename(self, prefix, relpath):
5074.5.8 by INADA Naoki
Use tempfile when filepath in tree is not be able to encode with fsencoding.
855
        """Replace unsafe character in `relpath` then join `self._root`,
856
        `prefix` and `relpath`."""
857
        fenc = self._fenc()
4634.171.4 by INADA Naoki
Append comment for why decode() needed before replace().
858
        # encoded_str.replace('?', '_') may break multibyte char.
859
        # So we should encode, decode, then replace(u'?', u'_')
4634.171.2 by INADA Naoki
Make temporary filename more friendly for non ascii filename.
860
        relpath_tmp = relpath.encode(fenc, 'replace').decode(fenc, 'replace')
4634.171.3 by INADA Naoki
Fix easy miss.
861
        relpath_tmp = relpath_tmp.replace(u'?', u'_')
5074.5.7 by INADA Naoki
Test for filename encoding can't test subprocess execution because
862
        return osutils.pathjoin(self._root, prefix, relpath_tmp)
863
6809.4.15 by Jelmer Vernooij
Fix some more tests.
864
    def _write_file(self, relpath, tree, prefix, force_temp=False,
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
865
                    allow_write=False):
5074.5.7 by INADA Naoki
Test for filename encoding can't test subprocess execution because
866
        if not force_temp and isinstance(tree, WorkingTree):
6809.4.15 by Jelmer Vernooij
Fix some more tests.
867
            full_path = tree.abspath(relpath)
5074.5.8 by INADA Naoki
Use tempfile when filepath in tree is not be able to encode with fsencoding.
868
            if self._is_safepath(full_path):
869
                return full_path
5074.5.7 by INADA Naoki
Test for filename encoding can't test subprocess execution because
870
871
        full_path = self._safe_filename(prefix, relpath)
4603.1.4 by Aaron Bentley
Implement DiffFromTool.edit_file
872
        if not force_temp and self._try_symlink_root(tree, prefix):
3123.6.5 by Aaron Bentley
Symlink to real files if possible
873
            return full_path
3123.6.4 by Aaron Bentley
Set mtime (and atime) on files for --using
874
        parent_dir = osutils.dirname(full_path)
3123.6.2 by Aaron Bentley
Implement diff --using natively
875
        try:
876
            os.makedirs(parent_dir)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
877
        except OSError as e:
3123.6.2 by Aaron Bentley
Implement diff --using natively
878
            if e.errno != errno.EEXIST:
879
                raise
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
880
        source = tree.get_file(relpath)
3123.6.2 by Aaron Bentley
Implement diff --using natively
881
        try:
6855.4.5 by Jelmer Vernooij
Fix more bees, use with rather than try/finally for some files.
882
            with open(full_path, 'wb') as target:
3123.6.2 by Aaron Bentley
Implement diff --using natively
883
                osutils.pumpfile(source, target)
884
        finally:
885
            source.close()
4976.1.3 by Jelmer Vernooij
Cope with ghosts in 'bzr diff'
886
        try:
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
887
            mtime = tree.get_file_mtime(relpath)
6731.1.1 by Jelmer Vernooij
Move FileTimestampUnavailable to breezy.tree.
888
        except FileTimestampUnavailable:
5151.3.2 by Martin
Don't try and warp files back to the 70s if no timestamp is available
889
            pass
890
        else:
891
            os.utime(full_path, (mtime, mtime))
5151.3.1 by Martin
Fix os.utime test failures, three on FAT filesystems and one with readonly files
892
        if not allow_write:
893
            osutils.make_readonly(full_path)
3123.6.4 by Aaron Bentley
Set mtime (and atime) on files for --using
894
        return full_path
3123.6.2 by Aaron Bentley
Implement diff --using natively
895
6809.4.15 by Jelmer Vernooij
Fix some more tests.
896
    def _prepare_files(self, old_path, new_path, force_temp=False,
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
897
                       allow_write_new=False):
898
        old_disk_path = self._write_file(
899
            old_path, self.old_tree, 'old', force_temp)
900
        new_disk_path = self._write_file(
901
            new_path, self.new_tree, 'new', force_temp,
902
            allow_write=allow_write_new)
3123.6.2 by Aaron Bentley
Implement diff --using natively
903
        return old_disk_path, new_disk_path
904
905
    def finish(self):
4354.6.1 by Martitza Mendez
Fix 363837 : catch OSError from osutils.rmtree and mutter to trace file.
906
        try:
907
            osutils.rmtree(self._root)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
908
        except OSError as e:
4354.6.1 by Martitza Mendez
Fix 363837 : catch OSError from osutils.rmtree and mutter to trace file.
909
            if e.errno != errno.ENOENT:
4399.1.1 by Ian Clatworthy
(igc) address temp file issue with diff --using on Windows (Martitza Mendez)
910
                mutter("The temporary directory \"%s\" was not "
7143.15.2 by Jelmer Vernooij
Run autopep8.
911
                       "cleanly removed: %s." % (self._root, e))
3123.6.2 by Aaron Bentley
Implement diff --using natively
912
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
913
    def diff(self, old_path, new_path, old_kind, new_kind):
3123.6.2 by Aaron Bentley
Implement diff --using natively
914
        if (old_kind, new_kind) != ('file', 'file'):
915
            return DiffPath.CANNOT_DIFF
4845.2.1 by Gary van der Merwe
When launching an external diff app, don't write temporary files for a working tree.
916
        (old_disk_path, new_disk_path) = self._prepare_files(
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
917
            old_path, new_path)
4845.2.1 by Gary van der Merwe
When launching an external diff app, don't write temporary files for a working tree.
918
        self._execute(old_disk_path, new_disk_path)
4603.1.1 by Aaron Bentley
Initial pass at shelve-via-editor.
919
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
920
    def edit_file(self, old_path, new_path):
4603.1.4 by Aaron Bentley
Implement DiffFromTool.edit_file
921
        """Use this tool to edit a file.
922
923
        A temporary copy will be edited, and the new contents will be
924
        returned.
925
926
        :return: The new contents of the file.
927
        """
5074.5.1 by INADA Naoki
merge #523746 fix from lp:~songofacandy/bzr/fix-523746-2
928
        old_abs_path, new_abs_path = self._prepare_files(
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
929
            old_path, new_path, allow_write_new=True, force_temp=True)
5074.5.1 by INADA Naoki
merge #523746 fix from lp:~songofacandy/bzr/fix-523746-2
930
        command = self._get_command(old_abs_path, new_abs_path)
4603.1.24 by Aaron Bentley
Fix call import/invocation.
931
        subprocess.call(command, cwd=self._root)
6855.4.5 by Jelmer Vernooij
Fix more bees, use with rather than try/finally for some files.
932
        with open(new_abs_path, 'rb') as new_file:
4603.1.4 by Aaron Bentley
Implement DiffFromTool.edit_file
933
            return new_file.read()
934
3123.6.2 by Aaron Bentley
Implement diff --using natively
935
3009.2.22 by Aaron Bentley
Update names & docstring
936
class DiffTree(object):
937
    """Provides textual representations of the difference between two trees.
938
939
    A DiffTree examines two trees and where a file-id has altered
940
    between them, generates a textual representation of the difference.
941
    DiffTree uses a sequence of DiffPath objects which are each
942
    given the opportunity to handle a given altered fileid. The list
943
    of DiffPath objects can be extended globally by appending to
944
    DiffTree.diff_factories, or for a specific diff operation by
3009.2.27 by Aaron Bentley
Use extra_factories instead of extra_diffs
945
    supplying the extra_factories option to the appropriate method.
3009.2.22 by Aaron Bentley
Update names & docstring
946
    """
947
948
    # list of factories that can provide instances of DiffPath objects
3009.2.17 by Aaron Bentley
Update docs
949
    # may be extended by plugins.
3009.2.28 by Aaron Bentley
Add from_diff_tree factories
950
    diff_factories = [DiffSymlink.from_diff_tree,
951
                      DiffDirectory.from_diff_tree]
3009.2.13 by Aaron Bentley
Refactor differ to support registering differ factories
952
3009.2.12 by Aaron Bentley
Associate labels with text diffing only
953
    def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8',
3009.2.28 by Aaron Bentley
Add from_diff_tree factories
954
                 diff_text=None, extra_factories=None):
3009.2.17 by Aaron Bentley
Update docs
955
        """Constructor
956
957
        :param old_tree: Tree to show as old in the comparison
958
        :param new_tree: Tree to show as new in the comparison
959
        :param to_file: File to write comparision to
960
        :param path_encoding: Character encoding to write paths in
3009.2.28 by Aaron Bentley
Add from_diff_tree factories
961
        :param diff_text: DiffPath-type object to use as a last resort for
3009.2.17 by Aaron Bentley
Update docs
962
            diffing text files.
3009.2.27 by Aaron Bentley
Use extra_factories instead of extra_diffs
963
        :param extra_factories: Factories of DiffPaths to try before any other
964
            DiffPaths"""
3009.2.28 by Aaron Bentley
Add from_diff_tree factories
965
        if diff_text is None:
966
            diff_text = DiffText(old_tree, new_tree, to_file, path_encoding,
7143.15.2 by Jelmer Vernooij
Run autopep8.
967
                                 '', '', internal_diff)
3009.2.4 by Aaron Bentley
Make old_tree/new_tree construction parameters of Differ
968
        self.old_tree = old_tree
969
        self.new_tree = new_tree
3009.2.2 by Aaron Bentley
Implement Differ object for abstracting diffing
970
        self.to_file = to_file
3009.2.28 by Aaron Bentley
Add from_diff_tree factories
971
        self.path_encoding = path_encoding
3009.2.13 by Aaron Bentley
Refactor differ to support registering differ factories
972
        self.differs = []
3009.2.27 by Aaron Bentley
Use extra_factories instead of extra_diffs
973
        if extra_factories is not None:
3009.2.28 by Aaron Bentley
Add from_diff_tree factories
974
            self.differs.extend(f(self) for f in extra_factories)
975
        self.differs.extend(f(self) for f in self.diff_factories)
976
        self.differs.extend([diff_text, DiffKindChange.from_diff_tree(self)])
3009.2.6 by Aaron Bentley
Convert show_diff_trees into a Differ method
977
978
    @classmethod
979
    def from_trees_options(klass, old_tree, new_tree, to_file,
3009.2.17 by Aaron Bentley
Update docs
980
                           path_encoding, external_diff_options, old_label,
6524.5.4 by Paul Nixon
Clearing up default context options
981
                           new_label, using, context_lines):
3009.2.22 by Aaron Bentley
Update names & docstring
982
        """Factory for producing a DiffTree.
3009.2.17 by Aaron Bentley
Update docs
983
984
        Designed to accept options used by show_diff_trees.
5891.1.3 by Andrew Bennetts
Move docstring formatting fixes.
985
3009.2.17 by Aaron Bentley
Update docs
986
        :param old_tree: The tree to show as old in the comparison
987
        :param new_tree: The tree to show as new in the comparison
988
        :param to_file: File to write comparisons to
989
        :param path_encoding: Character encoding to use for writing paths
990
        :param external_diff_options: If supplied, use the installed diff
991
            binary to perform file comparison, using supplied options.
992
        :param old_label: Prefix to use for old file labels
993
        :param new_label: Prefix to use for new file labels
3123.6.2 by Aaron Bentley
Implement diff --using natively
994
        :param using: Commandline to use to invoke an external diff tool
3009.2.17 by Aaron Bentley
Update docs
995
        """
3123.6.2 by Aaron Bentley
Implement diff --using natively
996
        if using is not None:
7143.15.2 by Jelmer Vernooij
Run autopep8.
997
            extra_factories = [DiffFromTool.make_from_diff_tree(
998
                using, external_diff_options)]
3123.6.2 by Aaron Bentley
Implement diff --using natively
999
        else:
1000
            extra_factories = []
3009.2.6 by Aaron Bentley
Convert show_diff_trees into a Differ method
1001
        if external_diff_options:
1002
            opts = external_diff_options.split()
7143.15.2 by Jelmer Vernooij
Run autopep8.
1003
6524.5.5 by Paul Nixon
Added tests of configurable context
1004
            def diff_file(olab, olines, nlab, nlines, to_file, path_encoding=None, context_lines=None):
4797.57.2 by Alexander Belchenko
fixing test with external_diff
1005
                """:param path_encoding: not used but required
1006
                        to match the signature of internal_diff.
1007
                """
3009.2.6 by Aaron Bentley
Convert show_diff_trees into a Differ method
1008
                external_diff(olab, olines, nlab, nlines, to_file, opts)
1009
        else:
1010
            diff_file = internal_diff
3009.2.28 by Aaron Bentley
Add from_diff_tree factories
1011
        diff_text = DiffText(old_tree, new_tree, to_file, path_encoding,
6524.5.1 by pauljnixon at gmail
Made diffs more configurable
1012
                             old_label, new_label, diff_file, context_lines=context_lines)
3123.6.2 by Aaron Bentley
Implement diff --using natively
1013
        return klass(old_tree, new_tree, to_file, path_encoding, diff_text,
1014
                     extra_factories)
3009.2.6 by Aaron Bentley
Convert show_diff_trees into a Differ method
1015
3009.2.12 by Aaron Bentley
Associate labels with text diffing only
1016
    def show_diff(self, specific_files, extra_trees=None):
3009.2.17 by Aaron Bentley
Update docs
1017
        """Write tree diff to self.to_file
1018
5131.1.4 by Jelmer Vernooij
Add test for custom diff format.
1019
        :param specific_files: the specific files to compare (recursive)
3009.2.17 by Aaron Bentley
Update docs
1020
        :param extra_trees: extra trees to use for mapping paths to file_ids
1021
        """
3123.6.2 by Aaron Bentley
Implement diff --using natively
1022
        try:
1023
            return self._show_diff(specific_files, extra_trees)
1024
        finally:
1025
            for differ in self.differs:
1026
                differ.finish()
1027
1028
    def _show_diff(self, specific_files, extra_trees):
3009.2.6 by Aaron Bentley
Convert show_diff_trees into a Differ method
1029
        # TODO: Generation of pseudo-diffs for added/deleted files could
1030
        # be usefully made into a much faster special case.
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1031
        iterator = self.new_tree.iter_changes(self.old_tree,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1032
                                              specific_files=specific_files,
1033
                                              extra_trees=extra_trees,
1034
                                              require_versioned=True)
3009.2.6 by Aaron Bentley
Convert show_diff_trees into a Differ method
1035
        has_changes = 0
7143.15.2 by Jelmer Vernooij
Run autopep8.
1036
3123.4.1 by Aaron Bentley
Diff sorts files in alphabetical order
1037
        def changes_key(change):
1038
            old_path, new_path = change[1]
1039
            path = new_path
1040
            if path is None:
1041
                path = old_path
1042
            return path
7143.15.2 by Jelmer Vernooij
Run autopep8.
1043
3123.4.1 by Aaron Bentley
Diff sorts files in alphabetical order
1044
        def get_encoded_path(path):
1045
            if path is not None:
1046
                return path.encode(self.path_encoding, "replace")
1047
        for (file_id, paths, changed_content, versioned, parent, name, kind,
1048
             executable) in sorted(iterator, key=changes_key):
3619.4.2 by Robert Collins
Change bzrlib.diff.DiffTree.show_diff to skip entries missing in both trees.
1049
            # The root does not get diffed, and items with no known kind (that
1050
            # is, missing) in both trees are skipped as well.
1051
            if parent == (None, None) or kind == (None, None):
3123.4.3 by Aaron Bentley
Tweak path handling
1052
                continue
7122.6.3 by Jelmer Vernooij
Merge trunk.
1053
            if kind[0] == 'symlink' and not self.new_tree.supports_symlinks():
1054
                warning(
7122.6.6 by Jelmer Vernooij
Fix more tests.
1055
                    'Ignoring "%s" as symlinks are not '
1056
                    'supported on this filesystem.' % (paths[0],))
6469.1.3 by Parth Malwankar
updated diff to ignore symlinks on windows
1057
                continue
3123.4.3 by Aaron Bentley
Tweak path handling
1058
            oldpath, newpath = paths
3123.4.1 by Aaron Bentley
Diff sorts files in alphabetical order
1059
            oldpath_encoded = get_encoded_path(paths[0])
1060
            newpath_encoded = get_encoded_path(paths[1])
1061
            old_present = (kind[0] is not None and versioned[0])
1062
            new_present = (kind[1] is not None and versioned[1])
1063
            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
1064
1065
            properties_changed = []
7143.15.2 by Jelmer Vernooij
Run autopep8.
1066
            properties_changed.extend(
1067
                get_executable_change(executable[0], executable[1]))
3268.1.1 by C Miller
Describe the property changes in diffs. Currently, this is the executable-bit
1068
1069
            if properties_changed:
6973.11.9 by Jelmer Vernooij
Fix tests.
1070
                prop_str = b" (properties changed: %s)" % (
7143.15.2 by Jelmer Vernooij
Run autopep8.
1071
                    b", ".join(properties_changed),)
3268.1.1 by C Miller
Describe the property changes in diffs. Currently, this is the executable-bit
1072
            else:
6973.10.1 by Jelmer Vernooij
Fix some tests.
1073
                prop_str = b""
3268.1.1 by C Miller
Describe the property changes in diffs. Currently, this is the executable-bit
1074
3123.4.1 by Aaron Bentley
Diff sorts files in alphabetical order
1075
            if (old_present, new_present) == (True, False):
6973.10.1 by Jelmer Vernooij
Fix some tests.
1076
                self.to_file.write(b"=== removed %s '%s'\n" %
1077
                                   (kind[0].encode('ascii'), oldpath_encoded))
3123.4.3 by Aaron Bentley
Tweak path handling
1078
                newpath = oldpath
3123.4.1 by Aaron Bentley
Diff sorts files in alphabetical order
1079
            elif (old_present, new_present) == (False, True):
6973.10.1 by Jelmer Vernooij
Fix some tests.
1080
                self.to_file.write(b"=== added %s '%s'\n" %
1081
                                   (kind[1].encode('ascii'), newpath_encoded))
3123.4.3 by Aaron Bentley
Tweak path handling
1082
                oldpath = newpath
3123.4.1 by Aaron Bentley
Diff sorts files in alphabetical order
1083
            elif renamed:
6973.10.1 by Jelmer Vernooij
Fix some tests.
1084
                self.to_file.write(b"=== renamed %s '%s' => '%s'%s\n" %
7143.15.2 by Jelmer Vernooij
Run autopep8.
1085
                                   (kind[0].encode('ascii'), oldpath_encoded, newpath_encoded, prop_str))
3123.4.2 by Aaron Bentley
Handle diff with property change correctly
1086
            else:
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1087
                # if it was produced by iter_changes, it must be
3123.4.2 by Aaron Bentley
Handle diff with property change correctly
1088
                # modified *somehow*, either content or execute bit.
6973.10.1 by Jelmer Vernooij
Fix some tests.
1089
                self.to_file.write(b"=== modified %s '%s'%s\n" % (kind[0].encode('ascii'),
7143.15.2 by Jelmer Vernooij
Run autopep8.
1090
                                                                  newpath_encoded, prop_str))
3123.4.1 by Aaron Bentley
Diff sorts files in alphabetical order
1091
            if changed_content:
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
1092
                self._diff(oldpath, newpath, kind[0], kind[1])
3123.4.1 by Aaron Bentley
Diff sorts files in alphabetical order
1093
                has_changes = 1
1094
            if renamed:
1095
                has_changes = 1
3009.2.6 by Aaron Bentley
Convert show_diff_trees into a Differ method
1096
        return has_changes
3009.2.2 by Aaron Bentley
Implement Differ object for abstracting diffing
1097
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
1098
    def diff(self, old_path, new_path):
3009.2.17 by Aaron Bentley
Update docs
1099
        """Perform a diff of a single file
1100
1101
        :param old_path: The path of the file in the old tree
1102
        :param new_path: The path of the file in the new tree
1103
        """
6809.4.11 by Jelmer Vernooij
Fix diff tests.
1104
        if old_path is None:
1105
            old_kind = None
1106
        else:
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
1107
            old_kind = self.old_tree.kind(old_path)
6809.4.11 by Jelmer Vernooij
Fix diff tests.
1108
        if new_path is None:
1109
            new_kind = None
1110
        else:
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
1111
            new_kind = self.new_tree.kind(new_path)
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
1112
        self._diff(old_path, new_path, old_kind, new_kind)
4377.3.1 by Ian Clatworthy
faster diff on large trees
1113
7206.6.2 by Jelmer Vernooij
Remove file_id from diff API.
1114
    def _diff(self, old_path, new_path, old_kind, new_kind):
1115
        result = DiffPath._diff_many(
1116
            self.differs, old_path, new_path, old_kind, new_kind)
3009.2.22 by Aaron Bentley
Update names & docstring
1117
        if result is DiffPath.CANNOT_DIFF:
3009.2.11 by Aaron Bentley
Refactor diff to be more pluggable
1118
            error_path = new_path
1119
            if error_path is None:
1120
                error_path = old_path
3009.2.22 by Aaron Bentley
Update names & docstring
1121
            raise errors.NoDiffFound(error_path)
5131.1.1 by Jelmer Vernooij
Add --format option to 'bzr diff'.
1122
1123
1124
format_registry = Registry()
1125
format_registry.register('default', DiffTree)