1# Copyright (C) 2017 Breezy Developers
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17from .. import (
18    config,
19    errors,
20    controldir,
21    pyutils,
22    registry,
23    )
24
25
26class LineEndingError(errors.BzrError):
27
28    _fmt = ("Line ending corrupted for file: %(file)s; "
29            "Maybe your files got corrupted in transport?")
30
31    def __init__(self, file):
32        self.file = file
33
34
35class BzrProber(controldir.Prober):
36    """Prober for formats that use a .bzr/ control directory."""
37
38    formats = registry.FormatRegistry(controldir.network_format_registry)
39    """The known .bzr formats."""
40
41    @classmethod
42    def priority(klass, transport):
43        return 10
44
45    @classmethod
46    def probe_transport(klass, transport):
47        """Return the .bzrdir style format present in a directory."""
48        try:
49            format_string = transport.get_bytes(".bzr/branch-format")
50        except errors.NoSuchFile:
51            raise errors.NotBranchError(path=transport.base)
52        except errors.BadHttpRequest as e:
53            if e.reason == 'no such method: .bzr':
54                # hgweb
55                raise errors.NotBranchError(path=transport.base)
56            raise
57
58        try:
59            first_line = format_string[:format_string.index(b"\n") + 1]
60        except ValueError:
61            first_line = format_string
62        if (first_line.startswith(b'<!DOCTYPE') or
63                first_line.startswith(b'<html')):
64            raise errors.NotBranchError(
65                path=transport.base, detail="format file looks like HTML")
66        try:
67            cls = klass.formats.get(first_line)
68        except KeyError:
69            if first_line.endswith(b"\r\n"):
70                raise LineEndingError(file=".bzr/branch-format")
71            else:
72                raise errors.UnknownFormatError(
73                    format=first_line, kind='bzrdir')
74        return cls.from_string(format_string)
75
76    @classmethod
77    def known_formats(cls):
78        result = []
79        for name, format in cls.formats.items():
80            if callable(format):
81                format = format()
82            result.append(format)
83        return result
84
85
86controldir.ControlDirFormat.register_prober(BzrProber)
87
88
89class RemoteBzrProber(controldir.Prober):
90    """Prober for remote servers that provide a Bazaar smart server."""
91
92    @classmethod
93    def priority(klass, transport):
94        return -10
95
96    @classmethod
97    def probe_transport(klass, transport):
98        """Return a RemoteBzrDirFormat object if it looks possible."""
99        try:
100            medium = transport.get_smart_medium()
101        except (NotImplementedError, AttributeError,
102                errors.TransportNotPossible, errors.NoSmartMedium,
103                errors.SmartProtocolError):
104            # no smart server, so not a branch for this format type.
105            raise errors.NotBranchError(path=transport.base)
106        else:
107            # Decline to open it if the server doesn't support our required
108            # version (3) so that the VFS-based transport will do it.
109            if medium.should_probe():
110                try:
111                    server_version = medium.protocol_version()
112                except errors.SmartProtocolError:
113                    # Apparently there's no usable smart server there, even though
114                    # the medium supports the smart protocol.
115                    raise errors.NotBranchError(path=transport.base)
116                if server_version != '2':
117                    raise errors.NotBranchError(path=transport.base)
118            from .remote import RemoteBzrDirFormat
119            return RemoteBzrDirFormat()
120
121    @classmethod
122    def known_formats(cls):
123        from .remote import RemoteBzrDirFormat
124        return [RemoteBzrDirFormat()]
125
126
127controldir.ControlDirFormat.register_prober(RemoteBzrProber)
128
129# Register bzr formats
130BzrProber.formats.register_lazy(
131    b"Bazaar-NG meta directory, format 1\n",
132    __name__ + '.bzrdir', 'BzrDirMetaFormat1')
133BzrProber.formats.register_lazy(
134    b"Bazaar meta directory, format 1 (with colocated branches)\n",
135    __name__ + '.bzrdir', 'BzrDirMetaFormat1Colo')
136
137
138def register_metadir(registry, key,
139                     repository_format, help, native=True, deprecated=False,
140                     branch_format=None,
141                     tree_format=None,
142                     hidden=False,
143                     experimental=False,
144                     bzrdir_format=None):
145    """Register a metadir subformat.
146
147    These all use a meta bzrdir, but can be parameterized by the
148    Repository/Branch/WorkingTreeformats.
149
150    :param repository_format: The fully-qualified repository format class
151        name as a string.
152    :param branch_format: Fully-qualified branch format class name as
153        a string.
154    :param tree_format: Fully-qualified tree format class name as
155        a string.
156    """
157    if bzrdir_format is None:
158        bzrdir_format = 'breezy.bzr.bzrdir.BzrDirMetaFormat1'
159    # This should be expanded to support setting WorkingTree and Branch
160    # formats, once the API supports that.
161
162    def _load(full_name):
163        mod_name, factory_name = full_name.rsplit('.', 1)
164        try:
165            factory = pyutils.get_named_object(mod_name, factory_name)
166        except ImportError as e:
167            raise ImportError('failed to load %s: %s' % (full_name, e))
168        except AttributeError:
169            raise AttributeError('no factory %s in module %r'
170                                 % (full_name, sys.modules[mod_name]))
171        return factory()
172
173    def helper():
174        bd = _load(bzrdir_format)
175        if branch_format is not None:
176            bd.set_branch_format(_load(branch_format))
177        if tree_format is not None:
178            bd.workingtree_format = _load(tree_format)
179        if repository_format is not None:
180            bd.repository_format = _load(repository_format)
181        return bd
182    registry.register(key, helper, help, native, deprecated, hidden,
183                      experimental)
184
185
186register_metadir(
187    controldir.format_registry, 'knit',
188    'breezy.bzr.knitrepo.RepositoryFormatKnit1',
189    'Format using knits.  Recommended for interoperation with bzr <= 0.14.',
190    branch_format='breezy.bzr.fullhistory.BzrBranchFormat5',
191    tree_format='breezy.bzr.workingtree_3.WorkingTreeFormat3',
192    hidden=True,
193    deprecated=True)
194register_metadir(
195    controldir.format_registry, 'dirstate',
196    'breezy.bzr.knitrepo.RepositoryFormatKnit1',
197    help='Format using dirstate for working trees. '
198    'Compatible with bzr 0.8 and '
199    'above when accessed over the network. Introduced in bzr 0.15.',
200    branch_format='breezy.bzr.fullhistory.BzrBranchFormat5',
201    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat4',
202    hidden=True,
203    deprecated=True)
204register_metadir(
205    controldir.format_registry, 'dirstate-tags',
206    'breezy.bzr.knitrepo.RepositoryFormatKnit1',
207    help='Variant of dirstate with support for tags. '
208    'Introduced in bzr 0.15.',
209    branch_format='breezy.bzr.branch.BzrBranchFormat6',
210    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat4',
211    hidden=True,
212    deprecated=True)
213register_metadir(
214    controldir.format_registry, 'rich-root',
215    'breezy.bzr.knitrepo.RepositoryFormatKnit4',
216    help='Variant of dirstate with better handling of tree roots. '
217    'Introduced in bzr 1.0.',
218    branch_format='breezy.bzr.branch.BzrBranchFormat6',
219    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat4',
220    hidden=True,
221    deprecated=True)
222register_metadir(
223    controldir.format_registry, 'dirstate-with-subtree',
224    'breezy.bzr.knitrepo.RepositoryFormatKnit3',
225    help='Variant of dirstate with support for nested trees. '
226    'Introduced in 0.15.',
227    branch_format='breezy.bzr.branch.BzrBranchFormat6',
228    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat4',
229    experimental=True,
230    hidden=True,
231    )
232register_metadir(
233    controldir.format_registry, 'pack-0.92',
234    'breezy.bzr.knitpack_repo.RepositoryFormatKnitPack1',
235    help='Pack-based format used in 1.x series. Introduced in 0.92. '
236    'Interoperates with bzr repositories before 0.92 but cannot be '
237    'read by bzr < 0.92.',
238    branch_format='breezy.bzr.branch.BzrBranchFormat6',
239    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat4',
240    deprecated=True,
241    hidden=True,
242    )
243register_metadir(
244    controldir.format_registry, 'pack-0.92-subtree',
245    'breezy.bzr.knitpack_repo.RepositoryFormatKnitPack3',
246    help='Pack-based format used in 1.x series, with subtree support. '
247    'Introduced in 0.92. Interoperates with '
248    'bzr repositories before 0.92 but cannot be read by bzr < 0.92.',
249    branch_format='breezy.bzr.branch.BzrBranchFormat6',
250    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat4',
251    hidden=True,
252    deprecated=True,
253    experimental=True,
254    )
255register_metadir(
256    controldir.format_registry, 'rich-root-pack',
257    'breezy.bzr.knitpack_repo.RepositoryFormatKnitPack4',
258    help='A variant of pack-0.92 that supports rich-root data '
259    '(needed for bzr-svn and bzr-git). Introduced in 1.0.',
260    branch_format='breezy.bzr.branch.BzrBranchFormat6',
261    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat4',
262    hidden=True,
263    deprecated=True,
264    )
265register_metadir(
266    controldir.format_registry, '1.6',
267    'breezy.bzr.knitpack_repo.RepositoryFormatKnitPack5',
268    help='A format that allows a branch to indicate that there is another '
269    '(stacked) repository that should be used to access data that is '
270    'not present locally.',
271    branch_format='breezy.bzr.branch.BzrBranchFormat7',
272    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat4',
273    hidden=True,
274    deprecated=True,
275    )
276register_metadir(
277    controldir.format_registry, '1.6.1-rich-root',
278    'breezy.bzr.knitpack_repo.RepositoryFormatKnitPack5RichRoot',
279    help='A variant of 1.6 that supports rich-root data '
280    '(needed for bzr-svn and bzr-git).',
281    branch_format='breezy.bzr.branch.BzrBranchFormat7',
282    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat4',
283    hidden=True,
284    deprecated=True,
285    )
286register_metadir(
287    controldir.format_registry, '1.9',
288    'breezy.bzr.knitpack_repo.RepositoryFormatKnitPack6',
289    help='A repository format using B+tree indexes. These indexes '
290    'are smaller in size, have smarter caching and provide faster '
291    'performance for most operations.',
292    branch_format='breezy.bzr.branch.BzrBranchFormat7',
293    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat4',
294    hidden=True,
295    deprecated=True,
296    )
297register_metadir(
298    controldir.format_registry, '1.9-rich-root',
299    'breezy.bzr.knitpack_repo.RepositoryFormatKnitPack6RichRoot',
300    help='A variant of 1.9 that supports rich-root data '
301    '(needed for bzr-svn and bzr-git).',
302    branch_format='breezy.bzr.branch.BzrBranchFormat7',
303    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat4',
304    hidden=True,
305    deprecated=True,
306    )
307register_metadir(
308    controldir.format_registry, '1.14',
309    'breezy.bzr.knitpack_repo.RepositoryFormatKnitPack6',
310    help='A working-tree format that supports content filtering.',
311    branch_format='breezy.bzr.branch.BzrBranchFormat7',
312    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat5',
313    hidden=True,
314    deprecated=True,
315    )
316register_metadir(
317    controldir.format_registry, '1.14-rich-root',
318    'breezy.bzr.knitpack_repo.RepositoryFormatKnitPack6RichRoot',
319    help='A variant of 1.14 that supports rich-root data '
320    '(needed for bzr-svn and bzr-git).',
321    branch_format='breezy.bzr.branch.BzrBranchFormat7',
322    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat5',
323    hidden=True,
324    deprecated=True,
325    )
326# The following un-numbered 'development' formats should always just be aliases.
327register_metadir(
328    controldir.format_registry, 'development-subtree',
329    'breezy.bzr.groupcompress_repo.RepositoryFormat2aSubtree',
330    help='Current development format, subtree variant. Can convert data to and '
331         'from pack-0.92-subtree (and anything compatible with '
332         'pack-0.92-subtree) format repositories. Repositories and branches in '
333         'this format can only be read by bzr.dev. Please read '
334         'https://www.breezy-vcs.org/developers/development-repo.html '
335         'before use.',
336    branch_format='breezy.bzr.branch.BzrBranchFormat8',
337    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat6',
338    experimental=True,
339    hidden=True,
340    )
341register_metadir(
342    controldir.format_registry, 'development5-subtree',
343    'breezy.bzr.knitpack_repo.RepositoryFormatPackDevelopment2Subtree',
344    help='Development format, subtree variant. Can convert data to and '
345    'from pack-0.92-subtree (and anything compatible with '
346    'pack-0.92-subtree) format repositories. Repositories and branches in '
347    'this format can only be read by bzr.dev. Please read '
348    'https://www.breezy-vcs.org/developers/development-repo.html '
349    'before use.',
350    branch_format='breezy.bzr.branch.BzrBranchFormat7',
351    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat6',
352    experimental=True,
353    hidden=True,
354    )
355
356register_metadir(
357    controldir.format_registry, 'development-colo',
358    'breezy.bzr.groupcompress_repo.RepositoryFormat2a',
359    help='The 2a format with experimental support for colocated branches.\n',
360    branch_format='breezy.bzr.branch.BzrBranchFormat7',
361    tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat6',
362    experimental=True,
363    bzrdir_format='breezy.bzr.bzrdir.BzrDirMetaFormat1Colo',
364    hidden=True,
365    )
366
367
368# And the development formats above will have aliased one of the following:
369
370# Finally, the current format.
371register_metadir(controldir.format_registry, '2a',
372                 'breezy.bzr.groupcompress_repo.RepositoryFormat2a',
373                 help='Format for the bzr 2.0 series.\n',
374                 branch_format='breezy.bzr.branch.BzrBranchFormat7',
375                 tree_format='breezy.bzr.workingtree_4.WorkingTreeFormat6',
376                 experimental=False,
377                 )
378
379# The following format should be an alias for the rich root equivalent
380# of the default format
381
382controldir.format_registry.register_alias(
383    'default-rich-root', '2a', hidden=True)
384
385# The following format should is just an alias for the default bzr format.
386controldir.format_registry.register_alias('bzr', '2a')
387
388# The current format that is made on 'bzr init'.
389format_name = config.GlobalStack().get('default_format')
390controldir.format_registry.set_default(format_name)
391