~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Michael Ellerman
  • Date: 2006-02-28 14:45:51 UTC
  • mto: (1558.1.18 Aaron's integration)
  • mto: This revision was merged to the branch mainline in revision 1586.
  • Revision ID: michael@ellerman.id.au-20060228144551-3d9941ecde4a0b0a
Update contrib/pwk for -p1 diffs from bzr

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005 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
20
20
directories.
21
21
"""
22
22
 
23
 
# TODO: remove unittest dependency; put that stuff inside the test suite
24
 
 
25
23
from copy import deepcopy
 
24
import os
26
25
from cStringIO import StringIO
27
 
import os
28
 
from stat import S_ISDIR
29
26
from unittest import TestSuite
30
27
 
31
28
import bzrlib
32
29
import bzrlib.errors as errors
33
 
from bzrlib.lockable_files import LockableFiles, TransportLock
34
 
from bzrlib.lockdir import LockDir
 
30
from bzrlib.lockable_files import LockableFiles
 
31
from bzrlib.osutils import safe_unicode
35
32
from bzrlib.osutils import (
36
33
                            abspath,
37
34
                            pathjoin,
39
36
                            sha_strings,
40
37
                            sha_string,
41
38
                            )
42
 
from bzrlib.store.revision.text import TextRevisionStore
43
39
from bzrlib.store.text import TextStore
44
 
from bzrlib.store.versioned import WeaveStore
 
40
from bzrlib.store.weave import WeaveStore
 
41
from bzrlib.symbol_versioning import *
45
42
from bzrlib.trace import mutter
46
 
from bzrlib.transactions import WriteTransaction
 
43
from bzrlib.transactions import PassThroughTransaction
47
44
from bzrlib.transport import get_transport
48
45
from bzrlib.transport.local import LocalTransport
49
 
import bzrlib.urlutils as urlutils
50
46
from bzrlib.weave import Weave
 
47
from bzrlib.weavefile import read_weave, write_weave
51
48
from bzrlib.xml4 import serializer_v4
52
 
import bzrlib.xml5
 
49
from bzrlib.xml5 import serializer_v5
53
50
 
54
51
 
55
52
class BzrDir(object):
64
61
        a transport connected to the directory this bzr was opened from.
65
62
    """
66
63
 
67
 
    def break_lock(self):
68
 
        """Invoke break_lock on the first object in the bzrdir.
69
 
 
70
 
        If there is a tree, the tree is opened and break_lock() called.
71
 
        Otherwise, branch is tried, and finally repository.
72
 
        """
73
 
        try:
74
 
            thing_to_unlock = self.open_workingtree()
75
 
        except (errors.NotLocalUrl, errors.NoWorkingTree):
76
 
            try:
77
 
                thing_to_unlock = self.open_branch()
78
 
            except errors.NotBranchError:
79
 
                try:
80
 
                    thing_to_unlock = self.open_repository()
81
 
                except errors.NoRepositoryPresent:
82
 
                    return
83
 
        thing_to_unlock.break_lock()
84
 
 
85
64
    def can_convert_format(self):
86
65
        """Return true if this bzrdir is one whose format we can convert from."""
87
66
        return True
88
67
 
89
 
    @staticmethod
90
 
    def _check_supported(format, allow_unsupported):
 
68
    def _check_supported(self, format, allow_unsupported):
91
69
        """Check whether format is a supported format.
92
70
 
93
71
        If allow_unsupported is True, this is a no-op.
94
72
        """
95
73
        if not allow_unsupported and not format.is_supported():
96
 
            # see open_downlevel to open legacy branches.
97
 
            raise errors.UnsupportedFormatError(format=format)
 
74
            raise errors.UnsupportedFormatError(format)
98
75
 
99
76
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
100
77
        """Clone this bzrdir and its contents to url verbatim.
116
93
        if local_repo:
117
94
            # may need to copy content in
118
95
            if force_new_repo:
119
 
                result_repo = local_repo.clone(
120
 
                    result,
121
 
                    revision_id=revision_id,
122
 
                    basis=basis_repo)
123
 
                result_repo.set_make_working_trees(local_repo.make_working_trees())
 
96
                local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
124
97
            else:
125
98
                try:
126
99
                    result_repo = result.find_repository()
132
105
                    result_repo.fetch(local_repo, revision_id=revision_id)
133
106
                except errors.NoRepositoryPresent:
134
107
                    # needed to make one anyway.
135
 
                    result_repo = local_repo.clone(
136
 
                        result,
137
 
                        revision_id=revision_id,
138
 
                        basis=basis_repo)
139
 
                    result_repo.set_make_working_trees(local_repo.make_working_trees())
 
108
                    local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
140
109
        # 1 if there is a branch present
141
110
        #   make sure its content is available in the target repository
142
111
        #   clone it.
171
140
                    basis_repo = None
172
141
        return basis_repo, basis_branch, basis_tree
173
142
 
174
 
    # TODO: This should be given a Transport, and should chdir up; otherwise
175
 
    # this will open a new connection.
176
143
    def _make_tail(self, url):
177
 
        head, tail = urlutils.split(url)
178
 
        if tail and tail != '.':
179
 
            t = bzrlib.transport.get_transport(head)
 
144
        segments = url.split('/')
 
145
        if segments and segments[-1] not in ('', '.'):
 
146
            parent = '/'.join(segments[:-1])
 
147
            t = bzrlib.transport.get_transport(parent)
180
148
            try:
181
 
                t.mkdir(tail)
 
149
                t.mkdir(segments[-1])
182
150
            except errors.FileExists:
183
151
                pass
184
152
 
185
 
    # TODO: Should take a Transport
186
 
    @classmethod
187
 
    def create(cls, base):
 
153
    @staticmethod
 
154
    def create(base):
188
155
        """Create a new BzrDir at the url 'base'.
189
156
        
190
157
        This will call the current default formats initialize with base
193
160
        If you need a specific format, consider creating an instance
194
161
        of that and calling initialize().
195
162
        """
196
 
        if cls is not BzrDir:
197
 
            raise AssertionError("BzrDir.create always creates the default format, "
198
 
                    "not one of %r" % cls)
199
 
        head, tail = urlutils.split(base)
200
 
        if tail and tail != '.':
201
 
            t = bzrlib.transport.get_transport(head)
 
163
        segments = base.split('/')
 
164
        if segments and segments[-1] not in ('', '.'):
 
165
            parent = '/'.join(segments[:-1])
 
166
            t = bzrlib.transport.get_transport(parent)
202
167
            try:
203
 
                t.mkdir(tail)
 
168
                t.mkdir(segments[-1])
204
169
            except errors.FileExists:
205
170
                pass
206
171
        return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
241
206
            return self.create_repository()
242
207
        
243
208
    @staticmethod
244
 
    def create_branch_convenience(base, force_new_repo=False,
245
 
                                  force_new_tree=None, format=None):
 
209
    def create_branch_convenience(base, force_new_repo=False, force_new_tree=None):
246
210
        """Create a new BzrDir, Branch and Repository at the url 'base'.
247
211
 
248
212
        This is a convenience function - it will use an existing repository
264
228
        :param force_new_repo: If True a new repository is always created.
265
229
        :param force_new_tree: If True or False force creation of a tree or 
266
230
                               prevent such creation respectively.
267
 
        :param format: Override for the for the bzrdir format to create
268
231
        """
269
232
        if force_new_tree:
270
233
            # check for non local urls
271
234
            t = get_transport(safe_unicode(base))
272
235
            if not isinstance(t, LocalTransport):
273
236
                raise errors.NotLocalUrl(base)
274
 
        if format is None:
275
 
            bzrdir = BzrDir.create(base)
276
 
        else:
277
 
            bzrdir = format.initialize(base)
 
237
        bzrdir = BzrDir.create(base)
278
238
        repo = bzrdir._find_or_create_repository(force_new_repo)
279
239
        result = bzrdir.create_branch()
280
240
        if force_new_tree or (repo.make_working_trees() and 
301
261
        that child class desires.
302
262
        """
303
263
        bzrdir = BzrDir.create(base)
304
 
        return bzrdir.create_repository(shared)
 
264
        return bzrdir.create_repository()
305
265
 
306
266
    @staticmethod
307
267
    def create_standalone_workingtree(base):
342
302
            pass
343
303
        next_transport = self.root_transport.clone('..')
344
304
        while True:
345
 
            # find the next containing bzrdir
346
305
            try:
347
306
                found_bzrdir = BzrDir.open_containing_from_transport(
348
307
                    next_transport)[0]
349
308
            except errors.NotBranchError:
350
 
                # none found
351
309
                raise errors.NoRepositoryPresent(self)
352
 
            # does it have a repository ?
353
310
            try:
354
311
                repository = found_bzrdir.open_repository()
355
312
            except errors.NoRepositoryPresent:
356
313
                next_transport = found_bzrdir.root_transport.clone('..')
357
 
                if (found_bzrdir.root_transport.base == next_transport.base):
358
 
                    # top of the file system
359
 
                    break
360
 
                else:
361
 
                    continue
 
314
                continue
362
315
            if ((found_bzrdir.root_transport.base == 
363
316
                 self.root_transport.base) or repository.is_shared()):
364
317
                return repository
371
324
 
372
325
        Note that bzr dirs that do not support format strings will raise
373
326
        IncompatibleFormat if the branch format they are given has
374
 
        a format string, and vice versa.
 
327
        a format string, and vice verca.
375
328
 
376
329
        If branch_format is None, the transport is returned with no 
377
330
        checking. if it is not None, then the returned transport is
384
337
 
385
338
        Note that bzr dirs that do not support format strings will raise
386
339
        IncompatibleFormat if the repository format they are given has
387
 
        a format string, and vice versa.
 
340
        a format string, and vice verca.
388
341
 
389
342
        If repository_format is None, the transport is returned with no 
390
343
        checking. if it is not None, then the returned transport is
397
350
 
398
351
        Note that bzr dirs that do not support format strings will raise
399
352
        IncompatibleFormat if the workingtree format they are given has
400
 
        a format string, and vice versa.
 
353
        a format string, and vice verca.
401
354
 
402
355
        If workingtree_format is None, the transport is returned with no 
403
356
        checking. if it is not None, then the returned transport is
418
371
        self.transport = _transport.clone('.bzr')
419
372
        self.root_transport = _transport
420
373
 
421
 
    def is_control_filename(self, filename):
422
 
        """True if filename is the name of a path which is reserved for bzrdir's.
423
 
        
424
 
        :param filename: A filename within the root transport of this bzrdir.
425
 
 
426
 
        This is true IF and ONLY IF the filename is part of the namespace reserved
427
 
        for bzr control dirs. Currently this is the '.bzr' directory in the root
428
 
        of the root_transport. it is expected that plugins will need to extend
429
 
        this in the future - for instance to make bzr talk with svn working
430
 
        trees.
431
 
        """
432
 
        # this might be better on the BzrDirFormat class because it refers to 
433
 
        # all the possible bzrdir disk formats. 
434
 
        # This method is tested via the workingtree is_control_filename tests- 
435
 
        # it was extracted from WorkingTree.is_control_filename. If the methods
436
 
        # contract is extended beyond the current trivial  implementation please
437
 
        # add new tests for it to the appropriate place.
438
 
        return filename == '.bzr' or filename.startswith('.bzr/')
439
 
 
440
374
    def needs_format_conversion(self, format=None):
441
375
        """Return true if this bzrdir needs convert_format run on it.
442
376
        
462
396
        t = get_transport(base)
463
397
        mutter("trying to open %r with transport %r", base, t)
464
398
        format = BzrDirFormat.find_format(t)
465
 
        BzrDir._check_supported(format, _unsupported)
 
399
        if not _unsupported and not format.is_supported():
 
400
            # see open_downlevel to open legacy branches.
 
401
            raise errors.UnsupportedFormatError(
 
402
                    'sorry, format %s not supported' % format,
 
403
                    ['use a different bzr version',
 
404
                     'or remove the .bzr directory'
 
405
                     ' and "bzr init" again'])
466
406
        return format.open(t, _found=True)
467
407
 
468
408
    def open_branch(self, unsupported=False):
495
435
        If there is one and it is either an unrecognised format or an unsupported 
496
436
        format, UnknownFormatError or UnsupportedFormatError are raised.
497
437
        If there is one, it is returned, along with the unused portion of url.
498
 
 
499
 
        :return: The BzrDir that contains the path, and a Unicode path 
500
 
                for the rest of the URL.
501
438
        """
502
439
        # this gets the normalised url back. I.e. '.' -> the full path.
503
440
        url = a_transport.base
504
441
        while True:
505
442
            try:
506
443
                format = BzrDirFormat.find_format(a_transport)
507
 
                BzrDir._check_supported(format, False)
508
 
                return format.open(a_transport), urlutils.unescape(a_transport.relpath(url))
 
444
                return format.open(a_transport), a_transport.relpath(url)
509
445
            except errors.NotBranchError, e:
510
 
                ## mutter('not a branch in: %r %s', a_transport.base, e)
511
 
                pass
 
446
                mutter('not a branch in: %r %s', a_transport.base, e)
512
447
            new_t = a_transport.clone('..')
513
448
            if new_t.base == a_transport.base:
514
449
                # reached the root, whatever that may be
534
469
        """
535
470
        raise NotImplementedError(self.open_workingtree)
536
471
 
537
 
    def has_branch(self):
538
 
        """Tell if this bzrdir contains a branch.
539
 
        
540
 
        Note: if you're going to open the branch, you should just go ahead
541
 
        and try, and not ask permission first.  (This method just opens the 
542
 
        branch and discards it, and that's somewhat expensive.) 
543
 
        """
544
 
        try:
545
 
            self.open_branch()
546
 
            return True
547
 
        except errors.NotBranchError:
548
 
            return False
549
 
 
550
 
    def has_workingtree(self):
551
 
        """Tell if this bzrdir contains a working tree.
552
 
 
553
 
        This will still raise an exception if the bzrdir has a workingtree that
554
 
        is remote & inaccessible.
555
 
        
556
 
        Note: if you're going to open the working tree, you should just go ahead
557
 
        and try, and not ask permission first.  (This method just opens the 
558
 
        workingtree and discards it, and that's somewhat expensive.) 
559
 
        """
560
 
        try:
561
 
            self.open_workingtree()
562
 
            return True
563
 
        except errors.NoWorkingTree:
564
 
            return False
565
 
 
566
472
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
567
473
        """Create a copy of this bzrdir prepared for use as a new line of
568
474
        development.
604
510
            # no repo available, make a new one
605
511
            result.create_repository()
606
512
        elif source_repository is not None and result_repo is None:
607
 
            # have source, and want to make a new target repo
608
 
            # we don't clone the repo because that preserves attributes
609
 
            # like is_shared(), and we have not yet implemented a 
610
 
            # repository sprout().
611
 
            result_repo = result.create_repository()
612
 
        if result_repo is not None:
 
513
            # have soure, and want to make a new target repo
 
514
            source_repository.clone(result,
 
515
                                    revision_id=revision_id,
 
516
                                    basis=basis_repo)
 
517
        else:
613
518
            # fetch needed content into target.
614
519
            if basis_repo:
615
520
                # XXX FIXME RBC 20060214 need tests for this when the basis
620
525
            source_branch.sprout(result, revision_id=revision_id)
621
526
        else:
622
527
            result.create_branch()
623
 
        # TODO: jam 20060426 we probably need a test in here in the
624
 
        #       case that the newly sprouted branch is a remote one
625
 
        if result_repo is None or result_repo.make_working_trees():
 
528
        try:
 
529
            self.open_workingtree().clone(result,
 
530
                                          revision_id=revision_id, 
 
531
                                          basis=basis_tree)
 
532
        except (errors.NoWorkingTree, errors.NotLocalUrl):
626
533
            result.create_workingtree()
627
534
        return result
628
535
 
633
540
    def __init__(self, _transport, _format):
634
541
        """See BzrDir.__init__."""
635
542
        super(BzrDirPreSplitOut, self).__init__(_transport, _format)
636
 
        assert self._format._lock_class == TransportLock
637
 
        assert self._format._lock_file_name == 'branch-lock'
638
543
        self._control_files = LockableFiles(self.get_branch_transport(None),
639
 
                                            self._format._lock_file_name,
640
 
                                            self._format._lock_class)
641
 
 
642
 
    def break_lock(self):
643
 
        """Pre-splitout bzrdirs do not suffer from stale locks."""
644
 
        raise NotImplementedError(self.break_lock)
 
544
                                            'branch-lock')
645
545
 
646
546
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
647
547
        """See BzrDir.clone()."""
648
548
        from bzrlib.workingtree import WorkingTreeFormat2
649
549
        self._make_tail(url)
650
 
        result = self._format._initialize_for_clone(url)
 
550
        result = self._format.initialize(url, _cloning=True)
651
551
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
652
552
        self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
653
 
        from_branch = self.open_branch()
654
 
        from_branch.clone(result, revision_id=revision_id)
 
553
        self.open_branch().clone(result, revision_id=revision_id)
655
554
        try:
656
555
            self.open_workingtree().clone(result, basis=basis_tree)
657
556
        except errors.NotLocalUrl:
658
557
            # make a new one, this format always has to have one.
659
 
            try:
660
 
                WorkingTreeFormat2().initialize(result)
661
 
            except errors.NotLocalUrl:
662
 
                # but we cannot do it for remote trees.
663
 
                to_branch = result.open_branch()
664
 
                WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
 
558
            WorkingTreeFormat2().initialize(result)
665
559
        return result
666
560
 
667
561
    def create_branch(self):
737
631
        """See BzrDir.sprout()."""
738
632
        from bzrlib.workingtree import WorkingTreeFormat2
739
633
        self._make_tail(url)
740
 
        result = self._format._initialize_for_clone(url)
 
634
        result = self._format.initialize(url, _cloning=True)
741
635
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
742
636
        try:
743
637
            self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
747
641
            self.open_branch().sprout(result, revision_id=revision_id)
748
642
        except errors.NotBranchError:
749
643
            pass
750
 
        # we always want a working tree
751
 
        WorkingTreeFormat2().initialize(result)
 
644
        try:
 
645
            self.open_workingtree().clone(result, basis=basis_tree)
 
646
        except (errors.NotBranchError, errors.NotLocalUrl):
 
647
            # we always want a working tree
 
648
            WorkingTreeFormat2().initialize(result)
752
649
        return result
753
650
 
754
651
 
810
707
    """A .bzr meta version 1 control object.
811
708
    
812
709
    This is the first control object where the 
813
 
    individual aspects are really split out: there are separate repository,
814
 
    workingtree and branch subdirectories and any subset of the three can be
815
 
    present within a BzrDir.
 
710
    individual formats are really split out.
816
711
    """
817
712
 
818
713
    def can_convert_format(self):
833
728
        from bzrlib.workingtree import WorkingTreeFormat
834
729
        return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
835
730
 
836
 
    def _get_mkdir_mode(self):
837
 
        """Figure out the mode to use when creating a bzrdir subdir."""
838
 
        temp_control = LockableFiles(self.transport, '', TransportLock)
839
 
        return temp_control._dir_mode
840
 
 
841
731
    def get_branch_transport(self, branch_format):
842
732
        """See BzrDir.get_branch_transport()."""
843
733
        if branch_format is None:
847
737
        except NotImplementedError:
848
738
            raise errors.IncompatibleFormat(branch_format, self._format)
849
739
        try:
850
 
            self.transport.mkdir('branch', mode=self._get_mkdir_mode())
 
740
            self.transport.mkdir('branch')
851
741
        except errors.FileExists:
852
742
            pass
853
743
        return self.transport.clone('branch')
861
751
        except NotImplementedError:
862
752
            raise errors.IncompatibleFormat(repository_format, self._format)
863
753
        try:
864
 
            self.transport.mkdir('repository', mode=self._get_mkdir_mode())
 
754
            self.transport.mkdir('repository')
865
755
        except errors.FileExists:
866
756
            pass
867
757
        return self.transport.clone('repository')
875
765
        except NotImplementedError:
876
766
            raise errors.IncompatibleFormat(workingtree_format, self._format)
877
767
        try:
878
 
            self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
 
768
            self.transport.mkdir('checkout')
879
769
        except errors.FileExists:
880
770
            pass
881
771
        return self.transport.clone('checkout')
943
833
    _formats = {}
944
834
    """The known formats."""
945
835
 
946
 
    _control_formats = []
947
 
    """The registered control formats - .bzr, ....
948
 
    
949
 
    This is a list of BzrDirFormat objects.
950
 
    """
951
 
 
952
 
    _lock_file_name = 'branch-lock'
953
 
 
954
 
    # _lock_class must be set in subclasses to the lock type, typ.
955
 
    # TransportLock or LockDir
956
 
 
957
836
    @classmethod
958
837
    def find_format(klass, transport):
959
 
        """Return the format present at transport."""
960
 
        for format in klass._control_formats:
961
 
            try:
962
 
                return format.probe_transport(transport)
963
 
            except errors.NotBranchError:
964
 
                # this format does not find a control dir here.
965
 
                pass
966
 
        raise errors.NotBranchError(path=transport.base)
967
 
 
968
 
    @classmethod
969
 
    def probe_transport(klass, transport):
970
 
        """Return the .bzrdir style transport present at URL."""
 
838
        """Return the format registered for URL."""
971
839
        try:
972
840
            format_string = transport.get(".bzr/branch-format").read()
 
841
            return klass._formats[format_string]
973
842
        except errors.NoSuchFile:
974
843
            raise errors.NotBranchError(path=transport.base)
975
 
 
976
 
        try:
977
 
            return klass._formats[format_string]
978
844
        except KeyError:
979
 
            raise errors.UnknownFormatError(format=format_string)
 
845
            raise errors.UnknownFormatError(format_string)
980
846
 
981
847
    @classmethod
982
848
    def get_default_format(klass):
987
853
        """Return the ASCII format string that identifies this format."""
988
854
        raise NotImplementedError(self.get_format_string)
989
855
 
990
 
    def get_format_description(self):
991
 
        """Return the short description for this format."""
992
 
        raise NotImplementedError(self.get_format_description)
993
 
 
994
856
    def get_converter(self, format=None):
995
857
        """Return the converter to use to convert bzrdirs needing converts.
996
858
 
997
859
        This returns a bzrlib.bzrdir.Converter object.
998
860
 
999
861
        This should return the best upgrader to step this format towards the
1000
 
        current default format. In the case of plugins we can/should provide
 
862
        current default format. In the case of plugins we can/shouold provide
1001
863
        some means for them to extend the range of returnable converters.
1002
864
 
1003
 
        :param format: Optional format to override the default format of the 
 
865
        :param format: Optional format to override the default foramt of the 
1004
866
                       library.
1005
867
        """
1006
868
        raise NotImplementedError(self.get_converter)
1007
869
 
1008
870
    def initialize(self, url):
1009
 
        """Create a bzr control dir at this url and return an opened copy.
1010
 
        
1011
 
        Subclasses should typically override initialize_on_transport
1012
 
        instead of this method.
1013
 
        """
1014
 
        return self.initialize_on_transport(get_transport(url))
1015
 
 
1016
 
    def initialize_on_transport(self, transport):
1017
 
        """Initialize a new bzrdir in the base directory of a Transport."""
 
871
        """Create a bzr control dir at this url and return an opened copy."""
1018
872
        # Since we don't have a .bzr directory, inherit the
1019
873
        # mode from the root directory
1020
 
        temp_control = LockableFiles(transport, '', TransportLock)
 
874
        t = get_transport(url)
 
875
        temp_control = LockableFiles(t, '')
1021
876
        temp_control._transport.mkdir('.bzr',
1022
 
                                      # FIXME: RBC 20060121 don't peek under
 
877
                                      # FIXME: RBC 20060121 dont peek under
1023
878
                                      # the covers
1024
879
                                      mode=temp_control._dir_mode)
1025
880
        file_mode = temp_control._file_mode
1026
881
        del temp_control
1027
 
        mutter('created control directory in ' + transport.base)
1028
 
        control = transport.clone('.bzr')
 
882
        mutter('created control directory in ' + t.base)
 
883
        control = t.clone('.bzr')
 
884
        lock_file = 'branch-lock'
1029
885
        utf8_files = [('README', 
1030
886
                       "This is a Bazaar-NG control directory.\n"
1031
887
                       "Do not change any files in this directory.\n"),
1032
888
                      ('branch-format', self.get_format_string()),
1033
889
                      ]
1034
890
        # NB: no need to escape relative paths that are url safe.
1035
 
        control_files = LockableFiles(control, self._lock_file_name, 
1036
 
                                      self._lock_class)
1037
 
        control_files.create_lock()
 
891
        control.put(lock_file, StringIO(), mode=file_mode)
 
892
        control_files = LockableFiles(control, lock_file)
1038
893
        control_files.lock_write()
1039
894
        try:
1040
895
            for file, content in utf8_files:
1041
896
                control_files.put_utf8(file, content)
1042
897
        finally:
1043
898
            control_files.unlock()
1044
 
        return self.open(transport, _found=True)
 
899
        return self.open(t, _found=True)
1045
900
 
1046
901
    def is_supported(self):
1047
902
        """Is this format supported?
1052
907
        """
1053
908
        return True
1054
909
 
1055
 
    @classmethod
1056
 
    def known_formats(klass):
1057
 
        """Return all the known formats.
1058
 
        
1059
 
        Concrete formats should override _known_formats.
1060
 
        """
1061
 
        # There is double indirection here to make sure that control 
1062
 
        # formats used by more than one dir format will only be probed 
1063
 
        # once. This can otherwise be quite expensive for remote connections.
1064
 
        result = set()
1065
 
        for format in klass._control_formats:
1066
 
            result.update(format._known_formats())
1067
 
        return result
1068
 
    
1069
 
    @classmethod
1070
 
    def _known_formats(klass):
1071
 
        """Return the known format instances for this control format."""
1072
 
        return set(klass._formats.values())
1073
 
 
1074
910
    def open(self, transport, _found=False):
1075
911
        """Return an instance of this format for the dir transport points at.
1076
912
        
1094
930
        klass._formats[format.get_format_string()] = format
1095
931
 
1096
932
    @classmethod
1097
 
    def register_control_format(klass, format):
1098
 
        """Register a format that does not use '.bzrdir' for its control dir.
1099
 
 
1100
 
        TODO: This should be pulled up into a 'ControlDirFormat' base class
1101
 
        which BzrDirFormat can inherit from, and renamed to register_format 
1102
 
        there. It has been done without that for now for simplicity of
1103
 
        implementation.
1104
 
        """
1105
 
        klass._control_formats.append(format)
1106
 
 
1107
 
    @classmethod
1108
933
    def set_default_format(klass, format):
1109
934
        klass._default_format = format
1110
935
 
1116
941
        assert klass._formats[format.get_format_string()] is format
1117
942
        del klass._formats[format.get_format_string()]
1118
943
 
1119
 
    @classmethod
1120
 
    def unregister_control_format(klass, format):
1121
 
        klass._control_formats.remove(format)
1122
 
 
1123
 
 
1124
 
# register BzrDirFormat as a control format
1125
 
BzrDirFormat.register_control_format(BzrDirFormat)
1126
 
 
1127
944
 
1128
945
class BzrDirFormat4(BzrDirFormat):
1129
946
    """Bzr dir format 4.
1138
955
    removed in format 5; write support for this format has been removed.
1139
956
    """
1140
957
 
1141
 
    _lock_class = TransportLock
1142
 
 
1143
958
    def get_format_string(self):
1144
959
        """See BzrDirFormat.get_format_string()."""
1145
960
        return "Bazaar-NG branch, format 0.0.4\n"
1146
961
 
1147
 
    def get_format_description(self):
1148
 
        """See BzrDirFormat.get_format_description()."""
1149
 
        return "All-in-one format 4"
1150
 
 
1151
962
    def get_converter(self, format=None):
1152
963
        """See BzrDirFormat.get_converter()."""
1153
964
        # there is one and only one upgrade path here.
1154
965
        return ConvertBzrDir4To5()
1155
966
        
1156
 
    def initialize_on_transport(self, transport):
 
967
    def initialize(self, url):
1157
968
        """Format 4 branches cannot be created."""
1158
969
        raise errors.UninitializableFormat(self)
1159
970
 
1188
999
       Unhashed stores in the repository.
1189
1000
    """
1190
1001
 
1191
 
    _lock_class = TransportLock
1192
 
 
1193
1002
    def get_format_string(self):
1194
1003
        """See BzrDirFormat.get_format_string()."""
1195
1004
        return "Bazaar-NG branch, format 5\n"
1196
1005
 
1197
 
    def get_format_description(self):
1198
 
        """See BzrDirFormat.get_format_description()."""
1199
 
        return "All-in-one format 5"
1200
 
 
1201
1006
    def get_converter(self, format=None):
1202
1007
        """See BzrDirFormat.get_converter()."""
1203
1008
        # there is one and only one upgrade path here.
1204
1009
        return ConvertBzrDir5To6()
1205
 
 
1206
 
    def _initialize_for_clone(self, url):
1207
 
        return self.initialize_on_transport(get_transport(url), _cloning=True)
1208
1010
        
1209
 
    def initialize_on_transport(self, transport, _cloning=False):
 
1011
    def initialize(self, url, _cloning=False):
1210
1012
        """Format 5 dirs always have working tree, branch and repository.
1211
1013
        
1212
1014
        Except when they are being cloned.
1214
1016
        from bzrlib.branch import BzrBranchFormat4
1215
1017
        from bzrlib.repository import RepositoryFormat5
1216
1018
        from bzrlib.workingtree import WorkingTreeFormat2
1217
 
        result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
 
1019
        result = super(BzrDirFormat5, self).initialize(url)
1218
1020
        RepositoryFormat5().initialize(result, _internal=True)
1219
1021
        if not _cloning:
1220
1022
            BzrBranchFormat4().initialize(result)
1242
1044
     - Format 6 repositories [always]
1243
1045
    """
1244
1046
 
1245
 
    _lock_class = TransportLock
1246
 
 
1247
1047
    def get_format_string(self):
1248
1048
        """See BzrDirFormat.get_format_string()."""
1249
1049
        return "Bazaar-NG branch, format 6\n"
1250
1050
 
1251
 
    def get_format_description(self):
1252
 
        """See BzrDirFormat.get_format_description()."""
1253
 
        return "All-in-one format 6"
1254
 
 
1255
1051
    def get_converter(self, format=None):
1256
1052
        """See BzrDirFormat.get_converter()."""
1257
1053
        # there is one and only one upgrade path here.
1258
1054
        return ConvertBzrDir6ToMeta()
1259
1055
        
1260
 
    def _initialize_for_clone(self, url):
1261
 
        return self.initialize_on_transport(get_transport(url), _cloning=True)
1262
 
 
1263
 
    def initialize_on_transport(self, transport, _cloning=False):
 
1056
    def initialize(self, url, _cloning=False):
1264
1057
        """Format 6 dirs always have working tree, branch and repository.
1265
1058
        
1266
1059
        Except when they are being cloned.
1268
1061
        from bzrlib.branch import BzrBranchFormat4
1269
1062
        from bzrlib.repository import RepositoryFormat6
1270
1063
        from bzrlib.workingtree import WorkingTreeFormat2
1271
 
        result = super(BzrDirFormat6, self).initialize_on_transport(transport)
 
1064
        result = super(BzrDirFormat6, self).initialize(url)
1272
1065
        RepositoryFormat6().initialize(result, _internal=True)
1273
1066
        if not _cloning:
1274
1067
            BzrBranchFormat4().initialize(result)
1302
1095
     - Format 7 repositories [optional]
1303
1096
    """
1304
1097
 
1305
 
    _lock_class = LockDir
1306
 
 
1307
1098
    def get_converter(self, format=None):
1308
1099
        """See BzrDirFormat.get_converter()."""
1309
1100
        if format is None:
1317
1108
        """See BzrDirFormat.get_format_string()."""
1318
1109
        return "Bazaar-NG meta directory, format 1\n"
1319
1110
 
1320
 
    def get_format_description(self):
1321
 
        """See BzrDirFormat.get_format_description()."""
1322
 
        return "Meta directory format 1"
1323
 
 
1324
1111
    def _open(self, transport):
1325
1112
        """See BzrDirFormat._open."""
1326
1113
        return BzrDirMeta1(transport, self)
1335
1122
    def __set_repository_format(self, value):
1336
1123
        """Allow changint the repository format for metadir formats."""
1337
1124
        self._repository_format = value
1338
 
 
1339
1125
    repository_format = property(__return_repository_format, __set_repository_format)
1340
1126
 
1341
1127
 
1342
1128
BzrDirFormat.register_format(BzrDirFormat4())
1343
1129
BzrDirFormat.register_format(BzrDirFormat5())
1344
 
BzrDirFormat.register_format(BzrDirFormat6())
1345
 
__default_format = BzrDirMetaFormat1()
 
1130
BzrDirFormat.register_format(BzrDirMetaFormat1())
 
1131
__default_format = BzrDirFormat6()
1346
1132
BzrDirFormat.register_format(__default_format)
1347
1133
BzrDirFormat.set_default_format(__default_format)
1348
1134
 
1376
1162
        return result
1377
1163
 
1378
1164
 
 
1165
class ScratchDir(BzrDir6):
 
1166
    """Special test class: a bzrdir that cleans up itself..
 
1167
 
 
1168
    >>> d = ScratchDir()
 
1169
    >>> base = d.transport.base
 
1170
    >>> isdir(base)
 
1171
    True
 
1172
    >>> b.transport.__del__()
 
1173
    >>> isdir(base)
 
1174
    False
 
1175
    """
 
1176
 
 
1177
    def __init__(self, files=[], dirs=[], transport=None):
 
1178
        """Make a test branch.
 
1179
 
 
1180
        This creates a temporary directory and runs init-tree in it.
 
1181
 
 
1182
        If any files are listed, they are created in the working copy.
 
1183
        """
 
1184
        if transport is None:
 
1185
            transport = bzrlib.transport.local.ScratchTransport()
 
1186
            # local import for scope restriction
 
1187
            BzrDirFormat6().initialize(transport.base)
 
1188
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
 
1189
            self.create_repository()
 
1190
            self.create_branch()
 
1191
            self.create_workingtree()
 
1192
        else:
 
1193
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
 
1194
 
 
1195
        # BzrBranch creates a clone to .bzr and then forgets about the
 
1196
        # original transport. A ScratchTransport() deletes itself and
 
1197
        # everything underneath it when it goes away, so we need to
 
1198
        # grab a local copy to prevent that from happening
 
1199
        self._transport = transport
 
1200
 
 
1201
        for d in dirs:
 
1202
            self._transport.mkdir(d)
 
1203
            
 
1204
        for f in files:
 
1205
            self._transport.put(f, 'content of %s' % f)
 
1206
 
 
1207
    def clone(self):
 
1208
        """
 
1209
        >>> orig = ScratchDir(files=["file1", "file2"])
 
1210
        >>> os.listdir(orig.base)
 
1211
        [u'.bzr', u'file1', u'file2']
 
1212
        >>> clone = orig.clone()
 
1213
        >>> if os.name != 'nt':
 
1214
        ...   os.path.samefile(orig.base, clone.base)
 
1215
        ... else:
 
1216
        ...   orig.base == clone.base
 
1217
        ...
 
1218
        False
 
1219
        >>> os.listdir(clone.base)
 
1220
        [u'.bzr', u'file1', u'file2']
 
1221
        """
 
1222
        from shutil import copytree
 
1223
        from bzrlib.osutils import mkdtemp
 
1224
        base = mkdtemp()
 
1225
        os.rmdir(base)
 
1226
        copytree(self.base, base, symlinks=True)
 
1227
        return ScratchDir(
 
1228
            transport=bzrlib.transport.local.ScratchTransport(base))
 
1229
 
 
1230
 
1379
1231
class Converter(object):
1380
1232
    """Converts a disk format object from one format to another."""
1381
1233
 
1422
1274
                self.bzrdir.transport.mkdir('weaves')
1423
1275
        except errors.NoSuchFile:
1424
1276
            self.bzrdir.transport.mkdir('weaves')
1425
 
        # deliberately not a WeaveFile as we want to build it up slowly.
1426
1277
        self.inv_weave = Weave('inventory')
1427
1278
        # holds in-memory weaves for all files
1428
1279
        self.text_weaves = {}
1467
1318
 
1468
1319
    def _convert_working_inv(self):
1469
1320
        inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1470
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1321
        new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1471
1322
        # FIXME inventory is a working tree change.
1472
1323
        self.branch.control_files.put('inventory', new_inv_xml)
1473
1324
 
1475
1326
        controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1476
1327
        weave_transport = self.bzrdir.transport.clone('weaves')
1477
1328
        weaves = WeaveStore(weave_transport, prefixed=False)
1478
 
        transaction = WriteTransaction()
 
1329
        transaction = PassThroughTransaction()
1479
1330
 
 
1331
        controlweaves.put_weave('inventory', self.inv_weave, transaction)
 
1332
        i = 0
1480
1333
        try:
1481
 
            i = 0
1482
1334
            for file_id, file_weave in self.text_weaves.items():
1483
1335
                self.pb.update('writing weave', i, len(self.text_weaves))
1484
 
                weaves._put_weave(file_id, file_weave, transaction)
 
1336
                weaves.put_weave(file_id, file_weave, transaction)
1485
1337
                i += 1
1486
 
            self.pb.update('inventory', 0, 1)
1487
 
            controlweaves._put_weave('inventory', self.inv_weave, transaction)
1488
 
            self.pb.update('inventory', 1, 1)
1489
1338
        finally:
1490
1339
            self.pb.clear()
1491
1340
 
1495
1344
        self.bzrdir.transport.mkdir('revision-store')
1496
1345
        revision_transport = self.bzrdir.transport.clone('revision-store')
1497
1346
        # TODO permissions
1498
 
        _revision_store = TextRevisionStore(TextStore(revision_transport,
1499
 
                                                      prefixed=False,
1500
 
                                                      compressed=True))
 
1347
        revision_store = TextStore(revision_transport,
 
1348
                                   prefixed=False,
 
1349
                                   compressed=True)
1501
1350
        try:
1502
 
            transaction = bzrlib.transactions.WriteTransaction()
1503
1351
            for i, rev_id in enumerate(self.converted_revs):
1504
1352
                self.pb.update('write revision', i, len(self.converted_revs))
1505
 
                _revision_store.add_revision(self.revisions[rev_id], transaction)
 
1353
                rev_tmp = StringIO()
 
1354
                serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
 
1355
                rev_tmp.seek(0)
 
1356
                revision_store.add(rev_tmp, rev_id)
1506
1357
        finally:
1507
1358
            self.pb.clear()
1508
1359
            
1514
1365
        self.pb.update('loading revision',
1515
1366
                       len(self.revisions),
1516
1367
                       len(self.known_revisions))
1517
 
        if not self.branch.repository.has_revision(rev_id):
 
1368
        if not self.branch.repository.revision_store.has_id(rev_id):
1518
1369
            self.pb.clear()
1519
1370
            self.pb.note('revision {%s} not present in branch; '
1520
1371
                         'will be converted as a ghost',
1521
1372
                         rev_id)
1522
1373
            self.absent_revisions.add(rev_id)
1523
1374
        else:
1524
 
            rev = self.branch.repository._revision_store.get_revision(rev_id,
1525
 
                self.branch.repository.get_transaction())
 
1375
            rev_xml = self.branch.repository.revision_store.get(rev_id).read()
 
1376
            rev = serializer_v4.read_revision_from_string(rev_xml)
1526
1377
            for parent_id in rev.parent_ids:
1527
1378
                self.known_revisions.add(parent_id)
1528
1379
                self.to_read.append(parent_id)
1541
1392
    def _load_updated_inventory(self, rev_id):
1542
1393
        assert rev_id in self.converted_revs
1543
1394
        inv_xml = self.inv_weave.get_text(rev_id)
1544
 
        inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(inv_xml)
 
1395
        inv = serializer_v5.read_inventory_from_string(inv_xml)
1545
1396
        return inv
1546
1397
 
1547
1398
    def _convert_one_rev(self, rev_id):
1564
1415
                assert hasattr(ie, 'revision'), \
1565
1416
                    'no revision on {%s} in {%s}' % \
1566
1417
                    (file_id, rev.revision_id)
1567
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1418
        new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1568
1419
        new_inv_sha1 = sha_string(new_inv_xml)
1569
 
        self.inv_weave.add_lines(rev.revision_id, 
1570
 
                                 present_parents,
1571
 
                                 new_inv_xml.splitlines(True))
 
1420
        self.inv_weave.add(rev.revision_id, 
 
1421
                           present_parents,
 
1422
                           new_inv_xml.splitlines(True),
 
1423
                           new_inv_sha1)
1572
1424
        rev.inventory_sha1 = new_inv_sha1
1573
1425
 
1574
1426
    def _convert_revision_contents(self, rev, inv, present_parents):
1598
1450
            w = Weave(file_id)
1599
1451
            self.text_weaves[file_id] = w
1600
1452
        text_changed = False
1601
 
        previous_entries = ie.find_previous_heads(parent_invs,
1602
 
                                                  None,
1603
 
                                                  None,
1604
 
                                                  entry_vf=w)
 
1453
        previous_entries = ie.find_previous_heads(parent_invs, w)
1605
1454
        for old_revision in previous_entries:
1606
1455
                # if this fails, its a ghost ?
1607
1456
                assert old_revision in self.converted_revs 
1622
1471
            if ie._unchanged(previous_ie):
1623
1472
                ie.revision = previous_ie.revision
1624
1473
                return
 
1474
        parent_indexes = map(w.lookup, previous_revisions)
1625
1475
        if ie.has_text():
1626
1476
            text = self.branch.repository.text_store.get(ie.text_id)
1627
1477
            file_lines = text.readlines()
1628
1478
            assert sha_strings(file_lines) == ie.text_sha1
1629
1479
            assert sum(map(len, file_lines)) == ie.text_size
1630
 
            w.add_lines(rev_id, previous_revisions, file_lines)
 
1480
            w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
1631
1481
            self.text_count += 1
1632
1482
        else:
1633
 
            w.add_lines(rev_id, previous_revisions, [])
 
1483
            w.add(rev_id, parent_indexes, [], None)
1634
1484
        ie.revision = rev_id
1635
1485
 
1636
1486
    def _make_order(self):
1668
1518
        return BzrDir.open(self.bzrdir.root_transport.base)
1669
1519
 
1670
1520
    def _convert_to_prefixed(self):
1671
 
        from bzrlib.store import TransportStore
 
1521
        from bzrlib.store import hash_prefix
1672
1522
        self.bzrdir.transport.delete('branch-format')
1673
1523
        for store_name in ["weaves", "revision-store"]:
1674
 
            self.pb.note("adding prefixes to %s" % store_name)
 
1524
            self.pb.note("adding prefixes to %s" % store_name) 
1675
1525
            store_transport = self.bzrdir.transport.clone(store_name)
1676
 
            store = TransportStore(store_transport, prefixed=True)
1677
 
            for urlfilename in store_transport.list_dir('.'):
1678
 
                filename = urlutils.unescape(urlfilename)
 
1526
            for filename in store_transport.list_dir('.'):
1679
1527
                if (filename.endswith(".weave") or
1680
1528
                    filename.endswith(".gz") or
1681
1529
                    filename.endswith(".sig")):
1682
1530
                    file_id = os.path.splitext(filename)[0]
1683
1531
                else:
1684
1532
                    file_id = filename
1685
 
                prefix_dir = store.hash_prefix(file_id)
 
1533
                prefix_dir = hash_prefix(file_id)
1686
1534
                # FIXME keep track of the dirs made RBC 20060121
1687
1535
                try:
1688
1536
                    store_transport.move(filename, prefix_dir + '/' + filename)
1714
1562
            pass
1715
1563
        # find out whats there
1716
1564
        self.step('Finding branch files')
1717
 
        last_revision = self.bzrdir.open_branch().last_revision()
 
1565
        last_revision = self.bzrdir.open_workingtree().last_revision()
1718
1566
        bzrcontents = self.bzrdir.transport.list_dir('.')
1719
1567
        for name in bzrcontents:
1720
1568
            if name.startswith('basis-inventory.'):
1721
1569
                self.garbage_inventories.append(name)
1722
1570
        # create new directories for repository, working tree and branch
1723
 
        self.dir_mode = self.bzrdir._control_files._dir_mode
 
1571
        dir_mode = self.bzrdir._control_files._dir_mode
1724
1572
        self.file_mode = self.bzrdir._control_files._file_mode
1725
1573
        repository_names = [('inventory.weave', True),
1726
1574
                            ('revision-store', True),
1727
1575
                            ('weaves', True)]
1728
1576
        self.step('Upgrading repository  ')
1729
 
        self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
 
1577
        self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1730
1578
        self.make_lock('repository')
1731
1579
        # we hard code the formats here because we are converting into
1732
1580
        # the meta format. The meta format upgrader can take this to a 
1736
1584
            self.move_entry('repository', entry)
1737
1585
 
1738
1586
        self.step('Upgrading branch      ')
1739
 
        self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
 
1587
        self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1740
1588
        self.make_lock('branch')
1741
1589
        self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1742
1590
        branch_files = [('revision-history', True),
1746
1594
            self.move_entry('branch', entry)
1747
1595
 
1748
1596
        self.step('Upgrading working tree')
1749
 
        self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
 
1597
        self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1750
1598
        self.make_lock('checkout')
1751
1599
        self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1752
1600
        self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1764
1612
    def make_lock(self, name):
1765
1613
        """Make a lock for the new control dir name."""
1766
1614
        self.step('Make %s lock' % name)
1767
 
        ld = LockDir(self.bzrdir.transport, 
1768
 
                     '%s/lock' % name,
1769
 
                     file_modebits=self.file_mode,
1770
 
                     dir_modebits=self.dir_mode)
1771
 
        ld.create()
 
1615
        self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1772
1616
 
1773
1617
    def move_entry(self, new_dir, entry):
1774
1618
        """Move then entry name into new_dir."""