27
from bzrlib.inventory import InventoryEntry
28
27
import bzrlib.inventory as inventory
29
28
from bzrlib.trace import mutter, note
30
from bzrlib.osutils import (isdir, quotefn, compact_date, rand_bytes,
29
from bzrlib.osutils import (isdir, quotefn,
31
30
rename, splitpath, sha_file, appendpath,
32
31
file_kind, abspath)
33
32
import bzrlib.errors as errors
237
236
def set_root_id(self, file_id):
238
237
raise NotImplementedError('set_root_id is abstract')
240
def add(self, files, ids=None):
241
"""Make files versioned.
243
Note that the command line normally calls smart_add instead,
244
which can automatically recurse.
246
This puts the files in the Added state, so that they will be
247
recorded by the next commit.
250
List of paths to add, relative to the base of the tree.
253
If set, use these instead of automatically generated ids.
254
Must be the same length as the list of files, but may
255
contain None for ids that are to be autogenerated.
257
TODO: Perhaps have an option to add the ids even if the files do
260
TODO: Perhaps yield the ids and paths as they're added.
262
raise NotImplementedError('add is abstract')
264
239
def print_file(self, file, revno):
265
240
"""Print `file` to stdout."""
266
241
raise NotImplementedError('print_file is abstract')
269
"""Return all unknown files.
271
These are files in the working directory that are not versioned or
272
control files or ignored.
274
>>> from bzrlib.workingtree import WorkingTree
275
>>> b = ScratchBranch(files=['foo', 'foo~'])
276
>>> map(str, b.unknowns())
279
>>> list(b.unknowns())
281
>>> WorkingTree(b.base, b).remove('foo')
282
>>> list(b.unknowns())
285
raise NotImplementedError('unknowns is abstract')
287
243
def append_revision(self, *revision_ids):
288
244
raise NotImplementedError('append_revision is abstract')
872
824
'or remove the .bzr directory'
873
825
' and "bzr init" again'])
875
828
def get_root_id(self):
876
829
"""See Branch.get_root_id."""
877
830
inv = self.get_inventory(self.last_revision())
878
831
return inv.root.file_id
881
def set_root_id(self, file_id):
882
"""See Branch.set_root_id."""
883
inv = self.working_tree().read_working_inventory()
884
orig_root_id = inv.root.file_id
885
del inv._byid[inv.root.file_id]
886
inv.root.file_id = file_id
887
inv._byid[inv.root.file_id] = inv.root
890
if entry.parent_id in (None, orig_root_id):
891
entry.parent_id = inv.root.file_id
892
self._write_inventory(inv)
895
def add(self, files, ids=None):
896
"""See Branch.add."""
897
# TODO: Re-adding a file that is removed in the working copy
898
# should probably put it back with the previous ID.
899
if isinstance(files, basestring):
900
assert(ids is None or isinstance(ids, basestring))
906
ids = [None] * len(files)
908
assert(len(ids) == len(files))
910
inv = self.working_tree().read_working_inventory()
911
for f,file_id in zip(files, ids):
912
if is_control_file(f):
913
raise BzrError("cannot add control file %s" % quotefn(f))
918
raise BzrError("cannot add top-level %r" % f)
920
fullpath = os.path.normpath(self.abspath(f))
923
kind = file_kind(fullpath)
925
# maybe something better?
926
raise BzrError('cannot add: not a regular file, symlink or directory: %s' % quotefn(f))
928
if not InventoryEntry.versionable_kind(kind):
929
raise BzrError('cannot add: not a versionable file ('
930
'i.e. regular file, symlink or directory): %s' % quotefn(f))
933
file_id = gen_file_id(f)
934
inv.add_path(f, kind=kind, file_id=file_id)
936
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
938
self.working_tree()._write_inventory(inv)
941
834
def print_file(self, file, revno):
942
835
"""See Branch.print_file."""
1150
1033
"""See Branch.pull."""
1151
1034
source.lock_read()
1036
old_count = len(self.revision_history())
1154
1038
self.update_revisions(source)
1155
1039
except DivergedBranches:
1156
1040
if not overwrite:
1158
1042
self.set_revision_history(source.revision_history())
1043
new_count = len(self.revision_history())
1044
return new_count - old_count
1160
1046
source.unlock()
1163
def rename_one(self, from_rel, to_rel):
1164
"""See Branch.rename_one."""
1165
tree = self.working_tree()
1166
inv = tree.inventory
1167
if not tree.has_filename(from_rel):
1168
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1169
if tree.has_filename(to_rel):
1170
raise BzrError("can't rename: new working file %r already exists" % to_rel)
1172
file_id = inv.path2id(from_rel)
1174
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1176
if inv.path2id(to_rel):
1177
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1179
to_dir, to_tail = os.path.split(to_rel)
1180
to_dir_id = inv.path2id(to_dir)
1181
if to_dir_id == None and to_dir != '':
1182
raise BzrError("can't determine destination directory id for %r" % to_dir)
1184
mutter("rename_one:")
1185
mutter(" file_id {%s}" % file_id)
1186
mutter(" from_rel %r" % from_rel)
1187
mutter(" to_rel %r" % to_rel)
1188
mutter(" to_dir %r" % to_dir)
1189
mutter(" to_dir_id {%s}" % to_dir_id)
1191
inv.rename(file_id, to_dir_id, to_tail)
1193
from_abs = self.abspath(from_rel)
1194
to_abs = self.abspath(to_rel)
1196
rename(from_abs, to_abs)
1198
raise BzrError("failed to rename %r to %r: %s"
1199
% (from_abs, to_abs, e[1]),
1200
["rename rolled back"])
1202
self.working_tree()._write_inventory(inv)
1205
def move(self, from_paths, to_name):
1206
"""See Branch.move."""
1208
## TODO: Option to move IDs only
1209
assert not isinstance(from_paths, basestring)
1210
tree = self.working_tree()
1211
inv = tree.inventory
1212
to_abs = self.abspath(to_name)
1213
if not isdir(to_abs):
1214
raise BzrError("destination %r is not a directory" % to_abs)
1215
if not tree.has_filename(to_name):
1216
raise BzrError("destination %r not in working directory" % to_abs)
1217
to_dir_id = inv.path2id(to_name)
1218
if to_dir_id == None and to_name != '':
1219
raise BzrError("destination %r is not a versioned directory" % to_name)
1220
to_dir_ie = inv[to_dir_id]
1221
if to_dir_ie.kind not in ('directory', 'root_directory'):
1222
raise BzrError("destination %r is not a directory" % to_abs)
1224
to_idpath = inv.get_idpath(to_dir_id)
1226
for f in from_paths:
1227
if not tree.has_filename(f):
1228
raise BzrError("%r does not exist in working tree" % f)
1229
f_id = inv.path2id(f)
1231
raise BzrError("%r is not versioned" % f)
1232
name_tail = splitpath(f)[-1]
1233
dest_path = appendpath(to_name, name_tail)
1234
if tree.has_filename(dest_path):
1235
raise BzrError("destination %r already exists" % dest_path)
1236
if f_id in to_idpath:
1237
raise BzrError("can't move %r to a subdirectory of itself" % f)
1239
# OK, so there's a race here, it's possible that someone will
1240
# create a file in this interval and then the rename might be
1241
# left half-done. But we should have caught most problems.
1243
for f in from_paths:
1244
name_tail = splitpath(f)[-1]
1245
dest_path = appendpath(to_name, name_tail)
1246
result.append((f, dest_path))
1247
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1249
rename(self.abspath(f), self.abspath(dest_path))
1251
raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
1252
["rename rolled back"])
1254
self.working_tree()._write_inventory(inv)
1257
1048
def get_parent(self):
1258
1049
"""See Branch.get_parent."""
1458
1249
filename = head
1463
def gen_file_id(name):
1464
"""Return new file id.
1466
This should probably generate proper UUIDs, but for the moment we
1467
cope with just randomness because running uuidgen every time is
1470
from binascii import hexlify
1471
from time import time
1473
# get last component
1474
idx = name.rfind('/')
1476
name = name[idx+1 : ]
1477
idx = name.rfind('\\')
1479
name = name[idx+1 : ]
1481
# make it not a hidden file
1482
name = name.lstrip('.')
1484
# remove any wierd characters; we don't escape them but rather
1485
# just pull them out
1486
name = re.sub(r'[^\w.]', '', name)
1488
s = hexlify(rand_bytes(8))
1489
return '-'.join((name, compact_date(time()), s))
1493
"""Return a new tree-root file id."""
1494
return gen_file_id('TREE_ROOT')