~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

  • Committer: Martin Pool
  • Date: 2005-05-19 08:31:06 UTC
  • Revision ID: mbp@sourcefrog.net-20050519083106-ebe71562d3bda4a7
- fix typo

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 osutils import pumpfile, appendpath, fingerprint_file
21
 
import os
22
 
 
23
 
from bzrlib.trace import mutter, note
24
 
from bzrlib.errors import BzrError
 
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
25
32
 
26
33
import bzrlib
27
34
 
28
 
exporters = {}
29
 
 
30
 
class Tree(object):
 
35
class Tree:
31
36
    """Abstract file tree.
32
37
 
33
38
    There are several subclasses:
58
63
 
59
64
    __contains__ = has_id
60
65
 
 
66
    def id_set(self):
 
67
        """Return set of all ids in this tree."""
 
68
        return self.inventory.id_set()
 
69
 
61
70
    def __iter__(self):
62
71
        return iter(self.inventory)
63
72
 
76
85
        
77
86
        if ie.text_size != None:
78
87
            if ie.text_size != fp['size']:
79
 
                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),
80
89
                        ["inventory expects %d bytes" % ie.text_size,
81
90
                         "file is actually %d bytes" % fp['size'],
82
91
                         "store is probably damaged/corrupt"])
83
92
 
84
93
        if ie.text_sha1 != fp['sha1']:
85
 
            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),
86
95
                    ["inventory expects %s" % ie.text_sha1,
87
96
                     "file is actually %s" % fp['sha1'],
88
97
                     "store is probably damaged/corrupt"])
94
103
        pumpfile(self.get_file(fileid), sys.stdout)
95
104
        
96
105
        
97
 
    def export(self, dest, format='dir', root=None):
98
 
        """Export this tree."""
99
 
        try:
100
 
            exporter = exporters[format]
101
 
        except KeyError:
102
 
            from bzrlib.errors import BzrCommandError
103
 
            raise BzrCommandError("export format %r not supported" % format)
104
 
        exporter(self, dest, root)
 
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))
105
131
 
106
132
 
107
133
 
131
157
 
132
158
    def get_file_sha1(self, file_id):
133
159
        ie = self._inventory[file_id]
134
 
        if ie.kind == "file":
135
 
            return ie.text_sha1
 
160
        return ie.text_sha1
136
161
 
137
162
    def has_filename(self, filename):
138
163
        return bool(self.inventory.path2id(filename))
144
169
 
145
170
 
146
171
class EmptyTree(Tree):
147
 
    def __init__(self, root_id):
148
 
        from bzrlib.inventory import Inventory
149
 
        self._inventory = Inventory(root_id)
 
172
    def __init__(self):
 
173
        self._inventory = Inventory()
150
174
 
151
175
    def has_filename(self, filename):
152
176
        return False
155
179
        if False:  # just to make it a generator
156
180
            yield None
157
181
    
158
 
    def __contains__(self, file_id):
159
 
        return file_id in self._inventory
160
 
 
161
 
    def get_file_sha1(self, file_id):
162
 
        assert self._inventory[file_id].kind == "root_directory"
163
 
        return None
164
 
 
165
 
 
166
182
 
167
183
 
168
184
######################################################################
229
245
        if old_name != new_name:
230
246
            yield (old_name, new_name)
231
247
            
232
 
 
233
 
 
234
 
######################################################################
235
 
# export
236
 
 
237
 
def dir_exporter(tree, dest, root):
238
 
    """Export this tree to a new directory.
239
 
 
240
 
    `dest` should not exist, and will be created holding the
241
 
    contents of this tree.
242
 
 
243
 
    TODO: To handle subdirectories we need to create the
244
 
           directories first.
245
 
 
246
 
    :note: If the export fails, the destination directory will be
247
 
           left in a half-assed state.
248
 
    """
249
 
    import os
250
 
    os.mkdir(dest)
251
 
    mutter('export version %r' % tree)
252
 
    inv = tree.inventory
253
 
    for dp, ie in inv.iter_entries():
254
 
        kind = ie.kind
255
 
        fullpath = appendpath(dest, dp)
256
 
        if kind == 'directory':
257
 
            os.mkdir(fullpath)
258
 
        elif kind == 'file':
259
 
            pumpfile(tree.get_file(ie.file_id), file(fullpath, 'wb'))
260
 
        else:
261
 
            raise BzrError("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
262
 
        mutter("  export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
263
 
exporters['dir'] = dir_exporter
264
 
 
265
 
try:
266
 
    import tarfile
267
 
except ImportError:
268
 
    pass
269
 
else:
270
 
    def get_root_name(dest):
271
 
        """Get just the root name for a tarball.
272
 
 
273
 
        >>> get_root_name('mytar.tar')
274
 
        'mytar'
275
 
        >>> get_root_name('mytar.tar.bz2')
276
 
        'mytar'
277
 
        >>> get_root_name('tar.tar.tar.tgz')
278
 
        'tar.tar.tar'
279
 
        >>> get_root_name('bzr-0.0.5.tar.gz')
280
 
        'bzr-0.0.5'
281
 
        >>> get_root_name('a/long/path/mytar.tgz')
282
 
        'mytar'
283
 
        >>> get_root_name('../parent/../dir/other.tbz2')
284
 
        'other'
285
 
        """
286
 
        endings = ['.tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2']
287
 
        dest = os.path.basename(dest)
288
 
        for end in endings:
289
 
            if dest.endswith(end):
290
 
                return dest[:-len(end)]
291
 
 
292
 
    def tar_exporter(tree, dest, root, compression=None):
293
 
        """Export this tree to a new tar file.
294
 
 
295
 
        `dest` will be created holding the contents of this tree; if it
296
 
        already exists, it will be clobbered, like with "tar -c".
297
 
        """
298
 
        from time import time
299
 
        now = time()
300
 
        compression = str(compression or '')
301
 
        if root is None:
302
 
            root = get_root_name(dest)
303
 
        try:
304
 
            ball = tarfile.open(dest, 'w:' + compression)
305
 
        except tarfile.CompressionError, e:
306
 
            raise BzrError(str(e))
307
 
        mutter('export version %r' % tree)
308
 
        inv = tree.inventory
309
 
        for dp, ie in inv.iter_entries():
310
 
            mutter("  export {%s} kind %s to %s" % (ie.file_id, ie.kind, dest))
311
 
            item = tarfile.TarInfo(os.path.join(root, dp))
312
 
            # TODO: would be cool to actually set it to the timestamp of the
313
 
            # revision it was last changed
314
 
            item.mtime = now
315
 
            if ie.kind == 'directory':
316
 
                item.type = tarfile.DIRTYPE
317
 
                fileobj = None
318
 
                item.name += '/'
319
 
                item.size = 0
320
 
                item.mode = 0755
321
 
            elif ie.kind == 'file':
322
 
                item.type = tarfile.REGTYPE
323
 
                fileobj = tree.get_file(ie.file_id)
324
 
                item.size = _find_file_size(fileobj)
325
 
                item.mode = 0644
326
 
            else:
327
 
                raise BzrError("don't know how to export {%s} of kind %r" %
328
 
                        (ie.file_id, ie.kind))
329
 
 
330
 
            ball.addfile(item, fileobj)
331
 
        ball.close()
332
 
    exporters['tar'] = tar_exporter
333
 
 
334
 
    def tgz_exporter(tree, dest, root):
335
 
        tar_exporter(tree, dest, root, compression='gz')
336
 
    exporters['tgz'] = tgz_exporter
337
 
 
338
 
    def tbz_exporter(tree, dest, root):
339
 
        tar_exporter(tree, dest, root, compression='bz2')
340
 
    exporters['tbz2'] = tbz_exporter
341
 
 
342
 
 
343
 
def _find_file_size(fileobj):
344
 
    offset = fileobj.tell()
345
 
    try:
346
 
        fileobj.seek(0, 2)
347
 
        size = fileobj.tell()
348
 
    except TypeError:
349
 
        # gzip doesn't accept second argument to seek()
350
 
        fileobj.seek(0)
351
 
        size = 0
352
 
        while True:
353
 
            nread = len(fileobj.read())
354
 
            if nread == 0:
355
 
                break
356
 
            size += nread
357
 
    fileobj.seek(offset)
358
 
    return size