/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

Use blob.chunked.

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.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
 
 
 
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
    )
33
67
import os
34
68
import tempfile
35
69
import urllib
36
70
import urlparse
37
 
 
38
 
from dulwich.pack import PackData, Pack
 
71
urlparse.uses_netloc.extend(['git', 'git+ssh'])
 
72
 
 
73
from dulwich.pack import load_pack_index
 
74
 
 
75
 
 
76
# Don't run any tests on GitSmartTransport as it is not intended to be
 
77
# a full implementation of Transport
 
78
def get_test_permutations():
 
79
    return []
 
80
 
 
81
 
 
82
def split_git_url(url):
 
83
    """Split a Git URL.
 
84
 
 
85
    :param url: Git URL
 
86
    :return: Tuple with host, port, username, path.
 
87
    """
 
88
    (scheme, netloc, loc, _, _) = urlparse.urlsplit(url)
 
89
    path = urllib.unquote(loc)
 
90
    if path.startswith("/~"):
 
91
        path = path[1:]
 
92
    (username, hostport) = urllib.splituser(netloc)
 
93
    (host, port) = urllib.splitnport(hostport, None)
 
94
    return (host, port, username, path)
39
95
 
40
96
 
41
97
class GitSmartTransport(Transport):
42
98
 
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) = \
 
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)
49
106
        self._client = _client
50
107
 
51
 
    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)
 
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
57
119
 
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)
 
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)
 
130
 
 
131
    def send_pack(self, get_changed_refs, generate_pack_contents):
 
132
        client = self._get_client(thin_packs=False)
 
133
        try:
 
134
            return client.send_pack(self._get_path(), get_changed_refs,
 
135
                generate_pack_contents)
 
136
        except GitProtocolError, e:
 
137
            raise BzrError(e)
64
138
 
65
139
    def get(self, path):
66
140
        raise NoSuchFile(path)
75
149
        else:
76
150
            newurl = urlutils.join(self.base, offset)
77
151
 
78
 
        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)
79
184
 
80
185
 
81
186
class RemoteGitDir(GitDir):
85
190
        self.root_transport = transport
86
191
        self.transport = transport
87
192
        self._lockfiles = lockfiles
 
193
        self._mode_check_done = None
 
194
 
 
195
    def _branch_name_to_ref(self, name):
 
196
        from bzrlib.plugins.git.branch import branch_name_to_ref
 
197
        return branch_name_to_ref(name, default="refs/heads/master")
88
198
 
89
199
    def open_repository(self):
90
200
        return RemoteGitRepository(self, self._lockfiles)
91
201
 
92
 
    def open_branch(self):
 
202
    def _open_branch(self, name=None, ignore_fallbacks=False, 
 
203
                    unsupported=False):
93
204
        repo = self.open_repository()
94
 
        # TODO: Support for multiple branches in one bzrdir in bzrlib!
95
 
        return RemoteGitBranch(self, repo, "HEAD", self._lockfiles)
 
205
        refname = self._branch_name_to_ref(name)
 
206
        return RemoteGitBranch(self, repo, refname, self._lockfiles)
96
207
 
97
 
    def open_workingtree(self):
 
208
    def open_workingtree(self, recommend_upgrade=False):
98
209
        raise NotLocalUrl(self.transport.base)
99
210
 
100
211
 
 
212
class EmptyObjectStoreIterator(dict):
 
213
 
 
214
    def iterobjects(self):
 
215
        return []
 
216
 
 
217
 
 
218
class TemporaryPackIterator(Pack):
 
219
 
 
220
    def __init__(self, path, resolve_ext_ref):
 
221
        super(TemporaryPackIterator, self).__init__(path)
 
222
        self.resolve_ext_ref = resolve_ext_ref
 
223
 
 
224
    @property
 
225
    def data(self):
 
226
        if self._data is None:
 
227
            self._data = PackData(self._data_path)
 
228
        return self._data
 
229
 
 
230
    @property
 
231
    def index(self):
 
232
        if self._idx is None:
 
233
            if not os.path.exists(self._idx_path):
 
234
                pb = ui.ui_factory.nested_progress_bar()
 
235
                try:
 
236
                    def report_progress(cur, total):
 
237
                        pb.update("generating index", cur, total)
 
238
                    self.data.create_index(self._idx_path, self.resolve_ext_ref,
 
239
                        progress=report_progress)
 
240
                finally:
 
241
                    pb.finished()
 
242
            self._idx = load_pack_index(self._idx_path)
 
243
        return self._idx
 
244
 
 
245
    def __del__(self):
 
246
        if self._idx is not None:
 
247
            self._idx.close()
 
248
            os.remove(self._idx_path)
 
249
        if self._data is not None:
 
250
            self._data.close()
 
251
            os.remove(self._data_path)
 
252
 
 
253
 
101
254
class RemoteGitRepository(GitRepository):
102
255
 
103
256
    def __init__(self, gitdir, lockfiles):
104
257
        GitRepository.__init__(self, gitdir, lockfiles)
105
 
 
106
 
    def fetch_pack(self, determine_wants, graph_walker, pack_data, 
 
258
        self._refs = None
 
259
 
 
260
    @property
 
261
    def inventories(self):
 
262
        raise GitSmartRemoteNotSupported()
 
263
 
 
264
    @property
 
265
    def revisions(self):
 
266
        raise GitSmartRemoteNotSupported()
 
267
 
 
268
    @property
 
269
    def texts(self):
 
270
        raise GitSmartRemoteNotSupported()
 
271
 
 
272
    def get_refs(self):
 
273
        if self._refs is not None:
 
274
            return self._refs
 
275
        self._refs = self.bzrdir.root_transport.fetch_pack(lambda x: [], None,
 
276
            lambda x: None, lambda x: trace.mutter("git: %s" % x))
 
277
        return self._refs
 
278
 
 
279
    def fetch_pack(self, determine_wants, graph_walker, pack_data,
107
280
                   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):
 
281
        return self._transport.fetch_pack(determine_wants, graph_walker,
 
282
                                          pack_data, progress)
 
283
 
 
284
    def send_pack(self, get_changed_refs, generate_pack_contents):
 
285
        return self._transport.send_pack(get_changed_refs, generate_pack_contents)
 
286
 
 
287
    def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
 
288
                      progress=None):
112
289
        fd, path = tempfile.mkstemp(suffix=".pack")
113
 
        self.fetch_pack(determine_wants, graph_walker, lambda x: os.write(fd, x), progress)
 
290
        self.fetch_pack(determine_wants, graph_walker,
 
291
            lambda x: os.write(fd, x), progress)
114
292
        os.close(fd)
 
293
        if os.path.getsize(path) == 0:
 
294
            return EmptyObjectStoreIterator()
 
295
        return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
 
296
 
 
297
    def lookup_bzr_revision_id(self, bzr_revid):
 
298
        # This won't work for any round-tripped bzr revisions, but it's a start..
115
299
        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)
 
300
            return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
 
301
        except InvalidRevisionId:
 
302
            raise NoSuchRevision(self, bzr_revid)
 
303
 
 
304
 
 
305
class RemoteGitTagDict(tag.BasicTags):
 
306
 
 
307
    def __init__(self, branch):
 
308
        self.branch = branch
 
309
        self.repository = branch.repository
 
310
 
 
311
    def get_tag_dict(self):
 
312
        tags = {}
 
313
        for k, v in extract_tags(self.repository.get_refs()).iteritems():
 
314
            tags[k] = self.branch.mapping.revision_id_foreign_to_bzr(v)
 
315
        return tags
 
316
 
 
317
    def set_tag(self, name, revid):
 
318
        # FIXME: Not supported yet, should do a push of a new ref
 
319
        raise NotImplementedError(self.set_tag)
123
320
 
124
321
 
125
322
class RemoteGitBranch(GitBranch):
126
323
 
127
324
    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)
 
325
        self._ref = None
 
326
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
 
327
                lockfiles)
 
328
 
 
329
    def revision_history(self):
 
330
        raise GitSmartRemoteNotSupported()
135
331
 
136
332
    def last_revision(self):
137
 
        return self.mapping.revision_id_foreign_to_bzr(self._ref)
 
333
        return self.mapping.revision_id_foreign_to_bzr(self.head)
 
334
 
 
335
    def _get_config(self):
 
336
        class EmptyConfig(object):
 
337
 
 
338
            def _get_configobj(self):
 
339
                return config.ConfigObj()
 
340
 
 
341
        return EmptyConfig()
 
342
 
 
343
    @property
 
344
    def head(self):
 
345
        if self._ref is not None:
 
346
            return self._ref
 
347
        heads = self.repository.get_refs()
 
348
        if self.name in heads:
 
349
            self._ref = heads[self.name]
 
350
        elif ("refs/heads/" + self.name) in heads:
 
351
            self._ref = heads["refs/heads/" + self.name]
 
352
        else:
 
353
            raise NoSuchRef(self.name)
 
354
        return self._ref
138
355
 
139
356
    def _synchronize_history(self, destination, revision_id):
140
357
        """See Branch._synchronize_history()."""
141
358
        destination.generate_revision_history(self.last_revision())
142
 
 
 
359
 
 
360
    def get_push_location(self):
 
361
        return None
 
362
 
 
363
    def set_push_location(self, url):
 
364
        pass