~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-09-17 21:19:56 UTC
  • mfrom: (1997.1.6 bind-does-not-push-or-pull)
  • Revision ID: pqm@pqm.ubuntu.com-20060917211956-6e30d07da410fd1a
(Robert Collins) Change the Branch bind method to just bind rather than binding and pushing (fixes #43744 and #39542)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005, 2006 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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
20
directories.
21
21
"""
22
22
 
 
23
# TODO: remove unittest dependency; put that stuff inside the test suite
 
24
 
 
25
# TODO: The Format probe_transport seems a bit redundant with just trying to
 
26
# open the bzrdir. -- mbp
 
27
#
 
28
# TODO: Can we move specific formats into separate modules to make this file
 
29
# smaller?
 
30
 
23
31
from copy import deepcopy
 
32
from cStringIO import StringIO
24
33
import os
25
 
from cStringIO import StringIO
 
34
from stat import S_ISDIR
26
35
from unittest import TestSuite
27
36
 
28
37
import bzrlib
36
45
                            sha_strings,
37
46
                            sha_string,
38
47
                            )
 
48
import bzrlib.revision
39
49
from bzrlib.store.revision.text import TextRevisionStore
40
50
from bzrlib.store.text import TextStore
41
51
from bzrlib.store.versioned import WeaveStore
42
 
from bzrlib.symbol_versioning import *
43
52
from bzrlib.trace import mutter
44
53
from bzrlib.transactions import WriteTransaction
45
54
from bzrlib.transport import get_transport
84
93
        """Return true if this bzrdir is one whose format we can convert from."""
85
94
        return True
86
95
 
 
96
    def check_conversion_target(self, target_format):
 
97
        target_repo_format = target_format.repository_format
 
98
        source_repo_format = self._format.repository_format
 
99
        source_repo_format.check_conversion_target(target_repo_format)
 
100
 
87
101
    @staticmethod
88
102
    def _check_supported(format, allow_unsupported):
89
103
        """Check whether format is a supported format.
92
106
        """
93
107
        if not allow_unsupported and not format.is_supported():
94
108
            # see open_downlevel to open legacy branches.
95
 
            raise errors.UnsupportedFormatError(
96
 
                    'sorry, format %s not supported' % format,
97
 
                    ['use a different bzr version',
98
 
                     'or remove the .bzr directory'
99
 
                     ' and "bzr init" again'])
 
109
            raise errors.UnsupportedFormatError(format=format)
100
110
 
101
111
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
102
112
        """Clone this bzrdir and its contents to url verbatim.
294
304
        This will use the current default BzrDirFormat, and use whatever 
295
305
        repository format that that uses for bzrdirformat.create_repository.
296
306
 
297
 
        ;param shared: Create a shared repository rather than a standalone
 
307
        :param shared: Create a shared repository rather than a standalone
298
308
                       repository.
299
309
        The Repository object is returned.
300
310
 
303
313
        that child class desires.
304
314
        """
305
315
        bzrdir = BzrDir.create(base)
306
 
        return bzrdir.create_repository()
 
316
        return bzrdir.create_repository(shared)
307
317
 
308
318
    @staticmethod
309
319
    def create_standalone_workingtree(base):
315
325
        repository format that that uses for bzrdirformat.create_workingtree,
316
326
        create_branch and create_repository.
317
327
 
318
 
        The WorkingTree object is returned.
 
328
        :return: The WorkingTree object.
319
329
        """
320
330
        t = get_transport(safe_unicode(base))
321
331
        if not isinstance(t, LocalTransport):
462
472
        _unsupported is a private parameter to the BzrDir class.
463
473
        """
464
474
        t = get_transport(base)
465
 
        mutter("trying to open %r with transport %r", base, t)
466
 
        format = BzrDirFormat.find_format(t)
 
475
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
 
476
 
 
477
    @staticmethod
 
478
    def open_from_transport(transport, _unsupported=False):
 
479
        """Open a bzrdir within a particular directory.
 
480
 
 
481
        :param transport: Transport containing the bzrdir.
 
482
        :param _unsupported: private.
 
483
        """
 
484
        format = BzrDirFormat.find_format(transport)
467
485
        BzrDir._check_supported(format, _unsupported)
468
 
        return format.open(t, _found=True)
 
486
        return format.open(transport, _found=True)
469
487
 
470
488
    def open_branch(self, unsupported=False):
471
489
        """Open the branch object at this BzrDir if one is present.
505
523
        url = a_transport.base
506
524
        while True:
507
525
            try:
508
 
                format = BzrDirFormat.find_format(a_transport)
509
 
                BzrDir._check_supported(format, False)
510
 
                return format.open(a_transport), urlutils.unescape(a_transport.relpath(url))
 
526
                result = BzrDir.open_from_transport(a_transport)
 
527
                return result, urlutils.unescape(a_transport.relpath(url))
511
528
            except errors.NotBranchError, e:
512
 
                ## mutter('not a branch in: %r %s', a_transport.base, e)
513
529
                pass
514
530
            new_t = a_transport.clone('..')
515
531
            if new_t.base == a_transport.base:
565
581
        except errors.NoWorkingTree:
566
582
            return False
567
583
 
 
584
    def cloning_metadir(self, basis=None):
 
585
        """Produce a metadir suitable for cloning with"""
 
586
        def related_repository(bzrdir):
 
587
            try:
 
588
                branch = bzrdir.open_branch()
 
589
                return branch.repository
 
590
            except errors.NotBranchError:
 
591
                source_branch = None
 
592
                return bzrdir.open_repository()
 
593
        result_format = self._format.__class__()
 
594
        try:
 
595
            try:
 
596
                source_repository = related_repository(self)
 
597
            except errors.NoRepositoryPresent:
 
598
                if basis is None:
 
599
                    raise
 
600
                source_repository = related_repository(self)
 
601
            result_format.repository_format = source_repository._format
 
602
        except errors.NoRepositoryPresent:
 
603
            pass
 
604
        return result_format
 
605
 
568
606
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
569
607
        """Create a copy of this bzrdir prepared for use as a new line of
570
608
        development.
580
618
            itself to download less data.
581
619
        """
582
620
        self._make_tail(url)
583
 
        result = self._format.initialize(url)
 
621
        cloning_format = self.cloning_metadir(basis)
 
622
        result = cloning_format.initialize(url)
584
623
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
585
624
        try:
586
625
            source_branch = self.open_branch()
617
656
                # XXX FIXME RBC 20060214 need tests for this when the basis
618
657
                # is incomplete
619
658
                result_repo.fetch(basis_repo, revision_id=revision_id)
620
 
            result_repo.fetch(source_repository, revision_id=revision_id)
 
659
            if source_repository is not None:
 
660
                result_repo.fetch(source_repository, revision_id=revision_id)
621
661
        if source_branch is not None:
622
662
            source_branch.sprout(result, revision_id=revision_id)
623
663
        else:
687
727
        # done on this format anyway. So - acceptable wart.
688
728
        result = self.open_workingtree()
689
729
        if revision_id is not None:
690
 
            result.set_last_revision(revision_id)
 
730
            if revision_id == bzrlib.revision.NULL_REVISION:
 
731
                result.set_parent_ids([])
 
732
            else:
 
733
                result.set_parent_ids([revision_id])
691
734
        return result
692
735
 
693
736
    def get_branch_transport(self, branch_format):
735
778
        self._check_supported(format, unsupported)
736
779
        return format.open(self, _found=True)
737
780
 
738
 
    def sprout(self, url, revision_id=None, basis=None):
 
781
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
739
782
        """See BzrDir.sprout()."""
740
783
        from bzrlib.workingtree import WorkingTreeFormat2
741
784
        self._make_tail(url)
972
1015
        """Return the .bzrdir style transport present at URL."""
973
1016
        try:
974
1017
            format_string = transport.get(".bzr/branch-format").read()
 
1018
        except errors.NoSuchFile:
 
1019
            raise errors.NotBranchError(path=transport.base)
 
1020
 
 
1021
        try:
975
1022
            return klass._formats[format_string]
976
 
        except errors.NoSuchFile:
977
 
            raise errors.NotBranchError(path=transport.base)
978
1023
        except KeyError:
979
 
            raise errors.UnknownFormatError(format_string)
 
1024
            raise errors.UnknownFormatError(format=format_string)
980
1025
 
981
1026
    @classmethod
982
1027
    def get_default_format(klass):
1052
1097
        """
1053
1098
        return True
1054
1099
 
 
1100
    def same_model(self, target_format):
 
1101
        return (self.repository_format.rich_root_data == 
 
1102
            target_format.rich_root_data)
 
1103
 
1055
1104
    @classmethod
1056
1105
    def known_formats(klass):
1057
1106
        """Return all the known formats.
1173
1222
    def __return_repository_format(self):
1174
1223
        """Circular import protection."""
1175
1224
        from bzrlib.repository import RepositoryFormat4
1176
 
        return RepositoryFormat4(self)
 
1225
        return RepositoryFormat4()
1177
1226
    repository_format = property(__return_repository_format)
1178
1227
 
1179
1228
 
1217
1266
        result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1218
1267
        RepositoryFormat5().initialize(result, _internal=True)
1219
1268
        if not _cloning:
1220
 
            BzrBranchFormat4().initialize(result)
1221
 
            WorkingTreeFormat2().initialize(result)
 
1269
            branch = BzrBranchFormat4().initialize(result)
 
1270
            try:
 
1271
                WorkingTreeFormat2().initialize(result)
 
1272
            except errors.NotLocalUrl:
 
1273
                # Even though we can't access the working tree, we need to
 
1274
                # create its control files.
 
1275
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1222
1276
        return result
1223
1277
 
1224
1278
    def _open(self, transport):
1228
1282
    def __return_repository_format(self):
1229
1283
        """Circular import protection."""
1230
1284
        from bzrlib.repository import RepositoryFormat5
1231
 
        return RepositoryFormat5(self)
 
1285
        return RepositoryFormat5()
1232
1286
    repository_format = property(__return_repository_format)
1233
1287
 
1234
1288
 
1271
1325
        result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1272
1326
        RepositoryFormat6().initialize(result, _internal=True)
1273
1327
        if not _cloning:
1274
 
            BzrBranchFormat4().initialize(result)
 
1328
            branch = BzrBranchFormat4().initialize(result)
1275
1329
            try:
1276
1330
                WorkingTreeFormat2().initialize(result)
1277
1331
            except errors.NotLocalUrl:
1278
 
                # emulate pre-check behaviour for working tree and silently 
1279
 
                # fail.
1280
 
                pass
 
1332
                # Even though we can't access the working tree, we need to
 
1333
                # create its control files.
 
1334
                WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1281
1335
        return result
1282
1336
 
1283
1337
    def _open(self, transport):
1287
1341
    def __return_repository_format(self):
1288
1342
        """Circular import protection."""
1289
1343
        from bzrlib.repository import RepositoryFormat6
1290
 
        return RepositoryFormat6(self)
 
1344
        return RepositoryFormat6()
1291
1345
    repository_format = property(__return_repository_format)
1292
1346
 
1293
1347
 
1376
1430
        return result
1377
1431
 
1378
1432
 
1379
 
class ScratchDir(BzrDir6):
1380
 
    """Special test class: a bzrdir that cleans up itself..
1381
 
 
1382
 
    >>> d = ScratchDir()
1383
 
    >>> base = d.transport.base
1384
 
    >>> isdir(base)
1385
 
    True
1386
 
    >>> b.transport.__del__()
1387
 
    >>> isdir(base)
1388
 
    False
1389
 
    """
1390
 
 
1391
 
    def __init__(self, files=[], dirs=[], transport=None):
1392
 
        """Make a test branch.
1393
 
 
1394
 
        This creates a temporary directory and runs init-tree in it.
1395
 
 
1396
 
        If any files are listed, they are created in the working copy.
1397
 
        """
1398
 
        if transport is None:
1399
 
            transport = bzrlib.transport.local.ScratchTransport()
1400
 
            # local import for scope restriction
1401
 
            BzrDirFormat6().initialize(transport.base)
1402
 
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1403
 
            self.create_repository()
1404
 
            self.create_branch()
1405
 
            self.create_workingtree()
1406
 
        else:
1407
 
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1408
 
 
1409
 
        # BzrBranch creates a clone to .bzr and then forgets about the
1410
 
        # original transport. A ScratchTransport() deletes itself and
1411
 
        # everything underneath it when it goes away, so we need to
1412
 
        # grab a local copy to prevent that from happening
1413
 
        self._transport = transport
1414
 
 
1415
 
        for d in dirs:
1416
 
            self._transport.mkdir(d)
1417
 
            
1418
 
        for f in files:
1419
 
            self._transport.put(f, 'content of %s' % f)
1420
 
 
1421
 
    def clone(self):
1422
 
        """
1423
 
        >>> orig = ScratchDir(files=["file1", "file2"])
1424
 
        >>> os.listdir(orig.base)
1425
 
        [u'.bzr', u'file1', u'file2']
1426
 
        >>> clone = orig.clone()
1427
 
        >>> if os.name != 'nt':
1428
 
        ...   os.path.samefile(orig.base, clone.base)
1429
 
        ... else:
1430
 
        ...   orig.base == clone.base
1431
 
        ...
1432
 
        False
1433
 
        >>> os.listdir(clone.base)
1434
 
        [u'.bzr', u'file1', u'file2']
1435
 
        """
1436
 
        from shutil import copytree
1437
 
        from bzrlib.osutils import mkdtemp
1438
 
        base = mkdtemp()
1439
 
        os.rmdir(base)
1440
 
        copytree(self.base, base, symlinks=True)
1441
 
        return ScratchDir(
1442
 
            transport=bzrlib.transport.local.ScratchTransport(base))
1443
 
 
1444
 
 
1445
1433
class Converter(object):
1446
1434
    """Converts a disk format object from one format to another."""
1447
1435
 
1535
1523
        inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1536
1524
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1537
1525
        # FIXME inventory is a working tree change.
1538
 
        self.branch.control_files.put('inventory', new_inv_xml)
 
1526
        self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1539
1527
 
1540
1528
    def _write_all_weaves(self):
1541
1529
        controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1598
1586
        assert rev_id not in self.converted_revs
1599
1587
        old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1600
1588
        inv = serializer_v4.read_inventory_from_string(old_inv_xml)
 
1589
        inv.revision_id = rev_id
1601
1590
        rev = self.revisions[rev_id]
1602
1591
        if rev.inventory_sha1:
1603
1592
            assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1623
1612
    def _store_new_weave(self, rev, inv, present_parents):
1624
1613
        # the XML is now updated with text versions
1625
1614
        if __debug__:
1626
 
            for file_id in inv:
1627
 
                ie = inv[file_id]
1628
 
                if ie.kind == 'root_directory':
1629
 
                    continue
1630
 
                assert hasattr(ie, 'revision'), \
 
1615
            entries = inv.iter_entries()
 
1616
            entries.next()
 
1617
            for path, ie in entries:
 
1618
                assert getattr(ie, 'revision', None) is not None, \
1631
1619
                    'no revision on {%s} in {%s}' % \
1632
1620
                    (file_id, rev.revision_id)
1633
1621
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
1645
1633
        mutter('converting texts of revision {%s}',
1646
1634
               rev_id)
1647
1635
        parent_invs = map(self._load_updated_inventory, present_parents)
1648
 
        for file_id in inv:
1649
 
            ie = inv[file_id]
 
1636
        entries = inv.iter_entries()
 
1637
        entries.next()
 
1638
        for path, ie in entries:
1650
1639
            self._convert_file_version(rev, ie, parent_invs)
1651
1640
 
1652
1641
    def _convert_file_version(self, rev, ie, parent_invs):
1655
1644
        The file needs to be added into the weave if it is a merge
1656
1645
        of >=2 parents or if it's changed from its parent.
1657
1646
        """
1658
 
        if ie.kind == 'root_directory':
1659
 
            return
1660
1647
        file_id = ie.file_id
1661
1648
        rev_id = rev.revision_id
1662
1649
        w = self.text_weaves.get(file_id)
1811
1798
        for entry in branch_files:
1812
1799
            self.move_entry('branch', entry)
1813
1800
 
1814
 
        self.step('Upgrading working tree')
1815
 
        self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1816
 
        self.make_lock('checkout')
1817
 
        self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1818
 
        self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1819
1801
        checkout_files = [('pending-merges', True),
1820
1802
                          ('inventory', True),
1821
1803
                          ('stat-cache', False)]
1822
 
        for entry in checkout_files:
1823
 
            self.move_entry('checkout', entry)
1824
 
        if last_revision is not None:
1825
 
            self.bzrdir._control_files.put_utf8('checkout/last-revision',
1826
 
                                                last_revision)
1827
 
        self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
 
1804
        # If a mandatory checkout file is not present, the branch does not have
 
1805
        # a functional checkout. Do not create a checkout in the converted
 
1806
        # branch.
 
1807
        for name, mandatory in checkout_files:
 
1808
            if mandatory and name not in bzrcontents:
 
1809
                has_checkout = False
 
1810
                break
 
1811
        else:
 
1812
            has_checkout = True
 
1813
        if not has_checkout:
 
1814
            self.pb.note('No working tree.')
 
1815
            # If some checkout files are there, we may as well get rid of them.
 
1816
            for name, mandatory in checkout_files:
 
1817
                if name in bzrcontents:
 
1818
                    self.bzrdir.transport.delete(name)
 
1819
        else:
 
1820
            self.step('Upgrading working tree')
 
1821
            self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
 
1822
            self.make_lock('checkout')
 
1823
            self.put_format(
 
1824
                'checkout', bzrlib.workingtree.WorkingTreeFormat3())
 
1825
            self.bzrdir.transport.delete_multi(
 
1826
                self.garbage_inventories, self.pb)
 
1827
            for entry in checkout_files:
 
1828
                self.move_entry('checkout', entry)
 
1829
            if last_revision is not None:
 
1830
                self.bzrdir._control_files.put_utf8(
 
1831
                    'checkout/last-revision', last_revision)
 
1832
        self.bzrdir._control_files.put_utf8(
 
1833
            'branch-format', BzrDirMetaFormat1().get_format_string())
1828
1834
        return BzrDir.open(self.bzrdir.root_transport.base)
1829
1835
 
1830
1836
    def make_lock(self, name):