~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Vincent Ladeuil
  • Date: 2012-03-09 16:48:55 UTC
  • mto: (6437.23.24 2.5)
  • mto: This revision was merged to the branch mainline in revision 6499.
  • Revision ID: v.ladeuil+lp@free.fr-20120309164855-htdn25hp7x65mmir
Rely on sphinx for texinfo doc generation

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2012 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
766
766
        """Print `file` to stdout."""
767
767
        raise NotImplementedError(self.print_file)
768
768
 
 
769
    @deprecated_method(deprecated_in((2, 4, 0)))
 
770
    def set_revision_history(self, rev_history):
 
771
        """See Branch.set_revision_history."""
 
772
        self._set_revision_history(rev_history)
 
773
 
 
774
    @needs_write_lock
 
775
    def _set_revision_history(self, rev_history):
 
776
        if len(rev_history) == 0:
 
777
            revid = _mod_revision.NULL_REVISION
 
778
        else:
 
779
            revid = rev_history[-1]
 
780
        if rev_history != self._lefthand_history(revid):
 
781
            raise errors.NotLefthandHistory(rev_history)
 
782
        self.set_last_revision_info(len(rev_history), revid)
 
783
        self._cache_revision_history(rev_history)
 
784
        for hook in Branch.hooks['set_rh']:
 
785
            hook(self, rev_history)
 
786
 
769
787
    @needs_write_lock
770
788
    def set_last_revision_info(self, revno, revision_id):
771
789
        """Set the last revision of this branch.
968
986
        This means the next call to revision_history will need to call
969
987
        _gen_revision_history.
970
988
 
971
 
        This API is semi-public; it is only for use by subclasses, all other
972
 
        code should consider it to be private.
 
989
        This API is semi-public; it only for use by subclasses, all other code
 
990
        should consider it to be private.
973
991
        """
974
992
        self._revision_history_cache = None
975
993
        self._revision_id_to_revno_cache = None
995
1013
        """
996
1014
        raise NotImplementedError(self._gen_revision_history)
997
1015
 
 
1016
    @deprecated_method(deprecated_in((2, 5, 0)))
 
1017
    @needs_read_lock
 
1018
    def revision_history(self):
 
1019
        """Return sequence of revision ids on this branch.
 
1020
 
 
1021
        This method will cache the revision history for as long as it is safe to
 
1022
        do so.
 
1023
        """
 
1024
        return self._revision_history()
 
1025
 
998
1026
    def _revision_history(self):
999
1027
        if 'evil' in debug.debug_flags:
1000
1028
            mutter_callsite(3, "revision_history scales with history.")
1034
1062
    def _read_last_revision_info(self):
1035
1063
        raise NotImplementedError(self._read_last_revision_info)
1036
1064
 
 
1065
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1066
    def import_last_revision_info(self, source_repo, revno, revid):
 
1067
        """Set the last revision info, importing from another repo if necessary.
 
1068
 
 
1069
        :param source_repo: Source repository to optionally fetch from
 
1070
        :param revno: Revision number of the new tip
 
1071
        :param revid: Revision id of the new tip
 
1072
        """
 
1073
        if not self.repository.has_same_location(source_repo):
 
1074
            self.repository.fetch(source_repo, revision_id=revid)
 
1075
        self.set_last_revision_info(revno, revid)
 
1076
 
1037
1077
    def import_last_revision_info_and_tags(self, source, revno, revid,
1038
1078
                                           lossy=False):
1039
1079
        """Set the last revision info, importing from another repo if necessary.
1577
1617
    def __ne__(self, other):
1578
1618
        return not (self == other)
1579
1619
 
 
1620
    @classmethod
 
1621
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1622
    def get_default_format(klass):
 
1623
        """Return the current default format."""
 
1624
        return format_registry.get_default()
 
1625
 
 
1626
    @classmethod
 
1627
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1628
    def get_formats(klass):
 
1629
        """Get all the known formats.
 
1630
 
 
1631
        Warning: This triggers a load of all lazy registered formats: do not
 
1632
        use except when that is desireed.
 
1633
        """
 
1634
        return format_registry._get_all()
 
1635
 
1580
1636
    def get_reference(self, controldir, name=None):
1581
1637
        """Get the target reference of the branch in controldir.
1582
1638
 
1670
1726
        """
1671
1727
        raise NotImplementedError(self.open)
1672
1728
 
 
1729
    @classmethod
 
1730
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1731
    def register_format(klass, format):
 
1732
        """Register a metadir format.
 
1733
 
 
1734
        See MetaDirBranchFormatFactory for the ability to register a format
 
1735
        without loading the code the format needs until it is actually used.
 
1736
        """
 
1737
        format_registry.register(format)
 
1738
 
 
1739
    @classmethod
 
1740
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1741
    def set_default_format(klass, format):
 
1742
        format_registry.set_default(format)
 
1743
 
1673
1744
    def supports_set_append_revisions_only(self):
1674
1745
        """True if this format supports set_append_revisions_only."""
1675
1746
        return False
1682
1753
        """True if this format supports leaving locks in place."""
1683
1754
        return False # by default
1684
1755
 
 
1756
    @classmethod
 
1757
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1758
    def unregister_format(klass, format):
 
1759
        format_registry.remove(format)
 
1760
 
1685
1761
    def __str__(self):
1686
1762
        return self.get_format_description().rstrip()
1687
1763
 
1729
1805
class BranchHooks(Hooks):
1730
1806
    """A dictionary mapping hook name to a list of callables for branch hooks.
1731
1807
 
1732
 
    e.g. ['post_push'] Is the list of items to be called when the
1733
 
    push function is invoked.
 
1808
    e.g. ['set_rh'] Is the list of items to be called when the
 
1809
    set_revision_history function is invoked.
1734
1810
    """
1735
1811
 
1736
1812
    def __init__(self):
1740
1816
        notified.
1741
1817
        """
1742
1818
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
 
1819
        self.add_hook('set_rh',
 
1820
            "Invoked whenever the revision history has been set via "
 
1821
            "set_revision_history. The api signature is (branch, "
 
1822
            "revision_history), and the branch will be write-locked. "
 
1823
            "The set_rh hook can be expensive for bzr to trigger, a better "
 
1824
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
1743
1825
        self.add_hook('open',
1744
1826
            "Called with the Branch object that has been opened after a "
1745
1827
            "branch is opened.", (1, 8))
1963
2045
 
1964
2046
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1965
2047
                           repository=None):
1966
 
        """Initialize a branch in a control dir, with specified files
 
2048
        """Initialize a branch in a bzrdir, with specified files
1967
2049
 
1968
2050
        :param a_bzrdir: The bzrdir to initialize the branch in
1969
2051
        :param utf8_files: The files to create as a list of
2039
2121
            recommend_upgrade=recommend_upgrade, basedir=basedir)
2040
2122
 
2041
2123
 
 
2124
class BzrBranchFormat5(BranchFormatMetadir):
 
2125
    """Bzr branch format 5.
 
2126
 
 
2127
    This format has:
 
2128
     - a revision-history file.
 
2129
     - a format string
 
2130
     - a lock dir guarding the branch itself
 
2131
     - all of this stored in a branch/ subdirectory
 
2132
     - works with shared repositories.
 
2133
 
 
2134
    This format is new in bzr 0.8.
 
2135
    """
 
2136
 
 
2137
    def _branch_class(self):
 
2138
        return BzrBranch5
 
2139
 
 
2140
    @classmethod
 
2141
    def get_format_string(cls):
 
2142
        """See BranchFormat.get_format_string()."""
 
2143
        return "Bazaar-NG branch format 5\n"
 
2144
 
 
2145
    def get_format_description(self):
 
2146
        """See BranchFormat.get_format_description()."""
 
2147
        return "Branch format 5"
 
2148
 
 
2149
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2150
                   append_revisions_only=None):
 
2151
        """Create a branch of this format in a_bzrdir."""
 
2152
        if append_revisions_only:
 
2153
            raise errors.UpgradeRequired(a_bzrdir.user_url)
 
2154
        utf8_files = [('revision-history', ''),
 
2155
                      ('branch-name', ''),
 
2156
                      ]
 
2157
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2158
 
 
2159
    def supports_tags(self):
 
2160
        return False
 
2161
 
 
2162
 
2042
2163
class BzrBranchFormat6(BranchFormatMetadir):
2043
2164
    """Branch format with last-revision and tags.
2044
2165
 
2293
2414
 
2294
2415
# formats which have no format string are not discoverable
2295
2416
# and not independently creatable, so are not registered.
 
2417
__format5 = BzrBranchFormat5()
2296
2418
__format6 = BzrBranchFormat6()
2297
2419
__format7 = BzrBranchFormat7()
2298
2420
__format8 = BzrBranchFormat8()
2299
 
format_registry.register_lazy(
2300
 
    "Bazaar-NG branch format 5\n", "bzrlib.branchfmt.fullhistory", "BzrBranchFormat5")
 
2421
format_registry.register(__format5)
2301
2422
format_registry.register(BranchReferenceFormat())
2302
2423
format_registry.register(__format6)
2303
2424
format_registry.register(__format7)
2360
2481
        self.control_files = _control_files
2361
2482
        self._transport = _control_files._transport
2362
2483
        self.repository = _repository
2363
 
        self.conf_store = None
2364
2484
        Branch.__init__(self, possible_transports)
2365
2485
 
2366
2486
    def __str__(self):
2382
2502
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2383
2503
 
2384
2504
    def _get_config_store(self):
2385
 
        if self.conf_store is None:
2386
 
            self.conf_store =  _mod_config.BranchStore(self)
2387
 
        return self.conf_store
 
2505
        return _mod_config.BranchStore(self)
2388
2506
 
2389
2507
    def is_locked(self):
2390
2508
        return self.control_files.is_locked()
2398
2516
        """
2399
2517
        if not self.is_locked():
2400
2518
            self._note_lock('w')
 
2519
        # All-in-one needs to always unlock/lock.
 
2520
        repo_control = getattr(self.repository, 'control_files', None)
 
2521
        if self.control_files == repo_control or not self.is_locked():
2401
2522
            self.repository._warn_if_deprecated(self)
2402
2523
            self.repository.lock_write()
2403
2524
            took_lock = True
2418
2539
        """
2419
2540
        if not self.is_locked():
2420
2541
            self._note_lock('r')
 
2542
        # All-in-one needs to always unlock/lock.
 
2543
        repo_control = getattr(self.repository, 'control_files', None)
 
2544
        if self.control_files == repo_control or not self.is_locked():
2421
2545
            self.repository._warn_if_deprecated(self)
2422
2546
            self.repository.lock_read()
2423
2547
            took_lock = True
2433
2557
 
2434
2558
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2435
2559
    def unlock(self):
2436
 
        if self.control_files._lock_count == 1 and self.conf_store is not None:
2437
 
            self.conf_store.save_changes()
2438
2560
        try:
2439
2561
            self.control_files.unlock()
2440
2562
        finally:
 
2563
            # All-in-one needs to always unlock/lock.
 
2564
            repo_control = getattr(self.repository, 'control_files', None)
 
2565
            if (self.control_files == repo_control or
 
2566
                not self.control_files.is_locked()):
 
2567
                self.repository.unlock()
2441
2568
            if not self.control_files.is_locked():
2442
 
                self.repository.unlock()
2443
2569
                # we just released the lock
2444
2570
                self._clear_cached_state()
2445
2571
 
2623
2749
        self.control_transport.put_bytes('format', self._format.as_string())
2624
2750
 
2625
2751
 
 
2752
class FullHistoryBzrBranch(BzrBranch):
 
2753
    """Bzr branch which contains the full revision history."""
 
2754
 
 
2755
    @needs_write_lock
 
2756
    def set_last_revision_info(self, revno, revision_id):
 
2757
        if not revision_id or not isinstance(revision_id, basestring):
 
2758
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2759
        revision_id = _mod_revision.ensure_null(revision_id)
 
2760
        # this old format stores the full history, but this api doesn't
 
2761
        # provide it, so we must generate, and might as well check it's
 
2762
        # correct
 
2763
        history = self._lefthand_history(revision_id)
 
2764
        if len(history) != revno:
 
2765
            raise AssertionError('%d != %d' % (len(history), revno))
 
2766
        self._set_revision_history(history)
 
2767
 
 
2768
    def _read_last_revision_info(self):
 
2769
        rh = self._revision_history()
 
2770
        revno = len(rh)
 
2771
        if revno:
 
2772
            return (revno, rh[-1])
 
2773
        else:
 
2774
            return (0, _mod_revision.NULL_REVISION)
 
2775
 
 
2776
    @deprecated_method(deprecated_in((2, 4, 0)))
 
2777
    @needs_write_lock
 
2778
    def set_revision_history(self, rev_history):
 
2779
        """See Branch.set_revision_history."""
 
2780
        self._set_revision_history(rev_history)
 
2781
 
 
2782
    def _set_revision_history(self, rev_history):
 
2783
        if 'evil' in debug.debug_flags:
 
2784
            mutter_callsite(3, "set_revision_history scales with history.")
 
2785
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2786
        for rev_id in rev_history:
 
2787
            check_not_reserved_id(rev_id)
 
2788
        if Branch.hooks['post_change_branch_tip']:
 
2789
            # Don't calculate the last_revision_info() if there are no hooks
 
2790
            # that will use it.
 
2791
            old_revno, old_revid = self.last_revision_info()
 
2792
        if len(rev_history) == 0:
 
2793
            revid = _mod_revision.NULL_REVISION
 
2794
        else:
 
2795
            revid = rev_history[-1]
 
2796
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2797
        self._write_revision_history(rev_history)
 
2798
        self._clear_cached_state()
 
2799
        self._cache_revision_history(rev_history)
 
2800
        for hook in Branch.hooks['set_rh']:
 
2801
            hook(self, rev_history)
 
2802
        if Branch.hooks['post_change_branch_tip']:
 
2803
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2804
 
 
2805
    def _write_revision_history(self, history):
 
2806
        """Factored out of set_revision_history.
 
2807
 
 
2808
        This performs the actual writing to disk.
 
2809
        It is intended to be called by set_revision_history."""
 
2810
        self._transport.put_bytes(
 
2811
            'revision-history', '\n'.join(history),
 
2812
            mode=self.bzrdir._get_file_mode())
 
2813
 
 
2814
    def _gen_revision_history(self):
 
2815
        history = self._transport.get_bytes('revision-history').split('\n')
 
2816
        if history[-1:] == ['']:
 
2817
            # There shouldn't be a trailing newline, but just in case.
 
2818
            history.pop()
 
2819
        return history
 
2820
 
 
2821
    def _synchronize_history(self, destination, revision_id):
 
2822
        if not isinstance(destination, FullHistoryBzrBranch):
 
2823
            super(BzrBranch, self)._synchronize_history(
 
2824
                destination, revision_id)
 
2825
            return
 
2826
        if revision_id == _mod_revision.NULL_REVISION:
 
2827
            new_history = []
 
2828
        else:
 
2829
            new_history = self._revision_history()
 
2830
        if revision_id is not None and new_history != []:
 
2831
            try:
 
2832
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2833
            except ValueError:
 
2834
                rev = self.repository.get_revision(revision_id)
 
2835
                new_history = rev.get_history(self.repository)[1:]
 
2836
        destination._set_revision_history(new_history)
 
2837
 
 
2838
    @needs_write_lock
 
2839
    def generate_revision_history(self, revision_id, last_rev=None,
 
2840
        other_branch=None):
 
2841
        """Create a new revision history that will finish with revision_id.
 
2842
 
 
2843
        :param revision_id: the new tip to use.
 
2844
        :param last_rev: The previous last_revision. If not None, then this
 
2845
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2846
        :param other_branch: The other branch that DivergedBranches should
 
2847
            raise with respect to.
 
2848
        """
 
2849
        self._set_revision_history(self._lefthand_history(revision_id,
 
2850
            last_rev, other_branch))
 
2851
 
 
2852
 
 
2853
class BzrBranch5(FullHistoryBzrBranch):
 
2854
    """A format 5 branch. This supports new features over plain branches.
 
2855
 
 
2856
    It has support for a master_branch which is the data for bound branches.
 
2857
    """
 
2858
 
 
2859
 
2626
2860
class BzrBranch8(BzrBranch):
2627
2861
    """A branch that stores tree-reference locations."""
2628
2862
 
2908
3142
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
2909
3143
    """
2910
3144
 
 
3145
    @deprecated_method(deprecated_in((2, 3, 0)))
 
3146
    def __int__(self):
 
3147
        """Return the relative change in revno.
 
3148
 
 
3149
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3150
        """
 
3151
        return self.new_revno - self.old_revno
 
3152
 
2911
3153
    def report(self, to_file):
2912
3154
        tag_conflicts = getattr(self, "tag_conflicts", None)
2913
3155
        tag_updates = getattr(self, "tag_updates", None)
2943
3185
        target, otherwise it will be None.
2944
3186
    """
2945
3187
 
 
3188
    @deprecated_method(deprecated_in((2, 3, 0)))
 
3189
    def __int__(self):
 
3190
        """Return the relative change in revno.
 
3191
 
 
3192
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3193
        """
 
3194
        return self.new_revno - self.old_revno
 
3195
 
2946
3196
    def report(self, to_file):
2947
3197
        # TODO: This function gets passed a to_file, but then
2948
3198
        # ignores it and calls note() instead. This is also
2995
3245
 
2996
3246
        # Copy source data into target
2997
3247
        new_branch._write_last_revision_info(*branch.last_revision_info())
2998
 
        new_branch.lock_write()
2999
 
        try:
3000
 
            new_branch.set_parent(branch.get_parent())
3001
 
            new_branch.set_bound_location(branch.get_bound_location())
3002
 
            new_branch.set_push_location(branch.get_push_location())
3003
 
        finally:
3004
 
            new_branch.unlock()
 
3248
        new_branch.set_parent(branch.get_parent())
 
3249
        new_branch.set_bound_location(branch.get_bound_location())
 
3250
        new_branch.set_push_location(branch.get_push_location())
3005
3251
 
3006
3252
        # New branch has no tags by default
3007
3253
        new_branch.tags._set_tag_dict({})
3013
3259
 
3014
3260
        # Clean up old files
3015
3261
        new_branch._transport.delete('revision-history')
3016
 
        branch.lock_write()
3017
3262
        try:
3018
 
            try:
3019
 
                branch.set_parent(None)
3020
 
            except errors.NoSuchFile:
3021
 
                pass
3022
 
            branch.set_bound_location(None)
3023
 
        finally:
3024
 
            branch.unlock()
 
3263
            branch.set_parent(None)
 
3264
        except errors.NoSuchFile:
 
3265
            pass
 
3266
        branch.set_bound_location(None)
3025
3267
 
3026
3268
 
3027
3269
class Converter6to7(object):
3104
3346
        raise NotImplementedError(self.fetch)
3105
3347
 
3106
3348
 
3107
 
def _fix_overwrite_type(overwrite):
3108
 
    if isinstance(overwrite, bool):
3109
 
        if overwrite:
3110
 
            return ["history", "tags"]
3111
 
        else:
3112
 
            return []
3113
 
    return overwrite
3114
 
 
3115
 
 
3116
3349
class GenericInterBranch(InterBranch):
3117
3350
    """InterBranch implementation that uses public Branch functions."""
3118
3351
 
3283
3516
        result.target_branch = self.target
3284
3517
        result.old_revno, result.old_revid = self.target.last_revision_info()
3285
3518
        self.source.update_references(self.target)
3286
 
        overwrite = _fix_overwrite_type(overwrite)
3287
3519
        if result.old_revid != stop_revision:
3288
3520
            # We assume that during 'push' this repository is closer than
3289
3521
            # the target.
3290
3522
            graph = self.source.repository.get_graph(self.target.repository)
3291
 
            self._update_revisions(stop_revision,
3292
 
                overwrite=("history" in overwrite),
3293
 
                graph=graph)
 
3523
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3524
                    graph=graph)
3294
3525
        if self.source._push_should_merge_tags():
3295
3526
            result.tag_updates, result.tag_conflicts = (
3296
 
                self.source.tags.merge_to(
3297
 
                self.target.tags, "tags" in overwrite))
 
3527
                self.source.tags.merge_to(self.target.tags, overwrite))
3298
3528
        result.new_revno, result.new_revid = self.target.last_revision_info()
3299
3529
        return result
3300
3530
 
3378
3608
            # -- JRV20090506
3379
3609
            result.old_revno, result.old_revid = \
3380
3610
                self.target.last_revision_info()
3381
 
            overwrite = _fix_overwrite_type(overwrite)
3382
 
            self._update_revisions(stop_revision,
3383
 
                overwrite=("history" in overwrite),
 
3611
            self._update_revisions(stop_revision, overwrite=overwrite,
3384
3612
                graph=graph)
3385
3613
            # TODO: The old revid should be specified when merging tags, 
3386
3614
            # so a tags implementation that versions tags can only 
3387
3615
            # pull in the most recent changes. -- JRV20090506
3388
3616
            result.tag_updates, result.tag_conflicts = (
3389
 
                self.source.tags.merge_to(self.target.tags,
3390
 
                    "tags" in overwrite,
 
3617
                self.source.tags.merge_to(self.target.tags, overwrite,
3391
3618
                    ignore_master=not merge_tags_to_master))
3392
3619
            result.new_revno, result.new_revid = self.target.last_revision_info()
3393
3620
            if _hook_master: