/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: 2018-05-06 11:48:54 UTC
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@jelmer.uk-20180506114854-h4qd9ojaqy8wxjsd
Move .mailmap to root.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
from breezy import (
20
20
    osutils,
21
 
    trace,
22
21
    )
23
22
from .sixish import (
24
23
    StringIO,
25
24
    )
26
 
from .tree import TreeChange
 
25
from .trace import is_quiet
27
26
 
28
27
 
29
28
class TreeDelta(object):
30
29
    """Describes changes from one tree to another.
31
30
 
32
 
    Contains seven lists with TreeChange objects.
 
31
    Contains seven lists:
33
32
 
34
33
    added
 
34
        (path, id, kind)
35
35
    removed
 
36
        (path, id, kind)
36
37
    renamed
37
 
    copied
 
38
        (oldpath, newpath, id, kind, text_modified, meta_modified)
38
39
    kind_changed
 
40
        (path, id, old_kind, new_kind)
39
41
    modified
 
42
        (path, id, kind, text_modified, meta_modified)
40
43
    unchanged
 
44
        (path, id, kind)
41
45
    unversioned
 
46
        (path, None, kind)
42
47
 
43
48
    Each id is listed only once.
44
49
 
45
 
    Files that are both modified and renamed or copied are listed only in
46
 
    renamed or copied, with the text_modified flag true. The text_modified
 
50
    Files that are both modified and renamed are listed only in
 
51
    renamed, with the text_modified flag true. The text_modified
47
52
    applies either to the content of the file or the target of the
48
53
    symbolic link, depending of the kind of file.
49
54
 
53
58
 
54
59
    The lists are normally sorted when the delta is created.
55
60
    """
56
 
 
57
61
    def __init__(self):
58
62
        self.added = []
59
63
        self.removed = []
60
64
        self.renamed = []
61
 
        self.copied = []
62
65
        self.kind_changed = []
63
66
        self.modified = []
64
67
        self.unchanged = []
69
72
        if not isinstance(other, TreeDelta):
70
73
            return False
71
74
        return self.added == other.added \
72
 
            and self.removed == other.removed \
73
 
            and self.renamed == other.renamed \
74
 
            and self.copied == other.copied \
75
 
            and self.modified == other.modified \
76
 
            and self.unchanged == other.unchanged \
77
 
            and self.kind_changed == other.kind_changed \
78
 
            and self.unversioned == other.unversioned
 
75
               and self.removed == other.removed \
 
76
               and self.renamed == other.renamed \
 
77
               and self.modified == other.modified \
 
78
               and self.unchanged == other.unchanged \
 
79
               and self.kind_changed == other.kind_changed \
 
80
               and self.unversioned == other.unversioned
79
81
 
80
82
    def __ne__(self, other):
81
83
        return not (self == other)
82
84
 
83
85
    def __repr__(self):
84
86
        return "TreeDelta(added=%r, removed=%r, renamed=%r," \
85
 
            " copied=%r, kind_changed=%r, modified=%r, unchanged=%r," \
86
 
            " unversioned=%r)" % (
87
 
                self.added, self.removed, self.renamed, self.copied,
88
 
                self.kind_changed, self.modified, self.unchanged,
89
 
                self.unversioned)
 
87
            " kind_changed=%r, modified=%r, unchanged=%r," \
 
88
            " unversioned=%r)" % (self.added,
 
89
            self.removed, self.renamed, self.kind_changed, self.modified,
 
90
            self.unchanged, self.unversioned)
90
91
 
91
92
    def has_changed(self):
92
93
        return bool(self.modified
93
94
                    or self.added
94
95
                    or self.removed
95
96
                    or self.renamed
96
 
                    or self.copied
97
97
                    or self.kind_changed)
98
98
 
 
99
    def touches_file_id(self, file_id):
 
100
        """Return True if file_id is modified by this delta."""
 
101
        for l in self.added, self.removed, self.modified:
 
102
            for v in l:
 
103
                if v[1] == file_id:
 
104
                    return True
 
105
        for v in self.renamed:
 
106
            if v[2] == file_id:
 
107
                return True
 
108
        for v in self.kind_changed:
 
109
            if v[1] == file_id:
 
110
                return True
 
111
        return False
 
112
 
99
113
    def get_changes_as_text(self, show_ids=False, show_unchanged=False,
100
114
                            short_status=False):
101
115
        output = StringIO()
110
124
    delta = TreeDelta()
111
125
    # mutter('start compare_trees')
112
126
 
113
 
    for change in new_tree.iter_changes(
114
 
            old_tree, want_unchanged, specific_files, extra_trees=extra_trees,
 
127
    for (file_id, path, content_change, versioned, parent_id, name, kind,
 
128
         executable) in new_tree.iter_changes(old_tree, want_unchanged,
 
129
            specific_files, extra_trees=extra_trees,
115
130
            require_versioned=require_versioned,
116
131
            want_unversioned=want_unversioned):
117
 
        if change.versioned == (False, False):
118
 
            delta.unversioned.append(change)
119
 
            continue
120
 
        if not include_root and (None, None) == change.parent_id:
121
 
            continue
122
 
        fully_present = tuple(
123
 
            (change.versioned[x] and change.kind[x] is not None)
124
 
            for x in range(2))
 
132
        if versioned == (False, False):
 
133
            delta.unversioned.append((path[1], None, kind[1]))
 
134
            continue
 
135
        if not include_root and (None, None) == parent_id:
 
136
            continue
 
137
        fully_present = tuple((versioned[x] and kind[x] is not None) for
 
138
                              x in range(2))
125
139
        if fully_present[0] != fully_present[1]:
126
140
            if fully_present[1] is True:
127
 
                delta.added.append(change)
 
141
                delta.added.append((path[1], file_id, kind[1]))
128
142
            else:
129
 
                if change.kind[0] == 'symlink' and not new_tree.supports_symlinks():
130
 
                    trace.warning(
131
 
                        'Ignoring "%s" as symlinks '
132
 
                        'are not supported on this filesystem.' % (change.path[0],))
133
 
                else:
134
 
                    delta.removed.append(change)
 
143
                delta.removed.append((path[0], file_id, kind[0]))
135
144
        elif fully_present[0] is False:
136
 
            delta.missing.append(change)
137
 
        elif change.name[0] != change.name[1] or change.parent_id[0] != change.parent_id[1]:
138
 
            # If the name changes, or the parent_id changes, we have a rename or copy
 
145
            delta.missing.append((path[1], file_id, kind[1]))
 
146
        elif name[0] != name[1] or parent_id[0] != parent_id[1]:
 
147
            # If the name changes, or the parent_id changes, we have a rename
139
148
            # (if we move a parent, that doesn't count as a rename for the
140
149
            # file)
141
 
            if change.copied:
142
 
                delta.copied.append(change)
143
 
            else:
144
 
                delta.renamed.append(change)
145
 
        elif change.kind[0] != change.kind[1]:
146
 
            delta.kind_changed.append(change)
147
 
        elif change.changed_content or change.executable[0] != change.executable[1]:
148
 
            delta.modified.append(change)
149
 
        else:
150
 
            delta.unchanged.append(change)
151
 
 
152
 
    def change_key(change):
153
 
        if change.path[0] is None:
154
 
            path = change.path[1]
155
 
        else:
156
 
            path = change.path[0]
157
 
        return (path, change.file_id)
158
 
 
159
 
    delta.removed.sort(key=change_key)
160
 
    delta.added.sort(key=change_key)
161
 
    delta.renamed.sort(key=change_key)
162
 
    delta.copied.sort(key=change_key)
163
 
    delta.missing.sort(key=change_key)
 
150
            delta.renamed.append((path[0],
 
151
                                  path[1],
 
152
                                  file_id,
 
153
                                  kind[1],
 
154
                                  content_change,
 
155
                                  (executable[0] != executable[1])))
 
156
        elif kind[0] != kind[1]:
 
157
            delta.kind_changed.append((path[1], file_id, kind[0], kind[1]))
 
158
        elif content_change or executable[0] != executable[1]:
 
159
            delta.modified.append((path[1], file_id, kind[1],
 
160
                                   content_change,
 
161
                                   (executable[0] != executable[1])))
 
162
        else:
 
163
            delta.unchanged.append((path[1], file_id, kind[1]))
 
164
 
 
165
    delta.removed.sort()
 
166
    delta.added.sort()
 
167
    delta.renamed.sort()
 
168
    delta.missing.sort()
164
169
    # TODO: jam 20060529 These lists shouldn't need to be sorted
165
170
    #       since we added them in alphabetical order.
166
 
    delta.modified.sort(key=change_key)
167
 
    delta.unchanged.sort(key=change_key)
168
 
    delta.unversioned.sort(key=change_key)
 
171
    delta.modified.sort()
 
172
    delta.unchanged.sort()
169
173
 
170
174
    return delta
171
175
 
195
199
        if output_file is not None:
196
200
            if output is not None:
197
201
                raise BzrError('Cannot specify both output and output_file')
198
 
 
199
202
            def output(fmt, *args):
200
203
                output_file.write((fmt % args) + '\n')
201
204
        self.output = output
210
213
                             'deleted': 'D',
211
214
                             'missing': '!',
212
215
                             }
213
 
        self.versioned_map = {'added': '+',  # versioned target
214
 
                              'unchanged': ' ',  # versioned in both
215
 
                              'removed': '-',  # versioned in source
216
 
                              'unversioned': '?',  # versioned in neither
 
216
        self.versioned_map = {'added': '+', # versioned target
 
217
                              'unchanged': ' ', # versioned in both
 
218
                              'removed': '-', # versioned in source
 
219
                              'unversioned': '?', # versioned in neither
217
220
                              }
218
221
        self.unversioned_filter = unversioned_filter
219
222
        if classify:
229
232
            self.output("Operating on whole tree but only reporting on "
230
233
                        "'%s' view." % (self.view_name,))
231
234
 
232
 
    def report(self, paths, versioned, renamed, copied, modified, exe_change,
 
235
    def report(self, file_id, paths, versioned, renamed, modified, exe_change,
233
236
               kind):
234
237
        """Report one change to a file
235
238
 
 
239
        :param file_id: The file_id of the file
236
240
        :param path: The old and new paths as generated by Tree.iter_changes.
237
241
        :param versioned: may be 'added', 'removed', 'unchanged', or
238
242
            'unversioned.
239
243
        :param renamed: may be True or False
240
 
        :param copied: may be True or False
241
244
        :param modified: may be 'created', 'deleted', 'kind changed',
242
245
            'modified' or 'unchanged'.
243
246
        :param exe_change: True if the execute bit has changed
244
247
        :param kind: A pair of file kinds, as generated by Tree.iter_changes.
245
248
            None indicates no file present.
246
249
        """
247
 
        if trace.is_quiet():
 
250
        if is_quiet():
248
251
            return
249
252
        if paths[1] == '' and versioned == 'added' and self.suppress_root_add:
250
253
            return
251
254
        if self.view_files and not osutils.is_inside_any(self.view_files,
252
 
                                                         paths[1]):
 
255
            paths[1]):
253
256
            return
254
257
        if versioned == 'unversioned':
255
258
            # skip ignored unversioned files if needed.
263
266
        # ( the path is different OR
264
267
        #   the kind is different)
265
268
        if (versioned == 'unchanged' and
266
 
                (renamed or copied or modified == 'kind changed')):
267
 
            if renamed or copied:
268
 
                # on a rename or copy, we show old and new
 
269
            (renamed or modified == 'kind changed')):
 
270
            if renamed:
 
271
                # on a rename, we show old and new
269
272
                old_path, path = paths
270
273
            else:
271
 
                # if it's not renamed or copied, we're showing both for kind
272
 
                # changes so only show the new path
 
274
                # if it's not renamed, we're showing both for kind changes
 
275
                # so only show the new path
273
276
                old_path, path = paths[1], paths[1]
274
277
            # if the file is not missing in the source, we show its kind
275
278
            # when we show two paths.
285
288
            path = paths[1]
286
289
        if renamed:
287
290
            rename = "R"
288
 
        elif copied:
289
 
            rename = "C"
290
291
        else:
291
292
            rename = self.versioned_map[versioned]
292
293
        # we show the old kind on the new path when the content is deleted.
302
303
        self.output("%s%s%s %s%s", rename, self.modified_map[modified], exe,
303
304
                    old_path, path)
304
305
 
305
 
 
306
306
def report_changes(change_iterator, reporter):
307
307
    """Report the changes from a change iterator.
308
308
 
319
319
        (False, True): 'added',
320
320
        (False, False): 'unversioned',
321
321
        }
322
 
 
323
 
    def path_key(change):
324
 
        if change.path[0] is not None:
325
 
            path = change.path[0]
326
 
        else:
327
 
            path = change.path[1]
328
 
        return osutils.splitpath(path)
329
 
    for change in sorted(change_iterator, key=path_key):
 
322
    for (file_id, path, content_change, versioned, parent_id, name, kind,
 
323
         executable) in change_iterator:
330
324
        exe_change = False
331
325
        # files are "renamed" if they are moved or if name changes, as long
332
326
        # as it had a value
333
 
        if None not in change.name and None not in change.parent_id and\
334
 
                (change.name[0] != change.name[1] or change.parent_id[0] != change.parent_id[1]):
335
 
            if change.copied:
336
 
                copied = True
337
 
                renamed = False
338
 
            else:
339
 
                renamed = True
340
 
                copied = False
 
327
        if None not in name and None not in parent_id and\
 
328
            (name[0] != name[1] or parent_id[0] != parent_id[1]):
 
329
            renamed = True
341
330
        else:
342
 
            copied = False
343
331
            renamed = False
344
 
        if change.kind[0] != change.kind[1]:
345
 
            if change.kind[0] is None:
 
332
        if kind[0] != kind[1]:
 
333
            if kind[0] is None:
346
334
                modified = "created"
347
 
            elif change.kind[1] is None:
 
335
            elif kind[1] is None:
348
336
                modified = "deleted"
349
337
            else:
350
338
                modified = "kind changed"
351
339
        else:
352
 
            if change.changed_content:
 
340
            if content_change:
353
341
                modified = "modified"
354
 
            elif change.kind[0] is None:
 
342
            elif kind[0] is None:
355
343
                modified = "missing"
356
344
            else:
357
345
                modified = "unchanged"
358
 
            if change.kind[1] == "file":
359
 
                exe_change = (change.executable[0] != change.executable[1])
360
 
        versioned_change = versioned_change_map[change.versioned]
361
 
        reporter.report(change.path, versioned_change, renamed, copied, modified,
362
 
                        exe_change, change.kind)
 
346
            if kind[1] == "file":
 
347
                exe_change = (executable[0] != executable[1])
 
348
        versioned_change = versioned_change_map[versioned]
 
349
        reporter.report(file_id, path, versioned_change, renamed, modified,
 
350
                        exe_change, kind)
363
351
 
364
352
 
365
353
def report_delta(to_file, delta, short_status=False, show_ids=False,
366
 
                 show_unchanged=False, indent='', predicate=None, classify=True):
 
354
        show_unchanged=False, indent='', predicate=None, classify=True):
367
355
    """Output this delta in status-like form to to_file.
368
356
 
369
357
    :param to_file: A file-like object where the output is displayed.
379
367
    :param indent: Added at the beginning of all output lines (for merged
380
368
        revisions).
381
369
 
382
 
    :param predicate: A callable receiving a path returning True if the path
383
 
        should be displayed.
 
370
    :param predicate: A callable receiving a path and a file id and
 
371
        returning True if the path should be displayed.
384
372
 
385
373
    :param classify: Add special symbols to indicate file kind.
386
374
    """
397
385
        return path
398
386
 
399
387
    def show_more_renamed(item):
400
 
        dec_new_path = decorate_path(item.path[1], item.kind[1], item.meta_modified())
 
388
        (oldpath, file_id, kind,
 
389
         text_modified, meta_modified, newpath) = item
 
390
        dec_new_path = decorate_path(newpath, kind, meta_modified)
401
391
        to_file.write(' => %s' % dec_new_path)
402
 
        if item.changed_content or item.meta_modified():
403
 
            extra_modified.append(TreeChange(
404
 
                item.file_id, (item.path[1], item.path[1]),
405
 
                item.changed_content,
406
 
                item.versioned,
407
 
                (item.parent_id[1], item.parent_id[1]),
408
 
                (item.name[1], item.name[1]),
409
 
                (item.kind[1], item.kind[1]),
410
 
                item.executable))
 
392
        if text_modified or meta_modified:
 
393
            extra_modified.append((newpath, file_id, kind,
 
394
                                   text_modified, meta_modified))
411
395
 
412
396
    def show_more_kind_changed(item):
413
 
        to_file.write(' (%s => %s)' % (item.kind[0], item.kind[1]))
 
397
        (path, file_id, old_kind, new_kind) = item
 
398
        to_file.write(' (%s => %s)' % (old_kind, new_kind))
414
399
 
415
 
    def show_path(path, kind, meta_modified,
 
400
    def show_path(path, file_id, kind, meta_modified,
416
401
                  default_format, with_file_id_format):
417
402
        dec_path = decorate_path(path, kind, meta_modified)
418
403
        if show_ids:
432
417
            prefix = indent + prefix + '  '
433
418
 
434
419
            for item in files:
435
 
                if item.path[0] is None:
436
 
                    path = item.path[1]
437
 
                    kind = item.kind[1]
438
 
                else:
439
 
                    path = item.path[0]
440
 
                    kind = item.kind[0]
441
 
                if predicate is not None and not predicate(path):
 
420
                path, file_id, kind = item[:3]
 
421
                if (predicate is not None and not predicate(path, file_id)):
442
422
                    continue
443
423
                if not header_shown and not short_status:
444
424
                    to_file.write(indent + long_status_name + ':\n')
445
425
                    header_shown = True
 
426
                meta_modified = None
 
427
                if len(item) == 5:
 
428
                    meta_modified = item[4]
 
429
 
446
430
                to_file.write(prefix)
447
 
                show_path(path, kind, item.meta_modified(),
 
431
                show_path(path, file_id, kind, meta_modified,
448
432
                          default_format, with_file_id_format)
449
433
                if show_more is not None:
450
434
                    show_more(item)
451
 
                if show_ids and getattr(item, 'file_id', None):
452
 
                    to_file.write(' %s' % item.file_id.decode('utf-8'))
 
435
                if show_ids:
 
436
                    to_file.write(' %s' % file_id)
453
437
                to_file.write('\n')
454
438
 
455
439
    show_list(delta.removed, 'removed', 'D')
456
440
    show_list(delta.added, 'added', 'A')
457
441
    show_list(delta.missing, 'missing', '!')
458
442
    extra_modified = []
459
 
    show_list(delta.renamed, 'renamed', 'R', with_file_id_format='%s',
460
 
              show_more=show_more_renamed)
461
 
    show_list(delta.copied, 'copied', 'C', with_file_id_format='%s',
 
443
    # Reorder delta.renamed tuples so that all lists share the same
 
444
    # order for their 3 first fields and that they also begin like
 
445
    # the delta.modified tuples
 
446
    renamed = [(p, i, k, tm, mm, np)
 
447
               for  p, np, i, k, tm, mm  in delta.renamed]
 
448
    show_list(renamed, 'renamed', 'R', with_file_id_format='%s',
462
449
              show_more=show_more_renamed)
463
450
    show_list(delta.kind_changed, 'kind changed', 'K',
464
451
              with_file_id_format='%s',
468
455
        show_list(delta.unchanged, 'unchanged', 'S')
469
456
 
470
457
    show_list(delta.unversioned, 'unknown', ' ')
 
458