~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: 2008-01-02 08:23:44 UTC
  • mfrom: (3140.1.9 find-branches)
  • Revision ID: pqm@pqm.ubuntu.com-20080102082344-qret383z2bdk1ud4
Optimize find_branches for standalone repositories (abentley)

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
from bzrlib.lazy_import import lazy_import
21
21
lazy_import(globals(), """
22
 
from copy import deepcopy
23
 
from unittest import TestSuite
24
22
from warnings import warn
25
23
 
26
24
import bzrlib
28
26
        bzrdir,
29
27
        cache_utf8,
30
28
        config as _mod_config,
 
29
        debug,
31
30
        errors,
32
31
        lockdir,
33
32
        lockable_files,
51
50
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
52
51
                           HistoryMissing, InvalidRevisionId,
53
52
                           InvalidRevisionNumber, LockError, NoSuchFile,
54
 
                           NoSuchRevision, NoWorkingTree, NotVersionedError,
 
53
                           NoSuchRevision, NotVersionedError,
55
54
                           NotBranchError, UninitializableFormat,
56
55
                           UnlistableStore, UnlistableBranch,
57
56
                           )
61
60
                                      DEPRECATED_PARAMETER,
62
61
                                      deprecated_passed,
63
62
                                      zero_eight, zero_nine, zero_sixteen,
 
63
                                      zero_ninetyone,
64
64
                                      )
65
 
from bzrlib.trace import mutter, note
 
65
from bzrlib.trace import mutter, mutter_callsite, note
66
66
 
67
67
 
68
68
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
118
118
            master.break_lock()
119
119
 
120
120
    @staticmethod
121
 
    @deprecated_method(zero_eight)
122
 
    def open_downlevel(base):
123
 
        """Open a branch which may be of an old format."""
124
 
        return Branch.open(base, _unsupported=True)
125
 
        
126
 
    @staticmethod
127
 
    def open(base, _unsupported=False):
 
121
    def open(base, _unsupported=False, possible_transports=None):
128
122
        """Open the branch rooted at base.
129
123
 
130
124
        For instance, if the branch is at URL/.bzr/branch,
131
125
        Branch.open(URL) -> a Branch instance.
132
126
        """
133
 
        control = bzrdir.BzrDir.open(base, _unsupported)
134
 
        return control.open_branch(_unsupported)
135
 
 
136
 
    @staticmethod
137
 
    def open_containing(url):
 
127
        control = bzrdir.BzrDir.open(base, _unsupported,
 
128
                                     possible_transports=possible_transports)
 
129
        return control.open_branch(_unsupported)
 
130
 
 
131
    @staticmethod
 
132
    def open_from_transport(transport, _unsupported=False):
 
133
        """Open the branch rooted at transport"""
 
134
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
 
135
        return control.open_branch(_unsupported)
 
136
 
 
137
    @staticmethod
 
138
    def open_containing(url, possible_transports=None):
138
139
        """Open an existing branch which contains url.
139
140
        
140
141
        This probes for a branch at url, and searches upwards from there.
145
146
        format, UnknownFormatError or UnsupportedFormatError are raised.
146
147
        If there is one, it is returned, along with the unused portion of url.
147
148
        """
148
 
        control, relpath = bzrdir.BzrDir.open_containing(url)
 
149
        control, relpath = bzrdir.BzrDir.open_containing(url,
 
150
                                                         possible_transports)
149
151
        return control.open_branch(), relpath
150
152
 
151
 
    @staticmethod
152
 
    @deprecated_function(zero_eight)
153
 
    def initialize(base):
154
 
        """Create a new working tree and branch, rooted at 'base' (url)
155
 
 
156
 
        NOTE: This will soon be deprecated in favour of creation
157
 
        through a BzrDir.
158
 
        """
159
 
        return bzrdir.BzrDir.create_standalone_workingtree(base).branch
160
 
 
161
 
    @deprecated_function(zero_eight)
162
 
    def setup_caching(self, cache_root):
163
 
        """Subclasses that care about caching should override this, and set
164
 
        up cached stores located under cache_root.
165
 
        
166
 
        NOTE: This is unused.
167
 
        """
168
 
        pass
169
 
 
170
153
    def get_config(self):
171
154
        return BranchConfig(self)
172
155
 
174
157
        return self.get_config().get_nickname()
175
158
 
176
159
    def _set_nick(self, nick):
177
 
        self.get_config().set_user_option('nickname', nick)
 
160
        self.get_config().set_user_option('nickname', nick, warn_masked=True)
178
161
 
179
162
    nick = property(_get_nick, _set_nick)
180
163
 
339
322
        return self.repository.get_commit_builder(self, parents, config,
340
323
            timestamp, timezone, committer, revprops, revision_id)
341
324
 
342
 
    def get_master_branch(self):
 
325
    def get_master_branch(self, possible_transports=None):
343
326
        """Return the branch we are bound to.
344
327
        
345
328
        :return: Either a Branch, or None
371
354
        """Print `file` to stdout."""
372
355
        raise NotImplementedError(self.print_file)
373
356
 
374
 
    def append_revision(self, *revision_ids):
375
 
        raise NotImplementedError(self.append_revision)
376
 
 
377
357
    def set_revision_history(self, rev_history):
378
358
        raise NotImplementedError(self.set_revision_history)
379
359
 
425
405
 
426
406
    @needs_read_lock
427
407
    def revision_history(self):
428
 
        """Return sequence of revision hashes on to this branch.
 
408
        """Return sequence of revision ids on this branch.
429
409
        
430
410
        This method will cache the revision history for as long as it is safe to
431
411
        do so.
432
412
        """
 
413
        if 'evil' in debug.debug_flags:
 
414
            mutter_callsite(3, "revision_history scales with history.")
433
415
        if self._revision_history_cache is not None:
434
416
            history = self._revision_history_cache
435
417
        else:
443
425
        That is equivalent to the number of revisions committed to
444
426
        this branch.
445
427
        """
446
 
        return len(self.revision_history())
 
428
        return self.last_revision_info()[0]
447
429
 
448
430
    def unbind(self):
449
431
        """Older format branches cannot bind or unbind."""
459
441
        if ph:
460
442
            return ph[-1]
461
443
        else:
462
 
            return None
 
444
            return _mod_revision.NULL_REVISION
463
445
 
464
446
    def last_revision_info(self):
465
447
        """Return information about the last revision.
507
489
 
508
490
    def revision_id_to_revno(self, revision_id):
509
491
        """Given a revision id, return its revno"""
510
 
        if revision_id is None:
 
492
        if _mod_revision.is_null(revision_id):
511
493
            return 0
512
 
        revision_id = osutils.safe_revision_id(revision_id)
513
494
        history = self.revision_history()
514
495
        try:
515
496
            return history.index(revision_id) + 1
519
500
    def get_rev_id(self, revno, history=None):
520
501
        """Find the revision id of the specified revno."""
521
502
        if revno == 0:
522
 
            return None
 
503
            return _mod_revision.NULL_REVISION
523
504
        if history is None:
524
505
            history = self.revision_history()
525
506
        if revno <= 0 or revno > len(history):
526
507
            raise errors.NoSuchRevision(self, revno)
527
508
        return history[revno - 1]
528
509
 
529
 
    def pull(self, source, overwrite=False, stop_revision=None):
 
510
    def pull(self, source, overwrite=False, stop_revision=None,
 
511
             possible_transports=None):
530
512
        """Mirror source into this branch.
531
513
 
532
514
        This branch is considered to be 'local', having low latency.
586
568
            url = ''
587
569
        elif make_relative:
588
570
            url = urlutils.relative_url(self.base, url)
589
 
        config.set_user_option(name, url)
 
571
        config.set_user_option(name, url, warn_masked=True)
590
572
 
591
573
    def _get_config_location(self, name, config=None):
592
574
        if config is None:
612
594
        pattern is that the user can override it by specifying a
613
595
        location.
614
596
        """
615
 
        self.get_config().set_user_option('submit_branch', location)
 
597
        self.get_config().set_user_option('submit_branch', location,
 
598
            warn_masked=True)
616
599
 
617
600
    def get_public_branch(self):
618
601
        """Return the public location of the branch.
702
685
        :param revision_id: The revision-id to truncate history at.  May
703
686
          be None to copy complete history.
704
687
        """
 
688
        if revision_id == _mod_revision.NULL_REVISION:
 
689
            new_history = []
705
690
        new_history = self.revision_history()
706
 
        if revision_id is not None:
707
 
            revision_id = osutils.safe_revision_id(revision_id)
 
691
        if revision_id is not None and new_history != []:
708
692
            try:
709
693
                new_history = new_history[:new_history.index(revision_id) + 1]
710
694
            except ValueError:
773
757
        return format
774
758
 
775
759
    def create_checkout(self, to_location, revision_id=None,
776
 
                        lightweight=False):
 
760
                        lightweight=False, accelerator_tree=None):
777
761
        """Create a checkout of a branch.
778
762
        
779
763
        :param to_location: The url to produce the checkout at
780
764
        :param revision_id: The revision to check out
781
765
        :param lightweight: If True, produce a lightweight checkout, otherwise,
782
766
        produce a bound branch (heavyweight checkout)
 
767
        :param accelerator_tree: A tree which can be used for retrieving file
 
768
            contents more quickly than the revision tree, i.e. a workingtree.
 
769
            The revision tree will be used for cases where accelerator_tree's
 
770
            content is different.
783
771
        :return: The tree of the created checkout
784
772
        """
785
773
        t = transport.get_transport(to_location)
787
775
        if lightweight:
788
776
            format = self._get_checkout_format()
789
777
            checkout = format.initialize_on_transport(t)
790
 
            BranchReferenceFormat().initialize(checkout, self)
 
778
            from_branch = BranchReferenceFormat().initialize(checkout, self)
791
779
        else:
792
780
            format = self._get_checkout_format()
793
781
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
797
785
            # pull up to the specified revision_id to set the initial 
798
786
            # branch tip correctly, and seed it with history.
799
787
            checkout_branch.pull(self, stop_revision=revision_id)
800
 
        tree = checkout.create_workingtree(revision_id)
 
788
            from_branch=None
 
789
        tree = checkout.create_workingtree(revision_id,
 
790
                                           from_branch=from_branch,
 
791
                                           accelerator_tree=accelerator_tree)
801
792
        basis_tree = tree.basis_tree()
802
793
        basis_tree.lock_read()
803
794
        try:
882
873
        """
883
874
        return None
884
875
 
 
876
    @classmethod
 
877
    def set_reference(self, a_bzrdir, to_branch):
 
878
        """Set the target reference of the branch in a_bzrdir.
 
879
 
 
880
        format probing must have been completed before calling
 
881
        this method - it is assumed that the format of the branch
 
882
        in a_bzrdir is correct.
 
883
 
 
884
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
885
        :param to_branch: branch that the checkout is to reference
 
886
        """
 
887
        raise NotImplementedError(self.set_reference)
 
888
 
885
889
    def get_format_string(self):
886
890
        """Return the ASCII format string that identifies this format."""
887
891
        raise NotImplementedError(self.get_format_string)
1018
1022
        # is read locked and the target branches write locked. The local
1019
1023
        # branch is the low-latency branch.
1020
1024
        self['post_pull'] = []
 
1025
        # invoked before a commit operation takes place.
 
1026
        # the api signature is
 
1027
        # (local, master, old_revno, old_revid, future_revno, future_revid,
 
1028
        #  tree_delta, future_tree).
 
1029
        # old_revid is NULL_REVISION for the first commit to a branch
 
1030
        # tree_delta is a TreeDelta object describing changes from the basis
 
1031
        # revision, hooks MUST NOT modify this delta
 
1032
        # future_tree is an in-memory tree obtained from
 
1033
        # CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
 
1034
        self['pre_commit'] = []
1021
1035
        # invoked after a commit operation completes.
1022
1036
        # the api signature is 
1023
1037
        # (local, master, old_revno, old_revid, new_revno, new_revid)
1131
1145
 
1132
1146
 
1133
1147
class BzrBranchFormat6(BzrBranchFormat5):
1134
 
    """Branch format with last-revision
 
1148
    """Branch format with last-revision and tags.
1135
1149
 
1136
1150
    Unlike previous formats, this has no explicit revision history. Instead,
1137
1151
    this just stores the last-revision, and the left-hand history leading
1138
1152
    up to there is the history.
1139
1153
 
1140
1154
    This format was introduced in bzr 0.15
 
1155
    and became the default in 0.91.
1141
1156
    """
1142
1157
 
1143
1158
    def get_format_string(self):
1151
1166
    def initialize(self, a_bzrdir):
1152
1167
        """Create a branch of this format in a_bzrdir."""
1153
1168
        utf8_files = [('last-revision', '0 null:\n'),
1154
 
                      ('branch-name', ''),
1155
1169
                      ('branch.conf', ''),
1156
1170
                      ('tags', ''),
1157
1171
                      ]
1196
1210
    def get_format_description(self):
1197
1211
        """See BranchFormat.get_format_description()."""
1198
1212
        return "Checkout reference format 1"
1199
 
        
 
1213
 
1200
1214
    def get_reference(self, a_bzrdir):
1201
1215
        """See BranchFormat.get_reference()."""
1202
1216
        transport = a_bzrdir.get_branch_transport(None)
1203
1217
        return transport.get('location').read()
1204
1218
 
 
1219
    def set_reference(self, a_bzrdir, to_branch):
 
1220
        """See BranchFormat.set_reference()."""
 
1221
        transport = a_bzrdir.get_branch_transport(None)
 
1222
        location = transport.put_bytes('location', to_branch.base)
 
1223
 
1205
1224
    def initialize(self, a_bzrdir, target_branch=None):
1206
1225
        """Create a branch of this format in a_bzrdir."""
1207
1226
        if target_branch is None:
1213
1232
        branch_transport.put_bytes('location',
1214
1233
            target_branch.bzrdir.root_transport.base)
1215
1234
        branch_transport.put_bytes('format', self.get_format_string())
1216
 
        return self.open(a_bzrdir, _found=True)
 
1235
        return self.open(
 
1236
            a_bzrdir, _found=True,
 
1237
            possible_transports=[target_branch.bzrdir.root_transport])
1217
1238
 
1218
1239
    def __init__(self):
1219
1240
        super(BranchReferenceFormat, self).__init__()
1229
1250
            # emit some sort of warning/error to the caller ?!
1230
1251
        return clone
1231
1252
 
1232
 
    def open(self, a_bzrdir, _found=False, location=None):
 
1253
    def open(self, a_bzrdir, _found=False, location=None,
 
1254
             possible_transports=None):
1233
1255
        """Return the branch that the branch reference in a_bzrdir points at.
1234
1256
 
1235
1257
        _found is a private parameter, do not use it. It is used to indicate
1240
1262
            assert format.__class__ == self.__class__
1241
1263
        if location is None:
1242
1264
            location = self.get_reference(a_bzrdir)
1243
 
        real_bzrdir = bzrdir.BzrDir.open(location)
 
1265
        real_bzrdir = bzrdir.BzrDir.open(
 
1266
            location, possible_transports=possible_transports)
1244
1267
        result = real_bzrdir.open_branch()
1245
1268
        # this changes the behaviour of result.clone to create a new reference
1246
1269
        # rather than a copy of the content of the branch.
1256
1279
 
1257
1280
# formats which have no format string are not discoverable
1258
1281
# and not independently creatable, so are not registered.
1259
 
__default_format = BzrBranchFormat5()
1260
 
BranchFormat.register_format(__default_format)
 
1282
__format5 = BzrBranchFormat5()
 
1283
__format6 = BzrBranchFormat6()
 
1284
BranchFormat.register_format(__format5)
1261
1285
BranchFormat.register_format(BranchReferenceFormat())
1262
 
BranchFormat.register_format(BzrBranchFormat6())
1263
 
BranchFormat.set_default_format(__default_format)
 
1286
BranchFormat.register_format(__format6)
 
1287
BranchFormat.set_default_format(__format6)
1264
1288
_legacy_formats = [BzrBranchFormat4(),
1265
1289
                   ]
1266
1290
 
1312
1336
    def get_root_id(self):
1313
1337
        """See Branch.get_root_id."""
1314
1338
        tree = self.repository.revision_tree(self.last_revision())
1315
 
        return tree.inventory.root.file_id
 
1339
        return tree.get_root_id()
1316
1340
 
1317
1341
    def is_locked(self):
1318
1342
        return self.control_files.is_locked()
1358
1382
        """See Branch.print_file."""
1359
1383
        return self.repository.print_file(file, revision_id)
1360
1384
 
1361
 
    @needs_write_lock
1362
 
    def append_revision(self, *revision_ids):
1363
 
        """See Branch.append_revision."""
1364
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1365
 
        for revision_id in revision_ids:
1366
 
            _mod_revision.check_not_reserved_id(revision_id)
1367
 
            mutter("add {%s} to revision-history" % revision_id)
1368
 
        rev_history = self.revision_history()
1369
 
        rev_history.extend(revision_ids)
1370
 
        self.set_revision_history(rev_history)
1371
 
 
1372
1385
    def _write_revision_history(self, history):
1373
1386
        """Factored out of set_revision_history.
1374
1387
 
1380
1393
    @needs_write_lock
1381
1394
    def set_revision_history(self, rev_history):
1382
1395
        """See Branch.set_revision_history."""
1383
 
        rev_history = [osutils.safe_revision_id(r) for r in rev_history]
 
1396
        if 'evil' in debug.debug_flags:
 
1397
            mutter_callsite(3, "set_revision_history scales with history.")
1384
1398
        self._clear_cached_state()
1385
1399
        self._write_revision_history(rev_history)
1386
1400
        self._cache_revision_history(rev_history)
1389
1403
 
1390
1404
    @needs_write_lock
1391
1405
    def set_last_revision_info(self, revno, revision_id):
1392
 
        revision_id = osutils.safe_revision_id(revision_id)
 
1406
        """Set the last revision of this branch.
 
1407
 
 
1408
        The caller is responsible for checking that the revno is correct
 
1409
        for this revision id.
 
1410
 
 
1411
        It may be possible to set the branch last revision to an id not
 
1412
        present in the repository.  However, branches can also be 
 
1413
        configured to check constraints on history, in which case this may not
 
1414
        be permitted.
 
1415
        """
1393
1416
        history = self._lefthand_history(revision_id)
1394
1417
        assert len(history) == revno, '%d != %d' % (len(history), revno)
1395
1418
        self.set_revision_history(history)
1403
1426
 
1404
1427
    def _lefthand_history(self, revision_id, last_rev=None,
1405
1428
                          other_branch=None):
 
1429
        if 'evil' in debug.debug_flags:
 
1430
            mutter_callsite(4, "_lefthand_history scales with history.")
1406
1431
        # stop_revision must be a descendant of last_revision
1407
1432
        stop_graph = self.repository.get_revision_graph(revision_id)
1408
 
        if last_rev is not None and last_rev not in stop_graph:
 
1433
        if (last_rev is not None and last_rev != _mod_revision.NULL_REVISION
 
1434
            and last_rev not in stop_graph):
1409
1435
            # our previous tip is not merged into stop_revision
1410
1436
            raise errors.DivergedBranches(self, other_branch)
1411
1437
        # make a new revision history from the graph
1432
1458
        :param other_branch: The other branch that DivergedBranches should
1433
1459
            raise with respect to.
1434
1460
        """
1435
 
        revision_id = osutils.safe_revision_id(revision_id)
1436
1461
        self.set_revision_history(self._lefthand_history(revision_id,
1437
1462
            last_rev, other_branch))
1438
1463
 
1439
1464
    @needs_write_lock
1440
 
    def update_revisions(self, other, stop_revision=None):
 
1465
    def update_revisions(self, other, stop_revision=None, overwrite=False):
1441
1466
        """See Branch.update_revisions."""
1442
1467
        other.lock_read()
1443
1468
        try:
 
1469
            other_last_revno, other_last_revision = other.last_revision_info()
1444
1470
            if stop_revision is None:
1445
 
                stop_revision = other.last_revision()
1446
 
                if stop_revision is None:
 
1471
                stop_revision = other_last_revision
 
1472
                if _mod_revision.is_null(stop_revision):
1447
1473
                    # if there are no commits, we're done.
1448
1474
                    return
1449
 
            else:
1450
 
                stop_revision = osutils.safe_revision_id(stop_revision)
1451
1475
            # whats the current last revision, before we fetch [and change it
1452
1476
            # possibly]
1453
 
            last_rev = self.last_revision()
1454
 
            # we fetch here regardless of whether we need to so that we pickup
1455
 
            # filled in ghosts.
 
1477
            last_rev = _mod_revision.ensure_null(self.last_revision())
 
1478
            # we fetch here so that we don't process data twice in the common
 
1479
            # case of having something to pull, and so that the check for 
 
1480
            # already merged can operate on the just fetched graph, which will
 
1481
            # be cached in memory.
1456
1482
            self.fetch(other, stop_revision)
1457
 
            my_ancestry = self.repository.get_ancestry(last_rev)
1458
 
            if stop_revision in my_ancestry:
1459
 
                # last_revision is a descendant of stop_revision
1460
 
                return
1461
 
            self.generate_revision_history(stop_revision, last_rev=last_rev,
1462
 
                other_branch=other)
 
1483
            # Check to see if one is an ancestor of the other
 
1484
            if not overwrite:
 
1485
                heads = self.repository.get_graph().heads([stop_revision,
 
1486
                                                           last_rev])
 
1487
                if heads == set([last_rev]):
 
1488
                    # The current revision is a decendent of the target,
 
1489
                    # nothing to do
 
1490
                    return
 
1491
                elif heads == set([stop_revision, last_rev]):
 
1492
                    # These branches have diverged
 
1493
                    raise errors.DivergedBranches(self, other)
 
1494
                assert heads == set([stop_revision])
 
1495
            if other_last_revision == stop_revision:
 
1496
                self.set_last_revision_info(other_last_revno,
 
1497
                                            other_last_revision)
 
1498
            else:
 
1499
                # TODO: jam 2007-11-29 Is there a way to determine the
 
1500
                #       revno without searching all of history??
 
1501
                if overwrite:
 
1502
                    self.generate_revision_history(stop_revision)
 
1503
                else:
 
1504
                    self.generate_revision_history(stop_revision,
 
1505
                        last_rev=last_rev, other_branch=other)
1463
1506
        finally:
1464
1507
            other.unlock()
1465
1508
 
1467
1510
        """See Branch.basis_tree."""
1468
1511
        return self.repository.revision_tree(self.last_revision())
1469
1512
 
1470
 
    @deprecated_method(zero_eight)
1471
 
    def working_tree(self):
1472
 
        """Create a Working tree object for this branch."""
1473
 
 
1474
 
        from bzrlib.transport.local import LocalTransport
1475
 
        if (self.base.find('://') != -1 or 
1476
 
            not isinstance(self._transport, LocalTransport)):
1477
 
            raise NoWorkingTree(self.base)
1478
 
        return self.bzrdir.open_workingtree()
1479
 
 
1480
1513
    @needs_write_lock
1481
1514
    def pull(self, source, overwrite=False, stop_revision=None,
1482
 
             _hook_master=None, run_hooks=True):
 
1515
             _hook_master=None, run_hooks=True, possible_transports=None):
1483
1516
        """See Branch.pull.
1484
1517
 
1485
1518
        :param _hook_master: Private parameter - set the branch to 
1494
1527
        source.lock_read()
1495
1528
        try:
1496
1529
            result.old_revno, result.old_revid = self.last_revision_info()
1497
 
            try:
1498
 
                self.update_revisions(source, stop_revision)
1499
 
            except DivergedBranches:
1500
 
                if not overwrite:
1501
 
                    raise
1502
 
            if overwrite:
1503
 
                if stop_revision is None:
1504
 
                    stop_revision = source.last_revision()
1505
 
                self.generate_revision_history(stop_revision)
1506
 
            result.tag_conflicts = source.tags.merge_to(self.tags)
 
1530
            self.update_revisions(source, stop_revision, overwrite=overwrite)
 
1531
            result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1507
1532
            result.new_revno, result.new_revid = self.last_revision_info()
1508
1533
            if _hook_master:
1509
1534
                result.master_branch = _hook_master
1612
1637
                raise
1613
1638
        if overwrite:
1614
1639
            target.set_revision_history(self.revision_history())
1615
 
        result.tag_conflicts = self.tags.merge_to(target.tags)
 
1640
        result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1616
1641
        result.new_revno, result.new_revid = target.last_revision_info()
1617
1642
        return result
1618
1643
 
1664
1689
            assert isinstance(url, str)
1665
1690
            self.control_files.put_bytes('parent', url + '\n')
1666
1691
 
1667
 
    @deprecated_function(zero_nine)
1668
 
    def tree_config(self):
1669
 
        """DEPRECATED; call get_config instead.  
1670
 
        TreeConfig has become part of BranchConfig."""
1671
 
        return TreeConfig(self)
1672
 
 
1673
1692
 
1674
1693
class BzrBranch5(BzrBranch):
1675
 
    """A format 5 branch. This supports new features over plan branches.
 
1694
    """A format 5 branch. This supports new features over plain branches.
1676
1695
 
1677
1696
    It has support for a master_branch which is the data for bound branches.
1678
1697
    """
1689
1708
        
1690
1709
    @needs_write_lock
1691
1710
    def pull(self, source, overwrite=False, stop_revision=None,
1692
 
             run_hooks=True):
 
1711
             run_hooks=True, possible_transports=None):
1693
1712
        """Pull from source into self, updating my master if any.
1694
1713
        
1695
1714
        :param run_hooks: Private parameter - if false, this branch
1700
1719
        master_branch = None
1701
1720
        if bound_location and source.base != bound_location:
1702
1721
            # not pulling from master, so we need to update master.
1703
 
            master_branch = self.get_master_branch()
 
1722
            master_branch = self.get_master_branch(possible_transports)
1704
1723
            master_branch.lock_write()
1705
1724
        try:
1706
1725
            if master_branch:
1721
1740
            return None
1722
1741
 
1723
1742
    @needs_read_lock
1724
 
    def get_master_branch(self):
 
1743
    def get_master_branch(self, possible_transports=None):
1725
1744
        """Return the branch we are bound to.
1726
1745
        
1727
1746
        :return: Either a Branch, or None
1735
1754
        if not bound_loc:
1736
1755
            return None
1737
1756
        try:
1738
 
            return Branch.open(bound_loc)
 
1757
            return Branch.open(bound_loc,
 
1758
                               possible_transports=possible_transports)
1739
1759
        except (errors.NotBranchError, errors.ConnectionError), e:
1740
1760
            raise errors.BoundBranchConnectionFailure(
1741
1761
                    self, bound_loc, e)
1779
1799
        # last_rev is not in the other_last_rev history, AND
1780
1800
        # other_last_rev is not in our history, and do it without pulling
1781
1801
        # history around
1782
 
        last_rev = self.last_revision()
1783
 
        if last_rev is not None:
1784
 
            other.lock_read()
1785
 
            try:
1786
 
                other_last_rev = other.last_revision()
1787
 
                if other_last_rev is not None:
1788
 
                    # neither branch is new, we have to do some work to
1789
 
                    # ascertain diversion.
1790
 
                    remote_graph = other.repository.get_revision_graph(
1791
 
                        other_last_rev)
1792
 
                    local_graph = self.repository.get_revision_graph(last_rev)
1793
 
                    if (last_rev not in remote_graph and
1794
 
                        other_last_rev not in local_graph):
1795
 
                        raise errors.DivergedBranches(self, other)
1796
 
            finally:
1797
 
                other.unlock()
1798
1802
        self.set_bound_location(other.base)
1799
1803
 
1800
1804
    @needs_write_lock
1803
1807
        return self.set_bound_location(None)
1804
1808
 
1805
1809
    @needs_write_lock
1806
 
    def update(self):
 
1810
    def update(self, possible_transports=None):
1807
1811
        """Synchronise this branch with the master branch if any. 
1808
1812
 
1809
1813
        :return: None or the last_revision that was pivoted out during the
1810
1814
                 update.
1811
1815
        """
1812
 
        master = self.get_master_branch()
 
1816
        master = self.get_master_branch(possible_transports)
1813
1817
        if master is not None:
1814
 
            old_tip = self.last_revision()
 
1818
            old_tip = _mod_revision.ensure_null(self.last_revision())
1815
1819
            self.pull(master, overwrite=True)
1816
 
            if old_tip in self.repository.get_ancestry(self.last_revision()):
 
1820
            if self.repository.get_graph().is_ancestor(old_tip,
 
1821
                _mod_revision.ensure_null(self.last_revision())):
1817
1822
                return None
1818
1823
            return old_tip
1819
1824
        return None
1862
1867
        return None
1863
1868
 
1864
1869
    @classmethod
 
1870
    def set_reference(self, a_bzrdir, to_branch):
 
1871
        """Set the target reference of the branch in a_bzrdir.
 
1872
 
 
1873
        format probing must have been completed before calling
 
1874
        this method - it is assumed that the format of the branch
 
1875
        in a_bzrdir is correct.
 
1876
 
 
1877
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1878
        :param to_branch: branch that the checkout is to reference
 
1879
        """
 
1880
        raise NotImplementedError(self.set_reference)
 
1881
 
 
1882
    @classmethod
1865
1883
    def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1866
1884
            lock_class):
1867
1885
        branch_transport = a_bzrdir.get_branch_transport(cls)
1933
1951
    def last_revision(self):
1934
1952
        """Return last revision id, or None"""
1935
1953
        revision_id = self.last_revision_info()[1]
1936
 
        if revision_id == _mod_revision.NULL_REVISION:
1937
 
            revision_id = None
1938
1954
        return revision_id
1939
1955
 
1940
1956
    def _write_last_revision_info(self, revno, revision_id):
1953
1969
 
1954
1970
    @needs_write_lock
1955
1971
    def set_last_revision_info(self, revno, revision_id):
1956
 
        revision_id = osutils.safe_revision_id(revision_id)
1957
1972
        if self._get_append_revisions_only():
1958
1973
            self._check_history_violation(revision_id)
1959
1974
        self._write_last_revision_info(revno, revision_id)
1960
1975
        self._clear_cached_state()
1961
1976
 
1962
1977
    def _check_history_violation(self, revision_id):
1963
 
        last_revision = self.last_revision()
1964
 
        if last_revision is None:
 
1978
        last_revision = _mod_revision.ensure_null(self.last_revision())
 
1979
        if _mod_revision.is_null(last_revision):
1965
1980
            return
1966
1981
        if last_revision not in self._lefthand_history(revision_id):
1967
1982
            raise errors.AppendRevisionsOnlyViolation(self.base)
1991
2006
        self._write_last_revision_info(len(history), last_revision)
1992
2007
 
1993
2008
    @needs_write_lock
1994
 
    def append_revision(self, *revision_ids):
1995
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1996
 
        if len(revision_ids) == 0:
1997
 
            return
1998
 
        prev_revno, prev_revision = self.last_revision_info()
1999
 
        for revision in self.repository.get_revisions(revision_ids):
2000
 
            if prev_revision == _mod_revision.NULL_REVISION:
2001
 
                if revision.parent_ids != []:
2002
 
                    raise errors.NotLeftParentDescendant(self, prev_revision,
2003
 
                                                         revision.revision_id)
2004
 
            else:
2005
 
                if revision.parent_ids[0] != prev_revision:
2006
 
                    raise errors.NotLeftParentDescendant(self, prev_revision,
2007
 
                                                         revision.revision_id)
2008
 
            prev_revision = revision.revision_id
2009
 
        self.set_last_revision_info(prev_revno + len(revision_ids),
2010
 
                                    revision_ids[-1])
2011
 
 
2012
 
    @needs_write_lock
2013
2009
    def _set_parent_location(self, url):
2014
2010
        """Set the parent branch"""
2015
2011
        self._set_config_location('parent_location', url, make_relative=True)
2031
2027
            if config.get_user_option('bound') != 'True':
2032
2028
                return False
2033
2029
            else:
2034
 
                config.set_user_option('bound', 'False')
 
2030
                config.set_user_option('bound', 'False', warn_masked=True)
2035
2031
                return True
2036
2032
        else:
2037
2033
            self._set_config_location('bound_location', location,
2038
2034
                                      config=config)
2039
 
            config.set_user_option('bound', 'True')
 
2035
            config.set_user_option('bound', 'True', warn_masked=True)
2040
2036
        return True
2041
2037
 
2042
2038
    def _get_bound_location(self, bound):
2062
2058
            value = 'True'
2063
2059
        else:
2064
2060
            value = 'False'
2065
 
        self.get_config().set_user_option('append_revisions_only', value)
 
2061
        self.get_config().set_user_option('append_revisions_only', value,
 
2062
            warn_masked=True)
2066
2063
 
2067
2064
    def _get_append_revisions_only(self):
2068
2065
        value = self.get_config().get_user_option('append_revisions_only')
2080
2077
        :param revision_id: The revision-id to truncate history at.  May
2081
2078
          be None to copy complete history.
2082
2079
        """
 
2080
        source_revno, source_revision_id = self.last_revision_info()
2083
2081
        if revision_id is None:
2084
 
            revno, revision_id = self.last_revision_info()
 
2082
            revno, revision_id = source_revno, source_revision_id
 
2083
        elif source_revision_id == revision_id:
 
2084
            # we know the revno without needing to walk all of history
 
2085
            revno = source_revno
2085
2086
        else:
2086
2087
            # To figure out the revno for a random revision, we need to build
2087
2088
            # the revision history, and count its length.
2097
2098
        return BasicTags(self)
2098
2099
 
2099
2100
 
2100
 
class BranchTestProviderAdapter(object):
2101
 
    """A tool to generate a suite testing multiple branch formats at once.
2102
 
 
2103
 
    This is done by copying the test once for each transport and injecting
2104
 
    the transport_server, transport_readonly_server, and branch_format
2105
 
    classes into each copy. Each copy is also given a new id() to make it
2106
 
    easy to identify.
2107
 
    """
2108
 
 
2109
 
    def __init__(self, transport_server, transport_readonly_server, formats,
2110
 
        vfs_transport_factory=None):
2111
 
        self._transport_server = transport_server
2112
 
        self._transport_readonly_server = transport_readonly_server
2113
 
        self._formats = formats
2114
 
    
2115
 
    def adapt(self, test):
2116
 
        result = TestSuite()
2117
 
        for branch_format, bzrdir_format in self._formats:
2118
 
            new_test = deepcopy(test)
2119
 
            new_test.transport_server = self._transport_server
2120
 
            new_test.transport_readonly_server = self._transport_readonly_server
2121
 
            new_test.bzrdir_format = bzrdir_format
2122
 
            new_test.branch_format = branch_format
2123
 
            def make_new_test_id():
2124
 
                # the format can be either a class or an instance
2125
 
                name = getattr(branch_format, '__name__',
2126
 
                        branch_format.__class__.__name__)
2127
 
                new_id = "%s(%s)" % (new_test.id(), name)
2128
 
                return lambda: new_id
2129
 
            new_test.id = make_new_test_id()
2130
 
            result.addTest(new_test)
2131
 
        return result
2132
 
 
2133
 
 
2134
2101
######################################################################
2135
2102
# results of operations
2136
2103