97
113
DEPRECATED_PARAMETER,
101
from bzrlib.trace import mutter, note
102
from bzrlib.transform import build_tree
103
from bzrlib.transport import get_transport
104
from bzrlib.transport.local import LocalTransport
105
from bzrlib.textui import show_status
111
# the regex removes any weird characters; we don't escape them
112
# but rather just pull them out
113
_gen_file_id_re = re.compile(r'[^\w.]')
114
_gen_id_suffix = None
118
def _next_id_suffix():
119
"""Create a new file id suffix that is reasonably unique.
121
On the first call we combine the current time with 64 bits of randomness
122
to give a highly probably globally unique number. Then each call in the same
123
process adds 1 to a serial number we append to that unique value.
125
# XXX TODO: change bzrlib.add.smart_add to call workingtree.add() rather
126
# than having to move the id randomness out of the inner loop like this.
127
# XXX TODO: for the global randomness this uses we should add the thread-id
128
# before the serial #.
129
global _gen_id_suffix, _gen_id_serial
130
if _gen_id_suffix is None:
131
_gen_id_suffix = "-%s-%s-" % (compact_date(time()), rand_chars(16))
133
return _gen_id_suffix + str(_gen_id_serial)
120
MERGE_MODIFIED_HEADER_1 = "BZR merge-modified list format 1"
121
CONFLICT_HEADER_1 = "BZR conflict list format 1"
123
ERROR_PATH_NOT_FOUND = 3 # WindowsError errno code, equivalent to ENOENT
126
@deprecated_function(zero_thirteen)
136
127
def gen_file_id(name):
137
128
"""Return new file id for the basename 'name'.
139
The uniqueness is supplied from _next_id_suffix.
130
Use bzrlib.generate_ids.gen_file_id() instead
141
# The real randomness is in the _next_id_suffix, the
142
# rest of the identifier is just to be nice.
144
# 1) Remove non-ascii word characters to keep the ids portable
145
# 2) squash to lowercase, so the file id doesn't have to
146
# be escaped (case insensitive filesystems would bork for ids
147
# that only differred in case without escaping).
148
# 3) truncate the filename to 20 chars. Long filenames also bork on some
150
# 4) Removing starting '.' characters to prevent the file ids from
151
# being considered hidden.
152
ascii_word_only = _gen_file_id_re.sub('', name.lower())
153
short_no_dots = ascii_word_only.lstrip('.')[:20]
154
return short_no_dots + _next_id_suffix()
132
return generate_ids.gen_file_id(name)
135
@deprecated_function(zero_thirteen)
157
136
def gen_root_id():
158
"""Return a new tree-root file id."""
159
return gen_file_id('TREE_ROOT')
162
def needs_tree_write_lock(unbound):
163
"""Decorate unbound to take out and release a tree_write lock."""
164
def tree_write_locked(self, *args, **kwargs):
165
self.lock_tree_write()
167
return unbound(self, *args, **kwargs)
170
tree_write_locked.__doc__ = unbound.__doc__
171
tree_write_locked.__name__ = unbound.__name__
172
return tree_write_locked
137
"""Return a new tree-root file id.
139
This has been deprecated in favor of bzrlib.generate_ids.gen_root_id()
141
return generate_ids.gen_root_id()
175
144
class TreeEntry(object):
599
581
__contains__ = has_id
601
583
def get_file_size(self, file_id):
584
file_id = osutils.safe_file_id(file_id)
602
585
return os.path.getsize(self.id2abspath(file_id))
605
def get_file_sha1(self, file_id, path=None):
588
def get_file_sha1(self, file_id, path=None, stat_value=None):
589
file_id = osutils.safe_file_id(file_id)
607
591
path = self._inventory.id2path(file_id)
608
return self._hashcache.get_sha1(path)
592
return self._hashcache.get_sha1(path, stat_value)
610
594
def get_file_mtime(self, file_id, path=None):
595
file_id = osutils.safe_file_id(file_id)
612
path = self._inventory.id2path(file_id)
597
path = self.inventory.id2path(file_id)
613
598
return os.lstat(self.abspath(path)).st_mtime
615
600
if not supports_executable():
616
601
def is_executable(self, file_id, path=None):
602
file_id = osutils.safe_file_id(file_id)
617
603
return self._inventory[file_id].executable
619
605
def is_executable(self, file_id, path=None):
621
path = self._inventory.id2path(file_id)
607
file_id = osutils.safe_file_id(file_id)
608
path = self.id2path(file_id)
622
609
mode = os.lstat(self.abspath(path)).st_mode
623
610
return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
625
612
@needs_tree_write_lock
626
def add(self, files, ids=None):
627
"""Make files versioned.
629
Note that the command line normally calls smart_add instead,
630
which can automatically recurse.
632
This adds the files to the inventory, so that they will be
633
recorded by the next commit.
636
List of paths to add, relative to the base of the tree.
639
If set, use these instead of automatically generated ids.
640
Must be the same length as the list of files, but may
641
contain None for ids that are to be autogenerated.
643
TODO: Perhaps have an option to add the ids even if the files do
646
TODO: Perhaps callback with the ids and paths as they're added.
613
def _add(self, files, ids, kinds):
614
"""See MutableTree._add."""
648
615
# TODO: Re-adding a file that is removed in the working copy
649
616
# should probably put it back with the previous ID.
650
if isinstance(files, basestring):
651
assert(ids is None or isinstance(ids, basestring))
657
ids = [None] * len(files)
659
assert(len(ids) == len(files))
617
# the read and write working inventory should not occur in this
618
# function - they should be part of lock_write and unlock.
661
619
inv = self.read_working_inventory()
662
for f,file_id in zip(files, ids):
663
if self.is_control_filename(f):
664
raise errors.ForbiddenControlFileError(filename=f)
669
raise BzrError("cannot add top-level %r" % f)
671
fullpath = normpath(self.abspath(f))
673
kind = file_kind(fullpath)
675
if e.errno == errno.ENOENT:
676
raise NoSuchFile(fullpath)
677
if not InventoryEntry.versionable_kind(kind):
678
raise errors.BadFileKindError(filename=f, kind=kind)
620
for f, file_id, kind in zip(files, ids, kinds):
621
assert kind is not None
679
622
if file_id is None:
680
623
inv.add_path(f, kind=kind)
625
file_id = osutils.safe_file_id(file_id)
682
626
inv.add_path(f, kind=kind, file_id=file_id)
684
627
self._write_inventory(inv)
686
629
@needs_tree_write_lock
630
def _gather_kinds(self, files, kinds):
631
"""See MutableTree._gather_kinds."""
632
for pos, f in enumerate(files):
633
if kinds[pos] is None:
634
fullpath = normpath(self.abspath(f))
636
kinds[pos] = file_kind(fullpath)
638
if e.errno == errno.ENOENT:
639
raise errors.NoSuchFile(fullpath)
687
642
def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
688
643
"""Add revision_id as a parent.
851
838
def merge_modified(self):
839
"""Return a dictionary of files modified by a merge.
841
The list is initialized by WorkingTree.set_merge_modified, which is
842
typically called after we make some automatic updates to the tree
845
This returns a map of file_id->sha1, containing only files which are
846
still in the working inventory and have that text hash.
853
849
hashfile = self._control_files.get('merge-hashes')
850
except errors.NoSuchFile:
856
852
merge_hashes = {}
858
854
if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
859
raise MergeModifiedFormatError()
855
raise errors.MergeModifiedFormatError()
860
856
except StopIteration:
861
raise MergeModifiedFormatError()
857
raise errors.MergeModifiedFormatError()
862
858
for s in RioReader(hashfile):
863
file_id = s.get("file_id")
859
# RioReader reads in Unicode, so convert file_ids back to utf8
860
file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
864
861
if file_id not in self.inventory:
867
if hash == self.get_file_sha1(file_id):
868
merge_hashes[file_id] = hash
863
text_hash = s.get("hash")
864
if text_hash == self.get_file_sha1(file_id):
865
merge_hashes[file_id] = text_hash
869
866
return merge_hashes
869
def mkdir(self, path, file_id=None):
870
"""See MutableTree.mkdir()."""
872
file_id = generate_ids.gen_file_id(os.path.basename(path))
873
os.mkdir(self.abspath(path))
874
self.add(path, file_id, 'directory')
871
877
def get_symlink_target(self, file_id):
878
file_id = osutils.safe_file_id(file_id)
872
879
return os.readlink(self.id2abspath(file_id))
874
def file_class(self, filename):
875
if self.path2id(filename):
877
elif self.is_ignored(filename):
882
def list_files(self):
882
def subsume(self, other_tree):
883
def add_children(inventory, entry):
884
for child_entry in entry.children.values():
885
inventory._byid[child_entry.file_id] = child_entry
886
if child_entry.kind == 'directory':
887
add_children(inventory, child_entry)
888
if other_tree.get_root_id() == self.get_root_id():
889
raise errors.BadSubsumeSource(self, other_tree,
890
'Trees have the same root')
892
other_tree_path = self.relpath(other_tree.basedir)
893
except errors.PathNotChild:
894
raise errors.BadSubsumeSource(self, other_tree,
895
'Tree is not contained by the other')
896
new_root_parent = self.path2id(osutils.dirname(other_tree_path))
897
if new_root_parent is None:
898
raise errors.BadSubsumeSource(self, other_tree,
899
'Parent directory is not versioned.')
900
# We need to ensure that the result of a fetch will have a
901
# versionedfile for the other_tree root, and only fetching into
902
# RepositoryKnit2 guarantees that.
903
if not self.branch.repository.supports_rich_root():
904
raise errors.SubsumeTargetNeedsUpgrade(other_tree)
905
other_tree.lock_tree_write()
907
new_parents = other_tree.get_parent_ids()
908
other_root = other_tree.inventory.root
909
other_root.parent_id = new_root_parent
910
other_root.name = osutils.basename(other_tree_path)
911
self.inventory.add(other_root)
912
add_children(self.inventory, other_root)
913
self._write_inventory(self.inventory)
914
# normally we don't want to fetch whole repositories, but i think
915
# here we really do want to consolidate the whole thing.
916
for parent_id in other_tree.get_parent_ids():
917
self.branch.fetch(other_tree.branch, parent_id)
918
self.add_parent_tree_id(parent_id)
921
other_tree.bzrdir.retire_bzrdir()
923
@needs_tree_write_lock
924
def extract(self, file_id, format=None):
925
"""Extract a subtree from this tree.
927
A new branch will be created, relative to the path for this tree.
931
segments = osutils.splitpath(path)
932
transport = self.branch.bzrdir.root_transport
933
for name in segments:
934
transport = transport.clone(name)
935
transport.ensure_base()
938
sub_path = self.id2path(file_id)
939
branch_transport = mkdirs(sub_path)
941
format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
942
branch_transport.ensure_base()
943
branch_bzrdir = format.initialize_on_transport(branch_transport)
945
repo = branch_bzrdir.find_repository()
946
except errors.NoRepositoryPresent:
947
repo = branch_bzrdir.create_repository()
948
assert repo.supports_rich_root()
950
if not repo.supports_rich_root():
951
raise errors.RootNotRich()
952
new_branch = branch_bzrdir.create_branch()
953
new_branch.pull(self.branch)
954
for parent_id in self.get_parent_ids():
955
new_branch.fetch(self.branch, parent_id)
956
tree_transport = self.bzrdir.root_transport.clone(sub_path)
957
if tree_transport.base != branch_transport.base:
958
tree_bzrdir = format.initialize_on_transport(tree_transport)
959
branch.BranchReferenceFormat().initialize(tree_bzrdir, new_branch)
961
tree_bzrdir = branch_bzrdir
962
wt = tree_bzrdir.create_workingtree(NULL_REVISION)
963
wt.set_parent_ids(self.get_parent_ids())
964
my_inv = self.inventory
965
child_inv = Inventory(root_id=None)
966
new_root = my_inv[file_id]
967
my_inv.remove_recursive_id(file_id)
968
new_root.parent_id = None
969
child_inv.add(new_root)
970
self._write_inventory(my_inv)
971
wt._write_inventory(child_inv)
974
def _serialize(self, inventory, out_file):
975
xml5.serializer_v5.write_inventory(self._inventory, out_file)
977
def _deserialize(selt, in_file):
978
return xml5.serializer_v5.read_inventory(in_file)
981
"""Write the in memory inventory to disk."""
982
# TODO: Maybe this should only write on dirty ?
983
if self._control_files._lock_mode != 'w':
984
raise errors.NotWriteLocked(self)
986
self._serialize(self._inventory, sio)
988
self._control_files.put('inventory', sio)
989
self._inventory_is_modified = False
991
def _kind(self, relpath):
992
return osutils.file_kind(self.abspath(relpath))
994
def list_files(self, include_root=False):
883
995
"""Recursively list all files as (path, class, kind, id, entry).
885
997
Lists, but does not descend into unversioned directories.
982
1095
new_children.sort()
983
1096
new_children = collections.deque(new_children)
984
1097
stack.append((f_ie.file_id, fp, fap, new_children))
985
# Break out of inner loop, so that we start outer loop with child
1098
# Break out of inner loop,
1099
# so that we start outer loop with child
988
1102
# if we finished all children, pop it off the stack
991
1105
@needs_tree_write_lock
992
def move(self, from_paths, to_name):
1106
def move(self, from_paths, to_dir=None, after=False, **kwargs):
993
1107
"""Rename files.
995
to_name must exist in the inventory.
1109
to_dir must exist in the inventory.
997
If to_name exists and is a directory, the files are moved into
1111
If to_dir exists and is a directory, the files are moved into
998
1112
it, keeping their old names.
1000
Note that to_name is only the last component of the new name;
1114
Note that to_dir is only the last component of the new name;
1001
1115
this doesn't change the directory.
1117
For each entry in from_paths the move mode will be determined
1120
The first mode moves the file in the filesystem and updates the
1121
inventory. The second mode only updates the inventory without
1122
touching the file on the filesystem. This is the new mode introduced
1125
move uses the second mode if 'after == True' and the target is not
1126
versioned but present in the working tree.
1128
move uses the second mode if 'after == False' and the source is
1129
versioned but no longer in the working tree, and the target is not
1130
versioned but present in the working tree.
1132
move uses the first mode if 'after == False' and the source is
1133
versioned and present in the working tree, and the target is not
1134
versioned and not present in the working tree.
1136
Everything else results in an error.
1003
1138
This returns a list of (from_path, to_path) pairs for each
1004
1139
entry that is moved.
1007
## TODO: Option to move IDs only
1144
# check for deprecated use of signature
1146
to_dir = kwargs.get('to_name', None)
1148
raise TypeError('You must supply a target directory')
1150
symbol_versioning.warn('The parameter to_name was deprecated'
1151
' in version 0.13. Use to_dir instead',
1154
# check destination directory
1008
1155
assert not isinstance(from_paths, basestring)
1009
1156
inv = self.inventory
1010
to_abs = self.abspath(to_name)
1157
to_abs = self.abspath(to_dir)
1011
1158
if not isdir(to_abs):
1012
raise BzrError("destination %r is not a directory" % to_abs)
1013
if not self.has_filename(to_name):
1014
raise BzrError("destination %r not in working directory" % to_abs)
1015
to_dir_id = inv.path2id(to_name)
1016
if to_dir_id is None and to_name != '':
1017
raise BzrError("destination %r is not a versioned directory" % to_name)
1159
raise errors.BzrMoveFailedError('',to_dir,
1160
errors.NotADirectory(to_abs))
1161
if not self.has_filename(to_dir):
1162
raise errors.BzrMoveFailedError('',to_dir,
1163
errors.NotInWorkingDirectory(to_dir))
1164
to_dir_id = inv.path2id(to_dir)
1165
if to_dir_id is None:
1166
raise errors.BzrMoveFailedError('',to_dir,
1167
errors.NotVersionedError(path=str(to_dir)))
1018
1169
to_dir_ie = inv[to_dir_id]
1019
1170
if to_dir_ie.kind != 'directory':
1020
raise BzrError("destination %r is not a directory" % to_abs)
1022
to_idpath = inv.get_idpath(to_dir_id)
1024
for f in from_paths:
1025
if not self.has_filename(f):
1026
raise BzrError("%r does not exist in working tree" % f)
1027
f_id = inv.path2id(f)
1029
raise BzrError("%r is not versioned" % f)
1030
name_tail = splitpath(f)[-1]
1031
dest_path = pathjoin(to_name, name_tail)
1032
if self.has_filename(dest_path):
1033
raise BzrError("destination %r already exists" % dest_path)
1034
if f_id in to_idpath:
1035
raise BzrError("can't move %r to a subdirectory of itself" % f)
1037
# OK, so there's a race here, it's possible that someone will
1038
# create a file in this interval and then the rename might be
1039
# left half-done. But we should have caught most problems.
1040
orig_inv = deepcopy(self.inventory)
1171
raise errors.BzrMoveFailedError('',to_dir,
1172
errors.NotADirectory(to_abs))
1174
# create rename entries and tuples
1175
for from_rel in from_paths:
1176
from_tail = splitpath(from_rel)[-1]
1177
from_id = inv.path2id(from_rel)
1179
raise errors.BzrMoveFailedError(from_rel,to_dir,
1180
errors.NotVersionedError(path=str(from_rel)))
1182
from_entry = inv[from_id]
1183
from_parent_id = from_entry.parent_id
1184
to_rel = pathjoin(to_dir, from_tail)
1185
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1187
from_tail=from_tail,
1188
from_parent_id=from_parent_id,
1189
to_rel=to_rel, to_tail=from_tail,
1190
to_parent_id=to_dir_id)
1191
rename_entries.append(rename_entry)
1192
rename_tuples.append((from_rel, to_rel))
1194
# determine which move mode to use. checks also for movability
1195
rename_entries = self._determine_mv_mode(rename_entries, after)
1197
original_modified = self._inventory_is_modified
1042
for f in from_paths:
1043
name_tail = splitpath(f)[-1]
1044
dest_path = pathjoin(to_name, name_tail)
1045
result.append((f, dest_path))
1046
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1048
rename(self.abspath(f), self.abspath(dest_path))
1050
raise BzrError("failed to rename %r to %r: %s" %
1051
(f, dest_path, e[1]),
1052
["rename rolled back"])
1200
self._inventory_is_modified = True
1201
self._move(rename_entries)
1054
1203
# restore the inventory on error
1055
self._set_inventory(orig_inv)
1204
self._inventory_is_modified = original_modified
1057
1206
self._write_inventory(inv)
1207
return rename_tuples
1209
def _determine_mv_mode(self, rename_entries, after=False):
1210
"""Determines for each from-to pair if both inventory and working tree
1211
or only the inventory has to be changed.
1213
Also does basic plausability tests.
1215
inv = self.inventory
1217
for rename_entry in rename_entries:
1218
# store to local variables for easier reference
1219
from_rel = rename_entry.from_rel
1220
from_id = rename_entry.from_id
1221
to_rel = rename_entry.to_rel
1222
to_id = inv.path2id(to_rel)
1223
only_change_inv = False
1225
# check the inventory for source and destination
1227
raise errors.BzrMoveFailedError(from_rel,to_rel,
1228
errors.NotVersionedError(path=str(from_rel)))
1229
if to_id is not None:
1230
raise errors.BzrMoveFailedError(from_rel,to_rel,
1231
errors.AlreadyVersionedError(path=str(to_rel)))
1233
# try to determine the mode for rename (only change inv or change
1234
# inv and file system)
1236
if not self.has_filename(to_rel):
1237
raise errors.BzrMoveFailedError(from_id,to_rel,
1238
errors.NoSuchFile(path=str(to_rel),
1239
extra="New file has not been created yet"))
1240
only_change_inv = True
1241
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
1242
only_change_inv = True
1243
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1244
only_change_inv = False
1246
# something is wrong, so lets determine what exactly
1247
if not self.has_filename(from_rel) and \
1248
not self.has_filename(to_rel):
1249
raise errors.BzrRenameFailedError(from_rel,to_rel,
1250
errors.PathsDoNotExist(paths=(str(from_rel),
1253
raise errors.RenameFailedFilesExist(from_rel, to_rel,
1254
extra="(Use --after to update the Bazaar id)")
1255
rename_entry.only_change_inv = only_change_inv
1256
return rename_entries
1258
def _move(self, rename_entries):
1259
"""Moves a list of files.
1261
Depending on the value of the flag 'only_change_inv', the
1262
file will be moved on the file system or not.
1264
inv = self.inventory
1267
for entry in rename_entries:
1269
self._move_entry(entry)
1271
self._rollback_move(moved)
1275
def _rollback_move(self, moved):
1276
"""Try to rollback a previous move in case of an filesystem error."""
1277
inv = self.inventory
1280
self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
1281
entry.to_tail, entry.to_parent_id, entry.from_rel,
1282
entry.from_tail, entry.from_parent_id,
1283
entry.only_change_inv))
1284
except errors.BzrMoveFailedError, e:
1285
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
1286
" The working tree is in an inconsistent state."
1287
" Please consider doing a 'bzr revert'."
1288
" Error message is: %s" % e)
1290
def _move_entry(self, entry):
1291
inv = self.inventory
1292
from_rel_abs = self.abspath(entry.from_rel)
1293
to_rel_abs = self.abspath(entry.to_rel)
1294
if from_rel_abs == to_rel_abs:
1295
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
1296
"Source and target are identical.")
1298
if not entry.only_change_inv:
1300
osutils.rename(from_rel_abs, to_rel_abs)
1302
raise errors.BzrMoveFailedError(entry.from_rel,
1304
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
1060
1306
@needs_tree_write_lock
1061
def rename_one(self, from_rel, to_rel):
1307
def rename_one(self, from_rel, to_rel, after=False):
1062
1308
"""Rename one file.
1064
1310
This can change the directory or the filename or both.
1312
rename_one has several 'modes' to work. First, it can rename a physical
1313
file and change the file_id. That is the normal mode. Second, it can
1314
only change the file_id without touching any physical file. This is
1315
the new mode introduced in version 0.15.
1317
rename_one uses the second mode if 'after == True' and 'to_rel' is not
1318
versioned but present in the working tree.
1320
rename_one uses the second mode if 'after == False' and 'from_rel' is
1321
versioned but no longer in the working tree, and 'to_rel' is not
1322
versioned but present in the working tree.
1324
rename_one uses the first mode if 'after == False' and 'from_rel' is
1325
versioned and present in the working tree, and 'to_rel' is not
1326
versioned and not present in the working tree.
1328
Everything else results in an error.
1066
1330
inv = self.inventory
1067
if not self.has_filename(from_rel):
1068
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1069
if self.has_filename(to_rel):
1070
raise BzrError("can't rename: new working file %r already exists" % to_rel)
1072
file_id = inv.path2id(from_rel)
1074
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1076
entry = inv[file_id]
1077
from_parent = entry.parent_id
1078
from_name = entry.name
1080
if inv.path2id(to_rel):
1081
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1333
# create rename entries and tuples
1334
from_tail = splitpath(from_rel)[-1]
1335
from_id = inv.path2id(from_rel)
1337
raise errors.BzrRenameFailedError(from_rel,to_rel,
1338
errors.NotVersionedError(path=str(from_rel)))
1339
from_entry = inv[from_id]
1340
from_parent_id = from_entry.parent_id
1083
1341
to_dir, to_tail = os.path.split(to_rel)
1084
1342
to_dir_id = inv.path2id(to_dir)
1085
if to_dir_id is None and to_dir != '':
1086
raise BzrError("can't determine destination directory id for %r" % to_dir)
1088
mutter("rename_one:")
1089
mutter(" file_id {%s}" % file_id)
1090
mutter(" from_rel %r" % from_rel)
1091
mutter(" to_rel %r" % to_rel)
1092
mutter(" to_dir %r" % to_dir)
1093
mutter(" to_dir_id {%s}" % to_dir_id)
1095
inv.rename(file_id, to_dir_id, to_tail)
1097
from_abs = self.abspath(from_rel)
1098
to_abs = self.abspath(to_rel)
1100
rename(from_abs, to_abs)
1102
inv.rename(file_id, from_parent, from_name)
1103
raise BzrError("failed to rename %r to %r: %s"
1104
% (from_abs, to_abs, e[1]),
1105
["rename rolled back"])
1343
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1345
from_tail=from_tail,
1346
from_parent_id=from_parent_id,
1347
to_rel=to_rel, to_tail=to_tail,
1348
to_parent_id=to_dir_id)
1349
rename_entries.append(rename_entry)
1351
# determine which move mode to use. checks also for movability
1352
rename_entries = self._determine_mv_mode(rename_entries, after)
1354
# check if the target changed directory and if the target directory is
1356
if to_dir_id is None:
1357
raise errors.BzrMoveFailedError(from_rel,to_rel,
1358
errors.NotVersionedError(path=str(to_dir)))
1360
# all checks done. now we can continue with our actual work
1361
mutter('rename_one:\n'
1366
' to_dir_id {%s}\n',
1367
from_id, from_rel, to_rel, to_dir, to_dir_id)
1369
self._move(rename_entries)
1106
1370
self._write_inventory(inv)
1372
class _RenameEntry(object):
1373
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
1374
to_rel, to_tail, to_parent_id, only_change_inv=False):
1375
self.from_rel = from_rel
1376
self.from_id = from_id
1377
self.from_tail = from_tail
1378
self.from_parent_id = from_parent_id
1379
self.to_rel = to_rel
1380
self.to_tail = to_tail
1381
self.to_parent_id = to_parent_id
1382
self.only_change_inv = only_change_inv
1108
1384
@needs_read_lock
1109
1385
def unknowns(self):
1110
1386
"""Return all unknown files.
1490
1746
@needs_read_lock
1491
1747
def read_working_inventory(self):
1492
"""Read the working inventory."""
1748
"""Read the working inventory.
1750
:raises errors.InventoryModified: read_working_inventory will fail
1751
when the current in memory inventory has been modified.
1753
# conceptually this should be an implementation detail of the tree.
1754
# XXX: Deprecate this.
1493
1755
# ElementTree does its own conversion from UTF-8, so open in
1495
result = bzrlib.xml5.serializer_v5.read_inventory(
1496
self._control_files.get('inventory'))
1497
self._set_inventory(result)
1757
if self._inventory_is_modified:
1758
raise errors.InventoryModified(self)
1759
result = self._deserialize(self._control_files.get('inventory'))
1760
self._set_inventory(result, dirty=False)
1500
1763
@needs_tree_write_lock
1501
def remove(self, files, verbose=False, to_file=None):
1502
"""Remove nominated files from the working inventory..
1504
This does not remove their text. This does not run on XXX on what? RBC
1506
TODO: Refuse to remove modified files unless --force is given?
1508
TODO: Do something useful with directories.
1510
TODO: Should this remove the text or not? Tough call; not
1511
removing may be useful and the user can just use use rm, and
1512
is the opposite of add. Removing it is consistent with most
1513
other tools. Maybe an option.
1764
def remove(self, files, verbose=False, to_file=None, keep_files=True,
1766
"""Remove nominated files from the working inventor.
1768
:files: File paths relative to the basedir.
1769
:keep_files: If true, the files will also be kept.
1770
:force: Delete files and directories, even if they are changed and
1771
even if the directories are not empty.
1515
1773
## TODO: Normalize names
1516
## TODO: Remove nested loops; better scalability
1517
1775
if isinstance(files, basestring):
1518
1776
files = [files]
1520
inv = self.inventory
1781
unknown_files_in_directory=set()
1783
def recurse_directory_to_add_files(directory):
1784
# recurse directory and add all files
1785
# so we can check if they have changed.
1786
for parent_info, file_infos in\
1787
osutils.walkdirs(self.abspath(directory),
1789
for relpath, basename, kind, lstat, abspath in file_infos:
1791
if self.path2id(relpath): #is it versioned?
1792
new_files.add(relpath)
1794
unknown_files_in_directory.add(
1795
(relpath, None, kind))
1797
for filename in files:
1798
# Get file name into canonical form.
1799
abspath = self.abspath(filename)
1800
filename = self.relpath(abspath)
1801
if len(filename) > 0:
1802
new_files.add(filename)
1803
if osutils.isdir(abspath):
1804
recurse_directory_to_add_files(filename)
1805
files = [f for f in new_files]
1807
# Sort needed to first handle directory content before the directory
1808
files.sort(reverse=True)
1809
if not keep_files and not force:
1810
tree_delta = self.changes_from(self.basis_tree(),
1811
specific_files=files)
1812
for unknown_file in unknown_files_in_directory:
1813
tree_delta.unversioned.extend((unknown_file,))
1814
if bool(tree_delta.modified
1816
or tree_delta.renamed
1817
or tree_delta.kind_changed
1818
or tree_delta.unversioned):
1819
raise errors.BzrRemoveChangedFilesError(tree_delta)
1522
1821
# do this before any modifications
1523
1822
for f in files:
1524
fid = inv.path2id(f)
1823
fid = self.path2id(f)
1526
# TODO: Perhaps make this just a warning, and continue?
1527
# This tends to happen when
1528
raise NotVersionedError(path=f)
1530
# having remove it, it must be either ignored or unknown
1531
if self.is_ignored(f):
1535
show_status(new_status, inv[fid].kind, f, to_file=to_file)
1538
self._write_inventory(inv)
1826
message="%s is not versioned." % (f,)
1829
# having removed it, it must be either ignored or unknown
1830
if self.is_ignored(f):
1834
textui.show_status(new_status, self.kind(fid), f,
1837
inv_delta.append((f, None, fid, None))
1838
message="removed %s" % (f,)
1841
abs_path = self.abspath(f)
1842
if osutils.lexists(abs_path):
1843
if (osutils.isdir(abs_path) and
1844
len(os.listdir(abs_path)) > 0):
1845
message="%s is not empty directory "\
1846
"and won't be deleted." % (f,)
1848
osutils.delete_any(abs_path)
1849
message="deleted %s" % (f,)
1850
elif message is not None:
1851
# only care if we haven't done anything yet.
1852
message="%s does not exist." % (f,)
1854
# print only one message (if any) per file.
1855
if message is not None:
1857
self.apply_inventory_delta(inv_delta)
1540
1859
@needs_tree_write_lock
1541
1860
def revert(self, filenames, old_tree=None, backups=True,
1542
pb=DummyProgress()):
1543
from transform import revert
1544
from conflicts import resolve
1861
pb=DummyProgress(), report_changes=False):
1862
from bzrlib.conflicts import resolve
1545
1863
if old_tree is None:
1546
1864
old_tree = self.basis_tree()
1547
conflicts = revert(self, old_tree, filenames, backups, pb)
1865
conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1548
1867
if not len(filenames):
1549
1868
self.set_parent_ids(self.get_parent_ids()[:1])
1726
2148
if text == False:
1728
2150
ctype = {True: 'text conflict', False: 'contents conflict'}[text]
1729
conflicts.append(Conflict.factory(ctype, path=conflicted,
2151
conflicts.append(_mod_conflicts.Conflict.factory(ctype,
1730
2153
file_id=self.path2id(conflicted)))
1731
2154
return conflicts
2156
def walkdirs(self, prefix=""):
2157
"""Walk the directories of this tree.
2159
returns a generator which yields items in the form:
2160
((curren_directory_path, fileid),
2161
[(file1_path, file1_name, file1_kind, (lstat), file1_id,
2164
This API returns a generator, which is only valid during the current
2165
tree transaction - within a single lock_read or lock_write duration.
2167
If the tree is not locked, it may cause an error to be raised,
2168
depending on the tree implementation.
2170
disk_top = self.abspath(prefix)
2171
if disk_top.endswith('/'):
2172
disk_top = disk_top[:-1]
2173
top_strip_len = len(disk_top) + 1
2174
inventory_iterator = self._walkdirs(prefix)
2175
disk_iterator = osutils.walkdirs(disk_top, prefix)
2177
current_disk = disk_iterator.next()
2178
disk_finished = False
2180
if not (e.errno == errno.ENOENT or
2181
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
2184
disk_finished = True
2186
current_inv = inventory_iterator.next()
2187
inv_finished = False
2188
except StopIteration:
2191
while not inv_finished or not disk_finished:
2192
if not disk_finished:
2193
# strip out .bzr dirs
2194
if current_disk[0][1][top_strip_len:] == '':
2195
# osutils.walkdirs can be made nicer -
2196
# yield the path-from-prefix rather than the pathjoined
2198
bzrdir_loc = bisect_left(current_disk[1], ('.bzr', '.bzr'))
2199
if current_disk[1][bzrdir_loc][0] == '.bzr':
2200
# we dont yield the contents of, or, .bzr itself.
2201
del current_disk[1][bzrdir_loc]
2203
# everything is unknown
2206
# everything is missing
2209
direction = cmp(current_inv[0][0], current_disk[0][0])
2211
# disk is before inventory - unknown
2212
dirblock = [(relpath, basename, kind, stat, None, None) for
2213
relpath, basename, kind, stat, top_path in current_disk[1]]
2214
yield (current_disk[0][0], None), dirblock
2216
current_disk = disk_iterator.next()
2217
except StopIteration:
2218
disk_finished = True
2220
# inventory is before disk - missing.
2221
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
2222
for relpath, basename, dkind, stat, fileid, kind in
2224
yield (current_inv[0][0], current_inv[0][1]), dirblock
2226
current_inv = inventory_iterator.next()
2227
except StopIteration:
2230
# versioned present directory
2231
# merge the inventory and disk data together
2233
for relpath, subiterator in itertools.groupby(sorted(
2234
current_inv[1] + current_disk[1], key=operator.itemgetter(0)), operator.itemgetter(1)):
2235
path_elements = list(subiterator)
2236
if len(path_elements) == 2:
2237
inv_row, disk_row = path_elements
2238
# versioned, present file
2239
dirblock.append((inv_row[0],
2240
inv_row[1], disk_row[2],
2241
disk_row[3], inv_row[4],
2243
elif len(path_elements[0]) == 5:
2245
dirblock.append((path_elements[0][0],
2246
path_elements[0][1], path_elements[0][2],
2247
path_elements[0][3], None, None))
2248
elif len(path_elements[0]) == 6:
2249
# versioned, absent file.
2250
dirblock.append((path_elements[0][0],
2251
path_elements[0][1], 'unknown', None,
2252
path_elements[0][4], path_elements[0][5]))
2254
raise NotImplementedError('unreachable code')
2255
yield current_inv[0], dirblock
2257
current_inv = inventory_iterator.next()
2258
except StopIteration:
2261
current_disk = disk_iterator.next()
2262
except StopIteration:
2263
disk_finished = True
2265
def _walkdirs(self, prefix=""):
2266
"""Walk the directories of this tree.
2268
:prefix: is used as the directrory to start with.
2269
returns a generator which yields items in the form:
2270
((curren_directory_path, fileid),
2271
[(file1_path, file1_name, file1_kind, None, file1_id,
2274
_directory = 'directory'
2275
# get the root in the inventory
2276
inv = self.inventory
2277
top_id = inv.path2id(prefix)
2281
pending = [(prefix, '', _directory, None, top_id, None)]
2284
currentdir = pending.pop()
2285
# 0 - relpath, 1- basename, 2- kind, 3- stat, 4-id, 5-kind
2286
top_id = currentdir[4]
2288
relroot = currentdir[0] + '/'
2291
# FIXME: stash the node in pending
2293
for name, child in entry.sorted_children():
2294
dirblock.append((relroot + name, name, child.kind, None,
2295
child.file_id, child.kind
2297
yield (currentdir[0], entry.file_id), dirblock
2298
# push the user specified dirs from dirblock
2299
for dir in reversed(dirblock):
2300
if dir[2] == _directory:
2303
@needs_tree_write_lock
2304
def auto_resolve(self):
2305
"""Automatically resolve text conflicts according to contents.
2307
Only text conflicts are auto_resolvable. Files with no conflict markers
2308
are considered 'resolved', because bzr always puts conflict markers
2309
into files that have text conflicts. The corresponding .THIS .BASE and
2310
.OTHER files are deleted, as per 'resolve'.
2311
:return: a tuple of ConflictLists: (un_resolved, resolved).
2313
un_resolved = _mod_conflicts.ConflictList()
2314
resolved = _mod_conflicts.ConflictList()
2315
conflict_re = re.compile('^(<{7}|={7}|>{7})')
2316
for conflict in self.conflicts():
2317
if (conflict.typestring != 'text conflict' or
2318
self.kind(conflict.file_id) != 'file'):
2319
un_resolved.append(conflict)
2321
my_file = open(self.id2abspath(conflict.file_id), 'rb')
2323
for line in my_file:
2324
if conflict_re.search(line):
2325
un_resolved.append(conflict)
2328
resolved.append(conflict)
2331
resolved.remove_files(self)
2332
self.set_conflicts(un_resolved)
2333
return un_resolved, resolved
2335
def _validate(self):
2336
"""Validate internal structures.
2338
This is meant mostly for the test suite. To give it a chance to detect
2339
corruption after actions have occurred. The default implementation is a
2342
:return: None. An exception should be raised if there is an error.
1734
2347
class WorkingTree2(WorkingTree):
1735
2348
"""This is the Format 2 working tree.