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
24
from bzrlib.errors import (
31
from bzrlib.transport import (
35
from bzrlib.plugins.git import (
40
from bzrlib.plugins.git.branch import (
44
from bzrlib.plugins.git.errors import (
45
GitSmartRemoteNotSupported,
48
from bzrlib.plugins.git.dir import (
51
from bzrlib.plugins.git.mapping import (
54
from bzrlib.plugins.git.repository import (
57
from bzrlib.plugins.git.refs import (
63
from dulwich.errors import (
66
from dulwich.pack import (
35
from dulwich.pack import PackData
74
urlparse.uses_netloc.extend(['git', 'git+ssh'])
76
from dulwich.pack import load_pack_index
79
# Don't run any tests on GitSmartTransport as it is not intended to be
80
# a full implementation of Transport
81
def get_test_permutations():
85
def split_git_url(url):
89
:return: Tuple with host, port, username, path.
91
(scheme, netloc, loc, _, _) = urlparse.urlsplit(url)
92
path = urllib.unquote(loc)
93
if path.startswith("/~"):
95
(username, hostport) = urllib.splituser(netloc)
96
(host, port) = urllib.splitnport(hostport, None)
97
return (host, port, username, path)
38
100
class GitSmartTransport(Transport):
40
102
def __init__(self, url, _client=None):
41
103
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)
104
(self._host, self._port, self._username, self._path) = \
106
if 'transport' in debug.debug_flags:
107
trace.mutter('host: %r, user: %r, port: %r, path: %r',
108
self._host, self._username, self._port, self._path)
109
self._client = _client
111
def external_url(self):
114
def has(self, relpath):
117
def _get_client(self, thin_packs):
118
raise NotImplementedError(self._get_client)
51
123
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
52
124
if progress is None:
53
125
def progress(text):
54
info("git: %s" % text)
55
self._client.fetch_pack(self._path, determine_wants, graph_walker,
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:
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)
134
def send_pack(self, get_changed_refs, generate_pack_contents):
135
client = self._get_client(thin_packs=False)
64
for o in p.iterobjects():
137
return client.send_pack(self._get_path(), get_changed_refs,
138
generate_pack_contents)
139
except GitProtocolError, e:
69
142
def get(self, path):
70
143
raise NoSuchFile(path)
145
def abspath(self, relpath):
146
return urlutils.join(self.base, relpath)
72
148
def clone(self, offset=None):
73
149
"""See Transport.clone()."""
74
150
if offset is None:
77
153
newurl = urlutils.join(self.base, offset)
79
return GitSmartTransport(newurl, self._client)
155
return self.__class__(newurl, self._client)
158
class TCPGitSmartTransport(GitSmartTransport):
162
def _get_client(self, thin_packs):
163
if self._client is not None:
167
return git.client.TCPGitClient(self._host, self._port,
168
thin_packs=thin_packs, report_activity=self._report_activity)
171
class SSHGitSmartTransport(GitSmartTransport):
176
if self._path.startswith("/~/"):
177
return self._path[3:]
180
def _get_client(self, thin_packs):
181
if self._client is not None:
185
location_config = config.LocationConfig(self.base)
186
client = git.client.SSHGitClient(self._host, self._port, self._username,
187
thin_packs=thin_packs, report_activity=self._report_activity)
188
# Set up alternate pack program paths
189
upload_pack = location_config.get_user_option('git_upload_pack')
191
client.alternative_paths["upload-pack"] = upload_pack
192
receive_pack = location_config.get_user_option('git_receive_pack')
194
client.alternative_paths["receive-pack"] = receive_pack
82
198
class RemoteGitDir(GitDir):
86
202
self.root_transport = transport
87
203
self.transport = transport
88
204
self._lockfiles = lockfiles
205
self._mode_check_done = None
209
return self.control_url
211
def _branch_name_to_ref(self, name, default=None):
212
return branch_name_to_ref(name, default=default)
90
214
def open_repository(self):
91
215
return RemoteGitRepository(self, self._lockfiles)
93
def open_branch(self):
217
def _open_branch(self, name=None, ignore_fallbacks=False,
94
219
repo = self.open_repository()
95
# TODO: Support for multiple branches in one bzrdir in bzrlib!
96
return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
220
refname = self._branch_name_to_ref(name)
221
return RemoteGitBranch(self, repo, refname, self._lockfiles)
98
def open_workingtree(self):
223
def open_workingtree(self, recommend_upgrade=False):
99
224
raise NotLocalUrl(self.transport.base)
227
class EmptyObjectStoreIterator(dict):
229
def iterobjects(self):
233
class TemporaryPackIterator(Pack):
235
def __init__(self, path, resolve_ext_ref):
236
super(TemporaryPackIterator, self).__init__(path)
237
self.resolve_ext_ref = resolve_ext_ref
241
if self._data is None:
242
self._data = ThinPackData(self.resolve_ext_ref, self._data_path)
247
if self._idx is None:
248
if not os.path.exists(self._idx_path):
249
pb = ui.ui_factory.nested_progress_bar()
251
def report_progress(cur, total):
252
pb.update("generating index", cur, total)
253
self.data.create_index(self._idx_path,
254
progress=report_progress)
257
self._idx = load_pack_index(self._idx_path)
261
if self._idx is not None:
263
os.remove(self._idx_path)
264
if self._data is not None:
266
os.remove(self._data_path)
102
269
class RemoteGitRepository(GitRepository):
104
271
def __init__(self, gitdir, lockfiles):
105
272
GitRepository.__init__(self, gitdir, lockfiles)
107
def fetch_pack(self, determine_wants, graph_walker, pack_data,
277
return self.control_url
280
def inventories(self):
281
raise GitSmartRemoteNotSupported()
285
raise GitSmartRemoteNotSupported()
289
raise GitSmartRemoteNotSupported()
292
if self._refs is not None:
294
self._refs = self.bzrdir.root_transport.fetch_pack(lambda x: [], None,
295
lambda x: None, lambda x: trace.mutter("git: %s" % x))
298
def fetch_pack(self, determine_wants, graph_walker, pack_data,
109
self._transport.fetch_pack(determine_wants, graph_walker, pack_data,
300
return self._transport.fetch_pack(determine_wants, graph_walker,
303
def send_pack(self, get_changed_refs, generate_pack_contents):
304
return self._transport.send_pack(get_changed_refs, generate_pack_contents)
306
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
308
fd, path = tempfile.mkstemp(suffix=".pack")
309
self.fetch_pack(determine_wants, graph_walker,
310
lambda x: os.write(fd, x), progress)
312
if os.path.getsize(path) == 0:
313
return EmptyObjectStoreIterator()
314
return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
316
def lookup_bzr_revision_id(self, bzr_revid):
317
# This won't work for any round-tripped bzr revisions, but it's a start..
319
return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
320
except InvalidRevisionId:
321
raise NoSuchRevision(self, bzr_revid)
323
def lookup_foreign_revision_id(self, foreign_revid, mapping=None):
324
"""Lookup a revision id.
328
mapping = self.get_mapping()
329
# Not really an easy way to parse foreign revids here..
330
return mapping.revision_id_foreign_to_bzr(foreign_revid)
333
class RemoteGitTagDict(GitTags):
336
return self.repository.get_refs()
338
def _iter_tag_refs(self, refs):
339
for k, (peeled, unpeeled) in extract_tags(refs).iteritems():
340
yield (k, peeled, unpeeled,
341
self.branch.mapping.revision_id_foreign_to_bzr(peeled))
343
def set_tag(self, name, revid):
344
# FIXME: Not supported yet, should do a push of a new ref
345
raise NotImplementedError(self.set_tag)
113
348
class RemoteGitBranch(GitBranch):
115
350
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)
352
super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
357
return self.control_url
360
def control_url(self):
363
def revision_history(self):
364
raise GitSmartRemoteNotSupported()
122
366
def last_revision(self):
123
return self.mapping.revision_id_foreign_to_bzr(self._ref)
367
return self.lookup_foreign_revision_id(self.head)
369
def _get_config(self):
370
class EmptyConfig(object):
372
def _get_configobj(self):
373
return config.ConfigObj()
379
if self._sha is not None:
381
heads = self.repository.get_refs()
382
name = self.bzrdir._branch_name_to_ref(self.name, "HEAD")
384
self._sha = heads[name]
386
raise NoSuchRef(self.name)
389
def _synchronize_history(self, destination, revision_id):
390
"""See Branch._synchronize_history()."""
391
destination.generate_revision_history(self.last_revision())
393
def get_push_location(self):
396
def set_push_location(self, url):