~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
 
from cStringIO import StringIO
19
 
 
20
18
from bzrlib.lazy_import import lazy_import
21
19
lazy_import(globals(), """
22
 
from copy import deepcopy
23
 
from unittest import TestSuite
24
 
from warnings import warn
25
 
 
26
 
import bzrlib
27
20
from bzrlib import (
28
21
        bzrdir,
29
22
        cache_utf8,
30
23
        config as _mod_config,
 
24
        debug,
31
25
        errors,
32
26
        lockdir,
33
27
        lockable_files,
34
 
        osutils,
35
28
        revision as _mod_revision,
36
29
        transport,
37
 
        tree,
38
30
        tsort,
39
31
        ui,
40
32
        urlutils,
41
33
        )
42
 
from bzrlib.config import BranchConfig, TreeConfig
43
 
from bzrlib.lockable_files import LockableFiles, TransportLock
 
34
from bzrlib.config import BranchConfig
44
35
from bzrlib.tag import (
45
36
    BasicTags,
46
37
    DisabledTags,
48
39
""")
49
40
 
50
41
from bzrlib.decorators import needs_read_lock, needs_write_lock
51
 
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
52
 
                           HistoryMissing, InvalidRevisionId,
53
 
                           InvalidRevisionNumber, LockError, NoSuchFile,
54
 
                           NoSuchRevision, NoWorkingTree, NotVersionedError,
55
 
                           NotBranchError, UninitializableFormat,
56
 
                           UnlistableStore, UnlistableBranch,
57
 
                           )
58
42
from bzrlib.hooks import Hooks
59
 
from bzrlib.symbol_versioning import (deprecated_function,
60
 
                                      deprecated_method,
61
 
                                      DEPRECATED_PARAMETER,
62
 
                                      deprecated_passed,
63
 
                                      zero_eight, zero_nine, zero_sixteen,
 
43
from bzrlib.symbol_versioning import (deprecated_method,
 
44
                                      zero_sixteen,
64
45
                                      )
65
 
from bzrlib.trace import mutter, note
 
46
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
66
47
 
67
48
 
68
49
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
118
99
            master.break_lock()
119
100
 
120
101
    @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):
 
102
    def open(base, _unsupported=False, possible_transports=None):
128
103
        """Open the branch rooted at base.
129
104
 
130
105
        For instance, if the branch is at URL/.bzr/branch,
131
106
        Branch.open(URL) -> a Branch instance.
132
107
        """
133
 
        control = bzrdir.BzrDir.open(base, _unsupported)
134
 
        return control.open_branch(_unsupported)
135
 
 
136
 
    @staticmethod
137
 
    def open_containing(url):
 
108
        control = bzrdir.BzrDir.open(base, _unsupported,
 
109
                                     possible_transports=possible_transports)
 
110
        return control.open_branch(_unsupported)
 
111
 
 
112
    @staticmethod
 
113
    def open_from_transport(transport, _unsupported=False):
 
114
        """Open the branch rooted at transport"""
 
115
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
 
116
        return control.open_branch(_unsupported)
 
117
 
 
118
    @staticmethod
 
119
    def open_containing(url, possible_transports=None):
138
120
        """Open an existing branch which contains url.
139
121
        
140
122
        This probes for a branch at url, and searches upwards from there.
145
127
        format, UnknownFormatError or UnsupportedFormatError are raised.
146
128
        If there is one, it is returned, along with the unused portion of url.
147
129
        """
148
 
        control, relpath = bzrdir.BzrDir.open_containing(url)
 
130
        control, relpath = bzrdir.BzrDir.open_containing(url,
 
131
                                                         possible_transports)
149
132
        return control.open_branch(), relpath
150
133
 
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
134
    def get_config(self):
171
135
        return BranchConfig(self)
172
136
 
174
138
        return self.get_config().get_nickname()
175
139
 
176
140
    def _set_nick(self, nick):
177
 
        self.get_config().set_user_option('nickname', nick)
 
141
        self.get_config().set_user_option('nickname', nick, warn_masked=True)
178
142
 
179
143
    nick = property(_get_nick, _set_nick)
180
144
 
296
260
            if last_revision is None:
297
261
                pb.update('get source history')
298
262
                last_revision = from_branch.last_revision()
299
 
                if last_revision is None:
300
 
                    last_revision = _mod_revision.NULL_REVISION
 
263
                last_revision = _mod_revision.ensure_null(last_revision)
301
264
            return self.repository.fetch(from_branch.repository,
302
265
                                         revision_id=last_revision,
303
266
                                         pb=nested_pb)
339
302
        return self.repository.get_commit_builder(self, parents, config,
340
303
            timestamp, timezone, committer, revprops, revision_id)
341
304
 
342
 
    def get_master_branch(self):
 
305
    def get_master_branch(self, possible_transports=None):
343
306
        """Return the branch we are bound to.
344
307
        
345
308
        :return: Either a Branch, or None
355
318
        assert isinstance(revno, int)
356
319
        rh = self.revision_history()
357
320
        if not (1 <= revno <= len(rh)):
358
 
            raise InvalidRevisionNumber(revno)
 
321
            raise errors.InvalidRevisionNumber(revno)
359
322
        return self.repository.get_revision_delta(rh[revno-1])
360
323
 
361
324
    @deprecated_method(zero_sixteen)
371
334
        """Print `file` to stdout."""
372
335
        raise NotImplementedError(self.print_file)
373
336
 
374
 
    def append_revision(self, *revision_ids):
375
 
        raise NotImplementedError(self.append_revision)
376
 
 
377
337
    def set_revision_history(self, rev_history):
378
338
        raise NotImplementedError(self.set_revision_history)
379
339
 
425
385
 
426
386
    @needs_read_lock
427
387
    def revision_history(self):
428
 
        """Return sequence of revision hashes on to this branch.
 
388
        """Return sequence of revision ids on this branch.
429
389
        
430
390
        This method will cache the revision history for as long as it is safe to
431
391
        do so.
432
392
        """
 
393
        if 'evil' in debug.debug_flags:
 
394
            mutter_callsite(3, "revision_history scales with history.")
433
395
        if self._revision_history_cache is not None:
434
396
            history = self._revision_history_cache
435
397
        else:
443
405
        That is equivalent to the number of revisions committed to
444
406
        this branch.
445
407
        """
446
 
        return len(self.revision_history())
 
408
        return self.last_revision_info()[0]
447
409
 
448
410
    def unbind(self):
449
411
        """Older format branches cannot bind or unbind."""
454
416
        raise errors.UpgradeRequired(self.base)
455
417
 
456
418
    def last_revision(self):
457
 
        """Return last revision id, or None"""
458
 
        ph = self.revision_history()
459
 
        if ph:
460
 
            return ph[-1]
461
 
        else:
462
 
            return None
 
419
        """Return last revision id, or NULL_REVISION."""
 
420
        return self.last_revision_info()[1]
463
421
 
464
422
    def last_revision_info(self):
465
423
        """Return information about the last revision.
486
444
        common_index = min(self_len, other_len) -1
487
445
        if common_index >= 0 and \
488
446
            self_history[common_index] != other_history[common_index]:
489
 
            raise DivergedBranches(self, other)
 
447
            raise errors.DivergedBranches(self, other)
490
448
 
491
449
        if stop_revision is None:
492
450
            stop_revision = other_len
507
465
 
508
466
    def revision_id_to_revno(self, revision_id):
509
467
        """Given a revision id, return its revno"""
510
 
        if revision_id is None:
 
468
        if _mod_revision.is_null(revision_id):
511
469
            return 0
512
 
        revision_id = osutils.safe_revision_id(revision_id)
513
470
        history = self.revision_history()
514
471
        try:
515
472
            return history.index(revision_id) + 1
519
476
    def get_rev_id(self, revno, history=None):
520
477
        """Find the revision id of the specified revno."""
521
478
        if revno == 0:
522
 
            return None
 
479
            return _mod_revision.NULL_REVISION
523
480
        if history is None:
524
481
            history = self.revision_history()
525
482
        if revno <= 0 or revno > len(history):
526
483
            raise errors.NoSuchRevision(self, revno)
527
484
        return history[revno - 1]
528
485
 
529
 
    def pull(self, source, overwrite=False, stop_revision=None):
 
486
    def pull(self, source, overwrite=False, stop_revision=None,
 
487
             possible_transports=None):
530
488
        """Mirror source into this branch.
531
489
 
532
490
        This branch is considered to be 'local', having low latency.
586
544
            url = ''
587
545
        elif make_relative:
588
546
            url = urlutils.relative_url(self.base, url)
589
 
        config.set_user_option(name, url)
 
547
        config.set_user_option(name, url, warn_masked=True)
590
548
 
591
549
    def _get_config_location(self, name, config=None):
592
550
        if config is None:
612
570
        pattern is that the user can override it by specifying a
613
571
        location.
614
572
        """
615
 
        self.get_config().set_user_option('submit_branch', location)
 
573
        self.get_config().set_user_option('submit_branch', location,
 
574
            warn_masked=True)
616
575
 
617
576
    def get_public_branch(self):
618
577
        """Return the public location of the branch.
664
623
        Zero (the NULL revision) is considered invalid
665
624
        """
666
625
        if revno < 1 or revno > self.revno():
667
 
            raise InvalidRevisionNumber(revno)
 
626
            raise errors.InvalidRevisionNumber(revno)
668
627
 
669
628
    @needs_read_lock
670
629
    def clone(self, to_bzrdir, revision_id=None):
702
661
        :param revision_id: The revision-id to truncate history at.  May
703
662
          be None to copy complete history.
704
663
        """
 
664
        if revision_id == _mod_revision.NULL_REVISION:
 
665
            new_history = []
705
666
        new_history = self.revision_history()
706
 
        if revision_id is not None:
707
 
            revision_id = osutils.safe_revision_id(revision_id)
 
667
        if revision_id is not None and new_history != []:
708
668
            try:
709
669
                new_history = new_history[:new_history.index(revision_id) + 1]
710
670
            except ValueError:
773
733
        return format
774
734
 
775
735
    def create_checkout(self, to_location, revision_id=None,
776
 
                        lightweight=False):
 
736
                        lightweight=False, accelerator_tree=None,
 
737
                        hardlink=False):
777
738
        """Create a checkout of a branch.
778
739
        
779
740
        :param to_location: The url to produce the checkout at
780
741
        :param revision_id: The revision to check out
781
742
        :param lightweight: If True, produce a lightweight checkout, otherwise,
782
743
        produce a bound branch (heavyweight checkout)
 
744
        :param accelerator_tree: A tree which can be used for retrieving file
 
745
            contents more quickly than the revision tree, i.e. a workingtree.
 
746
            The revision tree will be used for cases where accelerator_tree's
 
747
            content is different.
 
748
        :param hardlink: If true, hard-link files from accelerator_tree,
 
749
            where possible.
783
750
        :return: The tree of the created checkout
784
751
        """
785
752
        t = transport.get_transport(to_location)
786
 
        try:
787
 
            t.mkdir('.')
788
 
        except errors.FileExists:
789
 
            pass
 
753
        t.ensure_base()
790
754
        if lightweight:
791
755
            format = self._get_checkout_format()
792
756
            checkout = format.initialize_on_transport(t)
793
 
            BranchReferenceFormat().initialize(checkout, self)
 
757
            from_branch = BranchReferenceFormat().initialize(checkout, self)
794
758
        else:
795
759
            format = self._get_checkout_format()
796
760
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
800
764
            # pull up to the specified revision_id to set the initial 
801
765
            # branch tip correctly, and seed it with history.
802
766
            checkout_branch.pull(self, stop_revision=revision_id)
803
 
        tree = checkout.create_workingtree(revision_id)
 
767
            from_branch=None
 
768
        tree = checkout.create_workingtree(revision_id,
 
769
                                           from_branch=from_branch,
 
770
                                           accelerator_tree=accelerator_tree,
 
771
                                           hardlink=hardlink)
804
772
        basis_tree = tree.basis_tree()
805
773
        basis_tree.lock_read()
806
774
        try:
850
818
    _formats = {}
851
819
    """The known formats."""
852
820
 
 
821
    def __eq__(self, other):
 
822
        return self.__class__ is other.__class__
 
823
 
 
824
    def __ne__(self, other):
 
825
        return not (self == other)
 
826
 
853
827
    @classmethod
854
828
    def find_format(klass, a_bzrdir):
855
829
        """Return the format for the branch object in a_bzrdir."""
857
831
            transport = a_bzrdir.get_branch_transport(None)
858
832
            format_string = transport.get("format").read()
859
833
            return klass._formats[format_string]
860
 
        except NoSuchFile:
861
 
            raise NotBranchError(path=transport.base)
 
834
        except errors.NoSuchFile:
 
835
            raise errors.NotBranchError(path=transport.base)
862
836
        except KeyError:
863
 
            raise errors.UnknownFormatError(format=format_string)
 
837
            raise errors.UnknownFormatError(format=format_string, kind='branch')
864
838
 
865
839
    @classmethod
866
840
    def get_default_format(klass):
879
853
        """
880
854
        return None
881
855
 
 
856
    @classmethod
 
857
    def set_reference(self, a_bzrdir, to_branch):
 
858
        """Set the target reference of the branch in a_bzrdir.
 
859
 
 
860
        format probing must have been completed before calling
 
861
        this method - it is assumed that the format of the branch
 
862
        in a_bzrdir is correct.
 
863
 
 
864
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
865
        :param to_branch: branch that the checkout is to reference
 
866
        """
 
867
        raise NotImplementedError(self.set_reference)
 
868
 
882
869
    def get_format_string(self):
883
870
        """Return the ASCII format string that identifies this format."""
884
871
        raise NotImplementedError(self.get_format_string)
1000
987
        # (push_result)
1001
988
        # containing the members
1002
989
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
1003
 
        # where local is the local branch or None, master is the target 
 
990
        # where local is the local target branch or None, master is the target 
1004
991
        # master branch, and the rest should be self explanatory. The source
1005
992
        # is read locked and the target branches write locked. Source will
1006
993
        # be the local low-latency branch.
1015
1002
        # is read locked and the target branches write locked. The local
1016
1003
        # branch is the low-latency branch.
1017
1004
        self['post_pull'] = []
 
1005
        # invoked before a commit operation takes place.
 
1006
        # the api signature is
 
1007
        # (local, master, old_revno, old_revid, future_revno, future_revid,
 
1008
        #  tree_delta, future_tree).
 
1009
        # old_revid is NULL_REVISION for the first commit to a branch
 
1010
        # tree_delta is a TreeDelta object describing changes from the basis
 
1011
        # revision, hooks MUST NOT modify this delta
 
1012
        # future_tree is an in-memory tree obtained from
 
1013
        # CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
 
1014
        self['pre_commit'] = []
1018
1015
        # invoked after a commit operation completes.
1019
1016
        # the api signature is 
1020
1017
        # (local, master, old_revno, old_revid, new_revno, new_revid)
1123
1120
                              _control_files=control_files,
1124
1121
                              a_bzrdir=a_bzrdir,
1125
1122
                              _repository=a_bzrdir.find_repository())
1126
 
        except NoSuchFile:
1127
 
            raise NotBranchError(path=transport.base)
 
1123
        except errors.NoSuchFile:
 
1124
            raise errors.NotBranchError(path=transport.base)
1128
1125
 
1129
1126
 
1130
1127
class BzrBranchFormat6(BzrBranchFormat5):
1131
 
    """Branch format with last-revision
 
1128
    """Branch format with last-revision and tags.
1132
1129
 
1133
1130
    Unlike previous formats, this has no explicit revision history. Instead,
1134
1131
    this just stores the last-revision, and the left-hand history leading
1135
1132
    up to there is the history.
1136
1133
 
1137
1134
    This format was introduced in bzr 0.15
 
1135
    and became the default in 0.91.
1138
1136
    """
1139
1137
 
1140
1138
    def get_format_string(self):
1148
1146
    def initialize(self, a_bzrdir):
1149
1147
        """Create a branch of this format in a_bzrdir."""
1150
1148
        utf8_files = [('last-revision', '0 null:\n'),
1151
 
                      ('branch-name', ''),
1152
1149
                      ('branch.conf', ''),
1153
1150
                      ('tags', ''),
1154
1151
                      ]
1193
1190
    def get_format_description(self):
1194
1191
        """See BranchFormat.get_format_description()."""
1195
1192
        return "Checkout reference format 1"
1196
 
        
 
1193
 
1197
1194
    def get_reference(self, a_bzrdir):
1198
1195
        """See BranchFormat.get_reference()."""
1199
1196
        transport = a_bzrdir.get_branch_transport(None)
1200
1197
        return transport.get('location').read()
1201
1198
 
 
1199
    def set_reference(self, a_bzrdir, to_branch):
 
1200
        """See BranchFormat.set_reference()."""
 
1201
        transport = a_bzrdir.get_branch_transport(None)
 
1202
        location = transport.put_bytes('location', to_branch.base)
 
1203
 
1202
1204
    def initialize(self, a_bzrdir, target_branch=None):
1203
1205
        """Create a branch of this format in a_bzrdir."""
1204
1206
        if target_branch is None:
1210
1212
        branch_transport.put_bytes('location',
1211
1213
            target_branch.bzrdir.root_transport.base)
1212
1214
        branch_transport.put_bytes('format', self.get_format_string())
1213
 
        return self.open(a_bzrdir, _found=True)
 
1215
        return self.open(
 
1216
            a_bzrdir, _found=True,
 
1217
            possible_transports=[target_branch.bzrdir.root_transport])
1214
1218
 
1215
1219
    def __init__(self):
1216
1220
        super(BranchReferenceFormat, self).__init__()
1226
1230
            # emit some sort of warning/error to the caller ?!
1227
1231
        return clone
1228
1232
 
1229
 
    def open(self, a_bzrdir, _found=False, location=None):
 
1233
    def open(self, a_bzrdir, _found=False, location=None,
 
1234
             possible_transports=None):
1230
1235
        """Return the branch that the branch reference in a_bzrdir points at.
1231
1236
 
1232
1237
        _found is a private parameter, do not use it. It is used to indicate
1237
1242
            assert format.__class__ == self.__class__
1238
1243
        if location is None:
1239
1244
            location = self.get_reference(a_bzrdir)
1240
 
        real_bzrdir = bzrdir.BzrDir.open(location)
 
1245
        real_bzrdir = bzrdir.BzrDir.open(
 
1246
            location, possible_transports=possible_transports)
1241
1247
        result = real_bzrdir.open_branch()
1242
1248
        # this changes the behaviour of result.clone to create a new reference
1243
1249
        # rather than a copy of the content of the branch.
1253
1259
 
1254
1260
# formats which have no format string are not discoverable
1255
1261
# and not independently creatable, so are not registered.
1256
 
__default_format = BzrBranchFormat5()
1257
 
BranchFormat.register_format(__default_format)
 
1262
__format5 = BzrBranchFormat5()
 
1263
__format6 = BzrBranchFormat6()
 
1264
BranchFormat.register_format(__format5)
1258
1265
BranchFormat.register_format(BranchReferenceFormat())
1259
 
BranchFormat.register_format(BzrBranchFormat6())
1260
 
BranchFormat.set_default_format(__default_format)
 
1266
BranchFormat.register_format(__format6)
 
1267
BranchFormat.set_default_format(__format6)
1261
1268
_legacy_formats = [BzrBranchFormat4(),
1262
1269
                   ]
1263
1270
 
1309
1316
    def get_root_id(self):
1310
1317
        """See Branch.get_root_id."""
1311
1318
        tree = self.repository.revision_tree(self.last_revision())
1312
 
        return tree.inventory.root.file_id
 
1319
        return tree.get_root_id()
1313
1320
 
1314
1321
    def is_locked(self):
1315
1322
        return self.control_files.is_locked()
1355
1362
        """See Branch.print_file."""
1356
1363
        return self.repository.print_file(file, revision_id)
1357
1364
 
1358
 
    @needs_write_lock
1359
 
    def append_revision(self, *revision_ids):
1360
 
        """See Branch.append_revision."""
1361
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1362
 
        for revision_id in revision_ids:
1363
 
            _mod_revision.check_not_reserved_id(revision_id)
1364
 
            mutter("add {%s} to revision-history" % revision_id)
1365
 
        rev_history = self.revision_history()
1366
 
        rev_history.extend(revision_ids)
1367
 
        self.set_revision_history(rev_history)
1368
 
 
1369
1365
    def _write_revision_history(self, history):
1370
1366
        """Factored out of set_revision_history.
1371
1367
 
1377
1373
    @needs_write_lock
1378
1374
    def set_revision_history(self, rev_history):
1379
1375
        """See Branch.set_revision_history."""
1380
 
        rev_history = [osutils.safe_revision_id(r) for r in rev_history]
 
1376
        if 'evil' in debug.debug_flags:
 
1377
            mutter_callsite(3, "set_revision_history scales with history.")
1381
1378
        self._clear_cached_state()
1382
1379
        self._write_revision_history(rev_history)
1383
1380
        self._cache_revision_history(rev_history)
1386
1383
 
1387
1384
    @needs_write_lock
1388
1385
    def set_last_revision_info(self, revno, revision_id):
1389
 
        revision_id = osutils.safe_revision_id(revision_id)
 
1386
        """Set the last revision of this branch.
 
1387
 
 
1388
        The caller is responsible for checking that the revno is correct
 
1389
        for this revision id.
 
1390
 
 
1391
        It may be possible to set the branch last revision to an id not
 
1392
        present in the repository.  However, branches can also be 
 
1393
        configured to check constraints on history, in which case this may not
 
1394
        be permitted.
 
1395
        """
 
1396
        revision_id = _mod_revision.ensure_null(revision_id)
1390
1397
        history = self._lefthand_history(revision_id)
1391
1398
        assert len(history) == revno, '%d != %d' % (len(history), revno)
1392
1399
        self.set_revision_history(history)
1400
1407
 
1401
1408
    def _lefthand_history(self, revision_id, last_rev=None,
1402
1409
                          other_branch=None):
 
1410
        if 'evil' in debug.debug_flags:
 
1411
            mutter_callsite(4, "_lefthand_history scales with history.")
1403
1412
        # stop_revision must be a descendant of last_revision
1404
 
        stop_graph = self.repository.get_revision_graph(revision_id)
1405
 
        if last_rev is not None and last_rev not in stop_graph:
1406
 
            # our previous tip is not merged into stop_revision
1407
 
            raise errors.DivergedBranches(self, other_branch)
 
1413
        graph = self.repository.get_graph()
 
1414
        if last_rev is not None:
 
1415
            if not graph.is_ancestor(last_rev, revision_id):
 
1416
                # our previous tip is not merged into stop_revision
 
1417
                raise errors.DivergedBranches(self, other_branch)
1408
1418
        # make a new revision history from the graph
 
1419
        parents_map = graph.get_parent_map([revision_id])
 
1420
        if revision_id not in parents_map:
 
1421
            raise errors.NoSuchRevision(self, revision_id)
1409
1422
        current_rev_id = revision_id
1410
1423
        new_history = []
1411
 
        while current_rev_id not in (None, _mod_revision.NULL_REVISION):
 
1424
        # Do not include ghosts or graph origin in revision_history
 
1425
        while (current_rev_id in parents_map and
 
1426
               len(parents_map[current_rev_id]) > 0):
1412
1427
            new_history.append(current_rev_id)
1413
 
            current_rev_id_parents = stop_graph[current_rev_id]
1414
 
            try:
1415
 
                current_rev_id = current_rev_id_parents[0]
1416
 
            except IndexError:
1417
 
                current_rev_id = None
 
1428
            current_rev_id = parents_map[current_rev_id][0]
 
1429
            parents_map = graph.get_parent_map([current_rev_id])
1418
1430
        new_history.reverse()
1419
1431
        return new_history
1420
1432
 
1429
1441
        :param other_branch: The other branch that DivergedBranches should
1430
1442
            raise with respect to.
1431
1443
        """
1432
 
        revision_id = osutils.safe_revision_id(revision_id)
1433
1444
        self.set_revision_history(self._lefthand_history(revision_id,
1434
1445
            last_rev, other_branch))
1435
1446
 
1436
1447
    @needs_write_lock
1437
 
    def update_revisions(self, other, stop_revision=None):
 
1448
    def update_revisions(self, other, stop_revision=None, overwrite=False):
1438
1449
        """See Branch.update_revisions."""
1439
1450
        other.lock_read()
1440
1451
        try:
 
1452
            other_last_revno, other_last_revision = other.last_revision_info()
1441
1453
            if stop_revision is None:
1442
 
                stop_revision = other.last_revision()
1443
 
                if stop_revision is None:
 
1454
                stop_revision = other_last_revision
 
1455
                if _mod_revision.is_null(stop_revision):
1444
1456
                    # if there are no commits, we're done.
1445
1457
                    return
1446
 
            else:
1447
 
                stop_revision = osutils.safe_revision_id(stop_revision)
1448
1458
            # whats the current last revision, before we fetch [and change it
1449
1459
            # possibly]
1450
 
            last_rev = self.last_revision()
1451
 
            # we fetch here regardless of whether we need to so that we pickup
1452
 
            # filled in ghosts.
 
1460
            last_rev = _mod_revision.ensure_null(self.last_revision())
 
1461
            # we fetch here so that we don't process data twice in the common
 
1462
            # case of having something to pull, and so that the check for 
 
1463
            # already merged can operate on the just fetched graph, which will
 
1464
            # be cached in memory.
1453
1465
            self.fetch(other, stop_revision)
1454
 
            my_ancestry = self.repository.get_ancestry(last_rev)
1455
 
            if stop_revision in my_ancestry:
1456
 
                # last_revision is a descendant of stop_revision
1457
 
                return
1458
 
            self.generate_revision_history(stop_revision, last_rev=last_rev,
1459
 
                other_branch=other)
 
1466
            # Check to see if one is an ancestor of the other
 
1467
            if not overwrite:
 
1468
                heads = self.repository.get_graph().heads([stop_revision,
 
1469
                                                           last_rev])
 
1470
                if heads == set([last_rev]):
 
1471
                    # The current revision is a decendent of the target,
 
1472
                    # nothing to do
 
1473
                    return
 
1474
                elif heads == set([stop_revision, last_rev]):
 
1475
                    # These branches have diverged
 
1476
                    raise errors.DivergedBranches(self, other)
 
1477
                assert heads == set([stop_revision])
 
1478
            if other_last_revision == stop_revision:
 
1479
                self.set_last_revision_info(other_last_revno,
 
1480
                                            other_last_revision)
 
1481
            else:
 
1482
                # TODO: jam 2007-11-29 Is there a way to determine the
 
1483
                #       revno without searching all of history??
 
1484
                if overwrite:
 
1485
                    self.generate_revision_history(stop_revision)
 
1486
                else:
 
1487
                    self.generate_revision_history(stop_revision,
 
1488
                        last_rev=last_rev, other_branch=other)
1460
1489
        finally:
1461
1490
            other.unlock()
1462
1491
 
1464
1493
        """See Branch.basis_tree."""
1465
1494
        return self.repository.revision_tree(self.last_revision())
1466
1495
 
1467
 
    @deprecated_method(zero_eight)
1468
 
    def working_tree(self):
1469
 
        """Create a Working tree object for this branch."""
1470
 
 
1471
 
        from bzrlib.transport.local import LocalTransport
1472
 
        if (self.base.find('://') != -1 or 
1473
 
            not isinstance(self._transport, LocalTransport)):
1474
 
            raise NoWorkingTree(self.base)
1475
 
        return self.bzrdir.open_workingtree()
1476
 
 
1477
1496
    @needs_write_lock
1478
1497
    def pull(self, source, overwrite=False, stop_revision=None,
1479
 
        _hook_master=None, _run_hooks=True):
 
1498
             _hook_master=None, run_hooks=True, possible_transports=None):
1480
1499
        """See Branch.pull.
1481
1500
 
1482
1501
        :param _hook_master: Private parameter - set the branch to 
1483
1502
            be supplied as the master to push hooks.
1484
 
        :param _run_hooks: Private parameter - allow disabling of
1485
 
            hooks, used when pushing to a master branch.
 
1503
        :param run_hooks: Private parameter - if false, this branch
 
1504
            is being called because it's the master of the primary branch,
 
1505
            so it should not run its hooks.
1486
1506
        """
1487
1507
        result = PullResult()
1488
1508
        result.source_branch = source
1490
1510
        source.lock_read()
1491
1511
        try:
1492
1512
            result.old_revno, result.old_revid = self.last_revision_info()
1493
 
            try:
1494
 
                self.update_revisions(source, stop_revision)
1495
 
            except DivergedBranches:
1496
 
                if not overwrite:
1497
 
                    raise
1498
 
            if overwrite:
1499
 
                if stop_revision is None:
1500
 
                    stop_revision = source.last_revision()
1501
 
                self.generate_revision_history(stop_revision)
1502
 
            result.tag_conflicts = source.tags.merge_to(self.tags)
 
1513
            self.update_revisions(source, stop_revision, overwrite=overwrite)
 
1514
            result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1503
1515
            result.new_revno, result.new_revid = self.last_revision_info()
1504
1516
            if _hook_master:
1505
1517
                result.master_branch = _hook_master
1507
1519
            else:
1508
1520
                result.master_branch = self
1509
1521
                result.local_branch = None
1510
 
            if _run_hooks:
 
1522
            if run_hooks:
1511
1523
                for hook in Branch.hooks['post_pull']:
1512
1524
                    hook(result)
1513
1525
        finally:
1519
1531
        for l in _locs:
1520
1532
            try:
1521
1533
                return self.control_files.get(l).read().strip('\n')
1522
 
            except NoSuchFile:
 
1534
            except errors.NoSuchFile:
1523
1535
                pass
1524
1536
        return None
1525
1537
 
1526
1538
    @needs_read_lock
1527
1539
    def push(self, target, overwrite=False, stop_revision=None,
1528
 
        _hook_master=None, _run_hooks=True):
 
1540
             _override_hook_source_branch=None):
1529
1541
        """See Branch.push.
 
1542
 
 
1543
        This is the basic concrete implementation of push()
 
1544
 
 
1545
        :param _override_hook_source_branch: If specified, run
 
1546
        the hooks passing this Branch as the source, rather than self.  
 
1547
        This is for use of RemoteBranch, where push is delegated to the
 
1548
        underlying vfs-based Branch. 
 
1549
        """
 
1550
        # TODO: Public option to disable running hooks - should be trivial but
 
1551
        # needs tests.
 
1552
        target.lock_write()
 
1553
        try:
 
1554
            result = self._push_with_bound_branches(target, overwrite,
 
1555
                    stop_revision,
 
1556
                    _override_hook_source_branch=_override_hook_source_branch)
 
1557
            return result
 
1558
        finally:
 
1559
            target.unlock()
 
1560
 
 
1561
    def _push_with_bound_branches(self, target, overwrite,
 
1562
            stop_revision,
 
1563
            _override_hook_source_branch=None):
 
1564
        """Push from self into target, and into target's master if any.
1530
1565
        
1531
 
        :param _hook_master: Private parameter - set the branch to 
1532
 
            be supplied as the master to push hooks.
1533
 
        :param _run_hooks: Private parameter - allow disabling of
1534
 
            hooks, used when pushing to a master branch.
 
1566
        This is on the base BzrBranch class even though it doesn't support 
 
1567
        bound branches because the *target* might be bound.
 
1568
        """
 
1569
        def _run_hooks():
 
1570
            if _override_hook_source_branch:
 
1571
                result.source_branch = _override_hook_source_branch
 
1572
            for hook in Branch.hooks['post_push']:
 
1573
                hook(result)
 
1574
 
 
1575
        bound_location = target.get_bound_location()
 
1576
        if bound_location and target.base != bound_location:
 
1577
            # there is a master branch.
 
1578
            #
 
1579
            # XXX: Why the second check?  Is it even supported for a branch to
 
1580
            # be bound to itself? -- mbp 20070507
 
1581
            master_branch = target.get_master_branch()
 
1582
            master_branch.lock_write()
 
1583
            try:
 
1584
                # push into the master from this branch.
 
1585
                self._basic_push(master_branch, overwrite, stop_revision)
 
1586
                # and push into the target branch from this. Note that we push from
 
1587
                # this branch again, because its considered the highest bandwidth
 
1588
                # repository.
 
1589
                result = self._basic_push(target, overwrite, stop_revision)
 
1590
                result.master_branch = master_branch
 
1591
                result.local_branch = target
 
1592
                _run_hooks()
 
1593
                return result
 
1594
            finally:
 
1595
                master_branch.unlock()
 
1596
        else:
 
1597
            # no master branch
 
1598
            result = self._basic_push(target, overwrite, stop_revision)
 
1599
            # TODO: Why set master_branch and local_branch if there's no
 
1600
            # binding?  Maybe cleaner to just leave them unset? -- mbp
 
1601
            # 20070504
 
1602
            result.master_branch = target
 
1603
            result.local_branch = None
 
1604
            _run_hooks()
 
1605
            return result
 
1606
 
 
1607
    def _basic_push(self, target, overwrite, stop_revision):
 
1608
        """Basic implementation of push without bound branches or hooks.
 
1609
 
 
1610
        Must be called with self read locked and target write locked.
1535
1611
        """
1536
1612
        result = PushResult()
1537
1613
        result.source_branch = self
1538
1614
        result.target_branch = target
1539
 
        target.lock_write()
 
1615
        result.old_revno, result.old_revid = target.last_revision_info()
1540
1616
        try:
1541
 
            result.old_revno, result.old_revid = target.last_revision_info()
1542
 
            try:
1543
 
                target.update_revisions(self, stop_revision)
1544
 
            except DivergedBranches:
1545
 
                if not overwrite:
1546
 
                    raise
1547
 
            if overwrite:
1548
 
                target.set_revision_history(self.revision_history())
1549
 
            result.tag_conflicts = self.tags.merge_to(target.tags)
1550
 
            result.new_revno, result.new_revid = target.last_revision_info()
1551
 
            if _hook_master:
1552
 
                result.master_branch = _hook_master
1553
 
                result.local_branch = target
1554
 
            else:
1555
 
                result.master_branch = target
1556
 
                result.local_branch = None
1557
 
            if _run_hooks:
1558
 
                for hook in Branch.hooks['post_push']:
1559
 
                    hook(result)
1560
 
        finally:
1561
 
            target.unlock()
 
1617
            target.update_revisions(self, stop_revision)
 
1618
        except errors.DivergedBranches:
 
1619
            if not overwrite:
 
1620
                raise
 
1621
        if overwrite:
 
1622
            target.set_revision_history(self.revision_history())
 
1623
        result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
 
1624
        result.new_revno, result.new_revid = target.last_revision_info()
1562
1625
        return result
1563
1626
 
1564
1627
    def get_parent(self):
1609
1672
            assert isinstance(url, str)
1610
1673
            self.control_files.put_bytes('parent', url + '\n')
1611
1674
 
1612
 
    @deprecated_function(zero_nine)
1613
 
    def tree_config(self):
1614
 
        """DEPRECATED; call get_config instead.  
1615
 
        TreeConfig has become part of BranchConfig."""
1616
 
        return TreeConfig(self)
1617
 
 
1618
1675
 
1619
1676
class BzrBranch5(BzrBranch):
1620
 
    """A format 5 branch. This supports new features over plan branches.
 
1677
    """A format 5 branch. This supports new features over plain branches.
1621
1678
 
1622
1679
    It has support for a master_branch which is the data for bound branches.
1623
1680
    """
1634
1691
        
1635
1692
    @needs_write_lock
1636
1693
    def pull(self, source, overwrite=False, stop_revision=None,
1637
 
        _run_hooks=True):
1638
 
        """Extends branch.pull to be bound branch aware.
 
1694
             run_hooks=True, possible_transports=None):
 
1695
        """Pull from source into self, updating my master if any.
1639
1696
        
1640
 
        :param _run_hooks: Private parameter used to force hook running
1641
 
            off during bound branch double-pushing.
 
1697
        :param run_hooks: Private parameter - if false, this branch
 
1698
            is being called because it's the master of the primary branch,
 
1699
            so it should not run its hooks.
1642
1700
        """
1643
1701
        bound_location = self.get_bound_location()
1644
1702
        master_branch = None
1645
1703
        if bound_location and source.base != bound_location:
1646
1704
            # not pulling from master, so we need to update master.
1647
 
            master_branch = self.get_master_branch()
 
1705
            master_branch = self.get_master_branch(possible_transports)
1648
1706
            master_branch.lock_write()
1649
1707
        try:
1650
1708
            if master_branch:
1651
1709
                # pull from source into master.
1652
1710
                master_branch.pull(source, overwrite, stop_revision,
1653
 
                    _run_hooks=False)
 
1711
                    run_hooks=False)
1654
1712
            return super(BzrBranch5, self).pull(source, overwrite,
1655
1713
                stop_revision, _hook_master=master_branch,
1656
 
                _run_hooks=_run_hooks)
1657
 
        finally:
1658
 
            if master_branch:
1659
 
                master_branch.unlock()
1660
 
 
1661
 
    @needs_read_lock
1662
 
    def push(self, target, overwrite=False, stop_revision=None):
1663
 
        """Updates branch.push to be bound branch aware."""
1664
 
        bound_location = target.get_bound_location()
1665
 
        master_branch = None
1666
 
        if bound_location and target.base != bound_location:
1667
 
            # not pushing to master, so we need to update master.
1668
 
            master_branch = target.get_master_branch()
1669
 
            master_branch.lock_write()
1670
 
        try:
1671
 
            if master_branch:
1672
 
                # push into the master from this branch.
1673
 
                super(BzrBranch5, self).push(master_branch, overwrite,
1674
 
                    stop_revision, _run_hooks=False)
1675
 
            # and push into the target branch from this. Note that we push from
1676
 
            # this branch again, because its considered the highest bandwidth
1677
 
            # repository.
1678
 
            return super(BzrBranch5, self).push(target, overwrite,
1679
 
                stop_revision, _hook_master=master_branch)
 
1714
                run_hooks=run_hooks)
1680
1715
        finally:
1681
1716
            if master_branch:
1682
1717
                master_branch.unlock()
1688
1723
            return None
1689
1724
 
1690
1725
    @needs_read_lock
1691
 
    def get_master_branch(self):
 
1726
    def get_master_branch(self, possible_transports=None):
1692
1727
        """Return the branch we are bound to.
1693
1728
        
1694
1729
        :return: Either a Branch, or None
1702
1737
        if not bound_loc:
1703
1738
            return None
1704
1739
        try:
1705
 
            return Branch.open(bound_loc)
 
1740
            return Branch.open(bound_loc,
 
1741
                               possible_transports=possible_transports)
1706
1742
        except (errors.NotBranchError, errors.ConnectionError), e:
1707
1743
            raise errors.BoundBranchConnectionFailure(
1708
1744
                    self, bound_loc, e)
1718
1754
        else:
1719
1755
            try:
1720
1756
                self.control_files._transport.delete('bound')
1721
 
            except NoSuchFile:
 
1757
            except errors.NoSuchFile:
1722
1758
                return False
1723
1759
            return True
1724
1760
 
1746
1782
        # last_rev is not in the other_last_rev history, AND
1747
1783
        # other_last_rev is not in our history, and do it without pulling
1748
1784
        # history around
1749
 
        last_rev = self.last_revision()
1750
 
        if last_rev is not None:
1751
 
            other.lock_read()
1752
 
            try:
1753
 
                other_last_rev = other.last_revision()
1754
 
                if other_last_rev is not None:
1755
 
                    # neither branch is new, we have to do some work to
1756
 
                    # ascertain diversion.
1757
 
                    remote_graph = other.repository.get_revision_graph(
1758
 
                        other_last_rev)
1759
 
                    local_graph = self.repository.get_revision_graph(last_rev)
1760
 
                    if (last_rev not in remote_graph and
1761
 
                        other_last_rev not in local_graph):
1762
 
                        raise errors.DivergedBranches(self, other)
1763
 
            finally:
1764
 
                other.unlock()
1765
1785
        self.set_bound_location(other.base)
1766
1786
 
1767
1787
    @needs_write_lock
1770
1790
        return self.set_bound_location(None)
1771
1791
 
1772
1792
    @needs_write_lock
1773
 
    def update(self):
 
1793
    def update(self, possible_transports=None):
1774
1794
        """Synchronise this branch with the master branch if any. 
1775
1795
 
1776
1796
        :return: None or the last_revision that was pivoted out during the
1777
1797
                 update.
1778
1798
        """
1779
 
        master = self.get_master_branch()
 
1799
        master = self.get_master_branch(possible_transports)
1780
1800
        if master is not None:
1781
 
            old_tip = self.last_revision()
 
1801
            old_tip = _mod_revision.ensure_null(self.last_revision())
1782
1802
            self.pull(master, overwrite=True)
1783
 
            if old_tip in self.repository.get_ancestry(self.last_revision()):
 
1803
            if self.repository.get_graph().is_ancestor(old_tip,
 
1804
                _mod_revision.ensure_null(self.last_revision())):
1784
1805
                return None
1785
1806
            return old_tip
1786
1807
        return None
1787
1808
 
1788
1809
 
1789
 
class BzrBranchExperimental(BzrBranch5):
1790
 
    """Bzr experimental branch format
1791
 
 
1792
 
    This format has:
1793
 
     - a revision-history file.
1794
 
     - a format string
1795
 
     - a lock dir guarding the branch itself
1796
 
     - all of this stored in a branch/ subdirectory
1797
 
     - works with shared repositories.
1798
 
     - a tag dictionary in the branch
1799
 
 
1800
 
    This format is new in bzr 0.15, but shouldn't be used for real data, 
1801
 
    only for testing.
1802
 
 
1803
 
    This class acts as it's own BranchFormat.
1804
 
    """
1805
 
 
1806
 
    _matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1807
 
 
1808
 
    @classmethod
1809
 
    def get_format_string(cls):
1810
 
        """See BranchFormat.get_format_string()."""
1811
 
        return "Bazaar-NG branch format experimental\n"
1812
 
 
1813
 
    @classmethod
1814
 
    def get_format_description(cls):
1815
 
        """See BranchFormat.get_format_description()."""
1816
 
        return "Experimental branch format"
1817
 
 
1818
 
    @classmethod
1819
 
    def get_reference(cls, a_bzrdir):
1820
 
        """Get the target reference of the branch in a_bzrdir.
1821
 
 
1822
 
        format probing must have been completed before calling
1823
 
        this method - it is assumed that the format of the branch
1824
 
        in a_bzrdir is correct.
1825
 
 
1826
 
        :param a_bzrdir: The bzrdir to get the branch data from.
1827
 
        :return: None if the branch is not a reference branch.
1828
 
        """
1829
 
        return None
1830
 
 
1831
 
    @classmethod
1832
 
    def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1833
 
            lock_class):
1834
 
        branch_transport = a_bzrdir.get_branch_transport(cls)
1835
 
        control_files = lockable_files.LockableFiles(branch_transport,
1836
 
            lock_filename, lock_class)
1837
 
        control_files.create_lock()
1838
 
        control_files.lock_write()
1839
 
        try:
1840
 
            for filename, content in utf8_files:
1841
 
                control_files.put_utf8(filename, content)
1842
 
        finally:
1843
 
            control_files.unlock()
1844
 
        
1845
 
    @classmethod
1846
 
    def initialize(cls, a_bzrdir):
1847
 
        """Create a branch of this format in a_bzrdir."""
1848
 
        utf8_files = [('format', cls.get_format_string()),
1849
 
                      ('revision-history', ''),
1850
 
                      ('branch-name', ''),
1851
 
                      ('tags', ''),
1852
 
                      ]
1853
 
        cls._initialize_control_files(a_bzrdir, utf8_files,
1854
 
            'lock', lockdir.LockDir)
1855
 
        return cls.open(a_bzrdir, _found=True)
1856
 
 
1857
 
    @classmethod
1858
 
    def open(cls, a_bzrdir, _found=False):
1859
 
        """Return the branch object for a_bzrdir
1860
 
 
1861
 
        _found is a private parameter, do not use it. It is used to indicate
1862
 
               if format probing has already be done.
1863
 
        """
1864
 
        if not _found:
1865
 
            format = BranchFormat.find_format(a_bzrdir)
1866
 
            assert format.__class__ == cls
1867
 
        transport = a_bzrdir.get_branch_transport(None)
1868
 
        control_files = lockable_files.LockableFiles(transport, 'lock',
1869
 
                                                     lockdir.LockDir)
1870
 
        return cls(_format=cls,
1871
 
            _control_files=control_files,
1872
 
            a_bzrdir=a_bzrdir,
1873
 
            _repository=a_bzrdir.find_repository())
1874
 
 
1875
 
    @classmethod
1876
 
    def is_supported(cls):
1877
 
        return True
1878
 
 
1879
 
    def _make_tags(self):
1880
 
        return BasicTags(self)
1881
 
 
1882
 
    @classmethod
1883
 
    def supports_tags(cls):
1884
 
        return True
1885
 
 
1886
 
 
1887
 
BranchFormat.register_format(BzrBranchExperimental)
1888
 
 
1889
 
 
1890
1810
class BzrBranch6(BzrBranch5):
1891
1811
 
1892
1812
    @needs_read_lock
1897
1817
        revno = int(revno)
1898
1818
        return revno, revision_id
1899
1819
 
1900
 
    def last_revision(self):
1901
 
        """Return last revision id, or None"""
1902
 
        revision_id = self.last_revision_info()[1]
1903
 
        if revision_id == _mod_revision.NULL_REVISION:
1904
 
            revision_id = None
1905
 
        return revision_id
1906
 
 
1907
1820
    def _write_last_revision_info(self, revno, revision_id):
1908
1821
        """Simply write out the revision id, with no checks.
1909
1822
 
1913
1826
        Intended to be called by set_last_revision_info and
1914
1827
        _write_revision_history.
1915
1828
        """
1916
 
        if revision_id is None:
1917
 
            revision_id = 'null:'
 
1829
        assert revision_id is not None, "Use NULL_REVISION, not None"
1918
1830
        out_string = '%d %s\n' % (revno, revision_id)
1919
1831
        self.control_files.put_bytes('last-revision', out_string)
1920
1832
 
1921
1833
    @needs_write_lock
1922
1834
    def set_last_revision_info(self, revno, revision_id):
1923
 
        revision_id = osutils.safe_revision_id(revision_id)
 
1835
        revision_id = _mod_revision.ensure_null(revision_id)
1924
1836
        if self._get_append_revisions_only():
1925
1837
            self._check_history_violation(revision_id)
1926
1838
        self._write_last_revision_info(revno, revision_id)
1927
1839
        self._clear_cached_state()
1928
1840
 
1929
1841
    def _check_history_violation(self, revision_id):
1930
 
        last_revision = self.last_revision()
1931
 
        if last_revision is None:
 
1842
        last_revision = _mod_revision.ensure_null(self.last_revision())
 
1843
        if _mod_revision.is_null(last_revision):
1932
1844
            return
1933
1845
        if last_revision not in self._lefthand_history(revision_id):
1934
1846
            raise errors.AppendRevisionsOnlyViolation(self.base)
1958
1870
        self._write_last_revision_info(len(history), last_revision)
1959
1871
 
1960
1872
    @needs_write_lock
1961
 
    def append_revision(self, *revision_ids):
1962
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1963
 
        if len(revision_ids) == 0:
1964
 
            return
1965
 
        prev_revno, prev_revision = self.last_revision_info()
1966
 
        for revision in self.repository.get_revisions(revision_ids):
1967
 
            if prev_revision == _mod_revision.NULL_REVISION:
1968
 
                if revision.parent_ids != []:
1969
 
                    raise errors.NotLeftParentDescendant(self, prev_revision,
1970
 
                                                         revision.revision_id)
1971
 
            else:
1972
 
                if revision.parent_ids[0] != prev_revision:
1973
 
                    raise errors.NotLeftParentDescendant(self, prev_revision,
1974
 
                                                         revision.revision_id)
1975
 
            prev_revision = revision.revision_id
1976
 
        self.set_last_revision_info(prev_revno + len(revision_ids),
1977
 
                                    revision_ids[-1])
1978
 
 
1979
 
    @needs_write_lock
1980
1873
    def _set_parent_location(self, url):
1981
1874
        """Set the parent branch"""
1982
1875
        self._set_config_location('parent_location', url, make_relative=True)
1998
1891
            if config.get_user_option('bound') != 'True':
1999
1892
                return False
2000
1893
            else:
2001
 
                config.set_user_option('bound', 'False')
 
1894
                config.set_user_option('bound', 'False', warn_masked=True)
2002
1895
                return True
2003
1896
        else:
2004
1897
            self._set_config_location('bound_location', location,
2005
1898
                                      config=config)
2006
 
            config.set_user_option('bound', 'True')
 
1899
            config.set_user_option('bound', 'True', warn_masked=True)
2007
1900
        return True
2008
1901
 
2009
1902
    def _get_bound_location(self, bound):
2029
1922
            value = 'True'
2030
1923
        else:
2031
1924
            value = 'False'
2032
 
        self.get_config().set_user_option('append_revisions_only', value)
 
1925
        self.get_config().set_user_option('append_revisions_only', value,
 
1926
            warn_masked=True)
2033
1927
 
2034
1928
    def _get_append_revisions_only(self):
2035
1929
        value = self.get_config().get_user_option('append_revisions_only')
2047
1941
        :param revision_id: The revision-id to truncate history at.  May
2048
1942
          be None to copy complete history.
2049
1943
        """
 
1944
        source_revno, source_revision_id = self.last_revision_info()
2050
1945
        if revision_id is None:
2051
 
            revno, revision_id = self.last_revision_info()
 
1946
            revno, revision_id = source_revno, source_revision_id
 
1947
        elif source_revision_id == revision_id:
 
1948
            # we know the revno without needing to walk all of history
 
1949
            revno = source_revno
2052
1950
        else:
2053
 
            revno = self.revision_id_to_revno(revision_id)
 
1951
            # To figure out the revno for a random revision, we need to build
 
1952
            # the revision history, and count its length.
 
1953
            # We don't care about the order, just how long it is.
 
1954
            # Alternatively, we could start at the current location, and count
 
1955
            # backwards. But there is no guarantee that we will find it since
 
1956
            # it may be a merged revision.
 
1957
            revno = len(list(self.repository.iter_reverse_revision_history(
 
1958
                                                                revision_id)))
2054
1959
        destination.set_last_revision_info(revno, revision_id)
2055
1960
 
2056
1961
    def _make_tags(self):
2057
1962
        return BasicTags(self)
2058
1963
 
2059
 
 
2060
 
class BranchTestProviderAdapter(object):
2061
 
    """A tool to generate a suite testing multiple branch formats at once.
2062
 
 
2063
 
    This is done by copying the test once for each transport and injecting
2064
 
    the transport_server, transport_readonly_server, and branch_format
2065
 
    classes into each copy. Each copy is also given a new id() to make it
2066
 
    easy to identify.
2067
 
    """
2068
 
 
2069
 
    def __init__(self, transport_server, transport_readonly_server, formats,
2070
 
        vfs_transport_factory=None):
2071
 
        self._transport_server = transport_server
2072
 
        self._transport_readonly_server = transport_readonly_server
2073
 
        self._formats = formats
2074
 
    
2075
 
    def adapt(self, test):
2076
 
        result = TestSuite()
2077
 
        for branch_format, bzrdir_format in self._formats:
2078
 
            new_test = deepcopy(test)
2079
 
            new_test.transport_server = self._transport_server
2080
 
            new_test.transport_readonly_server = self._transport_readonly_server
2081
 
            new_test.bzrdir_format = bzrdir_format
2082
 
            new_test.branch_format = branch_format
2083
 
            def make_new_test_id():
2084
 
                # the format can be either a class or an instance
2085
 
                name = getattr(branch_format, '__name__',
2086
 
                        branch_format.__class__.__name__)
2087
 
                new_id = "%s(%s)" % (new_test.id(), name)
2088
 
                return lambda: new_id
2089
 
            new_test.id = make_new_test_id()
2090
 
            result.addTest(new_test)
2091
 
        return result
 
1964
    @needs_write_lock
 
1965
    def generate_revision_history(self, revision_id, last_rev=None,
 
1966
                                  other_branch=None):
 
1967
        """See BzrBranch5.generate_revision_history"""
 
1968
        history = self._lefthand_history(revision_id, last_rev, other_branch)
 
1969
        revno = len(history)
 
1970
        self.set_last_revision_info(revno, revision_id)
2092
1971
 
2093
1972
 
2094
1973
######################################################################
2122
2001
        return self.new_revno - self.old_revno
2123
2002
 
2124
2003
    def report(self, to_file):
2125
 
        if self.old_revid == self.new_revid:
2126
 
            to_file.write('No revisions to pull.\n')
2127
 
        else:
2128
 
            to_file.write('Now on revision %d.\n' % self.new_revno)
 
2004
        if not is_quiet():
 
2005
            if self.old_revid == self.new_revid:
 
2006
                to_file.write('No revisions to pull.\n')
 
2007
            else:
 
2008
                to_file.write('Now on revision %d.\n' % self.new_revno)
2129
2009
        self._show_tag_conficts(to_file)
2130
2010
 
2131
2011
 
2199
2079
        new_branch.control_files._transport.delete('revision-history')
2200
2080
        try:
2201
2081
            branch.set_parent(None)
2202
 
        except NoSuchFile:
 
2082
        except errors.NoSuchFile:
2203
2083
            pass
2204
2084
        branch.set_bound_location(None)