~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Aaron Bentley
  • Date: 2007-07-11 19:44:51 UTC
  • mto: This revision was merged to the branch mainline in revision 2606.
  • Revision ID: abentley@panoramicfeedback.com-20070711194451-3jqhye1nnd02a9uv
Restore original Branch.last_revision behavior, fix bits that care

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
        bzrdir,
27
27
        cache_utf8,
28
28
        config as _mod_config,
29
 
        debug,
30
29
        errors,
31
30
        lockdir,
32
31
        lockable_files,
50
49
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
51
50
                           HistoryMissing, InvalidRevisionId,
52
51
                           InvalidRevisionNumber, LockError, NoSuchFile,
53
 
                           NoSuchRevision, NotVersionedError,
 
52
                           NoSuchRevision, NoWorkingTree, NotVersionedError,
54
53
                           NotBranchError, UninitializableFormat,
55
54
                           UnlistableStore, UnlistableBranch,
56
55
                           )
60
59
                                      DEPRECATED_PARAMETER,
61
60
                                      deprecated_passed,
62
61
                                      zero_eight, zero_nine, zero_sixteen,
63
 
                                      zero_ninetyone,
64
62
                                      )
65
 
from bzrlib.trace import mutter, mutter_callsite, note
 
63
from bzrlib.trace import mutter, note
66
64
 
67
65
 
68
66
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
118
116
            master.break_lock()
119
117
 
120
118
    @staticmethod
121
 
    def open(base, _unsupported=False, possible_transports=None):
 
119
    @deprecated_method(zero_eight)
 
120
    def open_downlevel(base):
 
121
        """Open a branch which may be of an old format."""
 
122
        return Branch.open(base, _unsupported=True)
 
123
        
 
124
    @staticmethod
 
125
    def open(base, _unsupported=False):
122
126
        """Open the branch rooted at base.
123
127
 
124
128
        For instance, if the branch is at URL/.bzr/branch,
125
129
        Branch.open(URL) -> a Branch instance.
126
130
        """
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):
 
131
        control = bzrdir.BzrDir.open(base, _unsupported)
 
132
        return control.open_branch(_unsupported)
 
133
 
 
134
    @staticmethod
 
135
    def open_containing(url):
139
136
        """Open an existing branch which contains url.
140
137
        
141
138
        This probes for a branch at url, and searches upwards from there.
146
143
        format, UnknownFormatError or UnsupportedFormatError are raised.
147
144
        If there is one, it is returned, along with the unused portion of url.
148
145
        """
149
 
        control, relpath = bzrdir.BzrDir.open_containing(url,
150
 
                                                         possible_transports)
 
146
        control, relpath = bzrdir.BzrDir.open_containing(url)
151
147
        return control.open_branch(), relpath
152
148
 
 
149
    @staticmethod
 
150
    @deprecated_function(zero_eight)
 
151
    def initialize(base):
 
152
        """Create a new working tree and branch, rooted at 'base' (url)
 
153
 
 
154
        NOTE: This will soon be deprecated in favour of creation
 
155
        through a BzrDir.
 
156
        """
 
157
        return bzrdir.BzrDir.create_standalone_workingtree(base).branch
 
158
 
 
159
    @deprecated_function(zero_eight)
 
160
    def setup_caching(self, cache_root):
 
161
        """Subclasses that care about caching should override this, and set
 
162
        up cached stores located under cache_root.
 
163
        
 
164
        NOTE: This is unused.
 
165
        """
 
166
        pass
 
167
 
153
168
    def get_config(self):
154
169
        return BranchConfig(self)
155
170
 
322
337
        return self.repository.get_commit_builder(self, parents, config,
323
338
            timestamp, timezone, committer, revprops, revision_id)
324
339
 
325
 
    def get_master_branch(self, possible_transports=None):
 
340
    def get_master_branch(self):
326
341
        """Return the branch we are bound to.
327
342
        
328
343
        :return: Either a Branch, or None
354
369
        """Print `file` to stdout."""
355
370
        raise NotImplementedError(self.print_file)
356
371
 
 
372
    def append_revision(self, *revision_ids):
 
373
        raise NotImplementedError(self.append_revision)
 
374
 
357
375
    def set_revision_history(self, rev_history):
358
376
        raise NotImplementedError(self.set_revision_history)
359
377
 
405
423
 
406
424
    @needs_read_lock
407
425
    def revision_history(self):
408
 
        """Return sequence of revision ids on this branch.
 
426
        """Return sequence of revision hashes on to this branch.
409
427
        
410
428
        This method will cache the revision history for as long as it is safe to
411
429
        do so.
412
430
        """
413
 
        if 'evil' in debug.debug_flags:
414
 
            mutter_callsite(3, "revision_history scales with history.")
415
431
        if self._revision_history_cache is not None:
416
432
            history = self._revision_history_cache
417
433
        else:
425
441
        That is equivalent to the number of revisions committed to
426
442
        this branch.
427
443
        """
428
 
        return self.last_revision_info()[0]
 
444
        return len(self.revision_history())
429
445
 
430
446
    def unbind(self):
431
447
        """Older format branches cannot bind or unbind."""
441
457
        if ph:
442
458
            return ph[-1]
443
459
        else:
444
 
            return _mod_revision.NULL_REVISION
 
460
            return None
445
461
 
446
462
    def last_revision_info(self):
447
463
        """Return information about the last revision.
491
507
        """Given a revision id, return its revno"""
492
508
        if _mod_revision.is_null(revision_id):
493
509
            return 0
 
510
        revision_id = osutils.safe_revision_id(revision_id)
494
511
        history = self.revision_history()
495
512
        try:
496
513
            return history.index(revision_id) + 1
500
517
    def get_rev_id(self, revno, history=None):
501
518
        """Find the revision id of the specified revno."""
502
519
        if revno == 0:
503
 
            return _mod_revision.NULL_REVISION
 
520
            return None
504
521
        if history is None:
505
522
            history = self.revision_history()
506
523
        if revno <= 0 or revno > len(history):
507
524
            raise errors.NoSuchRevision(self, revno)
508
525
        return history[revno - 1]
509
526
 
510
 
    def pull(self, source, overwrite=False, stop_revision=None,
511
 
             possible_transports=None):
 
527
    def pull(self, source, overwrite=False, stop_revision=None):
512
528
        """Mirror source into this branch.
513
529
 
514
530
        This branch is considered to be 'local', having low latency.
689
705
            new_history = []
690
706
        new_history = self.revision_history()
691
707
        if revision_id is not None and new_history != []:
 
708
            revision_id = osutils.safe_revision_id(revision_id)
692
709
            try:
693
710
                new_history = new_history[:new_history.index(revision_id) + 1]
694
711
            except ValueError:
757
774
        return format
758
775
 
759
776
    def create_checkout(self, to_location, revision_id=None,
760
 
                        lightweight=False, accelerator_tree=None):
 
777
                        lightweight=False):
761
778
        """Create a checkout of a branch.
762
779
        
763
780
        :param to_location: The url to produce the checkout at
764
781
        :param revision_id: The revision to check out
765
782
        :param lightweight: If True, produce a lightweight checkout, otherwise,
766
783
        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.
771
784
        :return: The tree of the created checkout
772
785
        """
773
786
        t = transport.get_transport(to_location)
775
788
        if lightweight:
776
789
            format = self._get_checkout_format()
777
790
            checkout = format.initialize_on_transport(t)
778
 
            from_branch = BranchReferenceFormat().initialize(checkout, self)
 
791
            BranchReferenceFormat().initialize(checkout, self)
779
792
        else:
780
793
            format = self._get_checkout_format()
781
794
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
785
798
            # pull up to the specified revision_id to set the initial 
786
799
            # branch tip correctly, and seed it with history.
787
800
            checkout_branch.pull(self, stop_revision=revision_id)
788
 
            from_branch=None
789
 
        tree = checkout.create_workingtree(revision_id,
790
 
                                           from_branch=from_branch,
791
 
                                           accelerator_tree=accelerator_tree)
 
801
        tree = checkout.create_workingtree(revision_id)
792
802
        basis_tree = tree.basis_tree()
793
803
        basis_tree.lock_read()
794
804
        try:
873
883
        """
874
884
        return None
875
885
 
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
 
 
889
886
    def get_format_string(self):
890
887
        """Return the ASCII format string that identifies this format."""
891
888
        raise NotImplementedError(self.get_format_string)
1022
1019
        # is read locked and the target branches write locked. The local
1023
1020
        # branch is the low-latency branch.
1024
1021
        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'] = []
1035
1022
        # invoked after a commit operation completes.
1036
1023
        # the api signature is 
1037
1024
        # (local, master, old_revno, old_revid, new_revno, new_revid)
1145
1132
 
1146
1133
 
1147
1134
class BzrBranchFormat6(BzrBranchFormat5):
1148
 
    """Branch format with last-revision and tags.
 
1135
    """Branch format with last-revision
1149
1136
 
1150
1137
    Unlike previous formats, this has no explicit revision history. Instead,
1151
1138
    this just stores the last-revision, and the left-hand history leading
1152
1139
    up to there is the history.
1153
1140
 
1154
1141
    This format was introduced in bzr 0.15
1155
 
    and became the default in 0.91.
1156
1142
    """
1157
1143
 
1158
1144
    def get_format_string(self):
1166
1152
    def initialize(self, a_bzrdir):
1167
1153
        """Create a branch of this format in a_bzrdir."""
1168
1154
        utf8_files = [('last-revision', '0 null:\n'),
 
1155
                      ('branch-name', ''),
1169
1156
                      ('branch.conf', ''),
1170
1157
                      ('tags', ''),
1171
1158
                      ]
1210
1197
    def get_format_description(self):
1211
1198
        """See BranchFormat.get_format_description()."""
1212
1199
        return "Checkout reference format 1"
1213
 
 
 
1200
        
1214
1201
    def get_reference(self, a_bzrdir):
1215
1202
        """See BranchFormat.get_reference()."""
1216
1203
        transport = a_bzrdir.get_branch_transport(None)
1217
1204
        return transport.get('location').read()
1218
1205
 
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
 
 
1224
1206
    def initialize(self, a_bzrdir, target_branch=None):
1225
1207
        """Create a branch of this format in a_bzrdir."""
1226
1208
        if target_branch is None:
1232
1214
        branch_transport.put_bytes('location',
1233
1215
            target_branch.bzrdir.root_transport.base)
1234
1216
        branch_transport.put_bytes('format', self.get_format_string())
1235
 
        return self.open(
1236
 
            a_bzrdir, _found=True,
1237
 
            possible_transports=[target_branch.bzrdir.root_transport])
 
1217
        return self.open(a_bzrdir, _found=True)
1238
1218
 
1239
1219
    def __init__(self):
1240
1220
        super(BranchReferenceFormat, self).__init__()
1250
1230
            # emit some sort of warning/error to the caller ?!
1251
1231
        return clone
1252
1232
 
1253
 
    def open(self, a_bzrdir, _found=False, location=None,
1254
 
             possible_transports=None):
 
1233
    def open(self, a_bzrdir, _found=False, location=None):
1255
1234
        """Return the branch that the branch reference in a_bzrdir points at.
1256
1235
 
1257
1236
        _found is a private parameter, do not use it. It is used to indicate
1262
1241
            assert format.__class__ == self.__class__
1263
1242
        if location is None:
1264
1243
            location = self.get_reference(a_bzrdir)
1265
 
        real_bzrdir = bzrdir.BzrDir.open(
1266
 
            location, possible_transports=possible_transports)
 
1244
        real_bzrdir = bzrdir.BzrDir.open(location)
1267
1245
        result = real_bzrdir.open_branch()
1268
1246
        # this changes the behaviour of result.clone to create a new reference
1269
1247
        # rather than a copy of the content of the branch.
1279
1257
 
1280
1258
# formats which have no format string are not discoverable
1281
1259
# and not independently creatable, so are not registered.
1282
 
__format5 = BzrBranchFormat5()
1283
 
__format6 = BzrBranchFormat6()
1284
 
BranchFormat.register_format(__format5)
 
1260
__default_format = BzrBranchFormat5()
 
1261
BranchFormat.register_format(__default_format)
1285
1262
BranchFormat.register_format(BranchReferenceFormat())
1286
 
BranchFormat.register_format(__format6)
1287
 
BranchFormat.set_default_format(__format6)
 
1263
BranchFormat.register_format(BzrBranchFormat6())
 
1264
BranchFormat.set_default_format(__default_format)
1288
1265
_legacy_formats = [BzrBranchFormat4(),
1289
1266
                   ]
1290
1267
 
1336
1313
    def get_root_id(self):
1337
1314
        """See Branch.get_root_id."""
1338
1315
        tree = self.repository.revision_tree(self.last_revision())
1339
 
        return tree.get_root_id()
 
1316
        return tree.inventory.root.file_id
1340
1317
 
1341
1318
    def is_locked(self):
1342
1319
        return self.control_files.is_locked()
1382
1359
        """See Branch.print_file."""
1383
1360
        return self.repository.print_file(file, revision_id)
1384
1361
 
 
1362
    @needs_write_lock
 
1363
    def append_revision(self, *revision_ids):
 
1364
        """See Branch.append_revision."""
 
1365
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
 
1366
        for revision_id in revision_ids:
 
1367
            _mod_revision.check_not_reserved_id(revision_id)
 
1368
            mutter("add {%s} to revision-history" % revision_id)
 
1369
        rev_history = self.revision_history()
 
1370
        rev_history.extend(revision_ids)
 
1371
        self.set_revision_history(rev_history)
 
1372
 
1385
1373
    def _write_revision_history(self, history):
1386
1374
        """Factored out of set_revision_history.
1387
1375
 
1393
1381
    @needs_write_lock
1394
1382
    def set_revision_history(self, rev_history):
1395
1383
        """See Branch.set_revision_history."""
1396
 
        if 'evil' in debug.debug_flags:
1397
 
            mutter_callsite(3, "set_revision_history scales with history.")
 
1384
        rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1398
1385
        self._clear_cached_state()
1399
1386
        self._write_revision_history(rev_history)
1400
1387
        self._cache_revision_history(rev_history)
1403
1390
 
1404
1391
    @needs_write_lock
1405
1392
    def set_last_revision_info(self, revno, 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
        revision_id = osutils.safe_revision_id(revision_id)
1416
1394
        history = self._lefthand_history(revision_id)
1417
1395
        assert len(history) == revno, '%d != %d' % (len(history), revno)
1418
1396
        self.set_revision_history(history)
1426
1404
 
1427
1405
    def _lefthand_history(self, revision_id, last_rev=None,
1428
1406
                          other_branch=None):
1429
 
        if 'evil' in debug.debug_flags:
1430
 
            mutter_callsite(4, "_lefthand_history scales with history.")
1431
1407
        # stop_revision must be a descendant of last_revision
1432
1408
        stop_graph = self.repository.get_revision_graph(revision_id)
1433
1409
        if (last_rev is not None and last_rev != _mod_revision.NULL_REVISION
1458
1434
        :param other_branch: The other branch that DivergedBranches should
1459
1435
            raise with respect to.
1460
1436
        """
 
1437
        revision_id = osutils.safe_revision_id(revision_id)
1461
1438
        self.set_revision_history(self._lefthand_history(revision_id,
1462
1439
            last_rev, other_branch))
1463
1440
 
1464
1441
    @needs_write_lock
1465
 
    def update_revisions(self, other, stop_revision=None, overwrite=False):
 
1442
    def update_revisions(self, other, stop_revision=None):
1466
1443
        """See Branch.update_revisions."""
1467
1444
        other.lock_read()
1468
1445
        try:
1469
 
            other_last_revno, other_last_revision = other.last_revision_info()
1470
1446
            if stop_revision is None:
1471
 
                stop_revision = other_last_revision
1472
 
                if _mod_revision.is_null(stop_revision):
 
1447
                stop_revision = other.last_revision()
 
1448
                if stop_revision is None:
1473
1449
                    # if there are no commits, we're done.
1474
1450
                    return
 
1451
            else:
 
1452
                stop_revision = osutils.safe_revision_id(stop_revision)
1475
1453
            # whats the current last revision, before we fetch [and change it
1476
1454
            # possibly]
1477
1455
            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
            # we fetch here regardless of whether we need to so that we pickup
 
1457
            # filled in ghosts.
1482
1458
            self.fetch(other, stop_revision)
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)
 
1459
            my_ancestry = self.repository.get_ancestry(last_rev,
 
1460
                                                       topo_sorted=False)
 
1461
            if stop_revision in my_ancestry:
 
1462
                # last_revision is a descendant of stop_revision
 
1463
                return
 
1464
            self.generate_revision_history(stop_revision, last_rev=last_rev,
 
1465
                other_branch=other)
1506
1466
        finally:
1507
1467
            other.unlock()
1508
1468
 
1510
1470
        """See Branch.basis_tree."""
1511
1471
        return self.repository.revision_tree(self.last_revision())
1512
1472
 
 
1473
    @deprecated_method(zero_eight)
 
1474
    def working_tree(self):
 
1475
        """Create a Working tree object for this branch."""
 
1476
 
 
1477
        from bzrlib.transport.local import LocalTransport
 
1478
        if (self.base.find('://') != -1 or 
 
1479
            not isinstance(self._transport, LocalTransport)):
 
1480
            raise NoWorkingTree(self.base)
 
1481
        return self.bzrdir.open_workingtree()
 
1482
 
1513
1483
    @needs_write_lock
1514
1484
    def pull(self, source, overwrite=False, stop_revision=None,
1515
 
             _hook_master=None, run_hooks=True, possible_transports=None):
 
1485
             _hook_master=None, run_hooks=True):
1516
1486
        """See Branch.pull.
1517
1487
 
1518
1488
        :param _hook_master: Private parameter - set the branch to 
1527
1497
        source.lock_read()
1528
1498
        try:
1529
1499
            result.old_revno, result.old_revid = self.last_revision_info()
1530
 
            self.update_revisions(source, stop_revision, overwrite=overwrite)
1531
 
            result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
 
1500
            try:
 
1501
                self.update_revisions(source, stop_revision)
 
1502
            except DivergedBranches:
 
1503
                if not overwrite:
 
1504
                    raise
 
1505
            if overwrite:
 
1506
                if stop_revision is None:
 
1507
                    stop_revision = source.last_revision()
 
1508
                self.generate_revision_history(stop_revision)
 
1509
            result.tag_conflicts = source.tags.merge_to(self.tags)
1532
1510
            result.new_revno, result.new_revid = self.last_revision_info()
1533
1511
            if _hook_master:
1534
1512
                result.master_branch = _hook_master
1637
1615
                raise
1638
1616
        if overwrite:
1639
1617
            target.set_revision_history(self.revision_history())
1640
 
        result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
 
1618
        result.tag_conflicts = self.tags.merge_to(target.tags)
1641
1619
        result.new_revno, result.new_revid = target.last_revision_info()
1642
1620
        return result
1643
1621
 
1689
1667
            assert isinstance(url, str)
1690
1668
            self.control_files.put_bytes('parent', url + '\n')
1691
1669
 
 
1670
    @deprecated_function(zero_nine)
 
1671
    def tree_config(self):
 
1672
        """DEPRECATED; call get_config instead.  
 
1673
        TreeConfig has become part of BranchConfig."""
 
1674
        return TreeConfig(self)
 
1675
 
1692
1676
 
1693
1677
class BzrBranch5(BzrBranch):
1694
 
    """A format 5 branch. This supports new features over plain branches.
 
1678
    """A format 5 branch. This supports new features over plan branches.
1695
1679
 
1696
1680
    It has support for a master_branch which is the data for bound branches.
1697
1681
    """
1708
1692
        
1709
1693
    @needs_write_lock
1710
1694
    def pull(self, source, overwrite=False, stop_revision=None,
1711
 
             run_hooks=True, possible_transports=None):
 
1695
             run_hooks=True):
1712
1696
        """Pull from source into self, updating my master if any.
1713
1697
        
1714
1698
        :param run_hooks: Private parameter - if false, this branch
1719
1703
        master_branch = None
1720
1704
        if bound_location and source.base != bound_location:
1721
1705
            # not pulling from master, so we need to update master.
1722
 
            master_branch = self.get_master_branch(possible_transports)
 
1706
            master_branch = self.get_master_branch()
1723
1707
            master_branch.lock_write()
1724
1708
        try:
1725
1709
            if master_branch:
1740
1724
            return None
1741
1725
 
1742
1726
    @needs_read_lock
1743
 
    def get_master_branch(self, possible_transports=None):
 
1727
    def get_master_branch(self):
1744
1728
        """Return the branch we are bound to.
1745
1729
        
1746
1730
        :return: Either a Branch, or None
1754
1738
        if not bound_loc:
1755
1739
            return None
1756
1740
        try:
1757
 
            return Branch.open(bound_loc,
1758
 
                               possible_transports=possible_transports)
 
1741
            return Branch.open(bound_loc)
1759
1742
        except (errors.NotBranchError, errors.ConnectionError), e:
1760
1743
            raise errors.BoundBranchConnectionFailure(
1761
1744
                    self, bound_loc, e)
1799
1782
        # last_rev is not in the other_last_rev history, AND
1800
1783
        # other_last_rev is not in our history, and do it without pulling
1801
1784
        # history around
 
1785
        last_rev = _mod_revision.ensure_null(self.last_revision())
 
1786
        if not _mod_revision.is_null(last_rev):
 
1787
            other.lock_read()
 
1788
            try:
 
1789
                other_last_rev = other.last_revision()
 
1790
                if not _mod_revision.is_null(other_last_rev):
 
1791
                    # neither branch is new, we have to do some work to
 
1792
                    # ascertain diversion.
 
1793
                    remote_graph = other.repository.get_revision_graph(
 
1794
                        other_last_rev)
 
1795
                    local_graph = self.repository.get_revision_graph(last_rev)
 
1796
                    if (last_rev not in remote_graph and
 
1797
                        other_last_rev not in local_graph):
 
1798
                        raise errors.DivergedBranches(self, other)
 
1799
            finally:
 
1800
                other.unlock()
1802
1801
        self.set_bound_location(other.base)
1803
1802
 
1804
1803
    @needs_write_lock
1807
1806
        return self.set_bound_location(None)
1808
1807
 
1809
1808
    @needs_write_lock
1810
 
    def update(self, possible_transports=None):
 
1809
    def update(self):
1811
1810
        """Synchronise this branch with the master branch if any. 
1812
1811
 
1813
1812
        :return: None or the last_revision that was pivoted out during the
1814
1813
                 update.
1815
1814
        """
1816
 
        master = self.get_master_branch(possible_transports)
 
1815
        master = self.get_master_branch()
1817
1816
        if master is not None:
1818
 
            old_tip = _mod_revision.ensure_null(self.last_revision())
 
1817
            old_tip = self.last_revision()
1819
1818
            self.pull(master, overwrite=True)
1820
 
            if self.repository.get_graph().is_ancestor(old_tip,
1821
 
                _mod_revision.ensure_null(self.last_revision())):
 
1819
            if old_tip in self.repository.get_ancestry(
 
1820
                _mod_revision.ensure_null(self.last_revision()),
 
1821
                topo_sorted=False):
1822
1822
                return None
1823
1823
            return old_tip
1824
1824
        return None
1867
1867
        return None
1868
1868
 
1869
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
1883
1870
    def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1884
1871
            lock_class):
1885
1872
        branch_transport = a_bzrdir.get_branch_transport(cls)
1951
1938
    def last_revision(self):
1952
1939
        """Return last revision id, or None"""
1953
1940
        revision_id = self.last_revision_info()[1]
 
1941
        if revision_id == _mod_revision.NULL_REVISION:
 
1942
            revision_id = None
1954
1943
        return revision_id
1955
1944
 
1956
1945
    def _write_last_revision_info(self, revno, revision_id):
1969
1958
 
1970
1959
    @needs_write_lock
1971
1960
    def set_last_revision_info(self, revno, revision_id):
 
1961
        revision_id = osutils.safe_revision_id(revision_id)
1972
1962
        if self._get_append_revisions_only():
1973
1963
            self._check_history_violation(revision_id)
1974
1964
        self._write_last_revision_info(revno, revision_id)
2006
1996
        self._write_last_revision_info(len(history), last_revision)
2007
1997
 
2008
1998
    @needs_write_lock
 
1999
    def append_revision(self, *revision_ids):
 
2000
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
 
2001
        if len(revision_ids) == 0:
 
2002
            return
 
2003
        prev_revno, prev_revision = self.last_revision_info()
 
2004
        for revision in self.repository.get_revisions(revision_ids):
 
2005
            if prev_revision == _mod_revision.NULL_REVISION:
 
2006
                if revision.parent_ids != []:
 
2007
                    raise errors.NotLeftParentDescendant(self, prev_revision,
 
2008
                                                         revision.revision_id)
 
2009
            else:
 
2010
                if revision.parent_ids[0] != prev_revision:
 
2011
                    raise errors.NotLeftParentDescendant(self, prev_revision,
 
2012
                                                         revision.revision_id)
 
2013
            prev_revision = revision.revision_id
 
2014
        self.set_last_revision_info(prev_revno + len(revision_ids),
 
2015
                                    revision_ids[-1])
 
2016
 
 
2017
    @needs_write_lock
2009
2018
    def _set_parent_location(self, url):
2010
2019
        """Set the parent branch"""
2011
2020
        self._set_config_location('parent_location', url, make_relative=True)
2077
2086
        :param revision_id: The revision-id to truncate history at.  May
2078
2087
          be None to copy complete history.
2079
2088
        """
2080
 
        source_revno, source_revision_id = self.last_revision_info()
2081
2089
        if revision_id is None:
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
 
2090
            revno, revision_id = self.last_revision_info()
2086
2091
        else:
2087
2092
            # To figure out the revno for a random revision, we need to build
2088
2093
            # the revision history, and count its length.