/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: 2018-06-14 17:59:16 UTC
  • mto: This revision was merged to the branch mainline in revision 7065.
  • Revision ID: jelmer@jelmer.uk-20180614175916-a2e2xh5k533guq1x
Move breezy.plugins.git to breezy.git.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Remote dirs, repositories and branches."""
18
18
 
19
 
import gzip
 
19
from __future__ import absolute_import
 
20
 
20
21
from io import BytesIO
21
22
import re
22
23
 
43
44
    NoSuchTag,
44
45
    NotBranchError,
45
46
    NotLocalUrl,
46
 
    PermissionDenied,
 
47
    NoWorkingTree,
47
48
    UninitializableFormat,
48
49
    )
49
 
from ..revision import NULL_REVISION
50
50
from ..revisiontree import RevisionTree
51
51
from ..transport import (
52
52
    Transport,
55
55
 
56
56
from . import (
57
57
    lazy_check_versions,
58
 
    is_github_url,
59
58
    user_agent_for_github,
60
59
    )
61
60
lazy_check_versions()
70
69
from .dir import (
71
70
    GitControlDirFormat,
72
71
    GitDir,
 
72
    BareLocalGitControlDirFormat,
73
73
    )
74
74
from .errors import (
75
75
    GitSmartRemoteNotSupported,
76
76
    NoSuchRef,
77
77
    )
78
78
from .mapping import (
79
 
    encode_git_path,
80
79
    mapping_registry,
81
80
    )
82
81
from .object_store import (
87
86
    )
88
87
from .repository import (
89
88
    GitRepository,
90
 
    GitRepositoryFormat,
91
89
    )
92
90
from .refs import (
93
91
    branch_name_to_ref,
100
98
import dulwich.client
101
99
from dulwich.errors import (
102
100
    GitProtocolError,
103
 
    HangupException,
104
101
    )
105
102
from dulwich.pack import (
106
103
    Pack,
116
113
    )
117
114
import os
118
115
import select
 
116
import tempfile
 
117
import urllib
119
118
 
120
 
import urllib.parse as urlparse
121
 
from urllib.parse import splituser
 
119
try:
 
120
    import urllib.parse as urlparse
 
121
    from urllib.parse import splituser, splitnport
 
122
except ImportError:
 
123
    import urlparse
 
124
    from urllib import splituser, splitnport
122
125
 
123
126
# urlparse only supports a limited number of schemes by default
124
127
register_urlparse_netloc_protocol('git')
132
135
    def _lookup_revno(self, revid):
133
136
        try:
134
137
            return _quick_lookup_revno(self.source_branch, self.target_branch,
135
 
                                       revid)
 
138
                revid)
136
139
        except GitSmartRemoteNotSupported:
137
140
            return None
138
141
 
157
160
    :param url: Git URL
158
161
    :return: Tuple with host, port, username, path.
159
162
    """
160
 
    parsed_url = urlparse.urlparse(url)
161
 
    path = urlparse.unquote(parsed_url.path)
 
163
    (scheme, netloc, loc, _, _) = urlparse.urlsplit(url)
 
164
    path = urlparse.unquote(loc)
162
165
    if path.startswith("/~"):
163
166
        path = path[1:]
164
 
    return ((parsed_url.hostname or '', parsed_url.port, parsed_url.username, path))
 
167
    (username, hostport) = splituser(netloc)
 
168
    (host, port) = splitnport(hostport, None)
 
169
    return (host, port, username, path)
165
170
 
166
171
 
167
172
class RemoteGitError(BzrError):
169
174
    _fmt = "Remote server error: %(msg)s"
170
175
 
171
176
 
172
 
class HeadUpdateFailed(BzrError):
173
 
 
174
 
    _fmt = ("Unable to update remote HEAD branch. To update the master "
175
 
            "branch, specify the URL %(base_url)s,branch=master.")
176
 
 
177
 
    def __init__(self, base_url):
178
 
        super(HeadUpdateFailed, self).__init__()
179
 
        self.base_url = base_url
180
 
 
181
 
 
182
177
def parse_git_error(url, message):
183
178
    """Parse a remote git server error and return a bzr exception.
184
179
 
186
181
    :param message: Message sent by the remote git server
187
182
    """
188
183
    message = str(message).strip()
189
 
    if (message.startswith("Could not find Repository ")
190
 
        or message == 'Repository not found.'
191
 
            or (message.startswith('Repository ') and
192
 
                message.endswith(' not found.'))):
 
184
    if message.startswith("Could not find Repository "):
193
185
        return NotBranchError(url, message)
194
186
    if message == "HEAD failed to update":
195
 
        base_url = urlutils.strip_segment_parameters(url)
196
 
        return HeadUpdateFailed(base_url)
197
 
    if message.startswith('access denied or repository not exported:'):
198
 
        extra, path = message.split(':', 1)
199
 
        return PermissionDenied(path.strip(), extra)
200
 
    if message.endswith('You are not allowed to push code to this project.'):
201
 
        return PermissionDenied(url, message)
202
 
    if message.endswith(' does not appear to be a git repository'):
203
 
        return NotBranchError(url, message)
204
 
    if re.match('(.+) is not a valid repository name',
205
 
                message.splitlines()[0]):
206
 
        return NotBranchError(url, message)
207
 
    if message == (
208
 
            'GitLab: You are not allowed to push code to protected branches '
209
 
            'on this project.'):
210
 
        return PermissionDenied(url, message)
211
 
    m = re.match(r'Permission to ([^ ]+) denied to ([^ ]+)\.', message)
212
 
    if m:
213
 
        return PermissionDenied(m.group(1), 'denied to %s' % m.group(2))
 
187
        base_url, _ = urlutils.split_segment_parameters(url)
 
188
        raise BzrError(
 
189
            ("Unable to update remote HEAD branch. To update the master "
 
190
             "branch, specify the URL %s,branch=master.") % base_url)
214
191
    # Don't know, just return it to the user as-is
215
192
    return RemoteGitError(message)
216
193
 
217
194
 
218
 
def parse_git_hangup(url, e):
219
 
    """Parse the error lines from a git servers stderr on hangup.
220
 
 
221
 
    :param url: URL of the remote repository
222
 
    :param e: A HangupException
223
 
    """
224
 
    stderr_lines = getattr(e, 'stderr_lines', None)
225
 
    if not stderr_lines:
226
 
        return e
227
 
    if all(line.startswith(b'remote: ') for line in stderr_lines):
228
 
        stderr_lines = [
229
 
            line[len(b'remote: '):] for line in stderr_lines]
230
 
    interesting_lines = [
231
 
        line for line in stderr_lines
232
 
        if line and line.replace(b'=', b'')]
233
 
    if len(interesting_lines) == 1:
234
 
        interesting_line = interesting_lines[0]
235
 
        return parse_git_error(
236
 
            url, interesting_line.decode('utf-8', 'surrogateescape'))
237
 
    return RemoteGitError(
238
 
        b'\n'.join(stderr_lines).decode('utf-8', 'surrogateescape'))
239
 
 
240
 
 
241
195
class GitSmartTransport(Transport):
242
196
 
243
197
    def __init__(self, url, _client=None):
290
244
        if self._host == '':
291
245
            # return dulwich.client.LocalGitClient()
292
246
            return dulwich.client.SubprocessGitClient()
293
 
        return dulwich.client.TCPGitClient(
294
 
            self._host, self._port, report_activity=self._report_activity)
 
247
        return dulwich.client.TCPGitClient(self._host, self._port,
 
248
            report_activity=self._report_activity)
295
249
 
296
250
 
297
251
class SSHSocketWrapper(object):
316
270
        self.bzr_ssh_vendor = ssh._get_ssh_vendor()
317
271
 
318
272
    def run_command(self, host, command, username=None, port=None):
319
 
        connection = self.bzr_ssh_vendor.connect_ssh(
320
 
            username=username, password=None, port=port, host=host,
321
 
            command=command)
 
273
        connection = self.bzr_ssh_vendor.connect_ssh(username=username,
 
274
            password=None, port=port, host=host, command=command)
322
275
        (kind, io_object) = connection.get_sock_or_pipes()
323
276
        if kind == 'socket':
324
277
            return SSHSocketWrapper(io_object)
326
279
            raise AssertionError("Unknown io object kind %r'" % kind)
327
280
 
328
281
 
329
 
# dulwich.client.get_ssh_vendor = DulwichSSHVendor
 
282
#dulwich.client.get_ssh_vendor = DulwichSSHVendor
330
283
 
331
284
 
332
285
class SSHGitSmartTransport(GitSmartTransport):
345
298
            self._client = None
346
299
            return ret
347
300
        location_config = config.LocationConfig(self.base)
348
 
        client = dulwich.client.SSHGitClient(
349
 
            self._host, self._port, self._username,
 
301
        client = dulwich.client.SSHGitClient(self._host, self._port, self._username,
350
302
            report_activity=self._report_activity)
351
303
        # Set up alternate pack program paths
352
304
        upload_pack = location_config.get_user_option('git_upload_pack')
381
333
        self.pb = pb
382
334
 
383
335
    def progress(self, text):
384
 
        text = text.rstrip(b"\r\n")
385
 
        text = text.decode('utf-8')
386
 
        if text.lower().startswith('error: '):
387
 
            trace.show_error('git: %s', text[len(b'error: '):])
 
336
        text = text.rstrip("\r\n")
 
337
        if text.startswith('error: '):
 
338
            trace.show_error('git: %s', text[len('error: '):])
388
339
        else:
389
340
            trace.mutter("git: %s", text)
390
341
            g = self._GIT_PROGRESS_PARTIAL_RE.match(text)
416
367
    def _gitrepository_class(self):
417
368
        return RemoteGitRepository
418
369
 
419
 
    def archive(self, format, committish, write_data, progress=None,
420
 
                write_error=None, subdirs=None, prefix=None):
 
370
    def archive(self, format, committish, write_data, progress=None, write_error=None,
 
371
                subdirs=None, prefix=None):
 
372
        if format not in ('tar', 'zip'):
 
373
            raise errors.NoSuchExportFormat(format)
421
374
        if progress is None:
422
375
            pb = ui.ui_factory.nested_progress_bar()
423
376
            progress = DefaultProgressReporter(pb).progress
424
377
        else:
425
378
            pb = None
426
 
        def progress_wrapper(message):
427
 
            if message.startswith(b"fatal: Unknown archive format \'"):
428
 
                format = message.strip()[len(b"fatal: Unknown archive format '"):-1]
429
 
                raise errors.NoSuchExportFormat(format.decode('ascii'))
430
 
            return progress(message)
431
379
        try:
432
 
            self._client.archive(
433
 
                self._client_path, committish, write_data, progress_wrapper,
434
 
                write_error,
435
 
                format=(format.encode('ascii') if format else None),
436
 
                subdirs=subdirs,
437
 
                prefix=(encode_git_path(prefix) if prefix else None))
438
 
        except HangupException as e:
439
 
            raise parse_git_hangup(self.transport.external_url(), e)
 
380
            self._client.archive(self._client_path, committish,
 
381
                write_data, progress, write_error, format=format,
 
382
                subdirs=subdirs, prefix=prefix)
440
383
        except GitProtocolError as e:
441
384
            raise parse_git_error(self.transport.external_url(), e)
442
385
        finally:
443
386
            if pb is not None:
444
387
                pb.finished()
445
388
 
446
 
    def fetch_pack(self, determine_wants, graph_walker, pack_data,
447
 
                   progress=None):
 
389
    def fetch_pack(self, determine_wants, graph_walker, pack_data, progress=None):
448
390
        if progress is None:
449
391
            pb = ui.ui_factory.nested_progress_bar()
450
392
            progress = DefaultProgressReporter(pb).progress
451
393
        else:
452
394
            pb = None
453
395
        try:
454
 
            result = self._client.fetch_pack(
455
 
                self._client_path, determine_wants, graph_walker, pack_data,
456
 
                progress)
 
396
            result = self._client.fetch_pack(self._client_path, determine_wants,
 
397
                graph_walker, pack_data, progress)
457
398
            if result.refs is None:
458
399
                result.refs = {}
459
 
            self._refs = remote_refs_dict_to_container(
460
 
                result.refs, result.symrefs)
 
400
            self._refs = remote_refs_dict_to_container(result.refs, result.symrefs)
461
401
            return result
462
 
        except HangupException as e:
463
 
            raise parse_git_hangup(self.transport.external_url(), e)
464
402
        except GitProtocolError as e:
465
403
            raise parse_git_error(self.transport.external_url(), e)
466
404
        finally:
473
411
            progress = DefaultProgressReporter(pb).progress
474
412
        else:
475
413
            pb = None
476
 
 
477
 
        def get_changed_refs_wrapper(remote_refs):
478
 
            if self._refs is not None:
479
 
                update_refs_container(self._refs, remote_refs)
480
 
            return get_changed_refs(remote_refs)
 
414
        def get_changed_refs_wrapper(refs):
 
415
            # TODO(jelmer): This drops symref information
 
416
            self._refs = remote_refs_dict_to_container(refs)
 
417
            return get_changed_refs(refs)
481
418
        try:
482
 
            return self._client.send_pack(
483
 
                self._client_path, get_changed_refs_wrapper,
484
 
                generate_pack_data, progress)
485
 
        except HangupException as e:
486
 
            raise parse_git_hangup(self.transport.external_url(), e)
 
419
            return self._client.send_pack(self._client_path,
 
420
                    get_changed_refs_wrapper, generate_pack_data, progress)
487
421
        except GitProtocolError as e:
488
422
            raise parse_git_error(self.transport.external_url(), e)
489
423
        finally:
495
429
        refname = self._get_selected_ref(name, ref)
496
430
        if refname != b'HEAD' and refname in self.get_refs_container():
497
431
            raise AlreadyBranchError(self.user_url)
498
 
        ref_chain, unused_sha = self.get_refs_container().follow(
499
 
            self._get_selected_ref(name))
500
 
        if ref_chain and ref_chain[0] == b'HEAD':
501
 
            refname = ref_chain[1]
 
432
        if refname in self.get_refs_container():
 
433
            ref_chain, unused_sha = self.get_refs_container().follow(self._get_selected_ref(None))
 
434
            if ref_chain[0] == b'HEAD':
 
435
                refname = ref_chain[1]
502
436
        repo = self.open_repository()
503
437
        return RemoteGitBranch(self, repo, refname)
504
438
 
505
439
    def destroy_branch(self, name=None):
506
440
        refname = self._get_selected_ref(name)
507
 
 
508
441
        def get_changed_refs(old_refs):
509
 
            ret = {}
510
 
            if refname not in old_refs:
 
442
            ret = dict(old_refs)
 
443
            if not refname in ret:
511
444
                raise NotBranchError(self.user_url)
512
445
            ret[refname] = dulwich.client.ZERO_SHA
513
446
            return ret
514
 
 
515
447
        def generate_pack_data(have, want, ofs_delta=False):
516
448
            return pack_objects_to_data([])
517
 
        result = self.send_pack(get_changed_refs, generate_pack_data)
518
 
        if result is not None and not isinstance(result, dict):
519
 
            error = result.ref_status.get(refname)
520
 
            if error:
521
 
                raise RemoteGitError(error)
 
449
        self.send_pack(get_changed_refs, generate_pack_data)
522
450
 
523
451
    @property
524
452
    def user_url(self):
539
467
    def open_repository(self):
540
468
        return RemoteGitRepository(self)
541
469
 
542
 
    def get_branch_reference(self, name=None):
543
 
        ref = branch_name_to_ref(name)
544
 
        val = self.get_refs_container().read_ref(ref)
545
 
        if val.startswith(SYMREF):
546
 
            return val[len(SYMREF):]
547
 
        return None
548
 
 
549
470
    def open_branch(self, name=None, unsupported=False,
550
 
                    ignore_fallbacks=False, ref=None, possible_transports=None,
551
 
                    nascent_ok=False):
 
471
            ignore_fallbacks=False, ref=None, possible_transports=None,
 
472
            nascent_ok=False):
552
473
        repo = self.open_repository()
553
474
        ref = self._get_selected_ref(name, ref)
554
 
        try:
555
 
            if not nascent_ok and ref not in self.get_refs_container():
556
 
                raise NotBranchError(
557
 
                    self.root_transport.base, controldir=self)
558
 
        except NotGitRepository:
 
475
        if not nascent_ok and ref not in self.get_refs_container():
559
476
            raise NotBranchError(self.root_transport.base,
560
 
                                 controldir=self)
 
477
                    controldir=self)
561
478
        ref_chain, unused_sha = self.get_refs_container().follow(ref)
562
479
        return RemoteGitBranch(self, repo, ref_chain[-1])
563
480
 
574
491
        if self._refs is not None:
575
492
            return self._refs
576
493
        result = self.fetch_pack(lambda x: None, None,
577
 
                                 lambda x: None,
578
 
                                 lambda x: trace.mutter("git: %s" % x))
 
494
            lambda x: None, lambda x: trace.mutter("git: %s" % x))
579
495
        self._refs = remote_refs_dict_to_container(
580
 
            result.refs, result.symrefs)
 
496
                result.refs, result.symrefs)
581
497
        return self._refs
582
498
 
583
499
    def push_branch(self, source, revision_id=None, overwrite=False,
584
500
                    remember=False, create_prefix=False, lossy=False,
585
 
                    name=None, tag_selector=None):
 
501
                    name=None):
586
502
        """Push the source branch into this ControlDir."""
587
503
        if revision_id is None:
588
504
            # No revision supplied by the user, default to the branch
589
505
            # revision
590
506
            revision_id = source.last_revision()
591
 
        else:
592
 
            if not source.repository.has_revision(revision_id):
593
 
                raise NoSuchRevision(source, revision_id)
594
507
 
595
508
        push_result = GitPushResult()
596
509
        push_result.workingtree_updated = None
600
513
        push_result.branch_push_result = None
601
514
        repo = self.find_repository()
602
515
        refname = self._get_selected_ref(name)
603
 
        ref_chain, old_sha = self.get_refs_container().follow(refname)
604
 
        if ref_chain:
605
 
            actual_refname = ref_chain[-1]
606
 
        else:
607
 
            actual_refname = refname
608
516
        if isinstance(source, GitBranch) and lossy:
609
517
            raise errors.LossyPushToSameVCS(source.controldir, self)
610
518
        source_store = get_object_store(source.repository)
611
 
        fetch_tags = source.get_config_stack().get('branch.fetch_tags')
612
 
        def get_changed_refs(remote_refs):
613
 
            if self._refs is not None:
614
 
                update_refs_container(self._refs, remote_refs)
615
 
            ret = {}
616
 
            # TODO(jelmer): Unpeel if necessary
617
 
            push_result.new_original_revid = revision_id
618
 
            if lossy:
619
 
                new_sha = source_store._lookup_revision_sha1(revision_id)
620
 
            else:
621
 
                try:
622
 
                    new_sha = repo.lookup_bzr_revision_id(revision_id)[0]
623
 
                except errors.NoSuchRevision:
624
 
                    raise errors.NoRoundtrippingSupport(
625
 
                        source, self.open_branch(name=name, nascent_ok=True))
626
 
            if not overwrite:
627
 
                if remote_divergence(old_sha, new_sha, source_store):
628
 
                    raise DivergedBranches(
629
 
                        source, self.open_branch(name, nascent_ok=True))
630
 
            ret[actual_refname] = new_sha
631
 
            if fetch_tags:
632
 
                for tagname, revid in source.tags.get_tag_dict().items():
633
 
                    if tag_selector and not tag_selector(tagname):
634
 
                        continue
635
 
                    if lossy:
636
 
                        try:
637
 
                            new_sha = source_store._lookup_revision_sha1(revid)
638
 
                        except KeyError:
639
 
                            if source.repository.has_revision(revid):
640
 
                                raise
641
 
                    else:
642
 
                        try:
643
 
                            new_sha = repo.lookup_bzr_revision_id(revid)[0]
644
 
                        except errors.NoSuchRevision:
645
 
                            continue
646
 
                        else:
647
 
                            if not source.repository.has_revision(revid):
648
 
                                continue
649
 
                    ret[tag_name_to_ref(tagname)] = new_sha
650
 
            return ret
651
519
        with source_store.lock_read():
652
 
            def generate_pack_data(have, want, progress=None,
653
 
                                   ofs_delta=True):
654
 
                git_repo = getattr(source.repository, '_git', None)
655
 
                if git_repo:
656
 
                    shallow = git_repo.get_shallow()
657
 
                else:
658
 
                    shallow = None
 
520
            def get_changed_refs(refs):
 
521
                self._refs = remote_refs_dict_to_container(refs)
 
522
                ret = dict(refs)
 
523
                # TODO(jelmer): Unpeel if necessary
 
524
                push_result.new_original_revid = revision_id
659
525
                if lossy:
660
 
                    return source_store.generate_lossy_pack_data(
661
 
                        have, want, shallow=shallow,
662
 
                        progress=progress, ofs_delta=ofs_delta)
663
 
                elif shallow:
664
 
                    return source_store.generate_pack_data(
665
 
                        have, want, shallow=shallow,
666
 
                        progress=progress, ofs_delta=ofs_delta)
 
526
                    new_sha = source_store._lookup_revision_sha1(revision_id)
667
527
                else:
668
 
                    return source_store.generate_pack_data(
669
 
                        have, want, progress=progress, ofs_delta=ofs_delta)
670
 
            dw_result = self.send_pack(get_changed_refs, generate_pack_data)
671
 
            if not isinstance(dw_result, dict):
672
 
                new_refs = dw_result.refs
673
 
                error = dw_result.ref_status.get(actual_refname)
674
 
                if error:
675
 
                    raise RemoteGitError(error)
676
 
                for ref, error in dw_result.ref_status.items():
677
 
                    if error:
678
 
                        trace.warning('unable to open ref %s: %s',
679
 
                                      ref, error)
680
 
            else:  # dulwich < 0.20.4
681
 
                new_refs = dw_result
 
528
                    new_sha = repo.lookup_bzr_revision_id(revision_id)[0]
 
529
                if not overwrite:
 
530
                    if remote_divergence(ret.get(refname), new_sha, source_store):
 
531
                        raise DivergedBranches(
 
532
                                source, self.open_branch(name, nascent_ok=True))
 
533
                ret[refname] = new_sha
 
534
                return ret
 
535
            if lossy:
 
536
                generate_pack_data = source_store.generate_lossy_pack_data
 
537
            else:
 
538
                generate_pack_data = source_store.generate_pack_data
 
539
            new_refs = self.send_pack(get_changed_refs, generate_pack_data)
682
540
        push_result.new_revid = repo.lookup_foreign_revision_id(
683
 
            new_refs[actual_refname])
684
 
        if old_sha is not None:
685
 
            push_result.old_revid = repo.lookup_foreign_revision_id(old_sha)
686
 
        else:
687
 
            push_result.old_revid = NULL_REVISION
688
 
        if self._refs is not None:
689
 
            update_refs_container(self._refs, new_refs)
 
541
                new_refs[refname])
 
542
        try:
 
543
            old_remote = self._refs[refname]
 
544
        except KeyError:
 
545
            old_remote = ZERO_SHA
 
546
        push_result.old_revid = repo.lookup_foreign_revision_id(old_remote)
 
547
        self._refs = remote_refs_dict_to_container(new_refs)
690
548
        push_result.target_branch = self.open_branch(name)
691
 
        if old_sha is not None:
 
549
        if old_remote != ZERO_SHA:
692
550
            push_result.branch_push_result = GitBranchPushResult()
693
551
            push_result.branch_push_result.source_branch = source
694
 
            push_result.branch_push_result.target_branch = (
695
 
                push_result.target_branch)
 
552
            push_result.branch_push_result.target_branch = push_result.target_branch
696
553
            push_result.branch_push_result.local_branch = None
697
 
            push_result.branch_push_result.master_branch = (
698
 
                push_result.target_branch)
 
554
            push_result.branch_push_result.master_branch = push_result.target_branch
699
555
            push_result.branch_push_result.old_revid = push_result.old_revid
700
556
            push_result.branch_push_result.new_revid = push_result.new_revid
701
 
            push_result.branch_push_result.new_original_revid = (
702
 
                push_result.new_original_revid)
 
557
            push_result.branch_push_result.new_original_revid = push_result.new_original_revid
703
558
        if source.get_push_location() is None or remember:
704
559
            source.set_push_location(push_result.target_branch.base)
705
560
        return push_result
724
579
 
725
580
    def _idx_load_or_generate(self, path):
726
581
        if not os.path.exists(path):
727
 
            with ui.ui_factory.nested_progress_bar() as pb:
 
582
            pb = ui.ui_factory.nested_progress_bar()
 
583
            try:
728
584
                def report_progress(cur, total):
729
585
                    pb.update("generating index", cur, total)
730
 
                self.data.create_index(path, progress=report_progress)
 
586
                self.data.create_index(path,
 
587
                    progress=report_progress)
 
588
            finally:
 
589
                pb.finished()
731
590
        return load_pack_index(path)
732
591
 
733
592
    def __del__(self):
743
602
 
744
603
    def __init__(self, transport, *args, **kwargs):
745
604
        self.transport = transport
746
 
        url = urlutils.URL.from_string(transport.external_url())
747
 
        url.user = url.quoted_user = None
748
 
        url.password = url.quoted_password = None
749
 
        url = urlutils.strip_segment_parameters(str(url))
750
 
        super(BzrGitHttpClient, self).__init__(url, *args, **kwargs)
 
605
        super(BzrGitHttpClient, self).__init__(transport.external_url(), *args, **kwargs)
751
606
 
752
607
    def _http_request(self, url, headers=None, data=None,
753
608
                      allow_compression=False):
762
617
            `redirect_location` properties, and `read` is a consumable read
763
618
            method for the response data.
764
619
        """
765
 
        if is_github_url(url):
766
 
            headers['User-agent'] = user_agent_for_github()
 
620
        from breezy.transport.http._urllib2_wrappers import Request
 
621
        headers['User-agent'] = user_agent_for_github()
767
622
        headers["Pragma"] = "no-cache"
768
623
        if allow_compression:
769
624
            headers["Accept-Encoding"] = "gzip"
770
625
        else:
771
626
            headers["Accept-Encoding"] = "identity"
772
627
 
773
 
        response = self.transport.request(
 
628
        request = Request(
774
629
            ('GET' if data is None else 'POST'),
775
 
            url,
776
 
            body=data,
777
 
            headers=headers, retries=8)
778
 
 
779
 
        if response.status == 404:
 
630
            url, data, headers,
 
631
            accepted_errors=[200, 404])
 
632
 
 
633
        response = self.transport._perform(request)
 
634
 
 
635
        if response.code == 404:
780
636
            raise NotGitRepository()
781
 
        elif response.status != 200:
 
637
        elif response.code != 200:
782
638
            raise GitProtocolError("unexpected http resp %d for %s" %
783
 
                                   (response.status, url))
 
639
                                   (response.code, url))
784
640
 
785
641
        # TODO: Optimization available by adding `preload_content=False` to the
786
642
        # request and just passing the `read` method on instead of going via
788
644
        # before issuing the next to still allow for connection reuse from the
789
645
        # pool.
790
646
        if response.getheader("Content-Encoding") == "gzip":
791
 
            read = gzip.GzipFile(fileobj=BytesIO(response.read())).read
 
647
            read = gzip.GzipFile(fileobj=response).read
792
648
        else:
793
649
            read = response.read
794
650
 
796
652
 
797
653
            def __init__(self, response):
798
654
                self._response = response
799
 
                self.status = response.status
 
655
                self.status = response.code
800
656
                self.content_type = response.getheader("Content-Type")
801
 
                self.redirect_location = response._actual.geturl()
802
 
 
803
 
            def readlines(self):
804
 
                return self._response.readlines()
 
657
                self.redirect_location = response.geturl()
805
658
 
806
659
            def close(self):
807
 
                pass
 
660
                self._response.close()
808
661
 
809
662
        return WrapResponse(response), read
810
663
 
811
664
 
812
 
def _git_url_and_path_from_transport(external_url):
813
 
    url = urlutils.strip_segment_parameters(external_url)
814
 
    return urlparse.urlsplit(url)
815
 
 
816
 
 
817
665
class RemoteGitControlDirFormat(GitControlDirFormat):
818
666
    """The .git directory control format."""
819
667
 
826
674
    def get_branch_format(self):
827
675
        return RemoteGitBranchFormat()
828
676
 
829
 
    @property
830
 
    def repository_format(self):
831
 
        return GitRepositoryFormat()
832
 
 
833
677
    def is_initializable(self):
834
678
        return False
835
679
 
840
684
        """Open this directory.
841
685
 
842
686
        """
843
 
        split_url = _git_url_and_path_from_transport(transport.external_url())
 
687
        # we dont grok readonly - git isn't integrated with transport.
 
688
        url = transport.base
 
689
        if url.startswith('readonly+'):
 
690
            url = url[len('readonly+'):]
 
691
        scheme = urlparse.urlsplit(transport.external_url())[0]
844
692
        if isinstance(transport, GitSmartTransport):
845
693
            client = transport._get_client()
846
 
        elif split_url.scheme in ("http", "https"):
 
694
            client_path = transport._get_path()
 
695
        elif scheme in ("http", "https"):
847
696
            client = BzrGitHttpClient(transport)
848
 
        elif split_url.scheme in ('file', ):
 
697
            client_path, _ = urlutils.split_segment_parameters(transport._path)
 
698
        elif scheme == 'file':
849
699
            client = dulwich.client.LocalGitClient()
 
700
            client_path = transport.local_abspath('.')
850
701
        else:
851
702
            raise NotBranchError(transport.base)
852
703
        if not _found:
853
 
            pass  # TODO(jelmer): Actually probe for something
854
 
        return RemoteGitDir(transport, self, client, split_url.path)
 
704
            pass # TODO(jelmer): Actually probe for something
 
705
        return RemoteGitDir(transport, self, client, client_path)
855
706
 
856
707
    def get_format_description(self):
857
708
        return "Remote Git Repository"
864
715
            external_url = transport.external_url()
865
716
        except InProcessTransport:
866
717
            raise NotBranchError(path=transport.base)
867
 
        return (external_url.startswith("http:")
868
 
                or external_url.startswith("https:")
869
 
                or external_url.startswith("git+")
870
 
                or external_url.startswith("git:"))
 
718
        return (external_url.startswith("http:") or
 
719
                external_url.startswith("https:") or
 
720
                external_url.startswith("git+") or
 
721
                external_url.startswith("git:"))
871
722
 
872
723
 
873
724
class GitRemoteRevisionTree(RevisionTree):
883
734
        """
884
735
        commit = self._repository.lookup_bzr_revision_id(
885
736
            self.get_revision_id())[0]
886
 
        import tempfile
887
737
        f = tempfile.SpooledTemporaryFile()
888
738
        # git-upload-archive(1) generaly only supports refs. So let's see if we
889
739
        # can find one.
890
740
        reverse_refs = {
891
 
            v: k for (k, v) in
892
 
            self._repository.controldir.get_refs_container().as_dict().items()}
 
741
                v: k for (k, v) in
 
742
                self._repository.controldir.get_refs_container().as_dict().items()}
893
743
        try:
894
744
            committish = reverse_refs[commit]
895
745
        except KeyError:
897
747
            # Let's hope for the best.
898
748
            committish = commit
899
749
        self._repository.archive(
900
 
            format, committish, f.write,
901
 
            subdirs=([subdir] if subdir else None),
902
 
            prefix=(root + '/') if root else '')
 
750
                format, committish, f.write,
 
751
                subdirs=([subdir] if subdir else None),
 
752
                prefix=(root+'/') if root else '')
903
753
        f.seek(0)
904
754
        return osutils.file_iterator(f)
905
755
 
906
 
    def is_versioned(self, path):
907
 
        raise GitSmartRemoteNotSupported(self.is_versioned, self)
908
 
 
909
 
    def has_filename(self, path):
910
 
        raise GitSmartRemoteNotSupported(self.has_filename, self)
911
 
 
912
 
    def get_file_text(self, path):
913
 
        raise GitSmartRemoteNotSupported(self.get_file_text, self)
914
 
 
915
 
    def list_files(self, include_root=False, from_dir=None, recursive=True):
916
 
        raise GitSmartRemoteNotSupported(self.list_files, self)
917
 
 
918
756
 
919
757
class RemoteGitRepository(GitRepository):
920
758
 
921
 
    supports_random_access = False
922
 
 
923
759
    @property
924
760
    def user_url(self):
925
761
        return self.control_url
932
768
 
933
769
    def fetch_pack(self, determine_wants, graph_walker, pack_data,
934
770
                   progress=None):
935
 
        return self.controldir.fetch_pack(
936
 
            determine_wants, graph_walker, pack_data, progress)
 
771
        return self.controldir.fetch_pack(determine_wants, graph_walker,
 
772
                                          pack_data, progress)
937
773
 
938
774
    def send_pack(self, get_changed_refs, generate_pack_data):
939
775
        return self.controldir.send_pack(get_changed_refs, generate_pack_data)
940
776
 
941
777
    def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
942
778
                      progress=None):
943
 
        import tempfile
944
779
        fd, path = tempfile.mkstemp(suffix=".pack")
945
780
        try:
946
781
            self.fetch_pack(determine_wants, graph_walker,
947
 
                            lambda x: os.write(fd, x), progress)
 
782
                lambda x: os.write(fd, x), progress)
948
783
        finally:
949
784
            os.close(fd)
950
785
        if os.path.getsize(path) == 0:
952
787
        return TemporaryPackIterator(path[:-len(".pack")], resolve_ext_ref)
953
788
 
954
789
    def lookup_bzr_revision_id(self, bzr_revid, mapping=None):
955
 
        # This won't work for any round-tripped bzr revisions, but it's a
956
 
        # start..
 
790
        # This won't work for any round-tripped bzr revisions, but it's a start..
957
791
        try:
958
792
            return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
959
793
        except InvalidRevisionId:
989
823
 
990
824
    def _set_ref(self, name, sha):
991
825
        ref = tag_name_to_ref(name)
992
 
 
993
826
        def get_changed_refs(old_refs):
994
 
            ret = {}
995
 
            if sha == dulwich.client.ZERO_SHA and ref not in old_refs:
 
827
            ret = dict(old_refs)
 
828
            if sha == dulwich.client.ZERO_SHA and ref not in ret:
996
829
                raise NoSuchTag(name)
997
830
            ret[ref] = sha
998
831
            return ret
999
 
 
1000
832
        def generate_pack_data(have, want, ofs_delta=False):
1001
833
            return pack_objects_to_data([])
1002
 
        result = self.repository.send_pack(
1003
 
            get_changed_refs, generate_pack_data)
1004
 
        if result and not isinstance(result, dict):
1005
 
            error = result.ref_status.get(ref)
1006
 
            if error:
1007
 
                raise RemoteGitError(error)
 
834
        self.repository.send_pack(get_changed_refs, generate_pack_data)
1008
835
 
1009
836
 
1010
837
class RemoteGitBranch(GitBranch):
1012
839
    def __init__(self, controldir, repository, name):
1013
840
        self._sha = None
1014
841
        super(RemoteGitBranch, self).__init__(controldir, repository, name,
1015
 
                                              RemoteGitBranchFormat())
 
842
                RemoteGitBranchFormat())
1016
843
 
1017
844
    def last_revision_info(self):
1018
845
        raise GitSmartRemoteNotSupported(self.last_revision_info, self)
1045
872
 
1046
873
    def _synchronize_history(self, destination, revision_id):
1047
874
        """See Branch._synchronize_history()."""
1048
 
        if revision_id is None:
1049
 
            revision_id = self.last_revision()
1050
 
        destination.generate_revision_history(revision_id)
 
875
        destination.generate_revision_history(self.last_revision())
1051
876
 
1052
877
    def _get_parent_location(self):
1053
878
        return None
1072
897
                continue
1073
898
            peeled = refs.get_peeled(ref_name)
1074
899
            if peeled is None:
1075
 
                # Let's just hope it's a commit
1076
 
                peeled = unpeeled
1077
 
            if not isinstance(tag_name, str):
 
900
                try:
 
901
                    peeled = refs.peel_sha(unpeeled).id
 
902
                except KeyError:
 
903
                    # Let's just hope it's a commit
 
904
                    peeled = unpeeled
 
905
            if type(tag_name) is not unicode:
1078
906
                raise TypeError(tag_name)
1079
907
            yield (ref_name, tag_name, peeled, unpeeled)
1080
908
 
1081
 
    def set_last_revision_info(self, revno, revid):
1082
 
        self.generate_revision_history(revid)
1083
 
 
1084
 
    def generate_revision_history(self, revision_id, last_rev=None,
1085
 
                                  other_branch=None):
1086
 
        sha = self.lookup_bzr_revision_id(revision_id)[0]
1087
 
        def get_changed_refs(old_refs):
1088
 
            return {self.ref: sha}
1089
 
        def generate_pack_data(have, want, ofs_delta=False):
1090
 
            return pack_objects_to_data([])
1091
 
        result = self.repository.send_pack(
1092
 
            get_changed_refs, generate_pack_data)
1093
 
        if result is not None and not isinstance(result, dict):
1094
 
            error = result.ref_status.get(self.ref)
1095
 
            if error:
1096
 
                raise RemoteGitError(error)
1097
 
        self._sha = sha
1098
 
 
1099
909
 
1100
910
def remote_refs_dict_to_container(refs_dict, symrefs_dict={}):
1101
911
    base = {}
1105
915
            peeled[k[:-3]] = v
1106
916
        else:
1107
917
            base[k] = v
 
918
            peeled[k] = v
1108
919
    for name, target in symrefs_dict.items():
1109
920
        base[name] = SYMREF + target
1110
921
    ret = DictRefsContainer(base)
1111
922
    ret._peeled = peeled
1112
923
    return ret
1113
 
 
1114
 
 
1115
 
def update_refs_container(container, refs_dict):
1116
 
    peeled = {}
1117
 
    base = {}
1118
 
    for k, v in refs_dict.items():
1119
 
        if is_peeled(k):
1120
 
            peeled[k[:-3]] = v
1121
 
        else:
1122
 
            base[k] = v
1123
 
    container._peeled = peeled
1124
 
    container._refs.update(base)