[PATCH 1 of 2 V3] serve: add support for Mercurial subrepositories

Matt Harbison mharbison72 at gmail.com
Mon Mar 27 03:04:30 UTC 2017


# HG changeset patch
# User Matt Harbison <matt_harbison at yahoo.com>
# Date 1488146743 18000
#      Sun Feb 26 17:05:43 2017 -0500
# Node ID 0ff9bef3e0f67422cf29c200fa4a671d861d060b
# Parent  c60091fa1426892552dd6c0dd4b9c49e3c3da045
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.

Unfortunately, the root of the webserver when serving multiple repos is the
implicit html index file.  This makes the URL different for `hg serve` vs
`hg serve -S`, because the top level repo then needs to be part of the path.
That will be fixed next.

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -2285,6 +2285,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
@@ -4602,7 +4602,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,11 @@
     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.  Mercurial subrepositories with a
+    URL source are also 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,24 @@
         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.copy()
+        alluis.add(servui)
+
+        # 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, repo.wvfs.basename(repo.root) + '/',
+                                  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,17 @@
         self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
         self._initrepo(r, state[0], create)
 
+    @annotatesubrepoerror
+    def addwebdirpath(self, serverpath, webconf):
+        # The URL request contains the subrepo source path, not the local
+        # subrepo path.  The distinction matters for 'foo = ../foo' type
+        # entries.  It isn't possible to serve up 'foo = http://..' type
+        # entries, because the server path is relative to this local server.
+        src = self._state[0]
+        if util.url(src).islocal():
+            path = util.normpath(serverpath + src)
+            cmdutil.addwebdirpath(self._repo, path + '/', 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,53 @@
   adding main/main (glob)
   $ hg commit -R main -m "main import"
 
+#if serve
+  $ hg serve -R main --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
+  adding main/ = $TESTTMP/main (glob)
+  adding sub1/ = $TESTTMP/main/sub1 (glob)
+  adding sub2/ = $TESTTMP/main/sub1/sub2 (glob)
+  listening at http://*:$HGPORT/ (bound to *:$HGPORT) (glob)
+  adding main/ = $TESTTMP/main (glob)
+  adding sub1/ = $TESTTMP/main/sub1 (glob)
+  adding sub2/ = $TESTTMP/main/sub1/sub2 (glob)
+  $ cat hg1.pid >> $DAEMON_PIDS
+
+  $ hg clone http://localhost:$HGPORT/main 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
+  cloning subrepo sub1 from http://localhost:$HGPORT/sub1
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 3 changes to 3 files
+  cloning subrepo sub1/sub2 from http://localhost:$HGPORT/sub2 (glob)
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ cat access.log
+  * "GET /main?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /main?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /main?cmd=getbundle HTTP/1.1" 200 - * (glob)
+  * "GET /sub1?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /sub1?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /sub1?cmd=getbundle HTTP/1.1" 200 - * (glob)
+  * "GET /sub2?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /sub2?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /sub2?cmd=getbundle HTTP/1.1" 200 - * (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 repo/ = $TESTTMP/repo (glob)
+  adding repo/foo/ = $TESTTMP/repo/foo (glob)
+  adding repo/foo/bar/ = $TESTTMP/repo/foo/bar (glob)
+  listening at http://*:$HGPORT/ (bound to *:$HGPORT) (glob)
+  adding repo/ = $TESTTMP/repo (glob)
+  adding repo/foo/ = $TESTTMP/repo/foo (glob)
+  adding repo/foo/bar/ = $TESTTMP/repo/foo/bar (glob)
+  $ cat hg1.pid >> $DAEMON_PIDS
+
+  $ hg clone http://localhost:$HGPORT/repo 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/repo/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/repo/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 /repo?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /repo?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /repo?cmd=getbundle HTTP/1.1" 200 - * (glob)
+  * "GET /repo/foo?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /repo/foo?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /repo/foo?cmd=getbundle HTTP/1.1" 200 - * (glob)
+  * "GET /repo/foo/bar?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /repo/foo/bar?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /repo/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