50
51
from time import time
54
from bzrlib import bzrdir, errors, osutils, urlutils
52
55
from bzrlib.atomicfile import AtomicFile
53
from bzrlib.branch import (Branch,
55
57
from bzrlib.conflicts import Conflict, ConflictList, CONFLICT_SUFFIXES
56
import bzrlib.bzrdir as bzrdir
57
58
from bzrlib.decorators import needs_read_lock, needs_write_lock
58
import bzrlib.errors as errors
59
59
from bzrlib.errors import (BzrCheckError,
61
61
ConflictFormatError,
63
62
WeaveRevisionNotPresent,
92
90
from bzrlib.progress import DummyProgress, ProgressPhase
93
91
from bzrlib.revision import NULL_REVISION
94
92
from bzrlib.rio import RioReader, rio_file, Stanza
95
from bzrlib.symbol_versioning import *
96
from bzrlib.textui import show_status
93
from bzrlib.symbol_versioning import (deprecated_passed,
99
from bzrlib.trace import mutter, note
98
100
from bzrlib.transform import build_tree
99
from bzrlib.trace import mutter, note
100
101
from bzrlib.transport import get_transport
101
102
from bzrlib.transport.local import LocalTransport
103
from bzrlib.textui import show_status
103
106
import bzrlib.xml5
259
262
self._branch = branch
261
264
self._branch = self.bzrdir.open_branch()
262
assert isinstance(self.branch, Branch), \
263
"branch %r is not a Branch" % self.branch
264
265
self.basedir = realpath(basedir)
265
266
# if branch is at our basedir and is a format 6 or less
266
267
if isinstance(self._format, WorkingTreeFormat2):
267
268
# share control object
268
269
self._control_files = self.branch.control_files
270
# only ready for format 3
271
assert isinstance(self._format, WorkingTreeFormat3)
271
# assume all other formats have their own control files.
272
272
assert isinstance(_control_files, LockableFiles), \
273
273
"_control_files must be a LockableFiles, not %r" \
279
279
# if needed, or, when the cache sees a change, append it to the hash
280
280
# cache file, and have the parser take the most recent entry for a
281
281
# given path only.
282
cache_filename = self.bzrdir.get_workingtree_transport(None).abspath('stat-cache')
282
cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
283
283
hc = self._hashcache = HashCache(basedir, cache_filename, self._control_files._file_mode)
285
285
# is this scan needed ? it makes things kinda slow.
288
288
if hc.needs_write:
289
289
mutter("write hc")
349
349
run into /. If there isn't one, raises NotBranchError.
350
350
TODO: give this a new exception.
351
351
If there is one, it is returned, along with the unused portion of path.
353
:return: The WorkingTree that contains 'path', and the rest of path
356
path = osutils.getcwd()
355
357
control, relpath = bzrdir.BzrDir.open_containing(path)
356
359
return control.open_workingtree(), relpath
451
454
def get_file_byname(self, filename):
452
455
return file(self.abspath(filename), 'rb')
457
def get_parent_ids(self):
458
"""See Tree.get_parent_ids.
460
This implementation reads the pending merges list and last_revision
461
value and uses that to decide what the parents list should be.
463
last_rev = self.last_revision()
468
other_parents = self.pending_merges()
469
return parents + other_parents
454
471
def get_root_id(self):
455
472
"""Return the id of this trees root"""
456
473
inv = self.read_working_inventory()
505
522
# but with branch a kwarg now, passing in args as is results in the
506
523
#message being used for the branch
507
524
args = (DEPRECATED_PARAMETER, message, ) + args
508
Commit().commit(working_tree=self, revprops=revprops, *args, **kwargs)
525
committed_id = Commit().commit( working_tree=self, revprops=revprops,
509
527
self._set_inventory(self.read_working_inventory())
511
530
def id2abspath(self, file_id):
512
531
return self.abspath(self.id2path(file_id))
530
549
return os.path.getsize(self.id2abspath(file_id))
533
def get_file_sha1(self, file_id):
534
path = self._inventory.id2path(file_id)
552
def get_file_sha1(self, file_id, path=None):
554
path = self._inventory.id2path(file_id)
535
555
return self._hashcache.get_sha1(path)
537
def is_executable(self, file_id):
538
if not supports_executable():
557
def get_file_mtime(self, file_id, path=None):
559
path = self._inventory.id2path(file_id)
560
return os.lstat(self.abspath(path)).st_mtime
562
if not supports_executable():
563
def is_executable(self, file_id, path=None):
539
564
return self._inventory[file_id].executable
541
path = self._inventory.id2path(file_id)
566
def is_executable(self, file_id, path=None):
568
path = self._inventory.id2path(file_id)
542
569
mode = os.lstat(self.abspath(path)).st_mode
543
return bool(stat.S_ISREG(mode) and stat.S_IEXEC&mode)
570
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
545
572
@needs_write_lock
546
573
def add(self, files, ids=None):
589
616
raise BzrError("cannot add top-level %r" % f)
591
618
fullpath = normpath(self.abspath(f))
594
620
kind = file_kind(fullpath)
595
621
except OSError, e:
596
622
if e.errno == errno.ENOENT:
597
623
raise NoSuchFile(fullpath)
598
# maybe something better?
599
raise BzrError('cannot add: not a regular file, symlink or directory: %s' % quotefn(f))
601
624
if not InventoryEntry.versionable_kind(kind):
602
raise BzrError('cannot add: not a versionable file ('
603
'i.e. regular file, symlink or directory): %s' % quotefn(f))
625
raise errors.BadFileKindError(filename=f, kind=kind)
605
626
if file_id is None:
606
627
inv.add_path(f, kind=kind)
700
719
Skips the control directory.
702
721
inv = self._inventory
704
def descend(from_dir_relpath, from_dir_id, dp):
722
# Convert these into local objects to save lookup times
723
pathjoin = bzrlib.osutils.pathjoin
724
file_kind = bzrlib.osutils.file_kind
726
# transport.base ends in a slash, we want the piece
727
# between the last two slashes
728
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
730
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
732
# directory file_id, relative path, absolute path, reverse sorted children
733
children = os.listdir(self.basedir)
735
# jam 20060527 The kernel sized tree seems equivalent whether we
736
# use a deque and popleft to keep them sorted, or if we use a plain
737
# list and just reverse() them.
738
children = collections.deque(children)
739
stack = [(inv.root.file_id, u'', self.basedir, children)]
741
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
744
f = children.popleft()
708
745
## TODO: If we find a subdirectory with its own .bzr
709
746
## directory, then that is a separate tree and we
710
747
## should exclude it.
712
749
# the bzrdir for this tree
713
if self.bzrdir.transport.base.endswith(f + '/'):
750
if transport_base_dir == f:
717
fp = appendpath(from_dir_relpath, f)
753
# we know that from_dir_relpath and from_dir_abspath never end in a slash
754
# and 'f' doesn't begin with one, we can do a string op, rather
755
# than the checks of pathjoin(), all relative paths will have an extra slash
757
fp = from_dir_relpath + '/' + f
720
fap = appendpath(dp, f)
760
fap = from_dir_abspath + '/' + f
722
762
f_ie = inv.get_child(from_dir_id, f)
725
elif self.is_ignored(fp):
765
elif self.is_ignored(fp[1:]):
768
# we may not have found this file, because of a unicode issue
769
f_norm, can_access = osutils.normalized_filename(f)
770
if f == f_norm or not can_access:
771
# No change, so treat this file normally
774
# this file can be accessed by a normalized path
775
# check again if it is versioned
776
# these lines are repeated here for performance
778
fp = from_dir_relpath + '/' + f
779
fap = from_dir_abspath + '/' + f
780
f_ie = inv.get_child(from_dir_id, f)
783
elif self.is_ignored(fp[1:]):
730
788
fk = file_kind(fap)
738
796
# make a last minute entry
798
yield fp[1:], c, fk, f_ie.file_id, f_ie
742
if fk == 'directory':
743
entry = TreeDirectory()
746
elif fk == 'symlink':
801
yield fp[1:], c, fk, None, fk_entries[fk]()
803
yield fp[1:], c, fk, None, TreeEntry()
751
yield fp, c, fk, (f_ie and f_ie.file_id), entry
753
806
if fk != 'directory':
757
# don't descend unversioned directories
760
for ff in descend(fp, f_ie.file_id, fap):
809
# But do this child first
810
new_children = os.listdir(fap)
812
new_children = collections.deque(new_children)
813
stack.append((f_ie.file_id, fp, fap, new_children))
814
# Break out of inner loop, so that we start outer loop with child
817
# if we finished all children, pop it off the stack
763
for f in descend(u'', inv.root.file_id, self.basedir):
766
821
@needs_write_lock
767
822
def move(self, from_paths, to_name):
887
942
These are files in the working directory that are not versioned or
888
943
control files or ignored.
890
>>> from bzrlib.bzrdir import ScratchDir
891
>>> d = ScratchDir(files=['foo', 'foo~'])
892
>>> b = d.open_branch()
893
>>> tree = d.open_workingtree()
894
>>> map(str, tree.unknowns())
897
>>> list(b.unknowns())
899
>>> tree.remove('foo')
900
>>> list(b.unknowns())
903
945
for subp in self.extras():
904
946
if not self.is_ignored(subp):
974
1017
for subf in os.listdir(dirabs):
976
and (subf not in dir_entry.children)):
1020
if subf not in dir_entry.children:
1021
subf_norm, can_access = osutils.normalized_filename(subf)
1022
if subf_norm != subf and can_access:
1023
if subf_norm not in dir_entry.children:
1024
fl.append(subf_norm)
981
subp = appendpath(path, subf)
1030
subp = pathjoin(path, subf)
984
1033
def _translate_ignore_rule(self, rule):
1051
1100
if hasattr(self, '_ignorelist'):
1052
1101
return self._ignorelist
1054
l = bzrlib.DEFAULT_IGNORE[:]
1055
1104
if self.has_filename(bzrlib.IGNORE_FILENAME):
1056
1105
f = self.get_file_byname(bzrlib.IGNORE_FILENAME)
1057
l.extend([line.rstrip("\n\r") for line in f.readlines()])
1106
l.extend([line.rstrip("\n\r").decode('utf-8')
1107
for line in f.readlines()])
1058
1108
self._ignorelist = l
1059
1109
self._ignore_regex = self._combine_ignore_rules(l)
1167
1217
def _cache_basis_inventory(self, new_revision):
1168
1218
"""Cache new_revision as the basis inventory."""
1219
# TODO: this should allow the ready-to-use inventory to be passed in,
1220
# as commit already has that ready-to-use [while the format is the
1170
1223
# this double handles the inventory - unpack and repack -
1171
1224
# but is easier to understand. We can/should put a conditional
1172
1225
# in here based on whether the inventory is in the latest format
1173
1226
# - 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)
1228
# the fast path is to copy the raw xml from the repository. If the
1229
# xml contains 'revision_id="', then we assume the right
1230
# revision_id is set. We must check for this full string, because a
1231
# root node id can legitimately look like 'revision_id' but cannot
1233
xml = self.branch.repository.get_inventory_xml(new_revision)
1234
if not 'revision_id="' in xml.split('\n', 1)[0]:
1235
inv = self.branch.repository.deserialise_inventory(
1237
inv.revision_id = new_revision
1238
xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1239
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1179
1240
path = self._basis_inventory_name()
1180
self._control_files.put_utf8(path, xml)
1242
self._control_files.put(path, sio)
1181
1243
except WeaveRevisionNotPresent:
1184
1246
def read_basis_inventory(self):
1185
1247
"""Read the cached basis inventory."""
1186
1248
path = self._basis_inventory_name()
1187
return self._control_files.get_utf8(path).read()
1249
return self._control_files.get(path).read()
1189
1251
@needs_read_lock
1190
1252
def read_working_inventory(self):
1225
1287
# TODO: Perhaps make this just a warning, and continue?
1226
1288
# This tends to happen when
1227
1289
raise NotVersionedError(path=f)
1228
mutter("remove inventory entry %s {%s}", quotefn(f), fid)
1230
1291
# having remove it, it must be either ignored or unknown
1231
1292
if self.is_ignored(f):
1232
1293
new_status = 'I'
1234
1295
new_status = '?'
1235
show_status(new_status, inv[fid].kind, quotefn(f))
1296
show_status(new_status, inv[fid].kind, f, to_file=to_file)
1238
1299
self._write_inventory(inv)
1305
1366
# of a nasty hack; probably it's better to have a transaction object,
1306
1367
# which can do some finalization when it's either successfully or
1307
1368
# unsuccessfully completed. (Denys's original patch did that.)
1308
# RBC 20060206 hookinhg into transaction will couple lock and transaction
1309
# wrongly. Hookinh into unllock on the control files object is fine though.
1369
# RBC 20060206 hooking into transaction will couple lock and transaction
1370
# wrongly. Hooking into unlock on the control files object is fine though.
1311
1372
# TODO: split this per format so there is no ugly if block
1312
1373
if self._hashcache.needs_write and (
1401
1465
if file_kind(self.abspath(conflicted)) != "file":
1404
if e.errno == errno.ENOENT:
1467
except errors.NoSuchFile:
1408
1469
if text is True:
1409
1470
for suffix in ('.THIS', '.OTHER'):
1411
1472
kind = file_kind(self.abspath(conflicted+suffix))
1413
if e.errno == errno.ENOENT:
1475
except errors.NoSuchFile:
1421
1479
ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1422
1480
conflicts.append(Conflict.factory(ctype, path=conflicted,
1680
1745
def initialize(self, a_bzrdir, revision_id=None):
1681
1746
"""See WorkingTreeFormat.initialize().
1683
revision_id allows creating a working tree at a differnet
1748
revision_id allows creating a working tree at a different
1684
1749
revision than the branch is at.
1686
1751
if not isinstance(a_bzrdir.transport, LocalTransport):
1728
1793
raise NotImplementedError
1729
1794
if not isinstance(a_bzrdir.transport, LocalTransport):
1730
1795
raise errors.NotLocalUrl(a_bzrdir.transport.base)
1731
control_files = self._open_control_files(a_bzrdir)
1732
return WorkingTree3(a_bzrdir.root_transport.base,
1796
return self._open(a_bzrdir, self._open_control_files(a_bzrdir))
1798
def _open(self, a_bzrdir, control_files):
1799
"""Open the tree itself.
1801
:param a_bzrdir: the dir for the tree.
1802
:param control_files: the control files for the tree.
1804
return WorkingTree3(a_bzrdir.root_transport.local_abspath('.'),
1733
1805
_internal=True,
1735
1807
_bzrdir=a_bzrdir,