~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

  • Committer: Martin Pool
  • Date: 2005-06-27 01:39:26 UTC
  • Revision ID: mbp@sourcefrog.net-20050627013926-49413d5928809350
- import effbot.org http client

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
"""
19
19
 
20
20
from sets import Set
21
 
import os.path, os, fnmatch
 
21
import os.path, os, fnmatch, time
22
22
 
23
 
from osutils import pumpfile, compare_files, filesize, quotefn, sha_file, \
 
23
from osutils import pumpfile, filesize, quotefn, sha_file, \
24
24
     joinpath, splitpath, appendpath, isdir, isfile, file_kind, fingerprint_file
25
25
import errno
26
26
from stat import S_ISREG, S_ISDIR, ST_MODE, ST_SIZE
27
27
 
28
 
from inventory import Inventory
29
 
from trace import mutter, note
30
 
from errors import bailout
 
28
from bzrlib.inventory import Inventory
 
29
from bzrlib.trace import mutter, note
 
30
from bzrlib.errors import BzrError
31
31
import branch
32
32
 
33
33
import bzrlib
34
34
 
35
 
class Tree:
 
35
exporters = {}
 
36
 
 
37
class Tree(object):
36
38
    """Abstract file tree.
37
39
 
38
40
    There are several subclasses:
63
65
 
64
66
    __contains__ = has_id
65
67
 
66
 
    def id_set(self):
67
 
        """Return set of all ids in this tree."""
68
 
        return self.inventory.id_set()
69
 
 
70
68
    def __iter__(self):
71
69
        return iter(self.inventory)
72
70
 
85
83
        
86
84
        if ie.text_size != None:
87
85
            if ie.text_size != fp['size']:
88
 
                bailout("mismatched size for file %r in %r" % (ie.file_id, self._store),
 
86
                raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
89
87
                        ["inventory expects %d bytes" % ie.text_size,
90
88
                         "file is actually %d bytes" % fp['size'],
91
89
                         "store is probably damaged/corrupt"])
92
90
 
93
91
        if ie.text_sha1 != fp['sha1']:
94
 
            bailout("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
 
92
            raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
95
93
                    ["inventory expects %s" % ie.text_sha1,
96
94
                     "file is actually %s" % fp['sha1'],
97
95
                     "store is probably damaged/corrupt"])
103
101
        pumpfile(self.get_file(fileid), sys.stdout)
104
102
        
105
103
        
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))
 
104
    def export(self, dest, format='dir'):
 
105
        """Export this tree."""
 
106
        try:
 
107
            exporter = exporters[format]
 
108
        except KeyError:
 
109
            raise BzrCommandError("export format %r not supported" % format)
 
110
        exporter(self, dest)
131
111
 
132
112
 
133
113
 
245
225
        if old_name != new_name:
246
226
            yield (old_name, new_name)
247
227
            
 
228
 
 
229
 
 
230
######################################################################
 
231
# export
 
232
 
 
233
def dir_exporter(tree, dest):
 
234
    """Export this tree to a new directory.
 
235
 
 
236
    `dest` should not exist, and will be created holding the
 
237
    contents of this tree.
 
238
 
 
239
    TODO: To handle subdirectories we need to create the
 
240
           directories first.
 
241
 
 
242
    :note: If the export fails, the destination directory will be
 
243
           left in a half-assed state.
 
244
    """
 
245
    os.mkdir(dest)
 
246
    mutter('export version %r' % tree)
 
247
    inv = tree.inventory
 
248
    for dp, ie in inv.iter_entries():
 
249
        kind = ie.kind
 
250
        fullpath = appendpath(dest, dp)
 
251
        if kind == 'directory':
 
252
            os.mkdir(fullpath)
 
253
        elif kind == 'file':
 
254
            pumpfile(tree.get_file(ie.file_id), file(fullpath, 'wb'))
 
255
        else:
 
256
            raise BzrError("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
 
257
        mutter("  export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
 
258
exporters['dir'] = dir_exporter
 
259
 
 
260
try:
 
261
    import tarfile
 
262
except ImportError:
 
263
    pass
 
264
else:
 
265
    def tar_exporter(tree, dest, compression=None):
 
266
        """Export this tree to a new tar file.
 
267
 
 
268
        `dest` will be created holding the contents of this tree; if it
 
269
        already exists, it will be clobbered, like with "tar -c".
 
270
        """
 
271
        now = time.time()
 
272
        compression = str(compression or '')
 
273
        try:
 
274
            ball = tarfile.open(dest, 'w:' + compression)
 
275
        except tarfile.CompressionError, e:
 
276
            raise BzrError(str(e))
 
277
        mutter('export version %r' % tree)
 
278
        inv = tree.inventory
 
279
        for dp, ie in inv.iter_entries():
 
280
            mutter("  export {%s} kind %s to %s" % (ie.file_id, ie.kind, dest))
 
281
            item = tarfile.TarInfo(dp)
 
282
            # TODO: would be cool to actually set it to the timestamp of the
 
283
            # revision it was last changed
 
284
            item.mtime = now
 
285
            if ie.kind == 'directory':
 
286
                item.type = tarfile.DIRTYPE
 
287
                fileobj = None
 
288
                item.name += '/'
 
289
                item.size = 0
 
290
                item.mode = 0755
 
291
            elif ie.kind == 'file':
 
292
                item.type = tarfile.REGTYPE
 
293
                fileobj = tree.get_file(ie.file_id)
 
294
                item.size = _find_file_size(fileobj)
 
295
                item.mode = 0644
 
296
            else:
 
297
                raise BzrError("don't know how to export {%s} of kind %r" %
 
298
                        (ie.file_id, ie.kind))
 
299
 
 
300
            ball.addfile(item, fileobj)
 
301
        ball.close()
 
302
    exporters['tar'] = tar_exporter
 
303
 
 
304
    def tgz_exporter(tree, dest):
 
305
        tar_exporter(tree, dest, compression='gz')
 
306
    exporters['tgz'] = tgz_exporter
 
307
 
 
308
    def tbz_exporter(tree, dest):
 
309
        tar_exporter(tree, dest, compression='bz2')
 
310
    exporters['tbz2'] = tbz_exporter
 
311
 
 
312
 
 
313
def _find_file_size(fileobj):
 
314
    offset = fileobj.tell()
 
315
    try:
 
316
        fileobj.seek(0, 2)
 
317
        size = fileobj.tell()
 
318
    except TypeError:
 
319
        # gzip doesn't accept second argument to seek()
 
320
        fileobj.seek(0)
 
321
        size = 0
 
322
        while True:
 
323
            nread = len(fileobj.read())
 
324
            if nread == 0:
 
325
                break
 
326
            size += nread
 
327
    fileobj.seek(offset)
 
328
    return size