/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 breezy/git/__init__.py

  • Committer: Jelmer Vernooij
  • Date: 2020-02-19 23:18:42 UTC
  • mto: (7490.3.4 work)
  • mto: This revision was merged to the branch mainline in revision 7495.
  • Revision ID: jelmer@jelmer.uk-20200219231842-agwjh2db66cpajqg
Consistent return values.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2009-2018 Jelmer Vernooij <jelmer@jelmer.uk>
 
2
# Copyright (C) 2006-2009 Canonical Ltd
 
3
 
2
4
# Authors: Robert Collins <robert.collins@canonical.com>
 
5
#          Jelmer Vernooij <jelmer@jelmer.uk>
 
6
#          John Carr <john.carr@unrouted.co.uk>
3
7
#
4
8
# This program is free software; you can redistribute it and/or modify
5
9
# it under the terms of the GNU General Public License as published by
13
17
#
14
18
# You should have received a copy of the GNU General Public License
15
19
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
20
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
21
 
18
22
 
19
23
"""A GIT branch and repository format implementation for bzr."""
20
24
 
21
 
try:
22
 
    import dulwich as git
23
 
except ImportError:
24
 
    import os, sys
25
 
    sys.path.insert(0, os.path.join(os.path.dirname(__file__), "dulwich"))
26
 
    import dulwich as git
27
 
from bzrlib import bzrdir
28
 
from bzrlib.foreign import ForeignVcs, VcsMappingRegistry, foreign_vcs_registry
29
 
from bzrlib.plugins.git.dir import LocalGitBzrDirFormat, RemoteGitBzrDirFormat
30
 
from bzrlib.transport import register_lazy_transport
31
 
 
32
 
bzrdir.format_registry.register(
33
 
    'git', LocalGitBzrDirFormat,
34
 
    help='GIT repository.', 
35
 
    native=False, experimental=True,
36
 
    )
37
 
 
38
 
bzrdir.BzrDirFormat.register_control_format(LocalGitBzrDirFormat)
39
 
bzrdir.BzrDirFormat.register_control_format(RemoteGitBzrDirFormat)
40
 
 
41
 
register_lazy_transport("git://", 'bzrlib.plugins.git.remote',
42
 
                        'GitSmartTransport')
43
 
 
44
 
 
45
 
class ForeignGit(ForeignVcs):
46
 
    """Foreign Git."""
47
 
 
48
 
 
49
 
git_mapping_registry = VcsMappingRegistry()
50
 
git_mapping_registry.register_lazy('git-experimental', "bzrlib.plugins.git.mapping",
51
 
                                   "BzrGitMappingExperimental")
52
 
foreign_vcs_registry.register("git", ForeignGit(git_mapping_registry), 
53
 
                                      "Stupid content tracker")
 
25
from __future__ import absolute_import
 
26
 
 
27
import os
 
28
import sys
 
29
 
 
30
dulwich_minimum_version = (0, 19, 11)
 
31
 
 
32
from .. import (  # noqa: F401
 
33
    __version__ as breezy_version,
 
34
    errors as brz_errors,
 
35
    trace,
 
36
    urlutils,
 
37
    version_info,
 
38
    )
 
39
 
 
40
from ..controldir import (
 
41
    ControlDirFormat,
 
42
    Prober,
 
43
    format_registry,
 
44
    network_format_registry as controldir_network_format_registry,
 
45
    )
 
46
 
 
47
from ..transport import (
 
48
    register_lazy_transport,
 
49
    register_transport_proto,
 
50
    transport_server_registry,
 
51
    )
 
52
from ..commands import (
 
53
    plugin_cmds,
 
54
    )
 
55
 
 
56
 
 
57
if getattr(sys, "frozen", None):
 
58
    # allow import additional libs from ./_lib for bzr.exe only
 
59
    sys.path.append(os.path.normpath(
 
60
        os.path.join(os.path.dirname(__file__), '_lib')))
 
61
 
 
62
 
 
63
def import_dulwich():
 
64
    try:
 
65
        from dulwich import __version__ as dulwich_version
 
66
    except ImportError:
 
67
        raise brz_errors.DependencyNotPresent(
 
68
            "dulwich",
 
69
            "bzr-git: Please install dulwich, https://www.dulwich.io/")
 
70
    else:
 
71
        if dulwich_version < dulwich_minimum_version:
 
72
            raise brz_errors.DependencyNotPresent(
 
73
                "dulwich",
 
74
                "bzr-git: Dulwich is too old; at least %d.%d.%d is required" %
 
75
                dulwich_minimum_version)
 
76
 
 
77
 
 
78
_versions_checked = False
 
79
 
 
80
 
 
81
def lazy_check_versions():
 
82
    global _versions_checked
 
83
    if _versions_checked:
 
84
        return
 
85
    import_dulwich()
 
86
    _versions_checked = True
 
87
 
 
88
 
 
89
format_registry.register_lazy(
 
90
    'git', __name__ + ".dir", "LocalGitControlDirFormat",
 
91
    help='GIT repository.', native=False, experimental=False)
 
92
 
 
93
format_registry.register_lazy(
 
94
    'git-bare', __name__ + ".dir", "BareLocalGitControlDirFormat",
 
95
    help='Bare GIT repository (no working tree).', native=False,
 
96
    experimental=False)
 
97
 
 
98
from ..revisionspec import (RevisionSpec_dwim, revspec_registry)
 
99
revspec_registry.register_lazy("git:", __name__ + ".revspec",
 
100
                               "RevisionSpec_git")
 
101
RevisionSpec_dwim.append_possible_lazy_revspec(
 
102
    __name__ + ".revspec", "RevisionSpec_git")
 
103
 
 
104
 
 
105
class LocalGitProber(Prober):
 
106
 
 
107
    @classmethod
 
108
    def priority(klass, transport):
 
109
        return 10
 
110
 
 
111
    def probe_transport(self, transport):
 
112
        try:
 
113
            external_url = transport.external_url()
 
114
        except brz_errors.InProcessTransport:
 
115
            raise brz_errors.NotBranchError(path=transport.base)
 
116
        if (external_url.startswith("http:") or
 
117
                external_url.startswith("https:")):
 
118
            # Already handled by RemoteGitProber
 
119
            raise brz_errors.NotBranchError(path=transport.base)
 
120
        if urlutils.split(transport.base)[1] == ".git":
 
121
            raise brz_errors.NotBranchError(path=transport.base)
 
122
        if not transport.has_any(['objects', '.git/objects', '.git']):
 
123
            raise brz_errors.NotBranchError(path=transport.base)
 
124
        lazy_check_versions()
 
125
        from .dir import (
 
126
            BareLocalGitControlDirFormat,
 
127
            LocalGitControlDirFormat,
 
128
            )
 
129
        if transport.has_any(['.git/objects', '.git']):
 
130
            return LocalGitControlDirFormat()
 
131
        if transport.has('info') and transport.has('objects'):
 
132
            return BareLocalGitControlDirFormat()
 
133
        raise brz_errors.NotBranchError(path=transport.base)
 
134
 
 
135
    @classmethod
 
136
    def known_formats(cls):
 
137
        from .dir import (
 
138
            BareLocalGitControlDirFormat,
 
139
            LocalGitControlDirFormat,
 
140
            )
 
141
        return [BareLocalGitControlDirFormat(), LocalGitControlDirFormat()]
 
142
 
 
143
 
 
144
def user_agent_for_github():
 
145
    # GitHub requires we lie. https://github.com/dulwich/dulwich/issues/562
 
146
    return "git/Breezy/%s" % breezy_version
 
147
 
 
148
 
 
149
def is_github_url(url):
 
150
    (scheme, user, password, host, port,
 
151
     path) = urlutils.parse_url(url)
 
152
    return host == "github.com"
 
153
 
 
154
 
 
155
class RemoteGitProber(Prober):
 
156
 
 
157
    @classmethod
 
158
    def priority(klass, transport):
 
159
        # This is a surprisingly good heuristic to determine whether this
 
160
        # prober is more likely to succeed than the Bazaar one.
 
161
        if 'git' in transport.base:
 
162
            return -15
 
163
        return -10
 
164
 
 
165
    def probe_http_transport(self, transport):
 
166
        # This function intentionally doesn't use any of the support code under
 
167
        # breezy.git, since it's called for every repository that's
 
168
        # accessed over HTTP, whether it's Git, Bzr or something else.
 
169
        # Importing Dulwich and the other support code adds unnecessray slowdowns.
 
170
        base_url = urlutils.strip_segment_parameters(transport.external_url())
 
171
        url = urlutils.URL.from_string(base_url)
 
172
        url.user = url.quoted_user = None
 
173
        url.password = url.quoted_password = None
 
174
        host = url.host
 
175
        url = urlutils.join(str(url), "info/refs") + "?service=git-upload-pack"
 
176
        headers = {"Content-Type": "application/x-git-upload-pack-request",
 
177
                   "Accept": "application/x-git-upload-pack-result",
 
178
                   }
 
179
        if is_github_url(url):
 
180
            # GitHub requires we lie.
 
181
            # https://github.com/dulwich/dulwich/issues/562
 
182
            headers["User-Agent"] = user_agent_for_github()
 
183
        elif host == "bazaar.launchpad.net":
 
184
            # Don't attempt Git probes against bazaar.launchpad.net; pad.lv/1744830
 
185
            raise brz_errors.NotBranchError(transport.base)
 
186
        resp = transport.request('GET', url, headers=headers)
 
187
        if resp.status in (404, 405):
 
188
            raise brz_errors.NotBranchError(transport.base)
 
189
        elif resp.status != 200:
 
190
            raise brz_errors.InvalidHttpResponse(
 
191
                url, 'Unable to handle http code %d' % resp.status)
 
192
 
 
193
        ct = resp.getheader("Content-Type")
 
194
        if ct and ct.startswith("application/x-git"):
 
195
            from .remote import RemoteGitControlDirFormat
 
196
            return RemoteGitControlDirFormat()
 
197
        elif not ct:
 
198
            from .dir import (
 
199
                BareLocalGitControlDirFormat,
 
200
                )
 
201
            ret = BareLocalGitControlDirFormat()
 
202
            ret._refs_text = resp.read()
 
203
            return ret
 
204
        raise brz_errors.NotBranchError(transport.base)
 
205
 
 
206
    def probe_transport(self, transport):
 
207
        try:
 
208
            external_url = transport.external_url()
 
209
        except brz_errors.InProcessTransport:
 
210
            raise brz_errors.NotBranchError(path=transport.base)
 
211
 
 
212
        if (external_url.startswith("http:") or
 
213
                external_url.startswith("https:")):
 
214
            return self.probe_http_transport(transport)
 
215
 
 
216
        if (not external_url.startswith("git://") and
 
217
                not external_url.startswith("git+")):
 
218
            raise brz_errors.NotBranchError(transport.base)
 
219
 
 
220
        # little ugly, but works
 
221
        from .remote import (
 
222
            GitSmartTransport,
 
223
            RemoteGitControlDirFormat,
 
224
            )
 
225
        if isinstance(transport, GitSmartTransport):
 
226
            return RemoteGitControlDirFormat()
 
227
        raise brz_errors.NotBranchError(path=transport.base)
 
228
 
 
229
    @classmethod
 
230
    def known_formats(cls):
 
231
        from .remote import RemoteGitControlDirFormat
 
232
        return [RemoteGitControlDirFormat()]
 
233
 
 
234
 
 
235
ControlDirFormat.register_prober(LocalGitProber)
 
236
ControlDirFormat.register_prober(RemoteGitProber)
 
237
 
 
238
register_transport_proto(
 
239
    'git://', help="Access using the Git smart server protocol.")
 
240
register_transport_proto(
 
241
    'git+ssh://',
 
242
    help="Access using the Git smart server protocol over SSH.")
 
243
 
 
244
register_lazy_transport("git://", __name__ + '.remote',
 
245
                        'TCPGitSmartTransport')
 
246
register_lazy_transport("git+ssh://", __name__ + '.remote',
 
247
                        'SSHGitSmartTransport')
 
248
 
 
249
 
 
250
plugin_cmds.register_lazy("cmd_git_import", [], __name__ + ".commands")
 
251
plugin_cmds.register_lazy("cmd_git_object", ["git-objects", "git-cat"],
 
252
                          __name__ + ".commands")
 
253
plugin_cmds.register_lazy("cmd_git_refs", [], __name__ + ".commands")
 
254
plugin_cmds.register_lazy("cmd_git_apply", [], __name__ + ".commands")
 
255
plugin_cmds.register_lazy("cmd_git_push_pristine_tar_deltas",
 
256
                          ['git-push-pristine-tar', 'git-push-pristine'],
 
257
                          __name__ + ".commands")
 
258
 
 
259
 
 
260
def extract_git_foreign_revid(rev):
 
261
    try:
 
262
        foreign_revid = rev.foreign_revid
 
263
    except AttributeError:
 
264
        from .mapping import mapping_registry
 
265
        foreign_revid, mapping = \
 
266
            mapping_registry.parse_revision_id(rev.revision_id)
 
267
        return foreign_revid
 
268
    else:
 
269
        from .mapping import foreign_vcs_git
 
270
        if rev.mapping.vcs == foreign_vcs_git:
 
271
            return foreign_revid
 
272
        else:
 
273
            raise brz_errors.InvalidRevisionId(rev.revision_id, None)
 
274
 
 
275
 
 
276
def update_stanza(rev, stanza):
 
277
    try:
 
278
        git_commit = extract_git_foreign_revid(rev)
 
279
    except brz_errors.InvalidRevisionId:
 
280
        pass
 
281
    else:
 
282
        stanza.add("git-commit", git_commit)
 
283
 
 
284
 
 
285
from ..hooks import install_lazy_named_hook
 
286
install_lazy_named_hook(
 
287
    "breezy.version_info_formats.format_rio",
 
288
    "RioVersionInfoBuilder.hooks", "revision", update_stanza,
 
289
    "git commits")
 
290
 
 
291
transport_server_registry.register_lazy(
 
292
    'git', __name__ + '.server', 'serve_git',
 
293
    'Git Smart server protocol over TCP. (default port: 9418)')
 
294
 
 
295
transport_server_registry.register_lazy(
 
296
    'git-receive-pack', __name__ + '.server',
 
297
    'serve_git_receive_pack',
 
298
    help='Git Smart server receive pack command. (inetd mode only)')
 
299
transport_server_registry.register_lazy(
 
300
    'git-upload-pack', __name__ + 'git.server',
 
301
    'serve_git_upload_pack',
 
302
    help='Git Smart server upload pack command. (inetd mode only)')
 
303
 
 
304
from ..repository import (
 
305
    format_registry as repository_format_registry,
 
306
    network_format_registry as repository_network_format_registry,
 
307
    )
 
308
repository_network_format_registry.register_lazy(
 
309
    b'git', __name__ + '.repository', 'GitRepositoryFormat')
 
310
 
 
311
register_extra_lazy_repository_format = getattr(repository_format_registry,
 
312
                                                "register_extra_lazy")
 
313
register_extra_lazy_repository_format(__name__ + '.repository',
 
314
                                      'GitRepositoryFormat')
 
315
 
 
316
from ..branch import (
 
317
    network_format_registry as branch_network_format_registry,
 
318
    )
 
319
branch_network_format_registry.register_lazy(
 
320
    b'git', __name__ + '.branch', 'LocalGitBranchFormat')
 
321
 
 
322
 
 
323
from ..branch import (
 
324
    format_registry as branch_format_registry,
 
325
    )
 
326
branch_format_registry.register_extra_lazy(
 
327
    __name__ + '.branch',
 
328
    'LocalGitBranchFormat',
 
329
    )
 
330
branch_format_registry.register_extra_lazy(
 
331
    __name__ + '.remote',
 
332
    'RemoteGitBranchFormat',
 
333
    )
 
334
 
 
335
 
 
336
from ..workingtree import (
 
337
    format_registry as workingtree_format_registry,
 
338
    )
 
339
workingtree_format_registry.register_extra_lazy(
 
340
    __name__ + '.workingtree',
 
341
    'GitWorkingTreeFormat',
 
342
    )
 
343
 
 
344
controldir_network_format_registry.register_lazy(
 
345
    b'git', __name__ + ".dir", "GitControlDirFormat")
 
346
 
 
347
 
 
348
from ..diff import format_registry as diff_format_registry
 
349
diff_format_registry.register_lazy(
 
350
    'git', __name__ + '.send',
 
351
    'GitDiffTree', 'Git am-style diff format')
 
352
 
 
353
from ..send import (
 
354
    format_registry as send_format_registry,
 
355
    )
 
356
send_format_registry.register_lazy('git', __name__ + '.send',
 
357
                                   'send_git', 'Git am-style diff format')
 
358
 
 
359
from ..directory_service import directories
 
360
directories.register_lazy('github:', __name__ + '.directory',
 
361
                          'GitHubDirectory',
 
362
                          'GitHub directory.')
 
363
directories.register_lazy('git@github.com:', __name__ + '.directory',
 
364
                          'GitHubDirectory',
 
365
                          'GitHub directory.')
 
366
 
 
367
from ..help_topics import (
 
368
    topic_registry,
 
369
    )
 
370
topic_registry.register_lazy(
 
371
    'git', __name__ + '.help', 'help_git', 'Using Bazaar with Git')
 
372
 
 
373
from ..foreign import (
 
374
    foreign_vcs_registry,
 
375
    )
 
376
foreign_vcs_registry.register_lazy(
 
377
    "git", __name__ + ".mapping", "foreign_vcs_git", "Stupid content tracker")
 
378
 
 
379
 
 
380
def update_git_cache(repository, revid):
 
381
    """Update the git cache after a local commit."""
 
382
    if getattr(repository, "_git", None) is not None:
 
383
        return  # No need to update cache for git repositories
 
384
 
 
385
    if not repository.control_transport.has("git"):
 
386
        return  # No existing cache, don't bother updating
 
387
    try:
 
388
        lazy_check_versions()
 
389
    except brz_errors.DependencyNotPresent as e:
 
390
        # dulwich is probably missing. silently ignore
 
391
        trace.mutter("not updating git map for %r: %s",
 
392
                     repository, e)
 
393
 
 
394
    from .object_store import BazaarObjectStore
 
395
    store = BazaarObjectStore(repository)
 
396
    with store.lock_write():
 
397
        try:
 
398
            parent_revisions = set(repository.get_parent_map([revid])[revid])
 
399
        except KeyError:
 
400
            # Isn't this a bit odd - how can a revision that was just committed
 
401
            # be missing?
 
402
            return
 
403
        missing_revisions = store._missing_revisions(parent_revisions)
 
404
        if not missing_revisions:
 
405
            store._cache.idmap.start_write_group()
 
406
            try:
 
407
                # Only update if the cache was up to date previously
 
408
                store._update_sha_map_revision(revid)
 
409
            except BaseException:
 
410
                store._cache.idmap.abort_write_group()
 
411
                raise
 
412
            else:
 
413
                store._cache.idmap.commit_write_group()
 
414
 
 
415
 
 
416
def post_commit_update_cache(local_branch, master_branch, old_revno, old_revid,
 
417
                             new_revno, new_revid):
 
418
    if local_branch is not None:
 
419
        update_git_cache(local_branch.repository, new_revid)
 
420
    update_git_cache(master_branch.repository, new_revid)
 
421
 
 
422
 
 
423
def loggerhead_git_hook(branch_app, environ):
 
424
    branch = branch_app.branch
 
425
    config_stack = branch.get_config_stack()
 
426
    if config_stack.get('http_git'):
 
427
        return None
 
428
    from .server import git_http_hook
 
429
    return git_http_hook(branch, environ['REQUEST_METHOD'],
 
430
                         environ['PATH_INFO'])
 
431
 
 
432
 
 
433
install_lazy_named_hook("breezy.branch",
 
434
                        "Branch.hooks", "post_commit",
 
435
                        post_commit_update_cache, "git cache")
 
436
install_lazy_named_hook("breezy.plugins.loggerhead.apps.branch",
 
437
                        "BranchWSGIApp.hooks", "controller",
 
438
                        loggerhead_git_hook, "git support")
 
439
 
 
440
 
 
441
from ..config import (
 
442
    option_registry,
 
443
    Option,
 
444
    bool_from_store,
 
445
    )
 
446
 
 
447
option_registry.register(
 
448
    Option('git.http',
 
449
           default=None, from_unicode=bool_from_store, invalid='warning',
 
450
           help='''\
 
451
Allow fetching of Git packs over HTTP.
 
452
 
 
453
This enables support for fetching Git packs over HTTP in Loggerhead.
 
454
'''))
54
455
 
55
456
 
56
457
def test_suite():
57
 
    from bzrlib.plugins.git import tests
 
458
    from . import tests
58
459
    return tests.test_suite()