14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
# TODO: Don't allow WorkingTrees to be constructed for remote branches.
19
# FIXME: I don't know if writing out the cache from the destructor is really a
20
# good idea, because destructors are considered poor taste in Python, and
21
# it's not predictable when it will be written out.
21
from errors import BzrCheckError
22
from trace import mutter
27
from bzrlib.osutils import appendpath, file_kind, isdir, splitpath
28
from bzrlib.errors import BzrCheckError
29
from bzrlib.trace import mutter
25
31
class WorkingTree(bzrlib.tree.Tree):
26
32
"""Working copy tree.
31
37
It is possible for a `WorkingTree` to have a filename which is
32
38
not listed in the Inventory and vice versa.
36
40
def __init__(self, basedir, inv):
41
from bzrlib.hashcache import HashCache
42
from bzrlib.trace import note, mutter
37
44
self._inventory = inv
38
45
self.basedir = basedir
39
46
self.path2id = inv.path2id
40
self._update_statcache()
48
# update the whole cache up front and write to disk if anything changed;
49
# in the future we might want to do this more selectively
50
hc = self._hashcache = HashCache(basedir)
60
if self._hashcache.needs_write:
61
self._hashcache.write()
42
64
def __iter__(self):
43
65
"""Iterate through file_ids for this tree.
46
68
and the working file exists.
48
70
inv = self._inventory
49
for file_id in self._inventory:
50
# TODO: This is slightly redundant; we should be able to just
51
# check the statcache but it only includes regular files.
52
# only include files which still exist on disk
55
if ((file_id in self._statcache)
56
or (os.path.exists(self.abspath(inv.id2path(file_id))))):
71
for path, ie in inv.iter_entries():
72
if bzrlib.osutils.lexists(self.abspath(path)):
61
76
def __repr__(self):
62
77
return "<%s of %s>" % (self.__class__.__name__,
78
getattr(self, 'basedir', None))
65
82
def abspath(self, filename):
66
83
return os.path.join(self.basedir, filename)
68
85
def has_filename(self, filename):
69
return os.path.exists(self.abspath(filename))
86
return bzrlib.osutils.lexists(self.abspath(filename))
71
88
def get_file(self, file_id):
72
89
return self.get_file_byname(self.id2path(file_id))
78
95
## XXX: badly named; this isn't in the store at all
79
96
return self.abspath(self.id2path(file_id))
99
def id2abspath(self, file_id):
100
return self.abspath(self.id2path(file_id))
82
103
def has_id(self, file_id):
83
104
# files that have been deleted are excluded
84
if not self.inventory.has_id(file_id):
105
inv = self._inventory
106
if not inv.has_id(file_id):
86
if file_id in self._statcache:
88
return os.path.exists(self.abspath(self.id2path(file_id)))
108
path = inv.id2path(file_id)
109
return bzrlib.osutils.lexists(self.abspath(path))
91
112
__contains__ = has_id
94
def _update_statcache(self):
96
if not self._statcache:
97
self._statcache = statcache.update_cache(self.basedir, self.inventory)
99
115
def get_file_size(self, file_id):
101
return os.stat(self._get_store_filename(file_id))[stat.ST_SIZE]
116
return os.path.getsize(self.id2abspath(file_id))
104
118
def get_file_sha1(self, file_id):
105
return self._statcache[file_id][statcache.SC_SHA1]
119
path = self._inventory.id2path(file_id)
120
return self._hashcache.get_sha1(path)
122
def get_symlink_target(self, file_id):
123
return os.readlink(self.id2path(file_id))
108
125
def file_class(self, filename):
109
126
if self.path2id(filename):
125
142
Skips the control directory.
127
from osutils import appendpath, file_kind
144
inv = self._inventory
132
146
def descend(from_dir_relpath, from_dir_id, dp):
133
147
ls = os.listdir(dp)
194
208
Currently returned depth-first, sorted by name within directories.
196
210
## TODO: Work from given directory downwards
197
from osutils import isdir, appendpath
199
211
for path, dir_entry in self.inventory.directories():
200
212
mutter("search for unknowns in %r" % path)
201
213
dirabs = self.abspath(path)