443
463
The path may be absolute or relative. If its a relative path it is
444
464
interpreted relative to the python current working directory.
446
return osutils.relpath(self.basedir, path)
466
return relpath(self.basedir, path)
448
468
def has_filename(self, filename):
449
469
return osutils.lexists(self.abspath(filename))
451
471
def get_file(self, file_id):
452
file_id = osutils.safe_file_id(file_id)
453
472
return self.get_file_byname(self.id2path(file_id))
455
474
def get_file_text(self, file_id):
456
file_id = osutils.safe_file_id(file_id)
457
475
return self.get_file(file_id).read()
459
477
def get_file_byname(self, filename):
460
478
return file(self.abspath(filename), 'rb')
463
def annotate_iter(self, file_id):
464
"""See Tree.annotate_iter
466
This implementation will use the basis tree implementation if possible.
467
Lines not in the basis are attributed to CURRENT_REVISION
469
If there are pending merges, lines added by those merges will be
470
incorrectly attributed to CURRENT_REVISION (but after committing, the
471
attribution will be correct).
473
file_id = osutils.safe_file_id(file_id)
474
basis = self.basis_tree()
477
changes = self._iter_changes(basis, True, [self.id2path(file_id)],
478
require_versioned=True).next()
479
changed_content, kind = changes[2], changes[6]
480
if not changed_content:
481
return basis.annotate_iter(file_id)
485
if kind[0] != 'file':
488
old_lines = list(basis.annotate_iter(file_id))
490
for tree in self.branch.repository.revision_trees(
491
self.get_parent_ids()[1:]):
492
if file_id not in tree:
494
old.append(list(tree.annotate_iter(file_id)))
495
return annotate.reannotate(old, self.get_file(file_id).readlines(),
500
480
def get_parent_ids(self):
501
481
"""See Tree.get_parent_ids.
503
483
This implementation reads the pending merges list and last_revision
504
484
value and uses that to decide what the parents list should be.
506
last_rev = _mod_revision.ensure_null(self._last_revision())
507
if _mod_revision.NULL_REVISION == last_rev:
486
last_rev = self._last_revision()
510
490
parents = [last_rev]
512
merges_file = self._control_files.get('pending-merges')
513
except errors.NoSuchFile:
492
merges_file = self._control_files.get_utf8('pending-merges')
516
496
for l in merges_file.readlines():
517
revision_id = osutils.safe_revision_id(l.rstrip('\n'))
518
parents.append(revision_id)
497
parents.append(l.rstrip('\n'))
522
500
def get_root_id(self):
523
501
"""Return the id of this trees root"""
524
return self._inventory.root.file_id
502
inv = self.read_working_inventory()
503
return inv.root.file_id
526
505
def _get_store_filename(self, file_id):
527
506
## XXX: badly named; this is not in the store at all
528
file_id = osutils.safe_file_id(file_id)
529
507
return self.abspath(self.id2path(file_id))
532
def clone(self, to_bzrdir, revision_id=None):
510
def clone(self, to_bzrdir, revision_id=None, basis=None):
533
511
"""Duplicate this working tree into to_bzr, including all state.
535
513
Specifically modified files are kept as modified, but
842
806
def merge_modified(self):
843
"""Return a dictionary of files modified by a merge.
845
The list is initialized by WorkingTree.set_merge_modified, which is
846
typically called after we make some automatic updates to the tree
849
This returns a map of file_id->sha1, containing only files which are
850
still in the working inventory and have that text hash.
853
808
hashfile = self._control_files.get('merge-hashes')
854
except errors.NoSuchFile:
856
811
merge_hashes = {}
858
813
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
859
raise errors.MergeModifiedFormatError()
814
raise MergeModifiedFormatError()
860
815
except StopIteration:
861
raise errors.MergeModifiedFormatError()
816
raise MergeModifiedFormatError()
862
817
for s in RioReader(hashfile):
863
# RioReader reads in Unicode, so convert file_ids back to utf8
864
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
818
file_id = s.get("file_id")
865
819
if file_id not in self.inventory:
867
text_hash = s.get("hash")
868
if text_hash == self.get_file_sha1(file_id):
869
merge_hashes[file_id] = text_hash
822
if hash == self.get_file_sha1(file_id):
823
merge_hashes[file_id] = hash
870
824
return merge_hashes
872
826
@needs_write_lock
873
827
def mkdir(self, path, file_id=None):
874
828
"""See MutableTree.mkdir()."""
875
829
if file_id is None:
876
file_id = generate_ids.gen_file_id(os.path.basename(path))
830
file_id = gen_file_id(os.path.basename(path))
877
831
os.mkdir(self.abspath(path))
878
832
self.add(path, file_id, 'directory')
881
835
def get_symlink_target(self, file_id):
882
file_id = osutils.safe_file_id(file_id)
883
836
return os.readlink(self.id2abspath(file_id))
886
def subsume(self, other_tree):
887
def add_children(inventory, entry):
888
for child_entry in entry.children.values():
889
inventory._byid[child_entry.file_id] = child_entry
890
if child_entry.kind == 'directory':
891
add_children(inventory, child_entry)
892
if other_tree.get_root_id() == self.get_root_id():
893
raise errors.BadSubsumeSource(self, other_tree,
894
'Trees have the same root')
896
other_tree_path = self.relpath(other_tree.basedir)
897
except errors.PathNotChild:
898
raise errors.BadSubsumeSource(self, other_tree,
899
'Tree is not contained by the other')
900
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
901
if new_root_parent is None:
902
raise errors.BadSubsumeSource(self, other_tree,
903
'Parent directory is not versioned.')
904
# We need to ensure that the result of a fetch will have a
905
# versionedfile for the other_tree root, and only fetching into
906
# RepositoryKnit2 guarantees that.
907
if not self.branch.repository.supports_rich_root():
908
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
909
other_tree.lock_tree_write()
911
new_parents = other_tree.get_parent_ids()
912
other_root = other_tree.inventory.root
913
other_root.parent_id = new_root_parent
914
other_root.name = osutils.basename(other_tree_path)
915
self.inventory.add(other_root)
916
add_children(self.inventory, other_root)
917
self._write_inventory(self.inventory)
918
# normally we don't want to fetch whole repositories, but i think
919
# here we really do want to consolidate the whole thing.
920
for parent_id in other_tree.get_parent_ids():
921
self.branch.fetch(other_tree.branch, parent_id)
922
self.add_parent_tree_id(parent_id)
925
other_tree.bzrdir.retire_bzrdir()
927
@needs_tree_write_lock
928
def extract(self, file_id, format=None):
929
"""Extract a subtree from this tree.
931
A new branch will be created, relative to the path for this tree.
935
segments = osutils.splitpath(path)
936
transport = self.branch.bzrdir.root_transport
937
for name in segments:
938
transport = transport.clone(name)
939
transport.ensure_base()
942
sub_path = self.id2path(file_id)
943
branch_transport = mkdirs(sub_path)
945
format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
946
branch_transport.ensure_base()
947
branch_bzrdir = format.initialize_on_transport(branch_transport)
949
repo = branch_bzrdir.find_repository()
950
except errors.NoRepositoryPresent:
951
repo = branch_bzrdir.create_repository()
952
assert repo.supports_rich_root()
954
if not repo.supports_rich_root():
955
raise errors.RootNotRich()
956
new_branch = branch_bzrdir.create_branch()
957
new_branch.pull(self.branch)
958
for parent_id in self.get_parent_ids():
959
new_branch.fetch(self.branch, parent_id)
960
tree_transport = self.bzrdir.root_transport.clone(sub_path)
961
if tree_transport.base != branch_transport.base:
962
tree_bzrdir = format.initialize_on_transport(tree_transport)
963
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
965
tree_bzrdir = branch_bzrdir
966
wt = tree_bzrdir.create_workingtree(NULL_REVISION)
967
wt.set_parent_ids(self.get_parent_ids())
968
my_inv = self.inventory
969
child_inv = Inventory(root_id=None)
970
new_root = my_inv[file_id]
971
my_inv.remove_recursive_id(file_id)
972
new_root.parent_id = None
973
child_inv.add(new_root)
974
self._write_inventory(my_inv)
975
wt._write_inventory(child_inv)
978
def _serialize(self, inventory, out_file):
979
xml5.serializer_v5.write_inventory(self._inventory, out_file)
981
def _deserialize(selt, in_file):
982
return xml5.serializer_v5.read_inventory(in_file)
985
"""Write the in memory inventory to disk."""
986
# TODO: Maybe this should only write on dirty ?
987
if self._control_files._lock_mode != 'w':
988
raise errors.NotWriteLocked(self)
990
self._serialize(self._inventory, sio)
992
self._control_files.put('inventory', sio)
993
self._inventory_is_modified = False
995
def _kind(self, relpath):
996
return osutils.file_kind(self.abspath(relpath))
998
def list_files(self, include_root=False):
838
def file_class(self, filename):
839
if self.path2id(filename):
841
elif self.is_ignored(filename):
846
def list_files(self):
999
847
"""Recursively list all files as (path, class, kind, id, entry).
1001
849
Lists, but does not descend into unversioned directories.
1099
946
new_children.sort()
1100
947
new_children = collections.deque(new_children)
1101
948
stack.append((f_ie.file_id, fp, fap, new_children))
1102
# Break out of inner loop,
1103
# so that we start outer loop with child
949
# Break out of inner loop, so that we start outer loop with child
1106
952
# if we finished all children, pop it off the stack
1109
955
@needs_tree_write_lock
1110
def move(self, from_paths, to_dir=None, after=False, **kwargs):
956
def move(self, from_paths, to_name):
1111
957
"""Rename files.
1113
to_dir must exist in the inventory.
959
to_name must exist in the inventory.
1115
If to_dir exists and is a directory, the files are moved into
961
If to_name exists and is a directory, the files are moved into
1116
962
it, keeping their old names.
1118
Note that to_dir is only the last component of the new name;
964
Note that to_name is only the last component of the new name;
1119
965
this doesn't change the directory.
1121
For each entry in from_paths the move mode will be determined
1124
The first mode moves the file in the filesystem and updates the
1125
inventory. The second mode only updates the inventory without
1126
touching the file on the filesystem. This is the new mode introduced
1129
move uses the second mode if 'after == True' and the target is not
1130
versioned but present in the working tree.
1132
move uses the second mode if 'after == False' and the source is
1133
versioned but no longer in the working tree, and the target is not
1134
versioned but present in the working tree.
1136
move uses the first mode if 'after == False' and the source is
1137
versioned and present in the working tree, and the target is not
1138
versioned and not present in the working tree.
1140
Everything else results in an error.
1142
967
This returns a list of (from_path, to_path) pairs for each
1143
968
entry that is moved.
1148
# check for deprecated use of signature
1150
to_dir = kwargs.get('to_name', None)
1152
raise TypeError('You must supply a target directory')
1154
symbol_versioning.warn('The parameter to_name was deprecated'
1155
' in version 0.13. Use to_dir instead',
1158
# check destination directory
971
## TODO: Option to move IDs only
1159
972
assert not isinstance(from_paths, basestring)
1160
973
inv = self.inventory
1161
to_abs = self.abspath(to_dir)
974
to_abs = self.abspath(to_name)
1162
975
if not isdir(to_abs):
1163
raise errors.BzrMoveFailedError('',to_dir,
1164
errors.NotADirectory(to_abs))
1165
if not self.has_filename(to_dir):
1166
raise errors.BzrMoveFailedError('',to_dir,
1167
errors.NotInWorkingDirectory(to_dir))
1168
to_dir_id = inv.path2id(to_dir)
1169
if to_dir_id is None:
1170
raise errors.BzrMoveFailedError('',to_dir,
1171
errors.NotVersionedError(path=str(to_dir)))
976
raise BzrError("destination %r is not a directory" % to_abs)
977
if not self.has_filename(to_name):
978
raise BzrError("destination %r not in working directory" % to_abs)
979
to_dir_id = inv.path2id(to_name)
980
if to_dir_id is None and to_name != '':
981
raise BzrError("destination %r is not a versioned directory" % to_name)
1173
982
to_dir_ie = inv[to_dir_id]
1174
983
if to_dir_ie.kind != 'directory':
1175
raise errors.BzrMoveFailedError('',to_dir,
1176
errors.NotADirectory(to_abs))
1178
# create rename entries and tuples
1179
for from_rel in from_paths:
1180
from_tail = splitpath(from_rel)[-1]
1181
from_id = inv.path2id(from_rel)
1183
raise errors.BzrMoveFailedError(from_rel,to_dir,
1184
errors.NotVersionedError(path=str(from_rel)))
1186
from_entry = inv[from_id]
1187
from_parent_id = from_entry.parent_id
1188
to_rel = pathjoin(to_dir, from_tail)
1189
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1191
from_tail=from_tail,
1192
from_parent_id=from_parent_id,
1193
to_rel=to_rel, to_tail=from_tail,
1194
to_parent_id=to_dir_id)
1195
rename_entries.append(rename_entry)
1196
rename_tuples.append((from_rel, to_rel))
1198
# determine which move mode to use. checks also for movability
1199
rename_entries = self._determine_mv_mode(rename_entries, after)
1201
original_modified = self._inventory_is_modified
984
raise BzrError("destination %r is not a directory" % to_abs)
986
to_idpath = inv.get_idpath(to_dir_id)
989
if not self.has_filename(f):
990
raise BzrError("%r does not exist in working tree" % f)
991
f_id = inv.path2id(f)
993
raise BzrError("%r is not versioned" % f)
994
name_tail = splitpath(f)[-1]
995
dest_path = pathjoin(to_name, name_tail)
996
if self.has_filename(dest_path):
997
raise BzrError("destination %r already exists" % dest_path)
998
if f_id in to_idpath:
999
raise BzrError("can't move %r to a subdirectory of itself" % f)
1001
# OK, so there's a race here, it's possible that someone will
1002
# create a file in this interval and then the rename might be
1003
# left half-done. But we should have caught most problems.
1004
orig_inv = deepcopy(self.inventory)
1204
self._inventory_is_modified = True
1205
self._move(rename_entries)
1006
for f in from_paths:
1007
name_tail = splitpath(f)[-1]
1008
dest_path = pathjoin(to_name, name_tail)
1009
result.append((f, dest_path))
1010
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1012
rename(self.abspath(f), self.abspath(dest_path))
1014
raise BzrError("failed to rename %r to %r: %s" %
1015
(f, dest_path, e[1]),
1016
["rename rolled back"])
1207
1018
# restore the inventory on error
1208
self._inventory_is_modified = original_modified
1019
self._set_inventory(orig_inv)
1210
1021
self._write_inventory(inv)
1211
return rename_tuples
1213
def _determine_mv_mode(self, rename_entries, after=False):
1214
"""Determines for each from-to pair if both inventory and working tree
1215
or only the inventory has to be changed.
1217
Also does basic plausability tests.
1219
inv = self.inventory
1221
for rename_entry in rename_entries:
1222
# store to local variables for easier reference
1223
from_rel = rename_entry.from_rel
1224
from_id = rename_entry.from_id
1225
to_rel = rename_entry.to_rel
1226
to_id = inv.path2id(to_rel)
1227
only_change_inv = False
1229
# check the inventory for source and destination
1231
raise errors.BzrMoveFailedError(from_rel,to_rel,
1232
errors.NotVersionedError(path=str(from_rel)))
1233
if to_id is not None:
1234
raise errors.BzrMoveFailedError(from_rel,to_rel,
1235
errors.AlreadyVersionedError(path=str(to_rel)))
1237
# try to determine the mode for rename (only change inv or change
1238
# inv and file system)
1240
if not self.has_filename(to_rel):
1241
raise errors.BzrMoveFailedError(from_id,to_rel,
1242
errors.NoSuchFile(path=str(to_rel),
1243
extra="New file has not been created yet"))
1244
only_change_inv = True
1245
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
1246
only_change_inv = True
1247
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1248
only_change_inv = False
1250
# something is wrong, so lets determine what exactly
1251
if not self.has_filename(from_rel) and \
1252
not self.has_filename(to_rel):
1253
raise errors.BzrRenameFailedError(from_rel,to_rel,
1254
errors.PathsDoNotExist(paths=(str(from_rel),
1257
raise errors.RenameFailedFilesExist(from_rel, to_rel,
1258
extra="(Use --after to update the Bazaar id)")
1259
rename_entry.only_change_inv = only_change_inv
1260
return rename_entries
1262
def _move(self, rename_entries):
1263
"""Moves a list of files.
1265
Depending on the value of the flag 'only_change_inv', the
1266
file will be moved on the file system or not.
1268
inv = self.inventory
1271
for entry in rename_entries:
1273
self._move_entry(entry)
1275
self._rollback_move(moved)
1279
def _rollback_move(self, moved):
1280
"""Try to rollback a previous move in case of an filesystem error."""
1281
inv = self.inventory
1284
self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
1285
entry.to_tail, entry.to_parent_id, entry.from_rel,
1286
entry.from_tail, entry.from_parent_id,
1287
entry.only_change_inv))
1288
except errors.BzrMoveFailedError, e:
1289
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
1290
" The working tree is in an inconsistent state."
1291
" Please consider doing a 'bzr revert'."
1292
" Error message is: %s" % e)
1294
def _move_entry(self, entry):
1295
inv = self.inventory
1296
from_rel_abs = self.abspath(entry.from_rel)
1297
to_rel_abs = self.abspath(entry.to_rel)
1298
if from_rel_abs == to_rel_abs:
1299
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
1300
"Source and target are identical.")
1302
if not entry.only_change_inv:
1304
osutils.rename(from_rel_abs, to_rel_abs)
1306
raise errors.BzrMoveFailedError(entry.from_rel,
1308
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
1310
1024
@needs_tree_write_lock
1311
def rename_one(self, from_rel, to_rel, after=False):
1025
def rename_one(self, from_rel, to_rel):
1312
1026
"""Rename one file.
1314
1028
This can change the directory or the filename or both.
1316
rename_one has several 'modes' to work. First, it can rename a physical
1317
file and change the file_id. That is the normal mode. Second, it can
1318
only change the file_id without touching any physical file. This is
1319
the new mode introduced in version 0.15.
1321
rename_one uses the second mode if 'after == True' and 'to_rel' is not
1322
versioned but present in the working tree.
1324
rename_one uses the second mode if 'after == False' and 'from_rel' is
1325
versioned but no longer in the working tree, and 'to_rel' is not
1326
versioned but present in the working tree.
1328
rename_one uses the first mode if 'after == False' and 'from_rel' is
1329
versioned and present in the working tree, and 'to_rel' is not
1330
versioned and not present in the working tree.
1332
Everything else results in an error.
1334
1030
inv = self.inventory
1337
# create rename entries and tuples
1338
from_tail = splitpath(from_rel)[-1]
1339
from_id = inv.path2id(from_rel)
1341
raise errors.BzrRenameFailedError(from_rel,to_rel,
1342
errors.NotVersionedError(path=str(from_rel)))
1343
from_entry = inv[from_id]
1344
from_parent_id = from_entry.parent_id
1031
if not self.has_filename(from_rel):
1032
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1033
if self.has_filename(to_rel):
1034
raise BzrError("can't rename: new working file %r already exists" % to_rel)
1036
file_id = inv.path2id(from_rel)
1038
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1040
entry = inv[file_id]
1041
from_parent = entry.parent_id
1042
from_name = entry.name
1044
if inv.path2id(to_rel):
1045
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1345
1047
to_dir, to_tail = os.path.split(to_rel)
1346
1048
to_dir_id = inv.path2id(to_dir)
1347
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1349
from_tail=from_tail,
1350
from_parent_id=from_parent_id,
1351
to_rel=to_rel, to_tail=to_tail,
1352
to_parent_id=to_dir_id)
1353
rename_entries.append(rename_entry)
1355
# determine which move mode to use. checks also for movability
1356
rename_entries = self._determine_mv_mode(rename_entries, after)
1358
# check if the target changed directory and if the target directory is
1360
if to_dir_id is None:
1361
raise errors.BzrMoveFailedError(from_rel,to_rel,
1362
errors.NotVersionedError(path=str(to_dir)))
1364
# all checks done. now we can continue with our actual work
1365
mutter('rename_one:\n'
1370
' to_dir_id {%s}\n',
1371
from_id, from_rel, to_rel, to_dir, to_dir_id)
1373
self._move(rename_entries)
1049
if to_dir_id is None and to_dir != '':
1050
raise BzrError("can't determine destination directory id for %r" % to_dir)
1052
mutter("rename_one:")
1053
mutter(" file_id {%s}" % file_id)
1054
mutter(" from_rel %r" % from_rel)
1055
mutter(" to_rel %r" % to_rel)
1056
mutter(" to_dir %r" % to_dir)
1057
mutter(" to_dir_id {%s}" % to_dir_id)
1059
inv.rename(file_id, to_dir_id, to_tail)
1061
from_abs = self.abspath(from_rel)
1062
to_abs = self.abspath(to_rel)
1064
rename(from_abs, to_abs)
1066
inv.rename(file_id, from_parent, from_name)
1067
raise BzrError("failed to rename %r to %r: %s"
1068
% (from_abs, to_abs, e[1]),
1069
["rename rolled back"])
1374
1070
self._write_inventory(inv)
1376
class _RenameEntry(object):
1377
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
1378
to_rel, to_tail, to_parent_id, only_change_inv=False):
1379
self.from_rel = from_rel
1380
self.from_id = from_id
1381
self.from_tail = from_tail
1382
self.from_parent_id = from_parent_id
1383
self.to_rel = to_rel
1384
self.to_tail = to_tail
1385
self.to_parent_id = to_parent_id
1386
self.only_change_inv = only_change_inv
1388
1072
@needs_read_lock
1389
1073
def unknowns(self):
1390
1074
"""Return all unknown files.
1750
1463
@needs_read_lock
1751
1464
def read_working_inventory(self):
1752
"""Read the working inventory.
1754
:raises errors.InventoryModified: read_working_inventory will fail
1755
when the current in memory inventory has been modified.
1757
# conceptually this should be an implementation detail of the tree.
1758
# XXX: Deprecate this.
1465
"""Read the working inventory."""
1759
1466
# ElementTree does its own conversion from UTF-8, so open in
1761
if self._inventory_is_modified:
1762
raise errors.InventoryModified(self)
1763
result = self._deserialize(self._control_files.get('inventory'))
1764
self._set_inventory(result, dirty=False)
1468
result = bzrlib.xml5.serializer_v5.read_inventory(
1469
self._control_files.get('inventory'))
1470
self._set_inventory(result)
1767
1473
@needs_tree_write_lock
1768
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1770
"""Remove nominated files from the working inventory.
1772
:files: File paths relative to the basedir.
1773
:keep_files: If true, the files will also be kept.
1774
:force: Delete files and directories, even if they are changed and
1775
even if the directories are not empty.
1474
def remove(self, files, verbose=False, to_file=None):
1475
"""Remove nominated files from the working inventory..
1477
This does not remove their text. This does not run on XXX on what? RBC
1479
TODO: Refuse to remove modified files unless --force is given?
1481
TODO: Do something useful with directories.
1483
TODO: Should this remove the text or not? Tough call; not
1484
removing may be useful and the user can just use use rm, and
1485
is the opposite of add. Removing it is consistent with most
1486
other tools. Maybe an option.
1777
1488
## TODO: Normalize names
1489
## TODO: Remove nested loops; better scalability
1779
1490
if isinstance(files, basestring):
1780
1491
files = [files]
1785
unknown_files_in_directory=set()
1787
def recurse_directory_to_add_files(directory):
1788
# recurse directory and add all files
1789
# so we can check if they have changed.
1790
for parent_info, file_infos in\
1791
osutils.walkdirs(self.abspath(directory),
1793
for relpath, basename, kind, lstat, abspath in file_infos:
1795
if self.path2id(relpath): #is it versioned?
1796
new_files.add(relpath)
1798
unknown_files_in_directory.add(
1799
(relpath, None, kind))
1801
for filename in files:
1802
# Get file name into canonical form.
1803
abspath = self.abspath(filename)
1804
filename = self.relpath(abspath)
1805
if len(filename) > 0:
1806
new_files.add(filename)
1807
if osutils.isdir(abspath):
1808
recurse_directory_to_add_files(filename)
1809
files = [f for f in new_files]
1812
return # nothing to do
1814
# Sort needed to first handle directory content before the directory
1815
files.sort(reverse=True)
1816
if not keep_files and not force:
1817
has_changed_files = len(unknown_files_in_directory) > 0
1818
if not has_changed_files:
1819
for (file_id, path, content_change, versioned, parent_id, name,
1820
kind, executable) in self._iter_changes(self.basis_tree(),
1821
include_unchanged=True, require_versioned=False,
1822
want_unversioned=True, specific_files=files):
1823
# check if it's unknown OR changed but not deleted:
1824
if (versioned == (False, False)
1825
or (content_change and kind[1] != None)):
1826
has_changed_files = True
1829
if has_changed_files:
1830
# make delta to show ALL applicable changes in error message.
1831
tree_delta = self.changes_from(self.basis_tree(),
1832
specific_files=files)
1833
for unknown_file in unknown_files_in_directory:
1834
tree_delta.unversioned.extend((unknown_file,))
1835
raise errors.BzrRemoveChangedFilesError(tree_delta)
1493
inv = self.inventory
1837
1495
# do this before any modifications
1838
1496
for f in files:
1839
fid = self.path2id(f)
1497
fid = inv.path2id(f)
1842
message="%s is not versioned." % (f,)
1845
# having removed it, it must be either ignored or unknown
1846
if self.is_ignored(f):
1850
textui.show_status(new_status, self.kind(fid), f,
1853
inv_delta.append((f, None, fid, None))
1854
message="removed %s" % (f,)
1857
abs_path = self.abspath(f)
1858
if osutils.lexists(abs_path):
1859
if (osutils.isdir(abs_path) and
1860
len(os.listdir(abs_path)) > 0):
1861
message="%s is not empty directory "\
1862
"and won't be deleted." % (f,)
1864
osutils.delete_any(abs_path)
1865
message="deleted %s" % (f,)
1866
elif message is not None:
1867
# only care if we haven't done anything yet.
1868
message="%s does not exist." % (f,)
1870
# print only one message (if any) per file.
1871
if message is not None:
1873
self.apply_inventory_delta(inv_delta)
1499
# TODO: Perhaps make this just a warning, and continue?
1500
# This tends to happen when
1501
raise NotVersionedError(path=f)
1503
# having remove it, it must be either ignored or unknown
1504
if self.is_ignored(f):
1508
show_status(new_status, inv[fid].kind, f, to_file=to_file)
1511
self._write_inventory(inv)
1875
1513
@needs_tree_write_lock
1876
1514
def revert(self, filenames, old_tree=None, backups=True,
1877
pb=DummyProgress(), report_changes=False):
1878
from bzrlib.conflicts import resolve
1515
pb=DummyProgress()):
1516
from transform import revert
1517
from conflicts import resolve
1879
1518
if old_tree is None:
1880
1519
old_tree = self.basis_tree()
1881
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1520
conflicts = revert(self, old_tree, filenames, backups, pb)
1883
1521
if not len(filenames):
1884
1522
self.set_parent_ids(self.get_parent_ids()[:1])
2087
1640
# the working tree had the same last-revision as the master
2088
1641
# branch did. We may still have pivot local work from the local
2089
1642
# branch into old_tip:
2090
if (old_tip is not None and not _mod_revision.is_null(old_tip)):
1643
if old_tip is not None:
2091
1644
self.add_parent_tree_id(old_tip)
2092
if (old_tip is not None and not _mod_revision.is_null(old_tip)
2093
and old_tip != last_rev):
1645
if old_tip and old_tip != last_rev:
2094
1646
# our last revision was not the prior branch last revision
2095
1647
# and we have converted that last revision to a pending merge.
2096
1648
# base is somewhere between the branch tip now
2097
1649
# and the now pending merge
2099
# Since we just modified the working tree and inventory, flush out
2100
# the current state, before we modify it again.
2101
# TODO: jam 20070214 WorkingTree3 doesn't require this, dirstate
2102
# requires it only because TreeTransform directly munges the
2103
# inventory and calls tree._write_inventory(). Ultimately we
2104
# should be able to remove this extra flush.
2106
graph = self.branch.repository.get_graph()
2107
base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1650
from bzrlib.revision import common_ancestor
1652
base_rev_id = common_ancestor(self.branch.last_revision(),
1654
self.branch.repository)
1655
except errors.NoCommonAncestor:
2109
1657
base_tree = self.branch.repository.revision_tree(base_rev_id)
2110
1658
other_tree = self.branch.repository.revision_tree(old_tip)
2111
result += merge.merge_inner(
1659
result += merge_inner(self.branch,
2116
change_reporter=change_reporter)
2119
def _write_hashcache_if_dirty(self):
2120
"""Write out the hashcache if it is dirty."""
2121
if self._hashcache.needs_write:
2123
self._hashcache.write()
2125
if e.errno not in (errno.EPERM, errno.EACCES):
2127
# TODO: jam 20061219 Should this be a warning? A single line
2128
# warning might be sufficient to let the user know what
2130
mutter('Could not write hashcache for %s\nError: %s',
2131
self._hashcache.cache_file_name(), e)
2133
1665
@needs_tree_write_lock
2134
1666
def _write_inventory(self, inv):
2135
1667
"""Write inventory as the current inventory."""
2136
self._set_inventory(inv, dirty=True)
1669
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
1671
self._control_files.put('inventory', sio)
1672
self._set_inventory(inv)
1673
mutter('wrote working inventory')
2139
1675
def set_conflicts(self, arg):
2140
raise errors.UnsupportedOperation(self.set_conflicts, self)
1676
raise UnsupportedOperation(self.set_conflicts, self)
2142
1678
def add_conflicts(self, arg):
2143
raise errors.UnsupportedOperation(self.add_conflicts, self)
1679
raise UnsupportedOperation(self.add_conflicts, self)
2145
1681
@needs_read_lock
2146
1682
def conflicts(self):
2147
conflicts = _mod_conflicts.ConflictList()
1683
conflicts = ConflictList()
2148
1684
for conflicted in self._iter_conflicts():
2163
1699
if text == False:
2165
1701
ctype = {True: 'text conflict', False: 'contents conflict'}[text]
2166
conflicts.append(_mod_conflicts.Conflict.factory(ctype,
1702
conflicts.append(Conflict.factory(ctype, path=conflicted,
2168
1703
file_id=self.path2id(conflicted)))
2169
1704
return conflicts
2171
def walkdirs(self, prefix=""):
2172
"""Walk the directories of this tree.
2174
returns a generator which yields items in the form:
2175
((curren_directory_path, fileid),
2176
[(file1_path, file1_name, file1_kind, (lstat), file1_id,
2179
This API returns a generator, which is only valid during the current
2180
tree transaction - within a single lock_read or lock_write duration.
2182
If the tree is not locked, it may cause an error to be raised,
2183
depending on the tree implementation.
2185
disk_top = self.abspath(prefix)
2186
if disk_top.endswith('/'):
2187
disk_top = disk_top[:-1]
2188
top_strip_len = len(disk_top) + 1
2189
inventory_iterator = self._walkdirs(prefix)
2190
disk_iterator = osutils.walkdirs(disk_top, prefix)
2192
current_disk = disk_iterator.next()
2193
disk_finished = False
2195
if not (e.errno == errno.ENOENT or
2196
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
2199
disk_finished = True
2201
current_inv = inventory_iterator.next()
2202
inv_finished = False
2203
except StopIteration:
2206
while not inv_finished or not disk_finished:
2207
if not disk_finished:
2208
# strip out .bzr dirs
2209
if current_disk[0][1][top_strip_len:] == '':
2210
# osutils.walkdirs can be made nicer -
2211
# yield the path-from-prefix rather than the pathjoined
2213
bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
2214
if current_disk[1][bzrdir_loc][0] == '.bzr':
2215
# we dont yield the contents of, or, .bzr itself.
2216
del current_disk[1][bzrdir_loc]
2218
# everything is unknown
2221
# everything is missing
2224
direction = cmp(current_inv[0][0], current_disk[0][0])
2226
# disk is before inventory - unknown
2227
dirblock = [(relpath, basename, kind, stat, None, None) for
2228
relpath, basename, kind, stat, top_path in current_disk[1]]
2229
yield (current_disk[0][0], None), dirblock
2231
current_disk = disk_iterator.next()
2232
except StopIteration:
2233
disk_finished = True
2235
# inventory is before disk - missing.
2236
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2237
for relpath, basename, dkind, stat, fileid, kind in
2239
yield (current_inv[0][0], current_inv[0][1]), dirblock
2241
current_inv = inventory_iterator.next()
2242
except StopIteration:
2245
# versioned present directory
2246
# merge the inventory and disk data together
2248
for relpath, subiterator in itertools.groupby(sorted(
2249
current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
2250
path_elements = list(subiterator)
2251
if len(path_elements) == 2:
2252
inv_row, disk_row = path_elements
2253
# versioned, present file
2254
dirblock.append((inv_row[0],
2255
inv_row[1], disk_row[2],
2256
disk_row[3], inv_row[4],
2258
elif len(path_elements[0]) == 5:
2260
dirblock.append((path_elements[0][0],
2261
path_elements[0][1], path_elements[0][2],
2262
path_elements[0][3], None, None))
2263
elif len(path_elements[0]) == 6:
2264
# versioned, absent file.
2265
dirblock.append((path_elements[0][0],
2266
path_elements[0][1], 'unknown', None,
2267
path_elements[0][4], path_elements[0][5]))
2269
raise NotImplementedError('unreachable code')
2270
yield current_inv[0], dirblock
2272
current_inv = inventory_iterator.next()
2273
except StopIteration:
2276
current_disk = disk_iterator.next()
2277
except StopIteration:
2278
disk_finished = True
2280
def _walkdirs(self, prefix=""):
2281
"""Walk the directories of this tree.
2283
:prefix: is used as the directrory to start with.
2284
returns a generator which yields items in the form:
2285
((curren_directory_path, fileid),
2286
[(file1_path, file1_name, file1_kind, None, file1_id,
2289
_directory = 'directory'
2290
# get the root in the inventory
2291
inv = self.inventory
2292
top_id = inv.path2id(prefix)
2296
pending = [(prefix, '', _directory, None, top_id, None)]
2299
currentdir = pending.pop()
2300
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2301
top_id = currentdir[4]
2303
relroot = currentdir[0] + '/'
2306
# FIXME: stash the node in pending
2308
for name, child in entry.sorted_children():
2309
dirblock.append((relroot + name, name, child.kind, None,
2310
child.file_id, child.kind
2312
yield (currentdir[0], entry.file_id), dirblock
2313
# push the user specified dirs from dirblock
2314
for dir in reversed(dirblock):
2315
if dir[2] == _directory:
2318
@needs_tree_write_lock
2319
def auto_resolve(self):
2320
"""Automatically resolve text conflicts according to contents.
2322
Only text conflicts are auto_resolvable. Files with no conflict markers
2323
are considered 'resolved', because bzr always puts conflict markers
2324
into files that have text conflicts. The corresponding .THIS .BASE and
2325
.OTHER files are deleted, as per 'resolve'.
2326
:return: a tuple of ConflictLists: (un_resolved, resolved).
2328
un_resolved = _mod_conflicts.ConflictList()
2329
resolved = _mod_conflicts.ConflictList()
2330
conflict_re = re.compile('^(<{7}|={7}|>{7})')
2331
for conflict in self.conflicts():
2332
if (conflict.typestring != 'text conflict' or
2333
self.kind(conflict.file_id) != 'file'):
2334
un_resolved.append(conflict)
2336
my_file = open(self.id2abspath(conflict.file_id), 'rb')
2338
for line in my_file:
2339
if conflict_re.search(line):
2340
un_resolved.append(conflict)
2343
resolved.append(conflict)
2346
resolved.remove_files(self)
2347
self.set_conflicts(un_resolved)
2348
return un_resolved, resolved
2350
def _validate(self):
2351
"""Validate internal structures.
2353
This is meant mostly for the test suite. To give it a chance to detect
2354
corruption after actions have occurred. The default implementation is a
2357
:return: None. An exception should be raised if there is an error.
2362
1707
class WorkingTree2(WorkingTree):
2363
1708
"""This is the Format 2 working tree.