class Abort(Exception):
    """Raised if a command needs to print an error and exit.""" class AmbiguousGraftAbort(Abort):
    """Cannot graft the changeset.""" class NothingToGraftException(Exception):
    """Not found any tree to graft.""" class OldUpgradeAbort(UpgradeAbort):
    """Metadata needs a consistency check."""
    def __init__(self):
        super(OldUpgradeAbort, self).__init__(
            'Metadata from git-cinnabar versions older than 0.3.0 is not '
            'supported.\n'
            'Please run `git cinnabar fsck` with version 0.3.x first.'
        ) class UpgradeAbort(Abort):
    """Metadata needs an upgrade."""
    def __init__(self, message=None):
        super(UpgradeAbort, self).__init__(
            message or
            'Git-cinnabar metadata needs upgrade. '
            'Please run `git cinnabar upgrade`.'
        ) class StreamHandler(logging.StreamHandler):
class Formatter(logging.Formatter):
class ConfigSetFunc(object):
class Progress(object):
class IOLogger(object):
class OrderedDefaultDict(OrderedDict):
class VersionedDict(object):
class lrucache(object):
    class node(object):
class chunkbuffer(object):
class HTTPReader(object):
class Seekable(object):
class Process(object):
class TypedProperty(object):
class MemoryCPUReporter(Thread):
class VersionCheck(Thread):
    def __init__(self):
    def emit(self, record):
    def __init__(self):
    def format(self, record):
def init_logging():
    def __init__(self, key, values, extra_values=(), default='', remote=None):
    def __call__(self, name):
def interval_expired(config_key, interval, globl=False):
    def __init__(self, fmt):
    def progress(self, count=None):
    def _print_count(self, count, t1=None):
    def finish(self, count=None):
def progress_iter(fmt, iter):
def progress_enum(fmt, enum_iter):
    def __init__(self, logger, reader, writer=None, prefix=''):
    def read(self, length=None, level=logging.INFO):
    def readline(self, level=logging.INFO):
    def write(self, data, level=logging.INFO):
    def flush(self):
    def __iter__(self):
def one(iterable):
def strip_suffix(s, suffix):
    def __init__(self, default_factory, *args, **kwargs):
    def __missing__(self, key):
    def __init__(self, content=None, **kwargs):
    def update(self, content=None, **kwargs):
    def __getitem__(self, key):
    def get(self, key, default=None):
    def __contains__(self, key):
    def __delitem__(self, key):
    def __setitem__(self, key, value):
    def __len__(self):
    def keys(self):
    def values(self):
    def __iter__(self):
    def items(self):
    def iteritems(self):
    def iterchanges(self):
    def flattened(self):
def _iter_diff_blocks(a, b):
def byte_diff(a, b):
def sorted_merge(iter_a, iter_b, key=lambda i: i[0], non_key=lambda i: i[1:]):
        def __init__(self):
        def insert(self, after):
        def detach(self):
    def __init__(self, size):
    def __call__(self, func):
        def wrapper(*args):
    def invalidate(self, *args):
    def __getitem__(self, key):
    def __setitem__(self, key, value):
    def __delitem__(self, key):
    def __len__(self):
    def __init__(self, in_iter):
        def splitbig(chunks):
    def read(self, l=None):
    def __init__(self, url):
    def read(self, size):
    def _reopen(self):
    def readable(self):
    def readinto(self, b):
    def __init__(self, reader, length):
    def read(self, length):
    def tell(self):
    def seek(self, pos, how=os.SEEK_SET):
    def __init__(self, *args, **kwargs):
    def _env_strings(self, env):
    def _popen(self, cmd, env, **kwargs):
    def wait(self):
    def pid(self):
    def stdin(self):
    def stdout(self):
    def stderr(self):
    def __init__(self, cls):
    def __get__(self, obj, cls=None):
    def __set__(self, obj, value):
    def __init__(self, memory=False, cpu=False):
    def _report(self, proc):
    def run(self):
    def shutdown(self):
    def __init__(self):
    def run(self):
    def join(self):
def run(func, args):
    def iteritems(d):
    def itervalues(d):
    def environ(k=None):
    def iteritems(d):
    def itervalues(d):
    def fsencode(s):
    def fsdecode(s):
    def environ(k=None):
    CREATED = 1
    MODIFIED = 2
    REMOVED = 3
        REPO = 'https://github.com/glandium/git-cinnabar' EMPTY_BLOB = b'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391' EMPTY_TREE = b'4b825dc642cb6eb9a060e54bf8d69288fbee4904' class Git(object):
    _notes_depth = {}
    _config = None
    _replace = {}

    @classmethod
    def iter(self, *args, **kwargs):
        start = time.time()

        if args[0] == 'config':
            self._config = None

        proc = GitProcess(*args, **kwargs)
        try:
            for line in proc.stdout or ():
                line = line.rstrip(b'\n')
                yield line

        finally:
            proc.wait()
            logging.getLogger(args[0]).info('[%d] wall time: %.3fs',
                                            proc.pid, time.time() - start)

    @classmethod
    def run(self, *args, **kwargs):
        stdout = kwargs.pop('stdout', None)
        return tuple(self.iter(*args, stdout=stdout, **kwargs))

    @classmethod
    def for_each_ref(self, *patterns):
        if not patterns:
            return
        # Ideally, this would not actually call for-each-ref if all refs
        # matching the given patterns are already known.
        for line in self.iter('for-each-ref', '--format',
                              '%(objectname) %(refname)', *patterns,
                              stderr=open(os.devnull, 'wb')):
            yield line.split(b' ', 1)

    @classmethod
    def resolve_ref(self, ref):
        return one(Git.iter('rev-parse', '--revs-only', ref,
                            stderr=open(os.devnull, 'wb')))

    @classmethod
    def ls_tree(self, treeish, path=b'', recursive=False):
        from .helper import GitHgHelper
        assert not treeish.startswith(b'refs/')

        if path.endswith(b'/') or recursive or path == b'':
            path = path.rstrip(b'/')
            for line in GitHgHelper.ls_tree(b'%s:%s' % (treeish, path),
                                            recursive):
                mode, typ, sha1, p = line
                if path:
                    yield mode, typ, sha1, posixpath.join(path, p)
                else:
                    yield mode, typ, sha1, p
        else:
            with GitHgHelper.query(b'ls', treeish, path) as stdout:
                line = stdout.readline()
                if not line.startswith(b'missing '):
                    yield split_ls_tree(line[:-1])

    @classmethod
    def update_ref(self, ref, newvalue):
        assert not newvalue.startswith(b'refs/')
        from .helper import GitHgHelper
        GitHgHelper.update_ref(ref, newvalue)

    @classmethod
    def delete_ref(self, ref):
        self.update_ref(ref, NULL_NODE_ID)

    @classmethod
    def config(self, name, remote=None, values={}, multiple=False):
        assert not (values and multiple)
        if self._config is None:
            proc = GitProcess('config', '-l', '-z')
            data = proc.stdout.read()
            proc.wait()
            self._config = {}
            for l in data.split(b'\0'):
                if l:
                    k, v = l.split(b'\n', 1)
                    if k in self._config:
                        self._config[k] += b'\0' + v
                    else:
                        self._config[k] = v
        var = name.encode('ascii')
        value = None
        if name.startswith('cinnabar.'):
            var = ('GIT_%s' % name.replace('.', '_').upper()).encode('ascii')
            value = environ(var)
            if value is None and remote:
                var = b'remote.%s.%s' % (
                    remote, name.replace('.', '-').encode('ascii'))
                value = self._config.get(var.lower())
        elif name == 'fetch.prune' and remote:
            var = b'remote.%s.prune' % remote
            value = self._config.get(var.lower())
        if value is None:
            var = name.encode('ascii')
            value = self._config.get(var.lower())
        if value:
            value = value.split(b'\0')
            if not multiple:
                value = value[-1]
        logging.getLogger('config').info('%s = %r', var, value)
        if values:
            if value in values:
                if isinstance(values, dict):
                    value = values[value]
            else:
                values = ', '.join(sorted('"%s"' % v.decode('ascii')
                                          for v in values
                                          if v is not None))
                if value is None:
                    raise InvalidConfig(
                        '%s must be set to one of %s' % (
                            var.decode('ascii'), values))
                else:
                    raise InvalidConfig(
                        'Invalid value for %s: "%s". Valid values: %s' % (
                            var.decode('ascii'), fsdecode(value), values))
        return value class GitProcess(Process):
    def __init__(self, *args, **kwargs):
        config = kwargs.pop('config', {})

        command = ['git']
        command += chain(*(['-c', '%s=%s' % (n, v)]
                           for n, v in config.items()))
        command += args

        kwargs.setdefault('logger', args[0])
        super(GitProcess, self).__init__(*command, **kwargs) NULL_NODE_ID = b'0' * 40 class RawRevChunk(bytearray, ParentsTrait):
    __slots__ = ()

    @staticmethod
    def _field(offset, size=None, filter=bytes):
        unfilter = unhexlify if filter == hexlify else None
        end = offset + size if size else None

        class descriptor(object):
            def __get__(self, obj, type=None):
                return filter(obj[offset:end])

            def __set__(self, obj, value):
                value = unfilter(value) if unfilter else value
                assert len(value) == size or not size
                self.ensure(obj, end or offset)
                obj[offset:end] = value

            def ensure(self, obj, length):
                if length > len(obj):
                    obj.extend(b'\0' * (length - len(obj)))

        return descriptor() class RevDiff(object):
    class Part(object):
        __slots__ = ('start', 'end', 'block_len', 'text_data')

        def __init__(self, rev_patch):
            self.start, self.end, self.block_len = \
                struct.unpack('>lll', rev_patch[:12].tobytes())
            self.text_data = rev_patch[12:12 + self.block_len]

        def __len__(self):
            return self.block_len + 12

    def __init__(self, buf):
        self._buf = memoryview(buf)

    def __iter__(self):
        start = 0
        while start < len(self._buf):
            part = self.Part(self._buf[start:])
            yield part
            start += len(part)

    def apply(self, raw_orig):
        new = bytearray()
        orig = memoryview(raw_orig)
        end = 0
        for diff in self:
            new += orig[end:diff.start]
            new += diff.text_data
            end = diff.end
        if new == b'' and end == 0:
            return raw_orig
        new += orig[end:]
        return bytes(new) class Authorship(object):
    __slots__ = ('name', 'email', 'timestamp', 'utcoffset')

    WHO_RE = re.compile(b'^(?P<name>.*?) ?(?:\\<(?P<email>.*?)\\>)')

    @classmethod
    def from_hg_str(cls, s, maybe_git_utcoffset=False):
        return cls.from_hg(*s.rsplit(b' ', 2),
                           maybe_git_utcoffset=maybe_git_utcoffset)

    @classmethod
    def from_hg(cls, who, timestamp, utcoffset, maybe_git_utcoffset=False):
        match = cls.WHO_RE.match(who)

        def cleanup(x):
            return x.replace(b'<', b'').replace(b'>', b'')

        if match:
            name = cleanup(match.group('name'))
            email = cleanup(match.group('email'))
        elif b'@' in who:
            name = b''
            email = cleanup(who)
        else:
            name = cleanup(who)
            email = b''

        # The UTC offset in mercurial author info is in seconds, formatted as
        # %d. It also has an opposite sign compared to traditional UTC offsets.
        # However, committer info stored in mercurial by hg-git can have
        # git-style UTC offsets, in the form [+-]hhmm.

        # When what we have is in the form +xxxx or -0yyy, it is obviously the
        # latter. When it's -1yyy, it could be either, so we assume that a
        # valid UTC offset is always a multiple of 15 minutes. By that
        # definition, a number between -1000 and -1800 can't be simultaneously
        # a valid UTC offset in seconds and a valid UTC offset in hhmm form.

        # (cf. https://en.wikipedia.org/wiki/List_of_UTC_time_offsets lists
        # there exist a few 15-minutes aligned time zones, but they don't match
        # anything that could match here anyways, but just in case someone one
        # day creates one, assume it won't be finer grained)
        if maybe_git_utcoffset and isinstance(utcoffset, bytes):
            is_git = False
            if utcoffset.startswith((b'+', b'-0')):
                is_git = True
            elif utcoffset.startswith(b'-1'):
                utcoffset = int(utcoffset)
                if (utcoffset > -1800 and utcoffset % 900 != 0 and
                        (utcoffset % 100) % 15 == 0):
                    is_git = True
            if is_git:
                return cls.from_git(b'%s <%s>' % (name, email),
                                    timestamp, utcoffset)

        result = cls()
        result.name = name
        result.email = email
        result.timestamp = int(timestamp)
        result.utcoffset = int(utcoffset)
        return result

    @classmethod
    def from_git_str(cls, s):
        return cls.from_git(*s.rsplit(b' ', 2))

    @classmethod
    def from_git(cls, who, timestamp, utcoffset):
        result = cls()
        match = cls.WHO_RE.match(who)
        # We don't ever expect a git `who` information to not match the regexp,
        # as git is very conservative in what it accepts.
        assert match
        result.name = match.group('name')
        result.email = match.group('email')
        result.timestamp = int(timestamp)
        utcoffset = int(utcoffset)
        sign = (utcoffset < 0) - (utcoffset > 0)
        utcoffset = abs(utcoffset)
        utcoffset = (utcoffset // 100) * 60 + (utcoffset % 100)
        result.utcoffset = sign * utcoffset * 60
        return result

    def to_git(self):
        sign = b'+' if self.utcoffset <= 0 else b'-'
        utcoffset = abs(self.utcoffset) // 60
        utcoffset = b'%c%02d%02d' % (sign, utcoffset // 60, utcoffset % 60)
        who = b'%s <%s>' % (self.name, self.email)
        return who, (b'%d' % self.timestamp), utcoffset

    def to_git_str(self):
        return b' '.join(self.to_git())

    def to_hg(self):
        if self.name and self.email:
            who = b'%s <%s>' % (self.name, self.email)
        else:
            who = self.name or b'<%s>' % self.email
        return who, (b'%d' % self.timestamp), (b'%d' % self.utcoffset)

    def to_hg_str(self):
        return b' '.join(self.to_hg()) class Changeset(HgObject):
    __slots__ = ('manifest', 'author', 'timestamp', 'utcoffset', 'body',
                 '__weakref__')

    def __init__(self, *args, **kwargs):
        super(Changeset, self).__init__(*args, **kwargs)
        self.manifest = NULL_NODE_ID
        self.author = b''
        self.timestamp = b''
        self.utcoffset = b''
        self.files = []
        self.body = b''

    @classmethod
    def from_chunk(cls, raw_chunk, delta_cs=None):
        this = super(Changeset, cls).from_chunk(raw_chunk, delta_cs)
        this.raw_data = raw_chunk.patch.apply(
            delta_cs.raw_data if delta_cs else b'')
        return this

    @HgObject.raw_data.setter
    def raw_data(self, data):
        metadata, self.body = data.split(b'\n\n', 1)
        lines = metadata.splitlines()
        self.manifest, self.author, date = lines[:3]
        date = date.split(b' ', 2)
        self.timestamp = date[0]
        self.utcoffset = date[1]
        if len(date) == 3:
            self.extra = date[2]
        self.files = lines[3:]

    files = TypedProperty(list)

    class ExtraData(dict):
        @classmethod
        def from_str(cls, s):
            return cls(i.split(b':', 1) for i in s.split(b'\0') if i)

        @classmethod
        def from_obj(cls, obj):
            if obj is None:
                return None
            if isinstance(obj, dict):
                return cls(obj)
            return cls.from_str(obj)

        def __str__(self):
            raise RuntimeError('Use to_str()')

        def to_str(self):
            return b'\0'.join(b':'.join(i) for i in sorted(self.items()))

    extra = TypedProperty(ExtraData)

    def _data_iter(self):
        yield self.manifest
        yield b'\n'
        yield self.author
        yield b'\n'
        yield self.timestamp
        yield b' '
        yield self.utcoffset
        if self.extra is not None:
            yield b' '
            yield self.extra.to_str()
        if self.files:
            yield b'\n'
            yield b'\n'.join(sorted(self.files))
        yield b'\n\n'
        yield self.body

    @property
    def changeset(self):
        return self.node

    @changeset.setter
    def changeset(self, value):
        assert value in (self.node, NULL_NODE_ID)

    class ExtraProperty(object):
        def __init__(self, name):
            self._name = name.encode('ascii')

        def __get__(self, obj, type=None):
            if obj.extra is None:
                return None
            return obj.extra.get(self._name)

        def __set__(self, obj, value):
            if not value:
                if obj.extra:
                    try:
                        del obj.extra[self._name]
                    except KeyError:
                        pass
                if not obj.extra:
                    obj.extra = None
            else:
                if obj.extra is None:
                    obj.extra = {}
                obj.extra[self._name] = value

    branch = ExtraProperty('branch')
    committer = ExtraProperty('committer')
    close = ExtraProperty('close') class File(HgObject):
    __slots__ = ('content', '__weakref__')

    def __init__(self, *args, **kwargs):
        super(File, self).__init__(*args, **kwargs)
        self.content = b''
        self.metadata = {}

    @classmethod
    def from_chunk(cls, raw_chunk, delta_file=None):
        this = super(File, cls).from_chunk(raw_chunk, delta_file)
        this.raw_data = raw_chunk.patch.apply(
            delta_file.raw_data if delta_file else b'')
        return this

    @HgObject.raw_data.setter
    def raw_data(self, data):
        if data.startswith(b'\1\n'):
            _, self.metadata, self.content = data.split(b'\1\n', 2)
        else:
            self.content = data

    class Metadata(OrderedDict):
        @classmethod
        def from_str(cls, s):
            return cls(
                l.split(b': ', 1)
                for l in s.splitlines()
            )

        @classmethod
        def from_dict(cls, d):
            if isinstance(d, OrderedDict):
                return cls(d)
            return cls(sorted(d.items()))

        @classmethod
        def from_obj(cls, obj):
            if isinstance(obj, dict):
                return cls.from_dict(obj)
            return cls.from_str(obj)

        def __str__(self):
            raise RuntimeError('Use to_str()')

        def to_str(self):
            return b''.join(b'%s: %s\n' % i for i in self.items())

    metadata = TypedProperty(Metadata)

    def _data_iter(self):
        metadata = self.metadata.to_str()
        if metadata or self.content.startswith(b'\1\n'):
            metadata = b'\1\n%s\1\n' % metadata
        if metadata:
            yield metadata
        if self.content:
            yield self.content class Manifest(HgObject):
    __slots__ = ('__weakref__', '_raw_data')

    def __init__(self, *args, **kwargs):
        super(Manifest, self).__init__(*args, **kwargs)
        self._items = []
        self._raw_data = None

    class ManifestItem(bytes):
        @classmethod
        def from_info(cls, path, sha1=None, attr=b''):
            if isinstance(path, cls):
                return path
            return cls(b'%s\0%s%s' % (path, sha1, attr))

        @property
        def path(self):
            attr_len = len(self.attr)
            assert self[-41 - attr_len:-40 - attr_len] == b'\0'
            return self[:-41 - attr_len]

        @property
        def attr(self):
            if self[-1] in b'lx':
                return self[-1:]
            return b''

        @property
        def sha1(self):
            attr_len = len(self.attr)
            if attr_len:
                return self[-40 - attr_len:-attr_len]
            return self[-40 - attr_len:]

    class ManifestList(list):
        def __init__(self, *args, **kwargs):
            super(Manifest.ManifestList, self).__init__(*args, **kwargs)
            self._last = None

        def append(self, value):
            assert isinstance(value, Manifest.ManifestItem)
            assert self._last is None or value > self._last
            super(Manifest.ManifestList, self).append(value)
            self._last = value

    _items = TypedProperty(ManifestList)

    @property
    def items(self):
        if self._raw_data is not None:
            self._items[:] = []
            for line in self._raw_data.splitlines():
                item = self.ManifestItem(line)
                self._items.append(item)
            self._raw_data = None
        return self._items

    def add(self, path, sha1=None, attr=b''):
        item = Manifest.ManifestItem.from_info(path, sha1, attr)
        self.items.append(item)

    def __iter__(self):
        return iter(self.items)

    def _data_iter(self):
        for item in self:
            yield item
            yield b'\n'

    @classmethod
    def from_chunk(cls, raw_chunk, delta_mn=None):
        this = super(Manifest, cls).from_chunk(raw_chunk, delta_mn)
        items = iter(delta_mn)
        offset = 0
        item = b''
        for part in raw_chunk.patch:
            while offset < part.start:
                item = next(items, None)
                if item is None:
                    break
                this._items.append(item)
                offset += len(item) + 1
            assert offset == part.start
            for item in part.text_data.tobytes().splitlines():
                item = this.ManifestItem(item)
                this._items.append(item)
            while offset < part.end:
                item = next(items, None)
                if item is None:
                    break
                offset += len(item) + 1
        for item in items:
            this._items.append(item)
        return this

    @property
    def raw_data(self):
        if self._raw_data is not None:
            return self._raw_data
        return super(Manifest, self).raw_data

    @raw_data.setter
    def raw_data(self, data):
        self._raw_data = bytes(data) class GitHgHelper(BaseHelper):
    VERSION = 3003
    MODE = 'import'
    _helper = False

    @classmethod
    def reload(self):
        with self.query(b'reload'):
            pass
        self.git2hg.invalidate()
        self.hg2git.invalidate()
        self._cat_commit.invalidate()
        # Force this method to be synchronous with the helper, using the
        # synchronous `version` command.
        with self.query(b'version %d' % self.VERSION) as stdout:
            stdout.readline()

    @classmethod
    def _cat_file(self, typ, sha1):
        with self.query(b'cat-file', sha1) as stdout:
            return self._read_file(typ, stdout)

    @classmethod
    @lrucache(16)
    def _cat_commit(self, sha1):
        return self._cat_file(b'commit', sha1)

    @classmethod
    def cat_file(self, typ, sha1):
        if typ == b'commit':
            return self._cat_commit(sha1)
        return self._cat_file(typ, sha1)

    @classmethod
    @lrucache(16)
    def git2hg(self, sha1):
        assert sha1 != b'changeset'
        with self.query(b'git2hg', sha1) as stdout:
            return self._read_file(b'blob', stdout)

    @classmethod
    def file_meta(self, sha1):
        with self.query(b'file-meta', sha1) as stdout:
            return self._read_file(b'blob', stdout)

    @classmethod
    @lrucache(16)
    def hg2git(self, hg_sha1):
        with self.query(b'hg2git', hg_sha1) as stdout:
            sha1 = stdout.read(41)
            assert sha1[-1:] == b'\n'
            return sha1[:40]

    @classmethod
    def manifest(self, hg_sha1):
        with self.query(b'manifest', hg_sha1) as stdout:
            return self._read_data(stdout)

    @classmethod
    def check_manifest(self, hg_sha1):
        with self.query(b'check-manifest', hg_sha1) as stdout:
            return stdout.readline().strip() == b'ok'

    @classmethod
    def check_file(self, hg_sha1, *parents):
        with self.query(b'check-file', hg_sha1, *parents) as stdout:
            return stdout.readline().strip() == b'ok'

    @classmethod
    def ls_tree(self, sha1, recursive=False):
        extra = () if not recursive else (b'-r',)
        with self.query(b'ls-tree', sha1, *extra) as stdout:
            for line in self._read_data(stdout).split(b'\0'):
                if line:
                    yield split_ls_tree(line)

    @classmethod
    def rev_list(self, *args):
        with self.query(b'rev-list', *args) as stdout:
            for line in self._read_data(stdout).splitlines():
                parents = line.split()
                commit = parents.pop(0)
                tree = parents.pop(0)
                yield commit, tree, parents

    @classmethod
    def diff_tree(self, rev1, rev2, detect_copy=False):
        extra = () if not detect_copy else (b'-C', b'-C')
        extra = extra + (b'--ignore-submodules=dirty', b'--')
        with self.query(b'diff-tree', rev1, rev2, *extra) as stdout:
            data = self._read_data(stdout)
            off = 0
            while off < len(data):
                tab = data.find(b'\t', off)
                assert tab != -1
                (mode_before, mode_after, sha1_before, sha1_after,
                 status) = data[off:tab].split(b' ')
                if detect_copy and status[:1] in b'RC':
                    orig = data.find(b'\0', tab + 1)
                    status = status[:1] + data[tab + 1:orig]
                    tab = orig
                end = data.find(b'\0', tab + 1)
                path = data[tab + 1:end]
                off = end + 1
                yield (mode_before, mode_after, sha1_before, sha1_after,
                       status, path)

    @classmethod
    def set(self, *args):
        if args[0] == b'changeset-metadata':
            self.git2hg.invalidate(self, self.hg2git(args[1]))
        elif args[0] != b'file-meta':
            self.hg2git.invalidate(self, args[1])
        with self.query(b'set', *args):
            pass

    @classmethod
    def store(self, what, *args):
        if what == b'metadata':
            with self.query(b'store', what, *args) as stdout:
                sha1 = stdout.read(41)
                assert sha1[-1:] == b'\n'
                return sha1[:40]
        elif what in (b'file', b'manifest'):
            obj = args[0]
            if isinstance(obj, RawRevChunk01):
                delta_node = obj.delta_node
            elif isinstance(obj, RawRevChunk02):
                delta_node = b'cg2'
            else:
                assert False
            with self.query(b'store', what, delta_node, b'%d' % len(obj)):
                self._helper.stdin.write(obj)
                self._helper.stdin.flush()
        elif what == b'manifest_changegroup':
            with self.query(b'store', what, *args):
                return self._helper.stdin
        else:
            assert False

    @classmethod
    @contextmanager
    def store_changegroup(self, version):
        with self.query(b'store', b'changegroup', b'%d' % version):
            yield self._helper.stdin

    @classmethod
    def heads(self, what):
        with self.query(b'heads', what) as stdout:
            data = self._read_data(stdout)
            return data.split()

    @classmethod
    def reset_heads(self, what):
        with self.query(b'reset-heads', what):
            pass

    @classmethod
    def upgrade(self):
        with self.query(b'upgrade') as stdout:
            return stdout.readline().strip() == b'ok'

    @classmethod
    def create_git_tree(self, manifest_sha1, ref_commit=None):
        extra_arg = (ref_commit,) if ref_commit else ()
        with self.query(b'create-git-tree', manifest_sha1,
                        *extra_arg) as stdout:
            sha1 = stdout.read(41)
            assert sha1[-1:] == b'\n'
            return sha1[:40]

    @classmethod
    def seen(self, typ, sha1):
        with self.query(b'seen', typ, sha1) as stdout:
            return stdout.readline().strip() == b'yes'

    @classmethod
    def dangling(self, typ):
        with self.query(b'dangling', typ) as stdout:
            data = self._read_data(stdout)
            return data.splitlines()

    @classmethod
    def update_ref(self, ref, newvalue):
        with self.query(b'reset', b'%s\nfrom %s\n' % (ref, newvalue)):
            self._helper.stdin.flush()

    @classmethod
    def cat_blob(self, ref):
        with self.query(b'cat-blob', ref) as stdout:
            sha1, blob, size = stdout.readline().split()
            assert blob == b'blob'
            size = int(size)
            content = stdout.read(size)
            lf = stdout.read(1)
            assert lf == b'\n'
            return content

    @classmethod
    def _get_last(self):
        with self.query(b'get-mark', b':1') as stdout:
            sha1 = stdout.read(41)
            assert sha1[-1:] == b'\n'
            return sha1[:40]

    @classmethod
    def put_blob(self, data=b'', want_sha1=True):
        with self.query(b'blob') as io:
            io.write(b'mark :1\n')
            io.write(b'data %d\n' % len(data))
            io.write(data)
            io.write(b'\n')
            io.flush()
            if want_sha1:
                return self._get_last()

    @classmethod
    @contextmanager
    def commit(self, ref, committer=b'<cinnabar@git> 0 +0000', author=None,
               message=b'', from_commit=None, parents=(), pseudo_mark=None):
        if isinstance(parents, GeneratorType):
            parents = tuple(parents)
        from_tree = None
        if parents and parents[0] == from_commit:
            _from = parents[0]
            merges = parents[1:]
        else:
            _from = NULL_NODE_ID
            merges = parents
            if from_commit:
                with self.query(b'ls', from_commit, b'') as stdout:
                    line = stdout.readline()
                    if line.startswith(b'missing '):
                        from_tree = None
                    else:
                        mode, typ, from_tree, path = split_ls_tree(line[:-1])

        helper = CommitHelper()
        helper.write(b'mark :1\n')
        # TODO: properly handle errors, like from the committer being badly
        # formatted.
        if author:
            helper.write(b'author %s\n' % author)
        helper.write(b'committer %s\n' % committer)
        helper.cmd_data(message)

        helper.write(b'from %s\n' % _from)
        for merge in merges:
            helper.write(b'merge %s\n' % merge)
        if from_tree:
            helper.write(b'M 040000 %s \n' % from_tree)

        yield helper
        helper.write(b'\n')

        with self.query(b'commit', ref) as stdout:
            stdout.write(helper.flush_buffer())
            stdout.flush()

        if not pseudo_mark:
            helper.sha1 = self._get_last()

    @classmethod
    def close(self, rollback=True):
        if not rollback and self._helper != self:
            with self.query(b'done'):
                pass
        super(GitHgHelper, self).close() def progress_iter(fmt, iter):
    return progress_enum(fmt, enumerate(iter, start=1)) class StreamHandler(logging.StreamHandler):
class Formatter(logging.Formatter):
class ConfigSetFunc(object):
class Progress(object):
class IOLogger(object):
class OrderedDefaultDict(OrderedDict):
class VersionedDict(object):
class lrucache(object):
    class node(object):
class chunkbuffer(object):
class HTTPReader(object):
class Seekable(object):
class Process(object):
class TypedProperty(object):
class MemoryCPUReporter(Thread):
class VersionCheck(Thread):
    def __init__(self):
    def emit(self, record):
    def __init__(self):
    def format(self, record):
def init_logging():
    def __init__(self, key, values, extra_values=(), default='', remote=None):
    def __call__(self, name):
def interval_expired(config_key, interval, globl=False):
    def __init__(self, fmt):
    def progress(self, count=None):
    def _print_count(self, count, t1=None):
    def finish(self, count=None):
def progress_iter(fmt, iter):
def progress_enum(fmt, enum_iter):
    def __init__(self, logger, reader, writer=None, prefix=''):
    def read(self, length=None, level=logging.INFO):
    def readline(self, level=logging.INFO):
    def write(self, data, level=logging.INFO):
    def flush(self):
    def __iter__(self):
def one(iterable):
def strip_suffix(s, suffix):
    def __init__(self, default_factory, *args, **kwargs):
    def __missing__(self, key):
    def __init__(self, content=None, **kwargs):
    def update(self, content=None, **kwargs):
    def __getitem__(self, key):
    def get(self, key, default=None):
    def __contains__(self, key):
    def __delitem__(self, key):
    def __setitem__(self, key, value):
    def __len__(self):
    def keys(self):
    def values(self):
    def __iter__(self):
    def items(self):
    def iteritems(self):
    def iterchanges(self):
    def flattened(self):
def _iter_diff_blocks(a, b):
def byte_diff(a, b):
def sorted_merge(iter_a, iter_b, key=lambda i: i[0], non_key=lambda i: i[1:]):
        def __init__(self):
        def insert(self, after):
        def detach(self):
    def __init__(self, size):
    def __call__(self, func):
        def wrapper(*args):
    def invalidate(self, *args):
    def __getitem__(self, key):
    def __setitem__(self, key, value):
    def __delitem__(self, key):
    def __len__(self):
    def __init__(self, in_iter):
        def splitbig(chunks):
    def read(self, l=None):
    def __init__(self, url):
    def read(self, size):
    def _reopen(self):
    def readable(self):
    def readinto(self, b):
    def __init__(self, reader, length):
    def read(self, length):
    def tell(self):
    def seek(self, pos, how=os.SEEK_SET):
    def __init__(self, *args, **kwargs):
    def _env_strings(self, env):
    def _popen(self, cmd, env, **kwargs):
    def wait(self):
    def pid(self):
    def stdin(self):
    def stdout(self):
    def stderr(self):
    def __init__(self, cls):
    def __get__(self, obj, cls=None):
    def __set__(self, obj, value):
    def __init__(self, memory=False, cpu=False):
    def _report(self, proc):
    def run(self):
    def shutdown(self):
    def __init__(self):
    def run(self):
    def join(self):
def run(func, args):
    def iteritems(d):
    def itervalues(d):
    def environ(k=None):
    def iteritems(d):
    def itervalues(d):
    def fsencode(s):
    def fsdecode(s):
    def environ(k=None):
    CREATED = 1
    MODIFIED = 2
    REMOVED = 3
        REPO = 'https://github.com/glandium/git-cinnabar' def fsdecode(s):
    return s
from binascii import hexlify, unhexlify
    from itertools import izip as zip
    from urllib.parse import quote_from_bytes, unquote_to_bytes
    from urllib import quote as quote_from_bytes
    from urllib import unquote as unquote_to_bytes
from collections import (
    OrderedDict,
    defaultdict,
)
    from collections.abc import Sequence
    from collections import Sequence
    from urllib2 import URLError
    from urllib.error import URLError
    from urlparse import urlparse
    from urllib.parse import urlparse
from .exceptions import (
    Abort,
    AmbiguousGraftAbort,
    NothingToGraftException,
    OldUpgradeAbort,
    UpgradeAbort,
)
from .util import (
    HTTPReader,
    Seekable,
    byte_diff,
    check_enabled,
    one,
    VersionedDict,
)
from .git import (
    EMPTY_BLOB,
    EMPTY_TREE,
    Git,
    GitProcess,
    NULL_NODE_ID,
)
from .hg.changegroup import (
    RawRevChunk,
    RevDiff,
)
from .hg.objects import (
    Authorship,
    Changeset,
    File,
    Manifest,
)
from .helper import GitHgHelper
from .util import progress_iter
from cinnabar import util
from cinnabar.util import fsdecode
                from gzip import GzipFile
        from .hg.repo import getbundle_params, stored_files
import io
import os
import shutil
import subprocess
import sys
import logging
            import json

        self.tag_changes = False

        metadata = self.metadata()
        if metadata:
            metadata, refs = metadata
        self._has_metadata = bool(metadata)
        self._metadata_refs = refs if metadata else {}
        self._manifest_heads_orig = set()
        self._generation = 0
        if metadata:
            changesets_ref = self._metadata_refs.get(
                b'refs/cinnabar/changesets')
            if changesets_ref:
                commit = GitCommit(changesets_ref)
                for n, head in enumerate(commit.body.splitlines()):
                    hghead, branch = head.split(b' ', 1)
                    self._hgheads._previous[hghead] = (branch, n)
                    self._generation = n + 1

            self._manifest_heads_orig = set(GitHgHelper.heads(b'manifests'))

            replace = {}
            for line in Git.ls_tree(metadata.tree):
                mode, typ, sha1, path = line
                replace[path] = sha1

            if self._replace and not replace:
                raise OldUpgradeAbort()

            # Delete old tag-cache, which may contain incomplete data.
            Git.delete_ref(b'refs/cinnabar/tag-cache')

    def prepare_graft(self):
        self._graft = Grafter(self)

    @staticmethod
    def _try_merge_branches(repo_url):
        parsed_url = urlparse(repo_url)
        branches = []
        path = parsed_url.path.lstrip(b'/').rstrip(b'/')
        if path:
            parts = list(reversed(path.split(b'/')))
        else:
            parts = []
        host = parsed_url.netloc.split(b':', 1)[0]
        if host:
            parts.append(host)
        last_path = b''
        for part in parts:
            if last_path:
                last_path = b'%s/%s' % (part, last_path)
            else:
                last_path = part
            branches.append(last_path)
        branches.append(b'metadata')
        return branches

    @staticmethod
    def _find_branch(branches, remote_refs):
        for branch in branches:
            if branch in remote_refs:
                return branch
            if b'refs/cinnabar/%s' % branch in remote_refs:
                return b'refs/cinnabar/%s' % branch
            if b'refs/heads/%s' % branch in remote_refs:
                return b'refs/heads/%s' % branch

    def merge(self, git_repo_url, hg_repo_url, branch=None):
        # Eventually we'll want to handle a full merge, but for now, we only
        # handle the case where we don't have metadata to begin with.
        # The caller should avoid calling this function otherwise.
        assert not self._has_metadata
        remote_refs = OrderedDict()
        for line in Git.iter('ls-remote', fsdecode(git_repo_url),
                             stderr=open(os.devnull, 'wb')):
            sha1, ref = line.split(None, 1)
            remote_refs[ref] = sha1
        bundle = None
        if not remote_refs and urlparse(git_repo_url).scheme in (b'http',
                                                                 b'https'):
            try:
                bundle = HTTPReader(git_repo_url)
            except URLError as e:
                logging.error(e.reason)
                return False
            if bundle.fh.headers.get('Content-Encoding', 'identity') == 'gzip':
                bundle = Seekable(bundle, bundle.length)
                bundle = GzipFile(mode='rb', fileobj=bundle)
            BUNDLE_SIGNATURE = b'# v2 git bundle\n'
            signature = bundle.read(len(BUNDLE_SIGNATURE))
            if signature != BUNDLE_SIGNATURE:
                logging.error('Could not find cinnabar metadata')
                return False
            bundle = io.BufferedReader(bundle)
            while True:
                line = bundle.readline().rstrip()
                if not line:
                    break
                sha1, ref = line.split(b' ', 1)
                remote_refs[ref] = sha1
        if branch:
            branches = [branch]
        else:
            branches = self._try_merge_branches(hg_repo_url)

        ref = self._find_branch(branches, remote_refs)
        if ref is None:
            logging.error('Could not find cinnabar metadata')
            return False

        if bundle:
            args = ('-v',) if util.progress else ()
            proc = GitProcess('index-pack', '--stdin', '--fix-thin', *args,
                              stdin=subprocess.PIPE,
                              stdout=open(os.devnull, 'wb'))
            shutil.copyfileobj(bundle, proc.stdin)
        else:
            fetch = ['fetch', '--no-tags', '--no-recurse-submodules', '-q']
            fetch.append('--progress' if util.progress else '--no-progress')

            fetch.append(fsdecode(git_repo_url))