/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tree.py

  • Committer: John Arbash Meinel
  • Date: 2006-06-19 14:40:19 UTC
  • mto: This revision was merged to the branch mainline in revision 1794.
  • Revision ID: john@arbash-meinel.com-20060619144019-873a4a8d252f7896
Refactor import stuff into separate functions. Update news

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
"""
19
19
 
20
20
import os
 
21
from cStringIO import StringIO
21
22
 
22
23
import bzrlib
 
24
from bzrlib.errors import BzrError, BzrCheckError
 
25
from bzrlib.inventory import Inventory
 
26
from bzrlib.osutils import fingerprint_file
 
27
import bzrlib.revision
23
28
from bzrlib.trace import mutter, note
24
 
from bzrlib.errors import BzrError
25
 
from bzrlib.inventory import Inventory
26
 
from bzrlib.osutils import pumpfile, appendpath, fingerprint_file
27
 
 
28
 
 
29
 
exporters = {}
30
29
 
31
30
class Tree(object):
32
31
    """Abstract file tree.
50
49
    trees or versioned trees.
51
50
    """
52
51
    
 
52
    def conflicts(self):
 
53
        """Get a list of the conflicts in the tree.
 
54
 
 
55
        Each conflict is an instance of bzrlib.conflicts.Conflict.
 
56
        """
 
57
        return []
 
58
 
 
59
    def get_parent_ids(self):
 
60
        """Get the parent ids for this tree. 
 
61
 
 
62
        :return: a list of parent ids. [] is returned to indicate
 
63
        a tree with no parents.
 
64
        :raises: BzrError if the parents are not known.
 
65
        """
 
66
        raise NotImplementedError(self.get_parent_ids)
 
67
    
53
68
    def has_filename(self, filename):
54
69
        """True if the tree has given filename."""
55
70
        raise NotImplementedError()
57
72
    def has_id(self, file_id):
58
73
        return self.inventory.has_id(file_id)
59
74
 
 
75
    def has_or_had_id(self, file_id):
 
76
        if file_id == self.inventory.root.file_id:
 
77
            return True
 
78
        return self.inventory.has_id(file_id)
 
79
 
60
80
    __contains__ = has_id
61
81
 
62
82
    def __iter__(self):
65
85
    def id2path(self, file_id):
66
86
        return self.inventory.id2path(file_id)
67
87
 
 
88
    def kind(self, file_id):
 
89
        raise NotImplementedError("subclasses must implement kind")
 
90
 
68
91
    def _get_inventory(self):
69
92
        return self._inventory
70
93
    
75
98
                         doc="Inventory of this Tree")
76
99
 
77
100
    def _check_retrieved(self, ie, f):
 
101
        if not __debug__:
 
102
            return  
78
103
        fp = fingerprint_file(f)
79
104
        f.seek(0)
80
105
        
92
117
                     "store is probably damaged/corrupt"])
93
118
 
94
119
 
95
 
    def print_file(self, fileid):
96
 
        """Print file with id `fileid` to stdout."""
 
120
    def print_file(self, file_id):
 
121
        """Print file with id `file_id` to stdout."""
97
122
        import sys
98
 
        pumpfile(self.get_file(fileid), sys.stdout)
99
 
        
100
 
        
101
 
    def export(self, dest, format='dir', root=None):
102
 
        """Export this tree."""
103
 
        try:
104
 
            exporter = exporters[format]
105
 
        except KeyError:
106
 
            from bzrlib.errors import BzrCommandError
107
 
            raise BzrCommandError("export format %r not supported" % format)
108
 
        exporter(self, dest, root)
109
 
 
110
 
 
111
 
 
 
123
        sys.stdout.write(self.get_file_text(file_id))
 
124
 
 
125
    def lock_read(self):
 
126
        pass
 
127
 
 
128
    def unknowns(self):
 
129
        """What files are present in this tree and unknown.
 
130
        
 
131
        :return: an iterator over the unknown files.
 
132
        """
 
133
        return iter([])
 
134
 
 
135
    def unlock(self):
 
136
        pass
 
137
 
 
138
    def filter_unversioned_files(self, paths):
 
139
        """Filter out paths that are not versioned.
 
140
 
 
141
        :return: set of paths.
 
142
        """
 
143
        # NB: we specifically *don't* call self.has_filename, because for
 
144
        # WorkingTrees that can indicate files that exist on disk but that 
 
145
        # are not versioned.
 
146
        pred = self.inventory.has_filename
 
147
        return set((p for p in paths if not pred(p)))
 
148
        
 
149
        
112
150
class RevisionTree(Tree):
113
151
    """Tree viewing a previous revision.
114
152
 
119
157
           or at least passing a description to the constructor.
120
158
    """
121
159
    
122
 
    def __init__(self, store, inv):
123
 
        self._store = store
 
160
    def __init__(self, branch, inv, revision_id):
 
161
        # for compatability the 'branch' parameter has not been renamed to 
 
162
        # repository at this point. However, we should change RevisionTree's
 
163
        # construction to always be via Repository and not via direct 
 
164
        # construction - this will mean that we can change the constructor
 
165
        # with much less chance of breaking client code.
 
166
        self._repository = branch
 
167
        self._weave_store = branch.weave_store
124
168
        self._inventory = inv
 
169
        self._revision_id = revision_id
 
170
 
 
171
    def get_parent_ids(self):
 
172
        """See Tree.get_parent_ids.
 
173
 
 
174
        A RevisionTree's parents match the revision graph.
 
175
        """
 
176
        parent_ids = self._repository.get_revision(self._revision_id).parent_ids
 
177
        return parent_ids
 
178
        
 
179
    def get_revision_id(self):
 
180
        """Return the revision id associated with this tree."""
 
181
        return self._revision_id
 
182
 
 
183
    def get_weave(self, file_id):
 
184
        return self._weave_store.get_weave(file_id,
 
185
                self._repository.get_transaction())
 
186
 
 
187
    def get_file_lines(self, file_id):
 
188
        ie = self._inventory[file_id]
 
189
        weave = self.get_weave(file_id)
 
190
        return weave.get_lines(ie.revision)
 
191
 
 
192
    def get_file_text(self, file_id):
 
193
        return ''.join(self.get_file_lines(file_id))
125
194
 
126
195
    def get_file(self, file_id):
127
 
        ie = self._inventory[file_id]
128
 
        f = self._store[ie.text_id]
129
 
        mutter("  get fileid{%s} from %r" % (file_id, self))
130
 
        self._check_retrieved(ie, f)
131
 
        return f
 
196
        return StringIO(self.get_file_text(file_id))
132
197
 
133
198
    def get_file_size(self, file_id):
134
199
        return self._inventory[file_id].text_size
135
200
 
136
 
    def get_file_sha1(self, file_id):
137
 
        ie = self._inventory[file_id]
138
 
        return ie.text_sha1
 
201
    def get_file_sha1(self, file_id, path=None):
 
202
        ie = self._inventory[file_id]
 
203
        if ie.kind == "file":
 
204
            return ie.text_sha1
 
205
        return None
 
206
 
 
207
    def get_file_mtime(self, file_id, path=None):
 
208
        ie = self._inventory[file_id]
 
209
        revision = self._repository.get_revision(ie.revision)
 
210
        return revision.timestamp
 
211
 
 
212
    def is_executable(self, file_id, path=None):
 
213
        ie = self._inventory[file_id]
 
214
        if ie.kind != "file":
 
215
            return None 
 
216
        return self._inventory[file_id].executable
139
217
 
140
218
    def has_filename(self, filename):
141
219
        return bool(self.inventory.path2id(filename))
143
221
    def list_files(self):
144
222
        # The only files returned by this are those from the version
145
223
        for path, entry in self.inventory.iter_entries():
146
 
            yield path, 'V', entry.kind, entry.file_id
 
224
            yield path, 'V', entry.kind, entry.file_id, entry
 
225
 
 
226
    def get_symlink_target(self, file_id):
 
227
        ie = self._inventory[file_id]
 
228
        return ie.symlink_target;
 
229
 
 
230
    def kind(self, file_id):
 
231
        return self._inventory[file_id].kind
 
232
 
 
233
    def lock_read(self):
 
234
        self._repository.lock_read()
 
235
 
 
236
    def unlock(self):
 
237
        self._repository.unlock()
147
238
 
148
239
 
149
240
class EmptyTree(Tree):
 
241
 
150
242
    def __init__(self):
151
243
        self._inventory = Inventory()
152
244
 
 
245
    def get_parent_ids(self):
 
246
        """See Tree.get_parent_ids.
 
247
 
 
248
        An EmptyTree always has NULL_REVISION as the only parent.
 
249
        """
 
250
        return []
 
251
 
 
252
    def get_symlink_target(self, file_id):
 
253
        return None
 
254
 
153
255
    def has_filename(self, filename):
154
256
        return False
155
257
 
 
258
    def kind(self, file_id):
 
259
        assert self._inventory[file_id].kind == "root_directory"
 
260
        return "root_directory"
 
261
 
156
262
    def list_files(self):
157
 
        if False:  # just to make it a generator
158
 
            yield None
 
263
        return iter([])
159
264
    
 
265
    def __contains__(self, file_id):
 
266
        return file_id in self._inventory
 
267
 
 
268
    def get_file_sha1(self, file_id, path=None):
 
269
        assert self._inventory[file_id].kind == "root_directory"
 
270
        return None
160
271
 
161
272
 
162
273
######################################################################
225
336
            
226
337
 
227
338
 
228
 
######################################################################
229
 
# export
230
 
 
231
 
def dir_exporter(tree, dest, root):
232
 
    """Export this tree to a new directory.
233
 
 
234
 
    `dest` should not exist, and will be created holding the
235
 
    contents of this tree.
236
 
 
237
 
    TODO: To handle subdirectories we need to create the
238
 
           directories first.
239
 
 
240
 
    :note: If the export fails, the destination directory will be
241
 
           left in a half-assed state.
242
 
    """
243
 
    import os
244
 
    os.mkdir(dest)
245
 
    mutter('export version %r' % tree)
246
 
    inv = tree.inventory
247
 
    for dp, ie in inv.iter_entries():
248
 
        kind = ie.kind
249
 
        fullpath = appendpath(dest, dp)
250
 
        if kind == 'directory':
251
 
            os.mkdir(fullpath)
252
 
        elif kind == 'file':
253
 
            pumpfile(tree.get_file(ie.file_id), file(fullpath, 'wb'))
254
 
        else:
255
 
            raise BzrError("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
256
 
        mutter("  export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
257
 
exporters['dir'] = dir_exporter
258
 
 
259
 
try:
260
 
    import tarfile
261
 
except ImportError:
262
 
    pass
263
 
else:
264
 
    def get_root_name(dest):
265
 
        """Get just the root name for a tarball.
266
 
 
267
 
        >>> get_root_name('mytar.tar')
268
 
        'mytar'
269
 
        >>> get_root_name('mytar.tar.bz2')
270
 
        'mytar'
271
 
        >>> get_root_name('tar.tar.tar.tgz')
272
 
        'tar.tar.tar'
273
 
        >>> get_root_name('bzr-0.0.5.tar.gz')
274
 
        'bzr-0.0.5'
275
 
        >>> get_root_name('a/long/path/mytar.tgz')
276
 
        'mytar'
277
 
        >>> get_root_name('../parent/../dir/other.tbz2')
278
 
        'other'
279
 
        """
280
 
        endings = ['.tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2']
281
 
        dest = os.path.basename(dest)
282
 
        for end in endings:
283
 
            if dest.endswith(end):
284
 
                return dest[:-len(end)]
285
 
 
286
 
    def tar_exporter(tree, dest, root, compression=None):
287
 
        """Export this tree to a new tar file.
288
 
 
289
 
        `dest` will be created holding the contents of this tree; if it
290
 
        already exists, it will be clobbered, like with "tar -c".
291
 
        """
292
 
        from time import time
293
 
        now = time()
294
 
        compression = str(compression or '')
295
 
        if root is None:
296
 
            root = get_root_name(dest)
297
 
        try:
298
 
            ball = tarfile.open(dest, 'w:' + compression)
299
 
        except tarfile.CompressionError, e:
300
 
            raise BzrError(str(e))
301
 
        mutter('export version %r' % tree)
302
 
        inv = tree.inventory
303
 
        for dp, ie in inv.iter_entries():
304
 
            mutter("  export {%s} kind %s to %s" % (ie.file_id, ie.kind, dest))
305
 
            item = tarfile.TarInfo(os.path.join(root, dp))
306
 
            # TODO: would be cool to actually set it to the timestamp of the
307
 
            # revision it was last changed
308
 
            item.mtime = now
309
 
            if ie.kind == 'directory':
310
 
                item.type = tarfile.DIRTYPE
311
 
                fileobj = None
312
 
                item.name += '/'
313
 
                item.size = 0
314
 
                item.mode = 0755
315
 
            elif ie.kind == 'file':
316
 
                item.type = tarfile.REGTYPE
317
 
                fileobj = tree.get_file(ie.file_id)
318
 
                item.size = _find_file_size(fileobj)
319
 
                item.mode = 0644
320
 
            else:
321
 
                raise BzrError("don't know how to export {%s} of kind %r" %
322
 
                        (ie.file_id, ie.kind))
323
 
 
324
 
            ball.addfile(item, fileobj)
325
 
        ball.close()
326
 
    exporters['tar'] = tar_exporter
327
 
 
328
 
    def tgz_exporter(tree, dest, root):
329
 
        tar_exporter(tree, dest, root, compression='gz')
330
 
    exporters['tgz'] = tgz_exporter
331
 
 
332
 
    def tbz_exporter(tree, dest, root):
333
 
        tar_exporter(tree, dest, root, compression='bz2')
334
 
    exporters['tbz2'] = tbz_exporter
335
 
 
336
 
 
337
 
def _find_file_size(fileobj):
338
 
    offset = fileobj.tell()
339
 
    try:
340
 
        fileobj.seek(0, 2)
341
 
        size = fileobj.tell()
342
 
    except TypeError:
343
 
        # gzip doesn't accept second argument to seek()
344
 
        fileobj.seek(0)
345
 
        size = 0
346
 
        while True:
347
 
            nread = len(fileobj.read())
348
 
            if nread == 0:
349
 
                break
350
 
            size += nread
351
 
    fileobj.seek(offset)
352
 
    return size