17
17
"""Tree classes, representing directory at point in time.
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
21
from cStringIO import StringIO
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
36
30
"""Abstract file tree.
38
32
There are several subclasses:
61
55
def has_id(self, file_id):
62
56
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)
64
63
__contains__ = has_id
67
"""Return set of all ids in this tree."""
68
return self.inventory.id_set()
70
65
def __iter__(self):
71
66
return iter(self.inventory)
73
68
def id2path(self, file_id):
74
69
return self.inventory.id2path(file_id)
71
def kind(self, file_id):
72
raise NotImplementedError("subclasses must implement kind")
76
74
def _get_inventory(self):
77
75
return self._inventory
77
def get_file_by_path(self, path):
78
return self.get_file(self._inventory.path2id(path))
79
80
inventory = property(_get_inventory,
80
81
doc="Inventory of this Tree")
82
83
def _check_retrieved(self, ie, f):
83
86
fp = fingerprint_file(f)
86
89
if ie.text_size != None:
87
90
if ie.text_size != fp['size']:
88
bailout("mismatched size for file %r in %r" % (ie.file_id, self._store),
91
raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
89
92
["inventory expects %d bytes" % ie.text_size,
90
93
"file is actually %d bytes" % fp['size'],
91
94
"store is probably damaged/corrupt"])
93
96
if ie.text_sha1 != fp['sha1']:
94
bailout("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
97
raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
95
98
["inventory expects %s" % ie.text_sha1,
96
99
"file is actually %s" % fp['sha1'],
97
100
"store is probably damaged/corrupt"])
100
def print_file(self, fileid):
101
"""Print file with id `fileid` to stdout."""
103
def print_file(self, file_id):
104
"""Print file with id `file_id` to stdout."""
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))
106
sys.stdout.write(self.get_file_text(file_id))
134
115
class RevisionTree(Tree):
135
116
"""Tree viewing a previous revision.
141
122
or at least passing a description to the constructor.
144
def __init__(self, store, inv):
125
def __init__(self, branch, inv, revision_id):
126
self._branch = branch
127
self._weave_store = branch.weave_store
146
128
self._inventory = inv
129
self._revision_id = revision_id
131
def get_weave(self, file_id):
132
import bzrlib.transactions as transactions
133
return self._weave_store.get_weave(file_id,
134
self._branch.get_transaction())
136
def get_weave_prelude(self, file_id):
137
import bzrlib.transactions as transactions
138
return self._weave_store.get_weave_prelude(file_id,
139
self._branch.get_transaction())
141
def get_file_lines(self, file_id):
142
ie = self._inventory[file_id]
143
weave = self.get_weave(file_id)
144
return weave.get(ie.revision)
146
def get_file_text(self, file_id):
147
return ''.join(self.get_file_lines(file_id))
148
149
def get_file(self, 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)
150
return StringIO(self.get_file_text(file_id))
155
152
def get_file_size(self, file_id):
156
153
return self._inventory[file_id].text_size
158
155
def get_file_sha1(self, file_id):
159
156
ie = self._inventory[file_id]
157
if ie.kind == "file":
160
def is_executable(self, file_id):
161
ie = self._inventory[file_id]
162
if ie.kind != "file":
164
return self._inventory[file_id].executable
162
166
def has_filename(self, filename):
163
167
return bool(self.inventory.path2id(filename))
165
169
def list_files(self):
166
170
# The only files returned by this are those from the version
167
171
for path, entry in self.inventory.iter_entries():
168
yield path, 'V', entry.kind, entry.file_id
172
yield path, 'V', entry.kind, entry.file_id, entry
174
def get_symlink_target(self, file_id):
175
ie = self._inventory[file_id]
176
return ie.symlink_target;
178
def kind(self, file_id):
179
return self._inventory[file_id].kind
182
self._branch.lock_read()
185
self._branch.unlock()
171
188
class EmptyTree(Tree):
172
189
def __init__(self):
173
190
self._inventory = Inventory()
192
def get_symlink_target(self, file_id):
175
195
def has_filename(self, filename):
198
def kind(self, file_id):
199
assert self._inventory[file_id].kind == "root_directory"
200
return "root_directory"
178
202
def list_files(self):
179
if False: # just to make it a generator
205
def __contains__(self, file_id):
206
return file_id in self._inventory
208
def get_file_sha1(self, file_id):
209
assert self._inventory[file_id].kind == "root_directory"
184
213
######################################################################