~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Martin Packman
  • Date: 2012-01-05 09:50:04 UTC
  • mfrom: (6424 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6426.
  • Revision ID: martin.packman@canonical.com-20120105095004-mia9xb7y0efmto0v
Merge bzr.dev to resolve conflicts in bzrlib.builtins

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
from __future__ import absolute_import
 
18
 
17
19
from bzrlib.lazy_import import lazy_import
18
20
lazy_import(globals(), """
19
21
import itertools
20
22
import time
21
23
 
22
24
from bzrlib import (
23
 
    bzrdir,
24
25
    config,
25
26
    controldir,
26
27
    debug,
35
36
    gpg,
36
37
    )
37
38
from bzrlib.bundle import serializer
 
39
from bzrlib.i18n import gettext
38
40
""")
39
41
 
40
42
from bzrlib import (
 
43
    bzrdir,
41
44
    errors,
42
45
    registry,
43
46
    symbol_versioning,
73
76
    record_root_entry = True
74
77
    # whether this commit builder supports the record_entry_contents interface
75
78
    supports_record_entry_contents = False
 
79
    # whether this commit builder will automatically update the branch that is
 
80
    # being committed to
 
81
    updates_branch = False
76
82
 
77
 
    def __init__(self, repository, parents, config, timestamp=None,
 
83
    def __init__(self, repository, parents, config_stack, timestamp=None,
78
84
                 timezone=None, committer=None, revprops=None,
79
85
                 revision_id=None, lossy=False):
80
86
        """Initiate a CommitBuilder.
89
95
        :param lossy: Whether to discard data that can not be natively
90
96
            represented, when pushing to a foreign VCS 
91
97
        """
92
 
        self._config = config
 
98
        self._config_stack = config_stack
93
99
        self._lossy = lossy
94
100
 
95
101
        if committer is None:
96
 
            self._committer = self._config.username()
 
102
            self._committer = self._config_stack.get('email')
97
103
        elif not isinstance(committer, unicode):
98
104
            self._committer = committer.decode() # throw if non-ascii
99
105
        else:
280
286
                raise
281
287
            mutter('abort_write_group failed')
282
288
            log_exception_quietly()
283
 
            note('bzr: ERROR (ignored): %s', exc)
 
289
            note(gettext('bzr: ERROR (ignored): %s'), exc)
284
290
        self._write_group = None
285
291
 
286
292
    def _abort_write_group(self):
341
347
        self.control_files.break_lock()
342
348
 
343
349
    @staticmethod
344
 
    def create(a_bzrdir):
345
 
        """Construct the current default format repository in a_bzrdir."""
346
 
        return RepositoryFormat.get_default_format().initialize(a_bzrdir)
 
350
    def create(controldir):
 
351
        """Construct the current default format repository in controldir."""
 
352
        return RepositoryFormat.get_default_format().initialize(controldir)
347
353
 
348
 
    def __init__(self, _format, a_bzrdir, control_files):
 
354
    def __init__(self, _format, controldir, control_files):
349
355
        """instantiate a Repository.
350
356
 
351
357
        :param _format: The format of the repository on disk.
352
 
        :param a_bzrdir: The BzrDir of the repository.
 
358
        :param controldir: The ControlDir of the repository.
353
359
        :param control_files: Control files to use for locking, etc.
354
360
        """
355
361
        # In the future we will have a single api for all stores for
358
364
        super(Repository, self).__init__()
359
365
        self._format = _format
360
366
        # the following are part of the public API for Repository:
361
 
        self.bzrdir = a_bzrdir
 
367
        self.bzrdir = controldir
362
368
        self.control_files = control_files
363
 
        self._transport = control_files._transport
364
 
        self.base = self._transport.base
365
369
        # for tests
366
370
        self._write_group = None
367
371
        # Additional places to query for data.
405
409
        """
406
410
        if self.__class__ is not other.__class__:
407
411
            return False
408
 
        return (self._transport.base == other._transport.base)
 
412
        return (self.control_url == other.control_url)
409
413
 
410
414
    def is_in_write_group(self):
411
415
        """Return True if there is an open write group.
548
552
            def __init__(self):
549
553
                self.first_call = True
550
554
 
551
 
            def __call__(self, bzrdir):
552
 
                # On the first call, the parameter is always the bzrdir
 
555
            def __call__(self, controldir):
 
556
                # On the first call, the parameter is always the controldir
553
557
                # containing the current repo.
554
558
                if not self.first_call:
555
559
                    try:
556
 
                        repository = bzrdir.open_repository()
 
560
                        repository = controldir.open_repository()
557
561
                    except errors.NoRepositoryPresent:
558
562
                        pass
559
563
                    else:
560
564
                        return False, ([], repository)
561
565
                self.first_call = False
562
 
                value = (bzrdir.list_branches(), None)
 
566
                value = (controldir.list_branches(), None)
563
567
                return True, value
564
568
 
565
569
        ret = []
566
 
        for branches, repository in bzrdir.BzrDir.find_bzrdirs(
 
570
        for branches, repository in controldir.ControlDir.find_bzrdirs(
567
571
                self.user_transport, evaluate=Evaluator()):
568
572
            if branches is not None:
569
573
                ret.extend(branches)
603
607
        For instance, if the repository is at URL/.bzr/repository,
604
608
        Repository.open(URL) -> a Repository instance.
605
609
        """
606
 
        control = bzrdir.BzrDir.open(base)
 
610
        control = controldir.ControlDir.open(base)
607
611
        return control.open_repository()
608
612
 
609
613
    def copy_content_into(self, destination, revision_id=None):
640
644
        """
641
645
 
642
646
    def suspend_write_group(self):
 
647
        """Suspend a write group.
 
648
 
 
649
        :raise UnsuspendableWriteGroup: If the write group can not be
 
650
            suspended.
 
651
        :return: List of tokens
 
652
        """
643
653
        raise errors.UnsuspendableWriteGroup(self)
644
654
 
645
655
    def refresh_data(self):
667
677
    def _resume_write_group(self, tokens):
668
678
        raise errors.UnsuspendableWriteGroup(self)
669
679
 
670
 
    def fetch(self, source, revision_id=None, find_ghosts=False,
671
 
            fetch_spec=None):
 
680
    def fetch(self, source, revision_id=None, find_ghosts=False):
672
681
        """Fetch the content required to construct revision_id from source.
673
682
 
674
 
        If revision_id is None and fetch_spec is None, then all content is
675
 
        copied.
 
683
        If revision_id is None, then all content is copied.
676
684
 
677
685
        fetch() may not be used when the repository is in a write group -
678
686
        either finish the current write group before using fetch, or use
684
692
        :param revision_id: If specified, all the content needed for this
685
693
            revision ID will be copied to the target.  Fetch will determine for
686
694
            itself which content needs to be copied.
687
 
        :param fetch_spec: If specified, a SearchResult or
688
 
            PendingAncestryResult that describes which revisions to copy.  This
689
 
            allows copying multiple heads at once.  Mutually exclusive with
690
 
            revision_id.
691
695
        """
692
 
        if fetch_spec is not None and revision_id is not None:
693
 
            raise AssertionError(
694
 
                "fetch_spec and revision_id are mutually exclusive.")
695
696
        if self.is_in_write_group():
696
697
            raise errors.InternalBzrError(
697
698
                "May not fetch while in a write group.")
699
700
        # TODO: lift out to somewhere common with RemoteRepository
700
701
        # <https://bugs.launchpad.net/bzr/+bug/401646>
701
702
        if (self.has_same_location(source)
702
 
            and fetch_spec is None
703
703
            and self._has_same_fallbacks(source)):
704
704
            # check that last_revision is in 'from' and then return a
705
705
            # no-operation.
708
708
                self.get_revision(revision_id)
709
709
            return 0, []
710
710
        inter = InterRepository.get(source, self)
711
 
        return inter.fetch(revision_id=revision_id,
712
 
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
 
711
        return inter.fetch(revision_id=revision_id, find_ghosts=find_ghosts)
713
712
 
714
713
    def create_bundle(self, target, base, fileobj, format=None):
715
714
        return serializer.write_bundle(self, target, base, fileobj, format)
716
715
 
717
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
 
716
    def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
718
717
                           timezone=None, committer=None, revprops=None,
719
718
                           revision_id=None, lossy=False):
720
719
        """Obtain a CommitBuilder for this repository.
721
720
 
722
721
        :param branch: Branch to commit to.
723
722
        :param parents: Revision ids of the parents of the new revision.
724
 
        :param config: Configuration to use.
 
723
        :param config_stack: Configuration stack to use.
725
724
        :param timestamp: Optional timestamp recorded for commit.
726
725
        :param timezone: Optional timezone for timestamp.
727
726
        :param committer: Optional committer to set for commit.
747
746
                repo.unlock()
748
747
 
749
748
    @needs_read_lock
750
 
    def clone(self, a_bzrdir, revision_id=None):
751
 
        """Clone this repository into a_bzrdir using the current format.
 
749
    def clone(self, controldir, revision_id=None):
 
750
        """Clone this repository into controldir using the current format.
752
751
 
753
752
        Currently no check is made that the format of this repository and
754
753
        the bzrdir format are compatible. FIXME RBC 20060201.
757
756
        """
758
757
        # TODO: deprecate after 0.16; cloning this with all its settings is
759
758
        # probably not very useful -- mbp 20070423
760
 
        dest_repo = self._create_sprouting_repo(a_bzrdir, shared=self.is_shared())
 
759
        dest_repo = self._create_sprouting_repo(
 
760
            controldir, shared=self.is_shared())
761
761
        self.copy_content_into(dest_repo, revision_id)
762
762
        return dest_repo
763
763
 
930
930
        parent_ids.discard(_mod_revision.NULL_REVISION)
931
931
        return parent_ids
932
932
 
933
 
    def fileids_altered_by_revision_ids(self, revision_ids):
934
 
        """Find the file ids and versions affected by revisions.
935
 
 
936
 
        :param revisions: an iterable containing revision ids.
937
 
        :return: a dictionary mapping altered file-ids to an iterable of
938
 
            revision_ids. Each altered file-ids has the exact revision_ids
939
 
            that altered it listed explicitly.
940
 
        """
941
 
        raise NotImplementedError(self.fileids_altered_by_revision_ids)
942
 
 
943
933
    def iter_files_bytes(self, desired_files):
944
934
        """Iterate through file versions.
945
935
 
1159
1149
                [parents_provider, other_repository._make_parents_provider()])
1160
1150
        return graph.Graph(parents_provider)
1161
1151
 
1162
 
    def revision_ids_to_search_result(self, result_set):
1163
 
        """Convert a set of revision ids to a graph SearchResult."""
1164
 
        result_parents = set()
1165
 
        for parents in self.get_graph().get_parent_map(
1166
 
            result_set).itervalues():
1167
 
            result_parents.update(parents)
1168
 
        included_keys = result_set.intersection(result_parents)
1169
 
        start_keys = result_set.difference(included_keys)
1170
 
        exclude_keys = result_parents.difference(result_set)
1171
 
        result = graph.SearchResult(start_keys, exclude_keys,
1172
 
            len(result_set), result_set)
1173
 
        return result
1174
 
 
1175
1152
    @needs_write_lock
1176
1153
    def set_make_working_trees(self, new_value):
1177
1154
        """Set the policy flag for making working trees when creating branches.
1195
1172
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1196
1173
 
1197
1174
    @needs_read_lock
1198
 
    def verify_revision(self, revision_id, gpg_strategy):
 
1175
    def verify_revision_signature(self, revision_id, gpg_strategy):
1199
1176
        """Verify the signature on a revision.
1200
1177
        
1201
1178
        :param revision_id: the revision to verify
1316
1293
        """Returns the policy for making working trees on new branches."""
1317
1294
        return not self._transport.has('no-working-trees')
1318
1295
 
 
1296
    @needs_write_lock
 
1297
    def update_feature_flags(self, updated_flags):
 
1298
        """Update the feature flags for this branch.
 
1299
 
 
1300
        :param updated_flags: Dictionary mapping feature names to necessities
 
1301
            A necessity can be None to indicate the feature should be removed
 
1302
        """
 
1303
        self._format._update_feature_flags(updated_flags)
 
1304
        self.control_transport.put_bytes('format', self._format.as_string())
 
1305
 
1319
1306
 
1320
1307
class RepositoryFormatRegistry(controldir.ControlComponentFormatRegistry):
1321
1308
    """Repository format registry."""
1322
1309
 
1323
1310
    def get_default(self):
1324
1311
        """Return the current default format."""
1325
 
        from bzrlib import bzrdir
1326
 
        return bzrdir.format_registry.make_bzrdir('default').repository_format
 
1312
        return controldir.format_registry.make_bzrdir('default').repository_format
1327
1313
 
1328
1314
 
1329
1315
network_format_registry = registry.FormatRegistry()
1372
1358
    created.
1373
1359
 
1374
1360
    Common instance attributes:
1375
 
    _matchingbzrdir - the bzrdir format that the repository format was
 
1361
    _matchingbzrdir - the controldir format that the repository format was
1376
1362
    originally written to work with. This can be used if manually
1377
1363
    constructing a bzrdir and repository, or more commonly for test suite
1378
1364
    parameterization.
1418
1404
    rich_root_data = None
1419
1405
    # Does this format support explicitly versioned directories?
1420
1406
    supports_versioned_directories = None
 
1407
    # Can other repositories be nested into one of this format?
 
1408
    supports_nesting_repositories = None
 
1409
    # Is it possible for revisions to be present without being referenced
 
1410
    # somewhere ?
 
1411
    supports_unreferenced_revisions = None
1421
1412
 
1422
1413
    def __repr__(self):
1423
1414
        return "%s()" % self.__class__.__name__
1430
1421
        return not self == other
1431
1422
 
1432
1423
    @classmethod
1433
 
    def find_format(klass, a_bzrdir):
1434
 
        """Return the format for the repository object in a_bzrdir.
1435
 
 
1436
 
        This is used by bzr native formats that have a "format" file in
1437
 
        the repository.  Other methods may be used by different types of
1438
 
        control directory.
1439
 
        """
1440
 
        try:
1441
 
            transport = a_bzrdir.get_repository_transport(None)
1442
 
            format_string = transport.get_bytes("format")
1443
 
            return format_registry.get(format_string)
1444
 
        except errors.NoSuchFile:
1445
 
            raise errors.NoRepositoryPresent(a_bzrdir)
1446
 
        except KeyError:
1447
 
            raise errors.UnknownFormatError(format=format_string,
1448
 
                                            kind='repository')
1449
 
 
1450
 
    @classmethod
1451
1424
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1452
1425
    def register_format(klass, format):
1453
1426
        format_registry.register(format)
1463
1436
        """Return the current default format."""
1464
1437
        return format_registry.get_default()
1465
1438
 
1466
 
    def get_format_string(self):
1467
 
        """Return the ASCII format string that identifies this format.
1468
 
 
1469
 
        Note that in pre format ?? repositories the format string is
1470
 
        not permitted nor written to disk.
1471
 
        """
1472
 
        raise NotImplementedError(self.get_format_string)
1473
 
 
1474
1439
    def get_format_description(self):
1475
1440
        """Return the short description for this format."""
1476
1441
        raise NotImplementedError(self.get_format_description)
1477
1442
 
1478
 
    def initialize(self, a_bzrdir, shared=False):
1479
 
        """Initialize a repository of this format in a_bzrdir.
 
1443
    def initialize(self, controldir, shared=False):
 
1444
        """Initialize a repository of this format in controldir.
1480
1445
 
1481
 
        :param a_bzrdir: The bzrdir to put the new repository in it.
 
1446
        :param controldir: The controldir to put the new repository in it.
1482
1447
        :param shared: The repository should be initialized as a sharable one.
1483
1448
        :returns: The new repository object.
1484
1449
 
1485
1450
        This may raise UninitializableFormat if shared repository are not
1486
 
        compatible the a_bzrdir.
 
1451
        compatible the controldir.
1487
1452
        """
1488
1453
        raise NotImplementedError(self.initialize)
1489
1454
 
1525
1490
                'Does not support nested trees', target_format,
1526
1491
                from_format=self)
1527
1492
 
1528
 
    def open(self, a_bzrdir, _found=False):
1529
 
        """Return an instance of this format for the bzrdir a_bzrdir.
 
1493
    def open(self, controldir, _found=False):
 
1494
        """Return an instance of this format for a controldir.
1530
1495
 
1531
1496
        _found is a private parameter, do not use it.
1532
1497
        """
1533
1498
        raise NotImplementedError(self.open)
1534
1499
 
1535
 
    def _run_post_repo_init_hooks(self, repository, a_bzrdir, shared):
1536
 
        from bzrlib.bzrdir import BzrDir, RepoInitHookParams
1537
 
        hooks = BzrDir.hooks['post_repo_init']
 
1500
    def _run_post_repo_init_hooks(self, repository, controldir, shared):
 
1501
        from bzrlib.controldir import ControlDir, RepoInitHookParams
 
1502
        hooks = ControlDir.hooks['post_repo_init']
1538
1503
        if not hooks:
1539
1504
            return
1540
 
        params = RepoInitHookParams(repository, self, a_bzrdir, shared)
 
1505
        params = RepoInitHookParams(repository, self, controldir, shared)
1541
1506
        for hook in hooks:
1542
1507
            hook(params)
1543
1508
 
1544
1509
 
1545
 
class MetaDirRepositoryFormat(RepositoryFormat):
 
1510
class RepositoryFormatMetaDir(bzrdir.BzrFormat, RepositoryFormat):
1546
1511
    """Common base class for the new repositories using the metadir layout."""
1547
1512
 
1548
1513
    rich_root_data = False
1549
1514
    supports_tree_reference = False
1550
1515
    supports_external_lookups = False
1551
1516
    supports_leaving_lock = True
 
1517
    supports_nesting_repositories = True
1552
1518
 
1553
1519
    @property
1554
1520
    def _matchingbzrdir(self):
1557
1523
        return matching
1558
1524
 
1559
1525
    def __init__(self):
1560
 
        super(MetaDirRepositoryFormat, self).__init__()
 
1526
        RepositoryFormat.__init__(self)
 
1527
        bzrdir.BzrFormat.__init__(self)
1561
1528
 
1562
1529
    def _create_control_files(self, a_bzrdir):
1563
1530
        """Create the required files and the initial control_files object."""
1587
1554
        finally:
1588
1555
            control_files.unlock()
1589
1556
 
1590
 
    def network_name(self):
1591
 
        """Metadir formats have matching disk and network format strings."""
1592
 
        return self.get_format_string()
 
1557
    @classmethod
 
1558
    def find_format(klass, a_bzrdir):
 
1559
        """Return the format for the repository object in a_bzrdir.
 
1560
 
 
1561
        This is used by bzr native formats that have a "format" file in
 
1562
        the repository.  Other methods may be used by different types of
 
1563
        control directory.
 
1564
        """
 
1565
        try:
 
1566
            transport = a_bzrdir.get_repository_transport(None)
 
1567
            format_string = transport.get_bytes("format")
 
1568
        except errors.NoSuchFile:
 
1569
            raise errors.NoRepositoryPresent(a_bzrdir)
 
1570
        return klass._find_format(format_registry, 'repository', format_string)
 
1571
 
 
1572
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
1573
            basedir=None):
 
1574
        RepositoryFormat.check_support_status(self,
 
1575
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
 
1576
            basedir=basedir)
 
1577
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
 
1578
            recommend_upgrade=recommend_upgrade, basedir=basedir)
1593
1579
 
1594
1580
 
1595
1581
# formats which have no format string are not discoverable or independently
1596
1582
# creatable on disk, so are not registered in format_registry.  They're
1597
1583
# all in bzrlib.repofmt.knitreponow.  When an instance of one of these is
1598
 
# needed, it's constructed directly by the BzrDir.  Non-native formats where
 
1584
# needed, it's constructed directly by the ControlDir.  Non-native formats where
1599
1585
# the repository is not separately opened are similar.
1600
1586
 
1601
1587
format_registry.register_lazy(
1712
1698
        self.target.fetch(self.source, revision_id=revision_id)
1713
1699
 
1714
1700
    @needs_write_lock
1715
 
    def fetch(self, revision_id=None, find_ghosts=False,
1716
 
            fetch_spec=None):
 
1701
    def fetch(self, revision_id=None, find_ghosts=False):
1717
1702
        """Fetch the content required to construct revision_id.
1718
1703
 
1719
1704
        The content is copied from self.source to self.target.
1799
1784
        # trigger an assertion if not such
1800
1785
        repo._format.get_format_string()
1801
1786
        self.repo_dir = repo.bzrdir
1802
 
        pb.update('Moving repository to repository.backup')
 
1787
        pb.update(gettext('Moving repository to repository.backup'))
1803
1788
        self.repo_dir.transport.move('repository', 'repository.backup')
1804
1789
        backup_transport =  self.repo_dir.transport.clone('repository.backup')
1805
1790
        repo._format.check_conversion_target(self.target_format)
1806
1791
        self.source_repo = repo._format.open(self.repo_dir,
1807
1792
            _found=True,
1808
1793
            _override_transport=backup_transport)
1809
 
        pb.update('Creating new repository')
 
1794
        pb.update(gettext('Creating new repository'))
1810
1795
        converted = self.target_format.initialize(self.repo_dir,
1811
1796
                                                  self.source_repo.is_shared())
1812
1797
        converted.lock_write()
1813
1798
        try:
1814
 
            pb.update('Copying content')
 
1799
            pb.update(gettext('Copying content'))
1815
1800
            self.source_repo.copy_content_into(converted)
1816
1801
        finally:
1817
1802
            converted.unlock()
1818
 
        pb.update('Deleting old repository content')
 
1803
        pb.update(gettext('Deleting old repository content'))
1819
1804
        self.repo_dir.transport.delete_tree('repository.backup')
1820
 
        ui.ui_factory.note('repository converted')
 
1805
        ui.ui_factory.note(gettext('repository converted'))
1821
1806
        pb.finished()
1822
1807
 
1823
1808