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
188
187
"""Return the id of this branches root"""
189
188
raise NotImplementedError('get_root_id is abstract')
191
def set_root_id(self, file_id):
192
raise NotImplementedError('set_root_id is abstract')
194
def add(self, files, ids=None):
195
"""Make files versioned.
197
Note that the command line normally calls smart_add instead,
198
which can automatically recurse.
200
This puts the files in the Added state, so that they will be
201
recorded by the next commit.
204
List of paths to add, relative to the base of the tree.
207
If set, use these instead of automatically generated ids.
208
Must be the same length as the list of files, but may
209
contain None for ids that are to be autogenerated.
211
TODO: Perhaps have an option to add the ids even if the files do
214
TODO: Perhaps yield the ids and paths as they're added.
216
raise NotImplementedError('add is abstract')
218
190
def print_file(self, file, revno):
219
191
"""Print `file` to stdout."""
220
192
raise NotImplementedError('print_file is abstract')
223
"""Return all unknown files.
225
These are files in the working directory that are not versioned or
226
control files or ignored.
228
>>> from bzrlib.workingtree import WorkingTree
229
>>> b = ScratchBranch(files=['foo', 'foo~'])
230
>>> map(str, b.unknowns())
233
>>> list(b.unknowns())
235
>>> WorkingTree(b.base, b).remove('foo')
236
>>> list(b.unknowns())
239
raise NotImplementedError('unknowns is abstract')
241
194
def append_revision(self, *revision_ids):
242
195
raise NotImplementedError('append_revision is abstract')
638
592
self.storage.unlock()
639
593
LockableFiles.unlock(self)
642
def set_root_id(self, file_id):
643
"""See Branch.set_root_id."""
644
inv = self.working_tree().read_working_inventory()
645
orig_root_id = inv.root.file_id
646
del inv._byid[inv.root.file_id]
647
inv.root.file_id = file_id
648
inv._byid[inv.root.file_id] = inv.root
651
if entry.parent_id in (None, orig_root_id):
652
entry.parent_id = inv.root.file_id
653
self._write_inventory(inv)
656
def add(self, files, ids=None):
657
"""See Branch.add."""
658
# TODO: Re-adding a file that is removed in the working copy
659
# should probably put it back with the previous ID.
660
if isinstance(files, basestring):
661
assert(ids is None or isinstance(ids, basestring))
667
ids = [None] * len(files)
669
assert(len(ids) == len(files))
671
inv = self.working_tree().read_working_inventory()
672
for f,file_id in zip(files, ids):
673
if is_control_file(f):
674
raise BzrError("cannot add control file %s" % quotefn(f))
679
raise BzrError("cannot add top-level %r" % f)
681
fullpath = os.path.normpath(self.abspath(f))
684
kind = file_kind(fullpath)
686
# maybe something better?
687
raise BzrError('cannot add: not a regular file, symlink or directory: %s' % quotefn(f))
689
if not InventoryEntry.versionable_kind(kind):
690
raise BzrError('cannot add: not a versionable file ('
691
'i.e. regular file, symlink or directory): %s' % quotefn(f))
694
file_id = gen_file_id(f)
695
inv.add_path(f, kind=kind, file_id=file_id)
697
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
699
self.working_tree()._write_inventory(inv)
702
596
def print_file(self, file, revno):
703
597
"""See Branch.print_file."""
704
598
return self.storage.print_file(file, self.get_rev_id(revno))
707
"""See Branch.unknowns."""
708
return self.working_tree().unknowns()
710
600
@needs_write_lock
711
601
def append_revision(self, *revision_ids):
712
602
"""See Branch.append_revision."""
795
685
def working_tree(self):
796
686
"""See Branch.working_tree."""
797
687
from bzrlib.workingtree import WorkingTree
798
# TODO: In the future, perhaps WorkingTree should utilize Transport
799
# RobertCollins 20051003 - I don't think it should - working trees are
800
# much more complex to keep consistent than our careful .bzr subset.
801
# instead, we should say that working trees are local only, and optimise
803
688
if self._transport.base.find('://') != -1:
804
689
raise NoWorkingTree(self.base)
805
690
return WorkingTree(self.base, branch=self)
822
def rename_one(self, from_rel, to_rel):
823
"""See Branch.rename_one."""
824
tree = self.working_tree()
826
if not tree.has_filename(from_rel):
827
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
828
if tree.has_filename(to_rel):
829
raise BzrError("can't rename: new working file %r already exists" % to_rel)
831
file_id = inv.path2id(from_rel)
833
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
835
if inv.path2id(to_rel):
836
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
838
to_dir, to_tail = os.path.split(to_rel)
839
to_dir_id = inv.path2id(to_dir)
840
if to_dir_id == None and to_dir != '':
841
raise BzrError("can't determine destination directory id for %r" % to_dir)
843
mutter("rename_one:")
844
mutter(" file_id {%s}" % file_id)
845
mutter(" from_rel %r" % from_rel)
846
mutter(" to_rel %r" % to_rel)
847
mutter(" to_dir %r" % to_dir)
848
mutter(" to_dir_id {%s}" % to_dir_id)
850
inv.rename(file_id, to_dir_id, to_tail)
852
from_abs = self.abspath(from_rel)
853
to_abs = self.abspath(to_rel)
855
rename(from_abs, to_abs)
857
raise BzrError("failed to rename %r to %r: %s"
858
% (from_abs, to_abs, e[1]),
859
["rename rolled back"])
861
self.working_tree()._write_inventory(inv)
864
def move(self, from_paths, to_name):
865
"""See Branch.move."""
867
## TODO: Option to move IDs only
868
assert not isinstance(from_paths, basestring)
869
tree = self.working_tree()
871
to_abs = self.abspath(to_name)
872
if not isdir(to_abs):
873
raise BzrError("destination %r is not a directory" % to_abs)
874
if not tree.has_filename(to_name):
875
raise BzrError("destination %r not in working directory" % to_abs)
876
to_dir_id = inv.path2id(to_name)
877
if to_dir_id == None and to_name != '':
878
raise BzrError("destination %r is not a versioned directory" % to_name)
879
to_dir_ie = inv[to_dir_id]
880
if to_dir_ie.kind not in ('directory', 'root_directory'):
881
raise BzrError("destination %r is not a directory" % to_abs)
883
to_idpath = inv.get_idpath(to_dir_id)
886
if not tree.has_filename(f):
887
raise BzrError("%r does not exist in working tree" % f)
888
f_id = inv.path2id(f)
890
raise BzrError("%r is not versioned" % f)
891
name_tail = splitpath(f)[-1]
892
dest_path = appendpath(to_name, name_tail)
893
if tree.has_filename(dest_path):
894
raise BzrError("destination %r already exists" % dest_path)
895
if f_id in to_idpath:
896
raise BzrError("can't move %r to a subdirectory of itself" % f)
898
# OK, so there's a race here, it's possible that someone will
899
# create a file in this interval and then the rename might be
900
# left half-done. But we should have caught most problems.
903
name_tail = splitpath(f)[-1]
904
dest_path = appendpath(to_name, name_tail)
905
result.append((f, dest_path))
906
inv.rename(inv.path2id(f), to_dir_id, name_tail)
908
rename(self.abspath(f), self.abspath(dest_path))
910
raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
911
["rename rolled back"])
913
self.working_tree()._write_inventory(inv)
916
706
def get_parent(self):
917
707
"""See Branch.get_parent."""
1045
def gen_file_id(name):
1046
"""Return new file id.
1048
This should probably generate proper UUIDs, but for the moment we
1049
cope with just randomness because running uuidgen every time is
1052
from binascii import hexlify
1053
from time import time
1055
# get last component
1056
idx = name.rfind('/')
1058
name = name[idx+1 : ]
1059
idx = name.rfind('\\')
1061
name = name[idx+1 : ]
1063
# make it not a hidden file
1064
name = name.lstrip('.')
1066
# remove any wierd characters; we don't escape them but rather
1067
# just pull them out
1068
name = re.sub(r'[^\w.]', '', name)
1070
s = hexlify(rand_bytes(8))
1071
return '-'.join((name, compact_date(time()), s))
1075
"""Return a new tree-root file id."""
1076
return gen_file_id('TREE_ROOT')