17
17
"""Tree classes, representing directory at point in time.
21
from cStringIO import StringIO
21
import os.path, os, fnmatch
23
from osutils import pumpfile, filesize, quotefn, sha_file, \
24
joinpath, splitpath, appendpath, isdir, isfile, file_kind, fingerprint_file
26
from stat import S_ISREG, S_ISDIR, ST_MODE, ST_SIZE
28
from inventory import Inventory
29
from trace import mutter, note
30
from errors import bailout
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
30
36
"""Abstract file tree.
32
38
There are several subclasses:
55
61
def has_id(self, file_id):
56
62
return self.inventory.has_id(file_id)
58
def has_or_had_id(self, file_id):
59
if file_id == self.inventory.root.file_id:
61
return self.inventory.has_id(file_id)
63
64
__contains__ = has_id
67
"""Return set of all ids in this tree."""
68
return self.inventory.id_set()
65
70
def __iter__(self):
66
71
return iter(self.inventory)
68
73
def id2path(self, file_id):
69
74
return self.inventory.id2path(file_id)
71
def kind(self, file_id):
72
raise NotImplementedError("subclasses must implement kind")
74
76
def _get_inventory(self):
75
77
return self._inventory
77
def get_file_by_path(self, path):
78
return self.get_file(self._inventory.path2id(path))
80
79
inventory = property(_get_inventory,
81
80
doc="Inventory of this Tree")
83
82
def _check_retrieved(self, ie, f):
86
83
fp = fingerprint_file(f)
89
86
if ie.text_size != None:
90
87
if ie.text_size != fp['size']:
91
raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
88
bailout("mismatched size for file %r in %r" % (ie.file_id, self._store),
92
89
["inventory expects %d bytes" % ie.text_size,
93
90
"file is actually %d bytes" % fp['size'],
94
91
"store is probably damaged/corrupt"])
96
93
if ie.text_sha1 != fp['sha1']:
97
raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
94
bailout("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
98
95
["inventory expects %s" % ie.text_sha1,
99
96
"file is actually %s" % fp['sha1'],
100
97
"store is probably damaged/corrupt"])
103
def print_file(self, file_id):
104
"""Print file with id `file_id` to stdout."""
100
def print_file(self, fileid):
101
"""Print file with id `fileid` to stdout."""
106
sys.stdout.write(self.get_file_text(file_id))
103
pumpfile(self.get_file(fileid), sys.stdout)
106
def export(self, dest):
107
"""Export this tree to a new directory.
109
`dest` should not exist, and will be created holding the
110
contents of this tree.
112
TODO: To handle subdirectories we need to create the
115
:note: If the export fails, the destination directory will be
116
left in a half-assed state.
119
mutter('export version %r' % self)
121
for dp, ie in inv.iter_entries():
123
fullpath = appendpath(dest, dp)
124
if kind == 'directory':
127
pumpfile(self.get_file(ie.file_id), file(fullpath, 'wb'))
129
bailout("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
130
mutter(" export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
109
134
class RevisionTree(Tree):
110
135
"""Tree viewing a previous revision.
116
141
or at least passing a description to the constructor.
119
def __init__(self, weave_store, inv, revision_id):
120
self._weave_store = weave_store
144
def __init__(self, store, inv):
121
146
self._inventory = inv
122
self._revision_id = revision_id
124
def get_weave(self, file_id):
125
# FIXME: RevisionTree should be given a branch
126
# not a store, or the store should know the branch.
127
import bzrlib.transactions as transactions
128
return self._weave_store.get_weave(file_id,
129
transactions.PassThroughTransaction())
132
def get_file_lines(self, file_id):
133
ie = self._inventory[file_id]
134
weave = self.get_weave(file_id)
135
return weave.get(ie.revision)
138
def get_file_text(self, file_id):
139
return ''.join(self.get_file_lines(file_id))
142
148
def get_file(self, file_id):
143
return StringIO(self.get_file_text(file_id))
149
ie = self._inventory[file_id]
150
f = self._store[ie.text_id]
151
mutter(" get fileid{%s} from %r" % (file_id, self))
152
self._check_retrieved(ie, f)
145
155
def get_file_size(self, file_id):
146
156
return self._inventory[file_id].text_size
148
158
def get_file_sha1(self, file_id):
149
159
ie = self._inventory[file_id]
150
if ie.kind == "file":
153
def is_executable(self, file_id):
154
ie = self._inventory[file_id]
155
if ie.kind != "file":
157
return self._inventory[file_id].executable
159
162
def has_filename(self, filename):
160
163
return bool(self.inventory.path2id(filename))
162
165
def list_files(self):
163
166
# The only files returned by this are those from the version
164
167
for path, entry in self.inventory.iter_entries():
165
yield path, 'V', entry.kind, entry.file_id, entry
167
def get_symlink_target(self, file_id):
168
ie = self._inventory[file_id]
169
return ie.symlink_target;
171
def kind(self, file_id):
172
return self._inventory[file_id].kind
168
yield path, 'V', entry.kind, entry.file_id
175
171
class EmptyTree(Tree):
176
172
def __init__(self):
177
173
self._inventory = Inventory()
179
def get_symlink_target(self, file_id):
182
175
def has_filename(self, filename):
185
def kind(self, file_id):
186
assert self._inventory[file_id].kind == "root_directory"
187
return "root_directory"
189
178
def list_files(self):
179
if False: # just to make it a generator
192
def __contains__(self, file_id):
193
return file_id in self._inventory
195
def get_file_sha1(self, file_id):
196
assert self._inventory[file_id].kind == "root_directory"
200
184
######################################################################