My experiences with Mercurial

Brendan Cully brendan at kublai.com
Sun Jun 17 22:39:52 UTC 2007


On Sunday, 17 June 2007 at 18:38, mirza wrote:
> Hi,
> > So really what we need is a simple & secure way  for the users 
> > (developers in this case) to share their changesets.  "hg serve" seems 
> > to fit the definition of simple but not secure. The  trouble of 
> > setting up IIS/Apache and configuring SSL or setting up  ssh servers 
> > on each developer's machine (win & unix) outweighs the  benefits of 
> > using mercurial. We were mainly looking to remove the  need for the 
> > central server.
> 
> I had this problem too, so I proposed exchanging bundles via email/ftp 
> with --create-digest option. It is Issue 392 now and I don't have idea 
> when/if it will be implemented. That would certainly solve problem for 
> all those people that doesn't want even to _think_ about setting up 
> Apache for this (sounds sooooo undistributed to me).

Something like that doesn't sound too tricky. Here's a prototype
extension I knocked up a few minutes ago. From 'hg help digest' with
the extension loaded:

hg digest FILE

Create a digest of the repository

    A digest is a small file containing a summary of the nodes in the
    repository. It may be used with outgoing or bundle in other repositories.
    The digest will be written into FILE.

    Example usage:
    cd repo1
    hg digest ../repo1.digest
    cd ../repo2
    hg bundle ../to-repo1.hg digest://../repo1.digest

I can't say it's particularly well tested though.
-------------- next part --------------
# Copyright (C) 2007 Brendan Cully <brendan at kublai.com>
# Published under the GNU GPL

'''digest extension'''

from mercurial.node import *
from mercurial.i18n import _
from mercurial import cmdutil, hg, remoterepo, revlog, util
import sys

class digestrepo(remoterepo.remoterepository):
    "Representation of a repository via a digest of its changelog nodes"
    SIGNATURE = 'HDDGGZ'

    def __init__(self, ui, path):
        self.nodes = []
        self.ui = ui
        self.path = path
        if path:
            self.read(path)

    def loadchangelog(self, changelog):
        self.nodes = [changelog.node(i) for i in xrange(changelog.count())]

    def write(self, filename):
        def chunknodes(nodes):
            CHUNKSIZE = 4096
            start = 0
            l = len(nodes)
            while start < l:
                yield ''.join(nodes[start:start+CHUNKSIZE])
                start += CHUNKSIZE

        fd = file(filename, "wb")
        fd.write(self.SIGNATURE)
        for i in chunknodes(self.nodes):
            fd.write(i)
        fd.close()

    def read(self, filename):
        def chunkstream(stream):
            start = 0
            l = len(stream)
            while start < l:
                yield stream[start:start+20]
                start += 20

        fd = file(filename, "rb")

        hdr = fd.read(6)
        if hdr != self.SIGNATURE:
            raise util.Abort("The digest file is not in a recognized format")

        nodes = []
        for n in chunkstream(fd.read()):
            nodes.append(n)
        self.nodes = nodes

    def dump(self):
        for n in self.nodes:
            print hex(n)

def digest(ui, repo, filename):
    '''Create a digest of the repository

    A digest is a small file containing a summary of the nodes in the
    repository. It may be used with outgoing or bundle in other repositories.
    The digest will be written into FILE.

    Example usage:
    cd repo1
    hg digest ../repo1.digest
    cd ../repo2
    hg bundle ../to-repo1.hg digest://../repo1.digest
'''
    d = digestrepo(ui, None)
    d.loadchangelog(repo.changelog)
    d.write(filename)

def reposetup(ui, repo):
    def addscheme(name, mod):
        hg.schemes[name] = mod

    class monkeyrepo(repo.__class__):
        def findoutgoing(self, remote, base=None, heads=None, force=False):
            remotedigest = isinstance(remote, digestrepo)
            if not base and remotedigest:
                base = {}
                parents = {}
                for n in remote.nodes:
                    try:
                        i = self.changelog.rev(n)
                        base[n] = 1
                    except revlog.LookupError:
                        pass

            s = super(monkeyrepo, self)
            return s.findoutgoing(remote, base=base, heads=heads, force=force)

    addscheme('digest', sys.modules[__name__])

    if repo.local():
        repo.__class__ = monkeyrepo

def instance(ui, path, create):
    if create:
        raise util.Abort(_('cannot create new digest repository'))
    return digestrepo(ui, util.drop_scheme('digest', path))

cmdtable = {
    'digest': (digest, [], "hg digest FILE")
}


More information about the Mercurial mailing list