/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-03-22 01:35:14 UTC
  • mfrom: (7490.7.6 work)
  • mto: This revision was merged to the branch mainline in revision 7499.
  • Revision ID: jelmer@jelmer.uk-20200322013514-7vw1ntwho04rcuj3
merge lp:brz/3.1.

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