/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-06-03 23:48:08 UTC
  • mfrom: (7316 work)
  • mto: This revision was merged to the branch mainline in revision 7328.
  • Revision ID: jelmer@jelmer.uk-20190603234808-15yk5c7054tj8e2b
Merge trunk.

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
        headers = {"Content-Type": "application/x-git-upload-pack-request",
 
160
                   "Accept": "application/x-git-upload-pack-result",
 
161
                   }
 
162
        (scheme, user, password, host, port,
 
163
         path) = urlutils.parse_url(url)
 
164
        if host == "github.com":
 
165
            # GitHub requires we lie.
 
166
            # https://github.com/dulwich/dulwich/issues/562
 
167
            req.add_header("User-Agent", user_agent_for_github())
 
168
        elif host == "bazaar.launchpad.net":
 
169
            # Don't attempt Git probes against bazaar.launchpad.net; pad.lv/1744830
 
170
            raise brz_errors.NotBranchError(transport.base)
 
171
        resp = transport.request('GET', url, headers=headers)
 
172
        if resp.status in (404, 405):
 
173
            raise brz_errors.NotBranchError(transport.base)
 
174
        else:
 
175
            raise errors.InvalidHttpResponse(
 
176
                url, 'Unable to handle http code %d' % resp.status)
 
177
 
 
178
        headers = resp.headers
 
179
        ct = headers.get("Content-Type")
 
180
        if ct is None:
 
181
            raise brz_errors.NotBranchError(transport.base)
 
182
        if ct.startswith("application/x-git"):
 
183
            from .remote import RemoteGitControlDirFormat
 
184
            return RemoteGitControlDirFormat()
 
185
        else:
 
186
            from .dir import (
 
187
                BareLocalGitControlDirFormat,
 
188
                )
 
189
            ret = BareLocalGitControlDirFormat()
 
190
            ret._refs_text = resp.read()
 
191
            return ret
 
192
 
 
193
    def probe_transport(self, transport):
 
194
        try:
 
195
            external_url = transport.external_url()
 
196
        except brz_errors.InProcessTransport:
 
197
            raise brz_errors.NotBranchError(path=transport.base)
 
198
 
 
199
        if (external_url.startswith("http:") or
 
200
                external_url.startswith("https:")):
 
201
            return self.probe_http_transport(transport)
 
202
 
 
203
        if (not external_url.startswith("git://") and
 
204
                not external_url.startswith("git+")):
 
205
            raise brz_errors.NotBranchError(transport.base)
 
206
 
 
207
        # little ugly, but works
 
208
        from .remote import (
 
209
            GitSmartTransport,
 
210
            RemoteGitControlDirFormat,
 
211
            )
 
212
        if isinstance(transport, GitSmartTransport):
 
213
            return RemoteGitControlDirFormat()
 
214
        raise brz_errors.NotBranchError(path=transport.base)
 
215
 
 
216
    @classmethod
 
217
    def known_formats(cls):
 
218
        from .remote import RemoteGitControlDirFormat
 
219
        return [RemoteGitControlDirFormat()]
 
220
 
 
221
 
 
222
ControlDirFormat.register_prober(LocalGitProber)
 
223
ControlDirFormat._server_probers.append(RemoteGitProber)
 
224
 
 
225
register_transport_proto(
 
226
    'git://', help="Access using the Git smart server protocol.")
 
227
register_transport_proto(
 
228
    'git+ssh://',
 
229
    help="Access using the Git smart server protocol over SSH.")
 
230
 
 
231
register_lazy_transport("git://", __name__ + '.remote',
 
232
                        'TCPGitSmartTransport')
 
233
register_lazy_transport("git+ssh://", __name__ + '.remote',
 
234
                        'SSHGitSmartTransport')
 
235
 
 
236
 
 
237
plugin_cmds.register_lazy("cmd_git_import", [], __name__ + ".commands")
 
238
plugin_cmds.register_lazy("cmd_git_object", ["git-objects", "git-cat"],
 
239
                          __name__ + ".commands")
 
240
plugin_cmds.register_lazy("cmd_git_refs", [], __name__ + ".commands")
 
241
plugin_cmds.register_lazy("cmd_git_apply", [], __name__ + ".commands")
 
242
plugin_cmds.register_lazy("cmd_git_push_pristine_tar_deltas",
 
243
                          ['git-push-pristine-tar', 'git-push-pristine'],
 
244
                          __name__ + ".commands")
 
245
 
 
246
 
 
247
def extract_git_foreign_revid(rev):
 
248
    try:
 
249
        foreign_revid = rev.foreign_revid
 
250
    except AttributeError:
 
251
        from .mapping import mapping_registry
 
252
        foreign_revid, mapping = \
 
253
            mapping_registry.parse_revision_id(rev.revision_id)
 
254
        return foreign_revid
 
255
    else:
 
256
        from .mapping import foreign_vcs_git
 
257
        if rev.mapping.vcs == foreign_vcs_git:
 
258
            return foreign_revid
 
259
        else:
 
260
            raise brz_errors.InvalidRevisionId(rev.revision_id, None)
 
261
 
 
262
 
 
263
def update_stanza(rev, stanza):
 
264
    try:
 
265
        git_commit = extract_git_foreign_revid(rev)
 
266
    except brz_errors.InvalidRevisionId:
 
267
        pass
 
268
    else:
 
269
        stanza.add("git-commit", git_commit)
 
270
 
 
271
 
 
272
from ..hooks import install_lazy_named_hook
 
273
install_lazy_named_hook(
 
274
    "breezy.version_info_formats.format_rio",
 
275
    "RioVersionInfoBuilder.hooks", "revision", update_stanza,
 
276
    "git commits")
 
277
 
 
278
transport_server_registry.register_lazy(
 
279
    'git', __name__ + '.server', 'serve_git',
 
280
    'Git Smart server protocol over TCP. (default port: 9418)')
 
281
 
 
282
transport_server_registry.register_lazy(
 
283
    'git-receive-pack', __name__ + '.server',
 
284
    'serve_git_receive_pack',
 
285
    help='Git Smart server receive pack command. (inetd mode only)')
 
286
transport_server_registry.register_lazy(
 
287
    'git-upload-pack', __name__ + 'git.server',
 
288
    'serve_git_upload_pack',
 
289
    help='Git Smart server upload pack command. (inetd mode only)')
 
290
 
 
291
from ..repository import (
 
292
    format_registry as repository_format_registry,
 
293
    network_format_registry as repository_network_format_registry,
 
294
    )
 
295
repository_network_format_registry.register_lazy(
 
296
    b'git', __name__ + '.repository', 'GitRepositoryFormat')
 
297
 
 
298
register_extra_lazy_repository_format = getattr(repository_format_registry,
 
299
                                                "register_extra_lazy")
 
300
register_extra_lazy_repository_format(__name__ + '.repository',
 
301
                                      'GitRepositoryFormat')
 
302
 
 
303
from ..branch import (
 
304
    network_format_registry as branch_network_format_registry,
 
305
    )
 
306
branch_network_format_registry.register_lazy(
 
307
    b'git', __name__ + '.branch', 'LocalGitBranchFormat')
 
308
 
 
309
 
 
310
from ..branch import (
 
311
    format_registry as branch_format_registry,
 
312
    )
 
313
branch_format_registry.register_extra_lazy(
 
314
    __name__ + '.branch',
 
315
    'LocalGitBranchFormat',
 
316
    )
 
317
branch_format_registry.register_extra_lazy(
 
318
    __name__ + '.remote',
 
319
    'RemoteGitBranchFormat',
 
320
    )
 
321
 
 
322
 
 
323
from ..workingtree import (
 
324
    format_registry as workingtree_format_registry,
 
325
    )
 
326
workingtree_format_registry.register_extra_lazy(
 
327
    __name__ + '.workingtree',
 
328
    'GitWorkingTreeFormat',
 
329
    )
 
330
 
 
331
controldir_network_format_registry.register_lazy(
 
332
    b'git', __name__ + ".dir", "GitControlDirFormat")
 
333
 
 
334
 
 
335
from ..diff import format_registry as diff_format_registry
 
336
diff_format_registry.register_lazy(
 
337
    'git', __name__ + '.send',
 
338
    'GitDiffTree', 'Git am-style diff format')
 
339
 
 
340
from ..send import (
 
341
    format_registry as send_format_registry,
 
342
    )
 
343
send_format_registry.register_lazy('git', __name__ + '.send',
 
344
                                   'send_git', 'Git am-style diff format')
 
345
 
 
346
from ..directory_service import directories
 
347
directories.register_lazy('github:', __name__ + '.directory',
 
348
                          'GitHubDirectory',
 
349
                          'GitHub directory.')
 
350
directories.register_lazy('git@github.com:', __name__ + '.directory',
 
351
                          'GitHubDirectory',
 
352
                          'GitHub directory.')
 
353
 
 
354
from ..help_topics import (
 
355
    topic_registry,
 
356
    )
 
357
topic_registry.register_lazy(
 
358
    'git', __name__ + '.help', 'help_git', 'Using Bazaar with Git')
 
359
 
 
360
from ..foreign import (
 
361
    foreign_vcs_registry,
 
362
    )
 
363
foreign_vcs_registry.register_lazy(
 
364
    "git", __name__ + ".mapping", "foreign_vcs_git", "Stupid content tracker")
 
365
 
 
366
 
 
367
def update_git_cache(repository, revid):
 
368
    """Update the git cache after a local commit."""
 
369
    if getattr(repository, "_git", None) is not None:
 
370
        return  # No need to update cache for git repositories
 
371
 
 
372
    if not repository.control_transport.has("git"):
 
373
        return  # No existing cache, don't bother updating
 
374
    try:
 
375
        lazy_check_versions()
 
376
    except brz_errors.DependencyNotPresent as e:
 
377
        # dulwich is probably missing. silently ignore
 
378
        trace.mutter("not updating git map for %r: %s",
 
379
                     repository, e)
 
380
 
 
381
    from .object_store import BazaarObjectStore
 
382
    store = BazaarObjectStore(repository)
 
383
    with store.lock_write():
 
384
        try:
 
385
            parent_revisions = set(repository.get_parent_map([revid])[revid])
 
386
        except KeyError:
 
387
            # Isn't this a bit odd - how can a revision that was just committed
 
388
            # be missing?
 
389
            return
 
390
        missing_revisions = store._missing_revisions(parent_revisions)
 
391
        if not missing_revisions:
 
392
            store._cache.idmap.start_write_group()
 
393
            try:
 
394
                # Only update if the cache was up to date previously
 
395
                store._update_sha_map_revision(revid)
 
396
            except BaseException:
 
397
                store._cache.idmap.abort_write_group()
 
398
                raise
 
399
            else:
 
400
                store._cache.idmap.commit_write_group()
 
401
 
 
402
 
 
403
def post_commit_update_cache(local_branch, master_branch, old_revno, old_revid,
 
404
                             new_revno, new_revid):
 
405
    if local_branch is not None:
 
406
        update_git_cache(local_branch.repository, new_revid)
 
407
    update_git_cache(master_branch.repository, new_revid)
 
408
 
 
409
 
 
410
def loggerhead_git_hook(branch_app, environ):
 
411
    branch = branch_app.branch
 
412
    config_stack = branch.get_config_stack()
 
413
    if config_stack.get('http_git'):
 
414
        return None
 
415
    from .server import git_http_hook
 
416
    return git_http_hook(branch, environ['REQUEST_METHOD'],
 
417
                         environ['PATH_INFO'])
 
418
 
 
419
 
 
420
install_lazy_named_hook("breezy.branch",
 
421
                        "Branch.hooks", "post_commit",
 
422
                        post_commit_update_cache, "git cache")
 
423
install_lazy_named_hook("breezy.plugins.loggerhead.apps.branch",
 
424
                        "BranchWSGIApp.hooks", "controller",
 
425
                        loggerhead_git_hook, "git support")
 
426
 
 
427
 
 
428
from ..config import (
 
429
    option_registry,
 
430
    Option,
 
431
    bool_from_store,
 
432
    )
 
433
 
 
434
option_registry.register(
 
435
    Option('git.http',
 
436
           default=None, from_unicode=bool_from_store, invalid='warning',
 
437
           help='''\
 
438
Allow fetching of Git packs over HTTP.
 
439
 
 
440
This enables support for fetching Git packs over HTTP in Loggerhead.
 
441
'''))
54
442
 
55
443
 
56
444
def test_suite():
57
 
    from bzrlib.plugins.git import tests
 
445
    from . import tests
58
446
    return tests.test_suite()