~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Merge bzr.dev.

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
46
45
from bzrlib.tag import (
47
46
    BasicTags,
48
47
    DisabledTags,
57
56
    needs_write_lock,
58
57
    only_raises,
59
58
    )
60
 
from bzrlib.hooks import HookPoint, Hooks
 
59
from bzrlib.hooks import Hooks
61
60
from bzrlib.inter import InterObject
62
61
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
63
62
from bzrlib import registry
695
694
 
696
695
    def get_commit_builder(self, parents, config=None, timestamp=None,
697
696
                           timezone=None, committer=None, revprops=None,
698
 
                           revision_id=None):
 
697
                           revision_id=None, lossy=False):
699
698
        """Obtain a CommitBuilder for this branch.
700
699
 
701
700
        :param parents: Revision ids of the parents of the new revision.
705
704
        :param committer: Optional committer to set for commit.
706
705
        :param revprops: Optional dictionary of revision properties.
707
706
        :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 
708
709
        """
709
710
 
710
711
        if config is None:
711
712
            config = self.get_config()
712
713
 
713
714
        return self.repository.get_commit_builder(self, parents, config,
714
 
            timestamp, timezone, committer, revprops, revision_id)
 
715
            timestamp, timezone, committer, revprops, revision_id,
 
716
            lossy)
715
717
 
716
718
    def get_master_branch(self, possible_transports=None):
717
719
        """Return the branch we are bound to.
1019
1021
            self.repository.fetch(source_repo, revision_id=revid)
1020
1022
        self.set_last_revision_info(revno, revid)
1021
1023
 
1022
 
    def import_last_revision_info_and_tags(self, source, revno, revid):
 
1024
    def import_last_revision_info_and_tags(self, source, revno, revid,
 
1025
                                           lossy=False):
1023
1026
        """Set the last revision info, importing from another repo if necessary.
1024
1027
 
1025
1028
        This is used by the bound branch code to upload a revision to
1029
1032
        :param source: Source branch to optionally fetch from
1030
1033
        :param revno: Revision number of the new tip
1031
1034
        :param revid: Revision id of the new tip
 
1035
        :param lossy: Whether to discard metadata that can not be
 
1036
            natively represented
 
1037
        :return: Tuple with the new revision number and revision id
 
1038
            (should only be different from the arguments when lossy=True)
1032
1039
        """
1033
1040
        if not self.repository.has_same_location(source.repository):
1034
1041
            self.fetch(source, revid)
1035
1042
        self.set_last_revision_info(revno, revid)
 
1043
        return (revno, revid)
1036
1044
 
1037
1045
    def revision_id_to_revno(self, revision_id):
1038
1046
        """Given a revision id, return its revno"""
1635
1643
        for hook in hooks:
1636
1644
            hook(params)
1637
1645
 
1638
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1639
 
                           repository=None):
1640
 
        """Initialize a branch in a bzrdir, with specified files
1641
 
 
1642
 
        :param a_bzrdir: The bzrdir to initialize the branch in
1643
 
        :param utf8_files: The files to create as a list of
1644
 
            (filename, content) tuples
1645
 
        :param name: Name of colocated branch to create, if any
1646
 
        :return: a branch in this format
1647
 
        """
1648
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1649
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1650
 
        control_files = lockable_files.LockableFiles(branch_transport,
1651
 
            'lock', lockdir.LockDir)
1652
 
        control_files.create_lock()
1653
 
        control_files.lock_write()
1654
 
        try:
1655
 
            utf8_files += [('format', self.get_format_string())]
1656
 
            for (filename, content) in utf8_files:
1657
 
                branch_transport.put_bytes(
1658
 
                    filename, content,
1659
 
                    mode=a_bzrdir._get_file_mode())
1660
 
        finally:
1661
 
            control_files.unlock()
1662
 
        branch = self.open(a_bzrdir, name, _found=True,
1663
 
                found_repository=repository)
1664
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1665
 
        return branch
1666
 
 
1667
1646
    def initialize(self, a_bzrdir, name=None, repository=None):
1668
1647
        """Create a branch of this format in a_bzrdir.
1669
1648
        
1798
1777
        These are all empty initially, because by default nothing should get
1799
1778
        notified.
1800
1779
        """
1801
 
        Hooks.__init__(self)
1802
 
        self.create_hook(HookPoint('set_rh',
 
1780
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
 
1781
        self.add_hook('set_rh',
1803
1782
            "Invoked whenever the revision history has been set via "
1804
1783
            "set_revision_history. The api signature is (branch, "
1805
1784
            "revision_history), and the branch will be write-locked. "
1806
1785
            "The set_rh hook can be expensive for bzr to trigger, a better "
1807
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1808
 
        self.create_hook(HookPoint('open',
 
1786
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
 
1787
        self.add_hook('open',
1809
1788
            "Called with the Branch object that has been opened after a "
1810
 
            "branch is opened.", (1, 8), None))
1811
 
        self.create_hook(HookPoint('post_push',
 
1789
            "branch is opened.", (1, 8))
 
1790
        self.add_hook('post_push',
1812
1791
            "Called after a push operation completes. post_push is called "
1813
1792
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1814
 
            "bzr client.", (0, 15), None))
1815
 
        self.create_hook(HookPoint('post_pull',
 
1793
            "bzr client.", (0, 15))
 
1794
        self.add_hook('post_pull',
1816
1795
            "Called after a pull operation completes. post_pull is called "
1817
1796
            "with a bzrlib.branch.PullResult object and only runs in the "
1818
 
            "bzr client.", (0, 15), None))
1819
 
        self.create_hook(HookPoint('pre_commit',
 
1797
            "bzr client.", (0, 15))
 
1798
        self.add_hook('pre_commit',
1820
1799
            "Called after a commit is calculated but before it is "
1821
1800
            "completed. pre_commit is called with (local, master, old_revno, "
1822
1801
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1825
1804
            "basis revision. hooks MUST NOT modify this delta. "
1826
1805
            " future_tree is an in-memory tree obtained from "
1827
1806
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1828
 
            "tree.", (0,91), None))
1829
 
        self.create_hook(HookPoint('post_commit',
 
1807
            "tree.", (0,91))
 
1808
        self.add_hook('post_commit',
1830
1809
            "Called in the bzr client after a commit has completed. "
1831
1810
            "post_commit is called with (local, master, old_revno, old_revid, "
1832
1811
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1833
 
            "commit to a branch.", (0, 15), None))
1834
 
        self.create_hook(HookPoint('post_uncommit',
 
1812
            "commit to a branch.", (0, 15))
 
1813
        self.add_hook('post_uncommit',
1835
1814
            "Called in the bzr client after an uncommit completes. "
1836
1815
            "post_uncommit is called with (local, master, old_revno, "
1837
1816
            "old_revid, new_revno, new_revid) where local is the local branch "
1838
1817
            "or None, master is the target branch, and an empty branch "
1839
 
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
1840
 
        self.create_hook(HookPoint('pre_change_branch_tip',
 
1818
            "receives new_revno of 0, new_revid of None.", (0, 15))
 
1819
        self.add_hook('pre_change_branch_tip',
1841
1820
            "Called in bzr client and server before a change to the tip of a "
1842
1821
            "branch is made. pre_change_branch_tip is called with a "
1843
1822
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1844
 
            "commit, uncommit will all trigger this hook.", (1, 6), None))
1845
 
        self.create_hook(HookPoint('post_change_branch_tip',
 
1823
            "commit, uncommit will all trigger this hook.", (1, 6))
 
1824
        self.add_hook('post_change_branch_tip',
1846
1825
            "Called in bzr client and server after a change to the tip of a "
1847
1826
            "branch is made. post_change_branch_tip is called with a "
1848
1827
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1849
 
            "commit, uncommit will all trigger this hook.", (1, 4), None))
1850
 
        self.create_hook(HookPoint('transform_fallback_location',
 
1828
            "commit, uncommit will all trigger this hook.", (1, 4))
 
1829
        self.add_hook('transform_fallback_location',
1851
1830
            "Called when a stacked branch is activating its fallback "
1852
1831
            "locations. transform_fallback_location is called with (branch, "
1853
1832
            "url), and should return a new url. Returning the same url "
1858
1837
            "fallback locations have not been activated. When there are "
1859
1838
            "multiple hooks installed for transform_fallback_location, "
1860
1839
            "all are called with the url returned from the previous hook."
1861
 
            "The order is however undefined.", (1, 9), None))
1862
 
        self.create_hook(HookPoint('automatic_tag_name',
 
1840
            "The order is however undefined.", (1, 9))
 
1841
        self.add_hook('automatic_tag_name',
1863
1842
            "Called to determine an automatic tag name for a revision. "
1864
1843
            "automatic_tag_name is called with (branch, revision_id) and "
1865
1844
            "should return a tag name or None if no tag name could be "
1866
1845
            "determined. The first non-None tag name returned will be used.",
1867
 
            (2, 2), None))
1868
 
        self.create_hook(HookPoint('post_branch_init',
 
1846
            (2, 2))
 
1847
        self.add_hook('post_branch_init',
1869
1848
            "Called after new branch initialization completes. "
1870
1849
            "post_branch_init is called with a "
1871
1850
            "bzrlib.branch.BranchInitHookParams. "
1872
1851
            "Note that init, branch and checkout (both heavyweight and "
1873
 
            "lightweight) will all trigger this hook.", (2, 2), None))
1874
 
        self.create_hook(HookPoint('post_switch',
 
1852
            "lightweight) will all trigger this hook.", (2, 2))
 
1853
        self.add_hook('post_switch',
1875
1854
            "Called after a checkout switches branch. "
1876
1855
            "post_switch is called with a "
1877
 
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
 
1856
            "bzrlib.branch.SwitchHookParams.", (2, 2))
1878
1857
 
1879
1858
 
1880
1859
 
2000
1979
        """What class to instantiate on open calls."""
2001
1980
        raise NotImplementedError(self._branch_class)
2002
1981
 
 
1982
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1983
                           repository=None):
 
1984
        """Initialize a branch in a bzrdir, with specified files
 
1985
 
 
1986
        :param a_bzrdir: The bzrdir to initialize the branch in
 
1987
        :param utf8_files: The files to create as a list of
 
1988
            (filename, content) tuples
 
1989
        :param name: Name of colocated branch to create, if any
 
1990
        :return: a branch in this format
 
1991
        """
 
1992
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1993
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
1994
        control_files = lockable_files.LockableFiles(branch_transport,
 
1995
            'lock', lockdir.LockDir)
 
1996
        control_files.create_lock()
 
1997
        control_files.lock_write()
 
1998
        try:
 
1999
            utf8_files += [('format', self.get_format_string())]
 
2000
            for (filename, content) in utf8_files:
 
2001
                branch_transport.put_bytes(
 
2002
                    filename, content,
 
2003
                    mode=a_bzrdir._get_file_mode())
 
2004
        finally:
 
2005
            control_files.unlock()
 
2006
        branch = self.open(a_bzrdir, name, _found=True,
 
2007
                found_repository=repository)
 
2008
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2009
        return branch
 
2010
 
2003
2011
    def network_name(self):
2004
2012
        """A simple byte string uniquely identifying this format for RPC calls.
2005
2013
 
2138
2146
                      ]
2139
2147
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2140
2148
 
2141
 
    def __init__(self):
2142
 
        super(BzrBranchFormat8, self).__init__()
2143
 
        self._matchingbzrdir.repository_format = \
2144
 
            RepositoryFormatKnitPack5RichRoot()
2145
 
 
2146
2149
    def make_tags(self, branch):
2147
2150
        """See bzrlib.branch.BranchFormat.make_tags()."""
2148
2151
        return BasicTags(branch)
2156
2159
    supports_reference_locations = True
2157
2160
 
2158
2161
 
2159
 
class BzrBranchFormat7(BzrBranchFormat8):
 
2162
class BzrBranchFormat7(BranchFormatMetadir):
2160
2163
    """Branch format with last-revision, tags, and a stacked location pointer.
2161
2164
 
2162
2165
    The stacked location pointer is passed down to the repository and requires
2187
2190
    def supports_set_append_revisions_only(self):
2188
2191
        return True
2189
2192
 
 
2193
    def supports_stacking(self):
 
2194
        return True
 
2195
 
 
2196
    def make_tags(self, branch):
 
2197
        """See bzrlib.branch.BranchFormat.make_tags()."""
 
2198
        return BasicTags(branch)
 
2199
 
2190
2200
    supports_reference_locations = False
2191
2201
 
2192
2202
 
2491
2501
            'revision-history', '\n'.join(history),
2492
2502
            mode=self.bzrdir._get_file_mode())
2493
2503
 
2494
 
    @needs_write_lock
 
2504
    @deprecated_method(deprecated_in((2, 4, 0)))
2495
2505
    def set_revision_history(self, rev_history):
2496
2506
        """See Branch.set_revision_history."""
 
2507
        self._set_revision_history(rev_history)
 
2508
 
 
2509
    @needs_write_lock
 
2510
    def _set_revision_history(self, rev_history):
2497
2511
        if 'evil' in debug.debug_flags:
2498
2512
            mutter_callsite(3, "set_revision_history scales with history.")
2499
2513
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2543
2557
            except ValueError:
2544
2558
                rev = self.repository.get_revision(revision_id)
2545
2559
                new_history = rev.get_history(self.repository)[1:]
2546
 
        destination.set_revision_history(new_history)
 
2560
        destination._set_revision_history(new_history)
2547
2561
 
2548
2562
    @needs_write_lock
2549
2563
    def set_last_revision_info(self, revno, revision_id):
2557
2571
        configured to check constraints on history, in which case this may not
2558
2572
        be permitted.
2559
2573
        """
2560
 
        revision_id = _mod_revision.ensure_null(revision_id)
 
2574
        if not revision_id or not isinstance(revision_id, basestring):
 
2575
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2561
2576
        # this old format stores the full history, but this api doesn't
2562
2577
        # provide it, so we must generate, and might as well check it's
2563
2578
        # correct
2564
2579
        history = self._lefthand_history(revision_id)
2565
2580
        if len(history) != revno:
2566
2581
            raise AssertionError('%d != %d' % (len(history), revno))
2567
 
        self.set_revision_history(history)
 
2582
        self._set_revision_history(history)
2568
2583
 
2569
2584
    def _gen_revision_history(self):
2570
2585
        history = self._transport.get_bytes('revision-history').split('\n')
2584
2599
        :param other_branch: The other branch that DivergedBranches should
2585
2600
            raise with respect to.
2586
2601
        """
2587
 
        self.set_revision_history(self._lefthand_history(revision_id,
 
2602
        self._set_revision_history(self._lefthand_history(revision_id,
2588
2603
            last_rev, other_branch))
2589
2604
 
2590
2605
    def basis_tree(self):
2793
2808
 
2794
2809
    @needs_write_lock
2795
2810
    def set_last_revision_info(self, revno, revision_id):
2796
 
        revision_id = _mod_revision.ensure_null(revision_id)
 
2811
        if not revision_id or not isinstance(revision_id, basestring):
 
2812
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2797
2813
        old_revno, old_revid = self.last_revision_info()
2798
2814
        if self._get_append_revisions_only():
2799
2815
            self._check_history_violation(revision_id)