[PATCH V4] serve: add support for Mercurial subrepositories

Matt Harbison mharbison72 at gmail.com
Sat Apr 15 19:09:20 EDT 2017


This depends on the hosting at '/' patch submitted earlier today.

> On Apr 15, 2017, at 7:00 PM, Matt Harbison <mharbison72 at gmail.com> wrote:
> 
> # HG changeset patch
> # User Matt Harbison <matt_harbison at yahoo.com>
> # Date 1492293940 14400
> #      Sat Apr 15 18:05:40 2017 -0400
> # Node ID 2804acb18b59dec98abb216d813cc0e3ecec4e14
> # Parent  3fd50d5f9314a96f43eb73480763f224c4d05831
> serve: add support for Mercurial subrepositories
> 
> I've been using `hg serve --web-conf ...` with a simple '/=projects/**' [paths]
> configuration for awhile without issue.  Let's ditch the need for the manual
> configuration in this case, and limit the repos served to the actual subrepos.
> 
> This doesn't attempt to handle the case where a new subrepo appears while the
> server is running.  That could probably be handled with a hook if somebody wants
> it.  But it's such a rare case, it probably doesn't matter for the temporary
> serves.
> 
> The main repo is served at '/', just like a repository without subrepos.  I'm
> not sure why the duplicate 'adding ...' lines appear on Linux.  They don't
> appear on Windows (see 594dd384803c), so they are optional.
> 
> Subrepositories that are configured with '../path' or absolute paths are not
> cloneable from the server.  (They aren't cloneable locally either, unless they
> also exist at their configured source, perhaps via the share extension.)  They
> are still served, so that they can be browsed, or cloned individually.  If we
> care about that cloning someday, we can probably just add the extra entries to
> the webconf dictionary.  Even if the entries use '../' to escape the root, only
> the related subrepositories would end up in the dictionary.
> 
> diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
> --- a/mercurial/cmdutil.py
> +++ b/mercurial/cmdutil.py
> @@ -2290,6 +2290,15 @@
>         bad.extend(f for f in rejected if f in match.files())
>     return bad
> 
> +def addwebdirpath(repo, serverpath, webconf):
> +    webconf[serverpath] = repo.root
> +    repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
> +
> +    for r in repo.revs('filelog("path:.hgsub")'):
> +        ctx = repo[r]
> +        for subpath in ctx.substate:
> +            ctx.sub(subpath).addwebdirpath(serverpath, webconf)
> +
> def forget(ui, repo, match, prefix, explicitonly):
>     join = lambda f: os.path.join(prefix, f)
>     bad = []
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -4619,7 +4619,8 @@
>     ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
>     ('', 'style', '', _('template style to use'), _('STYLE')),
>     ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
> -    ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
> +    ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
> +     + subrepoopts,
>     _('[OPTION]...'),
>     optionalrepo=True)
> def serve(ui, repo, **opts):
> diff --git a/mercurial/help/subrepos.txt b/mercurial/help/subrepos.txt
> --- a/mercurial/help/subrepos.txt
> +++ b/mercurial/help/subrepos.txt
> @@ -136,6 +136,10 @@
>     subrepository changes are available when referenced by top-level
>     repositories.  Push is a no-op for Subversion subrepositories.
> 
> +:serve: serve does not recurse into subrepositories unless
> +    -S/--subrepos is specified.  Git and Subversion subrepositories
> +    are currently silently ignored.
> +
> :status: status does not recurse into subrepositories unless
>     -S/--subrepos is specified. Subrepository changes are displayed as
>     regular Mercurial changes on the subrepository
> diff --git a/mercurial/server.py b/mercurial/server.py
> --- a/mercurial/server.py
> +++ b/mercurial/server.py
> @@ -15,6 +15,7 @@
> 
> from . import (
>     chgserver,
> +    cmdutil,
>     commandserver,
>     error,
>     hgweb,
> @@ -130,11 +131,22 @@
>         baseui = ui
>     webconf = opts.get('web_conf') or opts.get('webdir_conf')
>     if webconf:
> +        if opts.get('subrepos'):
> +            raise error.Abort(_('--web-conf cannot be used with --subrepos'))
> +
>         # load server settings (e.g. web.port) to "copied" ui, which allows
>         # hgwebdir to reload webconf cleanly
>         servui = ui.copy()
>         servui.readconfig(webconf, sections=['web'])
>         alluis.add(servui)
> +    elif opts.get('subrepos'):
> +        servui = ui
> +
> +        # If repo is None, hgweb.createapp() already raises a proper abort
> +        # message as long as webconf is None.
> +        if repo:
> +            webconf = dict()
> +            cmdutil.addwebdirpath(repo, "", webconf)
>     else:
>         servui = ui
> 
> diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
> --- a/mercurial/subrepo.py
> +++ b/mercurial/subrepo.py
> @@ -444,6 +444,15 @@
>         self._ctx = ctx
>         self._path = path
> 
> +    def addwebdirpath(self, serverpath, webconf):
> +        """Add the hgwebdir entries for this subrepo, and any of its subrepos.
> +
> +        ``serverpath`` is the path component of the URL for this repo.
> +
> +        ``webconf`` is the dictionary of hgwebdir entries.
> +        """
> +        pass
> +
>     def storeclean(self, path):
>         """
>         returns true if the repository has not changed since it was last
> @@ -651,6 +660,10 @@
>         self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
>         self._initrepo(r, state[0], create)
> 
> +    @annotatesubrepoerror
> +    def addwebdirpath(self, serverpath, webconf):
> +        cmdutil.addwebdirpath(self._repo, subrelpath(self), webconf)
> +
>     def storeclean(self, path):
>         with self._repo.lock():
>             return self._storeclean(path)
> diff --git a/tests/test-completion.t b/tests/test-completion.t
> --- a/tests/test-completion.t
> +++ b/tests/test-completion.t
> @@ -184,6 +184,7 @@
>   --repository
>   --stdio
>   --style
> +  --subrepos
>   --templates
>   --time
>   --traceback
> @@ -194,6 +195,7 @@
>   -A
>   -E
>   -R
> +  -S
>   -a
>   -d
>   -h
> @@ -225,7 +227,7 @@
>   pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
>   push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
>   remove: after, force, subrepos, include, exclude
> -  serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
> +  serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, subrepos
>   status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos, template
>   summary: remote
>   update: clean, check, merge, date, rev, tool
> diff --git a/tests/test-subrepo-deep-nested-change.t b/tests/test-subrepo-deep-nested-change.t
> --- a/tests/test-subrepo-deep-nested-change.t
> +++ b/tests/test-subrepo-deep-nested-change.t
> @@ -73,6 +73,43 @@
>   adding main/main (glob)
>   $ hg commit -R main -m "main import"
> 
> +#if serve
> +
> +Unfortunately, subrepos not at their nominal location cannot be cloned.  But
> +they are still served from their location within the local repository.  The only
> +reason why 'main' can be cloned via the filesystem is because 'sub1' and 'sub2'
> +are also available as siblings of 'main'.
> +
> +  $ hg serve -R main --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
> +  adding  = $TESTTMP/main (glob)
> +  adding sub1 = $TESTTMP/main/sub1 (glob)
> +  adding sub1/sub2 = $TESTTMP/main/sub1/sub2 (glob)
> +  listening at http://*:$HGPORT/ (bound to *:$HGPORT) (glob) (?)
> +  adding  = $TESTTMP/main (glob) (?)
> +  adding sub1 = $TESTTMP/main/sub1 (glob) (?)
> +  adding sub1/sub2 = $TESTTMP/main/sub1/sub2 (glob) (?)
> +  $ cat hg1.pid >> $DAEMON_PIDS
> +
> +  $ hg clone http://localhost:$HGPORT httpclone --config progress.disable=True
> +  requesting all changes
> +  adding changesets
> +  adding manifests
> +  adding file changes
> +  added 1 changesets with 3 changes to 3 files
> +  updating to branch default
> +  abort: HTTP Error 404: Not Found
> +  [255]
> +
> +  $ cat access.log
> +  * "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
> +  * "GET /?cmd=batch HTTP/1.1" 200 - * (glob)
> +  * "GET /?cmd=getbundle HTTP/1.1" 200 - * (glob)
> +  * "GET /../sub1?cmd=capabilities HTTP/1.1" 404 - (glob)
> +
> +  $ killdaemons.py
> +  $ rm hg1.pid error.log access.log
> +#endif
> +
> Cleaning both repositories, just as a clone -U
> 
>   $ hg up -C -R sub2 null
> diff --git a/tests/test-subrepo-recursion.t b/tests/test-subrepo-recursion.t
> --- a/tests/test-subrepo-recursion.t
> +++ b/tests/test-subrepo-recursion.t
> @@ -251,6 +251,60 @@
>    z1
>   +z2
> 
> +#if serve
> +  $ cd ..
> +  $ hg serve -R repo --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
> +  adding  = $TESTTMP/repo (glob)
> +  adding foo = $TESTTMP/repo/foo (glob)
> +  adding foo/bar = $TESTTMP/repo/foo/bar (glob)
> +  listening at http://*:$HGPORT/ (bound to *:$HGPORT) (glob) (?)
> +  adding  = $TESTTMP/repo (glob) (?)
> +  adding foo = $TESTTMP/repo/foo (glob) (?)
> +  adding foo/bar = $TESTTMP/repo/foo/bar (glob) (?)
> +  $ cat hg1.pid >> $DAEMON_PIDS
> +
> +  $ hg clone http://localhost:$HGPORT clone  --config progress.disable=True
> +  requesting all changes
> +  adding changesets
> +  adding manifests
> +  adding file changes
> +  added 3 changesets with 5 changes to 3 files
> +  updating to branch default
> +  cloning subrepo foo from http://localhost:$HGPORT/foo
> +  requesting all changes
> +  adding changesets
> +  adding manifests
> +  adding file changes
> +  added 4 changesets with 7 changes to 3 files
> +  cloning subrepo foo/bar from http://localhost:$HGPORT/foo/bar (glob)
> +  requesting all changes
> +  adding changesets
> +  adding manifests
> +  adding file changes
> +  added 3 changesets with 3 changes to 1 files
> +  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +
> +  $ cat clone/foo/bar/z.txt
> +  z1
> +  z2
> +  z3
> +
> +  $ cat access.log
> +  * "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
> +  * "GET /?cmd=batch HTTP/1.1" 200 - * (glob)
> +  * "GET /?cmd=getbundle HTTP/1.1" 200 - * (glob)
> +  * "GET /foo?cmd=capabilities HTTP/1.1" 200 - (glob)
> +  * "GET /foo?cmd=batch HTTP/1.1" 200 - * (glob)
> +  * "GET /foo?cmd=getbundle HTTP/1.1" 200 - * (glob)
> +  * "GET /foo/bar?cmd=capabilities HTTP/1.1" 200 - (glob)
> +  * "GET /foo/bar?cmd=batch HTTP/1.1" 200 - * (glob)
> +  * "GET /foo/bar?cmd=getbundle HTTP/1.1" 200 - * (glob)
> +
> +  $ killdaemons.py
> +  $ rm hg1.pid error.log access.log
> +  $ cd repo
> +#endif
> +
> Enable progress extension for archive tests:
> 
>   $ cp $HGRCPATH $HGRCPATH.no-progress


More information about the Mercurial-devel mailing list