/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

Provide a revision.get_history(repository) method for generating a synthetic revision history.

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_repository_transport(self, repository_format):
 
150
        """Get the transport for use by repository format in this BzrDir.
 
151
 
 
152
        Note that bzr dirs that do not support format strings will raise
 
153
        IncompatibleFormat if the repository format they are given has
 
154
        a format string, and vice verca.
 
155
 
 
156
        If repository_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_repository_transport)
 
161
        
 
162
    def get_workingtree_transport(self, branch_format):
 
163
        """Get the transport for use by workingtree format in this BzrDir.
 
164
 
 
165
        Note that bzr dirs that do not support format strings will raise
 
166
        IncompatibleFormat if the workingtree format they are given has
 
167
        a format string, and vice verca.
 
168
 
 
169
        If workingtree_format is None, the transport is returned with no 
 
170
        checking. if it is not None, then the returned transport is
 
171
        guaranteed to point to an existing directory ready for use.
 
172
        """
 
173
        raise NotImplementedError(self.get_workingtree_transport)
 
174
        
 
175
    def __init__(self, _transport, _format):
 
176
        """Initialize a Bzr control dir object.
 
177
        
 
178
        Only really common logic should reside here, concrete classes should be
 
179
        made with varying behaviours.
 
180
 
 
181
        _format: the format that is creating this BzrDir instance.
 
182
        _transport: the transport this dir is based at.
 
183
        """
 
184
        self._format = _format
 
185
        self.transport = _transport.clone('.bzr')
 
186
 
 
187
    @staticmethod
 
188
    def open_unsupported(base):
 
189
        """Open a branch which is not supported."""
 
190
        return BzrDir.open(base, _unsupported=True)
 
191
        
 
192
    @staticmethod
 
193
    def open(base, _unsupported=False):
 
194
        """Open an existing branch, rooted at 'base' (url)
 
195
        
 
196
        _unsupported is a private parameter to the BzrDir class.
 
197
        """
 
198
        t = get_transport(base)
 
199
        mutter("trying to open %r with transport %r", base, t)
 
200
        format = BzrDirFormat.find_format(t)
 
201
        if not _unsupported and not format.is_supported():
 
202
            # see open_downlevel to open legacy branches.
 
203
            raise errors.UnsupportedFormatError(
 
204
                    'sorry, format %s not supported' % format,
 
205
                    ['use a different bzr version',
 
206
                     'or remove the .bzr directory'
 
207
                     ' and "bzr init" again'])
 
208
        return format.open(t, _found=True)
 
209
 
 
210
    def open_branch(self, unsupported=False):
 
211
        """Open the branch object at this BzrDir if one is present.
 
212
 
 
213
        If unsupported is True, then no longer supported branch formats can
 
214
        still be opened.
 
215
        
 
216
        TODO: static convenience version of this?
 
217
        """
 
218
        raise NotImplementedError(self.open_branch)
 
219
 
 
220
    @staticmethod
 
221
    def open_containing(url):
 
222
        """Open an existing branch which contains url.
 
223
        
 
224
        This probes for a branch at url, and searches upwards from there.
 
225
 
 
226
        Basically we keep looking up until we find the control directory or
 
227
        run into the root.  If there isn't one, raises NotBranchError.
 
228
        If there is one and it is either an unrecognised format or an unsupported 
 
229
        format, UnknownFormatError or UnsupportedFormatError are raised.
 
230
        If there is one, it is returned, along with the unused portion of url.
 
231
        """
 
232
        t = get_transport(url)
 
233
        # this gets the normalised url back. I.e. '.' -> the full path.
 
234
        url = t.base
 
235
        while True:
 
236
            try:
 
237
                format = BzrDirFormat.find_format(t)
 
238
                return format.open(t), t.relpath(url)
 
239
            except errors.NotBranchError, e:
 
240
                mutter('not a branch in: %r %s', t.base, e)
 
241
            new_t = t.clone('..')
 
242
            if new_t.base == t.base:
 
243
                # reached the root, whatever that may be
 
244
                raise errors.NotBranchError(path=url)
 
245
            t = new_t
 
246
 
 
247
    def open_repository(self, _unsupported=False):
 
248
        """Open the repository object at this BzrDir if one is present.
 
249
 
 
250
        This will not follow the Branch object pointer - its strictly a direct
 
251
        open facility. Most client code should use open_branch().repository to
 
252
        get at a repository.
 
253
 
 
254
        _unsupported is a private parameter, not part of the api.
 
255
        TODO: static convenience version of this?
 
256
        """
 
257
        raise NotImplementedError(self.open_repository)
 
258
 
 
259
    def open_workingtree(self, _unsupported=False):
 
260
        """Open the workingtree object at this BzrDir if one is present.
 
261
        
 
262
        TODO: static convenience version of this?
 
263
        """
 
264
        raise NotImplementedError(self.open_workingtree)
 
265
 
 
266
 
 
267
class BzrDirPreSplitOut(BzrDir):
 
268
    """A common class for the all-in-one formats."""
 
269
 
 
270
    def create_branch(self):
 
271
        """See BzrDir.create_branch."""
 
272
        from bzrlib.branch import BzrBranchFormat4
 
273
        return BzrBranchFormat4().initialize(self)
 
274
 
 
275
    def get_branch_transport(self, branch_format):
 
276
        """See BzrDir.get_branch_transport()."""
 
277
        if branch_format is None:
 
278
            return self.transport
 
279
        try:
 
280
            branch_format.get_format_string()
 
281
        except NotImplementedError:
 
282
            return self.transport
 
283
        raise errors.IncompatibleFormat(branch_format, self._format)
 
284
 
 
285
    def get_repository_transport(self, repository_format):
 
286
        """See BzrDir.get_repository_transport()."""
 
287
        if repository_format is None:
 
288
            return self.transport
 
289
        try:
 
290
            repository_format.get_format_string()
 
291
        except NotImplementedError:
 
292
            return self.transport
 
293
        raise errors.IncompatibleFormat(repository_format, self._format)
 
294
 
 
295
    def get_workingtree_transport(self, workingtree_format):
 
296
        """See BzrDir.get_workingtree_transport()."""
 
297
        if workingtree_format is None:
 
298
            return self.transport
 
299
        try:
 
300
            workingtree_format.get_format_string()
 
301
        except NotImplementedError:
 
302
            return self.transport
 
303
        raise errors.IncompatibleFormat(workingtree_format, self._format)
 
304
 
 
305
    def open_branch(self, unsupported=False):
 
306
        """See BzrDir.open_branch."""
 
307
        from bzrlib.branch import BzrBranchFormat4
 
308
        format = BzrBranchFormat4()
 
309
        self._check_supported(format, unsupported)
 
310
        return format.open(self, _found=True)
 
311
 
 
312
 
 
313
class BzrDir4(BzrDirPreSplitOut):
 
314
    """A .bzr version 4 control object."""
 
315
 
 
316
    def create_repository(self):
 
317
        """See BzrDir.create_repository."""
 
318
        from bzrlib.repository import RepositoryFormat4
 
319
        return RepositoryFormat4().initialize(self)
 
320
 
 
321
    def open_repository(self):
 
322
        """See BzrDir.open_repository."""
 
323
        from bzrlib.repository import RepositoryFormat4
 
324
        return RepositoryFormat4().open(self, _found=True)
 
325
 
 
326
 
 
327
class BzrDir5(BzrDirPreSplitOut):
 
328
    """A .bzr version 5 control object."""
 
329
 
 
330
    def create_repository(self):
 
331
        """See BzrDir.create_repository."""
 
332
        from bzrlib.repository import RepositoryFormat5
 
333
        return RepositoryFormat5().initialize(self)
 
334
 
 
335
    def create_workingtree(self):
 
336
        """See BzrDir.create_workingtree."""
 
337
        from bzrlib.workingtree import WorkingTreeFormat2
 
338
        return WorkingTreeFormat2().initialize(self)
 
339
 
 
340
    def open_repository(self):
 
341
        """See BzrDir.open_repository."""
 
342
        from bzrlib.repository import RepositoryFormat5
 
343
        return RepositoryFormat5().open(self, _found=True)
 
344
 
 
345
    def open_workingtree(self, _unsupported=False):
 
346
        """See BzrDir.create_workingtree."""
 
347
        from bzrlib.workingtree import WorkingTreeFormat2
 
348
        return WorkingTreeFormat2().open(self, _found=True)
 
349
 
 
350
 
 
351
class BzrDir6(BzrDirPreSplitOut):
 
352
    """A .bzr version 6 control object."""
 
353
 
 
354
    def create_repository(self):
 
355
        """See BzrDir.create_repository."""
 
356
        from bzrlib.repository import RepositoryFormat6
 
357
        return RepositoryFormat6().initialize(self)
 
358
 
 
359
    def create_workingtree(self):
 
360
        """See BzrDir.create_workingtree."""
 
361
        from bzrlib.workingtree import WorkingTreeFormat2
 
362
        return WorkingTreeFormat2().initialize(self)
 
363
 
 
364
    def open_repository(self):
 
365
        """See BzrDir.open_repository."""
 
366
        from bzrlib.repository import RepositoryFormat6
 
367
        return RepositoryFormat6().open(self, _found=True)
 
368
 
 
369
    def open_workingtree(self, _unsupported=False):
 
370
        """See BzrDir.create_workingtree."""
 
371
        from bzrlib.workingtree import WorkingTreeFormat2
 
372
        return WorkingTreeFormat2().open(self, _found=True)
 
373
 
 
374
 
 
375
class BzrDirMeta1(BzrDir):
 
376
    """A .bzr meta version 1 control object.
 
377
    
 
378
    This is the first control object where the 
 
379
    individual formats are really split out.
 
380
    """
 
381
 
 
382
    def create_branch(self):
 
383
        """See BzrDir.create_branch."""
 
384
        from bzrlib.branch import BranchFormat
 
385
        return BranchFormat.get_default_format().initialize(self)
 
386
 
 
387
    def create_repository(self):
 
388
        """See BzrDir.create_repository."""
 
389
        from bzrlib.repository import RepositoryFormat
 
390
        return RepositoryFormat.get_default_format().initialize(self)
 
391
 
 
392
    def create_workingtree(self):
 
393
        """See BzrDir.create_workingtree."""
 
394
        from bzrlib.workingtree import WorkingTreeFormat
 
395
        return WorkingTreeFormat.get_default_format().initialize(self)
 
396
 
 
397
    def get_branch_transport(self, branch_format):
 
398
        """See BzrDir.get_branch_transport()."""
 
399
        if branch_format is None:
 
400
            return self.transport.clone('branch')
 
401
        try:
 
402
            branch_format.get_format_string()
 
403
        except NotImplementedError:
 
404
            raise errors.IncompatibleFormat(branch_format, self._format)
 
405
        try:
 
406
            self.transport.mkdir('branch')
 
407
        except errors.FileExists:
 
408
            pass
 
409
        return self.transport.clone('branch')
 
410
 
 
411
    def get_repository_transport(self, repository_format):
 
412
        """See BzrDir.get_repository_transport()."""
 
413
        if repository_format is None:
 
414
            return self.transport.clone('repository')
 
415
        try:
 
416
            repository_format.get_format_string()
 
417
        except NotImplementedError:
 
418
            raise errors.IncompatibleFormat(repository_format, self._format)
 
419
        try:
 
420
            self.transport.mkdir('repository')
 
421
        except errors.FileExists:
 
422
            pass
 
423
        return self.transport.clone('repository')
 
424
 
 
425
    def get_workingtree_transport(self, workingtree_format):
 
426
        """See BzrDir.get_workingtree_transport()."""
 
427
        if workingtree_format is None:
 
428
            return self.transport.clone('checkout')
 
429
        try:
 
430
            workingtree_format.get_format_string()
 
431
        except NotImplementedError:
 
432
            raise errors.IncompatibleFormat(workingtree_format, self._format)
 
433
        try:
 
434
            self.transport.mkdir('checkout')
 
435
        except errors.FileExists:
 
436
            pass
 
437
        return self.transport.clone('checkout')
 
438
 
 
439
    def open_branch(self, unsupported=False):
 
440
        """See BzrDir.open_branch."""
 
441
        from bzrlib.branch import BranchFormat
 
442
        format = BranchFormat.find_format(self)
 
443
        self._check_supported(format, unsupported)
 
444
        return format.open(self, _found=True)
 
445
 
 
446
    def open_repository(self, unsupported=False):
 
447
        """See BzrDir.open_repository."""
 
448
        from bzrlib.repository import RepositoryFormat
 
449
        format = RepositoryFormat.find_format(self)
 
450
        self._check_supported(format, unsupported)
 
451
        return format.open(self, _found=True)
 
452
 
 
453
    def open_workingtree(self, unsupported=False):
 
454
        """See BzrDir.create_workingtree."""
 
455
        from bzrlib.workingtree import WorkingTreeFormat
 
456
        format = WorkingTreeFormat.find_format(self)
 
457
        self._check_supported(format, unsupported)
 
458
        return format.open(self, _found=True)
 
459
 
 
460
 
 
461
class BzrDirFormat(object):
 
462
    """An encapsulation of the initialization and open routines for a format.
 
463
 
 
464
    Formats provide three things:
 
465
     * An initialization routine,
 
466
     * a format string,
 
467
     * an open routine.
 
468
 
 
469
    Formats are placed in an dict by their format string for reference 
 
470
    during bzrdir opening. These should be subclasses of BzrDirFormat
 
471
    for consistency.
 
472
 
 
473
    Once a format is deprecated, just deprecate the initialize and open
 
474
    methods on the format class. Do not deprecate the object, as the 
 
475
    object will be created every system load.
 
476
    """
 
477
 
 
478
    _default_format = None
 
479
    """The default format used for new .bzr dirs."""
 
480
 
 
481
    _formats = {}
 
482
    """The known formats."""
 
483
 
 
484
    @classmethod
 
485
    def find_format(klass, transport):
 
486
        """Return the format registered for URL."""
 
487
        try:
 
488
            format_string = transport.get(".bzr/branch-format").read()
 
489
            return klass._formats[format_string]
 
490
        except errors.NoSuchFile:
 
491
            raise errors.NotBranchError(path=transport.base)
 
492
        except KeyError:
 
493
            raise errors.UnknownFormatError(format_string)
 
494
 
 
495
    @classmethod
 
496
    def get_default_format(klass):
 
497
        """Return the current default format."""
 
498
        return klass._default_format
 
499
 
 
500
    def get_format_string(self):
 
501
        """Return the ASCII format string that identifies this format."""
 
502
        raise NotImplementedError(self.get_format_string)
 
503
 
 
504
    def initialize(self, url):
 
505
        """Create a bzr control dir at this url and return an opened copy."""
 
506
        # Since we don't have a .bzr directory, inherit the
 
507
        # mode from the root directory
 
508
        t = get_transport(url)
 
509
        temp_control = LockableFiles(t, '')
 
510
        temp_control._transport.mkdir('.bzr',
 
511
                                      # FIXME: RBC 20060121 dont peek under
 
512
                                      # the covers
 
513
                                      mode=temp_control._dir_mode)
 
514
        file_mode = temp_control._file_mode
 
515
        del temp_control
 
516
        mutter('created control directory in ' + t.base)
 
517
        control = t.clone('.bzr')
 
518
        lock_file = 'branch-lock'
 
519
        utf8_files = [('README', 
 
520
                       "This is a Bazaar-NG control directory.\n"
 
521
                       "Do not change any files in this directory.\n"),
 
522
                      ('branch-format', self.get_format_string()),
 
523
                      ]
 
524
        # NB: no need to escape relative paths that are url safe.
 
525
        control.put(lock_file, StringIO(), mode=file_mode)
 
526
        control_files = LockableFiles(control, lock_file)
 
527
        control_files.lock_write()
 
528
        try:
 
529
            for file, content in utf8_files:
 
530
                control_files.put_utf8(file, content)
 
531
        finally:
 
532
            control_files.unlock()
 
533
        return self.open(t, _found=True)
 
534
 
 
535
    def is_supported(self):
 
536
        """Is this format supported?
 
537
 
 
538
        Supported formats must be initializable and openable.
 
539
        Unsupported formats may not support initialization or committing or 
 
540
        some other features depending on the reason for not being supported.
 
541
        """
 
542
        return True
 
543
 
 
544
    def open(self, transport, _found=False):
 
545
        """Return an instance of this format for the dir transport points at.
 
546
        
 
547
        _found is a private parameter, do not use it.
 
548
        """
 
549
        if not _found:
 
550
            assert isinstance(BzrDirFormat.find_format(transport),
 
551
                              self.__class__)
 
552
        return self._open(transport)
 
553
 
 
554
    def _open(self, transport):
 
555
        """Template method helper for opening BzrDirectories.
 
556
 
 
557
        This performs the actual open and any additional logic or parameter
 
558
        passing.
 
559
        """
 
560
        raise NotImplementedError(self._open)
 
561
 
 
562
    @classmethod
 
563
    def register_format(klass, format):
 
564
        klass._formats[format.get_format_string()] = format
 
565
 
 
566
    @classmethod
 
567
    def set_default_format(klass, format):
 
568
        klass._default_format = format
 
569
 
 
570
    @classmethod
 
571
    def unregister_format(klass, format):
 
572
        assert klass._formats[format.get_format_string()] is format
 
573
        del klass._formats[format.get_format_string()]
 
574
 
 
575
 
 
576
class BzrDirFormat4(BzrDirFormat):
 
577
    """Bzr dir format 4.
 
578
 
 
579
    This format is a combined format for working tree, branch and repository.
 
580
    It has:
 
581
     - Format 1 working trees
 
582
     - Format 4 branches
 
583
     - Format 4 repositories
 
584
 
 
585
    This format is deprecated: it indexes texts using a text it which is
 
586
    removed in format 5; write support for this format has been removed.
 
587
    """
 
588
 
 
589
    def get_format_string(self):
 
590
        """See BzrDirFormat.get_format_string()."""
 
591
        return "Bazaar-NG branch, format 0.0.4\n"
 
592
 
 
593
    def initialize(self, url):
 
594
        """Format 4 branches cannot be created."""
 
595
        raise errors.UninitializableFormat(self)
 
596
 
 
597
    def is_supported(self):
 
598
        """Format 4 is not supported.
 
599
 
 
600
        It is not supported because the model changed from 4 to 5 and the
 
601
        conversion logic is expensive - so doing it on the fly was not 
 
602
        feasible.
 
603
        """
 
604
        return False
 
605
 
 
606
    def _open(self, transport):
 
607
        """See BzrDirFormat._open."""
 
608
        return BzrDir4(transport, self)
 
609
 
 
610
 
 
611
class BzrDirFormat5(BzrDirFormat):
 
612
    """Bzr control format 5.
 
613
 
 
614
    This format is a combined format for working tree, branch and repository.
 
615
    It has:
 
616
     - Format 2 working trees
 
617
     - Format 4 branches
 
618
     - Format 6 repositories
 
619
    """
 
620
 
 
621
    def get_format_string(self):
 
622
        """See BzrDirFormat.get_format_string()."""
 
623
        return "Bazaar-NG branch, format 5\n"
 
624
 
 
625
    def _open(self, transport):
 
626
        """See BzrDirFormat._open."""
 
627
        return BzrDir5(transport, self)
 
628
 
 
629
 
 
630
class BzrDirFormat6(BzrDirFormat):
 
631
    """Bzr control format 6.
 
632
 
 
633
    This format is a combined format for working tree, branch and repository.
 
634
    It has:
 
635
     - Format 2 working trees
 
636
     - Format 4 branches
 
637
     - Format 6 repositories
 
638
    """
 
639
 
 
640
    def get_format_string(self):
 
641
        """See BzrDirFormat.get_format_string()."""
 
642
        return "Bazaar-NG branch, format 6\n"
 
643
 
 
644
    def _open(self, transport):
 
645
        """See BzrDirFormat._open."""
 
646
        return BzrDir6(transport, self)
 
647
 
 
648
 
 
649
class BzrDirMetaFormat1(BzrDirFormat):
 
650
    """Bzr meta control format 1
 
651
 
 
652
    This is the first format with split out working tree, branch and repository
 
653
    disk storage.
 
654
    It has:
 
655
     - Format 3 working trees
 
656
     - Format 5 branches
 
657
     - Format 7 repositories
 
658
    """
 
659
 
 
660
    def get_format_string(self):
 
661
        """See BzrDirFormat.get_format_string()."""
 
662
        return "Bazaar-NG meta directory, format 1\n"
 
663
 
 
664
    def _open(self, transport):
 
665
        """See BzrDirFormat._open."""
 
666
        return BzrDirMeta1(transport, self)
 
667
 
 
668
 
 
669
BzrDirFormat.register_format(BzrDirFormat4())
 
670
BzrDirFormat.register_format(BzrDirFormat5())
 
671
BzrDirFormat.register_format(BzrDirMetaFormat1())
 
672
__default_format = BzrDirFormat6()
 
673
BzrDirFormat.register_format(__default_format)
 
674
BzrDirFormat.set_default_format(__default_format)
 
675
 
 
676
 
 
677
class BzrDirTestProviderAdapter(object):
 
678
    """A tool to generate a suite testing multiple bzrdir formats at once.
 
679
 
 
680
    This is done by copying the test once for each transport and injecting
 
681
    the transport_server, transport_readonly_server, and bzrdir_format
 
682
    classes into each copy. Each copy is also given a new id() to make it
 
683
    easy to identify.
 
684
    """
 
685
 
 
686
    def __init__(self, transport_server, transport_readonly_server, formats):
 
687
        self._transport_server = transport_server
 
688
        self._transport_readonly_server = transport_readonly_server
 
689
        self._formats = formats
 
690
    
 
691
    def adapt(self, test):
 
692
        result = TestSuite()
 
693
        for format in self._formats:
 
694
            new_test = deepcopy(test)
 
695
            new_test.transport_server = self._transport_server
 
696
            new_test.transport_readonly_server = self._transport_readonly_server
 
697
            new_test.bzrdir_format = format
 
698
            def make_new_test_id():
 
699
                new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
 
700
                return lambda: new_id
 
701
            new_test.id = make_new_test_id()
 
702
            result.addTest(new_test)
 
703
        return result
 
704
 
 
705
 
 
706
class ScratchDir(BzrDir6):
 
707
    """Special test class: a bzrdir that cleans up itself..
 
708
 
 
709
    >>> d = ScratchDir()
 
710
    >>> base = d.transport.base
 
711
    >>> isdir(base)
 
712
    True
 
713
    >>> b.transport.__del__()
 
714
    >>> isdir(base)
 
715
    False
 
716
    """
 
717
 
 
718
    def __init__(self, files=[], dirs=[], transport=None):
 
719
        """Make a test branch.
 
720
 
 
721
        This creates a temporary directory and runs init-tree in it.
 
722
 
 
723
        If any files are listed, they are created in the working copy.
 
724
        """
 
725
        if transport is None:
 
726
            transport = bzrlib.transport.local.ScratchTransport()
 
727
            # local import for scope restriction
 
728
            BzrDirFormat6().initialize(transport.base)
 
729
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
 
730
            self.create_repository()
 
731
            self.create_branch()
 
732
            from bzrlib.workingtree import WorkingTree
 
733
            WorkingTree.create(self.open_branch(), transport.base)
 
734
        else:
 
735
            super(ScratchDir, self).__init__(transport, BzrDirFormat6())
 
736
 
 
737
        # BzrBranch creates a clone to .bzr and then forgets about the
 
738
        # original transport. A ScratchTransport() deletes itself and
 
739
        # everything underneath it when it goes away, so we need to
 
740
        # grab a local copy to prevent that from happening
 
741
        self._transport = transport
 
742
 
 
743
        for d in dirs:
 
744
            self._transport.mkdir(d)
 
745
            
 
746
        for f in files:
 
747
            self._transport.put(f, 'content of %s' % f)
 
748
 
 
749
    def clone(self):
 
750
        """
 
751
        >>> orig = ScratchDir(files=["file1", "file2"])
 
752
        >>> os.listdir(orig.base)
 
753
        [u'.bzr', u'file1', u'file2']
 
754
        >>> clone = orig.clone()
 
755
        >>> if os.name != 'nt':
 
756
        ...   os.path.samefile(orig.base, clone.base)
 
757
        ... else:
 
758
        ...   orig.base == clone.base
 
759
        ...
 
760
        False
 
761
        >>> os.listdir(clone.base)
 
762
        [u'.bzr', u'file1', u'file2']
 
763
        """
 
764
        from shutil import copytree
 
765
        from bzrlib.osutils import mkdtemp
 
766
        base = mkdtemp()
 
767
        os.rmdir(base)
 
768
        copytree(self.base, base, symlinks=True)
 
769
        return ScratchDir(
 
770
            transport=bzrlib.transport.local.ScratchTransport(base))