~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-03-06 00:49:26 UTC
  • mfrom: (1587.1.14 bound-branches)
  • Revision ID: pqm@pqm.ubuntu.com-20060306004926-6d7a10c990bc17d1
Merge in bound branches core support.

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):
380
412
    def set_parent(self, url):
381
413
        raise NotImplementedError('set_parent is abstract')
382
414
 
 
415
    @needs_write_lock
 
416
    def update(self):
 
417
        """Synchronise this branch with the master branch if any. 
 
418
 
 
419
        :return: None or the last_revision pivoted out during the update.
 
420
        """
 
421
        return None
 
422
 
383
423
    def check_revno(self, revno):
384
424
        """\
385
425
        Check whether a revno corresponds to any revision.
656
696
            assert format.__class__ == self.__class__
657
697
        transport = a_bzrdir.get_branch_transport(None)
658
698
        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())
 
699
        return BzrBranch5(_format=self,
 
700
                          _control_files=control_files,
 
701
                          a_bzrdir=a_bzrdir,
 
702
                          _repository=a_bzrdir.find_repository())
 
703
 
 
704
    def __str__(self):
 
705
        return "Bazaar-NG Metadir branch format 5"
663
706
 
664
707
 
665
708
class BranchReferenceFormat(BranchFormat):
744
787
    really matter if it's on an nfs/smb/afs/coda/... share, as long as
745
788
    it's writable, and can be accessed via the normal filesystem API.
746
789
    """
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
790
    
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
791
    def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
759
792
                 relax_version_check=DEPRECATED_PARAMETER, _format=None,
760
793
                 _control_files=None, a_bzrdir=None, _repository=None):
901
934
        # TODO: test for failed two phase locks. This is known broken.
902
935
        self.repository.unlock()
903
936
        self.control_files.unlock()
904
 
 
 
937
        
905
938
    def peek_lock_mode(self):
906
939
        if self.control_files._lock_count == 0:
907
940
            return None
979
1012
            self.append_revision(*pullable_revs)
980
1013
 
981
1014
    def pullable_revisions(self, other, stop_revision):
982
 
        """See Branch.pullable_revisions."""
983
1015
        other_revno = other.revision_id_to_revno(stop_revision)
984
1016
        try:
985
1017
            return self.missing_revisions(other, other_revno)
1063
1095
    def tree_config(self):
1064
1096
        return TreeConfig(self)
1065
1097
 
1066
 
    def _get_truncated_history(self, revision_id):
1067
 
        history = self.revision_history()
1068
 
        if revision_id is None:
1069
 
            return history
 
1098
 
 
1099
class BzrBranch5(BzrBranch):
 
1100
    """A format 5 branch. This supports new features over plan branches.
 
1101
 
 
1102
    It has support for a master_branch which is the data for bound branches.
 
1103
    """
 
1104
 
 
1105
    def __init__(self,
 
1106
                 _format,
 
1107
                 _control_files,
 
1108
                 a_bzrdir,
 
1109
                 _repository):
 
1110
        super(BzrBranch5, self).__init__(_format=_format,
 
1111
                                         _control_files=_control_files,
 
1112
                                         a_bzrdir=a_bzrdir,
 
1113
                                         _repository=_repository)
 
1114
        
 
1115
    @needs_write_lock
 
1116
    def pull(self, source, overwrite=False, stop_revision=None):
 
1117
        """Updates branch.pull to be bound branch aware."""
 
1118
        bound_location = self.get_bound_location()
 
1119
        if source.base != bound_location:
 
1120
            # not pulling from master, so we need to update master.
 
1121
            master_branch = self.get_master_branch()
 
1122
            if master_branch:
 
1123
                master_branch.pull(source)
 
1124
                source = master_branch
 
1125
        return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
 
1126
 
 
1127
    def get_bound_location(self):
1070
1128
        try:
1071
 
            idx = history.index(revision_id)
1072
 
        except ValueError:
1073
 
            raise InvalidRevisionId(revision_id=revision, branch=self)
1074
 
        return history[:idx+1]
 
1129
            return self.control_files.get_utf8('bound').read()[:-1]
 
1130
        except errors.NoSuchFile:
 
1131
            return None
1075
1132
 
1076
1133
    @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
 
1134
    def get_master_branch(self):
 
1135
        """Return the branch we are bound to.
 
1136
        
 
1137
        :return: Either a Branch, or None
 
1138
 
 
1139
        This could memoise the branch, but if thats done
 
1140
        it must be revalidated on each new lock.
 
1141
        So for now we just dont memoise it.
 
1142
        # RBC 20060304 review this decision.
 
1143
        """
 
1144
        bound_loc = self.get_bound_location()
 
1145
        if not bound_loc:
 
1146
            return None
 
1147
        try:
 
1148
            return Branch.open(bound_loc)
 
1149
        except (errors.NotBranchError, errors.ConnectionError), e:
 
1150
            raise errors.BoundBranchConnectionFailure(
 
1151
                    self, bound_loc, e)
 
1152
 
 
1153
    @needs_write_lock
 
1154
    def set_bound_location(self, location):
 
1155
        """Set the target where this branch is bound to.
 
1156
 
 
1157
        :param location: URL to the target branch
 
1158
        """
 
1159
        if location:
 
1160
            self.control_files.put_utf8('bound', location+'\n')
 
1161
        else:
 
1162
            try:
 
1163
                self.control_files._transport.delete('bound')
 
1164
            except NoSuchFile:
 
1165
                return False
 
1166
            return True
 
1167
 
 
1168
    @needs_write_lock
 
1169
    def bind(self, other):
 
1170
        """Bind the local branch the other branch.
 
1171
 
 
1172
        :param other: The branch to bind to
 
1173
        :type other: Branch
 
1174
        """
 
1175
        # TODO: jam 20051230 Consider checking if the target is bound
 
1176
        #       It is debatable whether you should be able to bind to
 
1177
        #       a branch which is itself bound.
 
1178
        #       Committing is obviously forbidden,
 
1179
        #       but binding itself may not be.
 
1180
        #       Since we *have* to check at commit time, we don't
 
1181
        #       *need* to check here
 
1182
        self.pull(other)
 
1183
 
 
1184
        # we are now equal to or a suffix of other.
 
1185
 
 
1186
        # Since we have 'pulled' from the remote location,
 
1187
        # now we should try to pull in the opposite direction
 
1188
        # in case the local tree has more revisions than the
 
1189
        # remote one.
 
1190
        # There may be a different check you could do here
 
1191
        # rather than actually trying to install revisions remotely.
 
1192
        # TODO: capture an exception which indicates the remote branch
 
1193
        #       is not writeable. 
 
1194
        #       If it is up-to-date, this probably should not be a failure
 
1195
        
 
1196
        # lock other for write so the revision-history syncing cannot race
 
1197
        other.lock_write()
 
1198
        try:
 
1199
            other.pull(self)
 
1200
            # if this does not error, other now has the same last rev we do
 
1201
            # it can only error if the pull from other was concurrent with
 
1202
            # a commit to other from someone else.
 
1203
 
 
1204
            # until we ditch revision-history, we need to sync them up:
 
1205
            self.set_revision_history(other.revision_history())
 
1206
            # now other and self are up to date with each other and have the
 
1207
            # same revision-history.
 
1208
        finally:
 
1209
            other.unlock()
 
1210
 
 
1211
        self.set_bound_location(other.base)
 
1212
 
 
1213
    @needs_write_lock
 
1214
    def unbind(self):
 
1215
        """If bound, unbind"""
 
1216
        return self.set_bound_location(None)
 
1217
 
 
1218
    @needs_write_lock
 
1219
    def update(self):
 
1220
        """Synchronise this branch with the master branch if any. 
 
1221
 
 
1222
        :return: None or the last_revision that was pivoted out during the
 
1223
                 update.
 
1224
        """
 
1225
        master = self.get_master_branch()
 
1226
        if master is not None:
 
1227
            old_tip = self.last_revision()
 
1228
            self.pull(master, overwrite=True)
 
1229
            if old_tip in self.repository.get_ancestry(self.last_revision()):
 
1230
                return None
 
1231
            return old_tip
 
1232
        return None
1099
1233
 
1100
1234
 
1101
1235
class BranchTestProviderAdapter(object):