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

  • Committer: Jelmer Vernooij
  • Date: 2019-06-03 23:48:08 UTC
  • mfrom: (7316 work)
  • mto: This revision was merged to the branch mainline in revision 7328.
  • Revision ID: jelmer@jelmer.uk-20190603234808-15yk5c7054tj8e2b
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
from __future__ import absolute_import
20
20
 
21
21
import gzip
22
 
from io import BytesIO
23
22
import re
24
23
 
25
24
from .. import (
48
47
    PermissionDenied,
49
48
    UninitializableFormat,
50
49
    )
51
 
from ..revision import NULL_REVISION
52
50
from ..revisiontree import RevisionTree
53
51
from ..sixish import (
54
52
    text_type,
61
59
 
62
60
from . import (
63
61
    lazy_check_versions,
64
 
    is_github_url,
65
62
    user_agent_for_github,
66
63
    )
67
64
lazy_check_versions()
125
122
 
126
123
try:
127
124
    import urllib.parse as urlparse
128
 
    from urllib.parse import splituser
 
125
    from urllib.parse import splituser, splitnport
129
126
except ImportError:
130
127
    import urlparse
131
 
    from urllib import splituser
 
128
    from urllib import splituser, splitnport
132
129
 
133
130
# urlparse only supports a limited number of schemes by default
134
131
register_urlparse_netloc_protocol('git')
167
164
    :param url: Git URL
168
165
    :return: Tuple with host, port, username, path.
169
166
    """
170
 
    parsed_url = urlparse.urlparse(url)
171
 
    path = urlparse.unquote(parsed_url.path)
 
167
    (scheme, netloc, loc, _, _) = urlparse.urlsplit(url)
 
168
    path = urlparse.unquote(loc)
172
169
    if path.startswith("/~"):
173
170
        path = path[1:]
174
 
    return ((parsed_url.hostname or '', parsed_url.port, parsed_url.username, path))
 
171
    (username, hostport) = splituser(netloc)
 
172
    (host, port) = splitnport(hostport, None)
 
173
    return (host, port, username, path)
175
174
 
176
175
 
177
176
class RemoteGitError(BzrError):
202
201
                message.endswith(' not found.'))):
203
202
        return NotBranchError(url, message)
204
203
    if message == "HEAD failed to update":
205
 
        base_url = urlutils.strip_segment_parameters(url)
 
204
        base_url, _ = urlutils.split_segment_parameters(url)
206
205
        return HeadUpdateFailed(base_url)
207
206
    if message.startswith('access denied or repository not exported:'):
208
 
        extra, path = message.split(':', 1)
209
 
        return PermissionDenied(path.strip(), extra)
 
207
        extra, path = message.split(': ', 1)
 
208
        return PermissionDenied(path, extra)
210
209
    if message.endswith('You are not allowed to push code to this project.'):
211
210
        return PermissionDenied(url, message)
212
211
    if message.endswith(' does not appear to be a git repository'):
213
212
        return NotBranchError(url, message)
214
 
    if re.match('(.+) is not a valid repository name',
215
 
                message.splitlines()[0]):
216
 
        return NotBranchError(url, message)
217
213
    m = re.match(r'Permission to ([^ ]+) denied to ([^ ]+)\.', message)
218
214
    if m:
219
215
        return PermissionDenied(m.group(1), 'denied to %s' % m.group(2))
453
449
        else:
454
450
            pb = None
455
451
 
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)
 
452
        def get_changed_refs_wrapper(refs):
 
453
            # TODO(jelmer): This drops symref information
 
454
            self._refs = remote_refs_dict_to_container(refs)
 
455
            return get_changed_refs(refs)
460
456
        try:
461
457
            return self._client.send_pack(
462
458
                self._client_path, get_changed_refs_wrapper,
472
468
        refname = self._get_selected_ref(name, ref)
473
469
        if refname != b'HEAD' and refname in self.get_refs_container():
474
470
            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]
 
471
        if refname in self.get_refs_container():
 
472
            ref_chain, unused_sha = self.get_refs_container().follow(
 
473
                self._get_selected_ref(None))
 
474
            if ref_chain[0] == b'HEAD':
 
475
                refname = ref_chain[1]
479
476
        repo = self.open_repository()
480
477
        return RemoteGitBranch(self, repo, refname)
481
478
 
555
552
 
556
553
    def push_branch(self, source, revision_id=None, overwrite=False,
557
554
                    remember=False, create_prefix=False, lossy=False,
558
 
                    name=None, tag_selector=None):
 
555
                    name=None):
559
556
        """Push the source branch into this ControlDir."""
560
557
        if revision_id is None:
561
558
            # No revision supplied by the user, default to the branch
570
567
        push_result.branch_push_result = None
571
568
        repo = self.find_repository()
572
569
        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
570
        if isinstance(source, GitBranch) and lossy:
579
571
            raise errors.LossyPushToSameVCS(source.controldir, self)
580
572
        source_store = get_object_store(source.repository)
581
573
        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)
 
574
        def get_changed_refs(refs):
 
575
            self._refs = remote_refs_dict_to_container(refs)
585
576
            ret = {}
586
577
            # TODO(jelmer): Unpeel if necessary
587
578
            push_result.new_original_revid = revision_id
594
585
                    raise errors.NoRoundtrippingSupport(
595
586
                        source, self.open_branch(name=name, nascent_ok=True))
596
587
            if not overwrite:
597
 
                if remote_divergence(old_sha, new_sha, source_store):
 
588
                if remote_divergence(ret.get(refname), new_sha,
 
589
                                     source_store):
598
590
                    raise DivergedBranches(
599
591
                        source, self.open_branch(name, nascent_ok=True))
600
 
            ret[actual_refname] = new_sha
 
592
            ret[refname] = new_sha
601
593
            if fetch_tags:
602
594
                for tagname, revid in viewitems(source.tags.get_tag_dict()):
603
 
                    if tag_selector and not tag_selector(tagname):
604
 
                        continue
605
595
                    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
 
596
                        new_sha = source_store._lookup_revision_sha1(revid)
611
597
                    else:
612
598
                        try:
613
599
                            new_sha = repo.lookup_bzr_revision_id(revid)[0]
622
608
                generate_pack_data = source_store.generate_pack_data
623
609
            new_refs = self.send_pack(get_changed_refs, generate_pack_data)
624
610
        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)
 
611
            new_refs[refname])
 
612
        try:
 
613
            old_remote = self._refs[refname]
 
614
        except KeyError:
 
615
            old_remote = ZERO_SHA
 
616
        push_result.old_revid = repo.lookup_foreign_revision_id(old_remote)
 
617
        self._refs = remote_refs_dict_to_container(new_refs)
632
618
        push_result.target_branch = self.open_branch(name)
633
 
        if old_sha is not None:
 
619
        if old_remote != ZERO_SHA:
634
620
            push_result.branch_push_result = GitBranchPushResult()
635
621
            push_result.branch_push_result.source_branch = source
636
622
            push_result.branch_push_result.target_branch = (
666
652
 
667
653
    def _idx_load_or_generate(self, path):
668
654
        if not os.path.exists(path):
669
 
            with ui.ui_factory.nested_progress_bar() as pb:
 
655
            pb = ui.ui_factory.nested_progress_bar()
 
656
            try:
670
657
                def report_progress(cur, total):
671
658
                    pb.update("generating index", cur, total)
672
 
                self.data.create_index(path, progress=report_progress)
 
659
                self.data.create_index(path,
 
660
                                       progress=report_progress)
 
661
            finally:
 
662
                pb.finished()
673
663
        return load_pack_index(path)
674
664
 
675
665
    def __del__(self):
688
678
        url = urlutils.URL.from_string(transport.external_url())
689
679
        url.user = url.quoted_user = None
690
680
        url.password = url.quoted_password = None
691
 
        url = urlutils.strip_segment_parameters(str(url))
692
 
        super(BzrGitHttpClient, self).__init__(url, *args, **kwargs)
 
681
        super(BzrGitHttpClient, self).__init__(str(url), *args, **kwargs)
693
682
 
694
683
    def _http_request(self, url, headers=None, data=None,
695
684
                      allow_compression=False):
704
693
            `redirect_location` properties, and `read` is a consumable read
705
694
            method for the response data.
706
695
        """
707
 
        if is_github_url(url):
708
 
            headers['User-agent'] = user_agent_for_github()
 
696
        headers['User-agent'] = user_agent_for_github()
709
697
        headers["Pragma"] = "no-cache"
710
698
        if allow_compression:
711
699
            headers["Accept-Encoding"] = "gzip"
714
702
 
715
703
        response = self.transport.request(
716
704
            ('GET' if data is None else 'POST'),
717
 
            url,
718
705
            body=data,
719
706
            headers=headers, retries=8)
720
707
 
722
709
            raise NotGitRepository()
723
710
        elif response.status != 200:
724
711
            raise GitProtocolError("unexpected http resp %d for %s" %
725
 
                                   (response.status, url))
 
712
                                   (response.code, url))
726
713
 
727
714
        # TODO: Optimization available by adding `preload_content=False` to the
728
715
        # request and just passing the `read` method on instead of going via
730
717
        # before issuing the next to still allow for connection reuse from the
731
718
        # pool.
732
719
        if response.getheader("Content-Encoding") == "gzip":
733
 
            read = gzip.GzipFile(fileobj=BytesIO(response.read())).read
 
720
            read = gzip.GzipFile(fileobj=response).read
734
721
        else:
735
722
            read = response.read
736
723
 
738
725
 
739
726
            def __init__(self, response):
740
727
                self._response = response
741
 
                self.status = response.status
 
728
                self.status = response.code
742
729
                self.content_type = response.getheader("Content-Type")
743
 
                self.redirect_location = response._actual.geturl()
 
730
                self.redirect_location = response.geturl()
744
731
 
745
732
            def readlines(self):
746
733
                return self._response.readlines()
747
734
 
748
735
            def close(self):
749
 
                pass
 
736
                self._response.close()
750
737
 
751
738
        return WrapResponse(response), read
752
739
 
753
740
 
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
741
class RemoteGitControlDirFormat(GitControlDirFormat):
760
742
    """The .git directory control format."""
761
743
 
782
764
        """Open this directory.
783
765
 
784
766
        """
785
 
        split_url = _git_url_and_path_from_transport(transport.external_url())
 
767
        # we dont grok readonly - git isn't integrated with transport.
 
768
        url = transport.base
 
769
        if url.startswith('readonly+'):
 
770
            url = url[len('readonly+'):]
 
771
        scheme = urlparse.urlsplit(transport.external_url())[0]
786
772
        if isinstance(transport, GitSmartTransport):
787
773
            client = transport._get_client()
788
 
        elif split_url.scheme in ("http", "https"):
 
774
            client_path = transport._get_path()
 
775
        elif scheme in ("http", "https"):
789
776
            client = BzrGitHttpClient(transport)
790
 
        elif split_url.scheme in ('file', ):
 
777
            client_path, _ = urlutils.split_segment_parameters(transport._path)
 
778
        elif scheme == 'file':
791
779
            client = dulwich.client.LocalGitClient()
 
780
            client_path = transport.local_abspath('.')
792
781
        else:
793
782
            raise NotBranchError(transport.base)
794
783
        if not _found:
795
784
            pass  # TODO(jelmer): Actually probe for something
796
 
        return RemoteGitDir(transport, self, client, split_url.path)
 
785
        return RemoteGitDir(transport, self, client, client_path)
797
786
 
798
787
    def get_format_description(self):
799
788
        return "Remote Git Repository"
853
842
    def get_file_text(self, path):
854
843
        raise GitSmartRemoteNotSupported(self.get_file_text, self)
855
844
 
856
 
    def list_files(self, include_root=False, from_dir=None, recursive=True):
857
 
        raise GitSmartRemoteNotSupported(self.list_files, self)
858
 
 
859
845
 
860
846
class RemoteGitRepository(GitRepository):
861
847
 
1040
1026
    ret = DictRefsContainer(base)
1041
1027
    ret._peeled = peeled
1042
1028
    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)