1
# Copyright (C) 2007-2009 Jelmer Vernooij <jelmer@samba.org>
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from bzrlib import branch, tag, urlutils
19
from bzrlib.bzrdir import BzrDir, BzrDirFormat
20
from bzrlib.errors import BzrError, 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 lazy_check_versions
29
from bzrlib.plugins.git.branch import GitBranch
30
from bzrlib.plugins.git.errors import NoSuchRef
31
from bzrlib.plugins.git.dir import GitDir
32
from bzrlib.plugins.git.foreign import ForeignBranch
33
from bzrlib.plugins.git.repository import GitFormat, GitRepository
41
from dulwich.errors import GitProtocolError
42
from dulwich.pack import PackData, Pack, PackIndex
44
# Don't run any tests on GitSmartTransport as it is not intended to be
45
# a full implementation of Transport
46
def get_test_permutations():
50
class GitSmartTransport(Transport):
52
def __init__(self, url, _client=None):
53
Transport.__init__(self, url)
54
(scheme, _, loc, _, _) = urlparse.urlsplit(url)
55
assert scheme == "git"
56
hostport, self._path = urllib.splithost(loc)
57
(self._host, self._port) = urllib.splitnport(hostport, git.protocol.TCP_GIT_PORT)
58
self._client = _client
60
def has(self, relpath):
63
def _get_client(self):
64
if self._client is not None:
68
return git.client.TCPGitClient(self._host, self._port, thin_packs=False)
70
def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
73
info("git: %s" % text)
74
client = self._get_client()
76
client.fetch_pack(self._path, determine_wants,
77
graph_walker, pack_data, progress)
78
except GitProtocolError, e:
82
raise NoSuchFile(path)
84
def abspath(self, relpath):
85
return urlutils.join(self.base, relpath)
87
def clone(self, offset=None):
88
"""See Transport.clone()."""
92
newurl = urlutils.join(self.base, offset)
94
return GitSmartTransport(newurl, self._client)
97
class RemoteGitDir(GitDir):
99
def __init__(self, transport, lockfiles, format):
100
self._format = format
101
self.root_transport = transport
102
self.transport = transport
103
self._lockfiles = lockfiles
105
def open_repository(self):
106
return RemoteGitRepository(self, self._lockfiles)
108
def open_branch(self, _unsupported=False):
109
repo = self.open_repository()
110
# TODO: Support for multiple branches in one bzrdir in bzrlib!
111
return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
113
def open_workingtree(self):
114
raise NotLocalUrl(self.transport.base)
117
class EmptyObjectStoreIterator(dict):
119
def iterobjects(self):
123
class TemporaryPackIterator(Pack):
125
def __init__(self, path, resolve_ext_ref):
126
self.resolve_ext_ref = resolve_ext_ref
127
super(TemporaryPackIterator, self).__init__(path)
131
if self._idx is None:
132
self._data.create_index_v2(self._idx_path, self.resolve_ext_ref)
133
self._idx = PackIndex(self._idx_path)
137
os.remove(self._data_path)
138
os.remove(self._idx_path)
141
class RemoteGitRepository(GitRepository):
143
def __init__(self, gitdir, lockfiles):
144
GitRepository.__init__(self, gitdir, lockfiles)
148
if self._refs is not None:
150
def determine_wants(heads):
153
self.bzrdir.root_transport.fetch_pack(determine_wants, None,
154
lambda x: None, lambda x: mutter("git: %s" % x))
157
def fetch_pack(self, determine_wants, graph_walker, pack_data,
159
self._transport.fetch_pack(determine_wants, graph_walker, pack_data,
162
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref, progress=None):
163
fd, path = tempfile.mkstemp(suffix=".pack")
164
self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
166
if os.path.getsize(path) == 0:
167
return EmptyObjectStoreIterator()
168
return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
171
class RemoteGitTagDict(tag.BasicTags):
173
def __init__(self, branch):
175
self.repository = branch.repository
177
def get_tag_dict(self):
179
refs = self.repository.get_refs()
180
for k,v in refs.iteritems():
181
if k.startswith("refs/tags/") and not k.endswith("^{}"):
182
v = refs.get(k+"^{}", v)
183
ret[k[len("refs/tags/"):]] = self.branch.mapping.revision_id_foreign_to_bzr(v)
186
def set_tag(self, name, revid):
187
# FIXME: Not supported yet, should do a push of a new ref
188
raise NotImplementedError(self.set_tag)
191
class RemoteGitBranch(GitBranch):
193
def __init__(self, bzrdir, repository, name, lockfiles):
194
heads = repository.get_refs()
195
if not name in heads:
196
raise NoSuchRef(name)
197
self._ref = heads[name]
198
super(RemoteGitBranch, self).__init__(bzrdir, repository, name, self._ref, lockfiles)
200
def last_revision(self):
201
return self.mapping.revision_id_foreign_to_bzr(self._ref)
203
def _synchronize_history(self, destination, revision_id):
204
"""See Branch._synchronize_history()."""
205
destination.generate_revision_history(self.last_revision())