~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

  • Committer: Martin Pool
  • Date: 2005-05-16 02:19:13 UTC
  • Revision ID: mbp@sourcefrog.net-20050516021913-3a933f871079e3fe
- patch from ddaa to create api/ directory 
  before building API docs

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""Tree classes, representing directory at point in time.
18
18
"""
19
19
 
20
 
import os
21
 
from cStringIO import StringIO
 
20
from sets import Set
 
21
import os.path, os, fnmatch
 
22
 
 
23
from osutils import pumpfile, filesize, quotefn, sha_file, \
 
24
     joinpath, splitpath, appendpath, isdir, isfile, file_kind, fingerprint_file
 
25
import errno
 
26
from stat import S_ISREG, S_ISDIR, ST_MODE, ST_SIZE
 
27
 
 
28
from inventory import Inventory
 
29
from trace import mutter, note
 
30
from errors import bailout
 
31
import branch
22
32
 
23
33
import bzrlib
24
 
from bzrlib.errors import BzrError, BzrCheckError
25
 
from bzrlib.inventory import Inventory
26
 
from bzrlib.osutils import fingerprint_file
27
 
import bzrlib.revision
28
 
from bzrlib.trace import mutter, note
29
34
 
30
 
class Tree(object):
 
35
class Tree:
31
36
    """Abstract file tree.
32
37
 
33
38
    There are several subclasses:
49
54
    trees or versioned trees.
50
55
    """
51
56
    
52
 
    def conflicts(self):
53
 
        """Get a list of the conflicts in the tree.
54
 
 
55
 
        Each conflict is an instance of bzrlib.conflicts.Conflict.
56
 
        """
57
 
        return []
58
 
 
59
 
    def get_parent_ids(self):
60
 
        """Get the parent ids for this tree. 
61
 
 
62
 
        :return: a list of parent ids. [] is returned to indicate
63
 
        a tree with no parents.
64
 
        :raises: BzrError if the parents are not known.
65
 
        """
66
 
        raise NotImplementedError(self.get_parent_ids)
67
 
    
68
57
    def has_filename(self, filename):
69
58
        """True if the tree has given filename."""
70
59
        raise NotImplementedError()
72
61
    def has_id(self, file_id):
73
62
        return self.inventory.has_id(file_id)
74
63
 
75
 
    def has_or_had_id(self, file_id):
76
 
        if file_id == self.inventory.root.file_id:
77
 
            return True
78
 
        return self.inventory.has_id(file_id)
79
 
 
80
64
    __contains__ = has_id
81
65
 
 
66
    def id_set(self):
 
67
        """Return set of all ids in this tree."""
 
68
        return self.inventory.id_set()
 
69
 
82
70
    def __iter__(self):
83
71
        return iter(self.inventory)
84
72
 
85
73
    def id2path(self, file_id):
86
74
        return self.inventory.id2path(file_id)
87
75
 
88
 
    def kind(self, file_id):
89
 
        raise NotImplementedError("subclasses must implement kind")
90
 
 
91
76
    def _get_inventory(self):
92
77
        return self._inventory
93
 
    
94
 
    def get_file_by_path(self, path):
95
 
        return self.get_file(self._inventory.path2id(path))
96
78
 
97
79
    inventory = property(_get_inventory,
98
80
                         doc="Inventory of this Tree")
99
81
 
100
82
    def _check_retrieved(self, ie, f):
101
 
        if not __debug__:
102
 
            return  
103
83
        fp = fingerprint_file(f)
104
84
        f.seek(0)
105
85
        
106
86
        if ie.text_size != None:
107
87
            if ie.text_size != fp['size']:
108
 
                raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
 
88
                bailout("mismatched size for file %r in %r" % (ie.file_id, self._store),
109
89
                        ["inventory expects %d bytes" % ie.text_size,
110
90
                         "file is actually %d bytes" % fp['size'],
111
91
                         "store is probably damaged/corrupt"])
112
92
 
113
93
        if ie.text_sha1 != fp['sha1']:
114
 
            raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
 
94
            bailout("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
115
95
                    ["inventory expects %s" % ie.text_sha1,
116
96
                     "file is actually %s" % fp['sha1'],
117
97
                     "store is probably damaged/corrupt"])
118
98
 
119
99
 
120
 
    def print_file(self, file_id):
121
 
        """Print file with id `file_id` to stdout."""
 
100
    def print_file(self, fileid):
 
101
        """Print file with id `fileid` to stdout."""
122
102
        import sys
123
 
        sys.stdout.write(self.get_file_text(file_id))
124
 
 
125
 
    def lock_read(self):
126
 
        pass
127
 
 
128
 
    def unknowns(self):
129
 
        """What files are present in this tree and unknown.
130
 
        
131
 
        :return: an iterator over the unknown files.
132
 
        """
133
 
        return iter([])
134
 
 
135
 
    def unlock(self):
136
 
        pass
137
 
 
138
 
    def filter_unversioned_files(self, paths):
139
 
        """Filter out paths that are not versioned.
140
 
 
141
 
        :return: set of paths.
142
 
        """
143
 
        # NB: we specifically *don't* call self.has_filename, because for
144
 
        # WorkingTrees that can indicate files that exist on disk but that 
145
 
        # are not versioned.
146
 
        pred = self.inventory.has_filename
147
 
        return set((p for p in paths if not pred(p)))
148
 
        
149
 
        
 
103
        pumpfile(self.get_file(fileid), sys.stdout)
 
104
        
 
105
        
 
106
    def export(self, dest):        
 
107
        """Export this tree to a new directory.
 
108
 
 
109
        `dest` should not exist, and will be created holding the
 
110
        contents of this tree.
 
111
 
 
112
        TODO: To handle subdirectories we need to create the
 
113
               directories first.
 
114
 
 
115
        :note: If the export fails, the destination directory will be
 
116
               left in a half-assed state.
 
117
        """
 
118
        os.mkdir(dest)
 
119
        mutter('export version %r' % self)
 
120
        inv = self.inventory
 
121
        for dp, ie in inv.iter_entries():
 
122
            kind = ie.kind
 
123
            fullpath = appendpath(dest, dp)
 
124
            if kind == 'directory':
 
125
                os.mkdir(fullpath)
 
126
            elif kind == 'file':
 
127
                pumpfile(self.get_file(ie.file_id), file(fullpath, 'wb'))
 
128
            else:
 
129
                bailout("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
 
130
            mutter("  export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
 
131
 
 
132
 
 
133
 
150
134
class RevisionTree(Tree):
151
135
    """Tree viewing a previous revision.
152
136
 
157
141
           or at least passing a description to the constructor.
158
142
    """
159
143
    
160
 
    def __init__(self, branch, inv, revision_id):
161
 
        # for compatability the 'branch' parameter has not been renamed to 
162
 
        # repository at this point. However, we should change RevisionTree's
163
 
        # construction to always be via Repository and not via direct 
164
 
        # construction - this will mean that we can change the constructor
165
 
        # with much less chance of breaking client code.
166
 
        self._repository = branch
167
 
        self._weave_store = branch.weave_store
 
144
    def __init__(self, store, inv):
 
145
        self._store = store
168
146
        self._inventory = inv
169
 
        self._revision_id = revision_id
170
 
 
171
 
    def get_parent_ids(self):
172
 
        """See Tree.get_parent_ids.
173
 
 
174
 
        A RevisionTree's parents match the revision graph.
175
 
        """
176
 
        parent_ids = self._repository.get_revision(self._revision_id).parent_ids
177
 
        return parent_ids
178
 
        
179
 
    def get_revision_id(self):
180
 
        """Return the revision id associated with this tree."""
181
 
        return self._revision_id
182
 
 
183
 
    def get_weave(self, file_id):
184
 
        return self._weave_store.get_weave(file_id,
185
 
                self._repository.get_transaction())
186
 
 
187
 
    def get_file_lines(self, file_id):
188
 
        ie = self._inventory[file_id]
189
 
        weave = self.get_weave(file_id)
190
 
        return weave.get_lines(ie.revision)
191
 
 
192
 
    def get_file_text(self, file_id):
193
 
        return ''.join(self.get_file_lines(file_id))
194
147
 
195
148
    def get_file(self, file_id):
196
 
        return StringIO(self.get_file_text(file_id))
 
149
        ie = self._inventory[file_id]
 
150
        f = self._store[ie.text_id]
 
151
        mutter("  get fileid{%s} from %r" % (file_id, self))
 
152
        self._check_retrieved(ie, f)
 
153
        return f
197
154
 
198
155
    def get_file_size(self, file_id):
199
156
        return self._inventory[file_id].text_size
200
157
 
201
 
    def get_file_sha1(self, file_id, path=None):
202
 
        ie = self._inventory[file_id]
203
 
        if ie.kind == "file":
204
 
            return ie.text_sha1
205
 
        return None
206
 
 
207
 
    def get_file_mtime(self, file_id, path=None):
208
 
        ie = self._inventory[file_id]
209
 
        revision = self._repository.get_revision(ie.revision)
210
 
        return revision.timestamp
211
 
 
212
 
    def is_executable(self, file_id, path=None):
213
 
        ie = self._inventory[file_id]
214
 
        if ie.kind != "file":
215
 
            return None 
216
 
        return self._inventory[file_id].executable
 
158
    def get_file_sha1(self, file_id):
 
159
        ie = self._inventory[file_id]
 
160
        return ie.text_sha1
217
161
 
218
162
    def has_filename(self, filename):
219
163
        return bool(self.inventory.path2id(filename))
221
165
    def list_files(self):
222
166
        # The only files returned by this are those from the version
223
167
        for path, entry in self.inventory.iter_entries():
224
 
            yield path, 'V', entry.kind, entry.file_id, entry
225
 
 
226
 
    def get_symlink_target(self, file_id):
227
 
        ie = self._inventory[file_id]
228
 
        return ie.symlink_target;
229
 
 
230
 
    def kind(self, file_id):
231
 
        return self._inventory[file_id].kind
232
 
 
233
 
    def lock_read(self):
234
 
        self._repository.lock_read()
235
 
 
236
 
    def unlock(self):
237
 
        self._repository.unlock()
 
168
            yield path, 'V', entry.kind, entry.file_id
238
169
 
239
170
 
240
171
class EmptyTree(Tree):
241
 
 
242
172
    def __init__(self):
243
173
        self._inventory = Inventory()
244
174
 
245
 
    def get_parent_ids(self):
246
 
        """See Tree.get_parent_ids.
247
 
 
248
 
        An EmptyTree always has NULL_REVISION as the only parent.
249
 
        """
250
 
        return []
251
 
 
252
 
    def get_symlink_target(self, file_id):
253
 
        return None
254
 
 
255
175
    def has_filename(self, filename):
256
176
        return False
257
177
 
258
 
    def kind(self, file_id):
259
 
        assert self._inventory[file_id].kind == "root_directory"
260
 
        return "root_directory"
261
 
 
262
178
    def list_files(self):
263
 
        return iter([])
 
179
        if False:  # just to make it a generator
 
180
            yield None
264
181
    
265
 
    def __contains__(self, file_id):
266
 
        return file_id in self._inventory
267
 
 
268
 
    def get_file_sha1(self, file_id, path=None):
269
 
        assert self._inventory[file_id].kind == "root_directory"
270
 
        return None
271
182
 
272
183
 
273
184
######################################################################
334
245
        if old_name != new_name:
335
246
            yield (old_name, new_name)
336
247
            
337
 
 
338