~bzr-pqm/bzr/bzr.dev

2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
1
# Copyright (C) 2005, 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
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
2225.1.1 by Aaron Bentley
Added revert change display, with tests
17
from bzrlib import (
18
    errors,
19
    osutils,
20
    )
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
21
from bzrlib.inventory import InventoryEntry
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
22
from bzrlib.trace import mutter
1904.2.1 by Martin Pool
compare_trees is deprecated in 0.9 not 0.10
23
from bzrlib.symbol_versioning import deprecated_function, zero_nine
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
24
1732.1.29 by John Arbash Meinel
Update documentation and TODO for compare_trees
25
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
26
class TreeDelta(object):
27
    """Describes changes from one tree to another.
28
29
    Contains four lists:
30
31
    added
32
        (path, id, kind)
33
    removed
34
        (path, id, kind)
35
    renamed
1398 by Robert Collins
integrate in Gustavos x-bit patch
36
        (oldpath, newpath, id, kind, text_modified, meta_modified)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
37
    modified
1398 by Robert Collins
integrate in Gustavos x-bit patch
38
        (path, id, kind, text_modified, meta_modified)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
39
    unchanged
40
        (path, id, kind)
41
42
    Each id is listed only once.
43
44
    Files that are both modified and renamed are listed only in
1092.2.6 by Robert Collins
symlink support updated to work
45
    renamed, with the text_modified flag true. The text_modified
46
    applies either to the the content of the file or the target of the
47
    symbolic link, depending of the kind of file.
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
48
49
    Files are only considered renamed if their name has changed or
50
    their parent directory has changed.  Renaming a directory
51
    does not count as renaming all its contents.
52
53
    The lists are normally sorted when the delta is created.
54
    """
55
    def __init__(self):
56
        self.added = []
57
        self.removed = []
58
        self.renamed = []
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
59
        self.kind_changed = []
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
60
        self.modified = []
61
        self.unchanged = []
62
63
    def __eq__(self, other):
64
        if not isinstance(other, TreeDelta):
65
            return False
66
        return self.added == other.added \
67
               and self.removed == other.removed \
68
               and self.renamed == other.renamed \
69
               and self.modified == other.modified \
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
70
               and self.unchanged == other.unchanged \
71
               and self.kind_changed == other.kind_changed
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
72
73
    def __ne__(self, other):
74
        return not (self == other)
75
76
    def __repr__(self):
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
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)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
81
82
    def has_changed(self):
1189 by Martin Pool
- BROKEN: partial support for commit into weave
83
        return bool(self.modified
84
                    or self.added
85
                    or self.removed
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
86
                    or self.renamed
87
                    or self.kind_changed)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
88
89
    def touches_file_id(self, file_id):
90
        """Return True if file_id is modified by this delta."""
91
        for l in self.added, self.removed, self.modified:
92
            for v in l:
93
                if v[1] == file_id:
94
                    return True
95
        for v in self.renamed:
96
            if v[2] == file_id:
97
                return True
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
98
        for v in self.kind_changed:
99
            if v[1] == file_id:
100
                return True
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
101
        return False
102
            
103
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
104
    def show(self, to_file, show_ids=False, show_unchanged=False,
105
             short_status=False):
1780.2.1 by Robert Collins
Remove some unused imports.
106
        """output this delta in status-like form to to_file."""
2147.2.1 by Keir Mierle
Add a --short flag to status to get svn-style status
107
        def show_list(files, short_status_letter=''):
1398 by Robert Collins
integrate in Gustavos x-bit patch
108
            for item in files:
109
                path, fid, kind = item[:3]
110
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
111
                if kind == 'directory':
112
                    path += '/'
113
                elif kind == 'symlink':
114
                    path += '@'
1398 by Robert Collins
integrate in Gustavos x-bit patch
115
116
                if len(item) == 5 and item[4]:
117
                    path += '*'
118
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
119
                if show_ids:
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
120
                    print >>to_file, '%s  %-30s %s' % (short_status_letter,
121
                        path, fid)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
122
                else:
2147.2.2 by Keir Mierle
Fix spacing error and add tests for status --short command flag.
123
                    print >>to_file, '%s  %s' % (short_status_letter, path)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
124
            
125
        if self.removed:
2147.2.1 by Keir Mierle
Add a --short flag to status to get svn-style status
126
            if not short_status:
127
                print >>to_file, 'removed:'
128
                show_list(self.removed)
129
            else:
130
                show_list(self.removed, 'D')
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
131
                
132
        if self.added:
2147.2.1 by Keir Mierle
Add a --short flag to status to get svn-style status
133
            if not short_status:
134
                print >>to_file, 'added:'
135
                show_list(self.added)
136
            else:
137
                show_list(self.added, 'A')
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
138
1185.36.2 by Daniel Silverstone
Allow the delta display routine to show when a file
139
        extra_modified = []
140
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
141
        if self.renamed:
2147.2.1 by Keir Mierle
Add a --short flag to status to get svn-style status
142
            short_status_letter = 'R'
143
            if not short_status:
144
                print >>to_file, 'renamed:'
145
                short_status_letter = ''
1398 by Robert Collins
integrate in Gustavos x-bit patch
146
            for (oldpath, newpath, fid, kind,
147
                 text_modified, meta_modified) in self.renamed:
1185.36.2 by Daniel Silverstone
Allow the delta display routine to show when a file
148
                if text_modified or meta_modified:
149
                    extra_modified.append((newpath, fid, kind,
150
                                           text_modified, meta_modified))
1398 by Robert Collins
integrate in Gustavos x-bit patch
151
                if meta_modified:
152
                    newpath += '*'
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
153
                if show_ids:
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
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
1185.36.2 by Daniel Silverstone
Allow the delta display routine to show when a file
174
        if self.modified or extra_modified:
2147.2.1 by Keir Mierle
Add a --short flag to status to get svn-style status
175
            short_status_letter = 'M'
176
            if not short_status:
177
                print >>to_file, 'modified:'
178
                short_status_letter = ''
179
            show_list(self.modified, short_status_letter)
180
            show_list(extra_modified, short_status_letter)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
181
            
182
        if show_unchanged and self.unchanged:
2147.2.1 by Keir Mierle
Add a --short flag to status to get svn-style status
183
            if not short_status:
184
                print >>to_file, 'unchanged:'
185
                show_list(self.unchanged)
186
            else:
187
                show_list(self.unchanged, 'S')
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
188
189
1904.2.1 by Martin Pool
compare_trees is deprecated in 0.9 not 0.10
190
@deprecated_function(zero_nine)
1852.10.3 by Robert Collins
Remove all uses of compare_trees and replace with Tree.changes_from throughout bzrlib.
191
def compare_trees(old_tree, new_tree, want_unchanged=False,
192
                  specific_files=None, extra_trees=None,
1731.1.62 by Aaron Bentley
Changes from review comments
193
                  require_versioned=False):
1852.11.1 by Robert Collins
Deprecate compare_trees and move its body to InterTree.changes_from.
194
    """compare_trees was deprecated in 0.10. Please see Tree.changes_from."""
195
    return new_tree.changes_from(old_tree,
196
        want_unchanged=want_unchanged,
197
        specific_files=specific_files,
198
        extra_trees=extra_trees,
1731.1.33 by Aaron Bentley
Revert no-special-root changes
199
        require_versioned=require_versioned,
1731.1.62 by Aaron Bentley
Changes from review comments
200
        include_root=False)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
201
202
203
def _compare_trees(old_tree, new_tree, want_unchanged, specific_file_ids,
204
                   include_root):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
205
    delta = TreeDelta()
1908.3.1 by Carl Friedrich Bolz
Clean up some mutter() calls.
206
    # mutter('start compare_trees')
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
207
2012.1.2 by Aaron Bentley
reimplement compare_trees
208
    for (file_id, path, content_change, versioned, parent_id, name, kind,
2012.1.10 by Aaron Bentley
Make iter_changes private, so it can be changed freely
209
         executable) in new_tree._iter_changes(old_tree, want_unchanged, 
210
                                               specific_file_ids):
2012.1.8 by Aaron Bentley
Merge from bzr.dev
211
        if not include_root and (None, None) == parent_id:
2012.1.2 by Aaron Bentley
reimplement compare_trees
212
            continue
2012.1.4 by Aaron Bentley
Simplify add/remove code
213
        fully_present = tuple((versioned[x] and kind[x] is not None) for
214
                              x in range(2))
2012.1.5 by Aaron Bentley
Implement specific file id and dangling id handling
215
        if fully_present[0] != fully_present[1]:
216
            if fully_present[1] is True:
217
                delta.added.append((path, file_id, kind[1]))
218
            else:
219
                assert fully_present[0] is True
220
                old_path = old_tree.id2path(file_id)
221
                delta.removed.append((old_path, file_id, kind[0]))
222
        elif fully_present[0] is False:
223
            continue
2012.1.3 by Aaron Bentley
Always generate tuples (because kind is always used, even when not different)
224
        elif name[0] != name[1] or parent_id[0] != parent_id[1]:
2012.1.4 by Aaron Bentley
Simplify add/remove code
225
            # If the name changes, or the parent_id changes, we have a rename
226
            # (if we move a parent, that doesn't count as a rename for the
227
            # file)
2012.1.2 by Aaron Bentley
reimplement compare_trees
228
            old_path = old_tree.id2path(file_id)
1732.1.7 by John Arbash Meinel
Instead of iterating randomly in both trees, _compare_trees now iterates in order on both trees simultaneously.
229
            delta.renamed.append((old_path,
2012.1.2 by Aaron Bentley
reimplement compare_trees
230
                                  path,
231
                                  file_id, 
2012.1.3 by Aaron Bentley
Always generate tuples (because kind is always used, even when not different)
232
                                  kind[1],
233
                                  content_change, 
234
                                  (executable[0] != executable[1])))
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
235
        elif kind[0] != kind[1]:
236
            delta.kind_changed.append((path, file_id, kind[0], kind[1]))
2012.1.3 by Aaron Bentley
Always generate tuples (because kind is always used, even when not different)
237
        elif content_change is True or executable[0] != executable[1]:
238
            delta.modified.append((path, file_id, kind[1],
239
                                   content_change, 
240
                                   (executable[0] != executable[1])))
2012.1.2 by Aaron Bentley
reimplement compare_trees
241
        else:
2012.1.3 by Aaron Bentley
Always generate tuples (because kind is always used, even when not different)
242
            delta.unchanged.append((path, file_id, kind[1]))
1732.1.7 by John Arbash Meinel
Instead of iterating randomly in both trees, _compare_trees now iterates in order on both trees simultaneously.
243
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
244
    delta.removed.sort()
245
    delta.added.sort()
246
    delta.renamed.sort()
1732.1.29 by John Arbash Meinel
Update documentation and TODO for compare_trees
247
    # TODO: jam 20060529 These lists shouldn't need to be sorted
248
    #       since we added them in alphabetical order.
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
249
    delta.modified.sort()
250
    delta.unchanged.sort()
251
252
    return delta
2225.1.1 by Aaron Bentley
Added revert change display, with tests
253
254
255
class ChangeReporter(object):
256
    """Report changes between two trees"""
257
1551.10.8 by Aaron Bentley
Make ChangeReporter interface nicer
258
    def __init__(self, old_inventory, output=None, suppress_root_add=True,
259
                 output_file=None):
1551.10.7 by Aaron Bentley
Use new-style output for status
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)
1551.10.8 by Aaron Bentley
Make ChangeReporter interface nicer
267
        :param output_file: If supplied, a file-like object to write to.
268
            Only one of output and output_file may be supplied.
1551.10.7 by Aaron Bentley
Use new-style output for status
269
        """
2225.1.1 by Aaron Bentley
Added revert change display, with tests
270
        self.old_inventory = old_inventory
1551.10.8 by Aaron Bentley
Make ChangeReporter interface nicer
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')
2225.1.1 by Aaron Bentley
Added revert change display, with tests
276
        self.output = output
277
        if self.output is None:
278
            from bzrlib import trace
279
            self.output = trace.note
1551.10.7 by Aaron Bentley
Use new-style output for status
280
        self.suppress_root_add = suppress_root_add
2225.1.1 by Aaron Bentley
Added revert change display, with tests
281
282
    def report(self, file_id, path, versioned, renamed, modified, exe_change,
283
               kind):
284
        """Report one change to a file
2225.1.5 by Aaron Bentley
Clean up whitespace changes
285
2225.1.1 by Aaron Bentley
Added revert change display, with tests
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
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
291
        :param modified: may be 'created', 'deleted', 'kind changed',
2225.1.1 by Aaron Bentley
Added revert change display, with tests
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
        """
1551.10.7 by Aaron Bentley
Use new-style output for status
297
        if path == '' and versioned == 'added' and self.suppress_root_add:
298
            return
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
299
        modified_map = {'kind changed': 'K',
300
                        'unchanged': ' ',
2225.1.1 by Aaron Bentley
Added revert change display, with tests
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])
1551.10.11 by Aaron Bentley
Handle case where file-id only is added
318
        elif kind[1] is not None:
2225.1.1 by Aaron Bentley
Added revert change display, with tests
319
            path += osutils.kind_marker(kind[1])
320
        if old_path != "":
1551.10.12 by Aaron Bentley
Handle simultaneous creation+rename
321
            if kind[0] is not None:
322
                old_path += osutils.kind_marker(kind[0])
323
            old_path += " => "
2225.1.1 by Aaron Bentley
Added revert change display, with tests
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
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
347
        if None not in name and (name[0] != name[1] or
2225.1.1 by Aaron Bentley
Added revert change display, with tests
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":
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
365
                exe_change = (executable[0] != executable[1])
2225.1.1 by Aaron Bentley
Added revert change display, with tests
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"
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
373
        reporter.report(file_id, path, versioned_change, renamed, modified,
2225.1.1 by Aaron Bentley
Added revert change display, with tests
374
                        exe_change, kind)