~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: mbp at sourcefrog
  • Date: 2007-02-13 05:22:39 UTC
  • mfrom: (2279 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2283.
  • Revision ID: mbp@sourcefrog.net-20070213052239-09atqsahwth6zdm1
(merge) trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
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
81
81
 
82
82
    base
83
83
        Base directory/url of the branch.
 
84
 
 
85
    hooks: An instance of BranchHooks.
84
86
    """
85
87
    # this is really an instance variable - FIXME move it there
86
88
    # - RBC 20060112
223
225
        try:
224
226
            if last_revision is None:
225
227
                pb.update('get source history')
226
 
                from_history = from_branch.revision_history()
227
 
                if from_history:
228
 
                    last_revision = from_history[-1]
229
 
                else:
230
 
                    # no history in the source branch
231
 
                    last_revision = _mod_revision.NULL_REVISION
 
228
                last_revision = from_branch.last_revision_info()[1]
232
229
            return self.repository.fetch(from_branch.repository,
233
230
                                         revision_id=last_revision,
234
231
                                         pb=nested_pb)
262
259
        if config is None:
263
260
            config = self.get_config()
264
261
        
265
 
        return self.repository.get_commit_builder(self, parents, config, 
 
262
        return self.repository.get_commit_builder(self, parents, config,
266
263
            timestamp, timezone, committer, revprops, revision_id)
267
264
 
268
265
    def get_master_branch(self):
322
319
        else:
323
320
            return None
324
321
 
 
322
    def last_revision_info(self):
 
323
        """Return information about the last revision.
 
324
 
 
325
        :return: A tuple (revno, last_revision_id).
 
326
        """
 
327
        rh = self.revision_history()
 
328
        revno = len(rh)
 
329
        if revno:
 
330
            return (revno, rh[-1])
 
331
        else:
 
332
            return (0, _mod_revision.NULL_REVISION)
 
333
 
325
334
    def missing_revisions(self, other, stop_revision=None):
326
335
        """Return a list of new revisions that would perfectly fit.
327
336
        
375
384
        return history[revno - 1]
376
385
 
377
386
    def pull(self, source, overwrite=False, stop_revision=None):
 
387
        """Mirror source into this branch.
 
388
 
 
389
        This branch is considered to be 'local', having low latency.
 
390
        """
378
391
        raise NotImplementedError(self.pull)
379
392
 
 
393
    def push(self, target, overwrite=False, stop_revision=None):
 
394
        """Mirror this branch into target.
 
395
 
 
396
        This branch is considered to be 'local', having low latency.
 
397
        """
 
398
        raise NotImplementedError(self.push)
 
399
 
380
400
    def basis_tree(self):
381
401
        """Return `Tree` object for last revision."""
382
402
        return self.repository.revision_tree(self.last_revision())
599
619
            format = self.repository.bzrdir.cloning_metadir()
600
620
        return format
601
621
 
602
 
    def create_checkout(self, to_location, revision_id=None, 
 
622
    def create_checkout(self, to_location, revision_id=None,
603
623
                        lightweight=False):
604
624
        """Create a checkout of a branch.
605
625
        
676
696
 
677
697
    def get_format_description(self):
678
698
        """Return the short format description for this format."""
679
 
        raise NotImplementedError(self.get_format_string)
 
699
        raise NotImplementedError(self.get_format_description)
680
700
 
681
701
    def initialize(self, a_bzrdir):
682
702
        """Create a branch of this format in a_bzrdir."""
716
736
        return self.get_format_string().rstrip()
717
737
 
718
738
 
 
739
class BranchHooks(dict):
 
740
    """A dictionary mapping hook name to a list of callables for branch hooks.
 
741
    
 
742
    e.g. ['set_rh'] Is the list of items to be called when the
 
743
    set_revision_history function is invoked.
 
744
    """
 
745
 
 
746
    def __init__(self):
 
747
        """Create the default hooks.
 
748
 
 
749
        These are all empty initially, because by default nothing should get
 
750
        notified.
 
751
        """
 
752
        dict.__init__(self)
 
753
        # Introduced in 0.15:
 
754
        # invoked whenever the revision history has been set
 
755
        # with set_revision_history. The api signature is
 
756
        # (branch, revision_history), and the branch will
 
757
        # be write-locked.
 
758
        self['set_rh'] = []
 
759
        # invoked after a push operation completes.
 
760
        # the api signature is
 
761
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
 
762
        # where local is the local branch or None, master is the target 
 
763
        # master branch, and the rest should be self explanatory. The source
 
764
        # is read locked and the target branches write locked. Source will
 
765
        # be the local low-latency branch.
 
766
        self['post_push'] = []
 
767
        # invoked after a pull operation completes.
 
768
        # the api signature is
 
769
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
 
770
        # where local is the local branch or None, master is the target 
 
771
        # master branch, and the rest should be self explanatory. The source
 
772
        # is read locked and the target branches write locked. The local
 
773
        # branch is the low-latency branch.
 
774
        self['post_pull'] = []
 
775
        # invoked after a commit operation completes.
 
776
        # the api signature is 
 
777
        # (local, master, old_revno, old_revid, new_revno, new_revid)
 
778
        # old_revid is NULL_REVISION for the first commit to a branch.
 
779
        self['post_commit'] = []
 
780
        # invoked after a uncommit operation completes.
 
781
        # the api signature is
 
782
        # (local, master, old_revno, old_revid, new_revno, new_revid) where
 
783
        # local is the local branch or None, master is the target branch,
 
784
        # and an empty branch recieves new_revno of 0, new_revid of None.
 
785
        self['post_uncommit'] = []
 
786
 
 
787
    def install_hook(self, hook_name, a_callable):
 
788
        """Install a_callable in to the hook hook_name.
 
789
 
 
790
        :param hook_name: A hook name. See the __init__ method of BranchHooks
 
791
            for the complete list of hooks.
 
792
        :param a_callable: The callable to be invoked when the hook triggers.
 
793
            The exact signature will depend on the hook - see the __init__ 
 
794
            method of BranchHooks for details on each hook.
 
795
        """
 
796
        try:
 
797
            self[hook_name].append(a_callable)
 
798
        except KeyError:
 
799
            raise errors.UnknownHook('branch', hook_name)
 
800
 
 
801
 
 
802
# install the default hooks into the Branch class.
 
803
Branch.hooks = BranchHooks()
 
804
 
 
805
 
719
806
class BzrBranchFormat4(BranchFormat):
720
807
    """Bzr branch format 4.
721
808
 
1101
1188
            # this call is disabled because revision_history is 
1102
1189
            # not really an object yet, and the transaction is for objects.
1103
1190
            # transaction.register_clean(history)
 
1191
        for hook in Branch.hooks['set_rh']:
 
1192
            hook(self, rev_history)
1104
1193
 
1105
1194
    @needs_read_lock
1106
1195
    def revision_history(self):
1188
1277
        return self.bzrdir.open_workingtree()
1189
1278
 
1190
1279
    @needs_write_lock
1191
 
    def pull(self, source, overwrite=False, stop_revision=None):
1192
 
        """See Branch.pull."""
 
1280
    def pull(self, source, overwrite=False, stop_revision=None,
 
1281
        _hook_master=None, _run_hooks=True):
 
1282
        """See Branch.pull.
 
1283
 
 
1284
        :param _hook_master: Private parameter - set the branch to 
 
1285
            be supplied as the master to push hooks.
 
1286
        :param _run_hooks: Private parameter - allow disabling of
 
1287
            hooks, used when pushing to a master branch.
 
1288
        """
1193
1289
        source.lock_read()
1194
1290
        try:
1195
 
            old_count = len(self.revision_history())
 
1291
            old_count, old_tip = self.last_revision_info()
1196
1292
            try:
1197
1293
                self.update_revisions(source, stop_revision)
1198
1294
            except DivergedBranches:
1200
1296
                    raise
1201
1297
            if overwrite:
1202
1298
                self.set_revision_history(source.revision_history())
1203
 
            new_count = len(self.revision_history())
 
1299
            new_count, new_tip = self.last_revision_info()
 
1300
            if _run_hooks:
 
1301
                if _hook_master:
 
1302
                    _hook_local = self
 
1303
                else:
 
1304
                    _hook_master = self
 
1305
                    _hook_local = None
 
1306
                for hook in Branch.hooks['post_pull']:
 
1307
                    hook(source, _hook_local, _hook_master, old_count, old_tip,
 
1308
                        new_count, new_tip)
1204
1309
            return new_count - old_count
1205
1310
        finally:
1206
1311
            source.unlock()
1207
1312
 
 
1313
    @needs_read_lock
 
1314
    def push(self, target, overwrite=False, stop_revision=None,
 
1315
        _hook_master=None, _run_hooks=True):
 
1316
        """See Branch.push.
 
1317
        
 
1318
        :param _hook_master: Private parameter - set the branch to 
 
1319
            be supplied as the master to push hooks.
 
1320
        :param _run_hooks: Private parameter - allow disabling of
 
1321
            hooks, used when pushing to a master branch.
 
1322
        """
 
1323
        target.lock_write()
 
1324
        try:
 
1325
            old_count, old_tip = target.last_revision_info()
 
1326
            try:
 
1327
                target.update_revisions(self, stop_revision)
 
1328
            except DivergedBranches:
 
1329
                if not overwrite:
 
1330
                    raise
 
1331
            if overwrite:
 
1332
                target.set_revision_history(self.revision_history())
 
1333
            new_count, new_tip = target.last_revision_info()
 
1334
            if _run_hooks:
 
1335
                if _hook_master:
 
1336
                    _hook_local = target
 
1337
                else:
 
1338
                    _hook_master = target
 
1339
                    _hook_local = None
 
1340
                for hook in Branch.hooks['post_push']:
 
1341
                    hook(self, _hook_local, _hook_master, old_count, old_tip,
 
1342
                        new_count, new_tip)
 
1343
            return new_count - old_count
 
1344
        finally:
 
1345
            target.unlock()
 
1346
 
1208
1347
    def get_parent(self):
1209
1348
        """See Branch.get_parent."""
1210
1349
 
1282
1421
                                         _repository=_repository)
1283
1422
        
1284
1423
    @needs_write_lock
1285
 
    def pull(self, source, overwrite=False, stop_revision=None):
1286
 
        """Updates branch.pull to be bound branch aware."""
 
1424
    def pull(self, source, overwrite=False, stop_revision=None,
 
1425
        _run_hooks=True):
 
1426
        """Extends branch.pull to be bound branch aware.
 
1427
        
 
1428
        :param _run_hooks: Private parameter used to force hook running
 
1429
            off during bound branch double-pushing.
 
1430
        """
1287
1431
        bound_location = self.get_bound_location()
1288
 
        if source.base != bound_location:
 
1432
        master_branch = None
 
1433
        if bound_location and source.base != bound_location:
1289
1434
            # not pulling from master, so we need to update master.
1290
1435
            master_branch = self.get_master_branch()
1291
 
            if master_branch:
1292
 
                master_branch.pull(source)
1293
 
                source = master_branch
1294
 
        return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
 
1436
            master_branch.lock_write()
 
1437
        try:
 
1438
            if master_branch:
 
1439
                # pull from source into master.
 
1440
                master_branch.pull(source, overwrite, stop_revision,
 
1441
                    _run_hooks=False)
 
1442
            return super(BzrBranch5, self).pull(source, overwrite,
 
1443
                stop_revision, _hook_master=master_branch,
 
1444
                _run_hooks=_run_hooks)
 
1445
        finally:
 
1446
            if master_branch:
 
1447
                master_branch.unlock()
 
1448
 
 
1449
    @needs_write_lock
 
1450
    def push(self, target, overwrite=False, stop_revision=None):
 
1451
        """Updates branch.push to be bound branch aware."""
 
1452
        bound_location = target.get_bound_location()
 
1453
        master_branch = None
 
1454
        if bound_location and target.base != bound_location:
 
1455
            # not pushing to master, so we need to update master.
 
1456
            master_branch = target.get_master_branch()
 
1457
            master_branch.lock_write()
 
1458
        try:
 
1459
            if master_branch:
 
1460
                # push into the master from this branch.
 
1461
                super(BzrBranch5, self).push(master_branch, overwrite,
 
1462
                    stop_revision, _run_hooks=False)
 
1463
            # and push into the target branch from this. Note that we push from
 
1464
            # this branch again, because its considered the highest bandwidth
 
1465
            # repository.
 
1466
            return super(BzrBranch5, self).push(target, overwrite,
 
1467
                stop_revision, _hook_master=master_branch)
 
1468
        finally:
 
1469
            if master_branch:
 
1470
                master_branch.unlock()
1295
1471
 
1296
1472
    def get_bound_location(self):
1297
1473
        try: