~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

  • Committer: Martin Pool
  • Date: 2005-06-10 06:34:26 UTC
  • Revision ID: mbp@sourcefrog.net-20050610063426-cfcf5c0f96c271ec
- split out updated progress indicator

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
 
import os
21
 
from cStringIO import StringIO
 
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
22
32
 
23
33
import bzrlib
24
 
from bzrlib.trace import mutter, note
25
 
from bzrlib.errors import BzrError, BzrCheckError
26
 
from bzrlib.inventory import Inventory
27
 
from bzrlib.osutils import pumpfile, appendpath, fingerprint_file
28
 
 
29
 
 
30
 
exporters = {}
31
34
 
32
35
class Tree(object):
33
36
    """Abstract file tree.
68
71
 
69
72
    def _get_inventory(self):
70
73
        return self._inventory
71
 
    
72
 
    def get_file_by_path(self, path):
73
 
        return self.get_file(self._inventory.path2id(path))
74
74
 
75
75
    inventory = property(_get_inventory,
76
76
                         doc="Inventory of this Tree")
81
81
        
82
82
        if ie.text_size != None:
83
83
            if ie.text_size != fp['size']:
84
 
                raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
 
84
                bailout("mismatched size for file %r in %r" % (ie.file_id, self._store),
85
85
                        ["inventory expects %d bytes" % ie.text_size,
86
86
                         "file is actually %d bytes" % fp['size'],
87
87
                         "store is probably damaged/corrupt"])
88
88
 
89
89
        if ie.text_sha1 != fp['sha1']:
90
 
            raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
 
90
            bailout("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
91
91
                    ["inventory expects %s" % ie.text_sha1,
92
92
                     "file is actually %s" % fp['sha1'],
93
93
                     "store is probably damaged/corrupt"])
94
94
 
95
95
 
96
 
    def print_file(self, file_id):
97
 
        """Print file with id `file_id` to stdout."""
 
96
    def print_file(self, fileid):
 
97
        """Print file with id `fileid` to stdout."""
98
98
        import sys
99
 
        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)
 
99
        pumpfile(self.get_file(fileid), sys.stdout)
 
100
        
 
101
        
 
102
    def export(self, dest):        
 
103
        """Export this tree to a new directory.
 
104
 
 
105
        `dest` should not exist, and will be created holding the
 
106
        contents of this tree.
 
107
 
 
108
        TODO: To handle subdirectories we need to create the
 
109
               directories first.
 
110
 
 
111
        :note: If the export fails, the destination directory will be
 
112
               left in a half-assed state.
 
113
        """
 
114
        os.mkdir(dest)
 
115
        mutter('export version %r' % self)
 
116
        inv = self.inventory
 
117
        for dp, ie in inv.iter_entries():
 
118
            kind = ie.kind
 
119
            fullpath = appendpath(dest, dp)
 
120
            if kind == 'directory':
 
121
                os.mkdir(fullpath)
 
122
            elif kind == 'file':
 
123
                pumpfile(self.get_file(ie.file_id), file(fullpath, 'wb'))
 
124
            else:
 
125
                bailout("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
 
126
            mutter("  export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
110
127
 
111
128
 
112
129
 
120
137
           or at least passing a description to the constructor.
121
138
    """
122
139
    
123
 
    def __init__(self, weave_store, inv, revision_id):
124
 
        self._weave_store = weave_store
 
140
    def __init__(self, store, inv):
 
141
        self._store = store
125
142
        self._inventory = inv
126
 
        self._revision_id = revision_id
127
 
 
128
 
    def get_weave(self, file_id):
129
 
        return self._weave_store.get_weave(file_id)
130
 
        
131
 
 
132
 
    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
143
143
 
144
144
    def get_file(self, file_id):
145
 
        return StringIO(self.get_file_text(file_id))
 
145
        ie = self._inventory[file_id]
 
146
        f = self._store[ie.text_id]
 
147
        mutter("  get fileid{%s} from %r" % (file_id, self))
 
148
        self._check_retrieved(ie, f)
 
149
        return f
146
150
 
147
151
    def get_file_size(self, file_id):
148
152
        return self._inventory[file_id].text_size
149
153
 
150
154
    def get_file_sha1(self, file_id):
151
155
        ie = self._inventory[file_id]
152
 
        if ie.kind == "file":
153
 
            return ie.text_sha1
 
156
        return ie.text_sha1
154
157
 
155
158
    def has_filename(self, filename):
156
159
        return bool(self.inventory.path2id(filename))
172
175
        if False:  # just to make it a generator
173
176
            yield None
174
177
    
175
 
    def __contains__(self, file_id):
176
 
        return file_id in self._inventory
177
 
 
178
 
    def get_file_sha1(self, file_id):
179
 
        assert self._inventory[file_id].kind == "root_directory"
180
 
        return None
181
 
 
182
 
 
183
178
 
184
179
 
185
180
######################################################################
246
241
        if old_name != new_name:
247
242
            yield (old_name, new_name)
248
243
            
249
 
 
250
 
 
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