279
280
# if needed, or, when the cache sees a change, append it to the hash
280
281
# cache file, and have the parser take the most recent entry for a
281
282
# given path only.
282
cache_filename = self.bzrdir.get_workingtree_transport(None).abspath('stat-cache')
283
cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
283
284
hc = self._hashcache = HashCache(basedir, cache_filename, self._control_files._file_mode)
285
286
# is this scan needed ? it makes things kinda slow.
288
289
if hc.needs_write:
289
290
mutter("write hc")
349
350
run into /. If there isn't one, raises NotBranchError.
350
351
TODO: give this a new exception.
351
352
If there is one, it is returned, along with the unused portion of path.
354
:return: The WorkingTree that contains 'path', and the rest of path
354
357
path = os.getcwdu()
355
358
control, relpath = bzrdir.BzrDir.open_containing(path)
356
360
return control.open_workingtree(), relpath
530
534
return os.path.getsize(self.id2abspath(file_id))
533
def get_file_sha1(self, file_id):
534
path = self._inventory.id2path(file_id)
537
def get_file_sha1(self, file_id, path=None):
539
path = self._inventory.id2path(file_id)
535
540
return self._hashcache.get_sha1(path)
537
def is_executable(self, file_id):
538
if not supports_executable():
542
def get_file_mtime(self, file_id, path=None):
544
path = self._inventory.id2path(file_id)
545
return os.lstat(self.abspath(path)).st_mtime
547
if not supports_executable():
548
def is_executable(self, file_id, path=None):
539
549
return self._inventory[file_id].executable
541
path = self._inventory.id2path(file_id)
551
def is_executable(self, file_id, path=None):
553
path = self._inventory.id2path(file_id)
542
554
mode = os.lstat(self.abspath(path)).st_mode
543
555
return bool(stat.S_ISREG(mode) and stat.S_IEXEC&mode)
692
704
def list_files(self):
693
"""Recursively list all files as (path, class, kind, id).
705
"""Recursively list all files as (path, class, kind, id, entry).
695
707
Lists, but does not descend into unversioned directories.
700
712
Skips the control directory.
702
714
inv = self._inventory
704
def descend(from_dir_relpath, from_dir_id, dp):
715
# Convert these into local objects to save lookup times
716
pathjoin = bzrlib.osutils.pathjoin
717
file_kind = bzrlib.osutils.file_kind
719
# transport.base ends in a slash, we want the piece
720
# between the last two slashes
721
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
723
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
725
# directory file_id, relative path, absolute path, reverse sorted children
726
children = os.listdir(self.basedir)
728
# jam 20060527 The kernel sized tree seems equivalent whether we
729
# use a deque and popleft to keep them sorted, or if we use a plain
730
# list and just reverse() them.
731
children = collections.deque(children)
732
stack = [(inv.root.file_id, u'', self.basedir, children)]
734
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
737
f = children.popleft()
708
738
## TODO: If we find a subdirectory with its own .bzr
709
739
## directory, then that is a separate tree and we
710
740
## should exclude it.
712
742
# the bzrdir for this tree
713
if self.bzrdir.transport.base.endswith(f + '/'):
743
if transport_base_dir == f:
717
fp = appendpath(from_dir_relpath, f)
746
# we know that from_dir_relpath and from_dir_abspath never end in a slash
747
# and 'f' doesn't begin with one, we can do a string op, rather
748
# than the checks of pathjoin(), all relative paths will have an extra slash
750
fp = from_dir_relpath + '/' + f
720
fap = appendpath(dp, f)
753
fap = from_dir_abspath + '/' + f
722
755
f_ie = inv.get_child(from_dir_id, f)
725
elif self.is_ignored(fp):
758
elif self.is_ignored(fp[1:]):
738
771
# make a last minute entry
773
yield fp[1:], c, fk, f_ie.file_id, f_ie
742
if fk == 'directory':
743
entry = TreeDirectory()
746
elif fk == 'symlink':
776
yield fp[1:], c, fk, None, fk_entries[fk]()
778
yield fp[1:], c, fk, None, TreeEntry()
751
yield fp, c, fk, (f_ie and f_ie.file_id), entry
753
781
if fk != 'directory':
757
# don't descend unversioned directories
760
for ff in descend(fp, f_ie.file_id, fap):
784
# But do this child first
785
new_children = os.listdir(fap)
787
new_children = collections.deque(new_children)
788
stack.append((f_ie.file_id, fp, fap, new_children))
789
# Break out of inner loop, so that we start outer loop with child
792
# if we finished all children, pop it off the stack
763
for f in descend(u'', inv.root.file_id, self.basedir):
766
796
@needs_write_lock
767
797
def move(self, from_paths, to_name):
804
834
raise BzrError("%r is not versioned" % f)
805
835
name_tail = splitpath(f)[-1]
806
dest_path = appendpath(to_name, name_tail)
836
dest_path = pathjoin(to_name, name_tail)
807
837
if self.has_filename(dest_path):
808
838
raise BzrError("destination %r already exists" % dest_path)
809
839
if f_id in to_idpath:
817
847
for f in from_paths:
818
848
name_tail = splitpath(f)[-1]
819
dest_path = appendpath(to_name, name_tail)
849
dest_path = pathjoin(to_name, name_tail)
820
850
result.append((f, dest_path))
821
851
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1054
1085
l = bzrlib.DEFAULT_IGNORE[:]
1055
1086
if self.has_filename(bzrlib.IGNORE_FILENAME):
1056
1087
f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1057
l.extend([line.rstrip("\n\r") for line in f.readlines()])
1088
l.extend([line.rstrip("\n\r").decode('utf-8')
1089
for line in f.readlines()])
1058
1090
self._ignorelist = l
1059
1091
self._ignore_regex = self._combine_ignore_rules(l)
1167
1199
def _cache_basis_inventory(self, new_revision):
1168
1200
"""Cache new_revision as the basis inventory."""
1201
# TODO: this should allow the ready-to-use inventory to be passed in,
1202
# as commit already has that ready-to-use [while the format is the
1170
1205
# this double handles the inventory - unpack and repack -
1171
1206
# but is easier to understand. We can/should put a conditional
1172
1207
# in here based on whether the inventory is in the latest format
1173
1208
# - perhaps we should repack all inventories on a repository
1175
inv = self.branch.repository.get_inventory(new_revision)
1176
inv.revision_id = new_revision
1177
xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1210
# the fast path is to copy the raw xml from the repository. If the
1211
# xml contains 'revision_id="', then we assume the right
1212
# revision_id is set. We must check for this full string, because a
1213
# root node id can legitimately look like 'revision_id' but cannot
1215
xml = self.branch.repository.get_inventory_xml(new_revision)
1216
if not 'revision_id="' in xml.split('\n', 1)[0]:
1217
inv = self.branch.repository.deserialise_inventory(
1219
inv.revision_id = new_revision
1220
xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1179
1222
path = self._basis_inventory_name()
1180
1223
self._control_files.put_utf8(path, xml)
1199
1242
@needs_write_lock
1200
def remove(self, files, verbose=False):
1243
def remove(self, files, verbose=False, to_file=None):
1201
1244
"""Remove nominated files from the working inventory..
1203
1246
This does not remove their text. This does not run on XXX on what? RBC
1232
1275
new_status = 'I'
1234
1277
new_status = '?'
1235
show_status(new_status, inv[fid].kind, quotefn(f))
1278
show_status(new_status, inv[fid].kind, quotefn(f), to_file=to_file)
1238
1281
self._write_inventory(inv)
1643
1686
raise NotImplementedError
1644
1687
if not isinstance(a_bzrdir.transport, LocalTransport):
1645
1688
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1646
return WorkingTree(a_bzrdir.root_transport.base,
1689
return WorkingTree(a_bzrdir.root_transport.local_abspath('.'),
1647
1690
_internal=True,
1649
1692
_bzrdir=a_bzrdir)
1729
1772
if not isinstance(a_bzrdir.transport, LocalTransport):
1730
1773
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1731
1774
control_files = self._open_control_files(a_bzrdir)
1732
return WorkingTree3(a_bzrdir.root_transport.base,
1775
return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
1733
1776
_internal=True,
1735
1778
_bzrdir=a_bzrdir,