/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

Merge http support.

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
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
16
16
 
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.dir import GitDir
29
 
from bzrlib.plugins.git.foreign import ForeignBranch
30
 
from bzrlib.plugins.git.repository import GitFormat, GitRepository
31
 
 
 
17
from bzrlib import (
 
18
    config,
 
19
    debug,
 
20
    tag,
 
21
    trace,
 
22
    ui,
 
23
    urlutils,
 
24
    )
 
25
from bzrlib.errors import (
 
26
    BzrError,
 
27
    InvalidRevisionId,
 
28
    NoSuchFile,
 
29
    NoSuchRevision,
 
30
    NotLocalUrl,
 
31
    )
 
32
from bzrlib.transport import (
 
33
    Transport,
 
34
    )
 
35
 
 
36
from bzrlib.plugins.git import (
 
37
    lazy_check_versions,
 
38
    )
 
39
lazy_check_versions()
 
40
 
 
41
from bzrlib.plugins.git.branch import (
 
42
    GitBranch,
 
43
    extract_tags,
 
44
    )
 
45
from bzrlib.plugins.git.errors import (
 
46
    GitSmartRemoteNotSupported,
 
47
    NoSuchRef,
 
48
    )
 
49
from bzrlib.plugins.git.dir import (
 
50
    GitDir,
 
51
    )
 
52
from bzrlib.plugins.git.mapping import (
 
53
    mapping_registry,
 
54
    )
 
55
from bzrlib.plugins.git.repository import (
 
56
    GitRepository,
 
57
    )
 
58
 
 
59
import dulwich as git
 
60
from dulwich.errors import (
 
61
    GitProtocolError,
 
62
    )
 
63
from dulwich.pack import (
 
64
    Pack,
 
65
    PackData,
 
66
    )
 
67
import os
 
68
import tempfile
32
69
import urllib
33
70
import urlparse
34
71
 
35
 
from dulwich.pack import PackData
 
72
from dulwich.pack import load_pack_index
 
73
 
 
74
 
 
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():
 
78
    return []
 
79
 
 
80
 
 
81
def split_git_url(url):
 
82
    """Split a Git URL.
 
83
 
 
84
    :param url: Git URL
 
85
    :return: Tuple with host, port, username, path.
 
86
    """
 
87
    (scheme, _, loc, _, _) = urlparse.urlsplit(url)
 
88
    hostport, escaped_path = urllib.splithost(loc)
 
89
    path = urllib.unquote(escaped_path)
 
90
    if path.startswith("/~"):
 
91
        path = path[1:]
 
92
    (username, hostport) = urllib.splituser(hostport)
 
93
    (host, port) = urllib.splitnport(hostport, None)
 
94
    return (host, port, username, path)
36
95
 
37
96
 
38
97
class GitSmartTransport(Transport):
39
98
 
40
99
    def __init__(self, url, _client=None):
41
100
        Transport.__init__(self, url)
42
 
        (scheme, _, loc, _, _) = urlparse.urlsplit(url)
43
 
        assert scheme == "git"
44
 
        hostport, self._path = urllib.splithost(loc)
45
 
        (self._host, self._port) = urllib.splitnport(hostport, git.protocol.TCP_GIT_PORT)
46
 
        if _client is not None:
47
 
            self._client = _client
48
 
        else:
49
 
            self._client = git.client.TCPGitClient(self._host, self._port)
 
101
        (self._host, self._port, self._username, self._path) = \
 
102
            split_git_url(url)
 
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)
 
106
        self._client = _client
 
107
 
 
108
    def external_url(self):
 
109
        return self.base
 
110
 
 
111
    def has(self, relpath):
 
112
        return False
 
113
 
 
114
    def _get_client(self, thin_packs):
 
115
        raise NotImplementedError(self._get_client)
 
116
 
 
117
    def _get_path(self):
 
118
        return self._path
50
119
 
51
120
    def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
52
121
        if progress is None:
53
122
            def progress(text):
54
 
                info("git: %s" % text)
55
 
        self._client.fetch_pack(self._path, determine_wants, graph_walker, 
56
 
                pack_data, progress)
 
123
                trace.info("git: %s" % text)
 
124
        client = self._get_client(thin_packs=False)
 
125
        try:
 
126
            return client.fetch_pack(self._get_path(), determine_wants,
 
127
                graph_walker, pack_data, progress)
 
128
        except GitProtocolError, e:
 
129
            raise BzrError(e)
57
130
 
58
 
    def fetch_objects(self, determine_wants, graph_walker, progress=None):
59
 
        fd, path = tempfile.mkstemp(dir=self.pack_dir(), suffix=".pack")
60
 
        self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
61
 
        os.close(fd)
 
131
    def send_pack(self, get_changed_refs, generate_pack_contents):
 
132
        client = self._get_client(thin_packs=False)
62
133
        try:
63
 
            p = PackData(path)
64
 
            for o in p.iterobjects():
65
 
                yield o
66
 
        finally:
67
 
            os.remove(path)
 
134
            return client.send_pack(self._get_path(), get_changed_refs,
 
135
                generate_pack_contents)
 
136
        except GitProtocolError, e:
 
137
            raise BzrError(e)
68
138
 
69
139
    def get(self, path):
70
140
        raise NoSuchFile(path)
71
141
 
 
142
    def abspath(self, relpath):
 
143
        return urlutils.join(self.base, relpath)
 
144
 
72
145
    def clone(self, offset=None):
73
146
        """See Transport.clone()."""
74
147
        if offset is None:
76
149
        else:
77
150
            newurl = urlutils.join(self.base, offset)
78
151
 
79
 
        return GitSmartTransport(newurl, self._client)
 
152
        return self.__class__(newurl, self._client)
 
153
 
 
154
 
 
155
class TCPGitSmartTransport(GitSmartTransport):
 
156
 
 
157
    _scheme = 'git'
 
158
 
 
159
    def _get_client(self, thin_packs):
 
160
        if self._client is not None:
 
161
            ret = self._client
 
162
            self._client = None
 
163
            return ret
 
164
        return git.client.TCPGitClient(self._host, self._port, thin_packs=thin_packs,
 
165
            report_activity=self._report_activity)
 
166
 
 
167
 
 
168
class SSHGitSmartTransport(GitSmartTransport):
 
169
 
 
170
    _scheme = 'git+ssh'
 
171
 
 
172
    def _get_path(self):
 
173
        if self._path.startswith("/~/"):
 
174
            return self._path[3:]
 
175
        return self._path
 
176
 
 
177
    def _get_client(self, thin_packs):
 
178
        if self._client is not None:
 
179
            ret = self._client
 
180
            self._client = None
 
181
            return ret
 
182
        return git.client.SSHGitClient(self._host, self._port, self._username,
 
183
            thin_packs=thin_packs, report_activity=self._report_activity)
80
184
 
81
185
 
82
186
class RemoteGitDir(GitDir):
86
190
        self.root_transport = transport
87
191
        self.transport = transport
88
192
        self._lockfiles = lockfiles
 
193
        self._mode_check_done = None
89
194
 
90
195
    def open_repository(self):
91
196
        return RemoteGitRepository(self, self._lockfiles)
92
197
 
93
 
    def open_branch(self):
 
198
    def open_branch(self, ignore_fallbacks=False):
94
199
        repo = self.open_repository()
95
200
        # TODO: Support for multiple branches in one bzrdir in bzrlib!
96
201
        return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
97
202
 
98
 
    def open_workingtree(self):
 
203
    def open_workingtree(self, recommend_upgrade=False):
99
204
        raise NotLocalUrl(self.transport.base)
100
205
 
101
206
 
 
207
class EmptyObjectStoreIterator(dict):
 
208
 
 
209
    def iterobjects(self):
 
210
        return []
 
211
 
 
212
 
 
213
class TemporaryPackIterator(Pack):
 
214
 
 
215
    def __init__(self, path, resolve_ext_ref):
 
216
        super(TemporaryPackIterator, self).__init__(path)
 
217
        self.resolve_ext_ref = resolve_ext_ref
 
218
 
 
219
    @property
 
220
    def data(self):
 
221
        if self._data is None:
 
222
            self._data = PackData(self._data_path)
 
223
        return self._data
 
224
 
 
225
    @property
 
226
    def index(self):
 
227
        if self._idx is None:
 
228
            if not os.path.exists(self._idx_path):
 
229
                pb = ui.ui_factory.nested_progress_bar()
 
230
                try:
 
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)
 
235
                finally:
 
236
                    pb.finished()
 
237
            self._idx = load_pack_index(self._idx_path)
 
238
        return self._idx
 
239
 
 
240
    def __del__(self):
 
241
        if self._idx is not None:
 
242
            self._idx.close()
 
243
            os.remove(self._idx_path)
 
244
        if self._data is not None:
 
245
            self._data.close()
 
246
            os.remove(self._data_path)
 
247
 
 
248
 
102
249
class RemoteGitRepository(GitRepository):
103
250
 
104
251
    def __init__(self, gitdir, lockfiles):
105
252
        GitRepository.__init__(self, gitdir, lockfiles)
106
 
 
107
 
    def fetch_pack(self, determine_wants, graph_walker, pack_data, 
 
253
        self._refs = None
 
254
 
 
255
    @property
 
256
    def inventories(self):
 
257
        raise GitSmartRemoteNotSupported()
 
258
 
 
259
    @property
 
260
    def revisions(self):
 
261
        raise GitSmartRemoteNotSupported()
 
262
 
 
263
    @property
 
264
    def texts(self):
 
265
        raise GitSmartRemoteNotSupported()
 
266
 
 
267
    def get_refs(self):
 
268
        if self._refs is not None:
 
269
            return self._refs
 
270
        self._refs = self.bzrdir.root_transport.fetch_pack(lambda x: [], None,
 
271
            lambda x: None, lambda x: trace.mutter("git: %s" % x))
 
272
        return self._refs
 
273
 
 
274
    def fetch_pack(self, determine_wants, graph_walker, pack_data,
108
275
                   progress=None):
109
 
        self._transport.fetch_pack(determine_wants, graph_walker, pack_data, 
110
 
            progress)
 
276
        return self._transport.fetch_pack(determine_wants, graph_walker,
 
277
                                          pack_data, progress)
 
278
 
 
279
    def send_pack(self, get_changed_refs, generate_pack_contents):
 
280
        return self._transport.send_pack(get_changed_refs, generate_pack_contents)
 
281
 
 
282
    def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
 
283
                      progress=None):
 
284
        fd, path = tempfile.mkstemp(suffix=".pack")
 
285
        self.fetch_pack(determine_wants, graph_walker,
 
286
            lambda x: os.write(fd, x), progress)
 
287
        os.close(fd)
 
288
        if os.path.getsize(path) == 0:
 
289
            return EmptyObjectStoreIterator()
 
290
        return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
 
291
 
 
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..
 
294
        try:
 
295
            return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
 
296
        except InvalidRevisionId:
 
297
            raise NoSuchRevision(self, bzr_revid)
 
298
 
 
299
 
 
300
class RemoteGitTagDict(tag.BasicTags):
 
301
 
 
302
    def __init__(self, branch):
 
303
        self.branch = branch
 
304
        self.repository = branch.repository
 
305
 
 
306
    def get_tag_dict(self):
 
307
        tags = {}
 
308
        for k, v in extract_tags(self.repository.get_refs()).iteritems():
 
309
            tags[k] = self.branch.mapping.revision_id_foreign_to_bzr(v)
 
310
        return tags
 
311
 
 
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)
111
315
 
112
316
 
113
317
class RemoteGitBranch(GitBranch):
114
318
 
115
319
    def __init__(self, bzrdir, repository, name, lockfiles):
116
 
        def determine_wants(heads):
117
 
            self._ref = heads[name]
118
 
        bzrdir.root_transport.fetch_pack(determine_wants, None, lambda x: None, 
119
 
                             lambda x: mutter("git: %s" % x))
120
 
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name, self._ref, lockfiles)
 
320
        self._ref = None
 
321
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
 
322
                lockfiles)
 
323
 
 
324
    def revision_history(self):
 
325
        raise GitSmartRemoteNotSupported()
121
326
 
122
327
    def last_revision(self):
123
 
        return self.mapping.revision_id_foreign_to_bzr(self._ref)
124
 
 
 
328
        return self.mapping.revision_id_foreign_to_bzr(self.head)
 
329
 
 
330
    def _get_config(self):
 
331
        class EmptyConfig(object):
 
332
 
 
333
            def _get_configobj(self):
 
334
                return config.ConfigObj()
 
335
 
 
336
        return EmptyConfig()
 
337
 
 
338
    @property
 
339
    def head(self):
 
340
        if self._ref is not None:
 
341
            return self._ref
 
342
        heads = self.repository.get_refs()
 
343
        if not self.name in heads:
 
344
            raise NoSuchRef(self.name)
 
345
        self._ref = heads[self.name]
 
346
        return self._ref
 
347
 
 
348
    def _synchronize_history(self, destination, revision_id):
 
349
        """See Branch._synchronize_history()."""
 
350
        destination.generate_revision_history(self.last_revision())
 
351
 
 
352
    def get_push_location(self):
 
353
        return None
 
354
 
 
355
    def set_push_location(self, url):
 
356
        pass