23
23
from inventory import Inventory
24
24
from trace import mutter, note
25
25
from osutils import pumpfile, compare_files, filesize, quotefn, sha_file, \
26
joinpath, splitpath, appendpath, isdir, isfile, file_kind, fingerprint_file
26
joinpath, splitpath, appendpath, isdir, isfile, file_kind
27
27
from errors import bailout
29
29
from stat import S_ISREG, S_ISDIR, ST_MODE, ST_SIZE
73
73
doc="Inventory of this Tree")
75
75
def _check_retrieved(self, ie, f):
76
fp = fingerprint_file(f)
76
# TODO: Test this check by damaging the store?
79
77
if ie.text_size is not None:
80
if ie.text_size != fp['size']:
79
if fs != ie.text_size:
81
80
bailout("mismatched size for file %r in %r" % (ie.file_id, self._store),
82
81
["inventory expects %d bytes" % ie.text_size,
83
"file is actually %d bytes" % fp['size'],
82
"file is actually %d bytes" % fs,
84
83
"store is probably damaged/corrupt"])
86
if ie.text_sha1 != fp['sha1']:
87
if ie.text_sha1 != f_hash:
87
88
bailout("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
88
89
["inventory expects %s" % ie.text_sha1,
89
"file is actually %s" % fp['sha1'],
90
"file is actually %s" % f_hash,
90
91
"store is probably damaged/corrupt"])
178
def file_kind(self, filename):
179
if isfile(self.abspath(filename)):
181
elif isdir(self.abspath(filename)):
177
187
def list_files(self):
178
188
"""Recursively list all files as (path, class, kind, id).
238
"""Yield all unknown files in this WorkingTree.
244
def unknowns(self, path='', dir_id=None):
245
"""Yield names of unknown files in this WorkingTree.
240
247
If there are any unknown directories then only the directory is
241
248
returned, not all its children. But if there are unknown files
244
251
Currently returned depth-first, sorted by name within directories.
246
## TODO: Work from given directory downwards
248
for path, dir_entry in self.inventory.directories():
249
mutter("search for unknowns in %r" % path)
250
dirabs = self.abspath(path)
251
if not isdir(dirabs):
252
# e.g. directory deleted
256
for subf in os.listdir(dirabs):
258
and (subf not in dir_entry.children)):
263
subp = appendpath(path, subf)
264
if self.is_ignored(subp):
253
for fpath, fclass, fkind, fid in self.list_files():
269
258
def ignored_files(self):
275
264
def get_ignore_list(self):
276
"""Return list of ignore patterns.
278
Cached in the Tree object after the first call.
280
if hasattr(self, '_ignorelist'):
281
return self._ignorelist
283
l = bzrlib.DEFAULT_IGNORE[:]
265
"""Return list of ignore patterns."""
284
266
if self.has_filename(bzrlib.IGNORE_FILENAME):
285
267
f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
286
l.extend([line.rstrip("\n\r") for line in f.readlines()])
268
return [line.rstrip("\n\r") for line in f.readlines()]
270
return bzrlib.DEFAULT_IGNORE
291
273
def is_ignored(self, filename):
292
274
"""Check whether the filename matches an ignore pattern.
294
276
Patterns containing '/' need to match the whole path; others
295
match against only the last component.
297
If the file is ignored, returns the pattern which caused it to
298
be ignored, otherwise None. So this can simply be used as a
299
boolean if desired."""
301
## TODO: Use '**' to match directories, and other extended globbing stuff from cvs/rsync.
277
match against only the last component."""
278
## TODO: Take them from a file, not hardcoded
279
## TODO: Use extended zsh-style globs maybe?
280
## TODO: Use '**' to match directories?
303
281
for pat in self.get_ignore_list():
305
283
if fnmatch.fnmatchcase(filename, pat):
308
286
if fnmatch.fnmatchcase(splitpath(filename)[-1], pat):
331
309
ie = self._inventory[file_id]
332
310
f = self._store[ie.text_id]
333
311
mutter(" get fileid{%s} from %r" % (file_id, self))
313
if ie.text_size is None:
314
note("warning: no text size recorded on %r" % ie)
334
315
self._check_retrieved(ie, f)