/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
800 by Martin Pool
Merge John's import-speedup branch:
22
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
23
import bzrlib
2079.1.1 by John Arbash Meinel
Create a deprecated bzrlib.tree.RevisionTree() in favor of bzrlib.revisiontree.RevisionTree()
24
from bzrlib import (
25
    delta,
2012.1.8 by Aaron Bentley
Merge from bzr.dev
26
    osutils,
2079.1.1 by John Arbash Meinel
Create a deprecated bzrlib.tree.RevisionTree() in favor of bzrlib.revisiontree.RevisionTree()
27
    symbol_versioning,
28
    )
1852.11.1 by Robert Collins
Deprecate compare_trees and move its body to InterTree.changes_from.
29
from bzrlib.decorators import needs_read_lock
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
30
from bzrlib.errors import BzrError, BzrCheckError
1551.7.14 by Aaron Bentley
Use specified_file_ids instead of is_inside_any in compare_trees
31
from bzrlib import errors
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
32
from bzrlib.inventory import Inventory
1852.8.2 by Robert Collins
Add InterTree class to represent InterTree operations.
33
from bzrlib.inter import InterObject
1732.1.1 by John Arbash Meinel
deprecating appendpath, it does exactly what pathjoin does
34
from bzrlib.osutils import fingerprint_file
1773.2.1 by Robert Collins
Teach all trees about unknowns, conflicts and get_parent_ids.
35
import bzrlib.revision
36
from bzrlib.trace import mutter, note
1 by mbp at sourcefrog
import from baz patch-364
37
1852.5.1 by Robert Collins
Deprecate EmptyTree in favour of using Repository.revision_tree.
38
558 by Martin Pool
- All top-level classes inherit from object
39
class Tree(object):
1 by mbp at sourcefrog
import from baz patch-364
40
    """Abstract file tree.
41
42
    There are several subclasses:
43
    
44
    * `WorkingTree` exists as files on disk editable by the user.
45
46
    * `RevisionTree` is a tree as recorded at some point in the past.
47
48
    Trees contain an `Inventory` object, and also know how to retrieve
49
    file texts mentioned in the inventory, either from a working
50
    directory or from a store.
51
52
    It is possible for trees to contain files that are not described
53
    in their inventory or vice versa; for this use `filenames()`.
54
55
    Trees can be compared, etc, regardless of whether they are working
56
    trees or versioned trees.
57
    """
58
    
1852.9.6 by Robert Collins
Merge the change from Tree.compare to Tree.changes_from.
59
    def changes_from(self, other, want_unchanged=False, specific_files=None,
1731.1.33 by Aaron Bentley
Revert no-special-root changes
60
        extra_trees=None, require_versioned=False, include_root=False):
1852.8.8 by Robert Collins
change Tree.compare to Tree.changes_from - its better for the common case.
61
        """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.
62
63
        :param other: A tree to compare with.
64
        :param specific_files: An optional list of file paths to restrict the
65
            comparison to. When mapping filenames to ids, all matches in all
66
            trees (including optional extra_trees) are used, and all children of
67
            matched directories are included.
1852.9.4 by Robert Collins
Add minimal test for Tree.compare(extra_trees=...).
68
        :param want_unchanged: An optional boolean requesting the inclusion of
69
            unchanged entries in the result.
70
        :param extra_trees: An optional list of additional trees to use when
71
            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.
72
        :param require_versioned: An optional boolean (defaults to False). When
73
            supplied and True all the 'specific_files' must be versioned, or
74
            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.
75
1852.8.4 by Robert Collins
Hook InterTree into Tree.
76
        The comparison will be performed by an InterTree object looked up on 
77
        self and other.
78
        """
1852.8.8 by Robert Collins
change Tree.compare to Tree.changes_from - its better for the common case.
79
        # Martin observes that Tree.changes_from returns a TreeDelta and this
80
        # may confuse people, because the class name of the returned object is
81
        # 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.
82
        return InterTree.get(other, self).compare(
1852.9.4 by Robert Collins
Add minimal test for Tree.compare(extra_trees=...).
83
            want_unchanged=want_unchanged,
84
            specific_files=specific_files,
1852.9.5 by Robert Collins
Add tests for require_versioned to the InterTree.compare() test suite.
85
            extra_trees=extra_trees,
86
            require_versioned=require_versioned,
1731.1.33 by Aaron Bentley
Revert no-special-root changes
87
            include_root=include_root
1852.9.5 by Robert Collins
Add tests for require_versioned to the InterTree.compare() test suite.
88
            )
2012.1.1 by Aaron Bentley
Implement change iterator
89
2012.1.10 by Aaron Bentley
Make iter_changes private, so it can be changed freely
90
    def _iter_changes(self, from_tree, include_unchanged=False, 
2012.1.16 by Aaron Bentley
Support progress bars in iter_changes
91
                     specific_file_ids=None, pb=None):
2012.1.5 by Aaron Bentley
Implement specific file id and dangling id handling
92
        intertree = InterTree.get(from_tree, self)
2012.1.10 by Aaron Bentley
Make iter_changes private, so it can be changed freely
93
        return intertree._iter_changes(from_tree, self, include_unchanged, 
2012.1.16 by Aaron Bentley
Support progress bars in iter_changes
94
                                       specific_file_ids, pb)
1852.8.4 by Robert Collins
Hook InterTree into Tree.
95
    
1773.2.1 by Robert Collins
Teach all trees about unknowns, conflicts and get_parent_ids.
96
    def conflicts(self):
97
        """Get a list of the conflicts in the tree.
98
99
        Each conflict is an instance of bzrlib.conflicts.Conflict.
100
        """
101
        return []
102
103
    def get_parent_ids(self):
104
        """Get the parent ids for this tree. 
105
106
        :return: a list of parent ids. [] is returned to indicate
107
        a tree with no parents.
108
        :raises: BzrError if the parents are not known.
109
        """
110
        raise NotImplementedError(self.get_parent_ids)
111
    
1 by mbp at sourcefrog
import from baz patch-364
112
    def has_filename(self, filename):
113
        """True if the tree has given filename."""
114
        raise NotImplementedError()
115
1185.12.39 by abentley
Propogated has_or_had_id to Tree
116
    def has_id(self, file_id):
117
        return self.inventory.has_id(file_id)
118
1852.6.9 by Robert Collins
Add more test trees to the tree-implementations tests.
119
    __contains__ = has_id
120
1185.12.39 by abentley
Propogated has_or_had_id to Tree
121
    def has_or_had_id(self, file_id):
122
        if file_id == self.inventory.root.file_id:
1185.12.38 by abentley
semi-broke merge
123
            return True
1 by mbp at sourcefrog
import from baz patch-364
124
        return self.inventory.has_id(file_id)
125
462 by Martin Pool
- New form 'file_id in tree' to check if the file is present
126
    def __iter__(self):
127
        return iter(self.inventory)
128
1 by mbp at sourcefrog
import from baz patch-364
129
    def id2path(self, file_id):
130
        return self.inventory.id2path(file_id)
131
1986.1.2 by Robert Collins
Various changes to allow non-workingtree specific tests to run entirely
132
    def is_control_filename(self, filename):
133
        """True if filename is the name of a control file in this tree.
134
        
135
        :param filename: A filename within the tree. This is a relative path
136
        from the root of this tree.
137
138
        This is true IF and ONLY IF the filename is part of the meta data
139
        that bzr controls in this tree. I.E. a random .bzr directory placed
140
        on disk will not be a control file for this tree.
141
        """
142
        return self.bzrdir.is_control_filename(filename)
143
2255.2.15 by Robert Collins
Dirstate - truncate state file fixing bug in saving a smaller file, get more tree_implementation tests passing.
144
    @needs_read_lock
1551.9.29 by Aaron Bentley
Optimize Tree._iter_changes with specific file_ids
145
    def iter_entries_by_dir(self, specific_file_ids=None):
1852.6.9 by Robert Collins
Add more test trees to the tree-implementations tests.
146
        """Walk the tree in 'by_dir' order.
147
148
        This will yield each entry in the tree as a (path, entry) tuple. The
149
        order that they are yielded is: the contents of a directory are 
150
        preceeded by the parent of a directory, and all the contents of a 
151
        directory are grouped together.
152
        """
1551.9.29 by Aaron Bentley
Optimize Tree._iter_changes with specific file_ids
153
        return self.inventory.iter_entries_by_dir(
154
            specific_file_ids=specific_file_ids)
1852.6.9 by Robert Collins
Add more test trees to the tree-implementations tests.
155
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
156
    def kind(self, file_id):
157
        raise NotImplementedError("subclasses must implement kind")
158
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
159
    def _comparison_data(self, entry, path):
2012.1.15 by Aaron Bentley
Minor tweaks
160
        """Return a tuple of kind, executable, stat_value for a file.
161
162
        entry may be None if there is no inventory entry for the file, but
163
        path must always be supplied.
164
165
        kind is None if there is no file present (even if an inventory id is
166
        present).  executable is False for non-file entries.
167
        """
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
168
        raise NotImplementedError(self._comparison_data)
169
2255.2.10 by Robert Collins
Now all tests matching dirstate pass - added generation of inventories for parent trees.
170
    def _file_size(self, entry, stat_value):
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
171
        raise NotImplementedError(self._file_size)
172
1 by mbp at sourcefrog
import from baz patch-364
173
    def _get_inventory(self):
174
        return self._inventory
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
175
    
1986.1.2 by Robert Collins
Various changes to allow non-workingtree specific tests to run entirely
176
    def get_file(self, file_id):
177
        """Return a file object for the file file_id in the tree."""
178
        raise NotImplementedError(self.get_file)
179
    
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
180
    def get_file_by_path(self, path):
181
        return self.get_file(self._inventory.path2id(path))
1 by mbp at sourcefrog
import from baz patch-364
182
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
183
    def annotate_iter(self, file_id):
1551.9.18 by Aaron Bentley
Updates from review comments
184
        """Return an iterator of revision_id, line tuples
185
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
186
        For working trees (and mutable trees in general), the special
187
        revision_id 'current:' will be used for lines that are new in this
188
        tree, e.g. uncommitted changes.
189
        :param file_id: The file to produce an annotated version from
190
        """
191
        raise NotImplementedError(self.annotate_iter)
192
1 by mbp at sourcefrog
import from baz patch-364
193
    inventory = property(_get_inventory,
194
                         doc="Inventory of this Tree")
195
196
    def _check_retrieved(self, ie, f):
1364 by Martin Pool
- remove extra verification of files retrieved from tree
197
        if not __debug__:
198
            return  
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
199
        fp = fingerprint_file(f)
200
        f.seek(0)
201
        
1963.2.6 by Robey Pointer
pychecker is on crack; go back to using 'is None'.
202
        if ie.text_size is not None:
131 by mbp at sourcefrog
check size and sha1 of files retrieved from the tree
203
            if ie.text_size != fp['size']:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
204
                raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
1 by mbp at sourcefrog
import from baz patch-364
205
                        ["inventory expects %d bytes" % ie.text_size,
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
206
                         "file is actually %d bytes" % fp['size'],
1 by mbp at sourcefrog
import from baz patch-364
207
                         "store is probably damaged/corrupt"])
208
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
209
        if ie.text_sha1 != fp['sha1']:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
210
            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
211
                    ["inventory expects %s" % ie.text_sha1,
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
212
                     "file is actually %s" % fp['sha1'],
1 by mbp at sourcefrog
import from baz patch-364
213
                     "store is probably damaged/corrupt"])
214
1986.1.2 by Robert Collins
Various changes to allow non-workingtree specific tests to run entirely
215
    def path2id(self, path):
216
        """Return the id for path in this tree."""
217
        return self._inventory.path2id(path)
1 by mbp at sourcefrog
import from baz patch-364
218
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
219
    def print_file(self, file_id):
220
        """Print file with id `file_id` to stdout."""
176 by mbp at sourcefrog
New cat command contributed by janmar.
221
        import sys
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
222
        sys.stdout.write(self.get_file_text(file_id))
1543.1.1 by Denys Duchier
lock operations for trees - use them for diff
223
224
    def lock_read(self):
225
        pass
226
1908.11.1 by Robert Collins
Add a new method ``Tree.revision_tree`` which allows access to cached
227
    def revision_tree(self, revision_id):
228
        """Obtain a revision tree for the revision revision_id.
229
230
        The intention of this method is to allow access to possibly cached
231
        tree data. Implementors of this method should raise NoSuchRevision if
232
        the tree is not locally available, even if they could obtain the 
233
        tree via a repository or some other means. Callers are responsible 
234
        for finding the ultimate source for a revision tree.
235
236
        :param revision_id: The revision_id of the requested tree.
237
        :return: A Tree.
238
        :raises: NoSuchRevision if the tree cannot be obtained.
239
        """
240
        raise errors.NoSuchRevisionInTree(self, revision_id)
241
1773.2.1 by Robert Collins
Teach all trees about unknowns, conflicts and get_parent_ids.
242
    def unknowns(self):
243
        """What files are present in this tree and unknown.
244
        
245
        :return: an iterator over the unknown files.
246
        """
247
        return iter([])
248
1543.1.1 by Denys Duchier
lock operations for trees - use them for diff
249
    def unlock(self):
250
        pass
1658.1.9 by Martin Pool
Give an error for bzr diff on an nonexistent file (Malone #3619)
251
252
    def filter_unversioned_files(self, paths):
253
        """Filter out paths that are not versioned.
254
255
        :return: set of paths.
256
        """
1658.1.10 by Martin Pool
diff on unversiond files should give an error (Malone #3619)
257
        # NB: we specifically *don't* call self.has_filename, because for
258
        # WorkingTrees that can indicate files that exist on disk but that 
259
        # are not versioned.
260
        pred = self.inventory.has_filename
261
        return set((p for p in paths if not pred(p)))
1852.7.1 by Robert Collins
Move RevisionTree out of tree.py.
262
1852.15.3 by Robert Collins
Add a first-cut Tree.walkdirs method.
263
    def walkdirs(self, prefix=""):
264
        """Walk the contents of this tree from path down.
265
266
        This yields all the data about the contents of a directory at a time.
267
        After each directory has been yielded, if the caller has mutated the
268
        list to exclude some directories, they are then not descended into.
269
        
270
        The data yielded is of the form:
1852.15.7 by Robert Collins
Start testing behaviour of unknowns in WorkingTree.walkdirs.
271
        ((directory-relpath, directory-path-from-root, directory-fileid),
1852.15.3 by Robert Collins
Add a first-cut Tree.walkdirs method.
272
        [(relpath, basename, kind, lstat, path_from_tree_root, file_id, 
1852.15.7 by Robert Collins
Start testing behaviour of unknowns in WorkingTree.walkdirs.
273
          versioned_kind), ...]),
274
         - directory-relpath is the containing dirs relpath from prefix
275
         - directory-path-from-root is the containing dirs path from /
276
         - directory-fileid is the id of the directory if it is versioned.
1852.15.3 by Robert Collins
Add a first-cut Tree.walkdirs method.
277
         - relpath is the relative path within the subtree being walked.
278
         - basename is the basename
279
         - kind is the kind of the file now. If unknonwn then the file is not
280
           present within the tree - but it may be recorded as versioned. See
281
           versioned_kind.
282
         - lstat is the stat data *if* the file was statted.
283
         - path_from_tree_root is the path from the root of the tree.
284
         - file_id is the file_id is the entry is versioned.
285
         - versioned_kind is the kind of the file as last recorded in the 
286
           versioning system. If 'unknown' the file is not versioned.
287
        One of 'kind' and 'versioned_kind' must not be 'unknown'.
288
289
        :param prefix: Start walking from prefix within the tree rather than
290
        at the root. This allows one to walk a subtree but get paths that are
291
        relative to a tree rooted higher up.
292
        :return: an iterator over the directory data.
293
        """
294
        raise NotImplementedError(self.walkdirs)
295
1852.7.1 by Robert Collins
Move RevisionTree out of tree.py.
296
1 by mbp at sourcefrog
import from baz patch-364
297
class EmptyTree(Tree):
1773.2.1 by Robert Collins
Teach all trees about unknowns, conflicts and get_parent_ids.
298
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
299
    def __init__(self):
1731.1.33 by Aaron Bentley
Revert no-special-root changes
300
        self._inventory = Inventory(root_id=None)
2079.1.1 by John Arbash Meinel
Create a deprecated bzrlib.tree.RevisionTree() in favor of bzrlib.revisiontree.RevisionTree()
301
        symbol_versioning.warn('EmptyTree is deprecated as of bzr 0.9 please'
302
                               ' use repository.revision_tree instead.',
303
                               DeprecationWarning, stacklevel=2)
1 by mbp at sourcefrog
import from baz patch-364
304
1773.2.1 by Robert Collins
Teach all trees about unknowns, conflicts and get_parent_ids.
305
    def get_parent_ids(self):
306
        return []
307
1092.2.6 by Robert Collins
symlink support updated to work
308
    def get_symlink_target(self, file_id):
309
        return None
310
1 by mbp at sourcefrog
import from baz patch-364
311
    def has_filename(self, filename):
312
        return False
313
1907.1.4 by Aaron Bentley
Restore RootEntry, but mark it deprecated, restore EmptyTree.kind
314
    def kind(self, file_id):
315
        assert self._inventory[file_id].kind == "directory"
316
        return "directory"
317
1731.1.56 by Aaron Bentley
Fix EmptyTree's default include_root
318
    def list_files(self, include_root=False):
1732.1.14 by John Arbash Meinel
Some speedups by not calling pathjoin()
319
        return iter([])
1 by mbp at sourcefrog
import from baz patch-364
320
    
974.1.12 by aaron.bentley at utoronto
Switched from text-id to hashcache for merge optimization
321
    def __contains__(self, file_id):
1711.9.11 by John Arbash Meinel
change return foo in bar to return (foo in bar)
322
        return (file_id in self._inventory)
974.1.12 by aaron.bentley at utoronto
Switched from text-id to hashcache for merge optimization
323
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
324
    def get_file_sha1(self, file_id, path=None, stat_value=None):
974.1.14 by aaron.bentley at utoronto
Fixed bugs in merge optimization
325
        return None
326
327
1 by mbp at sourcefrog
import from baz patch-364
328
######################################################################
329
# diff
330
331
# TODO: Merge these two functions into a single one that can operate
332
# on either a whole tree or a set of files.
333
334
# TODO: Return the diff in order by filename, not by category or in
335
# random order.  Can probably be done by lock-stepping through the
336
# filenames from both trees.
337
338
339
def file_status(filename, old_tree, new_tree):
340
    """Return single-letter status, old and new names for a file.
341
342
    The complexity here is in deciding how to represent renames;
343
    many complex cases are possible.
344
    """
345
    old_inv = old_tree.inventory
346
    new_inv = new_tree.inventory
347
    new_id = new_inv.path2id(filename)
348
    old_id = old_inv.path2id(filename)
349
350
    if not new_id and not old_id:
351
        # easy: doesn't exist in either; not versioned at all
352
        if new_tree.is_ignored(filename):
353
            return 'I', None, None
354
        else:
355
            return '?', None, None
356
    elif new_id:
357
        # There is now a file of this name, great.
358
        pass
359
    else:
360
        # There is no longer a file of this name, but we can describe
361
        # what happened to the file that used to have
362
        # this name.  There are two possibilities: either it was
363
        # deleted entirely, or renamed.
364
        assert old_id
365
        if new_inv.has_id(old_id):
366
            return 'X', old_inv.id2path(old_id), new_inv.id2path(old_id)
367
        else:
368
            return 'D', old_inv.id2path(old_id), None
369
370
    # if the file_id is new in this revision, it is added
371
    if new_id and not old_inv.has_id(new_id):
372
        return 'A'
373
374
    # if there used to be a file of this name, but that ID has now
375
    # disappeared, it is deleted
376
    if old_id and not new_inv.has_id(old_id):
377
        return 'D'
378
379
    return 'wtf?'
380
381
    
382
164 by mbp at sourcefrog
new 'renames' command
383
def find_renames(old_inv, new_inv):
384
    for file_id in old_inv:
385
        if file_id not in new_inv:
386
            continue
387
        old_name = old_inv.id2path(file_id)
388
        new_name = new_inv.id2path(file_id)
389
        if old_name != new_name:
390
            yield (old_name, new_name)
391
            
678 by Martin Pool
- export to tarballs
392
1551.7.22 by Aaron Bentley
Changes from review
393
def find_ids_across_trees(filenames, trees, require_versioned=True):
394
    """Find the ids corresponding to specified filenames.
395
    
396
    All matches in all trees will be used, and all children of matched
397
    directories will be used.
398
1551.10.7 by Aaron Bentley
Use new-style output for status
399
    :param filenames: The filenames to find file_ids for (if None, returns
400
        None)
1551.7.22 by Aaron Bentley
Changes from review
401
    :param trees: The trees to find file_ids within
402
    :param require_versioned: if true, all specified filenames must occur in
403
    at least one tree.
404
    :return: a set of file ids for the specified filenames and their children.
405
    """
406
    if not filenames:
407
        return None
2255.2.82 by Robert Collins
various notes about find_ids_across_trees
408
    specified_path_ids = _find_ids_across_trees(filenames, trees,
409
        require_versioned)
410
    return _find_children_across_trees(specified_path_ids, trees)
411
#    specified_ids = [id for path, id in _find_path_ids_across_trees(filenames, trees, require_versioned)]
412
#    return _find_children_across_trees(specified_ids, trees)
413
414
def find_path_ids_across_trees(filenames, trees, require_versioned=True):
415
    """Find the paths and ids corresponding to specified filenames.
416
    
417
    All matches in all trees will be used, and all children of matched
418
    directories will be included
419
420
    :param filenames: The filenames to find file_ids for
421
    :param trees: The trees to find file_ids within
422
    :param require_versioned: if true, all specified filenames must occur in
423
        at least one tree.
424
    :return: a set of (path, file ids) for the specified filenames and their
425
        children. The returned path is the path of the id in the first tree
426
        that contains it. This matters when files have been moved 
427
    """
428
    if not filenames:
429
        return set()
430
    # This function needs to know the ids for filenames in all trees, then
431
    # search for those same files and children in all the other trees.
432
    # it is complicated by the same path in two trees being able to have
433
    # different ids, which might both be present in both trees.
434
    # consider two trees, which have had 'mv foo bar' and 'mv baz foo' done
435
    # in this case, a diff of 'foo' should should changes to both the current
436
    # 'bar' and the current 'foo' which was baz. Its arguable that if 
437
    # the situation is 'mv parent/foo bar' and 'mv baz parent/foo', that 
438
    # we should return the current bar and the current parent/foo' - at the 
439
    # moment we do, but we loop around all ids and all trees: I*T checks.
440
    
441
    # Updating this algorithm to be fast in the common case:
442
    # nothing has moved, all files have the same id in parent, child and there
443
    # are only two trees (or one is working tree and the others are parents).
444
    # walk the dirstate. as we find each path, gather the paths of that
445
    # id in all trees. add a mapping from the id to the path in those trees.
446
    # now lookup children by id, again in all trees; for these trees that
447
    # nothing has moved in, the id->path mapping will allow us to find the
448
    # parent trivially. To answer 'has anything been moved' in one of the
449
    # dirstate parent trees though, we will need to stare harder at it.
450
451
    #  Now, given a path index, that is trivial for any one tree, and given
452
    #  that we can ask for additional data from a dirstate tree, its a single
453
    #  pass, though it will require scanning the entire tree to find paths
454
    #  that were at the current location.
455
    # ideal results?: There are three things: tree, path, id. Pathologically
456
    # we can have completely disjoint ids for each tree; but we cannot have 
457
    # disjoin paths for each tree, except if we scan each tree for the 
458
    # different ids from other trees.
459
460
    specified_path_ids = _find_ids_across_trees(filenames, trees,
461
        require_versioned)
462
    return _find_path_id_children_across_trees(specified_path_ids, trees)
463
464
465
def _find_ids_across_trees(filenames, trees, require_versioned):
1551.7.22 by Aaron Bentley
Changes from review
466
    """Find the ids corresponding to specified filenames.
467
    
2255.2.82 by Robert Collins
various notes about find_ids_across_trees
468
    All matches in all trees will be used, but subdirectories are not scanned.
1551.7.22 by Aaron Bentley
Changes from review
469
1551.7.14 by Aaron Bentley
Use specified_file_ids instead of is_inside_any in compare_trees
470
    :param filenames: The filenames to find file_ids for
471
    :param trees: The trees to find file_ids within
1551.7.16 by Aaron Bentley
Fix docs
472
    :param require_versioned: if true, all specified filenames must occur in
2255.2.82 by Robert Collins
various notes about find_ids_across_trees
473
        at least one tree.
474
    :return: a set of (path, file ids) for the specified filenames
1551.7.14 by Aaron Bentley
Use specified_file_ids instead of is_inside_any in compare_trees
475
    """
1551.7.17 by Aaron Bentley
Switch to PathsNotVersioned, accept extra_trees
476
    not_versioned = []
1551.7.18 by Aaron Bentley
Indentation and documentation fixes
477
    interesting_ids = set()
478
    for tree_path in filenames:
479
        not_found = True
480
        for tree in trees:
2255.2.82 by Robert Collins
various notes about find_ids_across_trees
481
            file_id = tree.path2id(tree_path)
1551.7.18 by Aaron Bentley
Indentation and documentation fixes
482
            if file_id is not None:
483
                interesting_ids.add(file_id)
484
                not_found = False
485
        if not_found:
486
            not_versioned.append(tree_path)
1551.7.22 by Aaron Bentley
Changes from review
487
    if len(not_versioned) > 0 and require_versioned:
488
        raise errors.PathsNotVersionedError(not_versioned)
489
    return interesting_ids
490
491
492
def _find_children_across_trees(specified_ids, trees):
493
    """Return a set including specified ids and their children
1551.7.18 by Aaron Bentley
Indentation and documentation fixes
494
    
1551.7.22 by Aaron Bentley
Changes from review
495
    All matches in all trees will be used.
496
497
    :param trees: The trees to find file_ids within
498
    :return: a set containing all specified ids and their children 
499
    """
500
    interesting_ids = set(specified_ids)
1551.7.18 by Aaron Bentley
Indentation and documentation fixes
501
    pending = interesting_ids
502
    # now handle children of interesting ids
503
    # we loop so that we handle all children of each id in both trees
504
    while len(pending) > 0:
505
        new_pending = set()
506
        for file_id in pending:
1551.7.14 by Aaron Bentley
Use specified_file_ids instead of is_inside_any in compare_trees
507
            for tree in trees:
2255.2.82 by Robert Collins
various notes about find_ids_across_trees
508
                if not tree.has_id(file_id):
1551.7.18 by Aaron Bentley
Indentation and documentation fixes
509
                    continue
510
                entry = tree.inventory[file_id]
511
                for child in getattr(entry, 'children', {}).itervalues():
512
                    if child.file_id not in interesting_ids:
513
                        new_pending.add(child.file_id)
514
        interesting_ids.update(new_pending)
515
        pending = new_pending
1551.7.14 by Aaron Bentley
Use specified_file_ids instead of is_inside_any in compare_trees
516
    return interesting_ids
1852.8.2 by Robert Collins
Add InterTree class to represent InterTree operations.
517
518
519
class InterTree(InterObject):
520
    """This class represents operations taking place between two Trees.
521
522
    Its instances have methods like 'compare' and contain references to the
523
    source and target trees these operations are to be carried out on.
524
525
    clients of bzrlib should not need to use InterTree directly, rather they
526
    should use the convenience methods on Tree such as 'Tree.compare()' which
527
    will pass through to InterTree as appropriate.
528
    """
529
1910.2.15 by Aaron Bentley
Back out inter.get changes, make optimizers an ordered list
530
    _optimisers = []
1852.8.2 by Robert Collins
Add InterTree class to represent InterTree operations.
531
1852.11.1 by Robert Collins
Deprecate compare_trees and move its body to InterTree.changes_from.
532
    @needs_read_lock
1852.9.4 by Robert Collins
Add minimal test for Tree.compare(extra_trees=...).
533
    def compare(self, want_unchanged=False, specific_files=None,
1731.1.33 by Aaron Bentley
Revert no-special-root changes
534
        extra_trees=None, require_versioned=False, include_root=False):
1852.9.3 by Robert Collins
Convert the test_delta tests to intertree_implementation and workingtree_implementation tests as appropriate.
535
        """Return the changes from source to target.
1852.8.3 by Robert Collins
Implement an InterTreeTestProvider and a trivial test_compare test case.
536
537
        :return: A TreeDelta.
1852.9.4 by Robert Collins
Add minimal test for Tree.compare(extra_trees=...).
538
        :param specific_files: An optional list of file paths to restrict the
539
            comparison to. When mapping filenames to ids, all matches in all
540
            trees (including optional extra_trees) are used, and all children of
541
            matched directories are included.
542
        :param want_unchanged: An optional boolean requesting the inclusion of
543
            unchanged entries in the result.
544
        :param extra_trees: An optional list of additional trees to use when
545
            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.
546
        :param require_versioned: An optional boolean (defaults to False). When
547
            supplied and True all the 'specific_files' must be versioned, or
548
            a PathsNotVersionedError will be thrown.
1852.8.3 by Robert Collins
Implement an InterTreeTestProvider and a trivial test_compare test case.
549
        """
2255.2.82 by Robert Collins
various notes about find_ids_across_trees
550
        # NB: show_status depends on being able to pass in non-versioned files
551
        # and report them as unknown
1852.11.1 by Robert Collins
Deprecate compare_trees and move its body to InterTree.changes_from.
552
        trees = (self.source, self.target)
553
        if extra_trees is not None:
554
            trees = trees + tuple(extra_trees)
555
        specific_file_ids = find_ids_across_trees(specific_files,
556
            trees, require_versioned=require_versioned)
557
        if specific_files and not specific_file_ids:
558
            # All files are unversioned, so just return an empty delta
559
            # _compare_trees would think we want a complete delta
560
            return delta.TreeDelta()
561
        return delta._compare_trees(self.source, self.target, want_unchanged,
1910.2.56 by Aaron Bentley
More work on bundles
562
            specific_file_ids, include_root)
2012.1.1 by Aaron Bentley
Implement change iterator
563
2012.1.10 by Aaron Bentley
Make iter_changes private, so it can be changed freely
564
    def _iter_changes(self, from_tree, to_tree, include_unchanged, 
2012.1.16 by Aaron Bentley
Support progress bars in iter_changes
565
                      specific_file_ids, pb):
2012.1.1 by Aaron Bentley
Implement change iterator
566
        """Generate an iterator of changes between trees.
567
568
        A tuple is returned:
569
        (file_id, path, changed_content, versioned, parent, name, kind,
570
         executable)
571
2012.1.3 by Aaron Bentley
Always generate tuples (because kind is always used, even when not different)
572
        Path is relative to the to_tree.  changed_content is True if the file's
573
        content has changed.  This includes changes to its kind, and to
574
        a symlink's target.
2012.1.1 by Aaron Bentley
Implement change iterator
575
2012.1.15 by Aaron Bentley
Minor tweaks
576
        versioned, parent, name, kind, executable are tuples of (from, to).
577
        If a file is missing in a tree, its kind is None.
2012.1.1 by Aaron Bentley
Implement change iterator
578
579
        Iteration is done in parent-to-child order, relative to the to_tree.
580
        """
581
        to_paths = {}
1551.9.29 by Aaron Bentley
Optimize Tree._iter_changes with specific file_ids
582
        from_entries_by_dir = list(from_tree.inventory.iter_entries_by_dir(
583
            specific_file_ids=specific_file_ids))
2012.1.16 by Aaron Bentley
Support progress bars in iter_changes
584
        from_data = dict((e.file_id, (p, e)) for p, e in from_entries_by_dir)
1551.9.31 by Aaron Bentley
Handle to_paths correctly when parent of file is not in specified_ids
585
        to_entries_by_dir = list(to_tree.inventory.iter_entries_by_dir(
586
            specific_file_ids=specific_file_ids))
1551.9.29 by Aaron Bentley
Optimize Tree._iter_changes with specific file_ids
587
        num_entries = len(from_entries_by_dir) + len(to_entries_by_dir)
2012.1.16 by Aaron Bentley
Support progress bars in iter_changes
588
        entry_count = 0
589
        for to_path, to_entry in to_entries_by_dir:
2012.1.1 by Aaron Bentley
Implement change iterator
590
            file_id = to_entry.file_id
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
591
            to_paths[file_id] = to_path
2012.1.16 by Aaron Bentley
Support progress bars in iter_changes
592
            entry_count += 1
2012.1.1 by Aaron Bentley
Implement change iterator
593
            changed_content = False
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
594
            from_path, from_entry = from_data.get(file_id, (None, None))
595
            from_versioned = (from_entry is not None)
596
            if from_entry is not None:
597
                from_versioned = True
598
                from_name = from_entry.name
2012.1.1 by Aaron Bentley
Implement change iterator
599
                from_parent = from_entry.parent_id
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
600
                from_kind, from_executable, from_stat = \
601
                    from_tree._comparison_data(from_entry, from_path)
1551.9.29 by Aaron Bentley
Optimize Tree._iter_changes with specific file_ids
602
                entry_count += 1
2012.1.1 by Aaron Bentley
Implement change iterator
603
            else:
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
604
                from_versioned = False
2012.1.1 by Aaron Bentley
Implement change iterator
605
                from_kind = None
606
                from_parent = None
607
                from_name = None
608
                from_executable = None
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
609
            versioned = (from_versioned, True)
610
            to_kind, to_executable, to_stat = \
611
                to_tree._comparison_data(to_entry, to_path)
2012.1.3 by Aaron Bentley
Always generate tuples (because kind is always used, even when not different)
612
            kind = (from_kind, to_kind)
613
            if kind[0] != kind[1]:
2012.1.1 by Aaron Bentley
Implement change iterator
614
                changed_content = True
2012.1.2 by Aaron Bentley
reimplement compare_trees
615
            elif from_kind == 'file':
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
616
                from_size = from_tree._file_size(from_entry, from_stat)
617
                to_size = to_tree._file_size(to_entry, to_stat)
618
                if from_size != to_size:
619
                    changed_content = True
620
                elif (from_tree.get_file_sha1(file_id, from_path, from_stat) !=
621
                    to_tree.get_file_sha1(file_id, to_path, to_stat)):
2012.1.2 by Aaron Bentley
reimplement compare_trees
622
                    changed_content = True
623
            elif from_kind == 'symlink':
624
                if (from_tree.get_symlink_target(file_id) != 
625
                    to_tree.get_symlink_target(file_id)):
626
                    changed_content = True
2012.1.3 by Aaron Bentley
Always generate tuples (because kind is always used, even when not different)
627
            parent = (from_parent, to_entry.parent_id)
628
            name = (from_name, to_entry.name)
629
            executable = (from_executable, to_executable)
2012.1.16 by Aaron Bentley
Support progress bars in iter_changes
630
            if pb is not None:
631
                pb.update('comparing files', entry_count, num_entries)
2012.1.3 by Aaron Bentley
Always generate tuples (because kind is always used, even when not different)
632
            if (changed_content is not False or versioned[0] != versioned[1] 
633
                or parent[0] != parent[1] or name[0] != name[1] or 
634
                executable[0] != executable[1] or include_unchanged):
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
635
                yield (file_id, to_path, changed_content, versioned, parent,
2012.1.1 by Aaron Bentley
Implement change iterator
636
                       name, kind, executable)
637
1551.9.31 by Aaron Bentley
Handle to_paths correctly when parent of file is not in specified_ids
638
        def get_to_path(from_entry):
639
            if from_entry.parent_id is None:
640
                to_path = ''
641
            else:
642
                if from_entry.parent_id not in to_paths:
643
                    get_to_path(from_tree.inventory[from_entry.parent_id])
644
                to_path = osutils.pathjoin(to_paths[from_entry.parent_id],
645
                                           from_entry.name)
646
            to_paths[from_entry.file_id] = to_path
647
            return to_path
648
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
649
        for path, from_entry in from_entries_by_dir:
2012.1.1 by Aaron Bentley
Implement change iterator
650
            file_id = from_entry.file_id
651
            if file_id in to_paths:
652
                continue
1551.9.31 by Aaron Bentley
Handle to_paths correctly when parent of file is not in specified_ids
653
            to_path = get_to_path(from_entry)
2012.1.16 by Aaron Bentley
Support progress bars in iter_changes
654
            entry_count += 1
655
            if pb is not None:
656
                pb.update('comparing files', entry_count, num_entries)
2012.1.1 by Aaron Bentley
Implement change iterator
657
            versioned = (True, False)
658
            parent = (from_entry.parent_id, None)
659
            name = (from_entry.name, None)
2012.1.7 by Aaron Bentley
Get tree._iter_changed down to ~ 1 stat per file
660
            from_kind, from_executable, stat_value = \
661
                from_tree._comparison_data(from_entry, path)
662
            kind = (from_kind, None)
2012.1.2 by Aaron Bentley
reimplement compare_trees
663
            executable = (from_executable, None)
2012.1.1 by Aaron Bentley
Implement change iterator
664
            changed_content = True
665
            # the parent's path is necessarily known at this point.
666
            yield(file_id, to_path, changed_content, versioned, parent,
667
                  name, kind, executable)
2012.1.8 by Aaron Bentley
Merge from bzr.dev
668
2079.1.1 by John Arbash Meinel
Create a deprecated bzrlib.tree.RevisionTree() in favor of bzrlib.revisiontree.RevisionTree()
669
670
# This was deprecated before 0.12, but did not have an official warning
671
@symbol_versioning.deprecated_function(symbol_versioning.zero_twelve)
672
def RevisionTree(*args, **kwargs):
673
    """RevisionTree has moved to bzrlib.revisiontree.RevisionTree()
674
675
    Accessing it as bzrlib.tree.RevisionTree has been deprecated as of
676
    bzr 0.12.
677
    """
678
    from bzrlib.revisiontree import RevisionTree as _RevisionTree
679
    return _RevisionTree(*args, **kwargs)
680
 
681