/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: Gustav Hartvigsson
  • Date: 2021-01-09 21:36:27 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20210109213627-h1xwcutzy9m7a99b
Added 'Case Preserving Working Tree Use Cases' from Canonical Wiki

* Addod a page from the Canonical Bazaar wiki
  with information on the scmeatics of case
  perserving filesystems an a case insensitive
  filesystem works.
  
  * Needs re-work, but this will do as it is the
    same inforamoton as what was on the linked
    page in the currint documentation.

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