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.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
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 (
38
from dulwich.pack import PackData, Pack
40
# Don't run any tests on GitSmartTransport as it is not intended to be
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
41
80
# a full implementation of Transport
42
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)
46
100
class GitSmartTransport(Transport):
48
102
def __init__(self, url, _client=None):
49
103
Transport.__init__(self, url)
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)
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)
54
109
self._client = _client
56
def _get_client(self):
57
if self._client is not None:
61
return git.client.TCPGitClient(self._host, self._port)
111
def external_url(self):
114
def has(self, relpath):
117
def _get_client(self, thin_packs):
118
raise NotImplementedError(self._get_client)
63
123
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
64
124
if progress is None:
65
125
def progress(text):
66
info("git: %s" % text)
67
self._get_client().fetch_pack(self._path, determine_wants,
68
graph_walker, pack_data, progress)
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:
70
142
def get(self, path):
71
143
raise NoSuchFile(path)
81
153
newurl = urlutils.join(self.base, offset)
83
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
86
198
class RemoteGitDir(GitDir):
90
202
self.root_transport = transport
91
203
self.transport = transport
92
204
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)
94
210
def open_repository(self):
95
211
return RemoteGitRepository(self, self._lockfiles)
97
def open_branch(self, _unsupported=False):
213
def _open_branch(self, name=None, ignore_fallbacks=False,
98
215
repo = self.open_repository()
99
# TODO: Support for multiple branches in one bzrdir in bzrlib!
100
return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
216
refname = self._branch_name_to_ref(name)
217
return RemoteGitBranch(self, repo, refname, self._lockfiles)
102
def open_workingtree(self):
219
def open_workingtree(self, recommend_upgrade=False):
103
220
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)
106
265
class RemoteGitRepository(GitRepository):
108
267
def __init__(self, gitdir, lockfiles):
109
268
GitRepository.__init__(self, gitdir, lockfiles)
111
def fetch_pack(self, determine_wants, graph_walker, pack_data,
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,
113
self._transport.fetch_pack(determine_wants, graph_walker, pack_data,
116
def fetch_objects(self, determine_wants, graph_walker, progress=None):
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,
117
300
fd, path = tempfile.mkstemp(suffix=".pack")
118
self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
301
self.fetch_pack(determine_wants, graph_walker,
302
lambda x: os.write(fd, x), progress)
120
basename = path[:-len(".pack")]
122
p.create_index_v2(basename+".idx")
123
pack = Pack(basename)
125
return (len(p), pack.iterobjects())
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)
128
342
class RemoteGitBranch(GitBranch):
130
344
def __init__(self, bzrdir, repository, name, lockfiles):
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)
346
super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
349
def revision_history(self):
350
raise GitSmartRemoteNotSupported()
139
352
def last_revision(self):
140
return self.mapping.revision_id_foreign_to_bzr(self._ref)
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)
142
375
def _synchronize_history(self, destination, revision_id):
143
376
"""See Branch._synchronize_history()."""
144
377
destination.generate_revision_history(self.last_revision())
379
def get_push_location(self):
382
def set_push_location(self, url):