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
# XXX This should be a WorkingTree method, not a Branch method.
263
raise NotImplementedError('add is abstract')
265
239
def print_file(self, file, revno):
266
240
"""Print `file` to stdout."""
267
241
raise NotImplementedError('print_file is abstract')
270
"""Return all unknown files.
272
These are files in the working directory that are not versioned or
273
control files or ignored.
275
>>> from bzrlib.workingtree import WorkingTree
276
>>> b = ScratchBranch(files=['foo', 'foo~'])
277
>>> map(str, b.unknowns())
280
>>> list(b.unknowns())
282
>>> WorkingTree(b.base, b).remove('foo')
283
>>> list(b.unknowns())
286
raise NotImplementedError('unknowns is abstract')
288
243
def append_revision(self, *revision_ids):
289
244
raise NotImplementedError('append_revision is abstract')
862
817
'or remove the .bzr directory'
863
818
' and "bzr init" again'])
865
821
def get_root_id(self):
866
822
"""See Branch.get_root_id."""
867
823
inv = self.get_inventory(self.last_revision())
868
824
return inv.root.file_id
871
def set_root_id(self, file_id):
872
"""See Branch.set_root_id."""
873
inv = self.working_tree().read_working_inventory()
874
orig_root_id = inv.root.file_id
875
del inv._byid[inv.root.file_id]
876
inv.root.file_id = file_id
877
inv._byid[inv.root.file_id] = inv.root
880
if entry.parent_id in (None, orig_root_id):
881
entry.parent_id = inv.root.file_id
882
self._write_inventory(inv)
885
def add(self, files, ids=None):
886
"""See Branch.add."""
887
# TODO: Re-adding a file that is removed in the working copy
888
# should probably put it back with the previous ID.
889
if isinstance(files, basestring):
890
assert(ids is None or isinstance(ids, basestring))
896
ids = [None] * len(files)
898
assert(len(ids) == len(files))
900
inv = self.working_tree().read_working_inventory()
901
for f,file_id in zip(files, ids):
902
if is_control_file(f):
903
raise BzrError("cannot add control file %s" % quotefn(f))
908
raise BzrError("cannot add top-level %r" % f)
910
fullpath = os.path.normpath(self.abspath(f))
913
kind = file_kind(fullpath)
915
# maybe something better?
916
raise BzrError('cannot add: not a regular file, symlink or directory: %s' % quotefn(f))
918
if not InventoryEntry.versionable_kind(kind):
919
raise BzrError('cannot add: not a versionable file ('
920
'i.e. regular file, symlink or directory): %s' % quotefn(f))
923
file_id = gen_file_id(f)
924
inv.add_path(f, kind=kind, file_id=file_id)
926
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
928
self.working_tree()._write_inventory(inv)
931
827
def print_file(self, file, revno):
932
828
"""See Branch.print_file."""
1103
995
def working_tree(self):
1104
996
"""See Branch.working_tree."""
1105
997
from bzrlib.workingtree import WorkingTree
1106
# TODO: In the future, perhaps WorkingTree should utilize Transport
1107
# RobertCollins 20051003 - I don't think it should - working trees are
1108
# much more complex to keep consistent than our careful .bzr subset.
1109
# instead, we should say that working trees are local only, and optimise
1111
998
if self._transport.base.find('://') != -1:
1112
999
raise NoWorkingTree(self.base)
1113
1000
return WorkingTree(self.base, branch=self)
1130
1017
source.unlock()
1133
def rename_one(self, from_rel, to_rel):
1134
"""See Branch.rename_one."""
1135
tree = self.working_tree()
1136
inv = tree.inventory
1137
if not tree.has_filename(from_rel):
1138
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1139
if tree.has_filename(to_rel):
1140
raise BzrError("can't rename: new working file %r already exists" % to_rel)
1142
file_id = inv.path2id(from_rel)
1144
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1146
if inv.path2id(to_rel):
1147
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1149
to_dir, to_tail = os.path.split(to_rel)
1150
to_dir_id = inv.path2id(to_dir)
1151
if to_dir_id == None and to_dir != '':
1152
raise BzrError("can't determine destination directory id for %r" % to_dir)
1154
mutter("rename_one:")
1155
mutter(" file_id {%s}" % file_id)
1156
mutter(" from_rel %r" % from_rel)
1157
mutter(" to_rel %r" % to_rel)
1158
mutter(" to_dir %r" % to_dir)
1159
mutter(" to_dir_id {%s}" % to_dir_id)
1161
inv.rename(file_id, to_dir_id, to_tail)
1163
from_abs = self.abspath(from_rel)
1164
to_abs = self.abspath(to_rel)
1166
rename(from_abs, to_abs)
1168
raise BzrError("failed to rename %r to %r: %s"
1169
% (from_abs, to_abs, e[1]),
1170
["rename rolled back"])
1172
self.working_tree()._write_inventory(inv)
1175
def move(self, from_paths, to_name):
1176
"""See Branch.move."""
1178
## TODO: Option to move IDs only
1179
assert not isinstance(from_paths, basestring)
1180
tree = self.working_tree()
1181
inv = tree.inventory
1182
to_abs = self.abspath(to_name)
1183
if not isdir(to_abs):
1184
raise BzrError("destination %r is not a directory" % to_abs)
1185
if not tree.has_filename(to_name):
1186
raise BzrError("destination %r not in working directory" % to_abs)
1187
to_dir_id = inv.path2id(to_name)
1188
if to_dir_id == None and to_name != '':
1189
raise BzrError("destination %r is not a versioned directory" % to_name)
1190
to_dir_ie = inv[to_dir_id]
1191
if to_dir_ie.kind not in ('directory', 'root_directory'):
1192
raise BzrError("destination %r is not a directory" % to_abs)
1194
to_idpath = inv.get_idpath(to_dir_id)
1196
for f in from_paths:
1197
if not tree.has_filename(f):
1198
raise BzrError("%r does not exist in working tree" % f)
1199
f_id = inv.path2id(f)
1201
raise BzrError("%r is not versioned" % f)
1202
name_tail = splitpath(f)[-1]
1203
dest_path = appendpath(to_name, name_tail)
1204
if tree.has_filename(dest_path):
1205
raise BzrError("destination %r already exists" % dest_path)
1206
if f_id in to_idpath:
1207
raise BzrError("can't move %r to a subdirectory of itself" % f)
1209
# OK, so there's a race here, it's possible that someone will
1210
# create a file in this interval and then the rename might be
1211
# left half-done. But we should have caught most problems.
1213
for f in from_paths:
1214
name_tail = splitpath(f)[-1]
1215
dest_path = appendpath(to_name, name_tail)
1216
result.append((f, dest_path))
1217
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1219
rename(self.abspath(f), self.abspath(dest_path))
1221
raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
1222
["rename rolled back"])
1224
self.working_tree()._write_inventory(inv)
1227
1019
def get_parent(self):
1228
1020
"""See Branch.get_parent."""
1345
1137
filename = head
1350
def gen_file_id(name):
1351
"""Return new file id.
1353
This should probably generate proper UUIDs, but for the moment we
1354
cope with just randomness because running uuidgen every time is
1357
from binascii import hexlify
1358
from time import time
1360
# get last component
1361
idx = name.rfind('/')
1363
name = name[idx+1 : ]
1364
idx = name.rfind('\\')
1366
name = name[idx+1 : ]
1368
# make it not a hidden file
1369
name = name.lstrip('.')
1371
# remove any wierd characters; we don't escape them but rather
1372
# just pull them out
1373
name = re.sub(r'[^\w.]', '', name)
1375
s = hexlify(rand_bytes(8))
1376
return '-'.join((name, compact_date(time()), s))
1380
"""Return a new tree-root file id."""
1381
return gen_file_id('TREE_ROOT')