/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
70 by mbp at sourcefrog
Prepare for smart recursive add.
1
# Copyright (C) 2005 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1 by mbp at sourcefrog
import from baz patch-364
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
#
1 by mbp at sourcefrog
import from baz patch-364
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
#
1 by mbp at sourcefrog
import from baz patch-364
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
17
"""Tree classes, representing directory at point in time.
18
"""
19
849 by Martin Pool
- Put files inside an exported tarball into a top-level directory rather than
20
import os
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
21
from cStringIO import StringIO
1852.5.1 by Robert Collins
Deprecate EmptyTree in favour of using Repository.revision_tree.
22
from warnings import warn
800 by Martin Pool
Merge John's import-speedup branch:
23
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
24
import bzrlib
2012.1.1 by Aaron Bentley
Implement change iterator
25
from bzrlib import delta, osutils
1852.11.1 by Robert Collins
Deprecate compare_trees and move its body to InterTree.changes_from.
26
from bzrlib.decorators import needs_read_lock
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
27
from bzrlib.errors import BzrError, BzrCheckError
1551.7.14 by Aaron Bentley
Use specified_file_ids instead of is_inside_any in compare_trees
28
from bzrlib import errors
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
29
from bzrlib.inventory import Inventory
1852.8.2 by Robert Collins
Add InterTree class to represent InterTree operations.
30
from bzrlib.inter import InterObject
1732.1.1 by John Arbash Meinel
deprecating appendpath, it does exactly what pathjoin does
31
from bzrlib.osutils import fingerprint_file
1773.2.1 by Robert Collins
Teach all trees about unknowns, conflicts and get_parent_ids.
32
import bzrlib.revision
33
from bzrlib.trace import mutter, note
1 by mbp at sourcefrog
import from baz patch-364
34
1852.5.1 by Robert Collins
Deprecate EmptyTree in favour of using Repository.revision_tree.
35
558 by Martin Pool
- All top-level classes inherit from object
36
class Tree(object):
1 by mbp at sourcefrog
import from baz patch-364
37
    """Abstract file tree.
38
39
    There are several subclasses:
40
    
41
    * `WorkingTree` exists as files on disk editable by the user.
42
43
    * `RevisionTree` is a tree as recorded at some point in the past.
44
45
    Trees contain an `Inventory` object, and also know how to retrieve
46
    file texts mentioned in the inventory, either from a working
47
    directory or from a store.
48
49
    It is possible for trees to contain files that are not described
50
    in their inventory or vice versa; for this use `filenames()`.
51
52
    Trees can be compared, etc, regardless of whether they are working
53
    trees or versioned trees.
54
    """
55
    
1852.9.6 by Robert Collins
Merge the change from Tree.compare to Tree.changes_from.
56
    def changes_from(self, other, want_unchanged=False, specific_files=None,
1907.1.1 by Aaron Bentley
Unshelved all changes except those related to removing RootEntry
57
        extra_trees=None, require_versioned=False):
1852.8.8 by Robert Collins
change Tree.compare to Tree.changes_from - its better for the common case.
58
        """Return a TreeDelta of the changes from other to this tree.
1852.9.3 by Robert Collins
Convert the test_delta tests to intertree_implementation and workingtree_implementation tests as appropriate.
59
60
        :param other: A tree to compare with.
61
        :param specific_files: An optional list of file paths to restrict the
62
            comparison to. When mapping filenames to ids, all matches in all
63
            trees (including optional extra_trees) are used, and all children of
64
            matched directories are included.
1852.9.4 by Robert Collins
Add minimal test for Tree.compare(extra_trees=...).
65
        :param want_unchanged: An optional boolean requesting the inclusion of
66
            unchanged entries in the result.
67
        :param extra_trees: An optional list of additional trees to use when
68
            mapping the contents of specific_files (paths) to file_ids.
1852.9.5 by Robert Collins
Add tests for require_versioned to the InterTree.compare() test suite.
69
        :param require_versioned: An optional boolean (defaults to False). When
70
            supplied and True all the 'specific_files' must be versioned, or
71
            a PathsNotVersionedError will be thrown.
1852.9.3 by Robert Collins
Convert the test_delta tests to intertree_implementation and workingtree_implementation tests as appropriate.
72
1852.8.4 by Robert Collins
Hook InterTree into Tree.
73
        The comparison will be performed by an InterTree object looked up on 
74
        self and other.
75
        """
1852.8.8 by Robert Collins
change Tree.compare to Tree.changes_from - its better for the common case.
76
        # Martin observes that Tree.changes_from returns a TreeDelta and this
77
        # may confuse people, because the class name of the returned object is
78
        # a synonym of the object referenced in the method name.
1852.9.6 by Robert Collins
Merge the change from Tree.compare to Tree.changes_from.
79
        return InterTree.get(other, self).compare(
1852.9.4 by Robert Collins
Add minimal test for Tree.compare(extra_trees=...).
80
            want_unchanged=want_unchanged,
81
            specific_files=specific_files,
1852.9.5 by Robert Collins
Add tests for require_versioned to the InterTree.compare() test suite.
82
            extra_trees=extra_trees,
83
            require_versioned=require_versioned,
84
            )
2012.1.1 by Aaron Bentley
Implement change iterator
85
86
    def iter_changes(self, from_tree, include_unchanged=False):
87
        return InterTree.get(from_tree, self).iter_changes(from_tree, self,
88
                                                           include_unchanged)
1852.8.4 by Robert Collins
Hook InterTree into Tree.
89
    
1773.2.1 by Robert Collins
Teach all trees about unknowns, conflicts and get_parent_ids.
90
    def conflicts(self):
91
        """Get a list of the conflicts in the tree.
92
93
        Each conflict is an instance of bzrlib.conflicts.Conflict.
94
        """
95
        return []
96
97
    def get_parent_ids(self):
98
        """Get the parent ids for this tree. 
99
100
        :return: a list of parent ids. [] is returned to indicate
101
        a tree with no parents.
102
        :raises: BzrError if the parents are not known.
103
        """
104
        raise NotImplementedError(self.get_parent_ids)
105
    
1 by mbp at sourcefrog
import from baz patch-364
106
    def has_filename(self, filename):
107
        """True if the tree has given filename."""
108
        raise NotImplementedError()
109
1185.12.39 by abentley
Propogated has_or_had_id to Tree
110
    def has_id(self, file_id):
111
        return self.inventory.has_id(file_id)
112
1852.6.9 by Robert Collins
Add more test trees to the tree-implementations tests.
113
    __contains__ = has_id
114
1185.12.39 by abentley
Propogated has_or_had_id to Tree
115
    def has_or_had_id(self, file_id):
116
        if file_id == self.inventory.root.file_id:
1185.12.38 by abentley
semi-broke merge
117
            return True
1 by mbp at sourcefrog
import from baz patch-364
118
        return self.inventory.has_id(file_id)
119
462 by Martin Pool
- New form 'file_id in tree' to check if the file is present
120
    def __iter__(self):
121
        return iter(self.inventory)
122
1 by mbp at sourcefrog
import from baz patch-364
123
    def id2path(self, file_id):
124
        return self.inventory.id2path(file_id)
125
1852.6.9 by Robert Collins
Add more test trees to the tree-implementations tests.
126
    def iter_entries_by_dir(self):
127
        """Walk the tree in 'by_dir' order.
128
129
        This will yield each entry in the tree as a (path, entry) tuple. The
130
        order that they are yielded is: the contents of a directory are 
131
        preceeded by the parent of a directory, and all the contents of a 
132
        directory are grouped together.
133
        """
134
        return self.inventory.iter_entries_by_dir()
135
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
136
    def kind(self, file_id):
137
        raise NotImplementedError("subclasses must implement kind")
138
1 by mbp at sourcefrog
import from baz patch-364
139
    def _get_inventory(self):
140
        return self._inventory
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
141
    
142
    def get_file_by_path(self, path):
143
        return self.get_file(self._inventory.path2id(path))
1 by mbp at sourcefrog
import from baz patch-364
144
145
    inventory = property(_get_inventory,
146
                         doc="Inventory of this Tree")
147
148
    def _check_retrieved(self, ie, f):
1364 by Martin Pool
- remove extra verification of files retrieved from tree
149
        if not __debug__:
150
            return  
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
151
        fp = fingerprint_file(f)
152
        f.seek(0)
153
        
1963.2.6 by Robey Pointer
pychecker is on crack; go back to using 'is None'.
154
        if ie.text_size is not None:
131 by mbp at sourcefrog
check size and sha1 of files retrieved from the tree
155
            if ie.text_size != fp['size']:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
156
                raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
1 by mbp at sourcefrog
import from baz patch-364
157
                        ["inventory expects %d bytes" % ie.text_size,
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
158
                         "file is actually %d bytes" % fp['size'],
1 by mbp at sourcefrog
import from baz patch-364
159
                         "store is probably damaged/corrupt"])
160
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
161
        if ie.text_sha1 != fp['sha1']:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
162
            raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
1 by mbp at sourcefrog
import from baz patch-364
163
                    ["inventory expects %s" % ie.text_sha1,
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
164
                     "file is actually %s" % fp['sha1'],
1 by mbp at sourcefrog
import from baz patch-364
165
                     "store is probably damaged/corrupt"])
166
167
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
168
    def print_file(self, file_id):
169
        """Print file with id `file_id` to stdout."""
176 by mbp at sourcefrog
New cat command contributed by janmar.
170
        import sys
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
171
        sys.stdout.write(self.get_file_text(file_id))
1543.1.1 by Denys Duchier
lock operations for trees - use them for diff
172
173
    def lock_read(self):
174
        pass
175
1773.2.1 by Robert Collins
Teach all trees about unknowns, conflicts and get_parent_ids.
176
    def unknowns(self):
177
        """What files are present in this tree and unknown.
178
        
179
        :return: an iterator over the unknown files.
180
        """
181
        return iter([])
182
1543.1.1 by Denys Duchier
lock operations for trees - use them for diff
183
    def unlock(self):
184
        pass
1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
185
186
    def filter_unversioned_files(self, paths):
187
        """Filter out paths that are not versioned.
188
189
        :return: set of paths.
190
        """
1658.1.10 by Martin Pool
diff on unversiond files should give an error (Malone #3619)
191
        # NB: we specifically *don't* call self.has_filename, because for
192
        # WorkingTrees that can indicate files that exist on disk but that 
193
        # are not versioned.
194
        pred = self.inventory.has_filename
195
        return set((p for p in paths if not pred(p)))
1852.7.1 by Robert Collins
Move RevisionTree out of tree.py.
196
197
198
# for compatibility
199
from bzrlib.revisiontree import RevisionTree
200
 
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
201
1 by mbp at sourcefrog
import from baz patch-364
202
class EmptyTree(Tree):
1773.2.1 by Robert Collins
Teach all trees about unknowns, conflicts and get_parent_ids.
203
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
204
    def __init__(self):
1907.1.1 by Aaron Bentley
Unshelved all changes except those related to removing RootEntry
205
        self._inventory = Inventory()
1852.5.1 by Robert Collins
Deprecate EmptyTree in favour of using Repository.revision_tree.
206
        warn('EmptyTree is deprecated as of bzr 0.9 please use '
207
            'repository.revision_tree instead.',
208
            DeprecationWarning, stacklevel=2)
1 by mbp at sourcefrog
import from baz patch-364
209
1773.2.1 by Robert Collins
Teach all trees about unknowns, conflicts and get_parent_ids.
210
    def get_parent_ids(self):
211
        return []
212
1092.2.6 by Robert Collins
symlink support updated to work
213
    def get_symlink_target(self, file_id):
214
        return None
215
1 by mbp at sourcefrog
import from baz patch-364
216
    def has_filename(self, filename):
217
        return False
218
1907.1.4 by Aaron Bentley
Restore RootEntry, but mark it deprecated, restore EmptyTree.kind
219
    def kind(self, file_id):
220
        assert self._inventory[file_id].kind == "directory"
221
        return "directory"
222
1907.1.5 by Aaron Bentley
Remove include_root parameter from list_files
223
    def list_files(self):
1732.1.14 by John Arbash Meinel
Some speedups by not calling pathjoin()
224
        return iter([])
1 by mbp at sourcefrog
import from baz patch-364
225
    
974.1.12 by aaron.bentley at utoronto
Switched from text-id to hashcache for merge optimization
226
    def __contains__(self, file_id):
1711.9.11 by John Arbash Meinel
change return foo in bar to return (foo in bar)
227
        return (file_id in self._inventory)
974.1.12 by aaron.bentley at utoronto
Switched from text-id to hashcache for merge optimization
228
1732.1.19 by John Arbash Meinel
If you have the path, use it rather than looking it up again
229
    def get_file_sha1(self, file_id, path=None):
974.1.14 by aaron.bentley at utoronto
Fixed bugs in merge optimization
230
        return None
231
232
1 by mbp at sourcefrog
import from baz patch-364
233
######################################################################
234
# diff
235
236
# TODO: Merge these two functions into a single one that can operate
237
# on either a whole tree or a set of files.
238
239
# TODO: Return the diff in order by filename, not by category or in
240
# random order.  Can probably be done by lock-stepping through the
241
# filenames from both trees.
242
243
244
def file_status(filename, old_tree, new_tree):
245
    """Return single-letter status, old and new names for a file.
246
247
    The complexity here is in deciding how to represent renames;
248
    many complex cases are possible.
249
    """
250
    old_inv = old_tree.inventory
251
    new_inv = new_tree.inventory
252
    new_id = new_inv.path2id(filename)
253
    old_id = old_inv.path2id(filename)
254
255
    if not new_id and not old_id:
256
        # easy: doesn't exist in either; not versioned at all
257
        if new_tree.is_ignored(filename):
258
            return 'I', None, None
259
        else:
260
            return '?', None, None
261
    elif new_id:
262
        # There is now a file of this name, great.
263
        pass
264
    else:
265
        # There is no longer a file of this name, but we can describe
266
        # what happened to the file that used to have
267
        # this name.  There are two possibilities: either it was
268
        # deleted entirely, or renamed.
269
        assert old_id
270
        if new_inv.has_id(old_id):
271
            return 'X', old_inv.id2path(old_id), new_inv.id2path(old_id)
272
        else:
273
            return 'D', old_inv.id2path(old_id), None
274
275
    # if the file_id is new in this revision, it is added
276
    if new_id and not old_inv.has_id(new_id):
277
        return 'A'
278
279
    # if there used to be a file of this name, but that ID has now
280
    # disappeared, it is deleted
281
    if old_id and not new_inv.has_id(old_id):
282
        return 'D'
283
284
    return 'wtf?'
285
286
    
287
164 by mbp at sourcefrog
new 'renames' command
288
def find_renames(old_inv, new_inv):
289
    for file_id in old_inv:
290
        if file_id not in new_inv:
291
            continue
292
        old_name = old_inv.id2path(file_id)
293
        new_name = new_inv.id2path(file_id)
294
        if old_name != new_name:
295
            yield (old_name, new_name)
296
            
678 by Martin Pool
- export to tarballs
297
1551.7.22 by Aaron Bentley
Changes from review
298
def find_ids_across_trees(filenames, trees, require_versioned=True):
299
    """Find the ids corresponding to specified filenames.
300
    
301
    All matches in all trees will be used, and all children of matched
302
    directories will be used.
303
304
    :param filenames: The filenames to find file_ids for
305
    :param trees: The trees to find file_ids within
306
    :param require_versioned: if true, all specified filenames must occur in
307
    at least one tree.
308
    :return: a set of file ids for the specified filenames and their children.
309
    """
310
    if not filenames:
311
        return None
312
    specified_ids = _find_filename_ids_across_trees(filenames, trees, 
313
                                                    require_versioned)
314
    return _find_children_across_trees(specified_ids, trees)
315
316
317
def _find_filename_ids_across_trees(filenames, trees, require_versioned):
318
    """Find the ids corresponding to specified filenames.
319
    
320
    All matches in all trees will be used.
321
1551.7.14 by Aaron Bentley
Use specified_file_ids instead of is_inside_any in compare_trees
322
    :param filenames: The filenames to find file_ids for
323
    :param trees: The trees to find file_ids within
1551.7.16 by Aaron Bentley
Fix docs
324
    :param require_versioned: if true, all specified filenames must occur in
325
    at least one tree.
326
    :return: a set of file ids for the specified filenames
1551.7.14 by Aaron Bentley
Use specified_file_ids instead of is_inside_any in compare_trees
327
    """
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
328
    not_versioned = []
1551.7.18 by Aaron Bentley
Indentation and documentation fixes
329
    interesting_ids = set()
330
    for tree_path in filenames:
331
        not_found = True
332
        for tree in trees:
333
            file_id = tree.inventory.path2id(tree_path)
334
            if file_id is not None:
335
                interesting_ids.add(file_id)
336
                not_found = False
337
        if not_found:
338
            not_versioned.append(tree_path)
1551.7.22 by Aaron Bentley
Changes from review
339
    if len(not_versioned) > 0 and require_versioned:
340
        raise errors.PathsNotVersionedError(not_versioned)
341
    return interesting_ids
342
343
344
def _find_children_across_trees(specified_ids, trees):
345
    """Return a set including specified ids and their children
1551.7.18 by Aaron Bentley
Indentation and documentation fixes
346
    
1551.7.22 by Aaron Bentley
Changes from review
347
    All matches in all trees will be used.
348
349
    :param trees: The trees to find file_ids within
350
    :return: a set containing all specified ids and their children 
351
    """
352
    interesting_ids = set(specified_ids)
1551.7.18 by Aaron Bentley
Indentation and documentation fixes
353
    pending = interesting_ids
354
    # now handle children of interesting ids
355
    # we loop so that we handle all children of each id in both trees
356
    while len(pending) > 0:
357
        new_pending = set()
358
        for file_id in pending:
1551.7.14 by Aaron Bentley
Use specified_file_ids instead of is_inside_any in compare_trees
359
            for tree in trees:
1551.7.18 by Aaron Bentley
Indentation and documentation fixes
360
                if file_id not in tree:
361
                    continue
362
                entry = tree.inventory[file_id]
363
                for child in getattr(entry, 'children', {}).itervalues():
364
                    if child.file_id not in interesting_ids:
365
                        new_pending.add(child.file_id)
366
        interesting_ids.update(new_pending)
367
        pending = new_pending
1551.7.14 by Aaron Bentley
Use specified_file_ids instead of is_inside_any in compare_trees
368
    return interesting_ids
1852.8.2 by Robert Collins
Add InterTree class to represent InterTree operations.
369
370
371
class InterTree(InterObject):
372
    """This class represents operations taking place between two Trees.
373
374
    Its instances have methods like 'compare' and contain references to the
375
    source and target trees these operations are to be carried out on.
376
377
    clients of bzrlib should not need to use InterTree directly, rather they
378
    should use the convenience methods on Tree such as 'Tree.compare()' which
379
    will pass through to InterTree as appropriate.
380
    """
381
1910.2.15 by Aaron Bentley
Back out inter.get changes, make optimizers an ordered list
382
    _optimisers = []
1852.8.2 by Robert Collins
Add InterTree class to represent InterTree operations.
383
1852.11.1 by Robert Collins
Deprecate compare_trees and move its body to InterTree.changes_from.
384
    @needs_read_lock
1852.9.4 by Robert Collins
Add minimal test for Tree.compare(extra_trees=...).
385
    def compare(self, want_unchanged=False, specific_files=None,
1907.1.1 by Aaron Bentley
Unshelved all changes except those related to removing RootEntry
386
        extra_trees=None, require_versioned=False):
1852.9.3 by Robert Collins
Convert the test_delta tests to intertree_implementation and workingtree_implementation tests as appropriate.
387
        """Return the changes from source to target.
1852.8.3 by Robert Collins
Implement an InterTreeTestProvider and a trivial test_compare test case.
388
389
        :return: A TreeDelta.
1852.9.4 by Robert Collins
Add minimal test for Tree.compare(extra_trees=...).
390
        :param specific_files: An optional list of file paths to restrict the
391
            comparison to. When mapping filenames to ids, all matches in all
392
            trees (including optional extra_trees) are used, and all children of
393
            matched directories are included.
394
        :param want_unchanged: An optional boolean requesting the inclusion of
395
            unchanged entries in the result.
396
        :param extra_trees: An optional list of additional trees to use when
397
            mapping the contents of specific_files (paths) to file_ids.
1852.9.5 by Robert Collins
Add tests for require_versioned to the InterTree.compare() test suite.
398
        :param require_versioned: An optional boolean (defaults to False). When
399
            supplied and True all the 'specific_files' must be versioned, or
400
            a PathsNotVersionedError will be thrown.
1852.8.3 by Robert Collins
Implement an InterTreeTestProvider and a trivial test_compare test case.
401
        """
1852.11.1 by Robert Collins
Deprecate compare_trees and move its body to InterTree.changes_from.
402
        # NB: show_status depends on being able to pass in non-versioned files and
403
        # report them as unknown
404
        trees = (self.source, self.target)
405
        if extra_trees is not None:
406
            trees = trees + tuple(extra_trees)
407
        specific_file_ids = find_ids_across_trees(specific_files,
408
            trees, require_versioned=require_versioned)
409
        if specific_files and not specific_file_ids:
410
            # All files are unversioned, so just return an empty delta
411
            # _compare_trees would think we want a complete delta
412
            return delta.TreeDelta()
413
        return delta._compare_trees(self.source, self.target, want_unchanged,
1907.1.1 by Aaron Bentley
Unshelved all changes except those related to removing RootEntry
414
            specific_file_ids)
2012.1.1 by Aaron Bentley
Implement change iterator
415
416
    def iter_changes(self, from_tree, to_tree, include_unchanged):
417
        """Generate an iterator of changes between trees.
418
419
        A tuple is returned:
420
        (file_id, path, changed_content, versioned, parent, name, kind,
421
         executable)
422
423
        file_id and path are always returned.  Path is relative to the to_tree.
424
        changed_content is True if the file's content has changed.  This
425
        includes changes to its kind.
426
427
        versioned, parent, name, kind, executable are None if unchanged, or
428
        tuples of (from, to) if changed.  If a file is missing in a tree, its
429
        kind is None.
430
431
        Iteration is done in parent-to-child order, relative to the to_tree.
432
        """
433
        def get_versioned_kind(tree, file_id):
434
            try:
435
                return tree.kind(file_id)
436
            except errors.NoSuchFile:
437
                return None
438
439
        def compared(from_value, to_value):
440
            if from_value != to_value:
441
                return (from_value, to_value)
442
            else:
443
                return None
444
445
        to_paths = {}
446
        for path, to_entry in to_tree.iter_entries_by_dir():
447
            file_id = to_entry.file_id
448
            to_paths[file_id] = path
449
            changed_content = False
450
            from_versioned = (file_id in from_tree)
451
            versioned = compared(from_versioned, True)
452
            if from_versioned:
453
                from_kind = get_versioned_kind(from_tree, file_id)
2012.1.2 by Aaron Bentley
reimplement compare_trees
454
                if from_kind is not None:
455
                    from_executable = (from_tree.is_executable(file_id) not in 
456
                                       (False, None))
457
                else:
458
                    from_executable = None
2012.1.1 by Aaron Bentley
Implement change iterator
459
                from_entry = from_tree.inventory[file_id]
460
                from_parent = from_entry.parent_id
461
                from_name = from_entry.name
462
            else:
463
                from_kind = None
464
                from_parent = None
465
                from_name = None
466
                from_executable = None
2012.1.2 by Aaron Bentley
reimplement compare_trees
467
            to_kind = get_versioned_kind(to_tree, file_id)
468
            kind = compared(from_kind, to_kind)
2012.1.1 by Aaron Bentley
Implement change iterator
469
            if kind is not None:
470
                changed_content = True
2012.1.2 by Aaron Bentley
reimplement compare_trees
471
            elif from_kind == 'file':
472
                if (from_tree.get_file_sha1(file_id) !=
473
                    to_tree.get_file_sha1(file_id)):
474
                    changed_content = True
475
            elif from_kind == 'symlink':
476
                if (from_tree.get_symlink_target(file_id) != 
477
                    to_tree.get_symlink_target(file_id)):
478
                    changed_content = True
2012.1.1 by Aaron Bentley
Implement change iterator
479
            parent = compared(from_parent, to_entry.parent_id)
480
            name = compared(from_name, to_entry.name)
2012.1.2 by Aaron Bentley
reimplement compare_trees
481
            if to_kind is not None:
482
                to_executable = (to_tree.is_executable(file_id) not in 
483
                                 (False, None))
484
            else:
485
                to_executable = None
486
            executable = compared(from_executable, to_executable)
2012.1.1 by Aaron Bentley
Implement change iterator
487
            if (changed_content is not False or versioned is not None or
488
                parent is not None or name is not None or executable is not
489
                None or include_unchanged):
490
                yield (file_id, path, changed_content, versioned, parent,
491
                       name, kind, executable)
492
493
        for path, from_entry in from_tree.iter_entries_by_dir():
494
            file_id = from_entry.file_id
495
            if file_id in to_paths:
496
                continue
497
            versioned = (True, False)
498
            parent = (from_entry.parent_id, None)
499
            name = (from_entry.name, None)
500
            kind = (get_versioned_kind(from_tree, file_id), None)
2012.1.2 by Aaron Bentley
reimplement compare_trees
501
            from_executable = (from_tree.is_executable(file_id) not in 
502
                               (False, None))
503
            executable = (from_executable, None)
2012.1.1 by Aaron Bentley
Implement change iterator
504
            changed_content = True
505
            # the parent's path is necessarily known at this point.
506
            to_path = osutils.pathjoin(to_paths[from_entry.parent_id],
507
                                       from_entry.name)
508
            to_paths[file_id] = to_path
509
            yield(file_id, to_path, changed_content, versioned, parent,
510
                  name, kind, executable)