~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Clean up the lock.py code to use less indenting, and conform to better coding practise.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
from bzrlib.lazy_import import lazy_import
21
21
lazy_import(globals(), """
 
22
from copy import deepcopy
 
23
from unittest import TestSuite
22
24
from warnings import warn
23
25
 
24
26
import bzrlib
33
35
        revision as _mod_revision,
34
36
        transport,
35
37
        tree,
36
 
        tsort,
37
38
        ui,
38
39
        urlutils,
39
40
        )
53
54
                           NotBranchError, UninitializableFormat,
54
55
                           UnlistableStore, UnlistableBranch,
55
56
                           )
56
 
from bzrlib.hooks import Hooks
57
57
from bzrlib.symbol_versioning import (deprecated_function,
58
58
                                      deprecated_method,
59
59
                                      DEPRECATED_PARAMETER,
60
60
                                      deprecated_passed,
61
 
                                      zero_eight, zero_nine, zero_sixteen,
 
61
                                      zero_eight, zero_nine,
62
62
                                      )
63
63
from bzrlib.trace import mutter, note
64
64
 
98
98
 
99
99
    def __init__(self, *ignored, **ignored_too):
100
100
        self.tags = self._make_tags()
101
 
        self._revision_history_cache = None
102
 
        self._revision_id_to_revno_cache = None
103
101
 
104
102
    def break_lock(self):
105
103
        """Break a lock if one is present from another instance.
120
118
    def open_downlevel(base):
121
119
        """Open a branch which may be of an old format."""
122
120
        return Branch.open(base, _unsupported=True)
123
 
 
 
121
        
124
122
    @staticmethod
125
123
    def open(base, _unsupported=False):
126
124
        """Open the branch rooted at base.
132
130
        return control.open_branch(_unsupported)
133
131
 
134
132
    @staticmethod
135
 
    def open_from_transport(transport, _unsupported=False):
136
 
        """Open the branch rooted at transport"""
137
 
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
138
 
        return control.open_branch(_unsupported)
139
 
 
140
 
    @staticmethod
141
 
    def open_containing(url, possible_transports=None):
 
133
    def open_containing(url):
142
134
        """Open an existing branch which contains url.
143
135
        
144
136
        This probes for a branch at url, and searches upwards from there.
149
141
        format, UnknownFormatError or UnsupportedFormatError are raised.
150
142
        If there is one, it is returned, along with the unused portion of url.
151
143
        """
152
 
        control, relpath = bzrdir.BzrDir.open_containing(url,
153
 
                                                         possible_transports)
 
144
        control, relpath = bzrdir.BzrDir.open_containing(url)
154
145
        return control.open_branch(), relpath
155
146
 
156
147
    @staticmethod
179
170
        return self.get_config().get_nickname()
180
171
 
181
172
    def _set_nick(self, nick):
182
 
        self.get_config().set_user_option('nickname', nick, warn_masked=True)
 
173
        self.get_config().set_user_option('nickname', nick)
183
174
 
184
175
    nick = property(_get_nick, _set_nick)
185
176
 
202
193
    def get_physical_lock_status(self):
203
194
        raise NotImplementedError(self.get_physical_lock_status)
204
195
 
205
 
    @needs_read_lock
206
 
    def get_revision_id_to_revno_map(self):
207
 
        """Return the revision_id => dotted revno map.
208
 
 
209
 
        This will be regenerated on demand, but will be cached.
210
 
 
211
 
        :return: A dictionary mapping revision_id => dotted revno.
212
 
            This dictionary should not be modified by the caller.
213
 
        """
214
 
        if self._revision_id_to_revno_cache is not None:
215
 
            mapping = self._revision_id_to_revno_cache
216
 
        else:
217
 
            mapping = self._gen_revno_map()
218
 
            self._cache_revision_id_to_revno(mapping)
219
 
        # TODO: jam 20070417 Since this is being cached, should we be returning
220
 
        #       a copy?
221
 
        # I would rather not, and instead just declare that users should not
222
 
        # modify the return value.
223
 
        return mapping
224
 
 
225
 
    def _gen_revno_map(self):
226
 
        """Create a new mapping from revision ids to dotted revnos.
227
 
 
228
 
        Dotted revnos are generated based on the current tip in the revision
229
 
        history.
230
 
        This is the worker function for get_revision_id_to_revno_map, which
231
 
        just caches the return value.
232
 
 
233
 
        :return: A dictionary mapping revision_id => dotted revno.
234
 
        """
235
 
        last_revision = self.last_revision()
236
 
        revision_graph = self.repository.get_revision_graph(last_revision)
237
 
        merge_sorted_revisions = tsort.merge_sort(
238
 
            revision_graph,
239
 
            last_revision,
240
 
            None,
241
 
            generate_revno=True)
242
 
        revision_id_to_revno = dict((rev_id, revno)
243
 
                                    for seq_num, rev_id, depth, revno, end_of_merge
244
 
                                     in merge_sorted_revisions)
245
 
        return revision_id_to_revno
246
 
 
247
 
    def leave_lock_in_place(self):
248
 
        """Tell this branch object not to release the physical lock when this
249
 
        object is unlocked.
250
 
        
251
 
        If lock_write doesn't return a token, then this method is not supported.
252
 
        """
253
 
        self.control_files.leave_in_place()
254
 
 
255
 
    def dont_leave_lock_in_place(self):
256
 
        """Tell this branch object to release the physical lock when this
257
 
        object is unlocked, even if it didn't originally acquire it.
258
 
 
259
 
        If lock_write doesn't return a token, then this method is not supported.
260
 
        """
261
 
        self.control_files.dont_leave_in_place()
262
 
 
263
196
    def abspath(self, name):
264
197
        """Return absolute filename for something in the branch
265
198
        
363
296
            raise InvalidRevisionNumber(revno)
364
297
        return self.repository.get_revision_delta(rh[revno-1])
365
298
 
366
 
    @deprecated_method(zero_sixteen)
367
299
    def get_root_id(self):
368
 
        """Return the id of this branches root
369
 
 
370
 
        Deprecated: branches don't have root ids-- trees do.
371
 
        Use basis_tree().get_root_id() instead.
372
 
        """
 
300
        """Return the id of this branches root"""
373
301
        raise NotImplementedError(self.get_root_id)
374
302
 
375
303
    def print_file(self, file, revision_id):
382
310
    def set_revision_history(self, rev_history):
383
311
        raise NotImplementedError(self.set_revision_history)
384
312
 
385
 
    def _cache_revision_history(self, rev_history):
386
 
        """Set the cached revision history to rev_history.
387
 
 
388
 
        The revision_history method will use this cache to avoid regenerating
389
 
        the revision history.
390
 
 
391
 
        This API is semi-public; it only for use by subclasses, all other code
392
 
        should consider it to be private.
393
 
        """
394
 
        self._revision_history_cache = rev_history
395
 
 
396
 
    def _cache_revision_id_to_revno(self, revision_id_to_revno):
397
 
        """Set the cached revision_id => revno map to revision_id_to_revno.
398
 
 
399
 
        This API is semi-public; it only for use by subclasses, all other code
400
 
        should consider it to be private.
401
 
        """
402
 
        self._revision_id_to_revno_cache = revision_id_to_revno
403
 
 
404
 
    def _clear_cached_state(self):
405
 
        """Clear any cached data on this branch, e.g. cached revision history.
406
 
 
407
 
        This means the next call to revision_history will need to call
408
 
        _gen_revision_history.
409
 
 
410
 
        This API is semi-public; it only for use by subclasses, all other code
411
 
        should consider it to be private.
412
 
        """
413
 
        self._revision_history_cache = None
414
 
        self._revision_id_to_revno_cache = None
415
 
 
416
 
    def _gen_revision_history(self):
417
 
        """Return sequence of revision hashes on to this branch.
418
 
        
419
 
        Unlike revision_history, this method always regenerates or rereads the
420
 
        revision history, i.e. it does not cache the result, so repeated calls
421
 
        may be expensive.
422
 
 
423
 
        Concrete subclasses should override this instead of revision_history so
424
 
        that subclasses do not need to deal with caching logic.
425
 
        
426
 
        This API is semi-public; it only for use by subclasses, all other code
427
 
        should consider it to be private.
428
 
        """
429
 
        raise NotImplementedError(self._gen_revision_history)
430
 
 
431
 
    @needs_read_lock
432
313
    def revision_history(self):
433
 
        """Return sequence of revision hashes on to this branch.
434
 
        
435
 
        This method will cache the revision history for as long as it is safe to
436
 
        do so.
437
 
        """
438
 
        if self._revision_history_cache is not None:
439
 
            history = self._revision_history_cache
440
 
        else:
441
 
            history = self._gen_revision_history()
442
 
            self._cache_revision_history(history)
443
 
        return list(history)
 
314
        """Return sequence of revision hashes on to this branch."""
 
315
        raise NotImplementedError(self.revision_history)
444
316
 
445
317
    def revno(self):
446
318
        """Return current revision number for this branch.
512
384
 
513
385
    def revision_id_to_revno(self, revision_id):
514
386
        """Given a revision id, return its revno"""
515
 
        if _mod_revision.is_null(revision_id):
 
387
        if revision_id is None:
516
388
            return 0
517
389
        revision_id = osutils.safe_revision_id(revision_id)
518
390
        history = self.revision_history()
519
391
        try:
520
392
            return history.index(revision_id) + 1
521
393
        except ValueError:
522
 
            raise errors.NoSuchRevision(self, revision_id)
 
394
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
523
395
 
524
396
    def get_rev_id(self, revno, history=None):
525
397
        """Find the revision id of the specified revno."""
528
400
        if history is None:
529
401
            history = self.revision_history()
530
402
        if revno <= 0 or revno > len(history):
531
 
            raise errors.NoSuchRevision(self, revno)
 
403
            raise bzrlib.errors.NoSuchRevision(self, revno)
532
404
        return history[revno - 1]
533
405
 
534
406
    def pull(self, source, overwrite=False, stop_revision=None):
591
463
            url = ''
592
464
        elif make_relative:
593
465
            url = urlutils.relative_url(self.base, url)
594
 
        config.set_user_option(name, url, warn_masked=True)
 
466
        config.set_user_option(name, url)
595
467
 
596
468
    def _get_config_location(self, name, config=None):
597
469
        if config is None:
617
489
        pattern is that the user can override it by specifying a
618
490
        location.
619
491
        """
620
 
        self.get_config().set_user_option('submit_branch', location,
621
 
            warn_masked=True)
 
492
        self.get_config().set_user_option('submit_branch', location)
622
493
 
623
494
    def get_public_branch(self):
624
495
        """Return the public location of the branch.
638
509
 
639
510
    def get_push_location(self):
640
511
        """Return the None or the location to push this branch to."""
641
 
        push_loc = self.get_config().get_user_option('push_location')
642
 
        return push_loc
 
512
        raise NotImplementedError(self.get_push_location)
643
513
 
644
514
    def set_push_location(self, location):
645
515
        """Set a new push location for this branch."""
673
543
            raise InvalidRevisionNumber(revno)
674
544
 
675
545
    @needs_read_lock
676
 
    def clone(self, to_bzrdir, revision_id=None):
 
546
    def clone(self, *args, **kwargs):
677
547
        """Clone this branch into to_bzrdir preserving all semantic values.
678
548
        
679
549
        revision_id: if not None, the revision history in the new branch will
680
550
                     be truncated to end with revision_id.
681
551
        """
 
552
        # for API compatibility, until 0.8 releases we provide the old api:
 
553
        # def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
 
554
        # after 0.8 releases, the *args and **kwargs should be changed:
 
555
        # def clone(self, to_bzrdir, revision_id=None):
 
556
        if (kwargs.get('to_location', None) or
 
557
            kwargs.get('revision', None) or
 
558
            kwargs.get('basis_branch', None) or
 
559
            (len(args) and isinstance(args[0], basestring))):
 
560
            # backwards compatibility api:
 
561
            warn("Branch.clone() has been deprecated for BzrDir.clone() from"
 
562
                 " bzrlib 0.8.", DeprecationWarning, stacklevel=3)
 
563
            # get basis_branch
 
564
            if len(args) > 2:
 
565
                basis_branch = args[2]
 
566
            else:
 
567
                basis_branch = kwargs.get('basis_branch', None)
 
568
            if basis_branch:
 
569
                basis = basis_branch.bzrdir
 
570
            else:
 
571
                basis = None
 
572
            # get revision
 
573
            if len(args) > 1:
 
574
                revision_id = args[1]
 
575
            else:
 
576
                revision_id = kwargs.get('revision', None)
 
577
            # get location
 
578
            if len(args):
 
579
                url = args[0]
 
580
            else:
 
581
                # no default to raise if not provided.
 
582
                url = kwargs.get('to_location')
 
583
            return self.bzrdir.clone(url,
 
584
                                     revision_id=revision_id,
 
585
                                     basis=basis).open_branch()
 
586
        # new cleaner api.
 
587
        # generate args by hand 
 
588
        if len(args) > 1:
 
589
            revision_id = args[1]
 
590
        else:
 
591
            revision_id = kwargs.get('revision_id', None)
 
592
        if len(args):
 
593
            to_bzrdir = args[0]
 
594
        else:
 
595
            # no default to raise if not provided.
 
596
            to_bzrdir = kwargs.get('to_bzrdir')
682
597
        result = self._format.initialize(to_bzrdir)
683
598
        self.copy_content_into(result, revision_id=revision_id)
684
599
        return  result
708
623
        :param revision_id: The revision-id to truncate history at.  May
709
624
          be None to copy complete history.
710
625
        """
711
 
        if revision_id == _mod_revision.NULL_REVISION:
712
 
            new_history = []
713
626
        new_history = self.revision_history()
714
 
        if revision_id is not None and new_history != []:
 
627
        if revision_id is not None:
715
628
            revision_id = osutils.safe_revision_id(revision_id)
716
629
            try:
717
630
                new_history = new_history[:new_history.index(revision_id) + 1]
769
682
 
770
683
    def _get_checkout_format(self):
771
684
        """Return the most suitable metadir for a checkout of this branch.
772
 
        Weaves are used if this branch's repository uses weaves.
 
685
        Weaves are used if this branch's repostory uses weaves.
773
686
        """
774
687
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
775
688
            from bzrlib.repofmt import weaverepo
777
690
            format.repository_format = weaverepo.RepositoryFormat7()
778
691
        else:
779
692
            format = self.repository.bzrdir.checkout_metadir()
780
 
            format.set_branch_format(self._format)
 
693
            format.branch_format = self._format
781
694
        return format
782
695
 
783
696
    def create_checkout(self, to_location, revision_id=None,
791
704
        :return: The tree of the created checkout
792
705
        """
793
706
        t = transport.get_transport(to_location)
794
 
        t.ensure_base()
 
707
        try:
 
708
            t.mkdir('.')
 
709
        except errors.FileExists:
 
710
            pass
795
711
        if lightweight:
796
712
            format = self._get_checkout_format()
797
713
            checkout = format.initialize_on_transport(t)
855
771
    _formats = {}
856
772
    """The known formats."""
857
773
 
858
 
    def __eq__(self, other):
859
 
        return self.__class__ is other.__class__
860
 
 
861
 
    def __ne__(self, other):
862
 
        return not (self == other)
863
 
 
864
774
    @classmethod
865
775
    def find_format(klass, a_bzrdir):
866
776
        """Return the format for the branch object in a_bzrdir."""
878
788
        """Return the current default format."""
879
789
        return klass._default_format
880
790
 
881
 
    def get_reference(self, a_bzrdir):
882
 
        """Get the target reference of the branch in a_bzrdir.
883
 
 
884
 
        format probing must have been completed before calling
885
 
        this method - it is assumed that the format of the branch
886
 
        in a_bzrdir is correct.
887
 
 
888
 
        :param a_bzrdir: The bzrdir to get the branch data from.
889
 
        :return: None if the branch is not a reference branch.
890
 
        """
891
 
        return None
892
 
 
893
791
    def get_format_string(self):
894
792
        """Return the ASCII format string that identifies this format."""
895
793
        raise NotImplementedError(self.get_format_string)
986
884
            control_files.unlock()
987
885
 
988
886
 
989
 
class BranchHooks(Hooks):
 
887
class BranchHooks(dict):
990
888
    """A dictionary mapping hook name to a list of callables for branch hooks.
991
889
    
992
890
    e.g. ['set_rh'] Is the list of items to be called when the
999
897
        These are all empty initially, because by default nothing should get
1000
898
        notified.
1001
899
        """
1002
 
        Hooks.__init__(self)
 
900
        dict.__init__(self)
1003
901
        # Introduced in 0.15:
1004
902
        # invoked whenever the revision history has been set
1005
903
        # with set_revision_history. The api signature is
1011
909
        # (push_result)
1012
910
        # containing the members
1013
911
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
1014
 
        # where local is the local target branch or None, master is the target 
 
912
        # where local is the local branch or None, master is the target 
1015
913
        # master branch, and the rest should be self explanatory. The source
1016
914
        # is read locked and the target branches write locked. Source will
1017
915
        # be the local low-latency branch.
1038
936
        # and an empty branch recieves new_revno of 0, new_revid of None.
1039
937
        self['post_uncommit'] = []
1040
938
 
 
939
    def install_hook(self, hook_name, a_callable):
 
940
        """Install a_callable in to the hook hook_name.
 
941
 
 
942
        :param hook_name: A hook name. See the __init__ method of BranchHooks
 
943
            for the complete list of hooks.
 
944
        :param a_callable: The callable to be invoked when the hook triggers.
 
945
            The exact signature will depend on the hook - see the __init__ 
 
946
            method of BranchHooks for details on each hook.
 
947
        """
 
948
        try:
 
949
            self[hook_name].append(a_callable)
 
950
        except KeyError:
 
951
            raise errors.UnknownHook('branch', hook_name)
 
952
 
1041
953
 
1042
954
# install the default hooks into the Branch class.
1043
955
Branch.hooks = BranchHooks()
1126
1038
        if not _found:
1127
1039
            format = BranchFormat.find_format(a_bzrdir)
1128
1040
            assert format.__class__ == self.__class__
1129
 
        try:
1130
 
            transport = a_bzrdir.get_branch_transport(None)
1131
 
            control_files = lockable_files.LockableFiles(transport, 'lock',
1132
 
                                                         lockdir.LockDir)
1133
 
            return BzrBranch5(_format=self,
1134
 
                              _control_files=control_files,
1135
 
                              a_bzrdir=a_bzrdir,
1136
 
                              _repository=a_bzrdir.find_repository())
1137
 
        except NoSuchFile:
1138
 
            raise NotBranchError(path=transport.base)
 
1041
        transport = a_bzrdir.get_branch_transport(None)
 
1042
        control_files = lockable_files.LockableFiles(transport, 'lock',
 
1043
                                                     lockdir.LockDir)
 
1044
        return BzrBranch5(_format=self,
 
1045
                          _control_files=control_files,
 
1046
                          a_bzrdir=a_bzrdir,
 
1047
                          _repository=a_bzrdir.find_repository())
1139
1048
 
1140
1049
 
1141
1050
class BzrBranchFormat6(BzrBranchFormat5):
1205
1114
        """See BranchFormat.get_format_description()."""
1206
1115
        return "Checkout reference format 1"
1207
1116
        
1208
 
    def get_reference(self, a_bzrdir):
1209
 
        """See BranchFormat.get_reference()."""
1210
 
        transport = a_bzrdir.get_branch_transport(None)
1211
 
        return transport.get('location').read()
1212
 
 
1213
1117
    def initialize(self, a_bzrdir, target_branch=None):
1214
1118
        """Create a branch of this format in a_bzrdir."""
1215
1119
        if target_branch is None:
1237
1141
            # emit some sort of warning/error to the caller ?!
1238
1142
        return clone
1239
1143
 
1240
 
    def open(self, a_bzrdir, _found=False, location=None):
 
1144
    def open(self, a_bzrdir, _found=False):
1241
1145
        """Return the branch that the branch reference in a_bzrdir points at.
1242
1146
 
1243
1147
        _found is a private parameter, do not use it. It is used to indicate
1246
1150
        if not _found:
1247
1151
            format = BranchFormat.find_format(a_bzrdir)
1248
1152
            assert format.__class__ == self.__class__
1249
 
        if location is None:
1250
 
            location = self.get_reference(a_bzrdir)
1251
 
        real_bzrdir = bzrdir.BzrDir.open(location)
 
1153
        transport = a_bzrdir.get_branch_transport(None)
 
1154
        real_bzrdir = bzrdir.BzrDir.open(transport.get('location').read())
1252
1155
        result = real_bzrdir.open_branch()
1253
1156
        # this changes the behaviour of result.clone to create a new reference
1254
1157
        # rather than a copy of the content of the branch.
1280
1183
    it's writable, and can be accessed via the normal filesystem API.
1281
1184
    """
1282
1185
    
1283
 
    def __init__(self, _format=None,
 
1186
    def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
 
1187
                 relax_version_check=DEPRECATED_PARAMETER, _format=None,
1284
1188
                 _control_files=None, a_bzrdir=None, _repository=None):
1285
 
        """Create new branch object at a particular location."""
 
1189
        """Create new branch object at a particular location.
 
1190
 
 
1191
        transport -- A Transport object, defining how to access files.
 
1192
        
 
1193
        init -- If True, create new control files in a previously
 
1194
             unversioned directory.  If False, the branch must already
 
1195
             be versioned.
 
1196
 
 
1197
        relax_version_check -- If true, the usual check for the branch
 
1198
            version is not applied.  This is intended only for
 
1199
            upgrade/recovery type use; it's not guaranteed that
 
1200
            all operations will work on old format branches.
 
1201
        """
1286
1202
        Branch.__init__(self)
1287
1203
        if a_bzrdir is None:
1288
 
            raise ValueError('a_bzrdir must be supplied')
 
1204
            self.bzrdir = bzrdir.BzrDir.open(transport.base)
1289
1205
        else:
1290
1206
            self.bzrdir = a_bzrdir
1291
1207
        # self._transport used to point to the directory containing the
1297
1213
            raise ValueError('BzrBranch _control_files is None')
1298
1214
        self.control_files = _control_files
1299
1215
        self._transport = _control_files._transport
 
1216
        if deprecated_passed(init):
 
1217
            warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
 
1218
                 "deprecated as of bzr 0.8. Please use Branch.create().",
 
1219
                 DeprecationWarning,
 
1220
                 stacklevel=2)
 
1221
            if init:
 
1222
                # this is slower than before deprecation, oh well never mind.
 
1223
                # -> its deprecated.
 
1224
                self._initialize(transport.base)
 
1225
        self._check_format(_format)
 
1226
        if deprecated_passed(relax_version_check):
 
1227
            warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
 
1228
                 "relax_version_check parameter is deprecated as of bzr 0.8. "
 
1229
                 "Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
 
1230
                 "open() method.",
 
1231
                 DeprecationWarning,
 
1232
                 stacklevel=2)
 
1233
            if (not relax_version_check
 
1234
                and not self._format.is_supported()):
 
1235
                raise errors.UnsupportedFormatError(format=fmt)
 
1236
        if deprecated_passed(transport):
 
1237
            warn("BzrBranch.__init__(transport=XXX...): The transport "
 
1238
                 "parameter is deprecated as of bzr 0.8. "
 
1239
                 "Please use Branch.open, or bzrdir.open_branch().",
 
1240
                 DeprecationWarning,
 
1241
                 stacklevel=2)
1300
1242
        self.repository = _repository
1301
1243
 
1302
1244
    def __str__(self):
1310
1252
 
1311
1253
    base = property(_get_base, doc="The URL for the root of this branch.")
1312
1254
 
 
1255
    def _finish_transaction(self):
 
1256
        """Exit the current transaction."""
 
1257
        return self.control_files._finish_transaction()
 
1258
 
 
1259
    def get_transaction(self):
 
1260
        """Return the current active transaction.
 
1261
 
 
1262
        If no transaction is active, this returns a passthrough object
 
1263
        for which all data is immediately flushed and no caching happens.
 
1264
        """
 
1265
        # this is an explicit function so that we can do tricky stuff
 
1266
        # when the storage in rev_storage is elsewhere.
 
1267
        # we probably need to hook the two 'lock a location' and 
 
1268
        # 'have a transaction' together more delicately, so that
 
1269
        # we can have two locks (branch and storage) and one transaction
 
1270
        # ... and finishing the transaction unlocks both, but unlocking
 
1271
        # does not. - RBC 20051121
 
1272
        return self.control_files.get_transaction()
 
1273
 
 
1274
    def _set_transaction(self, transaction):
 
1275
        """Set a new active transaction."""
 
1276
        return self.control_files._set_transaction(transaction)
 
1277
 
1313
1278
    def abspath(self, name):
1314
1279
        """See Branch.abspath."""
1315
1280
        return self.control_files._transport.abspath(name)
1316
1281
 
1317
 
 
1318
 
    @deprecated_method(zero_sixteen)
 
1282
    def _check_format(self, format):
 
1283
        """Identify the branch format if needed.
 
1284
 
 
1285
        The format is stored as a reference to the format object in
 
1286
        self._format for code that needs to check it later.
 
1287
 
 
1288
        The format parameter is either None or the branch format class
 
1289
        used to open this branch.
 
1290
 
 
1291
        FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
 
1292
        """
 
1293
        if format is None:
 
1294
            format = BranchFormat.find_format(self.bzrdir)
 
1295
        self._format = format
 
1296
        mutter("got branch format %s", self._format)
 
1297
 
1319
1298
    @needs_read_lock
1320
1299
    def get_root_id(self):
1321
1300
        """See Branch.get_root_id."""
1325
1304
    def is_locked(self):
1326
1305
        return self.control_files.is_locked()
1327
1306
 
1328
 
    def lock_write(self, token=None):
1329
 
        repo_token = self.repository.lock_write()
 
1307
    def lock_write(self):
 
1308
        self.repository.lock_write()
1330
1309
        try:
1331
 
            token = self.control_files.lock_write(token=token)
 
1310
            self.control_files.lock_write()
1332
1311
        except:
1333
1312
            self.repository.unlock()
1334
1313
            raise
1335
 
        return token
1336
1314
 
1337
1315
    def lock_read(self):
1338
1316
        self.repository.lock_read()
1348
1326
            self.control_files.unlock()
1349
1327
        finally:
1350
1328
            self.repository.unlock()
1351
 
        if not self.control_files.is_locked():
1352
 
            # we just released the lock
1353
 
            self._clear_cached_state()
1354
1329
        
1355
1330
    def peek_lock_mode(self):
1356
1331
        if self.control_files._lock_count == 0:
1389
1364
    def set_revision_history(self, rev_history):
1390
1365
        """See Branch.set_revision_history."""
1391
1366
        rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1392
 
        self._clear_cached_state()
1393
1367
        self._write_revision_history(rev_history)
1394
 
        self._cache_revision_history(rev_history)
 
1368
        transaction = self.get_transaction()
 
1369
        history = transaction.map.find_revision_history()
 
1370
        if history is not None:
 
1371
            # update the revision history in the identity map.
 
1372
            history[:] = list(rev_history)
 
1373
            # this call is disabled because revision_history is 
 
1374
            # not really an object yet, and the transaction is for objects.
 
1375
            # transaction.register_dirty(history)
 
1376
        else:
 
1377
            transaction.map.add_revision_history(rev_history)
 
1378
            # this call is disabled because revision_history is 
 
1379
            # not really an object yet, and the transaction is for objects.
 
1380
            # transaction.register_clean(history)
1395
1381
        for hook in Branch.hooks['set_rh']:
1396
1382
            hook(self, rev_history)
1397
1383
 
1409
1395
            history.pop()
1410
1396
        return history
1411
1397
 
 
1398
    @needs_read_lock
 
1399
    def revision_history(self):
 
1400
        """See Branch.revision_history."""
 
1401
        transaction = self.get_transaction()
 
1402
        history = transaction.map.find_revision_history()
 
1403
        if history is not None:
 
1404
            # mutter("cache hit for revision-history in %s", self)
 
1405
            return list(history)
 
1406
        history = self._gen_revision_history()
 
1407
        transaction.map.add_revision_history(history)
 
1408
        # this call is disabled because revision_history is 
 
1409
        # not really an object yet, and the transaction is for objects.
 
1410
        # transaction.register_clean(history, precious=True)
 
1411
        return list(history)
 
1412
 
1412
1413
    def _lefthand_history(self, revision_id, last_rev=None,
1413
1414
                          other_branch=None):
1414
1415
        # stop_revision must be a descendant of last_revision
1415
1416
        stop_graph = self.repository.get_revision_graph(revision_id)
1416
 
        if (last_rev is not None and last_rev != _mod_revision.NULL_REVISION
1417
 
            and last_rev not in stop_graph):
 
1417
        if last_rev is not None and last_rev not in stop_graph:
1418
1418
            # our previous tip is not merged into stop_revision
1419
1419
            raise errors.DivergedBranches(self, other_branch)
1420
1420
        # make a new revision history from the graph
1459
1459
                stop_revision = osutils.safe_revision_id(stop_revision)
1460
1460
            # whats the current last revision, before we fetch [and change it
1461
1461
            # possibly]
1462
 
            last_rev = _mod_revision.ensure_null(self.last_revision())
 
1462
            last_rev = self.last_revision()
1463
1463
            # we fetch here regardless of whether we need to so that we pickup
1464
1464
            # filled in ghosts.
1465
1465
            self.fetch(other, stop_revision)
1466
 
            my_ancestry = self.repository.get_ancestry(last_rev,
1467
 
                                                       topo_sorted=False)
 
1466
            my_ancestry = self.repository.get_ancestry(last_rev)
1468
1467
            if stop_revision in my_ancestry:
1469
1468
                # last_revision is a descendant of stop_revision
1470
1469
                return
1489
1488
 
1490
1489
    @needs_write_lock
1491
1490
    def pull(self, source, overwrite=False, stop_revision=None,
1492
 
             _hook_master=None, run_hooks=True):
 
1491
        _hook_master=None, _run_hooks=True):
1493
1492
        """See Branch.pull.
1494
1493
 
1495
1494
        :param _hook_master: Private parameter - set the branch to 
1496
1495
            be supplied as the master to push hooks.
1497
 
        :param run_hooks: Private parameter - if false, this branch
1498
 
            is being called because it's the master of the primary branch,
1499
 
            so it should not run its hooks.
 
1496
        :param _run_hooks: Private parameter - allow disabling of
 
1497
            hooks, used when pushing to a master branch.
1500
1498
        """
1501
1499
        result = PullResult()
1502
1500
        result.source_branch = source
1521
1519
            else:
1522
1520
                result.master_branch = self
1523
1521
                result.local_branch = None
1524
 
            if run_hooks:
 
1522
            if _run_hooks:
1525
1523
                for hook in Branch.hooks['post_pull']:
1526
1524
                    hook(result)
1527
1525
        finally:
1539
1537
 
1540
1538
    @needs_read_lock
1541
1539
    def push(self, target, overwrite=False, stop_revision=None,
1542
 
             _override_hook_source_branch=None):
 
1540
        _hook_master=None, _run_hooks=True):
1543
1541
        """See Branch.push.
1544
 
 
1545
 
        This is the basic concrete implementation of push()
1546
 
 
1547
 
        :param _override_hook_source_branch: If specified, run
1548
 
        the hooks passing this Branch as the source, rather than self.  
1549
 
        This is for use of RemoteBranch, where push is delegated to the
1550
 
        underlying vfs-based Branch. 
1551
 
        """
1552
 
        # TODO: Public option to disable running hooks - should be trivial but
1553
 
        # needs tests.
1554
 
        target.lock_write()
1555
 
        try:
1556
 
            result = self._push_with_bound_branches(target, overwrite,
1557
 
                    stop_revision,
1558
 
                    _override_hook_source_branch=_override_hook_source_branch)
1559
 
            return result
1560
 
        finally:
1561
 
            target.unlock()
1562
 
 
1563
 
    def _push_with_bound_branches(self, target, overwrite,
1564
 
            stop_revision,
1565
 
            _override_hook_source_branch=None):
1566
 
        """Push from self into target, and into target's master if any.
1567
1542
        
1568
 
        This is on the base BzrBranch class even though it doesn't support 
1569
 
        bound branches because the *target* might be bound.
1570
 
        """
1571
 
        def _run_hooks():
1572
 
            if _override_hook_source_branch:
1573
 
                result.source_branch = _override_hook_source_branch
1574
 
            for hook in Branch.hooks['post_push']:
1575
 
                hook(result)
1576
 
 
1577
 
        bound_location = target.get_bound_location()
1578
 
        if bound_location and target.base != bound_location:
1579
 
            # there is a master branch.
1580
 
            #
1581
 
            # XXX: Why the second check?  Is it even supported for a branch to
1582
 
            # be bound to itself? -- mbp 20070507
1583
 
            master_branch = target.get_master_branch()
1584
 
            master_branch.lock_write()
1585
 
            try:
1586
 
                # push into the master from this branch.
1587
 
                self._basic_push(master_branch, overwrite, stop_revision)
1588
 
                # and push into the target branch from this. Note that we push from
1589
 
                # this branch again, because its considered the highest bandwidth
1590
 
                # repository.
1591
 
                result = self._basic_push(target, overwrite, stop_revision)
1592
 
                result.master_branch = master_branch
1593
 
                result.local_branch = target
1594
 
                _run_hooks()
1595
 
                return result
1596
 
            finally:
1597
 
                master_branch.unlock()
1598
 
        else:
1599
 
            # no master branch
1600
 
            result = self._basic_push(target, overwrite, stop_revision)
1601
 
            # TODO: Why set master_branch and local_branch if there's no
1602
 
            # binding?  Maybe cleaner to just leave them unset? -- mbp
1603
 
            # 20070504
1604
 
            result.master_branch = target
1605
 
            result.local_branch = None
1606
 
            _run_hooks()
1607
 
            return result
1608
 
 
1609
 
    def _basic_push(self, target, overwrite, stop_revision):
1610
 
        """Basic implementation of push without bound branches or hooks.
1611
 
 
1612
 
        Must be called with self read locked and target write locked.
 
1543
        :param _hook_master: Private parameter - set the branch to 
 
1544
            be supplied as the master to push hooks.
 
1545
        :param _run_hooks: Private parameter - allow disabling of
 
1546
            hooks, used when pushing to a master branch.
1613
1547
        """
1614
1548
        result = PushResult()
1615
1549
        result.source_branch = self
1616
1550
        result.target_branch = target
1617
 
        result.old_revno, result.old_revid = target.last_revision_info()
 
1551
        target.lock_write()
1618
1552
        try:
1619
 
            target.update_revisions(self, stop_revision)
1620
 
        except DivergedBranches:
1621
 
            if not overwrite:
1622
 
                raise
1623
 
        if overwrite:
1624
 
            target.set_revision_history(self.revision_history())
1625
 
        result.tag_conflicts = self.tags.merge_to(target.tags)
1626
 
        result.new_revno, result.new_revid = target.last_revision_info()
 
1553
            result.old_revno, result.old_revid = target.last_revision_info()
 
1554
            try:
 
1555
                target.update_revisions(self, stop_revision)
 
1556
            except DivergedBranches:
 
1557
                if not overwrite:
 
1558
                    raise
 
1559
            if overwrite:
 
1560
                target.set_revision_history(self.revision_history())
 
1561
            result.tag_conflicts = self.tags.merge_to(target.tags)
 
1562
            result.new_revno, result.new_revid = target.last_revision_info()
 
1563
            if _hook_master:
 
1564
                result.master_branch = _hook_master
 
1565
                result.local_branch = target
 
1566
            else:
 
1567
                result.master_branch = target
 
1568
                result.local_branch = None
 
1569
            if _run_hooks:
 
1570
                for hook in Branch.hooks['post_push']:
 
1571
                    hook(result)
 
1572
        finally:
 
1573
            target.unlock()
1627
1574
        return result
1628
1575
 
1629
1576
    def get_parent(self):
1642
1589
        except errors.InvalidURLJoin, e:
1643
1590
            raise errors.InaccessibleParent(parent, self.base)
1644
1591
 
 
1592
    def get_push_location(self):
 
1593
        """See Branch.get_push_location."""
 
1594
        push_loc = self.get_config().get_user_option('push_location')
 
1595
        return push_loc
 
1596
 
1645
1597
    def set_push_location(self, location):
1646
1598
        """See Branch.set_push_location."""
1647
1599
        self.get_config().set_user_option(
1661
1613
                try: 
1662
1614
                    url = url.encode('ascii')
1663
1615
                except UnicodeEncodeError:
1664
 
                    raise errors.InvalidURL(url,
 
1616
                    raise bzrlib.errors.InvalidURL(url,
1665
1617
                        "Urls must be 7-bit ascii, "
1666
1618
                        "use bzrlib.urlutils.escape")
1667
1619
            url = urlutils.relative_url(self.base, url)
1699
1651
        
1700
1652
    @needs_write_lock
1701
1653
    def pull(self, source, overwrite=False, stop_revision=None,
1702
 
             run_hooks=True):
1703
 
        """Pull from source into self, updating my master if any.
 
1654
        _run_hooks=True):
 
1655
        """Extends branch.pull to be bound branch aware.
1704
1656
        
1705
 
        :param run_hooks: Private parameter - if false, this branch
1706
 
            is being called because it's the master of the primary branch,
1707
 
            so it should not run its hooks.
 
1657
        :param _run_hooks: Private parameter used to force hook running
 
1658
            off during bound branch double-pushing.
1708
1659
        """
1709
1660
        bound_location = self.get_bound_location()
1710
1661
        master_branch = None
1716
1667
            if master_branch:
1717
1668
                # pull from source into master.
1718
1669
                master_branch.pull(source, overwrite, stop_revision,
1719
 
                    run_hooks=False)
 
1670
                    _run_hooks=False)
1720
1671
            return super(BzrBranch5, self).pull(source, overwrite,
1721
1672
                stop_revision, _hook_master=master_branch,
1722
 
                run_hooks=run_hooks)
 
1673
                _run_hooks=_run_hooks)
 
1674
        finally:
 
1675
            if master_branch:
 
1676
                master_branch.unlock()
 
1677
 
 
1678
    @needs_read_lock
 
1679
    def push(self, target, overwrite=False, stop_revision=None):
 
1680
        """Updates branch.push to be bound branch aware."""
 
1681
        bound_location = target.get_bound_location()
 
1682
        master_branch = None
 
1683
        if bound_location and target.base != bound_location:
 
1684
            # not pushing to master, so we need to update master.
 
1685
            master_branch = target.get_master_branch()
 
1686
            master_branch.lock_write()
 
1687
        try:
 
1688
            if master_branch:
 
1689
                # push into the master from this branch.
 
1690
                super(BzrBranch5, self).push(master_branch, overwrite,
 
1691
                    stop_revision, _run_hooks=False)
 
1692
            # and push into the target branch from this. Note that we push from
 
1693
            # this branch again, because its considered the highest bandwidth
 
1694
            # repository.
 
1695
            return super(BzrBranch5, self).push(target, overwrite,
 
1696
                stop_revision, _hook_master=master_branch)
1723
1697
        finally:
1724
1698
            if master_branch:
1725
1699
                master_branch.unlock()
1789
1763
        # last_rev is not in the other_last_rev history, AND
1790
1764
        # other_last_rev is not in our history, and do it without pulling
1791
1765
        # history around
1792
 
        last_rev = _mod_revision.ensure_null(self.last_revision())
1793
 
        if last_rev != _mod_revision.NULL_REVISION:
 
1766
        last_rev = self.last_revision()
 
1767
        if last_rev is not None:
1794
1768
            other.lock_read()
1795
1769
            try:
1796
1770
                other_last_rev = other.last_revision()
1797
 
                if not _mod_revision.is_null(other_last_rev):
 
1771
                if other_last_rev is not None:
1798
1772
                    # neither branch is new, we have to do some work to
1799
1773
                    # ascertain diversion.
1800
1774
                    remote_graph = other.repository.get_revision_graph(
1823
1797
        if master is not None:
1824
1798
            old_tip = self.last_revision()
1825
1799
            self.pull(master, overwrite=True)
1826
 
            if old_tip in self.repository.get_ancestry(
1827
 
                _mod_revision.ensure_null(self.last_revision()),
1828
 
                topo_sorted=False):
 
1800
            if old_tip in self.repository.get_ancestry(self.last_revision()):
1829
1801
                return None
1830
1802
            return old_tip
1831
1803
        return None
1861
1833
        return "Experimental branch format"
1862
1834
 
1863
1835
    @classmethod
1864
 
    def get_reference(cls, a_bzrdir):
1865
 
        """Get the target reference of the branch in a_bzrdir.
1866
 
 
1867
 
        format probing must have been completed before calling
1868
 
        this method - it is assumed that the format of the branch
1869
 
        in a_bzrdir is correct.
1870
 
 
1871
 
        :param a_bzrdir: The bzrdir to get the branch data from.
1872
 
        :return: None if the branch is not a reference branch.
1873
 
        """
1874
 
        return None
1875
 
 
1876
 
    @classmethod
1877
1836
    def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1878
1837
            lock_class):
1879
1838
        branch_transport = a_bzrdir.get_branch_transport(cls)
1969
1928
        if self._get_append_revisions_only():
1970
1929
            self._check_history_violation(revision_id)
1971
1930
        self._write_last_revision_info(revno, revision_id)
1972
 
        self._clear_cached_state()
 
1931
        transaction = self.get_transaction()
 
1932
        cached_history = transaction.map.find_revision_history()
 
1933
        if cached_history is not None:
 
1934
            transaction.map.remove_object(cached_history)
1973
1935
 
1974
1936
    def _check_history_violation(self, revision_id):
1975
 
        last_revision = _mod_revision.ensure_null(self.last_revision())
1976
 
        if _mod_revision.is_null(last_revision):
 
1937
        last_revision = self.last_revision()
 
1938
        if last_revision is None:
1977
1939
            return
1978
1940
        if last_revision not in self._lefthand_history(revision_id):
1979
1941
            raise errors.AppendRevisionsOnlyViolation(self.base)
2043
2005
            if config.get_user_option('bound') != 'True':
2044
2006
                return False
2045
2007
            else:
2046
 
                config.set_user_option('bound', 'False', warn_masked=True)
 
2008
                config.set_user_option('bound', 'False')
2047
2009
                return True
2048
2010
        else:
2049
2011
            self._set_config_location('bound_location', location,
2050
2012
                                      config=config)
2051
 
            config.set_user_option('bound', 'True', warn_masked=True)
 
2013
            config.set_user_option('bound', 'True')
2052
2014
        return True
2053
2015
 
2054
2016
    def _get_bound_location(self, bound):
2074
2036
            value = 'True'
2075
2037
        else:
2076
2038
            value = 'False'
2077
 
        self.get_config().set_user_option('append_revisions_only', value,
2078
 
            warn_masked=True)
 
2039
        self.get_config().set_user_option('append_revisions_only', value)
2079
2040
 
2080
2041
    def _get_append_revisions_only(self):
2081
2042
        value = self.get_config().get_user_option('append_revisions_only')
2096
2057
        if revision_id is None:
2097
2058
            revno, revision_id = self.last_revision_info()
2098
2059
        else:
2099
 
            # To figure out the revno for a random revision, we need to build
2100
 
            # the revision history, and count its length.
2101
 
            # We don't care about the order, just how long it is.
2102
 
            # Alternatively, we could start at the current location, and count
2103
 
            # backwards. But there is no guarantee that we will find it since
2104
 
            # it may be a merged revision.
2105
 
            revno = len(list(self.repository.iter_reverse_revision_history(
2106
 
                                                                revision_id)))
 
2060
            revno = self.revision_id_to_revno(revision_id)
2107
2061
        destination.set_last_revision_info(revno, revision_id)
2108
2062
 
2109
2063
    def _make_tags(self):
2110
2064
        return BasicTags(self)
2111
2065
 
2112
2066
 
 
2067
class BranchTestProviderAdapter(object):
 
2068
    """A tool to generate a suite testing multiple branch formats at once.
 
2069
 
 
2070
    This is done by copying the test once for each transport and injecting
 
2071
    the transport_server, transport_readonly_server, and branch_format
 
2072
    classes into each copy. Each copy is also given a new id() to make it
 
2073
    easy to identify.
 
2074
    """
 
2075
 
 
2076
    def __init__(self, transport_server, transport_readonly_server, formats):
 
2077
        self._transport_server = transport_server
 
2078
        self._transport_readonly_server = transport_readonly_server
 
2079
        self._formats = formats
 
2080
    
 
2081
    def adapt(self, test):
 
2082
        result = TestSuite()
 
2083
        for branch_format, bzrdir_format in self._formats:
 
2084
            new_test = deepcopy(test)
 
2085
            new_test.transport_server = self._transport_server
 
2086
            new_test.transport_readonly_server = self._transport_readonly_server
 
2087
            new_test.bzrdir_format = bzrdir_format
 
2088
            new_test.branch_format = branch_format
 
2089
            def make_new_test_id():
 
2090
                # the format can be either a class or an instance
 
2091
                name = getattr(branch_format, '__name__',
 
2092
                        branch_format.__class__.__name__)
 
2093
                new_id = "%s(%s)" % (new_test.id(), name)
 
2094
                return lambda: new_id
 
2095
            new_test.id = make_new_test_id()
 
2096
            result.addTest(new_test)
 
2097
        return result
 
2098
 
 
2099
 
2113
2100
######################################################################
2114
2101
# results of operations
2115
2102