/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/revisiontree.py

  • Committer: John Arbash Meinel
  • Date: 2009-06-18 18:18:36 UTC
  • mto: This revision was merged to the branch mainline in revision 4461.
  • Revision ID: john@arbash-meinel.com-20090618181836-biodfkat9a8eyzjz
The new add_inventory_by_delta is returning a CHKInventory when mapping from NULL
Which is completely valid, but 'broke' one of the tests.
So to fix it, changed the test to use CHKInventories on both sides, and add an __eq__
member. The nice thing is that CHKInventory.__eq__ is fairly cheap, since it only
has to check the root keys.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2007 Canonical Ltd
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""RevisionTree - a Tree implementation backed by repository data for a revision."""
 
18
 
 
19
from cStringIO import StringIO
 
20
 
 
21
from bzrlib import (
 
22
    errors,
 
23
    osutils,
 
24
    revision,
 
25
    symbol_versioning,
 
26
    tree,
 
27
    )
 
28
 
 
29
 
 
30
class RevisionTree(tree.Tree):
 
31
    """Tree viewing a previous revision.
 
32
 
 
33
    File text can be retrieved from the text store.
 
34
    """
 
35
 
 
36
    def __init__(self, branch, inv, revision_id):
 
37
        # for compatability the 'branch' parameter has not been renamed to
 
38
        # repository at this point. However, we should change RevisionTree's
 
39
        # construction to always be via Repository and not via direct
 
40
        # construction - this will mean that we can change the constructor
 
41
        # with much less chance of breaking client code.
 
42
        self._repository = branch
 
43
        self._inventory = inv
 
44
        self._revision_id = revision_id
 
45
        self._rules_searcher = None
 
46
 
 
47
    def supports_tree_reference(self):
 
48
        return getattr(self._repository._format, "supports_tree_reference",
 
49
            False)
 
50
 
 
51
    def get_parent_ids(self):
 
52
        """See Tree.get_parent_ids.
 
53
 
 
54
        A RevisionTree's parents match the revision graph.
 
55
        """
 
56
        if self._revision_id in (None, revision.NULL_REVISION):
 
57
            parent_ids = []
 
58
        else:
 
59
            parent_ids = self._repository.get_revision(
 
60
                self._revision_id).parent_ids
 
61
        return parent_ids
 
62
 
 
63
    def get_revision_id(self):
 
64
        """Return the revision id associated with this tree."""
 
65
        return self._revision_id
 
66
 
 
67
    def get_file_text(self, file_id, path=None):
 
68
        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
 
69
        return ''.join(content)
 
70
 
 
71
    def get_file(self, file_id, path=None):
 
72
        return StringIO(self.get_file_text(file_id))
 
73
 
 
74
    def iter_files_bytes(self, desired_files):
 
75
        """See Tree.iter_files_bytes.
 
76
 
 
77
        This version is implemented on top of Repository.extract_files_bytes"""
 
78
        repo_desired_files = [(f, self.inventory[f].revision, i)
 
79
                              for f, i in desired_files]
 
80
        try:
 
81
            for result in self._repository.iter_files_bytes(repo_desired_files):
 
82
                yield result
 
83
        except errors.RevisionNotPresent, e:
 
84
            raise errors.NoSuchFile(e.revision_id)
 
85
 
 
86
    def annotate_iter(self, file_id,
 
87
                      default_revision=revision.CURRENT_REVISION):
 
88
        """See Tree.annotate_iter"""
 
89
        text_key = (file_id, self.inventory[file_id].revision)
 
90
        annotations = self._repository.texts.annotate(text_key)
 
91
        return [(key[-1], line) for key, line in annotations]
 
92
 
 
93
    def get_file_size(self, file_id):
 
94
        """See Tree.get_file_size"""
 
95
        return self._inventory[file_id].text_size
 
96
 
 
97
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
98
        ie = self._inventory[file_id]
 
99
        if ie.kind == "file":
 
100
            return ie.text_sha1
 
101
        return None
 
102
 
 
103
    def get_file_mtime(self, file_id, path=None):
 
104
        ie = self._inventory[file_id]
 
105
        revision = self._repository.get_revision(ie.revision)
 
106
        return revision.timestamp
 
107
 
 
108
    def is_executable(self, file_id, path=None):
 
109
        ie = self._inventory[file_id]
 
110
        if ie.kind != "file":
 
111
            return None
 
112
        return ie.executable
 
113
 
 
114
    def has_filename(self, filename):
 
115
        return bool(self.inventory.path2id(filename))
 
116
 
 
117
    def list_files(self, include_root=False, from_dir=None, recursive=True):
 
118
        # The only files returned by this are those from the version
 
119
        inv = self.inventory
 
120
        if from_dir is None:
 
121
            from_dir_id = None
 
122
        else:
 
123
            from_dir_id = inv.path2id(from_dir)
 
124
            if from_dir_id is None:
 
125
                # Directory not versioned
 
126
                return
 
127
        entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
 
128
        if inv.root is not None and not include_root and from_dir is None:
 
129
            # skip the root for compatability with the current apis.
 
130
            entries.next()
 
131
        for path, entry in entries:
 
132
            yield path, 'V', entry.kind, entry.file_id, entry
 
133
 
 
134
    def get_symlink_target(self, file_id):
 
135
        ie = self._inventory[file_id]
 
136
        # Inventories store symlink targets in unicode
 
137
        return ie.symlink_target
 
138
 
 
139
    def get_reference_revision(self, file_id, path=None):
 
140
        return self.inventory[file_id].reference_revision
 
141
 
 
142
    def get_root_id(self):
 
143
        if self.inventory.root:
 
144
            return self.inventory.root.file_id
 
145
 
 
146
    def kind(self, file_id):
 
147
        return self._inventory[file_id].kind
 
148
 
 
149
    def path_content_summary(self, path):
 
150
        """See Tree.path_content_summary."""
 
151
        id = self.inventory.path2id(path)
 
152
        if id is None:
 
153
            return ('missing', None, None, None)
 
154
        entry = self._inventory[id]
 
155
        kind = entry.kind
 
156
        if kind == 'file':
 
157
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
 
158
        elif kind == 'symlink':
 
159
            return (kind, None, None, entry.symlink_target)
 
160
        else:
 
161
            return (kind, None, None, None)
 
162
 
 
163
    def _comparison_data(self, entry, path):
 
164
        if entry is None:
 
165
            return None, False, None
 
166
        return entry.kind, entry.executable, None
 
167
 
 
168
    def _file_size(self, entry, stat_value):
 
169
        return entry.text_size
 
170
 
 
171
    def _get_ancestors(self, default_revision):
 
172
        return set(self._repository.get_ancestry(self._revision_id,
 
173
                                                 topo_sorted=False))
 
174
 
 
175
    def lock_read(self):
 
176
        self._repository.lock_read()
 
177
 
 
178
    def __repr__(self):
 
179
        return '<%s instance at %x, rev_id=%r>' % (
 
180
            self.__class__.__name__, id(self), self._revision_id)
 
181
 
 
182
    def unlock(self):
 
183
        self._repository.unlock()
 
184
 
 
185
    def walkdirs(self, prefix=""):
 
186
        _directory = 'directory'
 
187
        inv = self.inventory
 
188
        top_id = inv.path2id(prefix)
 
189
        if top_id is None:
 
190
            pending = []
 
191
        else:
 
192
            pending = [(prefix, '', _directory, None, top_id, None)]
 
193
        while pending:
 
194
            dirblock = []
 
195
            currentdir = pending.pop()
 
196
            # 0 - relpath, 1- basename, 2- kind, 3- stat, id, v-kind
 
197
            if currentdir[0]:
 
198
                relroot = currentdir[0] + '/'
 
199
            else:
 
200
                relroot = ""
 
201
            # FIXME: stash the node in pending
 
202
            entry = inv[currentdir[4]]
 
203
            for name, child in entry.sorted_children():
 
204
                toppath = relroot + name
 
205
                dirblock.append((toppath, name, child.kind, None,
 
206
                    child.file_id, child.kind
 
207
                    ))
 
208
            yield (currentdir[0], entry.file_id), dirblock
 
209
            # push the user specified dirs from dirblock
 
210
            for dir in reversed(dirblock):
 
211
                if dir[2] == _directory:
 
212
                    pending.append(dir)
 
213
 
 
214
    def _get_rules_searcher(self, default_searcher):
 
215
        """See Tree._get_rules_searcher."""
 
216
        if self._rules_searcher is None:
 
217
            self._rules_searcher = super(RevisionTree,
 
218
                self)._get_rules_searcher(default_searcher)
 
219
        return self._rules_searcher
 
220
 
 
221
 
 
222
class InterCHKRevisionTree(tree.InterTree):
 
223
    """Fast path optimiser for RevisionTrees with CHK inventories."""
 
224
 
 
225
    @staticmethod
 
226
    def is_compatible(source, target):
 
227
        if (isinstance(source, RevisionTree)
 
228
            and isinstance(target, RevisionTree)):
 
229
            try:
 
230
                # Only CHK inventories have id_to_entry attribute
 
231
                source.inventory.id_to_entry
 
232
                target.inventory.id_to_entry
 
233
                return True
 
234
            except AttributeError:
 
235
                pass
 
236
        return False
 
237
 
 
238
    def iter_changes(self, include_unchanged=False,
 
239
                     specific_files=None, pb=None, extra_trees=[],
 
240
                     require_versioned=True, want_unversioned=False):
 
241
        lookup_trees = [self.source]
 
242
        if extra_trees:
 
243
             lookup_trees.extend(extra_trees)
 
244
        if specific_files == []:
 
245
            specific_file_ids = []
 
246
        else:
 
247
            specific_file_ids = self.target.paths2ids(specific_files,
 
248
                lookup_trees, require_versioned=require_versioned)
 
249
 
 
250
        # FIXME: It should be possible to delegate include_unchanged handling
 
251
        # to CHKInventory.iter_changes and do a better job there -- vila
 
252
        # 20090304
 
253
        if include_unchanged:
 
254
            changed_file_ids = []
 
255
        for result in self.target.inventory.iter_changes(self.source.inventory):
 
256
            if (specific_file_ids is not None
 
257
                and not result[0] in specific_file_ids):
 
258
                # CHKMap.iter_changes is clean and fast. Better filter out
 
259
                # the specific files *after* it did its job.
 
260
                continue
 
261
            yield result
 
262
            if include_unchanged:
 
263
                # Keep track of yielded results (cheaper than building the
 
264
                # whole inventory).
 
265
                changed_file_ids.append(result[0])
 
266
        if include_unchanged:
 
267
            # CHKMap avoid being O(tree), so we go to O(tree) only if
 
268
            # required to.
 
269
            # Now walk the whole inventory, excluding the already yielded
 
270
            # file ids
 
271
            changed_file_ids = set(changed_file_ids)
 
272
            for relpath, entry in self.target.inventory.iter_entries():
 
273
                if (specific_file_ids is not None
 
274
                    and not entry.file_id in specific_file_ids):
 
275
                    continue
 
276
                if not entry.file_id in changed_file_ids:
 
277
                    yield (entry.file_id,
 
278
                           (relpath, relpath), # Not renamed
 
279
                           False, # Not modified
 
280
                           (True, True), # Still  versioned
 
281
                           (entry.parent_id, entry.parent_id),
 
282
                           (entry.name, entry.name),
 
283
                           (entry.kind, entry.kind),
 
284
                           (entry.executable, entry.executable))
 
285
 
 
286
 
 
287
tree.InterTree.register_optimiser(InterCHKRevisionTree)