~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
At format 7 this was split out into Branch, Repository and Checkout control
20
20
directories.
 
21
 
 
22
Note: This module has a lot of ``open`` functions/methods that return
 
23
references to in-memory objects. As a rule, there are no matching ``close``
 
24
methods. To free any associated resources, simply stop referencing the
 
25
objects returned.
21
26
"""
22
27
 
23
 
# TODO: Can we move specific formats into separate modules to make this file
24
 
# smaller?
 
28
# TODO: Move old formats into a plugin to make this file smaller.
25
29
 
26
30
from cStringIO import StringIO
27
31
import os
35
39
import bzrlib
36
40
from bzrlib import (
37
41
    errors,
 
42
    graph,
38
43
    lockable_files,
39
44
    lockdir,
40
45
    registry,
86
91
    transport
87
92
        the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
88
93
    root_transport
89
 
        a transport connected to the directory this bzr was opened from.
 
94
        a transport connected to the directory this bzr was opened from
 
95
        (i.e. the parent directory holding the .bzr directory).
90
96
    """
91
97
 
92
98
    def break_lock(self):
149
155
    def clone(self, url, revision_id=None, force_new_repo=False):
150
156
        """Clone this bzrdir and its contents to url verbatim.
151
157
 
152
 
        If urls last component does not exist, it will be created.
 
158
        If url's last component does not exist, it will be created.
153
159
 
154
160
        if revision_id is not None, then the clone operation may tune
155
161
            itself to download less data.
218
224
    def create(cls, base, format=None, possible_transports=None):
219
225
        """Create a new BzrDir at the url 'base'.
220
226
        
221
 
        This will call the current default formats initialize with base
222
 
        as the only parameter.
223
 
 
224
227
        :param format: If supplied, the format of branch to create.  If not
225
228
            supplied, the default is used.
226
229
        :param possible_transports: If supplied, a list of transports that 
233
236
        t.ensure_base()
234
237
        if format is None:
235
238
            format = BzrDirFormat.get_default_format()
236
 
        return format.initialize(base, possible_transports)
 
239
        return format.initialize_on_transport(t)
237
240
 
238
241
    def create_branch(self):
239
242
        """Create a branch in this BzrDir.
240
243
 
241
 
        The bzrdirs format will control what branch format is created.
 
244
        The bzrdir's format will control what branch format is created.
242
245
        For more control see BranchFormatXX.create(a_bzrdir).
243
246
        """
244
247
        raise NotImplementedError(self.create_branch)
245
248
 
 
249
    def destroy_branch(self):
 
250
        """Destroy the branch in this BzrDir"""
 
251
        raise NotImplementedError(self.destroy_branch)
 
252
 
246
253
    @staticmethod
247
254
    def create_branch_and_repo(base, force_new_repo=False, format=None):
248
255
        """Create a new BzrDir, Branch and Repository at the url 'base'.
249
256
 
250
 
        This will use the current default BzrDirFormat, and use whatever 
 
257
        This will use the current default BzrDirFormat unless one is
 
258
        specified, and use whatever 
251
259
        repository format that that uses via bzrdir.create_branch and
252
260
        create_repository. If a shared repository is available that is used
253
261
        preferentially.
256
264
 
257
265
        :param base: The URL to create the branch at.
258
266
        :param force_new_repo: If True a new repository is always created.
 
267
        :param format: If supplied, the format of branch to create.  If not
 
268
            supplied, the default is used.
259
269
        """
260
270
        bzrdir = BzrDir.create(base, format)
261
271
        bzrdir._find_or_create_repository(force_new_repo)
280
290
        if possible, can be told explicitly whether to create a working tree or
281
291
        not.
282
292
 
283
 
        This will use the current default BzrDirFormat, and use whatever 
 
293
        This will use the current default BzrDirFormat unless one is
 
294
        specified, and use whatever 
284
295
        repository format that that uses via bzrdir.create_branch and
285
296
        create_repository. If a shared repository is available that is used
286
297
        preferentially. Whatever repository is used, its tree creation policy
295
306
        :param force_new_repo: If True a new repository is always created.
296
307
        :param force_new_tree: If True or False force creation of a tree or 
297
308
                               prevent such creation respectively.
298
 
        :param format: Override for the for the bzrdir format to create.
 
309
        :param format: Override for the bzrdir format to create.
299
310
        :param possible_transports: An optional reusable transports list.
300
311
        """
301
312
        if force_new_tree:
343
354
 
344
355
        'base' must be a local path or a file:// url.
345
356
 
346
 
        This will use the current default BzrDirFormat, and use whatever 
 
357
        This will use the current default BzrDirFormat unless one is
 
358
        specified, and use whatever 
347
359
        repository format that that uses for bzrdirformat.create_workingtree,
348
360
        create_branch and create_repository.
349
361
 
 
362
        :param format: Override for the bzrdir format to create.
350
363
        :return: The WorkingTree object.
351
364
        """
352
365
        t = get_transport(base)
364
377
        """
365
378
        raise NotImplementedError(self.create_workingtree)
366
379
 
367
 
    def retire_bzrdir(self):
 
380
    def retire_bzrdir(self, limit=10000):
368
381
        """Permanently disable the bzrdir.
369
382
 
370
383
        This is done by renaming it to give the user some ability to recover
372
385
 
373
386
        This will have horrible consequences if anyone has anything locked or
374
387
        in use.
 
388
        :param limit: number of times to retry
375
389
        """
376
 
        for i in xrange(10000):
 
390
        i  = 0
 
391
        while True:
377
392
            try:
378
393
                to_path = '.bzr.retired.%d' % i
379
394
                self.root_transport.rename('.bzr', to_path)
380
395
                note("renamed %s to %s"
381
396
                    % (self.root_transport.abspath('.bzr'), to_path))
382
 
                break
 
397
                return
383
398
            except (errors.TransportError, IOError, errors.PathError):
384
 
                pass
 
399
                i += 1
 
400
                if i > limit:
 
401
                    raise
 
402
                else:
 
403
                    pass
385
404
 
386
405
    def destroy_workingtree(self):
387
406
        """Destroy the working tree at this BzrDir.
399
418
        raise NotImplementedError(self.destroy_workingtree_metadata)
400
419
 
401
420
    def find_repository(self):
402
 
        """Find the repository that should be used for a_bzrdir.
 
421
        """Find the repository that should be used.
403
422
 
404
423
        This does not require a branch as we use it to find the repo for
405
424
        new branches as well as to hook existing branches up to their
452
471
        a format string, and vice versa.
453
472
 
454
473
        If branch_format is None, the transport is returned with no 
455
 
        checking. if it is not None, then the returned transport is
 
474
        checking. If it is not None, then the returned transport is
456
475
        guaranteed to point to an existing directory ready for use.
457
476
        """
458
477
        raise NotImplementedError(self.get_branch_transport)
465
484
        a format string, and vice versa.
466
485
 
467
486
        If repository_format is None, the transport is returned with no 
468
 
        checking. if it is not None, then the returned transport is
 
487
        checking. If it is not None, then the returned transport is
469
488
        guaranteed to point to an existing directory ready for use.
470
489
        """
471
490
        raise NotImplementedError(self.get_repository_transport)
478
497
        format string, and vice versa.
479
498
 
480
499
        If workingtree_format is None, the transport is returned with no 
481
 
        checking. if it is not None, then the returned transport is
 
500
        checking. If it is not None, then the returned transport is
482
501
        guaranteed to point to an existing directory ready for use.
483
502
        """
484
503
        raise NotImplementedError(self.get_workingtree_transport)
510
529
        # this might be better on the BzrDirFormat class because it refers to 
511
530
        # all the possible bzrdir disk formats. 
512
531
        # This method is tested via the workingtree is_control_filename tests- 
513
 
        # it was extracted from WorkingTree.is_control_filename. If the methods
514
 
        # contract is extended beyond the current trivial  implementation please
 
532
        # it was extracted from WorkingTree.is_control_filename. If the method's
 
533
        # contract is extended beyond the current trivial implementation, please
515
534
        # add new tests for it to the appropriate place.
516
535
        return filename == '.bzr' or filename.startswith('.bzr/')
517
536
 
532
551
        return BzrDir.open(base, _unsupported=True)
533
552
        
534
553
    @staticmethod
535
 
    def open(base, _unsupported=False):
536
 
        """Open an existing bzrdir, rooted at 'base' (url)
 
554
    def open(base, _unsupported=False, possible_transports=None):
 
555
        """Open an existing bzrdir, rooted at 'base' (url).
537
556
        
538
 
        _unsupported is a private parameter to the BzrDir class.
 
557
        :param _unsupported: a private parameter to the BzrDir class.
539
558
        """
540
 
        t = get_transport(base)
 
559
        t = get_transport(base, possible_transports=possible_transports)
541
560
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
542
561
 
543
562
    @staticmethod
564
583
            note('%s is%s redirected to %s',
565
584
                 transport.base, e.permanently, target)
566
585
            # Let's try with a new transport
567
 
            qualified_target = e.get_target_url()[:-len(relpath)]
568
586
            # FIXME: If 'transport' has a qualifier, this should
569
587
            # be applied again to the new transport *iff* the
570
 
            # schemes used are the same. It's a bit tricky to
571
 
            # verify, so I'll punt for now
 
588
            # schemes used are the same. Uncomment this code
 
589
            # once the function (and tests) exist.
572
590
            # -- vila20070212
 
591
            #target = urlutils.copy_url_qualifiers(original, target)
573
592
            return get_transport(target)
574
593
 
575
594
        try:
604
623
    
605
624
    @staticmethod
606
625
    def open_containing_from_transport(a_transport):
607
 
        """Open an existing branch which contains a_transport.base
 
626
        """Open an existing branch which contains a_transport.base.
608
627
 
609
628
        This probes for a branch at a_transport, and searches upwards from there.
610
629
 
658
677
    def open_repository(self, _unsupported=False):
659
678
        """Open the repository object at this BzrDir if one is present.
660
679
 
661
 
        This will not follow the Branch object pointer - its strictly a direct
 
680
        This will not follow the Branch object pointer - it's strictly a direct
662
681
        open facility. Most client code should use open_branch().repository to
663
682
        get at a repository.
664
683
 
665
 
        _unsupported is a private parameter, not part of the api.
 
684
        :param _unsupported: a private parameter, not part of the api.
666
685
        TODO: static convenience version of this?
667
686
        """
668
687
        raise NotImplementedError(self.open_repository)
708
727
            return False
709
728
 
710
729
    def _cloning_metadir(self):
711
 
        """Produce a metadir suitable for cloning with"""
 
730
        """Produce a metadir suitable for cloning with."""
712
731
        result_format = self._format.__class__()
713
732
        try:
714
733
            try:
741
760
        """Produce a metadir suitable for cloning or sprouting with.
742
761
 
743
762
        These operations may produce workingtrees (yes, even though they're
744
 
        "cloning" something that doesn't have a tree, so a viable workingtree
 
763
        "cloning" something that doesn't have a tree), so a viable workingtree
745
764
        format must be selected.
746
765
        """
747
766
        format, repository = self._cloning_metadir()
760
779
        """Create a copy of this bzrdir prepared for use as a new line of
761
780
        development.
762
781
 
763
 
        If urls last component does not exist, it will be created.
 
782
        If url's last component does not exist, it will be created.
764
783
 
765
784
        Attributes related to the identity of the source branch like
766
785
        branch nickname will be cleaned, a working tree is created
893
912
        """See BzrDir.create_branch."""
894
913
        return self.open_branch()
895
914
 
 
915
    def destroy_branch(self):
 
916
        """See BzrDir.destroy_branch."""
 
917
        raise errors.UnsupportedOperation(self.destroy_branch, self)
 
918
 
896
919
    def create_repository(self, shared=False):
897
920
        """See BzrDir.create_repository."""
898
921
        if shared:
1069
1092
        """See BzrDir.create_branch."""
1070
1093
        return self._format.get_branch_format().initialize(self)
1071
1094
 
 
1095
    def destroy_branch(self):
 
1096
        """See BzrDir.create_branch."""
 
1097
        self.transport.delete_tree('branch')
 
1098
 
1072
1099
    def create_repository(self, shared=False):
1073
1100
        """See BzrDir.create_repository."""
1074
1101
        return self._format.repository_format.initialize(self, shared)
1083
1110
        wt = self.open_workingtree(recommend_upgrade=False)
1084
1111
        repository = wt.branch.repository
1085
1112
        empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1086
 
        wt.revert([], old_tree=empty)
 
1113
        wt.revert(old_tree=empty)
1087
1114
        self.destroy_workingtree_metadata()
1088
1115
 
1089
1116
    def destroy_workingtree_metadata(self):
1215
1242
     * a format string,
1216
1243
     * an open routine.
1217
1244
 
1218
 
    Formats are placed in an dict by their format string for reference 
 
1245
    Formats are placed in a dict by their format string for reference 
1219
1246
    during bzrdir opening. These should be subclasses of BzrDirFormat
1220
1247
    for consistency.
1221
1248
 
1432
1459
        klass._default_format = format
1433
1460
 
1434
1461
    def __str__(self):
1435
 
        return self.get_format_string()[:-1]
 
1462
        # Trim the newline
 
1463
        return self.get_format_string().rstrip()
1436
1464
 
1437
1465
    @classmethod
1438
1466
    def unregister_format(klass, format):
1804
1832
    def _convert_working_inv(self):
1805
1833
        inv = xml4.serializer_v4.read_inventory(
1806
1834
                    self.branch.control_files.get('inventory'))
1807
 
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
 
1835
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
1808
1836
        # FIXME inventory is a working tree change.
1809
1837
        self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1810
1838
 
1889
1917
        present_parents = [p for p in rev.parent_ids
1890
1918
                           if p not in self.absent_revisions]
1891
1919
        self._convert_revision_contents(rev, inv, present_parents)
1892
 
        self._store_new_weave(rev, inv, present_parents)
 
1920
        self._store_new_inv(rev, inv, present_parents)
1893
1921
        self.converted_revs.add(rev_id)
1894
1922
 
1895
 
    def _store_new_weave(self, rev, inv, present_parents):
 
1923
    def _store_new_inv(self, rev, inv, present_parents):
1896
1924
        # the XML is now updated with text versions
1897
1925
        if __debug__:
1898
1926
            entries = inv.iter_entries()
1903
1931
                    (file_id, rev.revision_id)
1904
1932
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1905
1933
        new_inv_sha1 = sha_string(new_inv_xml)
1906
 
        self.inv_weave.add_lines(rev.revision_id, 
 
1934
        self.inv_weave.add_lines(rev.revision_id,
1907
1935
                                 present_parents,
1908
1936
                                 new_inv_xml.splitlines(True))
1909
1937
        rev.inventory_sha1 = new_inv_sha1
1934
1962
            w = Weave(file_id)
1935
1963
            self.text_weaves[file_id] = w
1936
1964
        text_changed = False
1937
 
        previous_entries = ie.find_previous_heads(parent_invs,
1938
 
                                                  None,
1939
 
                                                  None,
1940
 
                                                  entry_vf=w)
1941
 
        for old_revision in previous_entries:
1942
 
                # if this fails, its a ghost ?
1943
 
                assert old_revision in self.converted_revs, \
1944
 
                    "Revision {%s} not in converted_revs" % old_revision
 
1965
        parent_candiate_entries = ie.parent_candidates(parent_invs)
 
1966
        for old_revision in parent_candiate_entries.keys():
 
1967
            # if this fails, its a ghost ?
 
1968
            assert old_revision in self.converted_revs, \
 
1969
                "Revision {%s} not in converted_revs" % old_revision
 
1970
        heads = graph.Graph(self).heads(parent_candiate_entries.keys())
 
1971
        # XXX: Note that this is unordered - and this is tolerable because 
 
1972
        # the previous code was also unordered.
 
1973
        previous_entries = dict((head, parent_candiate_entries[head]) for head
 
1974
            in heads)
1945
1975
        self.snapshot_ie(previous_entries, ie, w, rev_id)
1946
1976
        del ie.text_id
1947
1977
        assert getattr(ie, 'revision', None) is not None
1948
1978
 
 
1979
    def get_parents(self, revision_ids):
 
1980
        for revision_id in revision_ids:
 
1981
            yield self.revisions[revision_id].parent_ids
 
1982
 
1949
1983
    def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1950
1984
        # TODO: convert this logic, which is ~= snapshot to
1951
1985
        # a call to:. This needs the path figured out. rather than a work_tree