~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/delta.py

  • Committer: John Arbash Meinel
  • Date: 2007-02-13 20:33:57 UTC
  • mfrom: (2283 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2294.
  • Revision ID: john@arbash-meinel.com-20070213203357-b7yg41mi9sk6cqd0
[merge] bzr.dev 2283
resolve conflicts in moved repository formats
small issue with osutils.contains_whitespace()

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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
from bzrlib import errors
 
17
from bzrlib import (
 
18
    errors,
 
19
    osutils,
 
20
    )
18
21
from bzrlib.inventory import InventoryEntry
19
22
from bzrlib.trace import mutter
20
23
from bzrlib.symbol_versioning import deprecated_function, zero_nine
53
56
        self.added = []
54
57
        self.removed = []
55
58
        self.renamed = []
 
59
        self.kind_changed = []
56
60
        self.modified = []
57
61
        self.unchanged = []
58
62
 
63
67
               and self.removed == other.removed \
64
68
               and self.renamed == other.renamed \
65
69
               and self.modified == other.modified \
66
 
               and self.unchanged == other.unchanged
 
70
               and self.unchanged == other.unchanged \
 
71
               and self.kind_changed == other.kind_changed
67
72
 
68
73
    def __ne__(self, other):
69
74
        return not (self == other)
70
75
 
71
76
    def __repr__(self):
72
 
        return "TreeDelta(added=%r, removed=%r, renamed=%r, modified=%r," \
73
 
            " unchanged=%r)" % (self.added, self.removed, self.renamed,
74
 
            self.modified, self.unchanged)
 
77
        return "TreeDelta(added=%r, removed=%r, renamed=%r," \
 
78
            " kind_changed=%r, modified=%r, unchanged=%r)" % (self.added,
 
79
            self.removed, self.renamed, self.kind_changed, self.modified,
 
80
            self.unchanged)
75
81
 
76
82
    def has_changed(self):
77
83
        return bool(self.modified
78
84
                    or self.added
79
85
                    or self.removed
80
 
                    or self.renamed)
 
86
                    or self.renamed
 
87
                    or self.kind_changed)
81
88
 
82
89
    def touches_file_id(self, file_id):
83
90
        """Return True if file_id is modified by this delta."""
88
95
        for v in self.renamed:
89
96
            if v[2] == file_id:
90
97
                return True
 
98
        for v in self.kind_changed:
 
99
            if v[1] == file_id:
 
100
                return True
91
101
        return False
92
102
            
93
103
 
94
 
    def show(self, to_file, show_ids=False, show_unchanged=False, short_status=False):
 
104
    def show(self, to_file, show_ids=False, show_unchanged=False,
 
105
             short_status=False):
95
106
        """output this delta in status-like form to to_file."""
96
107
        def show_list(files, short_status_letter=''):
97
108
            for item in files:
106
117
                    path += '*'
107
118
 
108
119
                if show_ids:
109
 
                    print >>to_file, '%s  %-30s %s' % (short_status_letter, path, fid)
 
120
                    print >>to_file, '%s  %-30s %s' % (short_status_letter,
 
121
                        path, fid)
110
122
                else:
111
123
                    print >>to_file, '%s  %s' % (short_status_letter, path)
112
124
            
139
151
                if meta_modified:
140
152
                    newpath += '*'
141
153
                if show_ids:
142
 
                    print >>to_file, '%s  %s => %s %s' % (short_status_letter,
143
 
                                                          oldpath, newpath, fid)
144
 
                else:
145
 
                    print >>to_file, '%s  %s => %s' % (short_status_letter,
146
 
                                                       oldpath, newpath)
147
 
                    
 
154
                    print >>to_file, '%s  %s => %s %s' % (
 
155
                        short_status_letter, oldpath, newpath, fid)
 
156
                else:
 
157
                    print >>to_file, '%s  %s => %s' % (
 
158
                        short_status_letter, oldpath, newpath)
 
159
 
 
160
        if self.kind_changed:
 
161
            if short_status:
 
162
                short_status_letter = 'K'
 
163
            else:
 
164
                print >>to_file, 'kind changed:'
 
165
                short_status_letter = ''
 
166
            for (path, fid, old_kind, new_kind) in self.kind_changed:
 
167
                if show_ids:
 
168
                    suffix = ' '+fid
 
169
                else:
 
170
                    suffix = ''
 
171
                print >>to_file, '%s  %s (%s => %s)%s' % (
 
172
                    short_status_letter, path, old_kind, new_kind, suffix)
 
173
 
148
174
        if self.modified or extra_modified:
149
175
            short_status_letter = 'M'
150
176
            if not short_status:
184
210
                                               specific_file_ids):
185
211
        if not include_root and (None, None) == parent_id:
186
212
            continue
187
 
        assert kind[0] == kind[1] or None in kind
188
 
        # the only 'kind change' permitted is creation/deletion
189
213
        fully_present = tuple((versioned[x] and kind[x] is not None) for
190
214
                              x in range(2))
191
215
        if fully_present[0] != fully_present[1]:
208
232
                                  kind[1],
209
233
                                  content_change, 
210
234
                                  (executable[0] != executable[1])))
 
235
        elif kind[0] != kind[1]:
 
236
            delta.kind_changed.append((path, file_id, kind[0], kind[1]))
211
237
        elif content_change is True or executable[0] != executable[1]:
212
238
            delta.modified.append((path, file_id, kind[1],
213
239
                                   content_change, 
224
250
    delta.unchanged.sort()
225
251
 
226
252
    return delta
 
253
 
 
254
 
 
255
class ChangeReporter(object):
 
256
    """Report changes between two trees"""
 
257
 
 
258
    def __init__(self, old_inventory, output=None, suppress_root_add=True,
 
259
                 output_file=None):
 
260
        """Constructor
 
261
 
 
262
        :param old_inventory: The inventory of the old tree
 
263
        :param output: a function with the signature of trace.note, i.e.
 
264
            accepts a format and parameters.
 
265
        :param supress_root_add: If true, adding the root will be ignored
 
266
            (i.e. when a tree has just been initted)
 
267
        :param output_file: If supplied, a file-like object to write to.
 
268
            Only one of output and output_file may be supplied.
 
269
        """
 
270
        self.old_inventory = old_inventory
 
271
        if output_file is not None:
 
272
            if output is not None:
 
273
                raise BzrError('Cannot specify both output and output_file')
 
274
            def output(fmt, *args):
 
275
                output_file.write((fmt % args) + '\n')
 
276
        self.output = output
 
277
        if self.output is None:
 
278
            from bzrlib import trace
 
279
            self.output = trace.note
 
280
        self.suppress_root_add = suppress_root_add
 
281
 
 
282
    def report(self, file_id, path, versioned, renamed, modified, exe_change,
 
283
               kind):
 
284
        """Report one change to a file
 
285
 
 
286
        :param file_id: The file_id of the file
 
287
        :param path: The path the file has (or would have) in the tree (as
 
288
            generated by Tree._iter_changes)
 
289
        :param versioned: may be 'added', 'removed', or 'unchanged'
 
290
        :param renamed: may be True or False
 
291
        :param modified: may be 'created', 'deleted', 'kind changed',
 
292
            'modified' or 'unchanged'.
 
293
        :param exe_change: True if the execute bit has changed
 
294
        :param kind: A pair of file kinds, as generated by Tree._iter_changes.
 
295
            None indicates no file present.
 
296
        """
 
297
        if path == '' and versioned == 'added' and self.suppress_root_add:
 
298
            return
 
299
        modified_map = {'kind changed': 'K',
 
300
                        'unchanged': ' ',
 
301
                        'created': 'N',
 
302
                        'modified': 'M',
 
303
                        'deleted': 'D'}
 
304
        versioned_map = {'added': '+',
 
305
                         'unchanged': ' ',
 
306
                         'removed': '-'}
 
307
        old_path = ""
 
308
        if renamed:
 
309
            old_path = self.old_inventory.id2path(file_id)
 
310
            rename = "R"
 
311
        else:
 
312
            rename = versioned_map[versioned]
 
313
        if modified == 'kind changed':
 
314
            if old_path == "":
 
315
                old_path = path
 
316
        if modified == 'deleted':
 
317
            path += osutils.kind_marker(kind[0])
 
318
        elif kind[1] is not None:
 
319
            path += osutils.kind_marker(kind[1])
 
320
        if old_path != "":
 
321
            if kind[0] is not None:
 
322
                old_path += osutils.kind_marker(kind[0])
 
323
            old_path += " => "
 
324
        if exe_change:
 
325
            exe = '*'
 
326
        else:
 
327
            exe = ' '
 
328
        self.output("%s%s%s %s%s", rename, modified_map[modified], exe,
 
329
                    old_path, path)
 
330
 
 
331
 
 
332
def report_changes(change_iterator, reporter):
 
333
    """Report the changes from a change iterator.
 
334
 
 
335
    This is essentially a translation from low-level to medium-level changes.
 
336
    Further processing may be required to produce a human-readable output.
 
337
    Unfortunately, some tree-changing operations are very complex
 
338
    :change_iterator: an iterator or sequence of changes in the format
 
339
        generated by Tree._iter_changes
 
340
    :param reporter: The ChangeReporter that will report the changes.
 
341
    """
 
342
    for (file_id, path, content_change, versioned, parent_id, name, kind,
 
343
         executable) in change_iterator:
 
344
        exe_change = False
 
345
        # files are "renamed" if they are moved or if name changes, as long
 
346
        # as it had a value
 
347
        if None not in name and (name[0] != name[1] or
 
348
                                 parent_id[0] != parent_id[1]):
 
349
            renamed = True
 
350
        else:
 
351
            renamed = False
 
352
        if kind[0] != kind[1]:
 
353
            if kind[0] is None:
 
354
                modified = "created"
 
355
            elif kind[1] is None:
 
356
                modified = "deleted"
 
357
            else:
 
358
                modified = "kind changed"
 
359
        else:
 
360
            if content_change:
 
361
                modified = "modified"
 
362
            else:
 
363
                modified = "unchanged"
 
364
            if kind[1] == "file":
 
365
                exe_change = (executable[0] != executable[1])
 
366
        if versioned[0] != versioned[1]:
 
367
            if versioned[0]:
 
368
                versioned_change = "removed"
 
369
            else:
 
370
                versioned_change = "added"
 
371
        else:
 
372
            versioned_change = "unchanged"
 
373
        reporter.report(file_id, path, versioned_change, renamed, modified,
 
374
                        exe_change, kind)