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
25
from bzrlib.errors import (
32
from bzrlib.transport import (
36
from bzrlib.plugins.git import (
41
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 (
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.branch import GitBranch
27
from bzrlib.plugins.git.errors import NoSuchRef
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
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
38
from dulwich.pack import PackData, Pack
40
# Don't run any tests on GitSmartTransport as it is not intended to be
80
41
# a full implementation of Transport
81
42
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)
100
46
class GitSmartTransport(Transport):
102
48
def __init__(self, url, _client=None):
103
49
Transport.__init__(self, url)
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)
50
(scheme, _, loc, _, _) = urlparse.urlsplit(url)
51
assert scheme == "git"
52
hostport, self._path = urllib.splithost(loc)
53
(self._host, self._port) = urllib.splitnport(hostport, git.protocol.TCP_GIT_PORT)
109
54
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)
56
def _get_client(self):
57
if self._client is not None:
61
return git.client.TCPGitClient(self._host, self._port)
123
63
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
124
64
if progress is None:
125
65
def progress(text):
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:
66
info("git: %s" % text)
67
self._get_client().fetch_pack(self._path, determine_wants,
68
graph_walker, pack_data, progress)
142
70
def get(self, path):
143
71
raise NoSuchFile(path)
153
81
newurl = urlutils.join(self.base, offset)
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
83
return GitSmartTransport(newurl, self._client)
198
86
class RemoteGitDir(GitDir):
202
90
self.root_transport = transport
203
91
self.transport = transport
204
92
self._lockfiles = lockfiles
205
self._mode_check_done = None
207
def _branch_name_to_ref(self, name, default=None):
208
return branch_name_to_ref(name, default=default)
210
94
def open_repository(self):
211
95
return RemoteGitRepository(self, self._lockfiles)
213
def _open_branch(self, name=None, ignore_fallbacks=False,
97
def open_branch(self, _unsupported=False):
215
98
repo = self.open_repository()
216
refname = self._branch_name_to_ref(name)
217
return RemoteGitBranch(self, repo, refname, self._lockfiles)
99
# TODO: Support for multiple branches in one bzrdir in bzrlib!
100
return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
219
def open_workingtree(self, recommend_upgrade=False):
102
def open_workingtree(self):
220
103
raise NotLocalUrl(self.transport.base)
223
class EmptyObjectStoreIterator(dict):
225
def iterobjects(self):
229
class TemporaryPackIterator(Pack):
231
def __init__(self, path, resolve_ext_ref):
232
super(TemporaryPackIterator, self).__init__(path)
233
self.resolve_ext_ref = resolve_ext_ref
237
if self._data is None:
238
self._data = ThinPackData(self.resolve_ext_ref, self._data_path)
243
if self._idx is None:
244
if not os.path.exists(self._idx_path):
245
pb = ui.ui_factory.nested_progress_bar()
247
def report_progress(cur, total):
248
pb.update("generating index", cur, total)
249
self.data.create_index(self._idx_path,
250
progress=report_progress)
253
self._idx = load_pack_index(self._idx_path)
257
if self._idx is not None:
259
os.remove(self._idx_path)
260
if self._data is not None:
262
os.remove(self._data_path)
265
106
class RemoteGitRepository(GitRepository):
267
108
def __init__(self, gitdir, lockfiles):
268
109
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))
290
def fetch_pack(self, determine_wants, graph_walker, pack_data,
111
def fetch_pack(self, determine_wants, graph_walker, pack_data,
292
return self._transport.fetch_pack(determine_wants, graph_walker,
295
def send_pack(self, get_changed_refs, generate_pack_contents):
296
return self._transport.send_pack(get_changed_refs, generate_pack_contents)
298
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
113
self._transport.fetch_pack(determine_wants, graph_walker, pack_data,
116
def fetch_objects(self, determine_wants, graph_walker, progress=None):
300
117
fd, path = tempfile.mkstemp(suffix=".pack")
301
self.fetch_pack(determine_wants, graph_walker,
302
lambda x: os.write(fd, x), progress)
118
self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
304
if os.path.getsize(path) == 0:
305
return EmptyObjectStoreIterator()
306
return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
308
def lookup_bzr_revision_id(self, bzr_revid):
309
# This won't work for any round-tripped bzr revisions, but it's a start..
311
return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
312
except InvalidRevisionId:
313
raise NoSuchRevision(self, bzr_revid)
315
def lookup_foreign_revision_id(self, foreign_revid, mapping=None):
316
"""Lookup a revision id.
320
mapping = self.get_mapping()
321
# Not really an easy way to parse foreign revids here..
322
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)
337
def set_tag(self, name, revid):
338
# FIXME: Not supported yet, should do a push of a new ref
339
raise NotImplementedError(self.set_tag)
120
basename = path[:-len(".pack")]
122
p.create_index_v2(basename+".idx")
123
pack = Pack(basename)
125
return (len(p), pack.iterobjects())
342
128
class RemoteGitBranch(GitBranch):
344
130
def __init__(self, bzrdir, repository, name, lockfiles):
346
super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
349
def revision_history(self):
350
raise GitSmartRemoteNotSupported()
131
def determine_wants(heads):
132
if not name in heads:
133
raise NoSuchRef(name)
134
self._ref = heads[name]
135
bzrdir.root_transport.fetch_pack(determine_wants, None, lambda x: None,
136
lambda x: mutter("git: %s" % x))
137
super(RemoteGitBranch, self).__init__(bzrdir, repository, name, self._ref, lockfiles)
352
139
def last_revision(self):
353
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
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)
140
return self.mapping.revision_id_foreign_to_bzr(self._ref)
375
142
def _synchronize_history(self, destination, revision_id):
376
143
"""See Branch._synchronize_history()."""
377
144
destination.generate_revision_history(self.last_revision())
379
def get_push_location(self):
382
def set_push_location(self, url):