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
34
from errors import bailout
63
88
:todo: mkdir() method.
65
def __init__(self, base, init=False):
90
def __init__(self, base, init=False, find_root=True):
66
91
"""Create new branch object at a particular location.
68
93
:param base: Base directory for the branch.
70
95
:param init: If True, create new control files in a previously
71
96
unversioned directory. If False, the branch must already
99
:param find_root: If true and init is false, find the root of the
100
existing branch containing base.
74
102
In the test suite, creation of new trees is tested using the
75
103
`ScratchBranch` class.
77
self.base = os.path.realpath(base)
106
self.base = os.path.realpath(base)
79
107
self._make_control()
109
self.base = find_branch_root(base)
111
self.base = os.path.realpath(base)
81
112
if not isdir(self.controlfilename('.')):
82
113
bailout("not a bzr branch: %s" % quotefn(base),
83
114
['use "bzr init" to initialize a new working tree',
84
115
'current bzr can only operate from top-of-tree'])
87
118
self.text_store = ImmutableStore(self.controlfilename('text-store'))
88
119
self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
136
180
In the future, we might need different in-memory Branch
137
181
classes to support downlevel branches. But not yet.
139
# read in binary mode to detect newline wierdness.
183
# This ignores newlines so that we can open branches created
184
# on Windows from Linux and so on. I think it might be better
185
# to always make all internal files in unix format.
140
186
fmt = self.controlfile('branch-format', 'rb').read()
187
fmt.replace('\r\n', '')
141
188
if fmt != BZR_BRANCH_FORMAT:
142
189
bailout('sorry, branch format %r not supported' % fmt,
143
190
['use a different bzr version',
229
280
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))
282
fullpath = os.path.normpath(self.abspath(f))
285
kind = file_kind(fullpath)
287
# maybe something better?
288
bailout('cannot add: not a regular file or directory: %s' % quotefn(f))
290
if kind != 'file' and kind != 'directory':
291
bailout('cannot add: not a regular file or directory: %s' % quotefn(f))
293
file_id = gen_file_id(f)
294
inv.add_path(f, kind=kind, file_id=file_id)
253
297
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))
299
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
257
301
self._write_inventory(inv)
304
def print_file(self, file, revno):
305
"""Print `file` to stdout."""
306
tree = self.revision_tree(self.lookup_revision(revno))
307
# use inventory as it was in that revision
308
file_id = tree.inventory.path2id(file)
310
bailout("%r is not present in revision %d" % (file, revno))
311
tree.print_file(file_id)
261
314
def remove(self, files, verbose=False):
262
315
"""Mark nominated files for removal from the inventory.
714
def rename_one(self, from_rel, to_rel):
715
tree = self.working_tree()
717
if not tree.has_filename(from_rel):
718
bailout("can't rename: old working file %r does not exist" % from_rel)
719
if tree.has_filename(to_rel):
720
bailout("can't rename: new working file %r already exists" % to_rel)
722
file_id = inv.path2id(from_rel)
724
bailout("can't rename: old name %r is not versioned" % from_rel)
726
if inv.path2id(to_rel):
727
bailout("can't rename: new name %r is already versioned" % to_rel)
729
to_dir, to_tail = os.path.split(to_rel)
730
to_dir_id = inv.path2id(to_dir)
731
if to_dir_id == None and to_dir != '':
732
bailout("can't determine destination directory id for %r" % to_dir)
734
mutter("rename_one:")
735
mutter(" file_id {%s}" % file_id)
736
mutter(" from_rel %r" % from_rel)
737
mutter(" to_rel %r" % to_rel)
738
mutter(" to_dir %r" % to_dir)
739
mutter(" to_dir_id {%s}" % to_dir_id)
741
inv.rename(file_id, to_dir_id, to_tail)
743
print "%s => %s" % (from_rel, to_rel)
745
from_abs = self.abspath(from_rel)
746
to_abs = self.abspath(to_rel)
748
os.rename(from_abs, to_abs)
750
bailout("failed to rename %r to %r: %s"
751
% (from_abs, to_abs, e[1]),
752
["rename rolled back"])
754
self._write_inventory(inv)
758
def move(self, from_paths, to_name):
761
to_name must exist as a versioned directory.
763
If to_name exists and is a directory, the files are moved into
764
it, keeping their old names. If it is a directory,
766
Note that to_name is only the last component of the new name;
767
this doesn't change the directory.
769
## TODO: Option to move IDs only
770
assert not isinstance(from_paths, basestring)
771
tree = self.working_tree()
773
to_abs = self.abspath(to_name)
774
if not isdir(to_abs):
775
bailout("destination %r is not a directory" % to_abs)
776
if not tree.has_filename(to_name):
777
bailout("destination %r not in working directory" % to_abs)
778
to_dir_id = inv.path2id(to_name)
779
if to_dir_id == None and to_name != '':
780
bailout("destination %r is not a versioned directory" % to_name)
781
to_dir_ie = inv[to_dir_id]
782
if to_dir_ie.kind not in ('directory', 'root_directory'):
783
bailout("destination %r is not a directory" % to_abs)
785
to_idpath = Set(inv.get_idpath(to_dir_id))
788
if not tree.has_filename(f):
789
bailout("%r does not exist in working tree" % f)
790
f_id = inv.path2id(f)
792
bailout("%r is not versioned" % f)
793
name_tail = splitpath(f)[-1]
794
dest_path = appendpath(to_name, name_tail)
795
if tree.has_filename(dest_path):
796
bailout("destination %r already exists" % dest_path)
797
if f_id in to_idpath:
798
bailout("can't move %r to a subdirectory of itself" % f)
800
# OK, so there's a race here, it's possible that someone will
801
# create a file in this interval and then the rename might be
802
# left half-done. But we should have caught most problems.
805
name_tail = splitpath(f)[-1]
806
dest_path = appendpath(to_name, name_tail)
807
print "%s => %s" % (f, dest_path)
808
inv.rename(inv.path2id(f), to_dir_id, name_tail)
810
os.rename(self.abspath(f), self.abspath(dest_path))
812
bailout("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
813
["rename rolled back"])
815
self._write_inventory(inv)
659
819
def show_status(branch, show_all=False):
660
820
"""Display single-line status for non-ignored working files.
734
894
If any files are listed, they are created in the working copy.
736
896
Branch.__init__(self, tempfile.mkdtemp(), init=True)
898
os.mkdir(self.abspath(d))
738
901
file(os.path.join(self.base, f), 'w').write('content of %s' % f)
741
904
def __del__(self):
742
905
"""Destroy the test branch, removing the scratch directory."""
743
shutil.rmtree(self.base)
907
shutil.rmtree(self.base)
909
# Work around for shutil.rmtree failing on Windows when
910
# readonly files are encountered
911
for root, dirs, files in os.walk(self.base, topdown=False):
913
os.chmod(os.path.join(root, name), 0700)
914
shutil.rmtree(self.base)