/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-04-21 23:54:16 UTC
  • mto: (4300.1.7 groupcompress_info)
  • mto: This revision was merged to the branch mainline in revision 4301.
  • Revision ID: john@arbash-meinel.com-20090421235416-f0cz6ilf5cufbugi
Fix bug #364900, properly remove the 64kB that was just encoded in the copy.
Also, stop supporting None as a copy length in 'encode_copy_instruction'.
It was only used by the test suite, and it is good to pull that sort of thing out of
production code. (Besides, setting the copy to 64kB has the same effect.)

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 True
 
49
 
 
50
    def get_parent_ids(self):
 
51
        """See Tree.get_parent_ids.
 
52
 
 
53
        A RevisionTree's parents match the revision graph.
 
54
        """
 
55
        if self._revision_id in (None, revision.NULL_REVISION):
 
56
            parent_ids = []
 
57
        else:
 
58
            parent_ids = self._repository.get_revision(
 
59
                self._revision_id).parent_ids
 
60
        return parent_ids
 
61
 
 
62
    def get_revision_id(self):
 
63
        """Return the revision id associated with this tree."""
 
64
        return self._revision_id
 
65
 
 
66
    def get_file_text(self, file_id, path=None):
 
67
        _, content = list(self.iter_files_bytes([(file_id, None)]))[0]
 
68
        return ''.join(content)
 
69
 
 
70
    def get_file(self, file_id, path=None):
 
71
        return StringIO(self.get_file_text(file_id))
 
72
 
 
73
    def iter_files_bytes(self, desired_files):
 
74
        """See Tree.iter_files_bytes.
 
75
 
 
76
        This version is implemented on top of Repository.extract_files_bytes"""
 
77
        repo_desired_files = [(f, self.inventory[f].revision, i)
 
78
                              for f, i in desired_files]
 
79
        try:
 
80
            for result in self._repository.iter_files_bytes(repo_desired_files):
 
81
                yield result
 
82
        except errors.RevisionNotPresent, e:
 
83
            raise errors.NoSuchFile(e.revision_id)
 
84
 
 
85
    def annotate_iter(self, file_id,
 
86
                      default_revision=revision.CURRENT_REVISION):
 
87
        """See Tree.annotate_iter"""
 
88
        text_key = (file_id, self.inventory[file_id].revision)
 
89
        annotations = self._repository.texts.annotate(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
        revision = self._repository.get_revision(ie.revision)
 
105
        return revision.timestamp
 
106
 
 
107
    def is_executable(self, file_id, path=None):
 
108
        ie = self._inventory[file_id]
 
109
        if ie.kind != "file":
 
110
            return None
 
111
        return ie.executable
 
112
 
 
113
    def has_filename(self, filename):
 
114
        return bool(self.inventory.path2id(filename))
 
115
 
 
116
    def list_files(self, include_root=False):
 
117
        # The only files returned by this are those from the version
 
118
        entries = self.inventory.iter_entries()
 
119
        # skip the root for compatability with the current apis.
 
120
        if self.inventory.root is not None and not include_root:
 
121
            # skip the root for compatability with the current apis.
 
122
            entries.next()
 
123
        for path, entry in entries:
 
124
            yield path, 'V', entry.kind, entry.file_id, entry
 
125
 
 
126
    def get_symlink_target(self, file_id):
 
127
        ie = self._inventory[file_id]
 
128
        return ie.symlink_target;
 
129
 
 
130
    def get_reference_revision(self, file_id, path=None):
 
131
        return self.inventory[file_id].reference_revision
 
132
 
 
133
    def get_root_id(self):
 
134
        if self.inventory.root:
 
135
            return self.inventory.root.file_id
 
136
 
 
137
    def kind(self, file_id):
 
138
        return self._inventory[file_id].kind
 
139
 
 
140
    def path_content_summary(self, path):
 
141
        """See Tree.path_content_summary."""
 
142
        id = self.inventory.path2id(path)
 
143
        if id is None:
 
144
            return ('missing', None, None, None)
 
145
        entry = self._inventory[id]
 
146
        kind = entry.kind
 
147
        if kind == 'file':
 
148
            return (kind, entry.text_size, entry.executable, entry.text_sha1)
 
149
        elif kind == 'symlink':
 
150
            return (kind, None, None, entry.symlink_target)
 
151
        else:
 
152
            return (kind, None, None, None)
 
153
 
 
154
    def _comparison_data(self, entry, path):
 
155
        if entry is None:
 
156
            return None, False, None
 
157
        return entry.kind, entry.executable, None
 
158
 
 
159
    def _file_size(self, entry, stat_value):
 
160
        return entry.text_size
 
161
 
 
162
    def _get_ancestors(self, default_revision):
 
163
        return set(self._repository.get_ancestry(self._revision_id,
 
164
                                                 topo_sorted=False))
 
165
 
 
166
    def lock_read(self):
 
167
        self._repository.lock_read()
 
168
 
 
169
    def __repr__(self):
 
170
        return '<%s instance at %x, rev_id=%r>' % (
 
171
            self.__class__.__name__, id(self), self._revision_id)
 
172
 
 
173
    def unlock(self):
 
174
        self._repository.unlock()
 
175
 
 
176
    def walkdirs(self, prefix=""):
 
177
        _directory = 'directory'
 
178
        inv = self.inventory
 
179
        top_id = inv.path2id(prefix)
 
180
        if top_id is None:
 
181
            pending = []
 
182
        else:
 
183
            pending = [(prefix, '', _directory, None, top_id, None)]
 
184
        while pending:
 
185
            dirblock = []
 
186
            currentdir = pending.pop()
 
187
            # 0 - relpath, 1- basename, 2- kind, 3- stat, id, v-kind
 
188
            if currentdir[0]:
 
189
                relroot = currentdir[0] + '/'
 
190
            else:
 
191
                relroot = ""
 
192
            # FIXME: stash the node in pending
 
193
            entry = inv[currentdir[4]]
 
194
            for name, child in entry.sorted_children():
 
195
                toppath = relroot + name
 
196
                dirblock.append((toppath, name, child.kind, None,
 
197
                    child.file_id, child.kind
 
198
                    ))
 
199
            yield (currentdir[0], entry.file_id), dirblock
 
200
            # push the user specified dirs from dirblock
 
201
            for dir in reversed(dirblock):
 
202
                if dir[2] == _directory:
 
203
                    pending.append(dir)
 
204
 
 
205
    def _get_rules_searcher(self, default_searcher):
 
206
        """See Tree._get_rules_searcher."""
 
207
        if self._rules_searcher is None:
 
208
            self._rules_searcher = super(RevisionTree,
 
209
                self)._get_rules_searcher(default_searcher)
 
210
        return self._rules_searcher
 
211
 
 
212
 
 
213
class InterCHKRevisionTree(tree.InterTree):
 
214
    """Fast path optimiser for RevisionTrees with CHK inventories."""
 
215
 
 
216
    @staticmethod
 
217
    def is_compatible(source, target):
 
218
        if (isinstance(source, RevisionTree)
 
219
            and isinstance(target, RevisionTree)):
 
220
            try:
 
221
                # Only CHK inventories have id_to_entry attribute
 
222
                source.inventory.id_to_entry
 
223
                target.inventory.id_to_entry
 
224
                return True
 
225
            except AttributeError:
 
226
                pass
 
227
        return False
 
228
 
 
229
    def iter_changes(self, include_unchanged=False,
 
230
                     specific_files=None, pb=None, extra_trees=[],
 
231
                     require_versioned=True, want_unversioned=False):
 
232
        lookup_trees = [self.source]
 
233
        if extra_trees:
 
234
             lookup_trees.extend(extra_trees)
 
235
        if specific_files == []:
 
236
            specific_file_ids = []
 
237
        else:
 
238
            specific_file_ids = self.target.paths2ids(specific_files,
 
239
                lookup_trees, require_versioned=require_versioned)
 
240
 
 
241
        # FIXME: It should be possible to delegate include_unchanged handling
 
242
        # to CHKInventory.iter_changes and do a better job there -- vila
 
243
        # 20090304
 
244
        if include_unchanged:
 
245
            changed_file_ids = []
 
246
        for result in self.target.inventory.iter_changes(self.source.inventory):
 
247
            if (specific_file_ids is not None
 
248
                and not result[0] in specific_file_ids):
 
249
                # CHKMap.iter_changes is clean and fast. Better filter out
 
250
                # the specific files *after* it did its job.
 
251
                continue
 
252
            yield result
 
253
            if include_unchanged:
 
254
                # Keep track of yielded results (cheaper than building the
 
255
                # whole inventory).
 
256
                changed_file_ids.append(result[0])
 
257
        if include_unchanged:
 
258
            # CHKMap avoid being O(tree), so we go to O(tree) only if
 
259
            # required to.
 
260
            # Now walk the whole inventory, excluding the already yielded
 
261
            # file ids
 
262
            changed_file_ids = set(changed_file_ids)
 
263
            for relpath, entry in self.target.inventory.iter_entries():
 
264
                if (specific_file_ids is not None
 
265
                    and not entry.file_id in specific_file_ids):
 
266
                    continue
 
267
                if not entry.file_id in changed_file_ids:
 
268
                    yield (entry.file_id,
 
269
                           (relpath, relpath), # Not renamed
 
270
                           False, # Not modified
 
271
                           (True, True), # Still  versioned
 
272
                           (entry.parent_id, entry.parent_id),
 
273
                           (entry.name, entry.name),
 
274
                           (entry.kind, entry.kind),
 
275
                           (entry.executable, entry.executable))
 
276
 
 
277
 
 
278
tree.InterTree.register_optimiser(InterCHKRevisionTree)