/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

Nearly complete .bzr/checkout splitout.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 Canonical Ltd
 
2
 
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
 
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
 
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
 
18
 
 
19
At format 7 this was split out into Branch, Repository and Checkout control
 
20
directories.
 
21
"""
 
22
 
 
23
from copy import deepcopy
 
24
from cStringIO import StringIO
 
25
from unittest import TestSuite
 
26
 
 
27
 
 
28
import bzrlib
 
29
import bzrlib.errors as errors
 
30
from bzrlib.lockable_files import LockableFiles
 
31
from bzrlib.osutils import safe_unicode
 
32
from bzrlib.trace import mutter
 
33
from bzrlib.symbol_versioning import *
 
34
from bzrlib.transport import get_transport
 
35
from bzrlib.transport.local import LocalTransport
 
36
 
 
37
 
 
38
class BzrDir(object):
 
39
    """A .bzr control diretory.
 
40
    
 
41
    BzrDir instances let you create or open any of the things that can be
 
42
    found within .bzr - checkouts, branches and repositories.
 
43
    
 
44
    transport
 
45
        the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
 
46
    """
 
47
 
 
48
    def _check_supported(self, format, allow_unsupported):
 
49
        """Check whether format is a supported format.
 
50
 
 
51
        If allow_unsupported is True, this is a no-op.
 
52
        """
 
53
        if not allow_unsupported and not format.is_supported():
 
54
            raise errors.UnsupportedFormatError(format)
 
55
 
 
56
    @staticmethod
 
57
    def create(base):
 
58
        """Create a new BzrDir at the url 'base'.
 
59
        
 
60
        This will call the current default formats initialize with base
 
61
        as the only parameter.
 
62
 
 
63
        If you need a specific format, consider creating an instance
 
64
        of that and calling initialize().
 
65
        """
 
66
        segments = base.split('/')
 
67
        if segments and segments[-1] not in ('', '.'):
 
68
            parent = '/'.join(segments[:-1])
 
69
            t = bzrlib.transport.get_transport(parent)
 
70
            try:
 
71
                t.mkdir(segments[-1])
 
72
            except errors.FileExists:
 
73
                pass
 
74
        return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
 
75
 
 
76
    def create_branch(self):
 
77
        """Create a branch in this BzrDir.
 
78
 
 
79
        The bzrdirs format will control what branch format is created.
 
80
        For more control see BranchFormatXX.create(a_bzrdir).
 
81
        """
 
82
        raise NotImplementedError(self.create_branch)
 
83
 
 
84
    @staticmethod
 
85
    def create_branch_and_repo(base):
 
86
        """Create a new BzrDir, Branch and Repository at the url 'base'.
 
87
 
 
88
        This will use the current default BzrDirFormat, and use whatever 
 
89
        repository format that that uses via bzrdir.create_branch and
 
90
        create_repository.
 
91
 
 
92
        The created Branch object is returned.
 
93
        """
 
94
        bzrdir = BzrDir.create(base)
 
95
        bzrdir.create_repository()
 
96
        return bzrdir.create_branch()
 
97
        
 
98
    @staticmethod
 
99
    def create_repository(base):
 
100
        """Create a new BzrDir and Repository at the url 'base'.
 
101
 
 
102
        This will use the current default BzrDirFormat, and use whatever 
 
103
        repository format that that uses for bzrdirformat.create_repository.
 
104
 
 
105
        The Repository object is returned.
 
106
 
 
107
        This must be overridden as an instance method in child classes, where
 
108
        it should take no parameters and construct whatever repository format
 
109
        that child class desires.
 
110
        """
 
111
        bzrdir = BzrDir.create(base)
 
112
        return bzrdir.create_repository()
 
113
 
 
114
    @staticmethod
 
115
    def create_standalone_workingtree(base):
 
116
        """Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
 
117
 
 
118
        'base' must be a local path or a file:// url.
 
119
 
 
120
        This will use the current default BzrDirFormat, and use whatever 
 
121
        repository format that that uses for bzrdirformat.create_workingtree,
 
122
        create_branch and create_repository.
 
123
 
 
124
        The WorkingTree object is returned.
 
125
        """
 
126
        t = get_transport(safe_unicode(base))
 
127
        if not isinstance(t, LocalTransport):
 
128
            raise errors.NotLocalUrl(base)
 
129
        bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base)).bzrdir
 
130
        return bzrdir.create_workingtree()
 
131
 
 
132
    def create_workingtree(self):
 
133
        """Create a working tree at this BzrDir"""
 
134
        raise NotImplementedError(self.create_workingtree)
 
135
 
 
136
    def get_branch_transport(self, branch_format):
 
137
        """Get the transport for use by branch format in this BzrDir.
 
138
 
 
139
        Note that bzr dirs that do not support format strings will raise
 
140
        IncompatibleFormat if the branch format they are given has
 
141
        a format string, and vice verca.
 
142
 
 
143
        If branch_format is None, the transport is returned with no 
 
144
        checking. if it is not None, then the returned transport is
 
145
        guaranteed to point to an existing directory ready for use.
 
146
        """
 
147
        raise NotImplementedError(self.get_branch_transport)
 
148
        
 
149
    def get_workingtree_transport(self, branch_format):
 
150
        """Get the transport for use by workingtree format in this BzrDir.
 
151
 
 
152
        Note that bzr dirs that do not support format strings will raise
 
153
        IncompatibleFormat if the workingtree format they are given has
 
154
        a format string, and vice verca.
 
155
 
 
156
        If workingtree_format is None, the transport is returned with no 
 
157
        checking. if it is not None, then the returned transport is
 
158
        guaranteed to point to an existing directory ready for use.
 
159
        """
 
160
        raise NotImplementedError(self.get_workingtree_transport)
 
161
        
 
162
    def __init__(self, _transport, _format):
 
163
        """Initialize a Bzr control dir object.
 
164
        
 
165
        Only really common logic should reside here, concrete classes should be
 
166
        made with varying behaviours.
 
167
 
 
168
        _format: the format that is creating this BzrDir instance.
 
169
        _transport: the transport this dir is based at.
 
170
        """
 
171
        self._format = _format
 
172
        self.transport = _transport.clone('.bzr')
 
173
 
 
174
    @staticmethod
 
175
    def open_unsupported(base):
 
176
        """Open a branch which is not supported."""
 
177
        return BzrDir.open(base, _unsupported=True)
 
178
        
 
179
    @staticmethod
 
180
    def open(base, _unsupported=False):
 
181
        """Open an existing branch, rooted at 'base' (url)
 
182
        
 
183
        _unsupported is a private parameter to the BzrDir class.
 
184
        """
 
185
        t = get_transport(base)
 
186
        mutter("trying to open %r with transport %r", base, t)
 
187
        format = BzrDirFormat.find_format(t)
 
188
        if not _unsupported and not format.is_supported():
 
189
            # see open_downlevel to open legacy branches.
 
190
            raise errors.UnsupportedFormatError(
 
191
                    'sorry, format %s not supported' % format,
 
192
                    ['use a different bzr version',
 
193
                     'or remove the .bzr directory'
 
194
                     ' and "bzr init" again'])
 
195
        return format.open(t, _found=True)
 
196
 
 
197
    def open_branch(self, unsupported=False):
 
198
        """Open the branch object at this BzrDir if one is present.
 
199
 
 
200
        If unsupported is True, then no longer supported branch formats can
 
201
        still be opened.
 
202
        
 
203
        TODO: static convenience version of this?
 
204
        """
 
205
        raise NotImplementedError(self.open_branch)
 
206
 
 
207
    @staticmethod
 
208
    def open_containing(url):
 
209
        """Open an existing branch which contains url.
 
210
        
 
211
        This probes for a branch at url, and searches upwards from there.
 
212
 
 
213
        Basically we keep looking up until we find the control directory or
 
214
        run into the root.  If there isn't one, raises NotBranchError.
 
215
        If there is one and it is either an unrecognised format or an unsupported 
 
216
        format, UnknownFormatError or UnsupportedFormatError are raised.
 
217
        If there is one, it is returned, along with the unused portion of url.
 
218
        """
 
219
        t = get_transport(url)
 
220
        # this gets the normalised url back. I.e. '.' -> the full path.
 
221
        url = t.base
 
222
        while True:
 
223
            try:
 
224
                format = BzrDirFormat.find_format(t)
 
225
                return format.open(t), t.relpath(url)
 
226
            except errors.NotBranchError, e:
 
227
                mutter('not a branch in: %r %s', t.base, e)
 
228
            new_t = t.clone('..')
 
229
            if new_t.base == t.base:
 
230
                # reached the root, whatever that may be
 
231
                raise errors.NotBranchError(path=url)
 
232
            t = new_t
 
233
 
 
234
    def open_repository(self):
 
235
        """Open the repository object at this BzrDir if one is present.
 
236
        
 
237
        TODO: static convenience version of this?
 
238
        TODO: NoRepositoryError that can be raised.
 
239
        """
 
240
        raise NotImplementedError(self.open_repository)
 
241
 
 
242
    def open_workingtree(self, _unsupported=False):
 
243
        """Open the workingtree object at this BzrDir if one is present.
 
244
        
 
245
        TODO: static convenience version of this?
 
246
        """
 
247
        raise NotImplementedError(self.open_workingtree)
 
248
 
 
249
 
 
250
class BzrDirPreSplitOut(BzrDir):
 
251
    """A common class for the all-in-one formats."""
 
252
 
 
253
    def create_branch(self):
 
254
        """See BzrDir.create_branch."""
 
255
        from bzrlib.branch import BzrBranchFormat4
 
256
        return BzrBranchFormat4().initialize(self)
 
257
 
 
258
    def get_branch_transport(self, branch_format):
 
259
        """See BzrDir.get_branch_transport()."""
 
260
        if branch_format is None:
 
261
            return self.transport
 
262
        try:
 
263
            branch_format.get_format_string()
 
264
        except NotImplementedError:
 
265
            return self.transport
 
266
        raise errors.IncompatibleFormat(branch_format, self._format)
 
267
 
 
268
    def get_workingtree_transport(self, workingtree_format):
 
269
        """See BzrDir.get_workingtree_transport()."""
 
270
        if workingtree_format is None:
 
271
            return self.transport
 
272
        try:
 
273
            workingtree_format.get_format_string()
 
274
        except NotImplementedError:
 
275
            return self.transport
 
276
        raise errors.IncompatibleFormat(workingtree_format, self._format)
 
277
 
 
278
    def open_branch(self, unsupported=False):
 
279
        """See BzrDir.open_branch."""
 
280
        from bzrlib.branch import BzrBranchFormat4
 
281
        format = BzrBranchFormat4()
 
282
        self._check_supported(format, unsupported)
 
283
        return format.open(self, _found=True)
 
284
 
 
285
 
 
286
class BzrDir4(BzrDirPreSplitOut):
 
287
    """A .bzr version 4 control object."""
 
288
 
 
289
    def create_repository(self):
 
290
        """See BzrDir.create_repository."""
 
291
        from bzrlib.repository import RepositoryFormat4
 
292
        return RepositoryFormat4().initialize(self)
 
293
 
 
294
    def open_repository(self):
 
295
        """See BzrDir.open_repository."""
 
296
        from bzrlib.repository import RepositoryFormat4
 
297
        return RepositoryFormat4().open(self, _found=True)
 
298
 
 
299
 
 
300
class BzrDir5(BzrDirPreSplitOut):
 
301
    """A .bzr version 5 control object."""
 
302
 
 
303
    def create_repository(self):
 
304
        """See BzrDir.create_repository."""
 
305
        from bzrlib.repository import RepositoryFormat5
 
306
        return RepositoryFormat5().initialize(self)
 
307
 
 
308
    def create_workingtree(self):
 
309
        """See BzrDir.create_workingtree."""
 
310
        from bzrlib.workingtree import WorkingTreeFormat2
 
311
        return WorkingTreeFormat2().initialize(self)
 
312
 
 
313
    def open_repository(self):
 
314
        """See BzrDir.open_repository."""
 
315
        from bzrlib.repository import RepositoryFormat5
 
316
        return RepositoryFormat5().open(self, _found=True)
 
317
 
 
318
    def open_workingtree(self, _unsupported=False):
 
319
        """See BzrDir.create_workingtree."""
 
320
        from bzrlib.workingtree import WorkingTreeFormat2
 
321
        return WorkingTreeFormat2().open(self, _found=True)
 
322
 
 
323
 
 
324
class BzrDir6(BzrDirPreSplitOut):
 
325
    """A .bzr version 6 control object."""
 
326
 
 
327
    def create_repository(self):
 
328
        """See BzrDir.create_repository."""
 
329
        from bzrlib.repository import RepositoryFormat6
 
330
        return RepositoryFormat6().initialize(self)
 
331
 
 
332
    def create_workingtree(self):
 
333
        """See BzrDir.create_workingtree."""
 
334
        from bzrlib.workingtree import WorkingTreeFormat2
 
335
        return WorkingTreeFormat2().initialize(self)
 
336
 
 
337
    def open_repository(self):
 
338
        """See BzrDir.open_repository."""
 
339
        from bzrlib.repository import RepositoryFormat6
 
340
        return RepositoryFormat6().open(self, _found=True)
 
341
 
 
342
    def open_workingtree(self, _unsupported=False):
 
343
        """See BzrDir.create_workingtree."""
 
344
        from bzrlib.workingtree import WorkingTreeFormat2
 
345
        return WorkingTreeFormat2().open(self, _found=True)
 
346
 
 
347
 
 
348
class BzrDirMeta1(BzrDir):
 
349
    """A .bzr meta version 1 control object.
 
350
    
 
351
    This is the first control object where the 
 
352
    individual formats are really split out.
 
353
    """
 
354
 
 
355
    def create_branch(self):
 
356
        """See BzrDir.create_branch."""
 
357
        from bzrlib.branch import BranchFormat
 
358
        return BranchFormat.get_default_format().initialize(self)
 
359
 
 
360
    def create_repository(self):
 
361
        """See BzrDir.create_repository."""
 
362
        from bzrlib.repository import RepositoryFormat6
 
363
        return RepositoryFormat6().initialize(self)
 
364
 
 
365
    def create_workingtree(self):
 
366
        """See BzrDir.create_workingtree."""
 
367
        from bzrlib.workingtree import WorkingTreeFormat
 
368
        return WorkingTreeFormat.get_default_format().initialize(self)
 
369
 
 
370
    def get_branch_transport(self, branch_format):
 
371
        """See BzrDir.get_branch_transport()."""
 
372
        if branch_format is None:
 
373
            return self.transport.clone('branch')
 
374
        try:
 
375
            branch_format.get_format_string()
 
376
        except NotImplementedError:
 
377
            raise errors.IncompatibleFormat(branch_format, self._format)
 
378
        try:
 
379
            self.transport.mkdir('branch')
 
380
        except errors.FileExists:
 
381
            pass
 
382
        return self.transport.clone('branch')
 
383
 
 
384
    def get_workingtree_transport(self, workingtree_format):
 
385
        """See BzrDir.get_workingtree_transport()."""
 
386
        if workingtree_format is None:
 
387
            return self.transport.clone('checkout')
 
388
        try:
 
389
            workingtree_format.get_format_string()
 
390
        except NotImplementedError:
 
391
            raise errors.IncompatibleFormat(workingtree_format, self._format)
 
392
        try:
 
393
            self.transport.mkdir('checkout')
 
394
        except errors.FileExists:
 
395
            pass
 
396
        return self.transport.clone('checkout')
 
397
 
 
398
    def open_branch(self, unsupported=False):
 
399
        """See BzrDir.open_branch."""
 
400
        from bzrlib.branch import BranchFormat
 
401
        format = BranchFormat.find_format(self)
 
402
        self._check_supported(format, unsupported)
 
403
        return format.open(self, _found=True)
 
404
 
 
405
    def open_repository(self):
 
406
        """See BzrDir.open_repository."""
 
407
        from bzrlib.repository import RepositoryFormat6
 
408
        return RepositoryFormat6().open(self, _found=True)
 
409
 
 
410
    def open_workingtree(self, unsupported=False):
 
411
        """See BzrDir.create_workingtree."""
 
412
        from bzrlib.workingtree import WorkingTreeFormat
 
413
        format = WorkingTreeFormat.find_format(self)
 
414
        self._check_supported(format, unsupported)
 
415
        return format.open(self, _found=True)
 
416
 
 
417
 
 
418
class BzrDirFormat(object):
 
419
    """An encapsulation of the initialization and open routines for a format.
 
420
 
 
421
    Formats provide three things:
 
422
     * An initialization routine,
 
423
     * a format string,
 
424
     * an open routine.
 
425
 
 
426
    Formats are placed in an dict by their format string for reference 
 
427
    during bzrdir opening. These should be subclasses of BzrDirFormat
 
428
    for consistency.
 
429
 
 
430
    Once a format is deprecated, just deprecate the initialize and open
 
431
    methods on the format class. Do not deprecate the object, as the 
 
432
    object will be created every system load.
 
433
    """
 
434
 
 
435
    _default_format = None
 
436
    """The default format used for new .bzr dirs."""
 
437
 
 
438
    _formats = {}
 
439
    """The known formats."""
 
440
 
 
441
    @classmethod
 
442
    def find_format(klass, transport):
 
443
        """Return the format registered for URL."""
 
444
        try:
 
445
            format_string = transport.get(".bzr/branch-format").read()
 
446
            return klass._formats[format_string]
 
447
        except errors.NoSuchFile:
 
448
            raise errors.NotBranchError(path=transport.base)
 
449
        except KeyError:
 
450
            raise errors.UnknownFormatError(format_string)
 
451
 
 
452
    @classmethod
 
453
    def get_default_format(klass):
 
454
        """Return the current default format."""
 
455
        return klass._default_format
 
456
 
 
457
    def get_format_string(self):
 
458
        """Return the ASCII format string that identifies this format."""
 
459
        raise NotImplementedError(self.get_format_string)
 
460
 
 
461
    def initialize(self, url):
 
462
        """Create a bzr control dir at this url and return an opened copy."""
 
463
        # Since we don't have a .bzr directory, inherit the
 
464
        # mode from the root directory
 
465
        t = get_transport(url)
 
466
        temp_control = LockableFiles(t, '')
 
467
        temp_control._transport.mkdir('.bzr',
 
468
                                      # FIXME: RBC 20060121 dont peek under
 
469
                                      # the covers
 
470
                                      mode=temp_control._dir_mode)
 
471
        file_mode = temp_control._file_mode
 
472
        del temp_control
 
473
        mutter('created control directory in ' + t.base)
 
474
        control = t.clone('.bzr')
 
475
        lock_file = 'branch-lock'
 
476
        utf8_files = [('README', 
 
477
                       "This is a Bazaar-NG control directory.\n"
 
478
                       "Do not change any files in this directory.\n"),
 
479
                      ('branch-format', self.get_format_string()),
 
480
                      ]
 
481
        # NB: no need to escape relative paths that are url safe.
 
482
        control.put(lock_file, StringIO(), mode=file_mode)
 
483
        control_files = LockableFiles(control, lock_file)
 
484
        control_files.lock_write()
 
485
        try:
 
486
            for file, content in utf8_files:
 
487
                control_files.put_utf8(file, content)
 
488
        finally:
 
489
            control_files.unlock()
 
490
        return self.open(t, _found=True)
 
491
 
 
492
    def is_supported(self):
 
493
        """Is this format supported?
 
494
 
 
495
        Supported formats must be initializable and openable.
 
496
        Unsupported formats may not support initialization or committing or 
 
497
        some other features depending on the reason for not being supported.
 
498
        """
 
499
        return True
 
500
 
 
501
    def open(self, transport, _found=False):
 
502
        """Return an instance of this format for the dir transport points at.
 
503
        
 
504
        _found is a private parameter, do not use it.
 
505
        """
 
506
        if not _found:
 
507
            assert isinstance(BzrDirFormat.find_format(transport),
 
508
                              self.__class__)
 
509
        return self._open(transport)
 
510
 
 
511
    def _open(self, transport):
 
512
        """Template method helper for opening BzrDirectories.
 
513
 
 
514
        This performs the actual open and any additional logic or parameter
 
515
        passing.
 
516
        """
 
517
        raise NotImplementedError(self._open)
 
518
 
 
519
    @classmethod
 
520
    def register_format(klass, format):
 
521
        klass._formats[format.get_format_string()] = format
 
522
 
 
523
    @classmethod
 
524
    def set_default_format(klass, format):
 
525
        klass._default_format = format
 
526
 
 
527
    @classmethod
 
528
    def unregister_format(klass, format):
 
529
        assert klass._formats[format.get_format_string()] is format
 
530
        del klass._formats[format.get_format_string()]
 
531
 
 
532
 
 
533
class BzrDirFormat4(BzrDirFormat):
 
534
    """Bzr dir format 4.
 
535
 
 
536
    This format is a combined format for working tree, branch and repository.
 
537
    It has:
 
538
     - Format 1 working trees
 
539
     - Format 4 branches
 
540
     - Format 4 repositories
 
541
 
 
542
    This format is deprecated: it indexes texts using a text it which is
 
543
    removed in format 5; write support for this format has been removed.
 
544
    """
 
545
 
 
546
    def get_format_string(self):
 
547
        """See BzrDirFormat.get_format_string()."""
 
548
        return "Bazaar-NG branch, format 0.0.4\n"
 
549
 
 
550
    def initialize(self, url):
 
551
        """Format 4 branches cannot be created."""
 
552
        raise errors.UninitializableFormat(self)
 
553
 
 
554
    def is_supported(self):
 
555
        """Format 4 is not supported.
 
556
 
 
557
        It is not supported because the model changed from 4 to 5 and the
 
558
        conversion logic is expensive - so doing it on the fly was not 
 
559
        feasible.
 
560
        """
 
561
        return False
 
562
 
 
563
    def _open(self, transport):
 
564
        """See BzrDirFormat._open."""
 
565
        return BzrDir4(transport, self)
 
566
 
 
567
 
 
568
class BzrDirFormat5(BzrDirFormat):
 
569
    """Bzr control format 5.
 
570
 
 
571
    This format is a combined format for working tree, branch and repository.
 
572
    It has:
 
573
     - Format 2 working trees
 
574
     - Format 4 branches
 
575
     - Format 6 repositories
 
576
    """
 
577
 
 
578
    def get_format_string(self):
 
579
        """See BzrDirFormat.get_format_string()."""
 
580
        return "Bazaar-NG branch, format 5\n"
 
581
 
 
582
    def _open(self, transport):
 
583
        """See BzrDirFormat._open."""
 
584
        return BzrDir5(transport, self)
 
585
 
 
586
 
 
587
class BzrDirFormat6(BzrDirFormat):
 
588
    """Bzr control format 6.
 
589
 
 
590
    This format is a combined format for working tree, branch and repository.
 
591
    It has:
 
592
     - Format 2 working trees
 
593
     - Format 4 branches
 
594
     - Format 6 repositories
 
595
    """
 
596
 
 
597
    def get_format_string(self):
 
598
        """See BzrDirFormat.get_format_string()."""
 
599
        return "Bazaar-NG branch, format 6\n"
 
600
 
 
601
    def _open(self, transport):
 
602
        """See BzrDirFormat._open."""
 
603
        return BzrDir6(transport, self)
 
604
 
 
605
 
 
606
class BzrDirMetaFormat1(BzrDirFormat):
 
607
    """Bzr meta control format 1
 
608
 
 
609
    This is the first format with split out working tree, branch and repository
 
610
    disk storage.
 
611
    It has:
 
612
     - Format 3 working trees
 
613
     - Format 5 branches
 
614
     - Format 7 repositories
 
615
    """
 
616
 
 
617
    def get_format_string(self):
 
618
        """See BzrDirFormat.get_format_string()."""
 
619
        return "Bazaar-NG meta directory, format 1\n"
 
620
 
 
621
    def _open(self, transport):
 
622
        """See BzrDirFormat._open."""
 
623
        return BzrDirMeta1(transport, self)
 
624
 
 
625
 
 
626
BzrDirFormat.register_format(BzrDirFormat4())
 
627
BzrDirFormat.register_format(BzrDirFormat5())
 
628
BzrDirFormat.register_format(BzrDirMetaFormat1())
 
629
__default_format = BzrDirFormat6()
 
630
BzrDirFormat.register_format(__default_format)
 
631
BzrDirFormat.set_default_format(__default_format)
 
632
 
 
633
 
 
634
class BzrDirTestProviderAdapter(object):
 
635
    """A tool to generate a suite testing multiple bzrdir formats at once.
 
636
 
 
637
    This is done by copying the test once for each transport and injecting
 
638
    the transport_server, transport_readonly_server, and bzrdir_format
 
639
    classes into each copy. Each copy is also given a new id() to make it
 
640
    easy to identify.
 
641
    """
 
642
 
 
643
    def __init__(self, transport_server, transport_readonly_server, formats):
 
644
        self._transport_server = transport_server
 
645
        self._transport_readonly_server = transport_readonly_server
 
646
        self._formats = formats
 
647
    
 
648
    def adapt(self, test):
 
649
        result = TestSuite()
 
650
        for format in self._formats:
 
651
            new_test = deepcopy(test)
 
652
            new_test.transport_server = self._transport_server
 
653
            new_test.transport_readonly_server = self._transport_readonly_server
 
654
            new_test.bzrdir_format = format
 
655
            def make_new_test_id():
 
656
                new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
 
657
                return lambda: new_id
 
658
            new_test.id = make_new_test_id()
 
659
            result.addTest(new_test)
 
660
        return result
 
661
 
 
662
 
 
663
class ScratchDir(BzrDir6):
 
664
    """Special test class: a bzrdir that cleans up itself..
 
665
 
 
666
    >>> d = ScratchDir()
 
667
    >>> base = d.transport.base
 
668
    >>> isdir(base)
 
669
    True
 
670
    >>> b.transport.__del__()
 
671
    >>> isdir(base)
 
672
    False
 
673
    """
 
674
 
 
675
    def __init__(self, files=[], dirs=[], transport=None):
 
676
        """Make a test branch.
 
677
 
 
678
        This creates a temporary directory and runs init-tree in it.
 
679
 
 
680
        If any files are listed, they are created in the working copy.
 
681
        """
 
682
        if transport is None:
 
683
            transport = bzrlib.transport.local.ScratchTransport()
 
684
            # local import for scope restriction
 
685
            BzrDirFormat6().initialize(transport.base)
 
686
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
 
687
            self.create_repository()
 
688
            self.create_branch()
 
689
            from bzrlib.workingtree import WorkingTree
 
690
            WorkingTree.create(self.open_branch(), transport.base)
 
691
        else:
 
692
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
 
693
 
 
694
        # BzrBranch creates a clone to .bzr and then forgets about the
 
695
        # original transport. A ScratchTransport() deletes itself and
 
696
        # everything underneath it when it goes away, so we need to
 
697
        # grab a local copy to prevent that from happening
 
698
        self._transport = transport
 
699
 
 
700
        for d in dirs:
 
701
            self._transport.mkdir(d)
 
702
            
 
703
        for f in files:
 
704
            self._transport.put(f, 'content of %s' % f)
 
705
 
 
706
    def clone(self):
 
707
        """
 
708
        >>> orig = ScratchDir(files=["file1", "file2"])
 
709
        >>> os.listdir(orig.base)
 
710
        [u'.bzr', u'file1', u'file2']
 
711
        >>> clone = orig.clone()
 
712
        >>> if os.name != 'nt':
 
713
        ...   os.path.samefile(orig.base, clone.base)
 
714
        ... else:
 
715
        ...   orig.base == clone.base
 
716
        ...
 
717
        False
 
718
        >>> os.listdir(clone.base)
 
719
        [u'.bzr', u'file1', u'file2']
 
720
        """
 
721
        from shutil import copytree
 
722
        from bzrlib.osutils import mkdtemp
 
723
        base = mkdtemp()
 
724
        os.rmdir(base)
 
725
        copytree(self.base, base, symlinks=True)
 
726
        return ScratchDir(
 
727
            transport=bzrlib.transport.local.ScratchTransport(base))