view mercurial/sshserver.py @ 25878:800e090e9c64 stable

localrepo: make journal.dirstate contain in-memory changes before transaction Before this patch, in-memory dirstate changes aren't written out at opening transaction, even though 'journal.dirstate' is created directly from '.hg/dirstate'. Therefore, subsequent 'hg rollback' uses incomplete 'undo.dirstate' to restore dirstate, if dirstate is changed and isn't written out before opening transaction. In cases below, the condition "dirstate is changed and isn't written out before opening transaction" isn't satisfied and this problem doesn't appear: - "wlock scope" and "transaction scope" are almost equivalent e.g. 'commit --amend', 'import' and so on - dirstate changes are written out before opening transaction e.g. 'rebase' (via 'dirstateguard') and 'commit -A' (by separated wlock scopes) On the other hand, 'backout' may satisfy the condition above. To make 'journal.dirstate' contain in-memory changes before opening transaction, this patch explicitly invokes 'dirstate.write()' in 'localrepository.transaction()'. 'dirstate.write()' is placed before not "writing journal files out" but "invoking pretxnopen hooks" for visibility of dirstate changes to external hook processes. BTW, in the test script, 'touch -t 200001010000' and 'hg status' are invoked to make file 'c' surely clean in dirstate, because "clean but unsure" files indirectly cause 'dirstate.write()' at 'repo.status()' in 'repo.commit()' (see fe03f522dda9 for detail) and prevents from certainly reproducing the issue.
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Thu, 30 Jul 2015 06:16:12 +0900
parents 9f6e0e7ef828
children 72b36785d7f4
line wrap: on
line source

# sshserver.py - ssh protocol server support for mercurial
#
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
# 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, hook, wireproto
import os, sys

class sshserver(wireproto.abstractserverproto):
    def __init__(self, ui, repo):
        self.ui = ui
        self.repo = repo
        self.lock = None
        self.fin = ui.fin
        self.fout = ui.fout

        hook.redirect(True)
        ui.fout = repo.ui.fout = ui.ferr

        # Prevent insertion/deletion of CRs
        util.setbinary(self.fin)
        util.setbinary(self.fout)

    def getargs(self, args):
        data = {}
        keys = args.split()
        for n in xrange(len(keys)):
            argline = self.fin.readline()[:-1]
            arg, l = argline.split()
            if arg not in keys:
                raise util.Abort("unexpected parameter %r" % arg)
            if arg == '*':
                star = {}
                for k in xrange(int(l)):
                    argline = self.fin.readline()[:-1]
                    arg, l = argline.split()
                    val = self.fin.read(int(l))
                    star[arg] = val
                data['*'] = star
            else:
                val = self.fin.read(int(l))
                data[arg] = val
        return [data[k] for k in keys]

    def getarg(self, name):
        return self.getargs(name)[0]

    def getfile(self, fpout):
        self.sendresponse('')
        count = int(self.fin.readline())
        while count:
            fpout.write(self.fin.read(count))
            count = int(self.fin.readline())

    def redirect(self):
        pass

    def groupchunks(self, changegroup):
        while True:
            d = changegroup.read(4096)
            if not d:
                break
            yield d

    def sendresponse(self, v):
        self.fout.write("%d\n" % len(v))
        self.fout.write(v)
        self.fout.flush()

    def sendstream(self, source):
        write = self.fout.write
        for chunk in source.gen:
            write(chunk)
        self.fout.flush()

    def sendpushresponse(self, rsp):
        self.sendresponse('')
        self.sendresponse(str(rsp.res))

    def sendpusherror(self, rsp):
        self.sendresponse(rsp.res)

    def sendooberror(self, rsp):
        self.ui.ferr.write('%s\n-\n' % rsp.message)
        self.ui.ferr.flush()
        self.fout.write('\n')
        self.fout.flush()

    def serve_forever(self):
        try:
            while self.serve_one():
                pass
        finally:
            if self.lock is not None:
                self.lock.release()
        sys.exit(0)

    handlers = {
        str: sendresponse,
        wireproto.streamres: sendstream,
        wireproto.pushres: sendpushresponse,
        wireproto.pusherr: sendpusherror,
        wireproto.ooberror: sendooberror,
    }

    def serve_one(self):
        cmd = self.fin.readline()[:-1]
        if cmd and cmd in wireproto.commands:
            rsp = wireproto.dispatch(self.repo, self, cmd)
            self.handlers[rsp.__class__](self, rsp)
        elif cmd:
            impl = getattr(self, 'do_' + cmd, None)
            if impl:
                r = impl()
                if r is not None:
                    self.sendresponse(r)
            else: self.sendresponse("")
        return cmd != ''

    def _client(self):
        client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
        return 'remote:ssh:' + client