/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: 2019-08-12 20:24:50 UTC
  • mto: (7290.1.35 work)
  • mto: This revision was merged to the branch mainline in revision 7405.
  • Revision ID: jelmer@jelmer.uk-20190812202450-vdpamxay6sebo93w
Fix path to brz.

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