view mercurial/httpconnection.py @ 38573:050fbd9d103a

test-convert: demonstrate an unstable hash issue for bzr -> hg -> hg It looks like the manifest value changing is the only difference, but I'm not sure why it's happening. I've got a similar divergence in a production repo that was also converted from bzr and has an octopus merge[1]. Unlike here, the manifest values for the destination merge commits reflect the initial merge only, instead of all four merges agreeing like this test. $ hg -R src_repo manifest -r 310 --debug | grep file # octopus fixup merge 2d8775bc2481bd28ac87038ecdf33e1dbddc80e9 644 file1 6adb9353a55bb8be76e71382efc724ec3ccf7ed5 644 file2 $ hg -R src_repo manifest -r 309 --debug | grep file # first merge 362e7cb5163153c4989daad1a834871ae849f205 644 file1 2c65d947191938c3ea616b7ceb7648ff3843261f 644 file2 $ hg -R dst_repo manifest -r 273 --debug | grep file # octopus fixup merge 362e7cb5163153c4989daad1a834871ae849f205 644 file1 2c65d947191938c3ea616b7ceb7648ff3843261f 644 file2 $ hg -R dst_repo manifest -r 272 --debug | grep file # first merge 362e7cb5163153c4989daad1a834871ae849f205 644 file1 2c65d947191938c3ea616b7ceb7648ff3843261f 644 file2 This divergence is espcially annoying because unlike changelog differences, I haven't figured out a way to fix this in code. The only way I found to work around it is to convert up to the point of divergence, `hg bundle` the bad revision in the source, apply it to the destination, add a line to the shamap, and fire off the conversion again. But I suspect that there's more to it than just the octopus merge because I also have a commit in the same repo, done in Mercurial (well after the conversion) that is exhibiting a similar issue (and it's not a merge commit). I'm almost positive that it was created with 4.4 or later. Any ideas? [1] https://www.mercurial-scm.org/pipermail/mercurial/2018-June/050924.html
author Matt Harbison <matt_harbison@yahoo.com>
date Thu, 05 Jul 2018 15:07:29 -0400
parents 5f9d436cd3b7
children c53f0ead5781
line wrap: on
line source

# httpconnection.py - urllib2 handler for new http support
#
# Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
# Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
# Copyright 2011 Google, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

from __future__ import absolute_import

import os

from .i18n import _
from . import (
    pycompat,
    util,
)

urlerr = util.urlerr
urlreq = util.urlreq

# moved here from url.py to avoid a cycle
class httpsendfile(object):
    """This is a wrapper around the objects returned by python's "open".

    Its purpose is to send file-like objects via HTTP.
    It do however not define a __len__ attribute because the length
    might be more than Py_ssize_t can handle.
    """

    def __init__(self, ui, *args, **kwargs):
        self.ui = ui
        self._data = open(*args, **kwargs)
        self.seek = self._data.seek
        self.close = self._data.close
        self.write = self._data.write
        self.length = os.fstat(self._data.fileno()).st_size
        self._pos = 0
        # We pass double the max for total because we currently have
        # to send the bundle twice in the case of a server that
        # requires authentication. Since we can't know until we try
        # once whether authentication will be required, just lie to
        # the user and maybe the push succeeds suddenly at 50%.
        self._progress = ui.makeprogress(_('sending'), unit=_('kb'),
                                         total=(self.length // 1024 * 2))

    def read(self, *args, **kwargs):
        ret = self._data.read(*args, **kwargs)
        if not ret:
            self._progress.complete()
            return ret
        self._pos += len(ret)
        self._progress.update(self._pos // 1024)
        return ret

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

# moved here from url.py to avoid a cycle
def readauthforuri(ui, uri, user):
    uri = pycompat.bytesurl(uri)
    # Read configuration
    groups = {}
    for key, val in ui.configitems('auth'):
        if key in ('cookiefile',):
            continue

        if '.' not in key:
            ui.warn(_("ignoring invalid [auth] key '%s'\n") % key)
            continue
        group, setting = key.rsplit('.', 1)
        gdict = groups.setdefault(group, {})
        if setting in ('username', 'cert', 'key'):
            val = util.expandpath(val)
        gdict[setting] = val

    # Find the best match
    scheme, hostpath = uri.split('://', 1)
    bestuser = None
    bestlen = 0
    bestauth = None
    for group, auth in groups.iteritems():
        if user and user != auth.get('username', user):
            # If a username was set in the URI, the entry username
            # must either match it or be unset
            continue
        prefix = auth.get('prefix')
        if not prefix:
            continue
        p = prefix.split('://', 1)
        if len(p) > 1:
            schemes, prefix = [p[0]], p[1]
        else:
            schemes = (auth.get('schemes') or 'https').split()
        if (prefix == '*' or hostpath.startswith(prefix)) and \
            (len(prefix) > bestlen or (len(prefix) == bestlen and \
                not bestuser and 'username' in auth)) \
             and scheme in schemes:
            bestlen = len(prefix)
            bestauth = group, auth
            bestuser = auth.get('username')
            if user and not bestuser:
                auth['username'] = user
    return bestauth