/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin
  • Date: 2010-05-16 15:18:43 UTC
  • mfrom: (5235 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5239.
  • Revision ID: gzlist@googlemail.com-20100516151843-lu53u7caehm3ie3i
Merge bzr.dev to resolve conflicts in NEWS and _chk_map_pyx

Show diffs side-by-side

added added

removed removed

Lines of Context:
49
49
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
50
50
from bzrlib.hooks import HookPoint, Hooks
51
51
from bzrlib.inter import InterObject
52
 
from bzrlib.lock import _RelockDebugMixin
 
52
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
53
53
from bzrlib import registry
54
54
from bzrlib.symbol_versioning import (
55
55
    deprecated_in,
63
63
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
64
64
 
65
65
 
66
 
# TODO: Maybe include checks for common corruption of newlines, etc?
67
 
 
68
 
# TODO: Some operations like log might retrieve the same revisions
69
 
# repeatedly to calculate deltas.  We could perhaps have a weakref
70
 
# cache in memory to make this faster.  In general anything can be
71
 
# cached in memory between lock and unlock operations. .. nb thats
72
 
# what the transaction identity map provides
73
 
 
74
 
 
75
 
######################################################################
76
 
# branch objects
77
 
 
78
 
class Branch(object):
 
66
class Branch(bzrdir.ControlComponent):
79
67
    """Branch holding a history of revisions.
80
68
 
81
 
    base
82
 
        Base directory/url of the branch.
 
69
    :ivar base:
 
70
        Base directory/url of the branch; using control_url and
 
71
        control_transport is more standardized.
83
72
 
84
73
    hooks: An instance of BranchHooks.
85
74
    """
87
76
    # - RBC 20060112
88
77
    base = None
89
78
 
 
79
    @property
 
80
    def control_transport(self):
 
81
        return self._transport
 
82
 
 
83
    @property
 
84
    def user_transport(self):
 
85
        return self.bzrdir.user_transport
 
86
 
90
87
    def __init__(self, *ignored, **ignored_too):
91
88
        self.tags = self._format.make_tags(self)
92
89
        self._revision_history_cache = None
107
104
        """Activate the branch/repository from url as a fallback repository."""
108
105
        repo = self._get_fallback_repository(url)
109
106
        if repo.has_same_location(self.repository):
110
 
            raise errors.UnstackableLocationError(self.base, url)
 
107
            raise errors.UnstackableLocationError(self.user_url, url)
111
108
        self.repository.add_fallback_repository(repo)
112
109
 
113
110
    def break_lock(self):
286
283
        new_history.reverse()
287
284
        return new_history
288
285
 
289
 
    def lock_write(self):
 
286
    def lock_write(self, token=None):
 
287
        """Lock the branch for write operations.
 
288
 
 
289
        :param token: A token to permit reacquiring a previously held and
 
290
            preserved lock.
 
291
        :return: A BranchWriteLockResult.
 
292
        """
290
293
        raise NotImplementedError(self.lock_write)
291
294
 
292
295
    def lock_read(self):
 
296
        """Lock the branch for read operations.
 
297
 
 
298
        :return: A bzrlib.lock.LogicalLockResult.
 
299
        """
293
300
        raise NotImplementedError(self.lock_read)
294
301
 
295
302
    def unlock(self):
420
427
            * 'include' - the stop revision is the last item in the result
421
428
            * 'with-merges' - include the stop revision and all of its
422
429
              merged revisions in the result
 
430
            * 'with-merges-without-common-ancestry' - filter out revisions 
 
431
              that are in both ancestries
423
432
        :param direction: either 'reverse' or 'forward':
424
433
            * reverse means return the start_revision_id first, i.e.
425
434
              start at the most recent revision and go backwards in history
456
465
            stop_revision_id, stop_rule)
457
466
        # Make sure we don't return revisions that are not part of the
458
467
        # start_revision_id ancestry.
459
 
        filtered = self._filter_non_ancestors(filtered)
 
468
        filtered = self._filter_start_non_ancestors(filtered)
460
469
        if direction == 'reverse':
461
470
            return filtered
462
471
        if direction == 'forward':
499
508
                       node.end_of_merge)
500
509
                if rev_id == stop_revision_id:
501
510
                    return
 
511
        elif stop_rule == 'with-merges-without-common-ancestry':
 
512
            # We want to exclude all revisions that are already part of the
 
513
            # stop_revision_id ancestry.
 
514
            graph = self.repository.get_graph()
 
515
            ancestors = graph.find_unique_ancestors(start_revision_id,
 
516
                                                    [stop_revision_id])
 
517
            for node in rev_iter:
 
518
                rev_id = node.key[-1]
 
519
                if rev_id not in ancestors:
 
520
                    continue
 
521
                yield (rev_id, node.merge_depth, node.revno,
 
522
                       node.end_of_merge)
502
523
        elif stop_rule == 'with-merges':
503
524
            stop_rev = self.repository.get_revision(stop_revision_id)
504
525
            if stop_rev.parent_ids:
527
548
        else:
528
549
            raise ValueError('invalid stop_rule %r' % stop_rule)
529
550
 
530
 
    def _filter_non_ancestors(self, rev_iter):
 
551
    def _filter_start_non_ancestors(self, rev_iter):
531
552
        # If we started from a dotted revno, we want to consider it as a tip
532
553
        # and don't want to yield revisions that are not part of its
533
554
        # ancestry. Given the order guaranteed by the merge sort, we will see
594
615
        :param other: The branch to bind to
595
616
        :type other: Branch
596
617
        """
597
 
        raise errors.UpgradeRequired(self.base)
 
618
        raise errors.UpgradeRequired(self.user_url)
598
619
 
599
620
    def set_append_revisions_only(self, enabled):
600
621
        if not self._format.supports_set_append_revisions_only():
601
 
            raise errors.UpgradeRequired(self.base)
 
622
            raise errors.UpgradeRequired(self.user_url)
602
623
        if enabled:
603
624
            value = 'True'
604
625
        else:
652
673
    def get_old_bound_location(self):
653
674
        """Return the URL of the branch we used to be bound to
654
675
        """
655
 
        raise errors.UpgradeRequired(self.base)
 
676
        raise errors.UpgradeRequired(self.user_url)
656
677
 
657
678
    def get_commit_builder(self, parents, config=None, timestamp=None,
658
679
                           timezone=None, committer=None, revprops=None,
736
757
            stacking.
737
758
        """
738
759
        if not self._format.supports_stacking():
739
 
            raise errors.UnstackableBranchFormat(self._format, self.base)
 
760
            raise errors.UnstackableBranchFormat(self._format, self.user_url)
740
761
        # XXX: Changing from one fallback repository to another does not check
741
762
        # that all the data you need is present in the new fallback.
742
763
        # Possibly it should.
893
914
 
894
915
    def unbind(self):
895
916
        """Older format branches cannot bind or unbind."""
896
 
        raise errors.UpgradeRequired(self.base)
 
917
        raise errors.UpgradeRequired(self.user_url)
897
918
 
898
919
    def last_revision(self):
899
920
        """Return last revision id, or NULL_REVISION."""
1059
1080
        try:
1060
1081
            return urlutils.join(self.base[:-1], parent)
1061
1082
        except errors.InvalidURLJoin, e:
1062
 
            raise errors.InaccessibleParent(parent, self.base)
 
1083
            raise errors.InaccessibleParent(parent, self.user_url)
1063
1084
 
1064
1085
    def _get_parent_location(self):
1065
1086
        raise NotImplementedError(self._get_parent_location)
1335
1356
        """
1336
1357
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1337
1358
        # clone call. Or something. 20090224 RBC/spiv.
 
1359
        # XXX: Should this perhaps clone colocated branches as well, 
 
1360
        # rather than just the default branch? 20100319 JRV
1338
1361
        if revision_id is None:
1339
1362
            revision_id = self.last_revision()
1340
1363
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1510
1533
        """Return the current default format."""
1511
1534
        return klass._default_format
1512
1535
 
1513
 
    def get_reference(self, a_bzrdir):
 
1536
    def get_reference(self, a_bzrdir, name=None):
1514
1537
        """Get the target reference of the branch in a_bzrdir.
1515
1538
 
1516
1539
        format probing must have been completed before calling
1518
1541
        in a_bzrdir is correct.
1519
1542
 
1520
1543
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1544
        :param name: Name of the colocated branch to fetch
1521
1545
        :return: None if the branch is not a reference branch.
1522
1546
        """
1523
1547
        return None
1524
1548
 
1525
1549
    @classmethod
1526
 
    def set_reference(self, a_bzrdir, to_branch):
 
1550
    def set_reference(self, a_bzrdir, name, to_branch):
1527
1551
        """Set the target reference of the branch in a_bzrdir.
1528
1552
 
1529
1553
        format probing must have been completed before calling
1531
1555
        in a_bzrdir is correct.
1532
1556
 
1533
1557
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1558
        :param name: Name of colocated branch to set, None for default
1534
1559
        :param to_branch: branch that the checkout is to reference
1535
1560
        """
1536
1561
        raise NotImplementedError(self.set_reference)
1543
1568
        """Return the short format description for this format."""
1544
1569
        raise NotImplementedError(self.get_format_description)
1545
1570
 
 
1571
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1572
        hooks = Branch.hooks['post_branch_init']
 
1573
        if not hooks:
 
1574
            return
 
1575
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1576
        for hook in hooks:
 
1577
            hook(params)
 
1578
 
1546
1579
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1547
1580
                           lock_type='metadir', set_format=True):
1548
1581
        """Initialize a branch in a bzrdir, with specified files
1556
1589
            elsewhere)
1557
1590
        :return: a branch in this format
1558
1591
        """
1559
 
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
 
1592
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1560
1593
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1561
1594
        lock_map = {
1562
1595
            'metadir': ('lock', lockdir.LockDir),
1584
1617
        finally:
1585
1618
            if lock_taken:
1586
1619
                control_files.unlock()
1587
 
        return self.open(a_bzrdir, name, _found=True)
 
1620
        branch = self.open(a_bzrdir, name, _found=True)
 
1621
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1622
        return branch
1588
1623
 
1589
1624
    def initialize(self, a_bzrdir, name=None):
1590
1625
        """Create a branch of this format in a_bzrdir.
1750
1785
            "should return a tag name or None if no tag name could be "
1751
1786
            "determined. The first non-None tag name returned will be used.",
1752
1787
            (2, 2), None))
 
1788
        self.create_hook(HookPoint('post_branch_init',
 
1789
            "Called after new branch initialization completes. "
 
1790
            "post_branch_init is called with a "
 
1791
            "bzrlib.branch.BranchInitHookParams. "
 
1792
            "Note that init, branch and checkout (both heavyweight and "
 
1793
            "lightweight) will all trigger this hook.", (2, 2), None))
 
1794
        self.create_hook(HookPoint('post_switch',
 
1795
            "Called after a checkout switches branch. "
 
1796
            "post_switch is called with a "
 
1797
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
1753
1798
 
1754
1799
 
1755
1800
 
1795
1840
            self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1796
1841
 
1797
1842
 
 
1843
class BranchInitHookParams(object):
 
1844
    """Object holding parameters passed to *_branch_init hooks.
 
1845
 
 
1846
    There are 4 fields that hooks may wish to access:
 
1847
 
 
1848
    :ivar format: the branch format
 
1849
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1850
    :ivar name: name of colocated branch, if any (or None)
 
1851
    :ivar branch: the branch created
 
1852
 
 
1853
    Note that for lightweight checkouts, the bzrdir and format fields refer to
 
1854
    the checkout, hence they are different from the corresponding fields in
 
1855
    branch, which refer to the original branch.
 
1856
    """
 
1857
 
 
1858
    def __init__(self, format, a_bzrdir, name, branch):
 
1859
        """Create a group of BranchInitHook parameters.
 
1860
 
 
1861
        :param format: the branch format
 
1862
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1863
            initialized
 
1864
        :param name: name of colocated branch, if any (or None)
 
1865
        :param branch: the branch created
 
1866
 
 
1867
        Note that for lightweight checkouts, the bzrdir and format fields refer
 
1868
        to the checkout, hence they are different from the corresponding fields
 
1869
        in branch, which refer to the original branch.
 
1870
        """
 
1871
        self.format = format
 
1872
        self.bzrdir = a_bzrdir
 
1873
        self.name = name
 
1874
        self.branch = branch
 
1875
 
 
1876
    def __eq__(self, other):
 
1877
        return self.__dict__ == other.__dict__
 
1878
 
 
1879
    def __repr__(self):
 
1880
        if self.branch:
 
1881
            return "<%s of %s>" % (self.__class__.__name__, self.branch)
 
1882
        else:
 
1883
            return "<%s of format:%s bzrdir:%s>" % (
 
1884
                self.__class__.__name__, self.branch,
 
1885
                self.format, self.bzrdir)
 
1886
 
 
1887
 
 
1888
class SwitchHookParams(object):
 
1889
    """Object holding parameters passed to *_switch hooks.
 
1890
 
 
1891
    There are 4 fields that hooks may wish to access:
 
1892
 
 
1893
    :ivar control_dir: BzrDir of the checkout to change
 
1894
    :ivar to_branch: branch that the checkout is to reference
 
1895
    :ivar force: skip the check for local commits in a heavy checkout
 
1896
    :ivar revision_id: revision ID to switch to (or None)
 
1897
    """
 
1898
 
 
1899
    def __init__(self, control_dir, to_branch, force, revision_id):
 
1900
        """Create a group of SwitchHook parameters.
 
1901
 
 
1902
        :param control_dir: BzrDir of the checkout to change
 
1903
        :param to_branch: branch that the checkout is to reference
 
1904
        :param force: skip the check for local commits in a heavy checkout
 
1905
        :param revision_id: revision ID to switch to (or None)
 
1906
        """
 
1907
        self.control_dir = control_dir
 
1908
        self.to_branch = to_branch
 
1909
        self.force = force
 
1910
        self.revision_id = revision_id
 
1911
 
 
1912
    def __eq__(self, other):
 
1913
        return self.__dict__ == other.__dict__
 
1914
 
 
1915
    def __repr__(self):
 
1916
        return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
 
1917
            self.control_dir, self.to_branch,
 
1918
            self.revision_id)
 
1919
 
 
1920
 
1798
1921
class BzrBranchFormat4(BranchFormat):
1799
1922
    """Bzr branch format 4.
1800
1923
 
1859
1982
            if format.__class__ != self.__class__:
1860
1983
                raise AssertionError("wrong format %r found for %r" %
1861
1984
                    (format, self))
 
1985
        transport = a_bzrdir.get_branch_transport(None, name=name)
1862
1986
        try:
1863
 
            transport = a_bzrdir.get_branch_transport(None, name=name)
1864
1987
            control_files = lockable_files.LockableFiles(transport, 'lock',
1865
1988
                                                         lockdir.LockDir)
1866
1989
            return self._branch_class()(_format=self,
2048
2171
        """See BranchFormat.get_format_description()."""
2049
2172
        return "Checkout reference format 1"
2050
2173
 
2051
 
    def get_reference(self, a_bzrdir):
 
2174
    def get_reference(self, a_bzrdir, name=None):
2052
2175
        """See BranchFormat.get_reference()."""
2053
 
        transport = a_bzrdir.get_branch_transport(None)
 
2176
        transport = a_bzrdir.get_branch_transport(None, name=name)
2054
2177
        return transport.get_bytes('location')
2055
2178
 
2056
 
    def set_reference(self, a_bzrdir, to_branch):
 
2179
    def set_reference(self, a_bzrdir, name, to_branch):
2057
2180
        """See BranchFormat.set_reference()."""
2058
 
        transport = a_bzrdir.get_branch_transport(None)
 
2181
        transport = a_bzrdir.get_branch_transport(None, name=name)
2059
2182
        location = transport.put_bytes('location', to_branch.base)
2060
2183
 
2061
2184
    def initialize(self, a_bzrdir, name=None, target_branch=None):
2064
2187
            # this format does not implement branch itself, thus the implicit
2065
2188
            # creation contract must see it as uninitializable
2066
2189
            raise errors.UninitializableFormat(self)
2067
 
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
 
2190
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2068
2191
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2069
2192
        branch_transport.put_bytes('location',
2070
 
            target_branch.bzrdir.root_transport.base)
 
2193
            target_branch.bzrdir.user_url)
2071
2194
        branch_transport.put_bytes('format', self.get_format_string())
2072
 
        return self.open(
 
2195
        branch = self.open(
2073
2196
            a_bzrdir, name, _found=True,
2074
2197
            possible_transports=[target_branch.bzrdir.root_transport])
 
2198
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2199
        return branch
2075
2200
 
2076
2201
    def __init__(self):
2077
2202
        super(BranchReferenceFormat, self).__init__()
2110
2235
                raise AssertionError("wrong format %r found for %r" %
2111
2236
                    (format, self))
2112
2237
        if location is None:
2113
 
            location = self.get_reference(a_bzrdir)
 
2238
            location = self.get_reference(a_bzrdir, name)
2114
2239
        real_bzrdir = bzrdir.BzrDir.open(
2115
2240
            location, possible_transports=possible_transports)
2116
2241
        result = real_bzrdir.open_branch(name=name, 
2154
2279
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2155
2280
 
2156
2281
 
 
2282
class BranchWriteLockResult(LogicalLockResult):
 
2283
    """The result of write locking a branch.
 
2284
 
 
2285
    :ivar branch_token: The token obtained from the underlying branch lock, or
 
2286
        None.
 
2287
    :ivar unlock: A callable which will unlock the lock.
 
2288
    """
 
2289
 
 
2290
    def __init__(self, unlock, branch_token):
 
2291
        LogicalLockResult.__init__(self, unlock)
 
2292
        self.branch_token = branch_token
 
2293
 
 
2294
    def __repr__(self):
 
2295
        return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
 
2296
            self.unlock)
 
2297
 
 
2298
 
2157
2299
class BzrBranch(Branch, _RelockDebugMixin):
2158
2300
    """A branch stored in the actual filesystem.
2159
2301
 
2193
2335
 
2194
2336
    def __str__(self):
2195
2337
        if self.name is None:
2196
 
            return '%s(%r)' % (self.__class__.__name__, self.base)
 
2338
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
2197
2339
        else:
2198
 
            return '%s(%r,%r)' % (self.__class__.__name__, self.base, self.name)
 
2340
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
 
2341
                self.name)
2199
2342
 
2200
2343
    __repr__ = __str__
2201
2344
 
2212
2355
        return self.control_files.is_locked()
2213
2356
 
2214
2357
    def lock_write(self, token=None):
 
2358
        """Lock the branch for write operations.
 
2359
 
 
2360
        :param token: A token to permit reacquiring a previously held and
 
2361
            preserved lock.
 
2362
        :return: A BranchWriteLockResult.
 
2363
        """
2215
2364
        if not self.is_locked():
2216
2365
            self._note_lock('w')
2217
2366
        # All-in-one needs to always unlock/lock.
2223
2372
        else:
2224
2373
            took_lock = False
2225
2374
        try:
2226
 
            return self.control_files.lock_write(token=token)
 
2375
            return BranchWriteLockResult(self.unlock,
 
2376
                self.control_files.lock_write(token=token))
2227
2377
        except:
2228
2378
            if took_lock:
2229
2379
                self.repository.unlock()
2230
2380
            raise
2231
2381
 
2232
2382
    def lock_read(self):
 
2383
        """Lock the branch for read operations.
 
2384
 
 
2385
        :return: A bzrlib.lock.LogicalLockResult.
 
2386
        """
2233
2387
        if not self.is_locked():
2234
2388
            self._note_lock('r')
2235
2389
        # All-in-one needs to always unlock/lock.
2242
2396
            took_lock = False
2243
2397
        try:
2244
2398
            self.control_files.lock_read()
 
2399
            return LogicalLockResult(self.unlock)
2245
2400
        except:
2246
2401
            if took_lock:
2247
2402
                self.repository.unlock()
2416
2571
        return result
2417
2572
 
2418
2573
    def get_stacked_on_url(self):
2419
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
2574
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2420
2575
 
2421
2576
    def set_push_location(self, location):
2422
2577
        """See Branch.set_push_location."""
2612
2767
        if _mod_revision.is_null(last_revision):
2613
2768
            return
2614
2769
        if last_revision not in self._lefthand_history(revision_id):
2615
 
            raise errors.AppendRevisionsOnlyViolation(self.base)
 
2770
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
2616
2771
 
2617
2772
    def _gen_revision_history(self):
2618
2773
        """Generate the revision history from last revision
2718
2873
        if branch_location is None:
2719
2874
            return Branch.reference_parent(self, file_id, path,
2720
2875
                                           possible_transports)
2721
 
        branch_location = urlutils.join(self.base, branch_location)
 
2876
        branch_location = urlutils.join(self.user_url, branch_location)
2722
2877
        return Branch.open(branch_location,
2723
2878
                           possible_transports=possible_transports)
2724
2879
 
2770
2925
        return stacked_url
2771
2926
 
2772
2927
    def _get_append_revisions_only(self):
2773
 
        value = self.get_config().get_user_option('append_revisions_only')
2774
 
        return value == 'True'
 
2928
        return self.get_config(
 
2929
            ).get_user_option_as_bool('append_revisions_only')
2775
2930
 
2776
2931
    @needs_write_lock
2777
2932
    def generate_revision_history(self, revision_id, last_rev=None,
2839
2994
    """
2840
2995
 
2841
2996
    def get_stacked_on_url(self):
2842
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
2997
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2843
2998
 
2844
2999
 
2845
3000
######################################################################
2932
3087
        :param verbose: Requests more detailed display of what was checked,
2933
3088
            if any.
2934
3089
        """
2935
 
        note('checked branch %s format %s', self.branch.base,
 
3090
        note('checked branch %s format %s', self.branch.user_url,
2936
3091
            self.branch._format)
2937
3092
        for error in self.errors:
2938
3093
            note('found error:%s', error)
3267
3422
        if local and not bound_location:
3268
3423
            raise errors.LocalRequiresBoundBranch()
3269
3424
        master_branch = None
3270
 
        if not local and bound_location and self.source.base != bound_location:
 
3425
        if not local and bound_location and self.source.user_url != bound_location:
3271
3426
            # not pulling from master, so we need to update master.
3272
3427
            master_branch = self.target.get_master_branch(possible_transports)
3273
3428
            master_branch.lock_write()