~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2006-03-06 07:11:09 UTC
  • mfrom: (1591 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1593.
  • Revision ID: mbp@sourcefrog.net-20060306071109-6b35534e38610c43
[merge] bzr.dev

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.
657
697
            assert format.__class__ == self.__class__
658
698
        transport = a_bzrdir.get_branch_transport(None)
659
699
        control_files = LockableFiles(transport, 'lock', TransportLock)
660
 
        return BzrBranch(_format=self,
661
 
                         _control_files=control_files,
662
 
                         a_bzrdir=a_bzrdir,
663
 
                         _repository=a_bzrdir.find_repository())
 
700
        return BzrBranch5(_format=self,
 
701
                          _control_files=control_files,
 
702
                          a_bzrdir=a_bzrdir,
 
703
                          _repository=a_bzrdir.find_repository())
 
704
 
 
705
    def __str__(self):
 
706
        return "Bazaar-NG Metadir branch format 5"
664
707
 
665
708
 
666
709
class BranchReferenceFormat(BranchFormat):
745
788
    really matter if it's on an nfs/smb/afs/coda/... share, as long as
746
789
    it's writable, and can be accessed via the normal filesystem API.
747
790
    """
748
 
    # We actually expect this class to be somewhat short-lived; part of its
749
 
    # purpose is to try to isolate what bits of the branch logic are tied to
750
 
    # filesystem access, so that in a later step, we can extricate them to
751
 
    # a separarte ("storage") class.
752
 
    _inventory_weave = None
753
791
    
754
 
    # Map some sort of prefix into a namespace
755
 
    # stuff like "revno:10", "revid:", etc.
756
 
    # This should match a prefix with a function which accepts
757
 
    REVISION_NAMESPACES = {}
758
 
 
759
792
    def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
760
793
                 relax_version_check=DEPRECATED_PARAMETER, _format=None,
761
794
                 _control_files=None, a_bzrdir=None, _repository=None):
902
935
        # TODO: test for failed two phase locks. This is known broken.
903
936
        self.repository.unlock()
904
937
        self.control_files.unlock()
905
 
 
 
938
        
906
939
    def peek_lock_mode(self):
907
940
        if self.control_files._lock_count == 0:
908
941
            return None
980
1013
            self.append_revision(*pullable_revs)
981
1014
 
982
1015
    def pullable_revisions(self, other, stop_revision):
983
 
        """See Branch.pullable_revisions."""
984
1016
        other_revno = other.revision_id_to_revno(stop_revision)
985
1017
        try:
986
1018
            return self.missing_revisions(other, other_revno)
1064
1096
    def tree_config(self):
1065
1097
        return TreeConfig(self)
1066
1098
 
1067
 
    def _get_truncated_history(self, revision_id):
1068
 
        history = self.revision_history()
1069
 
        if revision_id is None:
1070
 
            return history
 
1099
 
 
1100
class BzrBranch5(BzrBranch):
 
1101
    """A format 5 branch. This supports new features over plan branches.
 
1102
 
 
1103
    It has support for a master_branch which is the data for bound branches.
 
1104
    """
 
1105
 
 
1106
    def __init__(self,
 
1107
                 _format,
 
1108
                 _control_files,
 
1109
                 a_bzrdir,
 
1110
                 _repository):
 
1111
        super(BzrBranch5, self).__init__(_format=_format,
 
1112
                                         _control_files=_control_files,
 
1113
                                         a_bzrdir=a_bzrdir,
 
1114
                                         _repository=_repository)
 
1115
        
 
1116
    @needs_write_lock
 
1117
    def pull(self, source, overwrite=False, stop_revision=None):
 
1118
        """Updates branch.pull to be bound branch aware."""
 
1119
        bound_location = self.get_bound_location()
 
1120
        if source.base != bound_location:
 
1121
            # not pulling from master, so we need to update master.
 
1122
            master_branch = self.get_master_branch()
 
1123
            if master_branch:
 
1124
                master_branch.pull(source)
 
1125
                source = master_branch
 
1126
        return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
 
1127
 
 
1128
    def get_bound_location(self):
1071
1129
        try:
1072
 
            idx = history.index(revision_id)
1073
 
        except ValueError:
1074
 
            raise InvalidRevisionId(revision_id=revision, branch=self)
1075
 
        return history[:idx+1]
 
1130
            return self.control_files.get_utf8('bound').read()[:-1]
 
1131
        except errors.NoSuchFile:
 
1132
            return None
1076
1133
 
1077
1134
    @needs_read_lock
1078
 
    def _clone_weave(self, to_location, revision=None, basis_branch=None):
1079
 
        # prevent leakage
1080
 
        from bzrlib.workingtree import WorkingTree
1081
 
        assert isinstance(to_location, basestring)
1082
 
        if basis_branch is not None:
1083
 
            note("basis_branch is not supported for fast weave copy yet.")
1084
 
 
1085
 
        history = self._get_truncated_history(revision)
1086
 
        if not bzrlib.osutils.lexists(to_location):
1087
 
            os.mkdir(to_location)
1088
 
        bzrdir_to = self.bzrdir._format.initialize(to_location)
1089
 
        self.repository.clone(bzrdir_to)
1090
 
        branch_to = bzrdir_to.create_branch()
1091
 
        mutter("copy branch from %s to %s", self, branch_to)
1092
 
 
1093
 
        # FIXME duplicate code with base .clone().
1094
 
        # .. would template method be useful here?  RBC 20051207
1095
 
        branch_to.set_parent(self.base)
1096
 
        branch_to.append_revision(*history)
1097
 
        WorkingTree.create(branch_to, branch_to.base)
1098
 
        mutter("copied")
1099
 
        return branch_to
 
1135
    def get_master_branch(self):
 
1136
        """Return the branch we are bound to.
 
1137
        
 
1138
        :return: Either a Branch, or None
 
1139
 
 
1140
        This could memoise the branch, but if thats done
 
1141
        it must be revalidated on each new lock.
 
1142
        So for now we just dont memoise it.
 
1143
        # RBC 20060304 review this decision.
 
1144
        """
 
1145
        bound_loc = self.get_bound_location()
 
1146
        if not bound_loc:
 
1147
            return None
 
1148
        try:
 
1149
            return Branch.open(bound_loc)
 
1150
        except (errors.NotBranchError, errors.ConnectionError), e:
 
1151
            raise errors.BoundBranchConnectionFailure(
 
1152
                    self, bound_loc, e)
 
1153
 
 
1154
    @needs_write_lock
 
1155
    def set_bound_location(self, location):
 
1156
        """Set the target where this branch is bound to.
 
1157
 
 
1158
        :param location: URL to the target branch
 
1159
        """
 
1160
        if location:
 
1161
            self.control_files.put_utf8('bound', location+'\n')
 
1162
        else:
 
1163
            try:
 
1164
                self.control_files._transport.delete('bound')
 
1165
            except NoSuchFile:
 
1166
                return False
 
1167
            return True
 
1168
 
 
1169
    @needs_write_lock
 
1170
    def bind(self, other):
 
1171
        """Bind the local branch the other branch.
 
1172
 
 
1173
        :param other: The branch to bind to
 
1174
        :type other: Branch
 
1175
        """
 
1176
        # TODO: jam 20051230 Consider checking if the target is bound
 
1177
        #       It is debatable whether you should be able to bind to
 
1178
        #       a branch which is itself bound.
 
1179
        #       Committing is obviously forbidden,
 
1180
        #       but binding itself may not be.
 
1181
        #       Since we *have* to check at commit time, we don't
 
1182
        #       *need* to check here
 
1183
        self.pull(other)
 
1184
 
 
1185
        # we are now equal to or a suffix of other.
 
1186
 
 
1187
        # Since we have 'pulled' from the remote location,
 
1188
        # now we should try to pull in the opposite direction
 
1189
        # in case the local tree has more revisions than the
 
1190
        # remote one.
 
1191
        # There may be a different check you could do here
 
1192
        # rather than actually trying to install revisions remotely.
 
1193
        # TODO: capture an exception which indicates the remote branch
 
1194
        #       is not writeable. 
 
1195
        #       If it is up-to-date, this probably should not be a failure
 
1196
        
 
1197
        # lock other for write so the revision-history syncing cannot race
 
1198
        other.lock_write()
 
1199
        try:
 
1200
            other.pull(self)
 
1201
            # if this does not error, other now has the same last rev we do
 
1202
            # it can only error if the pull from other was concurrent with
 
1203
            # a commit to other from someone else.
 
1204
 
 
1205
            # until we ditch revision-history, we need to sync them up:
 
1206
            self.set_revision_history(other.revision_history())
 
1207
            # now other and self are up to date with each other and have the
 
1208
            # same revision-history.
 
1209
        finally:
 
1210
            other.unlock()
 
1211
 
 
1212
        self.set_bound_location(other.base)
 
1213
 
 
1214
    @needs_write_lock
 
1215
    def unbind(self):
 
1216
        """If bound, unbind"""
 
1217
        return self.set_bound_location(None)
 
1218
 
 
1219
    @needs_write_lock
 
1220
    def update(self):
 
1221
        """Synchronise this branch with the master branch if any. 
 
1222
 
 
1223
        :return: None or the last_revision that was pivoted out during the
 
1224
                 update.
 
1225
        """
 
1226
        master = self.get_master_branch()
 
1227
        if master is not None:
 
1228
            old_tip = self.last_revision()
 
1229
            self.pull(master, overwrite=True)
 
1230
            if old_tip in self.repository.get_ancestry(self.last_revision()):
 
1231
                return None
 
1232
            return old_tip
 
1233
        return None
1100
1234
 
1101
1235
 
1102
1236
class BranchTestProviderAdapter(object):