1
# Copyright (C) 2009-2018 Jelmer Vernooij <jelmer@jelmer.uk>
2
# Copyright (C) 2006-2009 Canonical Ltd
4
# Authors: Robert Collins <robert.collins@canonical.com>
5
# Jelmer Vernooij <jelmer@jelmer.uk>
6
# John Carr <john.carr@unrouted.co.uk>
8
# This program is free software; you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 2 of the License, or
11
# (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with this program; if not, write to the Free Software
20
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
"""A GIT branch and repository format implementation for bzr."""
25
from __future__ import absolute_import
33
bzr_compatible_versions,
34
bzr_plugin_version as version_info,
35
dulwich_minimum_version,
38
if version_info[3] == 'final':
39
version_string = '%d.%d.%d' % version_info[:3]
41
version_string = '%d.%d.%d%s%d' % version_info
42
__version__ = version_string
44
if breezy.version_info[:3] not in bzr_compatible_versions:
45
from ...errors import IncompatibleVersion
46
raise IncompatibleVersion(breezy,
47
bzr_compatible_versions, breezy.version_info[:3])
50
from ...i18n import load_plugin_translations
51
except ImportError: # No translations for bzr < 2.5
54
translation = load_plugin_translations("bzr-git")
55
gettext = translation.gettext
58
__version__ as breezy_version,
63
from ...controldir import (
67
network_format_registry as controldir_network_format_registry,
70
from ...transport import (
71
register_lazy_transport,
72
register_transport_proto,
73
transport_server_registry,
75
from ...commands import (
80
if getattr(sys, "frozen", None):
81
# allow import additional libs from ./_lib for bzr.exe only
82
sys.path.append(os.path.normpath(
83
os.path.join(os.path.dirname(__file__), '_lib')))
88
from dulwich import __version__ as dulwich_version
90
raise bzr_errors.DependencyNotPresent("dulwich",
91
"bzr-git: Please install dulwich, https://launchpad.net/dulwich")
93
if dulwich_version < dulwich_minimum_version:
94
raise bzr_errors.DependencyNotPresent("dulwich",
95
"bzr-git: Dulwich is too old; at least %d.%d.%d is required" %
96
dulwich_minimum_version)
99
_versions_checked = False
100
def lazy_check_versions():
101
global _versions_checked
102
if _versions_checked:
105
_versions_checked = True
107
format_registry.register_lazy('git',
108
__name__ + ".dir", "LocalGitControlDirFormat",
109
help='GIT repository.', native=False, experimental=False,
112
format_registry.register_lazy('git-bare',
113
__name__ + ".dir", "BareLocalGitControlDirFormat",
114
help='Bare GIT repository (no working tree).', native=False,
118
from ...revisionspec import (RevisionSpec_dwim, revspec_registry)
119
revspec_registry.register_lazy("git:", __name__ + ".revspec",
121
RevisionSpec_dwim.append_possible_lazy_revspec(
122
__name__ + ".revspec", "RevisionSpec_git")
125
class LocalGitProber(Prober):
127
def probe_transport(self, transport):
129
external_url = transport.external_url()
130
except bzr_errors.InProcessTransport:
131
raise bzr_errors.NotBranchError(path=transport.base)
132
if (external_url.startswith("http:") or
133
external_url.startswith("https:")):
134
# Already handled by RemoteGitProber
135
raise bzr_errors.NotBranchError(path=transport.base)
136
from ... import urlutils
137
if urlutils.split(transport.base)[1] == ".git":
138
raise bzr_errors.NotBranchError(path=transport.base)
139
if not transport.has_any(['objects', '.git/objects', '.git']):
140
raise bzr_errors.NotBranchError(path=transport.base)
141
lazy_check_versions()
143
BareLocalGitControlDirFormat,
144
LocalGitControlDirFormat,
146
if transport.has_any(['.git/objects', '.git']):
147
return LocalGitControlDirFormat()
148
if transport.has('info') and transport.has('objects'):
149
return BareLocalGitControlDirFormat()
150
raise bzr_errors.NotBranchError(path=transport.base)
153
def known_formats(cls):
155
BareLocalGitControlDirFormat,
156
LocalGitControlDirFormat,
158
return set([BareLocalGitControlDirFormat(), LocalGitControlDirFormat()])
161
class RemoteGitProber(Prober):
163
def probe_http_transport(self, transport):
164
from ... import urlutils
165
base_url, _ = urlutils.split_segment_parameters(transport.external_url())
166
url = urlutils.join(base_url, "info/refs") + "?service=git-upload-pack"
167
from ...transport.http._urllib import HttpTransport_urllib, Request
168
headers = {"Content-Type": "application/x-git-upload-pack-request"}
169
req = Request('GET', url, accepted_errors=[200, 403, 404, 405],
171
if req.get_host() == "github.com":
172
# GitHub requires we lie. https://github.com/dulwich/dulwich/issues/562
173
req.add_header("User-Agent", "git/Breezy/%s" % breezy_version)
174
elif req.get_host() == "bazaar.launchpad.net":
175
# Don't attempt Git probes against bazaar.launchpad.net; pad.lv/1744830
176
raise bzr_errors.NotBranchError(transport.base)
177
req.follow_redirections = True
178
resp = transport._perform(req)
179
if resp.code in (404, 405):
180
raise bzr_errors.NotBranchError(transport.base)
181
headers = resp.headers
182
ct = headers.getheader("Content-Type")
184
raise bzr_errors.NotBranchError(transport.base)
185
if ct.startswith("application/x-git"):
186
from .remote import RemoteGitControlDirFormat
187
return RemoteGitControlDirFormat()
190
BareLocalGitControlDirFormat,
192
ret = BareLocalGitControlDirFormat()
193
ret._refs_text = resp.read()
196
def probe_transport(self, transport):
198
external_url = transport.external_url()
199
except bzr_errors.InProcessTransport:
200
raise bzr_errors.NotBranchError(path=transport.base)
202
if (external_url.startswith("http:") or
203
external_url.startswith("https:")):
204
return self.probe_http_transport(transport)
206
if (not external_url.startswith("git://") and
207
not external_url.startswith("git+")):
208
raise bzr_errors.NotBranchError(transport.base)
210
# little ugly, but works
211
from .remote import (
213
RemoteGitControlDirFormat,
215
if isinstance(transport, GitSmartTransport):
216
return RemoteGitControlDirFormat()
217
raise bzr_errors.NotBranchError(path=transport.base)
220
def known_formats(cls):
221
from .remote import RemoteGitControlDirFormat
222
return set([RemoteGitControlDirFormat()])
225
ControlDirFormat.register_prober(LocalGitProber)
226
ControlDirFormat._server_probers.append(RemoteGitProber)
228
register_transport_proto('git://',
229
help="Access using the Git smart server protocol.")
230
register_transport_proto('git+ssh://',
231
help="Access using the Git smart server protocol over SSH.")
233
register_lazy_transport("git://", __name__ + '.remote',
234
'TCPGitSmartTransport')
235
register_lazy_transport("git+ssh://", __name__ + '.remote',
236
'SSHGitSmartTransport')
239
plugin_cmds.register_lazy("cmd_git_import", [], __name__ + ".commands")
240
plugin_cmds.register_lazy("cmd_git_object", ["git-objects", "git-cat"],
241
__name__ + ".commands")
242
plugin_cmds.register_lazy("cmd_git_refs", [], __name__ + ".commands")
243
plugin_cmds.register_lazy("cmd_git_apply", [], __name__ + ".commands")
244
plugin_cmds.register_lazy("cmd_git_push_pristine_tar_deltas",
245
['git-push-pristine-tar', 'git-push-pristine'],
246
__name__ + ".commands")
248
def extract_git_foreign_revid(rev):
250
foreign_revid = rev.foreign_revid
251
except AttributeError:
252
from .mapping import mapping_registry
253
foreign_revid, mapping = \
254
mapping_registry.parse_revision_id(rev.revision_id)
257
from .mapping import foreign_vcs_git
258
if rev.mapping.vcs == foreign_vcs_git:
261
raise bzr_errors.InvalidRevisionId(rev.revision_id, None)
264
def update_stanza(rev, stanza):
265
mapping = getattr(rev, "mapping", None)
267
git_commit = extract_git_foreign_revid(rev)
268
except bzr_errors.InvalidRevisionId:
271
stanza.add("git-commit", git_commit)
273
from ...hooks import install_lazy_named_hook
274
install_lazy_named_hook("breezy.version_info_formats.format_rio",
275
"RioVersionInfoBuilder.hooks", "revision", update_stanza,
279
transport_server_registry.register_lazy('git',
280
__name__ + '.server',
282
'Git Smart server protocol over TCP. (default port: 9418)')
284
transport_server_registry.register_lazy('git-receive-pack',
285
__name__ + '.server',
286
'serve_git_receive_pack',
287
help='Git Smart server receive pack command. (inetd mode only)')
288
transport_server_registry.register_lazy('git-upload-pack',
289
__name__ + 'git.server',
290
'serve_git_upload_pack',
291
help='Git Smart server upload pack command. (inetd mode only)')
293
from ...repository import (
294
format_registry as repository_format_registry,
295
network_format_registry as repository_network_format_registry,
297
repository_network_format_registry.register_lazy('git',
298
__name__ + '.repository', 'GitRepositoryFormat')
300
register_extra_lazy_repository_format = getattr(repository_format_registry,
301
"register_extra_lazy")
302
register_extra_lazy_repository_format(__name__ + '.repository',
303
'GitRepositoryFormat')
305
from ...branch import (
306
network_format_registry as branch_network_format_registry,
308
branch_network_format_registry.register_lazy('git',
309
__name__ + '.branch', 'LocalGitBranchFormat')
312
from ...branch import (
313
format_registry as branch_format_registry,
315
branch_format_registry.register_extra_lazy(
316
__name__ + '.branch',
317
'LocalGitBranchFormat',
319
branch_format_registry.register_extra_lazy(
320
__name__ + '.remote',
321
'RemoteGitBranchFormat',
325
from ...workingtree import (
326
format_registry as workingtree_format_registry,
328
workingtree_format_registry.register_extra_lazy(
329
__name__ + '.workingtree',
330
'GitWorkingTreeFormat',
333
controldir_network_format_registry.register_lazy('git',
334
__name__ + ".dir", "GitControlDirFormat")
338
from ...registry import register_lazy
340
from ...diff import format_registry as diff_format_registry
341
diff_format_registry.register_lazy('git', __name__ + '.send',
342
'GitDiffTree', 'Git am-style diff format')
344
from ...send import (
345
format_registry as send_format_registry,
347
send_format_registry.register_lazy('git', __name__ + '.send',
348
'send_git', 'Git am-style diff format')
350
from ...directory_service import directories
351
directories.register_lazy('github:', __name__ + '.directory',
354
directories.register_lazy('git@github.com:', __name__ + '.directory',
358
from ...help_topics import (
361
topic_registry.register_lazy('git', __name__ + '.help', 'help_git',
362
'Using Bazaar with Git')
364
from ...foreign import (
365
foreign_vcs_registry,
367
foreign_vcs_registry.register_lazy("git",
368
__name__ + ".mapping", "foreign_vcs_git", "Stupid content tracker")
370
register_lazy("breezy.diff", "format_registry",
371
'git', __name__ + '.send', 'GitDiffTree',
372
'Git am-style diff format')
373
register_lazy("breezy.send", "format_registry",
374
'git', __name__ + '.send', 'send_git',
375
'Git am-style diff format')
376
register_lazy('breezy.directory_service', 'directories', 'github:',
377
__name__ + '.directory', 'GitHubDirectory',
379
register_lazy('breezy.directory_service', 'directories',
380
'git@github.com:', __name__ + '.directory',
381
'GitHubDirectory', 'GitHub directory.')
382
register_lazy('breezy.help_topics', 'topic_registry',
383
'git', __name__ + '.help', 'help_git',
384
'Using Bazaar with Git')
385
register_lazy('breezy.foreign', 'foreign_vcs_registry', "git",
386
__name__ + ".mapping", "foreign_vcs_git", "Stupid content tracker")
388
def update_git_cache(repository, revid):
389
"""Update the git cache after a local commit."""
390
if getattr(repository, "_git", None) is not None:
391
return # No need to update cache for git repositories
393
if not repository.control_transport.has("git"):
394
return # No existing cache, don't bother updating
396
lazy_check_versions()
397
except bzr_errors.DependencyNotPresent, e:
398
# dulwich is probably missing. silently ignore
399
trace.mutter("not updating git map for %r: %s",
402
from .object_store import BazaarObjectStore
403
store = BazaarObjectStore(repository)
404
with store.lock_write():
406
parent_revisions = set(repository.get_parent_map([revid])[revid])
408
# Isn't this a bit odd - how can a revision that was just committed be missing?
410
missing_revisions = store._missing_revisions(parent_revisions)
411
if not missing_revisions:
412
# Only update if the cache was up to date previously
413
store._update_sha_map_revision(revid)
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)
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'):
428
from .server import git_http_hook
429
return git_http_hook(branch, environ['REQUEST_METHOD'],
430
environ['PATH_INFO'])
432
install_lazy_named_hook("breezy.branch",
433
"Branch.hooks", "post_commit", post_commit_update_cache,
435
install_lazy_named_hook("breezy.plugins.loggerhead.apps.branch",
436
"BranchWSGIApp.hooks", "controller",
437
loggerhead_git_hook, "git support")
440
from ...config import (
446
option_registry.register(
448
default=None, from_unicode=bool_from_store, invalid='warning',
450
Allow fetching of Git packs over HTTP.
452
This enables support for fetching Git packs over HTTP in Loggerhead.
457
return tests.test_suite()