changeset 3568:23f7d9621783

Merge with upstream
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 27 Oct 2006 23:09:46 +0200
parents 3bab1fc0ab75 (diff) ece5c53577eb (current diff)
children a27d90c9336e
files hgext/mq.py mercurial/commands.py mercurial/localrepo.py mercurial/util.py
diffstat 16 files changed, 1359 insertions(+), 561 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/vim/hgcommand.vim
+++ b/contrib/vim/hgcommand.vim
@@ -29,10 +29,10 @@
 " completes.  This allows various actions to only be taken by functions after
 " system initialization.
 
-if exists("loaded_hgcommand")
+if exists("g:loaded_hgcommand")
    finish
 endif
-let loaded_hgcommand = 1
+let g:loaded_hgcommand = 1
 
 " store 'compatible' settings
 let s:save_cpo = &cpo
@@ -45,7 +45,7 @@ function! s:HGCleanupOnFailure(err)
   echohl WarningMsg
   echomsg s:script_name . ":" a:err "Plugin not loaded"
   echohl None
-  let loaded_hgcommand = "no"
+  let g:loaded_hgcommand = "no"
   unlet s:save_cpo s:script_name
 endfunction
 
@@ -372,7 +372,7 @@ function! s:HGGetStatusVars(revisionVar,
       let revision="ADDED"
     else
       " The file is tracked, we can try to get is revision number
-      let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " parents -b  "
+      let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " parents "
       let statustext=system(hgCommand)
       if(v:shell_error)
           return ""
@@ -566,11 +566,11 @@ function! s:HGInstallDocumentation(full_
   1
   " Delete from first line to a line starts with
   " === START_DOC
-  silent 1,/^=\{3,}\s\+START_DOC\C/ d
+  silent 1,/^=\{3,}\s\+START_DOC\C/ delete _
   " Delete from a line starts with
   " === END_DOC
   " to the end of the documents:
-  silent /^=\{3,}\s\+END_DOC\C/,$ d
+  silent /^=\{3,}\s\+END_DOC\C/,$ delete _
 
   " Add modeline for help doc: the modeline string is mangled intentionally
   " to avoid it be recognized by VIM:
@@ -1048,7 +1048,7 @@ com! HGDisableBufferSetup call HGDisable
 com! HGEnableBufferSetup call HGEnableBufferSetup()
 
 " Allow reloading hgcommand.vim
-com! HGReload unlet! loaded_hgcommand | runtime plugin/hgcommand.vim
+com! HGReload unlet! g:loaded_hgcommand | runtime plugin/hgcommand.vim
 
 " Section: Plugin command mappings {{{1
 nnoremap <silent> <Plug>HGAdd :HGAdd<CR>
@@ -1200,7 +1200,7 @@ delfunction <SID>HGFlexiMkdir
 delfunction <SID>HGCleanupOnFailure
 unlet s:script_version s:script_name
 
-let loaded_hgcommand=2
+let g:loaded_hgcommand=2
 silent do HGCommand User HGPluginFinish
 
 let &cpo = s:save_cpo
--- a/contrib/zsh_completion
+++ b/contrib/zsh_completion
@@ -5,6 +5,7 @@
 # instance)
 #
 # Copyright (C) 2005 Steve Borho
+# Copyright (C) 2006 Brendan Cully <brendan@kublai.com>
 #
 # This is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free
@@ -13,499 +14,663 @@
 #
 
 local curcontext="$curcontext" state line
-typeset -A opt_args
-local repos newFiles addedFiles includeExclude commitMessage
-local ridx _hgroot
+typeset -A _hg_cmd_globals
+
+_hg() {
+  local cmd
+  integer i=2
+  _hg_cmd_globals=()
+
+  while (( i < $#words ))
+  do
+    case "$words[$i]" in
+      -R|--repository|--cwd|--config)
+        # pass along arguments to hg completer
+        _hg_cmd_globals+="$words[$i]"
+        _hg_cmd_globals+="$words[$i+1]"
+        (( i += 2 ))
+        continue
+      ;;
+      -R*)
+        _hg_cmd_globals+="$words[$i]"
+       (( i++ ))
+       continue
+      ;;
+      -*)
+        # skip option
+        (( i++ ))
+        continue
+      ;;
+    esac
+    if [[ -z "$cmd" ]]
+    then
+      cmd="$words[$i]"
+      words[$i]=()
+      (( CURRENT-- ))
+    fi
+    (( i++ ))
+  done
+
+  if [[ -z "$cmd" ]]
+  then
+    _arguments -s -w : $_hg_global_opts \
+    ':mercurial command:_hg_commands'
+    return
+  fi
+
+  # resolve abbreviations and aliases
+  if ! (( $+functions[_hg_cmd_${cmd}] ))
+  then
+    local cmdexp
+    (( $#_hg_cmd_list )) || _hg_get_commands
+
+    cmdexp=$_hg_cmd_list[(r)${cmd}*]
+    if [[ $cmdexp == $_hg_cmd_list[(R)${cmd}*] ]]
+    then
+      # might be nice to rewrite the command line with the expansion
+      cmd="$cmdexp"
+    fi
+    if [[ -n $_hg_alias_list[$cmd] ]]
+    then
+      cmd=$_hg_alias_list[$cmd]
+    fi
+  fi
+
+  if (( $+functions[_hg_cmd_${cmd}] ))
+  then
+    curcontext="${curcontext%:*:*}:hg-${cmd}:"
+    _hg_cmd_${cmd}
+    return
+  fi
+}
 
-# FIXME: why isn't opt_args available?
-[[ -d .hg ]] && _hgroot="$PWD"
-ridx=$words[(i)-R]
-(( $ridx < $#words )) && _hgroot="${words[$ridx+1]}"
+_hg_get_commands() {
+  typeset -ga _hg_cmd_list
+  typeset -gA _hg_alias_list
+  local hline cmd cmdalias
+  _call_program help hg --verbose help | while read -A hline
+  do
+    cmd="$hline[1]"
+    case $cmd in
+      *:)
+        cmd=${cmd%:}
+        _hg_cmd_list+=($cmd)
+      ;;
+      *,)
+        cmd=${cmd%,}
+        _hg_cmd_list+=($cmd)
+        integer i=2
+        while (( i <= $#hline ))
+        do
+          cmdalias=${hline[$i]%(:|,)}
+          _hg_cmd_list+=($cmdalias)
+          _hg_alias_list+=($cmdalias $cmd)
+          (( i++ ))
+        done
+      ;;
+    esac
+  done
+}
+
+_hg_commands() {
+  (( $#_hg_cmd_list )) || _hg_get_commands
+  _describe -t commands 'mercurial command' _hg_cmd_list
+}
+
+_hg_revrange() {
+  compset -P 1 '*:'
+  _hg_tags "$@"
+}
+
+_hg_tags() {
+  typeset -a tags
+  local tag rev
 
-# hg dispatch (borrowed from _cvs)
-(( $+functions[_hg_cmd] )) ||
-_hg_cmd () {
-  _call_program hg hg -R "$_hgroot" "$@"
+  _hg_cmd tags 2> /dev/null | while read tag rev
+  do
+    tags+=($tag)
+  done
+  (( $#tags )) && _describe -t tags 'tags' tags
+}
+
+_hg_status() {
+  status_files=(${(ps:\0:)"$(_hg_cmd status -0n$1 . 2>/dev/null)"})
+}
+
+_hg_unknown() {
+  typeset -a status_files
+  _hg_status u
+  (( $#status_files )) && _describe -t files 'unknown files' status_files
+}
+
+_hg_missing() {
+  typeset -a status_files
+  _hg_status d
+  (( $#status_files )) && _describe -t files 'missing files' status_files
+}
+
+_hg_addremove() {
+  _alternative 'files:unknown files:_hg_unknown' \
+    'files:missing files:_hg_missing'
+}
+
+_hg_urls() {
+  if compset -P bundle://
+  then
+    _files
+  elif [[ -prefix *: ]]
+  then
+    _urls
+  else
+    local expl
+    compset -S '[^:]*'
+    _wanted url-schemas expl 'URL schema' compadd -S '' - \
+      http:// https:// ssh:// bundle://
+  fi
 }
 
-(( $+functions[_hg_state] )) ||
-_hg_state () {
-  case "$state" in
-    (tags)
-      compadd $(_hg_cmd tags 2> /dev/null |
-        sed -e 's/[0-9]*:[a-f0-9]*$//; s/ *$//')
-    ;;
-    (qapplied)
-      compadd $(_hg_cmd qapplied)
-    ;;
-    (qunapplied)
-      compadd $(_hg_cmd qunapplied)
-    ;;
-  esac
+_hg_paths() {
+  typeset -a paths pnames
+  _hg_cmd paths 2> /dev/null | while read -A pnames
+  do
+    paths+=($pnames[1])
+  done
+  (( $#paths )) && _describe -t path-aliases 'repository alias' paths
+}
+
+_hg_remote() {
+  _alternative 'path-aliases:repository alias:_hg_paths' \
+    'directories:directory:_files -/' \
+    'urls:URL schema:_hg_urls'
+}
+
+# Common options
+_hg_global_opts=(
+    '(--repository -R)'{-R+,--repository}'[repository root directory]:repository:_files -/'
+    '--cwd[change working directory]:new working directory:_files -/'
+    '(--noninteractive -y)'{-y,--noninteractive}'[do not prompt, assume yes for any required answers]'
+    '(--verbose -v)'{-v,--verbose}'[enable additional output]'
+    '(--quiet -q)'{-q,--quiet}'[suppress output]'
+    '(--help -h)'{-h,--help}'[display help and exit]'
+    '--debug[debug mode]'
+    '--debugger[start debugger]'
+    '--traceback[print traceback on exception]'
+    '--time[time how long the command takes]'
+    '--profile[profile]'
+    '--version[output version information and exit]'
+)
+
+_hg_pat_opts=(
+  '*'{-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_diff_opts=(
+  '(--text -a)'{-a,--text}'[treat all files as text]'
+  '(--git -g)'{-g,--git}'[use git extended diff format]'
+  "--nodates[don't include dates in diff headers]")
+
+_hg_dryrun_opts=(
+  '(--dry-run -n)'{-n,--dry-run}'[do not perform actions, just print output]')
+
+_hg_style_opts=(
+  '--style[display using template map file]:'
+  '--template[display with template]:')
+
+_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:'
+  '(-e --edit -m --message --logfile -l)'{-l+,--logfile}'[read the commit message from <file>]:log file:_files')
+
+_hg_remote_opts=(
+  '(--ssh -e)'{-e+,--ssh}'[specify ssh command to use]:'
+  '--remotecmd[specify hg command to run on the remote side]:')
+
+_hg_cmd() {
+  _call_program hg hg "$_hg_cmd_globals[@]" "$@"
+}
+
+_hg_cmd_add() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
+  '*:unknown files:_hg_unknown'
+}
+
+_hg_cmd_addremove() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
+  '(--similarity -s)'{-s+,--similarity}'[guess renamed files by similarity (0<=s<=100)]:' \
+  '*:unknown or missing files:_hg_addremove'
+}
+
+_hg_cmd_annotate() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
+  '(--rev -r)'{-r+,--rev}'[annotate the specified revision]:revision:_hg_tags' \
+  '(--follow -f)'{-f,--follow}'[follow file copies and renames]' \
+  '(--text -a)'{-a,--text}'[treat all files as text]' \
+  '(--user -u)'{-u,--user}'[list the author]' \
+  '(--date -d)'{-d,--date}'[list the date]' \
+  '(--number -n)'{-n,--number}'[list the revision number (default)]' \
+  '(--changeset -c)'{-c,--changeset}'[list the changeset]' \
+  '*:files:_files -W $(_hg_cmd root)'
+}
+
+_hg_cmd_archive() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_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_tags' \
+  '(--type -t)'{-t+,--type}'[type of distribution to create]:archive type:(files tar tbz2 tgz uzip zip)' \
+  '*:destination:_files'
+}
+
+_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_tags' \
+  ':output file:_files' \
+  ':destination repository:_files -/'
+}
+
+_hg_cmd_cat() {
+  _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_tags' \
+  '*:file:_files -W $(_hg_cmd root)'
+}
+
+_hg_cmd_clone() {
+  _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
+  '(--noupdate -U)'{-U,--noupdate}'[do not update the new working directory]' \
+  '(--rev -r)'{-r+,--rev}'[a changeset you would like to have after cloning]:' \
+  '--uncompressed[use uncompressed transfer (fast over LAN)]' \
+  ':source repository:_hg_remote' \
+  ':destination:_files -/'  
+}
+
+_hg_cmd_commit() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_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:_file -g \*.txt' \
+  '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \
+  '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \
+  '*:file:_files -W $(_hg_cmd root)'
+}
+
+_hg_cmd_copy() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
+  '(--after -A)'{-A,--after}'[record a copy that has already occurred]' \
+  '(--force -f)'{-f,--force}'[forcibly copy over an existing managed file]' \
+  '*:file:_files -W $(_hg_cmd root)'
+}
+
+_hg_cmd_diff() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_diff_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]' \
+  '*:file:_files -W $(_hg_cmd root)'
+}
+
+_hg_cmd_export() {
+  _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]' \
+  '*:revision:_hg_tags'
+}
+
+_hg_cmd_grep() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
+  '(--print0 -0)'{-0,--print0}'[end filenames with NUL]' \
+  '--all[print all revisions with matches]' \
+  '(--follow -f)'{-f,--follow}'[follow changeset or file history]' \
+  '(--ignore-case -i)'{-i,--ignore-case}'[ignore case when matching]' \
+  '(--files-with-matches -l)'{-l,--files-with-matches}'[print only filenames and revs that match]' \
+  '(--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]' \
+  '*:search pattern:_files -W $(_hg_cmd root)'
+}
+
+_hg_cmd_heads() {
+  _arguments -s -w : $_hg_global_opts $_hg_style_opts \
+  '(--rev -r)'{-r+,--rev}'[show only heads which are descendants of rev]:revision:_hg_tags'
+}
+
+_hg_cmd_help() {
+  _arguments -s -w : $_hg_global_opts \
+  '*:mercurial command:_hg_commands'
 }
 
-(( $+_hg_commands )) ||
-_hg_commands=($(hg -v help | sed -e '1,/^list of commands:/d' \
-      -e '/^global options:/,$d' -e '/^ [^ ]/!d; s/[,:].*//g;'))
-
-# A lot of commands have these arguments
-includeExclude=(
-        '*-I-[include names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/'
-        '*--include-[include names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/'
-        '*-X-[exclude names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/'
-        '*--exclude-[exclude names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/')
-
-styleOpts=(
-        '--style[display using template map file]:'
-        '--template[display with template]:')
-
-commitMessage=(
-        '(-m --message -l --logfile --edit)-e[edit commit message]'
-        '(-m --message -l --logfile -e)--edit[edit commit message]'
-        '(-e --edit -l --logfile --message)-m[use <text> as commit message]:message:'
-        '(-e --edit -l --logfile -m)--message[use <text> as commit message]:message:'
-        '(-e --edit -m --message --logfile)-l[read the commit message from <file>]:log file:_files'
-        '(-e --edit -m --message -l)--logfile[read the commit message from <file>]:log file:_files')
+_hg_cmd_import() {
+  _arguments -s -w : $_hg_global_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]' \
+  '*:patch:_files'
+}
 
-if [[ $service == "hg" ]]; then
-    _arguments -C -A "-*" \
-    '(--repository)-R[repository root directory]:root:_files -/' \
-    '(-R)--repository[repository root directory]:root:_files -/' \
-    '--cwd[change working directory]:new working directory:_files -/' \
-    '(--noninteractive)-y[do not prompt, assume yes for any required answers]' \
-    '(-y)--noninteractive[do not prompt, assume yes for any required answers]' \
-    '(--verbose)-v[enable additional output]' \
-    '(-v)--verbose[enable additional output]' \
-    '(--quiet)-q[suppress output]' \
-    '(-q)--quiet[suppress output]' \
-    '(--help)-h[display help and exit]' \
-    '(-h)--help[display help and exit]' \
-    '--debug[debug mode]' \
-    '--debugger[start debugger]' \
-    '--traceback[print traceback on exception]' \
-    '--time[time how long the command takes]' \
-    '--profile[profile]' \
-    '--version[output version information and exit]' \
-    '*::command:->subcmd' && return 0
+_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]' \
+  '(--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]' \
+  '(--newest-first -n)'{-n,--newest-first}'[show newest record first]' \
+  '--bundle[file to store the bundles into]:bundle file:_files' \
+  ':source:_hg_remote'
+}
 
-    if (( CURRENT == 1 )); then
-        _wanted commands expl 'hg command' compadd -a _hg_commands
-        return
-    fi
-    service="$words[1]"
-    curcontext="${curcontext%:*}=$service:"
-fi
+_hg_cmd_init() {
+  _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
+  ':dir:_files -/'
+}
+
+_hg_cmd_locate() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
+  '(--rev -r)'{-r+,--rev}'[search repository as it stood at revision]:revision:_hg_tags' \
+  '(--print0 -0)'{-0,--print0}'[end filenames with NUL, for use with xargs]' \
+  '(--fullpath -f)'{-f,--fullpath}'[print complete paths]' \
+  '*:search pattern:_files -W $(_hg_cmd root)'
+}
 
-case $service in
-    (add)
-        newFiles=(${(ps:\0:)"$(_hg_cmd status -0un .)"})
-        _arguments $includeExclude \
-        '*:file:->unknown'
-        _wanted files expl 'unknown files' compadd -a newFiles
-    ;;
+_hg_cmd_log() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_style_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_tags' \
+  '*:files:_files -W $(_hg_cmd root)'
+}
 
-    (addremove)
-        _arguments $includeExclude \
-        '*:directories:_files -/'  # assume they want to add/remove a dir
-    ;;
-
-    (forget)
-        addedFiles=(${(ps:\0:)"$(_hg_cmd status -0an .)"})
-        _arguments $includeExclude  \
-        '*:file:->added'
-        _wanted files expl 'newly added files' compadd -a addedFiles
-    ;;
-
-    (remove|rm)
-        _arguments $includeExclude \
-        '*:file:_files'
-    ;;
+_hg_cmd_manifest() {
+  _arguments -s -w : $_hg_global_opts \
+  ':revision:_hg_tags'
+}
 
-    (copy|cp)
-        _arguments $includeExclude \
-        '(--after)-A[record a copy that has already occurred]' \
-        '(-A)--after[record a copy that has already occurred]' \
-        '(--force)-f[forcibly copy over an existing managed file]' \
-        '(-f)--force[forcibly copy over an existing managed file]' \
-        '(--parents)-p[append source path to dest]' \
-        '(-p)--parents[append source path to dest]' \
-        '*:files:_files'
-    ;;
+_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]' \
+  '(--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]' \
+  '(--newest-first -n)'{-n,--newest-first}'[show newest record first]' \
+  ':destination:_hg_remote'
+}
 
-    (rename|mv)
-        if (( CURRENT == 2 )); then
-            _arguments $includeExclude \
-            '(--after)-A[record a rename that has already occurred]' \
-            '(-A)--after[record a rename that has already occurred]' \
-            '(--force)-f[replace destination if it exists]' \
-            '(-F)--force[replace destination if it exists]' \
-            '(--parents)-p[append source path to dest]' \
-            '(-p)--parents[append source path to dest]' \
-            '*:files:_files'
-        else
-            _arguments '*:destination:_files'
-        fi
-    ;;
+_hg_cmd_parents() {
+  _arguments -s -w : $_hg_global_opts $_hg_style_opts \
+  '(--rev -r)'{-r+,--rev}'[show parents of the specified rev]:revision:_hg_tags' \
+  ':revision:_hg_tags'
+}
 
-    (diff)
-        _arguments $includeExclude \
-        '*-r[revision]:revision:->tags' \
-        '*--rev[revision]:revision:->tags' \
-        '(--text)-a[treat all files as text]' \
-        '(-a)--text[treat all files as text]' \
-        '*:file:_files'
-    ;;
+_hg_cmd_paths() {
+  _arguments -s -w : $_hg_global_opts \
+  ':path:_hg_paths'
+}
+
+_hg_cmd_pull() {
+  _arguments -s -w : $_hg_global_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]' \
+  ':source:_hg_remote'
+}
 
-    (status|st)
-        _arguments $includeExclude \
-        '(--no-status)-n[hide status prefix]' \
-        '(-n)--no-status[hide status prefix]' \
-        '(--print0)-0[end filenames with NUL, for use with xargs]' \
-        '(-0)--print0[end filenames with NUL, for use with xargs]' \
-        '(--modified)-m[show only modified files]' \
-        '(-m)--modified[show only modified files]' \
-        '(--added)-a[show only added files]' \
-        '(-a)--added[show only added files]' \
-        '(--removed)-r[show only removed files]' \
-        '(-r)--removed[show only removed files]' \
-        '(--unknown)-u[show only unknown files]' \
-        '(-u)--unknown[show only unknown files]' \
-        '*:search pattern, then files:_files'
-    ;;
+_hg_cmd_push() {
+  _arguments -s -w : $_hg_global_opts $_hg_remote_opts \
+  '(--force -f)'{-f,--force}'[force push]' \
+  '(--rev -r)'{-r+,--rev}'[a specific revision you would like to push]' \
+  ':destination:_hg_remote'
+}
+
+_hg_cmd_remove() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
+  '(--after -A)'{-A,--after}'[record remove that has already occurred]' \
+  '(--force -f)'{-f,--force}'[remove file even if modified]' \
+  '*:file:_files -W $(_hg_cmd root)'
+}
 
-    (revert)
-        addedFiles=(${(ps:\0:)"$(_hg_cmd status -0amrn .)"})
-        _arguments \
-        '(--rev)-r[revision to revert to]:revision:->tags' \
-        '(-r)--rev[revision to revert to]:revision:->tags' \
-        '(--nonrecursive)-n[do not recurse into subdirectories]' \
-        '(-n)--nonrecursive[do not recurse into subdirectories]' \
-        '*:file:->modified'
-        _wanted files expl 'mofified files' compadd -a addedFiles
-    ;;
+_hg_cmd_rename() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
+  '(--after -A)'{-A,--after}'[record a rename that has already occurred]' \
+  '(--force -f)'{-f,--force}'[forcibly copy over an existing managed file]' \
+  '*:file:_files -W $(_hg_cmd root)'
+}
 
-    (commit|ci)
-        addedFiles=(${(ps:\0:)"$(_hg_cmd status -0amrn .)"})
-        _arguments $includeExclude \
-        '(--addremove)-A[run addremove during commit]' \
-        '(-A)--addremove[run addremove during commit]' \
-        '(--message)-m[use <txt> as commit message]:string:' \
-        '(-m)--message[use <txt> as commit message]:string:' \
-        '(--logfile)-l[read commit message from <file>]:.log file:_file -g \*.txt' \
-        '(-l)--logfile[read commit message from <file>]:.log file:_file -g \*.txt' \
-        '(--date)-d[record datecode as commit date]:date code:' \
-        '(-d)--date[record datecode as commit date]:date code:' \
-        '(--user)-u[record user as commiter]:user:' \
-        '(-u)--user[record user as commiter]:user:' \
-        '*:file:->modified'
-        _wanted files expl 'mofified files' compadd -a addedFiles
-    ;;
-
-    (cat)
-        _arguments $includeExclude \
-        '(--output)-o[print output to file with formatted name]:filespec:' \
-        '(-o)--output[print output to file with formatted name]:filespec:' \
-        '(--rev)-r[revision]:revision:->tags' \
-        '(-r)--rev[revision]:revision:->tags' \
-        '*:file:_files'
-    ;;
+_hg_cmd_revert() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \
+  '(--all -a :)'{-a,--all}'[revert all changes when no arguments given]' \
+  '(--rev -r)'{-r+,--rev}'[revision to revert to]:revision:_hg_tags' \
+  '--no-backup[do not save backup copies of files]' \
+  '*:file:_files -W $(_hg_cmd root)'
+}
 
-    (annotate)
-        _arguments $includeExclude \
-        '(--rev)-r[annotate the specified revision]:revision:->tags' \
-        '(-r)--rev[annotate the specified revision]:revision:->tags' \
-        '(--text)-a[treat all files as text]' \
-        '(-a)--text[treat all files as text]' \
-        '(--user)-u[list the author]' \
-        '(-u)--user[list the author]' \
-        '(--changeset)-c[list the changeset]' \
-        '(-c)--changeset[list the changeset]' \
-        '(--number)-n[list the revision number (default)]' \
-        '(-n)--number[list the revision number (default)]' \
-        '*:files:_files'
-    ;;
+_hg_cmd_serve() {
+  _arguments -s -w : $_hg_global_opts \
+  '(--accesslog -A)'{-A+,--accesslog}'[name of access log file]:log file:_files' \
+  '(--errorlog -E)'{-E+,--errorlog}'[name of error log file]:log file:_files' \
+  '(--daemon -d)'{-d,--daemon}'[run server in background]' \
+  '(--port -p)'{-p+,--port}'[listen port]:listen port:' \
+  '(--address -a)'{-a+,--address}'[interface address]:interface address:' \
+  '(--name -n)'{-n+,--name}'[name to show in web pages]:repository name:' \
+  '(--templates -t)'{-t,--templates}'[web template directory]:template dir:_files -/' \
+  '--style[web template style]:style' \
+  '--stdio[for remote clients]' \
+  '(--ipv6 -6)'{-6,--ipv6}'[use IPv6 in addition to IPv4]'
+}
 
-    (grep)
-        _arguments $includeExclude \
-        '*-r[search in given revision range]:revision:->tags' \
-        '*--rev[search in given revision range]:revision:->tags' \
-        '--all[print all revisions with matches]' \
-        '(-print0)-0[end filenames with NUL, for use with xargs]' \
-        '(-0)--print0[end filenames with NUL, for use with xargs]' \
-        '(--ignore-case)-i[ignore case when matching]' \
-        '(-i)--ignore-case[ignore case when matching]' \
-        '(--files-with-matches)-l[print names of files and revs that match]' \
-        '(-l)--files-with-matches[print names of files and revs that match]' \
-        '(--line-number)-n[print matching line numbers]' \
-        '(-n)--line-number[print matching line numbers]' \
-        '(--user)-u[print user who committed change]' \
-        '(-u)--user[print user who committed change]' \
-        '*:search pattern:'
-    ;;
+_hg_cmd_status() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_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]' \
+  '(--removed -r)'{-r,--removed}'[show only removed files]' \
+  '(--deleted -d)'{-d,--deleted}'[show only deleted (but tracked) files]' \
+  '(--clean -c)'{-c,--clean}'[show only files without changes]' \
+  '(--unknown -u)'{-u,--unknown}'[show only unknown files]' \
+  '(--ignored -i)'{-i,--ignored}'[show ignored files]' \
+  '(--no-status -n)'{-n,--no-status}'[hide status prefix]' \
+  '(--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_tags' \
+  '*:files:_files'
+}
 
-    (locate)
-        _arguments $includeExclude \
-        '(--rev)-r[search repository as it stood at revision]:revision:->tags' \
-        '(-r)--rev[search repository as it stood at revision]:revision:->tags' \
-        '(--print0)-0[end filenames with NUL, for use with xargs]' \
-        '(-0)--print0[end filenames with NUL, for use with xargs]' \
-        '(--fullpath)-f[print complete paths]' \
-        '(-f)--fullpath[print complete paths]' \
-        '*:search pattern:'
-    ;;
+_hg_cmd_tag() {
+  _arguments -s -w : $_hg_global_opts \
+  '(--local -l)'{-l,--local}'[make the tag local]' \
+  '(--message -m)'{-m+,--message}'[message for tag commit log entry]:message:' \
+  '(--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_tags' \
+  ':tag name:'
+}
 
-    (log|history)
-        _arguments $includeExclude $styleOpts \
-        '*-r[show the specified revision or range]:revision:->tags' \
-        '*--rev[show the specified revision or range]:revision:->tags' \
-        '(--no-merges -M --only-merges)-m[show only merge revisions]' \
-        '(--no-merges -M -m)--only-merges[show only merge revisions]' \
-        '(--only-merges -m --no-merges)-M[do not show merge revisions]' \
-        '(--only-merges -m -M)--no-merges[do not show merge revisions]' \
-        '(--keyword)-k[search for a keyword]:keyword:' \
-        '(-k)--keyword[search for a keyword]:keyword:' \
-        '(--branch)-b[show branches]' \
-        '(-b)--branch[show branches]' \
-        '(--patch)-p[show patch]' \
-        '(-p)--patch[show patch]' \
-        '*:file:_files'
-    ;;
+_hg_cmd_tip() {
+  _arguments -s -w : $_hg_global_opts $_hg_style_opts \
+  '(--patch -p)'{-p,--patch}'[show patch]'
+}
+
+_hg_cmd_unbundle() {
+  _arguments -s -w : $_hg_global_opts \
+  '(--update -u)'{-u,--update}'[update to new tip if changesets were unbundled]' \
+  ':files:_files'
+}
 
-    (update|checkout|co)
-        _arguments \
-        '(--branch)-b[checkout the head of a specific branch]' \
-        '(-b)--branch[checkout the head of a specific branch]' \
-        '(-C --clean --merge)-m[allow merging of branches]' \
-        '(-C --clean -m)--merge[allow merging of branches]' \
-        '(-m --merge --clean)-C[overwrite locally modified files]' \
-        '(-m --merge -C)--clean[overwrite locally modified files]' \
-        '*:revision or tag:->tags'
-    ;;
+_hg_cmd_update() {
+  _arguments -s -w : $_hg_global_opts \
+  '(--clean -C)'{-C,--clean}'[overwrite locally modified files]' \
+  '(--force -f)'{-f,--force}'[force a merge with outstanding changes]' \
+  ':revision:_hg_tags'
+}
 
-    (tag)
-        _arguments \
-        '(--local)-l[make the tag local]' \
-        '(-l)--local[make the tag local]' \
-        '(--message)-m[message for tag commit log entry]:string:' \
-        '(-m)--message[message for tag commit log entry]:string:' \
-        '(--date)-d[record datecode as commit date]:date code:' \
-        '(-d)--date[record datecode as commit date]:date code:' \
-        '(--user)-u[record user as commiter]:user:' \
-        '(-u)--user[record user as commiter]:user:' \
-        '*:name, then revision:->tags'
-    ;;
+# HGK
+_hg_cmd_view() {
+  _arguments -s -w : $_hg_global_opts \
+  '(--limit -l)'{-l+,--limit}'[limit number of changes displayed]:' \
+  ':revision range:_hg_tags'
+}
 
-    (clone)
-        if (( CURRENT == 2 )); then
-            repos=( $(_hg_cmd paths | sed -e 's/^.*= //') )
-            _arguments \
-            '(--no-update)-U[do not update the new working directory]' \
-            '(-U)--no-update[do not update the new working directory]' \
-            '(--ssh)-e[specify ssh command to use]:string:' \
-            '(-e)--ssh[specify ssh command to use]:string:' \
-            '--pull[use pull protocol to copy metadata]' \
-            '--remotecmd[specify hg command to run on the remote side]:remote hg:' \
-            '*:local repo:_files -/'
-            _wanted source expl 'source repository' compadd -a repos
-        elif (( CURRENT == 3 )); then
-            _arguments '*:dest repo:_files -/'
-        fi
-    ;;
+# MQ
+_hg_qseries() {
+  typeset -a patches
+  patches=($(_hg_cmd qseries 2>/dev/null))
+  (( $#patches )) && _describe -t hg-patches 'patches' patches
+}
 
-    (rawcommit)
-        _arguments \
-        '(--parent)-p[parent revision]:revision:->tags' \
-        '(-p)--parent[parent revision]:revision:->tags' \
-        '(--date)-d[record datecode as commit date]:date code:' \
-        '(-d)--date[record datecode as commit date]:date code:' \
-        '(--user)-u[record user as commiter]:user:' \
-        '(-u)--user[record user as commiter]:user:' \
-        '(--message)-m[use <txt> as commit message]:string:' \
-        '(-m)--message[use <txt> as commit message]:string:' \
-        '(--logfile)-l[read commit message from <file>]:.log file:_file -g \*.txt' \
-        '(-l)--logfile[read commit message from <file>]:.log file:_file -g \*.txt' \
-        '(--files)-F[file list]:file list:_files' \
-        '(-F)--files[file list]:file list:_files' \
-        '*:files to commit:_files'
-    ;;
+_hg_qapplied() {
+  typeset -a patches
+  patches=($(_hg_cmd qapplied 2>/dev/null))
+  if (( $#patches ))
+  then
+    patches+=(qbase qtip)
+    _describe -t hg-applied-patches 'applied patches' patches
+  fi
+}
+
+_hg_qunapplied() {
+  typeset -a patches
+  patches=($(_hg_cmd qunapplied 2>/dev/null))
+  (( $#patches )) && _describe -t hg-unapplied-patches 'unapplied patches' patches
+}
 
-    (bundle)
-        if (( CURRENT == 2 )); then
-            _arguments '*:changegroup file:_files -g \*.hg'
-        elif (( CURRENT == 3 )); then
-            _arguments '*:other repo:_files -/'
-        fi
-    ;;
+_hg_qguards() {
+  typeset -a guards
+  local guard
+  compset -P "+|-"
+  _hg_cmd qselect -s 2>/dev/null | while read guard
+  do
+    guards+=(${guard#(+|-)})
+  done
+  (( $#guards )) && _describe -t hg-guards 'guards' guards
+}
 
-    (unbundle)
-        _arguments '*:changegroup .hg file:_files -g \*.hg'
-    ;;
+_hg_qseries_opts=(
+  '(--summary -s)'{-s,--summary}'[print first line of patch header]')
 
-    (incoming)
-        _arguments $styleOpts \
-        '(--patch)-p[show patch]' \
-        '(-p)--patch[show patch]' \
-        '(--no-merges)-M[do not show merge revisions]' \
-        '(-M)--no-merges[do not show merge revisions]' \
-        '(--newest-first)-n[show newest record first]' \
-        '(-n)--newest-first[show newest record first]' \
-        '*:mercurial repository:_files -/'
-    ;;
+_hg_cmd_qapplied() {
+  _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
+}
 
-    (import|patch)
-        _arguments \
-        '(--strip)-p[directory strip option for patch (default: 1)]:count:' \
-        '(-p)--strip[directory strip option for patch (default: 1)]:count:' \
-        '(--force)-f[skip check for outstanding uncommitted changes]' \
-        '(-f)--force[skip check for outstanding uncommitted changes]' \
-        '(--base)-b[base directory to read patches from]:file:_files -W $(_hg_cmd root) -/' \
-        '(-b)--base[base directory to read patches from]:file:_files -W $(_hg_cmd root) -/' \
-        '*:patch file:_files'
-    ;;
+_hg_cmd_qdelete() {
+  _arguments -s -w : $_hg_global_opts \
+  '(--keep -k)'{-k,--keep}'[keep patch file]' \
+  '*'{-r+,--rev}'[stop managing a revision]:applied patch:_hg_revrange' \
+  '*:unapplied patch:_hg_qunapplied'
+}
+
+_hg_cmd_qdiff() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts \
+  '*:pattern:_files -W $(_hg_cmd root)'
+}
 
-    (pull)
-        repos=( $(_hg_cmd paths | sed -e 's/^.*= //') )
-        _arguments \
-        '(--update)-u[update working directory to tip after pull]' \
-        '(-u)--update[update working directory to tip after pull]' \
-        '(--ssh)-e[specify ssh command to use]:ssh command:' \
-        '(-e)--ssh[specify ssh command to use]:ssh command:' \
-        '--remotecmd[specify hg command to run on the remote side]:remote hg:' \
-        '*:local repo:_files -/'
-        _wanted source expl 'source repository' compadd -a repos
-    ;;
+_hg_cmd_qfold() {
+  _arguments -s -w : $_hg_global_opts $_h_commit_opts \
+  '(--keep,-k)'{-k,--keep}'[keep folded patch files]' \
+  '*:unapplied patch:_hg_qunapplied'
+}
 
-    (outgoing)
-        _arguments $styleOpts \
-        '(--patch)-p[show patch]' \
-        '(-p)--patch[show patch]' \
-        '(--no-merges)-M[do not show merge revisions]' \
-        '(-M)--no-merges[do not show merge revisions]' \
-        '(--newest-first)-n[show newest record first]' \
-        '(-n)--newest-first[show newest record first]' \
-        '*:local repo:_files -/'
-        _wanted source expl 'source repository' compadd -a repos
-    ;;
+_hg_cmd_qguard() {
+  _arguments -s -w : $_hg_global_opts \
+  '(--list -l)'{-l,--list}'[list all patches and guards]' \
+  '(--none -n)'{-n,--none}'[drop all guards]' \
+  ':patch:_hg_qseries' \
+  '*:guards:_hg_qguards'
+}
+
+_hg_cmd_qheader() {
+  _arguments -s -w : $_hg_global_opts \
+  ':patch:_hg_qseries'
+}
 
-    (export)
-        _arguments \
-        '(--outout)-o[print output to file with formatted name]:filespec:' \
-        '(-o)--output[print output to file with formatted name]:filespec:' \
-        '(--text)-a[treat all files as text]' \
-        '(-a)--text[treat all files as text]' \
-        '*:revision:->revs'
-        _wanted revs expl 'revision or tag' compadd -a tags
-    ;;
+_hg_cmd_qimport() {
+  _arguments -s -w : $_hg_global_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' \
+  '*:patch:_files'
+}
 
-    (push)
-        repos=( $(_hg_cmd paths | sed -e 's/^.*= //') )
-        _arguments \
-        '(--force)-f[force push]' \
-        '(-f)--force[force push]' \
-        '(--ssh)-e[specify ssh command to use]:ssh command:' \
-        '(-e)--ssh[specify ssh command to use]:ssh command:' \
-        '--remotecmd[specify hg command to run on the remote side]:remote hg:' \
-        '*:local repo:_files -/'
-        _wanted source expl 'source repository' compadd -a repos
-    ;;
+_hg_cmd_qnew() {
+  _arguments -s -w : $_hg_global_opts $_hg_commit_opts \
+  '(--force -f)'{-f,--force}'[import uncommitted changes into patch]' \
+  ':patch:'
+}
+
+_hg_cmd_qnext() {
+  _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
+}
 
-    (serve)
-        _arguments \
-        '(--accesslog)-A[name of access log file]:log file:_files' \
-        '(-A)--accesslog[name of access log file]:log file:_files' \
-        '(--errorlog)-E[name of error log file]:log file:_files' \
-        '(-E)--errorlog[name of error log file]:log file:_files' \
-        '(--port)-p[listen port]:listen port:' \
-        '(-p)--port[listen port]:listen port:' \
-        '(--address)-a[interface address]:interface address:' \
-        '(-a)--address[interface address]:interface address:' \
-        '(--name)-n[name to show in web pages]:repository name:' \
-        '(-n)--name[name to show in web pages]:repository name:' \
-        '(--templates)-t[web template directory]:template dir:_files -/' \
-        '(-t)--templates[web template directory]:template dir:_files -/' \
-        '--style[web template style]:style' \
-        '--stdio[for remote clients]' \
-        '(--ipv6)-6[use IPv6 in addition to IPv4]' \
-        '(-6)--ipv6[use IPv6 in addition to IPv4]'
-    ;;
+_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]' \
+  ':patch:_hg_qapplied'
+}
 
-    (help)
-        _wanted commands expl 'hg command' compadd -a _hg_commands
-    ;;
+_hg_cmd_qprev() {
+  _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
+}
 
-    (heads)
-        _arguments $styleOpts \
-        '(--branches)-b[find branch info]' \
-        '(-b)--branches[find branch info]'
-    ;;
-
-    (paths)
-        _arguments '*:symbolic name:(default default-push)'
-    ;;
-
-    (init)
-        _arguments '*:new repo directory:_files -/'
-    ;;
+_hg_cmd_qpush() {
+  _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]' \
+  ':patch:_hg_qunapplied'
+}
 
-    (manifest)
-        _arguments '*:revision:->tags'
-    ;;
-
-    (par*)
-        _arguments $styleOpts \
-        '(--rev 1)-r[show parents of the specified rev]:revision:->tags' \
-        '(-r 1)--rev[show parents of the specified rev]:revision:->tags' \
-	'::revision:->tags'
-    ;;
-
-    (identify|recover|root|undo|verify|version|ct|tags)
-        # no arguments for these commands
-    ;;
+_hg_cmd_qrefresh() {
+  _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_commit_opts \
+  '(--git -g)'{-g,--git}'[use git extended diff format]' \
+  '(--short -s)'{-s,--short}'[short refresh]' \
+  '*:files:_files -W $(_hg_cmd root)'
+}
 
-    # HGK
-    (vi*)
-        _arguments \
-        '(--limit)-l[limit number of changes displayed]:' \
-        '(-l)--limit[limit number of changes displayed]:' \
-        '::revision range:->tags'
-    ;;
+_hg_cmd_qrename() {
+  _arguments -s -w : $_hg_global_opts \
+  ':patch:_hg_qseries' \
+  ':destination:'
+}
 
-    # MQ commands
-    (qdel*|qrm|qrem*)
-        _arguments \
-            {-k,--keep}'[keep patch file]' \
-            {-r,--rev}'[revision]:applied patch:->qapplied' \
-            '*:unapplied patches:->qunapplied'
-    ;;
+_hg_cmd_qselect() {
+  _arguments -s -w : $_hg_global_opts \
+  '(--none -n :)'{-n,--none}'[disable all guards]' \
+  '(--series -s :)'{-s,--series}'[list all guards in series file]' \
+  '--pop[pop to before first guarded applied patch]' \
+  '--reapply[pop and reapply patches]' \
+  '*:guards:_hg_qguards'
+}
 
-    (qnew)
-        _arguments $commitMessage \
-            {-f,--force}'[import uncommitted changes into patch]' \
-            ':patch name:'
-    ;;
+_hg_cmd_qseries() {
+  _arguments -s -w : $_hg_global_opts $_hg_qseries_opts \
+  '(--missing -m)'{-m,--missing}'[print patches not in series]'
+}
 
-    (qpo*)
-        _arguments \
-            (1){-a,--all}'[pop all patches]' \
-            {-f,--force}'[forget any local changes]' \
-            ':applied patch:->qapplied'
-    ;;
+_hg_cmd_qunapplied() {
+  _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
+}
 
-    (qpu*)
-        _arguments \
-            (1){-a,--all}'[apply all patches]' \
-            {-f,--force}'[apply if the patch has rejects]' \
-            ':unapplied patch:->qunapplied'
-    ;;
-    (qref*)
-        _arguments $commitMessage $includeExclude \
-            {-g,--git}'[use git extended diff format]' \
-            {-s,--short}'[short refresh]'
-    ;;
+_hg_cmd_qtop() {
+  _arguments -s -w : $_hg_global_opts $_hg_qseries_opts
+}
 
-    (*)
-        _message "unknown hg command completion: $service"
-    ;;
-esac
+_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]' \
+  ':revision:_hg_tags'
+}
 
-_hg_state
+_hg "$@"
--- a/doc/hg.1.txt
+++ b/doc/hg.1.txt
@@ -8,7 +8,7 @@ hg - Mercurial source code management sy
 
 SYNOPSIS
 --------
-'hg' [-v -d -q -y] <command> [command options] [files]
+'hg' [global option]... <command> [command/global option]... [argument]...
 
 DESCRIPTION
 -----------
--- a/doc/hgrc.5.txt
+++ b/doc/hgrc.5.txt
@@ -50,6 +50,9 @@ installed.
     particular repository.  This file is not version-controlled, and
     will not get transferred during a "clone" operation.  Options in
     this file override options in all other configuration files.
+    On Unix, most of this file will be ignored if it doesn't belong
+    to a trusted user or to a trusted group.  See the documentation
+    for the trusted section below for more details.
 
 SYNTAX
 ------
@@ -364,6 +367,22 @@ server::
     6Mbps), uncompressed streaming is slower, because of the extra
     data transfer overhead.  Default is False.
 
+trusted::
+  For security reasons, Mercurial will not use the settings in
+  the .hg/hgrc file from a repository if it doesn't belong to a
+  trusted user or to a trusted group.  The main exception is the
+  web interface, which automatically uses some safe settings, since
+  it's common to serve repositories from different users.
+
+  This section specifies what users and groups are trusted.  The
+  current user is always trusted.  To trust everybody, list a user
+  or a group with name "*".
+
+  users;;
+    Comma-separated list of trusted users.
+  groups;;
+    Comma-separated list of trusted groups.
+
 ui::
   User interface controls.
   debug;;
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -1337,13 +1337,20 @@ class queue:
 
         for filename in files:
             if existing:
+                if filename == '-':
+                    raise util.Abort(_('-e is incompatible with import from -'))
                 if not patchname:
                     patchname = filename
                 if not os.path.isfile(self.join(patchname)):
                     raise util.Abort(_("patch %s does not exist") % patchname)
             else:
                 try:
-                    text = file(filename).read()
+                    if filename == '-':
+                        if not patchname:
+                            raise util.Abort(_('need --name to import a patch from -'))
+                        text = sys.stdin.read()
+                    else:
+                        text = file(filename).read()
                 except IOError:
                     raise util.Abort(_("unable to read %s") % patchname)
                 if not patchname:
--- a/mercurial/bdiff.c
+++ b/mercurial/bdiff.c
@@ -13,11 +13,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-#ifdef __hpux
-#define inline
-#endif
-
-#ifdef __SUNPRO_C
+#if defined __hpux || defined __SUNPRO_C || defined _AIX
 # define inline
 #endif
 
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1196,7 +1196,7 @@ def debugcheckstate(ui, repo):
         error = _(".hg/dirstate inconsistent with current parent's manifest")
         raise util.Abort(error)
 
-def showconfig(ui, repo, *values):
+def showconfig(ui, repo, *values, **opts):
     """show combined config settings from all hgrc files
 
     With no args, print names and values of all config items.
@@ -1207,10 +1207,11 @@ def showconfig(ui, repo, *values):
     With multiple args, print names and values of all config items
     with matching section names."""
 
+    untrusted = bool(opts.get('untrusted'))
     if values:
         if len([v for v in values if '.' in v]) > 1:
             raise util.Abort(_('only one config item permitted'))
-    for section, name, value in ui.walkconfig():
+    for section, name, value in ui.walkconfig(untrusted=untrusted):
         sectname = section + '.' + name
         if values:
             for v in values:
@@ -2091,10 +2092,17 @@ def pull(ui, repo, source="default", **o
 
     Valid URLs are of the form:
 
-      local/filesystem/path
+      local/filesystem/path (or file://local/filesystem/path)
       http://[user@]host[:port]/[path]
       https://[user@]host[:port]/[path]
       ssh://[user@]host[:port]/[path]
+      static-http://host[:port]/[path]
+
+    Paths in the local filesystem can either point to Mercurial
+    repositories or to bundle files (as created by 'hg bundle' or
+    'hg incoming --bundle'). The static-http:// protocol, albeit slow,
+    allows access to a Mercurial repository where you simply use a web
+    server to publish the .hg directory as static content.
 
     Some notes about using SSH with Mercurial:
     - SSH requires an accessible shell account on the destination machine
@@ -2142,14 +2150,16 @@ def push(ui, repo, dest=None, **opts):
 
     Valid URLs are of the form:
 
-      local/filesystem/path
+      local/filesystem/path (or file://local/filesystem/path)
       ssh://[user@]host[:port]/[path]
+      http://[user@]host[:port]/[path]
+      https://[user@]host[:port]/[path]
 
     Look at the help text for the pull command for important details
     about ssh:// URLs.
 
-    Pushing to http:// and https:// URLs is possible, too, if this
-    feature is enabled on the remote Mercurial server.
+    Pushing to http:// and https:// URLs is only possible, if this
+    feature is explicitly enabled on the remote Mercurial server.
     """
     dest = ui.expandpath(dest or 'default-push', dest or 'default')
     setremoteconfig(ui, opts)
@@ -3074,7 +3084,10 @@ table = {
          _('hg revert [-r REV] [NAME]...')),
     "rollback": (rollback, [], _('hg rollback')),
     "root": (root, [], _('hg root')),
-    "showconfig|debugconfig": (showconfig, [], _('showconfig [NAME]...')),
+    "showconfig|debugconfig":
+        (showconfig,
+         [('u', 'untrusted', None, _('show untrusted configuration options'))],
+         _('showconfig [-u] [NAME]...')),
     "^serve":
         (serve,
          [('A', 'accesslog', '', _('name of access log file to write to')),
@@ -3486,6 +3499,15 @@ def dispatch(args):
             u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
         else:
             u.warn(_("abort: %s\n") % inst.strerror)
+    except util.UnexpectedOutput, inst:
+        u.warn(_("abort: %s") % inst[0])
+        if not isinstance(inst[1], basestring):
+            u.warn(" %r\n" % (inst[1],))
+        elif not inst[1]:
+            u.warn(_(" empty string\n"))
+        else:
+            u.warn("\n%r%s\n" %
+                    (inst[1][:400], len(inst[1]) > 400 and '...' or ''))
     except util.Abort, inst:
         u.warn(_("abort: %s\n") % inst)
     except TypeError, inst:
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -69,7 +69,7 @@ def revnavgen(pos, pagelen, limit, nodef
 class hgweb(object):
     def __init__(self, repo, name=None):
         if type(repo) == type(""):
-            self.repo = hg.repository(ui.ui(), repo)
+            self.repo = hg.repository(ui.ui(report_untrusted=False), repo)
         else:
             self.repo = repo
 
@@ -77,24 +77,41 @@ class hgweb(object):
         self.reponame = name
         self.archives = 'zip', 'gz', 'bz2'
         self.stripecount = 1
-        self.templatepath = self.repo.ui.config("web", "templates",
-                                                templater.templatepath())
+        # a repo owner may set web.templates in .hg/hgrc to get any file
+        # readable by the user running the CGI script
+        self.templatepath = self.config("web", "templates",
+                                        templater.templatepath(),
+                                        untrusted=False)
+
+    # The CGI scripts are often run by a user different from the repo owner.
+    # Trust the settings from the .hg/hgrc files by default.
+    def config(self, section, name, default=None, untrusted=True):
+        return self.repo.ui.config(section, name, default,
+                                   untrusted=untrusted)
+
+    def configbool(self, section, name, default=False, untrusted=True):
+        return self.repo.ui.configbool(section, name, default,
+                                       untrusted=untrusted)
+
+    def configlist(self, section, name, default=None, untrusted=True):
+        return self.repo.ui.configlist(section, name, default,
+                                       untrusted=untrusted)
 
     def refresh(self):
         mtime = get_mtime(self.repo.root)
         if mtime != self.mtime:
             self.mtime = mtime
             self.repo = hg.repository(self.repo.ui, self.repo.root)
-            self.maxchanges = int(self.repo.ui.config("web", "maxchanges", 10))
-            self.stripecount = int(self.repo.ui.config("web", "stripes", 1))
-            self.maxshortchanges = int(self.repo.ui.config("web", "maxshortchanges", 60))
-            self.maxfiles = int(self.repo.ui.config("web", "maxfiles", 10))
-            self.allowpull = self.repo.ui.configbool("web", "allowpull", True)
+            self.maxchanges = int(self.config("web", "maxchanges", 10))
+            self.stripecount = int(self.config("web", "stripes", 1))
+            self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
+            self.maxfiles = int(self.config("web", "maxfiles", 10))
+            self.allowpull = self.configbool("web", "allowpull", True)
 
     def archivelist(self, nodeid):
-        allowed = self.repo.ui.configlist("web", "allow_archive")
+        allowed = self.configlist("web", "allow_archive")
         for i, spec in self.archive_specs.iteritems():
-            if i in allowed or self.repo.ui.configbool("web", "allow" + i):
+            if i in allowed or self.configbool("web", "allow" + i):
                 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
 
     def listfilediffs(self, files, changeset):
@@ -169,7 +186,7 @@ class hgweb(object):
             modified, added, removed = map(lambda x: filterfiles(files, x),
                                            (modified, added, removed))
 
-        diffopts = patch.diffopts(self.repo.ui)
+        diffopts = patch.diffopts(self.repo.ui, untrusted=True)
         for f in modified:
             to = r.file(f).read(mmap1[f])
             tn = r.file(f).read(mmap2[f])
@@ -571,10 +588,10 @@ class hgweb(object):
         end = min(count, start + self.maxchanges)
 
         yield self.t("summary",
-                 desc = self.repo.ui.config("web", "description", "unknown"),
-                 owner = (self.repo.ui.config("ui", "username") or # preferred
-                          self.repo.ui.config("web", "contact") or # deprecated
-                          self.repo.ui.config("web", "author", "unknown")), # also
+                 desc = self.config("web", "description", "unknown"),
+                 owner = (self.config("ui", "username") or # preferred
+                          self.config("web", "contact") or # deprecated
+                          self.config("web", "author", "unknown")), # also
                  lastchange = cl.read(cl.tip())[2],
                  tags = tagentries,
                  heads = heads,
@@ -650,7 +667,7 @@ class hgweb(object):
             yield self.t("footer", **map)
 
         def motd(**map):
-            yield self.repo.ui.config("web", "motd", "")
+            yield self.config("web", "motd", "")
 
         def expand_form(form):
             shortcuts = {
@@ -748,7 +765,7 @@ class hgweb(object):
             fields = []
             if req.form.has_key('style'):
                 style = req.form['style'][0]
-                if style != self.repo.ui.config('web', 'style', ''):
+                if style != self.config('web', 'style', ''):
                     fields.append(('style', style))
 
             separator = req.url[-1] == '?' and ';' or '?'
@@ -761,7 +778,7 @@ class hgweb(object):
         expand_form(req.form)
         rewrite_request(req)
 
-        style = self.repo.ui.config("web", "style", "")
+        style = self.config("web", "style", "")
         if req.form.has_key('style'):
             style = req.form['style'][0]
         mapfile = style_map(self.templatepath, style)
@@ -771,7 +788,7 @@ class hgweb(object):
         urlbase = 'http://%s%s' % (req.env['SERVER_NAME'], port)
 
         if not self.reponame:
-            self.reponame = (self.repo.ui.config("web", "name")
+            self.reponame = (self.config("web", "name")
                              or req.env.get('REPO_NAME')
                              or req.url.strip('/') or self.repo.root)
 
@@ -985,9 +1002,9 @@ class hgweb(object):
     def do_archive(self, req):
         changeset = self.repo.lookup(req.form['node'][0])
         type_ = req.form['type'][0]
-        allowed = self.repo.ui.configlist("web", "allow_archive")
+        allowed = self.configlist("web", "allow_archive")
         if (type_ in self.archives and (type_ in allowed or
-            self.repo.ui.configbool("web", "allow" + type_, False))):
+            self.configbool("web", "allow" + type_, False))):
             self.archive(req, changeset, type_)
             return
 
@@ -995,15 +1012,17 @@ class hgweb(object):
 
     def do_static(self, req):
         fname = req.form['file'][0]
-        static = self.repo.ui.config("web", "static",
-                                     os.path.join(self.templatepath,
-                                                  "static"))
+        # a repo owner may set web.static in .hg/hgrc to get any file
+        # readable by the user running the CGI script
+        static = self.config("web", "static",
+                             os.path.join(self.templatepath, "static"),
+                             untrusted=False)
         req.write(staticfile(static, fname, req)
                   or self.t("error", error="%r not found" % fname))
 
     def do_capabilities(self, req):
         caps = ['unbundle', 'lookup', 'changegroupsubset']
-        if self.repo.ui.configbool('server', 'uncompressed'):
+        if self.configbool('server', 'uncompressed'):
             caps.append('stream=%d' % self.repo.revlogversion)
         resp = ' '.join(caps)
         req.httphdr("application/mercurial-0.1", length=len(resp))
@@ -1016,11 +1035,11 @@ class hgweb(object):
 
         user = req.env.get('REMOTE_USER')
 
-        deny = self.repo.ui.configlist('web', 'deny_' + op)
+        deny = self.configlist('web', 'deny_' + op)
         if deny and (not user or deny == ['*'] or user in deny):
             return False
 
-        allow = self.repo.ui.configlist('web', 'allow_' + op)
+        allow = self.configlist('web', 'allow_' + op)
         return (allow and (allow == ['*'] or user in allow)) or default
 
     def do_unbundle(self, req):
@@ -1036,7 +1055,7 @@ class hgweb(object):
 
         # require ssl by default, auth info cannot be sniffed and
         # replayed
-        ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
+        ssl_req = self.configbool('web', 'push_ssl', True)
         if ssl_req:
             if not req.env.get('HTTPS'):
                 bail(_('ssl required\n'))
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -87,9 +87,10 @@ class hgwebdir(object):
                                              "url": url})
 
         def archivelist(ui, nodeid, url):
-            allowed = ui.configlist("web", "allow_archive")
+            allowed = ui.configlist("web", "allow_archive", untrusted=True)
             for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
-                if i[0] in allowed or ui.configbool("web", "allow" + i[0]):
+                if i[0] in allowed or ui.configbool("web", "allow" + i[0],
+                                                    untrusted=True):
                     yield {"type" : i[0], "extension": i[1],
                            "node": nodeid, "url": url}
 
@@ -109,12 +110,13 @@ class hgwebdir(object):
             rows = []
             parity = 0
             for name, path in self.repos:
-                u = ui.ui()
+                u = ui.ui(report_untrusted=False)
                 try:
                     u.readconfig(os.path.join(path, '.hg', 'hgrc'))
                 except IOError:
                     pass
-                get = u.config
+                def get(section, name, default=None):
+                    return u.config(section, name, default, untrusted=True)
 
                 url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
                        .replace("//", "/")) + '/'
--- a/mercurial/httprepo.py
+++ b/mercurial/httprepo.py
@@ -218,9 +218,15 @@ class httprepository(remoterepository):
         self.ui.debug(_("sending %s command\n") % cmd)
         q = {"cmd": cmd}
         q.update(args)
-        qs = urllib.urlencode(q)
-        cu = "%s?%s" % (self._url, qs)
+        qs = '?%s' % urllib.urlencode(q)
+        cu = "%s%s" % (self._url, qs)
         try:
+            if data:
+                if isinstance(data, file):
+                    # urllib2 needs string or buffer when using a proxy
+                    data.seek(0)
+                    data = data.read()
+                self.ui.debug(_("sending %d bytes\n") % len(data))
             resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
         except urllib2.HTTPError, inst:
             if inst.code == 401:
@@ -233,6 +239,8 @@ class httprepository(remoterepository):
         except IndexError:
             # this only happens with Python 2.3, later versions raise URLError
             raise util.Abort(_('http error, possibly caused by proxy setting'))
+        # record the url we got redirected to
+        self._url = resp.geturl().rstrip(qs)
         try:
             proto = resp.getheader('content-type')
         except AttributeError:
@@ -273,8 +281,7 @@ class httprepository(remoterepository):
         try:
             return map(bin, d[:-1].split(" "))
         except:
-            self.ui.warn(_("unexpected response:\n") + d[:400] + "\n...\n")
-            raise
+            raise util.UnexpectedOutput(_("unexpected response:"), d)
 
     def branches(self, nodes):
         n = " ".join(map(hex, nodes))
@@ -283,8 +290,7 @@ class httprepository(remoterepository):
             br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
             return br
         except:
-            self.ui.warn(_("unexpected response:\n") + d[:400] + "\n...\n")
-            raise
+            raise util.UnexpectedOutput(_("unexpected response:"), d)
 
     def between(self, pairs):
         n = "\n".join(["-".join(map(hex, p)) for p in pairs])
@@ -293,8 +299,7 @@ class httprepository(remoterepository):
             p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
             return p
         except:
-            self.ui.warn(_("unexpected response:\n") + d[:400] + "\n...\n")
-            raise
+            raise util.UnexpectedOutput(_("unexpected response:"), d)
 
     def changegroup(self, nodes, kind):
         n = " ".join(map(hex, nodes))
@@ -340,7 +345,7 @@ class httprepository(remoterepository):
             try:
                 rfp = self.do_cmd(
                     'unbundle', data=fp,
-                    headers={'content-length': length,
+                    headers={'content-length': str(length),
                              'content-type': 'application/octet-stream'},
                     heads=' '.join(map(hex, heads)))
                 try:
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1795,17 +1795,32 @@ class localrepository(repo.repository):
 
     def stream_in(self, remote):
         fp = remote.stream_out()
-        resp = int(fp.readline())
+        l = fp.readline()
+        try:
+            resp = int(l)
+        except ValueError:
+            raise util.UnexpectedOutput(
+                _('Unexpected response from remote server:'), l)
         if resp != 0:
             raise util.Abort(_('operation forbidden by server'))
         self.ui.status(_('streaming all changes\n'))
-        total_files, total_bytes = map(int, fp.readline().split(' ', 1))
+        l = fp.readline()
+        try:
+            total_files, total_bytes = map(int, l.split(' ', 1))
+        except ValueError, TypeError:
+            raise util.UnexpectedOutput(
+                _('Unexpected response from remote server:'), l)
         self.ui.status(_('%d files to transfer, %s of data\n') %
                        (total_files, util.bytecount(total_bytes)))
         start = time.time()
         for i in xrange(total_files):
-            name, size = fp.readline().split('\0', 1)
-            size = int(size)
+            l = fp.readline()
+            try:
+                name, size = l.split('\0', 1)
+                size = int(size)
+            except ValueError, TypeError:
+                raise util.UnexpectedOutput(
+                    _('Unexpected response from remote server:'), l)
             self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
             ofp = self.sopener(name, 'w')
             for chunk in util.filechunkiter(fp, limit=size):
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -326,21 +326,18 @@ def patch(patchname, ui, strip=1, cwd=No
 
     return fuzz
 
-def diffopts(ui, opts={}):
+def diffopts(ui, opts={}, untrusted=False):
+    def get(key, name=None):
+        return (opts.get(key) or
+                ui.configbool('diff', name or key, None, untrusted=untrusted))
     return mdiff.diffopts(
         text=opts.get('text'),
-        git=(opts.get('git') or
-                  ui.configbool('diff', 'git', None)),
-        nodates=(opts.get('nodates') or
-                  ui.configbool('diff', 'nodates', None)),
-        showfunc=(opts.get('show_function') or
-                  ui.configbool('diff', 'showfunc', None)),
-        ignorews=(opts.get('ignore_all_space') or
-                  ui.configbool('diff', 'ignorews', None)),
-        ignorewsamount=(opts.get('ignore_space_change') or
-                        ui.configbool('diff', 'ignorewsamount', None)),
-        ignoreblanklines=(opts.get('ignore_blank_lines') or
-                          ui.configbool('diff', 'ignoreblanklines', None)))
+        git=get('git'),
+        nodates=get('nodates'),
+        showfunc=get('show_function', 'showfunc'),
+        ignorews=get('ignore_all_space', 'ignorews'),
+        ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
+        ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'))
 
 def updatedir(ui, repo, patches, wlock=None):
     '''Update dirstate after patch application according to metadata'''
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -26,7 +26,8 @@ def updateconfig(source, dest, sections=
 
 class ui(object):
     def __init__(self, verbose=False, debug=False, quiet=False,
-                 interactive=True, traceback=False, parentui=None):
+                 interactive=True, traceback=False, report_untrusted=True,
+                 parentui=None):
         self.overlay = None
         self.header = []
         self.prev_header = []
@@ -39,14 +40,23 @@ class ui(object):
             self.debugflag = debug
             self.interactive = interactive
             self.traceback = traceback
+            self.report_untrusted = report_untrusted
+            self.trusted_users = {}
+            self.trusted_groups = {}
+            # if ucdata is not None, its keys must be a superset of cdata's
             self.cdata = util.configparser()
+            self.ucdata = None
             self.readconfig(util.rcpath())
             self.updateopts(verbose, debug, quiet, interactive)
         else:
             # parentui may point to an ui object which is already a child
             self.parentui = parentui.parentui or parentui
             self.readhooks = self.parentui.readhooks[:]
+            self.trusted_users = parentui.trusted_users.copy()
+            self.trusted_groups = parentui.trusted_groups.copy()
             self.cdata = dupconfig(self.parentui.cdata)
+            if self.parentui.ucdata:
+                self.ucdata = dupconfig(self.parentui.ucdata)
             if self.parentui.overlay:
                 self.overlay = dupconfig(self.parentui.overlay)
 
@@ -82,14 +92,52 @@ class ui(object):
         elif self.verbose and self.quiet:
             self.quiet = self.verbose = False
 
+    def _is_trusted(self, fp, f, warn=True):
+        tusers = self.trusted_users
+        tgroups = self.trusted_groups
+        if (tusers or tgroups) and '*' not in tusers and '*' not in tgroups:
+            st = util.fstat(fp)
+            user = util.username(st.st_uid)
+            group = util.groupname(st.st_gid)
+            if user not in tusers and group not in tgroups:
+                if warn and self.report_untrusted:
+                    self.warn(_('Not trusting file %s from untrusted '
+                                'user %s, group %s\n') % (f, user, group))
+                return False
+        return True
+
     def readconfig(self, fn, root=None):
         if isinstance(fn, basestring):
             fn = [fn]
         for f in fn:
             try:
-                self.cdata.read(f)
+                fp = open(f)
+            except IOError:
+                continue
+            cdata = self.cdata
+            trusted = self._is_trusted(fp, f)
+            if not trusted:
+                if self.ucdata is None:
+                    self.ucdata = dupconfig(self.cdata)
+                cdata = self.ucdata
+            elif self.ucdata is not None:
+                # use a separate configparser, so that we don't accidentally
+                # override ucdata settings later on.
+                cdata = util.configparser()
+
+            try:
+                cdata.readfp(fp, f)
             except ConfigParser.ParsingError, inst:
-                raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
+                msg = _("Failed to parse %s\n%s") % (f, inst)
+                if trusted:
+                    raise util.Abort(msg)
+                self.warn(_("Ignored: %s\n") % msg)
+
+            if trusted:
+                if cdata != self.cdata:
+                    updateconfig(cdata, self.cdata)
+                if self.ucdata is not None:
+                    updateconfig(cdata, self.ucdata)
         # override data from config files with data set with ui.setconfig
         if self.overlay:
             updateconfig(self.overlay, self.cdata)
@@ -103,7 +151,10 @@ class ui(object):
         self.readhooks.append(hook)
 
     def readsections(self, filename, *sections):
-        "read filename and add only the specified sections to the config data"
+        """Read filename and add only the specified sections to the config data
+
+        The settings are added to the trusted config data.
+        """
         if not sections:
             return
 
@@ -119,6 +170,8 @@ class ui(object):
                 cdata.add_section(section)
 
         updateconfig(cdata, self.cdata, sections)
+        if self.ucdata:
+            updateconfig(cdata, self.ucdata, sections)
 
     def fixconfig(self, section=None, name=None, value=None, root=None):
         # translate paths relative to root (or home) into absolute paths
@@ -126,7 +179,7 @@ class ui(object):
             if root is None:
                 root = os.getcwd()
             items = section and [(name, value)] or []
-            for cdata in self.cdata, self.overlay:
+            for cdata in self.cdata, self.ucdata, self.overlay:
                 if not cdata: continue
                 if not items and cdata.has_section('paths'):
                     pathsitems = cdata.items('paths')
@@ -144,62 +197,111 @@ class ui(object):
             if name is None or name == 'interactive':
                 self.interactive = self.configbool("ui", "interactive", True)
 
+        # update trust information
+        if section is None or section == 'trusted':
+            user = util.username()
+            if user is not None:
+                self.trusted_users[user] = 1
+                for user in self.configlist('trusted', 'users'):
+                    self.trusted_users[user] = 1
+                for group in self.configlist('trusted', 'groups'):
+                    self.trusted_groups[group] = 1
+
     def setconfig(self, section, name, value):
         if not self.overlay:
             self.overlay = util.configparser()
-        for cdata in (self.overlay, self.cdata):
+        for cdata in (self.overlay, self.cdata, self.ucdata):
+            if not cdata: continue
             if not cdata.has_section(section):
                 cdata.add_section(section)
             cdata.set(section, name, value)
         self.fixconfig(section, name, value)
 
-    def _config(self, section, name, default, funcname):
-        if self.cdata.has_option(section, name):
+    def _get_cdata(self, untrusted):
+        if untrusted and self.ucdata:
+            return self.ucdata
+        return self.cdata
+
+    def _config(self, section, name, default, funcname, untrusted, abort):
+        cdata = self._get_cdata(untrusted)
+        if cdata.has_option(section, name):
             try:
-                func = getattr(self.cdata, funcname)
+                func = getattr(cdata, funcname)
                 return func(section, name)
             except ConfigParser.InterpolationError, inst:
-                raise util.Abort(_("Error in configuration section [%s] "
-                                   "parameter '%s':\n%s")
-                                 % (section, name, inst))
+                msg = _("Error in configuration section [%s] "
+                        "parameter '%s':\n%s") % (section, name, inst)
+                if abort:
+                    raise util.Abort(msg)
+                self.warn(_("Ignored: %s\n") % msg)
         return default
 
-    def config(self, section, name, default=None):
-        return self._config(section, name, default, 'get')
+    def _configcommon(self, section, name, default, funcname, untrusted):
+        value = self._config(section, name, default, funcname,
+                             untrusted, abort=True)
+        if self.debugflag and not untrusted and self.ucdata:
+            uvalue = self._config(section, name, None, funcname,
+                                  untrusted=True, abort=False)
+            if uvalue is not None and uvalue != value:
+                self.warn(_("Ignoring untrusted configuration option "
+                            "%s.%s = %s\n") % (section, name, uvalue))
+        return value
 
-    def configbool(self, section, name, default=False):
-        return self._config(section, name, default, 'getboolean')
+    def config(self, section, name, default=None, untrusted=False):
+        return self._configcommon(section, name, default, 'get', untrusted)
 
-    def configlist(self, section, name, default=None):
+    def configbool(self, section, name, default=False, untrusted=False):
+        return self._configcommon(section, name, default, 'getboolean',
+                                  untrusted)
+
+    def configlist(self, section, name, default=None, untrusted=False):
         """Return a list of comma/space separated strings"""
-        result = self.config(section, name)
+        result = self.config(section, name, untrusted=untrusted)
         if result is None:
             result = default or []
         if isinstance(result, basestring):
             result = result.replace(",", " ").split()
         return result
 
-    def has_config(self, section):
+    def has_config(self, section, untrusted=False):
         '''tell whether section exists in config.'''
-        return self.cdata.has_section(section)
+        cdata = self._get_cdata(untrusted)
+        return cdata.has_section(section)
 
-    def configitems(self, section):
+    def _configitems(self, section, untrusted, abort):
         items = {}
-        if self.cdata.has_section(section):
+        cdata = self._get_cdata(untrusted)
+        if cdata.has_section(section):
             try:
-                items.update(dict(self.cdata.items(section)))
+                items.update(dict(cdata.items(section)))
             except ConfigParser.InterpolationError, inst:
-                raise util.Abort(_("Error in configuration section [%s]:\n%s")
-                                 % (section, inst))
+                msg = _("Error in configuration section [%s]:\n"
+                        "%s") % (section, inst)
+                if abort:
+                    raise util.Abort(msg)
+                self.warn(_("Ignored: %s\n") % msg)
+        return items
+
+    def configitems(self, section, untrusted=False):
+        items = self._configitems(section, untrusted=untrusted, abort=True)
+        if self.debugflag and not untrusted and self.ucdata:
+            uitems = self._configitems(section, untrusted=True, abort=False)
+            keys = uitems.keys()
+            keys.sort()
+            for k in keys:
+                if uitems[k] != items.get(k):
+                    self.warn(_("Ignoring untrusted configuration option "
+                                "%s.%s = %s\n") % (section, k, uitems[k]))
         x = items.items()
         x.sort()
         return x
 
-    def walkconfig(self):
-        sections = self.cdata.sections()
+    def walkconfig(self, untrusted=False):
+        cdata = self._get_cdata(untrusted)
+        sections = cdata.sections()
         sections.sort()
         for section in sections:
-            for name, value in self.configitems(section):
+            for name, value in self.configitems(section, untrusted):
                 yield section, name, value.replace('\n', '\\n')
 
     def extensions(self):
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -138,6 +138,9 @@ def unique(g):
 class Abort(Exception):
     """Raised if a command needs to print an error and exit."""
 
+class UnexpectedOutput(Abort):
+    """Raised to print an error with part of output and exit."""
+
 def always(fn): return True
 def never(fn): return False
 
@@ -521,6 +524,36 @@ def is_win_9x():
     except AttributeError:
         return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
 
+def username(uid=None):
+    """Return the name of the user with the given uid.
+
+    If uid is None, return the name of the current user."""
+    try:
+        import pwd
+        if uid is None:
+            uid = os.getuid()
+        try:
+            return pwd.getpwuid(uid)[0]
+        except KeyError:
+            return str(uid)
+    except ImportError:
+        return None
+
+def groupname(gid=None):
+    """Return the name of the group with the given gid.
+
+    If gid is None, return the name of the current group."""
+    try:
+        import grp
+        if gid is None:
+            gid = os.getgid()
+        try:
+            return grp.getgrgid(gid)[0]
+        except KeyError:
+            return str(gid)
+    except ImportError:
+        return None
+
 # Platform specific variants
 if os.name == 'nt':
     demandload(globals(), "msvcrt")
new file mode 100644
--- /dev/null
+++ b/tests/test-trusted.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python
+# Since it's not easy to write a test that portably deals
+# with files from different users/groups, we cheat a bit by
+# monkey-patching some functions in the util module
+
+import os
+from mercurial import ui, util
+
+hgrc = os.environ['HGRCPATH']
+
+def testui(user='foo', group='bar', tusers=(), tgroups=(),
+           cuser='foo', cgroup='bar', debug=False, silent=False):
+    # user, group => owners of the file
+    # tusers, tgroups => trusted users/groups
+    # cuser, cgroup => user/group of the current process
+
+    # write a global hgrc with the list of trusted users/groups and
+    # some setting so that we can be sure it was read
+    f = open(hgrc, 'w')
+    f.write('[paths]\n')
+    f.write('global = /some/path\n\n')
+
+    if tusers or tgroups:
+        f.write('[trusted]\n')
+        if tusers:
+            f.write('users = %s\n' % ', '.join(tusers))
+        if tgroups:
+            f.write('groups = %s\n' % ', '.join(tgroups))
+    f.close()
+
+    # override the functions that give names to uids and gids
+    def username(uid=None):
+        if uid is None:
+            return cuser
+        return user
+    util.username = username
+
+    def groupname(gid=None):
+        if gid is None:
+            return 'bar'
+        return group
+    util.groupname = groupname
+
+    # try to read everything
+    #print '# File belongs to user %s, group %s' % (user, group)
+    #print '# trusted users = %s; trusted groups = %s' % (tusers, tgroups)
+    kind = ('different', 'same')
+    who = ('', 'user', 'group', 'user and the group')
+    trusted = who[(user in tusers) + 2*(group in tgroups)]
+    if trusted:
+        trusted = ', but we trust the ' + trusted
+    print '# %s user, %s group%s' % (kind[user == cuser], kind[group == cgroup],
+                                     trusted)
+
+    parentui = ui.ui()
+    parentui.updateopts(debug=debug)
+    u = ui.ui(parentui=parentui)
+    u.readconfig('.hg/hgrc')
+    if silent:
+        return u
+    print 'trusted'
+    for name, path in u.configitems('paths'):
+        print '   ', name, '=', path
+    print 'untrusted'
+    for name, path in u.configitems('paths', untrusted=True):
+        print '.',
+        u.config('paths', name) # warning with debug=True
+        print '.',
+        u.config('paths', name, untrusted=True) # no warnings
+        print name, '=', path
+    print
+
+    return u
+
+os.mkdir('repo')
+os.chdir('repo')
+os.mkdir('.hg')
+f = open('.hg/hgrc', 'w')
+f.write('[paths]\n')
+f.write('local = /another/path\n\n')
+f.write('interpolated = %(global)s%(local)s\n\n')
+f.close()
+
+#print '# Everything is run by user foo, group bar\n'
+
+# same user, same group
+testui()
+# same user, different group
+testui(group='def')
+# different user, same group
+testui(user='abc')
+# ... but we trust the group
+testui(user='abc', tgroups=['bar'])
+# different user, different group
+testui(user='abc', group='def')
+# ... but we trust the user
+testui(user='abc', group='def', tusers=['abc'])
+# ... but we trust the group
+testui(user='abc', group='def', tgroups=['def'])
+# ... but we trust the user and the group
+testui(user='abc', group='def', tusers=['abc'], tgroups=['def'])
+# ... but we trust all users
+print '# we trust all users'
+testui(user='abc', group='def', tusers=['*'])
+# ... but we trust all groups
+print '# we trust all groups'
+testui(user='abc', group='def', tgroups=['*'])
+# ... but we trust the whole universe
+print '# we trust all users and groups'
+testui(user='abc', group='def', tusers=['*'], tgroups=['*'])
+# ... check that users and groups are in different namespaces
+print "# we don't get confused by users and groups with the same name"
+testui(user='abc', group='def', tusers=['def'], tgroups=['abc'])
+# ... lists of user names work
+print "# list of user names"
+testui(user='abc', group='def', tusers=['foo', 'xyz', 'abc', 'bleh'],
+       tgroups=['bar', 'baz', 'qux'])
+# ... lists of group names work
+print "# list of group names"
+testui(user='abc', group='def', tusers=['foo', 'xyz', 'bleh'],
+       tgroups=['bar', 'def', 'baz', 'qux'])
+
+print "# Can't figure out the name of the user running this process"
+testui(user='abc', group='def', cuser=None)
+
+print "# prints debug warnings"
+u = testui(user='abc', group='def', cuser='foo', debug=True)
+
+print "# ui.readsections"
+filename = 'foobar'
+f = open(filename, 'w')
+f.write('[foobar]\n')
+f.write('baz = quux\n')
+f.close()
+u.readsections(filename, 'foobar')
+print u.config('foobar', 'baz')
+
+print
+print "# read trusted, untrusted, new ui, trusted"
+u = ui.ui()
+u.updateopts(debug=True)
+u.readconfig(filename)
+u2 = ui.ui(parentui=u)
+def username(uid=None):
+    return 'foo'
+util.username = username
+u2.readconfig('.hg/hgrc')
+print 'trusted:'
+print u2.config('foobar', 'baz')
+print u2.config('paths', 'interpolated')
+print 'untrusted:'
+print u2.config('foobar', 'baz', untrusted=True)
+print u2.config('paths', 'interpolated', untrusted=True)
+
+print 
+print "# error handling"
+
+def assertraises(f, exc=util.Abort):
+    try:
+        f()
+    except exc, inst:
+        print 'raised', inst.__class__.__name__
+    else:
+        print 'no exception?!'
+
+print "# file doesn't exist"
+os.unlink('.hg/hgrc')
+assert not os.path.exists('.hg/hgrc')
+testui(debug=True, silent=True)
+testui(user='abc', group='def', debug=True, silent=True)
+
+print
+print "# parse error"
+f = open('.hg/hgrc', 'w')
+f.write('foo = bar')
+f.close()
+testui(user='abc', group='def', silent=True)
+assertraises(lambda: testui(debug=True, silent=True))
+
+print
+print "# interpolation error"
+f = open('.hg/hgrc', 'w')
+f.write('[foo]\n')
+f.write('bar = %(')
+f.close()
+u = testui(debug=True, silent=True)
+print '# regular config:'
+print '  trusted',
+assertraises(lambda: u.config('foo', 'bar'))
+print 'untrusted',
+assertraises(lambda: u.config('foo', 'bar', untrusted=True))
+
+u = testui(user='abc', group='def', debug=True, silent=True)
+print '  trusted ',
+print u.config('foo', 'bar')
+print 'untrusted',
+assertraises(lambda: u.config('foo', 'bar', untrusted=True))
+
+print '# configitems:'
+print '  trusted ',
+print u.configitems('foo')
+print 'untrusted',
+assertraises(lambda: u.configitems('foo', untrusted=True))
+
new file mode 100644
--- /dev/null
+++ b/tests/test-trusted.py.out
@@ -0,0 +1,212 @@
+# same user, same group
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# same user, different group
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# different user, same group
+Not trusting file .hg/hgrc from untrusted user abc, group bar
+trusted
+    global = /some/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# different user, same group, but we trust the group
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# different user, different group
+Not trusting file .hg/hgrc from untrusted user abc, group def
+trusted
+    global = /some/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# different user, different group, but we trust the user
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# different user, different group, but we trust the group
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# different user, different group, but we trust the user and the group
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# we trust all users
+# different user, different group
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# we trust all groups
+# different user, different group
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# we trust all users and groups
+# different user, different group
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# we don't get confused by users and groups with the same name
+# different user, different group
+Not trusting file .hg/hgrc from untrusted user abc, group def
+trusted
+    global = /some/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# list of user names
+# different user, different group, but we trust the user
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# list of group names
+# different user, different group, but we trust the group
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# Can't figure out the name of the user running this process
+# different user, different group
+trusted
+    global = /some/path
+    interpolated = /some/path/another/path
+    local = /another/path
+untrusted
+. . global = /some/path
+. . interpolated = /some/path/another/path
+. . local = /another/path
+
+# prints debug warnings
+# different user, different group
+Not trusting file .hg/hgrc from untrusted user abc, group def
+trusted
+Ignoring untrusted configuration option paths.interpolated = /some/path/another/path
+Ignoring untrusted configuration option paths.local = /another/path
+    global = /some/path
+untrusted
+. . global = /some/path
+.Ignoring untrusted configuration option paths.interpolated = /some/path/another/path
+ . interpolated = /some/path/another/path
+.Ignoring untrusted configuration option paths.local = /another/path
+ . local = /another/path
+
+# ui.readsections
+quux
+
+# read trusted, untrusted, new ui, trusted
+Not trusting file foobar from untrusted user abc, group def
+trusted:
+Ignoring untrusted configuration option foobar.baz = quux
+None
+/some/path/another/path
+untrusted:
+quux
+/some/path/another/path
+
+# error handling
+# file doesn't exist
+# same user, same group
+# different user, different group
+
+# parse error
+# different user, different group
+Not trusting file .hg/hgrc from untrusted user abc, group def
+Ignored: Failed to parse .hg/hgrc
+File contains no section headers.
+file: .hg/hgrc, line: 1
+'foo = bar'
+# same user, same group
+raised Abort
+
+# interpolation error
+# same user, same group
+# regular config:
+  trusted raised Abort
+untrusted raised Abort
+# different user, different group
+Not trusting file .hg/hgrc from untrusted user abc, group def
+  trusted Ignored: Error in configuration section [foo] parameter 'bar':
+bad interpolation variable reference '%('
+ None
+untrusted raised Abort
+# configitems:
+  trusted Ignored: Error in configuration section [foo]:
+bad interpolation variable reference '%('
+ []
+untrusted raised Abort