~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Disable inheritance for getting at LockableFiles, rather use composition.

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
from bzrlib.tree import EmptyTree, RevisionTree
45
45
from bzrlib.inventory import Inventory
46
46
from bzrlib.lockablefiles import LockableFiles
 
47
from bzrlib.revstorage import RevisionStorage
47
48
from bzrlib.store import copy_all
48
49
import bzrlib.transactions as transactions
49
50
from bzrlib.transport import Transport, get_transport
 
51
import bzrlib.ui
50
52
import bzrlib.xml5
51
 
import bzrlib.ui
52
 
from bzrlib.rev_storage import RevisionStorage
53
53
 
54
54
 
55
55
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
164
164
    nick = property(_get_nick, _set_nick)
165
165
        
166
166
 
167
 
class _Branch(Branch, LockableFiles):
 
167
class _Branch(Branch):
168
168
    """A branch stored in the actual filesystem.
169
169
 
170
170
    Note that it's "local" in the context of the filesystem; it doesn't
223
223
        """
224
224
        assert isinstance(transport, Transport), \
225
225
            "%r is not a Transport" % transport
226
 
        LockableFiles.__init__(self, transport, 'branch-lock')
 
226
        self.control_files = LockableFiles(transport, 'branch-lock')
227
227
        if init:
228
228
            self._make_control()
229
229
        self._check_format(relax_version_check)
230
230
        self.storage = RevisionStorage(transport, self._branch_format)
231
231
 
232
232
    def __str__(self):
233
 
        return '%s(%r)' % (self.__class__.__name__, self._transport.base)
 
233
        return '%s(%r)' % (self.__class__.__name__, self.base)
234
234
 
235
235
    __repr__ = __str__
236
236
 
249
249
            self.cache_root = None
250
250
 
251
251
    def _get_base(self):
252
 
        if self._transport:
253
 
            return self._transport.base
 
252
        if self.control_files._transport:
 
253
            return self.control_files._transport.base
254
254
        return None
255
255
 
256
256
    base = property(_get_base, doc="The URL for the root of this branch.")
259
259
        """Return absolute filename for something in the branch
260
260
        
261
261
        XXX: Robert Collins 20051017 what is this used for? why is it a branch
262
 
        method and not a tree method.
 
262
        method and not a tree method?
263
263
        """
264
 
        return self._transport.abspath(name)
 
264
        return self.control_files._transport.abspath(name)
265
265
 
266
266
    def _make_control(self):
267
267
        from bzrlib.inventory import Inventory
292
292
            ('inventory.weave', empty_weave),
293
293
            ('ancestry.weave', empty_weave)
294
294
        ]
295
 
        cfn = self._rel_controlfilename
296
 
        self._transport.mkdir_multi([cfn(d) for d in dirs])
297
 
        self.put_controlfiles(files)
298
 
        mutter('created control directory in ' + self._transport.base)
 
295
        cfn = self.control_files._rel_controlfilename
 
296
        self.control_files._transport.mkdir_multi([cfn(d) for d in dirs])
 
297
        self.control_files.put_controlfiles(files)
 
298
        mutter('created control directory in ' + self.base)
299
299
 
300
300
    def _check_format(self, relax_version_check):
301
301
        """Check this branch format is supported.
307
307
        classes to support downlevel branches.  But not yet.
308
308
        """
309
309
        try:
310
 
            fmt = self.controlfile('branch-format', 'r').read()
 
310
            fmt = self.control_files.controlfile('branch-format', 'r').read()
311
311
        except NoSuchFile:
312
312
            raise NotBranchError(path=self.base)
313
313
        mutter("got branch format %r", fmt)
332
332
        return inv.root.file_id
333
333
 
334
334
    def lock_write(self):
335
 
        LockableFiles.lock_write(self)
 
335
        # TODO: test for failed two phase locks. This is known broken.
 
336
        self.control_files.lock_write()
336
337
        self.storage.lock_write()
337
338
 
338
339
    def lock_read(self):
339
 
        LockableFiles.lock_read(self)
 
340
        # TODO: test for failed two phase locks. This is known broken.
 
341
        self.control_files.lock_read()
340
342
        self.storage.lock_read()
341
343
 
342
344
    def unlock(self):
 
345
        # TODO: test for failed two phase locks. This is known broken.
343
346
        self.storage.unlock()
344
 
        LockableFiles.unlock(self)
 
347
        self.control_files.unlock()
345
348
 
346
349
    @needs_write_lock
347
350
    def set_root_id(self, file_id):
368
371
        bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
369
372
        sio.seek(0)
370
373
        # Transport handles atomicity
371
 
        self.put_controlfile('inventory', sio)
 
374
        self.control_files.put_controlfile('inventory', sio)
372
375
        
373
376
        mutter('wrote working inventory')
374
377
            
472
475
 
473
476
    @needs_write_lock
474
477
    def set_revision_history(self, rev_history):
475
 
        self.put_controlfile('revision-history', '\n'.join(rev_history))
 
478
        self.control_files.put_controlfile(
 
479
            'revision-history', '\n'.join(rev_history))
476
480
 
477
481
    def get_revision_delta(self, revno):
478
482
        """Return the delta for one revision.
510
514
    @needs_read_lock
511
515
    def revision_history(self):
512
516
        """Return sequence of revision hashes on to this branch."""
 
517
        # FIXME are transactions bound to control files ? RBC 20051121
513
518
        transaction = self.get_transaction()
514
519
        history = transaction.map.find_revision_history()
515
520
        if history is not None:
516
521
            mutter("cache hit for revision-history in %s", self)
517
522
            return list(history)
518
523
        history = [l.rstrip('\r\n') for l in
519
 
                self.controlfile('revision-history', 'r').readlines()]
 
524
                self.control_files.controlfile('revision-history', 'r').readlines()]
520
525
        transaction.map.add_revision_history(history)
521
526
        # this call is disabled because revision_history is 
522
527
        # not really an object yet, and the transaction is for objects.
649
654
        # much more complex to keep consistent than our careful .bzr subset.
650
655
        # instead, we should say that working trees are local only, and optimise
651
656
        # for that.
652
 
        if self._transport.base.find('://') != -1:
 
657
        if self.base.find('://') != -1:
653
658
            raise NoWorkingTree(self.base)
654
659
        return WorkingTree(self.base, branch=self)
655
660
 
833
838
        These are revisions that have been merged into the working
834
839
        directory but not yet committed.
835
840
        """
836
 
        cfn = self._rel_controlfilename('pending-merges')
837
 
        if not self._transport.has(cfn):
 
841
        cfn = self.control_files._rel_controlfilename('pending-merges')
 
842
        if not self.control_files._transport.has(cfn):
838
843
            return []
839
844
        p = []
840
 
        for l in self.controlfile('pending-merges', 'r').readlines():
 
845
        for l in self.control_files.controlfile(
 
846
                'pending-merges', 'r').readlines():
841
847
            p.append(l.rstrip('\n'))
842
848
        return p
843
849
 
857
863
 
858
864
    @needs_write_lock
859
865
    def set_pending_merges(self, rev_list):
860
 
        self.put_controlfile('pending-merges', '\n'.join(rev_list))
 
866
        self.control_files.put_controlfile(
 
867
            'pending-merges', '\n'.join(rev_list))
861
868
 
862
869
    def get_parent(self):
863
870
        """Return the parent location of the branch.
870
877
        _locs = ['parent', 'pull', 'x-pull']
871
878
        for l in _locs:
872
879
            try:
873
 
                return self.controlfile(l, 'r').read().strip('\n')
 
880
                return self.control_files.controlfile(l, 'r').read().strip('\n')
874
881
            except IOError, e:
875
882
                if e.errno != errno.ENOENT:
876
883
                    raise
891
898
    def set_parent(self, url):
892
899
        # TODO: Maybe delete old location files?
893
900
        from bzrlib.atomicfile import AtomicFile
894
 
        f = AtomicFile(self.controlfilename('parent'))
 
901
        f = AtomicFile(self.control_files.controlfilename('parent'))
895
902
        try:
896
903
            f.write(url + '\n')
897
904
            f.commit()
917
924
        if revno < 1 or revno > self.revno():
918
925
            raise InvalidRevisionNumber(revno)
919
926
        
920
 
 
 
927
    def _finish_transaction(self):
 
928
        """Exit the current transaction."""
 
929
        return self.control_files._finish_transaction()
 
930
 
 
931
    def get_transaction(self):
 
932
        """Return the current active transaction.
 
933
 
 
934
        If no transaction is active, this returns a passthrough object
 
935
        for which all data is immediately flushed and no caching happens.
 
936
        """
 
937
        # this is an explicit function so that we can do tricky stuff
 
938
        # when the storage in rev_storage is elsewhere.
 
939
        # we probably need to hook the two 'lock a location' and 
 
940
        # 'have a transaction' together more delicately, so that
 
941
        # we can have two locks (branch and storage) and one transaction
 
942
        # ... and finishing the transaction unlocks both, but unlocking
 
943
        # does not. - RBC 20051121
 
944
        return self.control_files.get_transaction()
 
945
 
 
946
    def _set_transaction(self, transaction):
 
947
        """Set a new active transaction."""
 
948
        return self.control_files._set_transaction(transaction)
921
949
 
922
950
class ScratchBranch(_Branch):
923
951
    """Special test class: a branch that cleans up after itself.
926
954
    >>> isdir(b.base)
927
955
    True
928
956
    >>> bd = b.base
929
 
    >>> b._transport.__del__()
 
957
    >>> b.control_files._transport.__del__()
930
958
    >>> isdir(bd)
931
959
    False
932
960
    """
945
973
            super(ScratchBranch, self).__init__(transport)
946
974
 
947
975
        for d in dirs:
948
 
            self._transport.mkdir(d)
 
976
            self.control_files._transport.mkdir(d)
949
977
            
950
978
        for f in files:
951
 
            self._transport.put(f, 'content of %s' % f)
952
 
 
 
979
            self.control_files._transport.put(f, 'content of %s' % f)
953
980
 
954
981
    def clone(self):
955
982
        """
991
1018
    return False
992
1019
 
993
1020
 
994
 
 
995
1021
def gen_file_id(name):
996
1022
    """Return new file id.
997
1023