/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 breezy/plugins/git/remote.py

Merge test-run support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
from __future__ import absolute_import
20
20
 
21
 
import gzip
22
21
from io import BytesIO
23
22
import re
24
23
 
25
 
from .. import (
 
24
from ... import (
26
25
    config,
27
26
    debug,
28
27
    errors,
31
30
    ui,
32
31
    urlutils,
33
32
    )
34
 
from ..push import (
 
33
from ...push import (
35
34
    PushResult,
36
35
    )
37
 
from ..errors import (
 
36
from ...errors import (
38
37
    AlreadyBranchError,
39
38
    BzrError,
40
39
    DivergedBranches,
45
44
    NoSuchTag,
46
45
    NotBranchError,
47
46
    NotLocalUrl,
48
 
    PermissionDenied,
 
47
    NoWorkingTree,
49
48
    UninitializableFormat,
50
49
    )
51
 
from ..revision import NULL_REVISION
52
 
from ..revisiontree import RevisionTree
53
 
from ..sixish import (
54
 
    text_type,
55
 
    viewitems,
56
 
    )
57
 
from ..transport import (
 
50
from ...revisiontree import RevisionTree
 
51
from ...sixish import text_type
 
52
from ...transport import (
58
53
    Transport,
59
54
    register_urlparse_netloc_protocol,
60
55
    )
61
56
 
62
57
from . import (
63
58
    lazy_check_versions,
64
 
    is_github_url,
65
59
    user_agent_for_github,
66
60
    )
67
61
lazy_check_versions()
76
70
from .dir import (
77
71
    GitControlDirFormat,
78
72
    GitDir,
 
73
    BareLocalGitControlDirFormat,
79
74
    )
80
75
from .errors import (
81
76
    GitSmartRemoteNotSupported,
92
87
    )
93
88
from .repository import (
94
89
    GitRepository,
95
 
    GitRepositoryFormat,
96
90
    )
97
91
from .refs import (
98
92
    branch_name_to_ref,
105
99
import dulwich.client
106
100
from dulwich.errors import (
107
101
    GitProtocolError,
108
 
    HangupException,
109
102
    )
110
103
from dulwich.pack import (
111
104
    Pack,
122
115
import os
123
116
import select
124
117
import tempfile
 
118
import urllib
125
119
 
126
120
try:
127
121
    import urllib.parse as urlparse
128
 
    from urllib.parse import splituser
 
122
    from urllib.parse import splituser, splitnport
129
123
except ImportError:
130
124
    import urlparse
131
 
    from urllib import splituser
 
125
    from urllib import splituser, splitnport
132
126
 
133
127
# urlparse only supports a limited number of schemes by default
134
128
register_urlparse_netloc_protocol('git')
142
136
    def _lookup_revno(self, revid):
143
137
        try:
144
138
            return _quick_lookup_revno(self.source_branch, self.target_branch,
145
 
                                       revid)
 
139
                revid)
146
140
        except GitSmartRemoteNotSupported:
147
141
            return None
148
142
 
167
161
    :param url: Git URL
168
162
    :return: Tuple with host, port, username, path.
169
163
    """
170
 
    parsed_url = urlparse.urlparse(url)
171
 
    path = urlparse.unquote(parsed_url.path)
 
164
    (scheme, netloc, loc, _, _) = urlparse.urlsplit(url)
 
165
    path = urlparse.unquote(loc)
172
166
    if path.startswith("/~"):
173
167
        path = path[1:]
174
 
    return ((parsed_url.hostname or '', parsed_url.port, parsed_url.username, path))
 
168
    (username, hostport) = splituser(netloc)
 
169
    (host, port) = splitnport(hostport, None)
 
170
    return (host, port, username, path)
175
171
 
176
172
 
177
173
class RemoteGitError(BzrError):
179
175
    _fmt = "Remote server error: %(msg)s"
180
176
 
181
177
 
182
 
class HeadUpdateFailed(BzrError):
183
 
 
184
 
    _fmt = ("Unable to update remote HEAD branch. To update the master "
185
 
            "branch, specify the URL %(base_url)s,branch=master.")
186
 
 
187
 
    def __init__(self, base_url):
188
 
        super(HeadUpdateFailed, self).__init__()
189
 
        self.base_url = base_url
190
 
 
191
 
 
192
178
def parse_git_error(url, message):
193
179
    """Parse a remote git server error and return a bzr exception.
194
180
 
196
182
    :param message: Message sent by the remote git server
197
183
    """
198
184
    message = str(message).strip()
199
 
    if (message.startswith("Could not find Repository ")
200
 
        or message == 'Repository not found.'
201
 
            or (message.startswith('Repository ') and
202
 
                message.endswith(' not found.'))):
 
185
    if message.startswith("Could not find Repository "):
203
186
        return NotBranchError(url, message)
204
187
    if message == "HEAD failed to update":
205
 
        base_url = urlutils.strip_segment_parameters(url)
206
 
        return HeadUpdateFailed(base_url)
207
 
    if message.startswith('access denied or repository not exported:'):
208
 
        extra, path = message.split(':', 1)
209
 
        return PermissionDenied(path.strip(), extra)
210
 
    if message.endswith('You are not allowed to push code to this project.'):
211
 
        return PermissionDenied(url, message)
212
 
    if message.endswith(' does not appear to be a git repository'):
213
 
        return NotBranchError(url, message)
214
 
    if re.match('(.+) is not a valid repository name',
215
 
                message.splitlines()[0]):
216
 
        return NotBranchError(url, message)
217
 
    m = re.match(r'Permission to ([^ ]+) denied to ([^ ]+)\.', message)
218
 
    if m:
219
 
        return PermissionDenied(m.group(1), 'denied to %s' % m.group(2))
 
188
        base_url, _ = urlutils.split_segment_parameters(url)
 
189
        raise BzrError(
 
190
            ("Unable to update remote HEAD branch. To update the master "
 
191
             "branch, specify the URL %s,branch=master.") % base_url)
220
192
    # Don't know, just return it to the user as-is
221
193
    return RemoteGitError(message)
222
194
 
273
245
        if self._host == '':
274
246
            # return dulwich.client.LocalGitClient()
275
247
            return dulwich.client.SubprocessGitClient()
276
 
        return dulwich.client.TCPGitClient(
277
 
            self._host, self._port, report_activity=self._report_activity)
 
248
        return dulwich.client.TCPGitClient(self._host, self._port,
 
249
            report_activity=self._report_activity)
278
250
 
279
251
 
280
252
class SSHSocketWrapper(object):
295
267
class DulwichSSHVendor(dulwich.client.SSHVendor):
296
268
 
297
269
    def __init__(self):
298
 
        from ..transport import ssh
 
270
        from ...transport import ssh
299
271
        self.bzr_ssh_vendor = ssh._get_ssh_vendor()
300
272
 
301
273
    def run_command(self, host, command, username=None, port=None):
302
 
        connection = self.bzr_ssh_vendor.connect_ssh(
303
 
            username=username, password=None, port=port, host=host,
304
 
            command=command)
 
274
        connection = self.bzr_ssh_vendor.connect_ssh(username=username,
 
275
            password=None, port=port, host=host, command=command)
305
276
        (kind, io_object) = connection.get_sock_or_pipes()
306
277
        if kind == 'socket':
307
278
            return SSHSocketWrapper(io_object)
309
280
            raise AssertionError("Unknown io object kind %r'" % kind)
310
281
 
311
282
 
312
 
# dulwich.client.get_ssh_vendor = DulwichSSHVendor
 
283
#dulwich.client.get_ssh_vendor = DulwichSSHVendor
313
284
 
314
285
 
315
286
class SSHGitSmartTransport(GitSmartTransport):
328
299
            self._client = None
329
300
            return ret
330
301
        location_config = config.LocationConfig(self.base)
331
 
        client = dulwich.client.SSHGitClient(
332
 
            self._host, self._port, self._username,
 
302
        client = dulwich.client.SSHGitClient(self._host, self._port, self._username,
333
303
            report_activity=self._report_activity)
334
304
        # Set up alternate pack program paths
335
305
        upload_pack = location_config.get_user_option('git_upload_pack')
364
334
        self.pb = pb
365
335
 
366
336
    def progress(self, text):
367
 
        text = text.rstrip(b"\r\n")
368
 
        text = text.decode('utf-8')
369
 
        if text.lower().startswith('error: '):
370
 
            trace.show_error('git: %s', text[len(b'error: '):])
 
337
        text = text.rstrip("\r\n")
 
338
        if text.startswith('error: '):
 
339
            trace.show_error('git: %s', text[len('error: '):])
371
340
        else:
372
341
            trace.mutter("git: %s", text)
373
342
            g = self._GIT_PROGRESS_PARTIAL_RE.match(text)
399
368
    def _gitrepository_class(self):
400
369
        return RemoteGitRepository
401
370
 
402
 
    def archive(self, format, committish, write_data, progress=None,
403
 
                write_error=None, subdirs=None, prefix=None):
 
371
    def archive(self, format, committish, write_data, progress=None, write_error=None,
 
372
                subdirs=None, prefix=None):
 
373
        if format not in ('tar', 'zip'):
 
374
            raise errors.NoSuchExportFormat(format)
404
375
        if progress is None:
405
376
            pb = ui.ui_factory.nested_progress_bar()
406
377
            progress = DefaultProgressReporter(pb).progress
407
378
        else:
408
379
            pb = None
409
 
        def progress_wrapper(message):
410
 
            if message.startswith(b"fatal: Unknown archive format \'"):
411
 
                format = message.strip()[len(b"fatal: Unknown archive format '"):-1]
412
 
                raise errors.NoSuchExportFormat(format.decode('ascii'))
413
 
            return progress(message)
414
380
        try:
415
 
            self._client.archive(
416
 
                self._client_path, committish, write_data, progress_wrapper,
417
 
                write_error,
418
 
                format=(format.encode('ascii') if format else None),
419
 
                subdirs=subdirs,
420
 
                prefix=(prefix.encode('utf-8') if prefix else None))
 
381
            self._client.archive(self._client_path, committish,
 
382
                write_data, progress, write_error, format=format,
 
383
                subdirs=subdirs, prefix=prefix)
421
384
        except GitProtocolError as e:
422
385
            raise parse_git_error(self.transport.external_url(), e)
423
386
        finally:
424
387
            if pb is not None:
425
388
                pb.finished()
426
389
 
427
 
    def fetch_pack(self, determine_wants, graph_walker, pack_data,
428
 
                   progress=None):
 
390
    def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
429
391
        if progress is None:
430
392
            pb = ui.ui_factory.nested_progress_bar()
431
393
            progress = DefaultProgressReporter(pb).progress
432
394
        else:
433
395
            pb = None
434
396
        try:
435
 
            result = self._client.fetch_pack(
436
 
                self._client_path, determine_wants, graph_walker, pack_data,
437
 
                progress)
 
397
            result = self._client.fetch_pack(self._client_path, determine_wants,
 
398
                graph_walker, pack_data, progress)
438
399
            if result.refs is None:
439
400
                result.refs = {}
440
 
            self._refs = remote_refs_dict_to_container(
441
 
                result.refs, result.symrefs)
 
401
            self._refs = remote_refs_dict_to_container(result.refs, result.symrefs)
442
402
            return result
443
403
        except GitProtocolError as e:
444
404
            raise parse_git_error(self.transport.external_url(), e)
452
412
            progress = DefaultProgressReporter(pb).progress
453
413
        else:
454
414
            pb = None
455
 
 
456
 
        def get_changed_refs_wrapper(remote_refs):
457
 
            if self._refs is not None:
458
 
                update_refs_container(self._refs, remote_refs)
459
 
            return get_changed_refs(remote_refs)
 
415
        def get_changed_refs_wrapper(refs):
 
416
            # TODO(jelmer): This drops symref information
 
417
            self._refs = remote_refs_dict_to_container(refs)
 
418
            return get_changed_refs(refs)
460
419
        try:
461
 
            return self._client.send_pack(
462
 
                self._client_path, get_changed_refs_wrapper,
463
 
                generate_pack_data, progress)
 
420
            return self._client.send_pack(self._client_path,
 
421
                    get_changed_refs_wrapper, generate_pack_data, progress)
464
422
        except GitProtocolError as e:
465
423
            raise parse_git_error(self.transport.external_url(), e)
466
424
        finally:
472
430
        refname = self._get_selected_ref(name, ref)
473
431
        if refname != b'HEAD' and refname in self.get_refs_container():
474
432
            raise AlreadyBranchError(self.user_url)
475
 
        ref_chain, unused_sha = self.get_refs_container().follow(
476
 
            self._get_selected_ref(name))
477
 
        if ref_chain and ref_chain[0] == b'HEAD':
478
 
            refname = ref_chain[1]
 
433
        if refname in self.get_refs_container():
 
434
            ref_chain, unused_sha = self.get_refs_container().follow(self._get_selected_ref(None))
 
435
            if ref_chain[0] == b'HEAD':
 
436
                refname = ref_chain[1]
479
437
        repo = self.open_repository()
480
438
        return RemoteGitBranch(self, repo, refname)
481
439
 
482
440
    def destroy_branch(self, name=None):
483
441
        refname = self._get_selected_ref(name)
484
 
 
485
442
        def get_changed_refs(old_refs):
486
 
            ret = {}
487
 
            if refname not in old_refs:
 
443
            ret = dict(old_refs)
 
444
            if not refname in ret:
488
445
                raise NotBranchError(self.user_url)
489
446
            ret[refname] = dulwich.client.ZERO_SHA
490
447
            return ret
491
 
 
492
448
        def generate_pack_data(have, want, ofs_delta=False):
493
449
            return pack_objects_to_data([])
494
450
        self.send_pack(get_changed_refs, generate_pack_data)
512
468
    def open_repository(self):
513
469
        return RemoteGitRepository(self)
514
470
 
515
 
    def get_branch_reference(self, name=None):
516
 
        ref = branch_name_to_ref(name)
517
 
        val = self.get_refs_container().read_ref(ref)
518
 
        if val.startswith(SYMREF):
519
 
            return val[len(SYMREF):]
520
 
        return None
521
 
 
522
471
    def open_branch(self, name=None, unsupported=False,
523
 
                    ignore_fallbacks=False, ref=None, possible_transports=None,
524
 
                    nascent_ok=False):
 
472
            ignore_fallbacks=False, ref=None, possible_transports=None,
 
473
            nascent_ok=False):
525
474
        repo = self.open_repository()
526
475
        ref = self._get_selected_ref(name, ref)
527
 
        try:
528
 
            if not nascent_ok and ref not in self.get_refs_container():
529
 
                raise NotBranchError(
530
 
                    self.root_transport.base, controldir=self)
531
 
        except NotGitRepository:
 
476
        if not nascent_ok and ref not in self.get_refs_container():
532
477
            raise NotBranchError(self.root_transport.base,
533
 
                                 controldir=self)
 
478
                    controldir=self)
534
479
        ref_chain, unused_sha = self.get_refs_container().follow(ref)
535
480
        return RemoteGitBranch(self, repo, ref_chain[-1])
536
481
 
547
492
        if self._refs is not None:
548
493
            return self._refs
549
494
        result = self.fetch_pack(lambda x: None, None,
550
 
                                 lambda x: None,
551
 
                                 lambda x: trace.mutter("git: %s" % x))
 
495
            lambda x: None, lambda x: trace.mutter("git: %s" % x))
552
496
        self._refs = remote_refs_dict_to_container(
553
 
            result.refs, result.symrefs)
 
497
                result.refs, result.symrefs)
554
498
        return self._refs
555
499
 
556
500
    def push_branch(self, source, revision_id=None, overwrite=False,
557
501
                    remember=False, create_prefix=False, lossy=False,
558
 
                    name=None, tag_selector=None):
 
502
                    name=None):
559
503
        """Push the source branch into this ControlDir."""
560
504
        if revision_id is None:
561
505
            # No revision supplied by the user, default to the branch
570
514
        push_result.branch_push_result = None
571
515
        repo = self.find_repository()
572
516
        refname = self._get_selected_ref(name)
573
 
        ref_chain, old_sha = self.get_refs_container().follow(refname)
574
 
        if ref_chain:
575
 
            actual_refname = ref_chain[-1]
576
 
        else:
577
 
            actual_refname = refname
578
517
        if isinstance(source, GitBranch) and lossy:
579
518
            raise errors.LossyPushToSameVCS(source.controldir, self)
580
519
        source_store = get_object_store(source.repository)
581
 
        fetch_tags = source.get_config_stack().get('branch.fetch_tags')
582
 
        def get_changed_refs(remote_refs):
583
 
            if self._refs is not None:
584
 
                update_refs_container(self._refs, remote_refs)
585
 
            ret = {}
586
 
            # TODO(jelmer): Unpeel if necessary
587
 
            push_result.new_original_revid = revision_id
588
 
            if lossy:
589
 
                new_sha = source_store._lookup_revision_sha1(revision_id)
590
 
            else:
591
 
                try:
 
520
        with source_store.lock_read():
 
521
            def get_changed_refs(refs):
 
522
                self._refs = remote_refs_dict_to_container(refs)
 
523
                ret = dict(refs)
 
524
                # TODO(jelmer): Unpeel if necessary
 
525
                push_result.new_original_revid = revision_id
 
526
                if lossy:
 
527
                    new_sha = source_store._lookup_revision_sha1(revision_id)
 
528
                else:
592
529
                    new_sha = repo.lookup_bzr_revision_id(revision_id)[0]
593
 
                except errors.NoSuchRevision:
594
 
                    raise errors.NoRoundtrippingSupport(
595
 
                        source, self.open_branch(name=name, nascent_ok=True))
596
 
            if not overwrite:
597
 
                if remote_divergence(old_sha, new_sha, source_store):
598
 
                    raise DivergedBranches(
599
 
                        source, self.open_branch(name, nascent_ok=True))
600
 
            ret[actual_refname] = new_sha
601
 
            if fetch_tags:
602
 
                for tagname, revid in viewitems(source.tags.get_tag_dict()):
603
 
                    if tag_selector and not tag_selector(tagname):
604
 
                        continue
605
 
                    if lossy:
606
 
                        try:
607
 
                            new_sha = source_store._lookup_revision_sha1(revid)
608
 
                        except KeyError:
609
 
                            if source.repository.has_revision(revid):
610
 
                                raise
611
 
                    else:
612
 
                        try:
613
 
                            new_sha = repo.lookup_bzr_revision_id(revid)[0]
614
 
                        except errors.NoSuchRevision:
615
 
                            continue
616
 
                    ret[tag_name_to_ref(tagname)] = new_sha
617
 
            return ret
618
 
        with source_store.lock_read():
 
530
                if not overwrite:
 
531
                    if remote_divergence(ret.get(refname), new_sha, source_store):
 
532
                        raise DivergedBranches(
 
533
                                source, self.open_branch(name, nascent_ok=True))
 
534
                ret[refname] = new_sha
 
535
                return ret
619
536
            if lossy:
620
537
                generate_pack_data = source_store.generate_lossy_pack_data
621
538
            else:
622
539
                generate_pack_data = source_store.generate_pack_data
623
540
            new_refs = self.send_pack(get_changed_refs, generate_pack_data)
624
541
        push_result.new_revid = repo.lookup_foreign_revision_id(
625
 
            new_refs[actual_refname])
626
 
        if old_sha is not None:
627
 
            push_result.old_revid = repo.lookup_foreign_revision_id(old_sha)
628
 
        else:
629
 
            push_result.old_revid = NULL_REVISION
630
 
        if self._refs is not None:
631
 
            update_refs_container(self._refs, new_refs)
 
542
                new_refs[refname])
 
543
        try:
 
544
            old_remote = self._refs[refname]
 
545
        except KeyError:
 
546
            old_remote = ZERO_SHA
 
547
        push_result.old_revid = repo.lookup_foreign_revision_id(old_remote)
 
548
        self._refs = remote_refs_dict_to_container(new_refs)
632
549
        push_result.target_branch = self.open_branch(name)
633
 
        if old_sha is not None:
 
550
        if old_remote != ZERO_SHA:
634
551
            push_result.branch_push_result = GitBranchPushResult()
635
552
            push_result.branch_push_result.source_branch = source
636
 
            push_result.branch_push_result.target_branch = (
637
 
                push_result.target_branch)
 
553
            push_result.branch_push_result.target_branch = push_result.target_branch
638
554
            push_result.branch_push_result.local_branch = None
639
 
            push_result.branch_push_result.master_branch = (
640
 
                push_result.target_branch)
 
555
            push_result.branch_push_result.master_branch = push_result.target_branch
641
556
            push_result.branch_push_result.old_revid = push_result.old_revid
642
557
            push_result.branch_push_result.new_revid = push_result.new_revid
643
 
            push_result.branch_push_result.new_original_revid = (
644
 
                push_result.new_original_revid)
 
558
            push_result.branch_push_result.new_original_revid = push_result.new_original_revid
645
559
        if source.get_push_location() is None or remember:
646
560
            source.set_push_location(push_result.target_branch.base)
647
561
        return push_result
666
580
 
667
581
    def _idx_load_or_generate(self, path):
668
582
        if not os.path.exists(path):
669
 
            with ui.ui_factory.nested_progress_bar() as pb:
 
583
            pb = ui.ui_factory.nested_progress_bar()
 
584
            try:
670
585
                def report_progress(cur, total):
671
586
                    pb.update("generating index", cur, total)
672
 
                self.data.create_index(path, progress=report_progress)
 
587
                self.data.create_index(path,
 
588
                    progress=report_progress)
 
589
            finally:
 
590
                pb.finished()
673
591
        return load_pack_index(path)
674
592
 
675
593
    def __del__(self):
685
603
 
686
604
    def __init__(self, transport, *args, **kwargs):
687
605
        self.transport = transport
688
 
        url = urlutils.URL.from_string(transport.external_url())
689
 
        url.user = url.quoted_user = None
690
 
        url.password = url.quoted_password = None
691
 
        url = urlutils.strip_segment_parameters(str(url))
692
 
        super(BzrGitHttpClient, self).__init__(url, *args, **kwargs)
 
606
        super(BzrGitHttpClient, self).__init__(transport.external_url(), *args, **kwargs)
693
607
 
694
608
    def _http_request(self, url, headers=None, data=None,
695
609
                      allow_compression=False):
704
618
            `redirect_location` properties, and `read` is a consumable read
705
619
            method for the response data.
706
620
        """
707
 
        if is_github_url(url):
708
 
            headers['User-agent'] = user_agent_for_github()
 
621
        from breezy.transport.http._urllib2_wrappers import Request
 
622
        headers['User-agent'] = user_agent_for_github()
709
623
        headers["Pragma"] = "no-cache"
710
624
        if allow_compression:
711
625
            headers["Accept-Encoding"] = "gzip"
712
626
        else:
713
627
            headers["Accept-Encoding"] = "identity"
714
628
 
715
 
        response = self.transport.request(
 
629
        request = Request(
716
630
            ('GET' if data is None else 'POST'),
717
 
            url,
718
 
            body=data,
719
 
            headers=headers, retries=8)
720
 
 
721
 
        if response.status == 404:
 
631
            url, data, headers,
 
632
            accepted_errors=[200, 404])
 
633
 
 
634
        response = self.transport._perform(request)
 
635
 
 
636
        if response.code == 404:
722
637
            raise NotGitRepository()
723
 
        elif response.status != 200:
 
638
        elif response.code != 200:
724
639
            raise GitProtocolError("unexpected http resp %d for %s" %
725
 
                                   (response.status, url))
 
640
                                   (response.code, url))
726
641
 
727
642
        # TODO: Optimization available by adding `preload_content=False` to the
728
643
        # request and just passing the `read` method on instead of going via
730
645
        # before issuing the next to still allow for connection reuse from the
731
646
        # pool.
732
647
        if response.getheader("Content-Encoding") == "gzip":
733
 
            read = gzip.GzipFile(fileobj=BytesIO(response.read())).read
 
648
            read = gzip.GzipFile(fileobj=response).read
734
649
        else:
735
650
            read = response.read
736
651
 
738
653
 
739
654
            def __init__(self, response):
740
655
                self._response = response
741
 
                self.status = response.status
 
656
                self.status = response.code
742
657
                self.content_type = response.getheader("Content-Type")
743
 
                self.redirect_location = response._actual.geturl()
744
 
 
745
 
            def readlines(self):
746
 
                return self._response.readlines()
 
658
                self.redirect_location = response.geturl()
747
659
 
748
660
            def close(self):
749
 
                pass
 
661
                self._response.close()
750
662
 
751
663
        return WrapResponse(response), read
752
664
 
753
665
 
754
 
def _git_url_and_path_from_transport(external_url):
755
 
    url = urlutils.strip_segment_parameters(external_url)
756
 
    return urlparse.urlsplit(url)
757
 
 
758
 
 
759
666
class RemoteGitControlDirFormat(GitControlDirFormat):
760
667
    """The .git directory control format."""
761
668
 
768
675
    def get_branch_format(self):
769
676
        return RemoteGitBranchFormat()
770
677
 
771
 
    @property
772
 
    def repository_format(self):
773
 
        return GitRepositoryFormat()
774
 
 
775
678
    def is_initializable(self):
776
679
        return False
777
680
 
782
685
        """Open this directory.
783
686
 
784
687
        """
785
 
        split_url = _git_url_and_path_from_transport(transport.external_url())
 
688
        # we dont grok readonly - git isn't integrated with transport.
 
689
        url = transport.base
 
690
        if url.startswith('readonly+'):
 
691
            url = url[len('readonly+'):]
 
692
        scheme = urlparse.urlsplit(transport.external_url())[0]
786
693
        if isinstance(transport, GitSmartTransport):
787
694
            client = transport._get_client()
788
 
        elif split_url.scheme in ("http", "https"):
 
695
            client_path = transport._get_path()
 
696
        elif scheme in ("http", "https"):
789
697
            client = BzrGitHttpClient(transport)
790
 
        elif split_url.scheme in ('file', ):
 
698
            client_path, _ = urlutils.split_segment_parameters(transport._path)
 
699
        elif scheme == 'file':
791
700
            client = dulwich.client.LocalGitClient()
 
701
            client_path = transport.local_abspath('.')
792
702
        else:
793
703
            raise NotBranchError(transport.base)
794
704
        if not _found:
795
 
            pass  # TODO(jelmer): Actually probe for something
796
 
        return RemoteGitDir(transport, self, client, split_url.path)
 
705
            pass # TODO(jelmer): Actually probe for something
 
706
        return RemoteGitDir(transport, self, client, client_path)
797
707
 
798
708
    def get_format_description(self):
799
709
        return "Remote Git Repository"
806
716
            external_url = transport.external_url()
807
717
        except InProcessTransport:
808
718
            raise NotBranchError(path=transport.base)
809
 
        return (external_url.startswith("http:")
810
 
                or external_url.startswith("https:")
811
 
                or external_url.startswith("git+")
812
 
                or external_url.startswith("git:"))
 
719
        return (external_url.startswith("http:") or
 
720
                external_url.startswith("https:") or
 
721
                external_url.startswith("git+") or
 
722
                external_url.startswith("git:"))
813
723
 
814
724
 
815
725
class GitRemoteRevisionTree(RevisionTree):
829
739
        # git-upload-archive(1) generaly only supports refs. So let's see if we
830
740
        # can find one.
831
741
        reverse_refs = {
832
 
            v: k for (k, v) in
833
 
            self._repository.controldir.get_refs_container().as_dict().items()}
 
742
                v: k for (k, v) in
 
743
                self._repository.controldir.get_refs_container().as_dict().items()}
834
744
        try:
835
745
            committish = reverse_refs[commit]
836
746
        except KeyError:
838
748
            # Let's hope for the best.
839
749
            committish = commit
840
750
        self._repository.archive(
841
 
            format, committish, f.write,
842
 
            subdirs=([subdir] if subdir else None),
843
 
            prefix=(root + '/') if root else '')
 
751
                format, committish, f.write,
 
752
                subdirs=([subdir] if subdir else None),
 
753
                prefix=(root+'/') if root else '')
844
754
        f.seek(0)
845
755
        return osutils.file_iterator(f)
846
756
 
847
 
    def is_versioned(self, path):
848
 
        raise GitSmartRemoteNotSupported(self.is_versioned, self)
849
 
 
850
 
    def has_filename(self, path):
851
 
        raise GitSmartRemoteNotSupported(self.has_filename, self)
852
 
 
853
 
    def get_file_text(self, path):
854
 
        raise GitSmartRemoteNotSupported(self.get_file_text, self)
855
 
 
856
 
    def list_files(self, include_root=False, from_dir=None, recursive=True):
857
 
        raise GitSmartRemoteNotSupported(self.list_files, self)
858
 
 
859
757
 
860
758
class RemoteGitRepository(GitRepository):
861
759
 
862
 
    supports_random_access = False
863
 
 
864
760
    @property
865
761
    def user_url(self):
866
762
        return self.control_url
873
769
 
874
770
    def fetch_pack(self, determine_wants, graph_walker, pack_data,
875
771
                   progress=None):
876
 
        return self.controldir.fetch_pack(
877
 
            determine_wants, graph_walker, pack_data, progress)
 
772
        return self.controldir.fetch_pack(determine_wants, graph_walker,
 
773
                                          pack_data, progress)
878
774
 
879
775
    def send_pack(self, get_changed_refs, generate_pack_data):
880
776
        return self.controldir.send_pack(get_changed_refs, generate_pack_data)
884
780
        fd, path = tempfile.mkstemp(suffix=".pack")
885
781
        try:
886
782
            self.fetch_pack(determine_wants, graph_walker,
887
 
                            lambda x: os.write(fd, x), progress)
 
783
                lambda x: os.write(fd, x), progress)
888
784
        finally:
889
785
            os.close(fd)
890
786
        if os.path.getsize(path) == 0:
892
788
        return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
893
789
 
894
790
    def lookup_bzr_revision_id(self, bzr_revid, mapping=None):
895
 
        # This won't work for any round-tripped bzr revisions, but it's a
896
 
        # start..
 
791
        # This won't work for any round-tripped bzr revisions, but it's a start..
897
792
        try:
898
793
            return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
899
794
        except InvalidRevisionId:
929
824
 
930
825
    def _set_ref(self, name, sha):
931
826
        ref = tag_name_to_ref(name)
932
 
 
933
827
        def get_changed_refs(old_refs):
934
 
            ret = {}
935
 
            if sha == dulwich.client.ZERO_SHA and ref not in old_refs:
 
828
            ret = dict(old_refs)
 
829
            if sha == dulwich.client.ZERO_SHA and ref not in ret:
936
830
                raise NoSuchTag(name)
937
831
            ret[ref] = sha
938
832
            return ret
939
 
 
940
833
        def generate_pack_data(have, want, ofs_delta=False):
941
834
            return pack_objects_to_data([])
942
835
        self.repository.send_pack(get_changed_refs, generate_pack_data)
947
840
    def __init__(self, controldir, repository, name):
948
841
        self._sha = None
949
842
        super(RemoteGitBranch, self).__init__(controldir, repository, name,
950
 
                                              RemoteGitBranchFormat())
 
843
                RemoteGitBranchFormat())
951
844
 
952
845
    def last_revision_info(self):
953
846
        raise GitSmartRemoteNotSupported(self.last_revision_info, self)
980
873
 
981
874
    def _synchronize_history(self, destination, revision_id):
982
875
        """See Branch._synchronize_history()."""
983
 
        if revision_id is None:
984
 
            revision_id = self.last_revision()
985
 
        destination.generate_revision_history(revision_id)
 
876
        destination.generate_revision_history(self.last_revision())
986
877
 
987
878
    def _get_parent_location(self):
988
879
        return None
1007
898
                continue
1008
899
            peeled = refs.get_peeled(ref_name)
1009
900
            if peeled is None:
1010
 
                # Let's just hope it's a commit
1011
 
                peeled = unpeeled
 
901
                try:
 
902
                    peeled = refs.peel_sha(unpeeled).id
 
903
                except KeyError:
 
904
                    # Let's just hope it's a commit
 
905
                    peeled = unpeeled
1012
906
            if not isinstance(tag_name, text_type):
1013
907
                raise TypeError(tag_name)
1014
908
            yield (ref_name, tag_name, peeled, unpeeled)
1015
909
 
1016
 
    def set_last_revision_info(self, revno, revid):
1017
 
        self.generate_revision_history(revid)
1018
 
 
1019
 
    def generate_revision_history(self, revision_id, last_rev=None,
1020
 
                                  other_branch=None):
1021
 
        sha = self.lookup_bzr_revision_id(revision_id)[0]
1022
 
        def get_changed_refs(old_refs):
1023
 
            return {self.ref: sha}
1024
 
        def generate_pack_data(have, want, ofs_delta=False):
1025
 
            return pack_objects_to_data([])
1026
 
        self.repository.send_pack(get_changed_refs, generate_pack_data)
1027
 
        self._sha = sha
1028
 
 
1029
910
 
1030
911
def remote_refs_dict_to_container(refs_dict, symrefs_dict={}):
1031
912
    base = {}
1035
916
            peeled[k[:-3]] = v
1036
917
        else:
1037
918
            base[k] = v
 
919
            peeled[k] = v
1038
920
    for name, target in symrefs_dict.items():
1039
921
        base[name] = SYMREF + target
1040
922
    ret = DictRefsContainer(base)
1041
923
    ret._peeled = peeled
1042
924
    return ret
1043
 
 
1044
 
 
1045
 
def update_refs_container(container, refs_dict):
1046
 
    peeled = {}
1047
 
    base = {}
1048
 
    for k, v in refs_dict.items():
1049
 
        if is_peeled(k):
1050
 
            peeled[k[:-3]] = v
1051
 
        else:
1052
 
            base[k] = v
1053
 
    container._peeled = peeled
1054
 
    container._refs.update(base)