~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.

Show diffs side-by-side

added added

removed removed

Lines of Context:
148
148
        NOTE: This will soon be deprecated in favour of creation
149
149
        through a BzrDir.
150
150
        """
151
 
        # imported here to prevent scope creep as this is going.
152
 
        from bzrlib.workingtree import WorkingTree
153
 
        return WorkingTree.create_standalone(safe_unicode(base)).branch
 
151
        return bzrdir.BzrDir.create_standalone_workingtree(base).branch
154
152
 
155
153
    def setup_caching(self, cache_root):
156
154
        """Subclasses that care about caching should override this, and set
370
368
        """
371
369
        if revno < 1 or revno > self.revno():
372
370
            raise InvalidRevisionNumber(revno)
373
 
        
374
 
    def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
375
 
        """Copy this branch into the existing directory to_location.
376
 
 
377
 
        Returns the newly created branch object.
378
 
 
379
 
        revision
380
 
            If not None, only revisions up to this point will be copied.
381
 
            The head of the new branch will be that revision.  Must be a
382
 
            revid or None.
383
 
    
384
 
        to_location -- The destination directory; must either exist and be 
385
 
            empty, or not exist, in which case it is created.
386
 
    
387
 
        basis_branch
388
 
            A local branch to copy revisions from, related to this branch. 
389
 
            This is used when branching from a remote (slow) branch, and we have
390
 
            a local branch that might contain some relevant revisions.
391
 
    
392
 
        to_branch_type
393
 
            Branch type of destination branch
394
 
        """
395
 
        from bzrlib.workingtree import WorkingTree
396
 
        assert isinstance(to_location, basestring)
397
 
        segments = to_location.split('/')
398
 
        if segments and segments[-1] not in ('', '.'):
399
 
            parent = '/'.join(segments[:-1])
400
 
            t = get_transport(parent)
 
371
 
 
372
    @needs_read_lock
 
373
    def clone(self, *args, **kwargs):
 
374
        """Clone this branch into to_bzrdir preserving all semantic values.
 
375
        
 
376
        revision_id: if not None, the revision history in the new branch will
 
377
                     be truncated to end with revision_id.
 
378
        """
 
379
        # for API compatability, until 0.8 releases we provide the old api:
 
380
        # def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
 
381
        # after 0.8 releases, the *args and **kwargs should be changed:
 
382
        # def clone(self, to_bzrdir, revision_id=None):
 
383
        if (kwargs.get('to_location', None) or
 
384
            kwargs.get('revision', None) or
 
385
            kwargs.get('basis_branch', None) or
 
386
            (len(args) and isinstance(args[0], basestring))):
 
387
            # backwards compatability api:
 
388
            warn("Branch.clone() has been deprecated for BzrDir.clone() from"
 
389
                 " bzrlib 0.8.", DeprecationWarning, stacklevel=3)
 
390
            # get basis_branch
 
391
            if len(args) > 2:
 
392
                basis_branch = args[2]
 
393
            else:
 
394
                basis_branch = kwargs.get('basis_branch', None)
 
395
            if basis_branch:
 
396
                basis = basis_branch.bzrdir
 
397
            else:
 
398
                basis = None
 
399
            # get revision
 
400
            if len(args) > 1:
 
401
                revision_id = args[1]
 
402
            else:
 
403
                revision_id = kwargs.get('revision', None)
 
404
            # get location
 
405
            if len(args):
 
406
                url = args[0]
 
407
            else:
 
408
                # no default to raise if not provided.
 
409
                url = kwargs.get('to_location')
 
410
            return self.bzrdir.clone(url,
 
411
                                     revision_id=revision_id,
 
412
                                     basis=basis).open_branch()
 
413
        # new cleaner api.
 
414
        # generate args by hand 
 
415
        if len(args) > 1:
 
416
            revision_id = args[1]
 
417
        else:
 
418
            revision_id = kwargs.get('revision_id', None)
 
419
        if len(args):
 
420
            to_bzrdir = args[0]
 
421
        else:
 
422
            # no default to raise if not provided.
 
423
            to_bzrdir = kwargs.get('to_bzrdir')
 
424
        result = self._format.initialize(to_bzrdir)
 
425
        self.copy_content_into(result, revision_id=revision_id)
 
426
        return  result
 
427
 
 
428
    @needs_read_lock
 
429
    def sprout(self, to_bzrdir, revision_id=None):
 
430
        """Create a new line of development from the branch, into to_bzrdir.
 
431
        
 
432
        revision_id: if not None, the revision history in the new branch will
 
433
                     be truncated to end with revision_id.
 
434
        """
 
435
        result = self._format.initialize(to_bzrdir)
 
436
        self.copy_content_into(result, revision_id=revision_id)
 
437
        result.set_parent(self.bzrdir.root_transport.base)
 
438
        return result
 
439
 
 
440
    @needs_read_lock
 
441
    def copy_content_into(self, destination, revision_id=None):
 
442
        """Copy the content of self into destination.
 
443
 
 
444
        revision_id: if not None, the revision history in the new branch will
 
445
                     be truncated to end with revision_id.
 
446
        """
 
447
        new_history = self.revision_history()
 
448
        if revision_id is not None:
401
449
            try:
402
 
                t.mkdir(segments[-1])
403
 
            except errors.FileExists:
404
 
                pass
405
 
        if to_branch_format is None:
406
 
            # use the default
407
 
            br_to = bzrdir.BzrDir.create_branch_and_repo(to_location)
408
 
        else:
409
 
            br_to = to_branch_format.initialize(to_location)
410
 
        mutter("copy branch from %s to %s", self, br_to)
411
 
        if revision is None:
412
 
            revision = self.last_revision()
413
 
        if basis_branch is not None:
414
 
            basis_branch.repository.push_stores(br_to.repository,
415
 
                                                revision=revision)
416
 
        br_to.update_revisions(self, stop_revision=revision)
417
 
        br_to.set_parent(self.base)
418
 
        mutter("copied")
419
 
        return br_to
 
450
                new_history = new_history[:new_history.index(revision_id) + 1]
 
451
            except ValueError:
 
452
                rev = self.repository.get_revision(revision_id)
 
453
                new_history = rev.get_history(self.repository)[1:]
 
454
        destination.set_revision_history(new_history)
 
455
        parent = self.get_parent()
 
456
        if parent:
 
457
            destination.set_parent(parent)
420
458
 
421
459
 
422
460
class BranchFormat(object):
619
657
                         a_bzrdir=a_bzrdir)
620
658
 
621
659
 
 
660
class BranchReferenceFormat(BranchFormat):
 
661
    """Bzr branch reference format.
 
662
 
 
663
    Branch references are used in implementing checkouts, they
 
664
    act as an alias to the real branch which is at some other url.
 
665
 
 
666
    This format has:
 
667
     - A location file
 
668
     - a format string
 
669
    """
 
670
 
 
671
    def get_format_string(self):
 
672
        """See BranchFormat.get_format_string()."""
 
673
        return "Bazaar-NG Branch Reference Format 1\n"
 
674
        
 
675
    def initialize(self, a_bzrdir, target_branch=None):
 
676
        """Create a branch of this format in a_bzrdir."""
 
677
        if target_branch is None:
 
678
            # this format does not implement branch itself, thus the implicit
 
679
            # creation contract must see it as uninitializable
 
680
            raise errors.UninitializableFormat(self)
 
681
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
 
682
        branch_transport = a_bzrdir.get_branch_transport(self)
 
683
        # FIXME rbc 20060209 one j-a-ms encoding branch lands this str() cast is not needed.
 
684
        branch_transport.put('location', StringIO(str(target_branch.bzrdir.root_transport.base)))
 
685
        branch_transport.put('format', StringIO(self.get_format_string()))
 
686
        return self.open(a_bzrdir, _found=True)
 
687
 
 
688
    def __init__(self):
 
689
        super(BranchReferenceFormat, self).__init__()
 
690
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
691
 
 
692
    def _make_reference_clone_function(format, a_branch):
 
693
        """Create a clone() routine for a branch dynamically."""
 
694
        def clone(to_bzrdir, revision_id=None):
 
695
            """See Branch.clone()."""
 
696
            return format.initialize(to_bzrdir, a_branch)
 
697
            # cannot obey revision_id limits when cloning a reference ...
 
698
            # FIXME RBC 20060210 either nuke revision_id for clone, or
 
699
            # emit some sort of warning/error to the caller ?!
 
700
        return clone
 
701
 
 
702
    def open(self, a_bzrdir, _found=False):
 
703
        """Return the branch that the branch reference in a_bzrdir points at.
 
704
 
 
705
        _found is a private parameter, do not use it. It is used to indicate
 
706
               if format probing has already be done.
 
707
        """
 
708
        if not _found:
 
709
            format = BranchFormat.find_format(a_bzrdir)
 
710
            assert format.__class__ == self.__class__
 
711
        transport = a_bzrdir.get_branch_transport(None)
 
712
        real_bzrdir = bzrdir.BzrDir.open(transport.get('location').read())
 
713
        result = real_bzrdir.open_branch()
 
714
        # this changes the behaviour of result.clone to create a new reference
 
715
        # rather than a copy of the content of the branch.
 
716
        # I did not use a proxy object because that needs much more extensive
 
717
        # testing, and we are only changing one behaviour at the moment.
 
718
        # If we decide to alter more behaviours - i.e. the implicit nickname
 
719
        # then this should be refactored to introduce a tested proxy branch
 
720
        # and a subclass of that for use in overriding clone() and ....
 
721
        # - RBC 20060210
 
722
        result.clone = self._make_reference_clone_function(result)
 
723
        return result
 
724
 
 
725
 
622
726
# formats which have no format string are not discoverable
623
727
# and not independently creatable, so are not registered.
624
728
__default_format = BzrBranchFormat5()
625
729
BranchFormat.register_format(__default_format)
 
730
BranchFormat.register_format(BranchReferenceFormat())
626
731
BranchFormat.set_default_format(__default_format)
627
732
_legacy_formats = [BzrBranchFormat4(),
628
733
                   ]
633
738
    Note that it's "local" in the context of the filesystem; it doesn't
634
739
    really matter if it's on an nfs/smb/afs/coda/... share, as long as
635
740
    it's writable, and can be accessed via the normal filesystem API.
636
 
 
637
741
    """
638
742
    # We actually expect this class to be somewhat short-lived; part of its
639
743
    # purpose is to try to isolate what bits of the branch logic are tied to
991
1095
        mutter("copied")
992
1096
        return branch_to
993
1097
 
994
 
    def clone(self, to_location, revision=None, basis_branch=None, to_branch_type=None):
995
 
        print "FIXME: clone via create and fetch is probably faster when versioned file comes in."
996
 
        if (to_branch_type is None
997
 
            and self.repository.weave_store.listable()
998
 
            and self.repository.revision_store.listable()):
999
 
            return self._clone_weave(to_location, revision, basis_branch)
1000
 
        else:
1001
 
            return Branch.clone(self, to_location, revision, basis_branch, to_branch_type)
1002
 
 
1003
1098
 
1004
1099
class BranchTestProviderAdapter(object):
1005
1100
    """A tool to generate a suite testing multiple branch formats at once.