29
28
from inventory import InventoryEntry, Inventory
30
29
from osutils import isdir, quotefn, isfile, uuid, sha_file, username, chomp, \
31
30
format_date, compact_date, pumpfile, user_email, rand_bytes, splitpath, \
32
joinpath, sha_string, file_kind, local_time_offset
31
joinpath, sha_string, file_kind, local_time_offset, appendpath
33
32
from store import ImmutableStore
34
33
from revision import Revision
35
from errors import bailout
34
from errors import bailout, BzrError
36
35
from textui import show_status
37
36
from diff import diff_trees
63
87
:todo: mkdir() method.
65
def __init__(self, base, init=False):
89
def __init__(self, base, init=False, find_root=True):
66
90
"""Create new branch object at a particular location.
68
92
:param base: Base directory for the branch.
70
94
:param init: If True, create new control files in a previously
71
95
unversioned directory. If False, the branch must already
98
:param find_root: If true and init is false, find the root of the
99
existing branch containing base.
74
101
In the test suite, creation of new trees is tested using the
75
102
`ScratchBranch` class.
77
self.base = os.path.realpath(base)
105
self.base = os.path.realpath(base)
79
106
self._make_control()
108
self.base = find_branch_root(base)
110
self.base = os.path.realpath(base)
81
111
if not isdir(self.controlfilename('.')):
82
112
bailout("not a bzr branch: %s" % quotefn(base),
83
113
['use "bzr init" to initialize a new working tree',
84
114
'current bzr can only operate from top-of-tree'])
87
117
self.text_store = ImmutableStore(self.controlfilename('text-store'))
88
118
self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
229
279
bailout("cannot add top-level %r" % f)
231
fullpath = os.path.normpath(self._rel(f))
235
elif isdir(fullpath):
238
bailout('cannot add: not a regular file or directory: %s' % quotefn(f))
241
parent_name = joinpath(fp[:-1])
242
mutter("lookup parent %r" % parent_name)
243
parent_id = inv.path2id(parent_name)
244
if parent_id == None:
245
bailout("cannot add: parent %r is not versioned"
250
file_id = _gen_file_id(fp[-1])
251
inv.add(InventoryEntry(file_id, fp[-1], kind=kind, parent_id=parent_id))
281
fullpath = os.path.normpath(self.abspath(f))
284
kind = file_kind(fullpath)
286
# maybe something better?
287
bailout('cannot add: not a regular file or directory: %s' % quotefn(f))
289
if kind != 'file' and kind != 'directory':
290
bailout('cannot add: not a regular file or directory: %s' % quotefn(f))
292
file_id = gen_file_id(f)
293
inv.add_path(f, kind=kind, file_id=file_id)
253
296
show_status('A', kind, quotefn(f))
255
mutter("add file %s file_id:{%s} kind=%r parent_id={%s}"
256
% (f, file_id, kind, parent_id))
298
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
257
300
self._write_inventory(inv)
303
def print_file(self, file, revno):
304
"""Print `file` to stdout."""
305
tree = self.revision_tree(self.lookup_revision(revno))
306
# use inventory as it was in that revision
307
file_id = tree.inventory.path2id(file)
309
bailout("%r is not present in revision %d" % (file, revno))
310
tree.print_file(file_id)
261
313
def remove(self, files, verbose=False):
262
314
"""Mark nominated files for removal from the inventory.
659
def show_status(branch, show_all=False):
715
def rename_one(self, from_rel, to_rel):
716
tree = self.working_tree()
718
if not tree.has_filename(from_rel):
719
bailout("can't rename: old working file %r does not exist" % from_rel)
720
if tree.has_filename(to_rel):
721
bailout("can't rename: new working file %r already exists" % to_rel)
723
file_id = inv.path2id(from_rel)
725
bailout("can't rename: old name %r is not versioned" % from_rel)
727
if inv.path2id(to_rel):
728
bailout("can't rename: new name %r is already versioned" % to_rel)
730
to_dir, to_tail = os.path.split(to_rel)
731
to_dir_id = inv.path2id(to_dir)
732
if to_dir_id == None and to_dir != '':
733
bailout("can't determine destination directory id for %r" % to_dir)
735
mutter("rename_one:")
736
mutter(" file_id {%s}" % file_id)
737
mutter(" from_rel %r" % from_rel)
738
mutter(" to_rel %r" % to_rel)
739
mutter(" to_dir %r" % to_dir)
740
mutter(" to_dir_id {%s}" % to_dir_id)
742
inv.rename(file_id, to_dir_id, to_tail)
744
print "%s => %s" % (from_rel, to_rel)
746
from_abs = self.abspath(from_rel)
747
to_abs = self.abspath(to_rel)
749
os.rename(from_abs, to_abs)
751
bailout("failed to rename %r to %r: %s"
752
% (from_abs, to_abs, e[1]),
753
["rename rolled back"])
755
self._write_inventory(inv)
759
def move(self, from_paths, to_name):
762
to_name must exist as a versioned directory.
764
If to_name exists and is a directory, the files are moved into
765
it, keeping their old names. If it is a directory,
767
Note that to_name is only the last component of the new name;
768
this doesn't change the directory.
770
## TODO: Option to move IDs only
771
assert not isinstance(from_paths, basestring)
772
tree = self.working_tree()
774
to_abs = self.abspath(to_name)
775
if not isdir(to_abs):
776
bailout("destination %r is not a directory" % to_abs)
777
if not tree.has_filename(to_name):
778
bailout("destination %r not in working directory" % to_abs)
779
to_dir_id = inv.path2id(to_name)
780
if to_dir_id == None and to_name != '':
781
bailout("destination %r is not a versioned directory" % to_name)
782
to_dir_ie = inv[to_dir_id]
783
if to_dir_ie.kind not in ('directory', 'root_directory'):
784
bailout("destination %r is not a directory" % to_abs)
786
to_idpath = Set(inv.get_idpath(to_dir_id))
789
if not tree.has_filename(f):
790
bailout("%r does not exist in working tree" % f)
791
f_id = inv.path2id(f)
793
bailout("%r is not versioned" % f)
794
name_tail = splitpath(f)[-1]
795
dest_path = appendpath(to_name, name_tail)
796
if tree.has_filename(dest_path):
797
bailout("destination %r already exists" % dest_path)
798
if f_id in to_idpath:
799
bailout("can't move %r to a subdirectory of itself" % f)
801
# OK, so there's a race here, it's possible that someone will
802
# create a file in this interval and then the rename might be
803
# left half-done. But we should have caught most problems.
806
name_tail = splitpath(f)[-1]
807
dest_path = appendpath(to_name, name_tail)
808
print "%s => %s" % (f, dest_path)
809
inv.rename(inv.path2id(f), to_dir_id, name_tail)
811
os.rename(self.abspath(f), self.abspath(dest_path))
813
bailout("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
814
["rename rolled back"])
816
self._write_inventory(inv)
820
def show_status(self, show_all=False):
660
821
"""Display single-line status for non-ignored working files.
662
823
The list is show sorted in order by file name.
734
893
If any files are listed, they are created in the working copy.
736
895
Branch.__init__(self, tempfile.mkdtemp(), init=True)
897
os.mkdir(self.abspath(d))
738
900
file(os.path.join(self.base, f), 'w').write('content of %s' % f)
741
903
def __del__(self):
742
904
"""Destroy the test branch, removing the scratch directory."""
743
shutil.rmtree(self.base)
906
shutil.rmtree(self.base)
908
# Work around for shutil.rmtree failing on Windows when
909
# readonly files are encountered
910
for root, dirs, files in os.walk(self.base, topdown=False):
912
os.chmod(os.path.join(root, name), 0700)
913
shutil.rmtree(self.base)