mercurial/streamclone.py
author Dirkjan Ochtman <dirkjan@ochtman.nl>
Fri, 16 Jul 2010 22:20:19 +0200
changeset 11624 67260651d09d
parent 10905 13a1b2fb7ef2
permissions -rw-r--r--
protocol: convert StreamException to generated error code This makes it much easier to handle these errors at the transport level.

# streamclone.py - streaming clone server support for mercurial
#
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

import util, error
from mercurial import store

# if server supports streaming clone, it advertises "stream"
# capability with value that is version+flags of repo it is serving.
# client only streams if it can read that repo format.

# stream file format is simple.
#
# server writes out line that says how many files, how many total
# bytes.  separator is ascii space, byte counts are strings.
#
# then for each file:
#
#   server writes out line that says filename, how many bytes in
#   file.  separator is ascii nul, byte count is string.
#
#   server writes out raw file data.

def allowed(ui):
    return ui.configbool('server', 'uncompressed', True, untrusted=True)

def stream_out(repo):
    '''stream out all metadata files in repository.
    writes to file-like object, must support write() and optional flush().'''

    if not allowed(repo.ui):
        yield '1\n' # error: 1
        return

    entries = []
    total_bytes = 0
    try:
        # get consistent snapshot of repo, lock during scan
        lock = repo.lock()
        try:
            repo.ui.debug('scanning\n')
            for name, ename, size in repo.store.walk():
                entries.append((name, size))
                total_bytes += size
        finally:
            lock.release()
    except error.LockError:
        yield '2\n' # error: 2
        return

    yield '0\n' # success
    repo.ui.debug('%d files, %d bytes to transfer\n' %
                  (len(entries), total_bytes))
    yield '%d %d\n' % (len(entries), total_bytes)
    for name, size in entries:
        repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
        # partially encode name over the wire for backwards compat
        yield '%s\0%d\n' % (store.encodedir(name), size)
        for chunk in util.filechunkiter(repo.sopener(name), limit=size):
            yield chunk