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.errors import NoSuchRef
29
from bzrlib.plugins.git.dir import GitDir
30
from bzrlib.plugins.git.foreign import ForeignBranch
31
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
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)
41
100
class GitSmartTransport(Transport):
43
102
def __init__(self, url, _client=None):
44
103
Transport.__init__(self, url)
45
(scheme, _, loc, _, _) = urlparse.urlsplit(url)
46
assert scheme == "git"
47
hostport, self._path = urllib.splithost(loc)
48
(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)
49
109
self._client = _client
51
def _get_client(self):
52
if self._client is not None:
56
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)
58
123
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
59
124
if progress is None:
60
125
def progress(text):
61
info("git: %s" % text)
62
self._get_client().fetch_pack(self._path, determine_wants,
63
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:
65
142
def get(self, path):
66
143
raise NoSuchFile(path)
85
193
self.root_transport = transport
86
194
self.transport = transport
87
195
self._lockfiles = lockfiles
196
self._mode_check_done = None
198
def _branch_name_to_ref(self, name, default=None):
199
return branch_name_to_ref(name, default=default)
89
201
def open_repository(self):
90
202
return RemoteGitRepository(self, self._lockfiles)
92
def open_branch(self):
204
def _open_branch(self, name=None, ignore_fallbacks=False,
93
206
repo = self.open_repository()
94
# TODO: Support for multiple branches in one bzrdir in bzrlib!
95
return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
207
refname = self._branch_name_to_ref(name)
208
return RemoteGitBranch(self, repo, refname, self._lockfiles)
97
def open_workingtree(self):
210
def open_workingtree(self, recommend_upgrade=False):
98
211
raise NotLocalUrl(self.transport.base)
214
class EmptyObjectStoreIterator(dict):
216
def iterobjects(self):
220
class TemporaryPackIterator(Pack):
222
def __init__(self, path, resolve_ext_ref):
223
super(TemporaryPackIterator, self).__init__(path)
224
self.resolve_ext_ref = resolve_ext_ref
228
if self._data is None:
229
self._data = ThinPackData(self.resolve_ext_ref, self._data_path)
234
if self._idx is None:
235
if not os.path.exists(self._idx_path):
236
pb = ui.ui_factory.nested_progress_bar()
238
def report_progress(cur, total):
239
pb.update("generating index", cur, total)
240
self.data.create_index(self._idx_path,
241
progress=report_progress)
244
self._idx = load_pack_index(self._idx_path)
248
if self._idx is not None:
250
os.remove(self._idx_path)
251
if self._data is not None:
253
os.remove(self._data_path)
101
256
class RemoteGitRepository(GitRepository):
103
258
def __init__(self, gitdir, lockfiles):
104
259
GitRepository.__init__(self, gitdir, lockfiles)
106
def fetch_pack(self, determine_wants, graph_walker, pack_data,
263
def inventories(self):
264
raise GitSmartRemoteNotSupported()
268
raise GitSmartRemoteNotSupported()
272
raise GitSmartRemoteNotSupported()
275
if self._refs is not None:
277
self._refs = self.bzrdir.root_transport.fetch_pack(lambda x: [], None,
278
lambda x: None, lambda x: trace.mutter("git: %s" % x))
281
def fetch_pack(self, determine_wants, graph_walker, pack_data,
108
self._transport.fetch_pack(determine_wants, graph_walker, pack_data,
111
def fetch_objects(self, determine_wants, graph_walker, progress=None):
283
return self._transport.fetch_pack(determine_wants, graph_walker,
286
def send_pack(self, get_changed_refs, generate_pack_contents):
287
return self._transport.send_pack(get_changed_refs, generate_pack_contents)
289
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
112
291
fd, path = tempfile.mkstemp(suffix=".pack")
113
self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
292
self.fetch_pack(determine_wants, graph_walker,
293
lambda x: os.write(fd, x), progress)
295
if os.path.getsize(path) == 0:
296
return EmptyObjectStoreIterator()
297
return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
299
def lookup_bzr_revision_id(self, bzr_revid):
300
# This won't work for any round-tripped bzr revisions, but it's a start..
116
basename = path[:-len(".pack")]
118
p.create_index_v2(basename+".idx")
119
for o in Pack(basename).iterobjects():
302
return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
303
except InvalidRevisionId:
304
raise NoSuchRevision(self, bzr_revid)
306
def lookup_foreign_revision_id(self, foreign_revid, mapping=None):
307
"""Lookup a revision id.
311
mapping = self.get_mapping()
312
# Not really an easy way to parse foreign revids here..
313
return mapping.revision_id_foreign_to_bzr(foreign_revid)
316
class RemoteGitTagDict(tag.BasicTags):
318
def __init__(self, branch):
320
self.repository = branch.repository
322
def get_tag_dict(self):
324
for k, v in extract_tags(self.repository.get_refs()).iteritems():
325
tags[k] = self.branch.mapping.revision_id_foreign_to_bzr(v)
328
def set_tag(self, name, revid):
329
# FIXME: Not supported yet, should do a push of a new ref
330
raise NotImplementedError(self.set_tag)
125
333
class RemoteGitBranch(GitBranch):
127
335
def __init__(self, bzrdir, repository, name, lockfiles):
128
def determine_wants(heads):
129
if not name in heads:
130
raise NoSuchRef(name)
131
self._ref = heads[name]
132
bzrdir.root_transport.fetch_pack(determine_wants, None, lambda x: None,
133
lambda x: mutter("git: %s" % x))
134
super(RemoteGitBranch, self).__init__(bzrdir, repository, name, self._ref, lockfiles)
337
super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
340
def revision_history(self):
341
raise GitSmartRemoteNotSupported()
136
343
def last_revision(self):
137
return self.mapping.revision_id_foreign_to_bzr(self._ref)
344
return self.lookup_foreign_revision_id(self.head)
346
def _get_config(self):
347
class EmptyConfig(object):
349
def _get_configobj(self):
350
return config.ConfigObj()
356
if self._sha is not None:
358
heads = self.repository.get_refs()
359
name = self.bzrdir._branch_name_to_ref(self.name, "HEAD")
361
self._sha = heads[name]
363
raise NoSuchRef(self.name)
139
366
def _synchronize_history(self, destination, revision_id):
140
367
"""See Branch._synchronize_history()."""
141
368
destination.generate_revision_history(self.last_revision())
370
def get_push_location(self):
373
def set_push_location(self, url):