~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

  • Committer: Aaron Bentley
  • Date: 2005-08-10 21:43:27 UTC
  • mto: (1092.1.41) (1185.3.4) (974.1.47)
  • mto: This revision was merged to the branch mainline in revision 1110.
  • Revision ID: abentley@panoramicfeedback.com-20050810214327-4e8c22e4cba24527
Eliminated ThreeWayInventory

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
import os
 
22
 
 
23
from bzrlib.trace import mutter, note
 
24
from bzrlib.errors import BzrError
32
25
 
33
26
import bzrlib
34
27
 
 
28
exporters = {}
 
29
 
35
30
class Tree(object):
36
31
    """Abstract file tree.
37
32
 
81
76
        
82
77
        if ie.text_size != None:
83
78
            if ie.text_size != fp['size']:
84
 
                bailout("mismatched size for file %r in %r" % (ie.file_id, self._store),
 
79
                raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
85
80
                        ["inventory expects %d bytes" % ie.text_size,
86
81
                         "file is actually %d bytes" % fp['size'],
87
82
                         "store is probably damaged/corrupt"])
88
83
 
89
84
        if ie.text_sha1 != fp['sha1']:
90
 
            bailout("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
 
85
            raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
91
86
                    ["inventory expects %s" % ie.text_sha1,
92
87
                     "file is actually %s" % fp['sha1'],
93
88
                     "store is probably damaged/corrupt"])
99
94
        pumpfile(self.get_file(fileid), sys.stdout)
100
95
        
101
96
        
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))
 
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)
127
105
 
128
106
 
129
107
 
153
131
 
154
132
    def get_file_sha1(self, file_id):
155
133
        ie = self._inventory[file_id]
156
 
        return ie.text_sha1
 
134
        if ie.kind == "file":
 
135
            return ie.text_sha1
157
136
 
158
137
    def has_filename(self, filename):
159
138
        return bool(self.inventory.path2id(filename))
165
144
 
166
145
 
167
146
class EmptyTree(Tree):
168
 
    def __init__(self):
169
 
        self._inventory = Inventory()
 
147
    def __init__(self, root_id):
 
148
        from bzrlib.inventory import Inventory
 
149
        self._inventory = Inventory(root_id)
170
150
 
171
151
    def has_filename(self, filename):
172
152
        return False
175
155
        if False:  # just to make it a generator
176
156
            yield None
177
157
    
 
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
 
178
166
 
179
167
 
180
168
######################################################################
241
229
        if old_name != new_name:
242
230
            yield (old_name, new_name)
243
231
            
 
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