67
67
raise NotImplementedError('find_branch() is not supported anymore, '
68
68
'please use one of the new branch constructors')
71
def needs_read_lock(unbound):
72
"""Decorate unbound to take out and release a read lock."""
73
def decorated(self, *args, **kwargs):
76
return unbound(self, *args, **kwargs)
82
def needs_write_lock(unbound):
83
"""Decorate unbound to take out and release a write lock."""
84
def decorated(self, *args, **kwargs):
87
return unbound(self, *args, **kwargs)
70
92
######################################################################
507
530
entry.parent_id = inv.root.file_id
508
531
self._write_inventory(inv)
510
534
def read_working_inventory(self):
511
535
"""Read the working inventory."""
514
# ElementTree does its own conversion from UTF-8, so open in
516
f = self.controlfile('inventory', 'rb')
517
return bzrlib.xml5.serializer_v5.read_inventory(f)
536
# ElementTree does its own conversion from UTF-8, so open in
538
f = self.controlfile('inventory', 'rb')
539
return bzrlib.xml5.serializer_v5.read_inventory(f)
522
542
def _write_inventory(self, inv):
523
543
"""Update the working inventory.
526
546
will be committed to the next revision.
528
548
from cStringIO import StringIO
532
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
534
# Transport handles atomicity
535
self.put_controlfile('inventory', sio)
550
bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
552
# Transport handles atomicity
553
self.put_controlfile('inventory', sio)
539
555
mutter('wrote working inventory')
541
557
inventory = property(read_working_inventory, _write_inventory, None,
542
558
"""Inventory for the working copy.""")
544
561
def add(self, files, ids=None):
545
562
"""Make files versioned.
577
594
assert(len(ids) == len(files))
581
inv = self.read_working_inventory()
582
for f,file_id in zip(files, ids):
583
if is_control_file(f):
584
raise BzrError("cannot add control file %s" % quotefn(f))
589
raise BzrError("cannot add top-level %r" % f)
591
fullpath = os.path.normpath(self.abspath(f))
594
kind = file_kind(fullpath)
596
# maybe something better?
597
raise BzrError('cannot add: not a regular file, symlink or directory: %s' % quotefn(f))
599
if not InventoryEntry.versionable_kind(kind):
600
raise BzrError('cannot add: not a versionable file ('
601
'i.e. regular file, symlink or directory): %s' % quotefn(f))
604
file_id = gen_file_id(f)
605
inv.add_path(f, kind=kind, file_id=file_id)
607
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
609
self._write_inventory(inv)
596
inv = self.read_working_inventory()
597
for f,file_id in zip(files, ids):
598
if is_control_file(f):
599
raise BzrError("cannot add control file %s" % quotefn(f))
604
raise BzrError("cannot add top-level %r" % f)
606
fullpath = os.path.normpath(self.abspath(f))
609
kind = file_kind(fullpath)
611
# maybe something better?
612
raise BzrError('cannot add: not a regular file, symlink or directory: %s' % quotefn(f))
614
if not InventoryEntry.versionable_kind(kind):
615
raise BzrError('cannot add: not a versionable file ('
616
'i.e. regular file, symlink or directory): %s' % quotefn(f))
619
file_id = gen_file_id(f)
620
inv.add_path(f, kind=kind, file_id=file_id)
622
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
624
self._write_inventory(inv)
614
627
def print_file(self, file, revno):
615
628
"""Print `file` to stdout."""
618
tree = self.revision_tree(self.get_rev_id(revno))
619
# use inventory as it was in that revision
620
file_id = tree.inventory.path2id(file)
622
raise BzrError("%r is not present in revision %s" % (file, revno))
623
tree.print_file(file_id)
628
def remove(self, files, verbose=False):
629
"""Mark nominated files for removal from the inventory.
631
This does not remove their text. This does not run on
633
TODO: Refuse to remove modified files unless --force is given?
635
TODO: Do something useful with directories.
637
TODO: Should this remove the text or not? Tough call; not
638
removing may be useful and the user can just use use rm, and
639
is the opposite of add. Removing it is consistent with most
640
other tools. Maybe an option.
642
## TODO: Normalize names
643
## TODO: Remove nested loops; better scalability
644
if isinstance(files, basestring):
650
tree = self.working_tree()
653
# do this before any modifications
657
raise BzrError("cannot remove unversioned file %s" % quotefn(f))
658
mutter("remove inventory entry %s {%s}" % (quotefn(f), fid))
660
# having remove it, it must be either ignored or unknown
661
if tree.is_ignored(f):
665
show_status(new_status, inv[fid].kind, quotefn(f))
668
self._write_inventory(inv)
629
tree = self.revision_tree(self.get_rev_id(revno))
630
# use inventory as it was in that revision
631
file_id = tree.inventory.path2id(file)
633
raise BzrError("%r is not present in revision %s" % (file, revno))
634
tree.print_file(file_id)
672
636
# FIXME: this doesn't need to be a branch method
673
637
def set_inventory(self, new_inventory_list):
701
665
>>> list(b.unknowns())
667
>>> WorkingTree(b.base, b).remove('foo')
704
668
>>> list(b.unknowns())
707
671
return self.working_tree().unknowns()
710
674
def append_revision(self, *revision_ids):
711
675
for revision_id in revision_ids:
712
676
mutter("add {%s} to revision-history" % revision_id)
715
rev_history = self.revision_history()
716
rev_history.extend(revision_ids)
717
self.put_controlfile('revision-history', '\n'.join(rev_history))
677
rev_history = self.revision_history()
678
rev_history.extend(revision_ids)
679
self.put_controlfile('revision-history', '\n'.join(rev_history))
721
681
def has_revision(self, revision_id):
722
682
"""True if this branch has a copy of the revision.
726
686
return (revision_id is None
727
687
or self.revision_store.has_id(revision_id))
729
690
def get_revision_xml_file(self, revision_id):
730
691
"""Return XML file object for revision object."""
731
692
if not revision_id or not isinstance(revision_id, basestring):
732
693
raise InvalidRevisionId(revision_id)
737
return self.revision_store.get(revision_id)
738
except (IndexError, KeyError):
739
raise bzrlib.errors.NoSuchRevision(self, revision_id)
695
return self.revision_store.get(revision_id)
696
except (IndexError, KeyError):
697
raise bzrlib.errors.NoSuchRevision(self, revision_id)
744
700
get_revision_xml = get_revision_xml_file
838
794
return self.get_inventory(revision_id)
840
797
def revision_history(self):
841
798
"""Return sequence of revision hashes on to this branch."""
844
transaction = self.get_transaction()
845
history = transaction.map.find_revision_history()
846
if history is not None:
847
mutter("cache hit for revision-history in %s", self)
849
history = [l.rstrip('\r\n') for l in
850
self.controlfile('revision-history', 'r').readlines()]
851
transaction.map.add_revision_history(history)
852
# this call is disabled because revision_history is
853
# not really an object yet, and the transaction is for objects.
854
# transaction.register_clean(history, precious=True)
799
transaction = self.get_transaction()
800
history = transaction.map.find_revision_history()
801
if history is not None:
802
mutter("cache hit for revision-history in %s", self)
855
803
return list(history)
804
history = [l.rstrip('\r\n') for l in
805
self.controlfile('revision-history', 'r').readlines()]
806
transaction.map.add_revision_history(history)
807
# this call is disabled because revision_history is
808
# not really an object yet, and the transaction is for objects.
809
# transaction.register_clean(history, precious=True)
860
813
"""Return current revision number for this branch.
1010
961
return self.revision_tree(self.last_revision())
1013
964
def rename_one(self, from_rel, to_rel):
1014
965
"""Rename one file.
1016
967
This can change the directory or the filename or both.
969
tree = self.working_tree()
971
if not tree.has_filename(from_rel):
972
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
973
if tree.has_filename(to_rel):
974
raise BzrError("can't rename: new working file %r already exists" % to_rel)
976
file_id = inv.path2id(from_rel)
978
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
980
if inv.path2id(to_rel):
981
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
983
to_dir, to_tail = os.path.split(to_rel)
984
to_dir_id = inv.path2id(to_dir)
985
if to_dir_id == None and to_dir != '':
986
raise BzrError("can't determine destination directory id for %r" % to_dir)
988
mutter("rename_one:")
989
mutter(" file_id {%s}" % file_id)
990
mutter(" from_rel %r" % from_rel)
991
mutter(" to_rel %r" % to_rel)
992
mutter(" to_dir %r" % to_dir)
993
mutter(" to_dir_id {%s}" % to_dir_id)
995
inv.rename(file_id, to_dir_id, to_tail)
997
from_abs = self.abspath(from_rel)
998
to_abs = self.abspath(to_rel)
1020
tree = self.working_tree()
1021
inv = tree.inventory
1022
if not tree.has_filename(from_rel):
1023
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1024
if tree.has_filename(to_rel):
1025
raise BzrError("can't rename: new working file %r already exists" % to_rel)
1027
file_id = inv.path2id(from_rel)
1029
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1031
if inv.path2id(to_rel):
1032
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1034
to_dir, to_tail = os.path.split(to_rel)
1035
to_dir_id = inv.path2id(to_dir)
1036
if to_dir_id == None and to_dir != '':
1037
raise BzrError("can't determine destination directory id for %r" % to_dir)
1039
mutter("rename_one:")
1040
mutter(" file_id {%s}" % file_id)
1041
mutter(" from_rel %r" % from_rel)
1042
mutter(" to_rel %r" % to_rel)
1043
mutter(" to_dir %r" % to_dir)
1044
mutter(" to_dir_id {%s}" % to_dir_id)
1046
inv.rename(file_id, to_dir_id, to_tail)
1048
from_abs = self.abspath(from_rel)
1049
to_abs = self.abspath(to_rel)
1051
rename(from_abs, to_abs)
1053
raise BzrError("failed to rename %r to %r: %s"
1054
% (from_abs, to_abs, e[1]),
1055
["rename rolled back"])
1057
self._write_inventory(inv)
1000
rename(from_abs, to_abs)
1002
raise BzrError("failed to rename %r to %r: %s"
1003
% (from_abs, to_abs, e[1]),
1004
["rename rolled back"])
1006
self._write_inventory(inv)
1062
1009
def move(self, from_paths, to_name):
1063
1010
"""Rename files.
1074
1021
entry that is moved.
1079
## TODO: Option to move IDs only
1080
assert not isinstance(from_paths, basestring)
1081
tree = self.working_tree()
1082
inv = tree.inventory
1083
to_abs = self.abspath(to_name)
1084
if not isdir(to_abs):
1085
raise BzrError("destination %r is not a directory" % to_abs)
1086
if not tree.has_filename(to_name):
1087
raise BzrError("destination %r not in working directory" % to_abs)
1088
to_dir_id = inv.path2id(to_name)
1089
if to_dir_id == None and to_name != '':
1090
raise BzrError("destination %r is not a versioned directory" % to_name)
1091
to_dir_ie = inv[to_dir_id]
1092
if to_dir_ie.kind not in ('directory', 'root_directory'):
1093
raise BzrError("destination %r is not a directory" % to_abs)
1095
to_idpath = inv.get_idpath(to_dir_id)
1097
for f in from_paths:
1098
if not tree.has_filename(f):
1099
raise BzrError("%r does not exist in working tree" % f)
1100
f_id = inv.path2id(f)
1102
raise BzrError("%r is not versioned" % f)
1103
name_tail = splitpath(f)[-1]
1104
dest_path = appendpath(to_name, name_tail)
1105
if tree.has_filename(dest_path):
1106
raise BzrError("destination %r already exists" % dest_path)
1107
if f_id in to_idpath:
1108
raise BzrError("can't move %r to a subdirectory of itself" % f)
1110
# OK, so there's a race here, it's possible that someone will
1111
# create a file in this interval and then the rename might be
1112
# left half-done. But we should have caught most problems.
1114
for f in from_paths:
1115
name_tail = splitpath(f)[-1]
1116
dest_path = appendpath(to_name, name_tail)
1117
result.append((f, dest_path))
1118
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1120
rename(self.abspath(f), self.abspath(dest_path))
1122
raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
1123
["rename rolled back"])
1125
self._write_inventory(inv)
1024
## TODO: Option to move IDs only
1025
assert not isinstance(from_paths, basestring)
1026
tree = self.working_tree()
1027
inv = tree.inventory
1028
to_abs = self.abspath(to_name)
1029
if not isdir(to_abs):
1030
raise BzrError("destination %r is not a directory" % to_abs)
1031
if not tree.has_filename(to_name):
1032
raise BzrError("destination %r not in working directory" % to_abs)
1033
to_dir_id = inv.path2id(to_name)
1034
if to_dir_id == None and to_name != '':
1035
raise BzrError("destination %r is not a versioned directory" % to_name)
1036
to_dir_ie = inv[to_dir_id]
1037
if to_dir_ie.kind not in ('directory', 'root_directory'):
1038
raise BzrError("destination %r is not a directory" % to_abs)
1040
to_idpath = inv.get_idpath(to_dir_id)
1042
for f in from_paths:
1043
if not tree.has_filename(f):
1044
raise BzrError("%r does not exist in working tree" % f)
1045
f_id = inv.path2id(f)
1047
raise BzrError("%r is not versioned" % f)
1048
name_tail = splitpath(f)[-1]
1049
dest_path = appendpath(to_name, name_tail)
1050
if tree.has_filename(dest_path):
1051
raise BzrError("destination %r already exists" % dest_path)
1052
if f_id in to_idpath:
1053
raise BzrError("can't move %r to a subdirectory of itself" % f)
1055
# OK, so there's a race here, it's possible that someone will
1056
# create a file in this interval and then the rename might be
1057
# left half-done. But we should have caught most problems.
1059
for f in from_paths:
1060
name_tail = splitpath(f)[-1]
1061
dest_path = appendpath(to_name, name_tail)
1062
result.append((f, dest_path))
1063
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1065
rename(self.abspath(f), self.abspath(dest_path))
1067
raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
1068
["rename rolled back"])
1070
self._write_inventory(inv)
1258
1192
raise InvalidRevisionNumber(revno)
1260
1194
def sign_revision(self, revision_id, gpg_strategy):
1263
plaintext = Testament.from_revision(self, revision_id).as_short_text()
1264
self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)),
1195
plaintext = Testament.from_revision(self, revision_id).as_short_text()
1196
self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1199
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1200
self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)),
1270
1204
class ScratchBranch(_Branch):