/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to remote.py

Reduce number of round trips when fetching from Git.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2008 Canonical Ltd
 
1
# Copyright (C) 2007-2009 Jelmer Vernooij <jelmer@samba.org>
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
import bzrlib
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
25
 
 
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
32
 
 
 
18
from bzrlib import (
 
19
    branch,
 
20
    tag,
 
21
    ui,
 
22
    urlutils,
 
23
    )
 
24
from bzrlib.errors import (
 
25
    BzrError,
 
26
    InvalidRevisionId,
 
27
    NoSuchFile,
 
28
    NoSuchRevision,
 
29
    NotLocalUrl,
 
30
    )
 
31
from bzrlib.trace import (
 
32
    info,
 
33
    )
 
34
from bzrlib.transport import (
 
35
    Transport,
 
36
    )
 
37
 
 
38
from bzrlib.plugins.git import (
 
39
    lazy_check_versions,
 
40
    )
 
41
lazy_check_versions()
 
42
 
 
43
from bzrlib.plugins.git.branch import (
 
44
    GitBranch,
 
45
    )
 
46
from bzrlib.plugins.git.errors import (
 
47
    GitSmartRemoteNotSupported,
 
48
    NoSuchRef,
 
49
    )
 
50
from bzrlib.plugins.git.dir import (
 
51
    GitDir,
 
52
    )
 
53
from bzrlib.plugins.git.mapping import (
 
54
    mapping_registry,
 
55
    )
 
56
from bzrlib.plugins.git.repository import (
 
57
    GitRepositoryFormat,
 
58
    GitRepository,
 
59
    )
 
60
 
 
61
import dulwich as git
 
62
from dulwich.errors import (
 
63
    GitProtocolError,
 
64
    )
 
65
from dulwich.pack import (
 
66
    Pack,
 
67
    PackData,
 
68
    )
33
69
import os
34
70
import tempfile
35
71
import urllib
36
72
import urlparse
37
73
 
38
 
from dulwich.pack import PackData, Pack
 
74
try:
 
75
    from dulwich.pack import load_pack_index
 
76
except ImportError:
 
77
    from dulwich.pack import PackIndex as load_pack_index
 
78
 
 
79
 
 
80
# Don't run any tests on GitSmartTransport as it is not intended to be 
 
81
# a full implementation of Transport
 
82
def get_test_permutations():
 
83
    return []
39
84
 
40
85
 
41
86
class GitSmartTransport(Transport):
43
88
    def __init__(self, url, _client=None):
44
89
        Transport.__init__(self, url)
45
90
        (scheme, _, loc, _, _) = urlparse.urlsplit(url)
46
 
        assert scheme == "git"
47
91
        hostport, self._path = urllib.splithost(loc)
48
 
        (self._host, self._port) = urllib.splitnport(hostport, git.protocol.TCP_GIT_PORT)
 
92
        (self._host, self._port) = urllib.splitnport(hostport, None)
49
93
        self._client = _client
50
94
 
 
95
    def has(self, relpath):
 
96
        return False
 
97
 
51
98
    def _get_client(self):
52
 
        if self._client is not None:
53
 
            ret = self._client
54
 
            self._client = None
55
 
            return ret
56
 
        return git.client.TCPGitClient(self._host, self._port)
 
99
        raise NotImplementedError(self._get_client)
57
100
 
58
101
    def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
59
102
        if progress is None:
60
103
            def progress(text):
61
104
                info("git: %s" % text)
62
 
        self._get_client().fetch_pack(self._path, determine_wants, 
63
 
            graph_walker, pack_data, progress)
 
105
        client = self._get_client()
 
106
        try:
 
107
            return client.fetch_pack(self._path, determine_wants, 
 
108
                graph_walker, pack_data, progress)
 
109
        except GitProtocolError, e:
 
110
            raise BzrError(e)
 
111
 
 
112
    def send_pack(self, get_changed_refs, generate_pack_contents):
 
113
        client = self._get_client()
 
114
        try:
 
115
            return client.send_pack(self._path, get_changed_refs, 
 
116
                generate_pack_contents)
 
117
        except GitProtocolError, e:
 
118
            raise BzrError(e)
64
119
 
65
120
    def get(self, path):
66
121
        raise NoSuchFile(path)
75
130
        else:
76
131
            newurl = urlutils.join(self.base, offset)
77
132
 
78
 
        return GitSmartTransport(newurl, self._client)
 
133
        return self.__class__(newurl, self._client)
 
134
 
 
135
 
 
136
class TCPGitSmartTransport(GitSmartTransport):
 
137
 
 
138
    _scheme = 'git'
 
139
 
 
140
    def _get_client(self):
 
141
        if self._client is not None:
 
142
            ret = self._client
 
143
            self._client = None
 
144
            return ret
 
145
        return git.client.TCPGitClient(self._host, self._port, thin_packs=False,
 
146
            report_activity=self._report_activity)
 
147
 
 
148
 
 
149
class SSHGitSmartTransport(GitSmartTransport):
 
150
 
 
151
    _scheme = 'git+ssh'
 
152
 
 
153
    def _get_client(self):
 
154
        if self._client is not None:
 
155
            ret = self._client
 
156
            self._client = None
 
157
            return ret
 
158
        return git.client.SSHGitClient(self._host, self._port, thin_packs=False,
 
159
            report_activity=self._report_activity)
79
160
 
80
161
 
81
162
class RemoteGitDir(GitDir):
85
166
        self.root_transport = transport
86
167
        self.transport = transport
87
168
        self._lockfiles = lockfiles
 
169
        self._mode_check_done = None
88
170
 
89
171
    def open_repository(self):
90
172
        return RemoteGitRepository(self, self._lockfiles)
91
173
 
92
 
    def open_branch(self):
 
174
    def open_branch(self, ignore_fallbacks=False):
93
175
        repo = self.open_repository()
94
176
        # TODO: Support for multiple branches in one bzrdir in bzrlib!
95
177
        return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
98
180
        raise NotLocalUrl(self.transport.base)
99
181
 
100
182
 
 
183
class EmptyObjectStoreIterator(dict):
 
184
 
 
185
    def iterobjects(self):
 
186
        return []
 
187
 
 
188
 
 
189
class TemporaryPackIterator(Pack):
 
190
 
 
191
    def __init__(self, path, resolve_ext_ref):
 
192
        super(TemporaryPackIterator, self).__init__(path)
 
193
        self.resolve_ext_ref = resolve_ext_ref
 
194
 
 
195
    @property
 
196
    def index(self):
 
197
        if self._idx is None:
 
198
            if self._data is None:
 
199
                self._data = PackData(self._data_path)
 
200
            pb = ui.ui_factory.nested_progress_bar()
 
201
            try:
 
202
                def report_progress(cur, total):
 
203
                    pb.update("generating index", cur, total)
 
204
                self._data.create_index_v2(self._idx_path, self.resolve_ext_ref,
 
205
                    progress=report_progress)
 
206
            finally:
 
207
                pb.finished()
 
208
            self._idx = load_pack_index(self._idx_path)
 
209
        return self._idx
 
210
 
 
211
    def __del__(self):
 
212
        os.remove(self._data_path)
 
213
        os.remove(self._idx_path)
 
214
 
 
215
 
101
216
class RemoteGitRepository(GitRepository):
102
217
 
103
218
    def __init__(self, gitdir, lockfiles):
104
219
        GitRepository.__init__(self, gitdir, lockfiles)
 
220
        self._refs = None
 
221
 
 
222
    @property
 
223
    def inventories(self):
 
224
        raise GitSmartRemoteNotSupported()
 
225
 
 
226
    @property
 
227
    def revisions(self):
 
228
        raise GitSmartRemoteNotSupported()
 
229
 
 
230
    @property
 
231
    def texts(self):
 
232
        raise GitSmartRemoteNotSupported()
 
233
 
 
234
    def get_refs(self):
 
235
        if self._refs is not None:
 
236
            return self._refs
 
237
        self._refs = self.bzrdir.root_transport.fetch_pack(lambda x: [], None, 
 
238
            lambda x: None, lambda x: mutter("git: %s" % x))
 
239
        return self._refs
105
240
 
106
241
    def fetch_pack(self, determine_wants, graph_walker, pack_data, 
107
242
                   progress=None):
108
 
        self._transport.fetch_pack(determine_wants, graph_walker, pack_data, 
109
 
            progress)
110
 
 
111
 
    def fetch_objects(self, determine_wants, graph_walker, progress=None):
 
243
        return self._transport.fetch_pack(determine_wants, graph_walker,
 
244
                                          pack_data, progress)
 
245
 
 
246
    def send_pack(self, get_changed_refs, generate_pack_contents):
 
247
        return self._transport.send_pack(get_changed_refs, generate_pack_contents)
 
248
 
 
249
    def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref, progress=None):
112
250
        fd, path = tempfile.mkstemp(suffix=".pack")
113
251
        self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
114
252
        os.close(fd)
 
253
        if os.path.getsize(path) == 0:
 
254
            return EmptyObjectStoreIterator()
 
255
        return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
 
256
 
 
257
    def lookup_git_revid(self, bzr_revid):
 
258
        # This won't work for any round-tripped bzr revisions, but it's a start..
115
259
        try:
116
 
            basename = path[:-len(".pack")]
117
 
            p = PackData(path)
118
 
            p.create_index_v2(basename+".idx")
119
 
            for o in Pack(basename).iterobjects():
120
 
                yield o
121
 
        finally:
122
 
            os.remove(path)
 
260
            return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
 
261
        except InvalidRevisionId:
 
262
            raise NoSuchRevision(self, bzr_revid)
 
263
 
 
264
 
 
265
class RemoteGitTagDict(tag.BasicTags):
 
266
 
 
267
    def __init__(self, branch):
 
268
        self.branch = branch
 
269
        self.repository = branch.repository
 
270
 
 
271
    def get_tag_dict(self):
 
272
        ret = {}
 
273
        refs = self.repository.get_refs()
 
274
        for k,v in refs.iteritems():
 
275
            if k.startswith("refs/tags/") and not k.endswith("^{}"):
 
276
                v = refs.get(k+"^{}", v)
 
277
                ret[k[len("refs/tags/"):]] = self.branch.mapping.revision_id_foreign_to_bzr(v)
 
278
        return ret
 
279
 
 
280
    def set_tag(self, name, revid):
 
281
        # FIXME: Not supported yet, should do a push of a new ref
 
282
        raise NotImplementedError(self.set_tag)
123
283
 
124
284
 
125
285
class RemoteGitBranch(GitBranch):
126
286
 
127
287
    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)
 
288
        self._ref = None
 
289
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name, 
 
290
                lockfiles)
 
291
 
 
292
    def revision_history(self):
 
293
        raise GitSmartRemoteNotSupported()
135
294
 
136
295
    def last_revision(self):
137
 
        return self.mapping.revision_id_foreign_to_bzr(self._ref)
 
296
        return self.mapping.revision_id_foreign_to_bzr(self.head)
 
297
 
 
298
    @property
 
299
    def head(self):
 
300
        if self._ref is None:
 
301
            return self._ref
 
302
        heads = repository.get_refs()
 
303
        if not name in heads:
 
304
            raise NoSuchRef(name)
 
305
        self._ref = heads[name]
 
306
        return self._ref
138
307
 
139
308
    def _synchronize_history(self, destination, revision_id):
140
309
        """See Branch._synchronize_history()."""