# HG changeset patch # User Matt Mackall # Date 1161129853 18000 # Node ID d96429ddc8e287457053c744f82e1279ed19b02a # Parent 357b5589dc62a0037e8460d46456f531768c1c51# Parent f29989e9746ebf5a1fd8f01580d0875fc1ae9386 Merge with crew diff --git a/MANIFEST.in b/MANIFEST.in --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,7 @@ include hg recursive-include mercurial *.py include hgweb.cgi hgwebdir.cgi include hgeditor rewrite-log -include tests/README tests/*.py tests/test-*[a-z0-9] tests/*.out +include tests/README tests/*.py tests/test-*[a-z0-9] tests/*.out tests/*.bin prune tests/*.err include *.txt include doc/README doc/Makefile doc/gendoc.py doc/*.txt doc/*.html doc/*.[0-9] diff --git a/contrib/macosx/Readme.html b/contrib/macosx/Readme.html --- a/contrib/macosx/Readme.html +++ b/contrib/macosx/Readme.html @@ -25,7 +25,7 @@


After you install


-

This package installs the hg executable in /usr/local/bin. This directory may not be in your shell's search path. Don't forget to check.

+

This package installs the hg executable in /Library/Frameworks/Python.framework/Versions/Current/bin. This directory may not be in your shell's search path. The MacPython installer will have created an entry in .profile for it but if your shell doesn't use .profile you'll need configure it yourself or create a symlink from a directory already in your path.


Reporting problems


diff --git a/contrib/win32/ReadMe.html b/contrib/win32/ReadMe.html --- a/contrib/win32/ReadMe.html +++ b/contrib/win32/ReadMe.html @@ -46,7 +46,7 @@ hg other Mercurial commands should work fine for you.

Configuration notes

-

The default editor for commit messages is 'vi'. You can set the EDITOR +

The default editor for commit messages is 'notepad'. You can set the EDITOR (or HGEDITOR) environment variable to specify your preference or set it in mercurial.ini:

diff --git a/contrib/win32/mercurial.ini b/contrib/win32/mercurial.ini
--- a/contrib/win32/mercurial.ini
+++ b/contrib/win32/mercurial.ini
@@ -3,6 +3,9 @@
 ; USERNAME is your Windows user name:
 ;   C:\Documents and Settings\USERNAME\Mercurial.ini
 
+[ui] 
+editor = notepad
+
 ; By default, we try to encode and decode all files that do not
 ; contain ASCII NUL characters.  What this means is that we try to set
 ; line endings to Windows style on update, and to Unix style on
diff --git a/hgext/acl.py b/hgext/acl.py
--- a/hgext/acl.py
+++ b/hgext/acl.py
@@ -80,7 +80,7 @@ class checker(object):
         self.user = getpass.getuser()
         cfg = self.ui.config('acl', 'config')
         if cfg:
-            self.ui.readconfig(cfg)
+            self.ui.readsections(cfg, 'acl.allow', 'acl.deny')
         self.allow, self.allowable = self.buildmatch('acl.allow')
         self.deny, self.deniable = self.buildmatch('acl.deny')
 
diff --git a/hgext/bugzilla.py b/hgext/bugzilla.py
--- a/hgext/bugzilla.py
+++ b/hgext/bugzilla.py
@@ -74,7 +74,7 @@ class bugzilla_2_16(object):
         timeout = int(self.ui.config('bugzilla', 'timeout', 5))
         usermap = self.ui.config('bugzilla', 'usermap')
         if usermap:
-            self.ui.readconfig(usermap)
+            self.ui.readsections(usermap, 'usermap')
         self.ui.note(_('connecting to %s:%s as %s, password %s\n') %
                      (host, db, user, '*' * len(passwd)))
         self.conn = MySQLdb.connect(host=host, user=user, passwd=passwd,
diff --git a/hgext/notify.py b/hgext/notify.py
--- a/hgext/notify.py
+++ b/hgext/notify.py
@@ -102,7 +102,7 @@ class notifier(object):
         self.ui = ui
         cfg = self.ui.config('notify', 'config')
         if cfg:
-            self.ui.readconfig(cfg)
+            self.ui.readsections(cfg, 'usersubs', 'reposubs')
         self.repo = repo
         self.stripcount = int(self.ui.config('notify', 'strip', 0))
         self.root = self.strip(self.repo.root)
diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py
--- a/mercurial/bundlerepo.py
+++ b/mercurial/bundlerepo.py
@@ -233,10 +233,12 @@ class bundlerepository(localrepo.localre
         self.bundlefile.close()
 
     def __del__(self):
-        if not self.bundlefile.closed:
-            self.bundlefile.close()
-        if self.tempfile is not None:
-            os.unlink(self.tempfile)
+        bundlefile = getattr(self, 'bundlefile', None)
+        if bundlefile and not bundlefile.closed:
+            bundlefile.close()
+        tempfile = getattr(self, 'tempfile', None)
+        if tempfile is not None:
+            os.unlink(tempfile)
 
 def instance(ui, path, create):
     if create:
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -28,7 +28,7 @@ def _up(p):
         return "/"
     return up + "/"
 
-def revnavgen(pos, pagelen, limit):
+def revnavgen(pos, pagelen, limit, nodefunc):
     def seq(factor, limit=None):
         if limit:
             yield limit
@@ -50,16 +50,19 @@ def revnavgen(pos, pagelen, limit):
                 break
             last = f
             if pos + f < limit:
-                l.append(("+%d" % f, pos + f))
+                l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
             if pos - f >= 0:
-                l.insert(0, ("-%d" % f, pos - f))
+                l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
+
+        try:
+            yield {"label": "(0)", "node": hex(nodefunc('0').node())}
 
-        yield {"label": "(0)", "rev": 0}
+            for label, node in l:
+                yield {"label": label, "node": node}
 
-        for label, rev in l:
-            yield {"label": label, "rev": rev}
-
-        yield {"label": "tip", "rev": "tip"}
+            yield {"label": "tip", "node": "tip"}
+        except hg.RepoError:
+            pass
 
     return nav
 
@@ -215,7 +218,7 @@ class hgweb(object):
         end = min(count, start + maxchanges)
         pos = end - 1
 
-        changenav = revnavgen(pos, maxchanges, count)
+        changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
 
         yield self.t(shortlog and 'shortlog' or 'changelog',
                      changenav=changenav,
@@ -338,7 +341,8 @@ class hgweb(object):
             for e in l:
                 yield e
 
-        nav = revnavgen(pos, pagelen, count)
+        nodefunc = lambda x: fctx.filectx(fileid=x)
+        nav = revnavgen(pos, pagelen, count, nodefunc)
         yield self.t("filelog", file=f, node=hex(fctx.node()), nav=nav,
                      entries=entries)
 
@@ -743,13 +747,9 @@ class hgweb(object):
             style = req.form['style'][0]
         mapfile = style_map(self.templatepath, style)
 
-        if not req.url:
-            port = req.env["SERVER_PORT"]
-            port = port != "80" and (":" + port) or ""
-            uri = req.env["REQUEST_URI"]
-            if "?" in uri:
-                uri = uri.split("?")[0]
-            req.url = "http://%s%s%s" % (req.env["SERVER_NAME"], port, uri)
+        port = req.env["SERVER_PORT"]
+        port = port != "80" and (":" + port) or ""
+        urlbase = 'http://%s%s' % (req.env['SERVER_NAME'], port)
 
         if not self.reponame:
             self.reponame = (self.repo.ui.config("web", "name")
@@ -758,6 +758,7 @@ class hgweb(object):
 
         self.t = templater.templater(mapfile, templater.common_filters,
                                      defaults={"url": req.url,
+                                               "urlbase": urlbase,
                                                "repo": self.reponame,
                                                "header": header,
                                                "footer": footer,
diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -8,7 +8,7 @@
 
 import os
 from mercurial.demandload import demandload
-demandload(globals(), "ConfigParser mimetools cStringIO")
+demandload(globals(), "mimetools cStringIO")
 demandload(globals(), "mercurial:ui,hg,util,templater")
 demandload(globals(), "mercurial.hgweb.hgweb_mod:hgweb")
 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile,style_map")
@@ -30,7 +30,7 @@ class hgwebdir(object):
             self.repos = cleannames(config.items())
             self.repos.sort()
         else:
-            cp = ConfigParser.SafeConfigParser()
+            cp = util.configparser()
             cp.read(config)
             self.repos = []
             if cp.has_section('web'):
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -11,12 +11,14 @@ demandload(globals(), "errno getpass os 
 demandload(globals(), "ConfigParser traceback util")
 
 def dupconfig(orig):
-    new = ConfigParser.SafeConfigParser(orig.defaults())
+    new = util.configparser(orig.defaults())
     updateconfig(orig, new)
     return new
 
-def updateconfig(source, dest):
-    for section in source.sections():
+def updateconfig(source, dest, sections=None):
+    if not sections:
+        sections = source.sections()
+    for section in sections:
         if not dest.has_section(section):
             dest.add_section(section)
         for name, value in source.items(section, raw=True):
@@ -37,7 +39,7 @@ class ui(object):
             self.debugflag = debug
             self.interactive = interactive
             self.traceback = traceback
-            self.cdata = ConfigParser.SafeConfigParser()
+            self.cdata = util.configparser()
             self.readconfig(util.rcpath())
             self.updateopts(verbose, debug, quiet, interactive)
         else:
@@ -100,6 +102,23 @@ class ui(object):
     def addreadhook(self, hook):
         self.readhooks.append(hook)
 
+    def readsections(self, filename, *sections):
+        "read filename and add only the specified sections to the config data"
+        if not sections:
+            return
+
+        cdata = util.configparser()
+        try:
+            cdata.read(filename)
+        except ConfigParser.ParsingError, inst:
+            raise util.Abort(_("failed to parse %s\n%s") % (f, inst))
+
+        for section in sections:
+            if not cdata.has_section(section):
+                cdata.add_section(section)
+
+        updateconfig(cdata, self.cdata, sections)
+
     def fixconfig(self, section=None, name=None, value=None, root=None):
         # translate paths relative to root (or home) into absolute paths
         if section is None or section == 'paths':
@@ -126,7 +145,7 @@ class ui(object):
 
     def setconfig(self, section, name, value):
         if not self.overlay:
-            self.overlay = ConfigParser.SafeConfigParser()
+            self.overlay = util.configparser()
         for cdata in (self.overlay, self.cdata):
             if not cdata.has_section(section):
                 cdata.add_section(section)
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -15,7 +15,7 @@ platform-specific details from the core.
 from i18n import gettext as _
 from demandload import *
 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
-demandload(globals(), "os threading time calendar")
+demandload(globals(), "os threading time calendar ConfigParser")
 
 # used by parsedate
 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
@@ -24,6 +24,11 @@ defaultdateformats = ('%Y-%m-%d %H:%M:%S
 class SignalInterrupt(Exception):
     """Exception raised on SIGTERM and SIGHUP."""
 
+# like SafeConfigParser but with case-sensitive keys
+class configparser(ConfigParser.SafeConfigParser):
+    def optionxform(self, optionstr):
+        return optionstr
+
 def cachefunc(func):
     '''cache the result of function calls'''
     # XXX doesn't handle keywords args
diff --git a/templates/gitweb/map b/templates/gitweb/map
--- a/templates/gitweb/map
+++ b/templates/gitweb/map
@@ -5,9 +5,9 @@ search = search.tmpl
 changelog = changelog.tmpl
 summary = summary.tmpl
 error = error.tmpl
-naventry = '#label|escape# '
-navshortentry = '#label|escape# '
-filenaventry = '{label|escape} '
+naventry = '{label|escape} '
+navshortentry = '{label|escape} '
+filenaventry = '{label|escape} '
 filedifflink = '#file|escape# '
 filenodelink = '#file|escape#file | annotate | diff | revisions'
 fileellipses = '...'
diff --git a/templates/map b/templates/map
--- a/templates/map
+++ b/templates/map
@@ -5,9 +5,9 @@ search = search.tmpl
 changelog = changelog.tmpl
 shortlog = shortlog.tmpl
 shortlogentry = shortlogentry.tmpl
-naventry = '#label|escape# '
-navshortentry = '#label|escape# '
-filenaventry = '{label|escape} '
+naventry = '{label|escape} '
+navshortentry = '{label|escape} '
+filenaventry = '{label|escape} '
 filedifflink = '#file|escape# '
 filenodelink = '#file|escape# '
 fileellipses = '...'
diff --git a/templates/old/map b/templates/old/map
--- a/templates/old/map
+++ b/templates/old/map
@@ -5,8 +5,8 @@ search = search.tmpl
 changelog = changelog.tmpl
 shortlog = shortlog.tmpl
 shortlogentry = shortlogentry.tmpl
-naventry = '#label|escape# '
-navshortentry = '#label|escape# '
+naventry = '{label|escape} '
+navshortentry = '{label|escape} '
 filedifflink = '#file|escape# '
 filenodelink = '#file|escape# '
 fileellipses = '...'
diff --git a/templates/rss/changelogentry.tmpl b/templates/rss/changelogentry.tmpl
--- a/templates/rss/changelogentry.tmpl
+++ b/templates/rss/changelogentry.tmpl
@@ -1,6 +1,6 @@
 
     #desc|strip|firstline|strip|escape#
-    #url#?cs=#node|short#
+    {urlbase}{url}rev/{node|short}
     
     #author|obfuscate#
     #date|rfc822date#
diff --git a/templates/rss/filelogentry.tmpl b/templates/rss/filelogentry.tmpl
--- a/templates/rss/filelogentry.tmpl
+++ b/templates/rss/filelogentry.tmpl
@@ -1,6 +1,6 @@
 
     #desc|strip|firstline|strip|escape#
-    #url#?f=#node|short#;file=#file|urlescape#
+    {urlbase}{url}log{#node|short#}/{file|urlescape}
     
     #author|obfuscate#
     #date|rfc822date#
diff --git a/templates/rss/header.tmpl b/templates/rss/header.tmpl
--- a/templates/rss/header.tmpl
+++ b/templates/rss/header.tmpl
@@ -2,5 +2,5 @@ Content-type: text/xml
 
 
   
-    #url#
+    {urlbase}{url}
     en-us
diff --git a/templates/rss/tagentry.tmpl b/templates/rss/tagentry.tmpl
--- a/templates/rss/tagentry.tmpl
+++ b/templates/rss/tagentry.tmpl
@@ -1,6 +1,6 @@
 
     #tag|escape#
-    #url#?cs=#node|short#
+    {urlbase}{url}rev/{node|short}
     
     #date|rfc822date#
 
diff --git a/tests/test-acl b/tests/test-acl
new file mode 100755
--- /dev/null
+++ b/tests/test-acl
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+do_push()
+{
+    user=$1
+    shift
+
+    echo "Pushing as user $user"
+    echo 'hgrc = """'
+    sed -e 1,2d b/.hg/hgrc
+    echo '"""'
+    if [ -e acl.config ]; then
+	echo 'acl.config = """'
+	cat acl.config
+	echo '"""'
+    fi
+    LOGNAME=$user hg --cwd a --debug push ../b
+    hg --cwd b rollback
+    hg --cwd b --quiet tip
+    echo
+}
+
+hg init a
+cd a
+mkdir foo foo/Bar quux
+echo 'in foo' > foo/file.txt
+echo 'in foo/Bar' > foo/Bar/file.txt
+echo 'in quux' > quux/file.py
+hg add
+hg ci -m 'add files' -d '1000000 0'
+echo >> foo/file.txt
+hg ci -m 'change foo/file' -d '1000001 0'
+echo >> foo/Bar/file.txt
+hg ci -m 'change foo/Bar/file' -d '1000002 0'
+echo >> quux/file.py
+hg ci -m 'change quux/file' -d '1000003 0'
+hg tip --quiet
+
+cd ..
+hg clone -r 0 a b
+
+echo '[extensions]' >> $HGRCPATH
+echo 'hgext.acl =' >> $HGRCPATH
+
+config=b/.hg/hgrc
+
+echo
+
+echo 'Extension disabled for lack of a hook'
+do_push fred
+
+echo '[hooks]' >> $config
+echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
+
+echo 'Extension disabled for lack of acl.sources'
+do_push fred
+
+echo 'No [acl.allow]/[acl.deny]'
+echo '[acl]' >> $config
+echo 'sources = push' >> $config
+do_push fred
+
+echo 'Empty [acl.allow]'
+echo '[acl.allow]' >> $config
+do_push fred
+
+echo 'fred is allowed inside foo/'
+echo 'foo/** = fred' >> $config
+do_push fred
+
+echo 'Empty [acl.deny]'
+echo '[acl.deny]' >> $config
+do_push barney
+
+echo 'fred is allowed inside foo/, but not foo/bar/ (case matters)'
+echo 'foo/bar/** = fred' >> $config
+do_push fred
+
+echo 'fred is allowed inside foo/, but not foo/Bar/'
+echo 'foo/Bar/** = fred' >> $config
+do_push fred
+
+echo 'barney is not mentioned => not allowed anywhere'
+do_push barney
+
+echo 'barney is allowed everywhere'
+echo '[acl.allow]' >> $config
+echo '** = barney' >> $config
+do_push barney
+
+echo 'wilma can change files with a .txt extension'
+echo '**/*.txt = wilma' >> $config
+do_push wilma
+
+echo 'file specified by acl.config does not exist'
+echo '[acl]' >> $config
+echo 'config = ../acl.config' >> $config
+do_push barney
+
+echo 'betty is allowed inside foo/ by a acl.config file'
+echo '[acl.allow]' >> acl.config
+echo 'foo/** = betty' >> acl.config
+do_push betty
+
+echo 'acl.config can set only [acl.allow]/[acl.deny]'
+echo '[hooks]' >> acl.config
+echo 'changegroup.acl = false' >> acl.config
+do_push barney
+
diff --git a/tests/test-acl.out b/tests/test-acl.out
new file mode 100644
--- /dev/null
+++ b/tests/test-acl.out
@@ -0,0 +1,517 @@
+adding foo/Bar/file.txt
+adding foo/file.txt
+adding quux/file.py
+3:911600dab2ae
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 3 changes to 3 files
+3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+Extension disabled for lack of a hook
+Pushing as user fred
+hgrc = """
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+rolling back last transaction
+0:6675d58eff77
+
+Extension disabled for lack of acl.sources
+Pushing as user fred
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow not enabled
+acl: acl.deny not enabled
+acl: changes have source "push" - skipping
+rolling back last transaction
+0:6675d58eff77
+
+No [acl.allow]/[acl.deny]
+Pushing as user fred
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow not enabled
+acl: acl.deny not enabled
+acl: allowing changeset ef1ea85a6374
+acl: allowing changeset f9cafe1212c8
+acl: allowing changeset 911600dab2ae
+rolling back last transaction
+0:6675d58eff77
+
+Empty [acl.allow]
+Pushing as user fred
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+[acl.allow]
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow enabled, 0 entries for user fred
+acl: acl.deny not enabled
+acl: user fred not allowed on foo/file.txt
+error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
+abort: acl: access denied for changeset ef1ea85a6374
+transaction abort!
+rollback completed
+no rollback information available
+0:6675d58eff77
+
+fred is allowed inside foo/
+Pushing as user fred
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+[acl.allow]
+foo/** = fred
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow enabled, 1 entries for user fred
+acl: acl.deny not enabled
+acl: allowing changeset ef1ea85a6374
+acl: allowing changeset f9cafe1212c8
+acl: user fred not allowed on quux/file.py
+error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
+abort: acl: access denied for changeset 911600dab2ae
+transaction abort!
+rollback completed
+no rollback information available
+0:6675d58eff77
+
+Empty [acl.deny]
+Pushing as user barney
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+[acl.allow]
+foo/** = fred
+[acl.deny]
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow enabled, 0 entries for user barney
+acl: acl.deny enabled, 0 entries for user barney
+acl: user barney not allowed on foo/file.txt
+error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
+abort: acl: access denied for changeset ef1ea85a6374
+transaction abort!
+rollback completed
+no rollback information available
+0:6675d58eff77
+
+fred is allowed inside foo/, but not foo/bar/ (case matters)
+Pushing as user fred
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+[acl.allow]
+foo/** = fred
+[acl.deny]
+foo/bar/** = fred
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow enabled, 1 entries for user fred
+acl: acl.deny enabled, 1 entries for user fred
+acl: allowing changeset ef1ea85a6374
+acl: allowing changeset f9cafe1212c8
+acl: user fred not allowed on quux/file.py
+error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
+abort: acl: access denied for changeset 911600dab2ae
+transaction abort!
+rollback completed
+no rollback information available
+0:6675d58eff77
+
+fred is allowed inside foo/, but not foo/Bar/
+Pushing as user fred
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+[acl.allow]
+foo/** = fred
+[acl.deny]
+foo/bar/** = fred
+foo/Bar/** = fred
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow enabled, 1 entries for user fred
+acl: acl.deny enabled, 2 entries for user fred
+acl: allowing changeset ef1ea85a6374
+acl: user fred denied on foo/Bar/file.txt
+error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
+abort: acl: access denied for changeset f9cafe1212c8
+transaction abort!
+rollback completed
+no rollback information available
+0:6675d58eff77
+
+barney is not mentioned => not allowed anywhere
+Pushing as user barney
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+[acl.allow]
+foo/** = fred
+[acl.deny]
+foo/bar/** = fred
+foo/Bar/** = fred
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow enabled, 0 entries for user barney
+acl: acl.deny enabled, 0 entries for user barney
+acl: user barney not allowed on foo/file.txt
+error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
+abort: acl: access denied for changeset ef1ea85a6374
+transaction abort!
+rollback completed
+no rollback information available
+0:6675d58eff77
+
+barney is allowed everywhere
+Pushing as user barney
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+[acl.allow]
+foo/** = fred
+[acl.deny]
+foo/bar/** = fred
+foo/Bar/** = fred
+[acl.allow]
+** = barney
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow enabled, 1 entries for user barney
+acl: acl.deny enabled, 0 entries for user barney
+acl: allowing changeset ef1ea85a6374
+acl: allowing changeset f9cafe1212c8
+acl: allowing changeset 911600dab2ae
+rolling back last transaction
+0:6675d58eff77
+
+wilma can change files with a .txt extension
+Pushing as user wilma
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+[acl.allow]
+foo/** = fred
+[acl.deny]
+foo/bar/** = fred
+foo/Bar/** = fred
+[acl.allow]
+** = barney
+**/*.txt = wilma
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow enabled, 1 entries for user wilma
+acl: acl.deny enabled, 0 entries for user wilma
+acl: allowing changeset ef1ea85a6374
+acl: allowing changeset f9cafe1212c8
+acl: user wilma not allowed on quux/file.py
+error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
+abort: acl: access denied for changeset 911600dab2ae
+transaction abort!
+rollback completed
+no rollback information available
+0:6675d58eff77
+
+file specified by acl.config does not exist
+Pushing as user barney
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+[acl.allow]
+foo/** = fred
+[acl.deny]
+foo/bar/** = fred
+foo/Bar/** = fred
+[acl.allow]
+** = barney
+**/*.txt = wilma
+[acl]
+config = ../acl.config
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow enabled, 1 entries for user barney
+acl: acl.deny enabled, 0 entries for user barney
+acl: allowing changeset ef1ea85a6374
+acl: allowing changeset f9cafe1212c8
+acl: allowing changeset 911600dab2ae
+rolling back last transaction
+0:6675d58eff77
+
+betty is allowed inside foo/ by a acl.config file
+Pushing as user betty
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+[acl.allow]
+foo/** = fred
+[acl.deny]
+foo/bar/** = fred
+foo/Bar/** = fred
+[acl.allow]
+** = barney
+**/*.txt = wilma
+[acl]
+config = ../acl.config
+"""
+acl.config = """
+[acl.allow]
+foo/** = betty
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow enabled, 1 entries for user betty
+acl: acl.deny enabled, 0 entries for user betty
+acl: allowing changeset ef1ea85a6374
+acl: allowing changeset f9cafe1212c8
+acl: user betty not allowed on quux/file.py
+error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
+abort: acl: access denied for changeset 911600dab2ae
+transaction abort!
+rollback completed
+no rollback information available
+0:6675d58eff77
+
+acl.config can set only [acl.allow]/[acl.deny]
+Pushing as user barney
+hgrc = """
+[hooks]
+pretxnchangegroup.acl = python:hgext.acl.hook
+[acl]
+sources = push
+[acl.allow]
+foo/** = fred
+[acl.deny]
+foo/bar/** = fred
+foo/Bar/** = fred
+[acl.allow]
+** = barney
+**/*.txt = wilma
+[acl]
+config = ../acl.config
+"""
+acl.config = """
+[acl.allow]
+foo/** = betty
+[hooks]
+changegroup.acl = false
+"""
+pushing to ../b
+searching for changes
+common changesets up to 6675d58eff77
+adding changesets
+add changeset ef1ea85a6374
+add changeset f9cafe1212c8
+add changeset 911600dab2ae
+adding manifests
+adding file changes
+adding foo/Bar/file.txt revisions
+adding foo/file.txt revisions
+adding quux/file.py revisions
+added 3 changesets with 3 changes to 3 files
+calling hook pretxnchangegroup.acl: hgext.acl.hook
+acl: acl.allow enabled, 1 entries for user barney
+acl: acl.deny enabled, 0 entries for user barney
+acl: allowing changeset ef1ea85a6374
+acl: allowing changeset f9cafe1212c8
+acl: allowing changeset 911600dab2ae
+rolling back last transaction
+0:6675d58eff77
+
diff --git a/tests/test-bad-pull b/tests/test-bad-pull
--- a/tests/test-bad-pull
+++ b/tests/test-bad-pull
@@ -2,7 +2,7 @@
 
 hg clone http://localhost:20059/ copy
 echo $?
-ls copy 2>/dev/null || echo copy: No such file or directory
+test -e copy || echo copy: No such file or directory
 
 cat > dumb.py <> $HGRCPATH
+echo 'KeY = Case Sensitive' >> $HGRCPATH
+echo 'key = lower case' >> $HGRCPATH
+
+hg showconfig
diff --git a/tests/test-config-case.out b/tests/test-config-case.out
new file mode 100644
--- /dev/null
+++ b/tests/test-config-case.out
@@ -0,0 +1,2 @@
+Section.KeY=Case Sensitive
+Section.key=lower case
diff --git a/tests/test-empty-dir b/tests/test-empty-dir
--- a/tests/test-empty-dir
+++ b/tests/test-empty-dir
@@ -11,6 +11,6 @@ hg commit -m "second" -d "1000000 0" sub
 cat sub/b
 hg co 0
 cat sub/b 2>/dev/null || echo "sub/b not present"
-ls sub 2>/dev/null || echo "sub not present"
+test -e sub || echo "sub not present"
 
 true
diff --git a/tests/test-static-http b/tests/test-static-http
--- a/tests/test-static-http
+++ b/tests/test-static-http
@@ -2,7 +2,7 @@
 
 http_proxy= hg clone static-http://localhost:20059/ copy
 echo $?
-ls copy 2>/dev/null || echo copy: No such file or directory
+test -e copy || echo copy: No such file or directory
 
 # This server doesn't do range requests so it's basically only good for
 # one pull