Beispiel #1
0
def getbundle(repo, store, heads, branch_names):
    if isinstance(repo, bundlerepo):
        bundle = repo._unbundler
    else:
        common = findcommon(repo, store, store.heads(branch_names))
        logging.info('common: %s', common)
        bundle = None
        got_partial = False
        if not common:
            if not store._has_metadata and not store._graft:
                manifest = Git.config('cinnabar.clone')
                if manifest is None and repo.capable('cinnabarclone'):
                    manifest = repo._call('cinnabarclone')
                if manifest:
                    got_partial = do_cinnabarclone(repo, manifest, store)
                    if not got_partial:
                        if check_enabled('cinnabarclone'):
                            raise Exception('cinnabarclone failed.')
                        logging.warn('Falling back to normal clone.')
            if not got_partial and repo.capable('clonebundles'):
                bundle = get_clonebundle(repo)
                got_partial = bool(bundle)
                if not got_partial and check_enabled('clonebundles'):
                    raise Exception('clonebundles failed.')
        if bundle:
            bundle = unbundler(bundle)
            # Manual move semantics
            apply_bundle = BundleApplier(bundle)
            del bundle
            apply_bundle(store)
            if not changegroup:
                BundleHelper.close()
        if got_partial:
            # Eliminate the heads that we got from the clonebundle or
            # cinnabarclone.
            heads = [h for h in heads if not store.changeset_ref(h)]
            if not heads:
                return
            common = findcommon(repo, store, store.heads(branch_names))
            logging.info('common: %s', common)

        kwargs = {}
        if unbundle20 and repo.capable('bundle2'):
            bundle2caps = {
                'HG20': (),
                'changegroup': ('01', '02'),
            }
            kwargs['bundlecaps'] = set((
                'HG20', 'bundle2=%s' % urllib.quote(encodecaps(bundle2caps))))

        bundle = repo.getbundle('bundle', heads=[unhexlify(h) for h in heads],
                                common=[unhexlify(h) for h in common],
                                **kwargs)

        bundle = unbundler(bundle)

    # Manual move semantics
    apply_bundle = BundleApplier(bundle)
    del bundle
    apply_bundle(store)
Beispiel #2
0
    def create_hg_metadata(self, commit, parents):
        if check_enabled('bundle'):
            real_changeset = self.changeset(self.hg_changeset(commit))
        manifest, changeset_files = self.create_hg_manifest(commit, parents)
        commit_data = GitCommit(commit)

        if manifest.node == NULL_NODE_ID:
            manifest.node = manifest.sha1
            if check_enabled('bundle'):
                if real_changeset and (manifest.node !=
                                       real_changeset.manifest):
                    for path, created, real in sorted_merge(
                            manifest,
                            self.manifest(real_changeset.manifest),
                            key=lambda i: i.path,
                            non_key=lambda i: i):
                        if bytes(created) != bytes(real):
                            logging.error('%r != %r', bytes(created),
                                          bytes(real))
            self._pushed.add(manifest.node)
            self.store_manifest(manifest)
            self._manifest_git_tree[manifest.node] = commit_data.tree

        changeset = Changeset.from_git_commit(commit_data)
        changeset.parents = tuple(self.hg_changeset(p) for p in parents)
        changeset.manifest = manifest.node
        changeset.files = changeset_files

        if parents:
            parent_changeset = self.changeset(changeset.parent1)
            if parent_changeset.branch:
                changeset.branch = parent_changeset.branch

        if self._graft is True and parents and changeset.body[-1:] == b'\n':
            parent_commit = GitCommit(parents[0])
            if (parent_commit.body[-1:] == b'\n'
                    and parent_commit.body[-2] == parent_changeset.body[-1]):
                self._graft = 'true'

        if self._graft == 'true' and changeset.body[-1:] == b'\n':
            changeset.body = changeset.body[:-1]

        changeset.node = changeset.sha1
        self._pushed.add(changeset.node)
        self.store_changeset(changeset, commit_data)

        if check_enabled('bundle') and real_changeset:
            error = False
            for k in ('files', 'manifest'):
                if getattr(real_changeset, k, []) != getattr(changeset, k, []):
                    logging.error('(%s) %r != %r', k,
                                  getattr(real_changeset, k, None),
                                  getattr(changeset, k, None))
                    error = True
            if error:
                raise Exception('Changeset mismatch')
Beispiel #3
0
    def create_hg_metadata(self, commit, parents):
        if check_enabled('bundle'):
            real_changeset = self.changeset(self.hg_changeset(commit))
        manifest, changeset_files = self.create_hg_manifest(commit, parents)
        commit_data = GitCommit(commit)

        if manifest.node == NULL_NODE_ID:
            manifest.node = manifest.sha1
            if check_enabled('bundle'):
                if real_changeset and (
                        manifest.node != real_changeset.manifest):
                    for path, created, real in sorted_merge(
                            manifest._lines,
                            self.manifest(real_changeset.manifest)._lines,
                            key=lambda i: i.name, non_key=lambda i: i):
                        if str(created) != str(real):
                            logging.error('%r != %r', str(created), str(real))
            self._pushed.add(manifest.node)
            self.store_manifest(manifest)
            self._manifest_git_tree[manifest.node] = commit_data.tree

        changeset = Changeset.from_git_commit(commit_data)
        changeset.parents = tuple(self.hg_changeset(p) for p in parents)
        changeset.manifest = manifest.node
        changeset.files = changeset_files

        if parents:
            parent_changeset = self.changeset(changeset.parent1)
            if parent_changeset.branch:
                changeset.branch = parent_changeset.branch

        if self._graft is True and parents and changeset.body[-1] == '\n':
            parent_commit = GitCommit(parents[0])
            if (parent_commit.body[-1] == '\n' and
                    parent_commit.body[-2] == parent_changeset.body[-1]):
                self._graft = 'true'

        if self._graft == 'true' and changeset.body[-1] == '\n':
            changeset.body = changeset.body[:-1]

        changeset.node = changeset.sha1
        self._pushed.add(changeset.node)
        self.store_changeset(changeset, commit_data)

        if check_enabled('bundle') and real_changeset:
            error = False
            for k in ('files', 'manifest'):
                if getattr(real_changeset, k, []) != getattr(changeset, k, []):
                    logging.error('(%s) %r != %r', k,
                                  getattr(real_changeset, k, None),
                                  getattr(changeset, k, None))
                    error = True
            if error:
                raise Exception('Changeset mismatch')
Beispiel #4
0
def get_repo(remote):
    if not changegroup or experiment('wire'):
        if not changegroup and not check_enabled('no-mercurial'):
            logging.warning('Mercurial libraries not found. Falling back to '
                            'native access.')
        logging.warning(
            'Native access to mercurial repositories is experimental!')

        stream = HgRepoHelper.connect(remote.url)
        if stream:
            return bundlerepo(remote.url, stream)
        return HelperRepo(remote.url)

    if remote.parsed_url.scheme == 'file':
        path = remote.parsed_url.path
        if sys.platform == 'win32':
            # TODO: This probably needs more thought.
            path = path.lstrip('/')
        if not os.path.isdir(path):
            return bundlerepo(path)
    ui = get_ui()
    if changegroup and remote.parsed_url.scheme == 'file':
        repo = localpeer(ui, path)
    else:
        try:
            repo = hg.peer(ui, {}, remote.url)
        except (error.RepoError, urllib2.HTTPError, IOError):
            return bundlerepo(remote.url, url.open(ui, remote.url))

    assert repo.capable('getbundle')

    return repo
Beispiel #5
0
def iter_initialized(get_missing, iterable, init=None):
    previous = None
    always_check = check_enabled('nodeid')
    for instance in iterable:
        check = always_check
        if instance.delta_node != NULL_NODE_ID:
            if not previous or instance.delta_node != previous.node:
                previous = get_missing(instance.delta_node)
                check = True
            if init:
                instance = init(instance, previous)
            else:
                instance.init(previous)
        elif init:
            instance = init(instance)
        else:
            instance.init(())
        if check and instance.node != instance.sha1:
            raise Exception(
                'sha1 mismatch for node %s with parents %s %s and '
                'previous %s' %
                (instance.node, instance.parent1, instance.parent2,
                 instance.delta_node)
            )
        yield instance
        previous = instance
Beispiel #6
0
def _get_repo(remote):
    if not changegroup or experiment('wire'):
        if not changegroup and not check_enabled('no-mercurial'):
            logging.warning('Mercurial libraries not found. Falling back to '
                            'experimental native access.')

        stream = HgRepoHelper.connect(remote.url)
        if stream:
            return bundlerepo(remote.url, stream)
        return HelperRepo(remote.url)

    if remote.parsed_url.scheme == b'file':
        # Make file://c:/... paths work by taking the netloc
        path = remote.parsed_url.netloc + remote.parsed_url.path
        if sys.platform == 'win32':
            # TODO: This probably needs more thought.
            path = path.lstrip(b'/')
        if not os.path.isdir(path):
            return bundlerepo(path)
    ui = get_ui()
    if changegroup and remote.parsed_url.scheme == b'file':
        repo = localpeer(ui, path)
    else:
        try:
            repo = hg.peer(ui, {}, remote.url)
        except (error.RepoError, HTTPError, IOError):
            if remote.parsed_url.scheme in ('http', 'https'):
                return bundlerepo(remote.url, HTTPReader(remote.url))
            raise

    assert repo.capable(b'getbundle')

    return repo
Beispiel #7
0
def get_repo(remote):
    if not changegroup or experiment('wire'):
        if not changegroup and not check_enabled('no-mercurial'):
            logging.warning('Mercurial libraries not found. Falling back to '
                            'native access.')
        logging.warning(
            'Native access to mercurial repositories is experimental!')

        stream = HgRepoHelper.connect(remote.url)
        if stream:
            return bundlerepo(remote.url, stream)
        return HelperRepo(remote.url)

    if remote.parsed_url.scheme == 'file':
        # Make file://c:/... paths work by taking the netloc
        path = remote.parsed_url.netloc + remote.parsed_url.path
        if sys.platform == 'win32':
            # TODO: This probably needs more thought.
            path = path.lstrip('/')
        if not os.path.isdir(path):
            return bundlerepo(path)
    ui = get_ui()
    if changegroup and remote.parsed_url.scheme == 'file':
        repo = localpeer(ui, path)
    else:
        try:
            repo = hg.peer(ui, {}, remote.url)
        except (error.RepoError, urllib2.HTTPError, IOError):
            return bundlerepo(remote.url, HTTPReader(remote.url))

    assert repo.capable('getbundle')

    return repo
Beispiel #8
0
def get_clonebundle(repo):
    url = Git.config('cinnabar.clonebundle')
    if not url:
        try:
            if check_enabled('no-mercurial'):
                raise ImportError('Do not use mercurial')
            from mercurial.exchange import (
                parseclonebundlesmanifest,
                filterclonebundleentries,
            )
        except ImportError:
            return None

        bundles = repo._call('clonebundles')

        class dummy(object):
            pass

        fakerepo = dummy()
        fakerepo.requirements = set()
        fakerepo.supportedformats = set()
        fakerepo.ui = repo.ui

        entries = parseclonebundlesmanifest(fakerepo, bundles)
        if not entries:
            return None

        entries = filterclonebundleentries(fakerepo, entries)
        if not entries:
            return None

        url = entries[0].get('URL')

    if not url:
        return None

    sys.stderr.write('Getting clone bundle from %s\n' % url)

    return unbundle_fh(HTTPReader(url), url)
Beispiel #9
0
def iter_initialized(get_missing, iterable):
    previous = None
    always_check = check_enabled('nodeid')
    for instance in iterable:
        check = always_check
        if instance.delta_node != NULL_NODE_ID:
            if previous and instance.delta_node == previous.node:
                instance.init(previous)
            else:
                instance.init(get_missing(instance.delta_node))
                check = True
        else:
            instance.init(())
        if check and instance.node != instance.sha1:
            raise Exception(
                'sha1 mismatch for node %s with parents %s %s and '
                'previous %s' %
                (instance.node, instance.parent1, instance.parent2,
                 instance.delta_node)
            )
        yield instance
        previous = instance
Beispiel #10
0
def iter_initialized(get_missing, iterable, init=None):
    previous = None
    check = check_enabled('nodeid')
    for instance in iterable:
        if instance.delta_node != NULL_NODE_ID:
            if not previous or instance.delta_node != previous.node:
                previous = get_missing(instance.delta_node)
            if init:
                instance = init(instance, previous)
            else:
                instance.init(previous)
        elif init:
            instance = init(instance)
        else:
            instance.init(())
        if check and instance.node != instance.sha1:
            raise Exception(
                'sha1 mismatch for node %s with parents %s %s and '
                'previous %s' %
                (instance.node, instance.parent1, instance.parent2,
                 instance.delta_node)
            )
        yield instance
        previous = instance
Beispiel #11
0
def getbundle(repo, store, heads, branch_names):
    if isinstance(repo, bundlerepo):
        bundle = repo._unbundler
    else:
        common = findcommon(repo, store, store.heads(branch_names))
        logging.info('common: %s', common)
        bundle = None
        got_partial = False
        if not common:
            if not store._has_metadata:
                manifest = Git.config('cinnabar.clone', remote=repo.remote)
                limit_schemes = False
                if manifest is None and repo.capable(b'cinnabarclone'):
                    # If no cinnabar.clone config was given, but a
                    # cinnabar.clonebundle config was, act as if an empty
                    # cinnabar.clone config had been given, and proceed with
                    # the mercurial clonebundle.
                    if not Git.config('cinnabar.clonebundle',
                                      remote=repo.remote):
                        manifest = repo._call(b'cinnabarclone')
                        limit_schemes = True
                if manifest:
                    got_partial = do_cinnabarclone(repo, manifest, store,
                                                   limit_schemes)
                    if not got_partial:
                        if check_enabled('cinnabarclone'):
                            raise Exception('cinnabarclone failed.')
                        logging.warn('Falling back to normal clone.')
            if not got_partial and repo.capable(b'clonebundles'):
                bundle = get_clonebundle(repo)
                got_partial = bool(bundle)
                if not got_partial and check_enabled('clonebundles'):
                    raise Exception('clonebundles failed.')
        if bundle:
            bundle = unbundler(bundle)
            # Manual move semantics
            apply_bundle = BundleApplier(bundle)
            del bundle
            apply_bundle(store)
            if not changegroup:
                BundleHelper.close()
        if got_partial:
            # Eliminate the heads that we got from the clonebundle or
            # cinnabarclone.
            heads = [h for h in heads if not store.changeset_ref(h)]
            if not heads:
                return
            common = findcommon(repo, store, store.heads(branch_names))
            logging.info('common: %s', common)

        kwargs = {}
        if unbundle20 and repo.capable(b'bundle2'):
            bundle2caps = {
                b'HG20': (),
                b'changegroup': (b'01', b'02'),
            }
            kwargs['bundlecaps'] = set(
                (b'HG20', b'bundle2=%s' %
                 quote_from_bytes(encodecaps(bundle2caps)).encode('ascii')))

        bundle = repo.getbundle(b'bundle',
                                heads=[unhexlify(h) for h in heads],
                                common=[unhexlify(h) for h in common],
                                **kwargs)

        bundle = unbundler(bundle)

    # Manual move semantics
    apply_bundle = BundleApplier(bundle)
    del bundle
    apply_bundle(store)
Beispiel #12
0
from collections import (
    defaultdict,
    deque,
)
from .bundle import (
    create_bundle,
    encodecaps,
    decodecaps,
)
from .changegroup import (
    RawRevChunk01,
    RawRevChunk02,
)

try:
    if check_enabled('no-mercurial'):
        raise ImportError('Do not use mercurial')
    # Old versions of mercurial use an old version of socketutil that tries to
    # assign a local PROTOCOL_SSLv2, copying it from the ssl module, without
    # ever using it. It shouldn't hurt to set it here.
    if not hasattr(ssl, 'PROTOCOL_SSLv2'):
        ssl.PROTOCOL_SSLv2 = 0
    if not hasattr(ssl, 'PROTOCOL_SSLv3'):
        ssl.PROTOCOL_SSLv3 = 1

    from mercurial import (
        changegroup,
        error,
        hg,
        ui,
        url,
Beispiel #13
0
def bundle_data(store, commits):
    manifests = OrderedDict()
    files = defaultdict(list)

    for node, parents in progress_iter('Bundling {} changesets', commits):
        if len(parents) > 2:
            raise Exception(
                'Pushing octopus merges to mercurial is not supported')

        changeset_data = store.read_changeset_data(node)
        is_new = changeset_data is None or check_enabled('bundle')
        if is_new:
            store.create_hg_metadata(node, parents)
        hg_changeset = store._changeset(node, include_parents=True)
        if is_new:
            store.add_head(hg_changeset.node, hg_changeset.parent1,
                           hg_changeset.parent2)
        yield hg_changeset
        manifest = hg_changeset.manifest
        if manifest not in manifests and manifest != NULL_NODE_ID:
            if manifest not in (store.changeset(p).manifest
                                for p in hg_changeset.parents):
                manifests[manifest] = hg_changeset.node

    yield None

    for manifest, changeset in progress_iter('Bundling {} manifests',
                                             manifests.iteritems()):
        hg_manifest = store.manifest(manifest, include_parents=True)
        hg_manifest.changeset = changeset
        yield hg_manifest
        manifest_ref = store.manifest_ref(manifest)
        parents = tuple(store.manifest_ref(p) for p in hg_manifest.parents)
        changes = get_changes(manifest_ref, parents)
        for path, hg_file, hg_fileparents in changes:
            if hg_file != NULL_NODE_ID:
                files[store.manifest_path(path)].append(
                    (hg_file, hg_fileparents, changeset, parents))

    yield None

    def iter_files(files):
        for path in sorted(files):
            yield path
            nodes = set()
            for node, parents, changeset, mn_parents in files[path]:
                if node in nodes:
                    continue
                nodes.add(node)
                file = store.file(node, parents, mn_parents, path)
                file.changeset = changeset
                assert file.node == file.sha1
                yield file

            yield None

    class Filt(object):
        def __init__(self):
            self._previous = None

        def __call__(self, chunk):
            ret = self._previous and chunk is not None
            self._previous = chunk
            return ret

    for chunk in progress_iter('Bundling {} files', iter_files(files), Filt()):
        yield chunk

    yield None
Beispiel #14
0
def bundle_data(store, commits):
    manifests = OrderedDict()
    files = defaultdict(list)

    for node, parents in progress_iter('Bundling {} changesets', commits):
        if len(parents) > 2:
            raise Exception(
                'Pushing octopus merges to mercurial is not supported')

        changeset_data = store.read_changeset_data(node)
        is_new = changeset_data is None or check_enabled('bundle')
        if is_new:
            store.create_hg_metadata(node, parents)
        hg_changeset = store._changeset(node, include_parents=True)
        if is_new:
            store.add_head(hg_changeset.node, hg_changeset.parent1,
                           hg_changeset.parent2)
        yield hg_changeset
        manifest = hg_changeset.manifest
        if manifest not in manifests and manifest != NULL_NODE_ID:
            if manifest not in (store.changeset(p).manifest
                                for p in hg_changeset.parents):
                manifests[manifest] = hg_changeset.node

    yield None

    for manifest, changeset in progress_iter('Bundling {} manifests',
                                             manifests.iteritems()):
        hg_manifest = store.manifest(manifest, include_parents=True)
        hg_manifest.changeset = changeset
        yield hg_manifest
        manifest_ref = store.manifest_ref(manifest)
        parents = tuple(store.manifest_ref(p) for p in hg_manifest.parents)
        changes = get_changes(manifest_ref, parents)
        for path, hg_file, hg_fileparents in changes:
            if hg_file != NULL_NODE_ID:
                files[store.manifest_path(path)].append(
                    (hg_file, hg_fileparents, changeset, parents))

    yield None

    def iter_files(files):
        count_chunks = 0
        for count_names, path in enumerate(sorted(files), 1):
            yield (count_chunks, count_names), path
            nodes = set()
            for node, parents, changeset, mn_parents in files[path]:
                if node in nodes:
                    continue
                count_chunks += 1
                nodes.add(node)
                file = store.file(node, parents, mn_parents, path)
                file.changeset = changeset
                assert file.node == file.sha1
                yield (count_chunks, count_names), file

            yield (count_chunks, count_names), None

    for chunk in progress_enum('Bundling {} revisions of {} files',
                               iter_files(files)):
        yield chunk

    yield None
Beispiel #15
0
def get_clonebundle_url(repo):
    bundles = repo._call(b'clonebundles')

    try:
        if check_enabled('no-mercurial'):
            raise ImportError('Do not use mercurial')
        from mercurial.exchange import (
            parseclonebundlesmanifest,
            filterclonebundleentries,
        )
    except ImportError:
        parseclonebundlesmanifest = False

    if parseclonebundlesmanifest:

        class dummy(object):
            pass

        fakerepo = dummy()
        fakerepo.requirements = set()
        fakerepo.supportedformats = set()
        fakerepo.ui = repo.ui

        entries = parseclonebundlesmanifest(fakerepo, bundles)
        if not entries:
            return None

        entries = filterclonebundleentries(fakerepo, entries)
        if not entries:
            return None

        return entries[0].get(b'URL')

    supported_bundles = (b'v1', b'v2')
    supported_compressions = tuple(k for k, v in (
        (b'none', b'UN'),
        (b'gzip', b'GZ'),
        (b'bzip2', b'BZ'),
        (b'zstd', b'ZS'),
    ) if HgRepoHelper.supports((b'compression', v)))

    has_sni = getattr(ssl, 'HAS_SNI', False)

    logger = logging.getLogger('clonebundle')

    for line in bundles.splitlines():
        attrs = line.split()
        if not attrs:
            continue
        url = attrs.pop(0)
        logger.debug(url)
        attrs = {
            unquote_to_bytes(k): unquote_to_bytes(v)
            for k, _, v in (a.partition(b'=') for a in attrs)
        }
        logger.debug(attrs)
        if b'REQUIRESNI' in attrs and not has_sni:
            logger.debug('Skip because of REQUIRESNI, but SNI unsupported')
            continue

        spec = attrs.get(b'BUNDLESPEC')
        if not spec:
            logger.debug('Skip because missing BUNDLESPEC')
            continue

        typ, _, params = spec.partition(b';')
        compression, _, version = typ.partition(b'-')

        if compression not in supported_compressions:
            logger.debug('Skip because unsupported compression (%s)',
                         compression)
            continue
        if version not in supported_bundles:
            logger.debug('Skip because unsupported bundle type (%s)', version)
            continue

        params_dict = {}
        for p in params.split(b':'):
            k, _, v = p.partition(b'=')
            params_dict[k] = v

        if 'stream' in params_dict:
            logger.debug('Skip because stream bundles are not supported')
            continue

        return url
Beispiel #16
0
def bundle_data(store, commits):
    manifests = OrderedDict()
    files = defaultdict(list)

    for node, parents in progress_iter('Bundling {} changesets', commits):
        if len(parents) > 2:
            raise Exception(
                'Pushing octopus merges to mercurial is not supported')

        changeset_data = store.read_changeset_data(node)
        is_new = changeset_data is None or check_enabled('bundle')
        if is_new:
            store.create_hg_metadata(node, parents)
        hg_changeset = store._changeset(node, include_parents=True)
        if is_new:
            store.add_head(hg_changeset.node, hg_changeset.parent1,
                           hg_changeset.parent2)
        yield hg_changeset
        manifest = hg_changeset.manifest
        if manifest not in manifests and manifest != NULL_NODE_ID:
            if manifest not in (store.changeset(p).manifest
                                for p in hg_changeset.parents):
                manifests[manifest] = hg_changeset.node

    yield None

    for manifest, changeset in progress_iter('Bundling {} manifests',
                                             iteritems(manifests)):
        hg_manifest = store.manifest(manifest, include_parents=True)
        hg_manifest.changeset = changeset
        yield hg_manifest
        manifest_ref = store.manifest_ref(manifest)
        parents = tuple(store.manifest_ref(p) for p in hg_manifest.parents)
        changes = get_changes(manifest_ref, parents)
        for path, hg_file, hg_fileparents in changes:
            if hg_file != NULL_NODE_ID:
                files[store.manifest_path(path)].append(
                    (hg_file, hg_fileparents, changeset, parents))

    yield None

    def iter_files(files):
        count_chunks = 0
        for count_names, path in enumerate(sorted(files), 1):
            yield (count_chunks, count_names), path
            nodes = set()
            for node, parents, changeset, mn_parents in files[path]:
                if node in nodes:
                    continue
                count_chunks += 1
                nodes.add(node)
                file = store.file(node, parents, mn_parents, path)
                file.changeset = changeset
                assert file.node == file.sha1
                yield (count_chunks, count_names), file

            yield (count_chunks, count_names), None

    for chunk in progress_enum('Bundling {} revisions of {} files',
                               iter_files(files)):
        yield chunk

    yield None
Beispiel #17
0
def get_clonebundle(repo):
    try:
        if check_enabled('no-mercurial'):
            raise ImportError('Do not use mercurial')
        from mercurial.exchange import (
            parseclonebundlesmanifest,
            filterclonebundleentries,
        )
    except ImportError:
        return None

    bundles = repo._call('clonebundles')

    class dummy(object):
        pass

    fakerepo = dummy()
    fakerepo.requirements = set()
    fakerepo.supportedformats = set()
    fakerepo.ui = repo.ui

    entries = parseclonebundlesmanifest(fakerepo, bundles)
    if not entries:
        return None

    entries = filterclonebundleentries(fakerepo, entries)
    if not entries:
        return None

    url = entries[0].get('URL')
    if not url:
        return None

    sys.stderr.write('Getting clone bundle from %s\n' % url)

    class Getter(object):
        def __init__(self, url):
            self.fh = urllib2.urlopen(url)
            self.url = url
            try:
                self.length = int(self.fh.headers['content-length'])
            except (ValueError, KeyError):
                self.length = None
            self.offset = 0

        def read(self, size):
            try:
                result = self.fh.read(size)
            except socket.error:
                result = ''

            # When self.length is None, self.offset < self.length is always
            # false.
            if not result and self.offset < self.length:
                # Processing large manifests or large files can be slow.
                # With highly compressed but nevertheless large bundles, this
                # means it can take time to process relatively small
                # (compressed) inputs: in the order of several minutes for a
                # few megabytes.  When that happens, SSL connections can end
                # up being aborted between two large TCP receives. In that
                # case, try again with an HTTP Range request if the server
                # supports it.
                # TODO: This is a stopgap until processing is faster.
                req = urllib2.Request(self.url)
                req.add_header('Range', 'bytes=%d-' % self.offset)
                self.fh = urllib2.urlopen(req)
                if self.fh.getcode() != 206:
                    return ''
                range = self.fh.headers['Content-Range'].split(None, 1)
                if len(range) != 2:
                    return ''
                unit, range = range
                if unit != 'bytes':
                    return ''
                range = range.split('-', 1)
                if len(range) != 2:
                    return ''
                start, end = range
                start = int(start)
                if start > self.offset:
                    return ''
                logging.getLogger('clonebundle').debug(
                    'Retrying from offset %d', start)
                while start < self.offset:
                    l = len(self.fh.read(self.offset - start))
                    if not l:
                        return ''
                    start += l
                result = self.fh.read(size)
            self.offset += len(result)
            return result

    return unbundle_fh(Getter(url), url)
Beispiel #18
0
    progress_enum,
    progress_iter,
)
from collections import (
    defaultdict,
    deque,
)
from .bundle import create_bundle
from .changegroup import (
    RawRevChunk01,
    RawRevChunk02,
)
from cStringIO import StringIO

try:
    if check_enabled('no-mercurial'):
        raise ImportError('Do not use mercurial')
    # Old versions of mercurial use an old version of socketutil that tries to
    # assign a local PROTOCOL_SSLv2, copying it from the ssl module, without
    # ever using it. It shouldn't hurt to set it here.
    import ssl
    if not hasattr(ssl, 'PROTOCOL_SSLv2'):
        ssl.PROTOCOL_SSLv2 = 0
    if not hasattr(ssl, 'PROTOCOL_SSLv3'):
        ssl.PROTOCOL_SSLv3 = 1

    from mercurial import (
        changegroup,
        error,
        hg,
        ui,
Beispiel #19
0
def get_clonebundle_url(repo):
    bundles = repo._call('clonebundles')

    try:
        if check_enabled('no-mercurial'):
            raise ImportError('Do not use mercurial')
        from mercurial.exchange import (
            parseclonebundlesmanifest,
            filterclonebundleentries,
        )
    except ImportError:
        parseclonebundlesmanifest = False

    if parseclonebundlesmanifest:
        class dummy(object):
            pass

        fakerepo = dummy()
        fakerepo.requirements = set()
        fakerepo.supportedformats = set()
        fakerepo.ui = repo.ui

        entries = parseclonebundlesmanifest(fakerepo, bundles)
        if not entries:
            return None

        entries = filterclonebundleentries(fakerepo, entries)
        if not entries:
            return None

        return entries[0].get('URL')

    supported_bundles = ('v1', 'v2')
    supported_compressions = tuple(
        k for k, v in (
            ('none', 'UN'),
            ('gzip', 'GZ'),
            ('bzip2', 'BZ'),
            ('zstd', 'ZS'),
        ) if HgRepoHelper.supports(('compression', v))
    )

    has_sni = getattr(ssl, 'HAS_SNI', False)

    logger = logging.getLogger('clonebundle')

    for line in bundles.splitlines():
        attrs = line.split()
        if not attrs:
            continue
        url = attrs.pop(0)
        logger.debug(url)
        attrs = {
            urllib.unquote(k): urllib.unquote(v)
            for k, _, v in (a.partition('=') for a in attrs)
        }
        logger.debug(attrs)
        if 'REQUIRESNI' in attrs and not has_sni:
            logger.debug('Skip because of REQUIRESNI, but SNI unsupported')
            continue

        spec = attrs.get('BUNDLESPEC')
        if not spec:
            logger.debug('Skip because missing BUNDLESPEC')
            continue

        typ, _, params = spec.partition(';')
        compression, _, version = typ.partition('-')

        if compression not in supported_compressions:
            logger.debug('Skip because unsupported compression (%s)',
                         compression)
            continue
        if version not in supported_bundles:
            logger.debug('Skip because unsupported bundle type (%s)',
                         version)
            continue

        return url
Beispiel #20
0
def getbundle(repo, store, heads, branch_names):
    if isinstance(repo, bundlerepo):
        bundle = repo._unbundler
    else:
        common = findcommon(repo, store, store.heads(branch_names))
        logging.info('common: %s', common)
        bundle = None
        got_partial = False
        if not common:
            if not store._has_metadata and not store._graft:
                manifest = Git.config('cinnabar.clone')
                if not manifest and experiment('git-clone') and \
                        repo.capable('cinnabarclone'):
                    manifest = repo._call('cinnabarclone')
                if manifest:
                    got_partial = do_cinnabarclone(repo, manifest, store)
                    if not got_partial:
                        if check_enabled('cinnabarclone'):
                            raise Exception('cinnabarclone failed.')
                        logging.warn('Falling back to normal clone.')
            if not got_partial and repo.capable('clonebundles'):
                bundle = get_clonebundle(repo)
                got_partial = bool(bundle)
                if not got_partial and check_enabled('clonebundles'):
                    raise Exception('clonebundles failed.')
        if bundle:
            bundle = unbundler(bundle)
            # Manual move semantics
            apply_bundle = BundleApplier(bundle)
            del bundle
            apply_bundle(store)
            if not changegroup:
                BundleHelper.close()
        if got_partial:
            # Eliminate the heads that we got from the clonebundle or
            # cinnabarclone.
            heads = [h for h in heads if not store.changeset_ref(h)]
            if not heads:
                return
            common = findcommon(repo, store, store.heads(branch_names))
            logging.info('common: %s', common)

        kwargs = {}
        if unbundle20 and repo.capable('bundle2'):
            bundle2caps = {
                'HG20': (),
                'changegroup': ('01', '02'),
            }
            kwargs['bundlecaps'] = set((
                'HG20', 'bundle2=%s' % urllib.quote(encodecaps(bundle2caps))))

        bundle = repo.getbundle('bundle', heads=[unhexlify(h) for h in heads],
                                common=[unhexlify(h) for h in common],
                                **kwargs)

        bundle = unbundler(bundle)

    # Manual move semantics
    apply_bundle = BundleApplier(bundle)
    del bundle
    apply_bundle(store)
Beispiel #21
0
    def create_hg_metadata(self, commit, parents):
        if check_enabled('bundle'):
            real_changeset_data = self.read_changeset_data(commit)
        manifest = self.create_hg_manifest(commit, parents)
        commit_data = GitCommit(commit)

        if manifest.node == NULL_NODE_ID:
            manifest.node = manifest.sha1
            if check_enabled('bundle'):
                if real_changeset_data and (manifest.node !=
                                            real_changeset_data['manifest']):
                    for path, created, real in sorted_merge(
                            manifest._lines,
                            self.manifest(
                                real_changeset_data['manifest'])._lines,
                            key=lambda i: i.name,
                            non_key=lambda i: i):
                        if str(created) != str(real):
                            logging.error('%r != %r', str(created), str(real))
            self._push_manifests[manifest.node] = manifest
            self.manifest_ref(manifest.node, hg2git=False, create=True)
            self._manifest_git_tree[manifest.node] = commit_data.tree

        extra = {}
        if commit_data.author != commit_data.committer:
            committer = self.hg_author_info(commit_data.committer)
            extra['committer'] = '%s %d %d' % committer

        if parents:
            parent_changeset_data = self.read_changeset_data(parents[0])
            branch = parent_changeset_data.get('extra', {}).get('branch')
            if branch:
                extra['branch'] = branch

        changeset_data = self._changeset_data_cache[commit] = {
            'files': sorted(chain(manifest.removed, manifest.modified)),
            'manifest': manifest.node,
        }
        if extra:
            changeset_data['extra'] = extra
        changeset = self._changeset(commit, include_parents=True)
        if self._graft is True and parents and changeset.data[-1] == '\n':
            parent_cs = self._changeset(parents[0], skip_patch=True)
            if 'patch' not in self._changeset_data_cache[parents[0]]:
                self._graft = False
            else:
                patch = self._changeset_data_cache[parents[0]]['patch'][-1]
                self._graft = (patch[1] == len(parent_cs.data)
                               and parent_cs.data[-1] == '\n')
            if self._graft:
                self._graft = 'true'

        if self._graft == 'true' and changeset.data[-1] == '\n':
            changeset.data = changeset.data[:-1]
            changeset_data['patch'] = ((len(changeset.data),
                                        len(changeset.data) + 1, ''), )
        changeset_data['changeset'] = changeset.changeset = changeset.node = \
            changeset.sha1
        self._push_changesets[changeset.node] = changeset
        # This is a horrible way to do this, but this method is not doing much
        # better overall anyways.
        if extra:
            if 'committer' in extra:
                del extra['committer']
            if not extra:
                del changeset_data['extra']
        self._changesets[changeset.node] = PseudoString(commit)

        if check_enabled('bundle') and real_changeset_data:
            error = False
            for k in ('files', 'manifest'):
                if real_changeset_data.get(k, []) != changeset_data.get(k):
                    logging.error('(%s) %r != %r', k,
                                  real_changeset_data.get(k),
                                  changeset_data.get(k))
                    error = True
            if error:
                raise Exception('Changeset mismatch')
Beispiel #22
0
    defaultdict,
    deque,
)
from .bundle import (
    create_bundle,
    encodecaps,
    decodecaps,
)
from .changegroup import (
    RawRevChunk01,
    RawRevChunk02,
)
from cStringIO import StringIO

try:
    if check_enabled('no-mercurial'):
        raise ImportError('Do not use mercurial')
    # Old versions of mercurial use an old version of socketutil that tries to
    # assign a local PROTOCOL_SSLv2, copying it from the ssl module, without
    # ever using it. It shouldn't hurt to set it here.
    if not hasattr(ssl, 'PROTOCOL_SSLv2'):
        ssl.PROTOCOL_SSLv2 = 0
    if not hasattr(ssl, 'PROTOCOL_SSLv3'):
        ssl.PROTOCOL_SSLv3 = 1

    from mercurial import (
        changegroup,
        error,
        hg,
        ui,
        url,