/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
1
# Copyright (C) 2004, 2005, 2006 Canonical Ltd.
1 by mbp at sourcefrog
import from baz patch-364
2
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.
7
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.
12
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
1534.4.35 by Robert Collins
Give branch its own basis tree and last_revision methods; deprecated branch.working_tree()
17
from bzrlib.delta import compare_trees
18
from bzrlib.errors import BzrError
1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
19
import bzrlib.errors as errors
1711.2.24 by John Arbash Meinel
Late bind to PatienceSequenceMatcher to allow plugin to override.
20
from bzrlib.patiencediff import unified_diff
21
import bzrlib.patiencediff
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
22
from bzrlib.symbol_versioning import *
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
23
from bzrlib.textfile import check_text_lines
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
24
from bzrlib.trace import mutter
1 by mbp at sourcefrog
import from baz patch-364
25
1711.2.24 by John Arbash Meinel
Late bind to PatienceSequenceMatcher to allow plugin to override.
26
767 by Martin Pool
- files are only reported as modified if their name or parent has changed,
27
# TODO: Rather than building a changeset object, we should probably
28
# invoke callbacks on an object.  That object can either accumulate a
29
# list, write them out directly, etc etc.
30
1558.15.11 by Aaron Bentley
Apply merge review suggestions
31
def internal_diff(old_filename, oldlines, new_filename, newlines, to_file,
1185.81.18 by Aaron Bentley
Merge from bzr.dev
32
                  allow_binary=False, sequence_matcher=None):
475 by Martin Pool
- rewrite diff using compare_trees()
33
    # FIXME: difflib is wrong if there is no trailing newline.
34
    # The syntax used by patch seems to be "\ No newline at
35
    # end of file" following the last diff line from that
36
    # file.  This is not trivial to insert into the
37
    # unified_diff output and it might be better to just fix
38
    # or replace that function.
39
40
    # In the meantime we at least make sure the patch isn't
41
    # mangled.
42
43
44
    # Special workaround for Python2.3, where difflib fails if
45
    # both sequences are empty.
46
    if not oldlines and not newlines:
47
        return
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
48
    
1558.15.11 by Aaron Bentley
Apply merge review suggestions
49
    if allow_binary is False:
50
        check_text_lines(oldlines)
51
        check_text_lines(newlines)
475 by Martin Pool
- rewrite diff using compare_trees()
52
1185.81.8 by John Arbash Meinel
Updating unified_diff to take a factory, using the new diff algorithm in the code.
53
    if sequence_matcher is None:
1711.2.24 by John Arbash Meinel
Late bind to PatienceSequenceMatcher to allow plugin to override.
54
        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.
55
    ud = unified_diff(oldlines, newlines,
1185.81.12 by John Arbash Meinel
[merge] jam-integration
56
                      fromfile=old_filename+'\t', 
57
                      tofile=new_filename+'\t',
1185.81.8 by John Arbash Meinel
Updating unified_diff to take a factory, using the new diff algorithm in the code.
58
                      sequencematcher=sequence_matcher)
475 by Martin Pool
- rewrite diff using compare_trees()
59
1092.1.50 by Robert Collins
make diff lsdiff/filterdiff friendly
60
    ud = list(ud)
475 by Martin Pool
- rewrite diff using compare_trees()
61
    # work-around for difflib being too smart for its own good
62
    # if /dev/null is "1,0", patch won't recognize it as /dev/null
63
    if not oldlines:
64
        ud[2] = ud[2].replace('-1,0', '-0,0')
65
    elif not newlines:
66
        ud[2] = ud[2].replace('+1,0', '+0,0')
1092.1.50 by Robert Collins
make diff lsdiff/filterdiff friendly
67
    # work around for difflib emitting random spaces after the label
68
    ud[0] = ud[0][:-2] + '\n'
69
    ud[1] = ud[1][:-2] + '\n'
475 by Martin Pool
- rewrite diff using compare_trees()
70
804 by Martin Pool
Patch from John:
71
    for line in ud:
72
        to_file.write(line)
974.1.5 by Aaron Bentley
Fixed handling of missing newlines in udiffs
73
        if not line.endswith('\n'):
74
            to_file.write("\n\\ No newline at end of file\n")
475 by Martin Pool
- rewrite diff using compare_trees()
75
    print >>to_file
76
77
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
78
def external_diff(old_filename, oldlines, new_filename, newlines, to_file,
571 by Martin Pool
- new --diff-options to pass options through to external
79
                  diff_opts):
568 by Martin Pool
- start adding support for showing diffs by calling out to
80
    """Display a diff by calling out to the external diff program."""
81
    import sys
82
    
83
    if to_file != sys.stdout:
84
        raise NotImplementedError("sorry, can't send external diff other than to stdout yet",
85
                                  to_file)
86
581 by Martin Pool
- make sure any bzr output is flushed before
87
    # make sure our own output is properly ordered before the diff
88
    to_file.flush()
89
568 by Martin Pool
- start adding support for showing diffs by calling out to
90
    from tempfile import NamedTemporaryFile
571 by Martin Pool
- new --diff-options to pass options through to external
91
    import os
568 by Martin Pool
- start adding support for showing diffs by calling out to
92
93
    oldtmpf = NamedTemporaryFile()
94
    newtmpf = NamedTemporaryFile()
95
96
    try:
97
        # TODO: perhaps a special case for comparing to or from the empty
98
        # sequence; can just use /dev/null on Unix
99
100
        # TODO: if either of the files being compared already exists as a
101
        # regular named file (e.g. in the working directory) then we can
102
        # compare directly to that, rather than copying it.
103
104
        oldtmpf.writelines(oldlines)
105
        newtmpf.writelines(newlines)
106
107
        oldtmpf.flush()
108
        newtmpf.flush()
109
571 by Martin Pool
- new --diff-options to pass options through to external
110
        if not diff_opts:
111
            diff_opts = []
112
        diffcmd = ['diff',
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
113
                   '--label', old_filename+'\t',
571 by Martin Pool
- new --diff-options to pass options through to external
114
                   oldtmpf.name,
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
115
                   '--label', new_filename+'\t',
571 by Martin Pool
- new --diff-options to pass options through to external
116
                   newtmpf.name]
117
118
        # diff only allows one style to be specified; they don't override.
119
        # note that some of these take optargs, and the optargs can be
120
        # directly appended to the options.
121
        # this is only an approximate parser; it doesn't properly understand
122
        # the grammar.
123
        for s in ['-c', '-u', '-C', '-U',
124
                  '-e', '--ed',
125
                  '-q', '--brief',
126
                  '--normal',
127
                  '-n', '--rcs',
128
                  '-y', '--side-by-side',
129
                  '-D', '--ifdef']:
130
            for j in diff_opts:
131
                if j.startswith(s):
132
                    break
133
            else:
134
                continue
135
            break
136
        else:
137
            diffcmd.append('-u')
138
                  
139
        if diff_opts:
140
            diffcmd.extend(diff_opts)
141
142
        rc = os.spawnvp(os.P_WAIT, 'diff', diffcmd)
143
        
144
        if rc != 0 and rc != 1:
145
            # returns 1 if files differ; that's OK
146
            if rc < 0:
147
                msg = 'signal %d' % (-rc)
148
            else:
149
                msg = 'exit code %d' % rc
150
                
151
            raise BzrError('external diff failed with %s; command: %r' % (rc, diffcmd))
568 by Martin Pool
- start adding support for showing diffs by calling out to
152
    finally:
153
        oldtmpf.close()                 # and delete
154
        newtmpf.close()
155
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
156
157
@deprecated_function(zero_eight)
1432 by Robert Collins
branch: namespace
158
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.
159
              revision2=None, output=None, b2=None):
619 by Martin Pool
doc
160
    """Shortcut for showing the diff to the working tree.
161
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
162
    Please use show_diff_trees instead.
163
619 by Martin Pool
doc
164
    b
165
        Branch.
166
167
    revision
1432 by Robert Collins
branch: namespace
168
        None for 'basis tree', or otherwise the old revision to compare against.
619 by Martin Pool
doc
169
    
170
    The more general form is show_diff_trees(), where the caller
171
    supplies any two trees.
172
    """
1092.1.47 by Robert Collins
make show_diff redirectable
173
    if output is None:
174
        import sys
175
        output = sys.stdout
475 by Martin Pool
- rewrite diff using compare_trees()
176
1432 by Robert Collins
branch: namespace
177
    if from_spec is None:
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
178
        old_tree = b.bzrdir.open_workingtree()
1185.35.28 by Aaron Bentley
Support diff with two branches as input.
179
        if b2 is None:
1534.4.35 by Robert Collins
Give branch its own basis tree and last_revision methods; deprecated branch.working_tree()
180
            old_tree = old_tree = old_tree.basis_tree()
329 by Martin Pool
- refactor command functions into command classes
181
    else:
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
182
        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
183
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
184
    if revision2 is None:
1185.35.28 by Aaron Bentley
Support diff with two branches as input.
185
        if b2 is None:
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
186
            new_tree = b.bzrdir.open_workingtree()
1185.35.28 by Aaron Bentley
Support diff with two branches as input.
187
        else:
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
188
            new_tree = b2.bzrdir.open_workingtree()
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
189
    else:
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
190
        new_tree = b.repository.revision_tree(revision2.in_history(b).rev_id)
329 by Martin Pool
- refactor command functions into command classes
191
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
192
    return show_diff_trees(old_tree, new_tree, output, specific_files,
193
                           external_diff_options)
571 by Martin Pool
- new --diff-options to pass options through to external
194
195
1551.2.15 by Aaron Bentley
Rename cmd_show_diff to diff_cmd_helper
196
def diff_cmd_helper(tree, specific_files, external_diff_options, 
1684.1.6 by Martin Pool
(patch) --diff-prefix option (goffredo, alexander)
197
                    old_revision_spec=None, new_revision_spec=None,
198
                    old_label='a/', new_label='b/'):
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
199
    """Helper for cmd_diff.
200
201
   tree 
202
        A WorkingTree
203
204
    specific_files
205
        The specific files to compare, or None
206
207
    external_diff_options
208
        If non-None, run an external diff, and pass it these options
209
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
210
    old_revision_spec
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
211
        If None, use basis tree as old revision, otherwise use the tree for
212
        the specified revision. 
213
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
214
    new_revision_spec
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
215
        If None, use working tree as new revision, otherwise use the tree for
216
        the specified revision.
217
    
218
    The more general form is show_diff_trees(), where the caller
219
    supplies any two trees.
220
    """
221
    import sys
222
    output = sys.stdout
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
223
    def spec_tree(spec):
224
        revision_id = spec.in_store(tree.branch).rev_id
225
        return tree.branch.repository.revision_tree(revision_id)
226
    if old_revision_spec is None:
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
227
        old_tree = tree.basis_tree()
228
    else:
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
229
        old_tree = spec_tree(old_revision_spec)
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
230
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
231
    if new_revision_spec is None:
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
232
        new_tree = tree
233
    else:
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
234
        new_tree = spec_tree(new_revision_spec)
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
235
1551.2.14 by Aaron Bentley
Updated argument names, DRY fixes.
236
    return show_diff_trees(old_tree, new_tree, sys.stdout, specific_files,
1684.1.6 by Martin Pool
(patch) --diff-prefix option (goffredo, alexander)
237
                           external_diff_options,
238
                           old_label=old_label, new_label=new_label)
1551.2.13 by Aaron Bentley
Got diff working properly with checkouts
239
571 by Martin Pool
- new --diff-options to pass options through to external
240
241
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)
242
                    external_diff_options=None,
243
                    old_label='a/', new_label='b/'):
550 by Martin Pool
- Refactor diff code into one that works purely on
244
    """Show in text form the changes from one tree to another.
245
246
    to_files
247
        If set, include only changes to these files.
571 by Martin Pool
- new --diff-options to pass options through to external
248
249
    external_diff_options
250
        If set, use an external GNU diff and pass these options.
550 by Martin Pool
- Refactor diff code into one that works purely on
251
    """
1543.1.1 by Denys Duchier
lock operations for trees - use them for diff
252
    old_tree.lock_read()
253
    try:
254
        new_tree.lock_read()
255
        try:
256
            return _show_diff_trees(old_tree, new_tree, to_file,
1684.1.6 by Martin Pool
(patch) --diff-prefix option (goffredo, alexander)
257
                                    specific_files, external_diff_options,
258
                                    old_label=old_label, new_label=new_label)
1543.1.1 by Denys Duchier
lock operations for trees - use them for diff
259
        finally:
260
            new_tree.unlock()
261
    finally:
262
        old_tree.unlock()
263
264
265
def _show_diff_trees(old_tree, new_tree, to_file,
1684.1.6 by Martin Pool
(patch) --diff-prefix option (goffredo, alexander)
266
                     specific_files, external_diff_options, 
267
                     old_label='a/', new_label='b/' ):
329 by Martin Pool
- refactor command functions into command classes
268
269
    DEVNULL = '/dev/null'
270
    # Windows users, don't panic about this filename -- it is a
271
    # special signal to GNU patch that the file should be created or
272
    # deleted respectively.
273
274
    # TODO: Generation of pseudo-diffs for added/deleted files could
275
    # be usefully made into a much faster special case.
276
1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
277
    _raise_if_doubly_unversioned(specific_files, old_tree, new_tree)
278
571 by Martin Pool
- new --diff-options to pass options through to external
279
    if external_diff_options:
280
        assert isinstance(external_diff_options, basestring)
281
        opts = external_diff_options.split()
282
        def diff_file(olab, olines, nlab, nlines, to_file):
283
            external_diff(olab, olines, nlab, nlines, to_file, opts)
284
    else:
285
        diff_file = internal_diff
286
    
478 by Martin Pool
- put back support for running diff or status on
287
    delta = compare_trees(old_tree, new_tree, want_unchanged=False,
483 by Martin Pool
- change 'file_list' to more explanatory 'specific_files'
288
                          specific_files=specific_files)
475 by Martin Pool
- rewrite diff using compare_trees()
289
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
290
    has_changes = 0
475 by Martin Pool
- rewrite diff using compare_trees()
291
    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.
292
        has_changes = 1
1694.2.4 by Martin Pool
When a diff prefix is given, don't show it in === summary lines, only on the diffs themselves.
293
        print >>to_file, '=== removed %s %r' % (kind, path)
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
294
        old_tree.inventory[file_id].diff(diff_file, old_label + path, old_tree,
295
                                         DEVNULL, None, None, to_file)
475 by Martin Pool
- rewrite diff using compare_trees()
296
    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.
297
        has_changes = 1
1694.2.4 by Martin Pool
When a diff prefix is given, don't show it in === summary lines, only on the diffs themselves.
298
        print >>to_file, '=== added %s %r' % (kind, path)
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
299
        new_tree.inventory[file_id].diff(diff_file, new_label + path, new_tree,
300
                                         DEVNULL, None, None, to_file, 
301
                                         reverse=True)
1398 by Robert Collins
integrate in Gustavos x-bit patch
302
    for (old_path, new_path, file_id, kind,
303
         text_modified, meta_modified) in delta.renamed:
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
304
        has_changes = 1
1398 by Robert Collins
integrate in Gustavos x-bit patch
305
        prop_str = get_prop_change(meta_modified)
306
        print >>to_file, '=== renamed %s %r => %r%s' % (
1694.2.4 by Martin Pool
When a diff prefix is given, don't show it in === summary lines, only on the diffs themselves.
307
                    kind, old_path, new_path, prop_str)
1092.2.6 by Robert Collins
symlink support updated to work
308
        _maybe_diff_file_or_symlink(old_label, old_path, old_tree, file_id,
309
                                    new_label, new_path, new_tree,
1092.2.9 by Robert Collins
bugfix _maybe_diff, the test was not catching the error
310
                                    text_modified, kind, to_file, diff_file)
1398 by Robert Collins
integrate in Gustavos x-bit patch
311
    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.
312
        has_changes = 1
1398 by Robert Collins
integrate in Gustavos x-bit patch
313
        prop_str = get_prop_change(meta_modified)
1694.2.4 by Martin Pool
When a diff prefix is given, don't show it in === summary lines, only on the diffs themselves.
314
        print >>to_file, '=== modified %s %r%s' % (kind, path, prop_str)
1398 by Robert Collins
integrate in Gustavos x-bit patch
315
        if text_modified:
316
            _maybe_diff_file_or_symlink(old_label, path, old_tree, file_id,
317
                                        new_label, path, new_tree,
318
                                        True, kind, to_file, diff_file)
1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
319
1490 by Robert Collins
Implement a 'bzr push' command, with saved locations; update diff to return 1.
320
    return has_changes
1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
321
322
323
def _raise_if_doubly_unversioned(specific_files, old_tree, new_tree):
324
    """Complain if paths are not versioned in either tree."""
1662.1.12 by Martin Pool
Translate unknown sftp errors to PathError, no NoSuchFile
325
    if not specific_files:
1658.1.10 by Martin Pool
diff on unversiond files should give an error (Malone #3619)
326
        return
1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
327
    old_unversioned = old_tree.filter_unversioned_files(specific_files)
328
    new_unversioned = new_tree.filter_unversioned_files(specific_files)
329
    unversioned = old_unversioned.intersection(new_unversioned)
330
    if unversioned:
331
        raise errors.PathsNotVersionedError(sorted(unversioned))
1092.3.4 by Robert Collins
update symlink branch to integration
332
    
1092.2.6 by Robert Collins
symlink support updated to work
333
1662.1.9 by Martin Pool
Give a clear error for bzr status of an unversioned, nonexistent file. (Malone #3619)
334
def _raise_if_nonexistent(paths, old_tree, new_tree):
335
    """Complain if paths are not in either inventory or tree.
336
337
    It's OK with the files exist in either tree's inventory, or 
338
    if they exist in the tree but are not versioned.
339
    
340
    This can be used by operations such as bzr status that can accept
341
    unknown or ignored files.
342
    """
343
    mutter("check paths: %r", paths)
344
    if not paths:
345
        return
346
    s = old_tree.filter_unversioned_files(paths)
347
    s = new_tree.filter_unversioned_files(s)
348
    s = [path for path in s if not new_tree.has_filename(path)]
349
    if s:
350
        raise errors.PathsDoNotExist(sorted(s))
351
352
1398 by Robert Collins
integrate in Gustavos x-bit patch
353
def get_prop_change(meta_modified):
354
    if meta_modified:
355
        return " (properties changed)"
356
    else:
357
        return  ""
358
359
1092.2.6 by Robert Collins
symlink support updated to work
360
def _maybe_diff_file_or_symlink(old_label, old_path, old_tree, file_id,
361
                                new_label, new_path, new_tree, text_modified,
1092.2.9 by Robert Collins
bugfix _maybe_diff, the test was not catching the error
362
                                kind, to_file, diff_file):
1092.2.6 by Robert Collins
symlink support updated to work
363
    if text_modified:
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
364
        new_entry = new_tree.inventory[file_id]
365
        old_tree.inventory[file_id].diff(diff_file,
366
                                         old_label + old_path, old_tree,
367
                                         new_label + new_path, new_entry, 
368
                                         new_tree, to_file)