14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from bzrlib import urlutils
19
from bzrlib.bzrdir import BzrDir, BzrDirFormat
20
from bzrlib.errors import NoSuchFile, NotLocalUrl
21
from bzrlib.lockable_files import TransportLock
22
from bzrlib.repository import Repository
23
from bzrlib.trace import info
24
from bzrlib.transport import Transport
26
from bzrlib.plugins.git import git
27
from bzrlib.plugins.git.branch import GitBranch
28
from bzrlib.plugins.git.dir import GitDir
29
from bzrlib.plugins.git.foreign import ForeignBranch
30
from bzrlib.plugins.git.repository import GitFormat, GitRepository
25
from bzrlib.errors import (
32
from bzrlib.transport import (
36
from bzrlib.plugins.git import (
41
from bzrlib.plugins.git.branch import (
45
from bzrlib.plugins.git.errors import (
46
GitSmartRemoteNotSupported,
49
from bzrlib.plugins.git.dir import (
52
from bzrlib.plugins.git.mapping import (
55
from bzrlib.plugins.git.repository import (
60
from dulwich.errors import (
63
from dulwich.pack import (
35
from dulwich.pack import PackData
72
from dulwich.pack import load_pack_index
75
# Don't run any tests on GitSmartTransport as it is not intended to be
76
# a full implementation of Transport
77
def get_test_permutations():
81
def split_git_url(url):
85
:return: Tuple with host, port, username, path.
87
(scheme, _, loc, _, _) = urlparse.urlsplit(url)
88
hostport, escaped_path = urllib.splithost(loc)
89
path = urllib.unquote(escaped_path)
90
if path.startswith("/~"):
92
(username, hostport) = urllib.splituser(hostport)
93
(host, port) = urllib.splitnport(hostport, None)
94
return (host, port, username, path)
38
97
class GitSmartTransport(Transport):
40
99
def __init__(self, url, _client=None):
41
100
Transport.__init__(self, url)
42
(scheme, _, loc, _, _) = urlparse.urlsplit(url)
43
assert scheme == "git"
44
hostport, self._path = urllib.splithost(loc)
45
(self._host, self._port) = urllib.splitnport(hostport, git.protocol.TCP_GIT_PORT)
46
if _client is not None:
47
self._client = _client
49
self._client = git.client.TCPGitClient(self._host, self._port)
101
(self._host, self._port, self._username, self._path) = \
103
if 'transport' in debug.debug_flags:
104
trace.mutter('host: %r, user: %r, port: %r, path: %r',
105
self._host, self._username, self._port, self._path)
106
self._client = _client
108
def external_url(self):
111
def has(self, relpath):
114
def _get_client(self, thin_packs):
115
raise NotImplementedError(self._get_client)
51
120
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
52
121
if progress is None:
53
122
def progress(text):
54
info("git: %s" % text)
55
self._client.fetch_pack(self._path, determine_wants, graph_walker,
123
trace.info("git: %s" % text)
124
client = self._get_client(thin_packs=False)
126
return client.fetch_pack(self._get_path(), determine_wants,
127
graph_walker, pack_data, progress)
128
except GitProtocolError, e:
58
def fetch_objects(self, determine_wants, graph_walker, progress=None):
59
fd, path = tempfile.mkstemp(dir=self.pack_dir(), suffix=".pack")
60
self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
131
def send_pack(self, get_changed_refs, generate_pack_contents):
132
client = self._get_client(thin_packs=False)
64
for o in p.iterobjects():
134
return client.send_pack(self._get_path(), get_changed_refs,
135
generate_pack_contents)
136
except GitProtocolError, e:
69
139
def get(self, path):
70
140
raise NoSuchFile(path)
142
def abspath(self, relpath):
143
return urlutils.join(self.base, relpath)
72
145
def clone(self, offset=None):
73
146
"""See Transport.clone()."""
74
147
if offset is None:
86
190
self.root_transport = transport
87
191
self.transport = transport
88
192
self._lockfiles = lockfiles
193
self._mode_check_done = None
90
195
def open_repository(self):
91
196
return RemoteGitRepository(self, self._lockfiles)
93
def open_branch(self):
198
def open_branch(self, name=None, ignore_fallbacks=False):
94
199
repo = self.open_repository()
95
200
# TODO: Support for multiple branches in one bzrdir in bzrlib!
96
return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
203
return RemoteGitBranch(self, repo, name, self._lockfiles)
98
def open_workingtree(self):
205
def open_workingtree(self, recommend_upgrade=False):
99
206
raise NotLocalUrl(self.transport.base)
209
class EmptyObjectStoreIterator(dict):
211
def iterobjects(self):
215
class TemporaryPackIterator(Pack):
217
def __init__(self, path, resolve_ext_ref):
218
super(TemporaryPackIterator, self).__init__(path)
219
self.resolve_ext_ref = resolve_ext_ref
223
if self._data is None:
224
self._data = PackData(self._data_path)
229
if self._idx is None:
230
if not os.path.exists(self._idx_path):
231
pb = ui.ui_factory.nested_progress_bar()
233
def report_progress(cur, total):
234
pb.update("generating index", cur, total)
235
self.data.create_index(self._idx_path, self.resolve_ext_ref,
236
progress=report_progress)
239
self._idx = load_pack_index(self._idx_path)
243
if self._idx is not None:
245
os.remove(self._idx_path)
246
if self._data is not None:
248
os.remove(self._data_path)
102
251
class RemoteGitRepository(GitRepository):
104
253
def __init__(self, gitdir, lockfiles):
105
254
GitRepository.__init__(self, gitdir, lockfiles)
107
def fetch_pack(self, determine_wants, graph_walker, pack_data,
258
def inventories(self):
259
raise GitSmartRemoteNotSupported()
263
raise GitSmartRemoteNotSupported()
267
raise GitSmartRemoteNotSupported()
270
if self._refs is not None:
272
self._refs = self.bzrdir.root_transport.fetch_pack(lambda x: [], None,
273
lambda x: None, lambda x: trace.mutter("git: %s" % x))
276
def fetch_pack(self, determine_wants, graph_walker, pack_data,
109
self._transport.fetch_pack(determine_wants, graph_walker, pack_data,
278
return self._transport.fetch_pack(determine_wants, graph_walker,
281
def send_pack(self, get_changed_refs, generate_pack_contents):
282
return self._transport.send_pack(get_changed_refs, generate_pack_contents)
284
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
286
fd, path = tempfile.mkstemp(suffix=".pack")
287
self.fetch_pack(determine_wants, graph_walker,
288
lambda x: os.write(fd, x), progress)
290
if os.path.getsize(path) == 0:
291
return EmptyObjectStoreIterator()
292
return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
294
def lookup_bzr_revision_id(self, bzr_revid):
295
# This won't work for any round-tripped bzr revisions, but it's a start..
297
return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
298
except InvalidRevisionId:
299
raise NoSuchRevision(self, bzr_revid)
302
class RemoteGitTagDict(tag.BasicTags):
304
def __init__(self, branch):
306
self.repository = branch.repository
308
def get_tag_dict(self):
310
for k, v in extract_tags(self.repository.get_refs()).iteritems():
311
tags[k] = self.branch.mapping.revision_id_foreign_to_bzr(v)
314
def set_tag(self, name, revid):
315
# FIXME: Not supported yet, should do a push of a new ref
316
raise NotImplementedError(self.set_tag)
113
319
class RemoteGitBranch(GitBranch):
115
321
def __init__(self, bzrdir, repository, name, lockfiles):
116
def determine_wants(heads):
117
self._ref = heads[name]
118
bzrdir.root_transport.fetch_pack(determine_wants, None, lambda x: None,
119
lambda x: mutter("git: %s" % x))
120
super(RemoteGitBranch, self).__init__(bzrdir, repository, name, self._ref, lockfiles)
323
super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
326
def revision_history(self):
327
raise GitSmartRemoteNotSupported()
122
329
def last_revision(self):
123
return self.mapping.revision_id_foreign_to_bzr(self._ref)
330
return self.mapping.revision_id_foreign_to_bzr(self.head)
332
def _get_config(self):
333
class EmptyConfig(object):
335
def _get_configobj(self):
336
return config.ConfigObj()
342
if self._ref is not None:
344
heads = self.repository.get_refs()
345
if not self.name in heads:
346
raise NoSuchRef(self.name)
347
self._ref = heads[self.name]
350
def _synchronize_history(self, destination, revision_id):
351
"""See Branch._synchronize_history()."""
352
destination.generate_revision_history(self.last_revision())
354
def get_push_location(self):
357
def set_push_location(self, url):