~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Jared Bunting
  • Date: 2010-10-21 22:27:43 UTC
  • mto: This revision was merged to the branch mainline in revision 5514.
  • Revision ID: jared.bunting@peachjean.com-20101021222743-tn9n0cgzg3z8cb25
Changed _win32_local_path_from_url to not allow "file:///C:" form.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
    check,
26
26
    chk_map,
27
27
    config,
 
28
    controldir,
28
29
    debug,
29
 
    errors,
30
30
    fetch as _mod_fetch,
31
31
    fifo_cache,
32
32
    generate_ids,
39
39
    lockdir,
40
40
    lru_cache,
41
41
    osutils,
 
42
    pyutils,
42
43
    revision as _mod_revision,
43
44
    static_tuple,
44
45
    symbol_versioning,
45
46
    trace,
46
47
    tsort,
47
 
    ui,
48
48
    versionedfile,
49
49
    )
50
50
from bzrlib.bundle import serializer
53
53
from bzrlib.testament import Testament
54
54
""")
55
55
 
 
56
import sys
 
57
from bzrlib import (
 
58
    errors,
 
59
    registry,
 
60
    ui,
 
61
    )
56
62
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
57
63
from bzrlib.inter import InterObject
58
64
from bzrlib.inventory import (
61
67
    ROOT_ID,
62
68
    entry_factory,
63
69
    )
64
 
from bzrlib.lock import _RelockDebugMixin
65
 
from bzrlib import registry
 
70
from bzrlib.recordcounter import RecordCounter
 
71
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
66
72
from bzrlib.trace import (
67
73
    log_exception_quietly, note, mutter, mutter_callsite, warning)
68
74
 
71
77
_deprecation_warning_done = False
72
78
 
73
79
 
 
80
class IsInWriteGroupError(errors.InternalBzrError):
 
81
 
 
82
    _fmt = "May not refresh_data of repo %(repo)s while in a write group."
 
83
 
 
84
    def __init__(self, repo):
 
85
        errors.InternalBzrError.__init__(self, repo=repo)
 
86
 
 
87
 
74
88
class CommitBuilder(object):
75
89
    """Provides an interface to build up a commit.
76
90
 
101
115
 
102
116
        if committer is None:
103
117
            self._committer = self._config.username()
 
118
        elif not isinstance(committer, unicode):
 
119
            self._committer = committer.decode() # throw if non-ascii
104
120
        else:
105
121
            self._committer = committer
106
122
 
231
247
 
232
248
    def _gen_revision_id(self):
233
249
        """Return new revision-id."""
234
 
        return generate_ids.gen_revision_id(self._config.username(),
235
 
                                            self._timestamp)
 
250
        return generate_ids.gen_revision_id(self._committer, self._timestamp)
236
251
 
237
252
    def _generate_revision_if_needed(self):
238
253
        """Create a revision id if None was supplied.
278
293
 
279
294
        :param tree: The tree which is being committed.
280
295
        """
281
 
        # NB: if there are no parents then this method is not called, so no
282
 
        # need to guard on parents having length.
 
296
        if len(self.parents) == 0:
 
297
            raise errors.RootMissing()
283
298
        entry = entry_factory['directory'](tree.path2id(''), '',
284
299
            None)
285
300
        entry.revision = self._new_revision_id
423
438
            else:
424
439
                # we don't need to commit this, because the caller already
425
440
                # determined that an existing revision of this file is
426
 
                # appropriate. If its not being considered for committing then
 
441
                # appropriate. If it's not being considered for committing then
427
442
                # it and all its parents to the root must be unaltered so
428
443
                # no-change against the basis.
429
444
                if ie.revision == self._new_revision_id:
745
760
                    # after iter_changes examines and decides it has changed,
746
761
                    # we will unconditionally record a new version even if some
747
762
                    # other process reverts it while commit is running (with
748
 
                    # the revert happening after iter_changes did it's
 
763
                    # the revert happening after iter_changes did its
749
764
                    # examination).
750
765
                    if change[7][1]:
751
766
                        entry.executable = True
860
875
        # versioned roots do not change unless the tree found a change.
861
876
 
862
877
 
863
 
class RepositoryWriteLockResult(object):
 
878
class RepositoryWriteLockResult(LogicalLockResult):
864
879
    """The result of write locking a repository.
865
880
 
866
881
    :ivar repository_token: The token obtained from the underlying lock, or
869
884
    """
870
885
 
871
886
    def __init__(self, unlock, repository_token):
 
887
        LogicalLockResult.__init__(self, unlock)
872
888
        self.repository_token = repository_token
873
 
        self.unlock = unlock
874
889
 
875
 
    def __str__(self):
 
890
    def __repr__(self):
876
891
        return "RepositoryWriteLockResult(%s, %s)" % (self.repository_token,
877
892
            self.unlock)
878
893
 
881
896
# Repositories
882
897
 
883
898
 
884
 
class Repository(_RelockDebugMixin, bzrdir.ControlComponent):
 
899
class Repository(_RelockDebugMixin, controldir.ControlComponent):
885
900
    """Repository holding history for one or more branches.
886
901
 
887
902
    The repository holds and retrieves historical information including
934
949
        pointing to .bzr/repository.
935
950
    """
936
951
 
937
 
    # What class to use for a CommitBuilder. Often its simpler to change this
 
952
    # What class to use for a CommitBuilder. Often it's simpler to change this
938
953
    # in a Repository class subclass rather than to override
939
954
    # get_commit_builder.
940
955
    _commit_builder_class = CommitBuilder
1035
1050
                " id and insertion revid (%r, %r)"
1036
1051
                % (inv.revision_id, revision_id))
1037
1052
        if inv.root is None:
1038
 
            raise AssertionError()
 
1053
            raise errors.RootMissing()
1039
1054
        return self._add_inventory_checked(revision_id, inv, parents)
1040
1055
 
1041
1056
    def _add_inventory_checked(self, revision_id, inv, parents):
1434
1449
            for repo in self._fallback_repositories:
1435
1450
                repo.lock_read()
1436
1451
            self._refresh_data()
1437
 
        return self
 
1452
        return LogicalLockResult(self.unlock)
1438
1453
 
1439
1454
    def get_physical_lock_status(self):
1440
1455
        return self.control_files.get_physical_lock_status()
1658
1673
        return missing_keys
1659
1674
 
1660
1675
    def refresh_data(self):
1661
 
        """Re-read any data needed to to synchronise with disk.
 
1676
        """Re-read any data needed to synchronise with disk.
1662
1677
 
1663
1678
        This method is intended to be called after another repository instance
1664
1679
        (such as one used by a smart server) has inserted data into the
1665
 
        repository. It may not be called during a write group, but may be
1666
 
        called at any other time.
 
1680
        repository. On all repositories this will work outside of write groups.
 
1681
        Some repository formats (pack and newer for bzrlib native formats)
 
1682
        support refresh_data inside write groups. If called inside a write
 
1683
        group on a repository that does not support refreshing in a write group
 
1684
        IsInWriteGroupError will be raised.
1667
1685
        """
1668
 
        if self.is_in_write_group():
1669
 
            raise errors.InternalBzrError(
1670
 
                "May not refresh_data while in a write group.")
1671
1686
        self._refresh_data()
1672
1687
 
1673
1688
    def resume_write_group(self, tokens):
1712
1727
                "May not fetch while in a write group.")
1713
1728
        # fast path same-url fetch operations
1714
1729
        # TODO: lift out to somewhere common with RemoteRepository
1715
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401646>
 
1730
        # <https://bugs.launchpad.net/bzr/+bug/401646>
1716
1731
        if (self.has_same_location(source)
1717
1732
            and fetch_spec is None
1718
1733
            and self._has_same_fallbacks(source)):
2500
2515
            ancestors will be traversed.
2501
2516
        """
2502
2517
        graph = self.get_graph()
2503
 
        next_id = revision_id
2504
 
        while True:
2505
 
            if next_id in (None, _mod_revision.NULL_REVISION):
2506
 
                return
2507
 
            try:
2508
 
                parents = graph.get_parent_map([next_id])[next_id]
2509
 
            except KeyError:
2510
 
                raise errors.RevisionNotPresent(next_id, self)
2511
 
            yield next_id
2512
 
            if len(parents) == 0:
2513
 
                return
2514
 
            else:
2515
 
                next_id = parents[0]
 
2518
        stop_revisions = (None, _mod_revision.NULL_REVISION)
 
2519
        return graph.iter_lefthand_ancestry(revision_id, stop_revisions)
2516
2520
 
2517
2521
    def is_shared(self):
2518
2522
        """Return True if this repository is flagged as a shared repository."""
2619
2623
        types it should be a no-op that just returns.
2620
2624
 
2621
2625
        This stub method does not require a lock, but subclasses should use
2622
 
        @needs_write_lock as this is a long running call its reasonable to
 
2626
        @needs_write_lock as this is a long running call it's reasonable to
2623
2627
        implicitly lock for the user.
2624
2628
 
2625
2629
        :param hint: If not supplied, the whole repository is packed.
2825
2829
            % (name, from_module),
2826
2830
            DeprecationWarning,
2827
2831
            stacklevel=2)
2828
 
        m = __import__(from_module, globals(), locals(), [name])
2829
2832
        try:
2830
 
            return getattr(m, name)
 
2833
            return pyutils.get_named_object(from_module, name)
2831
2834
        except AttributeError:
2832
2835
            raise AttributeError('module %s has no name %s'
2833
 
                    % (m, name))
 
2836
                    % (sys.modules[from_module], name))
2834
2837
    globals()[name] = _deprecated_repository_forwarder
2835
2838
 
2836
2839
for _name in [
3378
3381
    'bzrlib.repofmt.groupcompress_repo',
3379
3382
    'RepositoryFormat2a',
3380
3383
    )
 
3384
format_registry.register_lazy(
 
3385
    'Bazaar development format 8\n',
 
3386
    'bzrlib.repofmt.groupcompress_repo',
 
3387
    'RepositoryFormat2aSubtree',
 
3388
    )
3381
3389
 
3382
3390
 
3383
3391
class InterRepository(InterObject):
3837
3845
                basis_id, delta, current_revision_id, parents_parents)
3838
3846
            cache[current_revision_id] = parent_tree
3839
3847
 
3840
 
    def _fetch_batch(self, revision_ids, basis_id, cache, a_graph=None):
 
3848
    def _fetch_batch(self, revision_ids, basis_id, cache):
3841
3849
        """Fetch across a few revisions.
3842
3850
 
3843
3851
        :param revision_ids: The revisions to copy
3844
3852
        :param basis_id: The revision_id of a tree that must be in cache, used
3845
3853
            as a basis for delta when no other base is available
3846
3854
        :param cache: A cache of RevisionTrees that we can use.
3847
 
        :param a_graph: A Graph object to determine the heads() of the
3848
 
            rich-root data stream.
3849
3855
        :return: The revision_id of the last converted tree. The RevisionTree
3850
3856
            for it will be in cache
3851
3857
        """
3919
3925
        if root_keys_to_create:
3920
3926
            root_stream = _mod_fetch._new_root_data_stream(
3921
3927
                root_keys_to_create, self._revision_id_to_root_id, parent_map,
3922
 
                self.source, graph=a_graph)
 
3928
                self.source)
3923
3929
            to_texts.insert_record_stream(root_stream)
3924
3930
        to_texts.insert_record_stream(from_texts.get_record_stream(
3925
3931
            text_keys, self.target._format._fetch_order,
3982
3988
        cache[basis_id] = basis_tree
3983
3989
        del basis_tree # We don't want to hang on to it here
3984
3990
        hints = []
3985
 
        if self._converting_to_rich_root and len(revision_ids) > 100:
3986
 
            a_graph = _mod_fetch._get_rich_root_heads_graph(self.source,
3987
 
                                                            revision_ids)
3988
 
        else:
3989
 
            a_graph = None
 
3991
        a_graph = None
3990
3992
 
3991
3993
        for offset in range(0, len(revision_ids), batch_size):
3992
3994
            self.target.start_write_group()
3994
3996
                pb.update('Transferring revisions', offset,
3995
3997
                          len(revision_ids))
3996
3998
                batch = revision_ids[offset:offset+batch_size]
3997
 
                basis_id = self._fetch_batch(batch, basis_id, cache,
3998
 
                                             a_graph=a_graph)
 
3999
                basis_id = self._fetch_batch(batch, basis_id, cache)
3999
4000
            except:
4000
4001
                self.source._safe_to_return_from_cache = False
4001
4002
                self.target.abort_write_group()
4067
4068
            basis_id = first_rev.parent_ids[0]
4068
4069
            # only valid as a basis if the target has it
4069
4070
            self.target.get_revision(basis_id)
4070
 
            # Try to get a basis tree - if its a ghost it will hit the
 
4071
            # Try to get a basis tree - if it's a ghost it will hit the
4071
4072
            # NoSuchRevision case.
4072
4073
            basis_tree = self.source.revision_tree(basis_id)
4073
4074
        except (IndexError, errors.NoSuchRevision):
4273
4274
                is_resume = False
4274
4275
            try:
4275
4276
                # locked_insert_stream performs a commit|suspend.
4276
 
                return self._locked_insert_stream(stream, src_format, is_resume)
 
4277
                return self._locked_insert_stream(stream, src_format,
 
4278
                    is_resume)
4277
4279
            except:
4278
4280
                self.target_repo.abort_write_group(suppress_errors=True)
4279
4281
                raise
4326
4328
                # required if the serializers are different only in terms of
4327
4329
                # the inventory.
4328
4330
                if src_serializer == to_serializer:
4329
 
                    self.target_repo.revisions.insert_record_stream(
4330
 
                        substream)
 
4331
                    self.target_repo.revisions.insert_record_stream(substream)
4331
4332
                else:
4332
4333
                    self._extract_and_insert_revisions(substream,
4333
4334
                        src_serializer)
4441
4442
        """Create a StreamSource streaming from from_repository."""
4442
4443
        self.from_repository = from_repository
4443
4444
        self.to_format = to_format
 
4445
        self._record_counter = RecordCounter()
4444
4446
 
4445
4447
    def delta_on_metadata(self):
4446
4448
        """Return True if delta's are permitted on metadata streams.