~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

  • Committer: Martin Pool
  • Date: 2005-07-04 12:26:02 UTC
  • Revision ID: mbp@sourcefrog.net-20050704122602-69901910521e62c3
- check command checks that all inventory-ids are the same as in the revision.

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, compare_files, 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