/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

Some fixes for colocated branch handling.

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 _get_default_ref(self):
 
244
        return "refs/heads/master"
 
245
 
 
246
    def destroy_branch(self, name=None):
 
247
        refname = self._get_selected_ref(name)
 
248
        def get_changed_refs(old_refs):
 
249
            ret = dict(old_refs)
 
250
            if not refname in ret:
 
251
                raise NotBranchError(self.user_url)
 
252
            ret[refname] = "00" * 20
 
253
            return ret
 
254
        self.send_pack(get_changed_refs, lambda have, want: [])
 
255
 
 
256
    @property
 
257
    def user_url(self):
 
258
        return self.control_url
 
259
 
 
260
    @property
 
261
    def user_transport(self):
 
262
        return self.root_transport
 
263
 
 
264
    @property
 
265
    def control_url(self):
 
266
        return self.control_transport.base
 
267
 
 
268
    @property
 
269
    def control_transport(self):
 
270
        return self.root_transport
209
271
 
210
272
    def open_repository(self):
211
 
        return RemoteGitRepository(self, self._lockfiles)
 
273
        return RemoteGitRepository(self)
212
274
 
213
 
    def _open_branch(self, name=None, ignore_fallbacks=False, 
214
 
                    unsupported=False):
 
275
    def open_branch(self, name=None, unsupported=False,
 
276
            ignore_fallbacks=False, ref=None, possible_transports=None):
215
277
        repo = self.open_repository()
216
 
        refname = self._branch_name_to_ref(name)
217
 
        return RemoteGitBranch(self, repo, refname, self._lockfiles)
 
278
        refname = self._get_selected_ref(name, ref)
 
279
        return RemoteGitBranch(self, repo, refname)
218
280
 
219
281
    def open_workingtree(self, recommend_upgrade=False):
220
282
        raise NotLocalUrl(self.transport.base)
221
283
 
 
284
    def get_peeled(self, name):
 
285
        return self.get_refs_container().get_peeled(name)
 
286
 
 
287
    def get_refs_container(self):
 
288
        if self._refs is not None:
 
289
            return self._refs
 
290
        refs_dict = self.fetch_pack(lambda x: [], None,
 
291
            lambda x: None, lambda x: trace.mutter("git: %s" % x))
 
292
        self._refs = remote_refs_dict_to_container(refs_dict)
 
293
        return self._refs
 
294
 
222
295
 
223
296
class EmptyObjectStoreIterator(dict):
224
297
 
235
308
    @property
236
309
    def data(self):
237
310
        if self._data is None:
238
 
            self._data = ThinPackData(self.resolve_ext_ref, self._data_path)
 
311
            self._data = PackData(self._data_path)
239
312
        return self._data
240
313
 
241
314
    @property
262
335
            os.remove(self._data_path)
263
336
 
264
337
 
 
338
class BzrGitHttpClient(dulwich.client.HttpGitClient):
 
339
 
 
340
    def __init__(self, transport, *args, **kwargs):
 
341
        self.transport = transport
 
342
        super(BzrGitHttpClient, self).__init__(transport.external_url(), *args, **kwargs)
 
343
        import urllib2
 
344
        self._http_perform = getattr(self.transport, "_perform", urllib2.urlopen)
 
345
 
 
346
    def _perform(self, req):
 
347
        req.accepted_errors = (200, 404)
 
348
        req.follow_redirections = True
 
349
        req.redirected_to = None
 
350
        return self._http_perform(req)
 
351
 
 
352
 
 
353
class RemoteGitControlDirFormat(GitControlDirFormat):
 
354
    """The .git directory control format."""
 
355
 
 
356
    supports_workingtrees = False
 
357
 
 
358
    @classmethod
 
359
    def _known_formats(self):
 
360
        return set([RemoteGitControlDirFormat()])
 
361
 
 
362
    def is_initializable(self):
 
363
        return False
 
364
 
 
365
    def is_supported(self):
 
366
        return True
 
367
 
 
368
    def open(self, transport, _found=None):
 
369
        """Open this directory.
 
370
 
 
371
        """
 
372
        # we dont grok readonly - git isn't integrated with transport.
 
373
        url = transport.base
 
374
        if url.startswith('readonly+'):
 
375
            url = url[len('readonly+'):]
 
376
        if isinstance(transport, GitSmartTransport):
 
377
            get_client = transport._get_client
 
378
            client_path = transport._get_path()
 
379
        elif urlparse.urlsplit(transport.external_url())[0] in ("http", "https"):
 
380
            def get_client(thin_packs=False):
 
381
                return BzrGitHttpClient(transport, thin_packs=thin_packs)
 
382
            client_path, _ = urlutils.split_segment_parameters(transport._path)
 
383
        else:
 
384
            raise NotBranchError(transport.base)
 
385
        return RemoteGitDir(transport, self, get_client, client_path)
 
386
 
 
387
    def get_format_description(self):
 
388
        return "Remote Git Repository"
 
389
 
 
390
    def initialize_on_transport(self, transport):
 
391
        raise UninitializableFormat(self)
 
392
 
 
393
    def supports_transport(self, transport):
 
394
        try:
 
395
            external_url = transport.external_url()
 
396
        except InProcessTransport:
 
397
            raise NotBranchError(path=transport.base)
 
398
        return (external_url.startswith("http:") or
 
399
                external_url.startswith("https:") or
 
400
                external_url.startswith("git+") or
 
401
                external_url.startswith("git:"))
 
402
 
 
403
 
265
404
class RemoteGitRepository(GitRepository):
266
405
 
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
 
406
    @property
 
407
    def user_url(self):
 
408
        return self.control_url
 
409
 
 
410
    def get_parent_map(self, revids):
 
411
        raise GitSmartRemoteNotSupported(self.get_parent_map, self)
289
412
 
290
413
    def fetch_pack(self, determine_wants, graph_walker, pack_data,
291
414
                   progress=None):
292
 
        return self._transport.fetch_pack(determine_wants, graph_walker,
 
415
        return self.bzrdir.fetch_pack(determine_wants, graph_walker,
293
416
                                          pack_data, progress)
294
417
 
295
418
    def send_pack(self, get_changed_refs, generate_pack_contents):
296
 
        return self._transport.send_pack(get_changed_refs, generate_pack_contents)
 
419
        return self.bzrdir.send_pack(get_changed_refs, generate_pack_contents)
297
420
 
298
421
    def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
299
422
                      progress=None):
300
423
        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)
 
424
        try:
 
425
            self.fetch_pack(determine_wants, graph_walker,
 
426
                lambda x: os.write(fd, x), progress)
 
427
        finally:
 
428
            os.close(fd)
304
429
        if os.path.getsize(path) == 0:
305
430
            return EmptyObjectStoreIterator()
306
431
        return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
321
446
        # Not really an easy way to parse foreign revids here..
322
447
        return mapping.revision_id_foreign_to_bzr(foreign_revid)
323
448
 
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
 
449
    def revision_tree(self, revid):
 
450
        raise GitSmartRemoteNotSupported(self.revision_tree, self)
 
451
 
 
452
    def get_revisions(self, revids):
 
453
        raise GitSmartRemoteNotSupported(self.get_revisions, self)
 
454
 
 
455
    def has_revisions(self, revids):
 
456
        raise GitSmartRemoteNotSupported(self.get_revisions, self)
 
457
 
 
458
 
 
459
class RemoteGitTagDict(GitTags):
 
460
 
 
461
    def get_refs_container(self):
 
462
        return self.repository.bzrdir.get_refs_container()
336
463
 
337
464
    def set_tag(self, name, revid):
338
465
        # FIXME: Not supported yet, should do a push of a new ref
341
468
 
342
469
class RemoteGitBranch(GitBranch):
343
470
 
344
 
    def __init__(self, bzrdir, repository, name, lockfiles):
 
471
    def __init__(self, bzrdir, repository, name):
345
472
        self._sha = None
346
 
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
347
 
                lockfiles)
 
473
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name)
 
474
 
 
475
    def last_revision_info(self):
 
476
        raise GitSmartRemoteNotSupported(self.last_revision_info, self)
 
477
 
 
478
    @property
 
479
    def user_url(self):
 
480
        return self.control_url
 
481
 
 
482
    @property
 
483
    def control_url(self):
 
484
        return self.base
348
485
 
349
486
    def revision_history(self):
350
 
        raise GitSmartRemoteNotSupported()
 
487
        raise GitSmartRemoteNotSupported(self.revision_history, self)
 
488
 
 
489
    def revision_id_to_revno(self, revision_id):
 
490
        raise GitSmartRemoteNotSupported(self.revision_id_to_revno, self)
351
491
 
352
492
    def last_revision(self):
353
493
        return self.lookup_foreign_revision_id(self.head)
354
494
 
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
495
    @property
364
496
    def head(self):
365
497
        if self._sha is not None:
366
498
            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)
 
499
        refs = self.bzrdir.get_refs_container()
 
500
        name = branch_name_to_ref(self.name)
 
501
        try:
 
502
            self._sha = refs[name]
 
503
        except KeyError:
 
504
            raise NoSuchRef(name, self.repository.user_url, refs)
373
505
        return self._sha
374
506
 
375
507
    def _synchronize_history(self, destination, revision_id):
381
513
 
382
514
    def set_push_location(self, url):
383
515
        pass
 
516
 
 
517
 
 
518
def remote_refs_dict_to_container(refs_dict):
 
519
    base = {}
 
520
    peeled = {}
 
521
    for k, v in refs_dict.iteritems():
 
522
        if is_peeled(k):
 
523
            peeled[k[:-3]] = v
 
524
        else:
 
525
            base[k] = v
 
526
            peeled[k] = v
 
527
    ret = DictRefsContainer(base)
 
528
    ret._peeled = peeled
 
529
    return ret