/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 changes to avoid inventories.

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
class RemoteGitError(BzrError):
 
111
 
 
112
    _fmt = "Remote server error: %(message)s"
 
113
 
 
114
 
 
115
def parse_git_error(url, message):
 
116
    """Parse a remote git server error and return a bzr exception.
 
117
 
 
118
    :param url: URL of the remote repository
 
119
    :param message: Message sent by the remote git server
 
120
    """
 
121
    message = str(message).strip()
 
122
    if message.startswith("Could not find Repository "):
 
123
        return NotBranchError(url, message)
 
124
    if message == "HEAD failed to update":
 
125
        base_url, _ = urlutils.split_segment_parameters(url)
 
126
        raise BzrError(
 
127
            ("Unable to update remote HEAD branch. To update the master "
 
128
             "branch, specify the URL %s,branch=master.") % base_url)
 
129
    # Don't know, just return it to the user as-is
 
130
    return RemoteGitError(message)
 
131
 
 
132
 
100
133
class GitSmartTransport(Transport):
101
134
 
102
135
    def __init__(self, url, _client=None):
107
140
            trace.mutter('host: %r, user: %r, port: %r, path: %r',
108
141
                         self._host, self._username, self._port, self._path)
109
142
        self._client = _client
 
143
        if "," in self._path and bzrlib_version < (2, 5, 0):
 
144
            trace.warning(
 
145
                "ignoring parameters %r, not supported in bzr < 2.5.",
 
146
                self._path.rsplit(",", 1)[1])
 
147
        self._stripped_path = self._path.rsplit(",", 1)[0]
110
148
 
111
149
    def external_url(self):
112
150
        return self.base
118
156
        raise NotImplementedError(self._get_client)
119
157
 
120
158
    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)
 
159
        return self._stripped_path
141
160
 
142
161
    def get(self, path):
143
162
        raise NoSuchFile(path)
164
183
            ret = self._client
165
184
            self._client = None
166
185
            return ret
167
 
        return git.client.TCPGitClient(self._host, self._port,
 
186
        return dulwich.client.TCPGitClient(self._host, self._port,
168
187
            thin_packs=thin_packs, report_activity=self._report_activity)
169
188
 
170
189
 
173
192
    _scheme = 'git+ssh'
174
193
 
175
194
    def _get_path(self):
176
 
        if self._path.startswith("/~/"):
177
 
            return self._path[3:]
178
 
        return self._path
 
195
        path = self._stripped_path
 
196
        if path.startswith("/~/"):
 
197
            return path[3:]
 
198
        return path
179
199
 
180
200
    def _get_client(self, thin_packs):
181
201
        if self._client is not None:
183
203
            self._client = None
184
204
            return ret
185
205
        location_config = config.LocationConfig(self.base)
186
 
        client = git.client.SSHGitClient(self._host, self._port, self._username,
 
206
        client = dulwich.client.SSHGitClient(self._host, self._port, self._username,
187
207
            thin_packs=thin_packs, report_activity=self._report_activity)
188
208
        # Set up alternate pack program paths
189
209
        upload_pack = location_config.get_user_option('git_upload_pack')
197
217
 
198
218
class RemoteGitDir(GitDir):
199
219
 
200
 
    def __init__(self, transport, lockfiles, format):
 
220
    def __init__(self, transport, format, get_client, client_path):
201
221
        self._format = format
202
222
        self.root_transport = transport
203
223
        self.transport = transport
204
 
        self._lockfiles = lockfiles
205
224
        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)
 
225
        self._get_client = get_client
 
226
        self._client_path = client_path
 
227
        self.base = self.root_transport.base
 
228
        self._refs = None
 
229
 
 
230
    def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
 
231
        if progress is None:
 
232
            def progress(text):
 
233
                trace.info("git: %s" % text)
 
234
        def wrap_determine_wants(refs_dict):
 
235
            return determine_wants(remote_refs_dict_to_container(refs_dict))
 
236
        client = self._get_client(thin_packs=False)
 
237
        try:
 
238
            refs_dict = client.fetch_pack(self._client_path, wrap_determine_wants,
 
239
                graph_walker, pack_data, progress)
 
240
            self._refs = remote_refs_dict_to_container(refs_dict)
 
241
            return refs_dict
 
242
        except GitProtocolError, e:
 
243
            raise parse_git_error(self.transport.external_url(), e)
 
244
 
 
245
    def send_pack(self, get_changed_refs, generate_pack_contents):
 
246
        client = self._get_client(thin_packs=False)
 
247
        try:
 
248
            return client.send_pack(self._client_path, get_changed_refs,
 
249
                generate_pack_contents)
 
250
        except GitProtocolError, e:
 
251
            raise parse_git_error(self.transport.external_url(), e)
 
252
 
 
253
    def _get_default_ref(self):
 
254
        return "refs/heads/master"
 
255
 
 
256
    def destroy_branch(self, name=None):
 
257
        refname = self._get_selected_ref(name)
 
258
        def get_changed_refs(old_refs):
 
259
            ret = dict(old_refs)
 
260
            if not refname in ret:
 
261
                raise NotBranchError(self.user_url)
 
262
            ret[refname] = "00" * 20
 
263
            return ret
 
264
        self.send_pack(get_changed_refs, lambda have, want: [])
 
265
 
 
266
    @property
 
267
    def user_url(self):
 
268
        return self.control_url
 
269
 
 
270
    @property
 
271
    def user_transport(self):
 
272
        return self.root_transport
 
273
 
 
274
    @property
 
275
    def control_url(self):
 
276
        return self.control_transport.base
 
277
 
 
278
    @property
 
279
    def control_transport(self):
 
280
        return self.root_transport
209
281
 
210
282
    def open_repository(self):
211
 
        return RemoteGitRepository(self, self._lockfiles)
 
283
        return RemoteGitRepository(self)
212
284
 
213
 
    def _open_branch(self, name=None, ignore_fallbacks=False, 
214
 
                    unsupported=False):
 
285
    def open_branch(self, name=None, unsupported=False,
 
286
            ignore_fallbacks=False, ref=None, possible_transports=None):
215
287
        repo = self.open_repository()
216
 
        refname = self._branch_name_to_ref(name)
217
 
        return RemoteGitBranch(self, repo, refname, self._lockfiles)
 
288
        refname = self._get_selected_ref(name, ref)
 
289
        return RemoteGitBranch(self, repo, refname)
218
290
 
219
291
    def open_workingtree(self, recommend_upgrade=False):
220
292
        raise NotLocalUrl(self.transport.base)
221
293
 
 
294
    def get_peeled(self, name):
 
295
        return self.get_refs_container().get_peeled(name)
 
296
 
 
297
    def get_refs_container(self):
 
298
        if self._refs is not None:
 
299
            return self._refs
 
300
        refs_dict = self.fetch_pack(lambda x: [], None,
 
301
            lambda x: None, lambda x: trace.mutter("git: %s" % x))
 
302
        self._refs = remote_refs_dict_to_container(refs_dict)
 
303
        return self._refs
 
304
 
222
305
 
223
306
class EmptyObjectStoreIterator(dict):
224
307
 
235
318
    @property
236
319
    def data(self):
237
320
        if self._data is None:
238
 
            self._data = ThinPackData(self.resolve_ext_ref, self._data_path)
 
321
            self._data = PackData(self._data_path)
239
322
        return self._data
240
323
 
241
324
    @property
262
345
            os.remove(self._data_path)
263
346
 
264
347
 
 
348
class BzrGitHttpClient(dulwich.client.HttpGitClient):
 
349
 
 
350
    def __init__(self, transport, *args, **kwargs):
 
351
        self.transport = transport
 
352
        super(BzrGitHttpClient, self).__init__(transport.external_url(), *args, **kwargs)
 
353
        import urllib2
 
354
        self._http_perform = getattr(self.transport, "_perform", urllib2.urlopen)
 
355
 
 
356
    def _perform(self, req):
 
357
        req.accepted_errors = (200, 404)
 
358
        req.follow_redirections = True
 
359
        req.redirected_to = None
 
360
        return self._http_perform(req)
 
361
 
 
362
 
 
363
class RemoteGitControlDirFormat(GitControlDirFormat):
 
364
    """The .git directory control format."""
 
365
 
 
366
    supports_workingtrees = False
 
367
 
 
368
    @classmethod
 
369
    def _known_formats(self):
 
370
        return set([RemoteGitControlDirFormat()])
 
371
 
 
372
    def is_initializable(self):
 
373
        return False
 
374
 
 
375
    def is_supported(self):
 
376
        return True
 
377
 
 
378
    def open(self, transport, _found=None):
 
379
        """Open this directory.
 
380
 
 
381
        """
 
382
        # we dont grok readonly - git isn't integrated with transport.
 
383
        url = transport.base
 
384
        if url.startswith('readonly+'):
 
385
            url = url[len('readonly+'):]
 
386
        if isinstance(transport, GitSmartTransport):
 
387
            get_client = transport._get_client
 
388
            client_path = transport._get_path()
 
389
        elif urlparse.urlsplit(transport.external_url())[0] in ("http", "https"):
 
390
            def get_client(thin_packs=False):
 
391
                return BzrGitHttpClient(transport, thin_packs=thin_packs)
 
392
            client_path, _ = urlutils.split_segment_parameters(transport._path)
 
393
        else:
 
394
            raise NotBranchError(transport.base)
 
395
        return RemoteGitDir(transport, self, get_client, client_path)
 
396
 
 
397
    def get_format_description(self):
 
398
        return "Remote Git Repository"
 
399
 
 
400
    def initialize_on_transport(self, transport):
 
401
        raise UninitializableFormat(self)
 
402
 
 
403
    def supports_transport(self, transport):
 
404
        try:
 
405
            external_url = transport.external_url()
 
406
        except InProcessTransport:
 
407
            raise NotBranchError(path=transport.base)
 
408
        return (external_url.startswith("http:") or
 
409
                external_url.startswith("https:") or
 
410
                external_url.startswith("git+") or
 
411
                external_url.startswith("git:"))
 
412
 
 
413
 
265
414
class RemoteGitRepository(GitRepository):
266
415
 
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
 
416
    @property
 
417
    def user_url(self):
 
418
        return self.control_url
 
419
 
 
420
    def get_parent_map(self, revids):
 
421
        raise GitSmartRemoteNotSupported(self.get_parent_map, self)
289
422
 
290
423
    def fetch_pack(self, determine_wants, graph_walker, pack_data,
291
424
                   progress=None):
292
 
        return self._transport.fetch_pack(determine_wants, graph_walker,
 
425
        return self.bzrdir.fetch_pack(determine_wants, graph_walker,
293
426
                                          pack_data, progress)
294
427
 
295
428
    def send_pack(self, get_changed_refs, generate_pack_contents):
296
 
        return self._transport.send_pack(get_changed_refs, generate_pack_contents)
 
429
        return self.bzrdir.send_pack(get_changed_refs, generate_pack_contents)
297
430
 
298
431
    def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
299
432
                      progress=None):
300
433
        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)
 
434
        try:
 
435
            self.fetch_pack(determine_wants, graph_walker,
 
436
                lambda x: os.write(fd, x), progress)
 
437
        finally:
 
438
            os.close(fd)
304
439
        if os.path.getsize(path) == 0:
305
440
            return EmptyObjectStoreIterator()
306
441
        return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
321
456
        # Not really an easy way to parse foreign revids here..
322
457
        return mapping.revision_id_foreign_to_bzr(foreign_revid)
323
458
 
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
 
459
    def revision_tree(self, revid):
 
460
        raise GitSmartRemoteNotSupported(self.revision_tree, self)
 
461
 
 
462
    def get_revisions(self, revids):
 
463
        raise GitSmartRemoteNotSupported(self.get_revisions, self)
 
464
 
 
465
    def has_revisions(self, revids):
 
466
        raise GitSmartRemoteNotSupported(self.get_revisions, self)
 
467
 
 
468
 
 
469
class RemoteGitTagDict(GitTags):
 
470
 
 
471
    def get_refs_container(self):
 
472
        return self.repository.bzrdir.get_refs_container()
336
473
 
337
474
    def set_tag(self, name, revid):
338
475
        # FIXME: Not supported yet, should do a push of a new ref
341
478
 
342
479
class RemoteGitBranch(GitBranch):
343
480
 
344
 
    def __init__(self, bzrdir, repository, name, lockfiles):
 
481
    def __init__(self, bzrdir, repository, name):
345
482
        self._sha = None
346
 
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name,
347
 
                lockfiles)
 
483
        super(RemoteGitBranch, self).__init__(bzrdir, repository, name)
 
484
 
 
485
    def last_revision_info(self):
 
486
        raise GitSmartRemoteNotSupported(self.last_revision_info, self)
 
487
 
 
488
    @property
 
489
    def user_url(self):
 
490
        return self.control_url
 
491
 
 
492
    @property
 
493
    def control_url(self):
 
494
        return self.base
348
495
 
349
496
    def revision_history(self):
350
 
        raise GitSmartRemoteNotSupported()
 
497
        raise GitSmartRemoteNotSupported(self.revision_history, self)
 
498
 
 
499
    def revision_id_to_revno(self, revision_id):
 
500
        raise GitSmartRemoteNotSupported(self.revision_id_to_revno, self)
351
501
 
352
502
    def last_revision(self):
353
503
        return self.lookup_foreign_revision_id(self.head)
354
504
 
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
505
    @property
364
506
    def head(self):
365
507
        if self._sha is not None:
366
508
            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)
 
509
        refs = self.bzrdir.get_refs_container()
 
510
        name = branch_name_to_ref(self.name)
 
511
        try:
 
512
            self._sha = refs[name]
 
513
        except KeyError:
 
514
            raise NoSuchRef(name, self.repository.user_url, refs)
373
515
        return self._sha
374
516
 
375
517
    def _synchronize_history(self, destination, revision_id):
381
523
 
382
524
    def set_push_location(self, url):
383
525
        pass
 
526
 
 
527
 
 
528
def remote_refs_dict_to_container(refs_dict):
 
529
    base = {}
 
530
    peeled = {}
 
531
    for k, v in refs_dict.iteritems():
 
532
        if is_peeled(k):
 
533
            peeled[k[:-3]] = v
 
534
        else:
 
535
            base[k] = v
 
536
            peeled[k] = v
 
537
    ret = DictRefsContainer(base)
 
538
    ret._peeled = peeled
 
539
    return ret