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')
494
446
raise NotImplementedError('move is abstract')
496
def revert(self, filenames, old_tree=None, backups=True):
497
"""Restore selected files to the versions from a previous tree.
500
If true (default) backups are made of files before
503
raise NotImplementedError('revert is abstract')
505
def pending_merges(self):
506
"""Return a list of pending merges.
508
These are revisions that have been merged into the working
509
directory but not yet committed.
511
raise NotImplementedError('pending_merges is abstract')
513
def add_pending_merge(self, *revision_ids):
514
# TODO: Perhaps should check at this point that the
515
# history of the revision is actually present?
516
raise NotImplementedError('add_pending_merge is abstract')
518
def set_pending_merges(self, rev_list):
519
raise NotImplementedError('set_pending_merges is abstract')
521
448
def get_parent(self):
522
449
"""Return the parent location of the branch.
890
817
'or remove the .bzr directory'
891
818
' and "bzr init" again'])
893
821
def get_root_id(self):
894
822
"""See Branch.get_root_id."""
895
823
inv = self.get_inventory(self.last_revision())
896
824
return inv.root.file_id
899
def set_root_id(self, file_id):
900
"""See Branch.set_root_id."""
901
inv = self.working_tree().read_working_inventory()
902
orig_root_id = inv.root.file_id
903
del inv._byid[inv.root.file_id]
904
inv.root.file_id = file_id
905
inv._byid[inv.root.file_id] = inv.root
908
if entry.parent_id in (None, orig_root_id):
909
entry.parent_id = inv.root.file_id
910
self._write_inventory(inv)
913
def add(self, files, ids=None):
914
"""See Branch.add."""
915
# TODO: Re-adding a file that is removed in the working copy
916
# should probably put it back with the previous ID.
917
if isinstance(files, basestring):
918
assert(ids is None or isinstance(ids, basestring))
924
ids = [None] * len(files)
926
assert(len(ids) == len(files))
928
inv = self.working_tree().read_working_inventory()
929
for f,file_id in zip(files, ids):
930
if is_control_file(f):
931
raise BzrError("cannot add control file %s" % quotefn(f))
936
raise BzrError("cannot add top-level %r" % f)
938
fullpath = os.path.normpath(self.abspath(f))
941
kind = file_kind(fullpath)
943
# maybe something better?
944
raise BzrError('cannot add: not a regular file, symlink or directory: %s' % quotefn(f))
946
if not InventoryEntry.versionable_kind(kind):
947
raise BzrError('cannot add: not a versionable file ('
948
'i.e. regular file, symlink or directory): %s' % quotefn(f))
951
file_id = gen_file_id(f)
952
inv.add_path(f, kind=kind, file_id=file_id)
954
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
956
self.working_tree()._write_inventory(inv)
959
827
def print_file(self, file, revno):
960
828
"""See Branch.print_file."""
998
861
except (IndexError, KeyError):
999
862
raise bzrlib.errors.NoSuchRevision(self, revision_id)
1002
get_revision_xml = get_revision_xml_file
1004
864
def get_revision_xml(self, revision_id):
1005
865
"""See Branch.get_revision_xml."""
1006
return self.get_revision_xml_file(revision_id).read()
866
return self._get_revision_xml_file(revision_id).read()
1009
868
def get_revision(self, revision_id):
1010
869
"""See Branch.get_revision."""
1011
xml_file = self.get_revision_xml_file(revision_id)
870
xml_file = self._get_revision_xml_file(revision_id)
1014
873
r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
1126
def revision_id_to_revno(self, revision_id):
1127
"""Given a revision id, return its revno"""
1128
if revision_id is None:
1130
history = self.revision_history()
1132
return history.index(revision_id) + 1
1134
raise bzrlib.errors.NoSuchRevision(self, revision_id)
1136
def get_rev_id(self, revno, history=None):
1137
"""Find the revision id of the specified revno."""
1141
history = self.revision_history()
1142
elif revno <= 0 or revno > len(history):
1143
raise bzrlib.errors.NoSuchRevision(self, revno)
1144
return history[revno - 1]
1146
985
def revision_tree(self, revision_id):
1147
986
"""See Branch.revision_tree."""
1148
987
# TODO: refactor this to use an existing revision object
1170
1004
"""See Branch.pull."""
1171
1005
source.lock_read()
1007
old_count = len(self.revision_history())
1174
1009
self.update_revisions(source)
1175
1010
except DivergedBranches:
1176
1011
if not overwrite:
1178
1013
self.set_revision_history(source.revision_history())
1014
new_count = len(self.revision_history())
1015
return new_count - old_count
1180
1017
source.unlock()
1183
def rename_one(self, from_rel, to_rel):
1184
"""See Branch.rename_one."""
1185
tree = self.working_tree()
1186
inv = tree.inventory
1187
if not tree.has_filename(from_rel):
1188
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1189
if tree.has_filename(to_rel):
1190
raise BzrError("can't rename: new working file %r already exists" % to_rel)
1192
file_id = inv.path2id(from_rel)
1194
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1196
if inv.path2id(to_rel):
1197
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1199
to_dir, to_tail = os.path.split(to_rel)
1200
to_dir_id = inv.path2id(to_dir)
1201
if to_dir_id == None and to_dir != '':
1202
raise BzrError("can't determine destination directory id for %r" % to_dir)
1204
mutter("rename_one:")
1205
mutter(" file_id {%s}" % file_id)
1206
mutter(" from_rel %r" % from_rel)
1207
mutter(" to_rel %r" % to_rel)
1208
mutter(" to_dir %r" % to_dir)
1209
mutter(" to_dir_id {%s}" % to_dir_id)
1211
inv.rename(file_id, to_dir_id, to_tail)
1213
from_abs = self.abspath(from_rel)
1214
to_abs = self.abspath(to_rel)
1216
rename(from_abs, to_abs)
1218
raise BzrError("failed to rename %r to %r: %s"
1219
% (from_abs, to_abs, e[1]),
1220
["rename rolled back"])
1222
self.working_tree()._write_inventory(inv)
1225
def move(self, from_paths, to_name):
1226
"""See Branch.move."""
1228
## TODO: Option to move IDs only
1229
assert not isinstance(from_paths, basestring)
1230
tree = self.working_tree()
1231
inv = tree.inventory
1232
to_abs = self.abspath(to_name)
1233
if not isdir(to_abs):
1234
raise BzrError("destination %r is not a directory" % to_abs)
1235
if not tree.has_filename(to_name):
1236
raise BzrError("destination %r not in working directory" % to_abs)
1237
to_dir_id = inv.path2id(to_name)
1238
if to_dir_id == None and to_name != '':
1239
raise BzrError("destination %r is not a versioned directory" % to_name)
1240
to_dir_ie = inv[to_dir_id]
1241
if to_dir_ie.kind not in ('directory', 'root_directory'):
1242
raise BzrError("destination %r is not a directory" % to_abs)
1244
to_idpath = inv.get_idpath(to_dir_id)
1246
for f in from_paths:
1247
if not tree.has_filename(f):
1248
raise BzrError("%r does not exist in working tree" % f)
1249
f_id = inv.path2id(f)
1251
raise BzrError("%r is not versioned" % f)
1252
name_tail = splitpath(f)[-1]
1253
dest_path = appendpath(to_name, name_tail)
1254
if tree.has_filename(dest_path):
1255
raise BzrError("destination %r already exists" % dest_path)
1256
if f_id in to_idpath:
1257
raise BzrError("can't move %r to a subdirectory of itself" % f)
1259
# OK, so there's a race here, it's possible that someone will
1260
# create a file in this interval and then the rename might be
1261
# left half-done. But we should have caught most problems.
1263
for f in from_paths:
1264
name_tail = splitpath(f)[-1]
1265
dest_path = appendpath(to_name, name_tail)
1266
result.append((f, dest_path))
1267
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1269
rename(self.abspath(f), self.abspath(dest_path))
1271
raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
1272
["rename rolled back"])
1274
self.working_tree()._write_inventory(inv)
1277
1019
def get_parent(self):
1278
1020
"""See Branch.get_parent."""
1312
1054
def tree_config(self):
1313
1055
return TreeConfig(self)
1315
def check_revno(self, revno):
1317
Check whether a revno corresponds to any revision.
1318
Zero (the NULL revision) is considered valid.
1321
self.check_real_revno(revno)
1323
def check_real_revno(self, revno):
1325
Check whether a revno corresponds to a real revision.
1326
Zero (the NULL revision) is considered invalid
1328
if revno < 1 or revno > self.revno():
1329
raise InvalidRevisionNumber(revno)
1331
1057
def sign_revision(self, revision_id, gpg_strategy):
1332
1058
"""See Branch.sign_revision."""
1333
1059
plaintext = Testament.from_revision(self, revision_id).as_short_text()
1411
1137
filename = head
1416
def gen_file_id(name):
1417
"""Return new file id.
1419
This should probably generate proper UUIDs, but for the moment we
1420
cope with just randomness because running uuidgen every time is
1423
from binascii import hexlify
1424
from time import time
1426
# get last component
1427
idx = name.rfind('/')
1429
name = name[idx+1 : ]
1430
idx = name.rfind('\\')
1432
name = name[idx+1 : ]
1434
# make it not a hidden file
1435
name = name.lstrip('.')
1437
# remove any wierd characters; we don't escape them but rather
1438
# just pull them out
1439
name = re.sub(r'[^\w.]', '', name)
1441
s = hexlify(rand_bytes(8))
1442
return '-'.join((name, compact_date(time()), s))
1446
"""Return a new tree-root file id."""
1447
return gen_file_id('TREE_ROOT')