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.
660
587
if self._branch_format == 4:
661
self.inventory_store = get_store('inventory-store')
662
self.text_store = get_store('text-store')
663
self.revision_store = get_store('revision-store')
588
self.inventory_store = get_store(u'inventory-store')
589
self.text_store = get_store(u'text-store')
590
self.revision_store = get_store(u'revision-store')
664
591
elif self._branch_format == 5:
665
self.control_weaves = get_weave('')
666
self.weave_store = get_weave('weaves')
667
self.revision_store = get_store('revision-store', compressed=False)
592
self.control_weaves = get_weave(u'')
593
self.weave_store = get_weave(u'weaves')
594
self.revision_store = get_store(u'revision-store', compressed=False)
668
595
elif self._branch_format == 6:
669
self.control_weaves = get_weave('')
670
self.weave_store = get_weave('weaves', prefixed=True)
671
self.revision_store = get_store('revision-store', compressed=False,
596
self.control_weaves = get_weave(u'')
597
self.weave_store = get_weave(u'weaves', prefixed=True)
598
self.revision_store = get_store(u'revision-store', compressed=False,
673
600
self.revision_store.register_suffix('sig')
674
601
self._transaction = None
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
864
except (IndexError, KeyError):
999
865
raise bzrlib.errors.NoSuchRevision(self, revision_id)
1002
get_revision_xml = get_revision_xml_file
1004
867
def get_revision_xml(self, revision_id):
1005
868
"""See Branch.get_revision_xml."""
1006
return self.get_revision_xml_file(revision_id).read()
869
return self._get_revision_xml_file(revision_id).read()
1009
871
def get_revision(self, revision_id):
1010
872
"""See Branch.get_revision."""
1011
xml_file = self.get_revision_xml_file(revision_id)
873
xml_file = self._get_revision_xml_file(revision_id)
1014
876
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
988
def revision_tree(self, revision_id):
1147
989
"""See Branch.revision_tree."""
1148
990
# TODO: refactor this to use an existing revision object
1153
995
inv = self.get_revision_inventory(revision_id)
1154
996
return RevisionTree(self.weave_store, inv, revision_id)
998
def basis_tree(self):
999
"""See Branch.basis_tree."""
1001
revision_id = self.revision_history()[-1]
1002
xml = self.working_tree().read_basis_inventory(revision_id)
1003
inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
1004
return RevisionTree(self.weave_store, inv, revision_id)
1005
except (IndexError, NoSuchFile), e:
1006
return self.revision_tree(self.last_revision())
1156
1008
def working_tree(self):
1157
1009
"""See Branch.working_tree."""
1158
1010
from bzrlib.workingtree import WorkingTree
1159
# TODO: In the future, perhaps WorkingTree should utilize Transport
1160
# RobertCollins 20051003 - I don't think it should - working trees are
1161
# much more complex to keep consistent than our careful .bzr subset.
1162
# instead, we should say that working trees are local only, and optimise
1164
1011
if self._transport.base.find('://') != -1:
1165
1012
raise NoWorkingTree(self.base)
1166
1013
return WorkingTree(self.base, branch=self)
1170
1017
"""See Branch.pull."""
1171
1018
source.lock_read()
1173
old_history = self.revision_history()
1020
old_count = len(self.revision_history())
1175
1022
self.update_revisions(source)
1176
1023
except DivergedBranches:
1177
1024
if not overwrite:
1179
1026
self.set_revision_history(source.revision_history())
1180
new_history = self.revision_history()
1027
new_count = len(self.revision_history())
1028
return new_count - old_count
1182
1030
source.unlock()
1185
def rename_one(self, from_rel, to_rel):
1186
"""See Branch.rename_one."""
1187
tree = self.working_tree()
1188
inv = tree.inventory
1189
if not tree.has_filename(from_rel):
1190
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1191
if tree.has_filename(to_rel):
1192
raise BzrError("can't rename: new working file %r already exists" % to_rel)
1194
file_id = inv.path2id(from_rel)
1196
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1198
if inv.path2id(to_rel):
1199
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1201
to_dir, to_tail = os.path.split(to_rel)
1202
to_dir_id = inv.path2id(to_dir)
1203
if to_dir_id == None and to_dir != '':
1204
raise BzrError("can't determine destination directory id for %r" % to_dir)
1206
mutter("rename_one:")
1207
mutter(" file_id {%s}" % file_id)
1208
mutter(" from_rel %r" % from_rel)
1209
mutter(" to_rel %r" % to_rel)
1210
mutter(" to_dir %r" % to_dir)
1211
mutter(" to_dir_id {%s}" % to_dir_id)
1213
inv.rename(file_id, to_dir_id, to_tail)
1215
from_abs = self.abspath(from_rel)
1216
to_abs = self.abspath(to_rel)
1218
rename(from_abs, to_abs)
1220
raise BzrError("failed to rename %r to %r: %s"
1221
% (from_abs, to_abs, e[1]),
1222
["rename rolled back"])
1224
self.working_tree()._write_inventory(inv)
1227
def move(self, from_paths, to_name):
1228
"""See Branch.move."""
1230
## TODO: Option to move IDs only
1231
assert not isinstance(from_paths, basestring)
1232
tree = self.working_tree()
1233
inv = tree.inventory
1234
to_abs = self.abspath(to_name)
1235
if not isdir(to_abs):
1236
raise BzrError("destination %r is not a directory" % to_abs)
1237
if not tree.has_filename(to_name):
1238
raise BzrError("destination %r not in working directory" % to_abs)
1239
to_dir_id = inv.path2id(to_name)
1240
if to_dir_id == None and to_name != '':
1241
raise BzrError("destination %r is not a versioned directory" % to_name)
1242
to_dir_ie = inv[to_dir_id]
1243
if to_dir_ie.kind not in ('directory', 'root_directory'):
1244
raise BzrError("destination %r is not a directory" % to_abs)
1246
to_idpath = inv.get_idpath(to_dir_id)
1248
for f in from_paths:
1249
if not tree.has_filename(f):
1250
raise BzrError("%r does not exist in working tree" % f)
1251
f_id = inv.path2id(f)
1253
raise BzrError("%r is not versioned" % f)
1254
name_tail = splitpath(f)[-1]
1255
dest_path = appendpath(to_name, name_tail)
1256
if tree.has_filename(dest_path):
1257
raise BzrError("destination %r already exists" % dest_path)
1258
if f_id in to_idpath:
1259
raise BzrError("can't move %r to a subdirectory of itself" % f)
1261
# OK, so there's a race here, it's possible that someone will
1262
# create a file in this interval and then the rename might be
1263
# left half-done. But we should have caught most problems.
1265
for f in from_paths:
1266
name_tail = splitpath(f)[-1]
1267
dest_path = appendpath(to_name, name_tail)
1268
result.append((f, dest_path))
1269
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1271
rename(self.abspath(f), self.abspath(dest_path))
1273
raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
1274
["rename rolled back"])
1276
self.working_tree()._write_inventory(inv)
1279
1032
def get_parent(self):
1280
1033
"""See Branch.get_parent."""
1314
1067
def tree_config(self):
1315
1068
return TreeConfig(self)
1317
def check_revno(self, revno):
1319
Check whether a revno corresponds to any revision.
1320
Zero (the NULL revision) is considered valid.
1323
self.check_real_revno(revno)
1325
def check_real_revno(self, revno):
1327
Check whether a revno corresponds to a real revision.
1328
Zero (the NULL revision) is considered invalid
1330
if revno < 1 or revno > self.revno():
1331
raise InvalidRevisionNumber(revno)
1333
1070
def sign_revision(self, revision_id, gpg_strategy):
1334
1071
"""See Branch.sign_revision."""
1335
1072
plaintext = Testament.from_revision(self, revision_id).as_short_text()
1413
1150
filename = head
1418
def gen_file_id(name):
1419
"""Return new file id.
1421
This should probably generate proper UUIDs, but for the moment we
1422
cope with just randomness because running uuidgen every time is
1425
from binascii import hexlify
1426
from time import time
1428
# get last component
1429
idx = name.rfind('/')
1431
name = name[idx+1 : ]
1432
idx = name.rfind('\\')
1434
name = name[idx+1 : ]
1436
# make it not a hidden file
1437
name = name.lstrip('.')
1439
# remove any wierd characters; we don't escape them but rather
1440
# just pull them out
1441
name = re.sub(r'[^\w.]', '', name)
1443
s = hexlify(rand_bytes(8))
1444
return '-'.join((name, compact_date(time()), s))
1448
"""Return a new tree-root file id."""
1449
return gen_file_id('TREE_ROOT')