contrib/hgclient.py
author Durham Goode <durham@fb.com>
Fri, 19 Sep 2014 18:43:53 -0700
changeset 22573 f528bfb25b45
parent 22572 cc3d9f776632
child 22991 a94594f5d52f
permissions -rw-r--r--
revert: special case 'hg revert --all' On large repos, hg revert --all can take over 13 seconds. This is mainly due to it walking the tree three times: once to find the list of files in the dirstate, once to find the list of files in the target, and once to compute the status from the dirstate to the target. This optimizes the hg revert --all case to only require the final status. This speeds it up to 1.3 seconds or so (with hgwatchman enabled). Further optimizations could be done for the -r NODE and pattern cases, but they are significantly more complex.

# A minimal client for Mercurial's command server

import sys, struct, subprocess, cStringIO

def connect(path=None):
    cmdline = ['hg', 'serve', '--cmdserver', 'pipe']
    if path:
        cmdline += ['-R', path]

    server = subprocess.Popen(cmdline, stdin=subprocess.PIPE,
                              stdout=subprocess.PIPE)

    return server

def writeblock(server, data):
    server.stdin.write(struct.pack('>I', len(data)))
    server.stdin.write(data)
    server.stdin.flush()

def readchannel(server):
    data = server.stdout.read(5)
    if not data:
        raise EOFError
    channel, length = struct.unpack('>cI', data)
    if channel in 'IL':
        return channel, length
    else:
        return channel, server.stdout.read(length)

def sep(text):
    return text.replace('\\', '/')

def runcommand(server, args, output=sys.stdout, error=sys.stderr, input=None,
               outfilter=lambda x: x):
    print '*** runcommand', ' '.join(args)
    sys.stdout.flush()
    server.stdin.write('runcommand\n')
    writeblock(server, '\0'.join(args))

    if not input:
        input = cStringIO.StringIO()

    while True:
        ch, data = readchannel(server)
        if ch == 'o':
            output.write(outfilter(data))
            output.flush()
        elif ch == 'e':
            error.write(data)
            error.flush()
        elif ch == 'I':
            writeblock(server, input.read(data))
        elif ch == 'L':
            writeblock(server, input.readline(data))
        elif ch == 'r':
            ret, = struct.unpack('>i', data)
            if ret != 0:
                print ' [%d]' % ret
            return ret
        else:
            print "unexpected channel %c: %r" % (ch, data)
            if ch.isupper():
                return

def check(func, repopath=None):
    sys.stdout.flush()
    server = connect(repopath)
    try:
        return func(server)
    finally:
        server.stdin.close()
        server.wait()