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 (
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 (
38
from dulwich.pack import PackData, Pack
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)
41
97
class GitSmartTransport(Transport):
43
99
def __init__(self, url, _client=None):
44
100
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)
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)
49
106
self._client = _client
51
def _get_client(self):
52
if self._client is not None:
56
return git.client.TCPGitClient(self._host, self._port)
108
def external_url(self):
111
def has(self, relpath):
114
def _get_client(self, thin_packs):
115
raise NotImplementedError(self._get_client)
58
120
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
59
121
if progress is None:
60
122
def progress(text):
61
info("git: %s" % text)
62
self._get_client().fetch_pack(self._path, determine_wants,
63
graph_walker, pack_data, progress)
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:
131
def send_pack(self, get_changed_refs, generate_pack_contents):
132
client = self._get_client(thin_packs=False)
134
return client.send_pack(self._get_path(), get_changed_refs,
135
generate_pack_contents)
136
except GitProtocolError, e:
65
139
def get(self, path):
66
140
raise NoSuchFile(path)
85
190
self.root_transport = transport
86
191
self.transport = transport
87
192
self._lockfiles = lockfiles
193
self._mode_check_done = None
89
195
def open_repository(self):
90
196
return RemoteGitRepository(self, self._lockfiles)
92
def open_branch(self):
198
def open_branch(self, ignore_fallbacks=False, name=None):
93
199
repo = self.open_repository()
94
# TODO: Support for multiple branches in one bzrdir in bzrlib!
95
return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
200
refname = self._branch_name_to_ref(name)
201
return RemoteGitBranch(self, repo, refname, self._lockfiles)
97
def open_workingtree(self):
203
def open_workingtree(self, recommend_upgrade=False):
98
204
raise NotLocalUrl(self.transport.base)
207
class EmptyObjectStoreIterator(dict):
209
def iterobjects(self):
213
class TemporaryPackIterator(Pack):
215
def __init__(self, path, resolve_ext_ref):
216
super(TemporaryPackIterator, self).__init__(path)
217
self.resolve_ext_ref = resolve_ext_ref
221
if self._data is None:
222
self._data = PackData(self._data_path)
227
if self._idx is None:
228
if not os.path.exists(self._idx_path):
229
pb = ui.ui_factory.nested_progress_bar()
231
def report_progress(cur, total):
232
pb.update("generating index", cur, total)
233
self.data.create_index(self._idx_path, self.resolve_ext_ref,
234
progress=report_progress)
237
self._idx = load_pack_index(self._idx_path)
241
if self._idx is not None:
243
os.remove(self._idx_path)
244
if self._data is not None:
246
os.remove(self._data_path)
101
249
class RemoteGitRepository(GitRepository):
103
251
def __init__(self, gitdir, lockfiles):
104
252
GitRepository.__init__(self, gitdir, lockfiles)
106
def fetch_pack(self, determine_wants, graph_walker, pack_data,
256
def inventories(self):
257
raise GitSmartRemoteNotSupported()
261
raise GitSmartRemoteNotSupported()
265
raise GitSmartRemoteNotSupported()
268
if self._refs is not None:
270
self._refs = self.bzrdir.root_transport.fetch_pack(lambda x: [], None,
271
lambda x: None, lambda x: trace.mutter("git: %s" % x))
274
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):
276
return self._transport.fetch_pack(determine_wants, graph_walker,
279
def send_pack(self, get_changed_refs, generate_pack_contents):
280
return self._transport.send_pack(get_changed_refs, generate_pack_contents)
282
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
112
284
fd, path = tempfile.mkstemp(suffix=".pack")
113
self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
285
self.fetch_pack(determine_wants, graph_walker,
286
lambda x: os.write(fd, x), progress)
288
if os.path.getsize(path) == 0:
289
return EmptyObjectStoreIterator()
290
return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
292
def lookup_bzr_revision_id(self, bzr_revid):
293
# 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():
295
return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
296
except InvalidRevisionId:
297
raise NoSuchRevision(self, bzr_revid)
300
class RemoteGitTagDict(tag.BasicTags):
302
def __init__(self, branch):
304
self.repository = branch.repository
306
def get_tag_dict(self):
308
for k, v in extract_tags(self.repository.get_refs()).iteritems():
309
tags[k] = self.branch.mapping.revision_id_foreign_to_bzr(v)
312
def set_tag(self, name, revid):
313
# FIXME: Not supported yet, should do a push of a new ref
314
raise NotImplementedError(self.set_tag)
125
317
class RemoteGitBranch(GitBranch):
127
319
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)
321
super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
324
def revision_history(self):
325
raise GitSmartRemoteNotSupported()
136
327
def last_revision(self):
137
return self.mapping.revision_id_foreign_to_bzr(self._ref)
328
return self.mapping.revision_id_foreign_to_bzr(self.head)
330
def _get_config(self):
331
class EmptyConfig(object):
333
def _get_configobj(self):
334
return config.ConfigObj()
340
if self._ref is not None:
342
heads = self.repository.get_refs()
343
if not self.name in heads:
344
raise NoSuchRef(self.name)
345
self._ref = heads[self.name]
139
348
def _synchronize_history(self, destination, revision_id):
140
349
"""See Branch._synchronize_history()."""
141
350
destination.generate_revision_history(self.last_revision())
352
def get_push_location(self):
355
def set_push_location(self, url):