/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/delta.py

  • Committer: Jelmer Vernooij
  • Date: 2019-06-03 03:10:29 UTC
  • mfrom: (7312 work)
  • mto: This revision was merged to the branch mainline in revision 7318.
  • Revision ID: jelmer@jelmer.uk-20190603031029-b34je03bjulxxdwj
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from bzrlib import (
 
17
from __future__ import absolute_import
 
18
 
 
19
from breezy import (
18
20
    osutils,
19
 
    )
20
 
from bzrlib.trace import is_quiet
 
21
    trace,
 
22
    )
 
23
from .sixish import (
 
24
    StringIO,
 
25
    )
21
26
 
22
27
 
23
28
class TreeDelta(object):
53
58
 
54
59
    The lists are normally sorted when the delta is created.
55
60
    """
 
61
 
56
62
    def __init__(self):
57
63
        self.added = []
58
64
        self.removed = []
61
67
        self.modified = []
62
68
        self.unchanged = []
63
69
        self.unversioned = []
 
70
        self.missing = []
64
71
 
65
72
    def __eq__(self, other):
66
73
        if not isinstance(other, TreeDelta):
67
74
            return False
68
75
        return self.added == other.added \
69
 
               and self.removed == other.removed \
70
 
               and self.renamed == other.renamed \
71
 
               and self.modified == other.modified \
72
 
               and self.unchanged == other.unchanged \
73
 
               and self.kind_changed == other.kind_changed \
74
 
               and self.unversioned == other.unversioned
 
76
            and self.removed == other.removed \
 
77
            and self.renamed == other.renamed \
 
78
            and self.modified == other.modified \
 
79
            and self.unchanged == other.unchanged \
 
80
            and self.kind_changed == other.kind_changed \
 
81
            and self.unversioned == other.unversioned
75
82
 
76
83
    def __ne__(self, other):
77
84
        return not (self == other)
80
87
        return "TreeDelta(added=%r, removed=%r, renamed=%r," \
81
88
            " kind_changed=%r, modified=%r, unchanged=%r," \
82
89
            " unversioned=%r)" % (self.added,
83
 
            self.removed, self.renamed, self.kind_changed, self.modified,
84
 
            self.unchanged, self.unversioned)
 
90
                                  self.removed, self.renamed, self.kind_changed, self.modified,
 
91
                                  self.unchanged, self.unversioned)
85
92
 
86
93
    def has_changed(self):
87
94
        return bool(self.modified
106
113
 
107
114
    def get_changes_as_text(self, show_ids=False, show_unchanged=False,
108
115
                            short_status=False):
109
 
        import StringIO
110
 
        output = StringIO.StringIO()
 
116
        output = StringIO()
111
117
        report_delta(output, self, short_status, show_ids, show_unchanged)
112
118
        return output.getvalue()
113
119
 
121
127
 
122
128
    for (file_id, path, content_change, versioned, parent_id, name, kind,
123
129
         executable) in new_tree.iter_changes(old_tree, want_unchanged,
124
 
            specific_files, extra_trees=extra_trees,
125
 
            require_versioned=require_versioned,
126
 
            want_unversioned=want_unversioned):
 
130
                                              specific_files, extra_trees=extra_trees,
 
131
                                              require_versioned=require_versioned,
 
132
                                              want_unversioned=want_unversioned):
127
133
        if versioned == (False, False):
128
134
            delta.unversioned.append((path[1], None, kind[1]))
129
135
            continue
135
141
            if fully_present[1] is True:
136
142
                delta.added.append((path[1], file_id, kind[1]))
137
143
            else:
138
 
                delta.removed.append((path[0], file_id, kind[0]))
 
144
                if kind[0] == 'symlink' and not new_tree.supports_symlinks():
 
145
                    trace.warning('Ignoring "%s" as symlinks '
 
146
                        'are not supported on this filesystem.' % (path[0],))
 
147
                else:
 
148
                    delta.removed.append((path[0], file_id, kind[0]))
139
149
        elif fully_present[0] is False:
140
 
            continue
 
150
            delta.missing.append((path[1], file_id, kind[1]))
141
151
        elif name[0] != name[1] or parent_id[0] != parent_id[1]:
142
152
            # If the name changes, or the parent_id changes, we have a rename
143
153
            # (if we move a parent, that doesn't count as a rename for the
160
170
    delta.removed.sort()
161
171
    delta.added.sort()
162
172
    delta.renamed.sort()
 
173
 
 
174
    def missing_key(change):
 
175
        return (change[0] or '', change[1])
 
176
    delta.missing.sort(key=missing_key)
163
177
    # TODO: jam 20060529 These lists shouldn't need to be sorted
164
178
    #       since we added them in alphabetical order.
165
179
    delta.modified.sort()
166
180
    delta.unchanged.sort()
 
181
    delta.unversioned.sort()
167
182
 
168
183
    return delta
169
184
 
172
187
    """Report changes between two trees"""
173
188
 
174
189
    def __init__(self, output=None, suppress_root_add=True,
175
 
                 output_file=None, unversioned_filter=None, view_info=None):
 
190
                 output_file=None, unversioned_filter=None, view_info=None,
 
191
                 classify=True):
176
192
        """Constructor
177
193
 
178
194
        :param output: a function with the signature of trace.note, i.e.
187
203
        :param view_info: A tuple of view_name,view_files if only
188
204
            items inside a view are to be reported on, or None for
189
205
            no view filtering.
 
206
        :param classify: Add special symbols to indicate file kind.
190
207
        """
191
208
        if output_file is not None:
192
209
            if output is not None:
193
210
                raise BzrError('Cannot specify both output and output_file')
 
211
 
194
212
            def output(fmt, *args):
195
213
                output_file.write((fmt % args) + '\n')
196
214
        self.output = output
197
215
        if self.output is None:
198
 
            from bzrlib import trace
 
216
            from . import trace
199
217
            self.output = trace.note
200
218
        self.suppress_root_add = suppress_root_add
201
219
        self.modified_map = {'kind changed': 'K',
202
220
                             'unchanged': ' ',
203
221
                             'created': 'N',
204
222
                             'modified': 'M',
205
 
                             'deleted': 'D'}
206
 
        self.versioned_map = {'added': '+', # versioned target
207
 
                              'unchanged': ' ', # versioned in both
208
 
                              'removed': '-', # versioned in source
209
 
                              'unversioned': '?', # versioned in neither
 
223
                             'deleted': 'D',
 
224
                             'missing': '!',
 
225
                             }
 
226
        self.versioned_map = {'added': '+',  # versioned target
 
227
                              'unchanged': ' ',  # versioned in both
 
228
                              'removed': '-',  # versioned in source
 
229
                              'unversioned': '?',  # versioned in neither
210
230
                              }
211
231
        self.unversioned_filter = unversioned_filter
 
232
        if classify:
 
233
            self.kind_marker = osutils.kind_marker
 
234
        else:
 
235
            self.kind_marker = lambda kind: ''
212
236
        if view_info is None:
213
237
            self.view_name = None
214
238
            self.view_files = []
233
257
        :param kind: A pair of file kinds, as generated by Tree.iter_changes.
234
258
            None indicates no file present.
235
259
        """
236
 
        if is_quiet():
 
260
        if trace.is_quiet():
237
261
            return
238
262
        if paths[1] == '' and versioned == 'added' and self.suppress_root_add:
239
263
            return
240
264
        if self.view_files and not osutils.is_inside_any(self.view_files,
241
 
            paths[1]):
 
265
                                                         paths[1]):
242
266
            return
243
267
        if versioned == 'unversioned':
244
268
            # skip ignored unversioned files if needed.
252
276
        # ( the path is different OR
253
277
        #   the kind is different)
254
278
        if (versioned == 'unchanged' and
255
 
            (renamed or modified == 'kind changed')):
 
279
                (renamed or modified == 'kind changed')):
256
280
            if renamed:
257
281
                # on a rename, we show old and new
258
282
                old_path, path = paths
263
287
            # if the file is not missing in the source, we show its kind
264
288
            # when we show two paths.
265
289
            if kind[0] is not None:
266
 
                old_path += osutils.kind_marker(kind[0])
 
290
                old_path += self.kind_marker(kind[0])
267
291
            old_path += " => "
268
292
        elif versioned == 'removed':
269
293
            # not present in target
278
302
            rename = self.versioned_map[versioned]
279
303
        # we show the old kind on the new path when the content is deleted.
280
304
        if modified == 'deleted':
281
 
            path += osutils.kind_marker(kind[0])
 
305
            path += self.kind_marker(kind[0])
282
306
        # otherwise we always show the current kind when there is one
283
307
        elif kind[1] is not None:
284
 
            path += osutils.kind_marker(kind[1])
 
308
            path += self.kind_marker(kind[1])
285
309
        if exe_change:
286
310
            exe = '*'
287
311
        else:
289
313
        self.output("%s%s%s %s%s", rename, self.modified_map[modified], exe,
290
314
                    old_path, path)
291
315
 
 
316
 
292
317
def report_changes(change_iterator, reporter):
293
318
    """Report the changes from a change iterator.
294
319
 
300
325
    :param reporter: The _ChangeReporter that will report the changes.
301
326
    """
302
327
    versioned_change_map = {
303
 
        (True, True)  : 'unchanged',
304
 
        (True, False) : 'removed',
305
 
        (False, True) : 'added',
 
328
        (True, True): 'unchanged',
 
329
        (True, False): 'removed',
 
330
        (False, True): 'added',
306
331
        (False, False): 'unversioned',
307
332
        }
 
333
 
 
334
    def path_key(change):
 
335
        if change[1][0] is not None:
 
336
            path = change[1][0]
 
337
        else:
 
338
            path = change[1][1]
 
339
        return osutils.splitpath(path)
308
340
    for (file_id, path, content_change, versioned, parent_id, name, kind,
309
 
         executable) in change_iterator:
 
341
         executable) in sorted(change_iterator, key=path_key):
310
342
        exe_change = False
311
343
        # files are "renamed" if they are moved or if name changes, as long
312
344
        # as it had a value
313
345
        if None not in name and None not in parent_id and\
314
 
            (name[0] != name[1] or parent_id[0] != parent_id[1]):
 
346
                (name[0] != name[1] or parent_id[0] != parent_id[1]):
315
347
            renamed = True
316
348
        else:
317
349
            renamed = False
325
357
        else:
326
358
            if content_change:
327
359
                modified = "modified"
 
360
            elif kind[0] is None:
 
361
                modified = "missing"
328
362
            else:
329
363
                modified = "unchanged"
330
364
            if kind[1] == "file":
333
367
        reporter.report(file_id, path, versioned_change, renamed, modified,
334
368
                        exe_change, kind)
335
369
 
336
 
def report_delta(to_file, delta, short_status=False, show_ids=False, 
337
 
         show_unchanged=False, indent='', filter=None):
 
370
 
 
371
def report_delta(to_file, delta, short_status=False, show_ids=False,
 
372
                 show_unchanged=False, indent='', predicate=None, classify=True):
338
373
    """Output this delta in status-like form to to_file.
339
374
 
340
375
    :param to_file: A file-like object where the output is displayed.
350
385
    :param indent: Added at the beginning of all output lines (for merged
351
386
        revisions).
352
387
 
353
 
    :param filter: A callable receiving a path and a file id and
 
388
    :param predicate: A callable receiving a path and a file id and
354
389
        returning True if the path should be displayed.
 
390
 
 
391
    :param classify: Add special symbols to indicate file kind.
355
392
    """
356
393
 
357
394
    def decorate_path(path, kind, meta_modified=None):
 
395
        if not classify:
 
396
            return path
358
397
        if kind == 'directory':
359
398
            path += '/'
360
399
        elif kind == 'symlink':
397
436
 
398
437
            for item in files:
399
438
                path, file_id, kind = item[:3]
400
 
                if (filter is not None and not filter(path, file_id)):
 
439
                if (predicate is not None and not predicate(path, file_id)):
401
440
                    continue
402
441
                if not header_shown and not short_status:
403
442
                    to_file.write(indent + long_status_name + ':\n')
412
451
                if show_more is not None:
413
452
                    show_more(item)
414
453
                if show_ids:
415
 
                    to_file.write(' %s' % file_id)
 
454
                    to_file.write(' %s' % file_id.decode('utf-8'))
416
455
                to_file.write('\n')
417
456
 
418
457
    show_list(delta.removed, 'removed', 'D')
419
458
    show_list(delta.added, 'added', 'A')
 
459
    show_list(delta.missing, 'missing', '!')
420
460
    extra_modified = []
421
461
    # Reorder delta.renamed tuples so that all lists share the same
422
462
    # order for their 3 first fields and that they also begin like
423
463
    # the delta.modified tuples
424
464
    renamed = [(p, i, k, tm, mm, np)
425
 
               for  p, np, i, k, tm, mm  in delta.renamed]
 
465
               for p, np, i, k, tm, mm in delta.renamed]
426
466
    show_list(renamed, 'renamed', 'R', with_file_id_format='%s',
427
467
              show_more=show_more_renamed)
428
468
    show_list(delta.kind_changed, 'kind changed', 'K',
433
473
        show_list(delta.unchanged, 'unchanged', 'S')
434
474
 
435
475
    show_list(delta.unversioned, 'unknown', ' ')
436