26
26
from trace import mutter, note
27
27
from tree import Tree, EmptyTree, RevisionTree, WorkingTree
28
28
from inventory import InventoryEntry, Inventory
29
from osutils import isdir, quotefn, isfile, uuid, sha_file, username, chomp, \
29
from osutils import isdir, quotefn, isfile, uuid, sha_file, username, \
30
30
format_date, compact_date, pumpfile, user_email, rand_bytes, splitpath, \
31
joinpath, sha_string, file_kind, local_time_offset
31
joinpath, sha_string, file_kind, local_time_offset, appendpath
32
32
from store import ImmutableStore
33
33
from revision import Revision
34
from errors import bailout
34
from errors import bailout, BzrError
35
35
from textui import show_status
36
36
from diff import diff_trees
75
74
"""Branch holding a history of revisions.
77
:todo: Perhaps use different stores for different classes of object,
78
so that we can keep track of how much space each one uses,
79
or garbage-collect them.
81
:todo: Add a RemoteBranch subclass. For the basic case of read-only
82
HTTP access this should be very easy by,
83
just redirecting controlfile access into HTTP requests.
84
We would need a RemoteStore working similarly.
86
:todo: Keep the on-disk branch locked while the object exists.
88
:todo: mkdir() method.
77
Base directory of the branch.
90
def __init__(self, base, init=False, find_root=True):
81
def __init__(self, base, init=False, find_root=True, lock_mode='w'):
91
82
"""Create new branch object at a particular location.
93
:param base: Base directory for the branch.
84
base -- Base directory for the branch.
95
:param init: If True, create new control files in a previously
86
init -- If True, create new control files in a previously
96
87
unversioned directory. If False, the branch must already
99
:param find_root: If true and init is false, find the root of the
90
find_root -- If true and init is false, find the root of the
100
91
existing branch containing base.
102
93
In the test suite, creation of new trees is tested using the
127
119
__repr__ = __str__
123
def lock(self, mode='w'):
124
"""Lock the on-disk branch, excluding other processes."""
130
om = os.O_WRONLY | os.O_CREAT
135
raise BzrError("invalid locking mode %r" % mode)
137
# XXX: Old branches might not have the lock file, and
138
# won't get one until someone does a write-mode command on
139
# them or creates it by hand.
141
lockfile = os.open(self.controlfilename('branch-lock'), om)
142
fcntl.lockf(lockfile, lm)
144
fcntl.lockf(lockfile, fcntl.LOCK_UN)
146
self._lockmode = None
148
self._lockmode = mode
150
warning("please write a locking method for platform %r" % sys.platform)
152
self._lockmode = None
154
self._lockmode = mode
157
def _need_readlock(self):
158
if self._lockmode not in ['r', 'w']:
159
raise BzrError('need read lock on branch, only have %r' % self._lockmode)
161
def _need_writelock(self):
162
if self._lockmode not in ['w']:
163
raise BzrError('need write lock on branch, only have %r' % self._lockmode)
130
166
def abspath(self, name):
131
167
"""Return absolute filename for something in the branch"""
132
168
return os.path.join(self.base, name)
155
191
def controlfile(self, file_or_path, mode='r'):
156
"""Open a control file for this branch"""
157
return file(self.controlfilename(file_or_path), mode)
192
"""Open a control file for this branch.
194
There are two classes of file in the control directory: text
195
and binary. binary files are untranslated byte streams. Text
196
control files are stored with Unix newlines and in UTF-8, even
197
if the platform or locale defaults are different.
200
fn = self.controlfilename(file_or_path)
202
if mode == 'rb' or mode == 'wb':
203
return file(fn, mode)
204
elif mode == 'r' or mode == 'w':
205
# open in binary mode anyhow so there's no newline translation;
206
# codecs uses line buffering by default; don't want that.
208
return codecs.open(fn, mode + 'b', 'utf-8',
211
raise BzrError("invalid controlfile mode %r" % mode)
160
215
def _make_control(self):
541
622
## TODO: Also calculate and store the inventory SHA1
542
623
mutter("committing patch r%d" % (self.revno() + 1))
544
mutter("append to revision-history")
545
self.controlfile('revision-history', 'at').write(rev_id + '\n')
626
self.append_revision(rev_id)
629
note("commited r%d" % self.revno())
632
def append_revision(self, revision_id):
633
mutter("add {%s} to revision-history" % revision_id)
634
rev_history = self.revision_history()
636
tmprhname = self.controlfilename('revision-history.tmp')
637
rhname = self.controlfilename('revision-history')
639
f = file(tmprhname, 'wt')
640
rev_history.append(revision_id)
641
f.write('\n'.join(rev_history))
645
if sys.platform == 'win32':
647
os.rename(tmprhname, rhname)
550
651
def get_revision(self, revision_id):
551
652
"""Return the Revision object for a named revision"""
653
self._need_readlock()
552
654
r = Revision.read_xml(self.revision_store[revision_id])
553
655
assert r.revision_id == revision_id
689
797
for l in rev.message.split('\n'):
800
if verbose == True and precursor != None:
801
print 'changed files:'
802
tree = self.revision_tree(p)
803
prevtree = self.revision_tree(precursor)
805
for file_state, fid, old_name, new_name, kind in \
806
diff_trees(prevtree, tree, ):
807
if file_state == 'A' or file_state == 'M':
808
show_status(file_state, kind, new_name)
809
elif file_state == 'D':
810
show_status(file_state, kind, old_name)
811
elif file_state == 'R':
812
show_status(file_state, kind,
813
old_name + ' => ' + new_name)
697
def show_status(branch, show_all=False):
819
def rename_one(self, from_rel, to_rel):
822
This can change the directory or the filename or both.
824
self._need_writelock()
825
tree = self.working_tree()
827
if not tree.has_filename(from_rel):
828
bailout("can't rename: old working file %r does not exist" % from_rel)
829
if tree.has_filename(to_rel):
830
bailout("can't rename: new working file %r already exists" % to_rel)
832
file_id = inv.path2id(from_rel)
834
bailout("can't rename: old name %r is not versioned" % from_rel)
836
if inv.path2id(to_rel):
837
bailout("can't rename: new name %r is already versioned" % to_rel)
839
to_dir, to_tail = os.path.split(to_rel)
840
to_dir_id = inv.path2id(to_dir)
841
if to_dir_id == None and to_dir != '':
842
bailout("can't determine destination directory id for %r" % to_dir)
844
mutter("rename_one:")
845
mutter(" file_id {%s}" % file_id)
846
mutter(" from_rel %r" % from_rel)
847
mutter(" to_rel %r" % to_rel)
848
mutter(" to_dir %r" % to_dir)
849
mutter(" to_dir_id {%s}" % to_dir_id)
851
inv.rename(file_id, to_dir_id, to_tail)
853
print "%s => %s" % (from_rel, to_rel)
855
from_abs = self.abspath(from_rel)
856
to_abs = self.abspath(to_rel)
858
os.rename(from_abs, to_abs)
860
bailout("failed to rename %r to %r: %s"
861
% (from_abs, to_abs, e[1]),
862
["rename rolled back"])
864
self._write_inventory(inv)
868
def move(self, from_paths, to_name):
871
to_name must exist as a versioned directory.
873
If to_name exists and is a directory, the files are moved into
874
it, keeping their old names. If it is a directory,
876
Note that to_name is only the last component of the new name;
877
this doesn't change the directory.
879
self._need_writelock()
880
## TODO: Option to move IDs only
881
assert not isinstance(from_paths, basestring)
882
tree = self.working_tree()
884
to_abs = self.abspath(to_name)
885
if not isdir(to_abs):
886
bailout("destination %r is not a directory" % to_abs)
887
if not tree.has_filename(to_name):
888
bailout("destination %r not in working directory" % to_abs)
889
to_dir_id = inv.path2id(to_name)
890
if to_dir_id == None and to_name != '':
891
bailout("destination %r is not a versioned directory" % to_name)
892
to_dir_ie = inv[to_dir_id]
893
if to_dir_ie.kind not in ('directory', 'root_directory'):
894
bailout("destination %r is not a directory" % to_abs)
896
to_idpath = Set(inv.get_idpath(to_dir_id))
899
if not tree.has_filename(f):
900
bailout("%r does not exist in working tree" % f)
901
f_id = inv.path2id(f)
903
bailout("%r is not versioned" % f)
904
name_tail = splitpath(f)[-1]
905
dest_path = appendpath(to_name, name_tail)
906
if tree.has_filename(dest_path):
907
bailout("destination %r already exists" % dest_path)
908
if f_id in to_idpath:
909
bailout("can't move %r to a subdirectory of itself" % f)
911
# OK, so there's a race here, it's possible that someone will
912
# create a file in this interval and then the rename might be
913
# left half-done. But we should have caught most problems.
916
name_tail = splitpath(f)[-1]
917
dest_path = appendpath(to_name, name_tail)
918
print "%s => %s" % (f, dest_path)
919
inv.rename(inv.path2id(f), to_dir_id, name_tail)
921
os.rename(self.abspath(f), self.abspath(dest_path))
923
bailout("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
924
["rename rolled back"])
926
self._write_inventory(inv)
930
def show_status(self, show_all=False):
698
931
"""Display single-line status for non-ignored working files.
700
933
The list is show sorted in order by file name.