/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: Robert Collins
  • Date: 2010-07-04 06:22:11 UTC
  • mto: This revision was merged to the branch mainline in revision 5332.
  • Revision ID: robertc@robertcollins.net-20100704062211-tk9hw6bnsn5x47fm
``bzrlib.lsprof.profile`` will no longer silently generate bad threaded
profiles when concurrent profile requests are made. Instead the profile
requests will be serialised. Reentrant requests will now deadlock.
(Robert Collins)

Show diffs side-by-side

added added

removed removed

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