~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Use global osutils, otherwise it creates a local var.

Which works, but causes us to run the import on every call.

Show diffs side-by-side

added added

removed removed

Lines of Context:
42
42
        urlutils,
43
43
        )
44
44
from bzrlib.config import BranchConfig, TransportConfig
 
45
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
45
46
from bzrlib.tag import (
46
47
    BasicTags,
47
48
    DisabledTags,
669
670
        raise errors.UnsupportedOperation(self.get_reference_info, self)
670
671
 
671
672
    @needs_write_lock
672
 
    def fetch(self, from_branch, last_revision=None):
 
673
    def fetch(self, from_branch, last_revision=None, fetch_spec=None):
673
674
        """Copy revisions from from_branch into this branch.
674
675
 
675
676
        :param from_branch: Where to copy from.
676
677
        :param last_revision: What revision to stop at (None for at the end
677
678
                              of the branch.
 
679
        :param fetch_spec: If specified, a SearchResult or
 
680
            PendingAncestryResult that describes which revisions to copy.  This
 
681
            allows copying multiple heads at once.  Mutually exclusive with
 
682
            last_revision.
678
683
        :return: None
679
684
        """
680
 
        return InterBranch.get(from_branch, self).fetch(last_revision)
 
685
        return InterBranch.get(from_branch, self).fetch(last_revision,
 
686
            fetch_spec)
681
687
 
682
688
    def get_bound_location(self):
683
689
        """Return the URL of the branch we are bound to.
694
700
 
695
701
    def get_commit_builder(self, parents, config=None, timestamp=None,
696
702
                           timezone=None, committer=None, revprops=None,
697
 
                           revision_id=None, lossy=False):
 
703
                           revision_id=None):
698
704
        """Obtain a CommitBuilder for this branch.
699
705
 
700
706
        :param parents: Revision ids of the parents of the new revision.
704
710
        :param committer: Optional committer to set for commit.
705
711
        :param revprops: Optional dictionary of revision properties.
706
712
        :param revision_id: Optional revision id.
707
 
        :param lossy: Whether to discard data that can not be natively
708
 
            represented, when pushing to a foreign VCS 
709
713
        """
710
714
 
711
715
        if config is None:
712
716
            config = self.get_config()
713
717
 
714
718
        return self.repository.get_commit_builder(self, parents, config,
715
 
            timestamp, timezone, committer, revprops, revision_id,
716
 
            lossy)
 
719
            timestamp, timezone, committer, revprops, revision_id)
717
720
 
718
721
    def get_master_branch(self, possible_transports=None):
719
722
        """Return the branch we are bound to.
994
997
        else:
995
998
            return (0, _mod_revision.NULL_REVISION)
996
999
 
 
1000
    def update_revisions(self, other, stop_revision=None, overwrite=False,
 
1001
                         graph=None, fetch_tags=True):
 
1002
        """Pull in new perfect-fit revisions.
 
1003
 
 
1004
        :param other: Another Branch to pull from
 
1005
        :param stop_revision: Updated until the given revision
 
1006
        :param overwrite: Always set the branch pointer, rather than checking
 
1007
            to see if it is a proper descendant.
 
1008
        :param graph: A Graph object that can be used to query history
 
1009
            information. This can be None.
 
1010
        :param fetch_tags: Flag that specifies if tags from other should be
 
1011
            fetched too.
 
1012
        :return: None
 
1013
        """
 
1014
        return InterBranch.get(other, self).update_revisions(stop_revision,
 
1015
            overwrite, graph, fetch_tags=fetch_tags)
 
1016
 
997
1017
    @deprecated_method(deprecated_in((2, 4, 0)))
998
1018
    def import_last_revision_info(self, source_repo, revno, revid):
999
1019
        """Set the last revision info, importing from another repo if necessary.
1006
1026
            self.repository.fetch(source_repo, revision_id=revid)
1007
1027
        self.set_last_revision_info(revno, revid)
1008
1028
 
1009
 
    def import_last_revision_info_and_tags(self, source, revno, revid,
1010
 
                                           lossy=False):
 
1029
    def import_last_revision_info_and_tags(self, source, revno, revid):
1011
1030
        """Set the last revision info, importing from another repo if necessary.
1012
1031
 
1013
1032
        This is used by the bound branch code to upload a revision to
1017
1036
        :param source: Source branch to optionally fetch from
1018
1037
        :param revno: Revision number of the new tip
1019
1038
        :param revid: Revision id of the new tip
1020
 
        :param lossy: Whether to discard metadata that can not be
1021
 
            natively represented
1022
 
        :return: Tuple with the new revision number and revision id
1023
 
            (should only be different from the arguments when lossy=True)
1024
1039
        """
1025
1040
        if not self.repository.has_same_location(source.repository):
1026
 
            self.fetch(source, revid)
 
1041
            try:
 
1042
                tags_to_fetch = set(source.tags.get_reverse_tag_dict())
 
1043
            except errors.TagsNotSupported:
 
1044
                tags_to_fetch = set()
 
1045
            fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
1046
                source.repository, [revid],
 
1047
                if_present_ids=tags_to_fetch).execute()
 
1048
            self.repository.fetch(source.repository, fetch_spec=fetch_spec)
1027
1049
        self.set_last_revision_info(revno, revid)
1028
 
        return (revno, revid)
1029
1050
 
1030
1051
    def revision_id_to_revno(self, revision_id):
1031
1052
        """Given a revision id, return its revno"""
1624
1645
        for hook in hooks:
1625
1646
            hook(params)
1626
1647
 
 
1648
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1649
                           repository=None):
 
1650
        """Initialize a branch in a bzrdir, with specified files
 
1651
 
 
1652
        :param a_bzrdir: The bzrdir to initialize the branch in
 
1653
        :param utf8_files: The files to create as a list of
 
1654
            (filename, content) tuples
 
1655
        :param name: Name of colocated branch to create, if any
 
1656
        :return: a branch in this format
 
1657
        """
 
1658
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1659
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
1660
        control_files = lockable_files.LockableFiles(branch_transport,
 
1661
            'lock', lockdir.LockDir)
 
1662
        control_files.create_lock()
 
1663
        control_files.lock_write()
 
1664
        try:
 
1665
            utf8_files += [('format', self.get_format_string())]
 
1666
            for (filename, content) in utf8_files:
 
1667
                branch_transport.put_bytes(
 
1668
                    filename, content,
 
1669
                    mode=a_bzrdir._get_file_mode())
 
1670
        finally:
 
1671
            control_files.unlock()
 
1672
        branch = self.open(a_bzrdir, name, _found=True,
 
1673
                found_repository=repository)
 
1674
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1675
        return branch
 
1676
 
1627
1677
    def initialize(self, a_bzrdir, name=None, repository=None):
1628
1678
        """Create a branch of this format in a_bzrdir.
1629
1679
        
1960
2010
        """What class to instantiate on open calls."""
1961
2011
        raise NotImplementedError(self._branch_class)
1962
2012
 
1963
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1964
 
                           repository=None):
1965
 
        """Initialize a branch in a bzrdir, with specified files
1966
 
 
1967
 
        :param a_bzrdir: The bzrdir to initialize the branch in
1968
 
        :param utf8_files: The files to create as a list of
1969
 
            (filename, content) tuples
1970
 
        :param name: Name of colocated branch to create, if any
1971
 
        :return: a branch in this format
1972
 
        """
1973
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1974
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1975
 
        control_files = lockable_files.LockableFiles(branch_transport,
1976
 
            'lock', lockdir.LockDir)
1977
 
        control_files.create_lock()
1978
 
        control_files.lock_write()
1979
 
        try:
1980
 
            utf8_files += [('format', self.get_format_string())]
1981
 
            for (filename, content) in utf8_files:
1982
 
                branch_transport.put_bytes(
1983
 
                    filename, content,
1984
 
                    mode=a_bzrdir._get_file_mode())
1985
 
        finally:
1986
 
            control_files.unlock()
1987
 
        branch = self.open(a_bzrdir, name, _found=True,
1988
 
                found_repository=repository)
1989
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1990
 
        return branch
1991
 
 
1992
2013
    def network_name(self):
1993
2014
        """A simple byte string uniquely identifying this format for RPC calls.
1994
2015
 
2127
2148
                      ]
2128
2149
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2129
2150
 
 
2151
    def __init__(self):
 
2152
        super(BzrBranchFormat8, self).__init__()
 
2153
        self._matchingbzrdir.repository_format = \
 
2154
            RepositoryFormatKnitPack5RichRoot()
 
2155
 
2130
2156
    def make_tags(self, branch):
2131
2157
        """See bzrlib.branch.BranchFormat.make_tags()."""
2132
2158
        return BasicTags(branch)
2140
2166
    supports_reference_locations = True
2141
2167
 
2142
2168
 
2143
 
class BzrBranchFormat7(BranchFormatMetadir):
 
2169
class BzrBranchFormat7(BzrBranchFormat8):
2144
2170
    """Branch format with last-revision, tags, and a stacked location pointer.
2145
2171
 
2146
2172
    The stacked location pointer is passed down to the repository and requires
2171
2197
    def supports_set_append_revisions_only(self):
2172
2198
        return True
2173
2199
 
2174
 
    def supports_stacking(self):
2175
 
        return True
2176
 
 
2177
 
    def make_tags(self, branch):
2178
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
2179
 
        return BasicTags(branch)
2180
 
 
2181
2200
    supports_reference_locations = False
2182
2201
 
2183
2202
 
2482
2501
            'revision-history', '\n'.join(history),
2483
2502
            mode=self.bzrdir._get_file_mode())
2484
2503
 
2485
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
2504
    @needs_write_lock
2486
2505
    def set_revision_history(self, rev_history):
2487
2506
        """See Branch.set_revision_history."""
2488
 
        self._set_revision_history(rev_history)
2489
 
 
2490
 
    @needs_write_lock
2491
 
    def _set_revision_history(self, rev_history):
2492
2507
        if 'evil' in debug.debug_flags:
2493
2508
            mutter_callsite(3, "set_revision_history scales with history.")
2494
2509
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2538
2553
            except ValueError:
2539
2554
                rev = self.repository.get_revision(revision_id)
2540
2555
                new_history = rev.get_history(self.repository)[1:]
2541
 
        destination._set_revision_history(new_history)
 
2556
        destination.set_revision_history(new_history)
2542
2557
 
2543
2558
    @needs_write_lock
2544
2559
    def set_last_revision_info(self, revno, revision_id):
2552
2567
        configured to check constraints on history, in which case this may not
2553
2568
        be permitted.
2554
2569
        """
2555
 
        if not revision_id or not isinstance(revision_id, basestring):
2556
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2570
        revision_id = _mod_revision.ensure_null(revision_id)
2557
2571
        # this old format stores the full history, but this api doesn't
2558
2572
        # provide it, so we must generate, and might as well check it's
2559
2573
        # correct
2560
2574
        history = self._lefthand_history(revision_id)
2561
2575
        if len(history) != revno:
2562
2576
            raise AssertionError('%d != %d' % (len(history), revno))
2563
 
        self._set_revision_history(history)
 
2577
        self.set_revision_history(history)
2564
2578
 
2565
2579
    def _gen_revision_history(self):
2566
2580
        history = self._transport.get_bytes('revision-history').split('\n')
2580
2594
        :param other_branch: The other branch that DivergedBranches should
2581
2595
            raise with respect to.
2582
2596
        """
2583
 
        self._set_revision_history(self._lefthand_history(revision_id,
 
2597
        self.set_revision_history(self._lefthand_history(revision_id,
2584
2598
            last_rev, other_branch))
2585
2599
 
2586
2600
    def basis_tree(self):
2596
2610
                pass
2597
2611
        return None
2598
2612
 
 
2613
    def _basic_push(self, target, overwrite, stop_revision):
 
2614
        """Basic implementation of push without bound branches or hooks.
 
2615
 
 
2616
        Must be called with source read locked and target write locked.
 
2617
        """
 
2618
        result = BranchPushResult()
 
2619
        result.source_branch = self
 
2620
        result.target_branch = target
 
2621
        result.old_revno, result.old_revid = target.last_revision_info()
 
2622
        self.update_references(target)
 
2623
        if result.old_revid != stop_revision:
 
2624
            # We assume that during 'push' this repository is closer than
 
2625
            # the target.
 
2626
            graph = self.repository.get_graph(target.repository)
 
2627
            target.update_revisions(self, stop_revision,
 
2628
                overwrite=overwrite, graph=graph)
 
2629
        if self._push_should_merge_tags():
 
2630
            result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
 
2631
        result.new_revno, result.new_revid = target.last_revision_info()
 
2632
        return result
 
2633
 
2599
2634
    def get_stacked_on_url(self):
2600
2635
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2601
2636
 
2768
2803
 
2769
2804
    @needs_write_lock
2770
2805
    def set_last_revision_info(self, revno, revision_id):
2771
 
        if not revision_id or not isinstance(revision_id, basestring):
2772
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2806
        revision_id = _mod_revision.ensure_null(revision_id)
2773
2807
        old_revno, old_revid = self.last_revision_info()
2774
2808
        if self._get_append_revisions_only():
2775
2809
            self._check_history_violation(revision_id)
3251
3285
        raise NotImplementedError(self.pull)
3252
3286
 
3253
3287
    @needs_write_lock
 
3288
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3289
                         graph=None, fetch_tags=True):
 
3290
        """Pull in new perfect-fit revisions.
 
3291
 
 
3292
        :param stop_revision: Updated until the given revision
 
3293
        :param overwrite: Always set the branch pointer, rather than checking
 
3294
            to see if it is a proper descendant.
 
3295
        :param graph: A Graph object that can be used to query history
 
3296
            information. This can be None.
 
3297
        :param fetch_tags: Flag that specifies if tags from source should be
 
3298
            fetched too.
 
3299
        :return: None
 
3300
        """
 
3301
        raise NotImplementedError(self.update_revisions)
 
3302
 
 
3303
    @needs_write_lock
3254
3304
    def push(self, overwrite=False, stop_revision=None,
3255
3305
             _override_hook_source_branch=None):
3256
3306
        """Mirror the source branch into the target branch.
3269
3319
        raise NotImplementedError(self.copy_content_into)
3270
3320
 
3271
3321
    @needs_write_lock
3272
 
    def fetch(self, stop_revision=None):
 
3322
    def fetch(self, stop_revision=None, fetch_spec=None):
3273
3323
        """Fetch revisions.
3274
3324
 
3275
3325
        :param stop_revision: Last revision to fetch
 
3326
        :param fetch_spec: Fetch spec.
3276
3327
        """
3277
3328
        raise NotImplementedError(self.fetch)
3278
3329
 
3316
3367
            self.source.tags.merge_to(self.target.tags)
3317
3368
 
3318
3369
    @needs_write_lock
3319
 
    def fetch(self, stop_revision=None):
 
3370
    def fetch(self, stop_revision=None, fetch_spec=None):
 
3371
        if fetch_spec is not None and stop_revision is not None:
 
3372
            raise AssertionError(
 
3373
                "fetch_spec and last_revision are mutually exclusive.")
3320
3374
        if self.target.base == self.source.base:
3321
3375
            return (0, [])
3322
3376
        self.source.lock_read()
3323
3377
        try:
3324
 
            fetch_spec_factory = fetch.FetchSpecFactory()
3325
 
            fetch_spec_factory.source_branch = self.source
3326
 
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3327
 
            fetch_spec_factory.source_repo = self.source.repository
3328
 
            fetch_spec_factory.target_repo = self.target.repository
3329
 
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3330
 
            fetch_spec = fetch_spec_factory.make_fetch_spec()
 
3378
            if stop_revision is None and fetch_spec is None:
 
3379
                stop_revision = self.source.last_revision()
 
3380
                stop_revision = _mod_revision.ensure_null(stop_revision)
3331
3381
            return self.target.repository.fetch(self.source.repository,
3332
 
                fetch_spec=fetch_spec)
 
3382
                revision_id=stop_revision, fetch_spec=fetch_spec)
3333
3383
        finally:
3334
3384
            self.source.unlock()
3335
3385
 
3336
3386
    @needs_write_lock
3337
 
    def _update_revisions(self, stop_revision=None, overwrite=False,
3338
 
            graph=None):
 
3387
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3388
        graph=None, fetch_tags=True):
 
3389
        """See InterBranch.update_revisions()."""
3339
3390
        other_revno, other_last_revision = self.source.last_revision_info()
3340
3391
        stop_revno = None # unknown
3341
3392
        if stop_revision is None:
3352
3403
        # case of having something to pull, and so that the check for
3353
3404
        # already merged can operate on the just fetched graph, which will
3354
3405
        # be cached in memory.
3355
 
        self.fetch(stop_revision=stop_revision)
 
3406
        if fetch_tags:
 
3407
            fetch_spec_factory = fetch.FetchSpecFactory()
 
3408
            fetch_spec_factory.source_branch = self.source
 
3409
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
 
3410
            fetch_spec_factory.source_repo = self.source.repository
 
3411
            fetch_spec_factory.target_repo = self.target.repository
 
3412
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
 
3413
            fetch_spec = fetch_spec_factory.make_fetch_spec()
 
3414
        else:
 
3415
            fetch_spec = _mod_graph.NotInOtherForRevs(self.target.repository,
 
3416
                self.source.repository, revision_ids=[stop_revision]).execute()
 
3417
        self.target.fetch(self.source, fetch_spec=fetch_spec)
3356
3418
        # Check to see if one is an ancestor of the other
3357
3419
        if not overwrite:
3358
3420
            if graph is None:
3427
3489
        finally:
3428
3490
            self.source.unlock()
3429
3491
 
3430
 
    def _basic_push(self, overwrite, stop_revision):
3431
 
        """Basic implementation of push without bound branches or hooks.
3432
 
 
3433
 
        Must be called with source read locked and target write locked.
3434
 
        """
3435
 
        result = BranchPushResult()
3436
 
        result.source_branch = self.source
3437
 
        result.target_branch = self.target
3438
 
        result.old_revno, result.old_revid = self.target.last_revision_info()
3439
 
        self.source.update_references(self.target)
3440
 
        if result.old_revid != stop_revision:
3441
 
            # We assume that during 'push' this repository is closer than
3442
 
            # the target.
3443
 
            graph = self.source.repository.get_graph(self.target.repository)
3444
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3445
 
                    graph=graph)
3446
 
        if self.source._push_should_merge_tags():
3447
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3448
 
                overwrite)
3449
 
        result.new_revno, result.new_revid = self.target.last_revision_info()
3450
 
        return result
3451
 
 
3452
3492
    def _push_with_bound_branches(self, overwrite, stop_revision,
3453
3493
            _override_hook_source_branch=None):
3454
3494
        """Push from source into target, and into target's master if any.
3469
3509
            master_branch.lock_write()
3470
3510
            try:
3471
3511
                # push into the master from the source branch.
3472
 
                master_inter = InterBranch.get(self.source, master_branch)
3473
 
                master_inter._basic_push(overwrite, stop_revision)
3474
 
                # and push into the target branch from the source. Note that
3475
 
                # we push from the source branch again, because it's considered
3476
 
                # the highest bandwidth repository.
3477
 
                result = self._basic_push(overwrite, stop_revision)
 
3512
                self.source._basic_push(master_branch, overwrite, stop_revision)
 
3513
                # and push into the target branch from the source. Note that we
 
3514
                # push from the source branch again, because it's considered the
 
3515
                # highest bandwidth repository.
 
3516
                result = self.source._basic_push(self.target, overwrite,
 
3517
                    stop_revision)
3478
3518
                result.master_branch = master_branch
3479
3519
                result.local_branch = self.target
3480
3520
                _run_hooks()
3483
3523
                master_branch.unlock()
3484
3524
        else:
3485
3525
            # no master branch
3486
 
            result = self._basic_push(overwrite, stop_revision)
 
3526
            result = self.source._basic_push(self.target, overwrite,
 
3527
                stop_revision)
3487
3528
            # TODO: Why set master_branch and local_branch if there's no
3488
3529
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3489
3530
            # 20070504
3532
3573
            # -- JRV20090506
3533
3574
            result.old_revno, result.old_revid = \
3534
3575
                self.target.last_revision_info()
3535
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3536
 
                graph=graph)
 
3576
            self.target.update_revisions(self.source, stop_revision,
 
3577
                overwrite=overwrite, graph=graph)
3537
3578
            # TODO: The old revid should be specified when merging tags, 
3538
3579
            # so a tags implementation that versions tags can only 
3539
3580
            # pull in the most recent changes. -- JRV20090506