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))
114
def filter_unversioned_files(self, paths):
115
"""Filter out paths that are not versioned.
117
:return: set of paths.
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
# NB: we specifically *don't* call self.has_filename, because for
120
# WorkingTrees that can indicate files that exist on disk but that
122
pred = self.inventory.has_filename
123
return set((p for p in paths if not pred(p)))
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))
126
134
class RevisionTree(Tree):
127
135
"""Tree viewing a previous revision.
133
141
or at least passing a description to the constructor.
136
def __init__(self, branch, inv, revision_id):
137
self._branch = branch
138
self._weave_store = branch.weave_store
144
def __init__(self, store, inv):
139
146
self._inventory = inv
140
self._revision_id = revision_id
142
def get_weave(self, file_id):
143
return self._weave_store.get_weave(file_id,
144
self._branch.get_transaction())
146
def get_file_lines(self, file_id):
147
ie = self._inventory[file_id]
148
weave = self.get_weave(file_id)
149
return weave.get_lines(ie.revision)
151
def get_file_text(self, file_id):
152
return ''.join(self.get_file_lines(file_id))
154
148
def get_file(self, file_id):
155
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)
157
155
def get_file_size(self, file_id):
158
156
return self._inventory[file_id].text_size
160
158
def get_file_sha1(self, file_id):
161
159
ie = self._inventory[file_id]
162
if ie.kind == "file":
165
def is_executable(self, file_id):
166
ie = self._inventory[file_id]
167
if ie.kind != "file":
169
return self._inventory[file_id].executable
171
162
def has_filename(self, filename):
172
163
return bool(self.inventory.path2id(filename))
174
165
def list_files(self):
175
166
# The only files returned by this are those from the version
176
167
for path, entry in self.inventory.iter_entries():
177
yield path, 'V', entry.kind, entry.file_id, entry
179
def get_symlink_target(self, file_id):
180
ie = self._inventory[file_id]
181
return ie.symlink_target;
183
def kind(self, file_id):
184
return self._inventory[file_id].kind
187
self._branch.lock_read()
190
self._branch.unlock()
168
yield path, 'V', entry.kind, entry.file_id
193
171
class EmptyTree(Tree):
194
172
def __init__(self):
195
173
self._inventory = Inventory()
197
def get_symlink_target(self, file_id):
200
175
def has_filename(self, filename):
203
def kind(self, file_id):
204
assert self._inventory[file_id].kind == "root_directory"
205
return "root_directory"
207
178
def list_files(self):
179
if False: # just to make it a generator
210
def __contains__(self, file_id):
211
return file_id in self._inventory
213
def get_file_sha1(self, file_id):
214
assert self._inventory[file_id].kind == "root_directory"
218
184
######################################################################