~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/delta.py

(gz) Backslash escape selftest output when printing to non-unicode consoles
 (Martin [gz])

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
from bzrlib import (
18
 
    errors,
19
18
    osutils,
20
19
    )
21
 
from bzrlib.inventory import InventoryEntry
22
 
from bzrlib.trace import mutter, is_quiet
23
 
from bzrlib.symbol_versioning import deprecated_function
 
20
from bzrlib.trace import is_quiet
24
21
 
25
22
 
26
23
class TreeDelta(object):
64
61
        self.modified = []
65
62
        self.unchanged = []
66
63
        self.unversioned = []
 
64
        self.missing = []
67
65
 
68
66
    def __eq__(self, other):
69
67
        if not isinstance(other, TreeDelta):
107
105
                return True
108
106
        return False
109
107
 
110
 
 
111
 
    def show(self, to_file, show_ids=False, show_unchanged=False,
112
 
             short_status=False, indent='',
113
 
             filter=None):
114
 
        """Output this delta in status-like form to to_file.
115
 
 
116
 
        :param to_file: A file-like object where the output is displayed.
117
 
 
118
 
        :param show_ids: Output the file ids if True.
119
 
 
120
 
        :param show_unchanged: Output the unchanged files if True.
121
 
 
122
 
        :param short_status: Single-line status if True.
123
 
 
124
 
        :param indent: Added at the beginning of all output lines (for merged
125
 
            revisions).
126
 
 
127
 
        :param filter: A callable receiving a path and a file id and
128
 
            returning True if the path should be displayed.
129
 
        """
130
 
 
131
 
        def decorate_path(path, kind, meta_modified=None):
132
 
            if kind == 'directory':
133
 
                path += '/'
134
 
            elif kind == 'symlink':
135
 
                path += '@'
136
 
            if meta_modified:
137
 
                path += '*'
138
 
            return path
139
 
 
140
 
        def show_more_renamed(item):
141
 
            (oldpath, file_id, kind,
142
 
             text_modified, meta_modified, newpath) = item
143
 
            dec_new_path = decorate_path(newpath, kind, meta_modified)
144
 
            to_file.write(' => %s' % dec_new_path)
145
 
            if text_modified or meta_modified:
146
 
                extra_modified.append((newpath, file_id, kind,
147
 
                                       text_modified, meta_modified))
148
 
 
149
 
        def show_more_kind_changed(item):
150
 
            (path, file_id, old_kind, new_kind) = item
151
 
            to_file.write(' (%s => %s)' % (old_kind, new_kind))
152
 
 
153
 
        def show_path(path, file_id, kind, meta_modified,
154
 
                      default_format, with_file_id_format):
155
 
            dec_path = decorate_path(path, kind, meta_modified)
156
 
            if show_ids:
157
 
                to_file.write(with_file_id_format % dec_path)
158
 
            else:
159
 
                to_file.write(default_format % dec_path)
160
 
 
161
 
        def show_list(files, long_status_name, short_status_letter,
162
 
                      default_format='%s', with_file_id_format='%-30s',
163
 
                      show_more=None):
164
 
            if files:
165
 
                header_shown = False
166
 
                if short_status:
167
 
                    prefix = short_status_letter
168
 
                else:
169
 
                    prefix = ''
170
 
                prefix = indent + prefix + '  '
171
 
 
172
 
                for item in files:
173
 
                    path, file_id, kind = item[:3]
174
 
                    if (filter is not None and not filter(path, file_id)):
175
 
                        continue
176
 
                    if not header_shown and not short_status:
177
 
                        to_file.write(indent + long_status_name + ':\n')
178
 
                        header_shown = True
179
 
                    meta_modified = None
180
 
                    if len(item) == 5:
181
 
                        meta_modified = item[4]
182
 
 
183
 
                    to_file.write(prefix)
184
 
                    show_path(path, file_id, kind, meta_modified,
185
 
                              default_format, with_file_id_format)
186
 
                    if show_more is not None:
187
 
                        show_more(item)
188
 
                    if show_ids:
189
 
                        to_file.write(' %s' % file_id)
190
 
                    to_file.write('\n')
191
 
 
192
 
        show_list(self.removed, 'removed', 'D')#
193
 
        show_list(self.added, 'added', 'A')
194
 
        extra_modified = []
195
 
        # Reorder self.renamed tuples so that all lists share the same
196
 
        # order for their 3 first fields and that they also begin like
197
 
        # the self.modified tuples
198
 
        renamed = [(p, i, k, tm, mm, np)
199
 
                   for  p, np, i, k, tm, mm  in self.renamed]
200
 
        show_list(renamed, 'renamed', 'R', with_file_id_format='%s',
201
 
                  show_more=show_more_renamed)
202
 
        show_list(self.kind_changed, 'kind changed', 'K',
203
 
                  with_file_id_format='%s',
204
 
                  show_more=show_more_kind_changed)
205
 
        show_list(self.modified + extra_modified, 'modified', 'M')
206
 
        if show_unchanged:
207
 
            show_list(self.unchanged, 'unchanged', 'S')
208
 
 
209
 
        show_list(self.unversioned, 'unknown', ' ')
210
 
 
211
108
    def get_changes_as_text(self, show_ids=False, show_unchanged=False,
212
 
             short_status=False):
 
109
                            short_status=False):
213
110
        import StringIO
214
111
        output = StringIO.StringIO()
215
 
        self.show(output, show_ids, show_unchanged, short_status)
 
112
        report_delta(output, self, short_status, show_ids, show_unchanged)
216
113
        return output.getvalue()
217
114
 
218
115
 
241
138
            else:
242
139
                delta.removed.append((path[0], file_id, kind[0]))
243
140
        elif fully_present[0] is False:
244
 
            continue
 
141
            delta.missing.append((path[1], file_id, kind[1]))
245
142
        elif name[0] != name[1] or parent_id[0] != parent_id[1]:
246
143
            # If the name changes, or the parent_id changes, we have a rename
247
144
            # (if we move a parent, that doesn't count as a rename for the
264
161
    delta.removed.sort()
265
162
    delta.added.sort()
266
163
    delta.renamed.sort()
 
164
    delta.missing.sort()
267
165
    # TODO: jam 20060529 These lists shouldn't need to be sorted
268
166
    #       since we added them in alphabetical order.
269
167
    delta.modified.sort()
306
204
                             'unchanged': ' ',
307
205
                             'created': 'N',
308
206
                             'modified': 'M',
309
 
                             'deleted': 'D'}
 
207
                             'deleted': 'D',
 
208
                             'missing': '!',
 
209
                             }
310
210
        self.versioned_map = {'added': '+', # versioned target
311
211
                              'unchanged': ' ', # versioned in both
312
212
                              'removed': '-', # versioned in source
393
293
        self.output("%s%s%s %s%s", rename, self.modified_map[modified], exe,
394
294
                    old_path, path)
395
295
 
396
 
 
397
296
def report_changes(change_iterator, reporter):
398
297
    """Report the changes from a change iterator.
399
298
 
430
329
        else:
431
330
            if content_change:
432
331
                modified = "modified"
 
332
            elif kind[0] is None:
 
333
                modified = "missing"
433
334
            else:
434
335
                modified = "unchanged"
435
336
            if kind[1] == "file":
437
338
        versioned_change = versioned_change_map[versioned]
438
339
        reporter.report(file_id, path, versioned_change, renamed, modified,
439
340
                        exe_change, kind)
 
341
 
 
342
def report_delta(to_file, delta, short_status=False, show_ids=False, 
 
343
         show_unchanged=False, indent='', filter=None):
 
344
    """Output this delta in status-like form to to_file.
 
345
 
 
346
    :param to_file: A file-like object where the output is displayed.
 
347
 
 
348
    :param delta: A TreeDelta containing the changes to be displayed
 
349
 
 
350
    :param short_status: Single-line status if True.
 
351
 
 
352
    :param show_ids: Output the file ids if True.
 
353
 
 
354
    :param show_unchanged: Output the unchanged files if True.
 
355
 
 
356
    :param indent: Added at the beginning of all output lines (for merged
 
357
        revisions).
 
358
 
 
359
    :param filter: A callable receiving a path and a file id and
 
360
        returning True if the path should be displayed.
 
361
    """
 
362
 
 
363
    def decorate_path(path, kind, meta_modified=None):
 
364
        if kind == 'directory':
 
365
            path += '/'
 
366
        elif kind == 'symlink':
 
367
            path += '@'
 
368
        if meta_modified:
 
369
            path += '*'
 
370
        return path
 
371
 
 
372
    def show_more_renamed(item):
 
373
        (oldpath, file_id, kind,
 
374
         text_modified, meta_modified, newpath) = item
 
375
        dec_new_path = decorate_path(newpath, kind, meta_modified)
 
376
        to_file.write(' => %s' % dec_new_path)
 
377
        if text_modified or meta_modified:
 
378
            extra_modified.append((newpath, file_id, kind,
 
379
                                   text_modified, meta_modified))
 
380
 
 
381
    def show_more_kind_changed(item):
 
382
        (path, file_id, old_kind, new_kind) = item
 
383
        to_file.write(' (%s => %s)' % (old_kind, new_kind))
 
384
 
 
385
    def show_path(path, file_id, kind, meta_modified,
 
386
                  default_format, with_file_id_format):
 
387
        dec_path = decorate_path(path, kind, meta_modified)
 
388
        if show_ids:
 
389
            to_file.write(with_file_id_format % dec_path)
 
390
        else:
 
391
            to_file.write(default_format % dec_path)
 
392
 
 
393
    def show_list(files, long_status_name, short_status_letter,
 
394
                  default_format='%s', with_file_id_format='%-30s',
 
395
                  show_more=None):
 
396
        if files:
 
397
            header_shown = False
 
398
            if short_status:
 
399
                prefix = short_status_letter
 
400
            else:
 
401
                prefix = ''
 
402
            prefix = indent + prefix + '  '
 
403
 
 
404
            for item in files:
 
405
                path, file_id, kind = item[:3]
 
406
                if (filter is not None and not filter(path, file_id)):
 
407
                    continue
 
408
                if not header_shown and not short_status:
 
409
                    to_file.write(indent + long_status_name + ':\n')
 
410
                    header_shown = True
 
411
                meta_modified = None
 
412
                if len(item) == 5:
 
413
                    meta_modified = item[4]
 
414
 
 
415
                to_file.write(prefix)
 
416
                show_path(path, file_id, kind, meta_modified,
 
417
                          default_format, with_file_id_format)
 
418
                if show_more is not None:
 
419
                    show_more(item)
 
420
                if show_ids:
 
421
                    to_file.write(' %s' % file_id)
 
422
                to_file.write('\n')
 
423
 
 
424
    show_list(delta.removed, 'removed', 'D')
 
425
    show_list(delta.added, 'added', 'A')
 
426
    show_list(delta.missing, 'missing', '!')
 
427
    extra_modified = []
 
428
    # Reorder delta.renamed tuples so that all lists share the same
 
429
    # order for their 3 first fields and that they also begin like
 
430
    # the delta.modified tuples
 
431
    renamed = [(p, i, k, tm, mm, np)
 
432
               for  p, np, i, k, tm, mm  in delta.renamed]
 
433
    show_list(renamed, 'renamed', 'R', with_file_id_format='%s',
 
434
              show_more=show_more_renamed)
 
435
    show_list(delta.kind_changed, 'kind changed', 'K',
 
436
              with_file_id_format='%s',
 
437
              show_more=show_more_kind_changed)
 
438
    show_list(delta.modified + extra_modified, 'modified', 'M')
 
439
    if show_unchanged:
 
440
        show_list(delta.unchanged, 'unchanged', 'S')
 
441
 
 
442
    show_list(delta.unversioned, 'unknown', ' ')
 
443