~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

  • Committer: Martin Pool
  • Date: 2005-06-28 05:33:40 UTC
  • Revision ID: mbp@sourcefrog.net-20050628053340-ea73b03fbcde9c46
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
  called as needed.  

- Avoid importing xml and ElementTree library unless needed.

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