~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

  • Committer: Martin Pool
  • Date: 2005-07-05 08:09:30 UTC
  • Revision ID: mbp@sourcefrog.net-20050705080929-f44361a66a8b2e29
- Merge3.find_sync_regions always returns a zero-length sentinal at the end to
  ease matching.

- Merge3.merge partially done.

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 osutils import pumpfile, appendpath, fingerprint_file
 
21
 
 
22
from bzrlib.trace import mutter, note
 
23
from bzrlib.errors import BzrError
22
24
 
23
25
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 appendpath, fingerprint_file
 
26
 
 
27
exporters = {}
28
28
 
29
29
class Tree(object):
30
30
    """Abstract file tree.
55
55
    def has_id(self, file_id):
56
56
        return self.inventory.has_id(file_id)
57
57
 
58
 
    def has_or_had_id(self, file_id):
59
 
        if file_id == self.inventory.root.file_id:
60
 
            return True
61
 
        return self.inventory.has_id(file_id)
62
 
 
63
58
    __contains__ = has_id
64
59
 
65
60
    def __iter__(self):
68
63
    def id2path(self, file_id):
69
64
        return self.inventory.id2path(file_id)
70
65
 
71
 
    def kind(self, file_id):
72
 
        raise NotImplementedError("subclasses must implement kind")
73
 
 
74
66
    def _get_inventory(self):
75
67
        return self._inventory
76
 
    
77
 
    def get_file_by_path(self, path):
78
 
        return self.get_file(self._inventory.path2id(path))
79
68
 
80
69
    inventory = property(_get_inventory,
81
70
                         doc="Inventory of this Tree")
82
71
 
83
72
    def _check_retrieved(self, ie, f):
84
 
        if not __debug__:
85
 
            return  
86
73
        fp = fingerprint_file(f)
87
74
        f.seek(0)
88
75
        
100
87
                     "store is probably damaged/corrupt"])
101
88
 
102
89
 
103
 
    def print_file(self, file_id):
104
 
        """Print file with id `file_id` to stdout."""
 
90
    def print_file(self, fileid):
 
91
        """Print file with id `fileid` to stdout."""
105
92
        import sys
106
 
        sys.stdout.write(self.get_file_text(file_id))
107
 
        
108
 
        
 
93
        pumpfile(self.get_file(fileid), sys.stdout)
 
94
        
 
95
        
 
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)
 
103
 
 
104
 
 
105
 
109
106
class RevisionTree(Tree):
110
107
    """Tree viewing a previous revision.
111
108
 
116
113
           or at least passing a description to the constructor.
117
114
    """
118
115
    
119
 
    def __init__(self, branch, inv, revision_id):
120
 
        self._branch = branch
121
 
        self._weave_store = branch.weave_store
 
116
    def __init__(self, store, inv):
 
117
        self._store = store
122
118
        self._inventory = inv
123
 
        self._revision_id = revision_id
124
 
 
125
 
    def get_weave(self, file_id):
126
 
        import bzrlib.transactions as transactions
127
 
        return self._weave_store.get_weave(file_id,
128
 
                self._branch.get_transaction())
129
 
 
130
 
    def get_weave_prelude(self, file_id):
131
 
        import bzrlib.transactions as transactions
132
 
        return self._weave_store.get_weave_prelude(file_id,
133
 
                self._branch.get_transaction())
134
 
 
135
 
    def get_file_lines(self, file_id):
136
 
        ie = self._inventory[file_id]
137
 
        weave = self.get_weave(file_id)
138
 
        return weave.get(ie.revision)
139
 
 
140
 
    def get_file_text(self, file_id):
141
 
        return ''.join(self.get_file_lines(file_id))
142
119
 
143
120
    def get_file(self, file_id):
144
 
        return StringIO(self.get_file_text(file_id))
 
121
        ie = self._inventory[file_id]
 
122
        f = self._store[ie.text_id]
 
123
        mutter("  get fileid{%s} from %r" % (file_id, self))
 
124
        self._check_retrieved(ie, f)
 
125
        return f
145
126
 
146
127
    def get_file_size(self, file_id):
147
128
        return self._inventory[file_id].text_size
148
129
 
149
130
    def get_file_sha1(self, file_id):
150
131
        ie = self._inventory[file_id]
151
 
        if ie.kind == "file":
152
 
            return ie.text_sha1
153
 
 
154
 
    def is_executable(self, file_id):
155
 
        ie = self._inventory[file_id]
156
 
        if ie.kind != "file":
157
 
            return None 
158
 
        return self._inventory[file_id].executable
 
132
        return ie.text_sha1
159
133
 
160
134
    def has_filename(self, filename):
161
135
        return bool(self.inventory.path2id(filename))
163
137
    def list_files(self):
164
138
        # The only files returned by this are those from the version
165
139
        for path, entry in self.inventory.iter_entries():
166
 
            yield path, 'V', entry.kind, entry.file_id, entry
167
 
 
168
 
    def get_symlink_target(self, file_id):
169
 
        ie = self._inventory[file_id]
170
 
        return ie.symlink_target;
171
 
 
172
 
    def kind(self, file_id):
173
 
        return self._inventory[file_id].kind
 
140
            yield path, 'V', entry.kind, entry.file_id
174
141
 
175
142
 
176
143
class EmptyTree(Tree):
177
144
    def __init__(self):
 
145
        from bzrlib.inventory import Inventory
178
146
        self._inventory = Inventory()
179
147
 
180
 
    def get_symlink_target(self, file_id):
181
 
        return None
182
 
 
183
148
    def has_filename(self, filename):
184
149
        return False
185
150
 
186
 
    def kind(self, file_id):
187
 
        assert self._inventory[file_id].kind == "root_directory"
188
 
        return "root_directory"
189
 
 
190
151
    def list_files(self):
191
 
        return iter([])
 
152
        if False:  # just to make it a generator
 
153
            yield None
192
154
    
193
 
    def __contains__(self, file_id):
194
 
        return file_id in self._inventory
195
 
 
196
 
    def get_file_sha1(self, file_id):
197
 
        assert self._inventory[file_id].kind == "root_directory"
198
 
        return None
199
155
 
200
156
 
201
157
######################################################################
264
220
            
265
221
 
266
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