~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Ian Clatworthy
  • Date: 2010-02-19 03:02:07 UTC
  • mto: (4797.23.1 integration-2.1)
  • mto: This revision was merged to the branch mainline in revision 5055.
  • Revision ID: ian.clatworthy@canonical.com-20100219030207-zpbzx021zavx4sqt
What's New in 2.1 - a summary of changes since 2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2005-2010 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
46
46
    )
47
47
""")
48
48
 
49
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
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
53
from bzrlib import registry
53
54
from bzrlib.symbol_versioning import (
54
55
    deprecated_in,
149
150
        if self._partial_revision_history_cache[-1] == _mod_revision.NULL_REVISION:
150
151
            self._partial_revision_history_cache.pop()
151
152
 
 
153
    def _get_check_refs(self):
 
154
        """Get the references needed for check().
 
155
 
 
156
        See bzrlib.check.
 
157
        """
 
158
        revid = self.last_revision()
 
159
        return [('revision-existence', revid), ('lefthand-distance', revid)]
 
160
 
152
161
    @staticmethod
153
162
    def open(base, _unsupported=False, possible_transports=None):
154
163
        """Open the branch rooted at base.
438
447
        # start_revision_id.
439
448
        if self._merge_sorted_revisions_cache is None:
440
449
            last_revision = self.last_revision()
441
 
            graph = self.repository.get_graph()
442
 
            parent_map = dict(((key, value) for key, value in
443
 
                     graph.iter_ancestry([last_revision]) if value is not None))
444
 
            revision_graph = repository._strip_NULL_ghosts(parent_map)
445
 
            revs = tsort.merge_sort(revision_graph, last_revision, None,
446
 
                generate_revno=True)
447
 
            # Drop the sequence # before caching
448
 
            self._merge_sorted_revisions_cache = [r[1:] for r in revs]
449
 
 
 
450
            last_key = (last_revision,)
 
451
            known_graph = self.repository.revisions.get_known_graph_ancestry(
 
452
                [last_key])
 
453
            self._merge_sorted_revisions_cache = known_graph.merge_sort(
 
454
                last_key)
450
455
        filtered = self._filter_merge_sorted_revisions(
451
456
            self._merge_sorted_revisions_cache, start_revision_id,
452
457
            stop_revision_id, stop_rule)
462
467
        """Iterate over an inclusive range of sorted revisions."""
463
468
        rev_iter = iter(merge_sorted_revisions)
464
469
        if start_revision_id is not None:
465
 
            for rev_id, depth, revno, end_of_merge in rev_iter:
 
470
            for node in rev_iter:
 
471
                rev_id = node.key[-1]
466
472
                if rev_id != start_revision_id:
467
473
                    continue
468
474
                else:
469
475
                    # The decision to include the start or not
470
476
                    # depends on the stop_rule if a stop is provided
471
 
                    rev_iter = chain(
472
 
                        iter([(rev_id, depth, revno, end_of_merge)]),
473
 
                        rev_iter)
 
477
                    # so pop this node back into the iterator
 
478
                    rev_iter = chain(iter([node]), rev_iter)
474
479
                    break
475
480
        if stop_revision_id is None:
476
 
            for rev_id, depth, revno, end_of_merge in rev_iter:
477
 
                yield rev_id, depth, revno, end_of_merge
 
481
            # Yield everything
 
482
            for node in rev_iter:
 
483
                rev_id = node.key[-1]
 
484
                yield (rev_id, node.merge_depth, node.revno,
 
485
                       node.end_of_merge)
478
486
        elif stop_rule == 'exclude':
479
 
            for rev_id, depth, revno, end_of_merge in rev_iter:
 
487
            for node in rev_iter:
 
488
                rev_id = node.key[-1]
480
489
                if rev_id == stop_revision_id:
481
490
                    return
482
 
                yield rev_id, depth, revno, end_of_merge
 
491
                yield (rev_id, node.merge_depth, node.revno,
 
492
                       node.end_of_merge)
483
493
        elif stop_rule == 'include':
484
 
            for rev_id, depth, revno, end_of_merge in rev_iter:
485
 
                yield rev_id, depth, revno, end_of_merge
 
494
            for node in rev_iter:
 
495
                rev_id = node.key[-1]
 
496
                yield (rev_id, node.merge_depth, node.revno,
 
497
                       node.end_of_merge)
486
498
                if rev_id == stop_revision_id:
487
499
                    return
488
500
        elif stop_rule == 'with-merges':
491
503
                left_parent = stop_rev.parent_ids[0]
492
504
            else:
493
505
                left_parent = _mod_revision.NULL_REVISION
494
 
            for rev_id, depth, revno, end_of_merge in rev_iter:
 
506
            # left_parent is the actual revision we want to stop logging at,
 
507
            # since we want to show the merged revisions after the stop_rev too
 
508
            reached_stop_revision_id = False
 
509
            revision_id_whitelist = []
 
510
            for node in rev_iter:
 
511
                rev_id = node.key[-1]
495
512
                if rev_id == left_parent:
 
513
                    # reached the left parent after the stop_revision
496
514
                    return
497
 
                yield rev_id, depth, revno, end_of_merge
 
515
                if (not reached_stop_revision_id or
 
516
                        rev_id in revision_id_whitelist):
 
517
                    yield (rev_id, node.merge_depth, node.revno,
 
518
                       node.end_of_merge)
 
519
                    if reached_stop_revision_id or rev_id == stop_revision_id:
 
520
                        # only do the merged revs of rev_id from now on
 
521
                        rev = self.repository.get_revision(rev_id)
 
522
                        if rev.parent_ids:
 
523
                            reached_stop_revision_id = True
 
524
                            revision_id_whitelist.extend(rev.parent_ids)
498
525
        else:
499
526
            raise ValueError('invalid stop_rule %r' % stop_rule)
500
527
 
1076
1103
        params = ChangeBranchTipParams(
1077
1104
            self, old_revno, new_revno, old_revid, new_revid)
1078
1105
        for hook in hooks:
1079
 
            try:
1080
 
                hook(params)
1081
 
            except errors.TipChangeRejected:
1082
 
                raise
1083
 
            except Exception:
1084
 
                exc_info = sys.exc_info()
1085
 
                hook_name = Branch.hooks.get_hook_name(hook)
1086
 
                raise errors.HookFailed(
1087
 
                    'pre_change_branch_tip', hook_name, exc_info)
 
1106
            hook(params)
1088
1107
 
1089
1108
    @needs_write_lock
1090
1109
    def update(self):
1139
1158
        revision_id: if not None, the revision history in the new branch will
1140
1159
                     be truncated to end with revision_id.
1141
1160
        """
 
1161
        if (repository_policy is not None and
 
1162
            repository_policy.requires_stacking()):
 
1163
            to_bzrdir._format.require_stacking(_skip_repo=True)
1142
1164
        result = to_bzrdir.create_branch()
1143
1165
        result.lock_write()
1144
1166
        try:
1212
1234
        target._set_all_reference_info(target_reference_dict)
1213
1235
 
1214
1236
    @needs_read_lock
1215
 
    def check(self):
 
1237
    def check(self, refs):
1216
1238
        """Check consistency of the branch.
1217
1239
 
1218
1240
        In particular this checks that revisions given in the revision-history
1221
1243
 
1222
1244
        Callers will typically also want to check the repository.
1223
1245
 
 
1246
        :param refs: Calculated refs for this branch as specified by
 
1247
            branch._get_check_refs()
1224
1248
        :return: A BranchCheckResult.
1225
1249
        """
1226
 
        ret = BranchCheckResult(self)
1227
 
        mainline_parent_id = None
 
1250
        result = BranchCheckResult(self)
1228
1251
        last_revno, last_revision_id = self.last_revision_info()
1229
 
        real_rev_history = []
1230
 
        try:
1231
 
            for revid in self.repository.iter_reverse_revision_history(
1232
 
                last_revision_id):
1233
 
                real_rev_history.append(revid)
1234
 
        except errors.RevisionNotPresent:
1235
 
            ret.ghosts_in_mainline = True
1236
 
        else:
1237
 
            ret.ghosts_in_mainline = False
1238
 
        real_rev_history.reverse()
1239
 
        if len(real_rev_history) != last_revno:
1240
 
            raise errors.BzrCheckError('revno does not match len(mainline)'
1241
 
                ' %s != %s' % (last_revno, len(real_rev_history)))
1242
 
        # TODO: We should probably also check that real_rev_history actually
1243
 
        #       matches self.revision_history()
1244
 
        for revision_id in real_rev_history:
1245
 
            try:
1246
 
                revision = self.repository.get_revision(revision_id)
1247
 
            except errors.NoSuchRevision, e:
1248
 
                raise errors.BzrCheckError("mainline revision {%s} not in repository"
1249
 
                            % revision_id)
1250
 
            # In general the first entry on the revision history has no parents.
1251
 
            # But it's not illegal for it to have parents listed; this can happen
1252
 
            # in imports from Arch when the parents weren't reachable.
1253
 
            if mainline_parent_id is not None:
1254
 
                if mainline_parent_id not in revision.parent_ids:
1255
 
                    raise errors.BzrCheckError("previous revision {%s} not listed among "
1256
 
                                        "parents of {%s}"
1257
 
                                        % (mainline_parent_id, revision_id))
1258
 
            mainline_parent_id = revision_id
1259
 
        return ret
 
1252
        actual_revno = refs[('lefthand-distance', last_revision_id)]
 
1253
        if actual_revno != last_revno:
 
1254
            result.errors.append(errors.BzrCheckError(
 
1255
                'revno does not match len(mainline) %s != %s' % (
 
1256
                last_revno, actual_revno)))
 
1257
        # TODO: We should probably also check that self.revision_history
 
1258
        # matches the repository for older branch formats.
 
1259
        # If looking for the code that cross-checks repository parents against
 
1260
        # the iter_reverse_revision_history output, that is now a repository
 
1261
        # specific check.
 
1262
        return result
1260
1263
 
1261
1264
    def _get_checkout_format(self):
1262
1265
        """Return the most suitable metadir for a checkout of this branch.
1287
1290
        # clone call. Or something. 20090224 RBC/spiv.
1288
1291
        if revision_id is None:
1289
1292
            revision_id = self.last_revision()
1290
 
        try:
1291
 
            dir_to = self.bzrdir.clone_on_transport(to_transport,
1292
 
                revision_id=revision_id, stacked_on=stacked_on,
1293
 
                create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1294
 
        except errors.FileExists:
1295
 
            if not use_existing_dir:
1296
 
                raise
1297
 
        except errors.NoSuchFile:
1298
 
            if not create_prefix:
1299
 
                raise
 
1293
        dir_to = self.bzrdir.clone_on_transport(to_transport,
 
1294
            revision_id=revision_id, stacked_on=stacked_on,
 
1295
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1300
1296
        return dir_to.open_branch()
1301
1297
 
1302
1298
    def create_checkout(self, to_location, revision_id=None,
1442
1438
        """Return the format for the branch object in a_bzrdir."""
1443
1439
        try:
1444
1440
            transport = a_bzrdir.get_branch_transport(None)
1445
 
            format_string = transport.get("format").read()
 
1441
            format_string = transport.get_bytes("format")
1446
1442
            return klass._formats[format_string]
1447
1443
        except errors.NoSuchFile:
1448
 
            raise errors.NotBranchError(path=transport.base)
 
1444
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1449
1445
        except KeyError:
1450
1446
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1451
1447
 
1800
1796
                              _repository=a_bzrdir.find_repository(),
1801
1797
                              ignore_fallbacks=ignore_fallbacks)
1802
1798
        except errors.NoSuchFile:
1803
 
            raise errors.NotBranchError(path=transport.base)
 
1799
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1804
1800
 
1805
1801
    def __init__(self):
1806
1802
        super(BranchFormatMetadir, self).__init__()
1981
1977
    def get_reference(self, a_bzrdir):
1982
1978
        """See BranchFormat.get_reference()."""
1983
1979
        transport = a_bzrdir.get_branch_transport(None)
1984
 
        return transport.get('location').read()
 
1980
        return transport.get_bytes('location')
1985
1981
 
1986
1982
    def set_reference(self, a_bzrdir, to_branch):
1987
1983
        """See BranchFormat.set_reference()."""
2075
2071
BranchFormat.register_format(__format6)
2076
2072
BranchFormat.register_format(__format7)
2077
2073
BranchFormat.register_format(__format8)
2078
 
BranchFormat.set_default_format(__format6)
 
2074
BranchFormat.set_default_format(__format7)
2079
2075
_legacy_formats = [BzrBranchFormat4(),
2080
2076
    ]
2081
2077
network_format_registry.register(
2082
2078
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2083
2079
 
2084
2080
 
2085
 
class BzrBranch(Branch):
 
2081
class BzrBranch(Branch, _RelockDebugMixin):
2086
2082
    """A branch stored in the actual filesystem.
2087
2083
 
2088
2084
    Note that it's "local" in the context of the filesystem; it doesn't
2134
2130
        return self.control_files.is_locked()
2135
2131
 
2136
2132
    def lock_write(self, token=None):
 
2133
        if not self.is_locked():
 
2134
            self._note_lock('w')
2137
2135
        # All-in-one needs to always unlock/lock.
2138
2136
        repo_control = getattr(self.repository, 'control_files', None)
2139
2137
        if self.control_files == repo_control or not self.is_locked():
 
2138
            self.repository._warn_if_deprecated(self)
2140
2139
            self.repository.lock_write()
2141
2140
            took_lock = True
2142
2141
        else:
2149
2148
            raise
2150
2149
 
2151
2150
    def lock_read(self):
 
2151
        if not self.is_locked():
 
2152
            self._note_lock('r')
2152
2153
        # All-in-one needs to always unlock/lock.
2153
2154
        repo_control = getattr(self.repository, 'control_files', None)
2154
2155
        if self.control_files == repo_control or not self.is_locked():
 
2156
            self.repository._warn_if_deprecated(self)
2155
2157
            self.repository.lock_read()
2156
2158
            took_lock = True
2157
2159
        else:
2163
2165
                self.repository.unlock()
2164
2166
            raise
2165
2167
 
 
2168
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2166
2169
    def unlock(self):
2167
2170
        try:
2168
2171
            self.control_files.unlock()
2839
2842
 
2840
2843
    def __init__(self, branch):
2841
2844
        self.branch = branch
2842
 
        self.ghosts_in_mainline = False
 
2845
        self.errors = []
2843
2846
 
2844
2847
    def report_results(self, verbose):
2845
2848
        """Report the check results via trace.note.
2847
2850
        :param verbose: Requests more detailed display of what was checked,
2848
2851
            if any.
2849
2852
        """
2850
 
        note('checked branch %s format %s',
2851
 
             self.branch.base,
2852
 
             self.branch._format)
2853
 
        if self.ghosts_in_mainline:
2854
 
            note('branch contains ghosts in mainline')
 
2853
        note('checked branch %s format %s', self.branch.base,
 
2854
            self.branch._format)
 
2855
        for error in self.errors:
 
2856
            note('found error:%s', error)
2855
2857
 
2856
2858
 
2857
2859
class Converter5to6(object):