~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Robert Collins
  • Date: 2006-03-04 13:03:57 UTC
  • mfrom: (1505.1.30 bzr-bound-branch)
  • mto: This revision was merged to the branch mainline in revision 1590.
  • Revision ID: robertc@robertcollins.net-20060304130357-95990a95920f57bb
Update bound branch implementation to 0.8.

Show diffs side-by-side

added added

removed removed

Lines of Context:
183
183
        """
184
184
        raise NotImplementedError('abspath is abstract')
185
185
 
 
186
    def bind(self, other):
 
187
        """Bind the local branch the other branch.
 
188
 
 
189
        :param other: The branch to bind to
 
190
        :type other: Branch
 
191
        """
 
192
        raise errors.UpgradeRequired(self.base)
 
193
 
186
194
    @needs_write_lock
187
195
    def fetch(self, from_branch, last_revision=None, pb=None):
188
196
        """Copy revisions from from_branch into this branch.
217
225
        finally:
218
226
            from_branch.unlock()
219
227
 
 
228
    def get_bound_location(self):
 
229
        """Return the URL of the rbanch we are bound to.
 
230
 
 
231
        Older format branches cannot bind, please be sure to use a metadir
 
232
        branch.
 
233
        """
 
234
        return None
 
235
 
 
236
    def get_master_branch(self):
 
237
        """Return the branch we are bound to.
 
238
        
 
239
        :return: Either a Branch, or None
 
240
        """
 
241
        return None
 
242
 
220
243
    def get_root_id(self):
221
244
        """Return the id of this branches root"""
222
245
        raise NotImplementedError('get_root_id is abstract')
243
266
        """
244
267
        return len(self.revision_history())
245
268
 
 
269
    def unbind(self):
 
270
        """Older format branches cannot bind or unbind."""
 
271
        raise errors.UpgradeRequired(self.base)
 
272
 
246
273
    def last_revision(self):
247
274
        """Return last patch hash, or None if no history."""
248
275
        ph = self.revision_history()
251
278
        else:
252
279
            return None
253
280
 
254
 
    def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
 
281
    def missing_revisions(self, other, stop_revision=None):
255
282
        """Return a list of new revisions that would perfectly fit.
256
283
        
257
284
        If self and other have not diverged, return a list of the revisions
301
328
        return other_history[self_len:stop_revision]
302
329
 
303
330
    def update_revisions(self, other, stop_revision=None):
304
 
        """Pull in new perfect-fit revisions."""
 
331
        """Pull in new perfect-fit revisions.
 
332
 
 
333
        :param other: Another Branch to pull from
 
334
        :param stop_revision: Updated until the given revision
 
335
        :return: None
 
336
        """
305
337
        raise NotImplementedError('update_revisions is abstract')
306
338
 
307
339
    def pullable_revisions(self, other, stop_revision):
656
688
            assert format.__class__ == self.__class__
657
689
        transport = a_bzrdir.get_branch_transport(None)
658
690
        control_files = LockableFiles(transport, 'lock')
659
 
        return BzrBranch(_format=self,
660
 
                         _control_files=control_files,
661
 
                         a_bzrdir=a_bzrdir,
662
 
                         _repository=a_bzrdir.find_repository())
 
691
        return BzrBranch5(_format=self,
 
692
                          _control_files=control_files,
 
693
                          a_bzrdir=a_bzrdir,
 
694
                          _repository=a_bzrdir.find_repository())
663
695
 
664
696
 
665
697
class BranchReferenceFormat(BranchFormat):
744
776
    really matter if it's on an nfs/smb/afs/coda/... share, as long as
745
777
    it's writable, and can be accessed via the normal filesystem API.
746
778
    """
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
752
779
    
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 = {}
757
 
 
758
780
    def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
759
781
                 relax_version_check=DEPRECATED_PARAMETER, _format=None,
760
782
                 _control_files=None, a_bzrdir=None, _repository=None):
901
923
        # TODO: test for failed two phase locks. This is known broken.
902
924
        self.repository.unlock()
903
925
        self.control_files.unlock()
904
 
 
 
926
        
905
927
    def peek_lock_mode(self):
906
928
        if self.control_files._lock_count == 0:
907
929
            return None
979
1001
            self.append_revision(*pullable_revs)
980
1002
 
981
1003
    def pullable_revisions(self, other, stop_revision):
982
 
        """See Branch.pullable_revisions."""
983
1004
        other_revno = other.revision_id_to_revno(stop_revision)
984
1005
        try:
985
1006
            return self.missing_revisions(other, other_revno)
1063
1084
    def tree_config(self):
1064
1085
        return TreeConfig(self)
1065
1086
 
1066
 
    def _get_truncated_history(self, revision_id):
1067
 
        history = self.revision_history()
1068
 
        if revision_id is None:
1069
 
            return history
 
1087
 
 
1088
class BzrBranch5(BzrBranch):
 
1089
    """A format 5 branch. This supports new features over plan branches.
 
1090
 
 
1091
    It has support for a master_branch which is the data for bound branches.
 
1092
    """
 
1093
 
 
1094
    def __init__(self,
 
1095
                 _format,
 
1096
                 _control_files,
 
1097
                 a_bzrdir,
 
1098
                 _repository):
 
1099
        super(BzrBranch5, self).__init__(_format=_format,
 
1100
                                         _control_files=_control_files,
 
1101
                                         a_bzrdir=a_bzrdir,
 
1102
                                         _repository=_repository)
 
1103
        
 
1104
    @needs_write_lock
 
1105
    def pull(self, source, overwrite=False, stop_revision=None):
 
1106
        """Updates branch.pull to be bound branch aware."""
 
1107
        # TODO: jam 20051230 This does work, in that 'bzr pull'
 
1108
        #       will update the master branch before updating the
 
1109
        #       local branch. However, 'source' can also already
 
1110
        #       be the master branch. Which means that we are
 
1111
        #       asking it to update from itself, before we continue.
 
1112
        #       This probably causes double downloads, etc.
 
1113
        #       So we probably want to put in an explicit check
 
1114
        #       of whether source is already the master branch.
 
1115
        bound_location = self.get_bound_location()
 
1116
        if source.base != bound_location:
 
1117
            # not pulling from master, so we need to update master.
 
1118
            master_branch = self.get_master_branch()
 
1119
            if master_branch:
 
1120
                master_branch.pull(source)
 
1121
                source = master_branch
 
1122
        return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
 
1123
 
 
1124
    def get_bound_location(self):
1070
1125
        try:
1071
 
            idx = history.index(revision_id)
1072
 
        except ValueError:
1073
 
            raise InvalidRevisionId(revision_id=revision, branch=self)
1074
 
        return history[:idx+1]
 
1126
            return self.control_files.get_utf8('bound').read()[:-1]
 
1127
        except errors.NoSuchFile:
 
1128
            return None
1075
1129
 
1076
1130
    @needs_read_lock
1077
 
    def _clone_weave(self, to_location, revision=None, basis_branch=None):
1078
 
        # prevent leakage
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.")
1083
 
 
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)
1091
 
 
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)
1097
 
        mutter("copied")
1098
 
        return branch_to
 
1131
    def get_master_branch(self):
 
1132
        """Return the branch we are bound to.
 
1133
        
 
1134
        :return: Either a Branch, or None
 
1135
 
 
1136
        This could memoise the branch, but if thats done
 
1137
        it must be revalidated on each new lock.
 
1138
        So for now we just dont memoise it.
 
1139
        # RBC 20060304 review this decision.
 
1140
        """
 
1141
        bound_loc = self.get_bound_location()
 
1142
        if not bound_loc:
 
1143
            return None
 
1144
        try:
 
1145
            return Branch.open(bound_loc)
 
1146
        except (errors.NotBranchError, errors.ConnectionError), e:
 
1147
            raise errors.BoundBranchConnectionFailure(
 
1148
                    self, bound_loc, e)
 
1149
 
 
1150
    @needs_write_lock
 
1151
    def set_bound_location(self, location):
 
1152
        """Set the target where this branch is bound to.
 
1153
 
 
1154
        :param location: URL to the target branch
 
1155
        """
 
1156
        if location:
 
1157
            self.control_files.put_utf8('bound', location+'\n')
 
1158
        else:
 
1159
            try:
 
1160
                self.control_files._transport.delete('bound')
 
1161
            except NoSuchFile:
 
1162
                return False
 
1163
            return True
 
1164
 
 
1165
    @needs_write_lock
 
1166
    def bind(self, other):
 
1167
        """Bind the local branch the other branch.
 
1168
 
 
1169
        :param other: The branch to bind to
 
1170
        :type other: Branch
 
1171
        """
 
1172
        # TODO: jam 20051230 Consider checking if the target is bound
 
1173
        #       It is debatable whether you should be able to bind to
 
1174
        #       a branch which is itself bound.
 
1175
        #       Committing is obviously forbidden,
 
1176
        #       but binding itself may not be.
 
1177
        #       Since we *have* to check at commit time, we don't
 
1178
        #       *need* to check here
 
1179
        self.pull(other)
 
1180
 
 
1181
        # we are now equal to or a suffix of other.
 
1182
 
 
1183
        # Since we have 'pulled' from the remote location,
 
1184
        # now we should try to pull in the opposite direction
 
1185
        # in case the local tree has more revisions than the
 
1186
        # remote one.
 
1187
        # There may be a different check you could do here
 
1188
        # rather than actually trying to install revisions remotely.
 
1189
        # TODO: capture an exception which indicates the remote branch
 
1190
        #       is not writeable. 
 
1191
        #       If it is up-to-date, this probably should not be a failure
 
1192
        
 
1193
        # lock other for write so the revision-history syncing cannot race
 
1194
        other.lock_write()
 
1195
        try:
 
1196
            other.pull(self)
 
1197
            # if this does not error, other now has the same last rev we do
 
1198
            # it can only error if the pull from other was concurrent with
 
1199
            # a commit to other from someone else.
 
1200
 
 
1201
            # until we ditch revision-history, we need to sync them up:
 
1202
            self.set_revision_history(other.revision_history())
 
1203
            # now other and self are up to date with each other and have the
 
1204
            # same revision-history.
 
1205
        finally:
 
1206
            other.unlock()
 
1207
 
 
1208
        self.set_bound_location(other.base)
 
1209
 
 
1210
    @needs_write_lock
 
1211
    def unbind(self):
 
1212
        """If bound, unbind"""
 
1213
        return self.set_bound_location(None)
1099
1214
 
1100
1215
 
1101
1216
class BranchTestProviderAdapter(object):