97
107
return (host, port, username, path)
110
class RemoteGitError(BzrError):
112
_fmt = "Remote server error: %(message)s"
115
def parse_git_error(url, message):
116
"""Parse a remote git server error and return a bzr exception.
118
:param url: URL of the remote repository
119
:param message: Message sent by the remote git server
121
message = str(message).strip()
122
if message.startswith("Could not find Repository "):
123
return NotBranchError(url, message)
124
if message == "HEAD failed to update":
125
base_url, _ = urlutils.split_segment_parameters(url)
127
("Unable to update remote HEAD branch. To update the master "
128
"branch, specify the URL %s,branch=master.") % base_url)
129
# Don't know, just return it to the user as-is
130
return RemoteGitError(message)
100
133
class GitSmartTransport(Transport):
102
135
def __init__(self, url, _client=None):
118
156
raise NotImplementedError(self._get_client)
120
158
def _get_path(self):
123
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
126
trace.info("git: %s" % text)
127
client = self._get_client(thin_packs=False)
129
return client.fetch_pack(self._get_path(), determine_wants,
130
graph_walker, pack_data, progress)
131
except GitProtocolError, e:
134
def send_pack(self, get_changed_refs, generate_pack_contents):
135
client = self._get_client(thin_packs=False)
137
return client.send_pack(self._get_path(), get_changed_refs,
138
generate_pack_contents)
139
except GitProtocolError, e:
159
return self._stripped_path
142
161
def get(self, path):
143
162
raise NoSuchFile(path)
198
218
class RemoteGitDir(GitDir):
200
def __init__(self, transport, lockfiles, format):
220
def __init__(self, transport, format, get_client, client_path):
201
221
self._format = format
202
222
self.root_transport = transport
203
223
self.transport = transport
204
self._lockfiles = lockfiles
205
224
self._mode_check_done = None
207
def _branch_name_to_ref(self, name, default=None):
208
return branch_name_to_ref(name, default=default)
225
self._get_client = get_client
226
self._client_path = client_path
227
self.base = self.root_transport.base
230
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
233
trace.info("git: %s" % text)
234
def wrap_determine_wants(refs_dict):
235
return determine_wants(remote_refs_dict_to_container(refs_dict))
236
client = self._get_client(thin_packs=False)
238
refs_dict = client.fetch_pack(self._client_path, wrap_determine_wants,
239
graph_walker, pack_data, progress)
240
self._refs = remote_refs_dict_to_container(refs_dict)
242
except GitProtocolError, e:
243
raise parse_git_error(self.transport.external_url(), e)
245
def send_pack(self, get_changed_refs, generate_pack_contents):
246
client = self._get_client(thin_packs=False)
248
return client.send_pack(self._client_path, get_changed_refs,
249
generate_pack_contents)
250
except GitProtocolError, e:
251
raise parse_git_error(self.transport.external_url(), e)
253
def _get_default_ref(self):
254
return "refs/heads/master"
256
def destroy_branch(self, name=None):
257
refname = self._get_selected_ref(name)
258
def get_changed_refs(old_refs):
260
if not refname in ret:
261
raise NotBranchError(self.user_url)
262
ret[refname] = "00" * 20
264
self.send_pack(get_changed_refs, lambda have, want: [])
268
return self.control_url
271
def user_transport(self):
272
return self.root_transport
275
def control_url(self):
276
return self.control_transport.base
279
def control_transport(self):
280
return self.root_transport
210
282
def open_repository(self):
211
return RemoteGitRepository(self, self._lockfiles)
283
return RemoteGitRepository(self)
213
def _open_branch(self, name=None, ignore_fallbacks=False,
285
def open_branch(self, name=None, unsupported=False,
286
ignore_fallbacks=False, ref=None, possible_transports=None):
215
287
repo = self.open_repository()
216
refname = self._branch_name_to_ref(name)
217
return RemoteGitBranch(self, repo, refname, self._lockfiles)
288
refname = self._get_selected_ref(name, ref)
289
return RemoteGitBranch(self, repo, refname)
219
291
def open_workingtree(self, recommend_upgrade=False):
220
292
raise NotLocalUrl(self.transport.base)
294
def get_peeled(self, name):
295
return self.get_refs_container().get_peeled(name)
297
def get_refs_container(self):
298
if self._refs is not None:
300
refs_dict = self.fetch_pack(lambda x: [], None,
301
lambda x: None, lambda x: trace.mutter("git: %s" % x))
302
self._refs = remote_refs_dict_to_container(refs_dict)
223
306
class EmptyObjectStoreIterator(dict):
262
345
os.remove(self._data_path)
348
class BzrGitHttpClient(dulwich.client.HttpGitClient):
350
def __init__(self, transport, *args, **kwargs):
351
self.transport = transport
352
super(BzrGitHttpClient, self).__init__(transport.external_url(), *args, **kwargs)
354
self._http_perform = getattr(self.transport, "_perform", urllib2.urlopen)
356
def _perform(self, req):
357
req.accepted_errors = (200, 404)
358
req.follow_redirections = True
359
req.redirected_to = None
360
return self._http_perform(req)
363
class RemoteGitControlDirFormat(GitControlDirFormat):
364
"""The .git directory control format."""
366
supports_workingtrees = False
369
def _known_formats(self):
370
return set([RemoteGitControlDirFormat()])
372
def is_initializable(self):
375
def is_supported(self):
378
def open(self, transport, _found=None):
379
"""Open this directory.
382
# we dont grok readonly - git isn't integrated with transport.
384
if url.startswith('readonly+'):
385
url = url[len('readonly+'):]
386
if isinstance(transport, GitSmartTransport):
387
get_client = transport._get_client
388
client_path = transport._get_path()
389
elif urlparse.urlsplit(transport.external_url())[0] in ("http", "https"):
390
def get_client(thin_packs=False):
391
return BzrGitHttpClient(transport, thin_packs=thin_packs)
392
client_path, _ = urlutils.split_segment_parameters(transport._path)
394
raise NotBranchError(transport.base)
395
return RemoteGitDir(transport, self, get_client, client_path)
397
def get_format_description(self):
398
return "Remote Git Repository"
400
def initialize_on_transport(self, transport):
401
raise UninitializableFormat(self)
403
def supports_transport(self, transport):
405
external_url = transport.external_url()
406
except InProcessTransport:
407
raise NotBranchError(path=transport.base)
408
return (external_url.startswith("http:") or
409
external_url.startswith("https:") or
410
external_url.startswith("git+") or
411
external_url.startswith("git:"))
265
414
class RemoteGitRepository(GitRepository):
267
def __init__(self, gitdir, lockfiles):
268
GitRepository.__init__(self, gitdir, lockfiles)
272
def inventories(self):
273
raise GitSmartRemoteNotSupported()
277
raise GitSmartRemoteNotSupported()
281
raise GitSmartRemoteNotSupported()
284
if self._refs is not None:
286
self._refs = self.bzrdir.root_transport.fetch_pack(lambda x: [], None,
287
lambda x: None, lambda x: trace.mutter("git: %s" % x))
418
return self.control_url
420
def get_parent_map(self, revids):
421
raise GitSmartRemoteNotSupported(self.get_parent_map, self)
290
423
def fetch_pack(self, determine_wants, graph_walker, pack_data,
292
return self._transport.fetch_pack(determine_wants, graph_walker,
425
return self.bzrdir.fetch_pack(determine_wants, graph_walker,
293
426
pack_data, progress)
295
428
def send_pack(self, get_changed_refs, generate_pack_contents):
296
return self._transport.send_pack(get_changed_refs, generate_pack_contents)
429
return self.bzrdir.send_pack(get_changed_refs, generate_pack_contents)
298
431
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
300
433
fd, path = tempfile.mkstemp(suffix=".pack")
301
self.fetch_pack(determine_wants, graph_walker,
302
lambda x: os.write(fd, x), progress)
435
self.fetch_pack(determine_wants, graph_walker,
436
lambda x: os.write(fd, x), progress)
304
439
if os.path.getsize(path) == 0:
305
440
return EmptyObjectStoreIterator()
306
441
return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
321
456
# Not really an easy way to parse foreign revids here..
322
457
return mapping.revision_id_foreign_to_bzr(foreign_revid)
325
class RemoteGitTagDict(tag.BasicTags):
327
def __init__(self, branch):
329
self.repository = branch.repository
331
def get_tag_dict(self):
333
for k, v in extract_tags(self.repository.get_refs()).iteritems():
334
tags[k] = self.branch.mapping.revision_id_foreign_to_bzr(v)
459
def revision_tree(self, revid):
460
raise GitSmartRemoteNotSupported(self.revision_tree, self)
462
def get_revisions(self, revids):
463
raise GitSmartRemoteNotSupported(self.get_revisions, self)
465
def has_revisions(self, revids):
466
raise GitSmartRemoteNotSupported(self.get_revisions, self)
469
class RemoteGitTagDict(GitTags):
471
def get_refs_container(self):
472
return self.repository.bzrdir.get_refs_container()
337
474
def set_tag(self, name, revid):
338
475
# FIXME: Not supported yet, should do a push of a new ref
342
479
class RemoteGitBranch(GitBranch):
344
def __init__(self, bzrdir, repository, name, lockfiles):
481
def __init__(self, bzrdir, repository, name):
346
super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
483
super(RemoteGitBranch, self).__init__(bzrdir, repository, name)
485
def last_revision_info(self):
486
raise GitSmartRemoteNotSupported(self.last_revision_info, self)
490
return self.control_url
493
def control_url(self):
349
496
def revision_history(self):
350
raise GitSmartRemoteNotSupported()
497
raise GitSmartRemoteNotSupported(self.revision_history, self)
499
def revision_id_to_revno(self, revision_id):
500
raise GitSmartRemoteNotSupported(self.revision_id_to_revno, self)
352
502
def last_revision(self):
353
503
return self.lookup_foreign_revision_id(self.head)
355
def _get_config(self):
356
class EmptyConfig(object):
358
def _get_configobj(self):
359
return config.ConfigObj()
365
507
if self._sha is not None:
367
heads = self.repository.get_refs()
368
name = self.bzrdir._branch_name_to_ref(self.name, "HEAD")
370
self._sha = heads[name]
372
raise NoSuchRef(self.name)
509
refs = self.bzrdir.get_refs_container()
510
name = branch_name_to_ref(self.name)
512
self._sha = refs[name]
514
raise NoSuchRef(name, self.repository.user_url, refs)
375
517
def _synchronize_history(self, destination, revision_id):