/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
70 by mbp at sourcefrog
Prepare for smart recursive add.
1
# Copyright (C) 2005 Canonical Ltd
1 by mbp at sourcefrog
import from baz patch-364
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tree classes, representing directory at point in time.
18
"""
19
849 by Martin Pool
- Put files inside an exported tarball into a top-level directory rather than
20
import os
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
21
from cStringIO import StringIO
800 by Martin Pool
Merge John's import-speedup branch:
22
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
23
import bzrlib
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
24
from bzrlib.trace import mutter, note
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
25
from bzrlib.errors import BzrError, BzrCheckError
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
26
from bzrlib.inventory import Inventory
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
27
from bzrlib.osutils import appendpath, fingerprint_file
1 by mbp at sourcefrog
import from baz patch-364
28
29
678 by Martin Pool
- export to tarballs
30
exporters = {}
31
558 by Martin Pool
- All top-level classes inherit from object
32
class Tree(object):
1 by mbp at sourcefrog
import from baz patch-364
33
    """Abstract file tree.
34
35
    There are several subclasses:
36
    
37
    * `WorkingTree` exists as files on disk editable by the user.
38
39
    * `RevisionTree` is a tree as recorded at some point in the past.
40
41
    * `EmptyTree`
42
43
    Trees contain an `Inventory` object, and also know how to retrieve
44
    file texts mentioned in the inventory, either from a working
45
    directory or from a store.
46
47
    It is possible for trees to contain files that are not described
48
    in their inventory or vice versa; for this use `filenames()`.
49
50
    Trees can be compared, etc, regardless of whether they are working
51
    trees or versioned trees.
52
    """
53
    
54
    def has_filename(self, filename):
55
        """True if the tree has given filename."""
56
        raise NotImplementedError()
57
1185.12.39 by abentley
Propogated has_or_had_id to Tree
58
    def has_id(self, file_id):
59
        return self.inventory.has_id(file_id)
60
61
    def has_or_had_id(self, file_id):
62
        if file_id == self.inventory.root.file_id:
1185.12.38 by abentley
semi-broke merge
63
            return True
1 by mbp at sourcefrog
import from baz patch-364
64
        return self.inventory.has_id(file_id)
65
462 by Martin Pool
- New form 'file_id in tree' to check if the file is present
66
    __contains__ = has_id
67
68
    def __iter__(self):
69
        return iter(self.inventory)
70
1 by mbp at sourcefrog
import from baz patch-364
71
    def id2path(self, file_id):
72
        return self.inventory.id2path(file_id)
73
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
74
    def kind(self, file_id):
75
        raise NotImplementedError("subclasses must implement kind")
76
1 by mbp at sourcefrog
import from baz patch-364
77
    def _get_inventory(self):
78
        return self._inventory
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
79
    
80
    def get_file_by_path(self, path):
81
        return self.get_file(self._inventory.path2id(path))
1 by mbp at sourcefrog
import from baz patch-364
82
83
    inventory = property(_get_inventory,
84
                         doc="Inventory of this Tree")
85
86
    def _check_retrieved(self, ie, f):
1364 by Martin Pool
- remove extra verification of files retrieved from tree
87
        if not __debug__:
88
            return  
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
89
        fp = fingerprint_file(f)
90
        f.seek(0)
91
        
184 by mbp at sourcefrog
pychecker fixups
92
        if ie.text_size != None:
131 by mbp at sourcefrog
check size and sha1 of files retrieved from the tree
93
            if ie.text_size != fp['size']:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
94
                raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
1 by mbp at sourcefrog
import from baz patch-364
95
                        ["inventory expects %d bytes" % ie.text_size,
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
96
                         "file is actually %d bytes" % fp['size'],
1 by mbp at sourcefrog
import from baz patch-364
97
                         "store is probably damaged/corrupt"])
98
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
99
        if ie.text_sha1 != fp['sha1']:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
100
            raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
1 by mbp at sourcefrog
import from baz patch-364
101
                    ["inventory expects %s" % ie.text_sha1,
130 by mbp at sourcefrog
- fixup checks on retrieved files to cope with compression,
102
                     "file is actually %s" % fp['sha1'],
1 by mbp at sourcefrog
import from baz patch-364
103
                     "store is probably damaged/corrupt"])
104
105
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
106
    def print_file(self, file_id):
107
        """Print file with id `file_id` to stdout."""
176 by mbp at sourcefrog
New cat command contributed by janmar.
108
        import sys
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
109
        sys.stdout.write(self.get_file_text(file_id))
176 by mbp at sourcefrog
New cat command contributed by janmar.
110
        
111
        
849 by Martin Pool
- Put files inside an exported tarball into a top-level directory rather than
112
    def export(self, dest, format='dir', root=None):
678 by Martin Pool
- export to tarballs
113
        """Export this tree."""
114
        try:
115
            exporter = exporters[format]
116
        except KeyError:
849 by Martin Pool
- Put files inside an exported tarball into a top-level directory rather than
117
            from bzrlib.errors import BzrCommandError
678 by Martin Pool
- export to tarballs
118
            raise BzrCommandError("export format %r not supported" % format)
849 by Martin Pool
- Put files inside an exported tarball into a top-level directory rather than
119
        exporter(self, dest, root)
1 by mbp at sourcefrog
import from baz patch-364
120
121
122
123
class RevisionTree(Tree):
124
    """Tree viewing a previous revision.
125
126
    File text can be retrieved from the text store.
127
254 by Martin Pool
- Doc cleanups from Magnus Therning
128
    TODO: Some kind of `__repr__` method, but a good one
1 by mbp at sourcefrog
import from baz patch-364
129
           probably means knowing the branch and revision number,
130
           or at least passing a description to the constructor.
131
    """
132
    
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
133
    def __init__(self, weave_store, inv, revision_id):
134
        self._weave_store = weave_store
1 by mbp at sourcefrog
import from baz patch-364
135
        self._inventory = inv
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
136
        self._revision_id = revision_id
137
1185.82.3 by John Arbash Meinel
Working on creating a factor for serializing changesets.
138
    def get_revision_id(self):
139
        """Return the revision id associated with this tree."""
140
        return self._revision_id
141
1210 by Martin Pool
- get correct old file version in RevisionTree
142
    def get_weave(self, file_id):
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
143
        # FIXME: RevisionTree should be given a branch
144
        # not a store, or the store should know the branch.
145
        import bzrlib.transactions as transactions
146
        return self._weave_store.get_weave(file_id,
147
            transactions.PassThroughTransaction())
1369 by Martin Pool
- try to avoid redundant conversion of strings when retrieving from weaves
148
149
150
    def get_file_lines(self, file_id):
151
        ie = self._inventory[file_id]
152
        weave = self.get_weave(file_id)
1092.2.22 by Robert Collins
text_version and name_version unification looking reasonable
153
        return weave.get(ie.revision)
1210 by Martin Pool
- get correct old file version in RevisionTree
154
        
155
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
156
    def get_file_text(self, file_id):
1369 by Martin Pool
- try to avoid redundant conversion of strings when retrieving from weaves
157
        return ''.join(self.get_file_lines(file_id))
158
1 by mbp at sourcefrog
import from baz patch-364
159
160
    def get_file(self, file_id):
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
161
        return StringIO(self.get_file_text(file_id))
1 by mbp at sourcefrog
import from baz patch-364
162
163
    def get_file_size(self, file_id):
164
        return self._inventory[file_id].text_size
165
166
    def get_file_sha1(self, file_id):
167
        ie = self._inventory[file_id]
974.1.12 by aaron.bentley at utoronto
Switched from text-id to hashcache for merge optimization
168
        if ie.kind == "file":
169
            return ie.text_sha1
1 by mbp at sourcefrog
import from baz patch-364
170
1398 by Robert Collins
integrate in Gustavos x-bit patch
171
    def is_executable(self, file_id):
1185.12.28 by Aaron Bentley
Removed use of readonly path for executability test
172
        ie = self._inventory[file_id]
173
        if ie.kind != "file":
174
            return None 
1398 by Robert Collins
integrate in Gustavos x-bit patch
175
        return self._inventory[file_id].executable
176
1 by mbp at sourcefrog
import from baz patch-364
177
    def has_filename(self, filename):
178
        return bool(self.inventory.path2id(filename))
179
180
    def list_files(self):
181
        # The only files returned by this are those from the version
182
        for path, entry in self.inventory.iter_entries():
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
183
            yield path, 'V', entry.kind, entry.file_id, entry
1 by mbp at sourcefrog
import from baz patch-364
184
1092.2.6 by Robert Collins
symlink support updated to work
185
    def get_symlink_target(self, file_id):
186
        ie = self._inventory[file_id]
187
        return ie.symlink_target;
1 by mbp at sourcefrog
import from baz patch-364
188
1185.12.28 by Aaron Bentley
Removed use of readonly path for executability test
189
    def kind(self, file_id):
190
        return self._inventory[file_id].kind
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
191
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
192
1 by mbp at sourcefrog
import from baz patch-364
193
class EmptyTree(Tree):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
194
    def __init__(self):
195
        self._inventory = Inventory()
1 by mbp at sourcefrog
import from baz patch-364
196
1092.2.6 by Robert Collins
symlink support updated to work
197
    def get_symlink_target(self, file_id):
198
        return None
199
1 by mbp at sourcefrog
import from baz patch-364
200
    def has_filename(self, filename):
201
        return False
202
1465 by Robert Collins
Bugfix the new pull --clobber to not generate spurious conflicts.
203
    def kind(self, file_id):
204
        assert self._inventory[file_id].kind == "root_directory"
205
        return "root_directory"
206
1 by mbp at sourcefrog
import from baz patch-364
207
    def list_files(self):
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
208
        return iter([])
1 by mbp at sourcefrog
import from baz patch-364
209
    
974.1.12 by aaron.bentley at utoronto
Switched from text-id to hashcache for merge optimization
210
    def __contains__(self, file_id):
211
        return file_id in self._inventory
212
974.1.14 by aaron.bentley at utoronto
Fixed bugs in merge optimization
213
    def get_file_sha1(self, file_id):
214
        assert self._inventory[file_id].kind == "root_directory"
215
        return None
216
217
1 by mbp at sourcefrog
import from baz patch-364
218
######################################################################
219
# diff
220
221
# TODO: Merge these two functions into a single one that can operate
222
# on either a whole tree or a set of files.
223
224
# TODO: Return the diff in order by filename, not by category or in
225
# random order.  Can probably be done by lock-stepping through the
226
# filenames from both trees.
227
228
229
def file_status(filename, old_tree, new_tree):
230
    """Return single-letter status, old and new names for a file.
231
232
    The complexity here is in deciding how to represent renames;
233
    many complex cases are possible.
234
    """
235
    old_inv = old_tree.inventory
236
    new_inv = new_tree.inventory
237
    new_id = new_inv.path2id(filename)
238
    old_id = old_inv.path2id(filename)
239
240
    if not new_id and not old_id:
241
        # easy: doesn't exist in either; not versioned at all
242
        if new_tree.is_ignored(filename):
243
            return 'I', None, None
244
        else:
245
            return '?', None, None
246
    elif new_id:
247
        # There is now a file of this name, great.
248
        pass
249
    else:
250
        # There is no longer a file of this name, but we can describe
251
        # what happened to the file that used to have
252
        # this name.  There are two possibilities: either it was
253
        # deleted entirely, or renamed.
254
        assert old_id
255
        if new_inv.has_id(old_id):
256
            return 'X', old_inv.id2path(old_id), new_inv.id2path(old_id)
257
        else:
258
            return 'D', old_inv.id2path(old_id), None
259
260
    # if the file_id is new in this revision, it is added
261
    if new_id and not old_inv.has_id(new_id):
262
        return 'A'
263
264
    # if there used to be a file of this name, but that ID has now
265
    # disappeared, it is deleted
266
    if old_id and not new_inv.has_id(old_id):
267
        return 'D'
268
269
    return 'wtf?'
270
271
    
272
164 by mbp at sourcefrog
new 'renames' command
273
def find_renames(old_inv, new_inv):
274
    for file_id in old_inv:
275
        if file_id not in new_inv:
276
            continue
277
        old_name = old_inv.id2path(file_id)
278
        new_name = new_inv.id2path(file_id)
279
        if old_name != new_name:
280
            yield (old_name, new_name)
281
            
678 by Martin Pool
- export to tarballs
282
283
284
######################################################################
285
# export
286
849 by Martin Pool
- Put files inside an exported tarball into a top-level directory rather than
287
def dir_exporter(tree, dest, root):
678 by Martin Pool
- export to tarballs
288
    """Export this tree to a new directory.
289
290
    `dest` should not exist, and will be created holding the
291
    contents of this tree.
292
293
    TODO: To handle subdirectories we need to create the
294
           directories first.
295
296
    :note: If the export fails, the destination directory will be
297
           left in a half-assed state.
298
    """
800 by Martin Pool
Merge John's import-speedup branch:
299
    import os
678 by Martin Pool
- export to tarballs
300
    os.mkdir(dest)
1185.31.4 by John Arbash Meinel
Fixing mutter() calls to not have to do string processing.
301
    mutter('export version %r', tree)
678 by Martin Pool
- export to tarballs
302
    inv = tree.inventory
303
    for dp, ie in inv.iter_entries():
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
304
        ie.put_on_disk(dest, dp, tree)
305
678 by Martin Pool
- export to tarballs
306
exporters['dir'] = dir_exporter
307
308
try:
309
    import tarfile
310
except ImportError:
311
    pass
312
else:
849 by Martin Pool
- Put files inside an exported tarball into a top-level directory rather than
313
    def get_root_name(dest):
314
        """Get just the root name for a tarball.
315
316
        >>> get_root_name('mytar.tar')
317
        'mytar'
318
        >>> get_root_name('mytar.tar.bz2')
319
        'mytar'
320
        >>> get_root_name('tar.tar.tar.tgz')
321
        'tar.tar.tar'
322
        >>> get_root_name('bzr-0.0.5.tar.gz')
323
        'bzr-0.0.5'
324
        >>> get_root_name('a/long/path/mytar.tgz')
325
        'mytar'
326
        >>> get_root_name('../parent/../dir/other.tbz2')
327
        'other'
328
        """
329
        endings = ['.tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2']
330
        dest = os.path.basename(dest)
331
        for end in endings:
332
            if dest.endswith(end):
333
                return dest[:-len(end)]
334
335
    def tar_exporter(tree, dest, root, compression=None):
678 by Martin Pool
- export to tarballs
336
        """Export this tree to a new tar file.
337
338
        `dest` will be created holding the contents of this tree; if it
339
        already exists, it will be clobbered, like with "tar -c".
340
        """
800 by Martin Pool
Merge John's import-speedup branch:
341
        from time import time
342
        now = time()
678 by Martin Pool
- export to tarballs
343
        compression = str(compression or '')
849 by Martin Pool
- Put files inside an exported tarball into a top-level directory rather than
344
        if root is None:
345
            root = get_root_name(dest)
678 by Martin Pool
- export to tarballs
346
        try:
347
            ball = tarfile.open(dest, 'w:' + compression)
348
        except tarfile.CompressionError, e:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
349
            raise BzrError(str(e))
1185.31.4 by John Arbash Meinel
Fixing mutter() calls to not have to do string processing.
350
        mutter('export version %r', tree)
678 by Martin Pool
- export to tarballs
351
        inv = tree.inventory
352
        for dp, ie in inv.iter_entries():
1185.31.4 by John Arbash Meinel
Fixing mutter() calls to not have to do string processing.
353
            mutter("  export {%s} kind %s to %s", ie.file_id, ie.kind, dest)
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
354
            item, fileobj = ie.get_tar_item(root, dp, now, tree)
678 by Martin Pool
- export to tarballs
355
            ball.addfile(item, fileobj)
356
        ball.close()
1399.1.6 by Robert Collins
move exporting functionality into inventory.py - uncovers bug in symlink support
357
678 by Martin Pool
- export to tarballs
358
    exporters['tar'] = tar_exporter
359
849 by Martin Pool
- Put files inside an exported tarball into a top-level directory rather than
360
    def tgz_exporter(tree, dest, root):
361
        tar_exporter(tree, dest, root, compression='gz')
678 by Martin Pool
- export to tarballs
362
    exporters['tgz'] = tgz_exporter
363
849 by Martin Pool
- Put files inside an exported tarball into a top-level directory rather than
364
    def tbz_exporter(tree, dest, root):
365
        tar_exporter(tree, dest, root, compression='bz2')
678 by Martin Pool
- export to tarballs
366
    exporters['tbz2'] = tbz_exporter