/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

Add 'github:' directory service.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
from bzrlib import (
18
18
    config,
19
19
    debug,
20
 
    tag,
21
20
    trace,
22
21
    ui,
23
22
    urlutils,
 
23
    version_info as bzrlib_version,
24
24
    )
25
25
from bzrlib.errors import (
26
26
    BzrError,
 
27
    InProcessTransport,
27
28
    InvalidRevisionId,
28
29
    NoSuchFile,
29
30
    NoSuchRevision,
 
31
    NotBranchError,
30
32
    NotLocalUrl,
 
33
    UninitializableFormat,
31
34
    )
32
35
from bzrlib.transport import (
33
36
    Transport,
40
43
 
41
44
from bzrlib.plugins.git.branch import (
42
45
    GitBranch,
 
46
    GitTags,
 
47
    )
 
48
from bzrlib.plugins.git.dir import (
 
49
    GitControlDirFormat,
 
50
    GitDir,
43
51
    )
44
52
from bzrlib.plugins.git.errors import (
45
53
    GitSmartRemoteNotSupported,
46
54
    NoSuchRef,
47
55
    )
48
 
from bzrlib.plugins.git.dir import (
49
 
    GitDir,
50
 
    )
51
56
from bzrlib.plugins.git.mapping import (
52
57
    mapping_registry,
53
58
    )
55
60
    GitRepository,
56
61
    )
57
62
from bzrlib.plugins.git.refs import (
58
 
    extract_tags,
59
63
    branch_name_to_ref,
 
64
    is_peeled,
60
65
    )
61
66
 
62
 
import dulwich as git
 
67
import dulwich
 
68
import dulwich.client
63
69
from dulwich.errors import (
64
70
    GitProtocolError,
65
71
    )
66
72
from dulwich.pack import (
67
73
    Pack,
68
 
    ThinPackData,
 
74
    PackData,
69
75
    )
 
76
from dulwich.repo import DictRefsContainer
70
77
import os
71
78
import tempfile
72
79
import urllib
73
80
import urlparse
 
81
 
 
82
# urlparse only supports a limited number of schemes by default
 
83
 
74
84
urlparse.uses_netloc.extend(['git', 'git+ssh'])
75
85
 
76
86
from dulwich.pack import load_pack_index
97
107
    return (host, port, username, path)
98
108
 
99
109
 
 
110
def parse_git_error(url, message):
 
111
    """Parse a remote git server error and return a bzr exception.
 
112
 
 
113
    :param url: URL of the remote repository
 
114
    :param message: Message sent by the remote git server
 
115
    """
 
116
    message = str(message).strip()
 
117
    if message.startswith("Could not find Repository "):
 
118
        return NotBranchError(url, message)
 
119
    # Don't know, just return it to the user as-is
 
120
    return BzrError(message)
 
121
 
 
122
 
100
123
class GitSmartTransport(Transport):
101
124
 
102
125
    def __init__(self, url, _client=None):
107
130
            trace.mutter('host: %r, user: %r, port: %r, path: %r',
108
131
                         self._host, self._username, self._port, self._path)
109
132
        self._client = _client
 
133
        if "," in self._path and bzrlib_version < (2, 5, 0):
 
134
            trace.warning(
 
135
                "ignoring parameters %r, not supported in bzr < 2.5.",
 
136
                self._path.rsplit(",", 1)[1])
 
137
        self._stripped_path = self._path.rsplit(",", 1)[0]
110
138
 
111
139
    def external_url(self):
112
140
        return self.base
118
146
        raise NotImplementedError(self._get_client)
119
147
 
120
148
    def _get_path(self):
121
 
        return self._path
122
 
 
123
 
    def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
124
 
        if progress is None:
125
 
            def progress(text):
126
 
                trace.info("git: %s" % text)
127
 
        client = self._get_client(thin_packs=False)
128
 
        try:
129
 
            return client.fetch_pack(self._get_path(), determine_wants,
130
 
                graph_walker, pack_data, progress)
131
 
        except GitProtocolError, e:
132
 
            raise BzrError(e)
133
 
 
134
 
    def send_pack(self, get_changed_refs, generate_pack_contents):
135
 
        client = self._get_client(thin_packs=False)
136
 
        try:
137
 
            return client.send_pack(self._get_path(), get_changed_refs,
138
 
                generate_pack_contents)
139
 
        except GitProtocolError, e:
140
 
            raise BzrError(e)
 
149
        return self._stripped_path
141
150
 
142
151
    def get(self, path):
143
152
        raise NoSuchFile(path)
164
173
            ret = self._client
165
174
            self._client = None
166
175
            return ret
167
 
        return git.client.TCPGitClient(self._host, self._port,
 
176
        return dulwich.client.TCPGitClient(self._host, self._port,
168
177
            thin_packs=thin_packs, report_activity=self._report_activity)
169
178
 
170
179
 
173
182
    _scheme = 'git+ssh'
174
183
 
175
184
    def _get_path(self):
176
 
        if self._path.startswith("/~/"):
177
 
            return self._path[3:]
178
 
        return self._path
 
185
        path = self._stripped_path
 
186
        if path.startswith("/~/"):
 
187
            return path[3:]
 
188
        return path
179
189
 
180
190
    def _get_client(self, thin_packs):
181
191
        if self._client is not None:
183
193
            self._client = None
184
194
            return ret
185
195
        location_config = config.LocationConfig(self.base)
186
 
        client = git.client.SSHGitClient(self._host, self._port, self._username,
 
196
        client = dulwich.client.SSHGitClient(self._host, self._port, self._username,
187
197
            thin_packs=thin_packs, report_activity=self._report_activity)
188
198
        # Set up alternate pack program paths
189
199
        upload_pack = location_config.get_user_option('git_upload_pack')
197
207
 
198
208
class RemoteGitDir(GitDir):
199
209
 
200
 
    def __init__(self, transport, lockfiles, format):
 
210
    def __init__(self, transport, format, get_client, client_path):
201
211
        self._format = format
202
212
        self.root_transport = transport
203
213
        self.transport = transport
204
 
        self._lockfiles = lockfiles
205
214
        self._mode_check_done = None
206
 
 
207
 
    def _branch_name_to_ref(self, name, default=None):
208
 
        return branch_name_to_ref(name, default=default)
 
215
        self._get_client = get_client
 
216
        self._client_path = client_path
 
217
        self.base = self.root_transport.base
 
218
        self._refs = None
 
219
 
 
220
    def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
 
221
        if progress is None:
 
222
            def progress(text):
 
223
                trace.info("git: %s" % text)
 
224
        def wrap_determine_wants(refs_dict):
 
225
            return determine_wants(remote_refs_dict_to_container(refs_dict))
 
226
        client = self._get_client(thin_packs=False)
 
227
        try:
 
228
            refs_dict = client.fetch_pack(self._client_path, wrap_determine_wants,
 
229
                graph_walker, pack_data, progress)
 
230
            self._refs = remote_refs_dict_to_container(refs_dict)
 
231
            return refs_dict
 
232
        except GitProtocolError, e:
 
233
            raise parse_git_error(self.transport.external_url(), e)
 
234
 
 
235
    def send_pack(self, get_changed_refs, generate_pack_contents):
 
236
        client = self._get_client(thin_packs=False)
 
237
        try:
 
238
            return client.send_pack(self._client_path, get_changed_refs,
 
239
                generate_pack_contents)
 
240
        except GitProtocolError, e:
 
241
            raise parse_git_error(self.transport.external_url(), e)
 
242
 
 
243
    def destroy_branch(self, name=None):
 
244
        refname = self._get_selected_ref(name)
 
245
        if refname is None:
 
246
            refname = "HEAD"
 
247
        def get_changed_refs(old_refs):
 
248
            ret = dict(old_refs)
 
249
            if not refname in ret:
 
250
                raise NotBranchError(self.user_url)
 
251
            ret[refname] = "00" * 20
 
252
            return ret
 
253
        self.send_pack(get_changed_refs, lambda have, want: [])
 
254
 
 
255
    @property
 
256
    def user_url(self):
 
257
        return self.control_url
 
258
 
 
259
    @property
 
260
    def user_transport(self):
 
261
        return self.root_transport
 
262
 
 
263
    @property
 
264
    def control_url(self):
 
265
        return self.control_transport.base
 
266
 
 
267
    @property
 
268
    def control_transport(self):
 
269
        return self.root_transport
209
270
 
210
271
    def open_repository(self):
211
 
        return RemoteGitRepository(self, self._lockfiles)
 
272
        return RemoteGitRepository(self)
212
273
 
213
 
    def _open_branch(self, name=None, ignore_fallbacks=False, 
214
 
                    unsupported=False):
 
274
    def open_branch(self, name=None, unsupported=False,
 
275
            ignore_fallbacks=False, ref=None, possible_transports=None):
215
276
        repo = self.open_repository()
216
 
        refname = self._branch_name_to_ref(name)
217
 
        return RemoteGitBranch(self, repo, refname, self._lockfiles)
 
277
        refname = self._get_selected_ref(name, ref)
 
278
        return RemoteGitBranch(self, repo, refname)
218
279
 
219
280
    def open_workingtree(self, recommend_upgrade=False):
220
281
        raise NotLocalUrl(self.transport.base)
221
282
 
 
283
    def get_peeled(self, name):
 
284
        return self.get_refs_container().get_peeled(name)
 
285
 
 
286
    def get_refs_container(self):
 
287
        if self._refs is not None:
 
288
            return self._refs
 
289
        refs_dict = self.fetch_pack(lambda x: [], None,
 
290
            lambda x: None, lambda x: trace.mutter("git: %s" % x))
 
291
        self._refs = remote_refs_dict_to_container(refs_dict)
 
292
        return self._refs
 
293
 
222
294
 
223
295
class EmptyObjectStoreIterator(dict):
224
296
 
235
307
    @property
236
308
    def data(self):
237
309
        if self._data is None:
238
 
            self._data = ThinPackData(self.resolve_ext_ref, self._data_path)
 
310
            self._data = PackData(self._data_path)
239
311
        return self._data
240
312
 
241
313
    @property
262
334
            os.remove(self._data_path)
263
335
 
264
336
 
 
337
class BzrGitHttpClient(dulwich.client.HttpGitClient):
 
338
 
 
339
    def __init__(self, transport, *args, **kwargs):
 
340
        self.transport = transport
 
341
        super(BzrGitHttpClient, self).__init__(transport.external_url(), *args, **kwargs)
 
342
        import urllib2
 
343
        self._http_perform = getattr(self.transport, "_perform", urllib2.urlopen)
 
344
 
 
345
    def _perform(self, req):
 
346
        req.accepted_errors = (200, 404)
 
347
        req.follow_redirections = True
 
348
        req.redirected_to = None
 
349
        return self._http_perform(req)
 
350
 
 
351
 
 
352
class RemoteGitControlDirFormat(GitControlDirFormat):
 
353
    """The .git directory control format."""
 
354
 
 
355
    supports_workingtrees = False
 
356
 
 
357
    @classmethod
 
358
    def _known_formats(self):
 
359
        return set([RemoteGitControlDirFormat()])
 
360
 
 
361
    def is_initializable(self):
 
362
        return False
 
363
 
 
364
    def is_supported(self):
 
365
        return True
 
366
 
 
367
    def open(self, transport, _found=None):
 
368
        """Open this directory.
 
369
 
 
370
        """
 
371
        # we dont grok readonly - git isn't integrated with transport.
 
372
        url = transport.base
 
373
        if url.startswith('readonly+'):
 
374
            url = url[len('readonly+'):]
 
375
        if isinstance(transport, GitSmartTransport):
 
376
            get_client = transport._get_client
 
377
            client_path = transport._get_path()
 
378
        elif urlparse.urlsplit(transport.external_url())[0] in ("http", "https"):
 
379
            def get_client(thin_packs=False):
 
380
                return BzrGitHttpClient(transport, thin_packs=thin_packs)
 
381
            client_path, _ = urlutils.split_segment_parameters(transport._path)
 
382
        else:
 
383
            raise NotBranchError(transport.base)
 
384
        return RemoteGitDir(transport, self, get_client, client_path)
 
385
 
 
386
    def get_format_description(self):
 
387
        return "Remote Git Repository"
 
388
 
 
389
    def initialize_on_transport(self, transport):
 
390
        raise UninitializableFormat(self)
 
391
 
 
392
    def supports_transport(self, transport):
 
393
        try:
 
394
            external_url = transport.external_url()
 
395
        except InProcessTransport:
 
396
            raise NotBranchError(path=transport.base)
 
397
        return (external_url.startswith("http:") or
 
398
                external_url.startswith("https:") or
 
399
                external_url.startswith("git+") or
 
400
                external_url.startswith("git:"))
 
401
 
 
402
 
265
403
class RemoteGitRepository(GitRepository):
266
404
 
267
 
    def __init__(self, gitdir, lockfiles):
268
 
        GitRepository.__init__(self, gitdir, lockfiles)
269
 
        self._refs = None
270
 
 
271
 
    @property
272
 
    def inventories(self):
273
 
        raise GitSmartRemoteNotSupported()
274
 
 
275
 
    @property
276
 
    def revisions(self):
277
 
        raise GitSmartRemoteNotSupported()
278
 
 
279
 
    @property
280
 
    def texts(self):
281
 
        raise GitSmartRemoteNotSupported()
282
 
 
283
 
    def get_refs(self):
284
 
        if self._refs is not None:
285
 
            return self._refs
286
 
        self._refs = self.bzrdir.root_transport.fetch_pack(lambda x: [], None,
287
 
            lambda x: None, lambda x: trace.mutter("git: %s" % x))
288
 
        return self._refs
 
405
    @property
 
406
    def user_url(self):
 
407
        return self.control_url
 
408
 
 
409
    def get_parent_map(self, revids):
 
410
        raise GitSmartRemoteNotSupported(self.get_parent_map, self)
289
411
 
290
412
    def fetch_pack(self, determine_wants, graph_walker, pack_data,
291
413
                   progress=None):
292
 
        return self._transport.fetch_pack(determine_wants, graph_walker,
 
414
        return self.bzrdir.fetch_pack(determine_wants, graph_walker,
293
415
                                          pack_data, progress)
294
416
 
295
417
    def send_pack(self, get_changed_refs, generate_pack_contents):
296
 
        return self._transport.send_pack(get_changed_refs, generate_pack_contents)
 
418
        return self.bzrdir.send_pack(get_changed_refs, generate_pack_contents)
297
419
 
298
420
    def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
299
421
                      progress=None):
300
422
        fd, path = tempfile.mkstemp(suffix=".pack")
301
 
        self.fetch_pack(determine_wants, graph_walker,
302
 
            lambda x: os.write(fd, x), progress)
303
 
        os.close(fd)
 
423
        try:
 
424
            self.fetch_pack(determine_wants, graph_walker,
 
425
                lambda x: os.write(fd, x), progress)
 
426
        finally:
 
427
            os.close(fd)
304
428
        if os.path.getsize(path) == 0:
305
429
            return EmptyObjectStoreIterator()
306
430
        return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
321
445
        # Not really an easy way to parse foreign revids here..
322
446
        return mapping.revision_id_foreign_to_bzr(foreign_revid)
323
447
 
324
 
 
325
 
class RemoteGitTagDict(tag.BasicTags):
326
 
 
327
 
    def __init__(self, branch):
328
 
        self.branch = branch
329
 
        self.repository = branch.repository
330
 
 
331
 
    def get_tag_dict(self):
332
 
        tags = {}
333
 
        for k, v in extract_tags(self.repository.get_refs()).iteritems():
334
 
            tags[k] = self.branch.mapping.revision_id_foreign_to_bzr(v)
335
 
        return tags
 
448
    def revision_tree(self, revid):
 
449
        raise GitSmartRemoteNotSupported(self.revision_tree, self)
 
450
 
 
451
    def get_revisions(self, revids):
 
452
        raise GitSmartRemoteNotSupported(self.get_revisions, self)
 
453
 
 
454
 
 
455
class RemoteGitTagDict(GitTags):
 
456
 
 
457
    def get_refs_container(self):
 
458
        return self.repository.bzrdir.get_refs_container()
336
459
 
337
460
    def set_tag(self, name, revid):
338
461
        # FIXME: Not supported yet, should do a push of a new ref
341
464
 
342
465
class RemoteGitBranch(GitBranch):
343
466
 
344
 
    def __init__(self, bzrdir, repository, name, lockfiles):
 
467
    def __init__(self, bzrdir, repository, name):
345
468
        self._sha = None
346
 
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
347
 
                lockfiles)
 
469
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name)
 
470
 
 
471
    def last_revision_info(self):
 
472
        raise GitSmartRemoteNotSupported(self.last_revision_info, self)
 
473
 
 
474
    @property
 
475
    def user_url(self):
 
476
        return self.control_url
 
477
 
 
478
    @property
 
479
    def control_url(self):
 
480
        return self.base
348
481
 
349
482
    def revision_history(self):
350
 
        raise GitSmartRemoteNotSupported()
 
483
        raise GitSmartRemoteNotSupported(self.revision_history, self)
 
484
 
 
485
    def revision_id_to_revno(self, revision_id):
 
486
        raise GitSmartRemoteNotSupported(self.revision_id_to_revno, self)
351
487
 
352
488
    def last_revision(self):
353
489
        return self.lookup_foreign_revision_id(self.head)
354
490
 
355
 
    def _get_config(self):
356
 
        class EmptyConfig(object):
357
 
 
358
 
            def _get_configobj(self):
359
 
                return config.ConfigObj()
360
 
 
361
 
        return EmptyConfig()
362
 
 
363
491
    @property
364
492
    def head(self):
365
493
        if self._sha is not None:
366
494
            return self._sha
367
 
        heads = self.repository.get_refs()
368
 
        name = self.bzrdir._branch_name_to_ref(self.name, "HEAD")
369
 
        if name in heads:
370
 
            self._sha = heads[name]
371
 
        else:
372
 
            raise NoSuchRef(self.name)
 
495
        refs = self.bzrdir.get_refs_container()
 
496
        name = branch_name_to_ref(self.name, "HEAD")
 
497
        try:
 
498
            self._sha = refs[name]
 
499
        except KeyError:
 
500
            raise NoSuchRef(name, self.repository.user_url, refs)
373
501
        return self._sha
374
502
 
375
503
    def _synchronize_history(self, destination, revision_id):
381
509
 
382
510
    def set_push_location(self, url):
383
511
        pass
 
512
 
 
513
 
 
514
def remote_refs_dict_to_container(refs_dict):
 
515
    base = {}
 
516
    peeled = {}
 
517
    for k, v in refs_dict.iteritems():
 
518
        if is_peeled(k):
 
519
            peeled[k[:-3]] = v
 
520
        else:
 
521
            base[k] = v
 
522
            peeled[k] = v
 
523
    ret = DictRefsContainer(base)
 
524
    ret._peeled = peeled
 
525
    return ret