~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

  • Committer: Martin Pool
  • Date: 2006-06-04 22:04:20 UTC
  • mto: This revision was merged to the branch mainline in revision 1797.
  • Revision ID: mbp@sourcefrog.net-20060604220420-6bab3e2b6ebe013e
Cleanup more exception-formatting code

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
from bzrlib.trace import mutter, note
25
25
from bzrlib.errors import BzrError, BzrCheckError
26
26
from bzrlib.inventory import Inventory
27
 
from bzrlib.osutils import pumpfile, appendpath, fingerprint_file
28
 
 
29
 
 
30
 
exporters = {}
 
27
from bzrlib.osutils import fingerprint_file
31
28
 
32
29
class Tree(object):
33
30
    """Abstract file tree.
58
55
    def has_id(self, file_id):
59
56
        return self.inventory.has_id(file_id)
60
57
 
 
58
    def has_or_had_id(self, file_id):
 
59
        if file_id == self.inventory.root.file_id:
 
60
            return True
 
61
        return self.inventory.has_id(file_id)
 
62
 
61
63
    __contains__ = has_id
62
64
 
63
65
    def __iter__(self):
66
68
    def id2path(self, file_id):
67
69
        return self.inventory.id2path(file_id)
68
70
 
 
71
    def kind(self, file_id):
 
72
        raise NotImplementedError("subclasses must implement kind")
 
73
 
69
74
    def _get_inventory(self):
70
75
        return self._inventory
71
76
    
76
81
                         doc="Inventory of this Tree")
77
82
 
78
83
    def _check_retrieved(self, ie, f):
 
84
        if not __debug__:
 
85
            return  
79
86
        fp = fingerprint_file(f)
80
87
        f.seek(0)
81
88
        
97
104
        """Print file with id `file_id` to stdout."""
98
105
        import sys
99
106
        sys.stdout.write(self.get_file_text(file_id))
100
 
        
101
 
        
102
 
    def export(self, dest, format='dir', root=None):
103
 
        """Export this tree."""
104
 
        try:
105
 
            exporter = exporters[format]
106
 
        except KeyError:
107
 
            from bzrlib.errors import BzrCommandError
108
 
            raise BzrCommandError("export format %r not supported" % format)
109
 
        exporter(self, dest, root)
110
 
 
111
 
 
112
 
 
 
107
 
 
108
    def lock_read(self):
 
109
        pass
 
110
 
 
111
    def unlock(self):
 
112
        pass
 
113
 
 
114
    def filter_unversioned_files(self, paths):
 
115
        """Filter out paths that are not versioned.
 
116
 
 
117
        :return: set of paths.
 
118
        """
 
119
        # NB: we specifically *don't* call self.has_filename, because for
 
120
        # WorkingTrees that can indicate files that exist on disk but that 
 
121
        # are not versioned.
 
122
        pred = self.inventory.has_filename
 
123
        return set((p for p in paths if not pred(p)))
 
124
        
 
125
        
113
126
class RevisionTree(Tree):
114
127
    """Tree viewing a previous revision.
115
128
 
120
133
           or at least passing a description to the constructor.
121
134
    """
122
135
    
123
 
    def __init__(self, weave_store, inv, revision_id):
124
 
        self._weave_store = weave_store
 
136
    def __init__(self, branch, inv, revision_id):
 
137
        self._branch = branch
 
138
        self._weave_store = branch.weave_store
125
139
        self._inventory = inv
126
140
        self._revision_id = revision_id
127
141
 
 
142
    def get_revision_id(self):
 
143
        """Return the revision id associated with this tree."""
 
144
        return self._revision_id
 
145
 
128
146
    def get_weave(self, file_id):
129
 
        return self._weave_store.get_weave(file_id)
130
 
        
 
147
        return self._weave_store.get_weave(file_id,
 
148
                self._branch.get_transaction())
 
149
 
 
150
    def get_file_lines(self, file_id):
 
151
        ie = self._inventory[file_id]
 
152
        weave = self.get_weave(file_id)
 
153
        return weave.get_lines(ie.revision)
131
154
 
132
155
    def get_file_text(self, file_id):
133
 
        ie = self._inventory[file_id]
134
 
        weave = self.get_weave(file_id)
135
 
        idx = weave.lookup(ie.text_version)
136
 
        content = weave.get_text(idx)
137
 
        if len(content) != ie.text_size:
138
 
            raise BzrCheckError('mismatched size on revision %s of file %s: '
139
 
                                '%d vs %d bytes'
140
 
                                % (self._revision_id, file_id, len(content),
141
 
                                   ie.text_size))
142
 
        return content
 
156
        return ''.join(self.get_file_lines(file_id))
143
157
 
144
158
    def get_file(self, file_id):
145
159
        return StringIO(self.get_file_text(file_id))
147
161
    def get_file_size(self, file_id):
148
162
        return self._inventory[file_id].text_size
149
163
 
150
 
    def get_file_sha1(self, file_id):
 
164
    def get_file_sha1(self, file_id, path=None):
151
165
        ie = self._inventory[file_id]
152
166
        if ie.kind == "file":
153
167
            return ie.text_sha1
 
168
        return None
 
169
 
 
170
    def is_executable(self, file_id, path=None):
 
171
        ie = self._inventory[file_id]
 
172
        if ie.kind != "file":
 
173
            return None 
 
174
        return self._inventory[file_id].executable
154
175
 
155
176
    def has_filename(self, filename):
156
177
        return bool(self.inventory.path2id(filename))
158
179
    def list_files(self):
159
180
        # The only files returned by this are those from the version
160
181
        for path, entry in self.inventory.iter_entries():
161
 
            yield path, 'V', entry.kind, entry.file_id
 
182
            yield path, 'V', entry.kind, entry.file_id, entry
 
183
 
 
184
    def get_symlink_target(self, file_id):
 
185
        ie = self._inventory[file_id]
 
186
        return ie.symlink_target;
 
187
 
 
188
    def kind(self, file_id):
 
189
        return self._inventory[file_id].kind
 
190
 
 
191
    def lock_read(self):
 
192
        self._branch.lock_read()
 
193
 
 
194
    def unlock(self):
 
195
        self._branch.unlock()
162
196
 
163
197
 
164
198
class EmptyTree(Tree):
165
199
    def __init__(self):
166
200
        self._inventory = Inventory()
167
201
 
 
202
    def get_symlink_target(self, file_id):
 
203
        return None
 
204
 
168
205
    def has_filename(self, filename):
169
206
        return False
170
207
 
 
208
    def kind(self, file_id):
 
209
        assert self._inventory[file_id].kind == "root_directory"
 
210
        return "root_directory"
 
211
 
171
212
    def list_files(self):
172
 
        if False:  # just to make it a generator
173
 
            yield None
 
213
        return iter([])
174
214
    
175
215
    def __contains__(self, file_id):
176
216
        return file_id in self._inventory
177
217
 
178
 
    def get_file_sha1(self, file_id):
 
218
    def get_file_sha1(self, file_id, path=None):
179
219
        assert self._inventory[file_id].kind == "root_directory"
180
220
        return None
181
221
 
182
222
 
183
 
 
184
 
 
185
223
######################################################################
186
224
# diff
187
225
 
248
286
            
249
287
 
250
288
 
251
 
######################################################################
252
 
# export
253
 
 
254
 
def dir_exporter(tree, dest, root):
255
 
    """Export this tree to a new directory.
256
 
 
257
 
    `dest` should not exist, and will be created holding the
258
 
    contents of this tree.
259
 
 
260
 
    TODO: To handle subdirectories we need to create the
261
 
           directories first.
262
 
 
263
 
    :note: If the export fails, the destination directory will be
264
 
           left in a half-assed state.
265
 
    """
266
 
    import os
267
 
    os.mkdir(dest)
268
 
    mutter('export version %r' % tree)
269
 
    inv = tree.inventory
270
 
    for dp, ie in inv.iter_entries():
271
 
        kind = ie.kind
272
 
        fullpath = appendpath(dest, dp)
273
 
        if kind == 'directory':
274
 
            os.mkdir(fullpath)
275
 
        elif kind == 'file':
276
 
            pumpfile(tree.get_file(ie.file_id), file(fullpath, 'wb'))
277
 
        else:
278
 
            raise BzrError("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
279
 
        mutter("  export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
280
 
exporters['dir'] = dir_exporter
281
 
 
282
 
try:
283
 
    import tarfile
284
 
except ImportError:
285
 
    pass
286
 
else:
287
 
    def get_root_name(dest):
288
 
        """Get just the root name for a tarball.
289
 
 
290
 
        >>> get_root_name('mytar.tar')
291
 
        'mytar'
292
 
        >>> get_root_name('mytar.tar.bz2')
293
 
        'mytar'
294
 
        >>> get_root_name('tar.tar.tar.tgz')
295
 
        'tar.tar.tar'
296
 
        >>> get_root_name('bzr-0.0.5.tar.gz')
297
 
        'bzr-0.0.5'
298
 
        >>> get_root_name('a/long/path/mytar.tgz')
299
 
        'mytar'
300
 
        >>> get_root_name('../parent/../dir/other.tbz2')
301
 
        'other'
302
 
        """
303
 
        endings = ['.tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2']
304
 
        dest = os.path.basename(dest)
305
 
        for end in endings:
306
 
            if dest.endswith(end):
307
 
                return dest[:-len(end)]
308
 
 
309
 
    def tar_exporter(tree, dest, root, compression=None):
310
 
        """Export this tree to a new tar file.
311
 
 
312
 
        `dest` will be created holding the contents of this tree; if it
313
 
        already exists, it will be clobbered, like with "tar -c".
314
 
        """
315
 
        from time import time
316
 
        now = time()
317
 
        compression = str(compression or '')
318
 
        if root is None:
319
 
            root = get_root_name(dest)
320
 
        try:
321
 
            ball = tarfile.open(dest, 'w:' + compression)
322
 
        except tarfile.CompressionError, e:
323
 
            raise BzrError(str(e))
324
 
        mutter('export version %r' % tree)
325
 
        inv = tree.inventory
326
 
        for dp, ie in inv.iter_entries():
327
 
            mutter("  export {%s} kind %s to %s" % (ie.file_id, ie.kind, dest))
328
 
            item = tarfile.TarInfo(os.path.join(root, dp))
329
 
            # TODO: would be cool to actually set it to the timestamp of the
330
 
            # revision it was last changed
331
 
            item.mtime = now
332
 
            if ie.kind == 'directory':
333
 
                item.type = tarfile.DIRTYPE
334
 
                fileobj = None
335
 
                item.name += '/'
336
 
                item.size = 0
337
 
                item.mode = 0755
338
 
            elif ie.kind == 'file':
339
 
                item.type = tarfile.REGTYPE
340
 
                fileobj = tree.get_file(ie.file_id)
341
 
                item.size = _find_file_size(fileobj)
342
 
                item.mode = 0644
343
 
            else:
344
 
                raise BzrError("don't know how to export {%s} of kind %r" %
345
 
                        (ie.file_id, ie.kind))
346
 
 
347
 
            ball.addfile(item, fileobj)
348
 
        ball.close()
349
 
    exporters['tar'] = tar_exporter
350
 
 
351
 
    def tgz_exporter(tree, dest, root):
352
 
        tar_exporter(tree, dest, root, compression='gz')
353
 
    exporters['tgz'] = tgz_exporter
354
 
 
355
 
    def tbz_exporter(tree, dest, root):
356
 
        tar_exporter(tree, dest, root, compression='bz2')
357
 
    exporters['tbz2'] = tbz_exporter
358
 
 
359
 
 
360
 
def _find_file_size(fileobj):
361
 
    offset = fileobj.tell()
362
 
    try:
363
 
        fileobj.seek(0, 2)
364
 
        size = fileobj.tell()
365
 
    except TypeError:
366
 
        # gzip doesn't accept second argument to seek()
367
 
        fileobj.seek(0)
368
 
        size = 0
369
 
        while True:
370
 
            nread = len(fileobj.read())
371
 
            if nread == 0:
372
 
                break
373
 
            size += nread
374
 
    fileobj.seek(offset)
375
 
    return size