Merge with crew-stable
authorBryan O'Sullivan <bryano@fb.com>
Tue, 02 Oct 2012 14:31:25 -0700
changeset 17702 57fe5aca86af
parent 17701 f7b3518c9cb4 (diff)
parent 17698 6647ac9b9044 (current diff)
child 17704 3d1a781b23c0
Merge with crew-stable
hgext/largefiles/overrides.py
tests/test-largefiles.t
--- a/contrib/bash_completion	Mon Oct 01 23:11:28 2012 -0500
+++ b/contrib/bash_completion	Tue Oct 02 14:31:25 2012 -0700
@@ -386,6 +386,13 @@
     fi
 }
 
+_hg_cmd_rebase() {
+   if [[ "$prev" = @(-s|--source|-d|--dest|-b|--base|-r|--rev) ]]; then
+       _hg_labels
+       return
+   fi
+}
+
 _hg_cmd_strip()
 {
     _hg_labels
--- a/contrib/check-code.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/contrib/check-code.py	Tue Oct 02 14:31:25 2012 -0700
@@ -91,7 +91,7 @@
 uprefix = r"^  \$ "
 utestpats = [
   [
-    (r'^(\S|  $ ).*(\S[ \t]+|^[ \t]+)\n', "trailing whitespace on non-output"),
+    (r'^(\S.*||  [$>] .*)[ \t]\n', "trailing whitespace on non-output"),
     (uprefix + r'.*\|\s*sed[^|>\n]*\n',
      "use regex test output patterns instead of sed"),
     (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
@@ -136,7 +136,10 @@
     (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
     (r'^\s+\w+=\w+[^,)\n]$', "missing whitespace in assignment"),
     (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
-     r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Py2.4'),
+     r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'),
+    (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
+     r'((?:\n|\1\s.*\n)+?)\1finally:',
+     'no yield inside try/finally in Python 2.4'),
     (r'.{81}', "line too long"),
     (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
     (r'[^\n]\Z', "no trailing newline"),
@@ -190,8 +193,8 @@
      'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
     (r'opener\([^)]*\).read\(',
      "use opener.read() instead"),
-    (r'BaseException', 'not in Py2.4, use Exception'),
-    (r'os\.path\.relpath', 'os.path.relpath is not in Py2.5'),
+    (r'BaseException', 'not in Python 2.4, use Exception'),
+    (r'os\.path\.relpath', 'os.path.relpath is not in Python 2.5'),
     (r'opener\([^)]*\).write\(',
      "use opener.write() instead"),
     (r'[\s\(](open|file)\([^)]*\)\.read\(',
@@ -321,7 +324,7 @@
     :f: filepath
     :logfunc: function used to report error
               logfunc(filename, linenumber, linecontent, errormessage)
-    :maxerr: number of error to display before arborting.
+    :maxerr: number of error to display before aborting.
              Set to false (default) to report all errors
 
     return True if no error is found, False otherwise.
@@ -365,7 +368,7 @@
                 p, msg = pat
                 ignore = None
 
-            # fix-up regexes for multiline searches
+            # fix-up regexes for multi-line searches
             po = p
             # \s doesn't match \n
             p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/lock-checker.py	Tue Oct 02 14:31:25 2012 -0700
@@ -0,0 +1,48 @@
+"""Extension to verify locks are obtained in the required places.
+
+This works by wrapping functions that should be surrounded by a lock
+and asserting the lock is held. Missing locks are called out with a
+traceback printed to stderr.
+
+This currently only checks store locks, not working copy locks.
+"""
+import os
+import traceback
+
+def _warnstack(ui, msg, skip=1):
+    '''issue warning with the message and the current stack, skipping the
+    skip last entries'''
+    ui.warn('%s at:\n' % msg)
+    entries = traceback.extract_stack()[:-skip]
+    fnmax = max(len(entry[0]) for entry in entries)
+    for fn, ln, func, _text in entries:
+        ui.warn(' %*s:%-4s in %s\n' % (fnmax, fn, ln, func))
+
+def _checklock(repo):
+    l = repo._lockref and repo._lockref()
+    if l is None or not l.held:
+        _warnstack(repo.ui, 'missing lock', skip=2)
+
+def reposetup(ui, repo):
+    orig = repo.__class__
+    class lockcheckrepo(repo.__class__):
+        def _writejournal(self, *args, **kwargs):
+            _checklock(self)
+            return orig._writejournal(self, *args, **kwargs)
+
+        def transaction(self, *args, **kwargs):
+            _checklock(self)
+            return orig.transaction(self, *args, **kwargs)
+
+        # TODO(durin42): kiilerix had a commented-out lock check in
+        # writebranchcache and _writerequirements
+
+        def _tag(self, *args, **kwargs):
+            _checklock(self)
+            return orig._tag(self, *args, **kwargs)
+
+        def write(self, *args, **kwargs):
+            assert os.path.lexists(self._join('.hg/wlock'))
+            return orig.write(self, *args, **kwargs)
+
+    repo.__class__ = lockcheckrepo
--- a/contrib/mercurial.el	Mon Oct 01 23:11:28 2012 -0500
+++ b/contrib/mercurial.el	Tue Oct 02 14:31:25 2012 -0700
@@ -461,7 +461,7 @@
 (defun hg-complete-repo (string predicate all)
   "Attempt to complete a repository name.
 We complete on either symbolic names from Mercurial's config or real
-directory names from the file system.  We do not penalise URLs."
+directory names from the file system.  We do not penalize URLs."
   (or (if all
 	  (all-completions string hg-repo-completion-table predicate)
 	(try-completion string hg-repo-completion-table predicate))
--- a/contrib/perf.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/contrib/perf.py	Tue Oct 02 14:31:25 2012 -0700
@@ -195,6 +195,16 @@
         s.fncache.write()
     timer(d)
 
+def perffncacheencode(ui, repo):
+    from mercurial import store
+    s = store.store(set(['store','fncache','dotencode']),
+                    repo.path, scmutil.opener)
+    s.fncache._load()
+    def d():
+        for p in s.fncache.entries:
+            s.encode(p)
+    timer(d)
+
 def perfdiffwd(ui, repo):
     """Profile diff of working directory changes"""
     options = {
@@ -226,6 +236,7 @@
     'perfcca': (perfcca, []),
     'perffncacheload': (perffncacheload, []),
     'perffncachewrite': (perffncachewrite, []),
+    'perffncacheencode': (perffncacheencode, []),
     'perflookup': (perflookup, []),
     'perfrevrange': (perfrevrange, []),
     'perfnodelookup': (perfnodelookup, []),
--- a/contrib/vim/hgcommand.vim	Mon Oct 01 23:11:28 2012 -0500
+++ b/contrib/vim/hgcommand.vim	Tue Oct 02 14:31:25 2012 -0700
@@ -27,7 +27,7 @@
 "
 " You still can read the documentation at the end of this file. Locate it by
 " searching the "hgcommand-contents" string (and set ft=help to have
-" appropriate syntaxic coloration).
+" appropriate syntactic coloration).
 
 " Section: Plugin header {{{1
 
@@ -752,7 +752,7 @@
     " Protect against case and backslash issues in Windows.
     let autoPattern = '\c' . messageFileName
 
-    " Ensure existance of group
+    " Ensure existence of group
     augroup HGCommit
     augroup END
 
@@ -1442,7 +1442,7 @@
 
                                                  *hgcommand-mappings-override*
 
-   The default mappings can be overriden by user-provided instead by mapping
+   The default mappings can be overridden by user-provided instead by mapping
    to <Plug>CommandName.  This is especially useful when these mappings
    collide with other existing mappings (vim will warn of this during plugin
    initialization, but will not clobber the existing mappings).
--- a/contrib/vim/patchreview.vim	Mon Oct 01 23:11:28 2012 -0500
+++ b/contrib/vim/patchreview.vim	Tue Oct 02 14:31:25 2012 -0700
@@ -17,7 +17,7 @@
 "
 "   0.2.1 - Minor temp directory autodetection logic and cleanup
 "
-"   0.2 - Removed the need for filterdiff by implemeting it in pure vim script
+"   0.2 - Removed the need for filterdiff by implementing it in pure vim script
 "       - Added DiffReview command for reverse (changed repository to
 "         pristine state) reviews.
 "         (PatchReview does pristine repository to patch review)
@@ -55,7 +55,7 @@
 "   3) Optional (but recommended for speed)
 "
 "      Install patchutils ( http://cyberelk.net/tim/patchutils/ ) for your
-"      OS. For windows it is availble from Cygwin
+"      OS. For windows it is available from Cygwin
 "
 "         http://www.cygwin.com
 "
--- a/contrib/win32/hgwebdir_wsgi.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/contrib/win32/hgwebdir_wsgi.py	Tue Oct 02 14:31:25 2012 -0700
@@ -27,7 +27,7 @@
 #   On 64-bit systems, make sure it's assigned a 32-bit app pool.
 #
 # - In the application, setup a wildcard script handler mapping of type
-#   IpsapiModule with the shim dll as its executable. This file MUST reside
+#   IsapiModule with the shim dll as its executable. This file MUST reside
 #   in the same directory as the shim. Remove all other handlers, if you wish.
 #
 # - Make sure the ISAPI and CGI restrictions (configured globally on the
--- a/contrib/wix/templates.wxs	Mon Oct 01 23:11:28 2012 -0500
+++ b/contrib/wix/templates.wxs	Tue Oct 02 14:31:25 2012 -0700
@@ -33,7 +33,6 @@
           <File Name="map-cmdline.default" />
           <File Name="map-cmdline.bisect" />
           <File Name="map-cmdline.xml" />
-          <File Name="template-vars.txt" />
         </Component>
 
         <Directory Id="templates.atomdir" Name="atom">
--- a/contrib/zsh_completion	Mon Oct 01 23:11:28 2012 -0500
+++ b/contrib/zsh_completion	Tue Oct 02 14:31:25 2012 -0700
@@ -361,6 +361,19 @@
     'urls:URL:_hg_urls'
 }
 
+_hg_add_help_topics=(
+    config dates diffs environment extensions filesets glossary hgignore hgweb
+    merge-tools multirevs obsolescence patterns phases revisions revsets
+    subrepos templating urls
+)
+
+_hg_help_topics() {
+    local topics
+    (( $#_hg_cmd_list )) || _hg_get_commands
+    topics=($_hg_cmd_list $_hg_add_help_topics)
+    _describe -t help_topics 'help topics' topics
+}
+
 # Common options
 _hg_global_opts=(
     '(--repository -R)'{-R+,--repository}'[repository root directory]:repository:_files -/'
@@ -385,18 +398,49 @@
   '*'{-I+,--include}'[include names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/'
   '*'{-X+,--exclude}'[exclude names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/')
 
+_hg_clone_opts=(
+  $_hg_remote_opts
+  '(--noupdate -U)'{-U,--noupdate}'[do not update the new working directory]'
+  '--pull[use pull protocol to copy metadata]'
+  '--uncompressed[use uncompressed transfer (fast over LAN)]')
+
+_hg_date_user_opts=(
+  '(--currentdate -D)'{-D,--currentdate}'[record the current date as commit date]'
+  '(--currentuser -U)'{-U,--currentuser}'[record the current user as committer]'
+  '(--date -d)'{-d+,--date}'[record the specified date as commit date]:date:'
+  '(--user -u)'{-u+,--user}'[record the specified user as committer]:user:')
+
+_hg_gitlike_opts=(
+  '(--git -g)'{-g,--git}'[use git extended diff format]')
+
 _hg_diff_opts=(
+  $_hg_gitlike_opts
   '(--text -a)'{-a,--text}'[treat all files as text]'
-  '(--git -g)'{-g,--git}'[use git extended diff format]'
-  "--nodates[omit dates from diff headers]")
+  '--nodates[omit dates from diff headers]')
+
+_hg_mergetool_opts=(
+  '(--tool -t)'{-t+,--tool}'[specify merge tool]:tool:')
 
 _hg_dryrun_opts=(
   '(--dry-run -n)'{-n,--dry-run}'[do not perform actions, just print output]')
 
+_hg_ignore_space_opts=(
+  '(--ignore-all-space -w)'{-w,--ignore-all-space}'[ignore white space when comparing lines]'
+  '(--ignore-space-change -b)'{-b,--ignore-space-change}'[ignore changes in the amount of white space]'
+  '(--ignore-blank-lines -B)'{-B,--ignore-blank-lines}'[ignore changes whose lines are all blank]')
+
 _hg_style_opts=(
   '--style[display using template map file]:'
   '--template[display with template]:')
 
+_hg_log_opts=(
+  $_hg_global_opts $_hg_style_opts $_hg_gitlike_opts
+  '(--limit -l)'{-l+,--limit}'[limit number of changes displayed]:'
+  '(--no-merges -M)'{-M,--no-merges}'[do not show merges]'
+  '(--patch -p)'{-p,--patch}'[show patch]'
+  '--stat[output diffstat-style summary of changes]'
+)
+
 _hg_commit_opts=(
   '(-m --message -l --logfile --edit -e)'{-e,--edit}'[edit commit message]'
   '(-e --edit -l --logfile --message -m)'{-m+,--message}'[use <text> as commit message]:message:'
@@ -406,12 +450,20 @@
   '(--ssh -e)'{-e+,--ssh}'[specify ssh command to use]:'
   '--remotecmd[specify hg command to run on the remote side]:')
 
+_hg_branch_bmark_opts=(
+  '(--bookmark -B)'{-B+,--bookmark}'[specify bookmark(s)]:bookmark:_hg_bookmarks'
+  '(--branch -b)'{-b+,--branch}'[specify branch(es)]:branch:_hg_branches'
+)
+
+_hg_subrepos_opts=(
+  '(--subrepos -S)'{-S,--subrepos}'[recurse into subrepositories]')
+
 _hg_cmd() {
   _call_program hg HGPLAIN=1 hg "$_hg_cmd_globals[@]" "$@" 2> /dev/null
 }
 
 _hg_cmd_add() {
-  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts $_hg_subrepos_opts \
   '*:unknown files:_hg_unknown'
 }
 
@@ -434,7 +486,7 @@
 }
 
 _hg_cmd_archive() {
-  _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_subrepos_opts \
   '--no-decode[do not pass files through decoders]' \
   '(--prefix -p)'{-p+,--prefix}'[directory prefix for files in archive]:' \
   '(--rev -r)'{-r+,--rev}'[revision to distribute]:revision:_hg_labels' \
@@ -443,7 +495,7 @@
 }
 
 _hg_cmd_backout() {
-  _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_mergetool_opts $_hg_pat_opts \
     '--merge[merge with old dirstate parent after backout]' \
     '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \
     '--parent[parent to choose when backing out merge]' \
@@ -456,6 +508,7 @@
 _hg_cmd_bisect() {
   _arguments -s -w : $_hg_global_opts \
   '(-)'{-r,--reset}'[reset bisect state]' \
+  '(--extend -e)'{-e,--extend}'[extend the bisect range]' \
   '(--good -g --bad -b --skip -s --reset -r)'{-g,--good}'[mark changeset good]'::revision:_hg_labels \
   '(--good -g --bad -b --skip -s --reset -r)'{-b,--bad}'[mark changeset bad]'::revision:_hg_labels \
   '(--good -g --bad -b --skip -s --reset -r)'{-s,--skip}'[skip testing changeset]' \
@@ -466,6 +519,7 @@
 _hg_cmd_bookmarks() {
   _arguments -s -w : $_hg_global_opts \
   '(--force -f)'{-f,--force}'[force]' \
+  '(--inactive -i)'{-i,--inactive}'[mark a bookmark inactive]' \
   '(--rev -r --delete -d --rename -m)'{-r+,--rev}'[revision]:revision:_hg_labels' \
   '(--rev -r --delete -d --rename -m)'{-d,--delete}'[delete a given bookmark]' \
   '(--rev -r --delete -d --rename -m)'{-m+,--rename}'[rename a given bookmark]:bookmark:_hg_bookmarks' \
@@ -480,13 +534,17 @@
 
 _hg_cmd_branches() {
   _arguments -s -w : $_hg_global_opts \
-  '(--active -a)'{-a,--active}'[show only branches that have unmerge heads]'
+  '(--active -a)'{-a,--active}'[show only branches that have unmerge heads]' \
+  '(--closed -c)'{-c,--closed}'[show normal and closed branches]'
 }
 
 _hg_cmd_bundle() {
   _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
   '(--force -f)'{-f,--force}'[run even when remote repository is unrelated]' \
   '(2)*--base[a base changeset to specify instead of a destination]:revision:_hg_labels' \
+  '(--branch -b)'{-b+,--branch}'[a specific branch to bundle]' \
+  '(--rev -r)'{-r+,--rev}'[changeset(s) to bundle]:' \
+  '--all[bundle all changesets in the repository]' \
   ':output file:_files' \
   ':destination repository:_files -/'
 }
@@ -495,26 +553,28 @@
   _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
   '(--output -o)'{-o+,--output}'[print output to file with formatted name]:filespec:' \
   '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_labels' \
+  '--decode[apply any matching decode filter]' \
   '*:file:_hg_files'
 }
 
 _hg_cmd_clone() {
-  _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
-  '(--noupdate -U)'{-U,--noupdate}'[do not update the new working directory]' \
+  _arguments -s -w : $_hg_global_opts $_hg_clone_opts \
   '(--rev -r)'{-r+,--rev}'[a changeset you would like to have after cloning]:' \
-  '--uncompressed[use uncompressed transfer (fast over LAN)]' \
+  '(--updaterev -u)'{-u+,--updaterev}'[revision, tag or branch to check out]' \
+  '(--branch -b)'{-b+,--branch}'[clone only the specified branch]' \
   ':source repository:_hg_remote' \
   ':destination:_hg_clone_dest'
 }
 
 _hg_cmd_commit() {
-  _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_subrepos_opts \
   '(--addremove -A)'{-A,--addremove}'[mark new/missing files as added/removed before committing]' \
   '(--message -m)'{-m+,--message}'[use <text> as commit message]:text:' \
   '(--logfile -l)'{-l+,--logfile}'[read commit message from <file>]:log file:_files -g \*.txt' \
   '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \
   '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \
   '--amend[amend the parent of the working dir]' \
+  '--close-branch[mark a branch as closed]' \
   '*:file:_hg_files'
 }
 
@@ -527,12 +587,15 @@
 
 _hg_cmd_diff() {
   typeset -A opt_args
-  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_diff_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_diff_opts $_hg_ignore_space_opts \
+                     $_hg_pat_opts $_hg_subrepos_opts \
   '*'{-r,--rev}'+[revision]:revision:_hg_revrange' \
   '(--show-function -p)'{-p,--show-function}'[show which function each change is in]' \
-  '(--ignore-all-space -w)'{-w,--ignore-all-space}'[ignore white space when comparing lines]' \
-  '(--ignore-space-change -b)'{-b,--ignore-space-change}'[ignore changes in the amount of white space]' \
-  '(--ignore-blank-lines -B)'{-B,--ignore-blank-lines}'[ignore changes whose lines are all blank]' \
+  '(--change -c)'{-c,--change}'[change made by revision]' \
+  '(--text -a)'{-a,--text}'[treat all files as text]' \
+  '--reverse[produce a diff that undoes the changes]' \
+  '(--unified -U)'{-U,--unified}'[number of lines of context to show]' \
+  '--stat[output diffstat-style summary of changes]' \
   '*:file:->diff_files'
 
   if [[ $state == 'diff_files' ]]
@@ -550,20 +613,21 @@
   _arguments -s -w : $_hg_global_opts $_hg_diff_opts \
   '(--outout -o)'{-o+,--output}'[print output to file with formatted name]:filespec:' \
   '--switch-parent[diff against the second parent]' \
+  '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_labels' \
   '*:revision:_hg_labels'
 }
 
+_hg_cmd_forget() {
+  _arguments -s -w : $_hg_global_opts \
+  '*:file:_hg_files'
+}
+
 _hg_cmd_graft() {
-  _arguments -s -w : $_hg_global_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_dryrun_opts \
+                     $_hg_date_user_opts $_hg_mergetool_opts \
   '(--continue -c)'{-c,--continue}'[resume interrupted graft]' \
   '(--edit -e)'{-e,--edit}'[invoke editor on commit messages]' \
   '--log[append graft info to log message]' \
-  '(--currentdate -D)'{-D,--currentdate}'[record the current date as commit date]' \
-  '(--currentuser -U)'{-U,--currentuser}'[record the current user as committer]' \
-  '(--date -d)'{-d,--date}'[record the specified date as commit date]' \
-  '(--user -u)'{-u,--user}'[record the specified user as committer]' \
-  '(--tool -t)'{-t,--tool}'[specify merge tool]' \
-  '(--dry-run -n)'{-n,--dry-run}'[do not perform actions, just print output]' \
   '*:revision:_hg_labels'
 }
 
@@ -577,44 +641,55 @@
   '(--line-number -n)'{-n,--line-number}'[print matching line numbers]' \
   '*'{-r+,--rev}'[search in given revision range]:revision:_hg_revrange' \
   '(--user -u)'{-u,--user}'[print user who committed change]' \
+  '(--date -d)'{-d,--date}'[print date of a changeset]' \
   '1:search pattern:' \
   '*:files:_hg_files'
 }
 
 _hg_cmd_heads() {
   _arguments -s -w : $_hg_global_opts $_hg_style_opts \
+  '(--topo -t)'{-t,--topo}'[show topological heads only]' \
+  '(--closed -c)'{-c,--closed}'[show normal and closed branch heads]' \
   '(--rev -r)'{-r+,--rev}'[show only heads which are descendants of rev]:revision:_hg_labels'
 }
 
 _hg_cmd_help() {
   _arguments -s -w : $_hg_global_opts \
-  '*:mercurial command:_hg_commands'
+  '(--extension -e)'{-e,--extension}'[show only help for extensions]' \
+  '(--command -c)'{-c,--command}'[show only help for commands]' \
+  '(--keyword -k)'{-k+,--keyword}'[show topics matching keyword]' \
+  '*:mercurial help topic:_hg_help_topics'
 }
 
 _hg_cmd_identify() {
-  _arguments -s -w : $_hg_global_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
   '(--rev -r)'{-r+,--rev}'[identify the specified rev]:revision:_hg_labels' \
   '(--num -n)'{-n+,--num}'[show local revision number]' \
   '(--id -i)'{-i+,--id}'[show global revision id]' \
   '(--branch -b)'{-b+,--branch}'[show branch]' \
+  '(--bookmark -B)'{-B+,--bookmark}'[show bookmarks]' \
   '(--tags -t)'{-t+,--tags}'[show tags]'
 }
 
 _hg_cmd_import() {
-  _arguments -s -w : $_hg_global_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_commit_opts \
   '(--strip -p)'{-p+,--strip}'[directory strip option for patch (default: 1)]:count:' \
-  '(--message -m)'{-m+,--message}'[use <text> as commit message]:text:' \
   '(--force -f)'{-f,--force}'[skip check for outstanding uncommitted changes]' \
   '--bypass[apply patch without touching the working directory]' \
+  '--no-commit[do not commit, just update the working directory]' \
+  '--exact[apply patch to the nodes from which it was generated]' \
+  '--import-branch[use any branch information in patch (implied by --exact)]' \
+  '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \
+  '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \
+  '(--similarity -s)'{-s+,--similarity}'[guess renamed files by similarity (0<=s<=100)]:' \
   '*:patch:_files'
 }
 
 _hg_cmd_incoming() {
-  _arguments -s -w : $_hg_global_opts $_hg_remote_opts $_hg_style_opts \
-  '(--no-merges -M)'{-M,--no-merges}'[do not show merge revisions]' \
+  _arguments -s -w : $_hg_log_opts $_hg_branch_bmark_opts $_hg_remote_opts \
+                     $_hg_subrepos_opts \
   '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \
-  '(--patch -p)'{-p,--patch}'[show patch]' \
-  '(--rev -r)'{-r+,--rev}'[a specific revision up to which you would like to pull]:revision:_hg_tags' \
+  '(--rev -r)'{-r+,--rev}'[a specific revision up to which you would like to pull]:revision:_hg_labels' \
   '(--newest-first -n)'{-n,--newest-first}'[show newest record first]' \
   '--bundle[file to store the bundles into]:bundle file:_files' \
   ':source:_hg_remote'
@@ -634,42 +709,41 @@
 }
 
 _hg_cmd_log() {
-  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_style_opts \
+  _arguments -s -w : $_hg_log_opts $_hg_pat_opts \
   '(--follow --follow-first -f)'{-f,--follow}'[follow changeset or history]' \
   '(-f --follow)--follow-first[only follow the first parent of merge changesets]' \
   '(--copies -C)'{-C,--copies}'[show copied files]' \
   '(--keyword -k)'{-k+,--keyword}'[search for a keyword]:' \
-  '(--limit -l)'{-l+,--limit}'[limit number of changes displayed]:' \
   '*'{-r,--rev}'[show the specified revision or range]:revision:_hg_revrange' \
-  '(--no-merges -M)'{-M,--no-merges}'[do not show merges]' \
   '(--only-merges -m)'{-m,--only-merges}'[show only merges]' \
-  '(--patch -p)'{-p,--patch}'[show patch]' \
   '(--prune -P)'{-P+,--prune}'[do not display revision or any of its ancestors]:revision:_hg_labels' \
+  '(--graph -G)'{-G+,--graph}'[show the revision DAG]' \
   '(--branch -b)'{-b+,--branch}'[show changesets within the given named branch]:branch:_hg_branches' \
+  '(--user -u)'{-u+,--user}'[revisions committed by user]:user:' \
+  '(--date -d)'{-d+,--date}'[show revisions matching date spec]:date:' \
   '*:files:_hg_files'
 }
 
 _hg_cmd_manifest() {
   _arguments -s -w : $_hg_global_opts \
   '--all[list files from all revisions]' \
+  '(--rev -r)'{-r+,--rev}'[revision to display]:revision:_hg_labels' \
   ':revision:_hg_labels'
 }
 
 _hg_cmd_merge() {
-  _arguments -s -w : $_hg_global_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_mergetool_opts \
   '(--force -f)'{-f,--force}'[force a merge with outstanding changes]' \
   '(--rev -r 1)'{-r,--rev}'[revision to merge]:revision:_hg_mergerevs' \
   '(--preview -P)'{-P,--preview}'[review revisions to merge (no merge is performed)]' \
-  '(--tool -t)'{-t,--tool}'[specify merge tool]' \
   ':revision:_hg_mergerevs'
 }
 
 _hg_cmd_outgoing() {
-  _arguments -s -w : $_hg_global_opts $_hg_remote_opts $_hg_style_opts \
-  '(--no-merges -M)'{-M,--no-merges}'[do not show merge revisions]' \
+  _arguments -s -w : $_hg_log_opts $_hg_branch_bmark_opts $_hg_remote_opts \
+                     $_hg_subrepos_opts \
   '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \
-  '(--patch -p)'{-p,--patch}'[show patch]' \
-  '(--rev -r)'{-r+,--rev}'[a specific revision you would like to push]' \
+  '*'{-r,--rev}'[a specific revision you would like to push]:revision:_hg_revrange' \
   '(--newest-first -n)'{-n,--newest-first}'[show newest record first]' \
   ':destination:_hg_remote'
 }
@@ -696,7 +770,7 @@
 }
 
 _hg_cmd_pull() {
-  _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_branch_bmark_opts $_hg_remote_opts \
   '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \
   '(--update -u)'{-u,--update}'[update to new tip if changesets were pulled]' \
   '(--rev -r)'{-r+,--rev}'[a specific revision up to which you would like to pull]:revision:' \
@@ -704,9 +778,10 @@
 }
 
 _hg_cmd_push() {
-  _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_branch_bmark_opts $_hg_remote_opts \
   '(--force -f)'{-f,--force}'[force push]' \
   '(--rev -r)'{-r+,--rev}'[a specific revision you would like to push]:revision:_hg_labels' \
+  '--new-branch[allow pushing a new branch]' \
   ':destination:_hg_remote'
 }
 
@@ -728,7 +803,9 @@
   local context state line
   typeset -A opt_args
 
-  _arguments -s -w : $_hg_global_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_mergetool_opts $_hg_pat_opts \
+  '(--all -a)'{-a,--all}'[select all unresolved files]' \
+  '(--no-status -n)'{-n,--no-status}'[hide status prefix]' \
   '(--list -l --mark -m --unmark -u)'{-l,--list}'[list state of files needing merge]:*:merged files:->resolve_files' \
   '(--mark -m --list -l --unmark -u)'{-m,--mark}'[mark files as resolved]:*:unresolved files:_hg_unresolved' \
   '(--unmark -u --list -l --mark -m)'{-u,--unmark}'[unmark files as resolved]:*:resolved files:_hg_resolved' \
@@ -749,6 +826,7 @@
   '(--all -a :)'{-a,--all}'[revert all changes when no arguments given]' \
   '(--rev -r)'{-r+,--rev}'[revision to revert to]:revision:_hg_labels' \
   '(--no-backup -C)'{-C,--no-backup}'[do not save backup copies of files]' \
+  '(--date -d)'{-d+,--date}'[tipmost revision matching date]:date code:' \
   '*:file:->diff_files'
 
   if [[ $state == 'diff_files' ]]
@@ -764,6 +842,11 @@
   fi
 }
 
+_hg_cmd_rollback() {
+  _arguments -s -w : $_hg_global_opts $_hg_dryrun_opts \
+  '(--force -f)'{-f,--force}'[ignore safety measures]' \
+}
+
 _hg_cmd_serve() {
   _arguments -s -w : $_hg_global_opts \
   '(--accesslog -A)'{-A+,--accesslog}'[name of access log file]:log file:_files' \
@@ -771,10 +854,15 @@
   '(--daemon -d)'{-d,--daemon}'[run server in background]' \
   '(--port -p)'{-p+,--port}'[listen port]:listen port:' \
   '(--address -a)'{-a+,--address}'[interface address]:interface address:' \
+  '--prefix[prefix path to serve from]:directory:_files' \
   '(--name -n)'{-n+,--name}'[name to show in web pages]:repository name:' \
+  '--web-conf[name of the hgweb config file]:webconf_file:_files' \
+  '--pid-file[name of file to write process ID to]:pid_file:_files' \
+  '--cmdserver[cmdserver mode]:mode:' \
   '(--templates -t)'{-t,--templates}'[web template directory]:template dir:_files -/' \
   '--style[web template style]:style' \
   '--stdio[for remote clients]' \
+  '--certificate[certificate file]:cert_file:_files' \
   '(--ipv6 -6)'{-6,--ipv6}'[use IPv6 in addition to IPv4]'
 }
 
@@ -785,7 +873,7 @@
 }
 
 _hg_cmd_status() {
-  _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_subrepos_opts \
   '(--all -A)'{-A,--all}'[show status of all files]' \
   '(--modified -m)'{-m,--modified}'[show only modified files]' \
   '(--added -a)'{-a,--added}'[show only added files]' \
@@ -798,6 +886,7 @@
   '(--copies -C)'{-C,--copies}'[show source of copied files]' \
   '(--print0 -0)'{-0,--print0}'[end filenames with NUL, for use with xargs]' \
   '--rev[show difference from revision]:revision:_hg_labels' \
+  '--change[list the changed files of a revision]:revision:_hg_labels' \
   '*:files:_files'
 }
 
@@ -813,11 +902,14 @@
   '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \
   '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \
   '(--rev -r)'{-r+,--rev}'[revision to tag]:revision:_hg_labels' \
+  '(--force -f)'{-f,--force}'[force tag]' \
+  '--remove[remove a tag]' \
+  '(--edit -e)'{-e,--edit}'[edit commit message]' \
   ':tag name:'
 }
 
 _hg_cmd_tip() {
-  _arguments -s -w : $_hg_global_opts $_hg_style_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_gitlike_opts $_hg_style_opts \
   '(--patch -p)'{-p,--patch}'[show patch]'
 }
 
@@ -831,6 +923,8 @@
   _arguments -s -w : $_hg_global_opts \
   '(--clean -C)'{-C,--clean}'[overwrite locally modified files]' \
   '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_labels' \
+  '(--check -c)'{-c,--check}'[update across branches if no uncommitted changes]' \
+  '(--date -d)'{-d+,--date}'[tipmost revision matching date]' \
   ':revision:_hg_labels'
 }
 
@@ -893,7 +987,16 @@
   '(--summary -s)'{-s,--summary}'[print first line of patch header]')
 
 _hg_cmd_qapplied() {
-  _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
+  _arguments -s -w : $_hg_global_opts $_hg_qseries_opts \
+  '(--last -1)'{-1,--last}'[show only the preceding applied patch]' \
+  '*:patch:_hg_qapplied'
+}
+
+_hg_cmd_qclone() {
+  _arguments -s -w : $_hg_global_opts $_hg_remote_opts $_hg_clone_opts \
+  '(--patches -p)'{-p+,--patches}'[location of source patch repository]' \
+  ':source repository:_hg_remote' \
+  ':destination:_hg_clone_dest'
 }
 
 _hg_cmd_qdelete() {
@@ -904,7 +1007,8 @@
 }
 
 _hg_cmd_qdiff() {
-  _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_diff_opts \
+                     $_hg_ignore_space_opts \
   '*:pattern:_hg_files'
 }
 
@@ -917,12 +1021,15 @@
 _hg_cmd_qfold() {
   _arguments -s -w : $_hg_global_opts $_h_commit_opts \
   '(--keep,-k)'{-k,--keep}'[keep folded patch files]' \
+  '(--force -f)'{-f,--force}'[overwrite any local changes]' \
+  '--no-backup[do not save backup copies of files]' \
   '*:unapplied patch:_hg_qunapplied'
 }
 
 _hg_cmd_qgoto() {
   _arguments -s -w : $_hg_global_opts \
   '(--force -f)'{-f,--force}'[overwrite any local changes]' \
+  '--keep-changes[tolerate non-conflicting local changes]' \
   ':patch:_hg_qseries'
 }
 
@@ -940,17 +1047,17 @@
 }
 
 _hg_cmd_qimport() {
-  _arguments -s -w : $_hg_global_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_gitlike_opts \
   '(--existing -e)'{-e,--existing}'[import file in patch dir]' \
   '(--name -n 2)'{-n+,--name}'[patch file name]:name:' \
   '(--force -f)'{-f,--force}'[overwrite existing files]' \
   '*'{-r+,--rev}'[place existing revisions under mq control]:revision:_hg_revrange' \
+  '(--push -P)'{-P,--push}'[qpush after importing]' \
   '*:patch:_files'
 }
 
 _hg_cmd_qnew() {
-  _arguments -s -w : $_hg_global_opts $_hg_commit_opts \
-  '(--force -f)'{-f,--force}'[import uncommitted changes into patch]' \
+  _arguments -s -w : $_hg_global_opts $_hg_commit_opts $_hg_date_user_opts $_hg_gitlike_opts \
   ':patch:'
 }
 
@@ -961,8 +1068,9 @@
 _hg_cmd_qpop() {
   _arguments -s -w : $_hg_global_opts \
   '(--all -a :)'{-a,--all}'[pop all patches]' \
-  '(--name -n)'{-n+,--name}'[queue name to pop]:' \
   '(--force -f)'{-f,--force}'[forget any local changes]' \
+  '--keep-changes[tolerate non-conflicting local changes]' \
+  '--no-backup[do not save backup copies of files]' \
   ':patch:_hg_qapplied'
 }
 
@@ -974,24 +1082,23 @@
   _arguments -s -w : $_hg_global_opts \
   '(--all -a :)'{-a,--all}'[apply all patches]' \
   '(--list -l)'{-l,--list}'[list patch name in commit text]' \
-  '(--merge -m)'{-m+,--merge}'[merge from another queue]:' \
-  '(--name -n)'{-n+,--name}'[merge queue name]:' \
   '(--force -f)'{-f,--force}'[apply if the patch has rejects]' \
   '(--exact -e)'{-e,--exact}'[apply the target patch to its recorded parent]' \
   '--move[reorder patch series and apply only the patch]' \
+  '--keep-changes[tolerate non-conflicting local changes]' \
+  '--no-backup[do not save backup copies of files]' \
   ':patch:_hg_qunapplied'
 }
 
 _hg_cmd_qrefresh() {
-  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_commit_opts \
-  '(--git -g)'{-g,--git}'[use git extended diff format]' \
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_commit_opts $_hg_gitlike_opts \
   '(--short -s)'{-s,--short}'[short refresh]' \
   '*:files:_hg_files'
 }
 
 _hg_cmd_qrename() {
   _arguments -s -w : $_hg_global_opts \
-  ':patch:_hg_qseries' \
+  ':patch:_hg_qunapplied' \
   ':destination:'
 }
 
@@ -1010,7 +1117,8 @@
 }
 
 _hg_cmd_qunapplied() {
-  _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
+  _arguments -s -w : $_hg_global_opts $_hg_qseries_opts \
+  '(--first -1)'{-1,--first}'[show only the first patch]'
 }
 
 _hg_cmd_qtop() {
@@ -1019,16 +1127,17 @@
 
 _hg_cmd_strip() {
   _arguments -s -w : $_hg_global_opts \
-  '(--force -f)'{-f,--force}'[force multi-head removal]' \
-  '(--backup -b)'{-b,--backup}'[bundle unrelated changesets]' \
-  '(--nobackup -n)'{-n,--nobackup}'[no backups]' \
+  '(--force -f)'{-f,--force}'[force removal, discard uncommitted changes, no backup]' \
+  '(--no-backup -n)'{-n,--no-backup}'[no backups]' \
+  '(--keep -k)'{-k,--keep}'[do not modify working copy during strip]' \
+  '(--bookmark -B)'{-B+,--bookmark}'[remove revs only reachable from given bookmark]:bookmark:_hg_bookmarks' \
+  '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_labels' \
   ':revision:_hg_labels'
 }
 
 # Patchbomb
 _hg_cmd_email() {
-  _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
-  '(--git -g)'{-g,--git}'[use git extended diff format]' \
+  _arguments -s -w : $_hg_global_opts $_hg_remote_opts $_hg_gitlike_opts \
   '--plain[omit hg patch header]' \
   '--body[send patches as inline message text (default)]' \
   '(--outgoing -o)'{-o,--outgoing}'[send changes not found in the target repository]' \
@@ -1058,20 +1167,61 @@
 
 # Rebase
 _hg_cmd_rebase() {
-  _arguments -s -w : $_hg_global_opts \
+  _arguments -s -w : $_hg_global_opts $_hg_commit_opts $_hg_mergetool_opts \
   '*'{-r,--rev}'[rebase these revisions]:revision:_hg_revrange' \
-  '(--source -s)'{-s,--source}'[rebase from the specified changeset]:revision:_hg_labels' \
-  '(--base -b)'{-b,--base}'[rebase from the base of the specified changeset]:revision:_hg_labels' \
-  '(--dest -d)'{-d,--dest}'[rebase onto the specified changeset]' \
+  '(--source -s)'{-s+,--source}'[rebase from the specified changeset]:revision:_hg_labels' \
+  '(--base -b)'{-b+,--base}'[rebase from the base of the specified changeset]:revision:_hg_labels' \
+  '(--dest -d)'{-d+,--dest}'[rebase onto the specified changeset]:revision:_hg_labels' \
   '--collapse[collapse the rebased changeset]' \
-  '(--message -m)'{-m+,--message}'[use <text> as collapse commit message]:text:' \
-  '(--edit -e)'{-e,--edit}'[invoke editor on commit messages]' \
-  '(--logfile -l)'{-l+,--logfile}'[read collapse commit message from <file>]:log file:_files -g \*.txt' \
   '--keep[keep original changeset]' \
   '--keepbranches[keep original branch name]' \
-  '(--tool -t)'{-t,--tool}'[specify merge tool]' \
   '(--continue -c)'{-c,--continue}'[continue an interrupted rebase]' \
   '(--abort -a)'{-a,--abort}'[abort an interrupted rebase]' \
 }
 
+# Record
+_hg_cmd_record() {
+  _arguments -s -w : $_hg_global_opts $_hg_commit_opts $_hg_pat_opts \
+                     $_hg_ignore_space_opts $_hg_subrepos_opts \
+  '(--addremove -A)'{-A,--addremove}'[mark new/missing files as added/removed before committing]' \
+  '--close-branch[mark a branch as closed, hiding it from the branch list]' \
+  '--amend[amend the parent of the working dir]' \
+  '(--date -d)'{-d+,--date}'[record the specified date as commit date]:date:' \
+  '(--user -u)'{-u+,--user}'[record the specified user as committer]:user:'
+}
+
+_hg_cmd_qrecord() {
+  _arguments -s -w : $_hg_global_opts $_hg_commit_opts $_hg_date_user_opts $_hg_gitlike_opts \
+                     $_hg_pat_opts $_hg_ignore_space_opts $_hg_subrepos_opts
+}
+
+# Convert
+_hg_cmd_convert() {
+_arguments -s -w : $_hg_global_opts \
+  '(--source-type -s)'{-s,--source-type}'[source repository type]' \
+  '(--dest-type -d)'{-d,--dest-type}'[destination repository type]' \
+  '(--rev -r)'{-r+,--rev}'[import up to target revision]:revision:' \
+  '(--authormap -A)'{-A+,--authormap}'[remap usernames using this file]:file:_files' \
+  '--filemap[remap file names using contents of file]:file:_files' \
+  '--splicemap[splice synthesized history into place]:file:_files' \
+  '--branchmap[change branch names while converting]:file:_files' \
+  '--branchsort[try to sort changesets by branches]' \
+  '--datesort[try to sort changesets by date]' \
+  '--sourcesort[preserve source changesets order]'
+}
+
+# Graphlog
+_hg_cmd_glog() {
+  _hg_cmd_log $@
+}
+
+# Purge
+_hg_cmd_purge() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_subrepos_opts \
+  '(--abort-on-err -a)'{-a,--abort-on-err}'[abort if an error occurs]' \
+  '--all[purge ignored files too]' \
+  '(--print -p)'{-p,--print}'[print filenames instead of deleting them]' \
+  '(--print0 -0)'{-0,--print0}'[end filenames with NUL, for use with xargs (implies -p/--print)]'
+}
+
 _hg "$@"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/check-seclevel.py	Tue Oct 02 14:31:25 2012 -0700
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+#
+# checkseclevel - checking section title levels in each online help documents
+
+import sys, os
+import optparse
+
+# import from the live mercurial repo
+sys.path.insert(0, "..")
+# fall back to pure modules if required C extensions are not available
+sys.path.append(os.path.join('..', 'mercurial', 'pure'))
+from mercurial import demandimport; demandimport.enable()
+from mercurial.commands import table
+from mercurial.help import helptable
+from mercurial import extensions
+from mercurial import minirst
+from mercurial import util
+
+_verbose = False
+
+def verbose(msg):
+    if _verbose:
+        print msg
+
+def error(msg):
+    sys.stderr.write('%s\n' % msg)
+
+level2mark = ['"', '=', '-', '.', '#']
+reservedmarks = ['"']
+
+mark2level = {}
+for m, l in zip(level2mark, xrange(len(level2mark))):
+    if m not in reservedmarks:
+        mark2level[m] = l
+
+initlevel_topic = 0
+initlevel_cmd = 1
+initlevel_ext = 1
+initlevel_ext_cmd = 3
+
+def showavailables(initlevel):
+    error('    available marks and order of them in this help: %s' %
+          (', '.join(['%r' % (m * 4) for m in level2mark[initlevel + 1:]])))
+
+def checkseclevel(doc, name, initlevel):
+    verbose('checking "%s"' % name)
+    blocks, pruned = minirst.parse(doc, 0, ['verbose'])
+    errorcnt = 0
+    curlevel = initlevel
+    for block in blocks:
+        if block['type'] != 'section':
+            continue
+        mark = block['underline']
+        title = block['lines'][0]
+        if (mark not in mark2level) or (mark2level[mark] <= initlevel):
+            error('invalid section mark %r for "%s" of %s' %
+                  (mark * 4, title, name))
+            showavailables(initlevel)
+            errorcnt += 1
+            continue
+        nextlevel = mark2level[mark]
+        if curlevel < nextlevel and curlevel + 1 != nextlevel:
+            error('gap of section level at "%s" of %s' %
+                  (title, name))
+            showavailables(initlevel)
+            errorcnt += 1
+            continue
+        verbose('appropriate section level for "%s %s"' %
+                (mark * (nextlevel * 2), title))
+        curlevel = nextlevel
+
+    return errorcnt
+
+def checkcmdtable(cmdtable, namefmt, initlevel):
+    errorcnt = 0
+    for k, entry in cmdtable.items():
+        name = k.split("|")[0].lstrip("^")
+        if not entry[0].__doc__:
+            verbose('skip checking %s: no help document' %
+                    (namefmt % name))
+            continue
+        errorcnt += checkseclevel(entry[0].__doc__,
+                                  namefmt % name,
+                                  initlevel)
+    return errorcnt
+
+def checkhghelps():
+    errorcnt = 0
+    for names, sec, doc in helptable:
+        if util.safehasattr(doc, '__call__'):
+            doc = doc()
+        errorcnt += checkseclevel(doc,
+                                  '%s help topic' % names[0],
+                                  initlevel_topic)
+
+    errorcnt += checkcmdtable(table, '%s command', initlevel_cmd)
+
+    for name in sorted(extensions.enabled().keys() +
+                       extensions.disabled().keys()):
+        mod = extensions.load(None, name, None)
+        if not mod.__doc__:
+            verbose('skip checking %s extension: no help document' % name)
+            continue
+        errorcnt += checkseclevel(mod.__doc__,
+                                  '%s extension' % name,
+                                  initlevel_ext)
+
+        cmdtable = getattr(mod, 'cmdtable', None)
+        if cmdtable:
+            errorcnt += checkcmdtable(cmdtable,
+                                      '%s command of ' + name + ' extension',
+                                      initlevel_ext_cmd)
+    return errorcnt
+
+def checkfile(filename, initlevel):
+    if filename == '-':
+        filename = 'stdin'
+        doc = sys.stdin.read()
+    else:
+        fp = open(filename)
+        try:
+            doc = fp.read()
+        finally:
+            fp.close()
+
+    verbose('checking input from %s with initlevel %d' %
+            (filename, initlevel))
+    return checkseclevel(doc, 'input from %s' % filename, initlevel)
+
+if __name__ == "__main__":
+    optparser = optparse.OptionParser("""%prog [options]
+
+This checks all help documents of Mercurial (topics, commands,
+extensions and commands of them), if no file is specified by --file
+option.
+""")
+    optparser.add_option("-v", "--verbose",
+                         help="enable additional output",
+                         action="store_true")
+    optparser.add_option("-f", "--file",
+                         help="filename to read in (or '-' for stdin)",
+                         action="store", default="")
+
+    optparser.add_option("-t", "--topic",
+                         help="parse file as help topic",
+                         action="store_const", dest="initlevel", const=0)
+    optparser.add_option("-c", "--command",
+                         help="parse file as help of core command",
+                         action="store_const", dest="initlevel", const=1)
+    optparser.add_option("-e", "--extension",
+                         help="parse file as help of extension",
+                         action="store_const", dest="initlevel", const=1)
+    optparser.add_option("-C", "--extension-command",
+                         help="parse file as help of extension command",
+                         action="store_const", dest="initlevel", const=3)
+
+    optparser.add_option("-l", "--initlevel",
+                         help="set initial section level manually",
+                         action="store", type="int", default=0)
+
+    (options, args) = optparser.parse_args()
+
+    _verbose = options.verbose
+
+    if options.file:
+        if checkfile(options.file, options.initlevel):
+            sys.exit(1)
+    else:
+        if checkhghelps():
+            sys.exit(1)
--- a/doc/hgmanpage.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/doc/hgmanpage.py	Tue Oct 02 14:31:25 2012 -0700
@@ -158,7 +158,7 @@
     """"""
 
     words_and_spaces = re.compile(r'\S+| +|\n')
-    document_start = """Man page generated from reStructeredText."""
+    document_start = """Man page generated from reStructuredText."""
 
     def __init__(self, document):
         nodes.NodeVisitor.__init__(self, document)
@@ -900,7 +900,7 @@
         # ``.PP`` : Start standard indented paragraph.
         # ``.LP`` : Start block paragraph, all except the first.
         # ``.P [type]``  : Start paragraph type.
-        # NOTE dont use paragraph starts because they reset indentation.
+        # NOTE don't use paragraph starts because they reset indentation.
         # ``.sp`` is only vertical space
         self.ensure_eol()
         self.body.append('.sp\n')
--- a/hgext/bugzilla.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/bugzilla.py	Tue Oct 02 14:31:25 2012 -0700
@@ -45,7 +45,7 @@
 email to the Bugzilla email interface to submit comments to bugs.
 The From: address in the email is set to the email address of the Mercurial
 user, so the comment appears to come from the Mercurial user. In the event
-that the Mercurial user email is not recognised by Bugzilla as a Bugzilla
+that the Mercurial user email is not recognized by Bugzilla as a Bugzilla
 user, the email associated with the Bugzilla username used to log into
 Bugzilla is used instead as the source of the comment. Marking bugs fixed
 works on all supported Bugzilla versions.
@@ -53,7 +53,7 @@
 Configuration items common to all access modes:
 
 bugzilla.version
-  This access type to use. Values recognised are:
+  The access type to use. Values recognized are:
 
   :``xmlrpc``:       Bugzilla XMLRPC interface.
   :``xmlrpc+email``: Bugzilla XMLRPC and email interfaces.
@@ -303,7 +303,7 @@
     # Methods to be implemented by access classes.
     #
     # 'bugs' is a dict keyed on bug id, where values are a dict holding
-    # updates to bug state. Recognised dict keys are:
+    # updates to bug state. Recognized dict keys are:
     #
     # 'hours': Value, float containing work hours to be updated.
     # 'fix':   If key present, bug is to be marked fixed. Value ignored.
@@ -516,7 +516,7 @@
             raise util.Abort(_('unknown database schema'))
         return ids[0][0]
 
-# Buzgilla via XMLRPC interface.
+# Bugzilla via XMLRPC interface.
 
 class cookietransportrequest(object):
     """A Transport request method that retains cookies over its lifetime.
--- a/hgext/color.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/color.py	Tue Oct 02 14:31:25 2012 -0700
@@ -499,5 +499,5 @@
                 orig(m.group(2), **opts)
                 m = re.match(ansire, m.group(3))
         finally:
-            # Explicity reset original attributes
+            # Explicitly reset original attributes
             _kernel32.SetConsoleTextAttribute(stdout, origattr)
--- a/hgext/convert/__init__.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/convert/__init__.py	Tue Oct 02 14:31:25 2012 -0700
@@ -74,7 +74,7 @@
 
     The authormap is a simple text file that maps each source commit
     author to a destination commit author. It is handy for source SCMs
-    that use unix logins to identify authors (eg: CVS). One line per
+    that use unix logins to identify authors (e.g.: CVS). One line per
     author mapping and the line format is::
 
       source author = destination author
--- a/hgext/convert/bzr.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/convert/bzr.py	Tue Oct 02 14:31:25 2012 -0700
@@ -246,7 +246,7 @@
                         # register the change as move
                         renames[topath] = frompath
 
-                # no futher changes, go to the next change
+                # no further changes, go to the next change
                 continue
 
             # we got unicode paths, need to convert them
--- a/hgext/convert/convcmd.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/convert/convcmd.py	Tue Oct 02 14:31:25 2012 -0700
@@ -167,7 +167,7 @@
 
     def toposort(self, parents, sortmode):
         '''Return an ordering such that every uncommitted changeset is
-        preceeded by all its uncommitted ancestors.'''
+        preceded by all its uncommitted ancestors.'''
 
         def mapchildren(parents):
             """Return a (children, roots) tuple where 'children' maps parent
--- a/hgext/convert/cvs.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/convert/cvs.py	Tue Oct 02 14:31:25 2012 -0700
@@ -202,7 +202,7 @@
     def getfile(self, name, rev):
 
         def chunkedread(fp, count):
-            # file-objects returned by socked.makefile() do not handle
+            # file-objects returned by socket.makefile() do not handle
             # large read() requests very well.
             chunksize = 65536
             output = StringIO()
--- a/hgext/convert/cvsps.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/convert/cvsps.py	Tue Oct 02 14:31:25 2012 -0700
@@ -156,8 +156,8 @@
         # The cvsps cache pickle needs a uniquified name, based on the
         # repository location. The address may have all sort of nasties
         # in it, slashes, colons and such. So here we take just the
-        # alphanumerics, concatenated in a way that does not mix up the
-        # various components, so that
+        # alphanumeric characters, concatenated in a way that does not
+        # mix up the various components, so that
         #    :pserver:user@server:/path
         # and
         #    /pserver/user/server/path
@@ -503,7 +503,7 @@
 
         # Check if log entry belongs to the current changeset or not.
 
-        # Since CVS is file centric, two different file revisions with
+        # Since CVS is file-centric, two different file revisions with
         # different branchpoints should be treated as belonging to two
         # different changesets (and the ordering is important and not
         # honoured by cvsps at this point).
--- a/hgext/convert/gnuarch.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/convert/gnuarch.py	Tue Oct 02 14:31:25 2012 -0700
@@ -89,7 +89,7 @@
 
             # Get the complete list of revisions for that tree version
             output, status = self.runlines('revisions', '-r', '-f', treeversion)
-            self.checkexit(status, 'failed retrieveing revisions for %s'
+            self.checkexit(status, 'failed retrieving revisions for %s'
                            % treeversion)
 
             # No new iteration unless a revision has a continuation-of header
--- a/hgext/convert/monotone.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/convert/monotone.py	Tue Oct 02 14:31:25 2012 -0700
@@ -225,7 +225,6 @@
             return [self.rev]
 
     def getchanges(self, rev):
-        #revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
         revision = self.mtnrun("get_revision", rev).split("\n\n")
         files = {}
         ignoremove = {}
--- a/hgext/convert/subversion.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/convert/subversion.py	Tue Oct 02 14:31:25 2012 -0700
@@ -887,8 +887,8 @@
             io = StringIO()
             info = svn.ra.get_file(self.ra, file, revnum, io)
             data = io.getvalue()
-            # ra.get_files() seems to keep a reference on the input buffer
-            # preventing collection. Release it explicitely.
+            # ra.get_file() seems to keep a reference on the input buffer
+            # preventing collection. Release it explicitly.
             io.close()
             if isinstance(info, list):
                 info = info[-1]
@@ -923,7 +923,7 @@
         # Given the repository url of this wc, say
         #   "http://server/plone/CMFPlone/branches/Plone-2_0-branch"
         # extract the "entry" portion (a relative path) from what
-        # svn log --xml says, ie
+        # svn log --xml says, i.e.
         #   "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py"
         # that is to say "tests/PloneTestCase.py"
         if path.startswith(module):
--- a/hgext/convert/transport.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/convert/transport.py	Tue Oct 02 14:31:25 2012 -0700
@@ -43,7 +43,7 @@
         svn.client.get_ssl_client_cert_pw_file_provider(pool),
         svn.client.get_ssl_server_trust_file_provider(pool),
         ]
-    # Platform-dependant authentication methods
+    # Platform-dependent authentication methods
     getprovider = getattr(svn.core, 'svn_auth_get_platform_specific_provider',
                           None)
     if getprovider:
--- a/hgext/eol.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/eol.py	Tue Oct 02 14:31:25 2012 -0700
@@ -99,7 +99,7 @@
 # Matches a lone LF, i.e., one that is not part of CRLF.
 singlelf = re.compile('(^|[^\r])\n')
 # Matches a single EOL which can either be a CRLF where repeated CR
-# are removed or a LF. We do not care about old Machintosh files, so a
+# are removed or a LF. We do not care about old Macintosh files, so a
 # stray CR is an error.
 eolre = re.compile('\r*\n')
 
--- a/hgext/extdiff.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/extdiff.py	Tue Oct 02 14:31:25 2012 -0700
@@ -109,7 +109,7 @@
     return dirname, fns_and_mtime
 
 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
-    '''Do the actuall diff:
+    '''Do the actual diff:
 
     - copy to a temp structure if diffing 2 internal revisions
     - copy to a temp structure if diffing working revision with
--- a/hgext/hgcia.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/hgcia.py	Tue Oct 02 14:31:25 2012 -0700
@@ -22,7 +22,7 @@
   # Style to use (optional)
   #style = foo
   # The URL of the CIA notification service (optional)
-  # You can use mailto: URLs to send by email, eg
+  # You can use mailto: URLs to send by email, e.g.
   # mailto:cia@cia.vc
   # Make sure to set email.from if you do this.
   #url = http://cia.vc/
--- a/hgext/histedit.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/histedit.py	Tue Oct 02 14:31:25 2012 -0700
@@ -142,20 +142,21 @@
     import cPickle as pickle
 except ImportError:
     import pickle
-import tempfile
 import os
 
 from mercurial import bookmarks
 from mercurial import cmdutil
 from mercurial import discovery
 from mercurial import error
+from mercurial import copies
+from mercurial import context
 from mercurial import hg
 from mercurial import lock as lockmod
 from mercurial import node
-from mercurial import patch
 from mercurial import repair
 from mercurial import scmutil
 from mercurial import util
+from mercurial import merge as mergemod
 from mercurial.i18n import _
 
 cmdtable = {}
@@ -175,24 +176,97 @@
 #
 """)
 
-def between(repo, old, new, keep):
-    revs = [old]
-    current = old
-    while current != new:
-        ctx = repo[current]
-        if not keep and len(ctx.children()) > 1:
-            raise util.Abort(_('cannot edit history that would orphan nodes'))
-        if len(ctx.parents()) != 1 and ctx.parents()[1] != node.nullid:
-            raise util.Abort(_("can't edit history with merges"))
-        if not ctx.children():
-            current = new
+def applychanges(ui, repo, ctx, opts):
+    """Merge changeset from ctx (only) in the current working directory"""
+    wcpar = repo.dirstate.parents()[0]
+    if ctx.p1().node() == wcpar:
+        # edition ar "in place" we do not need to make any merge,
+        # just applies changes on parent for edition
+        cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
+        stats = None
+    else:
+        try:
+            # ui.forcemerge is an internal variable, do not document
+            repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
+            stats = mergemod.update(repo, ctx.node(), True, True, False,
+                                    ctx.p1().node())
+        finally:
+            repo.ui.setconfig('ui', 'forcemerge', '')
+        repo.setparents(wcpar, node.nullid)
+        repo.dirstate.write()
+        # fix up dirstate for copies and renames
+    cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
+    return stats
+
+def collapse(repo, first, last, commitopts):
+    """collapse the set of revisions from first to last as new one.
+
+    Expected commit options are:
+        - message
+        - date
+        - username
+    Edition of commit message is trigered in all case.
+
+    This function works in memory."""
+    ctxs = list(repo.set('%d::%d', first, last))
+    if not ctxs:
+        return None
+    base = first.parents()[0]
+
+    # commit a new version of the old changeset, including the update
+    # collect all files which might be affected
+    files = set()
+    for ctx in ctxs:
+        files.update(ctx.files())
+
+    # Recompute copies (avoid recording a -> b -> a)
+    copied = copies.pathcopies(first, last)
+
+    # prune files which were reverted by the updates
+    def samefile(f):
+        if f in last.manifest():
+            a = last.filectx(f)
+            if f in base.manifest():
+                b = base.filectx(f)
+                return (a.data() == b.data()
+                        and a.flags() == b.flags())
+            else:
+                return False
         else:
-            current = ctx.children()[0].node()
-            revs.append(current)
-    if len(repo[current].children()) and not keep:
-        raise util.Abort(_('cannot edit history that would orphan nodes'))
-    return revs
+            return f not in base.manifest()
+    files = [f for f in files if not samefile(f)]
+    # commit version of these files as defined by head
+    headmf = last.manifest()
+    def filectxfn(repo, ctx, path):
+        if path in headmf:
+            fctx = last[path]
+            flags = fctx.flags()
+            mctx = context.memfilectx(fctx.path(), fctx.data(),
+                                      islink='l' in flags,
+                                      isexec='x' in flags,
+                                      copied=copied.get(path))
+            return mctx
+        raise IOError()
 
+    if commitopts.get('message'):
+        message = commitopts['message']
+    else:
+        message = first.description()
+    user = commitopts.get('user')
+    date = commitopts.get('date')
+    extra = first.extra()
+
+    parents = (first.p1().node(), first.p2().node())
+    new = context.memctx(repo,
+                         parents=parents,
+                         text=message,
+                         files=files,
+                         filectxfn=filectxfn,
+                         user=user,
+                         date=date,
+                         extra=extra)
+    new._text = cmdutil.commitforceeditor(repo, new, [])
+    return repo.commitctx(new)
 
 def pick(ui, repo, ctx, ha, opts):
     oldctx = repo[ha]
@@ -200,57 +274,24 @@
         ui.debug('node %s unchanged\n' % ha)
         return oldctx, [], [], []
     hg.update(repo, ctx.node())
-    fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
-    fp = os.fdopen(fd, 'w')
-    diffopts = patch.diffopts(ui, opts)
-    diffopts.git = True
-    diffopts.ignorews = False
-    diffopts.ignorewsamount = False
-    diffopts.ignoreblanklines = False
-    gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
-    for chunk in gen:
-        fp.write(chunk)
-    fp.close()
-    try:
-        files = set()
-        try:
-            patch.patch(ui, repo, patchfile, files=files, eolmode=None)
-            if not files:
-                ui.warn(_('%s: empty changeset')
-                             % node.hex(ha))
-                return ctx, [], [], []
-        finally:
-            os.unlink(patchfile)
-    except Exception:
+    stats = applychanges(ui, repo, oldctx, opts)
+    if stats and stats[3] > 0:
         raise util.Abort(_('Fix up the change and run '
                            'hg histedit --continue'))
+    # drop the second merge parent
     n = repo.commit(text=oldctx.description(), user=oldctx.user(),
                     date=oldctx.date(), extra=oldctx.extra())
+    if n is None:
+        ui.warn(_('%s: empty changeset\n')
+                     % node.hex(ha))
+        return ctx, [], [], []
     return repo[n], [n], [oldctx.node()], []
 
 
 def edit(ui, repo, ctx, ha, opts):
     oldctx = repo[ha]
     hg.update(repo, ctx.node())
-    fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
-    fp = os.fdopen(fd, 'w')
-    diffopts = patch.diffopts(ui, opts)
-    diffopts.git = True
-    diffopts.ignorews = False
-    diffopts.ignorewsamount = False
-    diffopts.ignoreblanklines = False
-    gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
-    for chunk in gen:
-        fp.write(chunk)
-    fp.close()
-    try:
-        files = set()
-        try:
-            patch.patch(ui, repo, patchfile, files=files, eolmode=None)
-        finally:
-            os.unlink(patchfile)
-    except Exception:
-        pass
+    applychanges(ui, repo, oldctx, opts)
     raise util.Abort(_('Make changes as needed, you may commit or record as '
                        'needed now.\nWhen you are finished, run hg'
                        ' histedit --continue to resume.'))
@@ -258,65 +299,41 @@
 def fold(ui, repo, ctx, ha, opts):
     oldctx = repo[ha]
     hg.update(repo, ctx.node())
-    fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
-    fp = os.fdopen(fd, 'w')
-    diffopts = patch.diffopts(ui, opts)
-    diffopts.git = True
-    diffopts.ignorews = False
-    diffopts.ignorewsamount = False
-    diffopts.ignoreblanklines = False
-    gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
-    for chunk in gen:
-        fp.write(chunk)
-    fp.close()
-    try:
-        files = set()
-        try:
-            patch.patch(ui, repo, patchfile, files=files, eolmode=None)
-            if not files:
-                ui.warn(_('%s: empty changeset')
-                             % node.hex(ha))
-                return ctx, [], [], []
-        finally:
-            os.unlink(patchfile)
-    except Exception:
+    stats = applychanges(ui, repo, oldctx, opts)
+    if stats and stats[3] > 0:
         raise util.Abort(_('Fix up the change and run '
                            'hg histedit --continue'))
     n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
                     date=oldctx.date(), extra=oldctx.extra())
+    if n is None:
+        ui.warn(_('%s: empty changeset')
+                     % node.hex(ha))
+        return ctx, [], [], []
     return finishfold(ui, repo, ctx, oldctx, n, opts, [])
 
 def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
     parent = ctx.parents()[0].node()
     hg.update(repo, parent)
-    fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
-    fp = os.fdopen(fd, 'w')
-    diffopts = patch.diffopts(ui, opts)
-    diffopts.git = True
-    diffopts.ignorews = False
-    diffopts.ignorewsamount = False
-    diffopts.ignoreblanklines = False
-    gen = patch.diff(repo, parent, newnode, opts=diffopts)
-    for chunk in gen:
-        fp.write(chunk)
-    fp.close()
-    files = set()
-    try:
-        patch.patch(ui, repo, patchfile, files=files, eolmode=None)
-    finally:
-        os.unlink(patchfile)
+    ### prepare new commit data
+    commitopts = opts.copy()
+    # username
+    if ctx.user() == oldctx.user():
+        username = ctx.user()
+    else:
+        username = ui.username()
+    commitopts['user'] = username
+    # commit message
     newmessage = '\n***\n'.join(
         [ctx.description()] +
         [repo[r].description() for r in internalchanges] +
         [oldctx.description()]) + '\n'
-    # If the changesets are from the same author, keep it.
-    if ctx.user() == oldctx.user():
-        username = ctx.user()
-    else:
-        username = ui.username()
-    newmessage = ui.edit(newmessage, username)
-    n = repo.commit(text=newmessage, user=username,
-                    date=max(ctx.date(), oldctx.date()), extra=oldctx.extra())
+    commitopts['message'] = newmessage
+    # date
+    commitopts['date'] = max(ctx.date(), oldctx.date())
+    n = collapse(repo, ctx, repo[newnode], commitopts)
+    if n is None:
+        return ctx, [], [], []
+    hg.update(repo, n)
     return repo[n], [n], [oldctx.node(), ctx.node()], [newnode]
 
 def drop(ui, repo, ctx, ha, opts):
@@ -326,24 +343,8 @@
 def message(ui, repo, ctx, ha, opts):
     oldctx = repo[ha]
     hg.update(repo, ctx.node())
-    fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
-    fp = os.fdopen(fd, 'w')
-    diffopts = patch.diffopts(ui, opts)
-    diffopts.git = True
-    diffopts.ignorews = False
-    diffopts.ignorewsamount = False
-    diffopts.ignoreblanklines = False
-    gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
-    for chunk in gen:
-        fp.write(chunk)
-    fp.close()
-    try:
-        files = set()
-        try:
-            patch.patch(ui, repo, patchfile, files=files, eolmode=None)
-        finally:
-            os.unlink(patchfile)
-    except Exception:
+    stats = applychanges(ui, repo, oldctx, opts)
+    if stats and stats[3] > 0:
         raise util.Abort(_('Fix up the change and run '
                            'hg histedit --continue'))
     message = oldctx.description() + '\n'
@@ -356,14 +357,6 @@
     # We didn't make an edit, so just indicate no replaced nodes
     return newctx, [new], [], []
 
-
-def makedesc(c):
-    summary = ''
-    if c.description():
-        summary = c.description().splitlines()[0]
-    line = 'pick %s %d %s' % (c.hex()[:12], c.rev(), summary)
-    return line[:80]  # trim to 80 chars so it's not stupidly wide in my editor
-
 actiontable = {'p': pick,
                'pick': pick,
                'e': edit,
@@ -424,86 +417,22 @@
     if opts.get('continue', False):
         if len(parent) != 0:
             raise util.Abort(_('no arguments allowed with --continue'))
-        (parentctxnode, created, replaced,
-         tmpnodes, existing, rules, keep, tip, replacemap) = readstate(repo)
-        currentparent, wantnull = repo.dirstate.parents()
+        (parentctxnode, created, replaced, tmpnodes,
+         existing, rules, keep, topmost, replacemap) = readstate(repo)
         parentctx = repo[parentctxnode]
-        # existing is the list of revisions initially considered by
-        # histedit. Here we use it to list new changesets, descendants
-        # of parentctx without an 'existing' changeset in-between. We
-        # also have to exclude 'existing' changesets which were
-        # previously dropped.
-        descendants = set(c.node() for c in
-                repo.set('(%n::) - %n', parentctxnode, parentctxnode))
         existing = set(existing)
-        notdropped = set(n for n in existing if n in descendants and
-                (n not in replacemap or replacemap[n] in descendants))
-        # Discover any nodes the user has added in the interim. We can
-        # miss changesets which were dropped and recreated the same.
-        newchildren = list(c.node() for c in repo.set(
-            'sort(%ln - (%ln or %ln::))', descendants, existing, notdropped))
-        action, currentnode = rules.pop(0)
-        if action in ('f', 'fold'):
-            tmpnodes.extend(newchildren)
-        else:
-            created.extend(newchildren)
-
-        m, a, r, d = repo.status()[:4]
-        oldctx = repo[currentnode]
-        message = oldctx.description() + '\n'
-        if action in ('e', 'edit', 'm', 'mess'):
-            message = ui.edit(message, ui.username())
-        elif action in ('f', 'fold'):
-            message = 'fold-temp-revision %s' % currentnode
-        new = None
-        if m or a or r or d:
-            new = repo.commit(text=message, user=oldctx.user(),
-                              date=oldctx.date(), extra=oldctx.extra())
-
-        # If we're resuming a fold and we have new changes, mark the
-        # replacements and finish the fold. If not, it's more like a
-        # drop of the changesets that disappeared, and we can skip
-        # this step.
-        if action in ('f', 'fold') and (new or newchildren):
-            if new:
-                tmpnodes.append(new)
-            else:
-                new = newchildren[-1]
-            (parentctx, created_, replaced_, tmpnodes_) = finishfold(
-                ui, repo, parentctx, oldctx, new, opts, newchildren)
-            replaced.extend(replaced_)
-            created.extend(created_)
-            tmpnodes.extend(tmpnodes_)
-        elif action not in ('d', 'drop'):
-            if new != oldctx.node():
-                replaced.append(oldctx.node())
-            if new:
-                if new != oldctx.node():
-                    created.append(new)
-                parentctx = repo[new]
-
+        parentctx = bootstrapcontinue(ui, repo, parentctx, existing,
+                                      replacemap, rules, tmpnodes, created,
+                                      replaced, opts)
     elif opts.get('abort', False):
         if len(parent) != 0:
             raise util.Abort(_('no arguments allowed with --abort'))
         (parentctxnode, created, replaced, tmpnodes,
-         existing, rules, keep, tip, replacemap) = readstate(repo)
-        ui.debug('restore wc to old tip %s\n' % node.hex(tip))
-        hg.clean(repo, tip)
-        ui.debug('should strip created nodes %s\n' %
-                 ', '.join([node.hex(n)[:12] for n in created]))
-        ui.debug('should strip temp nodes %s\n' %
-                 ', '.join([node.hex(n)[:12] for n in tmpnodes]))
-        for nodes in (created, tmpnodes):
-            lock = None
-            try:
-                lock = repo.lock()
-                for n in reversed(nodes):
-                    try:
-                        repair.strip(ui, repo, n)
-                    except error.LookupError:
-                        pass
-            finally:
-                lockmod.release(lock)
+         existing, rules, keep, topmost, replacemap) = readstate(repo)
+        ui.debug('restore wc to old parent %s\n' % node.short(topmost))
+        hg.clean(repo, topmost)
+        cleanupnode(ui, repo, 'created', created)
+        cleanupnode(ui, repo, 'temp', tmpnodes)
         os.unlink(os.path.join(repo.path, 'histedit-state'))
         return
     else:
@@ -512,7 +441,7 @@
             raise util.Abort(_('history edit already in progress, try '
                                '--continue or --abort'))
 
-        tip, empty = repo.dirstate.parents()
+        topmost, empty = repo.dirstate.parents()
 
 
         if len(parent) != 1:
@@ -520,7 +449,7 @@
         parent = scmutil.revsingle(repo, parent[0]).node()
 
         keep = opts.get('keep', False)
-        revs = between(repo, parent, tip, keep)
+        revs = between(repo, parent, topmost, keep)
 
         ctxs = [repo[r] for r in revs]
         existing = [r.node() for r in ctxs]
@@ -528,7 +457,7 @@
         if not rules:
             rules = '\n'.join([makedesc(c) for c in ctxs])
             rules += '\n\n'
-            rules += editcomment % (node.hex(parent)[:12], node.hex(tip)[:12])
+            rules += editcomment % (node.short(parent), node.short(topmost))
             rules = ui.edit(rules, ui.username())
             # Save edit rules in .hg/histedit-last-edit.txt in case
             # the user needs to ask for help after something
@@ -554,8 +483,9 @@
 
     while rules:
         writestate(repo, parentctx.node(), created, replaced,
-                   tmpnodes, existing, rules, keep, tip, replacemap)
+                   tmpnodes, existing, rules, keep, topmost, replacemap)
         action, ha = rules.pop(0)
+        ui.debug('histedit: processing %s %s\n' % (action, ha))
         (parentctx, created_, replaced_, tmpnodes_) = actiontable[action](
             ui, repo, parentctx, ha, opts)
 
@@ -608,85 +538,125 @@
 
     if not keep:
         if replacemap:
-            ui.note(_('histedit: Should update metadata for the following '
-                      'changes:\n'))
-
-            def copybms(old, new):
-                if old in tmpnodes or old in created:
-                    # can't have any metadata we'd want to update
-                    return
-                while new in replacemap:
-                    new = replacemap[new]
-                ui.note(_('histedit:  %s to %s\n') % (node.short(old),
-                                                      node.short(new)))
-                octx = repo[old]
-                marks = octx.bookmarks()
-                if marks:
-                    ui.note(_('histedit:     moving bookmarks %s\n') %
-                              ', '.join(marks))
-                    for mark in marks:
-                        repo._bookmarks[mark] = new
-                    bookmarks.write(repo)
-
-            # We assume that bookmarks on the tip should remain
-            # tipmost, but bookmarks on non-tip changesets should go
-            # to their most reasonable successor. As a result, find
-            # the old tip and new tip and copy those bookmarks first,
-            # then do the rest of the bookmark copies.
-            oldtip = sorted(replacemap.keys(), key=repo.changelog.rev)[-1]
-            newtip = sorted(replacemap.values(), key=repo.changelog.rev)[-1]
-            copybms(oldtip, newtip)
+            movebookmarks(ui, repo, replacemap, tmpnodes, created)
+            # TODO update mq state
+        cleanupnode(ui, repo, 'replaced', replaced)
 
-            for old, new in sorted(replacemap.iteritems()):
-                copybms(old, new)
-                # TODO update mq state
-
-        ui.debug('should strip replaced nodes %s\n' %
-                 ', '.join([node.hex(n)[:12] for n in replaced]))
-        lock = None
-        try:
-            lock = repo.lock()
-            for n in sorted(replaced, key=lambda x: repo[x].rev()):
-                try:
-                    repair.strip(ui, repo, n)
-                except error.LookupError:
-                    pass
-        finally:
-            lockmod.release(lock)
-
-    ui.debug('should strip temp nodes %s\n' %
-             ', '.join([node.hex(n)[:12] for n in tmpnodes]))
-    lock = None
-    try:
-        lock = repo.lock()
-        for n in reversed(tmpnodes):
-            try:
-                repair.strip(ui, repo, n)
-            except error.LookupError:
-                pass
-    finally:
-        lockmod.release(lock)
+    cleanupnode(ui, repo, 'temp', tmpnodes)
     os.unlink(os.path.join(repo.path, 'histedit-state'))
     if os.path.exists(repo.sjoin('undo')):
         os.unlink(repo.sjoin('undo'))
 
 
+def bootstrapcontinue(ui, repo, parentctx, existing, replacemap, rules,
+                      tmpnodes, created, replaced, opts):
+    currentparent, wantnull = repo.dirstate.parents()
+    # existing is the list of revisions initially considered by
+    # histedit. Here we use it to list new changesets, descendants
+    # of parentctx without an 'existing' changeset in-between. We
+    # also have to exclude 'existing' changesets which were
+    # previously dropped.
+    descendants = set(c.node() for c in
+            repo.set('(%d::) - %d', parentctx, parentctx))
+    notdropped = set(n for n in existing if n in descendants and
+            (n not in replacemap or replacemap[n] in descendants))
+    # Discover any nodes the user has added in the interim. We can
+    # miss changesets which were dropped and recreated the same.
+    newchildren = list(c.node() for c in repo.set(
+        'sort(%ln - (%ln or %ln::))', descendants, existing, notdropped))
+    action, currentnode = rules.pop(0)
+    if action in ('f', 'fold'):
+        tmpnodes.extend(newchildren)
+    else:
+        created.extend(newchildren)
+
+    m, a, r, d = repo.status()[:4]
+    oldctx = repo[currentnode]
+    message = oldctx.description() + '\n'
+    if action in ('e', 'edit', 'm', 'mess'):
+        message = ui.edit(message, ui.username())
+    elif action in ('f', 'fold'):
+        message = 'fold-temp-revision %s' % currentnode
+    new = None
+    if m or a or r or d:
+        new = repo.commit(text=message, user=oldctx.user(),
+                          date=oldctx.date(), extra=oldctx.extra())
+
+    # If we're resuming a fold and we have new changes, mark the
+    # replacements and finish the fold. If not, it's more like a
+    # drop of the changesets that disappeared, and we can skip
+    # this step.
+    if action in ('f', 'fold') and (new or newchildren):
+        if new:
+            tmpnodes.append(new)
+        else:
+            new = newchildren[-1]
+        (parentctx, created_, replaced_, tmpnodes_) = finishfold(
+            ui, repo, parentctx, oldctx, new, opts, newchildren)
+        replaced.extend(replaced_)
+        created.extend(created_)
+        tmpnodes.extend(tmpnodes_)
+    elif action not in ('d', 'drop'):
+        if new != oldctx.node():
+            replaced.append(oldctx.node())
+        if new:
+            if new != oldctx.node():
+                created.append(new)
+            parentctx = repo[new]
+    return parentctx
+
+
+def between(repo, old, new, keep):
+    """select and validate the set of revision to edit
+
+    When keep is false, the specified set can't have children."""
+    revs = [old]
+    current = old
+    while current != new:
+        ctx = repo[current]
+        if not keep and len(ctx.children()) > 1:
+            raise util.Abort(_('cannot edit history that would orphan nodes'))
+        if len(ctx.parents()) != 1 and ctx.parents()[1] != node.nullid:
+            raise util.Abort(_("can't edit history with merges"))
+        if not ctx.children():
+            current = new
+        else:
+            current = ctx.children()[0].node()
+            revs.append(current)
+    if len(repo[current].children()) and not keep:
+        raise util.Abort(_('cannot edit history that would orphan nodes'))
+    return revs
+
+
 def writestate(repo, parentctxnode, created, replaced,
-               tmpnodes, existing, rules, keep, oldtip, replacemap):
+               tmpnodes, existing, rules, keep, topmost, replacemap):
     fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
     pickle.dump((parentctxnode, created, replaced,
-                 tmpnodes, existing, rules, keep, oldtip, replacemap),
+                 tmpnodes, existing, rules, keep, topmost, replacemap),
                 fp)
     fp.close()
 
 def readstate(repo):
     """Returns a tuple of (parentnode, created, replaced, tmp, existing, rules,
-                           keep, oldtip, replacemap ).
+                           keep, topmost, replacemap ).
     """
     fp = open(os.path.join(repo.path, 'histedit-state'))
     return pickle.load(fp)
 
 
+def makedesc(c):
+    """build a initial action line for a ctx `c`
+
+    line are in the form:
+
+      pick <hash> <rev> <summary>
+    """
+    summary = ''
+    if c.description():
+        summary = c.description().splitlines()[0]
+    line = 'pick %s %d %s' % (c, c.rev(), summary)
+    return line[:80]  # trim to 80 chars so it's not stupidly wide in my editor
+
 def verifyrules(rules, repo, ctxs):
     """Verify that there exists exactly one edit rule per given changeset.
 
@@ -714,3 +684,60 @@
             raise util.Abort(_('unknown action "%s"') % action)
         parsed.append([action, ha])
     return parsed
+
+def movebookmarks(ui, repo, replacemap, tmpnodes, created):
+    """Move bookmark from old to newly created node"""
+    ui.note(_('histedit: Should update metadata for the following '
+              'changes:\n'))
+
+    def copybms(old, new):
+        if old in tmpnodes or old in created:
+            # can't have any metadata we'd want to update
+            return
+        while new in replacemap:
+            new = replacemap[new]
+        ui.note(_('histedit:  %s to %s\n') % (node.short(old),
+                                              node.short(new)))
+        octx = repo[old]
+        marks = octx.bookmarks()
+        if marks:
+            ui.note(_('histedit:     moving bookmarks %s\n') %
+                      ', '.join(marks))
+            for mark in marks:
+                repo._bookmarks[mark] = new
+            bookmarks.write(repo)
+
+    # We assume that bookmarks on the tip should remain
+    # tipmost, but bookmarks on non-tip changesets should go
+    # to their most reasonable successor. As a result, find
+    # the old tip and new tip and copy those bookmarks first,
+    # then do the rest of the bookmark copies.
+    oldtip = sorted(replacemap.keys(), key=repo.changelog.rev)[-1]
+    newtip = sorted(replacemap.values(), key=repo.changelog.rev)[-1]
+    copybms(oldtip, newtip)
+
+    for old, new in sorted(replacemap.iteritems()):
+        copybms(old, new)
+
+def cleanupnode(ui, repo, name, nodes):
+    """strip a group of nodes from the repository
+
+    The set of node to strip may contains unknown nodes."""
+    ui.debug('should strip %s nodes %s\n' %
+             (name, ', '.join([node.short(n) for n in nodes])))
+    lock = None
+    try:
+        lock = repo.lock()
+        # Find all node that need to be stripped
+        # (we hg %lr instead of %ln to silently ignore unknown item
+        nm = repo.changelog.nodemap
+        nodes = [n for n in nodes if n in nm]
+        roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
+        for c in roots:
+            # We should process node in reverse order to strip tip most first.
+            # but this trigger a bug in changegroup hook.
+            # This would reduce bundle overhead
+            repair.strip(ui, repo, c)
+    finally:
+        lockmod.release(lock)
+
--- a/hgext/inotify/linux/_inotify.c	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/inotify/linux/_inotify.c	Tue Oct 02 14:31:25 2012 -0700
@@ -61,7 +61,7 @@
 	init_doc,
 	"init() -> fd\n"
 	"\n"
-	"Initialise an inotify instance.\n"
+	"Initialize an inotify instance.\n"
 	"Return a file descriptor associated with a new inotify event queue.");
 
 static PyObject *add_watch(PyObject *self, PyObject *args)
--- a/hgext/inotify/linux/watcher.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/inotify/linux/watcher.py	Tue Oct 02 14:31:25 2012 -0700
@@ -282,7 +282,7 @@
         callable that takes one parameter.  It will be called each time
         a directory is about to be automatically watched.  If it returns
         True, the directory will be watched if it still exists,
-        otherwise, it will beb skipped.'''
+        otherwise, it will be skipped.'''
 
         super(autowatcher, self).__init__()
         self.addfilter = addfilter
--- a/hgext/keyword.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/keyword.py	Tue Oct 02 14:31:25 2012 -0700
@@ -7,7 +7,7 @@
 #
 # $Id$
 #
-# Keyword expansion hack against the grain of a DSCM
+# Keyword expansion hack against the grain of a Distributed SCM
 #
 # There are many good reasons why this is not needed in a distributed
 # SCM, still it may be useful in very small projects based on single
@@ -168,7 +168,7 @@
     return subfunc(r'$\1$', text)
 
 def _preselect(wstatus, changed):
-    '''Retrieves modfied and added files from a working directory state
+    '''Retrieves modified and added files from a working directory state
     and returns the subset of each contained in given changed files
     retrieved from a change context.'''
     modified, added = wstatus[:2]
--- a/hgext/largefiles/basestore.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/largefiles/basestore.py	Tue Oct 02 14:31:25 2012 -0700
@@ -55,7 +55,7 @@
     def get(self, files):
         '''Get the specified largefiles from the store and write to local
         files under repo.root.  files is a list of (filename, hash)
-        tuples.  Return (success, missing), lists of files successfuly
+        tuples.  Return (success, missing), lists of files successfully
         downloaded and those not found in the store.  success is a list
         of (filename, hash) tuples; missing is a list of filenames that
         we could not get.  (The detailed error message will already have
--- a/hgext/largefiles/lfcommands.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/largefiles/lfcommands.py	Tue Oct 02 14:31:25 2012 -0700
@@ -65,7 +65,7 @@
         dstlock = rdst.lock()
 
         # Get a list of all changesets in the source.  The easy way to do this
-        # is to simply walk the changelog, using changelog.nodesbewteen().
+        # is to simply walk the changelog, using changelog.nodesbetween().
         # Take a look at mercurial/revlog.py:639 for more details.
         # Use a generator instead of a list to decrease memory usage
         ctxs = (rsrc[ctx] for ctx in rsrc.changelog.nodesbetween(None,
@@ -177,7 +177,7 @@
         if f not in lfiles and f not in normalfiles:
             islfile = _islfile(f, ctx, matcher, size)
             # If this file was renamed or copied then copy
-            # the lfileness of its predecessor
+            # the largefile-ness of its predecessor
             if f in ctx.manifest():
                 fctx = ctx.filectx(f)
                 renamed = fctx.renamed()
@@ -389,7 +389,7 @@
         # If we are mid-merge, then we have to trust the standin that is in the
         # working copy to have the correct hashvalue.  This is because the
         # original hg.merge() already updated the standin as part of the normal
-        # merge process -- we just have to udpate the largefile to match.
+        # merge process -- we just have to update the largefile to match.
         if (getattr(repo, "_ismerging", False) and
              os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
             expectedhash = lfutil.readstandin(repo, lfile)
@@ -444,11 +444,13 @@
             cachelfiles(ui, repo, '.', lfiles)
 
         updated, removed = 0, 0
-        for i in map(lambda f: _updatelfile(repo, lfdirstate, f), lfiles):
-            # increment the appropriate counter according to _updatelfile's
-            # return value
-            updated += i > 0 and i or 0
-            removed -= i < 0 and i or 0
+        for f in lfiles:
+            i = _updatelfile(repo, lfdirstate, f)
+            if i:
+                if i > 0:
+                    updated += i
+                else:
+                    removed -= i
             if printmessage and (removed or updated) and not printed:
                 ui.status(_('getting changed largefiles\n'))
                 printed = True
--- a/hgext/largefiles/lfutil.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/largefiles/lfutil.py	Tue Oct 02 14:31:25 2012 -0700
@@ -296,8 +296,8 @@
     '''Return the repo-relative path to the standin for the specified big
     file.'''
     # Notes:
-    # 1) Most callers want an absolute path, but _createstandin() needs
-    #    it repo-relative so lfadd() can pass it to repoadd().  So leave
+    # 1) Some callers want an absolute path, but for instance addlargefiles
+    #    needs it repo-relative so it can be passed to repoadd().  So leave
     #    it up to the caller to use repo.wjoin() to get an absolute path.
     # 2) Join with '/' because that's what dirstate always uses, even on
     #    Windows. Change existing separator to '/' first in case we are
--- a/hgext/largefiles/localstore.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/largefiles/localstore.py	Tue Oct 02 14:31:25 2012 -0700
@@ -18,7 +18,7 @@
 
 class localstore(basestore.basestore):
     '''localstore first attempts to grab files out of the store in the remote
-    Mercurial repository.  Failling that, it attempts to grab the files from
+    Mercurial repository.  Failing that, it attempts to grab the files from
     the user cache.'''
 
     def __init__(self, ui, repo, remote):
--- a/hgext/largefiles/overrides.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/largefiles/overrides.py	Tue Oct 02 14:31:25 2012 -0700
@@ -158,7 +158,7 @@
             ui.status(_('removing %s\n') % m.rel(f))
 
     # Need to lock because standin files are deleted then removed from the
-    # repository and we could race inbetween.
+    # repository and we could race in-between.
     wlock = repo.wlock()
     try:
         lfdirstate = lfutil.openlfdirstate(ui, repo)
@@ -256,7 +256,7 @@
 
 # Override needs to refresh standins so that update's normal merge
 # will go through properly. Then the other update hook (overriding repo.update)
-# will get the new files. Filemerge is also overriden so that the merge
+# will get the new files. Filemerge is also overridden so that the merge
 # will merge standins correctly.
 def overrideupdate(orig, ui, repo, *pats, **opts):
     lfdirstate = lfutil.openlfdirstate(ui, repo)
@@ -701,7 +701,7 @@
         result = orig(ui, repo, source, **opts)
         # If we do not have the new largefiles for any new heads we pulled, we
         # will run into a problem later if we try to merge or rebase with one of
-        # these heads, so cache the largefiles now direclty into the system
+        # these heads, so cache the largefiles now directly into the system
         # cache.
         ui.status(_("caching new largefiles\n"))
         numcached = 0
@@ -933,7 +933,7 @@
             ui.status(_('removing %s\n') % m.rel(f))
 
     # Need to lock because standin files are deleted then removed from the
-    # repository and we could race inbetween.
+    # repository and we could race in-between.
     wlock = repo.wlock()
     try:
         lfdirstate = lfutil.openlfdirstate(ui, repo)
--- a/hgext/largefiles/remotestore.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/largefiles/remotestore.py	Tue Oct 02 14:31:25 2012 -0700
@@ -4,7 +4,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-'''remote largefile store; the base class for servestore'''
+'''remote largefile store; the base class for wirestore'''
 
 import urllib2
 
--- a/hgext/largefiles/reposetup.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/largefiles/reposetup.py	Tue Oct 02 14:31:25 2012 -0700
@@ -156,7 +156,7 @@
                 # Create a function that we can use to override what is
                 # normally the ignore matcher.  We've already checked
                 # for ignored files on the first dirstate walk, and
-                # unecessarily re-checking here causes a huge performance
+                # unnecessarily re-checking here causes a huge performance
                 # hit because lfdirstate only knows about largefiles
                 def _ignoreoverride(self):
                     return False
@@ -192,7 +192,7 @@
                         (unsure, modified, added, removed, missing, unknown,
                                 ignored, clean) = s
                         # Replace the list of ignored and unknown files with
-                        # the previously caclulated lists, and strip out the
+                        # the previously calculated lists, and strip out the
                         # largefiles
                         lfiles = set(lfdirstate._map)
                         ignored = set(result[5]).difference(lfiles)
--- a/hgext/mq.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/mq.py	Tue Oct 02 14:31:25 2012 -0700
@@ -1522,7 +1522,7 @@
             #
             # this should really read:
             #   mm, dd, aa = repo.status(top, patchparent)[:3]
-            # but we do it backwards to take advantage of manifest/chlog
+            # but we do it backwards to take advantage of manifest/changelog
             # caching against the next repo.status call
             mm, aa, dd = repo.status(patchparent, top)[:3]
             changes = repo.changelog.read(top)
@@ -1535,7 +1535,7 @@
                 # if amending a patch, we start with existing
                 # files plus specified files - unfiltered
                 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
-                # filter with inc/exl options
+                # filter with include/exclude options
                 matchfn = scmutil.match(repo[None], opts=opts)
             else:
                 match = scmutil.matchall(repo)
@@ -3185,9 +3185,9 @@
     revs = scmutil.revrange(repo, revrange)
     if repo['.'].rev() in revs and repo[None].files():
         ui.warn(_('warning: uncommitted changes in the working directory\n'))
-    # queue.finish may changes phases but leave the responsability to lock the
+    # queue.finish may changes phases but leave the responsibility to lock the
     # repo to the caller to avoid deadlock with wlock. This command code is
-    # responsability for this locking.
+    # responsibility for this locking.
     lock = repo.lock()
     try:
         q.finish(repo, revs)
--- a/hgext/rebase.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/rebase.py	Tue Oct 02 14:31:25 2012 -0700
@@ -15,7 +15,7 @@
 '''
 
 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
-from mercurial import extensions, patch, scmutil, phases
+from mercurial import extensions, patch, scmutil, phases, obsolete
 from mercurial.commands import templateopts
 from mercurial.node import nullrev
 from mercurial.lock import release
@@ -184,7 +184,8 @@
                 rebaseset = repo.revs(
                     '(children(ancestor(%ld, %d)) and ::(%ld))::',
                     base, dest, base)
-
+            # temporary top level filtering of extinct revisions
+            rebaseset = repo.revs('%ld - hidden()', rebaseset)
             if rebaseset:
                 root = min(rebaseset)
             else:
@@ -193,8 +194,8 @@
             if not rebaseset:
                 repo.ui.debug('base is ancestor of destination\n')
                 result = None
-            elif not keepf and list(repo.revs('first(children(%ld) - %ld)',
-                                              rebaseset, rebaseset)):
+            elif not keepf and repo.revs('first(children(%ld) - %ld)-hidden()',
+                                         rebaseset, rebaseset):
                 raise util.Abort(
                     _("can't remove original changesets with"
                       " unrebased descendants"),
@@ -310,15 +311,10 @@
                     nstate[repo[k].node()] = repo[v].node()
 
         if not keepf:
-            # Remove no more useful revisions
-            rebased = [rev for rev in state if state[rev] != nullmerge]
-            if rebased:
-                if set(repo.changelog.descendants([min(rebased)])) - set(state):
-                    ui.warn(_("warning: new changesets detected "
-                              "on source branch, not stripping\n"))
-                else:
-                    # backup the old csets by default
-                    repair.strip(ui, repo, repo[min(rebased)].node(), "all")
+            collapsedas = None
+            if collapsef:
+                collapsedas = newrev
+            clearrebased(ui, repo, state, collapsedas)
 
         if currentbookmarks:
             updatebookmarks(repo, nstate, currentbookmarks, **opts)
@@ -664,6 +660,31 @@
         state.update(dict.fromkeys(detachset, nullmerge))
     return repo['.'].rev(), dest.rev(), state
 
+def clearrebased(ui, repo, state, collapsedas=None):
+    """dispose of rebased revision at the end of the rebase
+
+    If `collapsedas` is not None, the rebase was a collapse whose result if the
+    `collapsedas` node."""
+    if obsolete._enabled:
+        markers = []
+        for rev, newrev in sorted(state.items()):
+            if newrev >= 0:
+                if collapsedas is not None:
+                    newrev = collapsedas
+                markers.append((repo[rev], (repo[newrev],)))
+        if markers:
+            obsolete.createmarkers(repo, markers)
+    else:
+        rebased = [rev for rev in state if state[rev] != nullmerge]
+        if rebased:
+            if set(repo.changelog.descendants([min(rebased)])) - set(state):
+                ui.warn(_("warning: new changesets detected "
+                          "on source branch, not stripping\n"))
+            else:
+                # backup the old csets by default
+                repair.strip(ui, repo, repo[min(rebased)].node(), "all")
+
+
 def pullrebase(orig, ui, repo, *args, **opts):
     'Call rebase after pull if the latter has been invoked with --rebase'
     if opts.get('rebase'):
--- a/hgext/record.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/record.py	Tue Oct 02 14:31:25 2012 -0700
@@ -33,7 +33,7 @@
     - ('file',    [header_lines + fromfile + tofile])
     - ('context', [context_lines])
     - ('hunk',    [hunk_lines])
-    - ('range',   (-start,len, +start,len, diffp))
+    - ('range',   (-start,len, +start,len, proc))
     """
     lr = patch.linereader(fp)
 
@@ -81,7 +81,7 @@
 class header(object):
     """patch header
 
-    XXX shoudn't we move this to mercurial/patch.py ?
+    XXX shouldn't we move this to mercurial/patch.py ?
     """
     diffgit_re = re.compile('diff --git a/(.*) b/(.*)$')
     diff_re = re.compile('diff -r .* (.*)$')
@@ -393,11 +393,11 @@
             if skipfile is None and skipall is None:
                 chunk.pretty(ui)
             if total == 1:
-                msg = _('record this change to %r?') % chunk.filename()
+                msg = _("record this change to '%s'?") % chunk.filename()
             else:
                 idx = pos - len(h.hunks) + i
-                msg = _('record change %d/%d to %r?') % (idx, total,
-                                                         chunk.filename())
+                msg = _("record change %d/%d to '%s'?") % (idx, total,
+                                                           chunk.filename())
             r, skipfile, skipall, newpatches = prompt(skipfile,
                     skipall, msg, chunk)
             if r:
@@ -496,6 +496,9 @@
         raise util.Abort(_('running non-interactively, use %s instead') %
                          cmdsuggest)
 
+    # make sure username is set before going interactive
+    ui.username()
+
     def recordfunc(ui, repo, message, match, opts):
         """This is generic record driver.
 
--- a/hgext/schemes.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/schemes.py	Tue Oct 02 14:31:25 2012 -0700
@@ -61,7 +61,7 @@
         return '<ShortRepository: %s>' % self.scheme
 
     def instance(self, ui, url, create):
-        # Should this use urlmod.url(), or is manual parsing better?
+        # Should this use the util.url class, or is manual parsing better?
         url = url.split('://', 1)[1]
         parts = url.split('/', self.parts)
         if len(parts) > self.parts:
--- a/hgext/win32mbcs.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/win32mbcs.py	Tue Oct 02 14:31:25 2012 -0700
@@ -119,7 +119,7 @@
     def f(*args, **kwds):
         return wrapper(func, args, kwds)
     try:
-        f.__name__ = func.__name__                # fail with python23
+        f.__name__ = func.__name__ # fails with Python 2.3
     except Exception:
         pass
     setattr(module, name, f)
--- a/hgext/zeroconf/Zeroconf.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/hgext/zeroconf/Zeroconf.py	Tue Oct 02 14:31:25 2012 -0700
@@ -66,7 +66,7 @@
 				 using select() for socket reads
 				 tested on Debian unstable with Python 2.2.2"""
 
-"""0.05 update - ensure case insensitivty on domain names
+"""0.05 update - ensure case insensitivity on domain names
                  support for unicast DNS queries"""
 
 """0.04 update - added some unit tests
@@ -114,7 +114,7 @@
 _FLAGS_QR_QUERY = 0x0000 # query
 _FLAGS_QR_RESPONSE = 0x8000 # response
 
-_FLAGS_AA = 0x0400 # Authorative answer
+_FLAGS_AA = 0x0400 # Authoritative answer
 _FLAGS_TC = 0x0200 # Truncated
 _FLAGS_RD = 0x0100 # Recursion desired
 _FLAGS_RA = 0x8000 # Recursion available
@@ -335,7 +335,7 @@
 		raise AbstractMethodException
 
 	def toString(self, other):
-		"""String representation with addtional information"""
+		"""String representation with additional information"""
 		arg = "%s/%s,%s" % (self.ttl, self.getRemainingTTL(currentTimeMillis()), other)
 		return DNSEntry.toString(self, "record", arg)
 
@@ -650,7 +650,7 @@
 			if now == 0 or not record.isExpired(now):
 				self.answers.append((record, now))
 
-	def addAuthorativeAnswer(self, record):
+	def addAuthoritativeAnswer(self, record):
 		"""Adds an authoritative answer"""
 		self.authorities.append(record)
 
@@ -904,7 +904,7 @@
 	to cache information as it arrives.
 
 	It requires registration with an Engine object in order to have
-	the read() method called when a socket is availble for reading."""
+	the read() method called when a socket is available for reading."""
 
 	def __init__(self, zeroconf):
 		self.zeroconf = zeroconf
@@ -1140,7 +1140,7 @@
 		return self.port
 
 	def getPriority(self):
-		"""Pirority accessor"""
+		"""Priority accessor"""
 		return self.priority
 
 	def getWeight(self):
@@ -1259,7 +1259,7 @@
 			# SO_REUSEADDR should be equivalent to SO_REUSEPORT for
 			# multicast UDP sockets (p 731, "TCP/IP Illustrated,
 			# Volume 2"), but some BSD-derived systems require
-			# SO_REUSEPORT to be specified explicity.  Also, not all
+			# SO_REUSEPORT to be specified explicitly.  Also, not all
 			# versions of Python have SO_REUSEPORT available.  So
 			# if you're on a BSD-based system, and haven't upgraded
 			# to Python 2.3 yet, you may find this library doesn't
@@ -1272,10 +1272,8 @@
 			self.socket.bind(self.group)
 		except Exception:
 			# Some versions of linux raise an exception even though
-			# the SO_REUSE* options have been set, so ignore it
-			#
+			# SO_REUSEADDR and SO_REUSEPORT have been set, so ignore it
 			pass
-		#self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(self.intf) + socket.inet_aton('0.0.0.0'))
 		self.socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0'))
 
 		self.listeners = []
@@ -1433,7 +1431,7 @@
 			out = DNSOutgoing(_FLAGS_QR_QUERY | _FLAGS_AA)
 			self.debug = out
 			out.addQuestion(DNSQuestion(info.type, _TYPE_PTR, _CLASS_IN))
-			out.addAuthorativeAnswer(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, info.name))
+			out.addAuthoritativeAnswer(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, info.name))
 			self.send(out)
 			i += 1
 			nextTime += _CHECK_TIME
--- a/i18n/ru.po	Mon Oct 01 23:11:28 2012 -0500
+++ b/i18n/ru.po	Tue Oct 02 14:31:25 2012 -0700
@@ -14555,7 +14555,7 @@
 "    Update sets the working directory's parent revision to the specified\n"
 "    changeset (see :hg:`help parents`)."
 msgstr ""
-"    Update устанавливает ревизию родителя рабочего каталога в заданный\n"
+"    Update делает заданный набор изменений родительской ревизией рабочего\n"
 "    набор изменений (см. :hg:`help parents`)."
 
 msgid ""
--- a/mercurial/archival.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/archival.py	Tue Oct 02 14:31:25 2012 -0700
@@ -14,6 +14,10 @@
 import zlib, gzip
 import struct
 
+# from unzip source code:
+_UNX_IFREG = 0x8000
+_UNX_IFLNK = 0xa000
+
 def tidyprefix(dest, kind, prefix):
     '''choose prefix to use for names in archive.  make sure prefix is
     safe for consumers.'''
@@ -175,10 +179,10 @@
         # unzip will not honor unix file modes unless file creator is
         # set to unix (id 3).
         i.create_system = 3
-        ftype = 0x8000 # UNX_IFREG in unzip source code
+        ftype = _UNX_IFREG
         if islink:
             mode = 0777
-            ftype = 0xa000 # UNX_IFLNK in unzip source code
+            ftype = _UNX_IFLNK
         i.external_attr = (mode | ftype) << 16L
         # add "extended-timestamp" extra block, because zip archives
         # without this will be extracted with unexpected timestamp,
--- a/mercurial/bookmarks.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/bookmarks.py	Tue Oct 02 14:31:25 2012 -0700
@@ -7,7 +7,7 @@
 
 from mercurial.i18n import _
 from mercurial.node import hex
-from mercurial import encoding, error, util
+from mercurial import encoding, error, util, obsolete, phases
 import errno, os
 
 def valid(mark):
@@ -58,7 +58,7 @@
             raise
         return None
     try:
-        # No readline() in posixfile_nt, reading everything is cheap
+        # No readline() in osutil.posixfile, reading everything is cheap
         mark = encoding.tolocal((file.readlines() or [''])[0])
         if mark == '' or mark not in repo._bookmarks:
             mark = None
@@ -159,7 +159,7 @@
         if mark and marks[mark] in parents:
             old = repo[marks[mark]]
             new = repo[node]
-            if new in old.descendants() and mark == cur:
+            if old.descendant(new) and mark == cur:
                 marks[cur] = new.node()
                 update = True
             if mark != cur:
@@ -209,7 +209,7 @@
                 cl = repo[nl]
                 if cl.rev() >= cr.rev():
                     continue
-                if cr in cl.descendants():
+                if validdest(repo, cl, cr):
                     repo._bookmarks[k] = cr.node()
                     changed = True
                     ui.status(_("updating bookmark %s\n") % k)
@@ -237,18 +237,48 @@
     if changed:
         write(repo)
 
-def diff(ui, repo, remote):
+def diff(ui, dst, src):
     ui.status(_("searching for changed bookmarks\n"))
 
-    lmarks = repo.listkeys('bookmarks')
-    rmarks = remote.listkeys('bookmarks')
+    smarks = src.listkeys('bookmarks')
+    dmarks = dst.listkeys('bookmarks')
 
-    diff = sorted(set(rmarks) - set(lmarks))
+    diff = sorted(set(smarks) - set(dmarks))
     for k in diff:
-        mark = ui.debugflag and rmarks[k] or rmarks[k][:12]
+        mark = ui.debugflag and smarks[k] or smarks[k][:12]
         ui.write("   %-25s %s\n" % (k, mark))
 
     if len(diff) <= 0:
         ui.status(_("no changed bookmarks found\n"))
         return 1
     return 0
+
+def validdest(repo, old, new):
+    """Is the new bookmark destination a valid update from the old one"""
+    if old == new:
+        # Old == new -> nothing to update.
+        return False
+    elif not old:
+        # old is nullrev, anything is valid.
+        # (new != nullrev has been excluded by the previous check)
+        return True
+    elif repo.obsstore:
+        # We only need this complicated logic if there is obsolescence
+        # XXX will probably deserve an optimised rset.
+
+        validdests = set([old])
+        plen = -1
+        # compute the whole set of successors or descendants
+        while len(validdests) != plen:
+            plen = len(validdests)
+            succs = set(c.node() for c in validdests)
+            for c in validdests:
+                if c.phase() > phases.public:
+                    # obsolescence marker does not apply to public changeset
+                    succs.update(obsolete.anysuccessors(repo.obsstore,
+                                                        c.node()))
+            validdests = set(repo.set('%ln::', succs))
+        validdests.remove(old)
+        return new in validdests
+    else:
+        return old.descendant(new)
--- a/mercurial/byterange.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/byterange.py	Tue Oct 02 14:31:25 2012 -0700
@@ -32,7 +32,7 @@
 
     This was extremely simple. The Range header is a HTTP feature to
     begin with so all this class does is tell urllib2 that the
-    "206 Partial Content" reponse from the HTTP server is what we
+    "206 Partial Content" response from the HTTP server is what we
     expected.
 
     Example:
@@ -64,7 +64,7 @@
 
 class RangeableFileObject(object):
     """File object wrapper to enable raw range handling.
-    This was implemented primarilary for handling range
+    This was implemented primarily for handling range
     specifications for file:// urls. This object effectively makes
     a file object look like it consists only of a range of bytes in
     the stream.
@@ -431,7 +431,7 @@
     Return a tuple whose first element is guaranteed to be an int
     and whose second element will be '' (meaning: the last byte) or
     an int. Finally, return None if the normalized tuple == (0,'')
-    as that is equivelant to retrieving the entire file.
+    as that is equivalent to retrieving the entire file.
     """
     if range_tup is None:
         return None
--- a/mercurial/changelog.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/changelog.py	Tue Oct 02 14:31:25 2012 -0700
@@ -120,6 +120,82 @@
         self._realopener = opener
         self._delayed = False
         self._divert = False
+        self.filteredrevs = ()
+
+    def tip(self):
+        """filtered version of revlog.tip"""
+        for i in xrange(len(self) -1, -2, -1):
+            if i not in self.filteredrevs:
+                return self.node(i)
+
+    def __iter__(self):
+        """filtered version of revlog.__iter__"""
+        for i in xrange(len(self)):
+            if i not in self.filteredrevs:
+                yield i
+
+    def revs(self, start=0, stop=None):
+        """filtered version of revlog.revs"""
+        for i in super(changelog, self).revs(start, stop):
+            if i not in self.filteredrevs:
+                yield i
+
+    @util.propertycache
+    def nodemap(self):
+        # XXX need filtering too
+        self.rev(self.node(0))
+        return self._nodecache
+
+    def hasnode(self, node):
+        """filtered version of revlog.hasnode"""
+        try:
+            i = self.rev(node)
+            return i not in self.filteredrevs
+        except KeyError:
+            return False
+
+    def headrevs(self):
+        if self.filteredrevs:
+            # XXX we should fix and use the C version
+            return self._headrevs()
+        return super(changelog, self).headrevs()
+
+    def strip(self, *args, **kwargs):
+        # XXX make something better than assert
+        # We can't expect proper strip behavior if we are filtered.
+        assert not self.filteredrevs
+        super(changelog, self).strip(*args, **kwargs)
+
+    def rev(self, node):
+        """filtered version of revlog.rev"""
+        r = super(changelog, self).rev(node)
+        if r in self.filteredrevs:
+            raise error.LookupError(node, self.indexfile, _('no node'))
+        return r
+
+    def node(self, rev):
+        """filtered version of revlog.node"""
+        if rev in self.filteredrevs:
+            raise IndexError(rev)
+        return super(changelog, self).node(rev)
+
+    def linkrev(self, rev):
+        """filtered version of revlog.linkrev"""
+        if rev in self.filteredrevs:
+            raise IndexError(rev)
+        return super(changelog, self).linkrev(rev)
+
+    def parentrevs(self, rev):
+        """filtered version of revlog.parentrevs"""
+        if rev in self.filteredrevs:
+            raise IndexError(rev)
+        return super(changelog, self).parentrevs(rev)
+
+    def flags(self, rev):
+        """filtered version of revlog.flags"""
+        if rev in self.filteredrevs:
+            raise IndexError(rev)
+        return super(changelog, self).flags(rev)
 
     def delayupdate(self):
         "delay visibility of index updates to other readers"
@@ -183,7 +259,7 @@
         nodeid\n        : manifest node in ascii
         user\n          : user, no \n or \r allowed
         time tz extra\n : date (time is int or float, timezone is int)
-                        : extra is metadatas, encoded and separated by '\0'
+                        : extra is metadata, encoded and separated by '\0'
                         : older versions ignore it
         files\n\n       : files modified by the cset, no \n or \r allowed
         (.*)            : comment (free text, ideally utf-8)
--- a/mercurial/cmdutil.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/cmdutil.py	Tue Oct 02 14:31:25 2012 -0700
@@ -10,7 +10,8 @@
 import os, sys, errno, re, tempfile
 import util, scmutil, templater, patch, error, templatekw, revlog, copies
 import match as matchmod
-import subrepo, context, repair, bookmarks, graphmod, revset, phases
+import subrepo, context, repair, bookmarks, graphmod, revset, phases, obsolete
+import lock as lockmod
 
 def parsealiases(cmd):
     return cmd.lstrip("^").split("|")
@@ -547,30 +548,37 @@
         prev = (parents and parents[0]) or nullid
 
         shouldclose = False
-        if not fp:
+        if not fp and len(template) > 0:
             desc_lines = ctx.description().rstrip().split('\n')
             desc = desc_lines[0]    #Commit always has a first line.
             fp = makefileobj(repo, template, node, desc=desc, total=total,
                              seqno=seqno, revwidth=revwidth, mode='ab')
             if fp != template:
                 shouldclose = True
-        if fp != sys.stdout and util.safehasattr(fp, 'name'):
+        if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
             repo.ui.note("%s\n" % fp.name)
 
-        fp.write("# HG changeset patch\n")
-        fp.write("# User %s\n" % ctx.user())
-        fp.write("# Date %d %d\n" % ctx.date())
+        if not fp:
+            write = repo.ui.write
+        else:
+            def write(s, **kw):
+                fp.write(s)
+
+
+        write("# HG changeset patch\n")
+        write("# User %s\n" % ctx.user())
+        write("# Date %d %d\n" % ctx.date())
         if branch and branch != 'default':
-            fp.write("# Branch %s\n" % branch)
-        fp.write("# Node ID %s\n" % hex(node))
-        fp.write("# Parent  %s\n" % hex(prev))
+            write("# Branch %s\n" % branch)
+        write("# Node ID %s\n" % hex(node))
+        write("# Parent  %s\n" % hex(prev))
         if len(parents) > 1:
-            fp.write("# Parent  %s\n" % hex(parents[1]))
-        fp.write(ctx.description().rstrip())
-        fp.write("\n\n")
+            write("# Parent  %s\n" % hex(parents[1]))
+        write(ctx.description().rstrip())
+        write("\n\n")
 
-        for chunk in patch.diff(repo, prev, node, opts=opts):
-            fp.write(chunk)
+        for chunk, label in patch.diffui(repo, prev, node, opts=opts):
+            write(chunk, label=label)
 
         if shouldclose:
             fp.close()
@@ -991,12 +999,13 @@
 
     if not len(repo):
         return []
-
-    if follow:
-        defrange = '%s:0' % repo['.'].rev()
+    if opts.get('rev'):
+        revs = scmutil.revrange(repo, opts.get('rev'))
+    elif follow:
+        revs = repo.revs('reverse(:.)')
     else:
-        defrange = '-1:0'
-    revs = scmutil.revrange(repo, opts.get('rev') or [defrange])
+        revs = list(repo)
+        revs.reverse()
     if not revs:
         return []
     wanted = set()
@@ -1258,7 +1267,7 @@
     opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
     opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
     # pats/include/exclude are passed to match.match() directly in
-    # _matchfile() revset but walkchangerevs() builds its matcher with
+    # _matchfiles() revset but walkchangerevs() builds its matcher with
     # scmutil.match(). The difference is input pats are globbed on
     # platforms without shell expansion (windows).
     pctx = repo[None]
@@ -1304,7 +1313,7 @@
             fnopats = (('_ancestors', '_fancestors'),
                        ('_descendants', '_fdescendants'))
             if pats:
-                # follow() revset inteprets its file argument as a
+                # follow() revset interprets its file argument as a
                 # manifest entry, so use match.files(), not pats.
                 opts[fpats[followfirst]] = list(match.files())
             else:
@@ -1384,9 +1393,10 @@
         revs = scmutil.revrange(repo, opts['rev'])
     else:
         if follow and len(repo) > 0:
-            revs = scmutil.revrange(repo, ['.:0'])
+            revs = repo.revs('reverse(:.)')
         else:
-            revs = range(len(repo) - 1, -1, -1)
+            revs = list(repo.changelog)
+            revs.reverse()
     if not revs:
         return iter([]), None, None
     expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
@@ -1568,135 +1578,147 @@
     ui.note(_('amending changeset %s\n') % old)
     base = old.p1()
 
-    wlock = repo.wlock()
+    wlock = lock = None
     try:
-        # First, do a regular commit to record all changes in the working
-        # directory (if there are any)
-        ui.callhooks = False
+        wlock = repo.wlock()
+        lock = repo.lock()
+        tr = repo.transaction('amend')
         try:
-            node = commit(ui, repo, commitfunc, pats, opts)
-        finally:
-            ui.callhooks = True
-        ctx = repo[node]
-
-        # Participating changesets:
-        #
-        # node/ctx o - new (intermediate) commit that contains changes from
-        #          |   working dir to go into amending commit (or a workingctx
-        #          |   if there were no changes)
-        #          |
-        # old      o - changeset to amend
-        #          |
-        # base     o - parent of amending changeset
-
-        # Update extra dict from amended commit (e.g. to preserve graft source)
-        extra.update(old.extra())
-
-        # Also update it from the intermediate commit or from the wctx
-        extra.update(ctx.extra())
-
-        files = set(old.files())
-
-        # Second, we use either the commit we just did, or if there were no
-        # changes the parent of the working directory as the version of the
-        # files in the final amend commit
-        if node:
-            ui.note(_('copying changeset %s to %s\n') % (ctx, base))
-
-            user = ctx.user()
-            date = ctx.date()
-            message = ctx.description()
-            # Recompute copies (avoid recording a -> b -> a)
-            copied = copies.pathcopies(base, ctx)
-
-            # Prune files which were reverted by the updates: if old introduced
-            # file X and our intermediate commit, node, renamed that file, then
-            # those two files are the same and we can discard X from our list
-            # of files. Likewise if X was deleted, it's no longer relevant
-            files.update(ctx.files())
-
-            def samefile(f):
-                if f in ctx.manifest():
-                    a = ctx.filectx(f)
-                    if f in base.manifest():
-                        b = base.filectx(f)
-                        return (not a.cmp(b)
-                                and a.flags() == b.flags())
-                    else:
-                        return False
-                else:
-                    return f not in base.manifest()
-            files = [f for f in files if not samefile(f)]
-
-            def filectxfn(repo, ctx_, path):
-                try:
-                    fctx = ctx[path]
-                    flags = fctx.flags()
-                    mctx = context.memfilectx(fctx.path(), fctx.data(),
-                                              islink='l' in flags,
-                                              isexec='x' in flags,
-                                              copied=copied.get(path))
-                    return mctx
-                except KeyError:
-                    raise IOError
-        else:
-            ui.note(_('copying changeset %s to %s\n') % (old, base))
-
-            # Use version of files as in the old cset
-            def filectxfn(repo, ctx_, path):
-                try:
-                    return old.filectx(path)
-                except KeyError:
-                    raise IOError
-
             # See if we got a message from -m or -l, if not, open the editor
             # with the message of the changeset to amend
-            user = opts.get('user') or old.user()
-            date = opts.get('date') or old.date()
             message = logmessage(ui, opts)
-            if not message:
-                cctx = context.workingctx(repo, old.description(), user, date,
-                                          extra,
-                                          repo.status(base.node(), old.node()))
-                message = commitforceeditor(repo, cctx, [])
+            # First, do a regular commit to record all changes in the working
+            # directory (if there are any)
+            ui.callhooks = False
+            try:
+                opts['message'] = 'temporary amend commit for %s' % old
+                node = commit(ui, repo, commitfunc, pats, opts)
+            finally:
+                ui.callhooks = True
+            ctx = repo[node]
+
+            # Participating changesets:
+            #
+            # node/ctx o - new (intermediate) commit that contains changes
+            #          |   from working dir to go into amending commit
+            #          |   (or a workingctx if there were no changes)
+            #          |
+            # old      o - changeset to amend
+            #          |
+            # base     o - parent of amending changeset
+
+            # Update extra dict from amended commit (e.g. to preserve graft
+            # source)
+            extra.update(old.extra())
+
+            # Also update it from the intermediate commit or from the wctx
+            extra.update(ctx.extra())
+
+            files = set(old.files())
+
+            # Second, we use either the commit we just did, or if there were no
+            # changes the parent of the working directory as the version of the
+            # files in the final amend commit
+            if node:
+                ui.note(_('copying changeset %s to %s\n') % (ctx, base))
+
+                user = ctx.user()
+                date = ctx.date()
+                # Recompute copies (avoid recording a -> b -> a)
+                copied = copies.pathcopies(base, ctx)
+
+                # Prune files which were reverted by the updates: if old
+                # introduced file X and our intermediate commit, node,
+                # renamed that file, then those two files are the same and
+                # we can discard X from our list of files. Likewise if X
+                # was deleted, it's no longer relevant
+                files.update(ctx.files())
+
+                def samefile(f):
+                    if f in ctx.manifest():
+                        a = ctx.filectx(f)
+                        if f in base.manifest():
+                            b = base.filectx(f)
+                            return (not a.cmp(b)
+                                    and a.flags() == b.flags())
+                        else:
+                            return False
+                    else:
+                        return f not in base.manifest()
+                files = [f for f in files if not samefile(f)]
 
-        new = context.memctx(repo,
-                             parents=[base.node(), nullid],
-                             text=message,
-                             files=files,
-                             filectxfn=filectxfn,
-                             user=user,
-                             date=date,
-                             extra=extra)
-        ph = repo.ui.config('phases', 'new-commit', phases.draft)
-        try:
-            repo.ui.setconfig('phases', 'new-commit', old.phase())
-            newid = repo.commitctx(new)
+                def filectxfn(repo, ctx_, path):
+                    try:
+                        fctx = ctx[path]
+                        flags = fctx.flags()
+                        mctx = context.memfilectx(fctx.path(), fctx.data(),
+                                                  islink='l' in flags,
+                                                  isexec='x' in flags,
+                                                  copied=copied.get(path))
+                        return mctx
+                    except KeyError:
+                        raise IOError
+            else:
+                ui.note(_('copying changeset %s to %s\n') % (old, base))
+
+                # Use version of files as in the old cset
+                def filectxfn(repo, ctx_, path):
+                    try:
+                        return old.filectx(path)
+                    except KeyError:
+                        raise IOError
+
+                user = opts.get('user') or old.user()
+                date = opts.get('date') or old.date()
+            if not message:
+                message = old.description()
+
+            new = context.memctx(repo,
+                                 parents=[base.node(), nullid],
+                                 text=message,
+                                 files=files,
+                                 filectxfn=filectxfn,
+                                 user=user,
+                                 date=date,
+                                 extra=extra)
+            new._text = commitforceeditor(repo, new, [])
+            ph = repo.ui.config('phases', 'new-commit', phases.draft)
+            try:
+                repo.ui.setconfig('phases', 'new-commit', old.phase())
+                newid = repo.commitctx(new)
+            finally:
+                repo.ui.setconfig('phases', 'new-commit', ph)
+            if newid != old.node():
+                # Reroute the working copy parent to the new changeset
+                repo.setparents(newid, nullid)
+
+                # Move bookmarks from old parent to amend commit
+                bms = repo.nodebookmarks(old.node())
+                if bms:
+                    for bm in bms:
+                        repo._bookmarks[bm] = newid
+                    bookmarks.write(repo)
+            #commit the whole amend process
+            if obsolete._enabled and newid != old.node():
+                # mark the new changeset as successor of the rewritten one
+                new = repo[newid]
+                obs = [(old, (new,))]
+                if node:
+                    obs.append((ctx, (new,)))
+
+                obsolete.createmarkers(repo, obs)
+            tr.close()
         finally:
-            repo.ui.setconfig('phases', 'new-commit', ph)
-        if newid != old.node():
-            # Reroute the working copy parent to the new changeset
-            repo.setparents(newid, nullid)
-
-            # Move bookmarks from old parent to amend commit
-            bms = repo.nodebookmarks(old.node())
-            if bms:
-                for bm in bms:
-                    repo._bookmarks[bm] = newid
-                bookmarks.write(repo)
-
+            tr.release()
+        if (not obsolete._enabled) and newid != old.node():
             # Strip the intermediate commit (if there was one) and the amended
             # commit
-            lock = repo.lock()
-            try:
-                if node:
-                    ui.note(_('stripping intermediate changeset %s\n') % ctx)
-                ui.note(_('stripping amended changeset %s\n') % old)
-                repair.strip(ui, repo, old.node(), topic='amend-backup')
-            finally:
-                lock.release()
+            if node:
+                ui.note(_('stripping intermediate changeset %s\n') % ctx)
+            ui.note(_('stripping amended changeset %s\n') % old)
+            repair.strip(ui, repo, old.node(), topic='amend-backup')
     finally:
-        wlock.release()
+        lockmod.release(wlock, lock)
     return newid
 
 def commiteditor(repo, ctx, subs):
--- a/mercurial/commands.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/commands.py	Tue Oct 02 14:31:25 2012 -0700
@@ -543,7 +543,7 @@
           hg bisect --good
           hg bisect --bad
 
-      - mark the current revision, or a known revision, to be skipped (eg. if
+      - mark the current revision, or a known revision, to be skipped (e.g. if
         that revision is not usable because of another issue)::
 
           hg bisect --skip
@@ -833,7 +833,7 @@
             raise util.Abort(
                 _("a bookmark cannot have the name of an existing branch"))
         if rev:
-            marks[mark] = repo.lookup(rev)
+            marks[mark] = scmutil.revsingle(repo, rev).node()
         else:
             marks[mark] = cur
         if not inactive and cur == marks[mark]:
@@ -1252,7 +1252,7 @@
     Returns 0 on success, 1 if nothing changed.
     """
     if opts.get('subrepos'):
-        # Let --subrepos on the command line overide config setting.
+        # Let --subrepos on the command line override config setting.
         ui.setconfig('ui', 'commitsubrepos', True)
 
     extra = {}
@@ -1352,20 +1352,20 @@
         # printed anyway.
         #
         # Par Msg Comment
-        # NN   y  additional topo root
+        # N N  y  additional topo root
         #
-        # BN   y  additional branch root
-        # CN   y  additional topo head
-        # HN   n  usual case
+        # B N  y  additional branch root
+        # C N  y  additional topo head
+        # H N  n  usual case
         #
-        # BB   y  weird additional branch root
-        # CB   y  branch merge
-        # HB   n  merge with named branch
+        # B B  y  weird additional branch root
+        # C B  y  branch merge
+        # H B  n  merge with named branch
         #
-        # CC   y  additional head from merge
-        # CH   n  merge with a head
+        # C C  y  additional head from merge
+        # C H  n  merge with a head
         #
-        # HH   n  head merge: head count decreases
+        # H H  n  head merge: head count decreases
 
     if not opts.get('close_branch'):
         for r in parents:
@@ -1696,7 +1696,7 @@
     """format the changelog or an index DAG as a concise textual description
 
     If you pass a revlog index, the revlog's DAG is emitted. If you list
-    revision numbers, they get labelled in the output as rN.
+    revision numbers, they get labeled in the output as rN.
 
     Otherwise, the changelog DAG of the current repo is emitted.
     """
@@ -4181,7 +4181,7 @@
                     res.append(fn[plen:-slen])
         finally:
             lock.release()
-        for f in sorted(res):
+        for f in res:
             ui.write("%s\n" % f)
         return
 
@@ -4963,6 +4963,7 @@
                         ret = 1
                 finally:
                     ui.setconfig('ui', 'forcemerge', '')
+                    ms.commit()
 
                 # replace filemerge's .orig file with our resolve file
                 util.rename(a + ".resolve", a + ".orig")
--- a/mercurial/commandserver.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/commandserver.py	Tue Oct 02 14:31:25 2012 -0700
@@ -137,7 +137,7 @@
         if logpath:
             global logfile
             if logpath == '-':
-                # write log on a special 'd'ebug channel
+                # write log on a special 'd' (debug) channel
                 logfile = channeledoutput(sys.stdout, sys.stdout, 'd')
             else:
                 logfile = open(logpath, 'a')
--- a/mercurial/config.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/config.py	Tue Oct 02 14:31:25 2012 -0700
@@ -67,9 +67,9 @@
         return self._data.get(section, {}).get(item, default)
 
     def backup(self, section, item):
-        """return a tuple allowing restore to reinstall a previous valuesi
+        """return a tuple allowing restore to reinstall a previous value
 
-        The main reason we need it is because it handle the "no data" case.
+        The main reason we need it is because it handles the "no data" case.
         """
         try:
             value = self._data[section][item]
--- a/mercurial/context.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/context.py	Tue Oct 02 14:31:25 2012 -0700
@@ -11,6 +11,7 @@
 import copies
 import match as matchmod
 import os, errno, stat
+import obsolete as obsmod
 
 propertycache = util.propertycache
 
@@ -232,38 +233,15 @@
 
     def obsolete(self):
         """True if the changeset is obsolete"""
-        return (self.node() in self._repo.obsstore.precursors
-                and self.phase() > phases.public)
+        return self.rev() in obsmod.getobscache(self._repo, 'obsolete')
 
     def extinct(self):
         """True if the changeset is extinct"""
-        # We should just compute a cache a check againts it.
-        # see revset implementation for details
-        #
-        # But this naive implementation does not require cache
-        if self.phase() <= phases.public:
-            return False
-        if not self.obsolete():
-            return False
-        for desc in self.descendants():
-            if not desc.obsolete():
-                return False
-        return True
+        return self.rev() in obsmod.getobscache(self._repo, 'extinct')
 
     def unstable(self):
         """True if the changeset is not obsolete but it's ancestor are"""
-        # We should just compute /(obsolete()::) - obsolete()/
-        # and keep it in a cache.
-        #
-        # But this naive implementation does not require cache
-        if self.phase() <= phases.public:
-            return False
-        if self.obsolete():
-            return False
-        for anc in self.ancestors():
-            if anc.obsolete():
-                return True
-        return False
+        return self.rev() in obsmod.getobscache(self._repo, 'unstable')
 
     def _fileinfo(self, path):
         if '_manifest' in self.__dict__:
@@ -310,6 +288,10 @@
         n = self._repo.changelog.ancestor(self._node, n2)
         return changectx(self._repo, n)
 
+    def descendant(self, other):
+        """True if other is descendant of this changeset"""
+        return self._repo.changelog.descendant(self._rev, other._rev)
+
     def walk(self, match):
         fset = set(match.files())
         # for dirstate.walk, files=['.'] means "walk the whole tree".
@@ -885,8 +867,7 @@
         p = self._repo.dirstate.parents()
         if p[1] == nullid:
             p = p[:-1]
-        self._parents = [changectx(self._repo, x) for x in p]
-        return self._parents
+        return [changectx(self._repo, x) for x in p]
 
     def status(self, ignored=False, clean=False, unknown=False):
         """Explicit status query
@@ -1168,7 +1149,7 @@
 
         returns True if different than fctx.
         """
-        # fctx should be a filectx (not a wfctx)
+        # fctx should be a filectx (not a workingfilectx)
         # invert comparison to reuse the same code path
         return fctx.cmp(self)
 
--- a/mercurial/dagparser.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/dagparser.py	Tue Oct 02 14:31:25 2012 -0700
@@ -56,13 +56,13 @@
         ... :forkhere  # a label for the last of the 3 nodes from above
         ... +5         # 5 more nodes on one branch
         ... :mergethis # label again
-        ... <forkhere  # set default parent to labelled fork node
+        ... <forkhere  # set default parent to labeled fork node
         ... +10        # 10 more nodes on a parallel branch
         ... @stable    # following nodes will be annotated as "stable"
         ... +5         # 5 nodes in stable
         ... !addfile   # custom command; could trigger new file in next node
         ... +2         # two more nodes
-        ... /mergethis # merge last node with labelled node
+        ... /mergethis # merge last node with labeled node
         ... +4         # 4 more nodes descending from merge node
         ...
         ... """)))
--- a/mercurial/discovery.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/discovery.py	Tue Oct 02 14:31:25 2012 -0700
@@ -7,7 +7,7 @@
 
 from node import nullid, short
 from i18n import _
-import util, setdiscovery, treediscovery, phases, obsolete
+import util, setdiscovery, treediscovery, phases, obsolete, bookmarks
 
 def findcommonincoming(repo, remote, heads=None, force=False):
     """Return a tuple (common, anyincoming, heads) used to identify the common
@@ -21,7 +21,7 @@
       any longer.
     "heads" is either the supplied heads, or else the remote's heads.
 
-    If you pass heads and they are all known locally, the reponse lists justs
+    If you pass heads and they are all known locally, the response lists just
     these heads in "common" and in "heads".
 
     Please use findcommonoutgoing to compute the set of outgoing nodes to give
@@ -255,7 +255,7 @@
         rnode = remotebookmarks.get(bm)
         if rnode and rnode in repo:
             lctx, rctx = repo[bm], repo[rnode]
-            if rctx == lctx.ancestor(rctx):
+            if bookmarks.validdest(repo, rctx, lctx):
                 bookmarkedheads.add(lctx.node())
 
     # 3. Check for new heads.
@@ -264,24 +264,26 @@
     error = None
     unsynced = False
     allmissing = set(outgoing.missing)
+    allfuturecommon = set(c.node() for c in repo.set('%ld', outgoing.common))
+    allfuturecommon.update(allmissing)
     for branch, heads in headssum.iteritems():
         if heads[0] is None:
             # Maybe we should abort if we push more that one head
             # for new branches ?
             continue
-        if heads[2]:
-            unsynced = True
-        oldhs = set(heads[0])
         candidate_newhs = set(heads[1])
         # add unsynced data
+        oldhs = set(heads[0])
         oldhs.update(heads[2])
         candidate_newhs.update(heads[2])
         dhs = None
+        discardedheads = set()
         if repo.obsstore:
             # remove future heads which are actually obsolete by another
             # pushed element:
             #
-            # XXX There is several case this case does not handle properly
+            # XXX as above, There are several cases this case does not handle
+            # XXX properly
             #
             # (1) if <nh> is public, it won't be affected by obsolete marker
             #     and a new is created
@@ -293,13 +295,19 @@
             # more tricky for unsynced changes.
             newhs = set()
             for nh in candidate_newhs:
-                for suc in obsolete.anysuccessors(repo.obsstore, nh):
-                    if suc != nh and suc in allmissing:
-                        break
+                if nh in repo and repo[nh].phase() <= phases.public:
+                    newhs.add(nh)
                 else:
-                    newhs.add(nh)
+                    for suc in obsolete.anysuccessors(repo.obsstore, nh):
+                        if suc != nh and suc in allfuturecommon:
+                            discardedheads.add(nh)
+                            break
+                    else:
+                        newhs.add(nh)
         else:
             newhs = candidate_newhs
+        if [h for h in heads[2] if h not in discardedheads]:
+            unsynced = True
         if len(newhs) > len(oldhs):
             # strip updates to existing remote heads from the new heads list
             dhs = list(newhs - bookmarkedheads - oldhs)
@@ -348,7 +356,7 @@
 def visiblebranchmap(repo):
     """return a branchmap for the visible set"""
     # XXX Recomputing this data on the fly is very slow.  We should build a
-    # XXX cached version while computin the standard branchmap version.
+    # XXX cached version while computing the standard branchmap version.
     sroots = repo._phasecache.phaseroots[phases.secret]
     if sroots or repo.obsstore:
         vbranchmap = {}
--- a/mercurial/encoding.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/encoding.py	Tue Oct 02 14:31:25 2012 -0700
@@ -223,7 +223,7 @@
 
     Principles of operation:
 
-    - ASCII and UTF-8 data sucessfully round-trips and is understood
+    - ASCII and UTF-8 data successfully round-trips and is understood
       by Unicode-oriented clients
     - filenames and file contents in arbitrary other encodings can have
       be round-tripped or recovered by clueful clients
--- a/mercurial/formatter.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/formatter.py	Tue Oct 02 14:31:25 2012 -0700
@@ -26,6 +26,7 @@
         self._item = {}
     def data(self, **data):
         '''insert data into item that's not shown in default output'''
+        self._item.update(data)
     def write(self, fields, deftext, *fielddata, **opts):
         '''do default text output while assigning data to item'''
         for k, v in zip(fields.split(), fielddata):
--- a/mercurial/hbisect.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/hbisect.py	Tue Oct 02 14:31:25 2012 -0700
@@ -159,7 +159,7 @@
     Return a list of revision(s) that match the given status:
 
     - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
-    - ``goods``, ``bads``      : csets topologicaly good/bad
+    - ``goods``, ``bads``      : csets topologically good/bad
     - ``range``              : csets taking part in the bisection
     - ``pruned``             : csets that are goods, bads or skipped
     - ``untested``           : csets whose fate is yet unknown
@@ -170,8 +170,8 @@
     if status in ('good', 'bad', 'skip', 'current'):
         return map(repo.changelog.rev, state[status])
     else:
-        # In the floowing sets, we do *not* call 'bisect()' with more
-        # than one level of recusrsion, because that can be very, very
+        # In the following sets, we do *not* call 'bisect()' with more
+        # than one level of recursion, because that can be very, very
         # time consuming. Instead, we always develop the expression as
         # much as possible.
 
@@ -200,7 +200,7 @@
 
         # 'ignored' is all csets that were not used during the bisection
         # due to DAG topology, but may however have had an impact.
-        # Eg., a branch merged between bads and goods, but whose branch-
+        # E.g., a branch merged between bads and goods, but whose branch-
         # point is out-side of the range.
         iba = '::bisect(bad) - ::bisect(good)'  # Ignored bads' ancestors
         iga = '::bisect(good) - ::bisect(bad)'  # Ignored goods' ancestors
--- a/mercurial/help/config.txt	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/help/config.txt	Tue Oct 02 14:31:25 2012 -0700
@@ -604,9 +604,9 @@
 ``post-<command>``
   Run after successful invocations of the associated command. The
   contents of the command line are passed as ``$HG_ARGS`` and the result
-  code in ``$HG_RESULT``. Parsed command line arguments are passed as 
+  code in ``$HG_RESULT``. Parsed command line arguments are passed as
   ``$HG_PATS`` and ``$HG_OPTS``. These contain string representations of
-  the python data internally passed to <command>. ``$HG_OPTS`` is a 
+  the python data internally passed to <command>. ``$HG_OPTS`` is a
   dictionary of options (with unspecified options set to their defaults).
   ``$HG_PATS`` is a list of arguments. Hook failure is ignored.
 
@@ -616,7 +616,7 @@
   are passed as ``$HG_PATS`` and ``$HG_OPTS``. These contain string
   representations of the data internally passed to <command>. ``$HG_OPTS``
   is a  dictionary of options (with unspecified options set to their
-  defaults). ``$HG_PATS`` is a list of arguments. If the hook returns 
+  defaults). ``$HG_PATS`` is a list of arguments. If the hook returns
   failure, the command doesn't execute and Mercurial returns the failure
   code.
 
@@ -923,6 +923,17 @@
     Optional. Directory or URL to use when pushing if no destination
     is specified.
 
+Custom paths can be defined by assigning the path to a name that later can be
+used from the command line. Example::
+
+    [paths]
+    my_path = http://example.com/path
+
+To push to the path defined in ``my_path`` run the command::
+
+    hg push my_path
+
+
 ``phases``
 ----------
 
@@ -1431,7 +1442,7 @@
     Example: ``http://hgserver/static/``.
 
 ``stripes``
-    How many lines a "zebra stripe" should span in multiline output.
+    How many lines a "zebra stripe" should span in multi-line output.
     Default is 1; set to 0 to disable.
 
 ``style``
--- a/mercurial/help/hgweb.txt	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/help/hgweb.txt	Tue Oct 02 14:31:25 2012 -0700
@@ -11,7 +11,7 @@
   - paths
   - collections
 
-The ``web`` options are thorougly described in :hg:`help config`.
+The ``web`` options are thoroughly described in :hg:`help config`.
 
 The ``paths`` section maps URL paths to paths of repositories in the
 filesystem. hgweb will not expose the filesystem directly - only
--- a/mercurial/hg.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/hg.py	Tue Oct 02 14:31:25 2012 -0700
@@ -10,7 +10,7 @@
 from lock import release
 from node import hex, nullid
 import localrepo, bundlerepo, httppeer, sshpeer, statichttprepo, bookmarks
-import lock, util, extensions, error, node, scmutil
+import lock, util, extensions, error, node, scmutil, phases
 import cmdutil, discovery
 import merge as mergemod
 import verify as verifymod
@@ -303,7 +303,7 @@
 
         copy = False
         if (srcrepo and srcrepo.cancopy() and islocal(dest)
-            and not srcrepo.revs("secret()")):
+            and not phases.hassecret(srcrepo)):
             copy = not pull and not rev
 
         if copy:
--- a/mercurial/hgweb/common.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/hgweb/common.py	Tue Oct 02 14:31:25 2012 -0700
@@ -48,7 +48,7 @@
     # and replayed
     scheme = req.env.get('wsgi.url_scheme')
     if hgweb.configbool('web', 'push_ssl', True) and scheme != 'https':
-        raise ErrorResponse(HTTP_OK, 'ssl required')
+        raise ErrorResponse(HTTP_FORBIDDEN, 'ssl required')
 
     deny = hgweb.configlist('web', 'deny_push')
     if deny and (not user or deny == ['*'] or user in deny):
--- a/mercurial/hgweb/server.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/hgweb/server.py	Tue Oct 02 14:31:25 2012 -0700
@@ -12,7 +12,7 @@
 from mercurial.i18n import _
 
 def _splitURI(uri):
-    """ Return path and query splited from uri
+    """Return path and query that has been split from uri
 
     Just like CGI environment, the path is unquoted, the query is
     not.
--- a/mercurial/hgweb/wsgicgi.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/hgweb/wsgicgi.py	Tue Oct 02 14:31:25 2012 -0700
@@ -19,7 +19,7 @@
     environ = dict(os.environ.iteritems())
     environ.setdefault('PATH_INFO', '')
     if environ.get('SERVER_SOFTWARE', '').startswith('Microsoft-IIS'):
-        # IIS includes script_name in path_info
+        # IIS includes script_name in PATH_INFO
         scriptname = environ['SCRIPT_NAME']
         if environ['PATH_INFO'].startswith(scriptname):
             environ['PATH_INFO'] = environ['PATH_INFO'][len(scriptname):]
--- a/mercurial/hook.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/hook.py	Tue Oct 02 14:31:25 2012 -0700
@@ -154,7 +154,7 @@
                 oldstdout = os.dup(stdoutno)
                 os.dup2(stderrno, stdoutno)
         except AttributeError:
-            # __stdout/err__ doesn't have fileno(), it's not a real file
+            # __stdout__/__stderr__ doesn't have fileno(), it's not a real file
             pass
 
     try:
--- a/mercurial/httpclient/__init__.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/httpclient/__init__.py	Tue Oct 02 14:31:25 2012 -0700
@@ -170,7 +170,7 @@
         except socket.sslerror, e:
             if e.args[0] != socket.SSL_ERROR_WANT_READ:
                 raise
-            logger.debug('SSL_WANT_READ in _select, should retry later')
+            logger.debug('SSL_ERROR_WANT_READ in _select, should retry later')
             return True
         logger.debug('response read %d data during _select', len(data))
         # If the socket was readable and no data was read, that means
@@ -293,7 +293,7 @@
           host: The host to which we'll connect.
           port: Optional. The port over which we'll connect. Default 80 for
                 non-ssl, 443 for ssl.
-          use_ssl: Optional. Wether to use ssl. Defaults to False if port is
+          use_ssl: Optional. Whether to use ssl. Defaults to False if port is
                    not 443, true if port is 443.
           ssl_validator: a function(socket) to validate the ssl cert
           timeout: Optional. Connection timeout, default is TIMEOUT_DEFAULT.
@@ -374,7 +374,7 @@
         if self.ssl:
             # This is the default, but in the case of proxied SSL
             # requests the proxy logic above will have cleared
-            # blocking mode, so reenable it just to be safe.
+            # blocking mode, so re-enable it just to be safe.
             sock.setblocking(1)
             logger.debug('wrapping socket for ssl with options %r',
                          self.ssl_opts)
@@ -414,7 +414,7 @@
         """Close the connection to the server.
 
         This is a no-op if the connection is already closed. The
-        connection may automatically close if requessted by the server
+        connection may automatically close if requested by the server
         or required by the nature of a response.
         """
         if self.sock is None:
@@ -532,7 +532,7 @@
                         if e.args[0] != socket.SSL_ERROR_WANT_READ:
                             raise
                         logger.debug(
-                            'SSL_WANT_READ while sending data, retrying...')
+                            'SSL_ERROR_WANT_READ while sending data, retrying...')
                         continue
                     if not data:
                         logger.info('socket appears closed in read')
--- a/mercurial/httpclient/_readers.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/httpclient/_readers.py	Tue Oct 02 14:31:25 2012 -0700
@@ -120,7 +120,7 @@
         if data:
             assert not self._finished, (
                 'tried to add data (%r) to a closed reader!' % data)
-        logger.debug('%s read an addtional %d data', self.name, len(data))
+        logger.debug('%s read an additional %d data', self.name, len(data))
         self._done_chunks.append(data)
 
 
@@ -162,7 +162,7 @@
 
     def _load(self, data):
         assert not self._finished, 'tried to add data to a closed reader!'
-        logger.debug('chunked read an addtional %d data', len(data))
+        logger.debug('chunked read an additional %d data', len(data))
         position = 0
         if self._leftover_data:
             logger.debug('chunked reader trying to finish block from leftover data')
@@ -188,7 +188,7 @@
                 return
             if amt == 0:
                 self._finished = True
-                logger.debug('closing chunked redaer due to chunk of length 0')
+                logger.debug('closing chunked reader due to chunk of length 0')
                 return
             self._done_chunks.append(data[block_start:block_start + amt])
             position = block_start + amt + len(self._eol)
--- a/mercurial/httpconnection.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/httpconnection.py	Tue Oct 02 14:31:25 2012 -0700
@@ -73,7 +73,7 @@
     if '://' in uri:
         scheme, hostpath = uri.split('://', 1)
     else:
-        # py2.4.1 doesn't provide the full URI
+        # Python 2.4.1 doesn't provide the full URI
         scheme, hostpath = 'http', uri
     bestuser = None
     bestlen = 0
--- a/mercurial/keepalive.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/keepalive.py	Tue Oct 02 14:31:25 2012 -0700
@@ -67,8 +67,8 @@
 
     close_connection()  -  close the connection to the host
     readlines()         -  you know, readlines()
-    status              -  the return status (ie 404)
-    reason              -  english translation of status (ie 'File not found')
+    status              -  the return status (i.e. 404)
+    reason              -  english translation of status (i.e. 'File not found')
 
   If you want the best of both worlds, use this inside an
   AttributeError-catching try:
@@ -297,7 +297,7 @@
             # first.  We previously got into a nasty loop
             # where an exception was uncaught, and so the
             # connection stayed open.  On the next try, the
-            # same exception was raised, etc.  The tradeoff is
+            # same exception was raised, etc.  The trade-off is
             # that it's now possible this call will raise
             # a DIFFERENT exception
             if DEBUG:
@@ -370,17 +370,14 @@
     # so if you THEN do a normal read, you must first take stuff from
     # the buffer.
 
-    # the read method wraps the original to accomodate buffering,
+    # the read method wraps the original to accommodate buffering,
     # although read() never adds to the buffer.
     # Both readline and readlines have been stolen with almost no
     # modification from socket.py
 
 
     def __init__(self, sock, debuglevel=0, strict=0, method=None):
-        if method: # the httplib in python 2.3 uses the method arg
-            httplib.HTTPResponse.__init__(self, sock, debuglevel, method)
-        else: # 2.2 doesn't
-            httplib.HTTPResponse.__init__(self, sock, debuglevel)
+        httplib.HTTPResponse.__init__(self, sock, debuglevel, method)
         self.fileno = sock.fileno
         self.code = None
         self._rbuf = ''
@@ -442,7 +439,7 @@
                 try:
                     chunk_left = int(line, 16)
                 except ValueError:
-                    # close the connection as protocol synchronisation is
+                    # close the connection as protocol synchronization is
                     # probably lost
                     self.close()
                     raise httplib.IncompleteRead(value)
@@ -548,7 +545,7 @@
         read = getattr(str, 'read', None)
         if read is not None:
             if self.debuglevel > 0:
-                print "sendIng a read()able"
+                print "sending a read()able"
             data = read(blocksize)
             while data:
                 self.sock.sendall(data)
@@ -737,7 +734,7 @@
 
 
 def test(url, N=10):
-    print "checking error hander (do this on a non-200)"
+    print "checking error handler (do this on a non-200)"
     try: error_handler(url)
     except IOError:
         print "exiting - exception will prevent further tests"
--- a/mercurial/localrepo.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/localrepo.py	Tue Oct 02 14:31:25 2012 -0700
@@ -117,14 +117,14 @@
         return self.requirements[:]
 
     def __init__(self, baseui, path=None, create=False):
-        self.wopener = scmutil.opener(path, expand=True)
-        self.wvfs = self.wopener
+        self.wvfs = scmutil.vfs(path, expand=True)
+        self.wopener = self.wvfs
         self.root = self.wvfs.base
         self.path = self.wvfs.join(".hg")
         self.origroot = path
         self.auditor = scmutil.pathauditor(self.root, self._checknested)
-        self.opener = scmutil.opener(self.path)
-        self.vfs = self.opener
+        self.vfs = scmutil.vfs(self.path)
+        self.opener = self.vfs
         self.baseui = baseui
         self.ui = baseui.copy()
         # A list of callback to shape the phase if no data were found.
@@ -182,12 +182,12 @@
             if inst.errno != errno.ENOENT:
                 raise
 
-        self.store = store.store(requirements, self.sharedpath, scmutil.opener)
+        self.store = store.store(requirements, self.sharedpath, scmutil.vfs)
         self.spath = self.store.path
-        self.sopener = self.store.opener
-        self.svfs = self.sopener
+        self.svfs = self.store.vfs
+        self.sopener = self.svfs
         self.sjoin = self.store.join
-        self.opener.createmode = self.store.createmode
+        self.vfs.createmode = self.store.createmode
         self._applyrequirements(requirements)
         if create:
             self._writerequirements()
@@ -299,13 +299,15 @@
     def hiddenrevs(self):
         """hiddenrevs: revs that should be hidden by command and tools
 
-        This set is carried on the repo to ease initialisation and lazy
-        loading it'll probably move back to changelog for efficienty and
-        consistency reason
+        This set is carried on the repo to ease initialization and lazy
+        loading; it'll probably move back to changelog for efficiency and
+        consistency reasons.
 
         Note that the hiddenrevs will needs invalidations when
         - a new changesets is added (possible unstable above extinct)
         - a new obsolete marker is added (possible new extinct changeset)
+
+        hidden changesets cannot have non-hidden descendants
         """
         hidden = set()
         if self.obsstore:
@@ -361,14 +363,13 @@
         return len(self.changelog)
 
     def __iter__(self):
-        for i in xrange(len(self)):
-            yield i
+        return iter(self.changelog)
 
     def revs(self, expr, *args):
         '''Return a list of revisions matching the given revset'''
         expr = revset.formatspec(expr, *args)
         m = revset.match(None, expr)
-        return [r for r in m(self, range(len(self)))]
+        return [r for r in m(self, list(self))]
 
     def set(self, expr, *args):
         '''
@@ -601,7 +602,7 @@
         # TODO: rename this function?
         tiprev = len(self) - 1
         if lrev != tiprev:
-            ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
+            ctxgen = (self[r] for r in self.changelog.revs(lrev + 1, tiprev))
             self._updatebranchcache(partial, ctxgen)
             self._writebranchcache(partial, self.changelog.tip(), tiprev)
 
@@ -712,7 +713,7 @@
             # Remove candidate heads that no longer are in the repo (e.g., as
             # the result of a strip that just happened).  Avoid using 'node in
             # self' here because that dives down into branchcache code somewhat
-            # recrusively.
+            # recursively.
             bheadrevs = [self.changelog.rev(node) for node in bheads
                          if self.changelog.hasnode(node)]
             newheadrevs = [self.changelog.rev(node) for node in newnodes
@@ -732,7 +733,7 @@
                 iterrevs = list(bheadrevs)
 
             # This loop prunes out two kinds of heads - heads that are
-            # superceded by a head in newheadrevs, and newheadrevs that are not
+            # superseded by a head in newheadrevs, and newheadrevs that are not
             # heads because an existing head is their descendant.
             while iterrevs:
                 latest = iterrevs.pop()
@@ -1040,6 +1041,7 @@
 
         self._branchcache = None # in UTF-8
         self._branchcachetip = None
+        obsolete.clearobscaches(self)
 
     def invalidatedirstate(self):
         '''Invalidates the dirstate, causing the next call to dirstate
@@ -1479,7 +1481,7 @@
         and you also know the set of candidate new heads that may have resulted
         from the destruction, you can set newheadnodes.  This will enable the
         code to update the branchheads cache, rather than having future code
-        decide it's invalid and regenrating it from scratch.
+        decide it's invalid and regenerating it from scratch.
         '''
         # If we have info, newheadnodes, on how to update the branch cache, do
         # it, Otherwise, since nodes were destroyed, the cache is stale and this
@@ -1906,7 +1908,7 @@
                         ret = remote.addchangegroup(cg, 'push', self.url())
 
                 if ret:
-                    # push succeed, synchonize target of the push
+                    # push succeed, synchronize target of the push
                     cheads = outgoing.missingheads
                 elif revs is None:
                     # All out push fails. synchronize all common
@@ -1925,7 +1927,7 @@
                     #     missing = ((commonheads::missingheads) - commonheads)
                     #
                     # We can pick:
-                    # * missingheads part of comon (::commonheads)
+                    # * missingheads part of common (::commonheads)
                     common = set(outgoing.common)
                     cheads = [node for node in revs if node in common]
                     # and
@@ -1989,7 +1991,7 @@
                 if nr in self:
                     cr = self[nr]
                     cl = self[nl]
-                    if cl in cr.descendants():
+                    if bookmarks.validdest(self, cr, cl):
                         r = remote.pushkey('bookmarks', k, nr, nl)
                         if r:
                             self.ui.status(_("updating bookmark %s\n") % k)
@@ -2402,6 +2404,7 @@
             self.ui.status(_("added %d changesets"
                              " with %d changes to %d files%s\n")
                              % (changesets, revisions, files, htext))
+            obsolete.clearobscaches(self)
 
             if changesets > 0:
                 p = lambda: cl.writepending() and self.root or ""
@@ -2435,10 +2438,10 @@
             tr.close()
 
             if changesets > 0:
+                self.updatebranchcache()
                 def runhooks():
                     # forcefully update the on-disk branch cache
                     self.ui.debug("updating the branch cache\n")
-                    self.updatebranchcache()
                     self.hook("changegroup", node=hex(cl.node(clstart)),
                               source=srctype, url=url)
 
@@ -2539,7 +2542,7 @@
         # uncompressed only if compatible.
 
         if not stream:
-            # if the server explicitely prefer to stream (for fast LANs)
+            # if the server explicitly prefers to stream (for fast LANs)
             stream = remote.capable('stream-preferred')
 
         if stream and not heads:
--- a/mercurial/lock.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/lock.py	Tue Oct 02 14:31:25 2012 -0700
@@ -127,8 +127,8 @@
     def release(self):
         """release the lock and execute callback function if any
 
-        If the lock have been aquired multiple time, the actual release is
-        delayed to the last relase call."""
+        If the lock has been acquired multiple times, the actual release is
+        delayed to the last release call."""
         if self.held > 1:
             self.held -= 1
         elif self.held == 1:
--- a/mercurial/mail.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/mail.py	Tue Oct 02 14:31:25 2012 -0700
@@ -13,7 +13,7 @@
 _oldheaderinit = email.Header.Header.__init__
 def _unifiedheaderinit(self, *args, **kw):
     """
-    Python2.7 introduces a backwards incompatible change
+    Python 2.7 introduces a backwards incompatible change
     (Python issue1974, r70772) in email.Generator.Generator code:
     pre-2.7 code passed "continuation_ws='\t'" to the Header
     constructor, and 2.7 removed this parameter.
@@ -151,7 +151,7 @@
 
 def mimetextqp(body, subtype, charset):
     '''Return MIME message.
-    Qouted-printable transfer encoding will be used if necessary.
+    Quoted-printable transfer encoding will be used if necessary.
     '''
     enc = None
     for line in body.splitlines():
--- a/mercurial/manifest.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/manifest.py	Tue Oct 02 14:31:25 2012 -0700
@@ -64,9 +64,7 @@
 
         If the string is found m[start:end] are the line containing
         that string.  If start == end the string was not found and
-        they indicate the proper sorted insertion point.  This was
-        taken from bisect_left, and modified to find line start/end as
-        it goes along.
+        they indicate the proper sorted insertion point.
 
         m should be a buffer or a string
         s is a string'''
@@ -156,7 +154,7 @@
             # combine the changed lists into one list for sorting
             work = [(x, False) for x in added]
             work.extend((x, True) for x in removed)
-            # this could use heapq.merge() (from python2.6+) or equivalent
+            # this could use heapq.merge() (from Python 2.6+) or equivalent
             # since the lists are already sorted
             work.sort()
 
--- a/mercurial/match.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/match.py	Tue Oct 02 14:31:25 2012 -0700
@@ -49,7 +49,7 @@
         a pattern is one of:
         'glob:<glob>' - a glob relative to cwd
         're:<regexp>' - a regular expression
-        'path:<path>' - a path relative to canonroot
+        'path:<path>' - a path relative to repository root
         'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
         'relpath:<path>' - a path relative to cwd
         'relre:<regexp>' - a regexp that needn't match the start of a name
--- a/mercurial/minirst.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/minirst.py	Tue Oct 02 14:31:25 2012 -0700
@@ -133,7 +133,7 @@
     def match(lines, i, itemre, singleline):
         """Does itemre match an item at line i?
 
-        A list item can be followed by an idented line or another list
+        A list item can be followed by an indented line or another list
         item (but only if singleline is True).
         """
         line1 = lines[i]
--- a/mercurial/obsolete.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/obsolete.py	Tue Oct 02 14:31:25 2012 -0700
@@ -55,14 +55,12 @@
 import util, base85
 from i18n import _
 
-# the obsolete feature is not mature enought to be enabled by default.
-# you have to rely on third party extension extension to enable this.
-_enabled = False
-
 _pack = struct.pack
 _unpack = struct.unpack
 
-# the obsolete feature is not mature enought to be enabled by default.
+_SEEK_END = 2 # os.SEEK_END was introduced in Python 2.5
+
+# the obsolete feature is not mature enough to be enabled by default.
 # you have to rely on third party extension extension to enable this.
 _enabled = False
 
@@ -163,6 +161,8 @@
     """
 
     def __init__(self, sopener):
+        # caches for various obsolescence related cache
+        self.caches = {}
         self._all = []
         # new markers to serialize
         self.precursors = {}
@@ -211,7 +211,7 @@
                 # defined. So we must seek to the end before calling tell(),
                 # or we may get a zero offset for non-zero sized files on
                 # some platforms (issue3543).
-                f.seek(0, 2) # os.SEEK_END
+                f.seek(0, _SEEK_END)
                 offset = f.tell()
                 transaction.add('obsstore', offset)
                 # offset == 0: new file - add the version header
@@ -222,11 +222,13 @@
                 # call 'filecacheentry.refresh()'  here
                 f.close()
             self._load(new)
+            # new marker *may* have changed several set. invalidate the cache.
+            self.caches.clear()
         return len(new)
 
-    def mergemarkers(self, transation, data):
+    def mergemarkers(self, transaction, data):
         markers = _readmarkers(data)
-        self.add(transation, markers)
+        self.add(transaction, markers)
 
     def _load(self, markers):
         for mark in markers:
@@ -318,7 +320,7 @@
 def anysuccessors(obsstore, node):
     """Yield every successor of <node>
 
-    This this a linear yield unsuitable to detect splitted changeset."""
+    This is a linear yield unsuited to detecting split changesets."""
     remaining = set([node])
     seen = set(remaining)
     while remaining:
@@ -329,3 +331,103 @@
                 if suc not in seen:
                     seen.add(suc)
                     remaining.add(suc)
+
+# mapping of 'set-name' -> <function to computer this set>
+cachefuncs = {}
+def cachefor(name):
+    """Decorator to register a function as computing the cache for a set"""
+    def decorator(func):
+        assert name not in cachefuncs
+        cachefuncs[name] = func
+        return func
+    return decorator
+
+def getobscache(repo, name):
+    """Return the set of revision that belong to the <name> set
+
+    Such access may compute the set and cache it for future use"""
+    if not repo.obsstore:
+        return ()
+    if name not in repo.obsstore.caches:
+        repo.obsstore.caches[name] = cachefuncs[name](repo)
+    return repo.obsstore.caches[name]
+
+# To be simple we need to invalidate obsolescence cache when:
+#
+# - new changeset is added:
+# - public phase is changed
+# - obsolescence marker are added
+# - strip is used a repo
+def clearobscaches(repo):
+    """Remove all obsolescence related cache from a repo
+
+    This remove all cache in obsstore is the obsstore already exist on the
+    repo.
+
+    (We could be smarter here given the exact event that trigger the cache
+    clearing)"""
+    # only clear cache is there is obsstore data in this repo
+    if 'obsstore' in repo._filecache:
+        repo.obsstore.caches.clear()
+
+@cachefor('obsolete')
+def _computeobsoleteset(repo):
+    """the set of obsolete revisions"""
+    obs = set()
+    nm = repo.changelog.nodemap
+    for prec in repo.obsstore.precursors:
+        rev = nm.get(prec)
+        if rev is not None:
+            obs.add(rev)
+    return set(repo.revs('%ld - public()', obs))
+
+@cachefor('unstable')
+def _computeunstableset(repo):
+    """the set of non obsolete revisions with obsolete parents"""
+    return set(repo.revs('(obsolete()::) - obsolete()'))
+
+@cachefor('suspended')
+def _computesuspendedset(repo):
+    """the set of obsolete parents with non obsolete descendants"""
+    return set(repo.revs('obsolete() and obsolete()::unstable()'))
+
+@cachefor('extinct')
+def _computeextinctset(repo):
+    """the set of obsolete parents without non obsolete descendants"""
+    return set(repo.revs('obsolete() - obsolete()::unstable()'))
+
+def createmarkers(repo, relations, flag=0, metadata=None):
+    """Add obsolete markers between changesets in a repo
+
+    <relations> must be an iterable of (<old>, (<new>, ...)) tuple.
+    `old` and `news` are changectx.
+
+    Trying to obsolete a public changeset will raise an exception.
+
+    Current user and date are used except if specified otherwise in the
+    metadata attribute.
+
+    This function operates within a transaction of its own, but does
+    not take any lock on the repo.
+    """
+    # prepare metadata
+    if metadata is None:
+        metadata = {}
+    if 'date' not in metadata:
+        metadata['date'] = '%i %i' % util.makedate()
+    if 'user' not in metadata:
+        metadata['user'] = repo.ui.username()
+    tr = repo.transaction('add-obsolescence-marker')
+    try:
+        for prec, sucs in relations:
+            if not prec.mutable():
+                raise util.Abort("cannot obsolete immutable changeset: %s"
+                                 % prec)
+            nprec = prec.node()
+            nsucs = tuple(s.node() for s in sucs)
+            if nprec in nsucs:
+                raise util.Abort("changeset %s cannot obsolete itself" % prec)
+            repo.obsstore.create(tr, nprec, nsucs, flag, metadata)
+        tr.close()
+    finally:
+        tr.release()
--- a/mercurial/parser.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/parser.py	Tue Oct 02 14:31:25 2012 -0700
@@ -13,7 +13,7 @@
 # tokenizer is an iterator that returns type, value pairs
 # elements is a mapping of types to binding strength, prefix and infix actions
 # an action is a tree node name, a tree label, and an optional match
-# __call__(program) parses program into a labelled tree
+# __call__(program) parses program into a labeled tree
 
 import error
 from i18n import _
--- a/mercurial/parsers.c	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/parsers.c	Tue Oct 02 14:31:25 2012 -0700
@@ -9,6 +9,7 @@
 
 #include <Python.h>
 #include <ctype.h>
+#include <stddef.h>
 #include <string.h>
 
 #include "util.h"
@@ -72,7 +73,7 @@
 	for (start = cur = str, zero = NULL; cur < str + len; cur++) {
 		PyObject *file = NULL, *node = NULL;
 		PyObject *flags = NULL;
-		int nlen;
+		ptrdiff_t nlen;
 
 		if (!*cur) {
 			zero = cur;
@@ -94,7 +95,7 @@
 
 		nlen = cur - zero - 1;
 
-		node = unhexlify(zero + 1, nlen > 40 ? 40 : nlen);
+		node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
 		if (!node)
 			goto bail;
 
@@ -1505,11 +1506,16 @@
 
 static char parsers_doc[] = "Efficient content parsing.";
 
+PyObject *encodedir(PyObject *self, PyObject *args);
+PyObject *pathencode(PyObject *self, PyObject *args);
+
 static PyMethodDef methods[] = {
 	{"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
 	{"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
 	{"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
 	{"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
+	{"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
+	{"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
 	{NULL, NULL}
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/pathencode.c	Tue Oct 02 14:31:25 2012 -0700
@@ -0,0 +1,531 @@
+/*
+ pathencode.c - efficient path name encoding
+
+ Copyright 2012 Facebook
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License, incorporated herein by reference.
+*/
+
+/*
+ * An implementation of the name encoding scheme used by the fncache
+ * store.  The common case is of a path < 120 bytes long, which is
+ * handled either in a single pass with no allocations or two passes
+ * with a single allocation.  For longer paths, multiple passes are
+ * required.
+ */
+
+#include <Python.h>
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+/* state machine for the fast path */
+enum path_state {
+	START,   /* first byte of a path component */
+	A,       /* "AUX" */
+	AU,
+	THIRD,   /* third of a 3-byte sequence, e.g. "AUX", "NUL" */
+	C,       /* "CON" or "COMn" */
+	CO,
+	COMLPT,  /* "COM" or "LPT" */
+	COMLPTn,
+	L,
+	LP,
+	N,
+	NU,
+	P,       /* "PRN" */
+	PR,
+	LDOT,    /* leading '.' */
+	DOT,     /* '.' in a non-leading position */
+	H,       /* ".h" */
+	HGDI,    /* ".hg", ".d", or ".i" */
+	SPACE,
+	DEFAULT, /* byte of a path component after the first */
+};
+
+/* state machine for dir-encoding */
+enum dir_state {
+	DDOT,
+	DH,
+	DHGDI,
+	DDEFAULT,
+};
+
+static inline int inset(const uint32_t bitset[], char c)
+{
+	return bitset[((uint8_t)c) >> 5] & (1 << (((uint8_t)c) & 31));
+}
+
+static inline void charcopy(char *dest, Py_ssize_t *destlen, size_t destsize,
+                            char c)
+{
+	if (dest) {
+		assert(*destlen < destsize);
+		dest[*destlen] = c;
+	}
+	(*destlen)++;
+}
+
+static inline void memcopy(char *dest, Py_ssize_t *destlen, size_t destsize,
+                           const void *src, Py_ssize_t len)
+{
+	if (dest) {
+		assert(*destlen + len < destsize);
+		memcpy((void *)&dest[*destlen], src, len);
+	}
+	*destlen += len;
+}
+
+static inline void hexencode(char *dest, Py_ssize_t *destlen, size_t destsize,
+			     uint8_t c)
+{
+	static const char hexdigit[] = "0123456789abcdef";
+
+	charcopy(dest, destlen, destsize, hexdigit[c >> 4]);
+	charcopy(dest, destlen, destsize, hexdigit[c & 15]);
+}
+
+/* 3-byte escape: tilde followed by two hex digits */
+static inline void escape3(char *dest, Py_ssize_t *destlen, size_t destsize,
+			   char c)
+{
+	charcopy(dest, destlen, destsize, '~');
+	hexencode(dest, destlen, destsize, c);
+}
+
+static Py_ssize_t _encodedir(char *dest, size_t destsize,
+                             const char *src, Py_ssize_t len)
+{
+	enum dir_state state = DDEFAULT;
+	Py_ssize_t i = 0, destlen = 0;
+
+	while (i < len) {
+		switch (state) {
+		case DDOT:
+			switch (src[i]) {
+			case 'd':
+			case 'i':
+				state = DHGDI;
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			case 'h':
+				state = DH;
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			default:
+				state = DDEFAULT;
+				break;
+			}
+			break;
+		case DH:
+			if (src[i] == 'g') {
+				state = DHGDI;
+				charcopy(dest, &destlen, destsize, src[i++]);
+			}
+			else state = DDEFAULT;
+			break;
+		case DHGDI:
+			if (src[i] == '/') {
+				memcopy(dest, &destlen, destsize, ".hg", 3);
+				charcopy(dest, &destlen, destsize, src[i++]);
+			}
+			state = DDEFAULT;
+			break;
+		case DDEFAULT:
+			if (src[i] == '.')
+				state = DDOT;
+			charcopy(dest, &destlen, destsize, src[i++]);
+			break;
+		}
+	}
+
+	return destlen;
+}
+
+PyObject *encodedir(PyObject *self, PyObject *args)
+{
+	Py_ssize_t len, newlen;
+	PyObject *pathobj, *newobj;
+	char *path;
+
+	if (!PyArg_ParseTuple(args, "O:encodedir", &pathobj))
+		return NULL;
+
+	if (PyString_AsStringAndSize(pathobj, &path, &len) == -1) {
+		PyErr_SetString(PyExc_TypeError, "expected a string");
+		return NULL;
+	}
+
+	newlen = len ? _encodedir(NULL, 0, path, len + 1) : 1;
+
+	if (newlen == len + 1) {
+		Py_INCREF(pathobj);
+		return pathobj;
+	}
+
+	newobj = PyString_FromStringAndSize(NULL, newlen);
+
+	if (newobj) {
+		PyString_GET_SIZE(newobj)--;
+		_encodedir(PyString_AS_STRING(newobj), newlen, path,
+			   len + 1);
+	}
+
+	return newobj;
+}
+
+static Py_ssize_t _encode(const uint32_t twobytes[8], const uint32_t onebyte[8],
+			  char *dest, Py_ssize_t destlen, size_t destsize,
+			  const char *src, Py_ssize_t len,
+			  int encodedir)
+{
+	enum path_state state = START;
+	Py_ssize_t i = 0;
+
+	/*
+	 * Python strings end with a zero byte, which we use as a
+	 * terminal token as they are not valid inside path names.
+	 */
+
+	while (i < len) {
+		switch (state) {
+		case START:
+			switch (src[i]) {
+			case '/':
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			case '.':
+				state = LDOT;
+				escape3(dest, &destlen, destsize, src[i++]);
+				break;
+			case ' ':
+				state = DEFAULT;
+				escape3(dest, &destlen, destsize, src[i++]);
+				break;
+			case 'a':
+				state = A;
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			case 'c':
+				state = C;
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			case 'l':
+				state = L;
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			case 'n':
+				state = N;
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			case 'p':
+				state = P;
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			default:
+				state = DEFAULT;
+				break;
+			}
+			break;
+		case A:
+			if (src[i] == 'u') {
+				state = AU;
+				charcopy(dest, &destlen, destsize, src[i++]);
+			}
+			else state = DEFAULT;
+			break;
+		case AU:
+			if (src[i] == 'x') {
+				state = THIRD;
+				i++;
+			}
+			else state = DEFAULT;
+			break;
+		case THIRD:
+			state = DEFAULT;
+			switch (src[i]) {
+			case '.':
+			case '/':
+			case '\0':
+				escape3(dest, &destlen, destsize, src[i - 1]);
+				break;
+			default:
+				i--;
+				break;
+			}
+			break;
+		case C:
+			if (src[i] == 'o') {
+				state = CO;
+				charcopy(dest, &destlen, destsize, src[i++]);
+			}
+			else state = DEFAULT;
+			break;
+		case CO:
+			if (src[i] == 'm') {
+				state = COMLPT;
+				i++;
+			}
+			else if (src[i] == 'n') {
+				state = THIRD;
+				i++;
+			}
+			else state = DEFAULT;
+			break;
+		case COMLPT:
+			switch (src[i]) {
+			case '1': case '2': case '3': case '4': case '5':
+			case '6': case '7': case '8': case '9':
+				state = COMLPTn;
+				i++;
+				break;
+			default:
+				state = DEFAULT;
+				charcopy(dest, &destlen, destsize, src[i - 1]);
+				break;
+			}
+			break;
+		case COMLPTn:
+			state = DEFAULT;
+			switch (src[i]) {
+			case '.':
+			case '/':
+			case '\0':
+				escape3(dest, &destlen, destsize, src[i - 2]);
+				charcopy(dest, &destlen, destsize, src[i - 1]);
+				break;
+			default:
+				memcopy(dest, &destlen, destsize,
+					&src[i - 2], 2);
+				break;
+			}
+			break;
+		case L:
+			if (src[i] == 'p') {
+				state = LP;
+				charcopy(dest, &destlen, destsize, src[i++]);
+			}
+			else state = DEFAULT;
+			break;
+		case LP:
+			if (src[i] == 't') {
+				state = COMLPT;
+				i++;
+			}
+			else state = DEFAULT;
+			break;
+		case N:
+			if (src[i] == 'u') {
+				state = NU;
+				charcopy(dest, &destlen, destsize, src[i++]);
+			}
+			else state = DEFAULT;
+			break;
+		case NU:
+			if (src[i] == 'l') {
+				state = THIRD;
+				i++;
+			}
+			else state = DEFAULT;
+			break;
+		case P:
+			if (src[i] == 'r') {
+				state = PR;
+				charcopy(dest, &destlen, destsize, src[i++]);
+			}
+			else state = DEFAULT;
+			break;
+		case PR:
+			if (src[i] == 'n') {
+				state = THIRD;
+				i++;
+			}
+			else state = DEFAULT;
+			break;
+		case LDOT:
+			switch (src[i]) {
+			case 'd':
+			case 'i':
+				state = HGDI;
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			case 'h':
+				state = H;
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			default:
+				state = DEFAULT;
+				break;
+			}
+			break;
+		case DOT:
+			switch (src[i]) {
+			case '/':
+			case '\0':
+				state = START;
+				memcopy(dest, &destlen, destsize, "~2e", 3);
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			case 'd':
+			case 'i':
+				state = HGDI;
+				charcopy(dest, &destlen, destsize, '.');
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			case 'h':
+				state = H;
+				memcopy(dest, &destlen, destsize, ".h", 2);
+				i++;
+				break;
+			default:
+				state = DEFAULT;
+				charcopy(dest, &destlen, destsize, '.');
+				break;
+			}
+			break;
+		case H:
+			if (src[i] == 'g') {
+				state = HGDI;
+				charcopy(dest, &destlen, destsize, src[i++]);
+			}
+			else state = DEFAULT;
+			break;
+		case HGDI:
+			if (src[i] == '/') {
+				state = START;
+				if (encodedir)
+					memcopy(dest, &destlen, destsize, ".hg",
+						3);
+				charcopy(dest, &destlen, destsize, src[i++]);
+			}
+			else state = DEFAULT;
+			break;
+		case SPACE:
+			switch (src[i]) {
+			case '/':
+			case '\0':
+				state = START;
+				memcopy(dest, &destlen, destsize, "~20", 3);
+				charcopy(dest, &destlen, destsize, src[i++]);
+				break;
+			default:
+				state = DEFAULT;
+				charcopy(dest, &destlen, destsize, ' ');
+				break;
+			}
+			break;
+		case DEFAULT:
+			while (inset(onebyte, src[i])) {
+				charcopy(dest, &destlen, destsize, src[i++]);
+				if (i == len)
+					goto done;
+			}
+			switch (src[i]) {
+			case '.':
+				state = DOT;
+				i++;
+				break;
+			case ' ':
+				state = SPACE;
+				i++;
+				break;
+			case '/':
+				state = START;
+				charcopy(dest, &destlen, destsize, '/');
+				i++;
+				break;
+			default:
+				if (inset(onebyte, src[i])) {
+					do {
+						charcopy(dest, &destlen,
+							 destsize, src[i++]);
+					} while (i < len &&
+						 inset(onebyte, src[i]));
+				}
+				else if (inset(twobytes, src[i])) {
+					char c = src[i++];
+					charcopy(dest, &destlen, destsize, '_');
+					charcopy(dest, &destlen, destsize,
+						 c == '_' ? '_' : c + 32);
+				}
+				else
+					escape3(dest, &destlen, destsize,
+						src[i++]);
+				break;
+			}
+			break;
+		}
+	}
+done:
+	return destlen;
+}
+
+static Py_ssize_t basicencode(char *dest, size_t destsize,
+			      const char *src, Py_ssize_t len)
+{
+	static const uint32_t twobytes[8] = { 0, 0, 0x87fffffe };
+
+	static const uint32_t onebyte[8] = {
+		1, 0x2bff3bfa, 0x68000001, 0x2fffffff,
+	};
+
+	Py_ssize_t destlen = 0;
+
+	return _encode(twobytes, onebyte, dest, destlen, destsize,
+		       src, len, 1);
+}
+
+static const Py_ssize_t maxstorepathlen = 120;
+
+/*
+ * We currently implement only basic encoding.
+ *
+ * If a name is too long to encode due to Windows path name limits,
+ * this function returns None.
+ */
+PyObject *pathencode(PyObject *self, PyObject *args)
+{
+	Py_ssize_t len, newlen;
+	PyObject *pathobj, *newobj;
+	char *path;
+
+	if (!PyArg_ParseTuple(args, "O:pathencode", &pathobj))
+		return NULL;
+
+	if (PyString_AsStringAndSize(pathobj, &path, &len) == -1) {
+		PyErr_SetString(PyExc_TypeError, "expected a string");
+		return NULL;
+	}
+
+	if (len > maxstorepathlen) {
+		newobj = Py_None;
+		Py_INCREF(newobj);
+		return newobj;
+	}
+
+	newlen = len ? basicencode(NULL, 0, path, len + 1) : 1;
+
+	if (newlen <= maxstorepathlen + 1) {
+		if (newlen == len + 1) {
+			Py_INCREF(pathobj);
+			return pathobj;
+		}
+
+		newobj = PyString_FromStringAndSize(NULL, newlen);
+
+		if (newobj) {
+			PyString_GET_SIZE(newobj)--;
+			basicencode(PyString_AS_STRING(newobj), newlen, path,
+				    len + 1);
+		}
+	} else {
+		newobj = Py_None;
+		Py_INCREF(newobj);
+	}
+
+	return newobj;
+}
--- a/mercurial/phases.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/phases.py	Tue Oct 02 14:31:25 2012 -0700
@@ -104,6 +104,7 @@
 from node import nullid, nullrev, bin, hex, short
 from i18n import _
 import util
+import obsolete
 
 allphases = public, draft, secret = range(3)
 trackedphases = allphases[1:]
@@ -195,7 +196,7 @@
         return self._phaserevs
 
     def phase(self, repo, rev):
-        # We need a repo argument here to be able to build _phaserev
+        # We need a repo argument here to be able to build _phaserevs
         # if necessary. The repository instance is not stored in
         # phasecache to avoid reference cycles. The changelog instance
         # is not stored because it is a filecache() property and can
@@ -244,6 +245,7 @@
             # declare deleted root in the target phase
             if targetphase != 0:
                 self.retractboundary(repo, targetphase, delroots)
+        obsolete.clearobscaches(repo)
 
     def retractboundary(self, repo, targetphase, nodes):
         # Be careful to preserve shallow-copied values: do not update
@@ -260,6 +262,7 @@
             ctxs = repo.set('roots(%ln::)', currentroots)
             currentroots.intersection_update(ctx.node() for ctx in ctxs)
             self._updateroots(targetphase, currentroots)
+        obsolete.clearobscaches(repo)
 
 def advanceboundary(repo, targetphase, nodes):
     """Add nodes to a phase changing other nodes phases if necessary.
@@ -312,7 +315,7 @@
     return keys
 
 def pushphase(repo, nhex, oldphasestr, newphasestr):
-    """List phases root for serialisation over pushkey"""
+    """List phases root for serialization over pushkey"""
     lock = repo.lock()
     try:
         currentphase = repo[nhex].phase()
@@ -363,7 +366,7 @@
     """compute new head of a subset minus another
 
     * `heads`: define the first subset
-    * `rroots`: define the second we substract to the first"""
+    * `roots`: define the second we subtract from the first"""
     revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))',
                       heads, roots, roots, heads)
     return [c.node() for c in revset]
@@ -385,3 +388,6 @@
             msg = _("phases.new-commit: not a valid phase name ('%s')")
             raise error.ConfigError(msg % v)
 
+def hassecret(repo):
+    """utility function that check if a repo have any secret changeset."""
+    return bool(repo._phasecache.phaseroots[2])
--- a/mercurial/posix.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/posix.py	Tue Oct 02 14:31:25 2012 -0700
@@ -20,6 +20,16 @@
 umask = os.umask(0)
 os.umask(umask)
 
+def split(p):
+    '''Same as os.path.split, but faster'''
+    ht = p.rsplit('/', 1)
+    if len(ht) == 1:
+        return '', p
+    nh = ht[0].rstrip('/')
+    if nh:
+        return nh, ht[1]
+    return ht
+
 def openhardlinks():
     '''return true if it is safe to hold open file handles to hardlinks'''
     return True
--- a/mercurial/pure/osutil.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/pure/osutil.py	Tue Oct 02 14:31:25 2012 -0700
@@ -82,7 +82,7 @@
 
     _FILE_ATTRIBUTE_NORMAL = 0x80
 
-    # _open_osfhandle
+    # open_osfhandle flags
     _O_RDONLY = 0x0000
     _O_RDWR = 0x0002
     _O_APPEND = 0x0008
--- a/mercurial/pure/parsers.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/pure/parsers.py	Tue Oct 02 14:31:25 2012 -0700
@@ -70,7 +70,7 @@
 
 def parse_dirstate(dmap, copymap, st):
     parents = [st[:20], st[20: 40]]
-    # deref fields so they will be local in loop
+    # dereference fields so they will be local in loop
     format = ">cllll"
     e_size = struct.calcsize(format)
     pos1 = 40
--- a/mercurial/pvec.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/pvec.py	Tue Oct 02 14:31:25 2012 -0700
@@ -57,7 +57,7 @@
 _depthbytes = _depthbits / 8
 _vecbytes = _bytes - _depthbytes
 _vecbits = _vecbytes * 8
-_radius = (_vecbits - 30) / 2 # high probability vecs are related
+_radius = (_vecbits - 30) / 2 # high probability vectors are related
 
 def _bin(bs):
     '''convert a bytestring to a long'''
--- a/mercurial/py3kcompat.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/py3kcompat.py	Tue Oct 02 14:31:25 2012 -0700
@@ -12,7 +12,7 @@
 def bytesformatter(format, args):
     '''Custom implementation of a formatter for bytestrings.
 
-    This function currently relias on the string formatter to do the
+    This function currently relies on the string formatter to do the
     formatting and always returns bytes objects.
 
     >>> bytesformatter(20, 10)
--- a/mercurial/revlog.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/revlog.py	Tue Oct 02 14:31:25 2012 -0700
@@ -256,6 +256,13 @@
     def __iter__(self):
         for i in xrange(len(self)):
             yield i
+    def revs(self, start=0, stop=None):
+        """iterate over all rev in this revlog (from start to stop)"""
+        if stop is None:
+            stop = len(self)
+        else:
+            stop += 1
+        return xrange(start, stop)
 
     @util.propertycache
     def nodemap(self):
@@ -374,7 +381,7 @@
             return
 
         seen = set(revs)
-        for i in xrange(first + 1, len(self)):
+        for i in self.revs(start=first + 1):
             for x in self.parentrevs(i):
                 if x != nullrev and x in seen:
                     seen.add(i)
@@ -547,9 +554,9 @@
         # Our topologically sorted list of output nodes.
         orderedout = []
         # Don't start at nullid since we don't want nullid in our output list,
-        # and if nullid shows up in descedents, empty parents will look like
+        # and if nullid shows up in descendants, empty parents will look like
         # they're descendants.
-        for r in xrange(max(lowestrev, 0), highestrev + 1):
+        for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
             n = self.node(r)
             isdescendant = False
             if lowestrev == nullrev:  # Everybody is a descendant of nullid
@@ -600,16 +607,20 @@
         try:
             return self.index.headrevs()
         except AttributeError:
-            pass
+            return self._headrevs()
+
+    def _headrevs(self):
         count = len(self)
         if not count:
             return [nullrev]
-        ishead = [1] * (count + 1)
+        # we won't iter over filtered rev so nobody is a head at start
+        ishead = [0] * (count + 1)
         index = self.index
-        for r in xrange(count):
+        for r in self:
+            ishead[r] = 1  # I may be an head
             e = index[r]
-            ishead[e[5]] = ishead[e[6]] = 0
-        return [r for r in xrange(count) if ishead[r]]
+            ishead[e[5]] = ishead[e[6]] = 0  # my parent are not
+        return [r for r, val in enumerate(ishead) if val]
 
     def heads(self, start=None, stop=None):
         """return the list of all nodes that have no children
@@ -634,7 +645,7 @@
         heads = set((startrev,))
 
         parentrevs = self.parentrevs
-        for r in xrange(startrev + 1, len(self)):
+        for r in self.revs(start=startrev + 1):
             for p in parentrevs(r):
                 if p in reachable:
                     if r not in stoprevs:
@@ -649,7 +660,7 @@
         """find the children of a given node"""
         c = []
         p = self.rev(node)
-        for r in range(p + 1, len(self)):
+        for r in self.revs(start=p + 1):
             prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
             if prevs:
                 for pr in prevs:
@@ -1015,7 +1026,7 @@
         see addrevision for argument descriptions.
         invariants:
         - text is optional (can be None); if not set, cachedelta must be set.
-          if both are set, they must correspond to eachother.
+          if both are set, they must correspond to each other.
         """
         btext = [text]
         def buildtext():
--- a/mercurial/revset.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/revset.py	Tue Oct 02 14:31:25 2012 -0700
@@ -12,6 +12,7 @@
 import match as matchmod
 from i18n import _
 import encoding
+import obsolete as obsmod
 
 def _revancestors(repo, revs, followfirst):
     """Like revlog.ancestors(), but supports followfirst."""
@@ -214,11 +215,11 @@
 def rangeset(repo, subset, x, y):
     m = getset(repo, subset, x)
     if not m:
-        m = getset(repo, range(len(repo)), x)
+        m = getset(repo, list(repo), x)
 
     n = getset(repo, subset, y)
     if not n:
-        n = getset(repo, range(len(repo)), y)
+        n = getset(repo, list(repo), y)
 
     if not m or not n:
         return []
@@ -233,7 +234,7 @@
 
 def dagrange(repo, subset, x, y):
     if subset:
-        r = range(len(repo))
+        r = list(repo)
         xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
         s = set(subset)
         return [r for r in xs if r in s]
@@ -276,7 +277,7 @@
     """
     # i18n: "ancestor" is a keyword
     l = getargs(x, 2, 2, _("ancestor requires two arguments"))
-    r = range(len(repo))
+    r = list(repo)
     a = getset(repo, r, l[0])
     b = getset(repo, r, l[1])
     if len(a) != 1 or len(b) != 1:
@@ -287,7 +288,7 @@
     return [r for r in an if r in subset]
 
 def _ancestors(repo, subset, x, followfirst=False):
-    args = getset(repo, range(len(repo)), x)
+    args = getset(repo, list(repo), x)
     if not args:
         return []
     s = set(_revancestors(repo, args, followfirst)) | set(args)
@@ -335,7 +336,7 @@
     Changesets marked in the specified bisect status:
 
     - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
-    - ``goods``, ``bads``      : csets topologicaly good/bad
+    - ``goods``, ``bads``      : csets topologically good/bad
     - ``range``              : csets taking part in the bisection
     - ``pruned``             : csets that are goods, bads or skipped
     - ``untested``           : csets whose fate is yet unknown
@@ -414,7 +415,7 @@
         else:
             return [r for r in subset if matcher(repo[r].branch())]
 
-    s = getset(repo, range(len(repo)), x)
+    s = getset(repo, list(repo), x)
     b = set()
     for r in s:
         b.add(repo[r].branch())
@@ -465,7 +466,7 @@
     """``children(set)``
     Child changesets of changesets in set.
     """
-    s = set(getset(repo, range(len(repo)), x))
+    s = set(getset(repo, list(repo), x))
     cs = _children(repo, subset, s)
     return [r for r in subset if r in cs]
 
@@ -546,7 +547,7 @@
     return l
 
 def _descendants(repo, subset, x, followfirst=False):
-    args = getset(repo, range(len(repo)), x)
+    args = getset(repo, list(repo), x)
     if not args:
         return []
     s = set(_revdescendants(repo, args, followfirst)) | set(args)
@@ -570,9 +571,9 @@
     is the same as passing all().
     """
     if x is not None:
-        args = set(getset(repo, range(len(repo)), x))
+        args = set(getset(repo, list(repo), x))
     else:
-        args = set(getall(repo, range(len(repo)), x))
+        args = set(getall(repo, list(repo), x))
 
     dests = set()
 
@@ -594,7 +595,7 @@
 
             # The visited lineage is a match if the current source is in the arg
             # set.  Since every candidate dest is visited by way of iterating
-            # subset, any dests futher back in the lineage will be tested by a
+            # subset, any dests further back in the lineage will be tested by a
             # different iteration over subset.  Likewise, if the src was already
             # selected, the current lineage can be selected without going back
             # further.
@@ -621,8 +622,8 @@
     """
     # i18n: "extinct" is a keyword
     getargs(x, 0, 0, _("extinct takes no arguments"))
-    extinctset = set(repo.revs('(obsolete()::) - (::(not obsolete()))'))
-    return [r for r in subset if r in extinctset]
+    extincts = obsmod.getobscache(repo, 'extinct')
+    return [r for r in subset if r in extincts]
 
 def extra(repo, subset, x):
     """``extra(label, [value])``
@@ -838,6 +839,14 @@
     ps = set(parents(repo, subset, x))
     return [r for r in s if r not in ps]
 
+def hidden(repo, subset, x):
+    """``hidden()``
+    Hidden changesets.
+    """
+    # i18n: "hidden" is a keyword
+    getargs(x, 0, 0, _("hidden takes no arguments"))
+    return [r for r in subset if r in repo.hiddenrevs]
+
 def keyword(repo, subset, x):
     """``keyword(string)``
     Search commit message, user name, and names of changed files for
@@ -868,7 +877,7 @@
         # i18n: "limit" is a keyword
         raise error.ParseError(_("limit expects a number"))
     ss = set(subset)
-    os = getset(repo, range(len(repo)), l[0])[:lim]
+    os = getset(repo, list(repo), l[0])[:lim]
     return [r for r in os if r in ss]
 
 def last(repo, subset, x):
@@ -886,14 +895,14 @@
         # i18n: "last" is a keyword
         raise error.ParseError(_("last expects a number"))
     ss = set(subset)
-    os = getset(repo, range(len(repo)), l[0])[-lim:]
+    os = getset(repo, list(repo), l[0])[-lim:]
     return [r for r in os if r in ss]
 
 def maxrev(repo, subset, x):
     """``max(set)``
     Changeset with highest revision number in set.
     """
-    os = getset(repo, range(len(repo)), x)
+    os = getset(repo, list(repo), x)
     if os:
         m = max(os)
         if m in subset:
@@ -913,7 +922,7 @@
     """``min(set)``
     Changeset with lowest revision number in set.
     """
-    os = getset(repo, range(len(repo)), x)
+    os = getset(repo, list(repo), x)
     if os:
         m = min(os)
         if m in subset:
@@ -951,7 +960,8 @@
     Mutable changeset with a newer version."""
     # i18n: "obsolete" is a keyword
     getargs(x, 0, 0, _("obsolete takes no arguments"))
-    return [r for r in subset if repo[r].obsolete()]
+    obsoletes = obsmod.getobscache(repo, 'obsolete')
+    return [r for r in subset if r in obsoletes]
 
 def origin(repo, subset, x):
     """``origin([set])``
@@ -962,9 +972,9 @@
     for the first operation is selected.
     """
     if x is not None:
-        args = set(getset(repo, range(len(repo)), x))
+        args = set(getset(repo, list(repo), x))
     else:
-        args = set(getall(repo, range(len(repo)), x))
+        args = set(getall(repo, list(repo), x))
 
     def _firstsrc(rev):
         src = _getrevsource(repo, rev)
@@ -1014,7 +1024,7 @@
 
     ps = set()
     cl = repo.changelog
-    for r in getset(repo, range(len(repo)), x):
+    for r in getset(repo, list(repo), x):
         ps.add(cl.parentrevs(r)[0])
     return [r for r in subset if r in ps]
 
@@ -1032,7 +1042,7 @@
 
     ps = set()
     cl = repo.changelog
-    for r in getset(repo, range(len(repo)), x):
+    for r in getset(repo, list(repo), x):
         ps.add(cl.parentrevs(r)[1])
     return [r for r in subset if r in ps]
 
@@ -1046,7 +1056,7 @@
 
     ps = set()
     cl = repo.changelog
-    for r in getset(repo, range(len(repo)), x):
+    for r in getset(repo, list(repo), x):
         ps.update(cl.parentrevs(r))
     return [r for r in subset if r in ps]
 
@@ -1429,8 +1439,8 @@
     """
     # i18n: "unstable" is a keyword
     getargs(x, 0, 0, _("unstable takes no arguments"))
-    unstableset = set(repo.revs('(obsolete()::) - obsolete()'))
-    return [r for r in subset if r in unstableset]
+    unstables = obsmod.getobscache(repo, 'unstable')
+    return [r for r in subset if r in unstables]
 
 
 def user(repo, subset, x):
@@ -1484,6 +1494,7 @@
     "grep": grep,
     "head": head,
     "heads": heads,
+    "hidden": hidden,
     "id": node_,
     "keyword": keyword,
     "last": last,
--- a/mercurial/scmutil.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/scmutil.py	Tue Oct 02 14:31:25 2012 -0700
@@ -167,7 +167,7 @@
         # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
         self.auditeddir.update(prefixes)
 
-class abstractopener(object):
+class abstractvfs(object):
     """Abstract base class; cannot be instantiated"""
 
     def __init__(self, *args, **kwargs):
@@ -219,8 +219,8 @@
     def makedirs(self, path=None, mode=None):
         return util.makedirs(self.join(path), mode)
 
-class opener(abstractopener):
-    '''Open files relative to a base directory
+class vfs(abstractvfs):
+    '''Operate files relative to a base directory
 
     This class is used to hide the details of COW semantics and
     remote file access from higher level code.
@@ -229,13 +229,21 @@
         if expand:
             base = os.path.realpath(util.expandpath(base))
         self.base = base
-        self._audit = audit
-        if audit:
-            self.auditor = pathauditor(base)
+        self._setmustaudit(audit)
+        self.createmode = None
+        self._trustnlink = None
+
+    def _getmustaudit(self):
+        return self._audit
+
+    def _setmustaudit(self, onoff):
+        self._audit = onoff
+        if onoff:
+            self.auditor = pathauditor(self.base)
         else:
             self.auditor = util.always
-        self.createmode = None
-        self._trustnlink = None
+
+    mustaudit = property(_getmustaudit, _setmustaudit)
 
     @util.propertycache
     def _cansymlink(self):
@@ -258,7 +266,7 @@
             mode += "b" # for that other OS
 
         nlink = -1
-        dirname, basename = os.path.split(f)
+        dirname, basename = util.split(f)
         # If basename is empty, then the path is malformed because it points
         # to a directory. Let the posixfile() call below raise IOError.
         if basename and mode not in ('r', 'rb'):
@@ -327,8 +335,10 @@
         else:
             return self.base
 
-class filteropener(abstractopener):
-    '''Wrapper opener for filtering filenames with a function.'''
+opener = vfs
+
+class filtervfs(abstractvfs):
+    '''Wrapper vfs for filtering filenames with a function.'''
 
     def __init__(self, opener, filter):
         self._filter = filter
@@ -337,6 +347,8 @@
     def __call__(self, path, *args, **kwargs):
         return self._orig(self._filter(path), *args, **kwargs)
 
+filteropener = filtervfs
+
 def canonpath(root, cwd, myname, auditor=None):
     '''return the canonical path of myname, given cwd and root'''
     if util.endswithsep(root):
@@ -375,7 +387,7 @@
                 name = os.path.join(*rel)
                 auditor(name)
                 return util.pconvert(name)
-            dirname, basename = os.path.split(name)
+            dirname, basename = util.split(name)
             rel.append(basename)
             if dirname == name:
                 break
@@ -619,7 +631,7 @@
 
         # fall through to new-style queries if old-style fails
         m = revset.match(repo.ui, spec)
-        dl = [r for r in m(repo, xrange(len(repo))) if r not in seen]
+        dl = [r for r in m(repo, list(repo)) if r not in seen]
         l.extend(dl)
         seen.update(dl)
 
--- a/mercurial/setdiscovery.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/setdiscovery.py	Tue Oct 02 14:31:25 2012 -0700
@@ -84,9 +84,6 @@
                     abortwhenunrelated=True):
     '''Return a tuple (common, anyincoming, remoteheads) used to identify
     missing nodes from or in remote.
-
-    shortcutlocal determines whether we try use direct access to localrepo if
-    remote is actually local.
     '''
     roundtrips = 0
     cl = local.changelog
@@ -109,7 +106,8 @@
         srvheadhashes = srvheadhashesref.value
         yesno = yesnoref.value
     else:
-        # compatibitity with pre-batch, but post-known remotes during 1.9 devel
+        # compatibility with pre-batch, but post-known remotes during 1.9
+        # development
         srvheadhashes = remote.heads()
         sample = []
 
--- a/mercurial/sshserver.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/sshserver.py	Tue Oct 02 14:31:25 2012 -0700
@@ -71,8 +71,9 @@
         self.fout.flush()
 
     def sendstream(self, source):
+        write = self.fout.write
         for chunk in source.gen:
-            self.fout.write(chunk)
+            write(chunk)
         self.fout.flush()
 
     def sendpushresponse(self, rsp):
--- a/mercurial/statichttprepo.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/statichttprepo.py	Tue Oct 02 14:31:25 2012 -0700
@@ -64,7 +64,7 @@
     urlopener = url.opener(ui, authinfo)
     urlopener.add_handler(byterange.HTTPRangeHandler())
 
-    class statichttpopener(scmutil.abstractopener):
+    class statichttpvfs(scmutil.abstractvfs):
         def __init__(self, base):
             self.base = base
 
@@ -74,7 +74,7 @@
             f = "/".join((self.base, urllib.quote(path)))
             return httprangereader(f, urlopener)
 
-    return statichttpopener
+    return statichttpvfs
 
 class statichttppeer(localrepo.localpeer):
     def local(self):
--- a/mercurial/store.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/store.py	Tue Oct 02 14:31:25 2012 -0700
@@ -6,29 +6,31 @@
 # GNU General Public License version 2 or any later version.
 
 from i18n import _
-import osutil, scmutil, util
+import osutil, scmutil, util, parsers
 import os, stat, errno
 
 _sha = util.sha1
 
 # This avoids a collision between a file named foo and a dir named
 # foo.i or foo.d
-def encodedir(path):
+def _encodedir(path):
     '''
-    >>> encodedir('data/foo.i')
+    >>> _encodedir('data/foo.i')
     'data/foo.i'
-    >>> encodedir('data/foo.i/bla.i')
+    >>> _encodedir('data/foo.i/bla.i')
     'data/foo.i.hg/bla.i'
-    >>> encodedir('data/foo.i.hg/bla.i')
+    >>> _encodedir('data/foo.i.hg/bla.i')
     'data/foo.i.hg.hg/bla.i'
+    >>> _encodedir('data/foo.i\\ndata/foo.i/bla.i\\ndata/foo.i.hg/bla.i\\n')
+    'data/foo.i\\ndata/foo.i.hg/bla.i\\ndata/foo.i.hg.hg/bla.i\\n'
     '''
-    if not path.startswith('data/'):
-        return path
     return (path
             .replace(".hg/", ".hg.hg/")
             .replace(".i/", ".i.hg/")
             .replace(".d/", ".d.hg/"))
 
+encodedir = getattr(parsers, 'encodedir', _encodedir)
+
 def decodedir(path):
     '''
     >>> decodedir('data/foo.i')
@@ -38,7 +40,7 @@
     >>> decodedir('data/foo.i.hg.hg/bla.i')
     'data/foo.i.hg/bla.i'
     '''
-    if not path.startswith('data/') or ".hg/" not in path:
+    if ".hg/" not in path:
         return path
     return (path
             .replace(".d.hg/", ".d/")
@@ -91,10 +93,24 @@
                     pass
             else:
                 raise KeyError
-    return (lambda s: "".join([cmap[c] for c in encodedir(s)]),
-            lambda s: decodedir("".join(list(decode(s)))))
+    return (lambda s: ''.join([cmap[c] for c in s]),
+            lambda s: ''.join(list(decode(s))))
+
+_encodefname, _decodefname = _buildencodefun()
 
-encodefilename, decodefilename = _buildencodefun()
+def encodefilename(s):
+    '''
+    >>> encodefilename('foo.i/bar.d/bla.hg/hi:world?/HELLO')
+    'foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o'
+    '''
+    return _encodefname(encodedir(s))
+
+def decodefilename(s):
+    '''
+    >>> decodefilename('foo.i.hg/bar.d.hg/bla.hg.hg/hi~3aworld~3f/_h_e_l_l_o')
+    'foo.i/bar.d/bla.hg/hi:world?/HELLO'
+    '''
+    return decodedir(_decodefname(s))
 
 def _buildlowerencodefun():
     '''
@@ -118,47 +134,89 @@
 
 lowerencode = _buildlowerencodefun()
 
-_winreservednames = '''con prn aux nul
-    com1 com2 com3 com4 com5 com6 com7 com8 com9
-    lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
+# Windows reserved names: con, prn, aux, nul, com1..com9, lpt1..lpt9
+_winres3 = ('aux', 'con', 'prn', 'nul') # length 3
+_winres4 = ('com', 'lpt')               # length 4 (with trailing 1..9)
 def _auxencode(path, dotencode):
     '''
     Encodes filenames containing names reserved by Windows or which end in
     period or space. Does not touch other single reserved characters c.
     Specifically, c in '\\:*?"<>|' or ord(c) <= 31 are *not* encoded here.
     Additionally encodes space or period at the beginning, if dotencode is
-    True.
-    path is assumed to be all lowercase.
+    True. Parameter path is assumed to be all lowercase.
+    A segment only needs encoding if a reserved name appears as a
+    basename (e.g. "aux", "aux.foo"). A directory or file named "foo.aux"
+    doesn't need encoding.
 
-    >>> _auxencode('.foo/aux.txt/txt.aux/con/prn/nul/foo.', True)
-    '~2efoo/au~78.txt/txt.aux/co~6e/pr~6e/nu~6c/foo~2e'
-    >>> _auxencode('.com1com2/lpt9.lpt4.lpt1/conprn/foo.', False)
-    '.com1com2/lp~749.lpt4.lpt1/conprn/foo~2e'
-    >>> _auxencode('foo. ', True)
-    'foo.~20'
-    >>> _auxencode(' .foo', True)
-    '~20.foo'
+    >>> s = '.foo/aux.txt/txt.aux/con/prn/nul/foo.'
+    >>> _auxencode(s.split('/'), True)
+    ['~2efoo', 'au~78.txt', 'txt.aux', 'co~6e', 'pr~6e', 'nu~6c', 'foo~2e']
+    >>> s = '.com1com2/lpt9.lpt4.lpt1/conprn/com0/lpt0/foo.'
+    >>> _auxencode(s.split('/'), False)
+    ['.com1com2', 'lp~749.lpt4.lpt1', 'conprn', 'com0', 'lpt0', 'foo~2e']
+    >>> _auxencode(['foo. '], True)
+    ['foo.~20']
+    >>> _auxencode([' .foo'], True)
+    ['~20.foo']
     '''
-    res = []
-    for n in path.split('/'):
-        if n:
-            base = n.split('.')[0]
-            if base and (base in _winreservednames):
+    for i, n in enumerate(path):
+        if not n:
+            continue
+        if dotencode and n[0] in '. ':
+            n = "~%02x" % ord(n[0]) + n[1:]
+            path[i] = n
+        else:
+            l = n.find('.')
+            if l == -1:
+                l = len(n)
+            if ((l == 3 and n[:3] in _winres3) or
+                (l == 4 and n[3] <= '9' and n[3] >= '1'
+                        and n[:3] in _winres4)):
                 # encode third letter ('aux' -> 'au~78')
                 ec = "~%02x" % ord(n[2])
                 n = n[0:2] + ec + n[3:]
-            if n[-1] in '. ':
-                # encode last period or space ('foo...' -> 'foo..~2e')
-                n = n[:-1] + "~%02x" % ord(n[-1])
-            if dotencode and n[0] in '. ':
-                n = "~%02x" % ord(n[0]) + n[1:]
-        res.append(n)
-    return '/'.join(res)
+                path[i] = n
+        if n[-1] in '. ':
+            # encode last period or space ('foo...' -> 'foo..~2e')
+            path[i] = n[:-1] + "~%02x" % ord(n[-1])
+    return path
 
 _maxstorepathlen = 120
 _dirprefixlen = 8
 _maxshortdirslen = 8 * (_dirprefixlen + 1) - 4
-def _hybridencode(path, auxencode):
+
+def _hashencode(path, dotencode):
+    digest = _sha(path).hexdigest()
+    le = lowerencode(path).split('/')[1:]
+    parts = _auxencode(le, dotencode)
+    basename = parts[-1]
+    _root, ext = os.path.splitext(basename)
+    sdirs = []
+    sdirslen = 0
+    for p in parts[:-1]:
+        d = p[:_dirprefixlen]
+        if d[-1] in '. ':
+            # Windows can't access dirs ending in period or space
+            d = d[:-1] + '_'
+        if sdirslen == 0:
+            t = len(d)
+        else:
+            t = sdirslen + 1 + len(d)
+            if t > _maxshortdirslen:
+                break
+        sdirs.append(d)
+        sdirslen = t
+    dirs = '/'.join(sdirs)
+    if len(dirs) > 0:
+        dirs += '/'
+    res = 'dh/' + dirs + digest + ext
+    spaceleft = _maxstorepathlen - len(res)
+    if spaceleft > 0:
+        filler = basename[:spaceleft]
+        res = 'dh/' + dirs + filler + digest + ext
+    return res
+
+def _hybridencode(path, dotencode):
     '''encodes path with a length limit
 
     Encodes all paths that begin with 'data/', according to the following.
@@ -189,38 +247,33 @@
     The string 'data/' at the beginning is replaced with 'dh/', if the hashed
     encoding was used.
     '''
-    if not path.startswith('data/'):
-        return path
-    # escape directories ending with .i and .d
     path = encodedir(path)
-    ndpath = path[len('data/'):]
-    res = 'data/' + auxencode(encodefilename(ndpath))
+    ef = _encodefname(path).split('/')
+    res = '/'.join(_auxencode(ef, dotencode))
+    if len(res) > _maxstorepathlen:
+        res = _hashencode(path, dotencode)
+    return res
+
+def _pathencode(path):
+    if len(path) > _maxstorepathlen:
+        return None
+    ef = _encodefname(encodedir(path)).split('/')
+    res = '/'.join(_auxencode(ef, True))
     if len(res) > _maxstorepathlen:
-        digest = _sha(path).hexdigest()
-        aep = auxencode(lowerencode(ndpath))
-        _root, ext = os.path.splitext(aep)
-        parts = aep.split('/')
-        basename = parts[-1]
-        sdirs = []
-        for p in parts[:-1]:
-            d = p[:_dirprefixlen]
-            if d[-1] in '. ':
-                # Windows can't access dirs ending in period or space
-                d = d[:-1] + '_'
-            t = '/'.join(sdirs) + '/' + d
-            if len(t) > _maxshortdirslen:
-                break
-            sdirs.append(d)
-        dirs = '/'.join(sdirs)
-        if len(dirs) > 0:
-            dirs += '/'
-        res = 'dh/' + dirs + digest + ext
-        spaceleft = _maxstorepathlen - len(res)
-        if spaceleft > 0:
-            filler = basename[:spaceleft]
-            res = 'dh/' + dirs + filler + digest + ext
+        return None
     return res
 
+_pathencode = getattr(parsers, 'pathencode', _pathencode)
+
+def _dothybridencode(f):
+    ef = _pathencode(f)
+    if ef is None:
+        return _hashencode(encodedir(f), True)
+    return ef
+
+def _plainhybridencode(f):
+    return _hybridencode(f, False)
+
 def _calcmode(path):
     try:
         # files in .hg/ will be created using this mode
@@ -237,12 +290,13 @@
 
 class basicstore(object):
     '''base class for local repository stores'''
-    def __init__(self, path, openertype):
+    def __init__(self, path, vfstype):
         self.path = path
         self.createmode = _calcmode(path)
-        op = openertype(self.path)
-        op.createmode = self.createmode
-        self.opener = scmutil.filteropener(op, encodedir)
+        vfs = vfstype(self.path)
+        vfs.createmode = self.createmode
+        self.vfs = scmutil.filtervfs(vfs, encodedir)
+        self.opener = self.vfs
 
     def join(self, f):
         return self.path + '/' + encodedir(f)
@@ -287,12 +341,13 @@
         pass
 
 class encodedstore(basicstore):
-    def __init__(self, path, openertype):
+    def __init__(self, path, vfstype):
         self.path = path + '/store'
         self.createmode = _calcmode(self.path)
-        op = openertype(self.path)
-        op.createmode = self.createmode
-        self.opener = scmutil.filteropener(op, encodefilename)
+        vfs = vfstype(self.path)
+        vfs.createmode = self.createmode
+        self.vfs = scmutil.filtervfs(vfs, encodefilename)
+        self.opener = self.vfs
 
     def datafiles(self):
         for a, b, size in self._walk('data', True):
@@ -326,7 +381,7 @@
             # skip nonexistent file
             self.entries = set()
             return
-        self.entries = set(map(decodedir, fp.read().splitlines()))
+        self.entries = set(decodedir(fp.read()).splitlines())
         if '' in self.entries:
             fp.seek(0)
             for n, line in enumerate(fp):
@@ -338,7 +393,7 @@
     def _write(self, files, atomictemp):
         fp = self.opener('fncache', mode='wb', atomictemp=atomictemp)
         if files:
-            fp.write('\n'.join(map(encodedir, files)) + '\n')
+            fp.write(encodedir('\n'.join(files) + '\n'))
         fp.close()
         self._dirty = False
 
@@ -367,33 +422,47 @@
             self._load()
         return iter(self.entries)
 
-class _fncacheopener(scmutil.abstractopener):
+class _fncachevfs(scmutil.abstractvfs):
     def __init__(self, op, fnc, encode):
         self.opener = op
         self.fncache = fnc
         self.encode = encode
 
+    def _getmustaudit(self):
+        return self.opener.mustaudit
+
+    def _setmustaudit(self, onoff):
+        self.opener.mustaudit = onoff
+
+    mustaudit = property(_getmustaudit, _setmustaudit)
+
     def __call__(self, path, mode='r', *args, **kw):
         if mode not in ('r', 'rb') and path.startswith('data/'):
             self.fncache.add(path)
         return self.opener(self.encode(path), mode, *args, **kw)
 
 class fncachestore(basicstore):
-    def __init__(self, path, openertype, encode):
+    def __init__(self, path, vfstype, dotencode):
+        if dotencode:
+            encode = _dothybridencode
+        else:
+            encode = _plainhybridencode
         self.encode = encode
         self.path = path + '/store'
+        self.pathsep = self.path + '/'
         self.createmode = _calcmode(self.path)
-        op = openertype(self.path)
-        op.createmode = self.createmode
-        fnc = fncache(op)
+        vfs = vfstype(self.path)
+        vfs.createmode = self.createmode
+        fnc = fncache(vfs)
         self.fncache = fnc
-        self.opener = _fncacheopener(op, fnc, encode)
+        self.vfs = _fncachevfs(vfs, fnc, encode)
+        self.opener = self.vfs
 
     def join(self, f):
-        return self.path + '/' + self.encode(f)
+        return self.pathsep + self.encode(f)
 
     def getsize(self, path):
-        return os.stat(self.path + '/' + path).st_size
+        return os.stat(self.pathsep + path).st_size
 
     def datafiles(self):
         rewrite = False
@@ -422,11 +491,9 @@
     def write(self):
         self.fncache.write()
 
-def store(requirements, path, openertype):
+def store(requirements, path, vfstype):
     if 'store' in requirements:
         if 'fncache' in requirements:
-            auxencode = lambda f: _auxencode(f, 'dotencode' in requirements)
-            encode = lambda f: _hybridencode(f, auxencode)
-            return fncachestore(path, openertype, encode)
-        return encodedstore(path, openertype)
-    return basicstore(path, openertype)
+            return fncachestore(path, vfstype, 'dotencode' in requirements)
+        return encodedstore(path, vfstype)
+    return basicstore(path, vfstype)
--- a/mercurial/subrepo.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/subrepo.py	Tue Oct 02 14:31:25 2012 -0700
@@ -888,7 +888,7 @@
     def _gitnodir(self, commands, env=None, stream=False, cwd=None):
         """Calls the git command
 
-        The methods tries to call the git command. versions previor to 1.6.0
+        The methods tries to call the git command. versions prior to 1.6.0
         are not supported and very probably fail.
         """
         self._ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
--- a/mercurial/tags.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/tags.py	Tue Oct 02 14:31:25 2012 -0700
@@ -124,8 +124,8 @@
             continue
 
         # we prefer alltags[name] if:
-        #  it supercedes us OR
-        #  mutual supercedes and it has a higher rank
+        #  it supersedes us OR
+        #  mutual supersedes and it has a higher rank
         # otherwise we win because we're tip-most
         anode, ahist = nodehist
         bnode, bhist = alltags[name]
--- a/mercurial/templatefilters.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/templatefilters.py	Tue Oct 02 14:31:25 2012 -0700
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 import cgi, re, os, time, urllib
-import encoding, node, util
+import encoding, node, util, error
 import hbisect
 
 def addbreaks(text):
@@ -391,5 +391,34 @@
     "xmlescape": xmlescape,
 }
 
+def fillfunc(context, mapping, args):
+    if not (1 <= len(args) <= 2):
+        raise error.ParseError(_("fill expects one or two arguments"))
+
+    text = stringify(args[0][0](context, mapping, args[0][1]))
+    width = 76
+    if len(args) == 2:
+        try:
+            width = int(stringify(args[1][0](context, mapping, args[1][1])))
+        except ValueError:
+            raise error.ParseError(_("fill expects an integer width"))
+
+    return fill(text, width)
+
+def datefunc(context, mapping, args):
+    if not (1 <= len(args) <= 2):
+        raise error.ParseError(_("date expects one or two arguments"))
+
+    date = args[0][0](context, mapping, args[0][1])
+    if len(args) == 2:
+        fmt = stringify(args[1][0](context, mapping, args[1][1]))
+        return util.datestr(date, fmt)
+    return util.datestr(date)
+
+funcs = {
+    "fill": fillfunc,
+    "date": datefunc,
+}
+
 # tell hggettext to extract docstrings from these functions:
 i18nfunctions = filters.values()
--- a/mercurial/templatekw.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/templatekw.py	Tue Oct 02 14:31:25 2012 -0700
@@ -9,7 +9,27 @@
 import patch, util, error
 import hbisect
 
-def showlist(name, values, plural=None, **args):
+# This helper class allows us to handle both:
+#  "{files}" (legacy command-line-specific list hack) and
+#  "{files % '{file}\n'}" (hgweb-style with inlining and function support)
+
+class _hybrid(object):
+    def __init__(self, gen, values):
+        self.gen = gen
+        self.values = values
+    def __iter__(self):
+        return self.gen
+    def __call__(self):
+        for x in self.values:
+            yield x
+
+def showlist(name, values, plural=None, element=None, **args):
+    if not element:
+        element = name
+    f = _showlist(name, values, plural, **args)
+    return _hybrid(f, [{element: x} for x in values])
+
+def _showlist(name, values, plural=None, **args):
     '''expand set of values.
     name is name of key in template map.
     values is list of strings or dicts.
@@ -176,7 +196,7 @@
     """:children: List of strings. The children of the changeset."""
     ctx = args['ctx']
     childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
-    return showlist('children', childrevs, **args)
+    return showlist('children', childrevs, element='child', **args)
 
 def showdate(repo, ctx, templ, **args):
     """:date: Date information. The date when the changeset was committed."""
@@ -204,7 +224,8 @@
 def showfileadds(**args):
     """:file_adds: List of strings. Files added by this changeset."""
     repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
-    return showlist('file_add', getfiles(repo, ctx, revcache)[1], **args)
+    return showlist('file_add', getfiles(repo, ctx, revcache)[1],
+                    element='file', **args)
 
 def showfilecopies(**args):
     """:file_copies: List of strings. Files copied in this changeset with
@@ -223,7 +244,8 @@
                 copies.append((fn, rename[0]))
 
     c = [{'name': x[0], 'source': x[1]} for x in copies]
-    return showlist('file_copy', c, plural='file_copies', **args)
+    return showlist('file_copy', c, plural='file_copies',
+                    element='file', **args)
 
 # showfilecopiesswitch() displays file copies only if copy records are
 # provided before calling the templater, usually with a --copies
@@ -234,17 +256,20 @@
     """
     copies = args['revcache'].get('copies') or []
     c = [{'name': x[0], 'source': x[1]} for x in copies]
-    return showlist('file_copy', c, plural='file_copies', **args)
+    return showlist('file_copy', c, plural='file_copies',
+                    element='file', **args)
 
 def showfiledels(**args):
     """:file_dels: List of strings. Files removed by this changeset."""
     repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
-    return showlist('file_del', getfiles(repo, ctx, revcache)[2], **args)
+    return showlist('file_del', getfiles(repo, ctx, revcache)[2],
+                    element='file', **args)
 
 def showfilemods(**args):
     """:file_mods: List of strings. Files modified by this changeset."""
     repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
-    return showlist('file_mod', getfiles(repo, ctx, revcache)[0], **args)
+    return showlist('file_mod', getfiles(repo, ctx, revcache)[0],
+                    element='file', **args)
 
 def showfiles(**args):
     """:files: List of strings. All files modified, added, or removed by this
@@ -275,6 +300,28 @@
     """
     return ctx.hex()
 
+def showp1rev(repo, ctx, templ, **args):
+    """:p1rev: Integer. The repository-local revision number of the changeset's
+    first parent, or -1 if the changeset has no parents."""
+    return ctx.p1().rev()
+
+def showp2rev(repo, ctx, templ, **args):
+    """:p2rev: Integer. The repository-local revision number of the changeset's
+    second parent, or -1 if the changeset has no second parent."""
+    return ctx.p2().rev()
+
+def showp1node(repo, ctx, templ, **args):
+    """:p1node: String. The identification hash of the changeset's first parent,
+    as a 40 digit hexadecimal string. If the changeset has no parents, all
+    digits are 0."""
+    return ctx.p1().hex()
+
+def showp2node(repo, ctx, templ, **args):
+    """:p2node: String. The identification hash of the changeset's second
+    parent, as a 40 digit hexadecimal string. If the changeset has no second
+    parent, all digits are 0."""
+    return ctx.p2().hex()
+
 def showphase(repo, ctx, templ, **args):
     """:phase: String. The changeset phase name."""
     return ctx.phasestr()
@@ -320,6 +367,10 @@
     'latesttagdistance': showlatesttagdistance,
     'manifest': showmanifest,
     'node': shownode,
+    'p1rev': showp1rev,
+    'p1node': showp1node,
+    'p2rev': showp2rev,
+    'p2node': showp2node,
     'phase': showphase,
     'phaseidx': showphaseidx,
     'rev': showrev,
--- a/mercurial/templater.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/templater.py	Tue Oct 02 14:31:25 2012 -0700
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 from i18n import _
-import sys, os
+import sys, os, re
 import util, config, templatefilters, parser, error
 
 # template parsing
@@ -36,18 +36,21 @@
             if c == 'r':
                 pos += 1
                 c = program[pos]
-                decode = lambda x: x
+                decode = False
             else:
-                decode = lambda x: x.decode('string-escape')
+                decode = True
             pos += 1
             s = pos
             while pos < end: # find closing quote
                 d = program[pos]
-                if d == '\\': # skip over escaped characters
+                if decode and d == '\\': # skip over escaped characters
                     pos += 2
                     continue
                 if d == c:
-                    yield ('string', decode(program[s:pos]), s)
+                    if not decode:
+                        yield ('string', program[s:pos].replace('\\', r'\\'), s)
+                        break
+                    yield ('string', program[s:pos].decode('string-escape'), s)
                     break
                 pos += 1
             else:
@@ -146,23 +149,37 @@
 
 def runfilter(context, mapping, data):
     func, data, filt = data
-    return filt(func(context, mapping, data))
+    try:
+        return filt(func(context, mapping, data))
+    except (ValueError, AttributeError, TypeError):
+        if isinstance(data, tuple):
+            dt = data[1]
+        else:
+            dt = data
+        raise util.Abort(_("template filter '%s' is not compatible with "
+                           "keyword '%s'") % (filt.func_name, dt))
 
 def buildmap(exp, context):
     func, data = compileexp(exp[1], context)
     ctmpl = gettemplate(exp[2], context)
     return (runmap, (func, data, ctmpl))
 
+def runtemplate(context, mapping, template):
+    for func, data in template:
+        yield func(context, mapping, data)
+
 def runmap(context, mapping, data):
     func, data, ctmpl = data
     d = func(context, mapping, data)
+    if util.safehasattr(d, '__call__'):
+        d = d()
+
     lm = mapping.copy()
 
     for i in d:
         if isinstance(i, dict):
             lm.update(i)
-            for f, d in ctmpl:
-                yield f(context, lm, d)
+            yield runtemplate(context, lm, ctmpl)
         else:
             # v is not an iterable of dicts, this happen when 'key'
             # has been fully expanded already and format is useless.
@@ -175,12 +192,69 @@
     if n in funcs:
         f = funcs[n]
         return (f, args)
+    if n in templatefilters.funcs:
+        f = templatefilters.funcs[n]
+        return (f, args)
     if n in context._filters:
         if len(args) != 1:
             raise error.ParseError(_("filter %s expects one argument") % n)
         f = context._filters[n]
         return (runfilter, (args[0][0], args[0][1], f))
 
+def join(context, mapping, args):
+    if not (1 <= len(args) <= 2):
+        raise error.ParseError(_("join expects one or two arguments"))
+
+    joinset = args[0][0](context, mapping, args[0][1])
+    if util.safehasattr(joinset, '__call__'):
+        joinset = [x.values()[0] for x in joinset()]
+
+    joiner = " "
+    if len(args) > 1:
+        joiner = args[1][0](context, mapping, args[1][1])
+
+    first = True
+    for x in joinset:
+        if first:
+            first = False
+        else:
+            yield joiner
+        yield x
+
+def sub(context, mapping, args):
+    if len(args) != 3:
+        raise error.ParseError(_("sub expects three arguments"))
+
+    pat = stringify(args[0][0](context, mapping, args[0][1]))
+    rpl = stringify(args[1][0](context, mapping, args[1][1]))
+    src = stringify(args[2][0](context, mapping, args[2][1]))
+    yield re.sub(pat, rpl, src)
+
+def if_(context, mapping, args):
+    if not (2 <= len(args) <= 3):
+        raise error.ParseError(_("if expects two or three arguments"))
+
+    test = stringify(args[0][0](context, mapping, args[0][1]))
+    if test:
+        t = stringify(args[1][0](context, mapping, args[1][1]))
+        yield runtemplate(context, mapping, compiletemplate(t, context))
+    elif len(args) == 3:
+        t = stringify(args[2][0](context, mapping, args[2][1]))
+        yield runtemplate(context, mapping, compiletemplate(t, context))
+
+def ifeq(context, mapping, args):
+    if not (3 <= len(args) <= 4):
+        raise error.ParseError(_("ifeq expects three or four arguments"))
+
+    test = stringify(args[0][0](context, mapping, args[0][1]))
+    match = stringify(args[1][0](context, mapping, args[1][1]))
+    if test == match:
+        t = stringify(args[2][0](context, mapping, args[2][1]))
+        yield runtemplate(context, mapping, compiletemplate(t, context))
+    elif len(args) == 4:
+        t = stringify(args[3][0](context, mapping, args[3][1]))
+        yield runtemplate(context, mapping, compiletemplate(t, context))
+
 methods = {
     "string": lambda e, c: (runstring, e[1]),
     "symbol": lambda e, c: (runsymbol, e[1]),
@@ -192,6 +266,10 @@
     }
 
 funcs = {
+    "if": if_,
+    "ifeq": ifeq,
+    "join": join,
+    "sub": sub,
 }
 
 # template engine
@@ -265,6 +343,7 @@
         generator.'''
         return _flatten(func(self, mapping, data) for func, data in
                          self._load(t))
+        return _flatten(runtemplate(self, mapping, self._load(t)))
 
 engines = {'default': engine}
 
--- a/mercurial/templates/static/mercurial.js	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/templates/static/mercurial.js	Tue Oct 02 14:31:25 2012 -0700
@@ -25,7 +25,7 @@
 function Graph() {
 	
 	this.canvas = document.getElementById('graph');
-	if (navigator.userAgent.indexOf('MSIE') >= 0) this.canvas = window.G_vmlCanvasManager.initElement(this.canvas);
+	if (window.G_vmlCanvasManager) this.canvas = window.G_vmlCanvasManager.initElement(this.canvas);
 	this.ctx = this.canvas.getContext('2d');
 	this.ctx.strokeStyle = 'rgb(0, 0, 0)';
 	this.ctx.fillStyle = 'rgb(0, 0, 0)';
--- a/mercurial/templates/template-vars.txt	Mon Oct 01 23:11:28 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-repo          the name of the repo
-rev           a changeset.manifest revision
-node          a changeset node
-changesets    total number of changesets
-file          a filename
-filerev       a file revision
-filerevs      total number of file revisions
-up            the directory of the relevant file
-path          a path in the manifest, starting with "/"
-basename      a short pathname
-date          a date string
-age           age in hours, days, etc
-line          a line of text (escaped)
-desc          a description (escaped, with breaks)
-shortdesc     a short description (escaped)
-author        a name or email addressv(obfuscated)
-parent        a list of the parent
-child         a list of the children
-tags          a list of tag
-
-header        the global page header
-footer        the global page footer
-
-files         a list of file links
-file_copies   a list of pairs of name, source filenames
-dirs          a set of directory links
-diff          a diff of one or more files
-annotate      an annotated file
-entries       the entries relevant to the page
-
-url           base url of hgweb interface
-logourl       base url of logo
-staticurl     base url for static resources
-
-
-Templates and commands:
-  changelog(rev) - a page for browsing changesets
-    naventry - a link for jumping to a changeset number
-    filenodelink - jump to file diff
-    fileellipses - printed after maxfiles
-    changelogentry - an entry in the log
-  manifest - browse a manifest as a directory tree
--- a/mercurial/transaction.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/transaction.py	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-# transaction.py - simple journalling scheme for mercurial
+# transaction.py - simple journaling scheme for mercurial
 #
 # This transaction scheme is intended to gracefully handle program
 # errors and interruptions. More serious failures like system crashes
--- a/mercurial/ui.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/ui.py	Tue Oct 02 14:31:25 2012 -0700
@@ -713,8 +713,8 @@
         With stock hg, this is simply a debug message that is hidden
         by default, but with extensions or GUI tools it may be
         visible. 'topic' is the current operation, 'item' is a
-        non-numeric marker of the current position (ie the currently
-        in-process file), 'pos' is the current numeric position (ie
+        non-numeric marker of the current position (i.e. the currently
+        in-process file), 'pos' is the current numeric position (i.e.
         revision, bytes, etc.), unit is a corresponding unit label,
         and total is the highest expected pos.
 
--- a/mercurial/url.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/url.py	Tue Oct 02 14:31:25 2012 -0700
@@ -175,7 +175,7 @@
             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
             self.sock.connect((self.host, self.port))
             if _generic_proxytunnel(self):
-                # we do not support client x509 certificates
+                # we do not support client X.509 certificates
                 self.sock = sslutil.ssl_wrap_socket(self.sock, None, None)
         else:
             keepalive.HTTPConnection.connect(self)
@@ -278,7 +278,8 @@
     res.will_close = res._check_close()
 
     # do we have a Content-Length?
-    # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
+    # NOTE: RFC 2616, section 4.4, #3 says we ignore this if
+    # transfer-encoding is "chunked"
     length = res.msg.getheader('content-length')
     if length and not res.chunked:
         try:
--- a/mercurial/util.h	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/util.h	Tue Oct 02 14:31:25 2012 -0700
@@ -121,6 +121,7 @@
 #ifdef _MSC_VER
 /* msvc 6.0 has problems */
 #define inline __inline
+typedef unsigned char uint8_t;
 typedef unsigned long uint32_t;
 typedef unsigned __int64 uint64_t;
 #else
--- a/mercurial/util.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/util.py	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-# util.py - Mercurial utility functions and platform specfic implementations
+# util.py - Mercurial utility functions and platform specific implementations
 #
 #  Copyright 2005 K. Thananchayan <thananck@yahoo.com>
 #  Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
@@ -7,7 +7,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-"""Mercurial utility functions and platform specfic implementations.
+"""Mercurial utility functions and platform specific implementations.
 
 This contains helper routines that are independent of the SCM core and
 hide platform-specific details from the core.
@@ -62,6 +62,7 @@
 setsignalhandler = platform.setsignalhandler
 shellquote = platform.shellquote
 spawndetached = platform.spawndetached
+split = platform.split
 sshargs = platform.sshargs
 statfiles = platform.statfiles
 termwidth = platform.termwidth
@@ -799,7 +800,7 @@
     return temp
 
 class atomictempfile(object):
-    '''writeable file object that atomically updates a file
+    '''writable file object that atomically updates a file
 
     All writes will go to a temporary copy of the original file. Call
     close() when you are done writing, and atomictempfile will rename
@@ -1239,7 +1240,7 @@
         so overriding is needed to use width information of each characters.
 
         In addition, characters classified into 'ambiguous' width are
-        treated as wide in east asian area, but as narrow in other.
+        treated as wide in East Asian area, but as narrow in other.
 
         This requires use decision to determine width of such characters.
         """
@@ -1300,7 +1301,7 @@
                 width = self.width - len(indent)
 
                 # First chunk on line is whitespace -- drop it, unless this
-                # is the very beginning of the text (ie. no lines started yet).
+                # is the very beginning of the text (i.e. no lines started yet).
                 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
                     del chunks[-1]
 
@@ -1477,7 +1478,11 @@
                  for a in _hexdig for b in _hexdig)
 
 def _urlunquote(s):
-    """unquote('abc%20def') -> 'abc def'."""
+    """Decode HTTP/HTML % encoding.
+
+    >>> _urlunquote('abc%20def')
+    'abc def'
+    """
     res = s.split('%')
     # fastpath
     if len(res) == 1:
--- a/mercurial/win32.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/win32.py	Tue Oct 02 14:31:25 2012 -0700
@@ -328,7 +328,7 @@
     env += '\0'
 
     args = subprocess.list2cmdline(args)
-    # Not running the command in shell mode makes python26 hang when
+    # Not running the command in shell mode makes Python 2.6 hang when
     # writing to hgweb output socket.
     comspec = os.environ.get("COMSPEC", "cmd.exe")
     args = comspec + " /c " + args
--- a/mercurial/windows.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/windows.py	Tue Oct 02 14:31:25 2012 -0700
@@ -20,6 +20,7 @@
 samefile = win32.samefile
 setsignalhandler = win32.setsignalhandler
 spawndetached = win32.spawndetached
+split = os.path.split
 termwidth = win32.termwidth
 testpid = win32.testpid
 unlink = win32.unlink
@@ -152,7 +153,7 @@
 #   backslash
 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
 # So, to quote a string, we must surround it in double quotes, double
-# the number of backslashes that preceed double quotes and add another
+# the number of backslashes that precede double quotes and add another
 # backslash before every double quote (being careful with the double
 # quote we've appended to the end)
 _quotere = None
--- a/mercurial/wireproto.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/mercurial/wireproto.py	Tue Oct 02 14:31:25 2012 -0700
@@ -516,7 +516,7 @@
     it is serving. Client checks to see if it understands the format.
 
     The format is simple: the server writes out a line with the amount
-    of files, then the total amount of bytes to be transfered (separated
+    of files, then the total amount of bytes to be transferred (separated
     by a space). Then, for each file, the server first writes the filename
     and filesize (separated by the null character), then the file contents.
     '''
@@ -545,12 +545,33 @@
         repo.ui.debug('%d files, %d bytes to transfer\n' %
                       (len(entries), total_bytes))
         yield '%d %d\n' % (len(entries), total_bytes)
-        for name, size in entries:
-            repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
-            # partially encode name over the wire for backwards compat
-            yield '%s\0%d\n' % (store.encodedir(name), size)
-            for chunk in util.filechunkiter(repo.sopener(name), limit=size):
-                yield chunk
+
+        sopener = repo.sopener
+        oldaudit = sopener.mustaudit
+        debugflag = repo.ui.debugflag
+        sopener.mustaudit = False
+
+        try:
+            for name, size in entries:
+                if debugflag:
+                    repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
+                # partially encode name over the wire for backwards compat
+                yield '%s\0%d\n' % (store.encodedir(name), size)
+                if size <= 65536:
+                    fp = sopener(name)
+                    try:
+                        data = fp.read(size)
+                    finally:
+                        fp.close()
+                    yield data
+                else:
+                    for chunk in util.filechunkiter(sopener(name), limit=size):
+                        yield chunk
+        # replace with "finally:" when support for python 2.4 has been dropped
+        except Exception:
+            sopener.mustaudit = oldaudit
+            raise
+        sopener.mustaudit = oldaudit
 
     return streamres(streamer(repo, entries, total_bytes))
 
--- a/setup.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/setup.py	Tue Oct 02 14:31:25 2012 -0700
@@ -421,7 +421,8 @@
     Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
     Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
     Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
-    Extension('mercurial.parsers', ['mercurial/parsers.c']),
+    Extension('mercurial.parsers', ['mercurial/parsers.c',
+                                    'mercurial/pathencode.c']),
     ]
 
 osutil_ldflags = []
--- a/tests/blacklists/inotify-failures	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/blacklists/inotify-failures	Tue Oct 02 14:31:25 2012 -0700
@@ -10,7 +10,7 @@
 test-strict
 
 # --inotify activates de facto the inotify extension. It does not play well
-# with inotify-specific tests, which activate/desactivate inotify at will:
+# with inotify-specific tests, which activate/deactivate inotify at will:
 test-inotify
 test-inotify-debuginotify
 test-inotify-dirty-dirstate
--- a/tests/hghave.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/hghave.py	Tue Oct 02 14:31:25 2012 -0700
@@ -118,6 +118,9 @@
     except OSError:
         return False
 
+def has_killdaemons():
+    return True
+
 def has_cacheable_fs():
     from mercurial import util
 
@@ -286,6 +289,7 @@
     "hardlink": (has_hardlink, "hardlinks"),
     "icasefs": (has_icasefs, "case insensitive file system"),
     "inotify": (has_inotify, "inotify extension support"),
+    "killdaemons": (has_killdaemons, 'killdaemons.py support'),
     "lsprof": (has_lsprof, "python lsprof module"),
     "mtn": (has_mtn, "monotone client (>= 1.0)"),
     "outer-repo": (has_outer_repo, "outer repo"),
--- a/tests/killdaemons.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/killdaemons.py	Tue Oct 02 14:31:25 2012 -0700
@@ -1,25 +1,54 @@
 #!/usr/bin/env python
 
-import os, time, errno, signal
+import os, sys, time, errno, signal
 
-# Kill off any leftover daemon processes
-try:
-    fp = open(os.environ['DAEMON_PIDS'])
-    for line in fp:
-        try:
-            pid = int(line)
-        except ValueError:
-            continue
+if os.name =='nt':
+    import ctypes
+    def kill(pid, logfn, tryhard=True):
+        logfn('# Killing daemon process %d' % pid)
+        PROCESS_TERMINATE = 1
+        handle = ctypes.windll.kernel32.OpenProcess(
+                PROCESS_TERMINATE, False, pid)
+        ctypes.windll.kernel32.TerminateProcess(handle, -1)
+        ctypes.windll.kernel32.CloseHandle(handle)
+else:
+    def kill(pid, logfn, tryhard=True):
         try:
             os.kill(pid, 0)
+            logfn('# Killing daemon process %d' % pid)
             os.kill(pid, signal.SIGTERM)
-            for i in range(10):
-                time.sleep(0.05)
+            if tryhard:
+                for i in range(10):
+                    time.sleep(0.05)
+                    os.kill(pid, 0)
+            else:
+                time.sleep(0.1)
                 os.kill(pid, 0)
+            logfn('# Daemon process %d is stuck - really killing it' % pid)
             os.kill(pid, signal.SIGKILL)
         except OSError, err:
             if err.errno != errno.ESRCH:
                 raise
-    fp.close()
-except IOError:
-    pass
+
+def killdaemons(pidfile, tryhard=True, remove=False, logfn=None):
+    if not logfn:
+        logfn = lambda s: s
+    # Kill off any leftover daemon processes
+    try:
+        fp = open(pidfile)
+        for line in fp:
+            try:
+                pid = int(line)
+            except ValueError:
+                continue
+            kill(pid, logfn, tryhard)
+        fp.close()
+        if remove:
+            os.unlink(pidfile)
+    except IOError:
+        pass
+
+if __name__ == '__main__':
+    path, = sys.argv[1:]
+    killdaemons(path)
+
--- a/tests/run-tests.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/run-tests.py	Tue Oct 02 14:31:25 2012 -0700
@@ -54,6 +54,7 @@
 import time
 import re
 import threading
+import killdaemons as killmod
 
 processlock = threading.Lock()
 
@@ -348,29 +349,8 @@
         pass
 
 def killdaemons():
-    # Kill off any leftover daemon processes
-    try:
-        fp = open(DAEMON_PIDS)
-        for line in fp:
-            try:
-                pid = int(line)
-            except ValueError:
-                continue
-            try:
-                os.kill(pid, 0)
-                vlog('# Killing daemon process %d' % pid)
-                os.kill(pid, signal.SIGTERM)
-                time.sleep(0.1)
-                os.kill(pid, 0)
-                vlog('# Daemon process %d is stuck - really killing it' % pid)
-                os.kill(pid, signal.SIGKILL)
-            except OSError, err:
-                if err.errno != errno.ESRCH:
-                    raise
-        fp.close()
-        os.unlink(DAEMON_PIDS)
-    except IOError:
-        pass
+    return killmod.killdaemons(DAEMON_PIDS, tryhard=False, remove=True,
+                               logfn=vlog)
 
 def cleanup(options):
     if not options.keep_tmpdir:
@@ -1231,6 +1211,10 @@
             # can't remove on solaris
             os.environ[k] = ''
             del os.environ[k]
+    if 'HG' in os.environ:
+        # can't remove on solaris
+        os.environ['HG'] = ''
+        del os.environ['HG']
 
     global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
     TESTDIR = os.environ["TESTDIR"] = os.getcwd()
--- a/tests/test-annotate.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-annotate.t	Tue Oct 02 14:31:25 2012 -0700
@@ -279,10 +279,10 @@
   > EOF
   $ hg ci -Am "adda"
   adding a
-  $ cat > a <<EOF
+  $ sed 's/EOL$//g' > a <<EOF
   > a  a
   > 
-  >  
+  >  EOL
   > b  b
   > EOF
   $ hg ci -m "changea"
--- a/tests/test-archive.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-archive.t	Tue Oct 02 14:31:25 2012 -0700
@@ -24,7 +24,7 @@
   >     echo % $3 and $4 disallowed should both give 403
   >     "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.$3" | head -n 1
   >     "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.$4" | head -n 1
-  >     "$TESTDIR/killdaemons.py"
+  >     "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   >     cat errors.log
   >     cp .hg/hgrc-base .hg/hgrc
   > }
@@ -93,7 +93,7 @@
       testing: test-archive-2c0277f05ed4/foo   OK
   No errors detected in compressed data of archive.zip.
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 
   $ hg archive -t tar test.tar
   $ tar tf test.tar
--- a/tests/test-basic.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-basic.t	Tue Oct 02 14:31:25 2012 -0700
@@ -33,7 +33,7 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg identify -n
   0
- 
+
 
 Poke around at hashes:
 
--- a/tests/test-bookmarks-pushpull.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-bookmarks-pushpull.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,5 +1,16 @@
   $ "$TESTDIR/hghave" serve || exit 80
 
+  $ cat << EOF >> $HGRCPATH
+  > [phases]
+  > publish=False
+  > [extensions]
+  > EOF
+  $ cat > obs.py << EOF
+  > import mercurial.obsolete
+  > mercurial.obsolete._enabled = True
+  > EOF
+  $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
+
 initialize
 
   $ hg init a
@@ -40,6 +51,7 @@
   bookmarks	
   phases	
   namespaces	
+  obsolete	
   $ hg debugpushkey ../a bookmarks
   Y	4e3505fd95835d721066b76e75dbb8cc554d7f77
   X	4e3505fd95835d721066b76e75dbb8cc554d7f77
@@ -198,6 +210,41 @@
      Y                         3:f6fc62dde3c0
      Z                         1:0d2164f0ce0d
 
+
+Unrelated marker does not alter the decision
+
+  $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+  $ hg push http://localhost:$HGPORT2/
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  abort: push creates new remote head 4efff6d98829!
+  (did you forget to merge? use push -f to force)
+  [255]
+  $ hg -R ../a book
+   * X                         1:0d2164f0ce0d
+     Y                         3:f6fc62dde3c0
+     Z                         1:0d2164f0ce0d
+
+Update to a successor works
+
+  $ hg id --debug -r 3
+  f6fc62dde3c0771e29704af56ba4d8af77abcc2f
+  $ hg id --debug -r 4
+  4efff6d98829d9c824c621afd6e3f01865f5439f tip Y
+  $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f 4efff6d98829d9c824c621afd6e3f01865f5439f
+  $ hg push http://localhost:$HGPORT2/
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files (+1 heads)
+  updating bookmark Y
+  $ hg -R ../a book
+   * X                         1:0d2164f0ce0d
+     Y                         4:4efff6d98829
+     Z                         1:0d2164f0ce0d
+
 hgweb
 
   $ cat <<EOF > .hg/hgrc
@@ -210,10 +257,11 @@
   $ cat ../hg.pid >> $DAEMON_PIDS
   $ cd ../a
 
-  $ hg debugpushkey http://localhost:$HGPORT/ namespaces 
+  $ hg debugpushkey http://localhost:$HGPORT/ namespaces
   bookmarks	
   phases	
   namespaces	
+  obsolete	
   $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
   Y	4efff6d98829d9c824c621afd6e3f01865f5439f
   foobar	9b140be1080824d768c5a4691a564088eede71f9
@@ -251,16 +299,16 @@
   adding changesets
   adding manifests
   adding file changes
-  added 5 changesets with 5 changes to 3 files (+3 heads)
+  added 4 changesets with 4 changes to 3 files (+2 heads)
   updating to branch default
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg -R cloned-bookmarks bookmarks
      X                         1:9b140be10808
-     Y                         4:4efff6d98829
+     Y                         3:4efff6d98829
      Z                         2:0d2164f0ce0d
      foo                       -1:000000000000
      foobar                    1:9b140be10808
- 
+
   $ cd ..
 
 Pushing a bookmark should only push the changes required by that
@@ -270,7 +318,7 @@
   adding changesets
   adding manifests
   adding file changes
-  added 5 changesets with 5 changes to 3 files (+3 heads)
+  added 4 changesets with 4 changes to 3 files (+2 heads)
   updating to branch default
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd addmarks
@@ -286,7 +334,7 @@
   $ hg book -r tip add-bar
 Note: this push *must* push only a single changeset, as that's the point
 of this test.
-  $ hg push -B add-foo
+  $ hg push -B add-foo --traceback
   pushing to http://localhost:$HGPORT/
   searching for changes
   remote: adding changesets
--- a/tests/test-bookmarks-strip.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-bookmarks-strip.t	Tue Oct 02 14:31:25 2012 -0700
@@ -34,7 +34,7 @@
 
   $ hg book test2
 
-update to -2 (inactives the active bookmark)
+update to -2 (deactivates the active bookmark)
 
   $ hg update -r -2
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-bookmarks.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-bookmarks.t	Tue Oct 02 14:31:25 2012 -0700
@@ -154,6 +154,19 @@
    * Y                         2:db815d6d32e6
      Z                         0:f7b1eb17ad24
 
+bookmarks from a revset
+  $ hg bookmark -r '.^1' REVSET
+  $ hg bookmark -r ':tip' TIP
+  $ hg bookmarks
+     REVSET                    0:f7b1eb17ad24
+   * TIP                       2:db815d6d32e6
+     X2                        1:925d80f479bb
+     Y                         2:db815d6d32e6
+     Z                         0:f7b1eb17ad24
+
+  $ hg bookmark -d REVSET
+  $ hg bookmark -d TIP
+
 rename without new name
 
   $ hg bookmark -m Y
--- a/tests/test-check-code.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-check-code.t	Tue Oct 02 14:31:25 2012 -0700
@@ -43,6 +43,19 @@
   >             pass
   >     finally:
   >         pass
+  > 
+  > # yield inside a try/finally block is not allowed in Python 2.4
+  >     try:
+  >         pass
+  >         yield 1
+  >     finally:
+  >         pass
+  >     try:
+  >         yield
+  >         pass
+  >     finally:
+  >         pass
+  > 
   > EOF
   $ cat > classstyle.py <<EOF
   > class newstyle_class(object):
@@ -83,7 +96,13 @@
    any/all/format not available in Python 2.4
   ./non-py24.py:11:
    >     try:
-   no try/except/finally in Py2.4
+   no try/except/finally in Python 2.4
+  ./non-py24.py:28:
+   >     try:
+   no yield inside try/finally in Python 2.4
+  ./non-py24.py:33:
+   >     try:
+   no yield inside try/finally in Python 2.4
   ./classstyle.py:4:
    > class oldstyle_class:
    old-style class, use class foo(object)
--- a/tests/test-clone.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-clone.t	Tue Oct 02 14:31:25 2012 -0700
@@ -533,7 +533,7 @@
 
 destination directory not empty
 
-  $ mkdir a 
+  $ mkdir a
   $ echo stuff > a/a
   $ hg clone q a
   abort: destination 'a' is not empty
@@ -558,7 +558,7 @@
   $ test -d d/.hg
   [1]
 
-reenable perm to allow deletion
+re-enable perm to allow deletion
 
   $ chmod +rx c/.hg/store/data
 
--- a/tests/test-command-template.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-command-template.t	Tue Oct 02 14:31:25 2012 -0700
@@ -592,7 +592,8 @@
 
   $ for key in author branch branches date desc file_adds file_dels file_mods \
   >         file_copies file_copies_switch files \
-  >         manifest node parents rev tags diffstat extras; do
+  >         manifest node parents rev tags diffstat extras \
+  >         p1rev p2rev p1node p2node; do
   >     for mode in '' --verbose --debug; do
   >         hg log $mode --template "$key$mode: {$key}\n"
   >     done
@@ -1095,7 +1096,114 @@
   extras--debug: branch=default
   extras--debug: branch=default
   extras--debug: branch=default
-
+  p1rev: 7
+  p1rev: -1
+  p1rev: 5
+  p1rev: 3
+  p1rev: 3
+  p1rev: 2
+  p1rev: 1
+  p1rev: 0
+  p1rev: -1
+  p1rev--verbose: 7
+  p1rev--verbose: -1
+  p1rev--verbose: 5
+  p1rev--verbose: 3
+  p1rev--verbose: 3
+  p1rev--verbose: 2
+  p1rev--verbose: 1
+  p1rev--verbose: 0
+  p1rev--verbose: -1
+  p1rev--debug: 7
+  p1rev--debug: -1
+  p1rev--debug: 5
+  p1rev--debug: 3
+  p1rev--debug: 3
+  p1rev--debug: 2
+  p1rev--debug: 1
+  p1rev--debug: 0
+  p1rev--debug: -1
+  p2rev: -1
+  p2rev: -1
+  p2rev: 4
+  p2rev: -1
+  p2rev: -1
+  p2rev: -1
+  p2rev: -1
+  p2rev: -1
+  p2rev: -1
+  p2rev--verbose: -1
+  p2rev--verbose: -1
+  p2rev--verbose: 4
+  p2rev--verbose: -1
+  p2rev--verbose: -1
+  p2rev--verbose: -1
+  p2rev--verbose: -1
+  p2rev--verbose: -1
+  p2rev--verbose: -1
+  p2rev--debug: -1
+  p2rev--debug: -1
+  p2rev--debug: 4
+  p2rev--debug: -1
+  p2rev--debug: -1
+  p2rev--debug: -1
+  p2rev--debug: -1
+  p2rev--debug: -1
+  p2rev--debug: -1
+  p1node: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
+  p1node: 0000000000000000000000000000000000000000
+  p1node: 13207e5a10d9fd28ec424934298e176197f2c67f
+  p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
+  p1node: 10e46f2dcbf4823578cf180f33ecf0b957964c47
+  p1node: 97054abb4ab824450e9164180baf491ae0078465
+  p1node: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
+  p1node: 1e4e1b8f71e05681d422154f5421e385fec3454f
+  p1node: 0000000000000000000000000000000000000000
+  p1node--verbose: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
+  p1node--verbose: 0000000000000000000000000000000000000000
+  p1node--verbose: 13207e5a10d9fd28ec424934298e176197f2c67f
+  p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
+  p1node--verbose: 10e46f2dcbf4823578cf180f33ecf0b957964c47
+  p1node--verbose: 97054abb4ab824450e9164180baf491ae0078465
+  p1node--verbose: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
+  p1node--verbose: 1e4e1b8f71e05681d422154f5421e385fec3454f
+  p1node--verbose: 0000000000000000000000000000000000000000
+  p1node--debug: 29114dbae42b9f078cf2714dbe3a86bba8ec7453
+  p1node--debug: 0000000000000000000000000000000000000000
+  p1node--debug: 13207e5a10d9fd28ec424934298e176197f2c67f
+  p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
+  p1node--debug: 10e46f2dcbf4823578cf180f33ecf0b957964c47
+  p1node--debug: 97054abb4ab824450e9164180baf491ae0078465
+  p1node--debug: b608e9d1a3f0273ccf70fb85fd6866b3482bf965
+  p1node--debug: 1e4e1b8f71e05681d422154f5421e385fec3454f
+  p1node--debug: 0000000000000000000000000000000000000000
+  p2node: 0000000000000000000000000000000000000000
+  p2node: 0000000000000000000000000000000000000000
+  p2node: bbe44766e73d5f11ed2177f1838de10c53ef3e74
+  p2node: 0000000000000000000000000000000000000000
+  p2node: 0000000000000000000000000000000000000000
+  p2node: 0000000000000000000000000000000000000000
+  p2node: 0000000000000000000000000000000000000000
+  p2node: 0000000000000000000000000000000000000000
+  p2node: 0000000000000000000000000000000000000000
+  p2node--verbose: 0000000000000000000000000000000000000000
+  p2node--verbose: 0000000000000000000000000000000000000000
+  p2node--verbose: bbe44766e73d5f11ed2177f1838de10c53ef3e74
+  p2node--verbose: 0000000000000000000000000000000000000000
+  p2node--verbose: 0000000000000000000000000000000000000000
+  p2node--verbose: 0000000000000000000000000000000000000000
+  p2node--verbose: 0000000000000000000000000000000000000000
+  p2node--verbose: 0000000000000000000000000000000000000000
+  p2node--verbose: 0000000000000000000000000000000000000000
+  p2node--debug: 0000000000000000000000000000000000000000
+  p2node--debug: 0000000000000000000000000000000000000000
+  p2node--debug: bbe44766e73d5f11ed2177f1838de10c53ef3e74
+  p2node--debug: 0000000000000000000000000000000000000000
+  p2node--debug: 0000000000000000000000000000000000000000
+  p2node--debug: 0000000000000000000000000000000000000000
+  p2node--debug: 0000000000000000000000000000000000000000
+  p2node--debug: 0000000000000000000000000000000000000000
+  p2node--debug: 0000000000000000000000000000000000000000
 
 Filters work:
 
@@ -1245,7 +1353,7 @@
   $ hg add a
   $ hg commit -m future -d "`cat a`"
 
-  $ hg log -l1 --template '{date|age}\n' 
+  $ hg log -l1 --template '{date|age}\n'
   7 years from now
 
 Error on syntax:
@@ -1255,6 +1363,30 @@
   abort: t:3: unmatched quotes
   [255]
 
+Behind the scenes, this will throw TypeError
+
+  $ hg log -l 3 --template '{date|obfuscate}\n'
+  abort: template filter 'obfuscate' is not compatible with keyword 'date'
+  [255]
+
+Behind the scenes, this will throw a ValueError
+
+  $ hg log -l 3 --template 'line: {desc|shortdate}\n'
+  abort: template filter 'shortdate' is not compatible with keyword 'desc'
+  [255]
+
+Behind the scenes, this will throw AttributeError
+
+  $ hg log -l 3 --template 'line: {date|escape}\n'
+  abort: template filter 'escape' is not compatible with keyword 'date'
+  [255]
+
+Behind the scenes, this will throw ValueError
+
+  $ hg tip --template '{author|email|date}\n'
+  abort: template filter 'datefilter' is not compatible with keyword 'author'
+  [255]
+
   $ cd ..
 
 
@@ -1388,3 +1520,8 @@
   10,test
   branch: test
 
+Test new-style inline templating:
+
+  $ hg log -R latesttag -r tip --template 'modified files: {file_mods % " {file}\n"}\n'
+  modified files:  .hgtags
+  
--- a/tests/test-commit-amend.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-commit-amend.t	Tue Oct 02 14:31:25 2012 -0700
@@ -167,6 +167,8 @@
   $ echo a >> a
   $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
   amending changeset ffb49186f961
+  a
+  copying changeset a4f8a65b7c6a to ad120869acf0
   another precious commit message
   
   
@@ -177,9 +179,7 @@
   HG: branch 'default'
   HG: changed a
   a
-  copying changeset 27f3aacd3011 to ad120869acf0
-  a
-  stripping intermediate changeset 27f3aacd3011
+  stripping intermediate changeset a4f8a65b7c6a
   stripping amended changeset ffb49186f961
   2 changesets found
   saved backup bundle to $TESTTMP/.hg/strip-backup/ffb49186f961-amend-backup.hg (glob)
@@ -370,3 +370,75 @@
   11: draft
   13: secret
 
+Test amend with obsolete
+---------------------------
+
+Enable obsolete
+
+  $ cat > ${TESTTMP}/obs.py << EOF
+  > import mercurial.obsolete
+  > mercurial.obsolete._enabled = True
+  > EOF
+  $ echo '[extensions]' >> $HGRCPATH
+  $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
+
+
+Amend with no files changes
+
+  $ hg id -n
+  13
+  $ hg ci --amend -m 'babar'
+  $ hg id -n
+  14
+  $ hg log -Gl 3 --style=compact
+  @  14[tip]:11   43df5a5434ad   1970-01-01 00:00 +0000   test
+  |    babar
+  |
+  | o  12:0   2647734878ef   1970-01-01 00:00 +0000   test
+  | |    fork
+  | |
+  o |  11   7e09f708a0e9   1970-01-01 00:00 +0000   test
+  | |    a''
+  | |
+  $ hg log -Gl 4 --hidden --style=compact
+  @  14[tip]:11   43df5a5434ad   1970-01-01 00:00 +0000   test
+  |    babar
+  |
+  | x  13:11   175fafee6f44   1970-01-01 00:00 +0000   test
+  |/     amend for phase
+  |
+  | o  12:0   2647734878ef   1970-01-01 00:00 +0000   test
+  | |    fork
+  | |
+  o |  11   7e09f708a0e9   1970-01-01 00:00 +0000   test
+  | |    a''
+  | |
+
+Amend with files changes
+
+(note: the extra commit over 15 is a temporary junk I would be happy to get
+ride of)
+
+  $ echo 'babar' >> a
+  $ hg commit --amend
+  $ hg log -Gl 6 --hidden --style=compact
+  @  16[tip]:11   31e0a4a1b04a   1970-01-01 00:00 +0000   test
+  |    babar
+  |
+  | x  15   053c696ada75   1970-01-01 00:00 +0000   test
+  | |    temporary amend commit for 43df5a5434ad
+  | |
+  | x  14:11   43df5a5434ad   1970-01-01 00:00 +0000   test
+  |/     babar
+  |
+  | x  13:11   175fafee6f44   1970-01-01 00:00 +0000   test
+  |/     amend for phase
+  |
+  | o  12:0   2647734878ef   1970-01-01 00:00 +0000   test
+  | |    fork
+  | |
+  o |  11   7e09f708a0e9   1970-01-01 00:00 +0000   test
+  | |    a''
+  | |
+
+
--- a/tests/test-convert-cvsnt-mergepoints.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-convert-cvsnt-mergepoints.t	Tue Oct 02 14:31:25 2012 -0700
@@ -43,7 +43,7 @@
   $ cvscall -Q add foo
   $ cd foo
   $ echo foo > foo.txt
-  $ cvscall -Q add foo.txt 
+  $ cvscall -Q add foo.txt
   $ cvsci -m "add foo.txt" foo.txt
   $ cd ../..
   $ rm -rf cvsworktmp
--- a/tests/test-convert-darcs.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-convert-darcs.t	Tue Oct 02 14:31:25 2012 -0700
@@ -5,25 +5,6 @@
   $ echo 'graphlog =' >> $HGRCPATH
   $ DARCS_EMAIL='test@example.org'; export DARCS_EMAIL
 
-skip if we can't import elementtree
-
-  $ mkdir dummy
-  $ mkdir dummy/_darcs
-  $ if hg convert dummy 2>&1 | grep ElementTree > /dev/null; then
-  >     echo 'skipped: missing feature: elementtree module'
-  >     exit 80
-  > fi
-
-#if no-outer-repo
-
-try converting darcs1 repository
-
-  $ hg clone -q "$TESTDIR/bundles/darcs1.hg" darcs
-  $ hg convert -s darcs darcs/darcs1 2>&1 | grep darcs-1.0
-  darcs-1.0 repository format is unsupported, please upgrade
-
-#endif
-
 initialize darcs repo
 
   $ mkdir darcs-repo
@@ -44,6 +25,13 @@
   Finished recording patch 'p1.1'
   $ cd ..
 
+skip if we can't import elementtree
+
+  $ if hg convert darcs-repo darcs-dummy 2>&1 | grep ElementTree > /dev/null; then
+  >     echo 'skipped: missing feature: elementtree module'
+  >     exit 80
+  > fi
+
 update source
 
   $ cd darcs-repo
@@ -108,3 +96,13 @@
   1e88685f5ddec574a34c70af492f95b6debc8741 644   b
   37406831adc447ec2385014019599dfec953c806 644   dir2/d
   b783a337463792a5c7d548ad85a7d3253c16ba8c 644   ff
+
+#if no-outer-repo
+
+try converting darcs1 repository
+
+  $ hg clone -q "$TESTDIR/bundles/darcs1.hg" darcs
+  $ hg convert -s darcs darcs/darcs1 2>&1 | grep darcs-1.0
+  darcs-1.0 repository format is unsupported, please upgrade
+
+#endif
--- a/tests/test-convert-mtn-rename-directory.out	Mon Oct 01 23:11:28 2012 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-% tedious monotone keys configuration
-% create monotone repository
-mtn: adding dir1 to workspace manifest
-mtn: adding dir1/subdir1 to workspace manifest
-mtn: adding dir1/subdir1/file1 to workspace manifest
-mtn: beginning commit on branch 'com.selenic.test'
-mtn: committed revision 5ed13ff5582d8d1e319f079b694a37d2b45edfc8
-% rename directory
-mtn: skipping dir1, already accounted for in workspace
-mtn: renaming dir1/subdir1 to dir1/subdir2 in workspace manifest
-mtn: beginning commit on branch 'com.selenic.test'
-mtn: committed revision 985204142a822b22ee86b509d61f3c5ab6857d2b
-% convert
-assuming destination repo.mtn-hg
-initializing destination repo.mtn-hg repository
-scanning source...
-sorting...
-converting...
-1 initialize
-0 rename
-1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-% manifest
-dir1/subdir2/file1
--- a/tests/test-convert-mtn.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-convert-mtn.t	Tue Oct 02 14:31:25 2012 -0700
@@ -2,9 +2,18 @@
   $ "$TESTDIR/hghave" mtn || exit 80
 
 Monotone directory is called .monotone on *nix and monotone
-on Windows. Having a variable here ease test patching.
+on Windows.
+
+#if windows
+
+  $ mtndir=monotone
+
+#else
 
   $ mtndir=.monotone
+
+#endif
+
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "convert=" >> $HGRCPATH
   $ echo 'graphlog =' >> $HGRCPATH
@@ -210,7 +219,9 @@
 
 test large file support (> 32kB)
 
-  $ python -c 'for x in range(10000): print x' > large-file
+  >>> fp = file('large-file', 'wb')
+  >>> for x in xrange(10000): fp.write('%d\n' % x)
+  >>> fp.close()
   $ $TESTDIR/md5sum.py large-file
   5d6de8a95c3b6bf9e0ffb808ba5299c1  large-file
   $ mtn add large-file
--- a/tests/test-convert-svn-branches.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-convert-svn-branches.t	Tue Oct 02 14:31:25 2012 -0700
@@ -3,7 +3,7 @@
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
-  > convert = 
+  > convert =
   > graphlog =
   > EOF
 
@@ -14,7 +14,7 @@
 
   $ cat > branchmap <<EOF
   > old3 newbranch
-  >     
+  > 
   > 
   > EOF
   $ hg convert --branchmap=branchmap --datesort -r 10 svn-repo A-hg
@@ -95,9 +95,5 @@
 
 Test hg failing to call itself
 
-  $ HG=foobar hg convert svn-repo B-hg
-  * (glob)
-  initializing destination B-hg repository
+  $ HG=foobar hg convert svn-repo B-hg 2>&1 | grep abort
   abort: Mercurial failed to run itself, check hg executable is in PATH
-  [255]
-
--- a/tests/test-convert-svn-encoding.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-convert-svn-encoding.t	Tue Oct 02 14:31:25 2012 -0700
@@ -3,7 +3,7 @@
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
-  > convert = 
+  > convert =
   > graphlog =
   > EOF
 
--- a/tests/test-convert-svn-move.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-convert-svn-move.t	Tue Oct 02 14:31:25 2012 -0700
@@ -3,7 +3,7 @@
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
-  > convert = 
+  > convert =
   > graphlog =
   > EOF
 
@@ -155,7 +155,7 @@
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
-  > progress = 
+  > progress =
   > [progress]
   > assume-tty = 1
   > delay = 0
--- a/tests/test-convert-svn-sink.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-convert-svn-sink.t	Tue Oct 02 14:31:25 2012 -0700
@@ -16,7 +16,7 @@
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
-  > convert = 
+  > convert =
   > graphlog =
   > EOF
 
--- a/tests/test-convert-svn-source.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-convert-svn-source.t	Tue Oct 02 14:31:25 2012 -0700
@@ -3,7 +3,7 @@
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
-  > convert = 
+  > convert =
   > graphlog =
   > [convert]
   > svn.trunk = mytrunk
--- a/tests/test-convert-svn-startrev.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-convert-svn-startrev.t	Tue Oct 02 14:31:25 2012 -0700
@@ -3,7 +3,7 @@
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
-  > convert = 
+  > convert =
   > graphlog =
   > EOF
   $ convert()
--- a/tests/test-convert-svn-tags.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-convert-svn-tags.t	Tue Oct 02 14:31:25 2012 -0700
@@ -3,7 +3,7 @@
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
-  > convert = 
+  > convert =
   > graphlog =
   > EOF
 
--- a/tests/test-convert.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-convert.t	Tue Oct 02 14:31:25 2012 -0700
@@ -60,8 +60,8 @@
   
       The authormap is a simple text file that maps each source commit author to
       a destination commit author. It is handy for source SCMs that use unix
-      logins to identify authors (eg: CVS). One line per author mapping and the
-      line format is:
+      logins to identify authors (e.g.: CVS). One line per author mapping and
+      the line format is:
   
         source author = destination author
   
@@ -399,7 +399,7 @@
 
 test revset converted() lookup
 
-  $ hg --config convert.hg.saverev=True convert a c  
+  $ hg --config convert.hg.saverev=True convert a c
   initializing destination c repository
   scanning source...
   sorting...
--- a/tests/test-copy.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-copy.t	Tue Oct 02 14:31:25 2012 -0700
@@ -139,7 +139,7 @@
   foo3 does not exist!
   $ hg up -qC .
 
-copy --after to a nonexistant target filename
+copy --after to a nonexistent target filename
   $ hg cp -A foo dummy
   foo: not recording copy - dummy does not exist
 
--- a/tests/test-debugindexdot.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-debugindexdot.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-Just exercize debugindexdot
+Just exercise debugindexdot
 Create a short file history including a merge.
   $ hg init t
   $ cd t
--- a/tests/test-diff-issue2761.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-diff-issue2761.t	Tue Oct 02 14:31:25 2012 -0700
@@ -15,9 +15,9 @@
 
   $ hg up -C
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ touch doesnt-exist-in-1
+  $ touch does-not-exist-in-1
   $ hg add
-  adding doesnt-exist-in-1
+  adding does-not-exist-in-1
   $ hg ci -m third
-  $ rm doesnt-exist-in-1
+  $ rm does-not-exist-in-1
   $ hg diff -r 1
--- a/tests/test-diffstat.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-diffstat.t	Tue Oct 02 14:31:25 2012 -0700
@@ -68,5 +68,5 @@
   $ hg diff --stat --git
    file with spaces |  Bin 
    1 files changed, 0 insertions(+), 0 deletions(-)
-	
+
   $ cd ..
--- a/tests/test-encoding.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-encoding.t	Tue Oct 02 14:31:25 2012 -0700
@@ -252,5 +252,5 @@
 
   $ HGENCODING=latin-1 hg up `cat latin-1-tag`
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
- 
+
   $ cd ..
--- a/tests/test-export.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-export.t	Tue Oct 02 14:31:25 2012 -0700
@@ -124,7 +124,7 @@
 Checking if only alphanumeric characters are used in the file name (%m option):
 
   $ echo "line" >> foo
-  $ hg commit -m " !\"#$%&(,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\`abcdefghijklmnopqrstuvwxyz{|}~" 
+  $ hg commit -m " !\"#$%&(,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\`abcdefghijklmnopqrstuvwxyz{|}~"
   $ hg export -v -o %m.patch tip
   exporting patch:
   ____________0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______abcdefghijklmnopqrstuvwxyz____.patch
@@ -144,4 +144,28 @@
   abort: export requires at least one changeset
   [255]
 
+Check for color output
+  $ echo "[color]" >> $HGRCPATH
+  $ echo "mode = ansi" >> $HGRCPATH
+  $ echo "[extensions]" >> $HGRCPATH
+  $ echo "color=" >> $HGRCPATH
+
+  $ hg export --color always --nodates tip
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  # Node ID * (glob)
+  # Parent * (glob)
+   !"#$%&(,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+  
+  \x1b[0;1mdiff -r f3acbafac161 -r 197ecd81a57f foo\x1b[0m (esc)
+  \x1b[0;31;1m--- a/foo\x1b[0m (esc)
+  \x1b[0;32;1m+++ b/foo\x1b[0m (esc)
+  \x1b[0;35m@@ -10,3 +10,4 @@\x1b[0m (esc)
+   foo-9
+   foo-10
+   foo-11
+  \x1b[0;32m+line\x1b[0m (esc)
+
+
   $ cd ..
--- a/tests/test-filelog.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-filelog.py	Tue Oct 02 14:31:25 2012 -0700
@@ -12,7 +12,7 @@
 
 def addrev(text, renamed=False):
     if renamed:
-        # data doesnt matter. Just make sure filelog.renamed() returns True
+        # data doesn't matter. Just make sure filelog.renamed() returns True
         meta = dict(copyrev=hex(nullid), copy='bar')
     else:
         meta = {}
--- a/tests/test-glog.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-glog.t	Tue Oct 02 14:31:25 2012 -0700
@@ -83,7 +83,7 @@
 
   $ cat > printrevset.py <<EOF
   > from mercurial import extensions, revset, commands, cmdutil
-  >  
+  > 
   > def uisetup(ui):
   >     def printrevset(orig, ui, repo, *pats, **opts):
   >         if opts.get('print_revset'):
@@ -2044,20 +2044,40 @@
 Test --hidden
 
   $ cat > $HGTMP/testhidden.py << EOF
+  > from mercurial import util
   > def reposetup(ui, repo):
   >     for line in repo.opener('hidden'):
   >         ctx = repo[line.strip()]
   >         repo.hiddenrevs.add(ctx.rev())
+  >     if repo.revs('children(%ld) - %ld',  repo.hiddenrevs,  repo.hiddenrevs):
+  >       raise util.Abort('hidden revision with children!')
   > EOF
   $ echo '[extensions]' >> .hg/hgrc
   $ echo "hidden=$HGTMP/testhidden.py" >> .hg/hgrc
-  $ hg id --debug -i -r 0 > .hg/hidden
+  $ hg id --debug -i -r 8 > .hg/hidden
   $ testlog
   []
   []
   $ testlog --hidden
   []
   []
+  $ hg glog --template '{rev} {desc}\n'
+  o  7 Added tag foo-bar for changeset fc281d8ff18d
+  |
+  o    6 merge 5 and 4
+  |\
+  | o  5 add another e
+  | |
+  o |  4 mv dir/b e
+  |/
+  @  3 mv a b; add d
+  |
+  o  2 mv b dir/b
+  |
+  o  1 copy a b
+  |
+  o  0 add a
+  
 
 A template without trailing newline should do something sane
 
@@ -2066,6 +2086,8 @@
   |
   o  1 copy a b
   |
+  o  0 add a
+  
 
 Extra newlines must be preserved
 
@@ -2076,6 +2098,9 @@
   o
   |  1 copy a b
   |
+  o
+     0 add a
+  
 
 The almost-empty template should do something sane too ...
 
@@ -2084,5 +2109,7 @@
   |
   o
   |
+  o
+  
 
   $ cd ..
--- a/tests/test-help.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-help.t	Tue Oct 02 14:31:25 2012 -0700
@@ -802,3 +802,7 @@
   
    qclone clone main and patch repository at same time
 
+Test usage of section marks in help documents
+
+  $ cd "$TESTDIR"/../doc
+  $ python check-seclevel.py
--- a/tests/test-hgweb-commands.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-hgweb-commands.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1260,7 +1260,7 @@
 
 Stop and restart with HGENCODING=cp932 and preferuncompressed
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ HGENCODING=cp932 hg serve --config server.preferuncompressed=True -n test \
   >     -p $HGPORT -d --pid-file=hg.pid -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
@@ -1288,7 +1288,7 @@
 ERRORS ENCOUNTERED
 
   $ cat errors.log
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 
   $ cd ..
 
--- a/tests/test-hgweb-diffs.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-hgweb-diffs.t	Tue Oct 02 14:31:25 2012 -0700
@@ -291,7 +291,7 @@
 
 set up hgweb with git diffs
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ hg serve --config 'diff.git=1' -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
@@ -936,7 +936,7 @@
 
 raw revision with diff block numbers
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ cat <<EOF > .hg/hgrc
   > [web]
   > templates = rawdiff
@@ -973,7 +973,7 @@
   @@ -0,0 +1,1 @@
   +b
   
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ rm .hg/hgrc rawdiff/map
   $ rmdir rawdiff
   $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
--- a/tests/test-hgweb.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-hgweb.t	Tue Oct 02 14:31:25 2012 -0700
@@ -299,7 +299,7 @@
 
 stop and restart
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
   $ cat hg.pid >> $DAEMON_PIDS
 
--- a/tests/test-hgwebdir.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-hgwebdir.t	Tue Oct 02 14:31:25 2012 -0700
@@ -657,7 +657,7 @@
 
 Test collapse = True
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ cat >> paths.conf <<EOF
   > [web]
   > collapse=true
@@ -723,7 +723,7 @@
 
 Test descend = False
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ cat >> paths.conf <<EOF
   > descend=false
   > EOF
@@ -784,7 +784,7 @@
   $ hg id http://localhost:$HGPORT1/astar
   8580ff50825a
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ cat > paths.conf <<EOF
   > [paths]
   > t/a = $root/a
@@ -812,7 +812,7 @@
 
 Test collapse = True
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ cat >> paths.conf <<EOF
   > [web]
   > collapse=true
@@ -837,7 +837,7 @@
 
 test descend = False
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ cat >> paths.conf <<EOF
   > descend=false
   > EOF
@@ -857,7 +857,7 @@
   /t/a/
   /t/b/
   
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ cat > paths.conf <<EOF
   > [paths]
   > nostore = $root/nostore
@@ -956,7 +956,7 @@
 
   $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 'a/rss-log' | grep '<guid'
       <guid isPermaLink="true">http://hg.example.com:8080/a/rev/8580ff50825a</guid>
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   $ hg serve --config web.baseurl=http://hg.example.com:8080/foo/ -p $HGPORT2 -d \
   >     --pid-file=hg.pid --webdir-conf collections.conf \
   >     -A access-collections-2.log -E error-collections-2.log
--- a/tests/test-highlight.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-highlight.t	Tue Oct 02 14:31:25 2012 -0700
@@ -545,7 +545,7 @@
 errors encountered
 
   $ cat errors.log
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 
 Change the pygments style
 
@@ -579,7 +579,7 @@
   $ hg ci -Ama
   adding eucjp.txt
   $ hgserveget () {
-  >     "$TESTDIR/killdaemons.py"
+  >     "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   >     echo % HGENCODING="$1" hg serve
   >     HGENCODING="$1" hg serve -p $HGPORT -d -n test --pid-file=hg.pid -E errors.log
   >     cat hg.pid >> $DAEMON_PIDS
--- a/tests/test-histedit-fold-non-commute.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-histedit-fold-non-commute.t	Tue Oct 02 14:31:25 2012 -0700
@@ -6,79 +6,96 @@
   > histedit=
   > EOF
 
-  $ EDITED="$TESTTMP/editedhistory"
-  $ cat > $EDITED <<EOF
-  > pick 177f92b77385 c
-  > pick 055a42cdd887 d
-  > fold bfa474341cc9 does not commute with e
-  > pick e860deea161a e
-  > pick 652413bf663e f
-  > EOF
   $ initrepo ()
   > {
   >     hg init $1
   >     cd $1
   >     for x in a b c d e f ; do
+  >         echo $x$x$x$x$x > $x
+  >         hg add $x
+  >     done
+  >     hg ci -m 'Initial commit'
+  >     for x in a b c d e f ; do
   >         echo $x > $x
-  >         hg add $x
   >         hg ci -m $x
   >     done
-  >     echo a >> e
+  >     echo 'I can haz no commute' > e
   >     hg ci -m 'does not commute with e'
   >     cd ..
   > }
 
   $ initrepo r
   $ cd r
+Initial generation of the command files
+
+  $ EDITED="$TESTTMP/editedhistory"
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
+  $ hg log --template 'fold {node|short} {rev} {desc}\n' -r 7 >> $EDITED
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
+  $ cat $EDITED
+  pick 65a9a84f33fd 3 c
+  pick 00f1c5383965 4 d
+  fold 39522b764e3d 7 does not commute with e
+  pick 7b4e2f4b7bcd 5 e
+  pick 500cac37a696 6 f
 
 log before edit
   $ hg log --graph
-  @  changeset:   6:bfa474341cc9
+  @  changeset:   7:39522b764e3d
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     does not commute with e
   |
-  o  changeset:   5:652413bf663e
+  o  changeset:   6:500cac37a696
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   4:e860deea161a
+  o  changeset:   5:7b4e2f4b7bcd
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     e
   |
-  o  changeset:   3:055a42cdd887
+  o  changeset:   4:00f1c5383965
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   2:177f92b77385
+  o  changeset:   3:65a9a84f33fd
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     c
   |
-  o  changeset:   1:d2ae7f538514
+  o  changeset:   2:da6535b52e45
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     b
   |
-  o  changeset:   0:cb9a9f314b8b
+  o  changeset:   1:c1f09da44841
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     a
+  |
+  o  changeset:   0:1715188a53c7
      user:        test
      date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     a
+     summary:     Initial commit
   
 
 edit the history
-  $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  1 out of 1 hunks FAILED -- saving rejects to file e.rej
+  $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merging e
+  warning: conflicts during merge.
+  merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
   abort: Fix up the change and run hg histedit --continue
 
 fix up
-  $ echo a > e
-  $ hg add e
+  $ echo 'I can haz no commute' > e
+  $ hg resolve --mark e
   $ cat > cat.py <<EOF
   > import sys
   > print open(sys.argv[1]).read()
@@ -86,54 +103,74 @@
   > print
   > EOF
   $ HGEDITOR="python cat.py" hg histedit --continue 2>&1 | fixbundle | grep -v '2 files removed'
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   d
   ***
   does not commute with e
   
   
   
+  HG: Enter commit message.  Lines beginning with 'HG:' are removed.
+  HG: Leave message empty to abort commit.
+  HG: --
+  HG: user: test
+  HG: branch 'default'
+  HG: changed d
+  HG: changed e
+  
+  
+  
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  file e already exists
-  1 out of 1 hunks FAILED -- saving rejects to file e.rej
+  merging e
+  warning: conflicts during merge.
+  merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
   abort: Fix up the change and run hg histedit --continue
 
 just continue this time
+  $ hg revert -r 'p1()' e
+  $ hg resolve --mark e
   $ hg histedit --continue 2>&1 | fixbundle
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 log after edit
   $ hg log --graph
-  @  changeset:   4:f768fd60ca34
+  @  changeset:   5:2696a654c663
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   3:671efe372e33
+  o  changeset:   4:ec2c1cf833a8
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   2:177f92b77385
+  o  changeset:   3:65a9a84f33fd
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     c
   |
-  o  changeset:   1:d2ae7f538514
+  o  changeset:   2:da6535b52e45
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     b
   |
-  o  changeset:   0:cb9a9f314b8b
+  o  changeset:   1:c1f09da44841
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     a
+  |
+  o  changeset:   0:1715188a53c7
      user:        test
      date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     a
+     summary:     Initial commit
   
 
 contents of e
   $ hg cat e
-  a
+  I can haz no commute
 
 manifest
   $ hg manifest
--- a/tests/test-histedit-fold.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-histedit-fold.t	Tue Oct 02 14:31:25 2012 -0700
@@ -66,6 +66,7 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
@@ -156,16 +157,21 @@
 
   $ HGEDITOR='python editor.py' hg histedit 1
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  patching file file
-  Hunk #1 FAILED at 2
-  1 out of 1 hunks FAILED -- saving rejects to file file.rej
+  merging file
+  warning: conflicts during merge.
+  merging file incomplete! (edit conflicts, then use 'hg resolve --mark')
   abort: Fix up the change and run hg histedit --continue
   [255]
-There were conflicts, but we'll continue without resolving. This
+There were conflicts, we keep P1 content. This
 should effectively drop the changes from +6.
   $ hg status
+  M file
   ? editor.py
-  ? file.rej
+  ? file.orig
+  $ hg resolve -l
+  U file
+  $ hg revert -r 'p1()' file
+  $ hg resolve --mark file
   $ hg histedit --continue
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/*-backup.hg (glob)
@@ -216,12 +222,19 @@
   > EOF
   $ HGEDITOR="cat $EDITED >" hg histedit 1
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  patching file file
-  Hunk #1 FAILED at 2
-  1 out of 1 hunks FAILED -- saving rejects to file file.rej
+  merging file
+  warning: conflicts during merge.
+  merging file incomplete! (edit conflicts, then use 'hg resolve --mark')
   abort: Fix up the change and run hg histedit --continue
   [255]
-  $ echo 5 >> file
+  $ cat > file << EOF
+  > 1
+  > 2
+  > 3
+  > 4
+  > 5
+  > EOF
+  $ hg resolve --mark file
   $ hg commit -m '+5.2'
   created new head
   $ echo 6 >> file
@@ -232,7 +245,51 @@
   +5.2
   ***
   +6
+  
+  
+  
+  HG: Enter commit message.  Lines beginning with 'HG:' are removed.
+  HG: Leave message empty to abort commit.
+  HG: --
+  HG: user: test
+  HG: branch 'default'
+  HG: changed file
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-backup.hg (glob)
+  $ hg log -G
+  @  changeset:   1:e29e02896e6c
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     +4
+  |
+  o  changeset:   0:0189ba417d34
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     1+2+3
+  
+  $ hg export tip
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  # Node ID e29e02896e6c2b149d2228a0a64b4f3a9a4237f3
+  # Parent  0189ba417d34df9dda55f88b637dcae9917b5964
+  +4
+  ***
+  +5.2
+  ***
+  +6
+  
+  diff -r 0189ba417d34 -r e29e02896e6c file
+  --- a/file	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/file	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,3 +1,6 @@
+   1
+   2
+   3
+  +4
+  +5
+  +6
   $ cd ..
 
--- a/tests/test-histedit-non-commute-abort.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-histedit-non-commute-abort.t	Tue Oct 02 14:31:25 2012 -0700
@@ -73,23 +73,21 @@
 edit the history
   $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  1 out of 1 hunks FAILED -- saving rejects to file e.rej
+  remote changed e which local deleted
+  use (c)hanged version or leave (d)eleted? c
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merging e
+  warning: conflicts during merge.
+  merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
   abort: Fix up the change and run hg histedit --continue
 
-fix up (pre abort)
-  $ echo a > e
-  $ hg add e
-  $ hg histedit --continue 2>&1 | fixbundle
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  file e already exists
-  1 out of 1 hunks FAILED -- saving rejects to file e.rej
-  abort: Fix up the change and run hg histedit --continue
 
 abort the edit
   $ hg histedit --abort 2>&1 | fixbundle
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 log after abort
+  $ hg resolve -l
   $ hg log --graph
   @  changeset:   6:bfa474341cc9
   |  tag:         tip
--- a/tests/test-histedit-non-commute.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-histedit-non-commute.t	Tue Oct 02 14:31:25 2012 -0700
@@ -6,24 +6,20 @@
   > histedit=
   > EOF
 
-  $ EDITED="$TESTTMP/editedhistory"
-  $ cat > $EDITED <<EOF
-  > pick 177f92b77385 c
-  > pick 055a42cdd887 d
-  > pick bfa474341cc9 does not commute with e
-  > pick e860deea161a e
-  > pick 652413bf663e f
-  > EOF
   $ initrepo ()
   > {
   >     hg init $1
   >     cd $1
   >     for x in a b c d e f ; do
+  >         echo $x$x$x$x$x > $x
+  >         hg add $x
+  >     done
+  >     hg ci -m 'Initial commit'
+  >     for x in a b c d e f ; do
   >         echo $x > $x
-  >         hg add $x
   >         hg ci -m $x
   >     done
-  >     echo a >> e
+  >     echo 'I can haz no commute' > e
   >     hg ci -m 'does not commute with e'
   >     cd ..
   > }
@@ -31,49 +27,71 @@
   $ initrepo r1
   $ cd r1
 
+Initial generation of the command files
+
+  $ EDITED="$TESTTMP/editedhistory"
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 7 >> $EDITED
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
+  $ cat $EDITED
+  pick 65a9a84f33fd 3 c
+  pick 00f1c5383965 4 d
+  pick 39522b764e3d 7 does not commute with e
+  pick 7b4e2f4b7bcd 5 e
+  pick 500cac37a696 6 f
+
 log before edit
   $ hg log --graph
-  @  changeset:   6:bfa474341cc9
+  @  changeset:   7:39522b764e3d
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     does not commute with e
   |
-  o  changeset:   5:652413bf663e
+  o  changeset:   6:500cac37a696
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   4:e860deea161a
+  o  changeset:   5:7b4e2f4b7bcd
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     e
   |
-  o  changeset:   3:055a42cdd887
+  o  changeset:   4:00f1c5383965
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   2:177f92b77385
+  o  changeset:   3:65a9a84f33fd
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     c
   |
-  o  changeset:   1:d2ae7f538514
+  o  changeset:   2:da6535b52e45
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     b
   |
-  o  changeset:   0:cb9a9f314b8b
+  o  changeset:   1:c1f09da44841
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     a
+  |
+  o  changeset:   0:1715188a53c7
      user:        test
      date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     a
+     summary:     Initial commit
   
 
 edit the history
-  $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  1 out of 1 hunks FAILED -- saving rejects to file e.rej
+  $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merging e
+  warning: conflicts during merge.
+  merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
   abort: Fix up the change and run hg histedit --continue
 
 abort the edit
@@ -84,95 +102,113 @@
 second edit set
 
   $ hg log --graph
-  @  changeset:   6:bfa474341cc9
+  @  changeset:   7:39522b764e3d
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     does not commute with e
   |
-  o  changeset:   5:652413bf663e
+  o  changeset:   6:500cac37a696
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   4:e860deea161a
+  o  changeset:   5:7b4e2f4b7bcd
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     e
   |
-  o  changeset:   3:055a42cdd887
+  o  changeset:   4:00f1c5383965
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   2:177f92b77385
+  o  changeset:   3:65a9a84f33fd
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     c
   |
-  o  changeset:   1:d2ae7f538514
+  o  changeset:   2:da6535b52e45
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     b
   |
-  o  changeset:   0:cb9a9f314b8b
+  o  changeset:   1:c1f09da44841
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     a
+  |
+  o  changeset:   0:1715188a53c7
      user:        test
      date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     a
+     summary:     Initial commit
   
 
 edit the history
-  $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  1 out of 1 hunks FAILED -- saving rejects to file e.rej
+  $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merging e
+  warning: conflicts during merge.
+  merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
   abort: Fix up the change and run hg histedit --continue
 
 fix up
-  $ echo a > e
-  $ hg add e
+  $ echo 'I can haz no commute' > e
+  $ hg resolve --mark e
   $ hg histedit --continue 2>&1 | fixbundle
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  file e already exists
-  1 out of 1 hunks FAILED -- saving rejects to file e.rej
+  merging e
+  warning: conflicts during merge.
+  merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
   abort: Fix up the change and run hg histedit --continue
 
+This failure is caused by 7b4e2f4b7bcd "e" not rebasing the non commutative
+former children.
+
 just continue this time
+  $ hg revert -r 'p1()' e
+  $ hg resolve --mark e
   $ hg histedit --continue 2>&1 | fixbundle
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 log after edit
   $ hg log --graph
-  @  changeset:   5:9ab84894b459
+  @  changeset:   6:8e082d1a72ea
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   4:1fff3ae8199d
+  o  changeset:   5:13b04d775b81
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     does not commute with e
   |
-  o  changeset:   3:055a42cdd887
+  o  changeset:   4:00f1c5383965
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   2:177f92b77385
+  o  changeset:   3:65a9a84f33fd
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     c
   |
-  o  changeset:   1:d2ae7f538514
+  o  changeset:   2:da6535b52e45
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     b
   |
-  o  changeset:   0:cb9a9f314b8b
+  o  changeset:   1:c1f09da44841
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     a
+  |
+  o  changeset:   0:1715188a53c7
      user:        test
      date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     a
+     summary:     Initial commit
   
 
 start over
@@ -181,64 +217,79 @@
 
   $ initrepo r2
   $ cd r2
-  $ cat > $EDITED <<EOF
-  > pick 177f92b77385 c
-  > pick 055a42cdd887 d
-  > mess bfa474341cc9 does not commute with e
-  > pick e860deea161a e
-  > pick 652413bf663e f
-  > EOF
+  $ rm $EDITED
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
+  $ hg log --template 'mess {node|short} {rev} {desc}\n' -r 7 >> $EDITED
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
+  $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
+  $ cat $EDITED
+  pick 65a9a84f33fd 3 c
+  pick 00f1c5383965 4 d
+  mess 39522b764e3d 7 does not commute with e
+  pick 7b4e2f4b7bcd 5 e
+  pick 500cac37a696 6 f
 
 edit the history, this time with a fold action
-  $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  1 out of 1 hunks FAILED -- saving rejects to file e.rej
+  $ HGEDITOR="cat \"$EDITED\" > " hg histedit 3 2>&1 | fixbundle
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  merging e
+  warning: conflicts during merge.
+  merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
   abort: Fix up the change and run hg histedit --continue
 
-  $ echo a > e
-  $ hg add e
+  $ echo 'I can haz no commute' > e
+  $ hg resolve --mark e
   $ HGEDITOR="cat \"$EDITED\" > " hg histedit --continue 2>&1 | fixbundle
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  file e already exists
-  1 out of 1 hunks FAILED -- saving rejects to file e.rej
+  merging e
+  warning: conflicts during merge.
+  merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
   abort: Fix up the change and run hg histedit --continue
 second edit also fails, but just continue
+  $ hg revert -r 'p1()' e
+  $ hg resolve --mark e
   $ hg histedit --continue 2>&1 | fixbundle
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 post message fix
   $ hg log --graph
-  @  changeset:   5:6459970fb49b
+  @  changeset:   6:f14da722aa4b
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
-  o  changeset:   4:556f27c874b0
+  o  changeset:   5:382ff1adf0ed
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     pick 177f92b77385 c
+  |  summary:     pick 65a9a84f33fd 3 c
   |
-  o  changeset:   3:055a42cdd887
+  o  changeset:   4:00f1c5383965
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     d
   |
-  o  changeset:   2:177f92b77385
+  o  changeset:   3:65a9a84f33fd
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     c
   |
-  o  changeset:   1:d2ae7f538514
+  o  changeset:   2:da6535b52e45
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     b
   |
-  o  changeset:   0:cb9a9f314b8b
+  o  changeset:   1:c1f09da44841
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     a
+  |
+  o  changeset:   0:1715188a53c7
      user:        test
      date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     a
+     summary:     Initial commit
   
 
   $ cd ..
--- a/tests/test-hook.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-hook.t	Tue Oct 02 14:31:25 2012 -0700
@@ -529,12 +529,12 @@
   [1]
 
   $ echo '[hooks]' > .hg/hgrc
-  $ echo "update.ne = python:`pwd`/nonexisting.py:testhook" >> .hg/hgrc
+  $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc
   $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc
 
   $ hg up null
   loading update.ne hook failed:
-  abort: No such file or directory: $TESTTMP/d/repo/nonexisting.py
+  abort: No such file or directory: $TESTTMP/d/repo/nonexistent.py
   [255]
 
   $ hg id
@@ -581,7 +581,7 @@
   cb9a9f314b8b
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
-make sure --verbose (and --quiet/--debug etc.) are propogated to the local ui
+make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui
 that is passed to pre/post hooks
 
   $ echo '[hooks]' > .hg/hgrc
--- a/tests/test-http-branchmap.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-http-branchmap.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,7 +1,10 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
   $ hgserve() {
-  >     hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid -E errors.log -v $@
+  >     hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid \
+  >       -E errors.log -v $@ > startup.log
+  >     # Grepping hg serve stdout would hang on Windows
+  >     grep -v 'listening at' startup.log
   >     cat hg.pid >> "$DAEMON_PIDS"
   > }
   $ hg init a
@@ -12,7 +15,6 @@
   $ hg -R a ci -Am foo
   adding foo
   $ hgserve -R a --config web.push_ssl=False --config web.allow_push=* --encoding latin1
-  listening at http://*:$HGPORT1/ (bound to 127.0.0.1:$HGPORT1) (glob)
   $ hg --encoding utf-8 clone http://localhost:$HGPORT1 b
   requesting all changes
   adding changesets
@@ -52,7 +54,7 @@
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     foo
   
-  $ kill `cat hg.pid`
+  $ "$TESTDIR/killdaemons.py" hg.pid
 
 verify 7e7d56fe4833 (encoding fallback in branchmap to maintain compatibility with 1.3.x)
 
--- a/tests/test-hybridencode.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-hybridencode.py	Tue Oct 02 14:31:25 2012 -0700
@@ -1,14 +1,75 @@
 from mercurial import store
 
-auxencode = lambda f: store._auxencode(f, True)
-hybridencode = lambda f: store._hybridencode(f, auxencode)
+def show(s):
+    # show test input
+    print "A = '%s'" % s.encode("string_escape")
+
+    # show the result of the C implementation, if available
+    h = store._dothybridencode(s)
+    print "B = '%s'" % h.encode("string_escape")
+
+    # compare it with reference implementation in Python
+    r = store._hybridencode(s, True)
+    if h != r:
+        print "R = '%s'" % r.encode("string_escape")
+    print
 
-enc = hybridencode # used for 'dotencode' repo format
+show("data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&'()+,-.;=[]^`{}")
+
+print "uppercase char X is encoded as _x"
+show("data/ABCDEFGHIJKLMNOPQRSTUVWXYZ")
+
+print "underbar is doubled"
+show("data/_")
+
+print "tilde is character-encoded"
+show("data/~")
+
+print "characters in ASCII code range 1..31"
+show('data/\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
+          '\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f')
 
-def show(s):
-    print "A = '%s'" % s
-    print "B = '%s'" % enc(s)
-    print
+print "characters in ASCII code range 126..255"
+show('data/\x7e\x7f'
+          '\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f'
+          '\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f')
+show('data/\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf'
+          '\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf')
+show('data/\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf'
+          '\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf')
+show('data/\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef'
+          '\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff')
+
+print "Windows reserved characters"
+show('data/less <, greater >, colon :, double-quote ", backslash \\'
+           ', pipe |, question-mark ?, asterisk *')
+
+print "encoding directories ending in .hg, .i or .d with '.hg' suffix"
+show('data/x.h.i/x.hg/x.i/x.d/foo')
+show('data/a.hg/a.i/a.d/foo')
+show('data/au.hg/au.i/au.d/foo')
+show('data/aux.hg/aux.i/aux.d/foo')
+show('data/auxy.hg/auxy.i/auxy.d/foo')
+
+print "but these are not encoded on *filenames*"
+show('data/foo/x.hg')
+show('data/foo/x.i')
+show('data/foo/x.d')
+show('data/foo/a.hg')
+show('data/foo/a.i')
+show('data/foo/a.d')
+show('data/foo/au.hg')
+show('data/foo/au.i')
+show('data/foo/au.d')
+show('data/foo/aux.hg')
+show('data/foo/aux.i')
+show('data/foo/aux.d')
+show('data/foo/auxy.hg')
+show('data/foo/auxy.i')
+show('data/foo/auxy.d')
+
+print "plain .hg, .i and .d directories have the leading dot encoded"
+show('data/.hg/.i/.d/foo')
 
 show('data/aux.bla/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c.i')
 
@@ -25,3 +86,377 @@
      'Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt')
 show('data/foo.../foo   / /a./_. /__/.x../    bla/.FOO/something.i')
 
+show('data/c/co/com/com0/com1/com2/com3/com4/com5/com6/com7/com8/com9')
+show('data/C/CO/COM/COM0/COM1/COM2/COM3/COM4/COM5/COM6/COM7/COM8/COM9')
+show('data/c.x/co.x/com.x/com0.x/com1.x/com2.x/com3.x/com4.x/com5.x'
+                                        '/com6.x/com7.x/com8.x/com9.x')
+show('data/x.c/x.co/x.com0/x.com1/x.com2/x.com3/x.com4/x.com5'
+                                        '/x.com6/x.com7/x.com8/x.com9')
+show('data/cx/cox/comx/com0x/com1x/com2x/com3x/com4x/com5x'
+                                            '/com6x/com7x/com8x/com9x')
+show('data/xc/xco/xcom0/xcom1/xcom2/xcom3/xcom4/xcom5'
+                                            '/xcom6/xcom7/xcom8/xcom9')
+
+show('data/l/lp/lpt/lpt0/lpt1/lpt2/lpt3/lpt4/lpt5/lpt6/lpt7/lpt8/lpt9')
+show('data/L/LP/LPT/LPT0/LPT1/LPT2/LPT3/LPT4/LPT5/LPT6/LPT7/LPT8/LPT9')
+show('data/l.x/lp.x/lpt.x/lpt0.x/lpt1.x/lpt2.x/lpt3.x/lpt4.x/lpt5.x'
+                                        '/lpt6.x/lpt7.x/lpt8.x/lpt9.x')
+show('data/x.l/x.lp/x.lpt/x.lpt0/x.lpt1/x.lpt2/x.lpt3/x.lpt4/x.lpt5'
+                                        '/x.lpt6/x.lpt7/x.lpt8/x.lpt9')
+show('data/lx/lpx/lptx/lpt0x/lpt1x/lpt2x/lpt3x/lpt4x/lpt5x'
+                                            '/lpt6x/lpt7x/lpt8x/lpt9x')
+show('data/xl/xlp/xlpt/xlpt0/xlpt1/xlpt2/xlpt3/xlpt4/xlpt5'
+                                            '/xlpt6/xlpt7/xlpt8/xlpt9')
+
+show('data/con/p/pr/prn/a/au/aux/n/nu/nul')
+show('data/CON/P/PR/PRN/A/AU/AUX/N/NU/NUL')
+show('data/con.x/p.x/pr.x/prn.x/a.x/au.x/aux.x/n.x/nu.x/nul.x')
+show('data/x.con/x.p/x.pr/x.prn/x.a/x.au/x.aux/x.n/x.nu/x.nul')
+show('data/conx/px/prx/prnx/ax/aux/auxx/nx/nux/nulx')
+show('data/xcon/xp/xpr/xprn/xa/xau/xaux/xn/xnu/xnul')
+
+show('data/a./au./aux./auxy./aux.')
+show('data/c./co./con./cony./con.')
+show('data/p./pr./prn./prny./prn.')
+show('data/n./nu./nul./nuly./nul.')
+show('data/l./lp./lpt./lpt1./lpt1y./lpt1.')
+show('data/lpt9./lpt9y./lpt9.')
+show('data/com./com1./com1y./com1.')
+show('data/com9./com9y./com9.')
+
+show('data/a /au /aux /auxy /aux ')
+
+print "largest unhashed path"
+show('data/123456789-123456789-123456789-123456789-123456789-'
+          'unhashed--xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "shortest hashed path"
+show('data/123456789-123456789-123456789-123456789-123456789-'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "changing one char in part that's hashed away produces a different hash"
+show('data/123456789-123456789-123456789-123456789-123456789-'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxy-'
+          '123456789-123456')
+
+print "uppercase hitting length limit due to encoding"
+show('data/A23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/Z23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "compare with lowercase not hitting limit"
+show('data/a23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/z23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "not hitting limit with any of these"
+show("data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&'()+,-.;="
+          "[]^`{}xxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-"
+          "123456789-12345")
+
+print "underbar hitting length limit due to encoding"
+show('data/_23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "tilde hitting length limit due to encoding"
+show('data/~23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "Windows reserved characters hitting length limit"
+show('data/<23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/>23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/:23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/"23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/\\23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/|23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/?23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/*23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "initial space hitting length limit"
+show('data/ 23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "initial dot hitting length limit"
+show('data/.23456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "trailing space in filename hitting length limit"
+show('data/123456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-1234 ')
+
+print "trailing dot in filename hitting length limit"
+show('data/123456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-1234.')
+
+print "initial space in directory hitting length limit"
+show('data/ x/456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "initial dot in directory hitting length limit"
+show('data/.x/456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "trailing space in directory hitting length limit"
+show('data/x /456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "trailing dot in directory hitting length limit"
+show('data/x./456789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "with directories that need direncoding, hitting length limit"
+show('data/x.i/56789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/x.d/56789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/x.hg/5789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "Windows reserved filenames, hitting length limit"
+show('data/con/56789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/prn/56789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/aux/56789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/nul/56789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/com1/6789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/com9/6789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/lpt1/6789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+show('data/lpt9/6789-123456789-123456789-123456789-123456789-'
+          'xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "non-reserved names, just not hitting limit"
+show('data/123456789-123456789-123456789-123456789-123456789-'
+          '/com/com0/lpt/lpt0/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12345')
+
+print "hashed path with largest untruncated 1st dir"
+show('data/12345678/-123456789-123456789-123456789-123456789-'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with smallest truncated 1st dir"
+show('data/123456789/123456789-123456789-123456789-123456789-'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with largest untruncated two dirs"
+show('data/12345678/12345678/9-123456789-123456789-123456789-'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with smallest truncated two dirs"
+show('data/123456789/123456789/123456789-123456789-123456789-'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with largest untruncated three dirs"
+show('data/12345678/12345678/12345678/89-123456789-123456789-'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with smallest truncated three dirs"
+show('data/123456789/123456789/123456789/123456789-123456789-'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with largest untruncated four dirs"
+show('data/12345678/12345678/12345678/12345678/789-123456789-'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with smallest truncated four dirs"
+show('data/123456789/123456789/123456789/123456789/123456789-'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with largest untruncated five dirs"
+show('data/12345678/12345678/12345678/12345678/12345678/6789-'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with smallest truncated five dirs"
+show('data/123456789/123456789/123456789/123456789/123456789/'
+          'hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with largest untruncated six dirs"
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/ed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with smallest truncated six dirs"
+show('data/123456789/123456789/123456789/123456789/123456789/'
+          '123456789/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with largest untruncated seven dirs"
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/xxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with smallest truncated seven dirs"
+show('data/123456789/123456789/123456789/123456789/123456789/'
+          '123456789/123456789/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with largest untruncated eight dirs"
+print "(directory 8 is dropped because it hits _maxshortdirslen)"
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345678/xxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with smallest truncated eight dirs"
+print "(directory 8 is dropped because it hits _maxshortdirslen)"
+show('data/123456789/123456789/123456789/123456789/123456789/'
+          '123456789/123456789/123456789/xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with largest non-dropped directory 8"
+print "(just not hitting the _maxshortdirslen boundary)"
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "...adding one truncated char to dir 1..7 won't drop dir 8"
+show('data/12345678x/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+show('data/12345678/12345678x/12345678/12345678/12345678/12345'
+          '678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+show('data/12345678/12345678/12345678x/12345678/12345678/12345'
+          '678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+show('data/12345678/12345678/12345678/12345678x/12345678/12345'
+          '678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+show('data/12345678/12345678/12345678/12345678/12345678x/12345'
+          '678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678x/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678x/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path with shortest dropped directory 8"
+print "(just hitting the _maxshortdirslen boundary)"
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/123456/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "hashed path that drops dir 8 due to dot or space at end is"
+print "encoded, and thus causing to hit _maxshortdirslen"
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/1234./-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/1234 /-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print "... with dir 8 short enough for encoding"
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12./xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12 /xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-123456')
+
+print '''Extensions are replicated on hashed paths. Note that
+we only get to encode files that end in .i or .d inside the
+store. Encoded filenames are thus bound in length.'''
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.345.i')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.345.d')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.3456.i')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.34567.i')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.345678.i')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.3456789.i')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.3456789-.i')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.3456789-1.i')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.3456789-12.i')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.3456789-123.i')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.3456789-1234.i')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.3456789-12345.i')
+show('data/12345678/12345678/12345678/12345678/12345678/12345'
+          '678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-'
+          '123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWX'
+          'YZ-abcdefghjiklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPRSTU'
+          'VWXYZ-1234567890-xxxxxxxxx-xxxxxxxxx-xxxxxxxx-xxxx'
+          'xxxxx-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwww'
+          'wwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww.i')
--- a/tests/test-hybridencode.py.out	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-hybridencode.py.out	Tue Oct 02 14:31:25 2012 -0700
@@ -1,3 +1,105 @@
+A = 'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}'
+B = 'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}'
+
+uppercase char X is encoded as _x
+A = 'data/ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+B = 'data/_a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z'
+
+underbar is doubled
+A = 'data/_'
+B = 'data/__'
+
+tilde is character-encoded
+A = 'data/~'
+B = 'data/~7e'
+
+characters in ASCII code range 1..31
+A = 'data/\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f'
+B = 'data/~01~02~03~04~05~06~07~08~09~0a~0b~0c~0d~0e~0f~10~11~12~13~14~15~16~17~18~19~1a~1b~1c~1d~1e~1f'
+
+characters in ASCII code range 126..255
+A = 'data/~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f'
+B = 'data/~7e~7f~80~81~82~83~84~85~86~87~88~89~8a~8b~8c~8d~8e~8f~90~91~92~93~94~95~96~97~98~99~9a~9b~9c~9d~9e~9f'
+
+A = 'data/\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf'
+B = 'data/~a0~a1~a2~a3~a4~a5~a6~a7~a8~a9~aa~ab~ac~ad~ae~af~b0~b1~b2~b3~b4~b5~b6~b7~b8~b9~ba~bb~bc~bd~be~bf'
+
+A = 'data/\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf'
+B = 'data/~c0~c1~c2~c3~c4~c5~c6~c7~c8~c9~ca~cb~cc~cd~ce~cf~d0~d1~d2~d3~d4~d5~d6~d7~d8~d9~da~db~dc~dd~de~df'
+
+A = 'data/\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
+B = 'data/~e0~e1~e2~e3~e4~e5~e6~e7~e8~e9~ea~eb~ec~ed~ee~ef~f0~f1~f2~f3~f4~f5~f6~f7~f8~f9~fa~fb~fc~fd~fe~ff'
+
+Windows reserved characters
+A = 'data/less <, greater >, colon :, double-quote ", backslash \\, pipe |, question-mark ?, asterisk *'
+B = 'data/less ~3c, greater ~3e, colon ~3a, double-quote ~22, backslash ~5c, pipe ~7c, question-mark ~3f, asterisk ~2a'
+
+encoding directories ending in .hg, .i or .d with '.hg' suffix
+A = 'data/x.h.i/x.hg/x.i/x.d/foo'
+B = 'data/x.h.i.hg/x.hg.hg/x.i.hg/x.d.hg/foo'
+
+A = 'data/a.hg/a.i/a.d/foo'
+B = 'data/a.hg.hg/a.i.hg/a.d.hg/foo'
+
+A = 'data/au.hg/au.i/au.d/foo'
+B = 'data/au.hg.hg/au.i.hg/au.d.hg/foo'
+
+A = 'data/aux.hg/aux.i/aux.d/foo'
+B = 'data/au~78.hg.hg/au~78.i.hg/au~78.d.hg/foo'
+
+A = 'data/auxy.hg/auxy.i/auxy.d/foo'
+B = 'data/auxy.hg.hg/auxy.i.hg/auxy.d.hg/foo'
+
+but these are not encoded on *filenames*
+A = 'data/foo/x.hg'
+B = 'data/foo/x.hg'
+
+A = 'data/foo/x.i'
+B = 'data/foo/x.i'
+
+A = 'data/foo/x.d'
+B = 'data/foo/x.d'
+
+A = 'data/foo/a.hg'
+B = 'data/foo/a.hg'
+
+A = 'data/foo/a.i'
+B = 'data/foo/a.i'
+
+A = 'data/foo/a.d'
+B = 'data/foo/a.d'
+
+A = 'data/foo/au.hg'
+B = 'data/foo/au.hg'
+
+A = 'data/foo/au.i'
+B = 'data/foo/au.i'
+
+A = 'data/foo/au.d'
+B = 'data/foo/au.d'
+
+A = 'data/foo/aux.hg'
+B = 'data/foo/au~78.hg'
+
+A = 'data/foo/aux.i'
+B = 'data/foo/au~78.i'
+
+A = 'data/foo/aux.d'
+B = 'data/foo/au~78.d'
+
+A = 'data/foo/auxy.hg'
+B = 'data/foo/auxy.hg'
+
+A = 'data/foo/auxy.i'
+B = 'data/foo/auxy.i'
+
+A = 'data/foo/auxy.d'
+B = 'data/foo/auxy.d'
+
+plain .hg, .i and .d directories have the leading dot encoded
+A = 'data/.hg/.i/.d/foo'
+B = 'data/~2ehg.hg/~2ei.hg/~2ed.hg/foo'
+
 A = 'data/aux.bla/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c.i'
 B = 'data/au~78.bla/bla.aux/pr~6e/_p_r_n/lpt/co~6d3/nu~6c/coma/foo._n_u_l/normal.c.i'
 
@@ -19,3 +121,373 @@
 A = 'data/foo.../foo   / /a./_. /__/.x../    bla/.FOO/something.i'
 B = 'data/foo..~2e/foo  ~20/~20/a~2e/__.~20/____/~2ex.~2e/~20   bla/~2e_f_o_o/something.i'
 
+A = 'data/c/co/com/com0/com1/com2/com3/com4/com5/com6/com7/com8/com9'
+B = 'data/c/co/com/com0/co~6d1/co~6d2/co~6d3/co~6d4/co~6d5/co~6d6/co~6d7/co~6d8/co~6d9'
+
+A = 'data/C/CO/COM/COM0/COM1/COM2/COM3/COM4/COM5/COM6/COM7/COM8/COM9'
+B = 'data/_c/_c_o/_c_o_m/_c_o_m0/_c_o_m1/_c_o_m2/_c_o_m3/_c_o_m4/_c_o_m5/_c_o_m6/_c_o_m7/_c_o_m8/_c_o_m9'
+
+A = 'data/c.x/co.x/com.x/com0.x/com1.x/com2.x/com3.x/com4.x/com5.x/com6.x/com7.x/com8.x/com9.x'
+B = 'data/c.x/co.x/com.x/com0.x/co~6d1.x/co~6d2.x/co~6d3.x/co~6d4.x/co~6d5.x/co~6d6.x/co~6d7.x/co~6d8.x/co~6d9.x'
+
+A = 'data/x.c/x.co/x.com0/x.com1/x.com2/x.com3/x.com4/x.com5/x.com6/x.com7/x.com8/x.com9'
+B = 'data/x.c/x.co/x.com0/x.com1/x.com2/x.com3/x.com4/x.com5/x.com6/x.com7/x.com8/x.com9'
+
+A = 'data/cx/cox/comx/com0x/com1x/com2x/com3x/com4x/com5x/com6x/com7x/com8x/com9x'
+B = 'data/cx/cox/comx/com0x/com1x/com2x/com3x/com4x/com5x/com6x/com7x/com8x/com9x'
+
+A = 'data/xc/xco/xcom0/xcom1/xcom2/xcom3/xcom4/xcom5/xcom6/xcom7/xcom8/xcom9'
+B = 'data/xc/xco/xcom0/xcom1/xcom2/xcom3/xcom4/xcom5/xcom6/xcom7/xcom8/xcom9'
+
+A = 'data/l/lp/lpt/lpt0/lpt1/lpt2/lpt3/lpt4/lpt5/lpt6/lpt7/lpt8/lpt9'
+B = 'data/l/lp/lpt/lpt0/lp~741/lp~742/lp~743/lp~744/lp~745/lp~746/lp~747/lp~748/lp~749'
+
+A = 'data/L/LP/LPT/LPT0/LPT1/LPT2/LPT3/LPT4/LPT5/LPT6/LPT7/LPT8/LPT9'
+B = 'data/_l/_l_p/_l_p_t/_l_p_t0/_l_p_t1/_l_p_t2/_l_p_t3/_l_p_t4/_l_p_t5/_l_p_t6/_l_p_t7/_l_p_t8/_l_p_t9'
+
+A = 'data/l.x/lp.x/lpt.x/lpt0.x/lpt1.x/lpt2.x/lpt3.x/lpt4.x/lpt5.x/lpt6.x/lpt7.x/lpt8.x/lpt9.x'
+B = 'data/l.x/lp.x/lpt.x/lpt0.x/lp~741.x/lp~742.x/lp~743.x/lp~744.x/lp~745.x/lp~746.x/lp~747.x/lp~748.x/lp~749.x'
+
+A = 'data/x.l/x.lp/x.lpt/x.lpt0/x.lpt1/x.lpt2/x.lpt3/x.lpt4/x.lpt5/x.lpt6/x.lpt7/x.lpt8/x.lpt9'
+B = 'data/x.l/x.lp/x.lpt/x.lpt0/x.lpt1/x.lpt2/x.lpt3/x.lpt4/x.lpt5/x.lpt6/x.lpt7/x.lpt8/x.lpt9'
+
+A = 'data/lx/lpx/lptx/lpt0x/lpt1x/lpt2x/lpt3x/lpt4x/lpt5x/lpt6x/lpt7x/lpt8x/lpt9x'
+B = 'data/lx/lpx/lptx/lpt0x/lpt1x/lpt2x/lpt3x/lpt4x/lpt5x/lpt6x/lpt7x/lpt8x/lpt9x'
+
+A = 'data/xl/xlp/xlpt/xlpt0/xlpt1/xlpt2/xlpt3/xlpt4/xlpt5/xlpt6/xlpt7/xlpt8/xlpt9'
+B = 'data/xl/xlp/xlpt/xlpt0/xlpt1/xlpt2/xlpt3/xlpt4/xlpt5/xlpt6/xlpt7/xlpt8/xlpt9'
+
+A = 'data/con/p/pr/prn/a/au/aux/n/nu/nul'
+B = 'data/co~6e/p/pr/pr~6e/a/au/au~78/n/nu/nu~6c'
+
+A = 'data/CON/P/PR/PRN/A/AU/AUX/N/NU/NUL'
+B = 'data/_c_o_n/_p/_p_r/_p_r_n/_a/_a_u/_a_u_x/_n/_n_u/_n_u_l'
+
+A = 'data/con.x/p.x/pr.x/prn.x/a.x/au.x/aux.x/n.x/nu.x/nul.x'
+B = 'data/co~6e.x/p.x/pr.x/pr~6e.x/a.x/au.x/au~78.x/n.x/nu.x/nu~6c.x'
+
+A = 'data/x.con/x.p/x.pr/x.prn/x.a/x.au/x.aux/x.n/x.nu/x.nul'
+B = 'data/x.con/x.p/x.pr/x.prn/x.a/x.au/x.aux/x.n/x.nu/x.nul'
+
+A = 'data/conx/px/prx/prnx/ax/aux/auxx/nx/nux/nulx'
+B = 'data/conx/px/prx/prnx/ax/au~78/auxx/nx/nux/nulx'
+
+A = 'data/xcon/xp/xpr/xprn/xa/xau/xaux/xn/xnu/xnul'
+B = 'data/xcon/xp/xpr/xprn/xa/xau/xaux/xn/xnu/xnul'
+
+A = 'data/a./au./aux./auxy./aux.'
+B = 'data/a~2e/au~2e/au~78~2e/auxy~2e/au~78~2e'
+
+A = 'data/c./co./con./cony./con.'
+B = 'data/c~2e/co~2e/co~6e~2e/cony~2e/co~6e~2e'
+
+A = 'data/p./pr./prn./prny./prn.'
+B = 'data/p~2e/pr~2e/pr~6e~2e/prny~2e/pr~6e~2e'
+
+A = 'data/n./nu./nul./nuly./nul.'
+B = 'data/n~2e/nu~2e/nu~6c~2e/nuly~2e/nu~6c~2e'
+
+A = 'data/l./lp./lpt./lpt1./lpt1y./lpt1.'
+B = 'data/l~2e/lp~2e/lpt~2e/lp~741~2e/lpt1y~2e/lp~741~2e'
+
+A = 'data/lpt9./lpt9y./lpt9.'
+B = 'data/lp~749~2e/lpt9y~2e/lp~749~2e'
+
+A = 'data/com./com1./com1y./com1.'
+B = 'data/com~2e/co~6d1~2e/com1y~2e/co~6d1~2e'
+
+A = 'data/com9./com9y./com9.'
+B = 'data/co~6d9~2e/com9y~2e/co~6d9~2e'
+
+A = 'data/a /au /aux /auxy /aux '
+B = 'data/a~20/au~20/aux~20/auxy~20/aux~20'
+
+largest unhashed path
+A = 'data/123456789-123456789-123456789-123456789-123456789-unhashed--xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'data/123456789-123456789-123456789-123456789-123456789-unhashed--xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+
+shortest hashed path
+A = 'data/123456789-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/123456789-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxe9c55002b50bf5181e7a6fc1f60b126e2a6fcf71'
+
+changing one char in part that's hashed away produces a different hash
+A = 'data/123456789-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxy-123456789-123456'
+B = 'dh/123456789-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxd24fa4455faf8a94350c18e5eace7c2bb17af706'
+
+uppercase hitting length limit due to encoding
+A = 'data/A23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/a23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxcbbc657029b41b94ed510d05feb6716a5c03bc6b'
+
+A = 'data/Z23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/z23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxx938f32a725c89512833fb96b6602dd9ebff51ddd'
+
+compare with lowercase not hitting limit
+A = 'data/a23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'data/a23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+
+A = 'data/z23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'data/z23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+
+not hitting limit with any of these
+A = 'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}xxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'data/abcdefghijklmnopqrstuvwxyz0123456789 !#%&\'()+,-.;=[]^`{}xxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+
+underbar hitting length limit due to encoding
+A = 'data/_23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/_23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxx9921a01af50feeabc060ce00eee4cba6efc31d2b'
+
+tilde hitting length limit due to encoding
+A = 'data/~23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~7e23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx9cec6f97d569c10995f785720044ea2e4227481b'
+
+Windows reserved characters hitting length limit
+A = 'data/<23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~3c23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxee67d8f275876ca1ef2500fc542e63c885c4e62d'
+
+A = 'data/>23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~3e23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx387a85a5b1547cc9136310c974df716818458ddb'
+
+A = 'data/:23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~3a23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx2e4154fb571d13d22399c58cc4ef4858e4b75999'
+
+A = 'data/"23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~2223456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxfc7e3ec7b0687ee06ed8c32fef0eb0c1980259f5'
+
+A = 'data/\\23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~5c23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx944e1f2b7110687e116e0d151328ac648b06ab4a'
+
+A = 'data/|23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~7c23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx28b23dd3fd0242946334126ab62bcd772aac32f4'
+
+A = 'data/?23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~3f23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxa263022d3994d2143d98f94f431eef8b5e7e0f8a'
+
+A = 'data/*23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~2a23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx0e7e6020e3c00ba7bb7893d84ca2966fbf53e140'
+
+initial space hitting length limit
+A = 'data/ 23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~2023456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx92acbc78ef8c0b796111629a02601f07d8aec4ea'
+
+initial dot hitting length limit
+A = 'data/.23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~2e23456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxdbe19cc6505b3515ab9228cebf877ad07075168f'
+
+trailing space in filename hitting length limit
+A = 'data/123456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-1234 '
+B = 'dh/123456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxx0025dc73e04f97426db4893e3bf67d581dc6d066'
+
+trailing dot in filename hitting length limit
+A = 'data/123456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-1234.'
+B = 'dh/123456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxx85a16cf03ee7feba8a5abc626f1ba9886d01e89d'
+
+initial space in directory hitting length limit
+A = 'data/ x/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~20x/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx1b3a3b712b2ac00d6af14ae8b4c14fdbf904f516'
+
+initial dot in directory hitting length limit
+A = 'data/.x/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/~2ex/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx39dbc4c193a5643a8936fc69c3363cd7ac91ab14'
+
+trailing space in directory hitting length limit
+A = 'data/x /456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/x~20/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx2253c341df0b5290790ad312cd8499850f2273e5'
+
+trailing dot in directory hitting length limit
+A = 'data/x./456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/x~2e/456789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxcc0324d696d34562b44b5138db08ee1594ccc583'
+
+with directories that need direncoding, hitting length limit
+A = 'data/x.i/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/x.i.hg/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxa4c4399bdf81c67dbbbb7060aa0124d8dea94f74'
+
+A = 'data/x.d/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/x.d.hg/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxx1303fa90473b230615f5b3ea7b660e881ae5270a'
+
+A = 'data/x.hg/5789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/x.hg.hg/5789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxx26d724a8af68e7a4e4455e6602ea9adbd0eb801f'
+
+Windows reserved filenames, hitting length limit
+A = 'data/con/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/co~6e/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxc0794d4f4c605a2617900eb2563d7113cf6ea7d3'
+
+A = 'data/prn/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/pr~6e/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx64db876e1a9730e27236cb9b167aff942240e932'
+
+A = 'data/aux/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/au~78/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx8a178558405ca6fb4bbd75446dfa186f06751a0d'
+
+A = 'data/nul/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/nu~6c/56789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxc5e51b6fec1bd07bd243b053a0c3f7209855b886'
+
+A = 'data/com1/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/co~6d1/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx32f5f44ece3bb62b9327369ca84cc19c86259fcd'
+
+A = 'data/com9/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/co~6d9/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxx734360b28c66a3230f55849fe8926206d229f990'
+
+A = 'data/lpt1/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/lp~741/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxe6f16ab4b6b0637676b2842b3345c9836df46ef7'
+
+A = 'data/lpt9/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'dh/lp~749/6789-123456789-123456789-123456789-123456789-xxxxxxxxx-xxxxxxxxx-xxxxxa475814c51acead3e44f2ff801f0c4903f986157'
+
+non-reserved names, just not hitting limit
+A = 'data/123456789-123456789-123456789-123456789-123456789-/com/com0/lpt/lpt0/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+B = 'data/123456789-123456789-123456789-123456789-123456789-/com/com0/lpt/lpt0/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12345'
+
+hashed path with largest untruncated 1st dir
+A = 'data/12345678/-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/-123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxx4e9e9e384d00929a93b6835fbf976eb32321ff3c'
+
+hashed path with smallest truncated 1st dir
+A = 'data/123456789/123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/123456789-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxx1f4e4ec5f2be76e109bfaa8e31c062fe426d5490'
+
+hashed path with largest untruncated two dirs
+A = 'data/12345678/12345678/9-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/9-123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxx3332d8329d969cf835542a9f2cbcfb385b6cf39d'
+
+hashed path with smallest truncated two dirs
+A = 'data/123456789/123456789/123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/123456789-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx9699559798247dffa18717138859be5f8874840e'
+
+hashed path with largest untruncated three dirs
+A = 'data/12345678/12345678/12345678/89-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/89-123456789-123456789-hashed----xxxxxxxxx-xxxxxxxf0a2b053bb1369cce02f78c217d6a7aaea18c439'
+
+hashed path with smallest truncated three dirs
+A = 'data/123456789/123456789/123456789/123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/123456789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-1c6f8284967384ec13985a046d3553179d9d03cd'
+
+hashed path with largest untruncated four dirs
+A = 'data/12345678/12345678/12345678/12345678/789-123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/789-123456789-hashed----xxxxxxxxx-xxxxxxx0d30c99049d8f0ff97b94d4ef302027e8d54c6fd'
+
+hashed path with smallest truncated four dirs
+A = 'data/123456789/123456789/123456789/123456789/123456789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/123456789-hashed----xxxxxxxxx-xxxxxxxxx-x46162779e1a771810b37a737f82ae7ed33771402'
+
+hashed path with largest untruncated five dirs
+A = 'data/12345678/12345678/12345678/12345678/12345678/6789-hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/6789-hashed----xxxxxxxxx-xxxxxxxbfe752ddc8b003c2790c66a9f2eb1ea75c114390'
+
+hashed path with smallest truncated five dirs
+A = 'data/123456789/123456789/123456789/123456789/123456789/hashed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/hashed----xxxxxxxxx-xxxxxxxxx-xxb94c27b3532fa880cdd572b1c514785cab7b6ff2'
+
+hashed path with largest untruncated six dirs
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/ed----xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/ed----xxxxxxxxx-xxxxxxxcd8cc5483a0f3be409e0e5d4bf9e36e113c59235'
+
+hashed path with smallest truncated six dirs
+A = 'data/123456789/123456789/123456789/123456789/123456789/123456789/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxxxx-xxxxxxxxx-xxx47dd6f616f833a142da00701b334cebbf640da06'
+
+hashed path with largest untruncated seven dirs
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxx-xxxxxxx1c8ed635229fc22efe51035feeadeb4c8a0ecb82'
+
+hashed path with smallest truncated seven dirs
+A = 'data/123456789/123456789/123456789/123456789/123456789/123456789/123456789/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxxxx-xxxx298ff7d33f8ce6db57930837ffea2fb2f48bb926'
+
+hashed path with largest untruncated eight dirs
+(directory 8 is dropped because it hits _maxshortdirslen)
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxx-xxxxxxc8996ccd41b471f768057181a4d59d2febe7277d'
+
+hashed path with smallest truncated eight dirs
+(directory 8 is dropped because it hits _maxshortdirslen)
+A = 'data/123456789/123456789/123456789/123456789/123456789/123456789/123456789/123456789/xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxxxx-xxxx4fa04a839a6bda93e1c21c713f2edcbd16e8890d'
+
+hashed path with largest non-dropped directory 8
+(just not hitting the _maxshortdirslen boundary)
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxx4d43d1ccaa20efbfe99ec779dc063611536ff2c5'
+
+...adding one truncated char to dir 1..7 won't drop dir 8
+A = 'data/12345678x/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxx0f9efce65189cc60fd90fe4ffd49d7b58bbe0f2e'
+
+A = 'data/12345678/12345678x/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxx945ca395708cafdd54a94501859beabd3e243921'
+
+A = 'data/12345678/12345678/12345678x/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxac62bf6898c4fd0502146074547c11caa751a327'
+
+A = 'data/12345678/12345678/12345678/12345678x/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxx2ae5a2baed7983fae8974d0ca06c6bf08b9aee92'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678x/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxx214aba07b6687532a43d1e9eaf6e88cfca96b68c'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678x/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxe7a022ae82f0f55cf4e0498e55ba59ea4ebb55bf'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678x/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxb51ce61164996a80f36ce3cfe64b62d519aedae3'
+
+hashed path with shortest dropped directory 8
+(just hitting the _maxshortdirslen boundary)
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/123456/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/xxxxxxxxx-xxxx11fa9873cc6c3215eae864528b5530a04efc6cfe'
+
+hashed path that drops dir 8 due to dot or space at end is
+encoded, and thus causing to hit _maxshortdirslen
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/1234./-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/-xxxxxxxxx-xxx602df9b45bec564e2e1f0645d5140dddcc76ed58'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/1234 /-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/-xxxxxxxxx-xxxd99ff212bc84b4d1f70cd6b0071e3ef69d4e12ce'
+
+... with dir 8 short enough for encoding
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12./xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12~2e/xx-xxxxx7baeb5ed7f14a586ee1cacecdbcbff70032d1b3c'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12 /xx-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-123456'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12~20/xx-xxxxxcf79ca9795f77d7f75745da36807e5d772bd5182'
+
+Extensions are replicated on hashed paths. Note that
+we only get to encode files that end in .i or .d inside the
+store. Encoded filenames are thus bound in length.
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.345.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxc10ad03b5755ed524f5286aab1815dfe07729438.i'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.345.d'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx9eec83381f2b39ef5ac8b4ecdf2c94f7983f57c8.d'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxb7796dc7d175cfb0bb8a7728f58f6ebec9042568.i'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.34567.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxb515857a6bfeef017c4894d8df42458ac65d55b8.i'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.345678.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxb05a0f247bc0a776211cd6a32ab714fd9cc09f2b.i'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxf192b48bff08d9e0e12035fb52bc58c70de72c94.i'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx435551e0ed4c7b083b9ba83cee916670e02e80ad.i'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-1.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxa7f74eb98d8d58b716356dfd26e2f9aaa65d6a9a.i'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxed68d9bd43b931f0b100267fee488d65a0c66f62.i'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-123.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx5cea44de2b642d2ba2b4a30693ffb1049644d698.i'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-1234.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx68462f62a7f230b39c1b5400d73ec35920990b7e.i'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx4cb852a314c6da240a83eec94761cdd71c6ec22e.i'
+
+A = 'data/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPRSTUVWXYZ-1234567890-xxxxxxxxx-xxxxxxxxx-xxxxxxxx-xxxxxxxxx-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww-wwwwwwwww.i'
+B = 'dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/-xxxxx93352aa50377751d9e5ebdf52da1e6e69a6887a6.i'
+
--- a/tests/test-import-git.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-import-git.t	Tue Oct 02 14:31:25 2012 -0700
@@ -322,12 +322,12 @@
 
 Filenames with spaces:
 
-  $ hg import -d "1000000 0" -m spaces - <<EOF
+  $ sed 's,EOL$,,g' <<EOF | hg import -d "1000000 0" -m spaces -
   > diff --git a/foo bar b/foo bar
   > new file mode 100644
   > index 0000000..257cc56
   > --- /dev/null
-  > +++ b/foo bar	
+  > +++ b/foo bar	EOL
   > @@ -0,0 +1 @@
   > +foo
   > EOF
@@ -384,7 +384,7 @@
   b
   \x00 (no-eol) (esc)
 
-  $ hg st --copies --change . 
+  $ hg st --copies --change .
   A binary2
     text2
   R text2
--- a/tests/test-inotify-issue1371.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-inotify-issue1371.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,6 +1,6 @@
 
   $ "$TESTDIR/hghave" inotify || exit 80
-  $ hg init 
+  $ hg init
   $ touch a b c d e f
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "inotify=" >> $HGRCPATH
@@ -22,7 +22,7 @@
 
   $ sleep 1
 
-eed to test all file opperations
+eed to test all file operations
 
   $ hg rm a
   $ rm b
@@ -41,4 +41,4 @@
 
 Are we able to kill the service? if not, the service died on some error
 
-  $ kill `cat hg.pid` 
+  $ kill `cat hg.pid`
--- a/tests/test-keyword.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-keyword.t	Tue Oct 02 14:31:25 2012 -0700
@@ -727,7 +727,7 @@
   ignore $Id$
   a
 
-Write custom keyword and prepare multiline commit message
+Write custom keyword and prepare multi-line commit message
 
   $ echo '$Xinfo$' >> a
   $ cat <<EOF >> log
@@ -745,7 +745,7 @@
   ? c
   ? log
 
-Commit with multiline message and custom expansion
+Commit with multi-line message and custom expansion
 
   $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
   a
@@ -814,7 +814,7 @@
 
   $ cd ..
 
-Expansion in destinaton with global configuration
+Expansion in destination with global configuration
 
   $ hg --quiet clone Test globalconf
   $ cat globalconf/a
@@ -998,7 +998,7 @@
 
   $ echo '$Id$' > m
   $ hg add m
-  $ hg commit -m 4kw 
+  $ hg commit -m 4kw
   $ echo foo >> m
   $ hg commit -m 5foo
 
--- a/tests/test-known.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-known.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
 = Test the known() protocol function =
 
@@ -35,4 +35,5 @@
   $ hg debugknown http://localhost:$HGPORT/
   
   $ cat error.log
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 
--- a/tests/test-largefiles.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-largefiles.t	Tue Oct 02 14:31:25 2012 -0700
@@ -218,7 +218,7 @@
   -rw-r--r-- 9 normal4
 
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 #endif
 
 Test archiving the various revisions.  These hit corner cases known with
@@ -1291,7 +1291,7 @@
   [255]
 
 used all HGPORTs, kill all daemons
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 #endif
 
 vanilla clients locked out from largefiles ssh repos
@@ -1377,7 +1377,7 @@
   $ rm -rf empty
 
 used all HGPORTs, kill all daemons
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 
 #endif
 
--- a/tests/test-merge-tools.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-merge-tools.t	Tue Oct 02 14:31:25 2012 -0700
@@ -191,7 +191,7 @@
   false.whatever=
   true.priority=1
   # hg update -C 1
-  $ hg merge -r 2 --config merge-tools.true.executable=nonexistingmergetool
+  $ hg merge -r 2 --config merge-tools.true.executable=nonexistentmergetool
   merging f
   merging f failed!
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
@@ -212,7 +212,7 @@
   false.whatever=
   true.priority=1
   # hg update -C 1
-  $ hg merge -r 2 --config merge-tools.true.executable=/nonexisting/mergetool
+  $ hg merge -r 2 --config merge-tools.true.executable=/nonexistent/mergetool
   merging f
   merging f failed!
   0 files updated, 0 files merged, 0 files removed, 1 files unresolved
@@ -336,7 +336,7 @@
   true.priority=1
   true.executable=cat
   # hg update -C 1
-  $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=nonexistingmergetool
+  $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=nonexistentmergetool
   couldn't find merge tool true specified for f
   merging f
   merging f failed!
@@ -359,7 +359,7 @@
   true.priority=1
   true.executable=cat
   # hg update -C 1
-  $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=/nonexisting/mergetool
+  $ hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=/nonexistent/mergetool
   couldn't find merge tool true specified for f
   merging f
   merging f failed!
--- a/tests/test-mq-header-date.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-mq-header-date.t	Tue Oct 02 14:31:25 2012 -0700
@@ -128,7 +128,7 @@
   >     catlogd 6
   > 
   >     drop 6
-  >     
+  > 
   > 
   >     echo ==== qnew -u
   >     hg qnew -u jane 6.patch
--- a/tests/test-mq-qclone-http.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-mq-qclone-http.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
 hide outer repo
   $ hg init
@@ -151,3 +151,6 @@
   [1]
   $ hg --cwd d log --mq --template '{rev} {desc|firstline}\n'
   0 b.patch
+
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
+
--- a/tests/test-mq-qimport.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-mq-qimport.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
   $ cat > writelines.py <<EOF
   > import sys
@@ -169,7 +169,7 @@
 
   $ cat > appendfoo.diff <<EOF
   > append foo
-  >  
+  > 
   > diff -r 07f494440405 -r 261500830e46 baz
   > --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
   > +++ b/baz	Thu Jan 01 00:00:00 1970 +0000
@@ -179,7 +179,7 @@
 
   $ cat > appendbar.diff <<EOF
   > append bar
-  >  
+  > 
   > diff -r 07f494440405 -r 261500830e46 baz
   > --- a/baz	Thu Jan 01 00:00:00 1970 +0000
   > +++ b/baz	Thu Jan 01 00:00:00 1970 +0000
@@ -240,7 +240,7 @@
 
 qimport with bad name, should abort before reading file
 
-  $ hg qimport non-existant-file --name .hg
+  $ hg qimport non-existent-file --name .hg
   abort: patch name cannot begin with ".hg"
   [255]
 
@@ -278,3 +278,5 @@
   1: secret
 
   $ cd ..
+
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
--- a/tests/test-mq-qpush-fail.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-mq-qpush-fail.t	Tue Oct 02 14:31:25 2012 -0700
@@ -34,7 +34,7 @@
   $ python -c 'print "\xe9"' > message
   $ cat .hg/patches/bad-patch >> message
   $ mv message .hg/patches/bad-patch
-  $ hg qpush -a && echo 'qpush succeded?!'
+  $ hg qpush -a && echo 'qpush succeeded?!'
   applying patch1
   applying patch2
   applying bad-patch
--- a/tests/test-mq-qrefresh-replace-log-message.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-mq-qrefresh-replace-log-message.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-Environement setup for MQ
+Environment setup for MQ
 
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "mq=" >> $HGRCPATH
--- a/tests/test-mq-qrefresh.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-mq-qrefresh.t	Tue Oct 02 14:31:25 2012 -0700
@@ -207,7 +207,7 @@
 
   $ echo 'orphan' > orphanchild
   $ hg add orphanchild
-  $ hg qrefresh nonexistingfilename # clear patch
+  $ hg qrefresh nonexistentfilename # clear patch
   $ hg qrefresh --short 1/base
   $ hg qrefresh --short 2/base
 
--- a/tests/test-mq-subrepo.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-mq-subrepo.t	Tue Oct 02 14:31:25 2012 -0700
@@ -478,7 +478,7 @@
   adding .hgsub
   $ hg -R sub update -C 1
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg commit -Am '#2 in parent (but be rollbacked soon)'
+  $ hg commit -Am '#2 in parent (but will be rolled back soon)'
   $ hg rollback
   repository tip rolled back to revision 1 (undo commit)
   working directory now based on revision 1
--- a/tests/test-mv-cp-st-diff.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-mv-cp-st-diff.t	Tue Oct 02 14:31:25 2012 -0700
@@ -187,7 +187,7 @@
   +y1
   
   
-  $ tb "add a a1" "add a a2" "hg cp a b" "copy in working dir" 
+  $ tb "add a a1" "add a a2" "hg cp a b" "copy in working dir"
   updating to branch default
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
   created new head
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-obsolete-checkheads.t	Tue Oct 02 14:31:25 2012 -0700
@@ -0,0 +1,272 @@
+Check that obsolete properly strip heads
+  $ cat > obs.py << EOF
+  > import mercurial.obsolete
+  > mercurial.obsolete._enabled = True
+  > EOF
+  $ cat >> $HGRCPATH << EOF
+  > [phases]
+  > # public changeset are not obsolete
+  > publish=false
+  > [ui]
+  > logtemplate='{node|short} ({phase}) {desc|firstline}\n'
+  > [extensions]
+  > graphlog=
+  > EOF
+  $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "add $1"
+  > }
+  $ getid() {
+  >    hg id --debug -ir "desc('$1')"
+  > }
+
+
+  $ hg init remote
+  $ cd remote
+  $ mkcommit base
+  $ hg phase --public .
+  $ cd ..
+  $ cp -r remote base
+  $ hg clone remote local
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd local
+
+New head replaces old head
+==========================
+
+setup
+
+  $ mkcommit old
+  $ hg push
+  pushing to $TESTTMP/remote (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  $ hg up -q '.^'
+  $ mkcommit new
+  created new head
+  $ hg debugobsolete `getid old` `getid new`
+  $ hg glog --hidden
+  @  71e3228bffe1 (draft) add new
+  |
+  | x  c70b08862e08 (draft) add old
+  |/
+  o  b4952fcf48cf (public) add base
+  
+  $ cp -r ../remote ../backup1
+
+old exists remotely as draft. It is obsoleted by new that we now push.
+Push should not warn about creating new head
+
+  $ hg push
+  pushing to $TESTTMP/remote (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+
+old head is now public (public local version)
+=============================================
+
+setup
+
+  $ rm -fr ../remote
+  $ cp -r ../backup1 ../remote
+  $ hg -R ../remote phase --public c70b08862e08
+  $ hg pull -v
+  pulling from $TESTTMP/remote (glob)
+  searching for changes
+  no changes found
+  $ hg glog --hidden
+  @  71e3228bffe1 (draft) add new
+  |
+  | o  c70b08862e08 (public) add old
+  |/
+  o  b4952fcf48cf (public) add base
+  
+
+Abort: old will still be an head because it's public.
+
+  $ hg push
+  pushing to $TESTTMP/remote (glob)
+  searching for changes
+  abort: push creates new remote head 71e3228bffe1!
+  (did you forget to merge? use push -f to force)
+  [255]
+
+old head is now public (public remote version)
+==============================================
+
+TODO: Not implemented yet.
+
+# setup
+#
+#   $ rm -fr ../remote
+#   $ cp -r ../backup1 ../remote
+#   $ hg -R ../remote phase --public c70b08862e08
+#   $ hg phase --draft --force c70b08862e08
+#   $ hg glog --hidden
+#   @  71e3228bffe1 (draft) add new
+#   |
+#   | x  c70b08862e08 (draft) add old
+#   |/
+#   o  b4952fcf48cf (public) add base
+#
+#
+#
+# Abort: old will still be an head because it's public.
+#
+#   $ hg push
+#   pushing to $TESTTMP/remote
+#   searching for changes
+#   abort: push creates new remote head 71e3228bffe1!
+#   (did you forget to merge? use push -f to force)
+#   [255]
+
+old head is obsolete but replacement is not pushed
+==================================================
+
+setup
+
+  $ rm -fr ../remote
+  $ cp -r ../backup1 ../remote
+  $ hg phase --draft --force '(0::) - 0'
+  $ hg up -q '.^'
+  $ mkcommit other
+  created new head
+  $ hg glog --hidden
+  @  d7d41ccbd4de (draft) add other
+  |
+  | o  71e3228bffe1 (draft) add new
+  |/
+  | x  c70b08862e08 (draft) add old
+  |/
+  o  b4952fcf48cf (public) add base
+  
+
+old exists remotely as draft. It is obsoleted by new but we don't push new.
+Push should abort on new head
+
+  $ hg push -r 'desc("other")'
+  pushing to $TESTTMP/remote (glob)
+  searching for changes
+  abort: push creates new remote head d7d41ccbd4de!
+  (did you forget to merge? use push -f to force)
+  [255]
+
+
+
+Both precursors and successors are already know remotely. Descendant adds heads
+===============================================================================
+
+setup. (The obsolete marker is known locally only
+
+  $ cd ..
+  $ rm -rf local
+  $ hg clone remote local
+  updating to branch default
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd local
+  $ mkcommit old
+  old already tracked!
+  nothing changed
+  [1]
+  $ hg up -q '.^'
+  $ mkcommit new
+  created new head
+  $ hg push -f
+  pushing to $TESTTMP/remote (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  $ mkcommit desc1
+  $ hg up -q '.^'
+  $ mkcommit desc2
+  created new head
+  $ hg debugobsolete `getid old` `getid new`
+  $ hg glog --hidden
+  @  5fe37041cc2b (draft) add desc2
+  |
+  | o  a3ef1d111c5f (draft) add desc1
+  |/
+  o  71e3228bffe1 (draft) add new
+  |
+  | x  c70b08862e08 (draft) add old
+  |/
+  o  b4952fcf48cf (public) add base
+  
+  $ hg glog --hidden -R ../remote
+  o  71e3228bffe1 (draft) add new
+  |
+  | o  c70b08862e08 (draft) add old
+  |/
+  @  b4952fcf48cf (public) add base
+  
+  $ cp -r ../remote ../backup2
+
+Push should not warn about adding new heads. We create one, but we'll delete
+one anyway.
+
+  $ hg push
+  pushing to $TESTTMP/remote (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 2 files (+1 heads)
+
+
+Remote head is unknown but obsoleted by a local changeset
+=========================================================
+
+setup
+
+  $ rm -fr ../remote
+  $ cp -r ../backup1 ../remote
+  $ cd ..
+  $ rm -rf local
+  $ hg clone remote local -r 0
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  updating to branch default
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd local
+  $ mkcommit new
+  $ hg -R ../remote id --debug -r tip
+  c70b08862e0838ea6d7c59c85da2f1ed6c8d67da tip
+  $ hg  id --debug -r tip
+  71e3228bffe1886550777233d6c97bb5a6b2a650 tip
+  $ hg debugobsolete c70b08862e0838ea6d7c59c85da2f1ed6c8d67da 71e3228bffe1886550777233d6c97bb5a6b2a650
+  $ hg glog --hidden
+  @  71e3228bffe1 (draft) add new
+  |
+  o  b4952fcf48cf (public) add base
+  
+  $ hg glog --hidden -R ../remote
+  o  c70b08862e08 (draft) add old
+  |
+  @  b4952fcf48cf (public) add base
+  
+
+Push should not complain about new heads.
+
+It should not complain about "unsynced remote changes!" either but that's not
+handled yet.
+
+  $ hg push --traceback
+  pushing to $TESTTMP/remote (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
--- a/tests/test-obsolete.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-obsolete.t	Tue Oct 02 14:31:25 2012 -0700
@@ -68,7 +68,10 @@
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ mkcommit new_c
   created new head
+  $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
   $ hg debugobsolete `getid original_c`  `getid new_c` -d '56 12'
+  $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
+  2:245bde4270cd add original_c
   $ hg debugobsolete
   245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
 
--- a/tests/test-patchbomb.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-patchbomb.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1877,7 +1877,7 @@
   +b
   
 
-test mutiple flags for single patch:
+test multiple flags for single patch:
   $ hg email --date '1970-1-1 0:1' -n --flag fooFlag --flag barFlag -f quux -t foo \
   >  -c bar -s test -r 2
   this patch series consists of 1 patches.
--- a/tests/test-phases-exchange.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-phases-exchange.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
@@ -84,7 +84,7 @@
   
 
 pull did not updated ../alpha state.
-push from alpha to beta should update phase even if nothing is transfered
+push from alpha to beta should update phase even if nothing is transferred
 
   $ cd ../alpha
   $ hgph # not updated by remote pull
@@ -1062,4 +1062,5 @@
   |
   o  0 public a-A - 054250a37db4
   
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 
--- a/tests/test-pull-branch.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-pull-branch.t	Tue Oct 02 14:31:25 2012 -0700
@@ -142,7 +142,7 @@
   $ hg branch branchC
   marked working directory as branch branchC
   (branches are permanent and global, did you want a bookmark?)
-  $ echo b1 > bar 
+  $ echo b1 > bar
   $ hg ci -Am "commit on branchC on tt"
   adding bar
 
@@ -151,7 +151,7 @@
   $ cd ../t
   $ hg up -C default
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ echo a1 > bar 
+  $ echo a1 > bar
   $ hg ci -Am "commit on default on t"
   adding bar
 
--- a/tests/test-pull-http.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-pull-http.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
   $ hg init test
   $ cd test
@@ -28,7 +28,7 @@
   $ cat test3/.hg/hgrc
   [paths]
   default = http://foo@localhost:$HGPORT/
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 
 expect error, cloning not allowed
 
@@ -40,7 +40,7 @@
   requesting all changes
   abort: authorization failed
   [255]
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 
 serve errors
 
@@ -49,7 +49,7 @@
   >     hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
   >     cat hg.pid >> $DAEMON_PIDS
   >     hg --cwd ../test pull http://localhost:$HGPORT/
-  >     kill `cat hg.pid`
+  >     "$TESTDIR/killdaemons.py" hg.pid
   >     echo % serve errors
   >     cat errors.log
   > }
--- a/tests/test-pull.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-pull.t	Tue Oct 02 14:31:25 2012 -0700
@@ -68,7 +68,7 @@
 
 Test 'file:' uri handling:
 
-  $ hg pull -q file://../test-doesnt-exist
+  $ hg pull -q file://../test-does-not-exist
   abort: file:// URLs can only refer to localhost
   [255]
 
--- a/tests/test-push-http.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-push-http.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
   $ hg init test
   $ cd test
@@ -16,9 +16,11 @@
   >     hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
   >     cat hg.pid >> $DAEMON_PIDS
   >     hg --cwd ../test2 push http://localhost:$HGPORT/
-  >     "$TESTDIR/killdaemons.py"
+  >     exitstatus=$?
+  >     "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   >     echo % serve errors
   >     cat errors.log
+  >     return $exitstatus
   > }
   $ cd ../test
 
@@ -27,10 +29,9 @@
   $ req
   pushing to http://localhost:$HGPORT/
   searching for changes
-  remote: ssl required
-  remote: ssl required
-  updating cb9a9f314b8b to public failed!
+  abort: HTTP Error 403: ssl required
   % serve errors
+  [255]
 
 expect authorization error
 
@@ -41,6 +42,7 @@
   searching for changes
   abort: authorization failed
   % serve errors
+  [255]
 
 expect authorization error: must have authorized user
 
@@ -50,6 +52,7 @@
   searching for changes
   abort: authorization failed
   % serve errors
+  [255]
 
 expect success
 
@@ -110,6 +113,7 @@
   searching for changes
   abort: authorization failed
   % serve errors
+  [255]
 
 expect authorization error: some users denied, users must be authenticated
 
@@ -119,5 +123,6 @@
   searching for changes
   abort: authorization failed
   % serve errors
+  [255]
 
   $ cd ..
--- a/tests/test-qrecord.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-qrecord.t	Tue Oct 02 14:31:25 2012 -0700
@@ -108,9 +108,9 @@
 
 help (bad mq)
 
-  $ echo "mq=nonexistant" >> $HGRCPATH
+  $ echo "mq=nonexistent" >> $HGRCPATH
   $ hg help qrecord
-  *** failed to import extension mq from nonexistant: [Errno 2] * (glob)
+  *** failed to import extension mq from nonexistent: [Errno 2] * (glob)
   hg qrecord [OPTION]... PATCH [FILE]...
   
   interactively record a new patch
@@ -121,7 +121,7 @@
 
 help (mq present)
 
-  $ sed 's/mq=nonexistant/mq=/' $HGRCPATH > hgrc.tmp
+  $ sed 's/mq=nonexistent/mq=/' $HGRCPATH > hgrc.tmp
   $ mv hgrc.tmp $HGRCPATH
 
   $ hg help qrecord
--- a/tests/test-rebase-abort.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-rebase-abort.t	Tue Oct 02 14:31:25 2012 -0700
@@ -78,7 +78,7 @@
   $ cd ..
 
 
-Constrcut new repo:
+Construct new repo:
 
   $ hg init b
   $ cd b
--- a/tests/test-rebase-bookmarks.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-rebase-bookmarks.t	Tue Oct 02 14:31:25 2012 -0700
@@ -23,7 +23,7 @@
   adding b
   $ hg book 'X'
   $ hg book 'Y'
- 
+
   $ echo c > c
   $ hg ci -Am C
   adding c
@@ -38,7 +38,7 @@
 
   $ hg book W
 
-  $ hg tglog 
+  $ hg tglog
   @  3: 'D' bookmarks: W
   |
   | o  2: 'C' bookmarks: Y Z
@@ -47,7 +47,7 @@
   |/
   o  0: 'A' bookmarks:
   
- 
+
 Move only rebased bookmarks
 
   $ cd ..
@@ -59,7 +59,7 @@
   $ hg rebase -s Y -d 3
   saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
 
-  $ hg tglog 
+  $ hg tglog
   @  3: 'C' bookmarks: Y Z
   |
   o  2: 'D' bookmarks: W
@@ -79,7 +79,7 @@
   $ hg rebase -s 1 -d 3
   saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
 
-  $ hg tglog 
+  $ hg tglog
   @  3: 'C' bookmarks: Y Z
   |
   o  2: 'B' bookmarks: X
--- a/tests/test-rebase-cache.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-rebase-cache.t	Tue Oct 02 14:31:25 2012 -0700
@@ -73,7 +73,7 @@
   $ hg clone -q -u . a a1
   $ cd a1
 
-  $ hg tglog  
+  $ hg tglog
   @  8: 'F' branch3
   |
   o  7: 'branch3' branch3
@@ -120,7 +120,7 @@
   2: 'B' branch1
   0: 'A' 
 
-  $ hg tglog  
+  $ hg tglog
   @  8: 'E' branch3
   |
   o  7: 'D' branch3
@@ -244,7 +244,7 @@
   2: 'B' branch1
   0: 'A' 
 
-  $ hg tglog   
+  $ hg tglog
   @  7: 'F' branch2
   |
   o  6: 'E' branch2
--- a/tests/test-rebase-conflicts.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-rebase-conflicts.t	Tue Oct 02 14:31:25 2012 -0700
@@ -69,7 +69,7 @@
 
 Try to continue without solving the conflict:
 
-  $ hg rebase --continue 
+  $ hg rebase --continue
   abort: unresolved merge conflicts (see hg help resolve)
   [255]
 
--- a/tests/test-rebase-mq-skip.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-rebase-mq-skip.t	Tue Oct 02 14:31:25 2012 -0700
@@ -39,7 +39,7 @@
   $ hg add p1
   $ hg qref -m P1
 
-  $ hg export qtip > p1.patch 
+  $ hg export qtip > p1.patch
 
   $ hg up -q -C 1
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-rebase-obsolete.t	Tue Oct 02 14:31:25 2012 -0700
@@ -0,0 +1,282 @@
+==========================
+Test rebase with obsolete
+==========================
+
+Enable obsolete
+
+  $ cat > ${TESTTMP}/obs.py << EOF
+  > import mercurial.obsolete
+  > mercurial.obsolete._enabled = True
+  > EOF
+  $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > logtemplate= {rev}:{node|short} {desc|firstline}
+  > [phases]
+  > publish=False
+  > [extensions]'
+  > rebase=
+  > 
+  > obs=${TESTTMP}/obs.py
+  > EOF
+
+Setup rebase canonical repo
+
+  $ hg init base
+  $ cd base
+  $ hg unbundle "$TESTDIR/bundles/rebase.hg"
+  adding changesets
+  adding manifests
+  adding file changes
+  added 8 changesets with 7 changes to 7 files (+2 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg up tip
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log -G
+  @  7:02de42196ebe H
+  |
+  | o  6:eea13746799a G
+  |/|
+  o |  5:24b6387c8c8c F
+  | |
+  | o  4:9520eea781bc E
+  |/
+  | o  3:32af7686d403 D
+  | |
+  | o  2:5fddd98957c8 C
+  | |
+  | o  1:42ccdea3bb16 B
+  |/
+  o  0:cd010b8cd998 A
+  
+  $ cd ..
+
+simple rebase
+---------------------------------
+
+  $ hg clone base simple
+  updating to branch default
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd simple
+  $ hg up 32af7686d403
+  3 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg rebase -d eea13746799a
+  $ hg log -G
+  @  10:8eeb3c33ad33 D
+  |
+  o  9:2327fea05063 C
+  |
+  o  8:e4e5be0395b2 B
+  |
+  | o  7:02de42196ebe H
+  | |
+  o |  6:eea13746799a G
+  |\|
+  | o  5:24b6387c8c8c F
+  | |
+  o |  4:9520eea781bc E
+  |/
+  o  0:cd010b8cd998 A
+  
+  $ hg log --hidden -G
+  @  10:8eeb3c33ad33 D
+  |
+  o  9:2327fea05063 C
+  |
+  o  8:e4e5be0395b2 B
+  |
+  | o  7:02de42196ebe H
+  | |
+  o |  6:eea13746799a G
+  |\|
+  | o  5:24b6387c8c8c F
+  | |
+  o |  4:9520eea781bc E
+  |/
+  | x  3:32af7686d403 D
+  | |
+  | x  2:5fddd98957c8 C
+  | |
+  | x  1:42ccdea3bb16 B
+  |/
+  o  0:cd010b8cd998 A
+  
+  $ hg debugobsolete
+  42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 {'date': '*', 'user': 'test'} (glob)
+  5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 {'date': '*', 'user': 'test'} (glob)
+  32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 {'date': '*', 'user': 'test'} (glob)
+
+
+  $ cd ..
+
+empty changeset
+---------------------------------
+
+  $ hg clone base empty
+  updating to branch default
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd empty
+  $ hg up eea13746799a
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+We make a copy of both the first changeset in the rebased and some other in the
+set.
+
+  $ hg graft 42ccdea3bb16 32af7686d403
+  grafting revision 1
+  grafting revision 3
+  $ hg rebase  -s 42ccdea3bb16 -d .
+  $ hg log -G
+  @  10:5ae4c968c6ac C
+  |
+  o  9:08483444fef9 D
+  |
+  o  8:8877864f1edb B
+  |
+  | o  7:02de42196ebe H
+  | |
+  o |  6:eea13746799a G
+  |\|
+  | o  5:24b6387c8c8c F
+  | |
+  o |  4:9520eea781bc E
+  |/
+  o  0:cd010b8cd998 A
+  
+  $ hg log --hidden -G
+  @  10:5ae4c968c6ac C
+  |
+  o  9:08483444fef9 D
+  |
+  o  8:8877864f1edb B
+  |
+  | o  7:02de42196ebe H
+  | |
+  o |  6:eea13746799a G
+  |\|
+  | o  5:24b6387c8c8c F
+  | |
+  o |  4:9520eea781bc E
+  |/
+  | x  3:32af7686d403 D
+  | |
+  | x  2:5fddd98957c8 C
+  | |
+  | x  1:42ccdea3bb16 B
+  |/
+  o  0:cd010b8cd998 A
+  
+  $ hg debugobsolete
+  42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 08483444fef91d6224f6655ee586a65d263ad34c 0 {'date': '*', 'user': 'test'} (glob)
+  5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 {'date': '*', 'user': 'test'} (glob)
+  32af7686d403cf45b5d95f2d70cebea587ac806a 5ae4c968c6aca831df823664e706c9d4aa34473d 0 {'date': '*', 'user': 'test'} (glob)
+
+
+  $ cd ..
+
+collapse rebase
+---------------------------------
+
+  $ hg clone base collapse
+  updating to branch default
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd collapse
+  $ hg rebase  -s 42ccdea3bb16 -d eea13746799a --collapse
+  $ hg log -G
+  @  8:4dc2197e807b Collapsed revision
+  |
+  | o  7:02de42196ebe H
+  | |
+  o |  6:eea13746799a G
+  |\|
+  | o  5:24b6387c8c8c F
+  | |
+  o |  4:9520eea781bc E
+  |/
+  o  0:cd010b8cd998 A
+  
+  $ hg log --hidden -G
+  @  8:4dc2197e807b Collapsed revision
+  |
+  | o  7:02de42196ebe H
+  | |
+  o |  6:eea13746799a G
+  |\|
+  | o  5:24b6387c8c8c F
+  | |
+  o |  4:9520eea781bc E
+  |/
+  | x  3:32af7686d403 D
+  | |
+  | x  2:5fddd98957c8 C
+  | |
+  | x  1:42ccdea3bb16 B
+  |/
+  o  0:cd010b8cd998 A
+  
+  $ hg id --debug
+  4dc2197e807bae9817f09905b50ab288be2dbbcf tip
+  $ hg debugobsolete
+  42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 {'date': '*', 'user': 'test'} (glob)
+  5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 {'date': '*', 'user': 'test'} (glob)
+  32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 {'date': '*', 'user': 'test'} (glob)
+
+  $ cd ..
+
+Rebase set has hidden descendants
+---------------------------------
+
+We rebase a changeset which has a hidden changeset. The hidden changeset must
+not be rebased.
+
+  $ hg clone base hidden
+  updating to branch default
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd hidden
+  $ hg rebase -s 5fddd98957c8 -d eea13746799a
+  $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
+  $ hg log -G
+  @  10:7c6027df6a99 B
+  |
+  | o  9:cf44d2f5a9f4 D
+  | |
+  | o  8:e273c5e7d2d2 C
+  | |
+  o |  7:02de42196ebe H
+  | |
+  | o  6:eea13746799a G
+  |/|
+  o |  5:24b6387c8c8c F
+  | |
+  | o  4:9520eea781bc E
+  |/
+  o  0:cd010b8cd998 A
+  
+  $ hg log --hidden -G
+  @  10:7c6027df6a99 B
+  |
+  | o  9:cf44d2f5a9f4 D
+  | |
+  | o  8:e273c5e7d2d2 C
+  | |
+  o |  7:02de42196ebe H
+  | |
+  | o  6:eea13746799a G
+  |/|
+  o |  5:24b6387c8c8c F
+  | |
+  | o  4:9520eea781bc E
+  |/
+  | x  3:32af7686d403 D
+  | |
+  | x  2:5fddd98957c8 C
+  | |
+  | x  1:42ccdea3bb16 B
+  |/
+  o  0:cd010b8cd998 A
+  
+  $ hg debugobsolete
+  5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 {'date': '*', 'user': 'test'} (glob)
+  32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 {'date': '*', 'user': 'test'} (glob)
+  42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 {'date': '*', 'user': 'test'} (glob)
+
+  $ cd ..
--- a/tests/test-record.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-record.t	Tue Oct 02 14:31:25 2012 -0700
@@ -246,10 +246,19 @@
   +9
   +10
   
+Modify end of plain file with username unset
+
+  $ echo 11 >> plain
+  $ unset HGUSER
+  $ hg record --config ui.username= -d '8 0' -m end plain
+  abort: no username supplied (see "hg help config")
+  [255]
+
 
 Modify end of plain file
 
-  $ echo 11 >> plain
+  $ HGUSER="test"
+  $ export HGUSER
   $ hg record -d '8 0' -m end plain <<EOF
   > y
   > y
--- a/tests/test-rename.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-rename.t	Tue Oct 02 14:31:25 2012 -0700
@@ -66,7 +66,7 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ rm d2/c
 
-rename --after a single file to a nonexistant target filename
+rename --after a single file to a nonexistent target filename
 
   $ hg rename --after d1/a dummy
   d1/a: not recording move - dummy does not exist (glob)
--- a/tests/test-run-tests.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-run-tests.t	Tue Oct 02 14:31:25 2012 -0700
@@ -95,5 +95,5 @@
 
 Exit code:
 
-  $ (exit 1) 
+  $ (exit 1)
   [1]
--- a/tests/test-share.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-share.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
   $ echo "[extensions]"      >> $HGRCPATH
   $ echo "share = "          >> $HGRCPATH
@@ -127,3 +127,8 @@
   c2e0ac586386 tip
 
   $ cd ..
+
+Explicitly kill daemons to let the test exit on Windows
+
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
+
--- a/tests/test-simplemerge.py	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-simplemerge.py	Tue Oct 02 14:31:25 2012 -0700
@@ -138,7 +138,7 @@
                     ['aaa', 'bbb', 'zz'],
                     ['zz'])
 
-        # todo: should use a sentinal at end as from get_matching_blocks
+        # todo: should use a sentinel at end as from get_matching_blocks
         # to match without zz
         self.assertEquals(list(m3.find_sync_regions()),
                           [(0, 1,  2, 3,  0, 1),
@@ -156,7 +156,7 @@
         m3 = Merge3([],
                     ['aaa', 'bbb'],
                     [])
-        # todo: should use a sentinal at end as from get_matching_blocks
+        # todo: should use a sentinel at end as from get_matching_blocks
         # to match without zz
         self.assertEquals(list(m3.find_sync_regions()),
                           [(0, 0,  2, 2,  0, 0)])
--- a/tests/test-static-http.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-static-http.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
 #if windows
   $ hg clone http://localhost:$HGPORT/ copy
@@ -25,10 +25,27 @@
   >     httpd.serve_forever()
   > 
   > signal.signal(signal.SIGTERM, lambda x, y: sys.exit(0))
+  > fp = file('dumb.pid', 'wb')
+  > fp.write(str(os.getpid()) + '\n')
+  > fp.close()
   > run()
   > EOF
   $ python dumb.py 2>/dev/null &
-  $ echo $! >> $DAEMON_PIDS
+
+Cannot just read $!, it will not be set to the right value on Windows/MinGW
+
+  $ cat > wait.py <<EOF
+  > import time
+  > while True:
+  >     try:
+  >         if '\n' in file('dumb.pid', 'rb').read():
+  >             break
+  >     except IOError:
+  >         pass
+  >     time.sleep(0.2)
+  > EOF
+  $ python wait.py
+  $ cat dumb.pid >> $DAEMON_PIDS
   $ hg init remote
   $ cd remote
   $ echo foo > bar
@@ -97,8 +114,8 @@
 trying clone -r
 
   $ cd ..
-  $ hg clone -r donotexist static-http://localhost:$HGPORT/remote local0
-  abort: unknown revision 'donotexist'!
+  $ hg clone -r doesnotexist static-http://localhost:$HGPORT/remote local0
+  abort: unknown revision 'doesnotexist'!
   [255]
   $ hg clone -r 0 static-http://localhost:$HGPORT/remote local0
   adding changesets
@@ -171,4 +188,4 @@
   $ hg clone static-http://localhost:$HGPORT/notarepo local3
   abort: 'http://localhost:$HGPORT/notarepo' does not appear to be an hg repository!
   [255]
-  $ kill $!
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
--- a/tests/test-status.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-status.t	Tue Oct 02 14:31:25 2012 -0700
@@ -330,4 +330,9 @@
   $ hg status -A --rev 1 1
   R 1/2/3/4/5/b.txt
 
+#if windows
+  $ hg --config ui.slash=false status -A --rev 1 1
+  R 1\2\3\4\5\b.txt
+#endif
+
   $ cd ..
--- a/tests/test-subrepo-deep-nested-change.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-subrepo-deep-nested-change.t	Tue Oct 02 14:31:25 2012 -0700
@@ -100,7 +100,7 @@
    revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
 
 Check that deep archiving works
- 
+
   $ cd cloned
   $ echo 'test' > sub1/sub2/test.txt
   $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
--- a/tests/test-subrepo-git.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-subrepo-git.t	Tue Oct 02 14:31:25 2012 -0700
@@ -446,7 +446,7 @@
   $ git rev-parse HEAD
   da5f5b1d8ffcf62fb8327bcd3c89a4367a6018e7
   $ cd ..
-  $ hg update --clean tip > /dev/null 2>&1 
+  $ hg update --clean tip > /dev/null 2>&1
 
 Sticky subrepository, revision updates
   $ hg id -n
--- a/tests/test-subrepo-missing.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-subrepo-missing.t	Tue Oct 02 14:31:25 2012 -0700
@@ -60,7 +60,7 @@
   warning: subrepo spec file .hgsub not found
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ rm .hgsubstate
-  $ hg up 0  
+  $ hg up 0
   remote changed .hgsubstate which local deleted
   use (c)hanged version or leave (d)eleted? c
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-subrepo-relative-path.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-subrepo-relative-path.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
 Preparing the subrepository 'sub'
 
@@ -70,7 +70,7 @@
    source   ../sub
    revision 863c1745b441bd97a8c4a096e87793073f4fb215
 
-  $ "$TESTDIR/killdaemons.py"
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
 
 subrepo paths with ssh urls
 
--- a/tests/test-subrepo.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-subrepo.t	Tue Oct 02 14:31:25 2012 -0700
@@ -730,7 +730,7 @@
   925c17564ef8 tip
   $ hg -R s id
   12a213df6fa9 tip
-  $ hg -R t id  
+  $ hg -R t id
   52c0adc0515a tip
   $ hg update 11
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -738,7 +738,7 @@
   365661e5936a
   $ hg -R s id
   fc627a69481f
-  $ hg -R t id  
+  $ hg -R t id
   e95bcfa18a35
 
 Sticky subrepositorys, file changes
@@ -750,7 +750,7 @@
   365661e5936a+
   $ hg -R s id
   fc627a69481f+
-  $ hg -R t id  
+  $ hg -R t id
   e95bcfa18a35+
   $ hg update tip
    subrepository sources for s differ
@@ -764,7 +764,7 @@
   925c17564ef8+ tip
   $ hg -R s id
   fc627a69481f+
-  $ hg -R t id  
+  $ hg -R t id
   e95bcfa18a35+
   $ hg update --clean tip
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -774,7 +774,7 @@
   925c17564ef8 tip
   $ hg -R s id
   12a213df6fa9 tip
-  $ hg -R t id  
+  $ hg -R t id
   52c0adc0515a tip
   $ cd s
   $ hg update -r -2
@@ -792,7 +792,7 @@
   e45c8b14af55+
   $ hg -R s id
   02dcf1d70411
-  $ hg -R t id  
+  $ hg -R t id
   7af322bc1198
 
 Sticky subrepository, file changes and revision updates
@@ -804,7 +804,7 @@
   e45c8b14af55+
   $ hg -R s id
   02dcf1d70411+
-  $ hg -R t id  
+  $ hg -R t id
   7af322bc1198+
   $ hg update tip
    subrepository sources for s differ
@@ -818,7 +818,7 @@
   925c17564ef8+ tip
   $ hg -R s id
   02dcf1d70411+
-  $ hg -R t id  
+  $ hg -R t id
   7af322bc1198+
 
 Sticky repository, update --clean
@@ -828,7 +828,7 @@
   925c17564ef8 tip
   $ hg -R s id
   12a213df6fa9 tip
-  $ hg -R t id  
+  $ hg -R t id
   52c0adc0515a tip
 
 Test subrepo already at intended revision:
@@ -843,7 +843,7 @@
   11+
   $ hg -R s id
   fc627a69481f
-  $ hg -R t id 
+  $ hg -R t id
   e95bcfa18a35
 
 Test that removing .hgsubstate doesn't break anything:
--- a/tests/test-symlinks.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-symlinks.t	Tue Oct 02 14:31:25 2012 -0700
@@ -46,7 +46,7 @@
   $ rm dir/a.o
   $ rm dir/b.o
   $ mkdir dir/a.o
-  $ ln -s nonexist dir/b.o
+  $ ln -s nonexistent dir/b.o
   $ mkfifo a.c
 
 it should show a.c, dir/a.o and dir/b.o deleted
--- a/tests/test-tags.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-tags.t	Tue Oct 02 14:31:25 2012 -0700
@@ -137,7 +137,7 @@
   $ echo >> .hgtags
   $ echo "foo bar" >> .hgtags
   $ echo "a5a5 invalid" >> .hg/localtags
-  $ cat .hgtags 
+  $ cat .hgtags
   acb14030fe0a21b60322c440ad2d20cf7685a376 first
   spam
   
--- a/tests/test-template-engine.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-template-engine.t	Tue Oct 02 14:31:25 2012 -0700
@@ -36,4 +36,12 @@
   $ hg log --style=./mymap
   0 97e5f848f0936960273bbf75be6388cd0350a32b test
 
+  $ cat > changeset.txt << EOF
+  > {{p1rev}} {{p1node}} {{p2rev}} {{p2node}}
+  > EOF
+  $ hg ci -Ama
+  $ hg log --style=./mymap
+  0 97e5f848f0936960273bbf75be6388cd0350a32b -1 0000000000000000000000000000000000000000
+  -1 0000000000000000000000000000000000000000 -1 0000000000000000000000000000000000000000
+
   $ cd ..
--- a/tests/test-transplant.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-transplant.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
   $ cat <<EOF >> $HGRCPATH
   > [extensions]
@@ -632,3 +632,7 @@
   skipping emptied changeset 7a7d57e15850
   $ cd ..
 
+Explicitly kill daemons to let the test exit on Windows
+
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
+
--- a/tests/test-treediscovery-legacy.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-treediscovery-legacy.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
 Tests discovery against servers without getbundle support:
 
@@ -35,7 +35,7 @@
   >   cat hg.pid >> $DAEMON_PIDS
   > }
   $ tstop() {
-  >   "$TESTDIR/killdaemons.py"
+  >   "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   >   cp $HGRCPATH-withcap $HGRCPATH
   > }
 
@@ -330,7 +330,7 @@
   $ hg ci -Am A
   adding A
   $ cd ..
-  $ hg clone rlocal rremote  
+  $ hg clone rlocal rremote
   updating to branch default
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd rlocal
@@ -341,7 +341,7 @@
   $ tstart rremote
 
   $ cd rlocal
-  $ hg incoming $remote 
+  $ hg incoming $remote
   comparing with http://localhost:$HGPORT/
   searching for changes
   no changes found
--- a/tests/test-treediscovery.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-treediscovery.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
 Tests discovery against servers without getbundle support:
 
@@ -23,7 +23,7 @@
   >   cat hg.pid >> $DAEMON_PIDS
   > }
   $ tstop() {
-  >   "$TESTDIR/killdaemons.py"
+  >   "$TESTDIR/killdaemons.py" $DAEMON_PIDS
   > }
 
 Both are empty:
--- a/tests/test-unbundlehash.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-unbundlehash.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
 Test wire protocol unbundle with hashed heads (capability: unbundlehash)
 
@@ -30,3 +30,8 @@
 
   $ cat access.log | grep unbundle
   * - - [*] "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+6768033e216468247bd031a0a2d9876d79818f8f (glob)
+
+Explicitly kill daemons to let the test exit on Windows
+
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
+
--- a/tests/test-update-branches.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-update-branches.t	Tue Oct 02 14:31:25 2012 -0700
@@ -61,7 +61,7 @@
   >     hg up $opt $targetrev
   >     hg parent --template 'parent={rev}\n'
   >     hg stat -S
-  > }    
+  > }
 
   $ norevtest () {
   >     msg=$1
@@ -74,7 +74,7 @@
   >     hg up $opt
   >     hg parent --template 'parent={rev}\n'
   >     hg stat -S
-  > }    
+  > }
 
 Test cases are documented in a table in the update function of merge.py.
 Cases are run as shown in that table, row by row.
--- a/tests/test-wireproto.t	Mon Oct 01 23:11:28 2012 -0500
+++ b/tests/test-wireproto.t	Tue Oct 02 14:31:25 2012 -0700
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+  $ "$TESTDIR/hghave" killdaemons || exit 80
 
 Test wire protocol argument passing
 
@@ -112,3 +112,7 @@
   $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo eins zwei --five fuenf
   eins zwei None None None
 
+Explicitly kill daemons to let the test exit on Windows
+
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
+