1
1
# Copyright (C) 2005, 2006 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
624
667
def initialize(self, a_bzrdir):
625
668
"""Create a branch of this format in a_bzrdir."""
626
mutter('creating branch in %s', a_bzrdir.transport.base)
669
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
627
670
branch_transport = a_bzrdir.get_branch_transport(self)
629
671
utf8_files = [('revision-history', ''),
630
672
('branch-name', ''),
633
branch_transport.put(lock_file, StringIO()) # TODO get the file mode from the bzrdir lock files., mode=file_mode)
634
control_files = LockableFiles(branch_transport, 'lock')
674
control_files = LockableFiles(branch_transport, 'lock', LockDir)
675
control_files.create_lock()
635
676
control_files.lock_write()
636
677
control_files.put_utf8('format', self.get_format_string())
744
788
really matter if it's on an nfs/smb/afs/coda/... share, as long as
745
789
it's writable, and can be accessed via the normal filesystem API.
747
# We actually expect this class to be somewhat short-lived; part of its
748
# purpose is to try to isolate what bits of the branch logic are tied to
749
# filesystem access, so that in a later step, we can extricate them to
750
# a separarte ("storage") class.
751
_inventory_weave = None
753
# Map some sort of prefix into a namespace
754
# stuff like "revno:10", "revid:", etc.
755
# This should match a prefix with a function which accepts
756
REVISION_NAMESPACES = {}
758
792
def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
759
793
relax_version_check=DEPRECATED_PARAMETER, _format=None,
760
794
_control_files=None, a_bzrdir=None, _repository=None):
927
961
"""See Branch.set_revision_history."""
928
962
self.control_files.put_utf8(
929
963
'revision-history', '\n'.join(rev_history))
964
transaction = self.get_transaction()
965
history = transaction.map.find_revision_history()
966
if history is not None:
967
# update the revision history in the identity map.
968
history[:] = list(rev_history)
969
# this call is disabled because revision_history is
970
# not really an object yet, and the transaction is for objects.
971
# transaction.register_dirty(history)
973
transaction.map.add_revision_history(rev_history)
974
# this call is disabled because revision_history is
975
# not really an object yet, and the transaction is for objects.
976
# transaction.register_clean(history)
931
978
def get_revision_delta(self, revno):
932
979
"""Return the delta for one revision.
1063
1108
def tree_config(self):
1064
1109
return TreeConfig(self)
1066
def _get_truncated_history(self, revision_id):
1067
history = self.revision_history()
1068
if revision_id is None:
1112
class BzrBranch5(BzrBranch):
1113
"""A format 5 branch. This supports new features over plan branches.
1115
It has support for a master_branch which is the data for bound branches.
1123
super(BzrBranch5, self).__init__(_format=_format,
1124
_control_files=_control_files,
1126
_repository=_repository)
1129
def pull(self, source, overwrite=False, stop_revision=None):
1130
"""Updates branch.pull to be bound branch aware."""
1131
bound_location = self.get_bound_location()
1132
if source.base != bound_location:
1133
# not pulling from master, so we need to update master.
1134
master_branch = self.get_master_branch()
1136
master_branch.pull(source)
1137
source = master_branch
1138
return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
1140
def get_bound_location(self):
1071
idx = history.index(revision_id)
1073
raise InvalidRevisionId(revision_id=revision, branch=self)
1074
return history[:idx+1]
1142
return self.control_files.get_utf8('bound').read()[:-1]
1143
except errors.NoSuchFile:
1076
1146
@needs_read_lock
1077
def _clone_weave(self, to_location, revision=None, basis_branch=None):
1079
from bzrlib.workingtree import WorkingTree
1080
assert isinstance(to_location, basestring)
1081
if basis_branch is not None:
1082
note("basis_branch is not supported for fast weave copy yet.")
1084
history = self._get_truncated_history(revision)
1085
if not bzrlib.osutils.lexists(to_location):
1086
os.mkdir(to_location)
1087
bzrdir_to = self.bzrdir._format.initialize(to_location)
1088
self.repository.clone(bzrdir_to)
1089
branch_to = bzrdir_to.create_branch()
1090
mutter("copy branch from %s to %s", self, branch_to)
1092
# FIXME duplicate code with base .clone().
1093
# .. would template method be useful here? RBC 20051207
1094
branch_to.set_parent(self.base)
1095
branch_to.append_revision(*history)
1096
WorkingTree.create(branch_to, branch_to.base)
1147
def get_master_branch(self):
1148
"""Return the branch we are bound to.
1150
:return: Either a Branch, or None
1152
This could memoise the branch, but if thats done
1153
it must be revalidated on each new lock.
1154
So for now we just dont memoise it.
1155
# RBC 20060304 review this decision.
1157
bound_loc = self.get_bound_location()
1161
return Branch.open(bound_loc)
1162
except (errors.NotBranchError, errors.ConnectionError), e:
1163
raise errors.BoundBranchConnectionFailure(
1167
def set_bound_location(self, location):
1168
"""Set the target where this branch is bound to.
1170
:param location: URL to the target branch
1173
self.control_files.put_utf8('bound', location+'\n')
1176
self.control_files._transport.delete('bound')
1182
def bind(self, other):
1183
"""Bind the local branch the other branch.
1185
:param other: The branch to bind to
1188
# TODO: jam 20051230 Consider checking if the target is bound
1189
# It is debatable whether you should be able to bind to
1190
# a branch which is itself bound.
1191
# Committing is obviously forbidden,
1192
# but binding itself may not be.
1193
# Since we *have* to check at commit time, we don't
1194
# *need* to check here
1197
# we are now equal to or a suffix of other.
1199
# Since we have 'pulled' from the remote location,
1200
# now we should try to pull in the opposite direction
1201
# in case the local tree has more revisions than the
1203
# There may be a different check you could do here
1204
# rather than actually trying to install revisions remotely.
1205
# TODO: capture an exception which indicates the remote branch
1207
# If it is up-to-date, this probably should not be a failure
1209
# lock other for write so the revision-history syncing cannot race
1213
# if this does not error, other now has the same last rev we do
1214
# it can only error if the pull from other was concurrent with
1215
# a commit to other from someone else.
1217
# until we ditch revision-history, we need to sync them up:
1218
self.set_revision_history(other.revision_history())
1219
# now other and self are up to date with each other and have the
1220
# same revision-history.
1224
self.set_bound_location(other.base)
1228
"""If bound, unbind"""
1229
return self.set_bound_location(None)
1233
"""Synchronise this branch with the master branch if any.
1235
:return: None or the last_revision that was pivoted out during the
1238
master = self.get_master_branch()
1239
if master is not None:
1240
old_tip = self.last_revision()
1241
self.pull(master, overwrite=True)
1242
if old_tip in self.repository.get_ancestry(self.last_revision()):
1101
1248
class BranchTestProviderAdapter(object):