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