828
881
def get_symlink_target(self, file_id):
829
return os.readlink(self.id2abspath(file_id))
831
def file_class(self, filename):
832
if self.path2id(filename):
834
elif self.is_ignored(filename):
882
abspath = self.id2abspath(file_id)
883
target = osutils.readlink(abspath)
886
def subsume(self, other_tree):
887
raise NotImplementedError(self.subsume)
889
def _setup_directory_is_tree_reference(self):
890
if self._branch.repository._format.supports_tree_reference:
891
self._directory_is_tree_reference = \
892
self._directory_may_be_tree_reference
894
self._directory_is_tree_reference = \
895
self._directory_is_never_tree_reference
897
def _directory_is_never_tree_reference(self, relpath):
900
def _directory_may_be_tree_reference(self, relpath):
901
# as a special case, if a directory contains control files then
902
# it's a tree reference, except that the root of the tree is not
903
return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
904
# TODO: We could ask all the control formats whether they
905
# recognize this directory, but at the moment there's no cheap api
906
# to do that. Since we probably can only nest bzr checkouts and
907
# they always use this name it's ok for now. -- mbp 20060306
909
# FIXME: There is an unhandled case here of a subdirectory
910
# containing .bzr but not a branch; that will probably blow up
911
# when you try to commit it. It might happen if there is a
912
# checkout in a subdirectory. This can be avoided by not adding
915
def extract(self, file_id, format=None):
916
"""Extract a subtree from this tree.
918
A new branch will be created, relative to the path for this tree.
920
raise NotImplementedError(self.extract)
840
"""Write the in memory inventory to disk."""
841
# TODO: Maybe this should only write on dirty ?
842
if self._control_files._lock_mode != 'w':
843
raise errors.NotWriteLocked(self)
845
xml5.serializer_v5.write_inventory(self._inventory, sio)
847
self._control_files.put('inventory', sio)
848
self._inventory_is_modified = False
850
def list_files(self, include_root=False):
851
"""Recursively list all files as (path, class, kind, id, entry).
923
"""Write the in memory meta data to disk."""
924
raise NotImplementedError(self.flush)
926
def _kind(self, relpath):
927
return osutils.file_kind(self.abspath(relpath))
929
def list_files(self, include_root=False, from_dir=None, recursive=True):
930
"""List all files as (path, class, kind, id, entry).
853
932
Lists, but does not descend into unversioned directories.
855
933
This does not include files that have been deleted in this
934
tree. Skips the control directory.
858
Skips the control directory.
936
:param include_root: if True, return an entry for the root
937
:param from_dir: start from this directory or None for the root
938
:param recursive: whether to recurse into subdirectories or not
860
inv = self._inventory
861
if include_root is True:
862
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
863
# Convert these into local objects to save lookup times
864
pathjoin = osutils.pathjoin
865
file_kind = osutils.file_kind
867
# transport.base ends in a slash, we want the piece
868
# between the last two slashes
869
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
871
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
873
# directory file_id, relative path, absolute path, reverse sorted children
874
children = os.listdir(self.basedir)
876
# jam 20060527 The kernel sized tree seems equivalent whether we
877
# use a deque and popleft to keep them sorted, or if we use a plain
878
# list and just reverse() them.
879
children = collections.deque(children)
880
stack = [(inv.root.file_id, u'', self.basedir, children)]
882
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
885
f = children.popleft()
886
## TODO: If we find a subdirectory with its own .bzr
887
## directory, then that is a separate tree and we
888
## should exclude it.
890
# the bzrdir for this tree
891
if transport_base_dir == f:
894
# we know that from_dir_relpath and from_dir_abspath never end in a slash
895
# and 'f' doesn't begin with one, we can do a string op, rather
896
# than the checks of pathjoin(), all relative paths will have an extra slash
898
fp = from_dir_relpath + '/' + f
901
fap = from_dir_abspath + '/' + f
903
f_ie = inv.get_child(from_dir_id, f)
906
elif self.is_ignored(fp[1:]):
909
# we may not have found this file, because of a unicode issue
910
f_norm, can_access = osutils.normalized_filename(f)
911
if f == f_norm or not can_access:
912
# No change, so treat this file normally
915
# this file can be accessed by a normalized path
916
# check again if it is versioned
917
# these lines are repeated here for performance
919
fp = from_dir_relpath + '/' + f
920
fap = from_dir_abspath + '/' + f
921
f_ie = inv.get_child(from_dir_id, f)
924
elif self.is_ignored(fp[1:]):
933
raise BzrCheckError("file %r entered as kind %r id %r, "
935
% (fap, f_ie.kind, f_ie.file_id, fk))
937
# make a last minute entry
939
yield fp[1:], c, fk, f_ie.file_id, f_ie
942
yield fp[1:], c, fk, None, fk_entries[fk]()
944
yield fp[1:], c, fk, None, TreeEntry()
947
if fk != 'directory':
950
# But do this child first
951
new_children = os.listdir(fap)
953
new_children = collections.deque(new_children)
954
stack.append((f_ie.file_id, fp, fap, new_children))
955
# Break out of inner loop, so that we start outer loop with child
958
# if we finished all children, pop it off the stack
961
@needs_tree_write_lock
962
def move(self, from_paths, to_name):
940
raise NotImplementedError(self.list_files)
942
def move(self, from_paths, to_dir=None, after=False):
965
to_name must exist in the inventory.
967
If to_name exists and is a directory, the files are moved into
968
it, keeping their old names.
970
Note that to_name is only the last component of the new name;
945
to_dir must be known to the working tree.
947
If to_dir exists and is a directory, the files are moved into
948
it, keeping their old names.
950
Note that to_dir is only the last component of the new name;
971
951
this doesn't change the directory.
953
For each entry in from_paths the move mode will be determined
956
The first mode moves the file in the filesystem and updates the
957
working tree metadata. The second mode only updates the working tree
958
metadata without touching the file on the filesystem.
960
move uses the second mode if 'after == True' and the target is not
961
versioned but present in the working tree.
963
move uses the second mode if 'after == False' and the source is
964
versioned but no longer in the working tree, and the target is not
965
versioned but present in the working tree.
967
move uses the first mode if 'after == False' and the source is
968
versioned and present in the working tree, and the target is not
969
versioned and not present in the working tree.
971
Everything else results in an error.
973
973
This returns a list of (from_path, to_path) pairs for each
974
974
entry that is moved.
977
## TODO: Option to move IDs only
978
assert not isinstance(from_paths, basestring)
980
to_abs = self.abspath(to_name)
981
if not isdir(to_abs):
982
raise BzrError("destination %r is not a directory" % to_abs)
983
if not self.has_filename(to_name):
984
raise BzrError("destination %r not in working directory" % to_abs)
985
to_dir_id = inv.path2id(to_name)
986
if to_dir_id is None and to_name != '':
987
raise BzrError("destination %r is not a versioned directory" % to_name)
988
to_dir_ie = inv[to_dir_id]
989
if to_dir_ie.kind != 'directory':
990
raise BzrError("destination %r is not a directory" % to_abs)
992
to_idpath = inv.get_idpath(to_dir_id)
995
if not self.has_filename(f):
996
raise BzrError("%r does not exist in working tree" % f)
997
f_id = inv.path2id(f)
999
raise BzrError("%r is not versioned" % f)
1000
name_tail = splitpath(f)[-1]
1001
dest_path = pathjoin(to_name, name_tail)
1002
if self.has_filename(dest_path):
1003
raise BzrError("destination %r already exists" % dest_path)
1004
if f_id in to_idpath:
1005
raise BzrError("can't move %r to a subdirectory of itself" % f)
1007
# OK, so there's a race here, it's possible that someone will
1008
# create a file in this interval and then the rename might be
1009
# left half-done. But we should have caught most problems.
1010
orig_inv = deepcopy(self.inventory)
1011
original_modified = self._inventory_is_modified
1014
self._inventory_is_modified = True
1015
for f in from_paths:
1016
name_tail = splitpath(f)[-1]
1017
dest_path = pathjoin(to_name, name_tail)
1018
result.append((f, dest_path))
1019
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1021
osutils.rename(self.abspath(f), self.abspath(dest_path))
1023
raise BzrError("failed to rename %r to %r: %s" %
1024
(f, dest_path, e[1]))
1026
# restore the inventory on error
1027
self._set_inventory(orig_inv, dirty=original_modified)
1029
self._write_inventory(inv)
976
raise NotImplementedError(self.move)
1032
978
@needs_tree_write_lock
1033
def rename_one(self, from_rel, to_rel):
979
def rename_one(self, from_rel, to_rel, after=False):
1034
980
"""Rename one file.
1036
982
This can change the directory or the filename or both.
984
rename_one has several 'modes' to work. First, it can rename a physical
985
file and change the file_id. That is the normal mode. Second, it can
986
only change the file_id without touching any physical file.
988
rename_one uses the second mode if 'after == True' and 'to_rel' is not
989
versioned but present in the working tree.
991
rename_one uses the second mode if 'after == False' and 'from_rel' is
992
versioned but no longer in the working tree, and 'to_rel' is not
993
versioned but present in the working tree.
995
rename_one uses the first mode if 'after == False' and 'from_rel' is
996
versioned and present in the working tree, and 'to_rel' is not
997
versioned and not present in the working tree.
999
Everything else results in an error.
1038
inv = self.inventory
1039
if not self.has_filename(from_rel):
1040
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1041
if self.has_filename(to_rel):
1042
raise BzrError("can't rename: new working file %r already exists" % to_rel)
1044
file_id = inv.path2id(from_rel)
1046
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1048
entry = inv[file_id]
1049
from_parent = entry.parent_id
1050
from_name = entry.name
1052
if inv.path2id(to_rel):
1053
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1055
to_dir, to_tail = os.path.split(to_rel)
1056
to_dir_id = inv.path2id(to_dir)
1057
if to_dir_id is None and to_dir != '':
1058
raise BzrError("can't determine destination directory id for %r" % to_dir)
1060
mutter("rename_one:")
1061
mutter(" file_id {%s}" % file_id)
1062
mutter(" from_rel %r" % from_rel)
1063
mutter(" to_rel %r" % to_rel)
1064
mutter(" to_dir %r" % to_dir)
1065
mutter(" to_dir_id {%s}" % to_dir_id)
1067
inv.rename(file_id, to_dir_id, to_tail)
1069
from_abs = self.abspath(from_rel)
1070
to_abs = self.abspath(to_rel)
1072
osutils.rename(from_abs, to_abs)
1074
inv.rename(file_id, from_parent, from_name)
1075
raise BzrError("failed to rename %r to %r: %s"
1076
% (from_abs, to_abs, e[1]))
1077
self._write_inventory(inv)
1001
raise NotImplementedError(self.rename_one)
1079
1003
@needs_read_lock
1080
1004
def unknowns(self):
1425
1244
def get_physical_lock_status(self):
1426
1245
return self._control_files.get_physical_lock_status()
1428
def _basis_inventory_name(self):
1429
return 'basis-inventory-cache'
1247
def _reset_data(self):
1248
"""Reset transient data that cannot be revalidated."""
1249
raise NotImplementedError(self._reset_data)
1431
@needs_tree_write_lock
1432
1251
def set_last_revision(self, new_revision):
1433
1252
"""Change the last revision in the working tree."""
1434
if self._change_last_revision(new_revision):
1435
self._cache_basis_inventory(new_revision)
1253
raise NotImplementedError(self.set_last_revision)
1437
1255
def _change_last_revision(self, new_revision):
1438
1256
"""Template method part of set_last_revision to perform the change.
1440
1258
This is used to allow WorkingTree3 instances to not affect branch
1441
1259
when their last revision is set.
1443
if new_revision is None:
1444
self.branch.set_revision_history([])
1261
if _mod_revision.is_null(new_revision):
1262
self.branch.set_last_revision_info(0, new_revision)
1447
1265
self.branch.generate_revision_history(new_revision)
1448
1266
except errors.NoSuchRevision:
1449
1267
# not present in the repo - dont try to set it deeper than the tip
1450
self.branch.set_revision_history([new_revision])
1268
self.branch._set_revision_history([new_revision])
1453
def _write_basis_inventory(self, xml):
1454
"""Write the basis inventory XML to the basis-inventory file"""
1455
assert isinstance(xml, str), 'serialised xml must be bytestring.'
1456
path = self._basis_inventory_name()
1458
self._control_files.put(path, sio)
1460
def _create_basis_xml_from_inventory(self, revision_id, inventory):
1461
"""Create the text that will be saved in basis-inventory"""
1462
inventory.revision_id = revision_id
1463
return xml6.serializer_v6.write_inventory_to_string(inventory)
1465
def _cache_basis_inventory(self, new_revision):
1466
"""Cache new_revision as the basis inventory."""
1467
# TODO: this should allow the ready-to-use inventory to be passed in,
1468
# as commit already has that ready-to-use [while the format is the
1471
# this double handles the inventory - unpack and repack -
1472
# but is easier to understand. We can/should put a conditional
1473
# in here based on whether the inventory is in the latest format
1474
# - perhaps we should repack all inventories on a repository
1476
# the fast path is to copy the raw xml from the repository. If the
1477
# xml contains 'revision_id="', then we assume the right
1478
# revision_id is set. We must check for this full string, because a
1479
# root node id can legitimately look like 'revision_id' but cannot
1481
xml = self.branch.repository.get_inventory_xml(new_revision)
1482
firstline = xml.split('\n', 1)[0]
1483
if (not 'revision_id="' in firstline or
1484
'format="6"' not in firstline):
1485
inv = self.branch.repository.deserialise_inventory(
1487
xml = self._create_basis_xml_from_inventory(new_revision, inv)
1488
self._write_basis_inventory(xml)
1489
except (errors.NoSuchRevision, errors.RevisionNotPresent):
1492
def read_basis_inventory(self):
1493
"""Read the cached basis inventory."""
1494
path = self._basis_inventory_name()
1495
return self._control_files.get(path).read()
1498
def read_working_inventory(self):
1499
"""Read the working inventory.
1501
:raises errors.InventoryModified: read_working_inventory will fail
1502
when the current in memory inventory has been modified.
1504
# conceptually this should be an implementation detail of the tree.
1505
# XXX: Deprecate this.
1506
# ElementTree does its own conversion from UTF-8, so open in
1508
if self._inventory_is_modified:
1509
raise errors.InventoryModified(self)
1510
result = xml5.serializer_v5.read_inventory(
1511
self._control_files.get('inventory'))
1512
self._set_inventory(result, dirty=False)
1515
1271
@needs_tree_write_lock
1516
def remove(self, files, verbose=False, to_file=None):
1517
"""Remove nominated files from the working inventory..
1519
This does not remove their text. This does not run on XXX on what? RBC
1521
TODO: Refuse to remove modified files unless --force is given?
1523
TODO: Do something useful with directories.
1525
TODO: Should this remove the text or not? Tough call; not
1526
removing may be useful and the user can just use use rm, and
1527
is the opposite of add. Removing it is consistent with most
1528
other tools. Maybe an option.
1272
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1274
"""Remove nominated files from the working tree metadata.
1276
:files: File paths relative to the basedir.
1277
:keep_files: If true, the files will also be kept.
1278
:force: Delete files and directories, even if they are changed and
1279
even if the directories are not empty.
1530
## TODO: Normalize names
1531
## TODO: Remove nested loops; better scalability
1532
1281
if isinstance(files, basestring):
1533
1282
files = [files]
1535
inv = self.inventory
1537
# do this before any modifications
1286
all_files = set() # specified and nested files
1287
unknown_nested_files=set()
1289
to_file = sys.stdout
1291
files_to_backup = []
1293
def recurse_directory_to_add_files(directory):
1294
# Recurse directory and add all files
1295
# so we can check if they have changed.
1296
for parent_info, file_infos in self.walkdirs(directory):
1297
for relpath, basename, kind, lstat, fileid, kind in file_infos:
1298
# Is it versioned or ignored?
1299
if self.path2id(relpath):
1300
# Add nested content for deletion.
1301
all_files.add(relpath)
1303
# Files which are not versioned
1304
# should be treated as unknown.
1305
files_to_backup.append(relpath)
1307
for filename in files:
1308
# Get file name into canonical form.
1309
abspath = self.abspath(filename)
1310
filename = self.relpath(abspath)
1311
if len(filename) > 0:
1312
all_files.add(filename)
1313
recurse_directory_to_add_files(filename)
1315
files = list(all_files)
1318
return # nothing to do
1320
# Sort needed to first handle directory content before the directory
1321
files.sort(reverse=True)
1323
# Bail out if we are going to delete files we shouldn't
1324
if not keep_files and not force:
1325
for (file_id, path, content_change, versioned, parent_id, name,
1326
kind, executable) in self.iter_changes(self.basis_tree(),
1327
include_unchanged=True, require_versioned=False,
1328
want_unversioned=True, specific_files=files):
1329
if versioned[0] == False:
1330
# The record is unknown or newly added
1331
files_to_backup.append(path[1])
1332
elif (content_change and (kind[1] is not None) and
1333
osutils.is_inside_any(files, path[1])):
1334
# Versioned and changed, but not deleted, and still
1335
# in one of the dirs to be deleted.
1336
files_to_backup.append(path[1])
1338
def backup(file_to_backup):
1339
backup_name = self.bzrdir._available_backup_name(file_to_backup)
1340
osutils.rename(abs_path, self.abspath(backup_name))
1341
return "removed %s (but kept a copy: %s)" % (file_to_backup,
1344
# Build inv_delta and delete files where applicable,
1345
# do this before any modifications to meta data.
1538
1346
for f in files:
1539
fid = inv.path2id(f)
1347
fid = self.path2id(f)
1541
# TODO: Perhaps make this just a warning, and continue?
1542
# This tends to happen when
1543
raise NotVersionedError(path=f)
1545
# having remove it, it must be either ignored or unknown
1546
if self.is_ignored(f):
1550
textui.show_status(new_status, inv[fid].kind, f,
1554
self._write_inventory(inv)
1350
message = "%s is not versioned." % (f,)
1353
# having removed it, it must be either ignored or unknown
1354
if self.is_ignored(f):
1358
# XXX: Really should be a more abstract reporter interface
1359
kind_ch = osutils.kind_marker(self.kind(fid))
1360
to_file.write(new_status + ' ' + f + kind_ch + '\n')
1362
inv_delta.append((f, None, fid, None))
1363
message = "removed %s" % (f,)
1366
abs_path = self.abspath(f)
1367
if osutils.lexists(abs_path):
1368
if (osutils.isdir(abs_path) and
1369
len(os.listdir(abs_path)) > 0):
1371
osutils.rmtree(abs_path)
1372
message = "deleted %s" % (f,)
1376
if f in files_to_backup:
1379
osutils.delete_any(abs_path)
1380
message = "deleted %s" % (f,)
1381
elif message is not None:
1382
# Only care if we haven't done anything yet.
1383
message = "%s does not exist." % (f,)
1385
# Print only one message (if any) per file.
1386
if message is not None:
1388
self.apply_inventory_delta(inv_delta)
1556
1390
@needs_tree_write_lock
1557
def revert(self, filenames, old_tree=None, backups=True,
1558
pb=DummyProgress()):
1391
def revert(self, filenames=None, old_tree=None, backups=True,
1392
pb=None, report_changes=False):
1559
1393
from bzrlib.conflicts import resolve
1396
symbol_versioning.warn('Using [] to revert all files is deprecated'
1397
' as of bzr 0.91. Please use None (the default) instead.',
1398
DeprecationWarning, stacklevel=2)
1560
1399
if old_tree is None:
1561
old_tree = self.basis_tree()
1562
conflicts = transform.revert(self, old_tree, filenames, backups, pb)
1563
if not len(filenames):
1564
self.set_parent_ids(self.get_parent_ids()[:1])
1400
basis_tree = self.basis_tree()
1401
basis_tree.lock_read()
1402
old_tree = basis_tree
1567
resolve(self, filenames, ignore_misses=True)
1406
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1408
if filenames is None and len(self.get_parent_ids()) > 1:
1410
last_revision = self.last_revision()
1411
if last_revision != _mod_revision.NULL_REVISION:
1412
if basis_tree is None:
1413
basis_tree = self.basis_tree()
1414
basis_tree.lock_read()
1415
parent_trees.append((last_revision, basis_tree))
1416
self.set_parent_trees(parent_trees)
1419
resolve(self, filenames, ignore_misses=True, recursive=True)
1421
if basis_tree is not None:
1568
1423
return conflicts
1570
# XXX: This method should be deprecated in favour of taking in a proper
1571
# new Inventory object.
1572
@needs_tree_write_lock
1573
def set_inventory(self, new_inventory_list):
1574
from bzrlib.inventory import (Inventory,
1579
inv = Inventory(self.get_root_id())
1580
for path, file_id, parent, kind in new_inventory_list:
1581
name = os.path.basename(path)
1584
# fixme, there should be a factory function inv,add_??
1585
if kind == 'directory':
1586
inv.add(InventoryDirectory(file_id, name, parent))
1587
elif kind == 'file':
1588
inv.add(InventoryFile(file_id, name, parent))
1589
elif kind == 'symlink':
1590
inv.add(InventoryLink(file_id, name, parent))
1592
raise BzrError("unknown kind %r" % kind)
1593
self._write_inventory(inv)
1425
def revision_tree(self, revision_id):
1426
"""See Tree.revision_tree.
1428
WorkingTree can supply revision_trees for the basis revision only
1429
because there is only one cached inventory in the bzr directory.
1431
raise NotImplementedError(self.revision_tree)
1595
1433
@needs_tree_write_lock
1596
1434
def set_root_id(self, file_id):
1597
1435
"""Set the root id for this tree."""
1599
1437
if file_id is None:
1600
symbol_versioning.warn(symbol_versioning.zero_twelve
1601
% 'WorkingTree.set_root_id with fileid=None',
1605
inv = self._inventory
1606
orig_root_id = inv.root.file_id
1607
# TODO: it might be nice to exit early if there was nothing
1608
# to do, saving us from trigger a sync on unlock.
1609
self._inventory_is_modified = True
1610
# we preserve the root inventory entry object, but
1611
# unlinkit from the byid index
1612
del inv._byid[inv.root.file_id]
1613
inv.root.file_id = file_id
1614
# and link it into the index with the new changed id.
1615
inv._byid[inv.root.file_id] = inv.root
1616
# and finally update all children to reference the new id.
1617
# XXX: this should be safe to just look at the root.children
1618
# list, not the WHOLE INVENTORY.
1621
if entry.parent_id == orig_root_id:
1622
entry.parent_id = inv.root.file_id
1439
'WorkingTree.set_root_id with fileid=None')
1440
file_id = osutils.safe_file_id(file_id)
1441
self._set_root_id(file_id)
1443
def _set_root_id(self, file_id):
1444
"""Set the root id for this tree, in a format specific manner.
1446
:param file_id: The file id to assign to the root. It must not be
1447
present in the current inventory or an error will occur. It must
1448
not be None, but rather a valid file id.
1450
raise NotImplementedError(self._set_root_id)
1624
1452
def unlock(self):
1625
1453
"""See Branch.unlock.
1627
1455
WorkingTree locking just uses the Branch locking facilities.
1628
1456
This is current because all working trees have an embedded branch
1629
1457
within them. IF in the future, we were to make branch data shareable
1630
between multiple working trees, i.e. via shared storage, then we
1458
between multiple working trees, i.e. via shared storage, then we
1631
1459
would probably want to lock both the local tree, and the branch.
1633
1461
raise NotImplementedError(self.unlock)
1465
def update(self, change_reporter=None, possible_transports=None,
1466
revision=None, old_tip=_marker, show_base=False):
1636
1467
"""Update a working tree along its branch.
1638
1469
This will update the branch if its bound too, which means we have
1685
1524
# cant set that until we update the working trees last revision to be
1686
1525
# one from the new branch, because it will just get absorbed by the
1687
1526
# parent de-duplication logic.
1689
1528
# We MUST save it even if an error occurs, because otherwise the users
1690
1529
# local work is unreferenced and will appear to have been lost.
1694
1533
last_rev = self.get_parent_ids()[0]
1695
1534
except IndexError:
1697
if last_rev != self.branch.last_revision():
1698
# merge tree state up to new branch tip.
1535
last_rev = _mod_revision.NULL_REVISION
1536
if revision is None:
1537
revision = self.branch.last_revision()
1539
old_tip = old_tip or _mod_revision.NULL_REVISION
1541
if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1542
# the branch we are bound to was updated
1543
# merge those changes in first
1544
base_tree = self.basis_tree()
1545
other_tree = self.branch.repository.revision_tree(old_tip)
1546
nb_conflicts = merge.merge_inner(self.branch, other_tree,
1547
base_tree, this_tree=self,
1548
change_reporter=change_reporter,
1549
show_base=show_base)
1551
self.add_parent_tree((old_tip, other_tree))
1552
note('Rerun update after fixing the conflicts.')
1555
if last_rev != _mod_revision.ensure_null(revision):
1556
# the working tree is up to date with the branch
1557
# we can merge the specified revision from master
1558
to_tree = self.branch.repository.revision_tree(revision)
1559
to_root_id = to_tree.get_root_id()
1699
1561
basis = self.basis_tree()
1700
to_tree = self.branch.basis_tree()
1701
if basis.inventory.root is None:
1702
self.set_root_id(to_tree.inventory.root.file_id)
1703
result += merge.merge_inner(
1564
if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
1565
self.set_root_id(to_root_id)
1570
# determine the branch point
1571
graph = self.branch.repository.get_graph()
1572
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1574
base_tree = self.branch.repository.revision_tree(base_rev_id)
1576
nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1578
change_reporter=change_reporter,
1579
show_base=show_base)
1580
self.set_last_revision(revision)
1708
1581
# TODO - dedup parents list with things merged by pull ?
1709
1582
# reuse the tree we've updated to to set the basis:
1710
parent_trees = [(self.branch.last_revision(), to_tree)]
1583
parent_trees = [(revision, to_tree)]
1711
1584
merges = self.get_parent_ids()[1:]
1712
1585
# Ideally we ask the tree for the trees here, that way the working
1713
# tree can decide whether to give us teh entire tree or give us a
1586
# tree can decide whether to give us the entire tree or give us a
1714
1587
# lazy initialised tree. dirstate for instance will have the trees
1715
1588
# in ram already, whereas a last-revision + basis-inventory tree
1716
1589
# will not, but also does not need them when setting parents.
1717
1590
for parent in merges:
1718
1591
parent_trees.append(
1719
1592
(parent, self.branch.repository.revision_tree(parent)))
1720
if old_tip is not None:
1593
if not _mod_revision.is_null(old_tip):
1721
1594
parent_trees.append(
1722
1595
(old_tip, self.branch.repository.revision_tree(old_tip)))
1723
1596
self.set_parent_trees(parent_trees)
1724
1597
last_rev = parent_trees[0][0]
1600
def _write_hashcache_if_dirty(self):
1601
"""Write out the hashcache if it is dirty."""
1602
if self._hashcache.needs_write:
1604
self._hashcache.write()
1606
if e.errno not in (errno.EPERM, errno.EACCES):
1608
# TODO: jam 20061219 Should this be a warning? A single line
1609
# warning might be sufficient to let the user know what
1611
mutter('Could not write hashcache for %s\nError: %s',
1612
self._hashcache.cache_file_name(), e)
1614
def set_conflicts(self, arg):
1615
raise errors.UnsupportedOperation(self.set_conflicts, self)
1617
def add_conflicts(self, arg):
1618
raise errors.UnsupportedOperation(self.add_conflicts, self)
1620
def conflicts(self):
1621
raise NotImplementedError(self.conflicts)
1623
def walkdirs(self, prefix=""):
1624
"""Walk the directories of this tree.
1626
returns a generator which yields items in the form:
1627
((curren_directory_path, fileid),
1628
[(file1_path, file1_name, file1_kind, (lstat), file1_id,
1631
This API returns a generator, which is only valid during the current
1632
tree transaction - within a single lock_read or lock_write duration.
1634
If the tree is not locked, it may cause an error to be raised,
1635
depending on the tree implementation.
1637
disk_top = self.abspath(prefix)
1638
if disk_top.endswith('/'):
1639
disk_top = disk_top[:-1]
1640
top_strip_len = len(disk_top) + 1
1641
inventory_iterator = self._walkdirs(prefix)
1642
disk_iterator = osutils.walkdirs(disk_top, prefix)
1644
current_disk = disk_iterator.next()
1645
disk_finished = False
1647
if not (e.errno == errno.ENOENT or
1648
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
1651
disk_finished = True
1653
current_inv = inventory_iterator.next()
1654
inv_finished = False
1655
except StopIteration:
1658
while not inv_finished or not disk_finished:
1660
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1661
cur_disk_dir_content) = current_disk
1663
((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1664
cur_disk_dir_content) = ((None, None), None)
1665
if not disk_finished:
1666
# strip out .bzr dirs
1667
if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
1668
len(cur_disk_dir_content) > 0):
1669
# osutils.walkdirs can be made nicer -
1670
# yield the path-from-prefix rather than the pathjoined
1672
bzrdir_loc = bisect_left(cur_disk_dir_content,
1674
if (bzrdir_loc < len(cur_disk_dir_content)
1675
and self.bzrdir.is_control_filename(
1676
cur_disk_dir_content[bzrdir_loc][0])):
1677
# we dont yield the contents of, or, .bzr itself.
1678
del cur_disk_dir_content[bzrdir_loc]
1680
# everything is unknown
1683
# everything is missing
1686
direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
1688
# disk is before inventory - unknown
1689
dirblock = [(relpath, basename, kind, stat, None, None) for
1690
relpath, basename, kind, stat, top_path in
1691
cur_disk_dir_content]
1692
yield (cur_disk_dir_relpath, None), dirblock
1694
current_disk = disk_iterator.next()
1695
except StopIteration:
1696
disk_finished = True
1698
# inventory is before disk - missing.
1699
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
1700
for relpath, basename, dkind, stat, fileid, kind in
1702
yield (current_inv[0][0], current_inv[0][1]), dirblock
1704
current_inv = inventory_iterator.next()
1705
except StopIteration:
1708
# versioned present directory
1709
# merge the inventory and disk data together
1711
for relpath, subiterator in itertools.groupby(sorted(
1712
current_inv[1] + cur_disk_dir_content,
1713
key=operator.itemgetter(0)), operator.itemgetter(1)):
1714
path_elements = list(subiterator)
1715
if len(path_elements) == 2:
1716
inv_row, disk_row = path_elements
1717
# versioned, present file
1718
dirblock.append((inv_row[0],
1719
inv_row[1], disk_row[2],
1720
disk_row[3], inv_row[4],
1722
elif len(path_elements[0]) == 5:
1724
dirblock.append((path_elements[0][0],
1725
path_elements[0][1], path_elements[0][2],
1726
path_elements[0][3], None, None))
1727
elif len(path_elements[0]) == 6:
1728
# versioned, absent file.
1729
dirblock.append((path_elements[0][0],
1730
path_elements[0][1], 'unknown', None,
1731
path_elements[0][4], path_elements[0][5]))
1733
raise NotImplementedError('unreachable code')
1734
yield current_inv[0], dirblock
1736
current_inv = inventory_iterator.next()
1737
except StopIteration:
1740
current_disk = disk_iterator.next()
1741
except StopIteration:
1742
disk_finished = True
1744
def _walkdirs(self, prefix=""):
1745
"""Walk the directories of this tree.
1747
:prefix: is used as the directrory to start with.
1748
returns a generator which yields items in the form:
1749
((curren_directory_path, fileid),
1750
[(file1_path, file1_name, file1_kind, None, file1_id,
1753
raise NotImplementedError(self._walkdirs)
1755
@needs_tree_write_lock
1756
def auto_resolve(self):
1757
"""Automatically resolve text conflicts according to contents.
1759
Only text conflicts are auto_resolvable. Files with no conflict markers
1760
are considered 'resolved', because bzr always puts conflict markers
1761
into files that have text conflicts. The corresponding .THIS .BASE and
1762
.OTHER files are deleted, as per 'resolve'.
1763
:return: a tuple of ConflictLists: (un_resolved, resolved).
1765
un_resolved = _mod_conflicts.ConflictList()
1766
resolved = _mod_conflicts.ConflictList()
1767
conflict_re = re.compile('^(<{7}|={7}|>{7})')
1768
for conflict in self.conflicts():
1769
if (conflict.typestring != 'text conflict' or
1770
self.kind(conflict.file_id) != 'file'):
1771
un_resolved.append(conflict)
1773
my_file = open(self.id2abspath(conflict.file_id), 'rb')
1775
for line in my_file:
1776
if conflict_re.search(line):
1777
un_resolved.append(conflict)
1780
resolved.append(conflict)
1783
resolved.remove_files(self)
1784
self.set_conflicts(un_resolved)
1785
return un_resolved, resolved
1788
def _check(self, references):
1789
"""Check the tree for consistency.
1791
:param references: A dict with keys matching the items returned by
1792
self._get_check_refs(), and values from looking those keys up in
1795
tree_basis = self.basis_tree()
1796
tree_basis.lock_read()
1798
repo_basis = references[('trees', self.last_revision())]
1799
if len(list(repo_basis.iter_changes(tree_basis))) > 0:
1800
raise errors.BzrCheckError(
1801
"Mismatched basis inventory content.")
1806
def _validate(self):
1807
"""Validate internal structures.
1809
This is meant mostly for the test suite. To give it a chance to detect
1810
corruption after actions have occurred. The default implementation is a
1813
:return: None. An exception should be raised if there is an error.
1818
def check_state(self):
1819
"""Check that the working state is/isn't valid."""
1820
check_refs = self._get_check_refs()
1822
for ref in check_refs:
1825
refs[ref] = self.branch.repository.revision_tree(value)
1828
def reset_state(self, revision_ids=None):
1829
"""Reset the state of the working tree.
1831
This does a hard-reset to a last-known-good state. This is a way to
1832
fix if something got corrupted (like the .bzr/checkout/dirstate file)
1834
raise NotImplementedError(self.reset_state)
1836
def _get_rules_searcher(self, default_searcher):
1837
"""See Tree._get_rules_searcher."""
1838
if self._rules_searcher is None:
1839
self._rules_searcher = super(WorkingTree,
1840
self)._get_rules_searcher(default_searcher)
1841
return self._rules_searcher
1843
def get_shelf_manager(self):
1844
"""Return the ShelfManager for this WorkingTree."""
1845
from bzrlib.shelf import ShelfManager
1846
return ShelfManager(self, self._transport)
1849
class InventoryWorkingTree(WorkingTree,
1850
bzrlib.mutabletree.MutableInventoryTree):
1851
"""Base class for working trees that are inventory-oriented.
1853
The inventory is held in the `Branch` working-inventory, and the
1854
files are in a directory on disk.
1856
It is possible for a `WorkingTree` to have a filename which is
1857
not listed in the Inventory and vice versa.
1860
def __init__(self, basedir='.',
1861
branch=DEPRECATED_PARAMETER,
1863
_control_files=None,
1867
"""Construct a InventoryWorkingTree instance. This is not a public API.
1869
:param branch: A branch to override probing for the branch.
1871
super(InventoryWorkingTree, self).__init__(basedir=basedir,
1872
branch=branch, _control_files=_control_files, _internal=_internal,
1873
_format=_format, _bzrdir=_bzrdir)
1875
if _inventory is None:
1876
# This will be acquired on lock_read() or lock_write()
1877
self._inventory_is_modified = False
1878
self._inventory = None
1726
# the working tree had the same last-revision as the master
1727
# branch did. We may still have pivot local work from the local
1728
# branch into old_tip:
1729
if old_tip is not None:
1730
self.add_parent_tree_id(old_tip)
1731
if old_tip and old_tip != last_rev:
1732
# our last revision was not the prior branch last revision
1733
# and we have converted that last revision to a pending merge.
1734
# base is somewhere between the branch tip now
1735
# and the now pending merge
1736
from bzrlib.revision import common_ancestor
1738
base_rev_id = common_ancestor(self.branch.last_revision(),
1740
self.branch.repository)
1741
except errors.NoCommonAncestor:
1743
base_tree = self.branch.repository.revision_tree(base_rev_id)
1744
other_tree = self.branch.repository.revision_tree(old_tip)
1745
result += merge.merge_inner(
1880
# the caller of __init__ has provided an inventory,
1881
# we assume they know what they are doing - as its only
1882
# the Format factory and creation methods that are
1883
# permitted to do this.
1884
self._set_inventory(_inventory, dirty=False)
1886
def _set_inventory(self, inv, dirty):
1887
"""Set the internal cached inventory.
1889
:param inv: The inventory to set.
1890
:param dirty: A boolean indicating whether the inventory is the same
1891
logical inventory as whats on disk. If True the inventory is not
1892
the same and should be written to disk or data will be lost, if
1893
False then the inventory is the same as that on disk and any
1894
serialisation would be unneeded overhead.
1896
self._inventory = inv
1897
self._inventory_is_modified = dirty
1899
def _serialize(self, inventory, out_file):
1900
xml5.serializer_v5.write_inventory(self._inventory, out_file,
1903
def _deserialize(selt, in_file):
1904
return xml5.serializer_v5.read_inventory(in_file)
1752
1906
@needs_tree_write_lock
1753
1907
def _write_inventory(self, inv):
1755
1909
self._set_inventory(inv, dirty=True)
1758
def set_conflicts(self, arg):
1759
raise UnsupportedOperation(self.set_conflicts, self)
1761
def add_conflicts(self, arg):
1762
raise UnsupportedOperation(self.add_conflicts, self)
1765
def conflicts(self):
1766
conflicts = _mod_conflicts.ConflictList()
1767
for conflicted in self._iter_conflicts():
1912
# XXX: This method should be deprecated in favour of taking in a proper
1913
# new Inventory object.
1914
@needs_tree_write_lock
1915
def set_inventory(self, new_inventory_list):
1916
from bzrlib.inventory import (Inventory,
1920
inv = Inventory(self.get_root_id())
1921
for path, file_id, parent, kind in new_inventory_list:
1922
name = os.path.basename(path)
1925
# fixme, there should be a factory function inv,add_??
1926
if kind == 'directory':
1927
inv.add(InventoryDirectory(file_id, name, parent))
1928
elif kind == 'file':
1929
inv.add(InventoryFile(file_id, name, parent))
1930
elif kind == 'symlink':
1931
inv.add(InventoryLink(file_id, name, parent))
1933
raise errors.BzrError("unknown kind %r" % kind)
1934
self._write_inventory(inv)
1936
def _write_basis_inventory(self, xml):
1937
"""Write the basis inventory XML to the basis-inventory file"""
1938
path = self._basis_inventory_name()
1940
self._transport.put_file(path, sio,
1941
mode=self.bzrdir._get_file_mode())
1943
def _reset_data(self):
1944
"""Reset transient data that cannot be revalidated."""
1945
self._inventory_is_modified = False
1946
f = self._transport.get('inventory')
1948
result = self._deserialize(f)
1951
self._set_inventory(result, dirty=False)
1953
def _set_root_id(self, file_id):
1954
"""Set the root id for this tree, in a format specific manner.
1956
:param file_id: The file id to assign to the root. It must not be
1957
present in the current inventory or an error will occur. It must
1958
not be None, but rather a valid file id.
1960
inv = self._inventory
1961
orig_root_id = inv.root.file_id
1962
# TODO: it might be nice to exit early if there was nothing
1963
# to do, saving us from trigger a sync on unlock.
1964
self._inventory_is_modified = True
1965
# we preserve the root inventory entry object, but
1966
# unlinkit from the byid index
1967
del inv._byid[inv.root.file_id]
1968
inv.root.file_id = file_id
1969
# and link it into the index with the new changed id.
1970
inv._byid[inv.root.file_id] = inv.root
1971
# and finally update all children to reference the new id.
1972
# XXX: this should be safe to just look at the root.children
1973
# list, not the WHOLE INVENTORY.
1976
if entry.parent_id == orig_root_id:
1977
entry.parent_id = inv.root.file_id
1979
def all_file_ids(self):
1980
"""See Tree.iter_all_file_ids"""
1981
return set(self.inventory)
1983
def _cache_basis_inventory(self, new_revision):
1984
"""Cache new_revision as the basis inventory."""
1985
# TODO: this should allow the ready-to-use inventory to be passed in,
1986
# as commit already has that ready-to-use [while the format is the
1989
# this double handles the inventory - unpack and repack -
1990
# but is easier to understand. We can/should put a conditional
1991
# in here based on whether the inventory is in the latest format
1992
# - perhaps we should repack all inventories on a repository
1994
# the fast path is to copy the raw xml from the repository. If the
1995
# xml contains 'revision_id="', then we assume the right
1996
# revision_id is set. We must check for this full string, because a
1997
# root node id can legitimately look like 'revision_id' but cannot
1999
xml = self.branch.repository._get_inventory_xml(new_revision)
2000
firstline = xml.split('\n', 1)[0]
2001
if (not 'revision_id="' in firstline or
2002
'format="7"' not in firstline):
2003
inv = self.branch.repository._serializer.read_inventory_from_string(
2005
xml = self._create_basis_xml_from_inventory(new_revision, inv)
2006
self._write_basis_inventory(xml)
2007
except (errors.NoSuchRevision, errors.RevisionNotPresent):
2010
def _basis_inventory_name(self):
2011
return 'basis-inventory-cache'
2013
def _create_basis_xml_from_inventory(self, revision_id, inventory):
2014
"""Create the text that will be saved in basis-inventory"""
2015
inventory.revision_id = revision_id
2016
return xml7.serializer_v7.write_inventory_to_string(inventory)
2018
def read_basis_inventory(self):
2019
"""Read the cached basis inventory."""
2020
path = self._basis_inventory_name()
2021
return self._transport.get_bytes(path)
2024
def read_working_inventory(self):
2025
"""Read the working inventory.
2027
:raises errors.InventoryModified: read_working_inventory will fail
2028
when the current in memory inventory has been modified.
2030
# conceptually this should be an implementation detail of the tree.
2031
# XXX: Deprecate this.
2032
# ElementTree does its own conversion from UTF-8, so open in
2034
if self._inventory_is_modified:
2035
raise errors.InventoryModified(self)
2036
f = self._transport.get('inventory')
2038
result = self._deserialize(f)
2041
self._set_inventory(result, dirty=False)
2045
def get_root_id(self):
2046
"""Return the id of this trees root"""
2047
return self._inventory.root.file_id
2049
def has_id(self, file_id):
2050
# files that have been deleted are excluded
2051
inv = self.inventory
2052
if not inv.has_id(file_id):
2054
path = inv.id2path(file_id)
2055
return osutils.lexists(self.abspath(path))
2057
def has_or_had_id(self, file_id):
2058
if file_id == self.inventory.root.file_id:
2060
return self.inventory.has_id(file_id)
2062
__contains__ = has_id
2064
# should be deprecated - this is slow and in any case treating them as a
2065
# container is (we now know) bad style -- mbp 20070302
2066
## @deprecated_method(zero_fifteen)
2068
"""Iterate through file_ids for this tree.
2070
file_ids are in a WorkingTree if they are in the working inventory
2071
and the working file exists.
2073
inv = self._inventory
2074
for path, ie in inv.iter_entries():
2075
if osutils.lexists(self.abspath(path)):
2078
@needs_tree_write_lock
2079
def set_last_revision(self, new_revision):
2080
"""Change the last revision in the working tree."""
2081
if self._change_last_revision(new_revision):
2082
self._cache_basis_inventory(new_revision)
2084
@needs_tree_write_lock
2085
def reset_state(self, revision_ids=None):
2086
"""Reset the state of the working tree.
2088
This does a hard-reset to a last-known-good state. This is a way to
2089
fix if something got corrupted (like the .bzr/checkout/dirstate file)
2091
if revision_ids is None:
2092
revision_ids = self.get_parent_ids()
2093
if not revision_ids:
2094
rt = self.branch.repository.revision_tree(
2095
_mod_revision.NULL_REVISION)
2097
rt = self.branch.repository.revision_tree(revision_ids[0])
2098
self._write_inventory(rt.inventory)
2099
self.set_parent_ids(revision_ids)
2102
"""Write the in memory inventory to disk."""
2103
# TODO: Maybe this should only write on dirty ?
2104
if self._control_files._lock_mode != 'w':
2105
raise errors.NotWriteLocked(self)
2107
self._serialize(self._inventory, sio)
2109
self._transport.put_file('inventory', sio,
2110
mode=self.bzrdir._get_file_mode())
2111
self._inventory_is_modified = False
2114
def get_file_sha1(self, file_id, path=None, stat_value=None):
2116
path = self._inventory.id2path(file_id)
2117
return self._hashcache.get_sha1(path, stat_value)
2119
def get_file_mtime(self, file_id, path=None):
2120
"""See Tree.get_file_mtime."""
2122
path = self.inventory.id2path(file_id)
2123
return os.lstat(self.abspath(path)).st_mtime
2125
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2126
file_id = self.path2id(path)
2128
# For unversioned files on win32, we just assume they are not
2131
return self._inventory[file_id].executable
2133
def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
2134
mode = stat_result.st_mode
2135
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2137
if not supports_executable():
2138
def is_executable(self, file_id, path=None):
2139
return self._inventory[file_id].executable
2141
_is_executable_from_path_and_stat = \
2142
_is_executable_from_path_and_stat_from_basis
2144
def is_executable(self, file_id, path=None):
2146
path = self.id2path(file_id)
2147
mode = os.lstat(self.abspath(path)).st_mode
2148
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
2150
_is_executable_from_path_and_stat = \
2151
_is_executable_from_path_and_stat_from_stat
2153
@needs_tree_write_lock
2154
def _add(self, files, ids, kinds):
2155
"""See MutableTree._add."""
2156
# TODO: Re-adding a file that is removed in the working copy
2157
# should probably put it back with the previous ID.
2158
# the read and write working inventory should not occur in this
2159
# function - they should be part of lock_write and unlock.
2160
inv = self.inventory
2161
for f, file_id, kind in zip(files, ids, kinds):
2163
inv.add_path(f, kind=kind)
2165
inv.add_path(f, kind=kind, file_id=file_id)
2166
self._inventory_is_modified = True
2168
def revision_tree(self, revision_id):
2169
"""See WorkingTree.revision_id."""
2170
if revision_id == self.last_revision():
1770
if file_kind(self.abspath(conflicted)) != "file":
2172
xml = self.read_basis_inventory()
1772
2173
except errors.NoSuchFile:
1775
for suffix in ('.THIS', '.OTHER'):
2177
inv = xml7.serializer_v7.read_inventory_from_string(xml)
2178
# dont use the repository revision_tree api because we want
2179
# to supply the inventory.
2180
if inv.revision_id == revision_id:
2181
return revisiontree.InventoryRevisionTree(
2182
self.branch.repository, inv, revision_id)
2183
except errors.BadInventoryFormat:
2185
# raise if there was no inventory, or if we read the wrong inventory.
2186
raise errors.NoSuchRevisionInTree(self, revision_id)
2189
def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
2190
"""See Tree.annotate_iter
2192
This implementation will use the basis tree implementation if possible.
2193
Lines not in the basis are attributed to CURRENT_REVISION
2195
If there are pending merges, lines added by those merges will be
2196
incorrectly attributed to CURRENT_REVISION (but after committing, the
2197
attribution will be correct).
2199
maybe_file_parent_keys = []
2200
for parent_id in self.get_parent_ids():
2202
parent_tree = self.revision_tree(parent_id)
2203
except errors.NoSuchRevisionInTree:
2204
parent_tree = self.branch.repository.revision_tree(parent_id)
2205
parent_tree.lock_read()
2207
if file_id not in parent_tree:
2209
ie = parent_tree.inventory[file_id]
2210
if ie.kind != 'file':
2211
# Note: this is slightly unnecessary, because symlinks and
2212
# directories have a "text" which is the empty text, and we
2213
# know that won't mess up annotations. But it seems cleaner
2215
parent_text_key = (file_id, ie.revision)
2216
if parent_text_key not in maybe_file_parent_keys:
2217
maybe_file_parent_keys.append(parent_text_key)
2219
parent_tree.unlock()
2220
graph = _mod_graph.Graph(self.branch.repository.texts)
2221
heads = graph.heads(maybe_file_parent_keys)
2222
file_parent_keys = []
2223
for key in maybe_file_parent_keys:
2225
file_parent_keys.append(key)
2227
# Now we have the parents of this content
2228
annotator = self.branch.repository.texts.get_annotator()
2229
text = self.get_file_text(file_id)
2230
this_key =(file_id, default_revision)
2231
annotator.add_special_text(this_key, file_parent_keys, text)
2232
annotations = [(key[-1], line)
2233
for key, line in annotator.annotate_flat(this_key)]
2237
def merge_modified(self):
2238
"""Return a dictionary of files modified by a merge.
2240
The list is initialized by WorkingTree.set_merge_modified, which is
2241
typically called after we make some automatic updates to the tree
2244
This returns a map of file_id->sha1, containing only files which are
2245
still in the working inventory and have that text hash.
2248
hashfile = self._transport.get('merge-hashes')
2249
except errors.NoSuchFile:
2254
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
2255
raise errors.MergeModifiedFormatError()
2256
except StopIteration:
2257
raise errors.MergeModifiedFormatError()
2258
for s in _mod_rio.RioReader(hashfile):
2259
# RioReader reads in Unicode, so convert file_ids back to utf8
2260
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2261
if file_id not in self.inventory:
2263
text_hash = s.get("hash")
2264
if text_hash == self.get_file_sha1(file_id):
2265
merge_hashes[file_id] = text_hash
2271
def subsume(self, other_tree):
2272
def add_children(inventory, entry):
2273
for child_entry in entry.children.values():
2274
inventory._byid[child_entry.file_id] = child_entry
2275
if child_entry.kind == 'directory':
2276
add_children(inventory, child_entry)
2277
if other_tree.get_root_id() == self.get_root_id():
2278
raise errors.BadSubsumeSource(self, other_tree,
2279
'Trees have the same root')
2281
other_tree_path = self.relpath(other_tree.basedir)
2282
except errors.PathNotChild:
2283
raise errors.BadSubsumeSource(self, other_tree,
2284
'Tree is not contained by the other')
2285
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
2286
if new_root_parent is None:
2287
raise errors.BadSubsumeSource(self, other_tree,
2288
'Parent directory is not versioned.')
2289
# We need to ensure that the result of a fetch will have a
2290
# versionedfile for the other_tree root, and only fetching into
2291
# RepositoryKnit2 guarantees that.
2292
if not self.branch.repository.supports_rich_root():
2293
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
2294
other_tree.lock_tree_write()
2296
new_parents = other_tree.get_parent_ids()
2297
other_root = other_tree.inventory.root
2298
other_root.parent_id = new_root_parent
2299
other_root.name = osutils.basename(other_tree_path)
2300
self.inventory.add(other_root)
2301
add_children(self.inventory, other_root)
2302
self._write_inventory(self.inventory)
2303
# normally we don't want to fetch whole repositories, but i think
2304
# here we really do want to consolidate the whole thing.
2305
for parent_id in other_tree.get_parent_ids():
2306
self.branch.fetch(other_tree.branch, parent_id)
2307
self.add_parent_tree_id(parent_id)
2310
other_tree.bzrdir.retire_bzrdir()
2312
@needs_tree_write_lock
2313
def extract(self, file_id, format=None):
2314
"""Extract a subtree from this tree.
2316
A new branch will be created, relative to the path for this tree.
2320
segments = osutils.splitpath(path)
2321
transport = self.branch.bzrdir.root_transport
2322
for name in segments:
2323
transport = transport.clone(name)
2324
transport.ensure_base()
2327
sub_path = self.id2path(file_id)
2328
branch_transport = mkdirs(sub_path)
2330
format = self.bzrdir.cloning_metadir()
2331
branch_transport.ensure_base()
2332
branch_bzrdir = format.initialize_on_transport(branch_transport)
2334
repo = branch_bzrdir.find_repository()
2335
except errors.NoRepositoryPresent:
2336
repo = branch_bzrdir.create_repository()
2337
if not repo.supports_rich_root():
2338
raise errors.RootNotRich()
2339
new_branch = branch_bzrdir.create_branch()
2340
new_branch.pull(self.branch)
2341
for parent_id in self.get_parent_ids():
2342
new_branch.fetch(self.branch, parent_id)
2343
tree_transport = self.bzrdir.root_transport.clone(sub_path)
2344
if tree_transport.base != branch_transport.base:
2345
tree_bzrdir = format.initialize_on_transport(tree_transport)
2346
branch.BranchReferenceFormat().initialize(tree_bzrdir,
2347
target_branch=new_branch)
2349
tree_bzrdir = branch_bzrdir
2350
wt = tree_bzrdir.create_workingtree(_mod_revision.NULL_REVISION)
2351
wt.set_parent_ids(self.get_parent_ids())
2352
my_inv = self.inventory
2353
child_inv = inventory.Inventory(root_id=None)
2354
new_root = my_inv[file_id]
2355
my_inv.remove_recursive_id(file_id)
2356
new_root.parent_id = None
2357
child_inv.add(new_root)
2358
self._write_inventory(my_inv)
2359
wt._write_inventory(child_inv)
2362
def list_files(self, include_root=False, from_dir=None, recursive=True):
2363
"""List all files as (path, class, kind, id, entry).
2365
Lists, but does not descend into unversioned directories.
2366
This does not include files that have been deleted in this
2367
tree. Skips the control directory.
2369
:param include_root: if True, return an entry for the root
2370
:param from_dir: start from this directory or None for the root
2371
:param recursive: whether to recurse into subdirectories or not
2373
# list_files is an iterator, so @needs_read_lock doesn't work properly
2374
# with it. So callers should be careful to always read_lock the tree.
2375
if not self.is_locked():
2376
raise errors.ObjectNotLocked(self)
2378
inv = self.inventory
2379
if from_dir is None and include_root is True:
2380
yield ('', 'V', 'directory', inv.root.file_id, inv.root)
2381
# Convert these into local objects to save lookup times
2382
pathjoin = osutils.pathjoin
2383
file_kind = self._kind
2385
# transport.base ends in a slash, we want the piece
2386
# between the last two slashes
2387
transport_base_dir = self.bzrdir.transport.base.rsplit('/', 2)[1]
2389
fk_entries = {'directory':TreeDirectory, 'file':TreeFile, 'symlink':TreeLink}
2391
# directory file_id, relative path, absolute path, reverse sorted children
2392
if from_dir is not None:
2393
from_dir_id = inv.path2id(from_dir)
2394
if from_dir_id is None:
2395
# Directory not versioned
2397
from_dir_abspath = pathjoin(self.basedir, from_dir)
2399
from_dir_id = inv.root.file_id
2400
from_dir_abspath = self.basedir
2401
children = os.listdir(from_dir_abspath)
2403
# jam 20060527 The kernel sized tree seems equivalent whether we
2404
# use a deque and popleft to keep them sorted, or if we use a plain
2405
# list and just reverse() them.
2406
children = collections.deque(children)
2407
stack = [(from_dir_id, u'', from_dir_abspath, children)]
2409
from_dir_id, from_dir_relpath, from_dir_abspath, children = stack[-1]
2412
f = children.popleft()
2413
## TODO: If we find a subdirectory with its own .bzr
2414
## directory, then that is a separate tree and we
2415
## should exclude it.
2417
# the bzrdir for this tree
2418
if transport_base_dir == f:
2421
# we know that from_dir_relpath and from_dir_abspath never end in a slash
2422
# and 'f' doesn't begin with one, we can do a string op, rather
2423
# than the checks of pathjoin(), all relative paths will have an extra slash
2425
fp = from_dir_relpath + '/' + f
2428
fap = from_dir_abspath + '/' + f
2430
dir_ie = inv[from_dir_id]
2431
if dir_ie.kind == 'directory':
2432
f_ie = dir_ie.children.get(f)
2437
elif self.is_ignored(fp[1:]):
2440
# we may not have found this file, because of a unicode
2441
# issue, or because the directory was actually a symlink.
2442
f_norm, can_access = osutils.normalized_filename(f)
2443
if f == f_norm or not can_access:
2444
# No change, so treat this file normally
2447
# this file can be accessed by a normalized path
2448
# check again if it is versioned
2449
# these lines are repeated here for performance
2451
fp = from_dir_relpath + '/' + f
2452
fap = from_dir_abspath + '/' + f
2453
f_ie = inv.get_child(from_dir_id, f)
2456
elif self.is_ignored(fp[1:]):
2463
# make a last minute entry
2465
yield fp[1:], c, fk, f_ie.file_id, f_ie
1777
kind = file_kind(self.abspath(conflicted+suffix))
1780
except errors.NoSuchFile:
1784
ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1785
conflicts.append(_mod_conflicts.Conflict.factory(ctype,
1787
file_id=self.path2id(conflicted)))
1791
class WorkingTree2(WorkingTree):
1792
"""This is the Format 2 working tree.
1794
This was the first weave based working tree.
1795
- uses os locks for locking.
1796
- uses the branch last-revision.
1799
def lock_tree_write(self):
1800
"""See WorkingTree.lock_tree_write().
1802
In Format2 WorkingTrees we have a single lock for the branch and tree
1803
so lock_tree_write() degrades to lock_write().
2468
yield fp[1:], c, fk, None, fk_entries[fk]()
2470
yield fp[1:], c, fk, None, TreeEntry()
2473
if fk != 'directory':
2476
# But do this child first if recursing down
2478
new_children = os.listdir(fap)
2480
new_children = collections.deque(new_children)
2481
stack.append((f_ie.file_id, fp, fap, new_children))
2482
# Break out of inner loop,
2483
# so that we start outer loop with child
2486
# if we finished all children, pop it off the stack
2489
@needs_tree_write_lock
2490
def move(self, from_paths, to_dir=None, after=False):
2493
to_dir must exist in the inventory.
2495
If to_dir exists and is a directory, the files are moved into
2496
it, keeping their old names.
2498
Note that to_dir is only the last component of the new name;
2499
this doesn't change the directory.
2501
For each entry in from_paths the move mode will be determined
2504
The first mode moves the file in the filesystem and updates the
2505
inventory. The second mode only updates the inventory without
2506
touching the file on the filesystem.
2508
move uses the second mode if 'after == True' and the target is not
2509
versioned but present in the working tree.
2511
move uses the second mode if 'after == False' and the source is
2512
versioned but no longer in the working tree, and the target is not
2513
versioned but present in the working tree.
2515
move uses the first mode if 'after == False' and the source is
2516
versioned and present in the working tree, and the target is not
2517
versioned and not present in the working tree.
2519
Everything else results in an error.
2521
This returns a list of (from_path, to_path) pairs for each
2522
entry that is moved.
1805
self.branch.lock_write()
2527
# check for deprecated use of signature
2529
raise TypeError('You must supply a target directory')
2530
# check destination directory
2531
if isinstance(from_paths, basestring):
2533
inv = self.inventory
2534
to_abs = self.abspath(to_dir)
2535
if not isdir(to_abs):
2536
raise errors.BzrMoveFailedError('',to_dir,
2537
errors.NotADirectory(to_abs))
2538
if not self.has_filename(to_dir):
2539
raise errors.BzrMoveFailedError('',to_dir,
2540
errors.NotInWorkingDirectory(to_dir))
2541
to_dir_id = inv.path2id(to_dir)
2542
if to_dir_id is None:
2543
raise errors.BzrMoveFailedError('',to_dir,
2544
errors.NotVersionedError(path=to_dir))
2546
to_dir_ie = inv[to_dir_id]
2547
if to_dir_ie.kind != 'directory':
2548
raise errors.BzrMoveFailedError('',to_dir,
2549
errors.NotADirectory(to_abs))
2551
# create rename entries and tuples
2552
for from_rel in from_paths:
2553
from_tail = splitpath(from_rel)[-1]
2554
from_id = inv.path2id(from_rel)
2556
raise errors.BzrMoveFailedError(from_rel,to_dir,
2557
errors.NotVersionedError(path=from_rel))
2559
from_entry = inv[from_id]
2560
from_parent_id = from_entry.parent_id
2561
to_rel = pathjoin(to_dir, from_tail)
2562
rename_entry = InventoryWorkingTree._RenameEntry(
2565
from_tail=from_tail,
2566
from_parent_id=from_parent_id,
2567
to_rel=to_rel, to_tail=from_tail,
2568
to_parent_id=to_dir_id)
2569
rename_entries.append(rename_entry)
2570
rename_tuples.append((from_rel, to_rel))
2572
# determine which move mode to use. checks also for movability
2573
rename_entries = self._determine_mv_mode(rename_entries, after)
2575
original_modified = self._inventory_is_modified
1807
return self._control_files.lock_write()
2578
self._inventory_is_modified = True
2579
self._move(rename_entries)
1809
self.branch.unlock()
2581
# restore the inventory on error
2582
self._inventory_is_modified = original_modified
1813
# we share control files:
1814
if self._control_files._lock_count == 3:
1815
# _inventory_is_modified is always False during a read lock.
1816
if self._inventory_is_modified:
1818
if self._hashcache.needs_write:
1819
self._hashcache.write()
1820
# reverse order of locking.
1822
return self._control_files.unlock()
1824
self.branch.unlock()
1827
class WorkingTree3(WorkingTree):
2584
self._write_inventory(inv)
2585
return rename_tuples
2587
@needs_tree_write_lock
2588
def rename_one(self, from_rel, to_rel, after=False):
2591
This can change the directory or the filename or both.
2593
rename_one has several 'modes' to work. First, it can rename a physical
2594
file and change the file_id. That is the normal mode. Second, it can
2595
only change the file_id without touching any physical file.
2597
rename_one uses the second mode if 'after == True' and 'to_rel' is not
2598
versioned but present in the working tree.
2600
rename_one uses the second mode if 'after == False' and 'from_rel' is
2601
versioned but no longer in the working tree, and 'to_rel' is not
2602
versioned but present in the working tree.
2604
rename_one uses the first mode if 'after == False' and 'from_rel' is
2605
versioned and present in the working tree, and 'to_rel' is not
2606
versioned and not present in the working tree.
2608
Everything else results in an error.
2610
inv = self.inventory
2613
# create rename entries and tuples
2614
from_tail = splitpath(from_rel)[-1]
2615
from_id = inv.path2id(from_rel)
2617
# if file is missing in the inventory maybe it's in the basis_tree
2618
basis_tree = self.branch.basis_tree()
2619
from_id = basis_tree.path2id(from_rel)
2621
raise errors.BzrRenameFailedError(from_rel,to_rel,
2622
errors.NotVersionedError(path=from_rel))
2623
# put entry back in the inventory so we can rename it
2624
from_entry = basis_tree.inventory[from_id].copy()
2627
from_entry = inv[from_id]
2628
from_parent_id = from_entry.parent_id
2629
to_dir, to_tail = os.path.split(to_rel)
2630
to_dir_id = inv.path2id(to_dir)
2631
rename_entry = InventoryWorkingTree._RenameEntry(from_rel=from_rel,
2633
from_tail=from_tail,
2634
from_parent_id=from_parent_id,
2635
to_rel=to_rel, to_tail=to_tail,
2636
to_parent_id=to_dir_id)
2637
rename_entries.append(rename_entry)
2639
# determine which move mode to use. checks also for movability
2640
rename_entries = self._determine_mv_mode(rename_entries, after)
2642
# check if the target changed directory and if the target directory is
2644
if to_dir_id is None:
2645
raise errors.BzrMoveFailedError(from_rel,to_rel,
2646
errors.NotVersionedError(path=to_dir))
2648
# all checks done. now we can continue with our actual work
2649
mutter('rename_one:\n'
2654
' to_dir_id {%s}\n',
2655
from_id, from_rel, to_rel, to_dir, to_dir_id)
2657
self._move(rename_entries)
2658
self._write_inventory(inv)
2660
class _RenameEntry(object):
2661
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2662
to_rel, to_tail, to_parent_id, only_change_inv=False):
2663
self.from_rel = from_rel
2664
self.from_id = from_id
2665
self.from_tail = from_tail
2666
self.from_parent_id = from_parent_id
2667
self.to_rel = to_rel
2668
self.to_tail = to_tail
2669
self.to_parent_id = to_parent_id
2670
self.only_change_inv = only_change_inv
2672
def _determine_mv_mode(self, rename_entries, after=False):
2673
"""Determines for each from-to pair if both inventory and working tree
2674
or only the inventory has to be changed.
2676
Also does basic plausability tests.
2678
inv = self.inventory
2680
for rename_entry in rename_entries:
2681
# store to local variables for easier reference
2682
from_rel = rename_entry.from_rel
2683
from_id = rename_entry.from_id
2684
to_rel = rename_entry.to_rel
2685
to_id = inv.path2id(to_rel)
2686
only_change_inv = False
2688
# check the inventory for source and destination
2690
raise errors.BzrMoveFailedError(from_rel,to_rel,
2691
errors.NotVersionedError(path=from_rel))
2692
if to_id is not None:
2693
raise errors.BzrMoveFailedError(from_rel,to_rel,
2694
errors.AlreadyVersionedError(path=to_rel))
2696
# try to determine the mode for rename (only change inv or change
2697
# inv and file system)
2699
if not self.has_filename(to_rel):
2700
raise errors.BzrMoveFailedError(from_id,to_rel,
2701
errors.NoSuchFile(path=to_rel,
2702
extra="New file has not been created yet"))
2703
only_change_inv = True
2704
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
2705
only_change_inv = True
2706
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
2707
only_change_inv = False
2708
elif (not self.case_sensitive
2709
and from_rel.lower() == to_rel.lower()
2710
and self.has_filename(from_rel)):
2711
only_change_inv = False
2713
# something is wrong, so lets determine what exactly
2714
if not self.has_filename(from_rel) and \
2715
not self.has_filename(to_rel):
2716
raise errors.BzrRenameFailedError(from_rel,to_rel,
2717
errors.PathsDoNotExist(paths=(str(from_rel),
2720
raise errors.RenameFailedFilesExist(from_rel, to_rel)
2721
rename_entry.only_change_inv = only_change_inv
2722
return rename_entries
2724
def _move(self, rename_entries):
2725
"""Moves a list of files.
2727
Depending on the value of the flag 'only_change_inv', the
2728
file will be moved on the file system or not.
2730
inv = self.inventory
2733
for entry in rename_entries:
2735
self._move_entry(entry)
2737
self._rollback_move(moved)
2741
def _rollback_move(self, moved):
2742
"""Try to rollback a previous move in case of an filesystem error."""
2743
inv = self.inventory
2746
self._move_entry(WorkingTree._RenameEntry(
2747
entry.to_rel, entry.from_id,
2748
entry.to_tail, entry.to_parent_id, entry.from_rel,
2749
entry.from_tail, entry.from_parent_id,
2750
entry.only_change_inv))
2751
except errors.BzrMoveFailedError, e:
2752
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
2753
" The working tree is in an inconsistent state."
2754
" Please consider doing a 'bzr revert'."
2755
" Error message is: %s" % e)
2757
def _move_entry(self, entry):
2758
inv = self.inventory
2759
from_rel_abs = self.abspath(entry.from_rel)
2760
to_rel_abs = self.abspath(entry.to_rel)
2761
if from_rel_abs == to_rel_abs:
2762
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
2763
"Source and target are identical.")
2765
if not entry.only_change_inv:
2767
osutils.rename(from_rel_abs, to_rel_abs)
2769
raise errors.BzrMoveFailedError(entry.from_rel,
2771
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
2773
@needs_tree_write_lock
2774
def unversion(self, file_ids):
2775
"""Remove the file ids in file_ids from the current versioned set.
2777
When a file_id is unversioned, all of its children are automatically
2780
:param file_ids: The file ids to stop versioning.
2781
:raises: NoSuchId if any fileid is not currently versioned.
2783
for file_id in file_ids:
2784
if file_id not in self._inventory:
2785
raise errors.NoSuchId(self, file_id)
2786
for file_id in file_ids:
2787
if self._inventory.has_id(file_id):
2788
self._inventory.remove_recursive_id(file_id)
2790
# in the future this should just set a dirty bit to wait for the
2791
# final unlock. However, until all methods of workingtree start
2792
# with the current in -memory inventory rather than triggering
2793
# a read, it is more complex - we need to teach read_inventory
2794
# to know when to read, and when to not read first... and possibly
2795
# to save first when the in memory one may be corrupted.
2796
# so for now, we just only write it if it is indeed dirty.
2798
self._write_inventory(self._inventory)
2800
def stored_kind(self, file_id):
2801
"""See Tree.stored_kind"""
2802
return self.inventory[file_id].kind
2805
"""Yield all unversioned files in this WorkingTree.
2807
If there are any unversioned directories then only the directory is
2808
returned, not all its children. But if there are unversioned files
2809
under a versioned subdirectory, they are returned.
2811
Currently returned depth-first, sorted by name within directories.
2812
This is the same order used by 'osutils.walkdirs'.
2814
## TODO: Work from given directory downwards
2815
for path, dir_entry in self.inventory.directories():
2816
# mutter("search for unknowns in %r", path)
2817
dirabs = self.abspath(path)
2818
if not isdir(dirabs):
2819
# e.g. directory deleted
2823
for subf in os.listdir(dirabs):
2824
if self.bzrdir.is_control_filename(subf):
2826
if subf not in dir_entry.children:
2829
can_access) = osutils.normalized_filename(subf)
2830
except UnicodeDecodeError:
2831
path_os_enc = path.encode(osutils._fs_enc)
2832
relpath = path_os_enc + '/' + subf
2833
raise errors.BadFilenameEncoding(relpath,
2835
if subf_norm != subf and can_access:
2836
if subf_norm not in dir_entry.children:
2837
fl.append(subf_norm)
2843
subp = pathjoin(path, subf)
2846
def _walkdirs(self, prefix=""):
2847
"""Walk the directories of this tree.
2849
:prefix: is used as the directrory to start with.
2850
returns a generator which yields items in the form:
2851
((curren_directory_path, fileid),
2852
[(file1_path, file1_name, file1_kind, None, file1_id,
2855
_directory = 'directory'
2856
# get the root in the inventory
2857
inv = self.inventory
2858
top_id = inv.path2id(prefix)
2862
pending = [(prefix, '', _directory, None, top_id, None)]
2865
currentdir = pending.pop()
2866
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2867
top_id = currentdir[4]
2869
relroot = currentdir[0] + '/'
2872
# FIXME: stash the node in pending
2874
if entry.kind == 'directory':
2875
for name, child in entry.sorted_children():
2876
dirblock.append((relroot + name, name, child.kind, None,
2877
child.file_id, child.kind
2879
yield (currentdir[0], entry.file_id), dirblock
2880
# push the user specified dirs from dirblock
2881
for dir in reversed(dirblock):
2882
if dir[2] == _directory:
2886
class WorkingTree3(InventoryWorkingTree):
1828
2887
"""This is the Format 3 working tree.
1830
2889
This differs from the base WorkingTree by: