~bzr-pqm/bzr/bzr.dev

1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
1
# Copyright (C) 2004, 2005, 2006 Canonical Ltd.
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
2
#
1 by mbp at sourcefrog
import from baz patch-364
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
7
#
1 by mbp at sourcefrog
import from baz patch-364
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
12
#
1 by mbp at sourcefrog
import from baz patch-364
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
17
import errno
18
import os
1899.1.5 by John Arbash Meinel
Always buffer the output of diff, so we can check if retcode==2 is because of Binary files
19
import re
1692.8.7 by James Henstridge
changes suggested by John Meinel
20
import subprocess
1692.8.4 by James Henstridge
move external_diff imports to top of file
21
import sys
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
22
import tempfile
1740.2.5 by Aaron Bentley
Merge from bzr.dev
23
import time
24
1852.10.3 by Robert Collins
Remove all uses of compare_trees and replace with Tree.changes_from throughout bzrlib.
25
# compatability - plugins import compare_trees from diff!!!
26
# deprecated as of 0.10
1534.4.35 by Robert Collins
Give branch its own basis tree and last_revision methods; deprecated branch.working_tree()
27
from bzrlib.delta import compare_trees
28
from bzrlib.errors import BzrError
1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
29
import bzrlib.errors as errors
1711.2.58 by John Arbash Meinel
Use osutils.pumpfile so we don't have to buffer everything in ram
30
import bzrlib.osutils
1711.2.24 by John Arbash Meinel
Late bind to PatienceSequenceMatcher to allow plugin to override.
31
from bzrlib.patiencediff import unified_diff
32
import bzrlib.patiencediff
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
33
from bzrlib.symbol_versioning import (deprecated_function,
34
        zero_eight)
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
35
from bzrlib.textfile import check_text_lines
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
36
from bzrlib.trace import mutter, warning
1 by mbp at sourcefrog
import from baz patch-364
37
1711.2.24 by John Arbash Meinel
Late bind to PatienceSequenceMatcher to allow plugin to override.
38
767 by Martin Pool
- files are only reported as modified if their name or parent has changed,
39
# TODO: Rather than building a changeset object, we should probably
40
# invoke callbacks on an object.  That object can either accumulate a
41
# list, write them out directly, etc etc.
42
1558.15.11 by Aaron Bentley
Apply merge review suggestions
43
def internal_diff(old_filename, oldlines, new_filename, newlines, to_file,
1711.2.30 by John Arbash Meinel
Fix bug in internal_diff handling of unicode paths
44
                  allow_binary=False, sequence_matcher=None,
45
                  path_encoding='utf8'):
475 by Martin Pool
- rewrite diff using compare_trees()
46
    # FIXME: difflib is wrong if there is no trailing newline.
47
    # The syntax used by patch seems to be "\ No newline at
48
    # end of file" following the last diff line from that
49
    # file.  This is not trivial to insert into the
50
    # unified_diff output and it might be better to just fix
51
    # or replace that function.
52
53
    # In the meantime we at least make sure the patch isn't
54
    # mangled.
55
56
57
    # Special workaround for Python2.3, where difflib fails if
58
    # both sequences are empty.
59
    if not oldlines and not newlines:
60
        return
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
61
    
1558.15.11 by Aaron Bentley
Apply merge review suggestions
62
    if allow_binary is False:
63
        check_text_lines(oldlines)
64
        check_text_lines(newlines)
475 by Martin Pool
- rewrite diff using compare_trees()
65
1185.81.8 by John Arbash Meinel
Updating unified_diff to take a factory, using the new diff algorithm in the code.
66
    if sequence_matcher is None:
1711.2.24 by John Arbash Meinel
Late bind to PatienceSequenceMatcher to allow plugin to override.
67
        sequence_matcher = bzrlib.patiencediff.PatienceSequenceMatcher
1185.81.8 by John Arbash Meinel
Updating unified_diff to take a factory, using the new diff algorithm in the code.
68
    ud = unified_diff(oldlines, newlines,
1740.2.5 by Aaron Bentley
Merge from bzr.dev
69
                      fromfile=old_filename.encode(path_encoding),
70
                      tofile=new_filename.encode(path_encoding),
1185.81.8 by John Arbash Meinel
Updating unified_diff to take a factory, using the new diff algorithm in the code.
71
                      sequencematcher=sequence_matcher)
475 by Martin Pool
- rewrite diff using compare_trees()
72
1092.1.50 by Robert Collins
make diff lsdiff/filterdiff friendly
73
    ud = list(ud)
475 by Martin Pool
- rewrite diff using compare_trees()
74
    # work-around for difflib being too smart for its own good
75
    # if /dev/null is "1,0", patch won't recognize it as /dev/null
76
    if not oldlines:
77
        ud[2] = ud[2].replace('-1,0', '-0,0')
78
    elif not newlines:
79
        ud[2] = ud[2].replace('+1,0', '+0,0')
1092.1.50 by Robert Collins
make diff lsdiff/filterdiff friendly
80
    # work around for difflib emitting random spaces after the label
81
    ud[0] = ud[0][:-2] + '\n'
82
    ud[1] = ud[1][:-2] + '\n'
475 by Martin Pool
- rewrite diff using compare_trees()
83
804 by Martin Pool
Patch from John:
84
    for line in ud:
85
        to_file.write(line)
974.1.5 by Aaron Bentley
Fixed handling of missing newlines in udiffs
86
        if not line.endswith('\n'):
87
            to_file.write("\n\\ No newline at end of file\n")
475 by Martin Pool
- rewrite diff using compare_trees()
88
    print >>to_file
89
90
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
91
def _set_lang_C():
92
    """Set the env var LANG=C"""
93
    os.environ['LANG'] = 'C'
94
95
96
def _spawn_external_diff(diffcmd, capture_errors=True):
97
    """Spawn the externall diff process, and return the child handle.
98
99
    :param diffcmd: The command list to spawn
100
    :param capture_errors: Capture stderr as well as setting LANG=C.
101
        This lets us read and understand the output of diff, and respond 
102
        to any errors.
103
    :return: A Popen object.
104
    """
105
    if capture_errors:
106
        preexec_fn = _set_lang_C
107
        stderr = subprocess.PIPE
108
    else:
109
        preexec_fn = None
110
        stderr = None
111
112
    try:
113
        pipe = subprocess.Popen(diffcmd,
114
                                stdin=subprocess.PIPE,
115
                                stdout=subprocess.PIPE,
116
                                stderr=stderr,
117
                                preexec_fn=preexec_fn)
118
    except OSError, e:
119
        if e.errno == errno.ENOENT:
120
            raise errors.NoDiff(str(e))
121
        raise
122
123
    return pipe
124
125
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
126
def external_diff(old_filename, oldlines, new_filename, newlines, to_file,
571 by Martin Pool
- new --diff-options to pass options through to external
127
                  diff_opts):
568 by Martin Pool
- start adding support for showing diffs by calling out to
128
    """Display a diff by calling out to the external diff program."""
581 by Martin Pool
- make sure any bzr output is flushed before
129
    # make sure our own output is properly ordered before the diff
130
    to_file.flush()
131
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
132
    oldtmp_fd, old_abspath = tempfile.mkstemp(prefix='bzr-diff-old-')
133
    newtmp_fd, new_abspath = tempfile.mkstemp(prefix='bzr-diff-new-')
134
    oldtmpf = os.fdopen(oldtmp_fd, 'wb')
135
    newtmpf = os.fdopen(newtmp_fd, 'wb')
568 by Martin Pool
- start adding support for showing diffs by calling out to
136
137
    try:
138
        # TODO: perhaps a special case for comparing to or from the empty
139
        # sequence; can just use /dev/null on Unix
140
141
        # TODO: if either of the files being compared already exists as a
142
        # regular named file (e.g. in the working directory) then we can
143
        # compare directly to that, rather than copying it.
144
145
        oldtmpf.writelines(oldlines)
146
        newtmpf.writelines(newlines)
147
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
148
        oldtmpf.close()
149
        newtmpf.close()
568 by Martin Pool
- start adding support for showing diffs by calling out to
150
571 by Martin Pool
- new --diff-options to pass options through to external
151
        if not diff_opts:
152
            diff_opts = []
153
        diffcmd = ['diff',
1740.2.5 by Aaron Bentley
Merge from bzr.dev
154
                   '--label', old_filename,
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
155
                   old_abspath,
1740.2.5 by Aaron Bentley
Merge from bzr.dev
156
                   '--label', new_filename,
1711.2.56 by John Arbash Meinel
Raise NoDiff if 'diff' not present.
157
                   new_abspath,
158
                   '--binary',
159
                  ]
571 by Martin Pool
- new --diff-options to pass options through to external
160
161
        # diff only allows one style to be specified; they don't override.
162
        # note that some of these take optargs, and the optargs can be
163
        # directly appended to the options.
164
        # this is only an approximate parser; it doesn't properly understand
165
        # the grammar.
166
        for s in ['-c', '-u', '-C', '-U',
167
                  '-e', '--ed',
168
                  '-q', '--brief',
169
                  '--normal',
170
                  '-n', '--rcs',
171
                  '-y', '--side-by-side',
172
                  '-D', '--ifdef']:
173
            for j in diff_opts:
174
                if j.startswith(s):
175
                    break
176
            else:
177
                continue
178
            break
179
        else:
180
            diffcmd.append('-u')
181
                  
182
        if diff_opts:
183
            diffcmd.extend(diff_opts)
184
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
185
        pipe = _spawn_external_diff(diffcmd, capture_errors=True)
186
        out,err = pipe.communicate()
187
        rc = pipe.returncode
571 by Martin Pool
- new --diff-options to pass options through to external
188
        
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
189
        # internal_diff() adds a trailing newline, add one here for consistency
190
        out += '\n'
1899.1.5 by John Arbash Meinel
Always buffer the output of diff, so we can check if retcode==2 is because of Binary files
191
        if rc == 2:
192
            # 'diff' gives retcode == 2 for all sorts of errors
193
            # one of those is 'Binary files differ'.
194
            # Bad options could also be the problem.
195
            # '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
196
            lang_c_out = out
197
198
            # Since we got here, we want to make sure to give an i18n error
199
            pipe = _spawn_external_diff(diffcmd, capture_errors=False)
200
            out, err = pipe.communicate()
201
202
            # Write out the new i18n diff response
203
            to_file.write(out+'\n')
204
            if pipe.returncode != 2:
205
                raise BzrError('external diff failed with exit code 2'
206
                               ' when run with LANG=C, but not when run'
207
                               ' natively: %r' % (diffcmd,))
208
209
            first_line = lang_c_out.split('\n', 1)[0]
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
210
            m = re.match('^binary files.*differ$', first_line, re.I)
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
211
            if m is None:
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
212
                raise BzrError('external diff failed with exit code 2;'
213
                               ' command: %r' % (diffcmd,))
1920.1.1 by John Arbash Meinel
fix bug #56307, handle binary files even when LANG is not english
214
            else:
215
                # Binary files differ, just return
216
                return
217
218
        # If we got to here, we haven't written out the output of diff
219
        # do so now
220
        to_file.write(out)
221
        if rc not in (0, 1):
571 by Martin Pool
- new --diff-options to pass options through to external
222
            # returns 1 if files differ; that's OK
223
            if rc < 0:
224
                msg = 'signal %d' % (-rc)
225
            else:
226
                msg = 'exit code %d' % rc
227
                
1899.1.4 by John Arbash Meinel
Just swallow a return code of 2
228
            raise BzrError('external diff failed with %s; command: %r' 
229
                           % (rc, diffcmd))
1899.1.6 by John Arbash Meinel
internal_diff always adds a trailing \n, make sure external_diff does too
230
231
568 by Martin Pool
- start adding support for showing diffs by calling out to
232
    finally:
233
        oldtmpf.close()                 # and delete
234
        newtmpf.close()
1711.2.54 by John Arbash Meinel
Use mkstemp instead of NamedTemporary file for external diff.
235
        # Clean up. Warn in case the files couldn't be deleted
236
        # (in case windows still holds the file open, but not
237
        # if the files have already been deleted)
238
        try:
239
            os.remove(old_abspath)
240
        except OSError, e:
241
            if e.errno not in (errno.ENOENT,):
242
                warning('Failed to delete temporary file: %s %s',
243
                        old_abspath, e)
244
        try:
245
            os.remove(new_abspath)
246
        except OSError:
247
            if e.errno not in (errno.ENOENT,):
248
                warning('Failed to delete temporary file: %s %s',
249
                        new_abspath, e)
568 by Martin Pool
- start adding support for showing diffs by calling out to
250
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
251
252
@deprecated_function(zero_eight)
1432 by Robert Collins
branch: namespace
253
def show_diff(b, from_spec, specific_files, external_diff_options=None,
1185.35.28 by Aaron Bentley
Support diff with two branches as input.
254
              revision2=None, output=None, b2=None):
619 by Martin Pool
doc
255
    """Shortcut for showing the diff to the working tree.
256
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
257
    Please use show_diff_trees instead.
258
619 by Martin Pool
doc
259
    b
260
        Branch.
261
262
    revision
1432 by Robert Collins
branch: namespace
263
        None for 'basis tree', or otherwise the old revision to compare against.
619 by Martin Pool
doc
264
    
265
    The more general form is show_diff_trees(), where the caller
266
    supplies any two trees.
267
    """
1092.1.47 by Robert Collins
make show_diff redirectable
268
    if output is None:
269
        output = sys.stdout
475 by Martin Pool
- rewrite diff using compare_trees()
270
1432 by Robert Collins
branch: namespace
271
    if from_spec is None:
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
272
        old_tree = b.bzrdir.open_workingtree()
1185.35.28 by Aaron Bentley
Support diff with two branches as input.
273
        if b2 is None:
1534.4.35 by Robert Collins
Give branch its own basis tree and last_revision methods; deprecated branch.working_tree()
274
            old_tree = old_tree = old_tree.basis_tree()
329 by Martin Pool
- refactor command functions into command classes
275
    else:
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
276
        old_tree = b.repository.revision_tree(from_spec.in_history(b).rev_id)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
277
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
278
    if revision2 is None:
1185.35.28 by Aaron Bentley
Support diff with two branches as input.
279
        if b2 is None:
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
280
            new_tree = b.bzrdir.open_workingtree()
1185.35.28 by Aaron Bentley
Support diff with two branches as input.
281
        else:
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
282
            new_tree = b2.bzrdir.open_workingtree()
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
283
    else:
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
284
        new_tree = b.repository.revision_tree(revision2.in_history(b).rev_id)
329 by Martin Pool
- refactor command functions into command classes
285
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
286
    return show_diff_trees(old_tree, new_tree, output, specific_files,
287
                           external_diff_options)
571 by Martin Pool
- new --diff-options to pass options through to external
288
289
1551.2.15 by Aaron Bentley
Rename cmd_show_diff to diff_cmd_helper
290
def diff_cmd_helper(tree, specific_files, external_diff_options, 
1684.1.6 by Martin Pool
(patch) --diff-prefix option (goffredo, alexander)
291
                    old_revision_spec=None, new_revision_spec=None,
292
                    old_label='a/', new_label='b/'):
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
293
    """Helper for cmd_diff.
294
295
   tree 
296
        A WorkingTree
297
298
    specific_files
299
        The specific files to compare, or None
300
301
    external_diff_options
302
        If non-None, run an external diff, and pass it these options
303
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
304
    old_revision_spec
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
305
        If None, use basis tree as old revision, otherwise use the tree for
306
        the specified revision. 
307
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
308
    new_revision_spec
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
309
        If None, use working tree as new revision, otherwise use the tree for
310
        the specified revision.
311
    
312
    The more general form is show_diff_trees(), where the caller
313
    supplies any two trees.
314
    """
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
315
    def spec_tree(spec):
1732.3.1 by Matthieu Moy
Implementation of -r revno:N:/path/to/branch
316
        if tree:
317
            revision = spec.in_store(tree.branch)
318
        else:
319
            revision = spec.in_store(None)
320
        revision_id = revision.rev_id
321
        branch = revision.branch
322
        return branch.repository.revision_tree(revision_id)
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
323
    if old_revision_spec is None:
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
324
        old_tree = tree.basis_tree()
325
    else:
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
326
        old_tree = spec_tree(old_revision_spec)
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
327
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
328
    if new_revision_spec is None:
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
329
        new_tree = tree
330
    else:
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
331
        new_tree = spec_tree(new_revision_spec)
1551.7.19 by Aaron Bentley
Always include working tree when calculating file ids for diff
332
    if new_tree is not tree:
333
        extra_trees = (tree,)
334
    else:
335
        extra_trees = None
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
336
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
337
    return show_diff_trees(old_tree, new_tree, sys.stdout, specific_files,
1684.1.6 by Martin Pool
(patch) --diff-prefix option (goffredo, alexander)
338
                           external_diff_options,
1551.7.19 by Aaron Bentley
Always include working tree when calculating file ids for diff
339
                           old_label=old_label, new_label=new_label,
340
                           extra_trees=extra_trees)
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
341
571 by Martin Pool
- new --diff-options to pass options through to external
342
343
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)
344
                    external_diff_options=None,
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
345
                    old_label='a/', new_label='b/',
346
                    extra_trees=None):
550 by Martin Pool
- Refactor diff code into one that works purely on
347
    """Show in text form the changes from one tree to another.
348
349
    to_files
350
        If set, include only changes to these files.
571 by Martin Pool
- new --diff-options to pass options through to external
351
352
    external_diff_options
353
        If set, use an external GNU diff and pass these options.
1551.7.18 by Aaron Bentley
Indentation and documentation fixes
354
355
    extra_trees
356
        If set, more Trees to use for looking up file ids
550 by Martin Pool
- Refactor diff code into one that works purely on
357
    """
1543.1.1 by Denys Duchier
lock operations for trees - use them for diff
358
    old_tree.lock_read()
359
    try:
360
        new_tree.lock_read()
361
        try:
362
            return _show_diff_trees(old_tree, new_tree, to_file,
1684.1.6 by Martin Pool
(patch) --diff-prefix option (goffredo, alexander)
363
                                    specific_files, external_diff_options,
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
364
                                    old_label=old_label, new_label=new_label,
365
                                    extra_trees=extra_trees)
1543.1.1 by Denys Duchier
lock operations for trees - use them for diff
366
        finally:
367
            new_tree.unlock()
368
    finally:
369
        old_tree.unlock()
370
371
372
def _show_diff_trees(old_tree, new_tree, to_file,
1684.1.6 by Martin Pool
(patch) --diff-prefix option (goffredo, alexander)
373
                     specific_files, external_diff_options, 
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
374
                     old_label='a/', new_label='b/', extra_trees=None):
329 by Martin Pool
- refactor command functions into command classes
375
1740.2.5 by Aaron Bentley
Merge from bzr.dev
376
    # GNU Patch uses the epoch date to detect files that are being added
377
    # or removed in a diff.
378
    EPOCH_DATE = '1970-01-01 00:00:00 +0000'
1706.1.2 by James Henstridge
Simplify logic a little
379
329 by Martin Pool
- refactor command functions into command classes
380
    # TODO: Generation of pseudo-diffs for added/deleted files could
381
    # be usefully made into a much faster special case.
382
571 by Martin Pool
- new --diff-options to pass options through to external
383
    if external_diff_options:
384
        assert isinstance(external_diff_options, basestring)
385
        opts = external_diff_options.split()
386
        def diff_file(olab, olines, nlab, nlines, to_file):
387
            external_diff(olab, olines, nlab, nlines, to_file, opts)
388
    else:
389
        diff_file = internal_diff
390
    
1852.10.3 by Robert Collins
Remove all uses of compare_trees and replace with Tree.changes_from throughout bzrlib.
391
    delta = new_tree.changes_from(old_tree,
392
        specific_files=specific_files,
393
        extra_trees=extra_trees, require_versioned=True)
475 by Martin Pool
- rewrite diff using compare_trees()
394
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
395
    has_changes = 0
475 by Martin Pool
- rewrite diff using compare_trees()
396
    for path, file_id, kind in delta.removed:
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
397
        has_changes = 1
1732.1.7 by John Arbash Meinel
Instead of iterating randomly in both trees, _compare_trees now iterates in order on both trees simultaneously.
398
        print >>to_file, '=== removed %s %r' % (kind, path.encode('utf8'))
1740.2.5 by Aaron Bentley
Merge from bzr.dev
399
        old_name = '%s%s\t%s' % (old_label, path,
400
                                 _patch_header_date(old_tree, file_id, path))
401
        new_name = '%s%s\t%s' % (new_label, path, EPOCH_DATE)
402
        old_tree.inventory[file_id].diff(diff_file, old_name, old_tree,
403
                                         new_name, None, None, to_file)
475 by Martin Pool
- rewrite diff using compare_trees()
404
    for path, file_id, kind in delta.added:
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
405
        has_changes = 1
1732.1.7 by John Arbash Meinel
Instead of iterating randomly in both trees, _compare_trees now iterates in order on both trees simultaneously.
406
        print >>to_file, '=== added %s %r' % (kind, path.encode('utf8'))
1740.2.5 by Aaron Bentley
Merge from bzr.dev
407
        old_name = '%s%s\t%s' % (old_label, path, EPOCH_DATE)
408
        new_name = '%s%s\t%s' % (new_label, path,
409
                                 _patch_header_date(new_tree, file_id, path))
410
        new_tree.inventory[file_id].diff(diff_file, new_name, new_tree,
411
                                         old_name, None, None, to_file, 
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
412
                                         reverse=True)
1398 by Robert Collins
integrate in Gustavos x-bit patch
413
    for (old_path, new_path, file_id, kind,
414
         text_modified, meta_modified) in delta.renamed:
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
415
        has_changes = 1
1398 by Robert Collins
integrate in Gustavos x-bit patch
416
        prop_str = get_prop_change(meta_modified)
417
        print >>to_file, '=== renamed %s %r => %r%s' % (
1711.2.28 by John Arbash Meinel
Fix up bzrlib.diff now that compare_trees is returning unicode paths.
418
                    kind, old_path.encode('utf8'),
419
                    new_path.encode('utf8'), prop_str)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
420
        old_name = '%s%s\t%s' % (old_label, old_path,
421
                                 _patch_header_date(old_tree, file_id,
422
                                                    old_path))
423
        new_name = '%s%s\t%s' % (new_label, new_path,
424
                                 _patch_header_date(new_tree, file_id,
425
                                                    new_path))
426
        _maybe_diff_file_or_symlink(old_name, old_tree, file_id,
427
                                    new_name, new_tree,
1092.2.9 by Robert Collins
bugfix _maybe_diff, the test was not catching the error
428
                                    text_modified, kind, to_file, diff_file)
1398 by Robert Collins
integrate in Gustavos x-bit patch
429
    for path, file_id, kind, text_modified, meta_modified in delta.modified:
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
430
        has_changes = 1
1398 by Robert Collins
integrate in Gustavos x-bit patch
431
        prop_str = get_prop_change(meta_modified)
1732.1.7 by John Arbash Meinel
Instead of iterating randomly in both trees, _compare_trees now iterates in order on both trees simultaneously.
432
        print >>to_file, '=== modified %s %r%s' % (kind, path.encode('utf8'), prop_str)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
433
        old_name = '%s%s\t%s' % (old_label, path,
434
                                 _patch_header_date(old_tree, file_id, path))
435
        new_name = '%s%s\t%s' % (new_label, path,
436
                                 _patch_header_date(new_tree, file_id, path))
1398 by Robert Collins
integrate in Gustavos x-bit patch
437
        if text_modified:
1740.2.5 by Aaron Bentley
Merge from bzr.dev
438
            _maybe_diff_file_or_symlink(old_name, old_tree, file_id,
439
                                        new_name, new_tree,
1398 by Robert Collins
integrate in Gustavos x-bit patch
440
                                        True, kind, to_file, diff_file)
1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
441
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
442
    return has_changes
1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
443
444
1740.2.5 by Aaron Bentley
Merge from bzr.dev
445
def _patch_header_date(tree, file_id, path):
446
    """Returns a timestamp suitable for use in a patch header."""
447
    tm = time.gmtime(tree.get_file_mtime(file_id, path))
448
    return time.strftime('%Y-%m-%d %H:%M:%S +0000', tm)
449
450
1662.1.9 by Martin Pool
Give a clear error for bzr status of an unversioned, nonexistent file. (Malone #3619)
451
def _raise_if_nonexistent(paths, old_tree, new_tree):
452
    """Complain if paths are not in either inventory or tree.
453
454
    It's OK with the files exist in either tree's inventory, or 
455
    if they exist in the tree but are not versioned.
456
    
457
    This can be used by operations such as bzr status that can accept
458
    unknown or ignored files.
459
    """
460
    mutter("check paths: %r", paths)
461
    if not paths:
462
        return
463
    s = old_tree.filter_unversioned_files(paths)
464
    s = new_tree.filter_unversioned_files(s)
465
    s = [path for path in s if not new_tree.has_filename(path)]
466
    if s:
467
        raise errors.PathsDoNotExist(sorted(s))
468
469
1398 by Robert Collins
integrate in Gustavos x-bit patch
470
def get_prop_change(meta_modified):
471
    if meta_modified:
472
        return " (properties changed)"
473
    else:
474
        return  ""
475
476
1740.2.5 by Aaron Bentley
Merge from bzr.dev
477
def _maybe_diff_file_or_symlink(old_path, old_tree, file_id,
478
                                new_path, new_tree, text_modified,
1092.2.9 by Robert Collins
bugfix _maybe_diff, the test was not catching the error
479
                                kind, to_file, diff_file):
1092.2.6 by Robert Collins
symlink support updated to work
480
    if text_modified:
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
481
        new_entry = new_tree.inventory[file_id]
482
        old_tree.inventory[file_id].diff(diff_file,
1740.2.5 by Aaron Bentley
Merge from bzr.dev
483
                                         old_path, old_tree,
484
                                         new_path, new_entry, 
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
485
                                         new_tree, to_file)