changeset 2956:6dddcba7596a

merge.
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Fri, 18 Aug 2006 21:17:28 -0700
parents 9d1c3529ebbc (current diff) 51ba31494c69 (diff)
children 6e062d9b188f
files mercurial/commands.py tests/test-help.out tests/test-up-local-change.out
diffstat 156 files changed, 6230 insertions(+), 2279 deletions(-) [+]
line wrap: on
line diff
--- a/.hgsigs	Sun Jul 23 09:04:14 2006 -0700
+++ b/.hgsigs	Fri Aug 18 21:17:28 2006 -0700
@@ -1,1 +1,2 @@
 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
+2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
--- a/.hgtags	Sun Jul 23 09:04:14 2006 -0700
+++ b/.hgtags	Fri Aug 18 21:17:28 2006 -0700
@@ -11,3 +11,4 @@
 3a56574f329a368d645853e0f9e09472aee62349 0.8
 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1
 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9
+2be3001847cb18a23c403439d9e7d0ace30804e9 0.9.1
--- a/CONTRIBUTORS	Sun Jul 23 09:04:14 2006 -0700
+++ b/CONTRIBUTORS	Fri Aug 18 21:17:28 2006 -0700
@@ -4,6 +4,7 @@
 Muli Ben-Yehuda <mulix at mulix.org>
 Mikael Berthe <mikael at lilotux.net>
 Benoit Boissinot <bboissin at gmail.com>
+Brendan Cully <brendan at kublai.com>
 Vincent Danjean <vdanjean.ml at free.fr>
 Jake Edge <jake at edge2.net>
 Michael Fetterman <michael.fetterman at intel.com>
--- a/MANIFEST.in	Sun Jul 23 09:04:14 2006 -0700
+++ b/MANIFEST.in	Fri Aug 18 21:17:28 2006 -0700
@@ -2,7 +2,7 @@
 recursive-include mercurial *.py
 include hgweb.cgi hgwebdir.cgi
 include hgeditor rewrite-log
-include tests/README tests/coverage.py tests/run-tests.py tests/md5sum.py tests/test-*[a-z0-9] tests/*.out
+include tests/README tests/*.py tests/test-*[a-z0-9] tests/*.out
 prune tests/*.err
 include *.txt
 include templates/map templates/map-*[a-z0-9]
@@ -10,8 +10,10 @@
 include templates/static/*
 include doc/README doc/Makefile doc/gendoc.py doc/*.txt doc/*.html doc/*.[0-9]
 recursive-include contrib *
+recursive-include hgext *
 include README
 include CONTRIBUTORS
 include COPYING
 include Makefile
 include MANIFEST.in
+prune *.elc *.orig *.rej *~ *.o *.so *.pyc *.swp *.prof
--- a/contrib/bash_completion	Sun Jul 23 09:04:14 2006 -0700
+++ b/contrib/bash_completion	Fri Aug 18 21:17:28 2006 -0700
@@ -288,7 +288,7 @@
 
 _hg_cmd_qdelete()
 {
-    _hg_ext_mq_patchlist qseries
+    _hg_ext_mq_patchlist qunapplied
 }
 
 _hg_cmd_qsave()
@@ -313,6 +313,11 @@
     COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
 }
 
+_hg_cmd_export()
+{
+    _hg_ext_mq_patchlist qapplied
+}
+
 
 # hbisect
 _hg_cmd_bisect()
--- a/contrib/convert-repo	Sun Jul 23 09:04:14 2006 -0700
+++ b/contrib/convert-repo	Fri Aug 18 21:17:28 2006 -0700
@@ -28,7 +28,8 @@
         self.path = path
 
     def getheads(self):
-        return [file(self.path + "/HEAD").read()[:-1]]
+        fh = os.popen("GIT_DIR=%s git-rev-parse --verify HEAD" % self.path)
+        return [fh.read()[:-1]]
 
     def catfile(self, rev, type):
         if rev == "0" * 40: raise IOError()
--- a/contrib/darcs2hg.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/contrib/darcs2hg.py	Fri Aug 18 21:17:28 2006 -0700
@@ -92,7 +92,7 @@
 
 def darcs_pull(hg_repo, darcs_repo, chash):
 	old_tip = darcs_tip(darcs_repo)
-	res     = cmd("darcs pull '%s' --all --match='hash %s'" % (darcs_repo, chash), hg_repo)
+	res     = cmd("darcs pull \"%s\" --all --match=\"hash %s\"" % (darcs_repo, chash), hg_repo)
 	print res
 	new_tip = darcs_tip(darcs_repo)
 	if not new_tip != old_tip + 1:
@@ -110,7 +110,8 @@
 	old_tip = hg_tip(hg_repo)
 	cmd("hg add -X _darcs", hg_repo)
 	cmd("hg remove -X _darcs --after", hg_repo)
-	res = cmd("hg commit -l %s -u '%s' -d '%s 0'"  % (tmpfile, author, date), hg_repo)
+	res = cmd("hg commit -l %s -u \"%s\" -d \"%s 0\""  % (tmpfile, author, date), hg_repo)
+	os.close(fd)
 	os.unlink(tmpfile)
 	new_tip = hg_tip(hg_repo)
 	if not new_tip == old_tip + 1:
@@ -156,7 +157,7 @@
 		print "Given HG repository must not exist when no SKIP is specified."
 		sys.exit(-1)
 	if skip == None:
-		cmd("hg init '%s'" % (hg_repo))
+		cmd("hg init \"%s\"" % (hg_repo))
 		cmd("darcs initialize", hg_repo)
 	# Get the changes from the Darcs repository
 	change_number = 0
--- a/contrib/macosx/Welcome.html	Sun Jul 23 09:04:14 2006 -0700
+++ b/contrib/macosx/Welcome.html	Fri Aug 18 21:17:28 2006 -0700
@@ -5,13 +5,73 @@
   <meta http-equiv="Content-Style-Type" content="text/css">
   <title></title>
   <style type="text/css">
-    p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica}
+    p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Helvetica}
     p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px}
   </style>
 </head>
 <body>
 <p class="p1">This is a prepackaged release of <a href="http://www.selenic.com/mercurial">Mercurial</a> for Mac OS X.</p>
 <p class="p2"><br></p>
-<p class="p1">It is based on Mercurial 0.9.</p>
+<p class="p1">It is based on Mercurial 0.9.1</p>
+<br>
+<pre>
+Release Notes
+-------------
+
+2006-07-24  v0.9.1
+
+Major changes between Mercurial 0.9 and 0.9.1:
+
+ New features:
+ - You can now configure your 'hgweb' server to let remote users
+   'push' changes over http.
+ - You can now 'import' a patch in a mail message by saving the mail
+   message, and importing it.  This works for patches sent either
+   inline or as attachments.
+ - The 'diff' command now accepts '-rA:B' syntax as a synonym for
+   '-r A -r B', and adds '-b' and '-B' options.
+
+ New contributions and extensions:
+ - The 'acl' extension lets you lock down parts of a repository
+   against incoming changes
+ - The 'extdiff' extension lets you run your favourite graphical
+   change viewer
+ - Comprehensive integration with the 'vim' editor
+ - A restricted shell for 'ssh'-hosted repositories
+ - An importer for 'darcs' repositories
+
+ New hooks added:
+ - 'preupdate' is run before an update or merge in the working
+   directory.
+ - 'update' is run after an update or merge in the working
+   directory.
+
+ Behaviour changes:
+ - NOTE: Mercurial as installed by the Windows binary
+   installer no longer performs automatic line-ending conversion for
+   Unix/Linux compatibility.  To re-enable this feature, edit your
+   'mercurial.ini' file after you upgrade.
+ - The Windows binary installer now automatically adds 'hg' to your
+   '%PATH%'.
+ - The 'backout' command now runs an editor by default, to let you
+   modify the commit message for a backed-out changeset.
+ - An earlier problem with parsing of tags has been fixed.
+   This makes tag parsing slower but more reliable.
+
+ Memory usage and performance improvements:
+ - The 'remove' command has been rewritten to be hundreds of times
+   faster in large repositories.
+ - It is now possible to 'clone' a repository very quickly over a
+   LAN, if the server is configured to allow it.  See the new 'server'
+   section in the 'hgrc' documentation.
+
+ Other changes of note:
+ - Mercurial will now print help for an extension if you type 'hg
+   help EXT_NAME'.
+ - The usual array of bug fixes and documentation improvements.
+ - The integrated web server is now more WSGI-compliant.
+ - Work has begun to solidify Mercurial's API for use by third-party
+   packages.
+</pre>
 </body>
 </html>
--- a/contrib/mercurial.el	Sun Jul 23 09:04:14 2006 -0700
+++ b/contrib/mercurial.el	Fri Aug 18 21:17:28 2006 -0700
@@ -380,7 +380,9 @@
   (save-excursion
     (while hg-prev-buffer
       (set-buffer hg-prev-buffer))
-    (let ((path (or default (buffer-file-name) default-directory)))
+    (let ((path (or default
+                    (buffer-file-name)
+                    (expand-file-name default-directory))))
       (if (or (not path) current-prefix-arg)
           (expand-file-name
            (eval (list* 'read-file-name
@@ -716,7 +718,11 @@
       (goto-char pos)
       (end-of-line 1)
       (delete-region pos (point)))
-    (cd (hg-root))))
+    (let ((hg-root-dir (hg-root)))
+      (if (not hg-root-dir)
+	  (error "error: %s: directory is not part of a Mercurial repository."
+		 default-directory)
+	(cd hg-root-dir)))))
 
 (defun hg-add (path)
   "Add PATH to the Mercurial repository on the next commit.
@@ -972,7 +978,8 @@
       (cd (hg-root path)))
     (when update
       (with-current-buffer buf
-	(set (make-local-variable 'backup-inhibited) nil)
+        (when (local-variable-p 'backup-inhibited)
+          (kill-local-variable 'backup-inhibited))
 	(hg-mode-line)))))
 
 (defun hg-incoming (&optional repo)
--- a/contrib/vim/hgcommand.vim	Sun Jul 23 09:04:14 2006 -0700
+++ b/contrib/vim/hgcommand.vim	Fri Aug 18 21:17:28 2006 -0700
@@ -3,7 +3,7 @@
 " Vim plugin to assist in working with HG-controlled files.
 "
 " Last Change:   2006/02/22
-" Version:       1.76
+" Version:       1.77
 " Maintainer:    Mathieu Clabaut <mathieu.clabaut@gmail.com>
 " License:       This file is placed in the public domain.
 " Credits:
@@ -13,7 +13,7 @@
 
 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 "
-" Section: Documentation 
+" Section: Documentation
 "----------------------------
 "
 " Documentation should be available by ":help hgcommand" command, once the
@@ -21,7 +21,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 syntaxic coloration).
 
 " Section: Plugin header {{{1
 
@@ -34,11 +34,33 @@
 endif
 let loaded_hgcommand = 1
 
+" store 'compatible' settings
+let s:save_cpo = &cpo
+set cpo&vim
+
+" run checks
+let s:script_name = expand("<sfile>:t:r")
+
+function! s:HGCleanupOnFailure(err)
+  echohl WarningMsg
+  echomsg s:script_name . ":" a:err "Plugin not loaded"
+  echohl None
+  let loaded_hgcommand = "no"
+  unlet s:save_cpo s:script_name
+endfunction
+
 if v:version < 602
-  echohl WarningMsg|echomsg "HGCommand 1.69 or later requires VIM 6.2 or later"|echohl None
+  call <SID>HGCleanupOnFailure("VIM 6.2 or later required.")
   finish
 endif
 
+if !exists("*system")
+  call <SID>HGCleanupOnFailure("builtin system() function required.")
+  finish
+endif
+
+let s:script_version = "v0.2"
+
 " Section: Event group setup {{{1
 
 augroup HGCommand
@@ -63,7 +85,7 @@
 function! s:HGResolveLink(fileName)
   let resolved = resolve(a:fileName)
   if resolved != a:fileName
-    let resolved = s:HGResolveLink(resolved)
+    let resolved = <SID>HGResolveLink(resolved)
   endif
   return resolved
 endfunction
@@ -74,7 +96,7 @@
 
 function! s:HGChangeToCurrentFileDir(fileName)
   let oldCwd=getcwd()
-  let fileName=s:HGResolveLink(a:fileName)
+  let fileName=<SID>HGResolveLink(a:fileName)
   let newCwd=fnamemodify(fileName, ':h')
   if strlen(newCwd) > 0
     execute 'cd' escape(newCwd, ' ')
@@ -82,7 +104,7 @@
   return oldCwd
 endfunction
 
-" Function: s:HGGetOption(name, default) {{{2
+" Function: <SID>HGGetOption(name, default) {{{2
 " Grab a user-specified option to override the default provided.  Options are
 " searched in the window, buffer, then global spaces.
 
@@ -110,9 +132,9 @@
   "Name parameter will be pasted into expression.
   let name = escape(a:name, ' *?\')
 
-  let editCommand = s:HGGetOption('HGCommandEdit', 'edit')
+  let editCommand = <SID>HGGetOption('HGCommandEdit', 'edit')
   if editCommand != 'edit'
-    if s:HGGetOption('HGCommandSplit', 'horizontal') == 'horizontal'
+    if <SID>HGGetOption('HGCommandSplit', 'horizontal') == 'horizontal'
       if name == ""
         let editCommand = 'rightbelow new'
       else
@@ -154,8 +176,8 @@
 
   let resultBufferName=''
 
-  if s:HGGetOption("HGCommandNameResultBuffers", 0)
-    let nameMarker = s:HGGetOption("HGCommandNameMarker", '_')
+  if <SID>HGGetOption("HGCommandNameResultBuffers", 0)
+    let nameMarker = <SID>HGGetOption("HGCommandNameMarker", '_')
     if strlen(a:statusText) > 0
       let bufName=a:cmdName . ' -- ' . a:statusText
     else
@@ -170,7 +192,7 @@
     endwhile
   endif
 
-  let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " " . a:cmd
+  let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " " . a:cmd
   "echomsg "DBG :".hgCommand
   let hgOut = system(hgCommand)
   " HACK:  diff command does not return proper error codes
@@ -192,7 +214,7 @@
     return -1
   endif
 
-  if s:HGEditFile(resultBufferName, a:origBuffNR) == -1
+  if <SID>HGEditFile(resultBufferName, a:origBuffNR) == -1
     return -1
   endif
 
@@ -200,7 +222,7 @@
   set noswapfile
   set filetype=
 
-  if s:HGGetOption("HGCommandDeleteOnHide", 0)
+  if <SID>HGGetOption("HGCommandDeleteOnHide", 0)
     set bufhidden=delete
   endif
 
@@ -213,8 +235,8 @@
   " This could be fixed by explicitly detecting whether the last line is
   " within a fold, but I prefer to simply unfold the result buffer altogether.
 
-  if has('folding')
-    normal zR
+  if has("folding")
+    setlocal nofoldenable
   endif
 
   $d
@@ -243,7 +265,7 @@
       return origBuffer
     else
       " Original buffer no longer exists.
-      return -1 
+      return -1
     endif
   else
     " No original buffer
@@ -256,7 +278,7 @@
 " for the current buffer.
 
 function! s:HGCurrentBufferCheck()
-  return s:HGBufferCheck(bufnr("%"))
+  return <SID>HGBufferCheck(bufnr("%"))
 endfunction
 
 " Function: s:HGToggleDeleteOnHide() {{{2
@@ -275,8 +297,8 @@
 " Returns: name of the new command buffer containing the command results
 
 function! s:HGDoCommand(cmd, cmdName, statusText)
-  let hgBufferCheck=s:HGCurrentBufferCheck()
-  if hgBufferCheck == -1 
+  let hgBufferCheck=<SID>HGCurrentBufferCheck()
+  if hgBufferCheck == -1
     echo "Original buffer no longer exists, aborting."
     return -1
   endif
@@ -285,8 +307,8 @@
   if isdirectory(fileName)
     let fileName=fileName . "/" . getline(".")
   endif
-  let realFileName = fnamemodify(s:HGResolveLink(fileName), ':t')
-  let oldCwd=s:HGChangeToCurrentFileDir(fileName)
+  let realFileName = fnamemodify(<SID>HGResolveLink(fileName), ':t')
+  let oldCwd=<SID>HGChangeToCurrentFileDir(fileName)
   try
      " TODO
     "if !filereadable('HG/Root')
@@ -294,7 +316,7 @@
     "endif
     let fullCmd = a:cmd . ' "' . realFileName . '"'
     "echomsg "DEBUG".fullCmd
-    let resultBuffer=s:HGCreateCommandBuffer(fullCmd, a:cmdName, a:statusText, hgBufferCheck)
+    let resultBuffer=<SID>HGCreateCommandBuffer(fullCmd, a:cmdName, a:statusText, hgBufferCheck)
     return resultBuffer
   catch
     echoerr v:exception
@@ -314,17 +336,17 @@
 " Returns: string to be exec'd that sets the multiple return values.
 
 function! s:HGGetStatusVars(revisionVar, branchVar, repositoryVar)
-  let hgBufferCheck=s:HGCurrentBufferCheck()
+  let hgBufferCheck=<SID>HGCurrentBufferCheck()
   "echomsg "DBG : in HGGetStatusVars"
-  if hgBufferCheck == -1 
+  if hgBufferCheck == -1
     return ""
   endif
   let fileName=bufname(hgBufferCheck)
-  let fileNameWithoutLink=s:HGResolveLink(fileName)
+  let fileNameWithoutLink=<SID>HGResolveLink(fileName)
   let realFileName = fnamemodify(fileNameWithoutLink, ':t')
-  let oldCwd=s:HGChangeToCurrentFileDir(realFileName)
+  let oldCwd=<SID>HGChangeToCurrentFileDir(realFileName)
   try
-    let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " root  " 
+    let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " root  "
     let roottext=system(hgCommand)
     " Suppress ending null char ! Does it work in window ?
     let roottext=substitute(roottext,'^.*/\([^/\n\r]*\)\n\_.*$','\1','')
@@ -335,31 +357,31 @@
     if a:repositoryVar != ""
       let returnExpression=returnExpression . " | let " . a:repositoryVar . "='" . roottext . "'"
     endif
-    let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " status -mardui " . realFileName
+    let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " status -mardui " . realFileName
     let statustext=system(hgCommand)
     if(v:shell_error)
       return ""
     endif
-    if match(statustext, '^[?I]') >= 0 
+    if match(statustext, '^[?I]') >= 0
       let revision="NEW"
-    elseif match(statustext, '^[R]') >= 0 
+    elseif match(statustext, '^[R]') >= 0
       let revision="REMOVED"
-    elseif match(statustext, '^[D]') >= 0 
+    elseif match(statustext, '^[D]') >= 0
       let revision="DELETED"
-    elseif match(statustext, '^[A]') >= 0 
+    elseif match(statustext, '^[A]') >= 0
       let revision="ADDED"
     else
       " The file is tracked, we can try to get is revision number
-      let hgCommand = s:HGGetOption("HGCommandHGExec", "hg") . " parents -b  " 
+      let hgCommand = <SID>HGGetOption("HGCommandHGExec", "hg") . " parents -b  "
       let statustext=system(hgCommand)
       if(v:shell_error)
-	  return ""
+          return ""
       endif
       let revision=substitute(statustext, '^changeset:\s*\(\d\+\):.*\_$\_.*$', '\1', "")
 
       if a:branchVar != "" && match(statustext, '^\_.*\_^branch:') >= 0
-	let branch=substitute(statustext, '^\_.*\_^branch:\s*\(\S\+\)\n\_.*$', '\1', "")
-	let returnExpression=returnExpression . " | let " . a:branchVar . "='" . branch . "'"
+        let branch=substitute(statustext, '^\_.*\_^branch:\s*\(\S\+\)\n\_.*$', '\1', "")
+        let returnExpression=returnExpression . " | let " . a:branchVar . "='" . branch . "'"
       endif
     endif
     if (exists('revision'))
@@ -381,7 +403,7 @@
     return
   endif
 
-  if !s:HGGetOption("HGCommandEnableBufferSetup", 0)
+  if !<SID>HGGetOption("HGCommandEnableBufferSetup", 0)
         \ || @% == ""
         \ || s:HGCommandEditFileRunning > 0
         \ || exists("b:HGOrigBuffNR")
@@ -399,7 +421,7 @@
   let branch=""
   let repository=""
 
-  exec s:HGGetStatusVars('revision', 'branch', 'repository')
+  exec <SID>HGGetStatusVars('revision', 'branch', 'repository')
   "echomsg "DBG ".revision."#".branch."#".repository
   if revision != ""
     let b:HGRevision=revision
@@ -427,7 +449,7 @@
 function! s:HGMarkOrigBufferForSetup(hgBuffer)
   checktime
   if a:hgBuffer != -1
-    let origBuffer = s:HGBufferCheck(a:hgBuffer)
+    let origBuffer = <SID>HGBufferCheck(a:hgBuffer)
     "This should never not work, but I'm paranoid
     if origBuffer != a:hgBuffer
       call setbufvar(origBuffer, "HGBufferSetup", 0)
@@ -436,7 +458,7 @@
     "We are presumably in the original buffer
     let b:HGBufferSetup = 0
     "We do the setup now as now event will be triggered allowing it later.
-    call s:HGSetupBuffer()
+    call <SID>HGSetupBuffer()
   endif
   return a:hgBuffer
 endfunction
@@ -478,111 +500,93 @@
 "   1 if new document installed, 0 otherwise.
 " Note: Cleaned and generalized by guo-peng Wen
 "'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
-
-function! s:HGInstallDocumentation(full_name, revision)
-    " Name of the document path based on the system we use:
-    if (has("unix"))
-        " On UNIX like system, using forward slash:
-        let l:slash_char = '/'
-        let l:mkdir_cmd  = ':silent !mkdir -p '
-    else
-        " On M$ system, use backslash. Also mkdir syntax is different.
-        " This should only work on W2K and up.
-        let l:slash_char = '\'
-        let l:mkdir_cmd  = ':silent !mkdir '
-    endif
-
-    let l:doc_path = l:slash_char . 'doc'
-    let l:doc_home = l:slash_char . '.vim' . l:slash_char . 'doc'
+" Helper function to make mkdir as portable as possible
+function! s:HGFlexiMkdir(dir)
+  if exists("*mkdir") " we can use Vim's own mkdir()
+    call mkdir(a:dir)
+  elseif !exists("+shellslash")
+    call system("mkdir -p '".a:dir."'")
+  else " M$
+    let l:ssl = &shellslash
+    try
+      set shellslash
+      " no single quotes?
+      call system('mkdir "'.a:dir.'"')
+    finally
+      let &shellslash = l:ssl
+    endtry
+  endif
+endfunction
 
-    " Figure out document path based on full name of this script:
-    let l:vim_plugin_path = fnamemodify(a:full_name, ':h')
-    let l:vim_doc_path    = fnamemodify(a:full_name, ':h:h') . l:doc_path
-    if (!(filewritable(l:vim_doc_path) == 2))
-        echomsg "Doc path: " . l:vim_doc_path
-        execute l:mkdir_cmd . '"' . l:vim_doc_path . '"'
-        if (!(filewritable(l:vim_doc_path) == 2))
-            " Try a default configuration in user home:
-            let l:vim_doc_path = expand("~") . l:doc_home
-            if (!(filewritable(l:vim_doc_path) == 2))
-                execute l:mkdir_cmd . '"' . l:vim_doc_path . '"'
-                if (!(filewritable(l:vim_doc_path) == 2))
-                    " Put a warning:
-                    echomsg "Unable to open documentation directory"
-                    echomsg " type :help add-local-help for more informations."
-                    return 0
-                endif
-            endif
+function! s:HGInstallDocumentation(full_name)
+  " Figure out document path based on full name of this script:
+  let l:vim_doc_path = fnamemodify(a:full_name, ":h:h") . "/doc"
+  if filewritable(l:vim_doc_path) != 2
+    echomsg s:script_name . ": Trying to update docs at" l:vim_doc_path
+    silent! call <SID>HGFlexiMkdir(l:vim_doc_path)
+    if filewritable(l:vim_doc_path) != 2
+      " Try first item in 'runtimepath':
+      let l:vim_doc_path =
+            \ substitute(&runtimepath, '^\([^,]*\).*', '\1/doc', 'e')
+      if filewritable(l:vim_doc_path) != 2
+        echomsg s:script_name . ": Trying to update docs at" l:vim_doc_path
+        silent! call <SID>HGFlexiMkdir(l:vim_doc_path)
+        if filewritable(l:vim_doc_path) != 2
+          " Put a warning:
+          echomsg "Unable to open documentation directory"
+          echomsg " type `:help add-local-help' for more information."
+          return 0
         endif
+      endif
     endif
-
-    " Exit if we have problem to access the document directory:
-    if (!isdirectory(l:vim_plugin_path)
-        \ || !isdirectory(l:vim_doc_path)
-        \ || filewritable(l:vim_doc_path) != 2)
-        return 0
-    endif
-
-    " Full name of script and documentation file:
-    let l:script_name = fnamemodify(a:full_name, ':t')
-    let l:doc_name    = fnamemodify(a:full_name, ':t:r') . '.txt'
-    let l:plugin_file = l:vim_plugin_path . l:slash_char . l:script_name
-    let l:doc_file    = l:vim_doc_path    . l:slash_char . l:doc_name
+  endif
 
-    " Bail out if document file is still up to date:
-    if (filereadable(l:doc_file)  &&
-        \ getftime(l:plugin_file) < getftime(l:doc_file))
-        return 0
-    endif
+  " Full name of documentation file:
+  let l:doc_file =
+        \ l:vim_doc_path . "/" . s:script_name . ".txt"
+  " Bail out if document file is still up to date:
+  if filereadable(l:doc_file)  &&
+        \ getftime(a:full_name) < getftime(l:doc_file)
+    return 0
+  endif
 
-    " Prepare window position restoring command:
-    if (strlen(@%))
-        let l:go_back = 'b ' . bufnr("%")
-    else
-        let l:go_back = 'enew!'
-    endif
-
-    " Create a new buffer & read in the plugin file (me):
-    setl nomodeline
-    exe 'enew!'
-    exe 'r ' . l:plugin_file
-
-    setl modeline
-    let l:buf = bufnr("%")
-    setl noswapfile modifiable
-
-    norm zR
-    norm gg
+  " temporary global settings
+  let l:lz = &lazyredraw
+  let l:hls = &hlsearch
+  set lazyredraw nohlsearch
+  " Create a new buffer & read in the plugin file (me):
+  1 new
+  setlocal noswapfile modifiable nomodeline
+  if has("folding")
+    setlocal nofoldenable
+  endif
+  silent execute "read" escape(a:full_name, " ")
+  let l:doc_buf = bufnr("%")
 
-    " Delete from first line to a line starts with
-    " === START_DOC
-    1,/^=\{3,}\s\+START_DOC\C/ d
-
-    " Delete from a line starts with
-    " === END_DOC
-    " to the end of the documents:
-    /^=\{3,}\s\+END_DOC\C/,$ d
-
-    " Remove fold marks:
-    %s/{\{3}[1-9]/    /
+  1
+  " Delete from first line to a line starts with
+  " === START_DOC
+  silent 1,/^=\{3,}\s\+START_DOC\C/ d
+  " Delete from a line starts with
+  " === END_DOC
+  " to the end of the documents:
+  silent /^=\{3,}\s\+END_DOC\C/,$ d
 
-    " Add modeline for help doc: the modeline string is mangled intentionally
-    " to avoid it be recognized by VIM:
-    call append(line('$'), '')
-    call append(line('$'), ' v' . 'im:tw=78:ts=8:ft=help:norl:')
-
-    " Replace revision:
-    exe "normal :1s/#version#/ v" . a:revision . "/\<CR>"
+  " Add modeline for help doc: the modeline string is mangled intentionally
+  " to avoid it be recognized by VIM:
+  call append(line("$"), "")
+  call append(line("$"), " v" . "im:tw=78:ts=8:ft=help:norl:")
 
-    " Save the help document:
-    exe 'w! ' . l:doc_file
-    exe l:go_back
-    exe 'bw ' . l:buf
+  " Replace revision:
+  silent execute "normal :1s/#version#/" . s:script_version . "/\<CR>"
+  " Save the help document and wipe out buffer:
+  silent execute "wq!" escape(l:doc_file, " ") "| bw" l:doc_buf
+  " Build help tags:
+  silent execute "helptags" l:vim_doc_path
 
-    " Build help tags:
-    exe 'helptags ' . l:vim_doc_path
-
-    return 1
+  let &hlsearch = l:hls
+  let &lazyredraw = l:lz
+  return 1
 endfunction
 
 " Section: Public functions {{{1
@@ -593,7 +597,7 @@
 
 function! HGGetRevision()
   let revision=""
-  exec s:HGGetStatusVars('revision', '', '')
+  exec <SID>HGGetStatusVars('revision', '', '')
   return revision
 endfunction
 
@@ -612,16 +616,16 @@
   let g:HGCommandEnableBufferSetup=1
   augroup HGCommandPlugin
     au!
-    au BufEnter * call s:HGSetupBuffer()
-    au BufWritePost * call s:HGSetupBuffer()
+    au BufEnter * call <SID>HGSetupBuffer()
+    au BufWritePost * call <SID>HGSetupBuffer()
     " Force resetting up buffer on external file change (HG update)
-    au FileChangedShell * call s:HGSetupBuffer(1)
+    au FileChangedShell * call <SID>HGSetupBuffer(1)
   augroup END
 
   " Only auto-load if the plugin is fully loaded.  This gives other plugins a
   " chance to run.
   if g:loaded_hgcommand == 2
-    call s:HGSetupBuffer()
+    call <SID>HGSetupBuffer()
   endif
 endfunction
 
@@ -662,7 +666,7 @@
 
 " Function: s:HGAdd() {{{2
 function! s:HGAdd()
-  return s:HGMarkOrigBufferForSetup(s:HGDoCommand('add', 'hgadd', ''))
+  return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('add', 'hgadd', ''))
 endfunction
 
 " Function: s:HGAnnotate(...) {{{2
@@ -672,7 +676,7 @@
       " This is a HGAnnotate buffer.  Perform annotation of the version
       " indicated by the current line.
       let revision = substitute(getline("."),'\(^[0-9]*\):.*','\1','')
-      if s:HGGetOption('HGCommandAnnotateParent', 0) != 0 && revision > 0
+      if <SID>HGGetOption('HGCommandAnnotateParent', 0) != 0 && revision > 0
         let revision = revision - 1
       endif
     else
@@ -691,7 +695,7 @@
     return -1
   endif
 
-  let resultBuffer=s:HGDoCommand('annotate -ndu -r ' . revision, 'hgannotate', revision) 
+  let resultBuffer=<SID>HGDoCommand('annotate -ndu -r ' . revision, 'hgannotate', revision)
   "echomsg "DBG: ".resultBuffer
   if resultBuffer !=  -1
     set filetype=HGAnnotate
@@ -706,10 +710,10 @@
   " is used; if bang is supplied, an empty message is used; otherwise, the
   " user is provided a buffer from which to edit the commit message.
   if a:2 != "" || a:1 == "!"
-    return s:HGMarkOrigBufferForSetup(s:HGDoCommand('commit -m "' . a:2 . '"', 'hgcommit', ''))
+    return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('commit -m "' . a:2 . '"', 'hgcommit', ''))
   endif
 
-  let hgBufferCheck=s:HGCurrentBufferCheck()
+  let hgBufferCheck=<SID>HGCurrentBufferCheck()
   if hgBufferCheck ==  -1
     echo "Original buffer no longer exists, aborting."
     return -1
@@ -725,7 +729,7 @@
     let messageFileName = tempname()
 
     let fileName=bufname(hgBufferCheck)
-    let realFilePath=s:HGResolveLink(fileName)
+    let realFilePath=<SID>HGResolveLink(fileName)
     let newCwd=fnamemodify(realFilePath, ':h')
     if strlen(newCwd) == 0
       " Account for autochdir being in effect, which will make this blank, but
@@ -735,7 +739,7 @@
 
     let realFileName=fnamemodify(realFilePath, ':t')
 
-    if s:HGEditFile(messageFileName, hgBufferCheck) == -1
+    if <SID>HGEditFile(messageFileName, hgBufferCheck) == -1
       return
     endif
 
@@ -766,9 +770,9 @@
     silent put =\"HG: Enter Log.  Lines beginning with `HG:' are removed automatically\"
     silent put ='HG: Type <leader>cc (or your own <Plug>HGCommit mapping)'
 
-    if s:HGGetOption('HGCommandCommitOnWrite', 1) == 1
+    if <SID>HGGetOption('HGCommandCommitOnWrite', 1) == 1
       execute 'au HGCommit BufWritePre' autoPattern 'g/^HG:/d'
-      execute 'au HGCommit BufWritePost' autoPattern 'call s:HGFinishCommit("' . messageFileName . '", "' . newCwd . '", "' . realFileName . '", ' . hgBufferCheck . ') | au! * ' autoPattern
+      execute 'au HGCommit BufWritePost' autoPattern 'call <SID>HGFinishCommit("' . messageFileName . '", "' . newCwd . '", "' . realFileName . '", ' . hgBufferCheck . ') | au! * ' autoPattern
       silent put ='HG: or write this buffer'
     endif
 
@@ -797,7 +801,7 @@
     let caption = ''
   endif
 
-  let hgdiffopt=s:HGGetOption('HGCommandDiffOpt', 'w')
+  let hgdiffopt=<SID>HGGetOption('HGCommandDiffOpt', 'w')
 
   if hgdiffopt == ""
     let diffoptionstring=""
@@ -805,8 +809,8 @@
     let diffoptionstring=" -" . hgdiffopt . " "
   endif
 
-  let resultBuffer = s:HGDoCommand('diff ' . diffoptionstring . revOptions , 'hgdiff', caption)
-  if resultBuffer != -1 
+  let resultBuffer = <SID>HGDoCommand('diff ' . diffoptionstring . revOptions , 'hgdiff', caption)
+  if resultBuffer != -1
     set filetype=diff
   endif
   return resultBuffer
@@ -815,7 +819,7 @@
 
 " Function: s:HGGotoOriginal(["!]) {{{2
 function! s:HGGotoOriginal(...)
-  let origBuffNR = s:HGCurrentBufferCheck()
+  let origBuffNR = <SID>HGCurrentBufferCheck()
   if origBuffNR > 0
     let origWinNR = bufwinnr(origBuffNR)
     if origWinNR == -1
@@ -845,11 +849,11 @@
     if strlen(a:targetDir) > 0
       execute 'cd' escape(a:targetDir, ' ')
     endif
-    let resultBuffer=s:HGCreateCommandBuffer('commit -l "' . a:messageFile . '" "'. a:targetFile . '"', 'hgcommit', '', a:origBuffNR)
+    let resultBuffer=<SID>HGCreateCommandBuffer('commit -l "' . a:messageFile . '" "'. a:targetFile . '"', 'hgcommit', '', a:origBuffNR)
     execute 'cd' escape(oldCwd, ' ')
     execute 'bw' escape(a:messageFile, ' *?\')
     silent execute 'call delete("' . a:messageFile . '")'
-    return s:HGMarkOrigBufferForSetup(resultBuffer)
+    return <SID>HGMarkOrigBufferForSetup(resultBuffer)
   else
     echoerr "Can't read message file; no commit is possible."
     return -1
@@ -866,7 +870,7 @@
     let caption = a:1
   endif
 
-  let resultBuffer=s:HGDoCommand('log' . versionOption, 'hglog', caption)
+  let resultBuffer=<SID>HGDoCommand('log' . versionOption, 'hglog', caption)
   if resultBuffer != ""
     set filetype=rcslog
   endif
@@ -875,14 +879,14 @@
 
 " Function: s:HGRevert() {{{2
 function! s:HGRevert()
-  return s:HGMarkOrigBufferForSetup(s:HGDoCommand('revert', 'hgrevert', ''))
+  return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('revert', 'hgrevert', ''))
 endfunction
 
 " Function: s:HGReview(...) {{{2
 function! s:HGReview(...)
   if a:0 == 0
     let versiontag=""
-    if s:HGGetOption('HGCommandInteractive', 0)
+    if <SID>HGGetOption('HGCommandInteractive', 0)
       let versiontag=input('Revision:  ')
     endif
     if versiontag == ""
@@ -896,7 +900,7 @@
     let versionOption=" -r " . versiontag . " "
   endif
 
-  let resultBuffer = s:HGDoCommand('cat' . versionOption, 'hgreview', versiontag)
+  let resultBuffer = <SID>HGDoCommand('cat' . versionOption, 'hgreview', versiontag)
   if resultBuffer > 0
     let &filetype=getbufvar(b:HGOrigBuffNR, '&filetype')
   endif
@@ -906,18 +910,18 @@
 
 " Function: s:HGStatus() {{{2
 function! s:HGStatus()
-  return s:HGDoCommand('status', 'hgstatus', '')
+  return <SID>HGDoCommand('status', 'hgstatus', '')
 endfunction
 
 
 " Function: s:HGUpdate() {{{2
 function! s:HGUpdate()
-  return s:HGMarkOrigBufferForSetup(s:HGDoCommand('update', 'update', ''))
+  return <SID>HGMarkOrigBufferForSetup(<SID>HGDoCommand('update', 'update', ''))
 endfunction
 
 " Function: s:HGVimDiff(...) {{{2
 function! s:HGVimDiff(...)
-  let originalBuffer = s:HGCurrentBufferCheck()
+  let originalBuffer = <SID>HGCurrentBufferCheck()
   let s:HGCommandEditFileRunning = s:HGCommandEditFileRunning + 1
   try
     " If there's already a VimDiff'ed window, restore it.
@@ -925,16 +929,16 @@
 
     if exists("s:vimDiffSourceBuffer") && s:vimDiffSourceBuffer != originalBuffer
       " Clear the existing vimdiff setup by removing the result buffers.
-      call s:HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
+      call <SID>HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
     endif
 
     " Split and diff
     if(a:0 == 2)
       " Reset the vimdiff system, as 2 explicit versions were provided.
       if exists('s:vimDiffSourceBuffer')
-        call s:HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
+        call <SID>HGWipeoutCommandBuffers(s:vimDiffSourceBuffer, 'vimdiff')
       endif
-      let resultBuffer = s:HGReview(a:1)
+      let resultBuffer = <SID>HGReview(a:1)
       if resultBuffer < 0
         echomsg "Can't open HG revision " . a:1
         return resultBuffer
@@ -945,10 +949,10 @@
       let s:vimDiffScratchList = '{'. resultBuffer . '}'
       " If no split method is defined, cheat, and set it to vertical.
       try
-        call s:HGOverrideOption('HGCommandSplit', s:HGGetOption('HGCommandDiffSplit', s:HGGetOption('HGCommandSplit', 'vertical')))
-        let resultBuffer=s:HGReview(a:2)
+        call <SID>HGOverrideOption('HGCommandSplit', <SID>HGGetOption('HGCommandDiffSplit', <SID>HGGetOption('HGCommandSplit', 'vertical')))
+        let resultBuffer=<SID>HGReview(a:2)
       finally
-        call s:HGOverrideOption('HGCommandSplit')
+        call <SID>HGOverrideOption('HGCommandSplit')
       endtry
       if resultBuffer < 0
         echomsg "Can't open HG revision " . a:1
@@ -962,16 +966,16 @@
       " Add new buffer
       try
         " Force splitting behavior, otherwise why use vimdiff?
-        call s:HGOverrideOption("HGCommandEdit", "split")
-        call s:HGOverrideOption("HGCommandSplit", s:HGGetOption('HGCommandDiffSplit', s:HGGetOption('HGCommandSplit', 'vertical')))
+        call <SID>HGOverrideOption("HGCommandEdit", "split")
+        call <SID>HGOverrideOption("HGCommandSplit", <SID>HGGetOption('HGCommandDiffSplit', <SID>HGGetOption('HGCommandSplit', 'vertical')))
         if(a:0 == 0)
-          let resultBuffer=s:HGReview()
+          let resultBuffer=<SID>HGReview()
         else
-          let resultBuffer=s:HGReview(a:1)
+          let resultBuffer=<SID>HGReview(a:1)
         endif
       finally
-        call s:HGOverrideOption("HGCommandEdit")
-        call s:HGOverrideOption("HGCommandSplit")
+        call <SID>HGOverrideOption("HGCommandEdit")
+        call <SID>HGOverrideOption("HGCommandSplit")
       endtry
       if resultBuffer < 0
         echomsg "Can't open current HG revision"
@@ -990,14 +994,14 @@
         wincmd W
         execute 'buffer' originalBuffer
         " Store info for later original buffer restore
-        let s:vimDiffRestoreCmd = 
+        let s:vimDiffRestoreCmd =
               \    "call setbufvar(".originalBuffer.", \"&diff\", ".getbufvar(originalBuffer, '&diff').")"
               \ . "|call setbufvar(".originalBuffer.", \"&foldcolumn\", ".getbufvar(originalBuffer, '&foldcolumn').")"
               \ . "|call setbufvar(".originalBuffer.", \"&foldenable\", ".getbufvar(originalBuffer, '&foldenable').")"
               \ . "|call setbufvar(".originalBuffer.", \"&foldmethod\", '".getbufvar(originalBuffer, '&foldmethod')."')"
               \ . "|call setbufvar(".originalBuffer.", \"&scrollbind\", ".getbufvar(originalBuffer, '&scrollbind').")"
               \ . "|call setbufvar(".originalBuffer.", \"&wrap\", ".getbufvar(originalBuffer, '&wrap').")"
-              \ . "|if &foldmethod=='manual'|execute 'normal zE'|endif"
+              \ . "|if &foldmethod=='manual'|execute 'normal! zE'|endif"
         diffthis
         wincmd w
       else
@@ -1027,17 +1031,17 @@
 
 " Section: Command definitions {{{1
 " Section: Primary commands {{{2
-com! HGAdd call s:HGAdd()
-com! -nargs=? HGAnnotate call s:HGAnnotate(<f-args>)
-com! -bang -nargs=? HGCommit call s:HGCommit(<q-bang>, <q-args>)
-com! -nargs=* HGDiff call s:HGDiff(<f-args>)
-com! -bang HGGotoOriginal call s:HGGotoOriginal(<q-bang>)
-com! -nargs=? HGLog call s:HGLog(<f-args>)
-com! HGRevert call s:HGRevert()
-com! -nargs=? HGReview call s:HGReview(<f-args>)
-com! HGStatus call s:HGStatus()
-com! HGUpdate call s:HGUpdate()
-com! -nargs=* HGVimDiff call s:HGVimDiff(<f-args>)
+com! HGAdd call <SID>HGAdd()
+com! -nargs=? HGAnnotate call <SID>HGAnnotate(<f-args>)
+com! -bang -nargs=? HGCommit call <SID>HGCommit(<q-bang>, <q-args>)
+com! -nargs=* HGDiff call <SID>HGDiff(<f-args>)
+com! -bang HGGotoOriginal call <SID>HGGotoOriginal(<q-bang>)
+com! -nargs=? HGLog call <SID>HGLog(<f-args>)
+com! HGRevert call <SID>HGRevert()
+com! -nargs=? HGReview call <SID>HGReview(<f-args>)
+com! HGStatus call <SID>HGStatus()
+com! HGUpdate call <SID>HGUpdate()
+com! -nargs=* HGVimDiff call <SID>HGVimDiff(<f-args>)
 
 " Section: HG buffer management commands {{{2
 com! HGDisableBufferSetup call HGDisableBufferSetup()
@@ -1173,7 +1177,7 @@
 
 augroup HGVimDiffRestore
   au!
-  au BufUnload * call s:HGVimDiffRestore(expand("<abuf>"))
+  au BufUnload * call <SID>HGVimDiffRestore(expand("<abuf>"))
 augroup END
 
 " Section: Optional activation of buffer management {{{1
@@ -1183,20 +1187,24 @@
 endif
 
 " Section: Doc installation {{{1
-"
-  let s:revision="0.1"
-  silent! let s:install_status =
-      \ s:HGInstallDocumentation(expand('<sfile>:p'), s:revision)
-  if (s:install_status == 1)
-      echom expand("<sfile>:t:r") . ' v' . s:revision .
-		\ ': Help-documentation installed.'
-  endif
 
+if <SID>HGInstallDocumentation(expand("<sfile>:p"))
+  echomsg s:script_name s:script_version . ": updated documentation"
+endif
 
 " Section: Plugin completion {{{1
 
+" delete one-time vars and functions
+delfunction <SID>HGInstallDocumentation
+delfunction <SID>HGFlexiMkdir
+delfunction <SID>HGCleanupOnFailure
+unlet s:script_version s:script_name
+
 let loaded_hgcommand=2
 silent do HGCommand User HGPluginFinish
+
+let &cpo = s:save_cpo
+unlet s:save_cpo
 " vim:se expandtab sts=2 sw=2:
 finish
 
@@ -1228,16 +1236,16 @@
 ==============================================================================
 2. HGCommand Installation				   *hgcommand-install*
 
-   In order to install the plugin, place the hgcommand.vim file into a plugin' 
-   directory in your runtime path (please see |add-global-plugin| and 
+   In order to install the plugin, place the hgcommand.vim file into a plugin'
+   directory in your runtime path (please see |add-global-plugin| and
    |'runtimepath'|.
 
-   HGCommand may be customized by setting variables, creating maps, and 
+   HGCommand may be customized by setting variables, creating maps, and
    specifying event handlers.  Please see |hgcommand-customize| for more
    details.
 
                                                          *hgcommand-auto-help*
-   The help file is automagically generated when the |hgcommand| script is 
+   The help file is automagically generated when the |hgcommand| script is
    loaded for the first time.
 
 ==============================================================================
@@ -1245,32 +1253,32 @@
 3. HGCommand Intro					           *hgcommand*
                                                              *hgcommand-intro*
 
-   The HGCommand plugin provides global ex commands for manipulating 
-   HG-controlled source files.  In general, each command operates on the 
-   current buffer and accomplishes a separate hg function, such as update, 
+   The HGCommand plugin provides global ex commands for manipulating
+   HG-controlled source files.  In general, each command operates on the
+   current buffer and accomplishes a separate hg function, such as update,
    commit, log, and others (please see |hgcommand-commands| for a list of all
-   available commands).  The results of each operation are displayed in a 
-   scratch buffer.  Several buffer variables are defined for those scratch 
+   available commands).  The results of each operation are displayed in a
+   scratch buffer.  Several buffer variables are defined for those scratch
    buffers (please see |hgcommand-buffer-variables|).
 
-   The notion of "current file" means either the current buffer, or, in the 
+   The notion of "current file" means either the current buffer, or, in the
    case of a directory buffer, the file on the current line within the buffer.
 
-   For convenience, any HGCommand invoked on a HGCommand scratch buffer acts 
-   as though it was invoked on the original file and splits the screen so that 
+   For convenience, any HGCommand invoked on a HGCommand scratch buffer acts
+   as though it was invoked on the original file and splits the screen so that
    the output appears in a new window.
 
-   Many of the commands accept revisions as arguments.  By default, most 
-   operate on the most recent revision on the current branch if no revision is 
+   Many of the commands accept revisions as arguments.  By default, most
+   operate on the most recent revision on the current branch if no revision is
    specified (though see |HGCommandInteractive| to prompt instead).
 
-   Each HGCommand is mapped to a key sequence starting with the <Leader> 
-   keystroke.  The default mappings may be overridden by supplying different 
-   mappings before the plugin is loaded, such as in the vimrc, in the standard 
-   fashion for plugin mappings.  For examples, please see 
+   Each HGCommand is mapped to a key sequence starting with the <Leader>
+   keystroke.  The default mappings may be overridden by supplying different
+   mappings before the plugin is loaded, such as in the vimrc, in the standard
+   fashion for plugin mappings.  For examples, please see
    |hgcommand-mappings-override|.
 
-   The HGCommand plugin may be configured in several ways.  For more details, 
+   The HGCommand plugin may be configured in several ways.  For more details,
    please see |hgcommand-customize|.
 
 ==============================================================================
@@ -1294,85 +1302,85 @@
 
 :HGAdd							              *:HGAdd*
 
-   This command performs "hg add" on the current file.  Please note, this does 
+   This command performs "hg add" on the current file.  Please note, this does
    not commit the newly-added file.
 
 :HGAnnotate						         *:HGAnnotate*
 
-   This command performs "hg annotate" on the current file.  If an argument is 
-   given, the argument is used as a revision number to display.  If not given 
-   an argument, it uses the most recent version of the file on the current 
-   branch.  Additionally, if the current buffer is a HGAnnotate buffer 
+   This command performs "hg annotate" on the current file.  If an argument is
+   given, the argument is used as a revision number to display.  If not given
+   an argument, it uses the most recent version of the file on the current
+   branch.  Additionally, if the current buffer is a HGAnnotate buffer
    already, the version number on the current line is used.
 
-   If the |HGCommandAnnotateParent| variable is set to a non-zero value, the 
-   version previous to the one on the current line is used instead.  This 
+   If the |HGCommandAnnotateParent| variable is set to a non-zero value, the
+   version previous to the one on the current line is used instead.  This
    allows one to navigate back to examine the previous version of a line.
 
-   The filetype of the HGCommand scratch buffer is set to 'HGAnnotate', to 
+   The filetype of the HGCommand scratch buffer is set to 'HGAnnotate', to
    take advantage of the bundled syntax file.
 
 
 :HGCommit[!]						           *:HGCommit*
 
-   If called with arguments, this performs "hg commit" using the arguments as 
+   If called with arguments, this performs "hg commit" using the arguments as
    the log message.
 
    If '!' is used with no arguments, an empty log message is committed.
 
-   If called with no arguments, this is a two-step command.  The first step 
-   opens a buffer to accept a log message.  When that buffer is written, it is 
-   automatically closed and the file is committed using the information from 
-   that log message.  The commit can be abandoned if the log message buffer is 
+   If called with no arguments, this is a two-step command.  The first step
+   opens a buffer to accept a log message.  When that buffer is written, it is
+   automatically closed and the file is committed using the information from
+   that log message.  The commit can be abandoned if the log message buffer is
    deleted or wiped before being written.
 
-   Alternatively, the mapping that is used to invoke :HGCommit (by default 
-   <Leader>hgc) can be used in the log message buffer to immediately commit.  
-   This is useful if the |HGCommandCommitOnWrite| variable is set to 0 to 
+   Alternatively, the mapping that is used to invoke :HGCommit (by default
+   <Leader>hgc) can be used in the log message buffer to immediately commit.
+   This is useful if the |HGCommandCommitOnWrite| variable is set to 0 to
    disable the normal commit-on-write behavior.
 
 :HGDiff						                     *:HGDiff*
 
-   With no arguments, this performs "hg diff" on the current file against the 
+   With no arguments, this performs "hg diff" on the current file against the
    current repository version.
 
-   With one argument, "hg diff" is performed on the current file against the 
+   With one argument, "hg diff" is performed on the current file against the
    specified revision.
 
-   With two arguments, hg diff is performed between the specified revisions of 
+   With two arguments, hg diff is performed between the specified revisions of
    the current file.
 
-   This command uses the 'HGCommandDiffOpt' variable to specify diff options.  
-   If that variable does not exist, then 'wbBc' is assumed.  If you wish to 
+   This command uses the 'HGCommandDiffOpt' variable to specify diff options.
+   If that variable does not exist, then 'wbBc' is assumed.  If you wish to
    have no options, then set it to the empty string.
 
 
 :HGGotoOriginal					             *:HGGotoOriginal*
 
-   This command returns the current window to the source buffer, if the 
+   This command returns the current window to the source buffer, if the
    current buffer is a HG command output buffer.
 
 :HGGotoOriginal!
 
-   Like ":HGGotoOriginal" but also executes :bufwipeout on all HG command 
+   Like ":HGGotoOriginal" but also executes :bufwipeout on all HG command
    output buffers for the source buffer.
 
 :HGLog							              *:HGLog*
 
    Performs "hg log" on the current file.
 
-   If an argument is given, it is passed as an argument to the "-r" option of 
+   If an argument is given, it is passed as an argument to the "-r" option of
    "hg log".
 
 :HGRevert						           *:HGRevert*
 
-   Replaces the current file with the most recent version from the repository 
+   Replaces the current file with the most recent version from the repository
    in order to wipe out any undesired changes.
- 
+
 :HGReview						           *:HGReview*
 
-   Retrieves a particular version of the current file.  If no argument is 
-   given, the most recent version of the file on the current branch is 
+   Retrieves a particular version of the current file.  If no argument is
+   given, the most recent version of the file on the current branch is
    retrieved.  Otherwise, the specified version is retrieved.
 
 :HGStatus					 	           *:HGStatus*
@@ -1381,37 +1389,37 @@
 
 :HGUpdate						           *:HGUpdate*
 
-   Performs "hg update" on the current file.  This intentionally does not 
-   automatically reload the current buffer, though vim should prompt the user 
+   Performs "hg update" on the current file.  This intentionally does not
+   automatically reload the current buffer, though vim should prompt the user
    to do so if the underlying file is altered by this command.
 
 :HGVimDiff						          *:HGVimDiff*
 
-   With no arguments, this prompts the user for a revision and then uses 
-   vimdiff to display the differences between the current file and the 
-   specified revision.  If no revision is specified, the most recent version 
+   With no arguments, this prompts the user for a revision and then uses
+   vimdiff to display the differences between the current file and the
+   specified revision.  If no revision is specified, the most recent version
    of the file on the current branch is used.
 
-   With one argument, that argument is used as the revision as above.  With 
-   two arguments, the differences between the two revisions is displayed using 
+   With one argument, that argument is used as the revision as above.  With
+   two arguments, the differences between the two revisions is displayed using
    vimdiff.
 
-   With either zero or one argument, the original buffer is used to perform 
-   the vimdiff.  When the other buffer is closed, the original buffer will be 
+   With either zero or one argument, the original buffer is used to perform
+   the vimdiff.  When the other buffer is closed, the original buffer will be
    returned to normal mode.
 
-   Once vimdiff mode is started using the above methods, additional vimdiff 
-   buffers may be added by passing a single version argument to the command.  
+   Once vimdiff mode is started using the above methods, additional vimdiff
+   buffers may be added by passing a single version argument to the command.
    There may be up to 4 vimdiff buffers total.
 
-   Using the 2-argument form of the command resets the vimdiff to only those 2 
-   versions.  Additionally, invoking the command on a different file will 
+   Using the 2-argument form of the command resets the vimdiff to only those 2
+   versions.  Additionally, invoking the command on a different file will
    close the previous vimdiff buffers.
 
 
 4.2 Mappings						  *hgcommand-mappings*
 
-   By default, a mapping is defined for each command.  These mappings execute 
+   By default, a mapping is defined for each command.  These mappings execute
    the default (no-argument) form of each command.
 
       <Leader>hga HGAdd
@@ -1428,20 +1436,20 @@
 
                                                  *hgcommand-mappings-override*
 
-   The default mappings can be overriden 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 
+   The default mappings can be overriden 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).
 
-   For instance, to override the default mapping for :HGAdd to set it to 
+   For instance, to override the default mapping for :HGAdd to set it to
    '\add', add the following to the vimrc: >
 
       nmap \add <Plug>HGAdd
 <
 4.3 Automatic buffer variables			  *hgcommand-buffer-variables*
 
-   Several buffer variables are defined in each HGCommand result buffer.	
-   These may be useful for additional customization in callbacks defined in 
+   Several buffer variables are defined in each HGCommand result buffer.
+   These may be useful for additional customization in callbacks defined in
    the event handlers (please see |hgcommand-events|).
 
    The following variables are automatically defined:
@@ -1452,24 +1460,24 @@
 
 b:hgcmd						                     *b:hgcmd*
 
-   This variable is set to the name of the hg command that created the result 
+   This variable is set to the name of the hg command that created the result
    buffer.
 ==============================================================================
 
 5. Configuration and customization			 *hgcommand-customize*
                                                             *hgcommand-config*
 
-   The HGCommand plugin can be configured in two ways:  by setting 
-   configuration variables (see |hgcommand-options|) or by defining HGCommand 
-   event handlers (see |hgcommand-events|).  Additionally, the HGCommand 
-   plugin provides several option for naming the HG result buffers (see 
-   |hgcommand-naming|) and supported a customized status line (see 
+   The HGCommand plugin can be configured in two ways:  by setting
+   configuration variables (see |hgcommand-options|) or by defining HGCommand
+   event handlers (see |hgcommand-events|).  Additionally, the HGCommand
+   plugin provides several option for naming the HG result buffers (see
+   |hgcommand-naming|) and supported a customized status line (see
    |hgcommand-statusline| and |hgcommand-buffer-management|).
 
 5.1 HGCommand configuration variables			   *hgcommand-options*
 
-   Several variables affect the plugin's behavior.  These variables are 
-   checked at time of execution, and may be defined at the window, buffer, or 
+   Several variables affect the plugin's behavior.  These variables are
+   checked at time of execution, and may be defined at the window, buffer, or
    global level and are checked in that order of precedence.
 
 
@@ -1490,87 +1498,87 @@
 
 HGCommandAnnotateParent			             *HGCommandAnnotateParent*
 
-   This variable, if set to a non-zero value, causes the zero-argument form of 
-   HGAnnotate when invoked on a HGAnnotate buffer to go to the version 
-   previous to that displayed on the current line. If not set, it defaults to 
+   This variable, if set to a non-zero value, causes the zero-argument form of
+   HGAnnotate when invoked on a HGAnnotate buffer to go to the version
+   previous to that displayed on the current line. If not set, it defaults to
    0.
 
 HGCommandCommitOnWrite				      *HGCommandCommitOnWrite*
 
-   This variable, if set to a non-zero value, causes the pending hg commit to 
-   take place immediately as soon as the log message buffer is written.  If 
-   set to zero, only the HGCommit mapping will cause the pending commit to 
+   This variable, if set to a non-zero value, causes the pending hg commit to
+   take place immediately as soon as the log message buffer is written.  If
+   set to zero, only the HGCommit mapping will cause the pending commit to
    occur.  If not set, it defaults to 1.
 
 HGCommandHGExec				                     *HGCommandHGExec*
 
-   This variable controls the executable used for all HG commands.  If not 
+   This variable controls the executable used for all HG commands.  If not
    set, it defaults to "hg".
 
 HGCommandDeleteOnHide				       *HGCommandDeleteOnHide*
 
-   This variable, if set to a non-zero value, causes the temporary HG result 
+   This variable, if set to a non-zero value, causes the temporary HG result
    buffers to automatically delete themselves when hidden.
 
 HGCommandDiffOpt				            *HGCommandDiffOpt*
 
-   This variable, if set, determines the options passed to the diff command of 
+   This variable, if set, determines the options passed to the diff command of
    HG.  If not set, it defaults to 'w'.
 
 HGCommandDiffSplit				          *HGCommandDiffSplit*
 
-   This variable overrides the |HGCommandSplit| variable, but only for buffers 
+   This variable overrides the |HGCommandSplit| variable, but only for buffers
    created with |:HGVimDiff|.
 
 HGCommandEdit					               *HGCommandEdit*
 
-   This variable controls whether the original buffer is replaced ('edit') or 
+   This variable controls whether the original buffer is replaced ('edit') or
    split ('split').  If not set, it defaults to 'edit'.
 
 HGCommandEnableBufferSetup			  *HGCommandEnableBufferSetup*
 
-   This variable, if set to a non-zero value, activates HG buffer management 
-   mode see (|hgcommand-buffer-management|).  This mode means that three 
-   buffer variables, 'HGRepository', 'HGRevision' and 'HGBranch', are set if 
-   the file is HG-controlled.  This is useful for displaying version 
+   This variable, if set to a non-zero value, activates HG buffer management
+   mode see (|hgcommand-buffer-management|).  This mode means that three
+   buffer variables, 'HGRepository', 'HGRevision' and 'HGBranch', are set if
+   the file is HG-controlled.  This is useful for displaying version
    information in the status bar.
 
 HGCommandInteractive				        *HGCommandInteractive*
 
-   This variable, if set to a non-zero value, causes appropriate commands (for 
-   the moment, only |:HGReview|) to query the user for a revision to use 
+   This variable, if set to a non-zero value, causes appropriate commands (for
+   the moment, only |:HGReview|) to query the user for a revision to use
    instead of the current revision if none is specified.
 
 HGCommandNameMarker				         *HGCommandNameMarker*
 
-   This variable, if set, configures the special attention-getting characters 
-   that appear on either side of the hg buffer type in the buffer name.  This 
-   has no effect unless |HGCommandNameResultBuffers| is set to a true value.  
-   If not set, it defaults to '_'.  
+   This variable, if set, configures the special attention-getting characters
+   that appear on either side of the hg buffer type in the buffer name.  This
+   has no effect unless |HGCommandNameResultBuffers| is set to a true value.
+   If not set, it defaults to '_'.
 
 HGCommandNameResultBuffers			  *HGCommandNameResultBuffers*
 
-   This variable, if set to a true value, causes the hg result buffers to be 
-   named in the old way ('<source file name> _<hg command>_').  If not set or 
+   This variable, if set to a true value, causes the hg result buffers to be
+   named in the old way ('<source file name> _<hg command>_').  If not set or
    set to a false value, the result buffer is nameless.
 
 HGCommandSplit					              *HGCommandSplit*
 
-   This variable controls the orientation of the various window splits that 
-   may occur (such as with HGVimDiff, when using a HG command on a HG command 
-   buffer, or when the |HGCommandEdit| variable is set to 'split'.  If set to 
-   'horizontal', the resulting windows will be on stacked on top of one 
-   another.  If set to 'vertical', the resulting windows will be side-by-side.  
+   This variable controls the orientation of the various window splits that
+   may occur (such as with HGVimDiff, when using a HG command on a HG command
+   buffer, or when the |HGCommandEdit| variable is set to 'split'.  If set to
+   'horizontal', the resulting windows will be on stacked on top of one
+   another.  If set to 'vertical', the resulting windows will be side-by-side.
    If not set, it defaults to 'horizontal' for all but HGVimDiff windows.
 
 5.2 HGCommand events				            *hgcommand-events*
 
-   For additional customization, HGCommand can trigger user-defined events.  
-   Event handlers are provided by defining User event autocommands (see 
-   |autocommand|, |User|) in the HGCommand group with patterns matching the 
+   For additional customization, HGCommand can trigger user-defined events.
+   Event handlers are provided by defining User event autocommands (see
+   |autocommand|, |User|) in the HGCommand group with patterns matching the
    event name.
 
-   For instance, the following could be added to the vimrc to provide a 'q' 
+   For instance, the following could be added to the vimrc to provide a 'q'
    mapping to quit a HGCommand scratch buffer: >
 
       augroup HGCommand
@@ -1582,10 +1590,10 @@
    The following hooks are available:
 
 HGBufferCreated		This event is fired just after a hg command result
-                        buffer is created and filled with the result of a hg 
-                        command.  It is executed within the context of the HG 
-                        command buffer.  The HGCommand buffer variables may be 
-                        useful for handlers of this event (please see 
+                        buffer is created and filled with the result of a hg
+                        command.  It is executed within the context of the HG
+                        command buffer.  The HGCommand buffer variables may be
+                        useful for handlers of this event (please see
                         |hgcommand-buffer-variables|).
 
 HGBufferSetup		This event is fired just after HG buffer setup occurs,
@@ -1598,50 +1606,50 @@
                         loads.
 
 HGVimDiffFinish		This event is fired just after the HGVimDiff command
-                        executes to allow customization of, for instance, 
+                        executes to allow customization of, for instance,
                         window placement and focus.
 
 5.3 HGCommand buffer naming				    *hgcommand-naming*
 
-   By default, the buffers containing the result of HG commands are nameless 
-   scratch buffers.  It is intended that buffer variables of those buffers be 
-   used to customize the statusline option so that the user may fully control 
+   By default, the buffers containing the result of HG commands are nameless
+   scratch buffers.  It is intended that buffer variables of those buffers be
+   used to customize the statusline option so that the user may fully control
    the display of result buffers.
 
-   If the old-style naming is desired, please enable the 
-   |HGCommandNameResultBuffers| variable.  Then, each result buffer will 
-   receive a unique name that includes the source file name, the HG command, 
-   and any extra data (such as revision numbers) that were part of the 
+   If the old-style naming is desired, please enable the
+   |HGCommandNameResultBuffers| variable.  Then, each result buffer will
+   receive a unique name that includes the source file name, the HG command,
+   and any extra data (such as revision numbers) that were part of the
    command.
 
 5.4 HGCommand status line support			*hgcommand-statusline*
 
-   It is intended that the user will customize the |'statusline'| option to 
-   include HG result buffer attributes.  A sample function that may be used in 
-   the |'statusline'| option is provided by the plugin, HGGetStatusLine().  In 
-   order to use that function in the status line, do something like the 
+   It is intended that the user will customize the |'statusline'| option to
+   include HG result buffer attributes.  A sample function that may be used in
+   the |'statusline'| option is provided by the plugin, HGGetStatusLine().  In
+   order to use that function in the status line, do something like the
    following: >
 
       set statusline=%<%f\ %{HGGetStatusLine()}\ %h%m%r%=%l,%c%V\ %P
 <
    of which %{HGGetStatusLine()} is the relevant portion.
 
-   The sample HGGetStatusLine() function handles both HG result buffers and 
-   HG-managed files if HGCommand buffer management is enabled (please see 
+   The sample HGGetStatusLine() function handles both HG result buffers and
+   HG-managed files if HGCommand buffer management is enabled (please see
    |hgcommand-buffer-management|).
 
 5.5 HGCommand buffer management		         *hgcommand-buffer-management*
 
-   The HGCommand plugin can operate in buffer management mode, which means 
-   that it attempts to set two buffer variables ('HGRevision' and 'HGBranch') 
-   upon entry into a buffer.  This is rather slow because it means that 'hg 
-   status' will be invoked at each entry into a buffer (during the |BufEnter| 
+   The HGCommand plugin can operate in buffer management mode, which means
+   that it attempts to set two buffer variables ('HGRevision' and 'HGBranch')
+   upon entry into a buffer.  This is rather slow because it means that 'hg
+   status' will be invoked at each entry into a buffer (during the |BufEnter|
    autocommand).
 
-   This mode is enablmed by default.  In order to disable it, set the 
-   |HGCommandEnableBufferSetup| variable to a false (zero) value.  Enabling 
-   this mode simply provides the buffer variables mentioned above.  The user 
-   must explicitly include those in the |'statusline'| option if they are to 
+   This mode is enabled by default.  In order to disable it, set the
+   |HGCommandEnableBufferSetup| variable to a false (zero) value.  Enabling
+   this mode simply provides the buffer variables mentioned above.  The user
+   must explicitly include those in the |'statusline'| option if they are to
    appear in the status line (but see |hgcommand-statusline| for a simple way
    to do that).
 
@@ -1655,10 +1663,10 @@
                  \:set nowrap<CR>
 <
 
-   This splits the buffer vertically, puts an annotation on the left (minus 
-   the header) with the width set to 40. An editable/normal copy is placed on 
-   the right.  The two versions are scroll locked so they  move as one. and 
-   wrapping is turned off so that the lines line up correctly. The advantages 
+   This splits the buffer vertically, puts an annotation on the left (minus
+   the header) with the width set to 40. An editable/normal copy is placed on
+   the right.  The two versions are scroll locked so they  move as one. and
+   wrapping is turned off so that the lines line up correctly. The advantages
    are...
 
    1) You get a versioning on the right.
@@ -1671,9 +1679,9 @@
 
    Please let me know if you run across any.
 
-   HGVimDiff, when using the original (real) source buffer as one of the diff 
-   buffers, uses some hacks to try to restore the state of the original buffer 
-   when the scratch buffer containing the other version is destroyed.  There 
+   HGVimDiff, when using the original (real) source buffer as one of the diff
+   buffers, uses some hacks to try to restore the state of the original buffer
+   when the scratch buffer containing the other version is destroyed.  There
    may still be bugs in here, depending on many configuration details.
 
 ==============================================================================
@@ -1686,4 +1694,4 @@
 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 " v im:tw=78:ts=8:ft=help:norl:
 " vim600: set foldmethod=marker  tabstop=8 shiftwidth=2 softtabstop=2 smartindent smarttab  :
-"fileencoding=iso-8859-15 
+"fileencoding=iso-8859-15
--- a/contrib/win32/ReadMe.html	Sun Jul 23 09:04:14 2006 -0700
+++ b/contrib/win32/ReadMe.html	Fri Aug 18 21:17:28 2006 -0700
@@ -14,7 +14,7 @@
   </head>
 
   <body>
-    <h1>Mercurial version 0.9 for Windows</h1>
+    <h1>Mercurial version 0.9.1 for Windows</h1>
 
     <p>Welcome to Mercurial for Windows!</p>
 
--- a/contrib/win32/mercurial.iss	Sun Jul 23 09:04:14 2006 -0700
+++ b/contrib/win32/mercurial.iss	Fri Aug 18 21:17:28 2006 -0700
@@ -4,7 +4,7 @@
 [Setup]
 AppCopyright=Copyright 2005, 2006 Matt Mackall and others
 AppName=Mercurial
-AppVerName=Mercurial version 0.9
+AppVerName=Mercurial version 0.9.1
 InfoAfterFile=contrib/win32/postinstall.txt
 LicenseFile=COPYING
 ShowLanguageDialog=yes
@@ -14,10 +14,10 @@
 AppUpdatesURL=http://www.selenic.com/mercurial
 AppID={{4B95A5F1-EF59-4B08-BED8-C891C46121B3}
 AppContact=mercurial@selenic.com
-OutputBaseFilename=Mercurial-0.9
+OutputBaseFilename=Mercurial-0.9.1
 DefaultDirName={sd}\Mercurial
 SourceDir=C:\hg\hg-release
-VersionInfoVersion=0.9
+VersionInfoVersion=0.9.1
 VersionInfoDescription=Mercurial distributed SCM
 VersionInfoCopyright=Copyright 2005, 2006 Matt Mackall and others
 VersionInfoCompany=Matt Mackall and others
--- a/contrib/win32/postinstall.txt	Sun Jul 23 09:04:14 2006 -0700
+++ b/contrib/win32/postinstall.txt	Fri Aug 18 21:17:28 2006 -0700
@@ -7,6 +7,62 @@
 Release Notes
 -------------
 
+2006-07-24  v0.9.1
+
+Major changes between Mercurial 0.9 and 0.9.1:
+
+ New features:
+ - You can now configure your 'hgweb' server to let remote users
+   'push' changes over http.
+ - You can now 'import' a patch in a mail message by saving the mail
+   message, and importing it.  This works for patches sent either
+   inline or as attachments.
+ - The 'diff' command now accepts '-rA:B' syntax as a synonym for
+   '-r A -r B', and adds '-b' and '-B' options.
+
+ New contributions and extensions:
+ - The 'acl' extension lets you lock down parts of a repository
+   against incoming changes
+ - The 'extdiff' extension lets you run your favourite graphical
+   change viewer
+ - Comprehensive integration with the 'vim' editor
+ - A restricted shell for 'ssh'-hosted repositories
+ - An importer for 'darcs' repositories
+
+ New hooks added:
+ - 'preupdate' is run before an update or merge in the working
+   directory.
+ - 'update' is run after an update or merge in the working
+   directory.
+
+ Behaviour changes:
+ - NOTE: Mercurial as installed by the Windows binary
+   installer no longer performs automatic line-ending conversion for
+   Unix/Linux compatibility.  To re-enable this feature, edit your
+   'mercurial.ini' file after you upgrade.
+ - The Windows binary installer now automatically adds 'hg' to your
+   '%PATH%'.
+ - The 'backout' command now runs an editor by default, to let you
+   modify the commit message for a backed-out changeset.
+ - An earlier problem with parsing of tags has been fixed.
+   This makes tag parsing slower but more reliable.
+
+ Memory usage and performance improvements:
+ - The 'remove' command has been rewritten to be hundreds of times
+   faster in large repositories.
+ - It is now possible to 'clone' a repository very quickly over a
+   LAN, if the server is configured to allow it.  See the new 'server'
+   section in the 'hgrc' documentation.
+
+ Other changes of note:
+ - Mercurial will now print help for an extension if you type 'hg
+   help EXT_NAME'.
+ - The usual array of bug fixes and documentation improvements.
+ - The integrated web server is now more WSGI-compliant.
+ - Work has begun to solidify Mercurial's API for use by third-party
+   packages.
+
+
 2006-05-10  v0.9
 
 * Major changes between Mercurial 0.8.1 and 0.9:
--- a/doc/hg.1.txt	Sun Jul 23 09:04:14 2006 -0700
+++ b/doc/hg.1.txt	Fri Aug 18 21:17:28 2006 -0700
@@ -216,6 +216,6 @@
 
 COPYING
 -------
-Copyright \(C) 2005 Matt Mackall.
+Copyright \(C) 2005, 2006 Matt Mackall.
 Free use of this software is granted under the terms of the GNU General
 Public License (GPL).
--- a/doc/hgmerge.1.txt	Sun Jul 23 09:04:14 2006 -0700
+++ b/doc/hgmerge.1.txt	Fri Aug 18 21:17:28 2006 -0700
@@ -30,6 +30,6 @@
 
 COPYING
 -------
-Copyright \(C) 2005 Matt Mackall.
+Copyright \(C) 2005, 2006 Matt Mackall.
 Free use of this software is granted under the terms of the GNU General
 Public License (GPL).
--- a/doc/hgrc.5.txt	Sun Jul 23 09:04:14 2006 -0700
+++ b/doc/hgrc.5.txt	Fri Aug 18 21:17:28 2006 -0700
@@ -138,9 +138,17 @@
   from;;
     Optional.  Email address to use in "From" header and SMTP envelope
     of outgoing messages.
+  to;;
+    Optional.  Comma-separated list of recipients' email addresses.
+  cc;;
+    Optional.  Comma-separated list of carbon copy recipients'
+    email addresses.
+  bcc;;
+    Optional.  Comma-separated list of blind carbon copy
+    recipients' email addresses.  Cannot be set interactively.
   method;;
     Optional.  Method to use to send email messages.  If value is
-    "smtp" (default), use SMTP (see section "[mail]" for
+    "smtp" (default), use SMTP (see section "[smtp]" for
     configuration).  Otherwise, use as name of program to run that
     acts like sendmail (takes "-f" option for sender, list of
     recipients on command line, message on stdin).  Normally, setting
@@ -194,7 +202,8 @@
 
   changegroup;;
     Run after a changegroup has been added via push, pull or
-    unbundle. ID of the first new changeset is in $HG_NODE.
+    unbundle. ID of the first new changeset is in $HG_NODE.  URL from
+    which changes came is in $HG_URL.
   commit;;
     Run after a changeset has been created in the local repository.
     ID of the newly created changeset is in $HG_NODE.  Parent
@@ -202,7 +211,7 @@
   incoming;;
     Run after a changeset has been pulled, pushed, or unbundled into
     the local repository.  The ID of the newly arrived changeset is in
-    $HG_NODE.
+    $HG_NODE.  URL that was source of changes came is in $HG_URL.
   outgoing;;
     Run after sending changes from local repository to another.  ID of
     first changeset sent is in $HG_NODE.  Source of operation is in
@@ -210,7 +219,8 @@
   prechangegroup;;
     Run before a changegroup is added via push, pull or unbundle.
     Exit status 0 allows the changegroup to proceed.  Non-zero status
-    will cause the push, pull or unbundle to fail.
+    will cause the push, pull or unbundle to fail.  URL from which
+    changes will come is in $HG_URL.
   precommit;;
     Run before starting a local commit.  Exit status 0 allows the
     commit to proceed.  Non-zero status will cause the commit to fail.
@@ -236,7 +246,8 @@
     before accepting them.  Passed the ID of the first new changeset
     in $HG_NODE.  Exit status 0 allows the transaction to commit.
     Non-zero status will cause the transaction to be rolled back and
-    the push, pull or unbundle will fail.
+    the push, pull or unbundle will fail.  URL that was source of
+    changes is in $HG_URL.
   pretxncommit;;
     Run after a changeset has been created but the transaction not yet
     committed.  Changeset is visible to hook program.  This lets you
@@ -295,7 +306,7 @@
 smtp::
   Configuration for extensions that need to send email messages.
   host;;
-    Optional.  Host name of mail server.  Default: "mail".
+    Host name of mail server, e.g. "mail.example.com".
   port;;
     Optional.  Port to connect to on mail server.  Default: 25.
   tls;;
@@ -440,6 +451,9 @@
   push_ssl;;
     Whether to require that inbound pushes be transported over SSL to
     prevent password sniffing.  Default is true.
+  stripes;;
+    How many lines a "zebra stripe" should span in multiline output.
+    Default is 1; set to 0 to disable.
   style;;
     Which template map style to use.
   templates;;
--- a/doc/ja/hg.1.ja.txt	Sun Jul 23 09:04:14 2006 -0700
+++ b/doc/ja/hg.1.ja.txt	Fri Aug 18 21:17:28 2006 -0700
@@ -862,6 +862,6 @@
 
 著作権情報
 -----
-Copyright (C) 2005 Matt Mackall.
+Copyright (C) 2005, 2006 Matt Mackall.
 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
 認められます。
--- a/doc/ja/hgmerge.1.ja.txt	Sun Jul 23 09:04:14 2006 -0700
+++ b/doc/ja/hgmerge.1.ja.txt	Fri Aug 18 21:17:28 2006 -0700
@@ -32,6 +32,6 @@
 
 著作権情報
 ----
-Copyright (C) 2005 Matt Mackall.
+Copyright (C) 2005, 2006 Matt Mackall.
 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
 認められます。
--- a/hgext/extdiff.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/hgext/extdiff.py	Fri Aug 18 21:17:28 2006 -0700
@@ -5,34 +5,49 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 #
-# allow to use external programs to compare revisions, or revision
-# with working dir. program is called with two arguments: paths to
-# directories containing snapshots of files to compare.
+# The `extdiff' Mercurial extension allows you to use external programs
+# to compare revisions, or revision with working dir.  The external diff
+# programs are called with a configurable set of options and two
+# non-option arguments: paths to directories containing snapshots of
+# files to compare.
 #
-# to enable:
+# To enable this extension:
 #
 #   [extensions]
 #   hgext.extdiff =
 #
-# also allows to configure new diff commands, so you do not need to
-# type "hg extdiff -p kdiff3" always.
+# The `extdiff' extension also allows to configure new diff commands, so
+# you do not need to type "hg extdiff -p kdiff3" always.
 #
 #   [extdiff]
+#   # add new command that runs GNU diff(1) in 'context diff' mode
+#   cmd.cdiff = gdiff
+#   opts.cdiff = -Nprc5
 #   # add new command called vdiff, runs kdiff3
 #   cmd.vdiff = kdiff3
 #   # add new command called meld, runs meld (no need to name twice)
 #   cmd.meld =
+#   # add new command called vimdiff, runs gvimdiff with DirDiff plugin
+#   #(see http://www.vim.org/scripts/script.php?script_id=102)
+#   cmd.vimdiff = LC_ALL=C gvim -f '+bdel 1 2' '+ execute "DirDiff ".argv(0)." ".argv(1)'
 #
-# you can use -I/-X and list of file or directory names like normal
-# "hg diff" command. extdiff makes snapshots of only needed files, so
-# compare program will be fast.
+# Each custom diff commands can have two parts: a `cmd' and an `opts'
+# part.  The cmd.xxx option defines the name of an executable program
+# that will be run, and opts.xxx defines a set of command-line options
+# which will be inserted to the command between the program name and
+# the files/directories to diff (i.e. the cdiff example above).
+#
+# You can use -I/-X and list of file or directory names like normal
+# "hg diff" command.  The `extdiff' extension makes snapshots of only
+# needed files, so running the external diff program will actually be
+# pretty fast (at least faster than having to compare the entire tree).
 
 from mercurial.demandload import demandload
 from mercurial.i18n import gettext as _
 from mercurial.node import *
-demandload(globals(), 'mercurial:commands,util os shutil tempfile')
+demandload(globals(), 'mercurial:commands,cmdutil,util os shutil tempfile')
 
-def dodiff(ui, repo, diffcmd, pats, opts):
+def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
     def snapshot_node(files, node):
         '''snapshot files as of some revision'''
         changes = repo.changelog.read(node)
@@ -76,9 +91,9 @@
         return dirname
 
     node1, node2 = commands.revpair(ui, repo, opts['rev'])
-    files, matchfn, anypats = commands.matchpats(repo, pats, opts)
-    modified, added, removed, deleted, unknown = repo.changes(
-        node1, node2, files, match=matchfn)
+    files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
+    modified, added, removed, deleted, unknown = repo.status(
+        node1, node2, files, match=matchfn)[:5]
     if not (modified or added or removed):
         return 0
 
@@ -89,9 +104,12 @@
             dir2 = snapshot_node(modified + added, node2)
         else:
             dir2 = snapshot_wdir(modified + added)
-        util.system('%s %s "%s" "%s"' %
-                    (diffcmd, ' '.join(opts['option']), dir1, dir2),
-                    cwd=tmproot)
+        cmdline = ('%s %s %s %s' %
+                   (util.shellquote(diffcmd),
+                    ' '.join(map(util.shellquote, diffopts)),
+                    util.shellquote(dir1), util.shellquote(dir2)))
+        ui.debug('running %r in %s\n' % (cmdline, tmproot))
+        util.system(cmdline, cwd=tmproot)
         return 1
     finally:
         ui.note(_('cleaning up temp directory\n'))
@@ -101,7 +119,9 @@
     '''use external program to diff repository (or selected files)
 
     Show differences between revisions for the specified files, using
-    an external program.  The default program used is "diff -Npru".
+    an external program.  The default program used is diff, with
+    default options "-Npru".
+
     To select a different program, use the -p option.  The program
     will be passed the names of two directories to compare.  To pass
     additional options to the program, use the -o option.  These will
@@ -112,7 +132,8 @@
     specified then that revision is compared to the working
     directory, and, when no revisions are specified, the
     working directory files are compared to its parent.'''
-    return dodiff(ui, repo, opts['program'] or 'diff -Npru', pats, opts)
+    return dodiff(ui, repo, opts['program'] or 'diff',
+                  opts['option'] or ['-Npru'], pats, opts)
 
 cmdtable = {
     "extdiff":
@@ -130,20 +151,24 @@
         if not cmd.startswith('cmd.'): continue
         cmd = cmd[4:]
         if not path: path = cmd
+        diffopts = ui.config('extdiff', 'opts.' + cmd, '')
+        diffopts = diffopts and [diffopts] or []
         def save(cmd, path):
             '''use closure to save diff command to use'''
             def mydiff(ui, repo, *pats, **opts):
-                return dodiff(ui, repo, path, pats, opts)
-            mydiff.__doc__ = '''use %s to diff repository (or selected files)
+                return dodiff(ui, repo, path, diffopts, pats, opts)
+            mydiff.__doc__ = '''use %(path)r to diff repository (or selected files)
 
             Show differences between revisions for the specified
-            files, using the %s program.
+            files, using the %(path)r program.
 
             When two revision arguments are given, then changes are
             shown between those revisions. If only one revision is
             specified then that revision is compared to the working
             directory, and, when no revisions are specified, the
-            working directory files are compared to its parent.''' % (cmd, cmd)
+            working directory files are compared to its parent.''' % {
+                'path': path,
+                }
             return mydiff
         cmdtable[cmd] = (save(cmd, path),
                          cmdtable['extdiff'][1][1:],
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/fetch.py	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,99 @@
+# fetch.py - pull and merge remote changes
+#
+# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+from mercurial.demandload import *
+from mercurial.i18n import gettext as _
+from mercurial.node import *
+demandload(globals(), 'mercurial:commands,hg,node,util')
+
+def fetch(ui, repo, source='default', **opts):
+    '''Pull changes from a remote repository, merge new changes if needed.
+
+    This finds all changes from the repository at the specified path
+    or URL and adds them to the local repository.
+
+    If the pulled changes add a new head, the head is automatically
+    merged, and the result of the merge is committed.  Otherwise, the
+    working directory is updated.'''
+
+    def postincoming(other, modheads):
+        if modheads == 0:
+            return 0
+        if modheads == 1:
+            return hg.clean(repo, repo.changelog.tip(), wlock=wlock)
+        newheads = repo.heads(parent)
+        newchildren = [n for n in repo.heads(parent) if n != parent]
+        newparent = parent
+        if newchildren:
+            newparent = newchildren[0]
+            hg.clean(repo, newparent, wlock=wlock)
+        newheads = [n for n in repo.heads() if n != newparent]
+        err = False
+        if newheads:
+            ui.status(_('merging with new head %d:%s\n') %
+                      (repo.changelog.rev(newheads[0]), short(newheads[0])))
+            err = hg.merge(repo, newheads[0], remind=False, wlock=wlock)
+        if not err and len(newheads) > 1:
+            ui.status(_('not merging with %d other new heads '
+                        '(use "hg heads" and "hg merge" to merge them)') %
+                      (len(newheads) - 1))
+        if not err:
+            mod, add, rem = repo.status(wlock=wlock)[:3]
+            message = (commands.logmessage(opts) or
+                       (_('Automated merge with %s') % other.url()))
+            n = repo.commit(mod + add + rem, message,
+                            opts['user'], opts['date'], lock=lock, wlock=wlock,
+                            force_editor=opts.get('force_editor'))
+            ui.status(_('new changeset %d:%s merges remote changes '
+                        'with local\n') % (repo.changelog.rev(n),
+                                           short(n)))
+    def pull():
+        commands.setremoteconfig(ui, opts)
+
+        other = hg.repository(ui, ui.expandpath(source))
+        ui.status(_('pulling from %s\n') % ui.expandpath(source))
+        revs = None
+        if opts['rev'] and not other.local():
+            raise util.Abort(_("fetch -r doesn't work for remote repositories yet"))
+        elif opts['rev']:
+            revs = [other.lookup(rev) for rev in opts['rev']]
+        modheads = repo.pull(other, heads=revs, lock=lock)
+        return postincoming(other, modheads)
+        
+    parent, p2 = repo.dirstate.parents()
+    if parent != repo.changelog.tip():
+        raise util.Abort(_('working dir not at tip '
+                           '(use "hg update" to check out tip)'))
+    if p2 != nullid:
+        raise util.Abort(_('outstanding uncommitted merge'))
+    wlock = repo.wlock()
+    lock = repo.lock()
+    try:
+        mod, add, rem = repo.status(wlock=wlock)[:3]
+        if mod or add or rem:
+            raise util.Abort(_('outstanding uncommitted changes'))
+        if len(repo.heads()) > 1:
+            raise util.Abort(_('multiple heads in this repository '
+                               '(use "hg heads" and "hg merge" to merge)'))
+        return pull()
+    finally:
+        lock.release()
+        wlock.release()
+
+cmdtable = {
+    'fetch':
+    (fetch,
+     [('e', 'ssh', '', _('specify ssh command to use')),
+      ('m', 'message', '', _('use <text> as commit message')),
+      ('l', 'logfile', '', _('read the commit message from <file>')),
+      ('d', 'date', '', _('record datecode as commit date')),
+      ('u', 'user', '', _('record user as commiter')),
+      ('r', 'rev', [], _('a specific revision you would like to pull')),
+      ('f', 'force-editor', None, _('edit commit message')),
+      ('', 'remotecmd', '', _('hg command to run on the remote side'))],
+     'hg fetch [SOURCE]'),
+    }
--- a/hgext/gpg.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/hgext/gpg.py	Fri Aug 18 21:17:28 2006 -0700
@@ -221,7 +221,7 @@
         repo.opener("localsigs", "ab").write(sigmessage)
         return
 
-    for x in repo.changes():
+    for x in repo.status()[:5]:
         if ".hgsigs" in x and not opts["force"]:
             raise util.Abort(_("working copy of .hgsigs is changed "
                                "(please commit .hgsigs manually "
--- a/hgext/hbisect.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/hgext/hbisect.py	Fri Aug 18 21:17:28 2006 -0700
@@ -23,10 +23,10 @@
     return parents.pop()
 
 def check_clean(ui, repo):
-        modified, added, removed, deleted, unknown = repo.changes()
-        if modified or added or removed:
-            ui.warn("Repository is not clean, please commit or revert\n")
-            sys.exit(1)
+    modified, added, removed, deleted, unknown = repo.status()[:5]
+    if modified or added or removed:
+        ui.warn("Repository is not clean, please commit or revert\n")
+        sys.exit(1)
 
 class bisect(object):
     """dichotomic search in the DAG of changesets"""
@@ -50,7 +50,7 @@
             if r:
                 self.badrev = hg.bin(r.pop(0))
 
-    def __del__(self):
+    def write(self):
         if not os.path.isdir(self.path):
             return
         f = self.opener(self.good_path, "w")
@@ -197,7 +197,7 @@
         check_clean(self.ui, self.repo)
         rev = self.next()
         if rev is not None:
-            return self.repo.update(rev, force=True)
+            return hg.clean(self.repo, rev)
 
     def good(self, rev):
         self.goodrevs.append(rev)
@@ -288,7 +288,10 @@
     if len(args) > bisectcmdtable[cmd][1]:
         ui.warn(_("bisect: Too many arguments\n"))
         return help_()
-    return bisectcmdtable[cmd][0](*args)
+    try:
+        return bisectcmdtable[cmd][0](*args)
+    finally:
+        b.write()
 
 cmdtable = {
     "bisect": (bisect_run, [], _("hg bisect [help|init|reset|next|good|bad]")),
--- a/hgext/hgk.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/hgext/hgk.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,12 +1,13 @@
 # Minimal support for git commands on an hg repository
 #
-# Copyright 2005 Chris Mason <mason@suse.com>
+# Copyright 2005, 2006 Chris Mason <mason@suse.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-import time, sys, signal, os
-from mercurial import hg, mdiff, fancyopts, commands, ui, util
+from mercurial.demandload import *
+demandload(globals(), 'time sys signal os')
+demandload(globals(), 'mercurial:hg,mdiff,fancyopts,commands,ui,util')
 
 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
            changes=None, text=False):
@@ -14,7 +15,7 @@
         return time.asctime(time.gmtime(c[2][0]))
 
     if not changes:
-        changes = repo.changes(node1, node2, files, match=match)
+        changes = repo.status(node1, node2, files, match=match)[:5]
     modified, added, removed, deleted, unknown = changes
     if files:
         modified, added, removed = map(lambda x: filterfiles(files, x),
@@ -67,12 +68,12 @@
         if node2:
             change = repo.changelog.read(node2)
             mmap2 = repo.manifest.read(change[0])
-            modified, added, removed, deleted, unknown = repo.changes(node1, node2)
+            modified, added, removed, deleted, unknown = repo.status(node1, node2)[:5]
             def read(f): return repo.file(f).read(mmap2[f])
             date2 = date(change)
         else:
             date2 = time.asctime()
-            modified, added, removed, deleted, unknown = repo.changes(node1)
+            modified, added, removed, deleted, unknown = repo.status(node1)[:5]
             if not node1:
                 node1 = repo.dirstate.parents()[0]
             def read(f): return file(os.path.join(repo.root, f)).read()
@@ -334,6 +335,3 @@
                            ('n', 'max-count', 0, 'max-count')],
                  "hg debug-rev-list [options] revs"),
 }
-
-def reposetup(ui, repo):
-    pass
--- a/hgext/mq.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/hgext/mq.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 # queue.py - patch queues for mercurial
 #
-# Copyright 2005 Chris Mason <mason@suse.com>
+# Copyright 2005, 2006 Chris Mason <mason@suse.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -30,22 +30,30 @@
 '''
 
 from mercurial.demandload import *
+from mercurial.i18n import gettext as _
 demandload(globals(), "os sys re struct traceback errno bz2")
-from mercurial.i18n import gettext as _
-from mercurial import ui, hg, revlog, commands, util
+demandload(globals(), "mercurial:cmdutil,commands,hg,patch,revlog,ui,util")
+
+commands.norepo += " qclone qversion"
 
-versionstr = "0.45"
+class statusentry:
+    def __init__(self, rev, name=None):
+        if not name:
+            fields = rev.split(':')
+            if len(fields) == 2:
+                self.rev, self.name = fields
+            else:
+                self.rev, self.name = None, None
+        else:
+            self.rev, self.name = rev, name
 
-repomap = {}
+    def __str__(self):
+        return self.rev + ':' + self.name
 
-commands.norepo += " qversion"
 class queue:
     def __init__(self, ui, path, patchdir=None):
         self.basepath = path
-        if patchdir:
-            self.path = patchdir
-        else:
-            self.path = os.path.join(path, "patches")
+        self.path = patchdir or os.path.join(path, "patches")
         self.opener = util.opener(self.path)
         self.ui = ui
         self.applied = []
@@ -54,13 +62,26 @@
         self.series_dirty = 0
         self.series_path = "series"
         self.status_path = "status"
+        self.guards_path = "guards"
+        self.active_guards = None
+        self.guards_dirty = False
+        self._diffopts = None
 
-        if os.path.exists(os.path.join(self.path, self.series_path)):
+        if os.path.exists(self.join(self.series_path)):
             self.full_series = self.opener(self.series_path).read().splitlines()
-        self.read_series(self.full_series)
+        self.parse_series()
+
+        if os.path.exists(self.join(self.status_path)):
+            lines = self.opener(self.status_path).read().splitlines()
+            self.applied = [statusentry(l) for l in lines]
 
-        if os.path.exists(os.path.join(self.path, self.status_path)):
-            self.applied = self.opener(self.status_path).read().splitlines()
+    def diffopts(self):
+        if self._diffopts is None:
+            self._diffopts = patch.diffopts(self.ui)
+        return self._diffopts
+
+    def join(self, *p):
+        return os.path.join(self.path, *p)
 
     def find_series(self, patch):
         pre = re.compile("(\s*)([^#]+)")
@@ -75,34 +96,132 @@
             index += 1
         return None
 
-    def read_series(self, list):
-        def matcher(list):
-            pre = re.compile("(\s*)([^#]+)")
-            for l in list:
-                m = pre.match(l)
-                if m:
-                    s = m.group(2)
-                    s = s.rstrip()
-                    if len(s) > 0:
-                        yield s
+    guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
+
+    def parse_series(self):
         self.series = []
-        self.series = [ x for x in matcher(list) ]
+        self.series_guards = []
+        for l in self.full_series:
+            h = l.find('#')
+            if h == -1:
+                patch = l
+                comment = ''
+            elif h == 0:
+                continue
+            else:
+                patch = l[:h]
+                comment = l[h:]
+            patch = patch.strip()
+            if patch:
+                self.series.append(patch)
+                self.series_guards.append(self.guard_re.findall(comment))
+
+    def check_guard(self, guard):
+        bad_chars = '# \t\r\n\f'
+        first = guard[0]
+        for c in '-+':
+            if first == c:
+                return (_('guard %r starts with invalid character: %r') %
+                        (guard, c))
+        for c in bad_chars:
+            if c in guard:
+                return _('invalid character in guard %r: %r') % (guard, c)
+        
+    def set_active(self, guards):
+        for guard in guards:
+            bad = self.check_guard(guard)
+            if bad:
+                raise util.Abort(bad)
+        guards = dict.fromkeys(guards).keys()
+        guards.sort()
+        self.ui.debug('active guards: %s\n' % ' '.join(guards))
+        self.active_guards = guards
+        self.guards_dirty = True
+
+    def active(self):
+        if self.active_guards is None:
+            self.active_guards = []
+            try:
+                guards = self.opener(self.guards_path).read().split()
+            except IOError, err:
+                if err.errno != errno.ENOENT: raise
+                guards = []
+            for i, guard in enumerate(guards):
+                bad = self.check_guard(guard)
+                if bad:
+                    self.ui.warn('%s:%d: %s\n' %
+                                 (self.join(self.guards_path), i + 1, bad))
+                else:
+                    self.active_guards.append(guard)
+        return self.active_guards
+
+    def set_guards(self, idx, guards):
+        for g in guards:
+            if len(g) < 2:
+                raise util.Abort(_('guard %r too short') % g)
+            if g[0] not in '-+':
+                raise util.Abort(_('guard %r starts with invalid char') % g)
+            bad = self.check_guard(g[1:])
+            if bad:
+                raise util.Abort(bad)
+        drop = self.guard_re.sub('', self.full_series[idx])
+        self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
+        self.parse_series()
+        self.series_dirty = True
+        
+    def pushable(self, idx):
+        if isinstance(idx, str):
+            idx = self.series.index(idx)
+        patchguards = self.series_guards[idx]
+        if not patchguards:
+            return True, None
+        default = False
+        guards = self.active()
+        exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
+        if exactneg:
+            return False, exactneg[0]
+        pos = [g for g in patchguards if g[0] == '+']
+        exactpos = [g for g in pos if g[1:] in guards]
+        if pos:
+            if exactpos:
+                return True, exactpos[0]
+            return False, pos
+        return True, ''
+
+    def explain_pushable(self, idx, all_patches=False):
+        write = all_patches and self.ui.write or self.ui.warn
+        if all_patches or self.ui.verbose:
+            if isinstance(idx, str):
+                idx = self.series.index(idx)
+            pushable, why = self.pushable(idx)
+            if all_patches and pushable:
+                if why is None:
+                    write(_('allowing %s - no guards in effect\n') %
+                          self.series[idx])
+                else:
+                    if not why:
+                        write(_('allowing %s - no matching negative guards\n') %
+                              self.series[idx])
+                    else:
+                        write(_('allowing %s - guarded by %r\n') %
+                              (self.series[idx], why))
+            if not pushable:
+                if why:
+                    write(_('skipping %s - guarded by %r\n') %
+                          (self.series[idx], ' '.join(why)))
+                else:
+                    write(_('skipping %s - no matching guards\n') %
+                          self.series[idx])
 
     def save_dirty(self):
-        if self.applied_dirty:
-            if len(self.applied) > 0:
-                nl = "\n"
-            else:
-                nl = ""
-            f = self.opener(self.status_path, "w")
-            f.write("\n".join(self.applied) + nl)
-        if self.series_dirty:
-            if len(self.full_series) > 0:
-                nl = "\n"
-            else:
-                nl = ""
-            f = self.opener(self.series_path, "w")
-            f.write("\n".join(self.full_series) + nl)
+        def write_list(items, path):
+            fp = self.opener(path, 'w')
+            for i in items:
+                print >> fp, i
+            fp.close()
+        if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
+        if self.series_dirty: write_list(self.full_series, self.series_path)
+        if self.guards_dirty: write_list(self.active_guards, self.guards_path)
 
     def readheaders(self, patch):
         def eatdiff(lines):
@@ -122,7 +241,7 @@
                 else:
                     break
 
-        pf = os.path.join(self.path, patch)
+        pf = self.join(patch)
         message = []
         comments = []
         user = None
@@ -133,6 +252,9 @@
 
         for line in file(pf):
             line = line.rstrip()
+            if line.startswith('diff --git'):
+                diffstart = 2
+                break
             if diffstart:
                 if line.startswith('+++ '):
                     diffstart = 2
@@ -178,6 +300,13 @@
             message.insert(0, subject)
         return (message, comments, user, date, diffstart > 1)
 
+    def printdiff(self, repo, node1, node2=None, files=None,
+                  fp=None, changes=None, opts={}):
+        fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
+
+        patch.diff(repo, node1, node2, fns, match=matchfn,
+                   fp=fp, changes=changes, opts=self.diffopts())
+
     def mergeone(self, repo, mergeq, head, patch, rev, wlock):
         # first try just applying the patch
         (err, n) = self.apply(repo, [ patch ], update_status=False,
@@ -187,35 +316,31 @@
             return (err, n)
 
         if n is None:
-            self.ui.warn("apply failed for patch %s\n" % patch)
-            sys.exit(1)
+            raise util.Abort(_("apply failed for patch %s") % patch)
 
         self.ui.warn("patch didn't work out, merging %s\n" % patch)
 
         # apply failed, strip away that rev and merge.
-        repo.update(head, allow=False, force=True, wlock=wlock)
+        hg.clean(repo, head, wlock=wlock)
         self.strip(repo, n, update=False, backup='strip', wlock=wlock)
 
         c = repo.changelog.read(rev)
-        ret = repo.update(rev, allow=True, wlock=wlock)
+        ret = hg.merge(repo, rev, wlock=wlock)
         if ret:
-            self.ui.warn("update returned %d\n" % ret)
-            sys.exit(1)
+            raise util.Abort(_("update returned %d") % ret)
         n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
         if n == None:
-            self.ui.warn("repo commit failed\n")
-            sys.exit(1)
+            raise util.Abort(_("repo commit failed"))
         try:
             message, comments, user, date, patchfound = mergeq.readheaders(patch)
         except:
-            self.ui.warn("Unable to read %s\n" % patch)
-            sys.exit(1)
+            raise util.Abort(_("unable to read %s") % patch)
 
         patchf = self.opener(patch, "w")
         if comments:
             comments = "\n".join(comments) + '\n\n'
             patchf.write(comments)
-        commands.dodiff(patchf, self.ui, repo, head, n)
+        self.printdiff(repo, head, n, fp=patchf)
         patchf.close()
         return (0, n)
 
@@ -226,12 +351,10 @@
                 return p1
             if len(self.applied) == 0:
                 return None
-            (top, patch) = self.applied[-1].split(':')
-            top = revlog.bin(top)
-            return top
+            return revlog.bin(self.applied[-1].rev)
         pp = repo.changelog.parents(rev)
         if pp[1] != revlog.nullid:
-            arevs = [ x.split(':')[0] for x in self.applied ]
+            arevs = [ x.rev for x in self.applied ]
             p0 = revlog.hex(pp[0])
             p1 = revlog.hex(pp[1])
             if p0 in arevs:
@@ -251,17 +374,20 @@
             pname = ".hg.patches.merge.marker"
             n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
                             wlock=wlock)
-            self.applied.append(revlog.hex(n) + ":" + pname)
+            self.applied.append(statusentry(revlog.hex(n), pname))
             self.applied_dirty = 1
 
         head = self.qparents(repo)
 
         for patch in series:
-            patch = mergeq.lookup(patch)
+            patch = mergeq.lookup(patch, strict=True)
             if not patch:
                 self.ui.warn("patch %s does not exist\n" % patch)
                 return (1, None)
-
+            pushable, reason = self.pushable(patch)
+            if not pushable:
+                self.explain_pushable(patch, all_patches=True)
+                continue
             info = mergeq.isapplied(patch)
             if not info:
                 self.ui.warn("patch %s is not applied\n" % patch)
@@ -269,102 +395,80 @@
             rev = revlog.bin(info[1])
             (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
             if head:
-                self.applied.append(revlog.hex(head) + ":" + patch)
+                self.applied.append(statusentry(revlog.hex(head), patch))
                 self.applied_dirty = 1
             if err:
                 return (err, head)
         return (0, head)
 
+    def patch(self, repo, patchfile):
+        '''Apply patchfile  to the working directory.
+        patchfile: file name of patch'''
+        try:
+            (files, fuzz) = patch.patch(patchfile, self.ui, strip=1,
+                                        cwd=repo.root)
+        except Exception, inst:
+            self.ui.note(str(inst) + '\n')
+            if not self.ui.verbose:
+                self.ui.warn("patch failed, unable to continue (try -v)\n")
+            return (False, [], False)
+
+        return (True, files, fuzz)
+
     def apply(self, repo, series, list=False, update_status=True,
               strict=False, patchdir=None, merge=None, wlock=None):
         # TODO unify with commands.py
         if not patchdir:
             patchdir = self.path
-        pwd = os.getcwd()
-        os.chdir(repo.root)
         err = 0
         if not wlock:
             wlock = repo.wlock()
         lock = repo.lock()
         tr = repo.transaction()
         n = None
-        for patch in series:
-            self.ui.warn("applying %s\n" % patch)
-            pf = os.path.join(patchdir, patch)
+        for patchname in series:
+            pushable, reason = self.pushable(patchname)
+            if not pushable:
+                self.explain_pushable(patchname, all_patches=True)
+                continue
+            self.ui.warn("applying %s\n" % patchname)
+            pf = os.path.join(patchdir, patchname)
 
             try:
-                message, comments, user, date, patchfound = self.readheaders(patch)
+                message, comments, user, date, patchfound = self.readheaders(patchname)
             except:
-                self.ui.warn("Unable to read %s\n" % pf)
+                self.ui.warn("Unable to read %s\n" % patchname)
                 err = 1
                 break
 
             if not message:
-                message = "imported patch %s\n" % patch
+                message = "imported patch %s\n" % patchname
             else:
                 if list:
-                    message.append("\nimported patch %s" % patch)
+                    message.append("\nimported patch %s" % patchname)
                 message = '\n'.join(message)
 
-            try:
-                pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
-                f = os.popen("%s -p1 --no-backup-if-mismatch < '%s'" % (pp, pf))
-            except:
-                self.ui.warn("patch failed, unable to continue (try -v)\n")
-                err = 1
-                break
-            files = []
-            fuzz = False
-            for l in f:
-                l = l.rstrip('\r\n');
-                if self.ui.verbose:
-                    self.ui.warn(l + "\n")
-                if l[:14] == 'patching file ':
-                    pf = os.path.normpath(l[14:])
-                    # when patch finds a space in the file name, it puts
-                    # single quotes around the filename.  strip them off
-                    if pf[0] == "'" and pf[-1] == "'":
-                        pf = pf[1:-1]
-                    if pf not in files:
-                        files.append(pf)
-                    printed_file = False
-                    file_str = l
-                elif l.find('with fuzz') >= 0:
-                    if not printed_file:
-                        self.ui.warn(file_str + '\n')
-                        printed_file = True
-                    self.ui.warn(l + '\n')
-                    fuzz = True
-                elif l.find('saving rejects to file') >= 0:
-                    self.ui.warn(l + '\n')
-                elif l.find('FAILED') >= 0:
-                    if not printed_file:
-                        self.ui.warn(file_str + '\n')
-                        printed_file = True
-                    self.ui.warn(l + '\n')
-            patcherr = f.close()
+            (patcherr, files, fuzz) = self.patch(repo, pf)
+            patcherr = not patcherr
 
-            if merge and len(files) > 0:
+            if merge and files:
                 # Mark as merged and update dirstate parent info
-                repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
+                repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
                 p1, p2 = repo.dirstate.parents()
                 repo.dirstate.setparents(p1, merge)
-            if len(files) > 0:
-                commands.addremove_lock(self.ui, repo, files,
-                                        opts={}, wlock=wlock)
+            files = patch.updatedir(self.ui, repo, files, wlock=wlock)
             n = repo.commit(files, message, user, date, force=1, lock=lock,
                             wlock=wlock)
 
             if n == None:
-                self.ui.warn("repo commit failed\n")
-                sys.exit(1)
+                raise util.Abort(_("repo commit failed"))
 
             if update_status:
-                self.applied.append(revlog.hex(n) + ":" + patch)
+                self.applied.append(statusentry(revlog.hex(n), patchname))
 
             if patcherr:
                 if not patchfound:
-                    self.ui.warn("patch %s is empty\n" % patch)
+                    self.ui.warn("patch %s is empty\n" % patchname)
                     err = 0
                 else:
                     self.ui.warn("patch failed, rejects left in working dir\n")
@@ -376,49 +480,58 @@
                 err = 1
                 break
         tr.close()
-        os.chdir(pwd)
         return (err, n)
 
-    def delete(self, repo, patch):
-        patch = self.lookup(patch)
-        info = self.isapplied(patch)
-        if info:
-            self.ui.warn("cannot delete applied patch %s\n" % patch)
-            sys.exit(1)
-        if patch not in self.series:
-            self.ui.warn("patch %s not in series file\n" % patch)
-            sys.exit(1)
-        i = self.find_series(patch)
-        del self.full_series[i]
-        self.read_series(self.full_series)
+    def delete(self, repo, patches, keep=False):
+        realpatches = []
+        for patch in patches:
+            patch = self.lookup(patch, strict=True)
+            info = self.isapplied(patch)
+            if info:
+                raise util.Abort(_("cannot delete applied patch %s") % patch)
+            if patch not in self.series:
+                raise util.Abort(_("patch %s not in series file") % patch)
+            realpatches.append(patch)
+
+        if not keep:
+            r = self.qrepo()
+            if r:
+                r.remove(realpatches, True)
+            else:
+                os.unlink(self.join(patch))
+
+        indices = [self.find_series(p) for p in realpatches]
+        indices.sort()
+        for i in indices[-1::-1]:
+            del self.full_series[i]
+        self.parse_series()
         self.series_dirty = 1
 
     def check_toppatch(self, repo):
         if len(self.applied) > 0:
-            (top, patch) = self.applied[-1].split(':')
-            top = revlog.bin(top)
+            top = revlog.bin(self.applied[-1].rev)
             pp = repo.dirstate.parents()
             if top not in pp:
-                self.ui.warn("queue top not at dirstate parents. top %s dirstate %s %s\n" %( revlog.short(top), revlog.short(pp[0]), revlog.short(pp[1])))
-                sys.exit(1)
+                raise util.Abort(_("queue top not at same revision as working directory"))
             return top
         return None
-    def check_localchanges(self, repo):
-        (c, a, r, d, u) = repo.changes(None, None)
-        if c or a or d or r:
-            self.ui.write("Local changes found, refresh first\n")
-            sys.exit(1)
+    def check_localchanges(self, repo, force=False, refresh=True):
+        m, a, r, d = repo.status()[:4]
+        if m or a or r or d:
+            if not force:
+                if refresh:
+                    raise util.Abort(_("local changes found, refresh first"))
+                else:
+                    raise util.Abort(_("local changes found"))
+        return m, a, r, d
     def new(self, repo, patch, msg=None, force=None):
-        commitfiles = []
-        (c, a, r, d, u) = repo.changes(None, None)
-        if c or a or d or r:
-            if not force:
-                raise util.Abort(_("Local changes found, refresh first"))
-            else:
-                commitfiles = c + a + r
+        if os.path.exists(self.join(patch)):
+            raise util.Abort(_('patch "%s" already exists') % patch)
+        m, a, r, d = self.check_localchanges(repo, force)
+        commitfiles = m + a + r
         self.check_toppatch(repo)
         wlock = repo.wlock()
-        insert = self.series_end()
+        insert = self.full_series_end()
         if msg:
             n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
                             wlock=wlock)
@@ -426,11 +539,10 @@
             n = repo.commit(commitfiles,
                             "New patch: %s" % patch, force=True, wlock=wlock)
         if n == None:
-            self.ui.warn("repo commit failed\n")
-            sys.exit(1)
+            raise util.Abort(_("repo commit failed"))
         self.full_series[insert:insert] = [patch]
-        self.applied.append(revlog.hex(n) + ":" + patch)
-        self.read_series(self.full_series)
+        self.applied.append(statusentry(revlog.hex(n), patch))
+        self.parse_series()
         self.series_dirty = 1
         self.applied_dirty = 1
         p = self.opener(patch, "w")
@@ -509,9 +621,9 @@
             # we go in two steps here so the strip loop happens in a
             # sensible order.  When stripping many files, this helps keep
             # our disk access patterns under control.
-            list = seen.keys()
-            list.sort()
-            for f in list:
+            seen_list = seen.keys()
+            seen_list.sort()
+            for f in seen_list:
                 ff = repo.file(f)
                 filerev = seen[f]
                 if filerev != 0:
@@ -530,8 +642,9 @@
         revnum = chlog.rev(rev)
 
         if update:
+            self.check_localchanges(repo, refresh=False)
             urev = self.qparents(repo, rev)
-            repo.update(urev, allow=False, force=True, wlock=wlock)
+            hg.clean(repo, urev, wlock=wlock)
             repo.dirstate.write()
 
         # save is a list of all the branches we are truncating away
@@ -540,7 +653,6 @@
         saveheads = []
         savebases = {}
 
-        tip = chlog.tip()
         heads = limitheads(chlog, rev)
         seen = {}
 
@@ -571,7 +683,7 @@
                         savebases[x] = 1
 
         # create a changegroup for all the branches we need to keep
-        if backup is "all":
+        if backup == "all":
             backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
             bundle(backupch)
         if saveheads:
@@ -586,37 +698,89 @@
         if saveheads:
             self.ui.status("adding branch\n")
             commands.unbundle(self.ui, repo, chgrpfile, update=False)
-            if backup is not "strip":
+            if backup != "strip":
                 os.unlink(chgrpfile)
 
     def isapplied(self, patch):
         """returns (index, rev, patch)"""
         for i in xrange(len(self.applied)):
-            p = self.applied[i]
-            a = p.split(':')
-            if a[1] == patch:
-                return (i, a[0], a[1])
+            a = self.applied[i]
+            if a.name == patch:
+                return (i, a.rev, a.name)
         return None
 
-    def lookup(self, patch):
+    # if the exact patch name does not exist, we try a few 
+    # variations.  If strict is passed, we try only #1
+    #
+    # 1) a number to indicate an offset in the series file
+    # 2) a unique substring of the patch name was given
+    # 3) patchname[-+]num to indicate an offset in the series file
+    def lookup(self, patch, strict=False):
+        patch = patch and str(patch)
+
+        def partial_name(s):
+            if s in self.series:
+                return s
+            matches = [x for x in self.series if s in x]
+            if len(matches) > 1:
+                self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
+                for m in matches:
+                    self.ui.warn('  %s\n' % m)
+                return None
+            if matches:
+                return matches[0]
+            if len(self.series) > 0 and len(self.applied) > 0:
+                if s == 'qtip':
+                    return self.series[self.series_end()-1]
+                if s == 'qbase':
+                    return self.series[0]
+            return None
         if patch == None:
             return None
-        if patch in self.series:
-            return patch
-        if not os.path.isfile(os.path.join(self.path, patch)):
+
+        # we don't want to return a partial match until we make
+        # sure the file name passed in does not exist (checked below)
+        res = partial_name(patch)
+        if res and res == patch:
+            return res
+
+        if not os.path.isfile(self.join(patch)):
             try:
                 sno = int(patch)
             except(ValueError, OverflowError):
-                self.ui.warn("patch %s not in series\n" % patch)
-                sys.exit(1)
-            if sno >= len(self.series):
-                self.ui.warn("patch number %d is out of range\n" % sno)
-                sys.exit(1)
-            patch = self.series[sno]
-        else:
-            self.ui.warn("patch %s not in series\n" % patch)
-            sys.exit(1)
-        return patch
+                pass
+            else:
+                if sno < len(self.series):
+                    return self.series[sno]
+            if not strict:
+                # return any partial match made above
+                if res:
+                    return res
+                minus = patch.rsplit('-', 1)
+                if len(minus) > 1:
+                    res = partial_name(minus[0])
+                    if res:
+                        i = self.series.index(res)
+                        try:
+                            off = int(minus[1] or 1)
+                        except(ValueError, OverflowError):
+                            pass
+                        else:
+                            if i - off >= 0:
+                                return self.series[i - off]
+                plus = patch.rsplit('+', 1)
+                if len(plus) > 1:
+                    res = partial_name(plus[0])
+                    if res:
+                        i = self.series.index(res)
+                        try:
+                            off = int(plus[1] or 1)
+                        except(ValueError, OverflowError):
+                            pass
+                        else:
+                            if i + off < len(self.series):
+                                return self.series[i + off]
+        raise util.Abort(_("patch %s not in series") % patch)
 
     def push(self, repo, patch=None, force=False, list=False,
              mergeq=None, wlock=None):
@@ -624,10 +788,10 @@
             wlock = repo.wlock()
         patch = self.lookup(patch)
         if patch and self.isapplied(patch):
-            self.ui.warn("patch %s is already applied\n" % patch)
+            self.ui.warn(_("patch %s is already applied\n") % patch)
             sys.exit(1)
         if self.series_end() == len(self.series):
-            self.ui.warn("File series fully applied\n")
+            self.ui.warn(_("patch series fully applied\n"))
             sys.exit(1)
         if not force:
             self.check_localchanges(repo)
@@ -646,7 +810,7 @@
             ret = self.mergepatch(repo, mergeq, s, wlock)
         else:
             ret = self.apply(repo, s, list, wlock=wlock)
-        top = self.applied[-1].split(':')[1]
+        top = self.applied[-1].name
         if ret[0]:
             self.ui.write("Errors during apply, please fix and refresh %s\n" %
                           top)
@@ -654,7 +818,8 @@
             self.ui.write("Now at: %s\n" % top)
         return ret[0]
 
-    def pop(self, repo, patch=None, force=False, update=True, wlock=None):
+    def pop(self, repo, patch=None, force=False, update=True, all=False,
+            wlock=None):
         def getfile(f, rev):
             t = repo.file(f).read(rev)
             try:
@@ -675,15 +840,14 @@
                 patch = self.lookup(patch)
             info = self.isapplied(patch)
             if not info:
-                self.ui.warn("patch %s is not applied\n" % patch)
-                sys.exit(1)
+                raise util.Abort(_("patch %s is not applied") % patch)
         if len(self.applied) == 0:
-            self.ui.warn("No patches applied\n")
+            self.ui.warn(_("no patches applied\n"))
             sys.exit(1)
 
         if not update:
             parents = repo.dirstate.parents()
-            rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
+            rr = [ revlog.bin(x.rev) for x in self.applied ]
             for p in parents:
                 if p in rr:
                     self.ui.warn("qpop: forcing dirstate update\n")
@@ -695,7 +859,17 @@
         self.applied_dirty = 1;
         end = len(self.applied)
         if not patch:
-            info = [len(self.applied) - 1] + self.applied[-1].split(':')
+            if all:
+                popi = 0
+            else:
+                popi = len(self.applied) - 1
+        else:
+            popi = info[0] + 1
+            if popi >= end:
+                self.ui.warn("qpop: %s is already at the top\n" % patch)
+                return
+        info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
+
         start = info[0]
         rev = revlog.bin(info[1])
 
@@ -705,17 +879,16 @@
             top = self.check_toppatch(repo)
             qp = self.qparents(repo, rev)
             changes = repo.changelog.read(qp)
-            mf1 = repo.manifest.readflags(changes[0])
             mmap = repo.manifest.read(changes[0])
-            (c, a, r, d, u) = repo.changes(qp, top)
+            m, a, r, d, u = repo.status(qp, top)[:5]
             if d:
                 raise util.Abort("deletions found between repo revs")
-            for f in c:
+            for f in m:
                 getfile(f, mmap[f])
             for f in r:
                 getfile(f, mmap[f])
-                util.set_exec(repo.wjoin(f), mf1[f])
-            repo.dirstate.update(c + r, 'n')
+                util.set_exec(repo.wjoin(f), mmap.execf(f))
+            repo.dirstate.update(m + r, 'n')
             for f in a:
                 try: os.unlink(repo.wjoin(f))
                 except: raise
@@ -727,36 +900,46 @@
         self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
         del self.applied[start:end]
         if len(self.applied):
-            self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
+            self.ui.write("Now at: %s\n" % self.applied[-1].name)
         else:
             self.ui.write("Patch queue now empty\n")
 
-    def diff(self, repo, files):
+    def diff(self, repo, pats, opts):
         top = self.check_toppatch(repo)
         if not top:
             self.ui.write("No patches applied\n")
             return
         qp = self.qparents(repo, top)
-        commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
+        self.printdiff(repo, qp, files=pats, opts=opts)
 
-    def refresh(self, repo, short=False):
+    def refresh(self, repo, pats=None, **opts):
         if len(self.applied) == 0:
             self.ui.write("No patches applied\n")
             return
         wlock = repo.wlock()
         self.check_toppatch(repo)
-        qp = self.qparents(repo)
-        (top, patch) = self.applied[-1].split(':')
+        (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
         top = revlog.bin(top)
         cparents = repo.changelog.parents(top)
         patchparent = self.qparents(repo, top)
         message, comments, user, date, patchfound = self.readheaders(patch)
 
         patchf = self.opener(patch, "w")
+        msg = opts.get('msg', '').rstrip()
+        if msg:
+            if comments:
+                # Remove existing message.
+                ci = 0
+                for mi in range(len(message)):
+                    while message[mi] != comments[ci]:
+                        ci += 1
+                    del comments[ci]
+            comments.append(msg)
         if comments:
             comments = "\n".join(comments) + '\n\n'
             patchf.write(comments)
 
+        fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
         tip = repo.changelog.tip()
         if top == tip:
             # if the top of our patch queue is also the tip, there is an
@@ -769,30 +952,30 @@
             # patch already
             #
             # this should really read:
-            #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
+            #   mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
             # but we do it backwards to take advantage of manifest/chlog
-            # caching against the next repo.changes call
+            # caching against the next repo.status call
             #
-            (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
-            if short:
-                filelist = cc + aa + dd
+            mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
+            if opts.get('short'):
+                filelist = mm + aa + dd
             else:
                 filelist = None
-            (c, a, r, d, u) = repo.changes(None, None, filelist)
+            m, a, r, d, u = repo.status(files=filelist)[:5]
 
             # we might end up with files that were added between tip and
             # the dirstate parent, but then changed in the local dirstate.
             # in this case, we want them to only show up in the added section
-            for x in c:
+            for x in m:
                 if x not in aa:
-                    cc.append(x)
+                    mm.append(x)
             # we might end up with files added by the local dirstate that
             # were deleted by the patch.  In this case, they should only
             # show up in the changed section.
             for x in a:
                 if x in dd:
                     del dd[dd.index(x)]
-                    cc.append(x)
+                    mm.append(x)
                 else:
                     aa.append(x)
             # make sure any files deleted in the local dirstate
@@ -803,70 +986,97 @@
                     del aa[aa.index(x)]
                     forget.append(x)
                     continue
-                elif x in cc:
-                    del cc[cc.index(x)]
+                elif x in mm:
+                    del mm[mm.index(x)]
                 dd.append(x)
 
-            c = list(util.unique(cc))
+            m = list(util.unique(mm))
             r = list(util.unique(dd))
             a = list(util.unique(aa))
-            filelist = list(util.unique(c + r + a ))
-            commands.dodiff(patchf, self.ui, repo, patchparent, None,
-                            filelist, changes=(c, a, r, [], u))
+            filelist = filter(matchfn, util.unique(m + r + a))
+            self.printdiff(repo, patchparent, files=filelist,
+                           changes=(m, a, r, [], u), fp=patchf)
             patchf.close()
 
             changes = repo.changelog.read(tip)
             repo.dirstate.setparents(*cparents)
+            copies = [(f, repo.dirstate.copied(f)) for f in a]
             repo.dirstate.update(a, 'a')
+            for dst, src in copies:
+                repo.dirstate.copy(src, dst)
             repo.dirstate.update(r, 'r')
-            repo.dirstate.update(c, 'n')
+            # if the patch excludes a modified file, mark that file with mtime=0
+            # so status can see it.
+            mm = []
+            for i in range(len(m)-1, -1, -1):
+                if not matchfn(m[i]):
+                    mm.append(m[i])
+                    del m[i]
+            repo.dirstate.update(m, 'n')
+            repo.dirstate.update(mm, 'n', st_mtime=0)
             repo.dirstate.forget(forget)
 
-            if not message:
-                message = "patch queue: %s\n" % patch
+            if not msg:
+                if not message:
+                    message = "patch queue: %s\n" % patch
+                else:
+                    message = "\n".join(message)
             else:
-                message = "\n".join(message)
+                message = msg
+
             self.strip(repo, top, update=False, backup='strip', wlock=wlock)
             n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
-            self.applied[-1] = revlog.hex(n) + ':' + patch
+            self.applied[-1] = statusentry(revlog.hex(n), patch)
             self.applied_dirty = 1
         else:
-            commands.dodiff(patchf, self.ui, repo, patchparent, None)
+            self.printdiff(repo, patchparent, fp=patchf)
             patchf.close()
             self.pop(repo, force=True, wlock=wlock)
             self.push(repo, force=True, wlock=wlock)
 
     def init(self, repo, create=False):
         if os.path.isdir(self.path):
-            raise util.Abort("patch queue directory already exists")
+            raise util.Abort(_("patch queue directory already exists"))
         os.mkdir(self.path)
         if create:
             return self.qrepo(create=True)
 
     def unapplied(self, repo, patch=None):
         if patch and patch not in self.series:
-            self.ui.warn("%s not in the series file\n" % patch)
-            sys.exit(1)
+            raise util.Abort(_("patch %s is not in series file") % patch)
         if not patch:
             start = self.series_end()
         else:
             start = self.series.index(patch) + 1
-        for p in self.series[start:]:
-            self.ui.write("%s\n" % p)
+        unapplied = []
+        for i in xrange(start, len(self.series)):
+            pushable, reason = self.pushable(i)
+            if pushable:
+                unapplied.append((i, self.series[i]))
+            self.explain_pushable(i)
+        return unapplied
 
-    def qseries(self, repo, missing=None):
-        start = self.series_end()
+    def qseries(self, repo, missing=None, summary=False):
+        start = self.series_end(all_patches=True)
         if not missing:
-            for p in self.series[:start]:
+            for i in range(len(self.series)):
+                patch = self.series[i]
                 if self.ui.verbose:
-                    self.ui.write("%d A " % self.series.index(p))
-                self.ui.write("%s\n" % p)
-            for p in self.series[start:]:
-                if self.ui.verbose:
-                    self.ui.write("%d U " % self.series.index(p))
-                self.ui.write("%s\n" %  p)
+                    if i < start:
+                        status = 'A'
+                    elif self.pushable(i)[0]:
+                        status = 'U'
+                    else:
+                        status = 'G'
+                    self.ui.write('%d %s ' % (i, status))
+                if summary:
+                    msg = self.readheaders(patch)[0]
+                    msg = msg and ': ' + msg[0] or ': '
+                else:
+                    msg = ''
+                self.ui.write('%s%s\n' % (patch, msg))
         else:
-            list = []
+            msng_list = []
             for root, dirs, files in os.walk(self.path):
                 d = root[len(self.path) + 1:]
                 for f in files:
@@ -874,21 +1084,19 @@
                     if (fl not in self.series and
                         fl not in (self.status_path, self.series_path)
                         and not fl.startswith('.')):
-                        list.append(fl)
-            list.sort()
-            if list:
-                for x in list:
-                    if self.ui.verbose:
-                        self.ui.write("D ")
-                    self.ui.write("%s\n" % x)
+                        msng_list.append(fl)
+            msng_list.sort()
+            for x in msng_list:
+                if self.ui.verbose:
+                    self.ui.write("D ")
+                self.ui.write("%s\n" % x)
 
     def issaveline(self, l):
-        name = l.split(':')[1]
-        if name == '.hg.patches.save.line':
+        if l.name == '.hg.patches.save.line':
             return True
 
     def qrepo(self, create=False):
-        if create or os.path.isdir(os.path.join(self.path, ".hg")):
+        if create or os.path.isdir(self.join(".hg")):
             return hg.repository(self.ui, path=self.path, create=create)
 
     def restore(self, repo, rev, delete=None, qupdate=None):
@@ -909,19 +1117,18 @@
                 qpp = [ hg.bin(x) for x in l ]
             elif datastart != None:
                 l = lines[i].rstrip()
-                index = l.index(':')
-                id = l[:index]
-                file = l[index + 1:]
-                if id:
-                    applied.append(l)
-                series.append(file)
+                se = statusentry(l)
+                file_ = se.name
+                if se.rev:
+                    applied.append(se)
+                series.append(file_)
         if datastart == None:
             self.ui.warn("No saved patch data found\n")
             return 1
         self.ui.warn("restoring status: %s\n" % lines[0])
         self.full_series = series
         self.applied = applied
-        self.read_series(self.full_series)
+        self.parse_series()
         self.series_dirty = 1
         self.applied_dirty = 1
         heads = repo.changelog.heads()
@@ -945,7 +1152,7 @@
                 if not r:
                     self.ui.warn("Unable to load queue repository\n")
                     return 1
-                r.update(qpp[0], allow=False, force=True)
+                hg.clean(r, qpp[0])
 
     def save(self, repo, msg=None):
         if len(self.applied) == 0:
@@ -965,30 +1172,49 @@
             pp = r.dirstate.parents()
             msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
         msg += "\n\nPatch Data:\n"
-        text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
-                                                       + '\n' or "")
+        text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
+                   "\n".join(ar) + '\n' or "")
         n = repo.commit(None, text, user=None, force=1)
         if not n:
             self.ui.warn("repo commit failed\n")
             return 1
-        self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
+        self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
         self.applied_dirty = 1
 
-    def series_end(self):
+    def full_series_end(self):
+        if len(self.applied) > 0:
+            p = self.applied[-1].name
+            end = self.find_series(p)
+            if end == None:
+                return len(self.full_series)
+            return end + 1
+        return 0
+
+    def series_end(self, all_patches=False):
         end = 0
+        def next(start):
+            if all_patches:
+                return start
+            i = start
+            while i < len(self.series):
+                p, reason = self.pushable(i)
+                if p:
+                    break
+                self.explain_pushable(i)
+                i += 1
+            return i
         if len(self.applied) > 0:
-            (top, p) = self.applied[-1].split(':')
+            p = self.applied[-1].name
             try:
                 end = self.series.index(p)
             except ValueError:
                 return 0
-            return end + 1
-        return end
+            return next(end + 1)
+        return next(end)
 
     def qapplied(self, repo, patch=None):
         if patch and patch not in self.series:
-            self.ui.warn("%s not in the series file\n" % patch)
-            sys.exit(1)
+            raise util.Abort(_("patch %s is not in series file") % patch)
         if not patch:
             end = len(self.applied)
         else:
@@ -998,9 +1224,11 @@
             self.ui.write("%s\n" % p)
 
     def appliedname(self, index):
-        p = self.applied[index]
+        pname = self.applied[index].name
         if not self.ui.verbose:
-            p = p.split(':')[1]
+            p = pname
+        else:
+            p = str(self.series.index(pname)) + " " + pname
         return p
 
     def top(self, repo):
@@ -1015,7 +1243,10 @@
         if end == len(self.series):
             self.ui.write("All patches applied\n")
         else:
-            self.ui.write(self.series[end] + '\n')
+            p = self.series[end]
+            if self.ui.verbose:
+                self.ui.write("%d " % self.series.index(p))
+            self.ui.write(p + '\n')
 
     def prev(self, repo):
         if len(self.applied) > 1:
@@ -1028,36 +1259,33 @@
 
     def qimport(self, repo, files, patch=None, existing=None, force=None):
         if len(files) > 1 and patch:
-            self.ui.warn("-n option not valid when importing multiple files\n")
-            sys.exit(1)
+            raise util.Abort(_('option "-n" not valid when importing multiple '
+                               'files'))
         i = 0
         added = []
         for filename in files:
             if existing:
                 if not patch:
                     patch = filename
-                if not os.path.isfile(os.path.join(self.path, patch)):
-                    self.ui.warn("patch %s does not exist\n" % patch)
-                    sys.exit(1)
+                if not os.path.isfile(self.join(patch)):
+                    raise util.Abort(_("patch %s does not exist") % patch)
             else:
                 try:
                     text = file(filename).read()
                 except IOError:
-                    self.ui.warn("Unable to read %s\n" % patch)
-                    sys.exit(1)
+                    raise util.Abort(_("unable to read %s") % patch)
                 if not patch:
                     patch = os.path.split(filename)[1]
-                if not force and os.path.isfile(os.path.join(self.path, patch)):
-                    self.ui.warn("patch %s already exists\n" % patch)
-                    sys.exit(1)
+                if not force and os.path.exists(self.join(patch)):
+                    raise util.Abort(_('patch "%s" already exists') % patch)
                 patchf = self.opener(patch, "w")
                 patchf.write(text)
             if patch in self.series:
-                self.ui.warn("patch %s is already in the series file\n" % patch)
-                sys.exit(1)
-            index = self.series_end() + i
+                raise util.Abort(_('patch %s is already in the series file')
+                                 % patch)
+            index = self.full_series_end() + i
             self.full_series[index:index] = [patch]
-            self.read_series(self.full_series)
+            self.parse_series()
             self.ui.warn("adding %s to series file\n" % patch)
             i += 1
             added.append(patch)
@@ -1067,34 +1295,44 @@
         if qrepo:
             qrepo.add(added)
 
-def delete(ui, repo, patch, **opts):
-    """remove a patch from the series file"""
-    q = repomap[repo]
-    q.delete(repo, patch)
+def delete(ui, repo, patch, *patches, **opts):
+    """remove patches from queue
+
+    The patches must not be applied.
+    With -k, the patch files are preserved in the patch directory."""
+    q = repo.mq
+    q.delete(repo, (patch,) + patches, keep=opts.get('keep'))
     q.save_dirty()
     return 0
 
 def applied(ui, repo, patch=None, **opts):
     """print the patches already applied"""
-    repomap[repo].qapplied(repo, patch)
+    repo.mq.qapplied(repo, patch)
     return 0
 
 def unapplied(ui, repo, patch=None, **opts):
     """print the patches not yet applied"""
-    repomap[repo].unapplied(repo, patch)
-    return 0
+    for i, p in repo.mq.unapplied(repo, patch):
+        if ui.verbose:
+            ui.write("%d " % i)
+        ui.write("%s\n" % p)
 
 def qimport(ui, repo, *filename, **opts):
     """import a patch"""
-    q = repomap[repo]
+    q = repo.mq
     q.qimport(repo, filename, patch=opts['name'],
               existing=opts['existing'], force=opts['force'])
     q.save_dirty()
     return 0
 
 def init(ui, repo, **opts):
-    """init a new queue repository"""
-    q = repomap[repo]
+    """init a new queue repository
+
+    The queue repository is unversioned by default. If -c is
+    specified, qinit will create a separate nested repository
+    for patches. Use qcommit to commit changes to this queue
+    repository."""
+    q = repo.mq
     r = q.init(repo, create=opts['create_repo'])
     q.save_dirty()
     if r:
@@ -1106,68 +1344,254 @@
         r.add(['.hgignore', 'series'])
     return 0
 
+def clone(ui, source, dest=None, **opts):
+    '''clone main and patch repository at same time
+
+    If source is local, destination will have no patches applied.  If
+    source is remote, this command can not check if patches are
+    applied in source, so cannot guarantee that patches are not
+    applied in destination.  If you clone remote repository, be sure
+    before that it has no patches applied.
+
+    Source patch repository is looked for in <src>/.hg/patches by
+    default.  Use -p <url> to change.
+    '''
+    commands.setremoteconfig(ui, opts)
+    if dest is None:
+        dest = hg.defaultdest(source)
+    sr = hg.repository(ui, ui.expandpath(source))
+    qbase, destrev = None, None
+    if sr.local():
+        reposetup(ui, sr)
+        if sr.mq.applied:
+            qbase = revlog.bin(sr.mq.applied[0].rev)
+            if not hg.islocal(dest):
+                destrev = sr.parents(qbase)[0]
+    ui.note(_('cloning main repo\n'))
+    sr, dr = hg.clone(ui, sr, dest,
+                      pull=opts['pull'],
+                      rev=destrev,
+                      update=False,
+                      stream=opts['uncompressed'])
+    ui.note(_('cloning patch repo\n'))
+    spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
+                        dr.url() + '/.hg/patches',
+                        pull=opts['pull'],
+                        update=not opts['noupdate'],
+                        stream=opts['uncompressed'])
+    if dr.local():
+        if qbase:
+            ui.note(_('stripping applied patches from destination repo\n'))
+            reposetup(ui, dr)
+            dr.mq.strip(dr, qbase, update=False, backup=None)
+        if not opts['noupdate']:
+            ui.note(_('updating destination repo\n'))
+            hg.update(dr, dr.changelog.tip())
+
 def commit(ui, repo, *pats, **opts):
     """commit changes in the queue repository"""
-    q = repomap[repo]
+    q = repo.mq
     r = q.qrepo()
     if not r: raise util.Abort('no queue repository')
     commands.commit(r.ui, r, *pats, **opts)
 
 def series(ui, repo, **opts):
     """print the entire series file"""
-    repomap[repo].qseries(repo, missing=opts['missing'])
+    repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
     return 0
 
 def top(ui, repo, **opts):
     """print the name of the current patch"""
-    repomap[repo].top(repo)
+    repo.mq.top(repo)
     return 0
 
 def next(ui, repo, **opts):
     """print the name of the next patch"""
-    repomap[repo].next(repo)
+    repo.mq.next(repo)
     return 0
 
 def prev(ui, repo, **opts):
     """print the name of the previous patch"""
-    repomap[repo].prev(repo)
+    repo.mq.prev(repo)
     return 0
 
 def new(ui, repo, patch, **opts):
-    """create a new patch"""
-    q = repomap[repo]
-    q.new(repo, patch, msg=opts['message'], force=opts['force'])
+    """create a new patch
+
+    qnew creates a new patch on top of the currently-applied patch
+    (if any). It will refuse to run if there are any outstanding
+    changes unless -f is specified, in which case the patch will
+    be initialised with them.
+
+    -e, -m or -l set the patch header as well as the commit message.
+    If none is specified, the patch header is empty and the
+    commit message is 'New patch: PATCH'"""
+    q = repo.mq
+    message = commands.logmessage(opts)
+    if opts['edit']:
+        message = ui.edit(message, ui.username())
+    q.new(repo, patch, msg=message, force=opts['force'])
+    q.save_dirty()
+    return 0
+
+def refresh(ui, repo, *pats, **opts):
+    """update the current patch
+
+    If any file patterns are provided, the refreshed patch will contain only
+    the modifications that match those patterns; the remaining modifications
+    will remain in the working directory.
+    """
+    q = repo.mq
+    message = commands.logmessage(opts)
+    if opts['edit']:
+        if message:
+            raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
+        patch = q.applied[-1].name
+        (message, comment, user, date, hasdiff) = q.readheaders(patch)
+        message = ui.edit('\n'.join(message), user or ui.username())
+    q.refresh(repo, pats, msg=message, **opts)
     q.save_dirty()
     return 0
 
-def refresh(ui, repo, **opts):
-    """update the current patch"""
-    q = repomap[repo]
-    q.refresh(repo, short=opts['short'])
-    q.save_dirty()
+def diff(ui, repo, *pats, **opts):
+    """diff of the current patch"""
+    repo.mq.diff(repo, pats, opts)
     return 0
 
-def diff(ui, repo, *files, **opts):
-    """diff of the current patch"""
-    # deep in the dirstate code, the walkhelper method wants a list, not a tuple
-    repomap[repo].diff(repo, list(files))
-    return 0
+def fold(ui, repo, *files, **opts):
+    """fold the named patches into the current patch
+
+    Patches must not yet be applied. Each patch will be successively
+    applied to the current patch in the order given. If all the
+    patches apply successfully, the current patch will be refreshed
+    with the new cumulative patch, and the folded patches will
+    be deleted. With -k/--keep, the folded patch files will not
+    be removed afterwards.
+
+    The header for each folded patch will be concatenated with
+    the current patch header, separated by a line of '* * *'."""
+
+    q = repo.mq
+
+    if not files:
+        raise util.Abort(_('qfold requires at least one patch name'))
+    if not q.check_toppatch(repo):
+        raise util.Abort(_('No patches applied\n'))
+
+    message = commands.logmessage(opts)
+    if opts['edit']:
+        if message:
+            raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
+
+    parent = q.lookup('qtip')
+    patches = []
+    messages = []
+    for f in files:
+        p = q.lookup(f)
+        if p in patches or p == parent:
+            ui.warn(_('Skipping already folded patch %s') % p)
+        if q.isapplied(p):
+            raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
+        patches.append(p)
+
+    for p in patches:
+        if not message:
+            messages.append(q.readheaders(p)[0])
+        pf = q.join(p)
+        (patchsuccess, files, fuzz) = q.patch(repo, pf)
+        if not patchsuccess:
+            raise util.Abort(_('Error folding patch %s') % p)
+        patch.updatedir(ui, repo, files)
+
+    if not message:
+        message, comments, user = q.readheaders(parent)[0:3]
+        for msg in messages:
+            message.append('* * *')
+            message.extend(msg)
+        message = '\n'.join(message)
+
+    if opts['edit']:
+        message = ui.edit(message, user or ui.username())
+
+    q.refresh(repo, msg=message)
+    q.delete(repo, patches, keep=opts['keep'])
+    q.save_dirty()
+
+def guard(ui, repo, *args, **opts):
+    '''set or print guards for a patch
+
+    Guards control whether a patch can be pushed. A patch with no
+    guards is always pushed. A patch with a positive guard ("+foo") is
+    pushed only if the qselect command has activated it. A patch with
+    a negative guard ("-foo") is never pushed if the qselect command
+    has activated it.
+
+    With no arguments, print the currently active guards.
+    With arguments, set guards for the named patch.
+
+    To set a negative guard "-foo" on topmost patch ("--" is needed so
+    hg will not interpret "-foo" as an option):
+      hg qguard -- -foo
+
+    To set guards on another patch:
+      hg qguard other.patch +2.6.17 -stable    
+    '''
+    def status(idx):
+        guards = q.series_guards[idx] or ['unguarded']
+        ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
+    q = repo.mq
+    patch = None
+    args = list(args)
+    if opts['list']:
+        if args or opts['none']:
+            raise util.Abort(_('cannot mix -l/--list with options or arguments'))
+        for i in xrange(len(q.series)):
+            status(i)
+        return
+    if not args or args[0][0:1] in '-+':
+        if not q.applied:
+            raise util.Abort(_('no patches applied'))
+        patch = q.applied[-1].name
+    if patch is None and args[0][0:1] not in '-+':
+        patch = args.pop(0)
+    if patch is None:
+        raise util.Abort(_('no patch to work with'))
+    if args or opts['none']:
+        q.set_guards(q.find_series(patch), args)
+        q.save_dirty()
+    else:
+        status(q.series.index(q.lookup(patch)))
+
+def header(ui, repo, patch=None):
+    """Print the header of the topmost or specified patch"""
+    q = repo.mq
+
+    if patch:
+        patch = q.lookup(patch)
+    else:
+        if not q.applied:
+            ui.write('No patches applied\n')
+            return
+        patch = q.lookup('qtip')
+    message = repo.mq.readheaders(patch)[0]
+
+    ui.write('\n'.join(message) + '\n')
 
 def lastsavename(path):
-    (dir, base) = os.path.split(path)
-    names = os.listdir(dir)
+    (directory, base) = os.path.split(path)
+    names = os.listdir(directory)
     namere = re.compile("%s.([0-9]+)" % base)
-    max = None
+    maxindex = None
     maxname = None
     for f in names:
         m = namere.match(f)
         if m:
             index = int(m.group(1))
-            if max == None or index > max:
-                max = index
+            if maxindex == None or index > maxindex:
+                maxindex = index
                 maxname = f
     if maxname:
-        return (os.path.join(dir, maxname), max)
+        return (os.path.join(directory, maxname), maxindex)
     return (None, None)
 
 def savename(path):
@@ -1179,7 +1603,7 @@
 
 def push(ui, repo, patch=None, **opts):
     """push the next patch onto the stack"""
-    q = repomap[repo]
+    q = repo.mq
     mergeq = None
 
     if opts['all']:
@@ -1207,17 +1631,65 @@
         ui.warn('using patch queue: %s\n' % q.path)
         localupdate = False
     else:
-        q = repomap[repo]
-    if opts['all'] and len(q.applied) > 0:
-        patch = q.applied[0].split(':')[1]
-    q.pop(repo, patch, force=opts['force'], update=localupdate)
+        q = repo.mq
+    q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
     q.save_dirty()
     return 0
 
+def rename(ui, repo, patch, name=None, **opts):
+    """rename a patch
+
+    With one argument, renames the current patch to PATCH1.
+    With two arguments, renames PATCH1 to PATCH2."""
+
+    q = repo.mq
+
+    if not name:
+        name = patch
+        patch = None
+
+    if name in q.series:
+        raise util.Abort(_('A patch named %s already exists in the series file') % name)
+
+    absdest = q.join(name)
+    if os.path.exists(absdest):
+        raise util.Abort(_('%s already exists') % absdest)
+    
+    if patch:
+        patch = q.lookup(patch)
+    else:
+        if not q.applied:
+            ui.write(_('No patches applied\n'))
+            return
+        patch = q.lookup('qtip')
+
+    if ui.verbose:
+        ui.write('Renaming %s to %s\n' % (patch, name))
+    i = q.find_series(patch)
+    q.full_series[i] = name
+    q.parse_series()
+    q.series_dirty = 1
+
+    info = q.isapplied(patch)
+    if info:
+        q.applied[info[0]] = statusentry(info[1], name)
+    q.applied_dirty = 1
+
+    util.rename(q.join(patch), absdest)
+    r = q.qrepo()
+    if r:
+        wlock = r.wlock()
+        if r.dirstate.state(name) == 'r':
+            r.undelete([name], wlock)
+        r.copy(patch, name, wlock)
+        r.remove([patch], False, wlock)
+
+    q.save_dirty()
+
 def restore(ui, repo, rev, **opts):
     """restore the queue state saved by a rev"""
     rev = repo.lookup(rev)
-    q = repomap[repo]
+    q = repo.mq
     q.restore(repo, rev, delete=opts['delete'],
               qupdate=opts['update'])
     q.save_dirty()
@@ -1225,8 +1697,9 @@
 
 def save(ui, repo, **opts):
     """save current queue state"""
-    q = repomap[repo]
-    ret = q.save(repo, msg=opts['message'])
+    q = repo.mq
+    message = commands.logmessage(opts)
+    ret = q.save(repo, msg=message)
     if ret:
         return ret
     q.save_dirty()
@@ -1236,20 +1709,18 @@
             newpath = os.path.join(q.basepath, opts['name'])
             if os.path.exists(newpath):
                 if not os.path.isdir(newpath):
-                    ui.warn("destination %s exists and is not a directory\n" %
-                            newpath)
-                    sys.exit(1)
+                    raise util.Abort(_('destination %s exists and is not '
+                                       'a directory') % newpath)
                 if not opts['force']:
-                    ui.warn("destination %s exists, use -f to force\n" %
-                            newpath)
-                    sys.exit(1)
+                    raise util.Abort(_('destination %s exists, '
+                                       'use -f to force') % newpath)
         else:
             newpath = savename(path)
         ui.warn("copy %s to %s\n" % (path, newpath))
         util.copyfiles(path, newpath)
     if opts['empty']:
         try:
-            os.unlink(os.path.join(q.path, q.status_path))
+            os.unlink(q.join(q.status_path))
         except:
             pass
     return 0
@@ -1262,25 +1733,196 @@
         backup = 'strip'
     elif opts['nobackup']:
         backup = 'none'
-    repomap[repo].strip(repo, rev, backup=backup)
+    repo.mq.strip(repo, rev, backup=backup)
     return 0
 
-def version(ui, q=None):
-    """print the version number"""
-    ui.write("mq version %s\n" % versionstr)
-    return 0
+def select(ui, repo, *args, **opts):
+    '''set or print guarded patches to push
+
+    Use the qguard command to set or print guards on patch, then use
+    qselect to tell mq which guards to use. A patch will be pushed if it
+    has no guards or any positive guards match the currently selected guard,
+    but will not be pushed if any negative guards match the current guard.
+    For example:
+
+        qguard foo.patch -stable    (negative guard)
+        qguard bar.patch +stable    (positive guard)
+        qselect stable
+
+    This activates the "stable" guard. mq will skip foo.patch (because
+    it has a negative match) but push bar.patch (because it
+    has a positive match).
+
+    With no arguments, prints the currently active guards.
+    With one argument, sets the active guard.
+    
+    Use -n/--none to deactivate guards (no other arguments needed).
+    When no guards are active, patches with positive guards are skipped
+    and patches with negative guards are pushed.
+
+    qselect can change the guards on applied patches. It does not pop
+    guarded patches by default. Use --pop to pop back to the last applied
+    patch that is not guarded. Use --reapply (which implies --pop) to push
+    back to the current patch afterwards, but skip guarded patches.
+
+    Use -s/--series to print a list of all guards in the series file (no
+    other arguments needed). Use -v for more information.'''
+
+    q = repo.mq
+    guards = q.active()
+    if args or opts['none']:
+        old_unapplied = q.unapplied(repo)
+        old_guarded = [i for i in xrange(len(q.applied)) if
+                       not q.pushable(i)[0]]
+        q.set_active(args)
+        q.save_dirty()
+        if not args:
+            ui.status(_('guards deactivated\n'))
+        if not opts['pop'] and not opts['reapply']:
+            unapplied = q.unapplied(repo)
+            guarded = [i for i in xrange(len(q.applied))
+                       if not q.pushable(i)[0]]
+            if len(unapplied) != len(old_unapplied):
+                ui.status(_('number of unguarded, unapplied patches has '
+                            'changed from %d to %d\n') %
+                          (len(old_unapplied), len(unapplied)))
+            if len(guarded) != len(old_guarded):
+                ui.status(_('number of guarded, applied patches has changed '
+                            'from %d to %d\n') %
+                          (len(old_guarded), len(guarded)))
+    elif opts['series']:
+        guards = {}
+        noguards = 0
+        for gs in q.series_guards:
+            if not gs:
+                noguards += 1
+            for g in gs:
+                guards.setdefault(g, 0)
+                guards[g] += 1
+        if ui.verbose:
+            guards['NONE'] = noguards
+        guards = guards.items()
+        guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
+        if guards:
+            ui.note(_('guards in series file:\n'))
+            for guard, count in guards:
+                ui.note('%2d  ' % count)
+                ui.write(guard, '\n')
+        else:
+            ui.note(_('no guards in series file\n'))
+    else:
+        if guards:
+            ui.note(_('active guards:\n'))
+            for g in guards:
+                ui.write(g, '\n')
+        else:
+            ui.write(_('no active guards\n'))
+    reapply = opts['reapply'] and q.applied and q.appliedname(-1)
+    popped = False
+    if opts['pop'] or opts['reapply']:
+        for i in xrange(len(q.applied)):
+            pushable, reason = q.pushable(i)
+            if not pushable:
+                ui.status(_('popping guarded patches\n'))
+                popped = True
+                if i == 0:
+                    q.pop(repo, all=True)
+                else:
+                    q.pop(repo, i-1)
+                break
+    if popped:
+        try:
+            if reapply:
+                ui.status(_('reapplying unguarded patches\n'))
+                q.push(repo, reapply)
+        finally:
+            q.save_dirty()
 
 def reposetup(ui, repo):
-    repomap[repo] = queue(ui, repo.join(""))
+    class mqrepo(repo.__class__):
+        def abort_if_wdir_patched(self, errmsg, force=False):
+            if self.mq.applied and not force:
+                parent = revlog.hex(self.dirstate.parents()[0])
+                if parent in [s.rev for s in self.mq.applied]:
+                    raise util.Abort(errmsg)
+            
+        def commit(self, *args, **opts):
+            if len(args) >= 6:
+                force = args[5]
+            else:
+                force = opts.get('force')
+            self.abort_if_wdir_patched(
+                _('cannot commit over an applied mq patch'),
+                force)
+
+            return super(mqrepo, self).commit(*args, **opts)
+
+        def push(self, remote, force=False, revs=None):
+            if self.mq.applied and not force:
+                raise util.Abort(_('source has mq patches applied'))
+            return super(mqrepo, self).push(remote, force, revs)
+            
+        def tags(self):
+            if self.tagscache:
+                return self.tagscache
+
+            tagscache = super(mqrepo, self).tags()
+
+            q = self.mq
+            if not q.applied:
+                return tagscache
+
+            mqtags = [(patch.rev, patch.name) for patch in q.applied]
+            mqtags.append((mqtags[-1][0], 'qtip'))
+            mqtags.append((mqtags[0][0], 'qbase'))
+            for patch in mqtags:
+                if patch[1] in tagscache:
+                    self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
+                else:
+                    tagscache[patch[1]] = revlog.bin(patch[0])
+
+            return tagscache
+
+    if repo.local():
+        repo.__class__ = mqrepo
+        repo.mq = queue(ui, repo.join(""))
 
 cmdtable = {
     "qapplied": (applied, [], 'hg qapplied [PATCH]'),
+    "qclone": (clone,
+               [('', 'pull', None, _('use pull protocol to copy metadata')),
+                ('U', 'noupdate', None, _('do not update the new working directories')),
+                ('', 'uncompressed', None,
+                 _('use uncompressed transfer (fast over LAN)')),
+                ('e', 'ssh', '', _('specify ssh command to use')),
+                ('p', 'patches', '', _('location of source patch repo')),
+                ('', 'remotecmd', '',
+                 _('specify hg command to run on the remote side'))],
+               'hg qclone [OPTION]... SOURCE [DEST]'),
     "qcommit|qci":
         (commit,
          commands.table["^commit|ci"][1],
          'hg qcommit [OPTION]... [FILE]...'),
-    "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
-    "qdelete": (delete, [], 'hg qdelete PATCH'),
+    "^qdiff": (diff,
+               [('I', 'include', [], _('include names matching the given patterns')),
+                ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+               'hg qdiff [-I] [-X] [FILE]...'),
+    "qdelete|qremove|qrm":
+        (delete,
+         [('k', 'keep', None, _('keep patch file'))],
+          'hg qdelete [-k] PATCH'),
+    'qfold':
+        (fold,
+         [('e', 'edit', None, _('edit patch header')),
+          ('k', 'keep', None, _('keep folded patch files')),
+          ('m', 'message', '', _('set patch header to <text>')),
+          ('l', 'logfile', '', _('set patch header to contents of <file>'))],
+         'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
+    'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
+                       ('n', 'none', None, _('drop all guards'))],
+               'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
+    'qheader': (header, [],
+                _('hg qheader [PATCH]')),
     "^qimport":
         (qimport,
          [('e', 'existing', None, 'import file in patch dir'),
@@ -1293,9 +1935,11 @@
          'hg qinit [-c]'),
     "qnew":
         (new,
-         [('m', 'message', '', 'commit message'),
-          ('f', 'force', None, 'force')],
-         'hg qnew [-m TEXT] [-f] PATCH'),
+         [('e', 'edit', None, _('edit commit message')),
+          ('m', 'message', '', _('use <text> as commit message')),
+          ('l', 'logfile', '', _('read the commit message from <file>')),
+          ('f', 'force', None, _('import uncommitted changes into patch'))],
+         'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
     "qnext": (next, [], 'hg qnext'),
     "qprev": (prev, [], 'hg qprev'),
     "^qpop":
@@ -1314,8 +1958,15 @@
          'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
     "^qrefresh":
         (refresh,
-         [('s', 'short', None, 'short refresh')],
-         'hg qrefresh [-s]'),
+         [('e', 'edit', None, _('edit commit message')),
+          ('m', 'message', '', _('change commit message with <text>')),
+          ('l', 'logfile', '', _('change commit message with <file> content')),
+          ('s', 'short', None, 'short refresh'),
+          ('I', 'include', [], _('include names matching the given patterns')),
+          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
+    'qrename|qmv':
+        (rename, [], 'hg qrename PATCH1 [PATCH2]'),
     "qrestore":
         (restore,
          [('d', 'delete', None, 'delete save entry'),
@@ -1323,15 +1974,24 @@
          'hg qrestore [-d] [-u] REV'),
     "qsave":
         (save,
-         [('m', 'message', '', 'commit message'),
+         [('m', 'message', '', _('use <text> as commit message')),
+          ('l', 'logfile', '', _('read the commit message from <file>')),
           ('c', 'copy', None, 'copy patch directory'),
           ('n', 'name', '', 'copy directory name'),
           ('e', 'empty', None, 'clear queue status file'),
           ('f', 'force', None, 'force copy')],
-         'hg qsave [-m TEXT] [-c] [-n NAME] [-e] [-f]'),
+         'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
+    "qselect": (select,
+                [('n', 'none', None, _('disable all guards')),
+                 ('s', 'series', None, _('list all guards in series file')),
+                 ('', 'pop', None,
+                  _('pop to before first guarded applied patch')),
+                 ('', 'reapply', None, _('pop, then reapply patches'))],
+                'hg qselect [OPTION...] [GUARD...]'),
     "qseries":
         (series,
-         [('m', 'missing', None, 'print patches not in series')],
+         [('m', 'missing', None, 'print patches not in series'),
+          ('s', 'summary', None, _('print first line of patch header'))],
          'hg qseries [-m]'),
     "^strip":
         (strip,
@@ -1341,6 +2001,4 @@
          'hg strip [-f] [-b] [-n] REV'),
     "qtop": (top, [], 'hg qtop'),
     "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
-    "qversion": (version, [], 'hg qversion')
 }
-
--- a/hgext/notify.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/hgext/notify.py	Fri Aug 18 21:17:28 2006 -0700
@@ -67,8 +67,8 @@
 from mercurial.demandload import *
 from mercurial.i18n import gettext as _
 from mercurial.node import *
-demandload(globals(), 'email.Parser mercurial:commands,templater,util')
-demandload(globals(), 'fnmatch socket time')
+demandload(globals(), 'mercurial:commands,patch,templater,util,mail')
+demandload(globals(), 'email.Parser fnmatch socket time')
 
 # template for single changeset can include email headers.
 single_template = '''
@@ -229,8 +229,8 @@
         else:
             self.ui.status(_('notify: sending %d subscribers %d changes\n') %
                              (len(self.subs), count))
-            mail = self.ui.sendmail()
-            mail.sendmail(templater.email(msg['From']), self.subs, msgtext)
+            mail.sendmail(self.ui, templater.email(msg['From']),
+                          self.subs, msgtext)
 
     def diff(self, node, ref):
         maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
@@ -238,7 +238,7 @@
             return
         fp = templater.stringio()
         prev = self.repo.changelog.parents(node)[0]
-        commands.dodiff(fp, self.ui, self.repo, prev, ref)
+        patch.diff(self.repo, fp, prev, ref)
         difflines = fp.getvalue().splitlines(1)
         if maxdiff > 0 and len(difflines) > maxdiff:
             self.sio.write(_('\ndiffs (truncated from %d to %d lines):\n\n') %
@@ -255,7 +255,7 @@
     changegroup. else send one email per changeset.'''
     n = notifier(ui, repo, hooktype)
     if not n.subs:
-        ui.debug(_('notify: no subscribers to this repo\n'))
+        ui.debug(_('notify: no subscribers to repo %s\n' % n.root))
         return
     if n.skipsource(source):
         ui.debug(_('notify: changes have source "%s" - skipping\n') %
--- a/hgext/patchbomb.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/hgext/patchbomb.py	Fri Aug 18 21:17:28 2006 -0700
@@ -23,27 +23,52 @@
 # the changeset summary, so you can be sure you are sending the right
 # changes.
 #
-# It is best to run this script with the "-n" (test only) flag before
-# firing it up "for real", in which case it will use your pager to
-# display each of the messages that it would send.
+# To enable this extension:
 #
-# The "-m" (mbox) option will create an mbox file instead of sending
-# the messages directly. This can be reviewed e.g. with "mutt -R -f mbox",
-# and finally sent with "formail -s sendmail -bm -t < mbox".
+#   [extensions]
+#   hgext.patchbomb =
 #
 # To configure other defaults, add a section like this to your hgrc
 # file:
 #
-# [email]
-# from = My Name <my@email>
-# to = recipient1, recipient2, ...
-# cc = cc1, cc2, ...
+#   [email]
+#   from = My Name <my@email>
+#   to = recipient1, recipient2, ...
+#   cc = cc1, cc2, ...
+#   bcc = bcc1, bcc2, ...
+#
+# Then you can use the "hg email" command to mail a series of changesets
+# as a patchbomb.
+#
+# To avoid sending patches prematurely, it is a good idea to first run
+# the "email" command with the "-n" option (test only).  You will be
+# prompted for an email recipient address, a subject an an introductory
+# message describing the patches of your patchbomb.  Then when all is
+# done, your pager will be fired up once for each patchbomb message, so
+# you can verify everything is alright.
+#
+# The "-m" (mbox) option is also very useful.  Instead of previewing
+# each patchbomb message in a pager or sending the messages directly,
+# it will create a UNIX mailbox file with the patch emails.  This
+# mailbox file can be previewed with any mail user agent which supports
+# UNIX mbox files, i.e. with mutt:
+#
+#   % mutt -R -f mbox
+#
+# When you are previewing the patchbomb messages, you can use `formail'
+# (a utility that is commonly installed as part of the procmail package),
+# to send each message out:
+#
+#  % formail -s sendmail -bm -t < mbox
+#
+# That should be all.  Now your patchbomb is on its way out.
 
 from mercurial.demandload import *
 demandload(globals(), '''email.MIMEMultipart email.MIMEText email.Utils
-                         mercurial:commands,hg,ui
+                         mercurial:commands,hg,mail,ui
                          os errno popen2 socket sys tempfile time''')
 from mercurial.i18n import gettext as _
+from mercurial.node import *
 
 try:
     # readline gives raw_input editing capabilities, but is not
@@ -129,8 +154,26 @@
             while patch and not patch[0].strip(): patch.pop(0)
         if opts['diffstat']:
             body += cdiffstat('\n'.join(desc), patch) + '\n\n'
-        body += '\n'.join(patch)
-        msg = email.MIMEText.MIMEText(body)
+        if opts['attach']:
+            msg = email.MIMEMultipart.MIMEMultipart()
+            if body: msg.attach(email.MIMEText.MIMEText(body, 'plain'))
+            p = email.MIMEText.MIMEText('\n'.join(patch), 'x-patch')
+            binnode = bin(node)
+            # if node is mq patch, it will have patch file name as tag
+            patchname = [t for t in repo.nodetags(binnode)
+                         if t.endswith('.patch') or t.endswith('.diff')]
+            if patchname:
+                patchname = patchname[0]
+            elif total > 1:
+                patchname = commands.make_filename(repo, '%b-%n.patch',
+                                                   binnode, idx, total)
+            else:
+                patchname = commands.make_filename(repo, '%b.patch', binnode)
+            p['Content-Disposition'] = 'inline; filename=' + patchname
+            msg.attach(p)
+        else:
+            body += '\n'.join(patch)
+            msg = email.MIMEText.MIMEText(body)
         if total == 1:
             subj = '[PATCH] ' + desc[0].strip()
         else:
@@ -185,11 +228,14 @@
     to = getaddrs('to', 'To')
     cc = getaddrs('cc', 'Cc', '')
 
+    bcc = opts['bcc'] or (ui.config('email', 'bcc') or
+                          ui.config('patchbomb', 'bcc') or '').split(',')
+    bcc = [a.strip() for a in bcc if a.strip()]
+
     if len(patches) > 1:
         ui.write(_('\nWrite the introductory message for the patch series.\n\n'))
 
-        msg = email.MIMEMultipart.MIMEMultipart()
-        msg['Subject'] = '[PATCH 0 of %d] %s' % (
+        subj = '[PATCH 0 of %d] %s' % (
             len(patches),
             opts['subject'] or
             prompt('Subject:', rest = ' [PATCH 0 of %d] ' % len(patches)))
@@ -204,18 +250,21 @@
             if l == '.': break
             body.append(l)
 
-        msg.attach(email.MIMEText.MIMEText('\n'.join(body) + '\n'))
-
         if opts['diffstat']:
             d = cdiffstat(_('Final summary:\n'), jumbo)
-            if d: msg.attach(email.MIMEText.MIMEText(d))
+            if d: body.append('\n' + d)
+
+        body = '\n'.join(body) + '\n'
+
+        msg = email.MIMEText.MIMEText(body)
+        msg['Subject'] = subj
 
         msgs.insert(0, msg)
 
     ui.write('\n')
 
     if not opts['test'] and not opts['mbox']:
-        mail = ui.sendmail()
+        mailer = mail.connect(ui)
     parent = None
 
     # Calculate UTC offset
@@ -240,7 +289,8 @@
         start_time += 1
         m['From'] = sender
         m['To'] = ', '.join(to)
-        if cc: m['Cc'] = ', '.join(cc)
+        if cc: m['Cc']  = ', '.join(cc)
+        if bcc: m['Bcc'] = ', '.join(bcc)
         if opts['test']:
             ui.status('Displaying ', m['Subject'], ' ...\n')
             fp = os.popen(os.getenv('PAGER', 'more'), 'w')
@@ -261,12 +311,16 @@
             fp.close()
         else:
             ui.status('Sending ', m['Subject'], ' ...\n')
-            mail.sendmail(sender, to + cc, m.as_string(0))
+            # Exim does not remove the Bcc field
+            del m['Bcc']
+            mailer.sendmail(sender, to + bcc + cc, m.as_string(0))
 
 cmdtable = {
     'email':
     (patchbomb,
-     [('c', 'cc', [], 'email addresses of copy recipients'),
+     [('a', 'attach', None, 'send patches as inline attachments'),
+      ('', 'bcc', [], 'email addresses of blind copy recipients'),
+      ('c', 'cc', [], 'email addresses of copy recipients'),
       ('d', 'diffstat', None, 'add diffstat output to messages'),
       ('f', 'from', '', 'email address of sender'),
       ('', 'plain', None, 'omit hg patch header'),
--- a/mercurial/archival.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/archival.py	Fri Aug 18 21:17:28 2006 -0700
@@ -163,12 +163,12 @@
     change = repo.changelog.read(node)
     mn = change[0]
     archiver = archivers[kind](dest, prefix, mtime or change[2][0])
-    mf = repo.manifest.read(mn).items()
-    mff = repo.manifest.readflags(mn)
-    mf.sort()
+    m = repo.manifest.read(mn)
+    items = m.items()
+    items.sort()
     write('.hg_archival.txt', 0644,
           'repo: %s\nnode: %s\n' % (hex(repo.changelog.node(0)), hex(node)))
-    for filename, filenode in mf:
-        write(filename, mff[filename] and 0755 or 0644,
+    for filename, filenode in items:
+        write(filename, m.execf(filename) and 0755 or 0644,
               repo.file(filename).read(filenode))
     archiver.done()
--- a/mercurial/bdiff.c	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/bdiff.c	Fri Aug 18 21:17:28 2006 -0700
@@ -1,7 +1,7 @@
 /*
  bdiff.c - efficient binary diff extension for Mercurial
 
- Copyright 2005 Matt Mackall <mpm@selenic.com>
+ Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 
  This software may be used and distributed according to the terms of
  the GNU General Public License, incorporated herein by reference.
--- a/mercurial/bundlerepo.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/bundlerepo.py	Fri Aug 18 21:17:28 2006 -0700
@@ -159,6 +159,10 @@
 class bundlerepository(localrepo.localrepository):
     def __init__(self, ui, path, bundlename):
         localrepo.localrepository.__init__(self, ui, path)
+
+        self._url = 'bundle:' + bundlename
+        if path: self._url += '+' + path
+
         self.tempfile = None
         self.bundlefile = open(bundlename, "rb")
         header = self.bundlefile.read(6)
@@ -208,6 +212,9 @@
             for c in changegroup.chunkiter(self.bundlefile):
                 pass
 
+    def url(self):
+        return self._url
+
     def dev(self):
         return -1
 
@@ -230,3 +237,18 @@
             self.bundlefile.close()
         if self.tempfile is not None:
             os.unlink(self.tempfile)
+
+def instance(ui, path, create):
+    if create:
+        raise util.Abort(_('cannot create new bundle repository'))
+    path = util.drop_scheme('file', path)
+    if path.startswith('bundle:'):
+        path = util.drop_scheme('bundle', path)
+        s = path.split("+", 1)
+        if len(s) == 1:
+            repopath, bundlename = "", s[0]
+        else:
+            repopath, bundlename = s
+    else:
+        repopath, bundlename = '', path
+    return bundlerepository(ui, repopath, bundlename)
--- a/mercurial/changelog.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/changelog.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 # changelog.py - changelog class for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/cmdutil.py	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,111 @@
+# commands.py - command processing for mercurial
+#
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+from demandload import demandload
+from node import *
+from i18n import gettext as _
+demandload(globals(), 'util')
+demandload(globals(), 'os sys')
+
+def make_filename(repo, pat, node,
+                  total=None, seqno=None, revwidth=None, pathname=None):
+    node_expander = {
+        'H': lambda: hex(node),
+        'R': lambda: str(repo.changelog.rev(node)),
+        'h': lambda: short(node),
+        }
+    expander = {
+        '%': lambda: '%',
+        'b': lambda: os.path.basename(repo.root),
+        }
+
+    try:
+        if node:
+            expander.update(node_expander)
+        if node and revwidth is not None:
+            expander['r'] = (lambda:
+                    str(repo.changelog.rev(node)).zfill(revwidth))
+        if total is not None:
+            expander['N'] = lambda: str(total)
+        if seqno is not None:
+            expander['n'] = lambda: str(seqno)
+        if total is not None and seqno is not None:
+            expander['n'] = lambda:str(seqno).zfill(len(str(total)))
+        if pathname is not None:
+            expander['s'] = lambda: os.path.basename(pathname)
+            expander['d'] = lambda: os.path.dirname(pathname) or '.'
+            expander['p'] = lambda: pathname
+
+        newname = []
+        patlen = len(pat)
+        i = 0
+        while i < patlen:
+            c = pat[i]
+            if c == '%':
+                i += 1
+                c = pat[i]
+                c = expander[c]()
+            newname.append(c)
+            i += 1
+        return ''.join(newname)
+    except KeyError, inst:
+        raise util.Abort(_("invalid format spec '%%%s' in output file name"),
+                    inst.args[0])
+
+def make_file(repo, pat, node=None,
+              total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
+    if not pat or pat == '-':
+        return 'w' in mode and sys.stdout or sys.stdin
+    if hasattr(pat, 'write') and 'w' in mode:
+        return pat
+    if hasattr(pat, 'read') and 'r' in mode:
+        return pat
+    return open(make_filename(repo, pat, node, total, seqno, revwidth,
+                              pathname),
+                mode)
+
+def matchpats(repo, pats=[], opts={}, head=''):
+    cwd = repo.getcwd()
+    if not pats and cwd:
+        opts['include'] = [os.path.join(cwd, i)
+                           for i in opts.get('include', [])]
+        opts['exclude'] = [os.path.join(cwd, x)
+                           for x in opts.get('exclude', [])]
+        cwd = ''
+    return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
+                           opts.get('exclude'), head)
+
+def makewalk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
+    files, matchfn, anypats = matchpats(repo, pats, opts, head)
+    exact = dict(zip(files, files))
+    def walk():
+        for src, fn in repo.walk(node=node, files=files, match=matchfn,
+                                 badmatch=badmatch):
+            yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
+    return files, matchfn, walk()
+
+def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
+    files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
+    for r in results:
+        yield r
+
+def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None):
+    if dry_run is None:
+        dry_run = opts.get('dry_run')
+    add, remove = [], []
+    for src, abs, rel, exact in walk(repo, pats, opts):
+        if src == 'f' and repo.dirstate.state(abs) == '?':
+            add.append(abs)
+            if repo.ui.verbose or not exact:
+                repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
+        if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
+            remove.append(abs)
+            if repo.ui.verbose or not exact:
+                repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
+    if not dry_run:
+        repo.add(add, wlock=wlock)
+        repo.remove(remove, wlock=wlock)
--- a/mercurial/commands.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/commands.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 # commands.py - command processing for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -10,10 +10,10 @@
 from i18n import gettext as _
 demandload(globals(), "os re sys signal shutil imp urllib pdb")
 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
-demandload(globals(), "fnmatch mdiff random signal tempfile time")
+demandload(globals(), "fnmatch difflib patch random signal tempfile time")
 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
-demandload(globals(), "archival cStringIO changegroup email.Parser")
-demandload(globals(), "hgweb.server sshserver")
+demandload(globals(), "archival cStringIO changegroup")
+demandload(globals(), "cmdutil hgweb.server sshserver")
 
 class UnknownCommand(Exception):
     """Exception raised if command is not in the command table."""
@@ -21,47 +21,34 @@
     """Exception raised if command shortcut matches more than one command."""
 
 def bail_if_changed(repo):
-    modified, added, removed, deleted, unknown = repo.changes()
+    modified, added, removed, deleted = repo.status()[:4]
     if modified or added or removed or deleted:
         raise util.Abort(_("outstanding uncommitted changes"))
 
-def filterfiles(filters, files):
-    l = [x for x in files if x in filters]
-
-    for t in filters:
-        if t and t[-1] != "/":
-            t += "/"
-        l += [x for x in files if x.startswith(t)]
-    return l
-
 def relpath(repo, args):
     cwd = repo.getcwd()
     if cwd:
         return [util.normpath(os.path.join(cwd, x)) for x in args]
     return args
 
-def matchpats(repo, pats=[], opts={}, head=''):
-    cwd = repo.getcwd()
-    if not pats and cwd:
-        opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
-        opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
-        cwd = ''
-    return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
-                           opts.get('exclude'), head)
-
-def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
-    files, matchfn, anypats = matchpats(repo, pats, opts, head)
-    exact = dict(zip(files, files))
-    def walk():
-        for src, fn in repo.walk(node=node, files=files, match=matchfn,
-                                 badmatch=badmatch):
-            yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
-    return files, matchfn, walk()
-
-def walk(repo, pats, opts, node=None, head='', badmatch=None):
-    files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
-    for r in results:
-        yield r
+def logmessage(opts):
+    """ get the log message according to -m and -l option """
+    message = opts['message']
+    logfile = opts['logfile']
+
+    if message and logfile:
+        raise util.Abort(_('options --message and --logfile are mutually '
+                           'exclusive'))
+    if not message and logfile:
+        try:
+            if logfile == '-':
+                message = sys.stdin.read()
+            else:
+                message = open(logfile).read()
+        except IOError, inst:
+            raise util.Abort(_("can't read commit message '%s': %s") %
+                             (logfile, inst.strerror))
+    return message
 
 def walkchangerevs(ui, repo, pats, opts):
     '''Iterate over files and the revs they changed in.
@@ -105,12 +92,23 @@
                     windowsize *= 2
 
 
-    files, matchfn, anypats = matchpats(repo, pats, opts)
+    files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
+    follow = opts.get('follow') or opts.get('follow_first')
 
     if repo.changelog.count() == 0:
         return [], False, matchfn
 
-    revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
+    if follow:
+        p = repo.dirstate.parents()[0]
+        if p == nullid:
+            ui.warn(_('No working directory revision; defaulting to tip\n'))
+            start = 'tip'
+        else:
+            start = repo.changelog.rev(p)
+        defrange = '%s:0' % start
+    else:
+        defrange = 'tip:0'
+    revs = map(int, revrange(ui, repo, opts['rev'] or [defrange]))
     wanted = {}
     slowpath = anypats
     fncache = {}
@@ -125,37 +123,54 @@
     if not slowpath and not files:
         # No files, no patterns.  Display all revs.
         wanted = dict(zip(revs, revs))
+    copies = []
     if not slowpath:
         # Only files, no patterns.  Check the history of each file.
-        def filerevgen(filelog):
+        def filerevgen(filelog, node):
             cl_count = repo.changelog.count()
-            for i, window in increasing_windows(filelog.count()-1, -1):
+            if node is None:
+                last = filelog.count() - 1
+            else:
+                last = filelog.rev(node)
+            for i, window in increasing_windows(last, -1):
                 revs = []
                 for j in xrange(i - window, i + 1):
-                    revs.append(filelog.linkrev(filelog.node(j)))
+                    n = filelog.node(j)
+                    revs.append((filelog.linkrev(n),
+                                 follow and filelog.renamed(n)))
                 revs.reverse()
                 for rev in revs:
                     # only yield rev for which we have the changelog, it can
                     # happen while doing "hg log" during a pull or commit
-                    if rev < cl_count:
+                    if rev[0] < cl_count:
                         yield rev
-
+        def iterfiles():
+            for filename in files:
+                yield filename, None
+            for filename_node in copies:
+                yield filename_node
         minrev, maxrev = min(revs), max(revs)
-        for file_ in files:
+        for file_, node in iterfiles():
             filelog = repo.file(file_)
             # A zero count may be a directory or deleted file, so
             # try to find matching entries on the slow path.
             if filelog.count() == 0:
                 slowpath = True
                 break
-            for rev in filerevgen(filelog):
+            for rev, copied in filerevgen(filelog, node):
                 if rev <= maxrev:
                     if rev < minrev:
                         break
                     fncache.setdefault(rev, [])
                     fncache[rev].append(file_)
                     wanted[rev] = 1
+                    if follow and copied:
+                        copies.append(copied)
     if slowpath:
+        if follow:
+            raise util.Abort(_('can only follow copies/renames for explicit '
+                               'file names'))
+
         # The slow path checks files modified in every changeset.
         def changerevgen():
             for i, window in increasing_windows(repo.changelog.count()-1, -1):
@@ -168,11 +183,66 @@
                 fncache[rev] = matches
                 wanted[rev] = 1
 
+    class followfilter:
+        def __init__(self, onlyfirst=False):
+            self.startrev = -1
+            self.roots = []
+            self.onlyfirst = onlyfirst
+
+        def match(self, rev):
+            def realparents(rev):
+                if self.onlyfirst:
+                    return repo.changelog.parentrevs(rev)[0:1]
+                else:
+                    return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
+
+            if self.startrev == -1:
+                self.startrev = rev
+                return True
+
+            if rev > self.startrev:
+                # forward: all descendants
+                if not self.roots:
+                    self.roots.append(self.startrev)
+                for parent in realparents(rev):
+                    if parent in self.roots:
+                        self.roots.append(rev)
+                        return True
+            else:
+                # backwards: all parents
+                if not self.roots:
+                    self.roots.extend(realparents(self.startrev))
+                if rev in self.roots:
+                    self.roots.remove(rev)
+                    self.roots.extend(realparents(rev))
+                    return True
+
+            return False
+
+    # it might be worthwhile to do this in the iterator if the rev range
+    # is descending and the prune args are all within that range
+    for rev in opts.get('prune', ()):
+        rev = repo.changelog.rev(repo.lookup(rev))
+        ff = followfilter()
+        stop = min(revs[0], revs[-1])
+        for x in range(rev, stop-1, -1):
+            if ff.match(x) and wanted.has_key(x):
+                del wanted[x]
+
     def iterate():
+        if follow and not files:
+            ff = followfilter(onlyfirst=opts.get('follow_first'))
+            def want(rev):
+                if ff.match(rev) and rev in wanted:
+                    return True
+                return False
+        else:
+            def want(rev):
+                return rev in wanted
+
         for i, window in increasing_windows(0, len(revs)):
             yield 'window', revs[0] < revs[-1], revs[-1]
-            nrevs = [rev for rev in revs[i:i+window]
-                     if rev in wanted]
+            nrevs = [rev for rev in revs[i:i+window] if want(rev)]
             srevs = list(nrevs)
             srevs.sort()
             for rev in srevs:
@@ -252,62 +322,6 @@
             seen[rev] = 1
             yield str(rev)
 
-def make_filename(repo, pat, node,
-                  total=None, seqno=None, revwidth=None, pathname=None):
-    node_expander = {
-        'H': lambda: hex(node),
-        'R': lambda: str(repo.changelog.rev(node)),
-        'h': lambda: short(node),
-        }
-    expander = {
-        '%': lambda: '%',
-        'b': lambda: os.path.basename(repo.root),
-        }
-
-    try:
-        if node:
-            expander.update(node_expander)
-        if node and revwidth is not None:
-            expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
-        if total is not None:
-            expander['N'] = lambda: str(total)
-        if seqno is not None:
-            expander['n'] = lambda: str(seqno)
-        if total is not None and seqno is not None:
-            expander['n'] = lambda:str(seqno).zfill(len(str(total)))
-        if pathname is not None:
-            expander['s'] = lambda: os.path.basename(pathname)
-            expander['d'] = lambda: os.path.dirname(pathname) or '.'
-            expander['p'] = lambda: pathname
-
-        newname = []
-        patlen = len(pat)
-        i = 0
-        while i < patlen:
-            c = pat[i]
-            if c == '%':
-                i += 1
-                c = pat[i]
-                c = expander[c]()
-            newname.append(c)
-            i += 1
-        return ''.join(newname)
-    except KeyError, inst:
-        raise util.Abort(_("invalid format spec '%%%s' in output file name"),
-                    inst.args[0])
-
-def make_file(repo, pat, node=None,
-              total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
-    if not pat or pat == '-':
-        return 'w' in mode and sys.stdout or sys.stdin
-    if hasattr(pat, 'write') and 'w' in mode:
-        return pat
-    if hasattr(pat, 'read') and 'r' in mode:
-        return pat
-    return open(make_filename(repo, pat, node, total, seqno, revwidth,
-                              pathname),
-                mode)
-
 def write_bundle(cg, filename=None, compress=True):
     """Write a bundle file and return its filename.
 
@@ -360,83 +374,6 @@
         if cleanup is not None:
             os.unlink(cleanup)
 
-def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
-           changes=None, text=False, opts={}):
-    if not node1:
-        node1 = repo.dirstate.parents()[0]
-    # reading the data for node1 early allows it to play nicely
-    # with repo.changes and the revlog cache.
-    change = repo.changelog.read(node1)
-    mmap = repo.manifest.read(change[0])
-    date1 = util.datestr(change[2])
-
-    if not changes:
-        changes = repo.changes(node1, node2, files, match=match)
-    modified, added, removed, deleted, unknown = changes
-    if files:
-        modified, added, removed = map(lambda x: filterfiles(files, x),
-                                       (modified, added, removed))
-
-    if not modified and not added and not removed:
-        return
-
-    if node2:
-        change = repo.changelog.read(node2)
-        mmap2 = repo.manifest.read(change[0])
-        _date2 = util.datestr(change[2])
-        def date2(f):
-            return _date2
-        def read(f):
-            return repo.file(f).read(mmap2[f])
-    else:
-        tz = util.makedate()[1]
-        _date2 = util.datestr()
-        def date2(f):
-            try:
-                return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
-            except OSError, err:
-                if err.errno != errno.ENOENT: raise
-                return _date2
-        def read(f):
-            return repo.wread(f)
-
-    if ui.quiet:
-        r = None
-    else:
-        hexfunc = ui.verbose and hex or short
-        r = [hexfunc(node) for node in [node1, node2] if node]
-
-    diffopts = ui.diffopts()
-    showfunc = opts.get('show_function') or diffopts['showfunc']
-    ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
-    ignorewsamount = opts.get('ignore_space_change') or \
-                     diffopts['ignorewsamount']
-    ignoreblanklines = opts.get('ignore_blank_lines') or \
-                     diffopts['ignoreblanklines']
-    for f in modified:
-        to = None
-        if f in mmap:
-            to = repo.file(f).read(mmap[f])
-        tn = read(f)
-        fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
-                               showfunc=showfunc, ignorews=ignorews,
-                               ignorewsamount=ignorewsamount,
-                               ignoreblanklines=ignoreblanklines))
-    for f in added:
-        to = None
-        tn = read(f)
-        fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
-                               showfunc=showfunc, ignorews=ignorews,
-                               ignorewsamount=ignorewsamount,
-                               ignoreblanklines=ignoreblanklines))
-    for f in removed:
-        to = repo.file(f).read(mmap[f])
-        tn = None
-        fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
-                               showfunc=showfunc, ignorews=ignorews,
-                               ignorewsamount=ignorewsamount,
-                               ignoreblanklines=ignoreblanklines))
-
 def trimuser(ui, name, rev, revcache):
     """trim the name of the user who committed a change"""
     user = revcache.get(rev)
@@ -493,7 +430,7 @@
         self.ui.status(_("date:        %s\n") % date)
 
         if self.ui.debugflag:
-            files = self.repo.changes(log.parents(changenode)[0], changenode)
+            files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
             for key, value in zip([_("files:"), _("files+:"), _("files-:")],
                                   files):
                 if value:
@@ -537,12 +474,19 @@
         return t
     return changeset_printer(ui, repo)
 
+def setremoteconfig(ui, opts):
+    "copy remote options to ui tree"
+    if opts.get('ssh'):
+        ui.setconfig("ui", "ssh", opts['ssh'])
+    if opts.get('remotecmd'):
+        ui.setconfig("ui", "remotecmd", opts['remotecmd'])
+
 def show_version(ui):
     """output version and copyright information"""
     ui.write(_("Mercurial Distributed SCM (version %s)\n")
              % version.get_version())
     ui.status(_(
-        "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
+        "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
         "This is free software; see the source for copying conditions. "
         "There is NO\nwarranty; "
         "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
@@ -696,7 +640,7 @@
     """
 
     names = []
-    for src, abs, rel, exact in walk(repo, pats, opts):
+    for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
         if exact:
             if ui.verbose:
                 ui.status(_('adding %s\n') % rel)
@@ -715,22 +659,7 @@
     New files are ignored if they match any of the patterns in .hgignore. As
     with add, these changes take effect at the next commit.
     """
-    return addremove_lock(ui, repo, pats, opts)
-
-def addremove_lock(ui, repo, pats, opts, wlock=None):
-    add, remove = [], []
-    for src, abs, rel, exact in walk(repo, pats, opts):
-        if src == 'f' and repo.dirstate.state(abs) == '?':
-            add.append(abs)
-            if ui.verbose or not exact:
-                ui.status(_('adding %s\n') % ((pats and rel) or abs))
-        if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
-            remove.append(abs)
-            if ui.verbose or not exact:
-                ui.status(_('removing %s\n') % ((pats and rel) or abs))
-    if not opts.get('dry_run'):
-        repo.add(add, wlock=wlock)
-        repo.remove(remove, wlock=wlock)
+    return cmdutil.addremove(repo, pats, opts)
 
 def annotate(ui, repo, *pats, **opts):
     """show changeset information per file line
@@ -773,7 +702,8 @@
 
     ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
 
-    for src, abs, rel, exact in walk(repo, pats, opts, node=ctx.node()):
+    for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
+                                             node=ctx.node()):
         fctx = ctx.filectx(abs)
         if not opts['text'] and util.binary(fctx.data()):
             ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
@@ -825,10 +755,10 @@
             raise util.Abort(_('uncommitted merge - please provide a '
                                'specific revision'))
 
-    dest = make_filename(repo, dest, node)
+    dest = cmdutil.make_filename(repo, dest, node)
     if os.path.realpath(dest) == repo.root:
         raise util.Abort(_('repository root cannot be destination'))
-    dummy, matchfn, dummy = matchpats(repo, [], opts)
+    dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
     kind = opts.get('type') or 'files'
     prefix = opts['prefix']
     if dest == '-':
@@ -836,7 +766,7 @@
             raise util.Abort(_('cannot archive plain files to stdout'))
         dest = sys.stdout
         if not prefix: prefix = os.path.basename(repo.root) + '-%h'
-    prefix = make_filename(repo, prefix, node)
+    prefix = cmdutil.make_filename(repo, prefix, node)
     archival.archive(repo, dest, node, kind, not opts['no_decode'],
                      matchfn, prefix)
 
@@ -879,7 +809,7 @@
         if opts['parent']:
             raise util.Abort(_('cannot use --parent on non-merge changeset'))
         parent = p1
-    repo.update(node, force=True, show_stats=False)
+    hg.clean(repo, node, show_stats=False)
     revert_opts = opts.copy()
     revert_opts['rev'] = hex(parent)
     revert(ui, repo, **revert_opts)
@@ -896,11 +826,13 @@
     if op1 != node:
         if opts['merge']:
             ui.status(_('merging with changeset %s\n') % nice(op1))
-            doupdate(ui, repo, hex(op1), **opts)
+            n = _lookup(repo, hex(op1))
+            hg.merge(repo, n)
         else:
             ui.status(_('the backout changeset is a new head - '
                         'do not forget to merge\n'))
-            ui.status(_('(use "backout -m" if you want to auto-merge)\n'))
+            ui.status(_('(use "backout --merge" '
+                        'if you want to auto-merge)\n'))
 
 def bundle(ui, repo, fname, dest=None, **opts):
     """create a changegroup file
@@ -937,9 +869,10 @@
     %d   dirname of file being printed, or '.' if in repo root
     %p   root-relative path name of file being printed
     """
-    ctx = repo.changectx(opts['rev'] or -1)
-    for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
-        fp = make_file(repo, opts['output'], ctx.node(), pathname=abs)
+    ctx = repo.changectx(opts['rev'] or "-1")
+    for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
+                                             ctx.node()):
+        fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
         fp.write(ctx.filectx(abs).data())
 
 def clone(ui, source, dest=None, **opts):
@@ -954,10 +887,25 @@
     .hg/hgrc file, as the default to be used for future pulls.
 
     For efficiency, hardlinks are used for cloning whenever the source
-    and destination are on the same filesystem.  Some filesystems,
-    such as AFS, implement hardlinking incorrectly, but do not report
-    errors.  In these cases, use the --pull option to avoid
-    hardlinking.
+    and destination are on the same filesystem (note this applies only
+    to the repository data, not to the checked out files).  Some
+    filesystems, such as AFS, implement hardlinking incorrectly, but
+    do not report errors.  In these cases, use the --pull option to
+    avoid hardlinking.
+
+    You can safely clone repositories and checked out files using full
+    hardlinks with
+
+      $ cp -al REPO REPOCLONE
+
+    which is the fastest way to clone. However, the operation is not
+    atomic (making sure REPO is not modified during the operation is
+    up to you) and you have to make sure your editor breaks hardlinks
+    (Emacs and most Linux Kernel tools do so).
+
+    If you use the -r option to clone up to a specific revision, no
+    subsequent revisions will be present in the cloned repository.
+    This option implies --pull, even on local repositories.
 
     See pull for valid source format details.
 
@@ -965,7 +913,7 @@
     .hg/hgrc will be created on the remote side. Look at the help text
     for the pull command for important details about ssh:// URLs.
     """
-    ui.setconfig_remoteopts(**opts)
+    setremoteconfig(ui, opts)
     hg.clone(ui, ui.expandpath(source), dest,
              pull=opts['pull'],
              stream=opts['uncompressed'],
@@ -983,28 +931,13 @@
     If no commit message is specified, the editor configured in your hgrc
     or in the EDITOR environment variable is started to enter a message.
     """
-    message = opts['message']
-    logfile = opts['logfile']
-
-    if message and logfile:
-        raise util.Abort(_('options --message and --logfile are mutually '
-                           'exclusive'))
-    if not message and logfile:
-        try:
-            if logfile == '-':
-                message = sys.stdin.read()
-            else:
-                message = open(logfile).read()
-        except IOError, inst:
-            raise util.Abort(_("can't read commit message '%s': %s") %
-                             (logfile, inst.strerror))
+    message = logmessage(opts)
 
     if opts['addremove']:
-        addremove_lock(ui, repo, pats, opts)
-    fns, match, anypats = matchpats(repo, pats, opts)
+        cmdutil.addremove(repo, pats, opts)
+    fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
     if pats:
-        modified, added, removed, deleted, unknown = (
-            repo.changes(files=fns, match=match))
+        modified, added, removed = repo.status(files=fns, match=match)[:3]
         files = modified + added + removed
     else:
         files = []
@@ -1159,7 +1092,7 @@
     copylist = []
     for pat in pats:
         srcs = []
-        for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
+        for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
             origsrc = okaytocopy(abssrc, relsrc, exact)
             if origsrc:
                 srcs.append((origsrc, abssrc, relsrc, exact))
@@ -1233,9 +1166,9 @@
         rev = repo.lookup(rev)
     change = repo.changelog.read(rev)
     n = change[0]
-    files = repo.manifest.readflags(n)
+    files = repo.manifest.read(n)
     wlock = repo.wlock()
-    repo.dirstate.rebuild(rev, files.iteritems())
+    repo.dirstate.rebuild(rev, files)
 
 def debugcheckstate(ui, repo):
     """validate the correctness of the current dirstate"""
@@ -1376,7 +1309,7 @@
 
 def debugwalk(ui, repo, *pats, **opts):
     """show how files match on given patterns"""
-    items = list(walk(repo, pats, opts))
+    items = list(cmdutil.walk(repo, pats, opts))
     if not items:
         return
     fmt = '%%s  %%-%ds  %%-%ds  %%s' % (
@@ -1405,37 +1338,10 @@
     """
     node1, node2 = revpair(ui, repo, opts['rev'])
 
-    fns, matchfn, anypats = matchpats(repo, pats, opts)
-
-    dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
-           text=opts['text'], opts=opts)
-
-def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
-    node = repo.lookup(changeset)
-    parents = [p for p in repo.changelog.parents(node) if p != nullid]
-    if opts['switch_parent']:
-        parents.reverse()
-    prev = (parents and parents[0]) or nullid
-    change = repo.changelog.read(node)
-
-    fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
-                   revwidth=revwidth)
-    if fp != sys.stdout:
-        ui.note("%s\n" % fp.name)
-
-    fp.write("# HG changeset patch\n")
-    fp.write("# User %s\n" % change[1])
-    fp.write("# Date %d %d\n" % change[2])
-    fp.write("# Node ID %s\n" % hex(node))
-    fp.write("# Parent  %s\n" % hex(prev))
-    if len(parents) > 1:
-        fp.write("# Parent  %s\n" % hex(parents[1]))
-    fp.write(change[4].rstrip())
-    fp.write("\n\n")
-
-    dodiff(fp, ui, repo, prev, node, text=opts['text'])
-    if fp != sys.stdout:
-        fp.close()
+    fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
+
+    patch.diff(repo, node1, node2, fns, match=matchfn,
+               opts=patch.diffopts(ui, opts))
 
 def export(ui, repo, *changesets, **opts):
     """dump the header and diffs for one or more changesets
@@ -1466,15 +1372,14 @@
     """
     if not changesets:
         raise util.Abort(_("export requires at least one changeset"))
-    seqno = 0
     revs = list(revrange(ui, repo, changesets))
-    total = len(revs)
-    revwidth = max(map(len, revs))
-    msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
-    ui.note(msg)
-    for cset in revs:
-        seqno += 1
-        doexport(ui, repo, cset, seqno, total, revwidth, opts)
+    if len(revs) > 1:
+        ui.note(_('exporting patches:\n'))
+    else:
+        ui.note(_('exporting patch:\n'))
+    patch.export(repo, map(repo.lookup, revs), template=opts['output'],
+                 switch_parent=opts['switch_parent'],
+                 opts=patch.diffopts(ui, opts))
 
 def forget(ui, repo, *pats, **opts):
     """don't add the specified files on the next commit (DEPRECATED)
@@ -1487,7 +1392,7 @@
     """
     ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
     forget = []
-    for src, abs, rel, exact in walk(repo, pats, opts):
+    for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
         if repo.dirstate.state(abs) == 'a':
             forget.append(abs)
             if ui.verbose or not exact:
@@ -1544,42 +1449,56 @@
             self.linenum = linenum
             self.colstart = colstart
             self.colend = colend
+
         def __eq__(self, other):
             return self.line == other.line
-        def __hash__(self):
-            return hash(self.line)
 
     matches = {}
+    copies = {}
     def grepbody(fn, rev, body):
-        matches[rev].setdefault(fn, {})
+        matches[rev].setdefault(fn, [])
         m = matches[rev][fn]
         for lnum, cstart, cend, line in matchlines(body):
             s = linestate(line, lnum, cstart, cend)
-            m[s] = s
-
-    # FIXME: prev isn't used, why ?
+            m.append(s)
+
+    def difflinestates(a, b):
+        sm = difflib.SequenceMatcher(None, a, b)
+        for tag, alo, ahi, blo, bhi in sm.get_opcodes():
+            if tag == 'insert':
+                for i in range(blo, bhi):
+                    yield ('+', b[i])
+            elif tag == 'delete':
+                for i in range(alo, ahi):
+                    yield ('-', a[i])
+            elif tag == 'replace':
+                for i in range(alo, ahi):
+                    yield ('-', a[i])
+                for i in range(blo, bhi):
+                    yield ('+', b[i])
+
     prev = {}
     ucache = {}
     def display(fn, rev, states, prevstates):
-        diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
-        diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
         counts = {'-': 0, '+': 0}
         filerevmatches = {}
-        for l in diff:
+        if incrementing or not opts['all']:
+            a, b = prevstates, states
+        else:
+            a, b = states, prevstates
+        for change, l in difflinestates(a, b):
             if incrementing or not opts['all']:
-                change = ((l in prevstates) and '-') or '+'
                 r = rev
             else:
-                change = ((l in states) and '-') or '+'
                 r = prev[fn]
-            cols = [fn, str(rev)]
+            cols = [fn, str(r)]
             if opts['line_number']:
                 cols.append(str(l.linenum))
             if opts['all']:
                 cols.append(change)
             if opts['user']:
-                cols.append(trimuser(ui, getchange(rev)[1], rev,
-                                                  ucache))
+                cols.append(trimuser(ui, getchange(r)[1], rev,
+                                     ucache))
             if opts['files_with_matches']:
                 c = (fn, rev)
                 if c in filerevmatches:
@@ -1596,6 +1515,7 @@
     changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
     count = 0
     incrementing = False
+    follow = opts.get('follow')
     for st, rev, fns in changeiter:
         if st == 'window':
             incrementing = rev
@@ -1610,20 +1530,31 @@
                 fstate.setdefault(fn, {})
                 try:
                     grepbody(fn, rev, getfile(fn).read(mf[fn]))
+                    if follow:
+                        copied = getfile(fn).renamed(mf[fn])
+                        if copied:
+                            copies.setdefault(rev, {})[fn] = copied[0]
                 except KeyError:
                     pass
         elif st == 'iter':
             states = matches[rev].items()
             states.sort()
             for fn, m in states:
+                copy = copies.get(rev, {}).get(fn)
                 if fn in skip:
+                    if copy:
+                        skip[copy] = True
                     continue
                 if incrementing or not opts['all'] or fstate[fn]:
                     pos, neg = display(fn, rev, m, fstate[fn])
                     count += pos + neg
                     if pos and not opts['all']:
                         skip[fn] = True
+                        if copy:
+                            skip[copy] = True
                 fstate[fn] = m
+                if copy:
+                    fstate[copy] = m
                 prev[fn] = rev
 
     if not incrementing:
@@ -1632,7 +1563,8 @@
         for fn, state in fstate:
             if fn in skip:
                 continue
-            display(fn, rev, {}, state)
+            if fn not in copies.get(prev[fn], {}):
+                display(fn, rev, {}, state)
     return (count == 0 and 1) or 0
 
 def heads(ui, repo, **opts):
@@ -1670,7 +1602,7 @@
         return
 
     hexfunc = ui.verbose and hex or short
-    modified, added, removed, deleted, unknown = repo.changes()
+    modified, added, removed, deleted = repo.status()[:4]
     output = ["%s%s" %
               ('+'.join([hexfunc(parent) for parent in parents]),
               (modified or added or removed or deleted) and "+" or "")]
@@ -1714,81 +1646,23 @@
     d = opts["base"]
     strip = opts["strip"]
 
-    mailre = re.compile(r'(?:From |[\w-]+:)')
-
-    # attempt to detect the start of a patch
-    # (this heuristic is borrowed from quilt)
-    diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
-                        'retrieving revision [0-9]+(\.[0-9]+)*$|' +
-                        '(---|\*\*\*)[ \t])', re.MULTILINE)
-
-    for patch in patches:
-        pf = os.path.join(d, patch)
-
-        message = None
-        user = None
-        date = None
-        hgpatch = False
-
-        p = email.Parser.Parser()
+    wlock = repo.wlock()
+    lock = repo.lock()
+
+    for p in patches:
+        pf = os.path.join(d, p)
+
         if pf == '-':
-            msg = p.parse(sys.stdin)
             ui.status(_("applying patch from stdin\n"))
+            tmpname, message, user, date = patch.extract(ui, sys.stdin)
         else:
-            msg = p.parse(file(pf))
-            ui.status(_("applying %s\n") % patch)
-
-        fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
-        tmpfp = os.fdopen(fd, 'w')
+            ui.status(_("applying %s\n") % p)
+            tmpname, message, user, date = patch.extract(ui, file(pf))
+
+        if tmpname is None:
+            raise util.Abort(_('no diffs found'))
+
         try:
-            message = msg['Subject']
-            if message:
-                message = message.replace('\n\t', ' ')
-                ui.debug('Subject: %s\n' % message)
-            user = msg['From']
-            if user:
-                ui.debug('From: %s\n' % user)
-            diffs_seen = 0
-            ok_types = ('text/plain', 'text/x-patch')
-            for part in msg.walk():
-                content_type = part.get_content_type()
-                ui.debug('Content-Type: %s\n' % content_type)
-                if content_type not in ok_types:
-                    continue
-                payload = part.get_payload(decode=True)
-                m = diffre.search(payload)
-                if m:
-                    ui.debug(_('found patch at byte %d\n') % m.start(0))
-                    diffs_seen += 1
-                    hgpatch = False
-                    fp = cStringIO.StringIO()
-                    if message:
-                        fp.write(message)
-                        fp.write('\n')
-                    for line in payload[:m.start(0)].splitlines():
-                        if line.startswith('# HG changeset patch'):
-                            ui.debug(_('patch generated by hg export\n'))
-                            hgpatch = True
-                            # drop earlier commit message content
-                            fp.seek(0)
-                            fp.truncate()
-                        elif hgpatch:
-                            if line.startswith('# User '):
-                                user = line[7:]
-                                ui.debug('From: %s\n' % user)
-                            elif line.startswith("# Date "):
-                                date = line[7:]
-                        if not line.startswith('# '):
-                            fp.write(line)
-                            fp.write('\n')
-                    message = fp.getvalue()
-                    if tmpfp:
-                        tmpfp.write(payload)
-                        if not payload.endswith('\n'):
-                            tmpfp.write('\n')
-                elif not diffs_seen and message and content_type == 'text/plain':
-                    message += '\n' + payload
-
             if opts['message']:
                 # pickup the cmdline msg
                 message = opts['message']
@@ -1800,14 +1674,9 @@
                 message = None
             ui.debug(_('message:\n%s\n') % message)
 
-            tmpfp.close()
-            if not diffs_seen:
-                raise util.Abort(_('no diffs found'))
-
-            files = util.patch(strip, tmpname, ui)
-            if len(files) > 0:
-                addremove_lock(ui, repo, files, {})
-            repo.commit(files, message, user, date)
+            files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
+            files = patch.updatedir(ui, repo, files, wlock=wlock)
+            repo.commit(files, message, user, date, wlock=wlock, lock=lock)
         finally:
             os.unlink(tmpname)
 
@@ -1824,7 +1693,7 @@
     See pull for valid source format details.
     """
     source = ui.expandpath(source)
-    ui.setconfig_remoteopts(**opts)
+    setremoteconfig(ui, opts)
 
     other = hg.repository(ui, source)
     incoming = repo.findincoming(other, force=opts["force"])
@@ -1860,7 +1729,7 @@
             displayer.show(changenode=n)
             if opts['patch']:
                 prev = (parents and parents[0]) or nullid
-                dodiff(ui, ui, other, prev, n)
+                patch.diff(repo, other, prev, n)
                 ui.write("\n")
     finally:
         if hasattr(other, 'close'):
@@ -1880,7 +1749,7 @@
     Look at the help text for the pull command for important details
     about ssh:// URLs.
     """
-    ui.setconfig_remoteopts(**opts)
+    setremoteconfig(ui, opts)
     hg.repository(ui, dest, create=1)
 
 def locate(ui, repo, *pats, **opts):
@@ -1908,8 +1777,8 @@
     else:
         node = None
 
-    for src, abs, rel, exact in walk(repo, pats, opts, node=node,
-                                     head='(?:.*/|)'):
+    for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
+                                             head='(?:.*/|)'):
         if not node and repo.dirstate.state(abs) == '?':
             continue
         if opts['fullpath']:
@@ -1920,7 +1789,18 @@
 def log(ui, repo, *pats, **opts):
     """show revision history of entire repository or files
 
-    Print the revision history of the specified files or the entire project.
+    Print the revision history of the specified files or the entire
+    project.
+
+    File history is shown without following rename or copy history of
+    files.  Use -f/--follow with a file name to follow history across
+    renames and copies. --follow without a file name will only show
+    ancestors or descendants of the starting revision. --follow-first
+    only follows the first parent of merge revisions.
+
+    If no revision range is specified, the default is tip:0 unless
+    --follow is set, in which case the working directory parent is
+    used as the starting revision.
 
     By default this command outputs: changeset id and hash, tags,
     non-trivial parents, user, date and time, and a summary for each
@@ -2000,7 +1880,7 @@
             displayer.show(rev, brinfo=br)
             if opts['patch']:
                 prev = (parents and parents[0]) or nullid
-                dodiff(du, du, repo, prev, changenode, match=matchfn)
+                patch.diff(repo, prev, changenode, match=matchfn, fp=du)
                 du.write("\n\n")
         elif st == 'iter':
             if count == limit: break
@@ -2031,22 +1911,44 @@
     else:
         n = repo.manifest.tip()
     m = repo.manifest.read(n)
-    mf = repo.manifest.readflags(n)
     files = m.keys()
     files.sort()
 
     for f in files:
-        ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
-
-def merge(ui, repo, node=None, **opts):
+        ui.write("%40s %3s %s\n" % (hex(m[f]),
+                                    m.execf(f) and "755" or "644", f))
+
+def merge(ui, repo, node=None, force=None, branch=None):
     """Merge working directory with another revision
 
     Merge the contents of the current working directory and the
     requested revision. Files that changed between either parent are
     marked as changed for the next commit and a commit must be
     performed before any further updates are allowed.
+
+    If no revision is specified, the working directory's parent is a
+    head revision, and the repository contains exactly one other head,
+    the other head is merged with by default.  Otherwise, an explicit
+    revision to merge with must be provided.
     """
-    return doupdate(ui, repo, node=node, merge=True, **opts)
+
+    if node:
+        node = _lookup(repo, node, branch)
+    else:
+        heads = repo.heads()
+        if len(heads) > 2:
+            raise util.Abort(_('repo has %d heads - '
+                               'please merge with an explicit rev') %
+                             len(heads))
+        if len(heads) == 1:
+            raise util.Abort(_('there is nothing to merge - '
+                               'use "hg update" instead'))
+        parent = repo.dirstate.parents()[0]
+        if parent not in heads:
+            raise util.Abort(_('working dir not at a head rev - '
+                               'use "hg update" or merge with an explicit rev'))
+        node = parent == heads[0] and heads[-1] or heads[0]
+    return hg.merge(repo, node, force=force)
 
 def outgoing(ui, repo, dest=None, **opts):
     """show changesets not found in destination
@@ -2058,7 +1960,7 @@
     See pull for valid destination format details.
     """
     dest = ui.expandpath(dest or 'default-push', dest or 'default')
-    ui.setconfig_remoteopts(**opts)
+    setremoteconfig(ui, opts)
     revs = None
     if opts['rev']:
         revs = [repo.lookup(rev) for rev in opts['rev']]
@@ -2079,16 +1981,31 @@
         displayer.show(changenode=n)
         if opts['patch']:
             prev = (parents and parents[0]) or nullid
-            dodiff(ui, ui, repo, prev, n)
+            patch.diff(repo, prev, n)
             ui.write("\n")
 
-def parents(ui, repo, rev=None, branches=None, **opts):
+def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
     """show the parents of the working dir or revision
 
     Print the working directory's parent revisions.
     """
+    # legacy
+    if file_ and not rev:
+        try:
+            rev = repo.lookup(file_)
+            file_ = None
+        except hg.RepoError:
+            pass
+        else:
+            ui.warn(_("'hg parent REV' is deprecated, "
+                      "please use 'hg parents -r REV instead\n"))
+
     if rev:
-        p = repo.changelog.parents(repo.lookup(rev))
+        if file_:
+            ctx = repo.filectx(file_, changeid=rev)
+        else:
+            ctx = repo.changectx(rev)
+        p = [cp.node() for cp in ctx.parents()]
     else:
         p = repo.dirstate.parents()
 
@@ -2125,7 +2042,7 @@
         return
     if optupdate:
         if modheads == 1:
-            return doupdate(ui, repo)
+            return hg.update(repo, repo.changelog.tip()) # update
         else:
             ui.status(_("not updating, since new heads added\n"))
     if modheads > 1:
@@ -2165,7 +2082,7 @@
       with the --ssh command line option.
     """
     source = ui.expandpath(source)
-    ui.setconfig_remoteopts(**opts)
+    setremoteconfig(ui, opts)
 
     other = hg.repository(ui, source)
     ui.status(_('pulling from %s\n') % (source))
@@ -2203,7 +2120,7 @@
     feature is enabled on the remote Mercurial server.
     """
     dest = ui.expandpath(dest or 'default-push', dest or 'default')
-    ui.setconfig_remoteopts(**opts)
+    setremoteconfig(ui, opts)
 
     other = hg.repository(ui, dest)
     ui.status('pushing to %s\n' % (dest))
@@ -2257,7 +2174,7 @@
     operation. It should only be necessary when Mercurial suggests it.
     """
     if repo.recover():
-        return repo.verify()
+        return hg.verify(repo)
     return 1
 
 def remove(ui, repo, *pats, **opts):
@@ -2277,12 +2194,12 @@
     names = []
     if not opts['after'] and not pats:
         raise util.Abort(_('no files specified'))
-    files, matchfn, anypats = matchpats(repo, pats, opts)
+    files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
     exact = dict.fromkeys(files)
-    mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn))
+    mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
     modified, added, removed, deleted, unknown = mardu
     remove, forget = [], []
-    for src, abs, rel, exact in walk(repo, pats, opts):
+    for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
         reason = None
         if abs not in deleted and opts['after']:
             reason = _('is still present')
@@ -2389,20 +2306,21 @@
 
     # walk dirstate.
 
-    for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
+    for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
+                                             badmatch=mf.has_key):
         names[abs] = (rel, exact)
         if src == 'b':
             target_only[abs] = True
 
     # walk target manifest.
 
-    for src, abs, rel, exact in walk(repo, pats, opts, node=node,
-                                     badmatch=names.has_key):
+    for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
+                                             badmatch=names.has_key):
         if abs in names: continue
         names[abs] = (rel, exact)
         target_only[abs] = True
 
-    changes = repo.changes(match=names.has_key, wlock=wlock)
+    changes = repo.status(match=names.has_key, wlock=wlock)[:5]
     modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
 
     revert = ([], _('reverting %s\n'))
@@ -2474,8 +2392,7 @@
 
     if not opts.get('dry_run'):
         repo.dirstate.forget(forget[0])
-        r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
-                        show_stats=False)
+        r = hg.revert(repo, node, update.has_key, wlock)
         repo.dirstate.update(add[0], 'a')
         repo.dirstate.update(undelete[0], 'n')
         repo.dirstate.update(remove[0], 'r')
@@ -2593,37 +2510,44 @@
 def status(ui, repo, *pats, **opts):
     """show changed files in the working directory
 
-    Show changed files in the repository.  If names are
-    given, only files that match are shown.
+    Show status of files in the repository.  If names are given, only
+    files that match are shown.  Files that are clean or ignored, are
+    not listed unless -c (clean), -i (ignored) or -A is given.
 
     The codes used to show the status of files are:
     M = modified
     A = added
     R = removed
+    C = clean
     ! = deleted, but still tracked
     ? = not tracked
     I = ignored (not shown by default)
       = the previous added file was copied from here
     """
 
-    show_ignored = opts['ignored'] and True or False
-    files, matchfn, anypats = matchpats(repo, pats, opts)
+    all = opts['all']
+    
+    files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
     cwd = (pats and repo.getcwd()) or ''
-    modified, added, removed, deleted, unknown, ignored = [
+    modified, added, removed, deleted, unknown, ignored, clean = [
         [util.pathto(cwd, x) for x in n]
-        for n in repo.changes(files=files, match=matchfn,
-                              show_ignored=show_ignored)]
-
-    changetypes = [('modified', 'M', modified),
+        for n in repo.status(files=files, match=matchfn,
+                             list_ignored=all or opts['ignored'],
+                             list_clean=all or opts['clean'])]
+
+    changetypes = (('modified', 'M', modified),
                    ('added', 'A', added),
                    ('removed', 'R', removed),
                    ('deleted', '!', deleted),
                    ('unknown', '?', unknown),
-                   ('ignored', 'I', ignored)]
+                   ('ignored', 'I', ignored))
+
+    explicit_changetypes = changetypes + (('clean', 'C', clean),)
 
     end = opts['print0'] and '\0' or '\n'
 
-    for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
+    for opt, char, changes in ([ct for ct in explicit_changetypes
+                                if all or opts[ct[0]]]
                                or changetypes):
         if opts['no_status']:
             format = "%%s%s" % end
@@ -2632,7 +2556,7 @@
 
         for f in changes:
             ui.write(format % f)
-            if (opts.get('copies') and not opts.get('no_status')
+            if ((all or opts.get('copies')) and not opts.get('no_status')
                 and opt == 'added' and repo.dirstate.copies.has_key(f)):
                 ui.write('  %s%s' % (repo.dirstate.copies[f], end))
 
@@ -2645,7 +2569,7 @@
     very useful to compare different revision, to go back to significant
     earlier versions or to mark branch points as releases, etc.
 
-    If no revision is given, the tip is used.
+    If no revision is given, the parent of the working directory is used.
 
     To facilitate version control, distribution, and merging of tags,
     they are stored as a file named ".hgtags" which is managed
@@ -2653,8 +2577,8 @@
     necessary.  The file '.hg/localtags' is used for local tags (not
     shared among repositories).
     """
-    if name == "tip":
-        raise util.Abort(_("the name 'tip' is reserved"))
+    if name in ['tip', '.']:
+        raise util.Abort(_("the name '%s' is reserved") % name)
     if rev_ is not None:
         ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
                   "please use 'hg tag [-r REV] NAME' instead\n"))
@@ -2665,7 +2589,12 @@
     if rev_:
         r = hex(repo.lookup(rev_))
     else:
-        r = hex(repo.changelog.tip())
+        p1, p2 = repo.dirstate.parents()
+        if p1 == nullid:
+            raise util.Abort(_('no revision to tag'))
+        if p2 != nullid:
+            raise util.Abort(_('outstanding uncommitted merges'))
+        r = hex(p1)
 
     repo.tag(name, r, opts['local'], opts['message'], opts['user'],
              opts['date'])
@@ -2701,7 +2630,7 @@
         br = repo.branchlookup([n])
     show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
     if opts['patch']:
-        dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
+        patch.diff(repo, repo.changelog.parents(n)[0], n)
 
 def unbundle(ui, repo, fname, **opts):
     """apply a changegroup file
@@ -2730,7 +2659,8 @@
         raise util.Abort(_("%s: unknown bundle compression type")
                          % fname)
     gen = generator(util.filechunkiter(f, 4096))
-    modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle')
+    modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
+                                   'bundle:' + fname)
     return postincoming(ui, repo, modheads, opts['update'])
 
 def undo(ui, repo):
@@ -2745,7 +2675,7 @@
     repo.rollback()
 
 def update(ui, repo, node=None, merge=False, clean=False, force=None,
-           branch=None, **opts):
+           branch=None):
     """update or merge working directory
 
     Update the working directory to the specified revision.
@@ -2760,13 +2690,17 @@
     By default, update will refuse to run if doing so would require
     merging or discarding local changes.
     """
+    node = _lookup(repo, node, branch)
     if merge:
         ui.warn(_('(the -m/--merge option is deprecated; '
                   'use the merge command instead)\n'))
-    return doupdate(ui, repo, node, merge, clean, force, branch, **opts)
-
-def doupdate(ui, repo, node=None, merge=False, clean=False, force=None,
-             branch=None, **opts):
+        return hg.merge(repo, node, force=force)
+    elif clean:
+        return hg.clean(repo, node)
+    else:
+        return hg.update(repo, node)
+
+def _lookup(repo, node, branch=None):
     if branch:
         br = repo.branchlookup(branch=branch)
         found = []
@@ -2774,19 +2708,19 @@
             if branch in br[x]:
                 found.append(x)
         if len(found) > 1:
-            ui.warn(_("Found multiple heads for %s\n") % branch)
+            repo.ui.warn(_("Found multiple heads for %s\n") % branch)
             for x in found:
-                show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
-            return 1
+                show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
+            raise util.Abort("")
         if len(found) == 1:
             node = found[0]
-            ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
+            repo.ui.warn(_("Using head %s for branch %s\n")
+                         % (short(node), branch))
         else:
-            ui.warn(_("branch %s not found\n") % (branch))
-            return 1
+            raise util.Abort(_("branch %s not found\n") % (branch))
     else:
         node = node and repo.lookup(node) or repo.changelog.tip()
-    return repo.update(node, allow=merge, force=clean, forcemerge=force)
+    return node
 
 def verify(ui, repo):
     """verify the integrity of the repository
@@ -2798,7 +2732,7 @@
     the changelog, manifest, and tracked files, as well as the
     integrity of their crosslinks and indices.
     """
-    return repo.verify()
+    return hg.verify(repo)
 
 # Command options and aliases are listed here, alphabetically
 
@@ -2919,6 +2853,7 @@
           ('a', 'text', None, _('treat all files as text')),
           ('p', 'show-function', None,
            _('show which function each change is in')),
+          ('g', 'git', None, _('use git extended diff format')),
           ('w', 'ignore-all-space', None,
            _('ignore white space when comparing lines')),
           ('b', 'ignore-space-change', None,
@@ -2943,6 +2878,8 @@
         (grep,
          [('0', 'print0', None, _('end fields with NUL')),
           ('', 'all', None, _('print all revisions that match')),
+          ('f', 'follow', None,
+           _('follow changeset history, or file history across copies and renames')),
           ('i', 'ignore-case', None, _('ignore case when matching')),
           ('l', 'files-with-matches', None,
            _('print only filenames and revs that match')),
@@ -2979,7 +2916,7 @@
           ('n', 'newest-first', None, _('show newest record first')),
           ('', 'bundle', '', _('file to store the bundles into')),
           ('p', 'patch', None, _('show patch')),
-          ('r', 'rev', [], _('a specific revision you would like to pull')),
+          ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
           ('', 'template', '', _('display with template')),
           ('e', 'ssh', '', _('specify ssh command to use')),
           ('', 'remotecmd', '',
@@ -3005,6 +2942,10 @@
     "^log|history":
         (log,
          [('b', 'branches', None, _('show branches')),
+          ('f', 'follow', None,
+           _('follow changeset history, or file history across copies and renames')),
+          ('', 'follow-first', None,
+           _('only follow the first parent of merge changesets')),
           ('k', 'keyword', [], _('search for a keyword')),
           ('l', 'limit', '', _('limit number of changes displayed')),
           ('r', 'rev', [], _('show the specified revision or range')),
@@ -3012,6 +2953,7 @@
           ('', 'style', '', _('display using template map file')),
           ('m', 'only-merges', None, _('show only merges')),
           ('p', 'patch', None, _('show patch')),
+          ('P', 'prune', [], _('do not display revision or any of its ancestors')),
           ('', 'template', '', _('display with template')),
           ('I', 'include', [], _('include names matching the given patterns')),
           ('X', 'exclude', [], _('exclude names matching the given patterns'))],
@@ -3038,9 +2980,10 @@
     "^parents":
         (parents,
          [('b', 'branches', None, _('show branches')),
+          ('r', 'rev', '', _('show parents from the specified rev')),
           ('', 'style', '', _('display using template map file')),
           ('', 'template', '', _('display with template'))],
-         _('hg parents [-b] [REV]')),
+         _('hg parents [-b] [-r REV] [FILE]')),
     "paths": (paths, [], _('hg paths [NAME]')),
     "^pull":
         (pull,
@@ -3049,7 +2992,7 @@
           ('e', 'ssh', '', _('specify ssh command to use')),
           ('f', 'force', None,
            _('run even when remote repository is unrelated')),
-          ('r', 'rev', [], _('a specific revision you would like to pull')),
+          ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
           ('', 'remotecmd', '',
            _('specify hg command to run on the remote side'))],
          _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
@@ -3117,10 +3060,12 @@
          _('hg serve [OPTION]...')),
     "^status|st":
         (status,
-         [('m', 'modified', None, _('show only modified files')),
+         [('A', 'all', None, _('show status of all files')),
+          ('m', 'modified', None, _('show only modified files')),
           ('a', 'added', None, _('show only added files')),
           ('r', 'removed', None, _('show only removed files')),
           ('d', 'deleted', None, _('show only deleted (but tracked) files')),
+          ('c', 'clean', None, _('show only files without changes')),
           ('u', 'unknown', None, _('show only unknown (not tracked) files')),
           ('i', 'ignored', None, _('show ignored files')),
           ('n', 'no-status', None, _('hide status prefix')),
@@ -3286,24 +3231,16 @@
     try:
         return sys.modules[external[name]]
     except KeyError:
-        dotname = '.' + name
         for k, v in external.iteritems():
-            if k.endswith('.' + name) or v == name:
+            if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
                 return sys.modules[v]
         raise KeyError(name)
 
-def dispatch(args):
-    for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
-        num = getattr(signal, name, None)
-        if num: signal.signal(num, catchterm)
-
-    try:
-        u = ui.ui(traceback='--traceback' in sys.argv[1:])
-    except util.Abort, inst:
-        sys.stderr.write(_("abort: %s\n") % inst)
-        return -1
-
-    for ext_name, load_from_name in u.extensions():
+def load_extensions(ui):
+    added = []
+    for ext_name, load_from_name in ui.extensions():
+        if ext_name in external:
+            continue
         try:
             if load_from_name:
                 # the module will be loaded in sys.modules
@@ -3323,23 +3260,36 @@
                 except ImportError:
                     mod = importh(ext_name)
             external[ext_name] = mod.__name__
+            added.append((mod, ext_name))
         except (util.SignalInterrupt, KeyboardInterrupt):
             raise
         except Exception, inst:
-            u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst))
-            if u.print_exc():
+            ui.warn(_("*** failed to import extension %s: %s\n") %
+                    (ext_name, inst))
+            if ui.print_exc():
                 return 1
 
-    for name in external.itervalues():
-        mod = sys.modules[name]
+    for mod, name in added:
         uisetup = getattr(mod, 'uisetup', None)
         if uisetup:
-            uisetup(u)
+            uisetup(ui)
         cmdtable = getattr(mod, 'cmdtable', {})
         for t in cmdtable:
             if t in table:
-                u.warn(_("module %s overrides %s\n") % (name, t))
+                ui.warn(_("module %s overrides %s\n") % (name, t))
         table.update(cmdtable)
+    
+def dispatch(args):
+    for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
+        num = getattr(signal, name, None)
+        if num: signal.signal(num, catchterm)
+
+    try:
+        u = ui.ui(traceback='--traceback' in sys.argv[1:],
+                  readhooks=[load_extensions])
+    except util.Abort, inst:
+        sys.stderr.write(_("abort: %s\n") % inst)
+        return -1
 
     try:
         cmd, func, args, options, cmdoptions = parse(u, args)
@@ -3391,6 +3341,7 @@
                         mod = sys.modules[name]
                         if hasattr(mod, 'reposetup'):
                             mod.reposetup(u, repo)
+                            hg.repo_setup_hooks.append(mod.reposetup)
                 except hg.RepoError:
                     if cmd not in optionalrepo.split():
                         raise
@@ -3398,6 +3349,11 @@
             else:
                 d = lambda: func(u, *args, **cmdoptions)
 
+            # reupdate the options, repo/.hg/hgrc may have changed them
+            u.updateopts(options["verbose"], options["debug"], options["quiet"],
+                         not options["noninteractive"], options["traceback"],
+                         options["config"])
+
             try:
                 if options['profile']:
                     import hotshot, hotshot.stats
--- a/mercurial/context.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/context.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 # context.py - changeset and file context objects for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -11,9 +11,8 @@
     def __init__(self, repo, changeid):
         """changeid is a revision number, node, or tag"""
         self._repo = repo
-        self._id = changeid
 
-        self._node = self._repo.lookup(self._id)
+        self._node = self._repo.lookup(changeid)
         self._rev = self._repo.changelog.rev(self._node)
 
     def changeset(self):
@@ -74,39 +73,40 @@
            fileid can be a file revision or node."""
         self._repo = repo
         self._path = path
-        self._id = changeid
-        self._fileid = fileid
 
-        if self._id:
+        assert changeid or fileid
+
+        if not fileid:
             # if given a changeset id, go ahead and look up the file
-            self._changeset = self._repo.changelog.read(self._id)
-            node, flag = self._repo.manifest.find(self._changeset[0], path)
+            self._changeid = changeid
+            self._changectx = self.changectx()
             self._filelog = self._repo.file(self._path)
-            self._filenode = node
-        elif self._fileid:
+            self._filenode = self._changectx.filenode(self._path)
+        else:
             # else be lazy
             self._filelog = self._repo.file(self._path)
-            self._filenode = self._filelog.lookup(self._fileid)
+            self._filenode = self._filelog.lookup(fileid)
+            self._changeid = self._filelog.linkrev(self._filenode)
         self._filerev = self._filelog.rev(self._filenode)
 
-    def changeset(self):
+    def changectx(self):
         try:
-            return self._changeset
+            return self._changectx
         except AttributeError:
-            self._changeset = self._repo.changelog.read(self.node())
-            return self._changeset
+            self._changectx = changectx(self._repo, self._changeid)
+            return self._changectx
 
     def filerev(self): return self._filerev
     def filenode(self): return self._filenode
     def filelog(self): return self._filelog
 
-    def rev(self): return self.changeset().rev()
-    def node(self): return self.changeset().node()
-    def user(self): return self.changeset().user()
-    def date(self): return self.changeset().date()
-    def files(self): return self.changeset().files()
-    def description(self): return self.changeset().description()
-    def manifest(self): return self.changeset().manifest()
+    def rev(self): return self.changectx().rev()
+    def node(self): return self.changectx().node()
+    def user(self): return self.changectx().user()
+    def date(self): return self.changectx().date()
+    def files(self): return self.changectx().files()
+    def description(self): return self.changectx().description()
+    def manifest(self): return self.changectx().manifest()
 
     def data(self): return self._filelog.read(self._filenode)
     def metadata(self): return self._filelog.readmeta(self._filenode)
--- a/mercurial/demandload.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/demandload.py	Fri Aug 18 21:17:28 2006 -0700
@@ -96,6 +96,7 @@
 
     foo            import foo
     foo bar        import foo, bar
+    foo@bar        import foo as bar
     foo.bar        import foo.bar
     foo:bar        from foo import bar
     foo:bar,quux   from foo import bar, quux
@@ -108,6 +109,9 @@
             mod = mod[:col]
         else:
             fromlist = []
+        as = None
+        if '@' in mod:
+            mod, as = mod.split("@")
         importer = _importer(scope, mod, fromlist)
         if fromlist:
             for name in fromlist:
@@ -126,4 +130,6 @@
                     continue
             else:
                 basemod = mod
-            scope[basemod] = _replacer(importer, basemod)
+            if not as:
+                as = basemod
+            scope[as] = _replacer(importer, as)
--- a/mercurial/dirstate.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/dirstate.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,7 +1,7 @@
 """
 dirstate.py - working directory tracking for mercurial
 
-Copyright 2005 Matt Mackall <mpm@selenic.com>
+Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 
 This software may be used and distributed according to the terms
 of the GNU General Public License, incorporated herein by reference.
@@ -10,7 +10,7 @@
 from node import *
 from i18n import gettext as _
 from demandload import *
-demandload(globals(), "struct os time bisect stat util re errno")
+demandload(globals(), "struct os time bisect stat strutil util re errno")
 
 class dirstate(object):
     format = ">cllll"
@@ -22,6 +22,7 @@
         self.ui = ui
         self.map = None
         self.pl = None
+        self.dirs = None
         self.copies = {}
         self.ignorefunc = None
         self.blockignore = False
@@ -197,6 +198,38 @@
     def copied(self, file):
         return self.copies.get(file, None)
 
+    def initdirs(self):
+        if self.dirs is None:
+            self.dirs = {}
+            for f in self.map:
+                self.updatedirs(f, 1)
+        
+    def updatedirs(self, path, delta):
+        if self.dirs is not None:
+            for c in strutil.findall(path, '/'):
+                pc = path[:c]
+                self.dirs.setdefault(pc, 0)
+                self.dirs[pc] += delta
+
+    def checkshadows(self, files):
+        def prefixes(f):
+            for c in strutil.rfindall(f, '/'):
+                yield f[:c]
+        self.lazyread()
+        self.initdirs()
+        seendirs = {}
+        for f in files:
+            if self.dirs.get(f):
+                raise util.Abort(_('directory named %r already in dirstate') %
+                                 f)
+            for d in prefixes(f):
+                if d in seendirs:
+                    break
+                if d in self.map:
+                    raise util.Abort(_('file named %r already in dirstate') %
+                                     d)
+                seendirs[d] = True
+
     def update(self, files, state, **kw):
         ''' current states:
         n  normal
@@ -207,10 +240,16 @@
         if not files: return
         self.lazyread()
         self.markdirty()
+        if state == "a":
+            self.initdirs()
+            self.checkshadows(files)
         for f in files:
             if state == "r":
                 self.map[f] = ('r', 0, 0, 0)
+                self.updatedirs(f, -1)
             else:
+                if state == "a":
+                    self.updatedirs(f, 1)
                 s = os.lstat(self.wjoin(f))
                 st_size = kw.get('st_size', s.st_size)
                 st_mtime = kw.get('st_mtime', s.st_mtime)
@@ -222,9 +261,11 @@
         if not files: return
         self.lazyread()
         self.markdirty()
+        self.initdirs()
         for f in files:
             try:
                 del self.map[f]
+                self.updatedirs(f, -1)
             except KeyError:
                 self.ui.warn(_("not in dirstate: %s!\n") % f)
                 pass
@@ -232,14 +273,15 @@
     def clear(self):
         self.map = {}
         self.copies = {}
+        self.dirs = None
         self.markdirty()
 
     def rebuild(self, parent, files):
         self.clear()
         umask = os.umask(0)
         os.umask(umask)
-        for f, mode in files:
-            if mode:
+        for f in files:
+            if files.execf(f):
                 self.map[f] = ('n', ~umask, -1, 0)
             else:
                 self.map[f] = ('n', ~umask & 0666, -1, 0)
@@ -344,6 +386,10 @@
     # directly by this function, but might be modified by your statmatch call.
     #
     def walkhelper(self, files, statmatch, dc, badmatch=None):
+        # self.root may end with a path separator when self.root == '/'
+        common_prefix_len = len(self.root)
+        if not self.root.endswith('/'):
+            common_prefix_len += 1
         # recursion free walker, faster than os.walk.
         def findfiles(s):
             work = [s]
@@ -352,7 +398,7 @@
                 names = os.listdir(top)
                 names.sort()
                 # nd is the top of the repository dir tree
-                nd = util.normpath(top[len(self.root) + 1:])
+                nd = util.normpath(top[common_prefix_len:])
                 if nd == '.':
                     nd = ''
                 else:
@@ -434,15 +480,16 @@
             if not seen(k) and (statmatch(k, None)):
                 yield 'm', k, None
 
-    def changes(self, files=None, match=util.always, show_ignored=None):
+    def status(self, files=None, match=util.always, list_ignored=False,
+               list_clean=False):
         lookup, modified, added, unknown, ignored = [], [], [], [], []
-        removed, deleted = [], []
+        removed, deleted, clean = [], [], []
 
-        for src, fn, st in self.statwalk(files, match, ignored=show_ignored):
+        for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
             try:
                 type_, mode, size, time = self[fn]
             except KeyError:
-                if show_ignored and self.ignore(fn):
+                if list_ignored and self.ignore(fn):
                     ignored.append(fn)
                 else:
                     unknown.append(fn)
@@ -473,6 +520,8 @@
                     modified.append(fn)
                 elif time != st.st_mtime:
                     lookup.append(fn)
+                elif list_clean:
+                    clean.append(fn)
             elif type_ == 'm':
                 modified.append(fn)
             elif type_ == 'a':
@@ -480,4 +529,5 @@
             elif type_ == 'r':
                 removed.append(fn)
 
-        return (lookup, modified, added, removed, deleted, unknown, ignored)
+        return (lookup, modified, added, removed, deleted, unknown, ignored,
+                clean)
--- a/mercurial/filelog.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/filelog.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 # filelog.py - file history class for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -65,6 +65,26 @@
             return (m["copy"], bin(m["copyrev"]))
         return False
 
+    def size(self, rev):
+        """return the size of a given revision"""
+
+        # for revisions with renames, we have to go the slow way
+        node = self.node(rev)
+        if self.renamed(node):
+            return len(self.read(node))
+
+        return revlog.size(self, rev)
+
+    def cmp(self, node, text):
+        """compare text with a given file revision"""
+
+        # for renames, we have to go the slow way
+        if self.renamed(node):
+            t2 = self.read(node)
+            return t2 != text
+
+        return revlog.cmp(self, node, text)
+
     def annotate(self, node):
 
         def decorate(text, rev):
@@ -76,31 +96,59 @@
             return child
 
         # find all ancestors
-        needed = {node:1}
-        visit = [node]
+        needed = {(self, node):1}
+        files = [self]
+        visit = [(self, node)]
         while visit:
-            n = visit.pop(0)
-            for p in self.parents(n):
-                if p not in needed:
-                    needed[p] = 1
-                    visit.append(p)
+            f, n = visit.pop(0)
+            rn = f.renamed(n)
+            if rn:
+                f, n = rn
+                f = filelog(self.opener, f, self.defversion)
+                files.insert(0, f)
+                if (f, n) not in needed:
+                    needed[(f, n)] = 1
+                else:
+                    needed[(f, n)] += 1
+            for p in f.parents(n):
+                if p == nullid:
+                    continue
+                if (f, p) not in needed:
+                    needed[(f, p)] = 1
+                    visit.append((f, p))
                 else:
                     # count how many times we'll use this
-                    needed[p] += 1
+                    needed[(f, p)] += 1
 
-        # sort by revision which is a topological order
-        visit = [ (self.rev(n), n) for n in needed.keys() ]
-        visit.sort()
+        # sort by revision (per file) which is a topological order
+        visit = []
+        for f in files:
+            fn = [(f.rev(n[1]), f, n[1]) for n in needed.keys() if n[0] == f]
+            fn.sort()
+            visit.extend(fn)
         hist = {}
 
-        for r,n in visit:
-            curr = decorate(self.read(n), self.linkrev(n))
-            for p in self.parents(n):
+        for i in range(len(visit)):
+            r, f, n = visit[i]
+            curr = decorate(f.read(n), f.linkrev(n))
+            if r == -1:
+                continue
+            parents = f.parents(n)
+            # follow parents across renames
+            if r < 1 and i > 0:
+                j = i
+                while j > 0 and visit[j][1] == f:
+                    j -= 1
+                parents = (visit[j][2],)
+                f = visit[j][1]
+            else:
+                parents = f.parents(n)
+            for p in parents:
                 if p != nullid:
                     curr = pair(hist[p], curr)
                     # trim the history of unneeded revs
-                    needed[p] -= 1
-                    if not needed[p]:
+                    needed[(f, p)] -= 1
+                    if not needed[(f, p)]:
                         del hist[p]
             hist[n] = curr
 
--- a/mercurial/hg.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/hg.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,7 @@
 # hg.py - repository classes for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
+# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -10,69 +11,56 @@
 from demandload import *
 from i18n import gettext as _
 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
-demandload(globals(), "errno lock os shutil util")
-
-def bundle(ui, path):
-    if path.startswith('bundle://'):
-        path = path[9:]
-    else:
-        path = path[7:]
-    s = path.split("+", 1)
-    if len(s) == 1:
-        repopath, bundlename = "", s[0]
-    else:
-        repopath, bundlename = s
-    return bundlerepo.bundlerepository(ui, repopath, bundlename)
-
-def hg(ui, path):
-    ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
-    return httprepo.httprepository(ui, path.replace("hg://", "http://"))
+demandload(globals(), "errno lock os shutil util merge@_merge verify@_verify")
 
-def local_(ui, path, create=0):
-    if path.startswith('file:'):
-        path = path[5:]
-    return localrepo.localrepository(ui, path, create)
-
-def ssh_(ui, path, create=0):
-    return sshrepo.sshrepository(ui, path, create)
-
-def old_http(ui, path):
-    ui.warn(_("old-http:// syntax is deprecated, "
-              "please use static-http:// instead\n"))
-    return statichttprepo.statichttprepository(
-        ui, path.replace("old-http://", "http://"))
-
-def static_http(ui, path):
-    return statichttprepo.statichttprepository(
-        ui, path.replace("static-http://", "http://"))
+def _local(path):
+    return (os.path.isfile(path and util.drop_scheme('file', path)) and
+            bundlerepo or localrepo)
 
 schemes = {
-    'bundle': bundle,
-    'file': local_,
-    'hg': hg,
-    'http': lambda ui, path: httprepo.httprepository(ui, path),
-    'https': lambda ui, path: httprepo.httpsrepository(ui, path),
-    'old-http': old_http,
-    'ssh': ssh_,
-    'static-http': static_http,
+    'bundle': bundlerepo,
+    'file': _local,
+    'hg': httprepo,
+    'http': httprepo,
+    'https': httprepo,
+    'old-http': statichttprepo,
+    'ssh': sshrepo,
+    'static-http': statichttprepo,
     }
 
-def repository(ui, path=None, create=0):
-    scheme = None
+def _lookup(path):
+    scheme = 'file'
     if path:
         c = path.find(':')
         if c > 0:
-            scheme = schemes.get(path[:c])
-    else:
-        path = ''
-    ctor = scheme or schemes['file']
-    if create:
+            scheme = path[:c]
+    thing = schemes.get(scheme) or schemes['file']
+    try:
+        return thing(path)
+    except TypeError:
+        return thing
+
+def islocal(repo):
+    '''return true if repo or path is local'''
+    if isinstance(repo, str):
         try:
-            return ctor(ui, path, create)
-        except TypeError:
-            raise util.Abort(_('cannot create new repository over "%s" protocol') %
-                             scheme)
-    return ctor(ui, path)
+            return _lookup(repo).islocal(repo)
+        except AttributeError:
+            return False
+    return repo.local()
+
+repo_setup_hooks = []
+
+def repository(ui, path=None, create=False):
+    """return a repository object for the specified path"""
+    repo = _lookup(path).instance(ui, path, create)
+    for hook in repo_setup_hooks:
+        hook(ui, repo)
+    return repo
+
+def defaultdest(source):
+    '''return default destination of clone if none is given'''
+    return os.path.basename(os.path.normpath(source))
 
 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
           stream=False):
@@ -90,7 +78,9 @@
     If an exception is raised, the partly cloned/updated destination
     repository will be deleted.
 
-    Keyword arguments:
+    Arguments:
+
+    source: repository object or URL
 
     dest: URL of destination repository to create (defaults to base
     name of source repository)
@@ -105,8 +95,24 @@
     update: update working directory after clone completes, if
     destination is local repository
     """
+    if isinstance(source, str):
+        src_repo = repository(ui, source)
+    else:
+        src_repo = source
+        source = src_repo.url()
+
     if dest is None:
-        dest = os.path.basename(os.path.normpath(source))
+        dest = defaultdest(source)
+
+    def localpath(path):
+        if path.startswith('file://'):
+            return path[7:]
+        if path.startswith('file:'):
+            return path[5:]
+        return path
+
+    dest = localpath(dest)
+    source = localpath(source)
 
     if os.path.exists(dest):
         raise util.Abort(_("destination '%s' already exists"), dest)
@@ -121,8 +127,6 @@
             if self.dir_:
                 self.rmtree(self.dir_, True)
 
-    src_repo = repository(ui, source)
-
     dest_repo = None
     try:
         dest_repo = repository(ui, dest)
@@ -133,7 +137,7 @@
     dest_path = None
     dir_cleanup = None
     if dest_repo.local():
-        dest_path = os.path.realpath(dest)
+        dest_path = os.path.realpath(dest_repo.root)
         dir_cleanup = DirCleanup(dest_path)
 
     abspath = source
@@ -202,8 +206,31 @@
             dest_lock.release()
 
         if update:
-            dest_repo.update(dest_repo.changelog.tip())
+            _merge.update(dest_repo, dest_repo.changelog.tip())
     if dir_cleanup:
         dir_cleanup.close()
 
     return src_repo, dest_repo
+
+def update(repo, node):
+    """update the working directory to node, merging linear changes"""
+    return _merge.update(repo, node)
+
+def clean(repo, node, wlock=None, show_stats=True):
+    """forcibly switch the working directory to node, clobbering changes"""
+    return _merge.update(repo, node, force=True, wlock=wlock,
+                         show_stats=show_stats)
+
+def merge(repo, node, force=None, remind=True, wlock=None):
+    """branch merge with node, resolving changes"""
+    return _merge.update(repo, node, branchmerge=True, force=force,
+                         remind=remind, wlock=wlock)
+
+def revert(repo, node, choose, wlock):
+    """revert changes to revision in node without updating dirstate"""
+    return _merge.update(repo, node, force=True, partial=choose,
+                         show_stats=False, wlock=wlock)
+
+def verify(repo):
+    """verify the consistency of a repository"""
+    return _verify.verify(repo)
--- a/mercurial/hgweb/common.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/hgweb/common.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,7 +1,7 @@
 # hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod
 #
 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
--- a/mercurial/hgweb/hgweb_mod.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/hgweb/hgweb_mod.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,7 +1,7 @@
 # hgweb/hgweb_mod.py - Web interface for a repository.
 #
 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -11,7 +11,7 @@
 import mimetypes
 from mercurial.demandload import demandload
 demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
-demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone")
+demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone,patch")
 demandload(globals(), "mercurial:templater")
 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
 from mercurial.node import *
@@ -37,6 +37,7 @@
         self.mtime = -1
         self.reponame = name
         self.archives = 'zip', 'gz', 'bz2'
+        self.stripecount = 1
         self.templatepath = self.repo.ui.config("web", "templates",
                                                 templater.templatepath())
 
@@ -46,6 +47,8 @@
             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)
 
@@ -126,39 +129,29 @@
         date1 = util.datestr(change1[2])
         date2 = util.datestr(change2[2])
 
-        modified, added, removed, deleted, unknown = r.changes(node1, node2)
+        modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
         if files:
             modified, added, removed = map(lambda x: filterfiles(files, x),
                                            (modified, added, removed))
 
-        diffopts = self.repo.ui.diffopts()
-        showfunc = diffopts['showfunc']
-        ignorews = diffopts['ignorews']
-        ignorewsamount = diffopts['ignorewsamount']
-        ignoreblanklines = diffopts['ignoreblanklines']
+        diffopts = patch.diffopts(self.repo.ui)
         for f in modified:
             to = r.file(f).read(mmap1[f])
             tn = r.file(f).read(mmap2[f])
             yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
-                            showfunc=showfunc, ignorews=ignorews,
-                            ignorewsamount=ignorewsamount,
-                            ignoreblanklines=ignoreblanklines), f, tn)
+                                          opts=diffopts), f, tn)
         for f in added:
             to = None
             tn = r.file(f).read(mmap2[f])
             yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
-                            showfunc=showfunc, ignorews=ignorews,
-                            ignorewsamount=ignorewsamount,
-                            ignoreblanklines=ignoreblanklines), f, tn)
+                                          opts=diffopts), f, tn)
         for f in removed:
             to = r.file(f).read(mmap1[f])
             tn = None
             yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
-                            showfunc=showfunc, ignorews=ignorews,
-                            ignorewsamount=ignorewsamount,
-                            ignoreblanklines=ignoreblanklines), f, tn)
+                                          opts=diffopts), f, tn)
 
-    def changelog(self, pos):
+    def changelog(self, pos, shortlog=False):
         def changenav(**map):
             def seq(factor, maxchanges=None):
                 if maxchanges:
@@ -173,8 +166,9 @@
 
             l = []
             last = 0
-            for f in seq(1, self.maxchanges):
-                if f < self.maxchanges or f <= last:
+            maxchanges = shortlog and self.maxshortchanges or self.maxchanges
+            for f in seq(1, maxchanges):
+                if f < maxchanges or f <= last:
                     continue
                 if f > count:
                     break
@@ -219,14 +213,15 @@
             for e in l:
                 yield e
 
+        maxchanges = shortlog and self.maxshortchanges or self.maxchanges
         cl = self.repo.changelog
         mf = cl.read(cl.tip())[0]
         count = cl.count()
-        start = max(0, pos - self.maxchanges + 1)
-        end = min(count, start + self.maxchanges)
+        start = max(0, pos - maxchanges + 1)
+        end = min(count, start + maxchanges)
         pos = end - 1
 
-        yield self.t('changelog',
+        yield self.t(shortlog and 'shortlog' or 'changelog',
                      changenav=changenav,
                      manifest=hex(mf),
                      rev=pos, changesets=count, entries=changelist,
@@ -265,7 +260,7 @@
                 hn = hex(n)
 
                 yield self.t('searchentry',
-                             parity=count & 1,
+                             parity=self.stripes(count),
                              author=changes[1],
                              parent=self.siblings(cl.parents(n), cl.rev),
                              child=self.siblings(cl.children(n), cl.rev),
@@ -376,7 +371,7 @@
             for l, t in enumerate(text.splitlines(1)):
                 yield {"line": t,
                        "linenumber": "% 6d" % (l + 1),
-                       "parity": l & 1}
+                       "parity": self.stripes(l)}
 
         yield self.t("filerevision",
                      file=f,
@@ -393,7 +388,7 @@
                      parent=self.siblings(fl.parents(n), fl.rev, file=f),
                      child=self.siblings(fl.children(n), fl.rev, file=f),
                      rename=self.renamelink(fl, n),
-                     permissions=self.repo.manifest.readflags(mfn)[f])
+                     permissions=self.repo.manifest.read(mfn).execf(f))
 
     def fileannotate(self, f, node):
         bcache = {}
@@ -409,7 +404,7 @@
         mfn = cs[0]
 
         def annotate(**map):
-            parity = 1
+            parity = 0
             last = None
             for r, l in fl.annotate(n):
                 try:
@@ -447,7 +442,7 @@
                      rename=self.renamelink(fl, n),
                      parent=self.siblings(fl.parents(n), fl.rev, file=f),
                      child=self.siblings(fl.children(n), fl.rev, file=f),
-                     permissions=self.repo.manifest.readflags(mfn)[f])
+                     permissions=self.repo.manifest.read(mfn).execf(f))
 
     def manifest(self, mnode, path):
         man = self.repo.manifest
@@ -457,7 +452,6 @@
         rev = man.rev(mn)
         changerev = man.linkrev(mn)
         node = self.repo.changelog.node(changerev)
-        mff = man.readflags(mn)
 
         files = {}
 
@@ -489,10 +483,10 @@
                 yield {"file": full,
                        "manifest": mnode,
                        "filenode": hex(fnode),
-                       "parity": parity,
+                       "parity": self.stripes(parity),
                        "basename": f,
-                       "permissions": mff[full]}
-                parity = 1 - parity
+                       "permissions": mf.execf(full)}
+                parity += 1
 
         def dirlist(**map):
             parity = 0
@@ -503,11 +497,11 @@
                 if fnode:
                     continue
 
-                yield {"parity": parity,
+                yield {"parity": self.stripes(parity),
                        "path": os.path.join(path, f),
                        "manifest": mnode,
                        "basename": f[:-1]}
-                parity = 1 - parity
+                parity += 1
 
         yield self.t("manifest",
                      manifest=mnode,
@@ -530,12 +524,12 @@
             parity = 0
             for k,n in i:
                 if notip and k == "tip": continue
-                yield {"parity": parity,
+                yield {"parity": self.stripes(parity),
                        "tag": k,
                        "tagmanifest": hex(cl.read(n)[0]),
                        "date": cl.read(n)[2],
                        "node": hex(n)}
-                parity = 1 - parity
+                parity += 1
 
         yield self.t("tags",
                      manifest=hex(mf),
@@ -565,12 +559,12 @@
                 t = c[2]
 
                 yield self.t("tagentry",
-                             parity = parity,
+                             parity = self.stripes(parity),
                              tag = k,
                              node = hex(n),
                              date = t,
                              tagmanifest = hex(m))
-                parity = 1 - parity
+                parity += 1
 
         def changelist(**map):
             parity = 0
@@ -609,7 +603,8 @@
                  lastchange = (0, 0), # FIXME
                  manifest = hex(mf),
                  tags = tagentries,
-                 shortlog = changelist)
+                 shortlog = changelist,
+                 archives=self.archivelist("tip"))
 
     def filediff(self, file, changeset):
         cl = self.repo.changelog
@@ -689,6 +684,7 @@
         def expand_form(form):
             shortcuts = {
                 'cl': [('cmd', ['changelog']), ('rev', None)],
+                'sl': [('cmd', ['shortlog']), ('rev', None)],
                 'cs': [('cmd', ['changeset']), ('node', None)],
                 'f': [('cmd', ['file']), ('filenode', None)],
                 'fl': [('cmd', ['filelog']), ('filenode', None)],
@@ -752,6 +748,13 @@
         else:
             req.write(self.t("error"))
 
+    def stripes(self, parity):
+        "make horizontal stripes for easier reading"
+        if self.stripecount:
+            return (1 + parity / self.stripecount) & 1
+        else:
+            return 0
+
     def do_changelog(self, req):
         hi = self.repo.changelog.count() - 1
         if req.form.has_key('rev'):
@@ -764,6 +767,18 @@
 
         req.write(self.changelog(hi))
 
+    def do_shortlog(self, req):
+        hi = self.repo.changelog.count() - 1
+        if req.form.has_key('rev'):
+            hi = req.form['rev'][0]
+            try:
+                hi = self.repo.changelog.rev(self.repo.lookup(hi))
+            except hg.RepoError:
+                req.write(self.search(hi)) # XXX redirect to 404 page?
+                return
+
+        req.write(self.changelog(hi, shortlog = True))
+
     def do_changeset(self, req):
         req.write(self.changeset(req.form['node'][0]))
 
@@ -895,9 +910,13 @@
         # require ssl by default, auth info cannot be sniffed and
         # replayed
         ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
-        if ssl_req and not req.env.get('HTTPS'):
-            bail(_('ssl required\n'))
-            return
+        if ssl_req:
+            if not req.env.get('HTTPS'):
+                bail(_('ssl required\n'))
+                return
+            proto = 'https'
+        else:
+            proto = 'http'
 
         # do not allow push unless explicitly allowed
         if not self.check_perm(req, 'push', False):
@@ -943,7 +962,9 @@
                 sys.stdout = cStringIO.StringIO()
 
                 try:
-                    ret = self.repo.addchangegroup(fp, 'serve')
+                    url = 'remote:%s:%s' % (proto,
+                                            req.env.get('REMOTE_HOST', ''))
+                    ret = self.repo.addchangegroup(fp, 'serve', url)
                 finally:
                     val = sys.stdout.getvalue()
                     sys.stdout = old_stdout
--- a/mercurial/hgweb/hgwebdir_mod.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/hgweb/hgwebdir_mod.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,7 +1,7 @@
 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
 #
 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
--- a/mercurial/hgweb/request.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/hgweb/request.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,7 +1,7 @@
 # hgweb/request.py - An http request from either CGI or the standalone server.
 #
 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
--- a/mercurial/hgweb/server.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/hgweb/server.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,7 +1,7 @@
 # hgweb/server.py - The standalone hg web server.
 #
 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -198,6 +198,7 @@
             self.webdirmaker = hgwebdir
             self.repoviewmaker = hgweb
             self.reqmaker = wsgiapplication(self.make_handler)
+            self.daemon_threads = True
 
         def make_handler(self):
             if self.webdir_conf:
--- a/mercurial/httprangereader.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/httprangereader.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 # httprangereader.py - just what it says
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
--- a/mercurial/httprepo.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/httprepo.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,7 @@
 # httprepo.py - HTTP repository proxy classes for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
+# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -115,6 +116,7 @@
 
 class httprepository(remoterepository):
     def __init__(self, ui, path):
+        self.path = path
         self.caps = None
         scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
         if query or frag:
@@ -124,8 +126,8 @@
         host, port, user, passwd = netlocsplit(netloc)
 
         # urllib cannot handle URLs with embedded user or passwd
-        self.url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
-                                        urlpath, '', ''))
+        self._url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
+                                         urlpath, '', ''))
         self.ui = ui
 
         proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
@@ -189,6 +191,9 @@
         opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
         urllib2.install_opener(opener)
 
+    def url(self):
+        return self.path
+
     # look up capabilities only when needed
 
     def get_caps(self):
@@ -213,7 +218,7 @@
         q = {"cmd": cmd}
         q.update(args)
         qs = urllib.urlencode(q)
-        cu = "%s?%s" % (self.url, qs)
+        cu = "%s?%s" % (self._url, qs)
         try:
             resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
         except urllib2.HTTPError, inst:
@@ -234,13 +239,13 @@
                not proto.startswith('text/plain') and \
                not proto.startswith('application/hg-changegroup'):
             raise hg.RepoError(_("'%s' does not appear to be an hg repository") %
-                               self.url)
+                               self._url)
 
         if proto.startswith('application/mercurial'):
             version = proto[22:]
             if float(version) > 0.1:
                 raise hg.RepoError(_("'%s' uses newer protocol %s") %
-                                   (self.url, version))
+                                   (self._url, version))
 
         return resp
 
@@ -335,3 +340,13 @@
             raise util.Abort(_('Python support for SSL and HTTPS '
                                'is not installed'))
         httprepository.__init__(self, ui, path)
+
+def instance(ui, path, create):
+    if create:
+        raise util.Abort(_('cannot create new http repository'))
+    if path.startswith('hg:'):
+        ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
+        path = 'http:' + path[3:]
+    if path.startswith('https:'):
+        return httpsrepository(ui, path)
+    return httprepository(ui, path)
--- a/mercurial/i18n.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/i18n.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,7 +1,7 @@
 """
 i18n.py - internationalization support for mercurial
 
-Copyright 2005 Matt Mackall <mpm@selenic.com>
+Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 
 This software may be used and distributed according to the terms
 of the GNU General Public License, incorporated herein by reference.
--- a/mercurial/localrepo.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/localrepo.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 # localrepo.py - read/write repository class for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -83,6 +83,9 @@
 
         self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
 
+    def url(self):
+        return 'file:' + self.root
+
     def hook(self, name, throw=False, **args):
         def callhook(hname, funcname):
             '''call python hook. hook is callable object, looked up as
@@ -195,7 +198,7 @@
             self.hook('tag', node=node, tag=name, local=local)
             return
 
-        for x in self.changes():
+        for x in self.status()[:5]:
             if '.hgtags' in x:
                 raise util.Abort(_('working copy of .hgtags is changed '
                                    '(please commit .hgtags manually)'))
@@ -289,6 +292,10 @@
         try:
             return self.tags()[key]
         except KeyError:
+            if key == '.':
+                key = self.dirstate.parents()[0]
+                if key == nullid:
+                    raise repo.RepoError(_("no revision checked out"))
             try:
                 return self.changelog.lookup(key)
             except:
@@ -463,8 +470,7 @@
         p2 = p2 or self.dirstate.parents()[1] or nullid
         c1 = self.changelog.read(p1)
         c2 = self.changelog.read(p2)
-        m1 = self.manifest.read(c1[0])
-        mf1 = self.manifest.readflags(c1[0])
+        m1 = self.manifest.read(c1[0]).copy()
         m2 = self.manifest.read(c2[0])
         changed = []
 
@@ -477,36 +483,32 @@
             wlock = self.wlock()
         l = self.lock()
         tr = self.transaction()
-        mm = m1.copy()
-        mfm = mf1.copy()
         linkrev = self.changelog.count()
         for f in files:
             try:
                 t = self.wread(f)
-                tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
+                m1.set(f, util.is_exec(self.wjoin(f), m1.execf(f)))
                 r = self.file(f)
-                mfm[f] = tm
 
                 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
                 if entry:
-                    mm[f] = entry
+                    m1[f] = entry
                     continue
 
-                mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
+                m1[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
                 changed.append(f)
                 if update_dirstate:
                     self.dirstate.update([f], "n")
             except IOError:
                 try:
-                    del mm[f]
-                    del mfm[f]
+                    del m1[f]
                     if update_dirstate:
                         self.dirstate.forget([f])
                 except:
                     # deleted from p2?
                     pass
 
-        mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
+        mnode = self.manifest.add(m1, tr, linkrev, c1[0], c2[0])
         user = user or self.ui.username()
         n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
         tr.close()
@@ -530,15 +532,14 @@
                 else:
                     self.ui.warn(_("%s not tracked!\n") % f)
         else:
-            modified, added, removed, deleted, unknown = self.changes(match=match)
+            modified, added, removed, deleted, unknown = self.status(match=match)[:5]
             commit = modified + added
             remove = removed
 
         p1, p2 = self.dirstate.parents()
         c1 = self.changelog.read(p1)
         c2 = self.changelog.read(p2)
-        m1 = self.manifest.read(c1[0])
-        mf1 = self.manifest.readflags(c1[0])
+        m1 = self.manifest.read(c1[0]).copy()
         m2 = self.manifest.read(c2[0])
 
         if not commit and not remove and not force and p2 == nullid:
@@ -564,7 +565,7 @@
         for f in commit:
             self.ui.note(f + "\n")
             try:
-                mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
+                m1.set(f, util.is_exec(self.wjoin(f), m1.execf(f)))
                 t = self.wread(f)
             except IOError:
                 self.ui.warn(_("trouble committing %s!\n") % f)
@@ -591,12 +592,11 @@
             changed.append(f)
 
         # update manifest
-        m1 = m1.copy()
         m1.update(new)
         for f in remove:
             if f in m1:
                 del m1[f]
-        mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
+        mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0],
                                (new, remove))
 
         # add changeset
@@ -658,9 +658,9 @@
             for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
                 yield src, fn
 
-    def changes(self, node1=None, node2=None, files=[], match=util.always,
-                wlock=None, show_ignored=None):
-        """return changes between two nodes or node and working directory
+    def status(self, node1=None, node2=None, files=[], match=util.always,
+                wlock=None, list_ignored=False, list_clean=False):
+        """return status of files between two nodes or node and working directory
 
         If node1 is None, use the first dirstate parent instead.
         If node2 is None, compare node1 with working directory.
@@ -668,8 +668,7 @@
 
         def fcmp(fn, mf):
             t1 = self.wread(fn)
-            t2 = self.file(fn).read(mf.get(fn, nullid))
-            return cmp(t1, t2)
+            return self.file(fn).cmp(mf.get(fn, nullid), t1)
 
         def mfmatches(node):
             change = self.changelog.read(node)
@@ -679,7 +678,9 @@
                     del mf[fn]
             return mf
 
-        modified, added, removed, deleted, unknown, ignored = [],[],[],[],[],[]
+        modified, added, removed, deleted, unknown = [], [], [], [], []
+        ignored, clean = [], []
+
         compareworking = False
         if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
             compareworking = True
@@ -697,8 +698,9 @@
                     wlock = self.wlock(wait=0)
                 except lock.LockException:
                     wlock = None
-            lookup, modified, added, removed, deleted, unknown, ignored = (
-                self.dirstate.changes(files, match, show_ignored))
+            (lookup, modified, added, removed, deleted, unknown,
+             ignored, clean) = self.dirstate.status(files, match,
+                                                    list_ignored, list_clean)
 
             # are we comparing working dir against its parent?
             if compareworking:
@@ -721,12 +723,11 @@
                         del mf2[f]
         else:
             # we are comparing two revisions
-            deleted, unknown, ignored = [], [], []
             mf2 = mfmatches(node2)
 
         if not compareworking:
             # flush lists from dirstate before comparing manifests
-            modified, added = [], []
+            modified, added, clean = [], [], []
 
             # make sure to sort the files so we talk to the disk in a
             # reasonable order
@@ -736,6 +737,8 @@
                 if mf1.has_key(fn):
                     if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
                         modified.append(fn)
+                    elif list_clean:
+                        clean.append(fn)
                     del mf1[fn]
                 else:
                     added.append(fn)
@@ -743,12 +746,9 @@
             removed = mf1.keys()
 
         # sort and return results:
-        for l in modified, added, removed, deleted, unknown, ignored:
+        for l in modified, added, removed, deleted, unknown, ignored, clean:
             l.sort()
-        if show_ignored is None:
-            return (modified, added, removed, deleted, unknown)
-        else:
-            return (modified, added, removed, deleted, unknown, ignored)
+        return (modified, added, removed, deleted, unknown, ignored, clean)
 
     def add(self, list, wlock=None):
         if not wlock:
@@ -798,7 +798,6 @@
     def undelete(self, list, wlock=None):
         p = self.dirstate.parents()[0]
         mn = self.changelog.read(p)[0]
-        mf = self.manifest.readflags(mn)
         m = self.manifest.read(mn)
         if not wlock:
             wlock = self.wlock()
@@ -808,7 +807,7 @@
             else:
                 t = self.file(f).read(m[f])
                 self.wwrite(f, t)
-                util.set_exec(self.wjoin(f), mf[f])
+                util.set_exec(self.wjoin(f), m.execf(f))
                 self.dirstate.update([f], "n")
 
     def copy(self, source, dest, wlock=None):
@@ -1159,22 +1158,29 @@
         else:
             return subset
 
-    def pull(self, remote, heads=None, force=False):
-        l = self.lock()
+    def pull(self, remote, heads=None, force=False, lock=None):
+        mylock = False
+        if not lock:
+            lock = self.lock()
+            mylock = True
 
-        fetch = self.findincoming(remote, force=force)
-        if fetch == [nullid]:
-            self.ui.status(_("requesting all changes\n"))
+        try:
+            fetch = self.findincoming(remote, force=force)
+            if fetch == [nullid]:
+                self.ui.status(_("requesting all changes\n"))
 
-        if not fetch:
-            self.ui.status(_("no changes found\n"))
-            return 0
+            if not fetch:
+                self.ui.status(_("no changes found\n"))
+                return 0
 
-        if heads is None:
-            cg = remote.changegroup(fetch, 'pull')
-        else:
-            cg = remote.changegroupsubset(fetch, heads, 'pull')
-        return self.addchangegroup(cg, 'pull')
+            if heads is None:
+                cg = remote.changegroup(fetch, 'pull')
+            else:
+                cg = remote.changegroupsubset(fetch, heads, 'pull')
+            return self.addchangegroup(cg, 'pull', remote.url())
+        finally:
+            if mylock:
+                lock.release()
 
     def push(self, remote, force=False, revs=None):
         # there are two ways to push to remote repo:
@@ -1230,7 +1236,7 @@
         ret = self.prepush(remote, force, revs)
         if ret[0] is not None:
             cg, remote_heads = ret
-            return remote.addchangegroup(cg, 'push')
+            return remote.addchangegroup(cg, 'push', self.url())
         return ret[1]
 
     def push_unbundle(self, remote, force, revs):
@@ -1583,7 +1589,7 @@
 
         return util.chunkbuffer(gengroup())
 
-    def addchangegroup(self, source, srctype):
+    def addchangegroup(self, source, srctype, url):
         """add changegroup to repo.
         returns number of heads modified or added + 1."""
 
@@ -1597,7 +1603,7 @@
         if not source:
             return 0
 
-        self.hook('prechangegroup', throw=True, source=srctype)
+        self.hook('prechangegroup', throw=True, source=srctype, url=url)
 
         changesets = files = revisions = 0
 
@@ -1664,544 +1670,21 @@
 
         if changesets > 0:
             self.hook('pretxnchangegroup', throw=True,
-                      node=hex(self.changelog.node(cor+1)), source=srctype)
+                      node=hex(self.changelog.node(cor+1)), source=srctype,
+                      url=url)
 
         tr.close()
 
         if changesets > 0:
             self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
-                      source=srctype)
+                      source=srctype, url=url)
 
             for i in range(cor + 1, cnr + 1):
                 self.hook("incoming", node=hex(self.changelog.node(i)),
-                          source=srctype)
+                          source=srctype, url=url)
 
         return newheads - oldheads + 1
 
-    def update(self, node, allow=False, force=False, choose=None,
-               moddirstate=True, forcemerge=False, wlock=None, show_stats=True):
-        pl = self.dirstate.parents()
-        if not force and pl[1] != nullid:
-            raise util.Abort(_("outstanding uncommitted merges"))
-
-        err = False
-
-        p1, p2 = pl[0], node
-        pa = self.changelog.ancestor(p1, p2)
-        m1n = self.changelog.read(p1)[0]
-        m2n = self.changelog.read(p2)[0]
-        man = self.manifest.ancestor(m1n, m2n)
-        m1 = self.manifest.read(m1n)
-        mf1 = self.manifest.readflags(m1n)
-        m2 = self.manifest.read(m2n).copy()
-        mf2 = self.manifest.readflags(m2n)
-        ma = self.manifest.read(man)
-        mfa = self.manifest.readflags(man)
-
-        modified, added, removed, deleted, unknown = self.changes()
-
-        # is this a jump, or a merge?  i.e. is there a linear path
-        # from p1 to p2?
-        linear_path = (pa == p1 or pa == p2)
-
-        if allow and linear_path:
-            raise util.Abort(_("there is nothing to merge, just use "
-                               "'hg update' or look at 'hg heads'"))
-        if allow and not forcemerge:
-            if modified or added or removed:
-                raise util.Abort(_("outstanding uncommitted changes"))
-
-        if not forcemerge and not force:
-            for f in unknown:
-                if f in m2:
-                    t1 = self.wread(f)
-                    t2 = self.file(f).read(m2[f])
-                    if cmp(t1, t2) != 0:
-                        raise util.Abort(_("'%s' already exists in the working"
-                                           " dir and differs from remote") % f)
-
-        # resolve the manifest to determine which files
-        # we care about merging
-        self.ui.note(_("resolving manifests\n"))
-        self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
-                      (force, allow, moddirstate, linear_path))
-        self.ui.debug(_(" ancestor %s local %s remote %s\n") %
-                      (short(man), short(m1n), short(m2n)))
-
-        merge = {}
-        get = {}
-        remove = []
-
-        # construct a working dir manifest
-        mw = m1.copy()
-        mfw = mf1.copy()
-        umap = dict.fromkeys(unknown)
-
-        for f in added + modified + unknown:
-            mw[f] = ""
-            mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
-
-        if moddirstate and not wlock:
-            wlock = self.wlock()
-
-        for f in deleted + removed:
-            if f in mw:
-                del mw[f]
-
-            # If we're jumping between revisions (as opposed to merging),
-            # and if neither the working directory nor the target rev has
-            # the file, then we need to remove it from the dirstate, to
-            # prevent the dirstate from listing the file when it is no
-            # longer in the manifest.
-            if moddirstate and linear_path and f not in m2:
-                self.dirstate.forget((f,))
-
-        # Compare manifests
-        for f, n in mw.iteritems():
-            if choose and not choose(f):
-                continue
-            if f in m2:
-                s = 0
-
-                # is the wfile new since m1, and match m2?
-                if f not in m1:
-                    t1 = self.wread(f)
-                    t2 = self.file(f).read(m2[f])
-                    if cmp(t1, t2) == 0:
-                        n = m2[f]
-                    del t1, t2
-
-                # are files different?
-                if n != m2[f]:
-                    a = ma.get(f, nullid)
-                    # are both different from the ancestor?
-                    if n != a and m2[f] != a:
-                        self.ui.debug(_(" %s versions differ, resolve\n") % f)
-                        # merge executable bits
-                        # "if we changed or they changed, change in merge"
-                        a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
-                        mode = ((a^b) | (a^c)) ^ a
-                        merge[f] = (m1.get(f, nullid), m2[f], mode)
-                        s = 1
-                    # are we clobbering?
-                    # is remote's version newer?
-                    # or are we going back in time?
-                    elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
-                        self.ui.debug(_(" remote %s is newer, get\n") % f)
-                        get[f] = m2[f]
-                        s = 1
-                elif f in umap or f in added:
-                    # this unknown file is the same as the checkout
-                    # we need to reset the dirstate if the file was added
-                    get[f] = m2[f]
-
-                if not s and mfw[f] != mf2[f]:
-                    if force:
-                        self.ui.debug(_(" updating permissions for %s\n") % f)
-                        util.set_exec(self.wjoin(f), mf2[f])
-                    else:
-                        a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
-                        mode = ((a^b) | (a^c)) ^ a
-                        if mode != b:
-                            self.ui.debug(_(" updating permissions for %s\n")
-                                          % f)
-                            util.set_exec(self.wjoin(f), mode)
-                del m2[f]
-            elif f in ma:
-                if n != ma[f]:
-                    r = _("d")
-                    if not force and (linear_path or allow):
-                        r = self.ui.prompt(
-                            (_(" local changed %s which remote deleted\n") % f) +
-                             _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
-                    if r == _("d"):
-                        remove.append(f)
-                else:
-                    self.ui.debug(_("other deleted %s\n") % f)
-                    remove.append(f) # other deleted it
-            else:
-                # file is created on branch or in working directory
-                if force and f not in umap:
-                    self.ui.debug(_("remote deleted %s, clobbering\n") % f)
-                    remove.append(f)
-                elif n == m1.get(f, nullid): # same as parent
-                    if p2 == pa: # going backwards?
-                        self.ui.debug(_("remote deleted %s\n") % f)
-                        remove.append(f)
-                    else:
-                        self.ui.debug(_("local modified %s, keeping\n") % f)
-                else:
-                    self.ui.debug(_("working dir created %s, keeping\n") % f)
-
-        for f, n in m2.iteritems():
-            if choose and not choose(f):
-                continue
-            if f[0] == "/":
-                continue
-            if f in ma and n != ma[f]:
-                r = _("k")
-                if not force and (linear_path or allow):
-                    r = self.ui.prompt(
-                        (_("remote changed %s which local deleted\n") % f) +
-                         _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
-                if r == _("k"):
-                    get[f] = n
-            elif f not in ma:
-                self.ui.debug(_("remote created %s\n") % f)
-                get[f] = n
-            else:
-                if force or p2 == pa: # going backwards?
-                    self.ui.debug(_("local deleted %s, recreating\n") % f)
-                    get[f] = n
-                else:
-                    self.ui.debug(_("local deleted %s\n") % f)
-
-        del mw, m1, m2, ma
-
-        if force:
-            for f in merge:
-                get[f] = merge[f][1]
-            merge = {}
-
-        if linear_path or force:
-            # we don't need to do any magic, just jump to the new rev
-            branch_merge = False
-            p1, p2 = p2, nullid
-        else:
-            if not allow:
-                self.ui.status(_("this update spans a branch"
-                                 " affecting the following files:\n"))
-                fl = merge.keys() + get.keys()
-                fl.sort()
-                for f in fl:
-                    cf = ""
-                    if f in merge:
-                        cf = _(" (resolve)")
-                    self.ui.status(" %s%s\n" % (f, cf))
-                self.ui.warn(_("aborting update spanning branches!\n"))
-                self.ui.status(_("(use 'hg merge' to merge across branches"
-                                 " or 'hg update -C' to lose changes)\n"))
-                return 1
-            branch_merge = True
-
-        xp1 = hex(p1)
-        xp2 = hex(p2)
-        if p2 == nullid: xxp2 = ''
-        else: xxp2 = xp2
-
-        self.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2)
-
-        # get the files we don't need to change
-        files = get.keys()
-        files.sort()
-        for f in files:
-            if f[0] == "/":
-                continue
-            self.ui.note(_("getting %s\n") % f)
-            t = self.file(f).read(get[f])
-            self.wwrite(f, t)
-            util.set_exec(self.wjoin(f), mf2[f])
-            if moddirstate:
-                if branch_merge:
-                    self.dirstate.update([f], 'n', st_mtime=-1)
-                else:
-                    self.dirstate.update([f], 'n')
-
-        # merge the tricky bits
-        failedmerge = []
-        files = merge.keys()
-        files.sort()
-        for f in files:
-            self.ui.status(_("merging %s\n") % f)
-            my, other, flag = merge[f]
-            ret = self.merge3(f, my, other, xp1, xp2)
-            if ret:
-                err = True
-                failedmerge.append(f)
-            util.set_exec(self.wjoin(f), flag)
-            if moddirstate:
-                if branch_merge:
-                    # We've done a branch merge, mark this file as merged
-                    # so that we properly record the merger later
-                    self.dirstate.update([f], 'm')
-                else:
-                    # We've update-merged a locally modified file, so
-                    # we set the dirstate to emulate a normal checkout
-                    # of that file some time in the past. Thus our
-                    # merge will appear as a normal local file
-                    # modification.
-                    f_len = len(self.file(f).read(other))
-                    self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
-
-        remove.sort()
-        for f in remove:
-            self.ui.note(_("removing %s\n") % f)
-            util.audit_path(f)
-            try:
-                util.unlink(self.wjoin(f))
-            except OSError, inst:
-                if inst.errno != errno.ENOENT:
-                    self.ui.warn(_("update failed to remove %s: %s!\n") %
-                                 (f, inst.strerror))
-        if moddirstate:
-            if branch_merge:
-                self.dirstate.update(remove, 'r')
-            else:
-                self.dirstate.forget(remove)
-
-        if moddirstate:
-            self.dirstate.setparents(p1, p2)
-
-        if show_stats:
-            stats = ((len(get), _("updated")),
-                     (len(merge) - len(failedmerge), _("merged")),
-                     (len(remove), _("removed")),
-                     (len(failedmerge), _("unresolved")))
-            note = ", ".join([_("%d files %s") % s for s in stats])
-            self.ui.status("%s\n" % note)
-        if moddirstate:
-            if branch_merge:
-                if failedmerge:
-                    self.ui.status(_("There are unresolved merges,"
-                                    " you can redo the full merge using:\n"
-                                    "  hg update -C %s\n"
-                                    "  hg merge %s\n"
-                                    % (self.changelog.rev(p1),
-                                        self.changelog.rev(p2))))
-                else:
-                    self.ui.status(_("(branch merge, don't forget to commit)\n"))
-            elif failedmerge:
-                self.ui.status(_("There are unresolved merges with"
-                                 " locally modified files.\n"))
-
-        self.hook('update', parent1=xp1, parent2=xxp2, error=int(err))
-        return err
-
-    def merge3(self, fn, my, other, p1, p2):
-        """perform a 3-way merge in the working directory"""
-
-        def temp(prefix, node):
-            pre = "%s~%s." % (os.path.basename(fn), prefix)
-            (fd, name) = tempfile.mkstemp(prefix=pre)
-            f = os.fdopen(fd, "wb")
-            self.wwrite(fn, fl.read(node), f)
-            f.close()
-            return name
-
-        fl = self.file(fn)
-        base = fl.ancestor(my, other)
-        a = self.wjoin(fn)
-        b = temp("base", base)
-        c = temp("other", other)
-
-        self.ui.note(_("resolving %s\n") % fn)
-        self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
-                              (fn, short(my), short(other), short(base)))
-
-        cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
-               or "hgmerge")
-        r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
-                        environ={'HG_FILE': fn,
-                                 'HG_MY_NODE': p1,
-                                 'HG_OTHER_NODE': p2,
-                                 'HG_FILE_MY_NODE': hex(my),
-                                 'HG_FILE_OTHER_NODE': hex(other),
-                                 'HG_FILE_BASE_NODE': hex(base)})
-        if r:
-            self.ui.warn(_("merging %s failed!\n") % fn)
-
-        os.unlink(b)
-        os.unlink(c)
-        return r
-
-    def verify(self):
-        filelinkrevs = {}
-        filenodes = {}
-        changesets = revisions = files = 0
-        errors = [0]
-        warnings = [0]
-        neededmanifests = {}
-
-        def err(msg):
-            self.ui.warn(msg + "\n")
-            errors[0] += 1
-
-        def warn(msg):
-            self.ui.warn(msg + "\n")
-            warnings[0] += 1
-
-        def checksize(obj, name):
-            d = obj.checksize()
-            if d[0]:
-                err(_("%s data length off by %d bytes") % (name, d[0]))
-            if d[1]:
-                err(_("%s index contains %d extra bytes") % (name, d[1]))
-
-        def checkversion(obj, name):
-            if obj.version != revlog.REVLOGV0:
-                if not revlogv1:
-                    warn(_("warning: `%s' uses revlog format 1") % name)
-            elif revlogv1:
-                warn(_("warning: `%s' uses revlog format 0") % name)
-
-        revlogv1 = self.revlogversion != revlog.REVLOGV0
-        if self.ui.verbose or revlogv1 != self.revlogv1:
-            self.ui.status(_("repository uses revlog format %d\n") %
-                           (revlogv1 and 1 or 0))
-
-        seen = {}
-        self.ui.status(_("checking changesets\n"))
-        checksize(self.changelog, "changelog")
-
-        for i in range(self.changelog.count()):
-            changesets += 1
-            n = self.changelog.node(i)
-            l = self.changelog.linkrev(n)
-            if l != i:
-                err(_("incorrect link (%d) for changeset revision %d") %(l, i))
-            if n in seen:
-                err(_("duplicate changeset at revision %d") % i)
-            seen[n] = 1
-
-            for p in self.changelog.parents(n):
-                if p not in self.changelog.nodemap:
-                    err(_("changeset %s has unknown parent %s") %
-                                 (short(n), short(p)))
-            try:
-                changes = self.changelog.read(n)
-            except KeyboardInterrupt:
-                self.ui.warn(_("interrupted"))
-                raise
-            except Exception, inst:
-                err(_("unpacking changeset %s: %s") % (short(n), inst))
-                continue
-
-            neededmanifests[changes[0]] = n
-
-            for f in changes[3]:
-                filelinkrevs.setdefault(f, []).append(i)
-
-        seen = {}
-        self.ui.status(_("checking manifests\n"))
-        checkversion(self.manifest, "manifest")
-        checksize(self.manifest, "manifest")
-
-        for i in range(self.manifest.count()):
-            n = self.manifest.node(i)
-            l = self.manifest.linkrev(n)
-
-            if l < 0 or l >= self.changelog.count():
-                err(_("bad manifest link (%d) at revision %d") % (l, i))
-
-            if n in neededmanifests:
-                del neededmanifests[n]
-
-            if n in seen:
-                err(_("duplicate manifest at revision %d") % i)
-
-            seen[n] = 1
-
-            for p in self.manifest.parents(n):
-                if p not in self.manifest.nodemap:
-                    err(_("manifest %s has unknown parent %s") %
-                        (short(n), short(p)))
-
-            try:
-                delta = mdiff.patchtext(self.manifest.delta(n))
-            except KeyboardInterrupt:
-                self.ui.warn(_("interrupted"))
-                raise
-            except Exception, inst:
-                err(_("unpacking manifest %s: %s") % (short(n), inst))
-                continue
-
-            try:
-                ff = [ l.split('\0') for l in delta.splitlines() ]
-                for f, fn in ff:
-                    filenodes.setdefault(f, {})[bin(fn[:40])] = 1
-            except (ValueError, TypeError), inst:
-                err(_("broken delta in manifest %s: %s") % (short(n), inst))
-
-        self.ui.status(_("crosschecking files in changesets and manifests\n"))
-
-        for m, c in neededmanifests.items():
-            err(_("Changeset %s refers to unknown manifest %s") %
-                (short(m), short(c)))
-        del neededmanifests
-
-        for f in filenodes:
-            if f not in filelinkrevs:
-                err(_("file %s in manifest but not in changesets") % f)
-
-        for f in filelinkrevs:
-            if f not in filenodes:
-                err(_("file %s in changeset but not in manifest") % f)
-
-        self.ui.status(_("checking files\n"))
-        ff = filenodes.keys()
-        ff.sort()
-        for f in ff:
-            if f == "/dev/null":
-                continue
-            files += 1
-            if not f:
-                err(_("file without name in manifest %s") % short(n))
-                continue
-            fl = self.file(f)
-            checkversion(fl, f)
-            checksize(fl, f)
-
-            nodes = {nullid: 1}
-            seen = {}
-            for i in range(fl.count()):
-                revisions += 1
-                n = fl.node(i)
-
-                if n in seen:
-                    err(_("%s: duplicate revision %d") % (f, i))
-                if n not in filenodes[f]:
-                    err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
-                else:
-                    del filenodes[f][n]
-
-                flr = fl.linkrev(n)
-                if flr not in filelinkrevs.get(f, []):
-                    err(_("%s:%s points to unexpected changeset %d")
-                            % (f, short(n), flr))
-                else:
-                    filelinkrevs[f].remove(flr)
-
-                # verify contents
-                try:
-                    t = fl.read(n)
-                except KeyboardInterrupt:
-                    self.ui.warn(_("interrupted"))
-                    raise
-                except Exception, inst:
-                    err(_("unpacking file %s %s: %s") % (f, short(n), inst))
-
-                # verify parents
-                (p1, p2) = fl.parents(n)
-                if p1 not in nodes:
-                    err(_("file %s:%s unknown parent 1 %s") %
-                        (f, short(n), short(p1)))
-                if p2 not in nodes:
-                    err(_("file %s:%s unknown parent 2 %s") %
-                            (f, short(n), short(p1)))
-                nodes[n] = 1
-
-            # cross-check
-            for node in filenodes[f]:
-                err(_("node %s in manifests not in %s") % (hex(node), f))
-
-        self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
-                       (files, changesets, revisions))
-
-        if warnings[0]:
-            self.ui.warn(_("%d warnings encountered!\n") % warnings[0])
-        if errors[0]:
-            self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
-            return 1
 
     def stream_in(self, remote):
         fp = remote.stream_out()
@@ -2227,7 +1710,7 @@
                         util.bytecount(total_bytes / elapsed)))
         self.reload()
         return len(self.heads()) + 1
-        
+
     def clone(self, remote, heads=[], stream=False):
         '''clone remote repository.
 
@@ -2256,3 +1739,8 @@
                     os.path.join(p, "undo.dirstate"))
     return a
 
+def instance(ui, path, create):
+    return localrepository(ui, util.drop_scheme('file', path), create)
+    
+def islocal(path):
+    return True
--- a/mercurial/lock.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/lock.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 # lock.py - simple locking scheme for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/mail.py	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,68 @@
+# mail.py - mail sending bits for mercurial
+#
+# Copyright 2006 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+from i18n import gettext as _
+from demandload import *
+demandload(globals(), "os re smtplib templater util")
+
+def _smtp(ui):
+    '''send mail using smtp.'''
+
+    local_hostname = ui.config('smtp', 'local_hostname')
+    s = smtplib.SMTP(local_hostname=local_hostname)
+    mailhost = ui.config('smtp', 'host')
+    if not mailhost:
+        raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
+    mailport = int(ui.config('smtp', 'port', 25))
+    self.note(_('sending mail: smtp host %s, port %s\n') %
+              (mailhost, mailport))
+    s.connect(host=mailhost, port=mailport)
+    if ui.configbool('smtp', 'tls'):
+        ui.note(_('(using tls)\n'))
+        s.ehlo()
+        s.starttls()
+        s.ehlo()
+    username = ui.config('smtp', 'username')
+    password = ui.config('smtp', 'password')
+    if username and password:
+        ui.note(_('(authenticating to mail server as %s)\n') %
+                  (username))
+        s.login(username, password)
+    return s
+
+class _sendmail(object):
+    '''send mail using sendmail.'''
+
+    def __init__(self, ui, program):
+        self.ui = ui
+        self.program = program
+
+    def sendmail(self, sender, recipients, msg):
+        cmdline = '%s -f %s %s' % (
+            self.program, templater.email(sender),
+            ' '.join(map(templater.email, recipients)))
+        self.ui.note(_('sending mail: %s\n') % cmdline)
+        fp = os.popen(cmdline, 'w')
+        fp.write(msg)
+        ret = fp.close()
+        if ret:
+            raise util.Abort('%s %s' % (
+                os.path.basename(self.program.split(None, 1)[0]),
+                util.explain_exit(ret)[0]))
+
+def connect(ui):
+    '''make a mail connection. object returned has one method, sendmail.
+    call as sendmail(sender, list-of-recipients, msg).'''
+
+    method = ui.config('email', 'method', 'smtp')
+    if method == 'smtp':
+        return _smtp(ui)
+
+    return _sendmail(ui, method)
+
+def sendmail(ui, sender, recipients, msg):
+    return connect(ui).sendmail(sender, recipients, msg)
--- a/mercurial/manifest.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/manifest.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 # manifest.py - manifest revision class for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -10,6 +10,31 @@
 from demandload import *
 demandload(globals(), "array bisect struct")
 
+class manifestdict(dict):
+    def __init__(self, mapping=None, flags=None):
+        if mapping is None: mapping = {}
+        if flags is None: flags = {}
+        dict.__init__(self, mapping)
+        self._flags = flags
+    def flags(self, f):
+        return self._flags.get(f, "")
+    def execf(self, f):
+        "test for executable in manifest flags"
+        return "x" in self.flags(f)
+    def linkf(self, f):
+        "test for symlink in manifest flags"
+        return "l" in self.flags(f)
+    def rawset(self, f, entry):
+        self[f] = bin(entry[:40])
+        fl = entry[40:-1]
+        if fl: self._flags[f] = fl
+    def set(self, f, execf=False, linkf=False):
+        if linkf: self._flags[f] = "l"
+        elif execf: self._flags[f] = "x"
+        else: self._flags[f] = ""
+    def copy(self):
+        return manifestdict(dict.copy(self), dict.copy(self._flags))
+
 class manifest(revlog):
     def __init__(self, opener, defversion=REVLOGV0):
         self.mapcache = None
@@ -18,26 +43,18 @@
                         defversion)
 
     def read(self, node):
-        if node == nullid: return {} # don't upset local cache
+        if node == nullid: return manifestdict() # don't upset local cache
         if self.mapcache and self.mapcache[0] == node:
             return self.mapcache[1]
         text = self.revision(node)
-        map = {}
-        flag = {}
         self.listcache = array.array('c', text)
         lines = text.splitlines(1)
+        mapping = manifestdict()
         for l in lines:
             (f, n) = l.split('\0')
-            map[f] = bin(n[:40])
-            flag[f] = (n[40:-1] == "x")
-        self.mapcache = (node, map, flag)
-        return map
-
-    def readflags(self, node):
-        if node == nullid: return {} # don't upset local cache
-        if not self.mapcache or self.mapcache[0] != node:
-            self.read(node)
-        return self.mapcache[2]
+            mapping.rawset(f, n)
+        self.mapcache = (node, mapping)
+        return mapping
 
     def diff(self, a, b):
         return mdiff.textdiff(str(a), str(b))
@@ -86,7 +103,7 @@
         '''look up entry for a single file efficiently.
         return (node, flag) pair if found, (None, None) if not.'''
         if self.mapcache and node == self.mapcache[0]:
-            return self.mapcache[1].get(f), self.mapcache[2].get(f)
+            return self.mapcache[1].get(f), self.mapcache[1].flags(f)
         text = self.revision(node)
         start, end = self._search(text, f)
         if start == end:
@@ -95,7 +112,7 @@
         f, n = l.split('\0')
         return bin(n[:40]), n[40:-1] == 'x'
 
-    def add(self, map, flags, transaction, link, p1=None, p2=None,
+    def add(self, map, transaction, link, p1=None, p2=None,
             changed=None):
         # apply the changes collected during the bisect loop to our addlist
         # return a delta suitable for addrevision
@@ -123,9 +140,7 @@
 
             # if this is changed to support newlines in filenames,
             # be sure to check the templates/ dir again (especially *-raw.tmpl)
-            text = ["%s\000%s%s\n" %
-                            (f, hex(map[f]), flags[f] and "x" or '')
-                            for f in files]
+            text = ["%s\000%s%s\n" % (f, hex(map[f]), map.flags(f)) for f in files]
             self.listcache = array.array('c', "".join(text))
             cachedelta = None
         else:
@@ -151,8 +166,7 @@
                 # bs will either be the index of the item or the insert point
                 start, end = self._search(addbuf, f, start)
                 if w[1] == 0:
-                    l = "%s\000%s%s\n" % (f, hex(map[f]),
-                                          flags[f] and "x" or '')
+                    l = "%s\000%s%s\n" % (f, hex(map[f]), map.flags(f))
                 else:
                     l = ""
                 if start == end and w[1] == 1:
@@ -183,6 +197,6 @@
 
         n = self.addrevision(buffer(self.listcache), transaction, link, p1,  \
                              p2, cachedelta)
-        self.mapcache = (n, map, flags)
+        self.mapcache = (n, map)
 
         return n
--- a/mercurial/mdiff.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/mdiff.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 # mdiff.py - diff and patch routines for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -19,14 +19,41 @@
             lines[-1] = lines[-1][:-1]
     return lines
 
-def unidiff(a, ad, b, bd, fn, r=None, text=False,
-            showfunc=False, ignorews=False, ignorewsamount=False,
-            ignoreblanklines=False):
+class diffopts(object):
+    '''context is the number of context lines
+    text treats all files as text
+    showfunc enables diff -p output
+    git enables the git extended patch format
+    ignorews ignores all whitespace changes in the diff
+    ignorewsamount ignores changes in the amount of whitespace
+    ignoreblanklines ignores changes whose lines are all blank'''
 
+    defaults = {
+        'context': 3,
+        'text': False,
+        'showfunc': True,
+        'git': False,
+        'ignorews': False,
+        'ignorewsamount': False,
+        'ignoreblanklines': False,
+        }
+
+    __slots__ = defaults.keys()
+
+    def __init__(self, **opts):
+        for k in self.__slots__:
+            v = opts.get(k)
+            if v is None:
+                v = self.defaults[k]
+            setattr(self, k, v)
+
+defaultopts = diffopts()
+
+def unidiff(a, ad, b, bd, fn, r=None, opts=defaultopts):
     if not a and not b: return ""
     epoch = util.datestr((0, 0))
 
-    if not text and (util.binary(a) or util.binary(b)):
+    if not opts.text and (util.binary(a) or util.binary(b)):
         l = ['Binary file %s has changed\n' % fn]
     elif not a:
         b = splitnewlines(b)
@@ -49,10 +76,7 @@
     else:
         al = splitnewlines(a)
         bl = splitnewlines(b)
-        l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn,
-                          showfunc=showfunc, ignorews=ignorews,
-                          ignorewsamount=ignorewsamount,
-                          ignoreblanklines=ignoreblanklines))
+        l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn, opts=opts))
         if not l: return ""
         # difflib uses a space, rather than a tab
         l[0] = "%s\t%s\n" % (l[0][:-2], ad)
@@ -72,21 +96,15 @@
 # t1 and t2 are the text to be diffed
 # l1 and l2 are the text broken up into lines
 # header1 and header2 are the filenames for the diff output
-# context is the number of context lines
-# showfunc enables diff -p output
-# ignorews ignores all whitespace changes in the diff
-# ignorewsamount ignores changes in the amount of whitespace
-# ignoreblanklines ignores changes whose lines are all blank
-def bunidiff(t1, t2, l1, l2, header1, header2, context=3, showfunc=False,
-             ignorews=False, ignorewsamount=False, ignoreblanklines=False):
+def bunidiff(t1, t2, l1, l2, header1, header2, opts=defaultopts):
     def contextend(l, len):
-        ret = l + context
+        ret = l + opts.context
         if ret > len:
             ret = len
         return ret
 
     def contextstart(l):
-        ret = l - context
+        ret = l - opts.context
         if ret < 0:
             return 0
         return ret
@@ -101,7 +119,7 @@
         blen = b2 - bstart + aend - a2
 
         func = ""
-        if showfunc:
+        if opts.showfunc:
             # walk backwards from the start of the context
             # to find a line starting with an alphanumeric char.
             for x in xrange(astart, -1, -1):
@@ -119,14 +137,14 @@
 
     header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ]
 
-    if showfunc:
+    if opts.showfunc:
         funcre = re.compile('\w')
-    if ignorewsamount:
+    if opts.ignorewsamount:
         wsamountre = re.compile('[ \t]+')
         wsappendedre = re.compile(' \n')
-    if ignoreblanklines:
+    if opts.ignoreblanklines:
         wsblanklinesre = re.compile('\n')
-    if ignorews:
+    if opts.ignorews:
         wsre = re.compile('[ \t]')
 
     # bdiff.blocks gives us the matching sequences in the files.  The loop
@@ -159,13 +177,13 @@
         if not old and not new:
             continue
 
-        if ignoreblanklines:
+        if opts.ignoreblanklines:
             wsold = wsblanklinesre.sub('', "".join(old))
             wsnew = wsblanklinesre.sub('', "".join(new))
             if wsold == wsnew:
                 continue
 
-        if ignorewsamount:
+        if opts.ignorewsamount:
             wsold = wsamountre.sub(' ', "".join(old))
             wsold = wsappendedre.sub('\n', wsold)
             wsnew = wsamountre.sub(' ', "".join(new))
@@ -173,7 +191,7 @@
             if wsold == wsnew:
                 continue
 
-        if ignorews:
+        if opts.ignorews:
             wsold = wsre.sub('', "".join(old))
             wsnew = wsre.sub('', "".join(new))
             if wsold == wsnew:
@@ -184,7 +202,7 @@
         prev = None
         if hunk:
             # join with the previous hunk if it falls inside the context
-            if astart < hunk[1] + context + 1:
+            if astart < hunk[1] + opts.context + 1:
                 prev = hunk
                 astart = hunk[1]
                 bstart = hunk[3]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/merge.py	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,339 @@
+# merge.py - directory-level update/merge handling for Mercurial
+#
+# Copyright 2006 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+from node import *
+from i18n import gettext as _
+from demandload import *
+demandload(globals(), "util os tempfile")
+
+def fmerge(f, local, other, ancestor):
+    """merge executable flags"""
+    a, b, c = ancestor.execf(f), local.execf(f), other.execf(f)
+    return ((a^b) | (a^c)) ^ a
+
+def merge3(repo, fn, my, other, p1, p2):
+    """perform a 3-way merge in the working directory"""
+
+    def temp(prefix, node):
+        pre = "%s~%s." % (os.path.basename(fn), prefix)
+        (fd, name) = tempfile.mkstemp(prefix=pre)
+        f = os.fdopen(fd, "wb")
+        repo.wwrite(fn, fl.read(node), f)
+        f.close()
+        return name
+
+    fl = repo.file(fn)
+    base = fl.ancestor(my, other)
+    a = repo.wjoin(fn)
+    b = temp("base", base)
+    c = temp("other", other)
+
+    repo.ui.note(_("resolving %s\n") % fn)
+    repo.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
+                          (fn, short(my), short(other), short(base)))
+
+    cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge")
+           or "hgmerge")
+    r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root,
+                    environ={'HG_FILE': fn,
+                             'HG_MY_NODE': p1,
+                             'HG_OTHER_NODE': p2,
+                             'HG_FILE_MY_NODE': hex(my),
+                             'HG_FILE_OTHER_NODE': hex(other),
+                             'HG_FILE_BASE_NODE': hex(base)})
+    if r:
+        repo.ui.warn(_("merging %s failed!\n") % fn)
+
+    os.unlink(b)
+    os.unlink(c)
+    return r
+
+def update(repo, node, branchmerge=False, force=False, partial=None,
+           wlock=None, show_stats=True, remind=True):
+
+    overwrite = force and not branchmerge
+    forcemerge = force and branchmerge
+
+    if not wlock:
+        wlock = repo.wlock()
+
+    ### check phase
+
+    pl = repo.dirstate.parents()
+    if not overwrite and pl[1] != nullid:
+        raise util.Abort(_("outstanding uncommitted merges"))
+
+    p1, p2 = pl[0], node
+    pa = repo.changelog.ancestor(p1, p2)
+
+    # is there a linear path from p1 to p2?
+    linear_path = (pa == p1 or pa == p2)
+    if branchmerge and linear_path:
+        raise util.Abort(_("there is nothing to merge, just use "
+                           "'hg update' or look at 'hg heads'"))
+
+    if not overwrite and not linear_path and not branchmerge:
+        raise util.Abort(_("update spans branches, use 'hg merge' "
+                           "or 'hg update -C' to lose changes"))
+
+    modified, added, removed, deleted, unknown = repo.status()[:5]
+    if branchmerge and not forcemerge:
+        if modified or added or removed:
+            raise util.Abort(_("outstanding uncommitted changes"))
+
+    m1n = repo.changelog.read(p1)[0]
+    m2n = repo.changelog.read(p2)[0]
+    man = repo.manifest.ancestor(m1n, m2n)
+    m1 = repo.manifest.read(m1n)
+    m2 = repo.manifest.read(m2n).copy()
+    ma = repo.manifest.read(man)
+
+    if not force:
+        for f in unknown:
+            if f in m2:
+                if repo.file(f).cmp(m2[f], repo.wread(f)):
+                    raise util.Abort(_("'%s' already exists in the working"
+                                       " dir and differs from remote") % f)
+
+    # resolve the manifest to determine which files
+    # we care about merging
+    repo.ui.note(_("resolving manifests\n"))
+    repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") %
+                  (overwrite, branchmerge, bool(partial), linear_path))
+    repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
+                  (short(man), short(m1n), short(m2n)))
+
+    merge = {}
+    get = {}
+    remove = []
+    forget = []
+
+    # construct a working dir manifest
+    mw = m1.copy()
+    umap = dict.fromkeys(unknown)
+
+    for f in added + modified + unknown:
+        mw[f] = ""
+        # is the wfile new and matches m2?
+        if (f not in m1 and f in m2 and
+            not repo.file(f).cmp(m2[f], repo.wread(f))):
+            mw[f] = m2[f]
+
+        mw.set(f, util.is_exec(repo.wjoin(f), mw.execf(f)))
+
+    for f in deleted + removed:
+        if f in mw:
+            del mw[f]
+
+        # If we're jumping between revisions (as opposed to merging),
+        # and if neither the working directory nor the target rev has
+        # the file, then we need to remove it from the dirstate, to
+        # prevent the dirstate from listing the file when it is no
+        # longer in the manifest.
+        if linear_path and f not in m2:
+            forget.append(f)
+
+    # Compare manifests
+    for f, n in mw.iteritems():
+        if partial and not partial(f):
+            continue
+        if f in m2:
+            s = 0
+
+            # are files different?
+            if n != m2[f]:
+                a = ma.get(f, nullid)
+                # are both different from the ancestor?
+                if n != a and m2[f] != a:
+                    repo.ui.debug(_(" %s versions differ, resolve\n") % f)
+                    merge[f] = (fmerge(f, mw, m2, ma), m1.get(f, nullid), m2[f])
+                    s = 1
+                # are we clobbering?
+                # is remote's version newer?
+                # or are we going back in time?
+                elif overwrite or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
+                    repo.ui.debug(_(" remote %s is newer, get\n") % f)
+                    get[f] = (m2.execf(f), m2[f])
+                    s = 1
+            elif f in umap or f in added:
+                # this unknown file is the same as the checkout
+                # we need to reset the dirstate if the file was added
+                get[f] = (m2.execf(f), m2[f])
+
+            if not s and mw.execf(f) != m2.execf(f):
+                if overwrite:
+                    repo.ui.debug(_(" updating permissions for %s\n") % f)
+                    util.set_exec(repo.wjoin(f), m2.execf(f))
+                else:
+                    if fmerge(f, mw, m2, ma) != mw.execf(f):
+                        repo.ui.debug(_(" updating permissions for %s\n")
+                                      % f)
+                        util.set_exec(repo.wjoin(f), mode)
+            del m2[f]
+        elif f in ma:
+            if n != ma[f]:
+                r = _("d")
+                if not overwrite and (linear_path or branchmerge):
+                    r = repo.ui.prompt(
+                        (_(" local changed %s which remote deleted\n") % f) +
+                         _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
+                if r == _("d"):
+                    remove.append(f)
+            else:
+                repo.ui.debug(_("other deleted %s\n") % f)
+                remove.append(f) # other deleted it
+        else:
+            # file is created on branch or in working directory
+            if overwrite and f not in umap:
+                repo.ui.debug(_("remote deleted %s, clobbering\n") % f)
+                remove.append(f)
+            elif n == m1.get(f, nullid): # same as parent
+                if p2 == pa: # going backwards?
+                    repo.ui.debug(_("remote deleted %s\n") % f)
+                    remove.append(f)
+                else:
+                    repo.ui.debug(_("local modified %s, keeping\n") % f)
+            else:
+                repo.ui.debug(_("working dir created %s, keeping\n") % f)
+
+    for f, n in m2.iteritems():
+        if partial and not partial(f):
+            continue
+        if f[0] == "/":
+            continue
+        if f in ma and n != ma[f]:
+            r = _("k")
+            if not overwrite and (linear_path or branchmerge):
+                r = repo.ui.prompt(
+                    (_("remote changed %s which local deleted\n") % f) +
+                     _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
+            if r == _("k"):
+                get[f] = (m2.execf(f), n)
+        elif f not in ma:
+            repo.ui.debug(_("remote created %s\n") % f)
+            get[f] = (m2.execf(f), n)
+        else:
+            if overwrite or p2 == pa: # going backwards?
+                repo.ui.debug(_("local deleted %s, recreating\n") % f)
+                get[f] = (m2.execf(f), n)
+            else:
+                repo.ui.debug(_("local deleted %s\n") % f)
+
+    del mw, m1, m2, ma
+
+    ### apply phase
+
+    if overwrite:
+        for f in merge:
+            get[f] = merge[f][:2]
+        merge = {}
+
+    if linear_path or overwrite:
+        # we don't need to do any magic, just jump to the new rev
+        p1, p2 = p2, nullid
+
+    xp1 = hex(p1)
+    xp2 = hex(p2)
+    if p2 == nullid: xxp2 = ''
+    else: xxp2 = xp2
+
+    repo.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2)
+
+    # get the files we don't need to change
+    files = get.keys()
+    files.sort()
+    for f in files:
+        flag, node = get[f]
+        if f[0] == "/":
+            continue
+        repo.ui.note(_("getting %s\n") % f)
+        t = repo.file(f).read(node)
+        repo.wwrite(f, t)
+        util.set_exec(repo.wjoin(f), flag)
+
+    # merge the tricky bits
+    unresolved = []
+    files = merge.keys()
+    files.sort()
+    for f in files:
+        repo.ui.status(_("merging %s\n") % f)
+        flag, my, other = merge[f]
+        ret = merge3(repo, f, my, other, xp1, xp2)
+        if ret:
+            unresolved.append(f)
+        util.set_exec(repo.wjoin(f), flag)
+
+    remove.sort()
+    for f in remove:
+        repo.ui.note(_("removing %s\n") % f)
+        util.audit_path(f)
+        try:
+            util.unlink(repo.wjoin(f))
+        except OSError, inst:
+            if inst.errno != errno.ENOENT:
+                repo.ui.warn(_("update failed to remove %s: %s!\n") %
+                             (f, inst.strerror))
+
+    # update dirstate
+    if not partial:
+        repo.dirstate.setparents(p1, p2)
+        repo.dirstate.forget(forget)
+        if branchmerge:
+            repo.dirstate.update(remove, 'r')
+        else:
+            repo.dirstate.forget(remove)
+
+        files = get.keys()
+        files.sort()
+        for f in files:
+            if branchmerge:
+                repo.dirstate.update([f], 'n', st_mtime=-1)
+            else:
+                repo.dirstate.update([f], 'n')
+
+        files = merge.keys()
+        files.sort()
+        for f in files:
+            if branchmerge:
+                # We've done a branch merge, mark this file as merged
+                # so that we properly record the merger later
+                repo.dirstate.update([f], 'm')
+            else:
+                # We've update-merged a locally modified file, so
+                # we set the dirstate to emulate a normal checkout
+                # of that file some time in the past. Thus our
+                # merge will appear as a normal local file
+                # modification.
+                fl = repo.file(f)
+                f_len = fl.size(fl.rev(other))
+                repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
+
+    if show_stats:
+        stats = ((len(get), _("updated")),
+                 (len(merge) - len(unresolved), _("merged")),
+                 (len(remove), _("removed")),
+                 (len(unresolved), _("unresolved")))
+        note = ", ".join([_("%d files %s") % s for s in stats])
+        repo.ui.status("%s\n" % note)
+    if not partial:
+        if branchmerge:
+            if unresolved:
+                repo.ui.status(_("There are unresolved merges,"
+                                " you can redo the full merge using:\n"
+                                "  hg update -C %s\n"
+                                "  hg merge %s\n"
+                                % (repo.changelog.rev(p1),
+                                    repo.changelog.rev(p2))))
+            elif remind:
+                repo.ui.status(_("(branch merge, don't forget to commit)\n"))
+        elif unresolved:
+            repo.ui.status(_("There are unresolved merges with"
+                             " locally modified files.\n"))
+
+    repo.hook('update', parent1=xp1, parent2=xxp2, error=len(unresolved))
+    return len(unresolved)
+
--- a/mercurial/mpatch.c	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/mpatch.c	Fri Aug 18 21:17:28 2006 -0700
@@ -14,7 +14,7 @@
  allocation of intermediate Python objects. Working memory is about 2x
  the total number of hunks.
 
- Copyright 2005 Matt Mackall <mpm@selenic.com>
+ Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 
  This software may be used and distributed according to the terms
  of the GNU General Public License, incorporated herein by reference.
--- a/mercurial/node.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/node.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,7 +1,7 @@
 """
 node.py - basic nodeid manipulation for mercurial
 
-Copyright 2005 Matt Mackall <mpm@selenic.com>
+Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 
 This software may be used and distributed according to the terms
 of the GNU General Public License, incorporated herein by reference.
--- a/mercurial/packagescan.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/packagescan.py	Fri Aug 18 21:17:28 2006 -0700
@@ -2,7 +2,7 @@
 # Used for the py2exe distutil.
 # This module must be the first mercurial module imported in setup.py
 #
-# Copyright 2005 Volker Kleinfeld <Volker.Kleinfeld@gmx.de>
+# Copyright 2005, 2006 Volker Kleinfeld <Volker.Kleinfeld@gmx.de>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/patch.py	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,539 @@
+# patch.py - patch file parsing routines
+#
+# Copyright 2006 Brendan Cully <brendan@kublai.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+from demandload import demandload
+from i18n import gettext as _
+from node import *
+demandload(globals(), "cmdutil mdiff util")
+demandload(globals(), "cStringIO email.Parser errno os re shutil sys tempfile")
+
+# helper functions
+
+def copyfile(src, dst, basedir=None):
+    if not basedir:
+        basedir = os.getcwd()
+
+    abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
+    if os.path.exists(absdst):
+        raise util.Abort(_("cannot create %s: destination already exists") %
+                         dst)
+
+    targetdir = os.path.dirname(absdst)
+    if not os.path.isdir(targetdir):
+        os.makedirs(targetdir)
+    try:
+        shutil.copyfile(abssrc, absdst)
+        shutil.copymode(abssrc, absdst)
+    except shutil.Error, inst:
+        raise util.Abort(str(inst))
+
+# public functions
+
+def extract(ui, fileobj):
+    '''extract patch from data read from fileobj.
+
+    patch can be normal patch or contained in email message.
+
+    return tuple (filename, message, user, date). any item in returned
+    tuple can be None.  if filename is None, fileobj did not contain
+    patch. caller must unlink filename when done.'''
+
+    # attempt to detect the start of a patch
+    # (this heuristic is borrowed from quilt)
+    diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
+                        'retrieving revision [0-9]+(\.[0-9]+)*$|' +
+                        '(---|\*\*\*)[ \t])', re.MULTILINE)
+
+    fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
+    tmpfp = os.fdopen(fd, 'w')
+    try:
+        hgpatch = False
+
+        msg = email.Parser.Parser().parse(fileobj)
+
+        message = msg['Subject']
+        user = msg['From']
+        # should try to parse msg['Date']
+        date = None
+
+        if message:
+            message = message.replace('\n\t', ' ')
+            ui.debug('Subject: %s\n' % message)
+        if user:
+            ui.debug('From: %s\n' % user)
+        diffs_seen = 0
+        ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
+
+        for part in msg.walk():
+            content_type = part.get_content_type()
+            ui.debug('Content-Type: %s\n' % content_type)
+            if content_type not in ok_types:
+                continue
+            payload = part.get_payload(decode=True)
+            m = diffre.search(payload)
+            if m:
+                ui.debug(_('found patch at byte %d\n') % m.start(0))
+                diffs_seen += 1
+                cfp = cStringIO.StringIO()
+                if message:
+                    cfp.write(message)
+                    cfp.write('\n')
+                for line in payload[:m.start(0)].splitlines():
+                    if line.startswith('# HG changeset patch'):
+                        ui.debug(_('patch generated by hg export\n'))
+                        hgpatch = True
+                        # drop earlier commit message content
+                        cfp.seek(0)
+                        cfp.truncate()
+                    elif hgpatch:
+                        if line.startswith('# User '):
+                            user = line[7:]
+                            ui.debug('From: %s\n' % user)
+                        elif line.startswith("# Date "):
+                            date = line[7:]
+                    if not line.startswith('# '):
+                        cfp.write(line)
+                        cfp.write('\n')
+                message = cfp.getvalue()
+                if tmpfp:
+                    tmpfp.write(payload)
+                    if not payload.endswith('\n'):
+                        tmpfp.write('\n')
+            elif not diffs_seen and message and content_type == 'text/plain':
+                message += '\n' + payload
+    except:
+        tmpfp.close()
+        os.unlink(tmpname)
+        raise
+
+    tmpfp.close()
+    if not diffs_seen:
+        os.unlink(tmpname)
+        return None, message, user, date
+    return tmpname, message, user, date
+
+def readgitpatch(patchname):
+    """extract git-style metadata about patches from <patchname>"""
+    class gitpatch:
+        "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
+        def __init__(self, path):
+            self.path = path
+            self.oldpath = None
+            self.mode = None
+            self.op = 'MODIFY'
+            self.copymod = False
+            self.lineno = 0
+    
+    # Filter patch for git information
+    gitre = re.compile('diff --git a/(.*) b/(.*)')
+    pf = file(patchname)
+    gp = None
+    gitpatches = []
+    # Can have a git patch with only metadata, causing patch to complain
+    dopatch = False
+
+    lineno = 0
+    for line in pf:
+        lineno += 1
+        if line.startswith('diff --git'):
+            m = gitre.match(line)
+            if m:
+                if gp:
+                    gitpatches.append(gp)
+                src, dst = m.group(1,2)
+                gp = gitpatch(dst)
+                gp.lineno = lineno
+        elif gp:
+            if line.startswith('--- '):
+                if gp.op in ('COPY', 'RENAME'):
+                    gp.copymod = True
+                    dopatch = 'filter'
+                gitpatches.append(gp)
+                gp = None
+                if not dopatch:
+                    dopatch = True
+                continue
+            if line.startswith('rename from '):
+                gp.op = 'RENAME'
+                gp.oldpath = line[12:].rstrip()
+            elif line.startswith('rename to '):
+                gp.path = line[10:].rstrip()
+            elif line.startswith('copy from '):
+                gp.op = 'COPY'
+                gp.oldpath = line[10:].rstrip()
+            elif line.startswith('copy to '):
+                gp.path = line[8:].rstrip()
+            elif line.startswith('deleted file'):
+                gp.op = 'DELETE'
+            elif line.startswith('new file mode '):
+                gp.op = 'ADD'
+                gp.mode = int(line.rstrip()[-3:], 8)
+            elif line.startswith('new mode '):
+                gp.mode = int(line.rstrip()[-3:], 8)
+    if gp:
+        gitpatches.append(gp)
+
+    if not gitpatches:
+        dopatch = True
+
+    return (dopatch, gitpatches)
+
+def dogitpatch(patchname, gitpatches):
+    """Preprocess git patch so that vanilla patch can handle it"""
+    pf = file(patchname)
+    pfline = 1
+
+    fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
+    tmpfp = os.fdopen(fd, 'w')
+
+    try:
+        for i in range(len(gitpatches)):
+            p = gitpatches[i]
+            if not p.copymod:
+                continue
+
+            copyfile(p.oldpath, p.path)
+
+            # rewrite patch hunk
+            while pfline < p.lineno:
+                tmpfp.write(pf.readline())
+                pfline += 1
+            tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
+            line = pf.readline()
+            pfline += 1
+            while not line.startswith('--- a/'):
+                tmpfp.write(line)
+                line = pf.readline()
+                pfline += 1
+            tmpfp.write('--- a/%s\n' % p.path)
+
+        line = pf.readline()
+        while line:
+            tmpfp.write(line)
+            line = pf.readline()
+    except:
+        tmpfp.close()
+        os.unlink(patchname)
+        raise
+
+    tmpfp.close()
+    return patchname
+
+def patch(patchname, ui, strip=1, cwd=None):
+    """apply the patch <patchname> to the working directory.
+    a list of patched files is returned"""
+
+    (dopatch, gitpatches) = readgitpatch(patchname)
+
+    files = {}
+    fuzz = False
+    if dopatch:
+        if dopatch == 'filter':
+            patchname = dogitpatch(patchname, gitpatches)
+        patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
+        args = []
+        if cwd:
+            args.append('-d %s' % util.shellquote(cwd))
+        fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
+                                           util.shellquote(patchname)))
+
+        if dopatch == 'filter':
+            False and os.unlink(patchname)
+
+        for line in fp:
+            line = line.rstrip()
+            ui.note(line + '\n')
+            if line.startswith('patching file '):
+                pf = util.parse_patch_output(line)
+                printed_file = False
+                files.setdefault(pf, (None, None))
+            elif line.find('with fuzz') >= 0:
+                fuzz = True
+                if not printed_file:
+                    ui.warn(pf + '\n')
+                    printed_file = True
+                ui.warn(line + '\n')
+            elif line.find('saving rejects to file') >= 0:
+                ui.warn(line + '\n')
+            elif line.find('FAILED') >= 0:
+                if not printed_file:
+                    ui.warn(pf + '\n')
+                    printed_file = True
+                ui.warn(line + '\n')
+            
+        code = fp.close()
+        if code:
+            raise util.Abort(_("patch command failed: %s") %
+                             util.explain_exit(code)[0])
+
+    for gp in gitpatches:
+        files[gp.path] = (gp.op, gp)
+
+    return (files, fuzz)
+
+def diffopts(ui, opts={}):
+    return mdiff.diffopts(
+        text=opts.get('text'),
+        git=(opts.get('git') or
+                  ui.configbool('diff', 'git', 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)))
+
+def updatedir(ui, repo, patches, wlock=None):
+    '''Update dirstate after patch application according to metadata'''
+    if not patches:
+        return
+    copies = []
+    removes = []
+    cfiles = patches.keys()
+    copts = {'after': False, 'force': False}
+    cwd = repo.getcwd()
+    if cwd:
+        cfiles = [util.pathto(cwd, f) for f in patches.keys()]
+    for f in patches:
+        ctype, gp = patches[f]
+        if ctype == 'RENAME':
+            copies.append((gp.oldpath, gp.path, gp.copymod))
+            removes.append(gp.oldpath)
+        elif ctype == 'COPY':
+            copies.append((gp.oldpath, gp.path, gp.copymod))
+        elif ctype == 'DELETE':
+            removes.append(gp.path)
+    for src, dst, after in copies:
+        if not after:
+            copyfile(src, dst, repo.root)
+        repo.copy(src, dst, wlock=wlock)
+    if removes:
+        repo.remove(removes, True, wlock=wlock)
+    for f in patches:
+        ctype, gp = patches[f]
+        if gp and gp.mode:
+            x = gp.mode & 0100 != 0
+            dst = os.path.join(repo.root, gp.path)
+            util.set_exec(dst, x)
+    cmdutil.addremove(repo, cfiles, wlock=wlock)
+    files = patches.keys()
+    files.extend([r for r in removes if r not in files])
+    files.sort()
+
+    return files
+
+def diff(repo, node1=None, node2=None, files=None, match=util.always,
+         fp=None, changes=None, opts=None):
+    '''print diff of changes to files between two nodes, or node and
+    working directory.
+
+    if node1 is None, use first dirstate parent instead.
+    if node2 is None, compare node1 with working directory.'''
+
+    if opts is None:
+        opts = mdiff.defaultopts
+    if fp is None:
+        fp = repo.ui
+
+    if not node1:
+        node1 = repo.dirstate.parents()[0]
+
+    clcache = {}
+    def getchangelog(n):
+        if n not in clcache:
+            clcache[n] = repo.changelog.read(n)
+        return clcache[n]
+    mcache = {}
+    def getmanifest(n):
+        if n not in mcache:
+            mcache[n] = repo.manifest.read(n)
+        return mcache[n]
+    fcache = {}
+    def getfile(f):
+        if f not in fcache:
+            fcache[f] = repo.file(f)
+        return fcache[f]
+
+    # reading the data for node1 early allows it to play nicely
+    # with repo.status and the revlog cache.
+    change = getchangelog(node1)
+    mmap = getmanifest(change[0])
+    date1 = util.datestr(change[2])
+
+    if not changes:
+        changes = repo.status(node1, node2, files, match=match)[:5]
+    modified, added, removed, deleted, unknown = changes
+    if files:
+        def filterfiles(filters):
+            l = [x for x in filters if x in files]
+
+            for t in files:
+                if not t.endswith("/"):
+                    t += "/"
+                l += [x for x in filters if x.startswith(t)]
+            return l
+
+        modified, added, removed = map(filterfiles, (modified, added, removed))
+
+    if not modified and not added and not removed:
+        return
+
+    def renamedbetween(f, n1, n2):
+        r1, r2 = map(repo.changelog.rev, (n1, n2))
+        src = None
+        while r2 > r1:
+            cl = getchangelog(n2)[0]
+            m = getmanifest(cl)
+            try:
+                src = getfile(f).renamed(m[f])
+            except KeyError:
+                return None
+            if src:
+                f = src[0]
+            n2 = repo.changelog.parents(n2)[0]
+            r2 = repo.changelog.rev(n2)
+        return src
+
+    if node2:
+        change = getchangelog(node2)
+        mmap2 = getmanifest(change[0])
+        _date2 = util.datestr(change[2])
+        def date2(f):
+            return _date2
+        def read(f):
+            return getfile(f).read(mmap2[f])
+        def renamed(f):
+            return renamedbetween(f, node1, node2)
+    else:
+        tz = util.makedate()[1]
+        _date2 = util.datestr()
+        def date2(f):
+            try:
+                return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
+            except OSError, err:
+                if err.errno != errno.ENOENT: raise
+                return _date2
+        def read(f):
+            return repo.wread(f)
+        def renamed(f):
+            src = repo.dirstate.copies.get(f)
+            parent = repo.dirstate.parents()[0]
+            if src:
+                f = src[0]
+            of = renamedbetween(f, node1, parent)
+            if of:
+                return of
+            elif src:
+                cl = getchangelog(parent)[0]
+                return (src, getmanifest(cl)[src])
+            else:
+                return None
+
+    if repo.ui.quiet:
+        r = None
+    else:
+        hexfunc = repo.ui.verbose and hex or short
+        r = [hexfunc(node) for node in [node1, node2] if node]
+
+    if opts.git:
+        copied = {}
+        for f in added:
+            src = renamed(f)
+            if src:
+                copied[f] = src
+        srcs = [x[1][0] for x in copied.items()]
+
+    all = modified + added + removed
+    all.sort()
+    for f in all:
+        to = None
+        tn = None
+        dodiff = True
+        if f in mmap:
+            to = getfile(f).read(mmap[f])
+        if f not in removed:
+            tn = read(f)
+        if opts.git:
+            def gitmode(x):
+                return x and '100755' or '100644'
+            def addmodehdr(header, omode, nmode):
+                if omode != nmode:
+                    header.append('old mode %s\n' % omode)
+                    header.append('new mode %s\n' % nmode)
+
+            a, b = f, f
+            header = []
+            if f in added:
+                if node2:
+                    mode = gitmode(mmap2.execf(f))
+                else:
+                    mode = gitmode(util.is_exec(repo.wjoin(f), None))
+                if f in copied:
+                    a, arev = copied[f]
+                    omode = gitmode(mmap.execf(a))
+                    addmodehdr(header, omode, mode)
+                    op = a in removed and 'rename' or 'copy'
+                    header.append('%s from %s\n' % (op, a))
+                    header.append('%s to %s\n' % (op, f))
+                    to = getfile(a).read(arev)
+                else:
+                    header.append('new file mode %s\n' % mode)
+            elif f in removed:
+                if f in srcs:
+                    dodiff = False
+                else:
+                    mode = gitmode(mmap.execf(f))
+                    header.append('deleted file mode %s\n' % mode)
+            else:
+                omode = gitmode(mmap.execf(f))
+                nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
+                addmodehdr(header, omode, nmode)
+            r = None
+            if dodiff:
+                header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
+                fp.write(''.join(header))
+        if dodiff:
+            fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts))
+
+def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
+           opts=None):
+    '''export changesets as hg patches.'''
+
+    total = len(revs)
+    revwidth = max(map(len, revs))
+
+    def single(node, seqno, fp):
+        parents = [p for p in repo.changelog.parents(node) if p != nullid]
+        if switch_parent:
+            parents.reverse()
+        prev = (parents and parents[0]) or nullid
+        change = repo.changelog.read(node)
+
+        if not fp:
+            fp = cmdutil.make_file(repo, template, node, total=total,
+                                   seqno=seqno, revwidth=revwidth)
+        if fp not in (sys.stdout, repo.ui):
+            repo.ui.note("%s\n" % fp.name)
+
+        fp.write("# HG changeset patch\n")
+        fp.write("# User %s\n" % change[1])
+        fp.write("# Date %d %d\n" % change[2])
+        fp.write("# Node ID %s\n" % hex(node))
+        fp.write("# Parent  %s\n" % hex(prev))
+        if len(parents) > 1:
+            fp.write("# Parent  %s\n" % hex(parents[1]))
+        fp.write(change[4].rstrip())
+        fp.write("\n\n")
+
+        diff(repo, prev, node, fp=fp, opts=opts)
+        if fp not in (sys.stdout, repo.ui):
+            fp.close()
+
+    for seqno, cset in enumerate(revs):
+        single(cset, seqno, fp)
--- a/mercurial/remoterepo.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/remoterepo.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
-# remoterepo - remote repositort proxy classes for mercurial
+# remoterepo - remote repository proxy classes for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
--- a/mercurial/repo.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/repo.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,7 @@
 # repo.py - repository base classes for mercurial
 #
 # Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
--- a/mercurial/revlog.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/revlog.py	Fri Aug 18 21:17:28 2006 -0700
@@ -4,7 +4,7 @@
 This provides efficient delta storage with O(1) retrieve and append
 and O(changes) merge between branches
 
-Copyright 2005 Matt Mackall <mpm@selenic.com>
+Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 
 This software may be used and distributed according to the terms
 of the GNU General Public License, incorporated herein by reference.
@@ -469,7 +469,8 @@
             return self.nodemap[node]
         except KeyError:
             raise RevlogError(_('%s: no node %s') % (self.indexfile, hex(node)))
-    def linkrev(self, node): return self.index[self.rev(node)][-4]
+    def linkrev(self, node):
+        return (node == nullid) and -1 or self.index[self.rev(node)][-4]
     def parents(self, node):
         if node == nullid: return (nullid, nullid)
         r = self.rev(node)
@@ -743,13 +744,8 @@
 
     def lookup(self, id):
         """locate a node based on revision number or subset of hex nodeid"""
-        if id in self.nodemap:
-            return id
         if type(id) == type(0):
-            rev = id
-            if rev < 0: rev = self.count() + rev
-            if rev < 0 or rev >= self.count(): return None
-            return self.node(rev)
+            return self.node(id)
         try:
             rev = int(id)
             if str(rev) != id: raise ValueError
@@ -762,10 +758,26 @@
                 if hex(n).startswith(id):
                     c.append(n)
             if len(c) > 1: raise RevlogError(_("Ambiguous identifier"))
-            if len(c) < 1: raise RevlogError(_("No match found"))
-            return c[0]
+            if len(c) == 1: return c[0]
+
+        # might need fixing if we change hash lengths
+        if len(id) == 20 and id in self.nodemap:
+            return id
+
+        raise RevlogError(_("No match found"))
 
-        return None
+    def cmp(self, node, text):
+        """compare text with a given file revision"""
+        p1, p2 = self.parents(node)
+        return hash(text, p1, p2) != node
+
+    def makenode(self, node, text):
+        """calculate a file nodeid for text, descended or possibly
+        unchanged from node"""
+
+        if self.cmp(node, text):
+            return hash(text, node, nullid)
+        return node
 
     def diff(self, a, b):
         """return a delta between two revisions"""
--- a/mercurial/sshrepo.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/sshrepo.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 # sshrepo.py - ssh repository proxy class for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -13,7 +13,7 @@
 
 class sshrepository(remoterepository):
     def __init__(self, ui, path, create=0):
-        self.url = path
+        self._url = path
         self.ui = ui
 
         m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
@@ -48,6 +48,9 @@
 
         self.validate_repo(ui, sshcmd, args, remotecmd)
 
+    def url(self):
+        return self._url
+
     def validate_repo(self, ui, sshcmd, args, remotecmd):
         cmd = '%s %s "%s -R %s serve --stdio"'
         cmd = cmd % (sshcmd, args, remotecmd, self.path)
@@ -180,7 +183,7 @@
             return 1
         return int(r)
 
-    def addchangegroup(self, cg, source):
+    def addchangegroup(self, cg, source, url):
         d = self.call("addchangegroup")
         if d:
             raise hg.RepoError(_("push refused: %s") % d)
@@ -201,3 +204,5 @@
 
     def stream_out(self):
         return self.do_cmd('stream_out')
+
+instance = sshrepository
--- a/mercurial/sshserver.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/sshserver.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,7 @@
 # sshserver.py - ssh protocol server support for mercurial
 #
 # Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
@@ -117,9 +118,13 @@
             return
 
         self.respond("")
-        r = self.repo.addchangegroup(self.fin, 'serve')
+        r = self.repo.addchangegroup(self.fin, 'serve', self.client_url())
         self.respond(str(r))
 
+    def client_url(self):
+        client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
+        return 'remote:ssh:' + client
+        
     def do_unbundle(self):
         their_heads = self.getarg()[1].split()
 
@@ -159,7 +164,7 @@
                 # push can proceed
 
                 fp.seek(0)
-                r = self.repo.addchangegroup(fp, 'serve')
+                r = self.repo.addchangegroup(fp, 'serve', self.client_url())
                 self.respond(str(r))
             finally:
                 if not was_locked:
--- a/mercurial/statichttprepo.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/statichttprepo.py	Fri Aug 18 21:17:28 2006 -0700
@@ -2,14 +2,15 @@
 #
 # This provides read-only repo access to repositories exported via static http
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-from demandload import demandload
+from demandload import *
+from i18n import gettext as _
 demandload(globals(), "changelog filelog httprangereader")
-demandload(globals(), "localrepo manifest os urllib urllib2")
+demandload(globals(), "localrepo manifest os urllib urllib2 util")
 
 class rangereader(httprangereader.httprangereader):
     def read(self, size=None):
@@ -30,6 +31,7 @@
 
 class statichttprepository(localrepo.localrepository):
     def __init__(self, ui, path):
+        self._url = path
         self.path = (path + "/.hg")
         self.ui = ui
         self.revlogversion = 0
@@ -41,8 +43,22 @@
         self.encodepats = None
         self.decodepats = None
 
+    def url(self):
+        return 'static-' + self._url
+
     def dev(self):
         return -1
 
     def local(self):
         return False
+
+def instance(ui, path, create):
+    if create:
+        raise util.Abort(_('cannot create new static-http repository'))
+    if path.startswith('old-http:'):
+        ui.warn(_("old-http:// syntax is deprecated, "
+                  "please use static-http:// instead\n"))
+        path = path[4:]
+    else:
+        path = path[7:]
+    return statichttprepository(ui, path)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/strutil.py	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,34 @@
+# strutil.py - string utilities for Mercurial
+#
+# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+def findall(haystack, needle, start=0, end=None):
+    if end is None:
+        end = len(haystack)
+    if end < 0:
+        end += len(haystack)
+    if start < 0:
+        start += len(haystack)
+    while start < end:
+        c = haystack.find(needle, start, end)
+        if c == -1:
+            break
+        yield c
+        start = c + 1
+
+def rfindall(haystack, needle, start=0, end=None):
+    if end is None:
+        end = len(haystack)
+    if end < 0:
+        end += len(haystack)
+    if start < 0:
+        start += len(haystack)
+    while end >= 0:
+        c = haystack.rfind(needle, start, end)
+        if c == -1:
+            break
+        yield c
+        end = c - 1
--- a/mercurial/templater.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/templater.py	Fri Aug 18 21:17:28 2006 -0700
@@ -241,6 +241,7 @@
     return text.replace('\n', '<br/>\n')
 
 def obfuscate(text):
+    text = unicode(text, 'utf-8', 'replace')
     return ''.join(['&#%d;' % ord(c) for c in text])
 
 def domain(author):
@@ -458,7 +459,7 @@
                 yield x
 
         if self.ui.debugflag:
-            files = self.repo.changes(log.parents(changenode)[0], changenode)
+            files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
             def showfiles(**args):
                 for x in showlist('file', files[0], **args): yield x
             def showadds(**args):
--- a/mercurial/transaction.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/transaction.py	Fri Aug 18 21:17:28 2006 -0700
@@ -6,7 +6,7 @@
 # effectively log-structured, this should amount to simply truncating
 # anything that isn't referenced in the changelog.
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
--- a/mercurial/ui.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/ui.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,22 +1,24 @@
 # ui.py - user interface bits for mercurial
 #
-# Copyright 2005 Matt Mackall <mpm@selenic.com>
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
 from i18n import gettext as _
 from demandload import *
-demandload(globals(), "errno getpass os re smtplib socket sys tempfile")
-demandload(globals(), "ConfigParser templater traceback util")
+demandload(globals(), "errno getpass os re socket sys tempfile")
+demandload(globals(), "ConfigParser mdiff templater traceback util")
 
 class ui(object):
     def __init__(self, verbose=False, debug=False, quiet=False,
-                 interactive=True, traceback=False, parentui=None):
+                 interactive=True, traceback=False, parentui=None,
+                 readhooks=[]):
         self.overlay = {}
         if parentui is None:
             # this is the parent of all ui children
             self.parentui = None
+            self.readhooks = list(readhooks)
             self.cdata = ConfigParser.SafeConfigParser()
             self.readconfig(util.rcpath())
 
@@ -34,6 +36,7 @@
         else:
             # parentui may point to an ui object which is already a child
             self.parentui = parentui.parentui or parentui
+            self.readhooks = list(parentui.readhooks or readhooks)
             parent_cdata = self.parentui.cdata
             self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
             # make interpolation work
@@ -78,6 +81,8 @@
         for name, path in self.configitems("paths"):
             if path and "://" not in path and not os.path.isabs(path):
                 self.cdata.set("paths", name, os.path.join(root, path))
+        for hook in self.readhooks:
+            hook(self)
 
     def setconfig(self, section, name, val):
         self.overlay[(section, name)] = val
@@ -169,17 +174,6 @@
             result[key.lower()] = value
         return result
 
-    def diffopts(self):
-        if self.diffcache:
-            return self.diffcache
-        result = {'showfunc': True, 'ignorews': False,
-                  'ignorewsamount': False, 'ignoreblanklines': False}
-        for key, value in self.configitems("diff"):
-            if value:
-                result[key.lower()] = (value.lower() == 'true')
-        self.diffcache = result
-        return result
-
     def username(self):
         """Return default username to be used in commits.
 
@@ -197,7 +191,7 @@
             user = os.environ.get("EMAIL")
         if user is None:
             try:
-                user = '%s@%s' % (getpass.getuser(), socket.getfqdn())
+                user = '%s@%s' % (util.getuser(), socket.getfqdn())
             except KeyError:
                 raise util.Abort(_("Please specify a username."))
         return user
@@ -217,12 +211,6 @@
             path = self.config("paths", default)
         return path or loc
 
-    def setconfig_remoteopts(self, **opts):
-        if opts.get('ssh'):
-            self.setconfig("ui", "ssh", opts['ssh'])
-        if opts.get('remotecmd'):
-            self.setconfig("ui", "remotecmd", opts['remotecmd'])
-
     def write(self, *args):
         if self.header:
             if self.header != self.prev_header:
@@ -298,62 +286,6 @@
 
         return t
 
-    def sendmail(self):
-        '''send mail message. object returned has one method, sendmail.
-        call as sendmail(sender, list-of-recipients, msg).'''
-
-        def smtp():
-            '''send mail using smtp.'''
-
-            local_hostname = self.config('smtp', 'local_hostname')
-            s = smtplib.SMTP(local_hostname=local_hostname)
-            mailhost = self.config('smtp', 'host')
-            if not mailhost:
-                raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
-            mailport = int(self.config('smtp', 'port', 25))
-            self.note(_('sending mail: smtp host %s, port %s\n') %
-                      (mailhost, mailport))
-            s.connect(host=mailhost, port=mailport)
-            if self.configbool('smtp', 'tls'):
-                self.note(_('(using tls)\n'))
-                s.ehlo()
-                s.starttls()
-                s.ehlo()
-            username = self.config('smtp', 'username')
-            password = self.config('smtp', 'password')
-            if username and password:
-                self.note(_('(authenticating to mail server as %s)\n') %
-                          (username))
-                s.login(username, password)
-            return s
-
-        class sendmail(object):
-            '''send mail using sendmail.'''
-
-            def __init__(self, ui, program):
-                self.ui = ui
-                self.program = program
-
-            def sendmail(self, sender, recipients, msg):
-                cmdline = '%s -f %s %s' % (
-                    self.program, templater.email(sender),
-                    ' '.join(map(templater.email, recipients)))
-                self.ui.note(_('sending mail: %s\n') % cmdline)
-                fp = os.popen(cmdline, 'w')
-                fp.write(msg)
-                ret = fp.close()
-                if ret:
-                    raise util.Abort('%s %s' % (
-                        os.path.basename(self.program.split(None, 1)[0]),
-                        util.explain_exit(ret)[0]))
-
-        method = self.config('email', 'method', 'smtp')
-        if method == 'smtp':
-            mail = smtp()
-        else:
-            mail = sendmail(self, method)
-        return mail
-
     def print_exc(self):
         '''print exception traceback if traceback printing enabled.
         only to call in exception handler. returns true if traceback
--- a/mercurial/util.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/util.py	Fri Aug 18 21:17:28 2006 -0700
@@ -2,6 +2,8 @@
 util.py - Mercurial utility functions and platform specfic implementations
 
  Copyright 2005 K. Thananchayan <thananck@yahoo.com>
+ Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
+ Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
 
 This software may be used and distributed according to the terms
 of the GNU General Public License, incorporated herein by reference.
@@ -12,7 +14,7 @@
 
 from i18n import gettext as _
 from demandload import *
-demandload(globals(), "cStringIO errno popen2 re shutil sys tempfile")
+demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
 demandload(globals(), "os threading time")
 
 # used by parsedate
@@ -93,23 +95,6 @@
             return p_name
     return default
 
-def patch(strip, patchname, ui):
-    """apply the patch <patchname> to the working directory.
-    a list of patched files is returned"""
-    patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
-    fp = os.popen('%s -p%d < "%s"' % (patcher, strip, patchname))
-    files = {}
-    for line in fp:
-        line = line.rstrip()
-        ui.status("%s\n" % line)
-        if line.startswith('patching file '):
-            pf = parse_patch_output(line)
-            files.setdefault(pf, 1)
-    code = fp.close()
-    if code:
-        raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
-    return files.keys()
-
 def binary(s):
     """return true if a string is binary data using diff's heuristic"""
     if s and '\0' in s[:4096]:
@@ -510,6 +495,20 @@
     except AttributeError:
         return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
 
+getuser_fallback = None
+
+def getuser():
+    '''return name of current user'''
+    try:
+        return getpass.getuser()
+    except ImportError:
+        # import of pwd will fail on windows - try fallback
+        if getuser_fallback:
+            return getuser_fallback()
+    # raised if win32api not available
+    raise Abort(_('user name not available - set USERNAME '
+                  'environment variable'))
+
 # Platform specific variants
 if os.name == 'nt':
     demandload(globals(), "msvcrt")
@@ -593,6 +592,9 @@
     def samestat(s1, s2):
         return False
 
+    def shellquote(s):
+        return '"%s"' % s.replace('"', '\\"')
+
     def explain_exit(code):
         return _("exited with status %d") % code, code
 
@@ -682,6 +684,9 @@
             else:
                 raise
 
+    def shellquote(s):
+        return "'%s'" % s.replace("'", "'\\''")
+
     def testpid(pid):
         '''return False if pid dead, True if running or not sure'''
         try:
@@ -982,3 +987,11 @@
         if nbytes >= divisor * multiplier:
             return format % (nbytes / float(divisor))
     return units[-1][2] % nbytes
+
+def drop_scheme(scheme, path):
+    sc = scheme + ':'
+    if path.startswith(sc):
+        path = path[len(sc):]
+        if path.startswith('//'):
+            path = path[2:]
+    return path
--- a/mercurial/util_win32.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/util_win32.py	Fri Aug 18 21:17:28 2006 -0700
@@ -297,3 +297,5 @@
             win32file.SetEndOfFile(self.handle)
         except pywintypes.error, err:
             raise WinIOError(err)
+
+getuser_fallback = win32api.GetUserName
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/verify.py	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,200 @@
+# verify.py - repository integrity checking for Mercurial
+#
+# Copyright 2006 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+from node import *
+from i18n import gettext as _
+import revlog, mdiff
+
+def verify(repo):
+    filelinkrevs = {}
+    filenodes = {}
+    changesets = revisions = files = 0
+    errors = [0]
+    warnings = [0]
+    neededmanifests = {}
+
+    def err(msg):
+        repo.ui.warn(msg + "\n")
+        errors[0] += 1
+
+    def warn(msg):
+        repo.ui.warn(msg + "\n")
+        warnings[0] += 1
+
+    def checksize(obj, name):
+        d = obj.checksize()
+        if d[0]:
+            err(_("%s data length off by %d bytes") % (name, d[0]))
+        if d[1]:
+            err(_("%s index contains %d extra bytes") % (name, d[1]))
+
+    def checkversion(obj, name):
+        if obj.version != revlog.REVLOGV0:
+            if not revlogv1:
+                warn(_("warning: `%s' uses revlog format 1") % name)
+        elif revlogv1:
+            warn(_("warning: `%s' uses revlog format 0") % name)
+
+    revlogv1 = repo.revlogversion != revlog.REVLOGV0
+    if repo.ui.verbose or revlogv1 != repo.revlogv1:
+        repo.ui.status(_("repository uses revlog format %d\n") %
+                       (revlogv1 and 1 or 0))
+
+    seen = {}
+    repo.ui.status(_("checking changesets\n"))
+    checksize(repo.changelog, "changelog")
+
+    for i in range(repo.changelog.count()):
+        changesets += 1
+        n = repo.changelog.node(i)
+        l = repo.changelog.linkrev(n)
+        if l != i:
+            err(_("incorrect link (%d) for changeset revision %d") %(l, i))
+        if n in seen:
+            err(_("duplicate changeset at revision %d") % i)
+        seen[n] = 1
+
+        for p in repo.changelog.parents(n):
+            if p not in repo.changelog.nodemap:
+                err(_("changeset %s has unknown parent %s") %
+                             (short(n), short(p)))
+        try:
+            changes = repo.changelog.read(n)
+        except KeyboardInterrupt:
+            repo.ui.warn(_("interrupted"))
+            raise
+        except Exception, inst:
+            err(_("unpacking changeset %s: %s") % (short(n), inst))
+            continue
+
+        neededmanifests[changes[0]] = n
+
+        for f in changes[3]:
+            filelinkrevs.setdefault(f, []).append(i)
+
+    seen = {}
+    repo.ui.status(_("checking manifests\n"))
+    checkversion(repo.manifest, "manifest")
+    checksize(repo.manifest, "manifest")
+
+    for i in range(repo.manifest.count()):
+        n = repo.manifest.node(i)
+        l = repo.manifest.linkrev(n)
+
+        if l < 0 or l >= repo.changelog.count():
+            err(_("bad manifest link (%d) at revision %d") % (l, i))
+
+        if n in neededmanifests:
+            del neededmanifests[n]
+
+        if n in seen:
+            err(_("duplicate manifest at revision %d") % i)
+
+        seen[n] = 1
+
+        for p in repo.manifest.parents(n):
+            if p not in repo.manifest.nodemap:
+                err(_("manifest %s has unknown parent %s") %
+                    (short(n), short(p)))
+
+        try:
+            delta = mdiff.patchtext(repo.manifest.delta(n))
+        except KeyboardInterrupt:
+            repo.ui.warn(_("interrupted"))
+            raise
+        except Exception, inst:
+            err(_("unpacking manifest %s: %s") % (short(n), inst))
+            continue
+
+        try:
+            ff = [ l.split('\0') for l in delta.splitlines() ]
+            for f, fn in ff:
+                filenodes.setdefault(f, {})[bin(fn[:40])] = 1
+        except (ValueError, TypeError), inst:
+            err(_("broken delta in manifest %s: %s") % (short(n), inst))
+
+    repo.ui.status(_("crosschecking files in changesets and manifests\n"))
+
+    for m, c in neededmanifests.items():
+        err(_("Changeset %s refers to unknown manifest %s") %
+            (short(m), short(c)))
+    del neededmanifests
+
+    for f in filenodes:
+        if f not in filelinkrevs:
+            err(_("file %s in manifest but not in changesets") % f)
+
+    for f in filelinkrevs:
+        if f not in filenodes:
+            err(_("file %s in changeset but not in manifest") % f)
+
+    repo.ui.status(_("checking files\n"))
+    ff = filenodes.keys()
+    ff.sort()
+    for f in ff:
+        if f == "/dev/null":
+            continue
+        files += 1
+        if not f:
+            err(_("file without name in manifest %s") % short(n))
+            continue
+        fl = repo.file(f)
+        checkversion(fl, f)
+        checksize(fl, f)
+
+        nodes = {nullid: 1}
+        seen = {}
+        for i in range(fl.count()):
+            revisions += 1
+            n = fl.node(i)
+
+            if n in seen:
+                err(_("%s: duplicate revision %d") % (f, i))
+            if n not in filenodes[f]:
+                err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
+            else:
+                del filenodes[f][n]
+
+            flr = fl.linkrev(n)
+            if flr not in filelinkrevs.get(f, []):
+                err(_("%s:%s points to unexpected changeset %d")
+                        % (f, short(n), flr))
+            else:
+                filelinkrevs[f].remove(flr)
+
+            # verify contents
+            try:
+                t = fl.read(n)
+            except KeyboardInterrupt:
+                repo.ui.warn(_("interrupted"))
+                raise
+            except Exception, inst:
+                err(_("unpacking file %s %s: %s") % (f, short(n), inst))
+
+            # verify parents
+            (p1, p2) = fl.parents(n)
+            if p1 not in nodes:
+                err(_("file %s:%s unknown parent 1 %s") %
+                    (f, short(n), short(p1)))
+            if p2 not in nodes:
+                err(_("file %s:%s unknown parent 2 %s") %
+                        (f, short(n), short(p1)))
+            nodes[n] = 1
+
+        # cross-check
+        for node in filenodes[f]:
+            err(_("node %s in manifests not in %s") % (hex(node), f))
+
+    repo.ui.status(_("%d files, %d changesets, %d total revisions\n") %
+                   (files, changesets, revisions))
+
+    if warnings[0]:
+        repo.ui.warn(_("%d warnings encountered!\n") % warnings[0])
+    if errors[0]:
+        repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
+        return 1
+
--- a/mercurial/version.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/mercurial/version.py	Fri Aug 18 21:17:28 2006 -0700
@@ -1,4 +1,4 @@
-# Copyright (C) 2005 by Intevation GmbH
+# Copyright (C) 2005, 2006 by Intevation GmbH
 # Author(s):
 # Thomas Arendsen Hein <thomas@intevation.de>
 #
--- a/templates/changelog-gitweb.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/changelog-gitweb.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -20,7 +20,7 @@
 </div>
 
 <div class="page_nav">
-<a href="?cmd=summary;style=gitweb">summary</a> | changelog | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
+<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;rev=#rev#;style=gitweb">shortlog</a> | changelog | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#<br/>
 <br/>
 #changenav%naventry#<br/>
 </div>
--- a/templates/changelog.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/changelog.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -6,6 +6,7 @@
 <body>
 
 <div class="buttons">
+<a href="?sl=#rev#">shortlog</a>
 <a href="?cmd=tags">tags</a>
 <a href="?mf=#manifest|short#;path=/">manifest</a>
 #archives%archiveentry#
--- a/templates/changeset-gitweb.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/changeset-gitweb.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -10,7 +10,7 @@
 </div>
 
 <div class="page_nav">
-<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;rev=#rev#;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a> | changeset | <a href="?cmd=changeset;node=#node#;style=raw">raw</a> #archives%archiveentry#<br/>
+<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;rev=#rev#;style=gitweb">shortlog</a> | <a href="?cmd=changelog;rev=#rev#;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a> | changeset | <a href="?cmd=changeset;node=#node#;style=raw">raw</a> #archives%archiveentry#<br/>
 </div>
 
 <div>
--- a/templates/changeset.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/changeset.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -5,6 +5,7 @@
 
 <div class="buttons">
 <a href="?cl=#rev#">changelog</a>
+<a href="?sl=#rev#">shortlog</a>
 <a href="?cmd=tags">tags</a>
 <a href="?mf=#manifest|short#;path=/">manifest</a>
 <a href="?cs=#node|short#;style=raw">raw</a>
--- a/templates/error-gitweb.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/error-gitweb.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -10,7 +10,7 @@
 </div>
 
 <div class="page_nav">
-<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
+<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
 </div>
 
 <div>
--- a/templates/fileannotate-gitweb.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/fileannotate-gitweb.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -10,7 +10,7 @@
 </div>
 
 <div class="page_nav">
-<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | annotate | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/>
+<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | annotate | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/>
 </div>
 
 <div class="title">#file|escape#</div>
--- a/templates/fileannotate.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/fileannotate.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -5,6 +5,7 @@
 
 <div class="buttons">
 <a href="?cl=#rev#">changelog</a>
+<a href="?sl=#rev#">shortlog</a>
 <a href="?tags=">tags</a>
 <a href="?cs=#node|short#">changeset</a>
 <a href="?mf=#manifest|short#;path=#path|urlescape#">manifest</a>
--- a/templates/filediff.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/filediff.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -5,6 +5,7 @@
 
 <div class="buttons">
 <a href="?cl=#rev#">changelog</a>
+<a href="?sl=#rev#">shortlog</a>
 <a href="?tags=">tags</a>
 <a href="?cs=#node|short#">changeset</a>
 <a href="?f=#filenode|short#;file=#file|urlescape#">file</a>
--- a/templates/filelog-gitweb.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/filelog-gitweb.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -10,7 +10,7 @@
 </div>
 
 <div class="page_nav">
-<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | revisions | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?fl=#filenode|short#;file=#file|urlescape#;style=rss">rss</a><br/>
+<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=gitweb">file</a> | revisions | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?fl=#filenode|short#;file=#file|urlescape#;style=rss">rss</a><br/>
 </div>
 
 <div class="title" >#file|urlescape#</div>
--- a/templates/filelog.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/filelog.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -8,6 +8,7 @@
 
 <div class="buttons">
 <a href="?cl=tip">changelog</a>
+<a href="?sl=tip">shortlog</a>
 <a href="?tags=">tags</a>
 <a href="?f=#filenode|short#;file=#file|urlescape#">file</a>
 <a href="?fa=#filenode|short#;file=#file|urlescape#">annotate</a>
--- a/templates/filerevision-gitweb.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/filerevision-gitweb.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -10,7 +10,7 @@
 </div>
 
 <div class="page_nav">
-<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?mf=#manifest|short#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | file | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/>
+<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?mf=#manifest|short#;path=#path|urlescape#;style=gitweb">manifest</a> | <a href="?cmd=changeset;node=#node#;style=gitweb">changeset</a> | file | <a href="?cmd=filelog;file=#file|urlescape#;filenode=#filenode#;style=gitweb">revisions</a> | <a href="?cmd=annotate;file=#file|urlescape#;filenode=#filenode#;style=gitweb">annotate</a> | <a href="?cmd=file;file=#file|urlescape#;filenode=#filenode#;style=raw">raw</a><br/>
 </div>
 
 <div class="title">#file|escape#</div>
--- a/templates/filerevision.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/filerevision.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -5,6 +5,7 @@
 
 <div class="buttons">
 <a href="?cl=#rev#">changelog</a>
+<a href="?sl=#rev#">shortlog</a>
 <a href="?tags=">tags</a>
 <a href="?cs=#node|short#">changeset</a>
 <a href="?mf=#manifest|short#;path=#path|urlescape#">manifest</a>
--- a/templates/manifest-gitweb.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/manifest-gitweb.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -10,7 +10,7 @@
 </div>
 
 <div class="page_nav">
-<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | manifest | <a href="?cs=#node|short#;style=gitweb">changeset</a> #archives%archiveentry#<br/>
+<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | manifest | <a href="?cs=#node|short#;style=gitweb">changeset</a> #archives%archiveentry#<br/>
 </div>
 
 <div class="title" >#path|escape#</div>
--- a/templates/manifest.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/manifest.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -5,6 +5,7 @@
 
 <div class="buttons">
 <a href="?cl=#rev#">changelog</a>
+<a href="?sl=#rev#">shortlog</a>
 <a href="?tags=">tags</a>
 <a href="?cs=#node|short#">changeset</a>
 #archives%archiveentry#
--- a/templates/map	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/map	Fri Aug 18 21:17:28 2006 -0700
@@ -3,7 +3,10 @@
 footer = footer.tmpl
 search = search.tmpl
 changelog = changelog.tmpl
+shortlog = shortlog.tmpl
+shortlogentry = shortlogentry.tmpl
 naventry = '<a href="?cl=#rev#">#label|escape#</a> '
+navshortentry = '<a href="?sl=#rev#">#label|escape#</a> '
 filedifflink = '<a href="?fd=#node|short#;file=#file|urlescape#">#file|escape#</a> '
 filenodelink = '<a href="?f=#filenode|short#;file=#file|urlescape#">#file|escape#</a> '
 fileellipses = '...'
--- a/templates/search-gitweb.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/search-gitweb.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,6 @@
 #header#
 <div class="page_nav">
-<a href="?cmd=summary;style=gitweb">summary</a> | log | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
+<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
 </div>
 
 <h2>searching for #query|escape#</h2>
--- a/templates/search.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/search.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -5,6 +5,7 @@
 
 <div class="buttons">
 <a href="?cl=tip">changelog</a>
+<a href="?sl=tip">shortlog</a>
 <a href="?tags=">tags</a>
 <a href="?mf=#manifest|short#;path=/">manifest</a>
 </div>
--- a/templates/shortlog-gitweb.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/shortlog-gitweb.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -1,13 +1,32 @@
 #header#
+<title>#repo|escape#: Shortlog</title>
+<link rel="alternate" type="application/rss+xml"
+   href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
+</head>
+<body>
 
+<div class="page_header">
+<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / shortlog
+</div>
+
+<form action="#">
+<div class="search">
+<input type="hidden" name="repo" value="#repo|escape#"  />
+<input type="hidden" name="style" value="gitweb"  />
+<input type="hidden" name="cmd" value="changelog"  />
+<input type="text" name="rev"  />
+</div>
+</form>
+</div>
 <div class="page_nav">
-<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">log</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
+<a href="?cmd=summary;style=gitweb">summary</a> | shortlog | <a href="?cmd=changelog;rev=#rev#;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#<br/>
+<br/>
 
-#changenav%naventry#<br/>
+#changenav%navshortentry#<br/>
 </div>
 
 <table cellspacing="0">
-#entries#
+#entries%shortlogentry#
 </table>
 
 #footer#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/shortlog.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,38 @@
+#header#
+<title>#repo|escape#: shortlog</title>
+<link rel="alternate" type="application/rss+xml"
+   href="?cmd=changelog;style=rss" title="RSS feed for #repo|escape#">
+</head>
+<body>
+
+<div class="buttons">
+<a href="?cl=#rev#">changelog</a>
+<a href="?cmd=tags">tags</a>
+<a href="?mf=#manifest|short#;path=/">manifest</a>
+#archives%archiveentry#
+<a type="application/rss+xml" href="?style=rss">rss</a>
+</div>
+
+<h2>shortlog for #repo|escape#</h2>
+
+<form action="#">
+<p>
+<label for="search1">search:</label>
+<input type="hidden" name="cmd" value="changelog">
+<input name="rev" id="search1" type="text" size="30">
+navigate: <small class="navigate">#changenav%navshortentry#</small>
+</p>
+</form>
+
+#entries%shortlogentry#
+
+<form action="#">
+<p>
+<label for="search2">search:</label>
+<input type="hidden" name="cmd" value="changelog">
+<input name="rev" id="search2" type="text" size="30">
+navigate: <small class="navigate">#changenav%navshortentry#</small>
+</p>
+</form>
+
+#footer#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/shortlogentry.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,7 @@
+<table class="slogEntry parity#parity#">
+ <tr>
+  <td class="age">#date|age#</td>
+  <td class="author">#author|obfuscate#</td>
+  <td class="node"><a href="?cs=#node|short#">#desc|strip|firstline|escape#</a></td>
+ </tr>
+</table>
--- a/templates/static/style.css	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/static/style.css	Fri Aug 18 21:17:28 2006 -0700
@@ -57,6 +57,12 @@
 .logEntry th.age, .logEntry th.firstline { font-weight: bold; }
 .logEntry th.firstline { text-align: left; width: inherit; }
 
+/* Shortlog entries */
+.slogEntry { width: 100%; font-size: smaller; }
+.slogEntry .age { width: 7%; }
+.slogEntry td { font-weight: normal; text-align: left; vertical-align: top; }
+.slogEntry td.author { width: 35%; }
+
 /* Tag entries */
 #tagEntries { list-style: none; margin: 0; padding: 0; }
 #tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
--- a/templates/summary-gitweb.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/summary-gitweb.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -9,7 +9,8 @@
 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="?cmd=summary;style=gitweb">#repo|escape#</a> / summary
 </div>
 <div class="page_nav">
-summary | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a><br/>
+summary | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | <a href="?cmd=tags;style=gitweb">tags</a> | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>#archives%archiveentry#
+<br/>
 </div>
 
 <div class="title">&nbsp;</div>
--- a/templates/tags-gitweb.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/tags-gitweb.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -10,7 +10,7 @@
 </div>
 
 <div class="page_nav">
-<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | tags | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>
+<a href="?cmd=summary;style=gitweb">summary</a> | <a href="?cmd=shortlog;style=gitweb">shortlog</a> | <a href="?cmd=changelog;style=gitweb">changelog</a> | tags | <a href="?cmd=manifest;manifest=#manifest#;path=/;style=gitweb">manifest</a>
 <br/>
 </div>
 
--- a/templates/tags.tmpl	Sun Jul 23 09:04:14 2006 -0700
+++ b/templates/tags.tmpl	Fri Aug 18 21:17:28 2006 -0700
@@ -7,6 +7,7 @@
 
 <div class="buttons">
 <a href="?cl=tip">changelog</a>
+<a href="?sl=tip">shortlog</a>
 <a href="?mf=#manifest|short#;path=/">manifest</a>
 <a type="application/rss+xml" href="?cmd=tags;style=rss">rss</a>
 </div>
--- a/tests/README	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/README	Fri Aug 18 21:17:28 2006 -0700
@@ -28,6 +28,6 @@
 
 - diff will show the current time
 
-  use hg diff | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" to strip
-  dates
-
+  use hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+                    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+  to strip dates
--- a/tests/run-tests.py	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/run-tests.py	Fri Aug 18 21:17:28 2006 -0700
@@ -25,7 +25,7 @@
 parser.add_option("-v", "--verbose", action="store_true",
     help="output verbose messages")
 parser.add_option("-t", "--timeout", type="int",
-    help="output verbose messages")
+    help="kill errant tests after TIMEOUT seconds")
 parser.add_option("-c", "--cover", action="store_true",
     help="print a test coverage report")
 parser.add_option("-s", "--cover_stdlib", action="store_true",
@@ -201,6 +201,11 @@
     return ret, splitnewlines(output)
 
 def run_one(test):
+    '''tristate output:
+    None -> skipped
+    True -> passed
+    False -> failed'''
+
     vlog("# Test", test)
     if not verbose:
         sys.stdout.write('.')
@@ -217,15 +222,28 @@
     os.mkdir(tmpd)
     os.chdir(tmpd)
 
-    if test.endswith(".py"):
-        cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
-    else:
-        cmd = '"%s"' % (os.path.join(TESTDIR, test))
+    lctest = test.lower()
 
-    # To reliably get the error code from batch files on WinXP,
-    # the "cmd /c call" prefix is needed. Grrr
-    if os.name == 'nt' and test.endswith(".bat"):
+    if lctest.endswith('.py'):
+        cmd = '%s "%s"' % (sys.executable, os.path.join(TESTDIR, test))
+    elif lctest.endswith('.bat'):
+        # do not run batch scripts on non-windows
+        if os.name != 'nt':
+            print '\nSkipping %s: batch script' % test
+            return None
+        # To reliably get the error code from batch files on WinXP,
+        # the "cmd /c call" prefix is needed. Grrr
         cmd = 'cmd /c call "%s"' % (os.path.join(TESTDIR, test))
+    else:
+        # do not run shell scripts on windows
+        if os.name == 'nt':
+            print '\nSkipping %s: shell script' % test
+            return None
+        # do not try to run non-executable programs
+        if not os.access(os.path.join(TESTDIR, test), os.X_OK):
+            print '\nSkipping %s: not executable' % test
+            return None
+        cmd = '"%s"' % (os.path.join(TESTDIR, test))
 
     if options.timeout > 0:
         signal.alarm(options.timeout)
@@ -244,7 +262,7 @@
         ref_out = splitnewlines(f.read())
         f.close()
     else:
-        ref_out = ['']
+        ref_out = []
     if out != ref_out:
         diffret = 1
         print "\nERROR: %s output changed" % (test)
@@ -330,16 +348,23 @@
 
         tests = 0
         failed = 0
+        skipped = 0
 
         if len(args) == 0:
             args = os.listdir(".")
         for test in args:
-            if test.startswith("test-") and not '~' in test and not '.' in test:
-                if not run_one(test):
+            if (test.startswith("test-") and '~' not in test and
+                ('.' not in test or test.endswith('.py') or 
+                 test.endswith('.bat'))):
+                ret = run_one(test)
+                if ret is None:
+                    skipped += 1
+                elif not ret:
                     failed += 1
                 tests += 1
 
-        print "\n# Ran %d tests, %d failed." % (tests, failed)
+        print "\n# Ran %d tests, %d skipped, %d failed." % (tests, skipped,
+                                                            failed)
         if coverage:
             output_coverage()
     except KeyboardInterrupt:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-abort-checkin	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "mq=" >> $HGTMP/.hgrc
+cat > $HGTMP/false <<EOF
+#!/bin/sh
+exit 1
+EOF
+chmod +x $HGTMP/false
+
+hg init foo
+cd foo
+echo foo > foo
+hg add foo
+
+# mq may keep a reference to the repository so __del__ will not be called
+# and .hg/journal.dirstate will not be deleted:
+HGEDITOR=$HGTMP/false hg ci
+HGEDITOR=$HGTMP/false hg ci
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-abort-checkin.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,6 @@
+abort: edit failed: false exited with status 1
+transaction abort!
+rollback completed
+abort: edit failed: false exited with status 1
+transaction abort!
+rollback completed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-annotate	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+echo % init
+hg init
+
+echo % commit
+echo 'a' > a
+hg ci -A -m test -u nobody -d '1 0'
+
+echo % annotate -c
+hg annotate -c a
+
+echo % annotate -d
+hg annotate -d a
+
+echo % annotate -n
+hg annotate -n a
+
+echo % annotate -u
+hg annotate -u a
+
+echo % annotate -cdnu
+hg annotate -cdnu a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-annotate.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,13 @@
+% init
+% commit
+adding a
+% annotate -c
+8435f90966e4: a
+% annotate -d
+Thu Jan 01 00:00:01 1970 +0000: a
+% annotate -n
+0: a
+% annotate -u
+nobody: a
+% annotate -cdnu
+nobody 0 8435f90966e4 Thu Jan 01 00:00:01 1970 +0000: a
--- a/tests/test-backout.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-backout.out	Fri Aug 18 21:17:28 2006 -0700
@@ -27,7 +27,7 @@
 reverting a
 changeset 3:4cbb1e70196a backs out changeset 1:22bca4c721e5
 the backout changeset is a new head - do not forget to merge
-(use "backout -m" if you want to auto-merge)
+(use "backout --merge" if you want to auto-merge)
 b: No such file or directory
 adding a
 adding b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-bisect	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+set -e
+
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "hbisect=" >> $HGTMP/.hgrc
+
+echo % init
+hg init
+
+echo % committing changes
+count=0
+echo > a
+while test $count -lt 32 ; do
+    echo 'a' >> a
+    test $count -eq 0 && hg add
+    hg ci -m "msg $count" -d "$count 0"
+    echo % committed changeset $count
+    count=`expr $count + 1`
+done
+
+echo % log
+hg log
+
+echo % hg up -C
+hg up -C
+
+echo % bisect test
+hg bisect init
+hg bisect bad
+hg bisect good 1
+hg bisect good
+hg bisect good
+hg bisect good
+hg bisect bad
+hg bisect good
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-bisect.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,216 @@
+% init
+% committing changes
+adding a
+% committed changeset 0
+% committed changeset 1
+% committed changeset 2
+% committed changeset 3
+% committed changeset 4
+% committed changeset 5
+% committed changeset 6
+% committed changeset 7
+% committed changeset 8
+% committed changeset 9
+% committed changeset 10
+% committed changeset 11
+% committed changeset 12
+% committed changeset 13
+% committed changeset 14
+% committed changeset 15
+% committed changeset 16
+% committed changeset 17
+% committed changeset 18
+% committed changeset 19
+% committed changeset 20
+% committed changeset 21
+% committed changeset 22
+% committed changeset 23
+% committed changeset 24
+% committed changeset 25
+% committed changeset 26
+% committed changeset 27
+% committed changeset 28
+% committed changeset 29
+% committed changeset 30
+% committed changeset 31
+% log
+changeset:   31:58c80a7c8a40
+tag:         tip
+user:        test
+date:        Thu Jan 01 00:00:31 1970 +0000
+summary:     msg 31
+
+changeset:   30:ed2d2f24b11c
+user:        test
+date:        Thu Jan 01 00:00:30 1970 +0000
+summary:     msg 30
+
+changeset:   29:b5bd63375ab9
+user:        test
+date:        Thu Jan 01 00:00:29 1970 +0000
+summary:     msg 29
+
+changeset:   28:8e0c2264c8af
+user:        test
+date:        Thu Jan 01 00:00:28 1970 +0000
+summary:     msg 28
+
+changeset:   27:288867a866e9
+user:        test
+date:        Thu Jan 01 00:00:27 1970 +0000
+summary:     msg 27
+
+changeset:   26:3efc6fd51aeb
+user:        test
+date:        Thu Jan 01 00:00:26 1970 +0000
+summary:     msg 26
+
+changeset:   25:02a84173a97a
+user:        test
+date:        Thu Jan 01 00:00:25 1970 +0000
+summary:     msg 25
+
+changeset:   24:10e0acd3809e
+user:        test
+date:        Thu Jan 01 00:00:24 1970 +0000
+summary:     msg 24
+
+changeset:   23:5ec79163bff4
+user:        test
+date:        Thu Jan 01 00:00:23 1970 +0000
+summary:     msg 23
+
+changeset:   22:06c7993750ce
+user:        test
+date:        Thu Jan 01 00:00:22 1970 +0000
+summary:     msg 22
+
+changeset:   21:e5db6aa3fe2a
+user:        test
+date:        Thu Jan 01 00:00:21 1970 +0000
+summary:     msg 21
+
+changeset:   20:7128fb4fdbc9
+user:        test
+date:        Thu Jan 01 00:00:20 1970 +0000
+summary:     msg 20
+
+changeset:   19:52798545b482
+user:        test
+date:        Thu Jan 01 00:00:19 1970 +0000
+summary:     msg 19
+
+changeset:   18:86977a90077e
+user:        test
+date:        Thu Jan 01 00:00:18 1970 +0000
+summary:     msg 18
+
+changeset:   17:03515f4a9080
+user:        test
+date:        Thu Jan 01 00:00:17 1970 +0000
+summary:     msg 17
+
+changeset:   16:a2e6ea4973e9
+user:        test
+date:        Thu Jan 01 00:00:16 1970 +0000
+summary:     msg 16
+
+changeset:   15:e7fa0811edb0
+user:        test
+date:        Thu Jan 01 00:00:15 1970 +0000
+summary:     msg 15
+
+changeset:   14:ce8f0998e922
+user:        test
+date:        Thu Jan 01 00:00:14 1970 +0000
+summary:     msg 14
+
+changeset:   13:9d7d07bc967c
+user:        test
+date:        Thu Jan 01 00:00:13 1970 +0000
+summary:     msg 13
+
+changeset:   12:1941b52820a5
+user:        test
+date:        Thu Jan 01 00:00:12 1970 +0000
+summary:     msg 12
+
+changeset:   11:7b4cd9578619
+user:        test
+date:        Thu Jan 01 00:00:11 1970 +0000
+summary:     msg 11
+
+changeset:   10:7c5eff49a6b6
+user:        test
+date:        Thu Jan 01 00:00:10 1970 +0000
+summary:     msg 10
+
+changeset:   9:eb44510ef29a
+user:        test
+date:        Thu Jan 01 00:00:09 1970 +0000
+summary:     msg 9
+
+changeset:   8:453eb4dba229
+user:        test
+date:        Thu Jan 01 00:00:08 1970 +0000
+summary:     msg 8
+
+changeset:   7:03750880c6b5
+user:        test
+date:        Thu Jan 01 00:00:07 1970 +0000
+summary:     msg 7
+
+changeset:   6:a3d5c6fdf0d3
+user:        test
+date:        Thu Jan 01 00:00:06 1970 +0000
+summary:     msg 6
+
+changeset:   5:7874a09ea728
+user:        test
+date:        Thu Jan 01 00:00:05 1970 +0000
+summary:     msg 5
+
+changeset:   4:9b2ba8336a65
+user:        test
+date:        Thu Jan 01 00:00:04 1970 +0000
+summary:     msg 4
+
+changeset:   3:b53bea5e2fcb
+user:        test
+date:        Thu Jan 01 00:00:03 1970 +0000
+summary:     msg 3
+
+changeset:   2:db07c04beaca
+user:        test
+date:        Thu Jan 01 00:00:02 1970 +0000
+summary:     msg 2
+
+changeset:   1:5cd978ea5149
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     msg 1
+
+changeset:   0:b99c7b9c8e11
+user:        test
+date:        Thu Jan 01 00:00:00 1970 +0000
+summary:     msg 0
+
+% hg up -C
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% bisect test
+Testing changeset 16:a2e6ea4973e9 (30 changesets remaining, ~4 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 23:5ec79163bff4 (15 changesets remaining, ~3 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 27:288867a866e9 (8 changesets remaining, ~3 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 29:b5bd63375ab9 (4 changesets remaining, ~2 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+Testing changeset 28:8e0c2264c8af (2 changesets remaining, ~1 tests)
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+The first bad revision is:
+changeset:   29:b5bd63375ab9
+user:        test
+date:        Thu Jan 01 00:00:29 1970 +0000
+summary:     msg 29
+
--- a/tests/test-bundle	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-bundle	Fri Aug 18 21:17:28 2006 -0700
@@ -30,14 +30,20 @@
 hg init empty
 hg -R test bundle full.hg empty
 hg -R test unbundle full.hg
-hg -R empty unbundle full.hg
 hg -R empty heads
 hg -R empty verify
 
+hg --cwd test pull ../full.hg
+hg --cwd empty pull ../full.hg
+hg -R empty rollback
+hg --cwd empty pull ../full.hg
+
 rm -rf empty
 hg init empty
 cd empty
 hg -R bundle://../full.hg log
+echo '[hooks]' >> .hg/hgrc
+echo 'changegroup = echo changegroup: u=$HG_URL' >> .hg/hgrc
 #doesn't work (yet ?)
 #hg -R bundle://../full.hg verify
 hg pull bundle://../full.hg
--- a/tests/test-bundle.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-bundle.out	Fri Aug 18 21:17:28 2006 -0700
@@ -11,28 +11,34 @@
 adding file changes
 added 0 changesets with 0 changes to 4 files
 (run 'hg update' to get a working copy)
+changeset:   -1:000000000000
+tag:         tip
+user:        
+date:        Thu Jan 01 00:00:00 1970 +0000
+
+checking changesets
+checking manifests
+crosschecking files in changesets and manifests
+checking files
+0 files, 0 changesets, 0 total revisions
+pulling from ../full.hg
+searching for changes
+no changes found
+pulling from ../full.hg
+requesting all changes
 adding changesets
 adding manifests
 adding file changes
 added 9 changesets with 7 changes to 4 files (+1 heads)
 (run 'hg heads' to see heads, 'hg merge' to merge)
-changeset:   8:836ac62537ab
-tag:         tip
-parent:      3:ac69c658229d
-user:        test
-date:        Mon Jan 12 13:46:40 1970 +0000
-summary:     0.3m
-
-changeset:   7:80fe151401c2
-user:        test
-date:        Mon Jan 12 13:46:40 1970 +0000
-summary:     1.3m
-
-checking changesets
-checking manifests
-crosschecking files in changesets and manifests
-checking files
-4 files, 9 changesets, 7 total revisions
+rolling back last transaction
+pulling from ../full.hg
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 9 changesets with 7 changes to 4 files (+1 heads)
+(run 'hg heads' to see heads, 'hg merge' to merge)
 changeset:   8:836ac62537ab
 tag:         tip
 parent:      3:ac69c658229d
@@ -81,6 +87,7 @@
 date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     0.0
 
+changegroup: u=bundle:../full.hg
 pulling from bundle://../full.hg
 requesting all changes
 adding changesets
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-diff-subdir	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+hg init
+
+mkdir alpha
+touch alpha/one
+mkdir beta
+touch beta/two
+
+hg add alpha/one beta/two
+hg ci -m "start" -d "1000000 0"
+
+echo 1 > alpha/one
+echo 2 > beta/two
+
+echo EVERYTHING
+hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+              -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+
+echo BETA ONLY
+hg diff beta | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+                -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+
+echo INSIDE BETA
+cd beta
+hg diff . | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+                -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-diff-subdir.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,23 @@
+EVERYTHING
+diff -r ec612a6291f1 alpha/one
+--- a/alpha/one
++++ b/alpha/one
+@@ -0,0 +1,1 @@
++1
+diff -r ec612a6291f1 beta/two
+--- a/beta/two
++++ b/beta/two
+@@ -0,0 +1,1 @@
++2
+BETA ONLY
+diff -r ec612a6291f1 beta/two
+--- a/beta/two
++++ b/beta/two
+@@ -0,0 +1,1 @@
++2
+INSIDE BETA
+diff -r ec612a6291f1 beta/two
+--- a/beta/two
++++ b/beta/two
+@@ -0,0 +1,1 @@
++2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-extdiff	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "extdiff=" >> $HGTMP/.hgrc
+
+hg init a
+cd a
+echo a > a
+hg add
+diff -N /dev/null /dev/null 2> /dev/null
+if [ $? -ne 0 ]; then
+	opt="-p gdiff"
+fi
+hg extdiff -o -Nr $opt
+
+echo "[extdiff]" >> $HGTMP/.hgrc
+echo "cmd.falabala=echo" >> $HGTMP/.hgrc
+echo "opts.falabala=diffing" >> $HGTMP/.hgrc
+
+hg falabala
+
+hg help falabala
+
+hg ci -d '0 0' -mtest1
+
+echo b >> a
+hg ci -d '1 0' -mtest2
+
+hg falabala -r 0:1 || echo "diff-like tools yield a non-zero exit code"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-extdiff.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,32 @@
+adding a
+making snapshot of 0 files from rev 000000000000
+making snapshot of 1 files from working dir
+diff -Nr a.000000000000/a a/a
+0a1
+> a
+making snapshot of 0 files from rev 000000000000
+making snapshot of 1 files from working dir
+diffing a.000000000000 a
+hg falabala [OPT]... [FILE]...
+
+use 'echo' to diff repository (or selected files)
+
+            Show differences between revisions for the specified
+            files, using the 'echo' program.
+
+            When two revision arguments are given, then changes are
+            shown between those revisions. If only one revision is
+            specified then that revision is compared to the working
+            directory, and, when no revisions are specified, the
+            working directory files are compared to its parent.
+
+options:
+
+ -o --option   pass option to comparison program
+ -r --rev      revision
+ -I --include  include names matching the given patterns
+ -X --exclude  exclude names matching the given patterns
+making snapshot of 1 files from rev e27a2475d60a
+making snapshot of 1 files from rev 5e49ec8d3f05
+diffing a.e27a2475d60a a.5e49ec8d3f05
+diff-like tools yield a non-zero exit code
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-fetch	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "fetch=" >> $HGTMP/.hgrc
+
+hg init a
+echo a > a/a
+hg --cwd a commit -d '1 0' -Ama
+
+hg clone a b
+hg clone a c
+
+echo b > a/b
+hg --cwd a commit -d '2 0' -Amb
+hg --cwd a parents -q
+
+echo % should pull one change
+hg --cwd b fetch ../a
+hg --cwd b parents -q
+
+echo c > c/c
+hg --cwd c commit -d '3 0' -Amc
+hg --cwd c fetch -d '4 0' -m 'automated merge' ../a
+ls c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-fetch.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,27 @@
+adding a
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+adding b
+1:97d72e5f12c7
+% should pull one change
+pulling from ../a
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1:97d72e5f12c7
+adding c
+pulling from ../a
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files (+1 heads)
+merging with new head 2:97d72e5f12c7
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+new changeset 3:cd3a41621cf0 merges remote changes with local
+a
+b
+c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-git-export	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+hg init a
+cd a
+
+echo start > start
+hg ci -Amstart -d '0 0'
+echo new > new
+hg ci -Amnew -d '0 0'
+echo '% new file'
+hg diff --git -r 0 | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+
+hg cp new copy
+hg ci -mcopy -d '0 0'
+echo '% copy'
+hg diff --git -r 1:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+
+hg mv copy rename
+hg ci -mrename -d '0 0'
+echo '% rename'
+hg diff --git -r 2:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+
+hg rm rename
+hg ci -mdelete -d '0 0'
+echo '% delete'
+hg diff --git -r 3:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+
+cat > src <<EOF
+1
+2
+3
+4
+5
+EOF
+hg ci -Amsrc -d '0 0'
+chmod +x src
+hg ci -munexec -d '0 0'
+echo '% chmod 644'
+hg diff --git -r 5:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+
+hg mv src dst
+chmod -x dst
+echo a >> dst
+hg ci -mrenamemod -d '0 0'
+echo '% rename+mod+chmod'
+hg diff --git -r 6:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-git-export.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,42 @@
+adding start
+adding new
+% new file
+diff --git a/new b/new
+new file mode 100644
+--- /dev/null
++++ b/new
+@@ -0,0 +1,1 @@
++new
+% copy
+diff --git a/new b/copy
+copy from new
+copy to copy
+% rename
+diff --git a/copy b/rename
+rename from copy
+rename to rename
+% delete
+diff --git a/rename b/rename
+deleted file mode 100644
+--- a/rename
++++ /dev/null
+@@ -1,1 +0,0 @@
+-new
+adding src
+% chmod 644
+diff --git a/src b/src
+old mode 100644
+new mode 100755
+% rename+mod+chmod
+diff --git a/src b/dst
+old mode 100755
+new mode 100644
+rename from src
+rename to dst
+--- a/dst
++++ b/dst
+@@ -3,3 +3,4 @@ 3
+ 3
+ 4
+ 5
++a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-git-import	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+hg init a
+cd a
+
+echo % new file
+hg import -mnew - <<EOF
+diff --git a/new b/new
+new file mode 100644
+index 0000000..7898192
+--- /dev/null
++++ b/new
+@@ -0,0 +1 @@
++a
+EOF
+
+echo % chmod +x
+hg import -msetx - <<EOF
+diff --git a/new b/new
+old mode 100644
+new mode 100755
+EOF
+
+test -x new || echo failed
+
+echo % copy
+hg import -mcopy - <<EOF
+diff --git a/new b/copy
+old mode 100755
+new mode 100644
+similarity index 100%
+copy from new
+copy to copy
+diff --git a/new b/copyx
+similarity index 100%
+copy from new
+copy to copyx
+EOF
+
+test -f copy -a ! -x copy || echo failed
+test -x copyx || echo failed
+cat copy
+hg cat copy
+
+echo % rename
+hg import -mrename - <<EOF
+diff --git a/copy b/rename
+similarity index 100%
+rename from copy
+rename to rename
+EOF
+
+hg locate
+
+echo % delete
+hg import -mdelete - <<EOF
+diff --git a/copyx b/copyx
+deleted file mode 100755
+index 7898192..0000000
+--- a/copyx
++++ /dev/null
+@@ -1 +0,0 @@
+-a
+EOF
+
+hg locate
+test -f copyx && echo failed || true
+
+echo % regular diff
+hg import -mregular - <<EOF
+diff --git a/rename b/rename
+index 7898192..72e1fe3 100644
+--- a/rename
++++ b/rename
+@@ -1 +1,5 @@
+ a
++a
++a
++a
++a
+EOF
+
+echo % copy and modify
+hg import -mcopymod - <<EOF
+diff --git a/rename b/copy2
+similarity index 80%
+copy from rename
+copy to copy2
+index 72e1fe3..b53c148 100644
+--- a/rename
++++ b/copy2
+@@ -1,5 +1,5 @@
+ a
+ a
+-a
++b
+ a
+ a
+EOF
+
+hg cat copy2
+
+echo % rename and modify
+hg import -mrenamemod - <<EOF
+diff --git a/copy2 b/rename2
+similarity index 80%
+rename from copy2
+rename to rename2
+index b53c148..8f81e29 100644
+--- a/copy2
++++ b/rename2
+@@ -1,5 +1,5 @@
+ a
+ a
+ b
+-a
++c
+ a
+EOF
+
+hg locate copy2
+hg cat rename2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-git-import.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,34 @@
+% new file
+applying patch from stdin
+% chmod +x
+applying patch from stdin
+% copy
+applying patch from stdin
+a
+a
+% rename
+applying patch from stdin
+copyx
+new
+rename
+% delete
+applying patch from stdin
+new
+rename
+% regular diff
+applying patch from stdin
+% copy and modify
+applying patch from stdin
+a
+a
+b
+a
+a
+% rename and modify
+applying patch from stdin
+copy2: No such file or directory
+a
+a
+b
+c
+a
--- a/tests/test-globalopts	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-globalopts	Fri Aug 18 21:17:28 2006 -0700
@@ -45,7 +45,7 @@
 hg --cwd b tip --verbose
 
 echo %% --config
-hg --cwd c --config paths.quuxfoo=bar paths | grep -q quuxfoo && echo quuxfoo
+hg --cwd c --config paths.quuxfoo=bar paths | grep quuxfoo > /dev/null && echo quuxfoo
 hg --cwd c --config '' tip -q
 hg --cwd c --config a.b tip -q
 hg --cwd c --config a tip -q
--- a/tests/test-grep	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-grep	Fri Aug 18 21:17:28 2006 -0700
@@ -18,6 +18,13 @@
 mv port1 port
 hg commit -m 4 -u spam -d '4 0'
 hg grep port port
-echo 'FIXME: history is wrong here'
 hg grep --all -nu port port
 hg grep import port
+
+hg cp port port2
+hg commit -m 4 -u spam -d '5 0'
+echo '% follow'
+hg grep -f 'import$' port2
+echo deport >> port2
+hg commit -m 5 -u eggs -d '6 0'
+hg grep -f --all -nu port port2
--- a/tests/test-grep.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-grep.out	Fri Aug 18 21:17:28 2006 -0700
@@ -1,10 +1,25 @@
 port:4:export
 port:4:vaportight
 port:4:import/export
-FIXME: history is wrong here
-port:1:1:-:eggs:import
-port:1:2:+:eggs:vaportight
-port:1:3:+:eggs:import/export
-port:0:2:+:spam:export
-port:0:1:+:spam:import
+port:4:4:-:spam:import/export
+port:3:4:+:eggs:import/export
+port:2:1:-:spam:import
+port:2:2:-:spam:export
+port:2:1:+:spam:export
+port:2:2:+:spam:vaportight
+port:2:3:+:spam:import/export
+port:1:2:+:eggs:export
+port:0:1:+:eggs:import
 port:4:import/export
+% follow
+port:0:import
+port2:6:4:+:eggs:deport
+port:4:4:-:spam:import/export
+port:3:4:+:eggs:import/export
+port:2:1:-:spam:import
+port:2:2:-:spam:export
+port:2:1:+:spam:export
+port:2:2:+:spam:vaportight
+port:2:3:+:spam:import/export
+port:1:2:+:eggs:export
+port:0:1:+:eggs:import
--- a/tests/test-help.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-help.out	Fri Aug 18 21:17:28 2006 -0700
@@ -178,6 +178,7 @@
  -r --rev                  revision
  -a --text                 treat all files as text
  -p --show-function        show which function each change is in
+ -g --git                  use git extended diff format
  -w --ignore-all-space     ignore white space when comparing lines
  -b --ignore-space-change  ignore changes in the amount of white space
  -B --ignore-blank-lines   ignore changes whose lines are all blank
@@ -187,13 +188,15 @@
 
 show changed files in the working directory
 
-    Show changed files in the repository.  If names are
-    given, only files that match are shown.
+    Show status of files in the repository.  If names are given, only
+    files that match are shown.  Files that are clean or ignored, are
+    not listed unless -c (clean), -i (ignored) or -A is given.
 
     The codes used to show the status of files are:
     M = modified
     A = added
     R = removed
+    C = clean
     ! = deleted, but still tracked
     ? = not tracked
     I = ignored (not shown by default)
@@ -203,10 +206,12 @@
 
 options:
 
+ -A --all        show status of all files
  -m --modified   show only modified files
  -a --added      show only added files
  -r --removed    show only removed files
  -d --deleted    show only deleted (but tracked) files
+ -c --clean      show only files without changes
  -u --unknown    show only unknown (not tracked) files
  -i --ignored    show ignored files
  -n --no-status  hide status prefix
--- a/tests/test-hook	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-hook	Fri Aug 18 21:17:28 2006 -0700
@@ -17,9 +17,9 @@
 
 # changegroup hooks can see env vars
 echo '[hooks]' > .hg/hgrc
-echo 'prechangegroup = echo prechangegroup hook' >> .hg/hgrc
-echo 'changegroup = echo changegroup hook: n=$HG_NODE' >> .hg/hgrc
-echo 'incoming = echo incoming hook: n=$HG_NODE' >> .hg/hgrc
+echo 'prechangegroup = echo prechangegroup hook: u=`echo $HG_URL | sed s,file:.*,file:,`' >> .hg/hgrc
+echo 'changegroup = echo changegroup hook: n=$HG_NODE u=`echo $HG_URL | sed s,file:.*,file:,`' >> .hg/hgrc
+echo 'incoming = echo incoming hook: n=$HG_NODE u=`echo $HG_URL | sed s,file:.*,file:,`' >> .hg/hgrc
 
 # pretxncommit and commit hooks can see both parents of merge
 cd ../a
--- a/tests/test-hook.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-hook.out	Fri Aug 18 21:17:28 2006 -0700
@@ -22,11 +22,11 @@
 3:4c52fb2e4022
 commit hook: n=4c52fb2e402287dd5dc052090682536c8406c321 p1=1324a5531bac09b329c3845d35ae6a7526874edb p2=b702efe9688826e3a91283852b328b84dbf37bc2
 commit hook b
-prechangegroup hook
-changegroup hook: n=b702efe9688826e3a91283852b328b84dbf37bc2
-incoming hook: n=b702efe9688826e3a91283852b328b84dbf37bc2
-incoming hook: n=1324a5531bac09b329c3845d35ae6a7526874edb
-incoming hook: n=4c52fb2e402287dd5dc052090682536c8406c321
+prechangegroup hook: u=file:
+changegroup hook: n=b702efe9688826e3a91283852b328b84dbf37bc2 u=file:
+incoming hook: n=b702efe9688826e3a91283852b328b84dbf37bc2 u=file:
+incoming hook: n=1324a5531bac09b329c3845d35ae6a7526874edb u=file:
+incoming hook: n=4c52fb2e402287dd5dc052090682536c8406c321 u=file:
 pulling from ../a
 searching for changes
 adding changesets
--- a/tests/test-http	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-http	Fri Aug 18 21:17:28 2006 -0700
@@ -4,22 +4,31 @@
 cd test
 echo foo>foo
 hg commit -A -d '0 0' -m 1
-hg --config server.uncompressed=True serve -p 20059 -d --pid-file=hg1.pid
-cat hg1.pid >> $DAEMON_PIDS
-hg serve -p 20060 -d --pid-file=hg2.pid
-cat hg2.pid >> $DAEMON_PIDS
+hg --config server.uncompressed=True serve -p 20059 -d --pid-file=../hg1.pid
+hg serve -p 20060 -d --pid-file=../hg2.pid
 cd ..
+cat hg1.pid hg2.pid >> $DAEMON_PIDS
 
 echo % clone via stream
 http_proxy= hg clone --uncompressed http://localhost:20059/ copy 2>&1 | \
   sed -e 's/[0-9][0-9.]*/XXX/g'
-cd copy
-hg verify
+hg verify -R copy
 
 echo % try to clone via stream, should use pull instead
 http_proxy= hg clone --uncompressed http://localhost:20060/ copy2
 
 echo % clone via pull
 http_proxy= hg clone http://localhost:20059/ copy-pull
+hg verify -R copy-pull
+
+cd test
+echo bar > bar
+hg commit -A -d '1 0' -m 2
+cd ..
+
+echo % pull
 cd copy-pull
-hg verify
+echo '[hooks]' >> .hg/hgrc
+echo 'changegroup = echo changegroup: u=$HG_URL' >> .hg/hgrc
+hg pull
+cd ..
--- a/tests/test-http.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-http.out	Fri Aug 18 21:17:28 2006 -0700
@@ -28,3 +28,13 @@
 crosschecking files in changesets and manifests
 checking files
 1 files, 1 changesets, 1 total revisions
+adding bar
+% pull
+changegroup: u=http://localhost:20059/
+pulling from http://localhost:20059/
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+(run 'hg update' to get a working copy)
--- a/tests/test-import	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-import	Fri Aug 18 21:17:28 2006 -0700
@@ -1,7 +1,10 @@
 #!/bin/sh
 
 hg init a
+mkdir a/d1
+mkdir a/d1/d2
 echo line 1 > a/a
+echo line 1 > a/d1/d2/a
 hg --cwd a ci -d '0 0' -Ama
 
 echo line 2 >> a/a
@@ -69,7 +72,7 @@
 
 echo % plain diff in email, no subject, no message body, should fail
 hg clone -r0 a b
-grep -v '^\(Subject\|email\)' msg.patch | hg --cwd b import -
+egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
 rm -rf b
 
 echo % hg export in email, should use patch header
@@ -79,3 +82,20 @@
 hg --cwd b tip | grep second
 rm -rf b
 
+# bug non regression test
+# importing a patch in a subdirectory failed at the commit stage
+echo line 2 >> a/d1/d2/a
+hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
+echo % hg import in a subdirectory
+hg clone -r0 a b
+hg --cwd a export tip | sed -e 's/d1\/d2\///' > tip.patch
+dir=`pwd`
+cd b/d1/d2 2>&1 > /dev/null
+hg import  ../../../tip.patch
+cd $dir
+echo "% message should be 'subdir change'"
+hg --cwd b tip | grep 'subdir change'
+echo "% committer should be 'someoneelse'"
+hg --cwd b tip | grep someoneelse
+echo "% should be empty"
+hg --cwd b status
--- a/tests/test-import.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-import.out	Fri Aug 18 21:17:28 2006 -0700
@@ -1,13 +1,13 @@
 adding a
+adding d1/d2/a
 % import exported patch
 requesting all changes
 adding changesets
 adding manifests
 adding file changes
-added 1 changesets with 1 changes to 1 files
-1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../tip.patch
-patching file a
 % message should be same
 summary:     second change
 % committer should be same
@@ -17,10 +17,9 @@
 adding changesets
 adding manifests
 adding file changes
-added 1 changesets with 1 changes to 1 files
-1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../tip.patch
-patching file a
 transaction abort!
 rollback completed
 % import of plain diff should be ok with message
@@ -28,38 +27,34 @@
 adding changesets
 adding manifests
 adding file changes
-added 1 changesets with 1 changes to 1 files
-1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../tip.patch
-patching file a
 % import from stdin
 requesting all changes
 adding changesets
 adding manifests
 adding file changes
-added 1 changesets with 1 changes to 1 files
-1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
-patching file a
 % override commit message
 requesting all changes
 adding changesets
 adding manifests
 adding file changes
-added 1 changesets with 1 changes to 1 files
-1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
-patching file a
 summary:     override
 % plain diff in email, subject, message body
 requesting all changes
 adding changesets
 adding manifests
 adding file changes
-added 1 changesets with 1 changes to 1 files
-1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying ../msg.patch
-patching file a
 user:        email patcher
 summary:     email patch
 % plain diff in email, no subject, message body
@@ -67,28 +62,25 @@
 adding changesets
 adding manifests
 adding file changes
-added 1 changesets with 1 changes to 1 files
-1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
-patching file a
 % plain diff in email, subject, no message body
 requesting all changes
 adding changesets
 adding manifests
 adding file changes
-added 1 changesets with 1 changes to 1 files
-1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
-patching file a
 % plain diff in email, no subject, no message body, should fail
 requesting all changes
 adding changesets
 adding manifests
 adding file changes
-added 1 changesets with 1 changes to 1 files
-1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
-patching file a
 transaction abort!
 rollback completed
 % hg export in email, should use patch header
@@ -96,8 +88,20 @@
 adding changesets
 adding manifests
 adding file changes
-added 1 changesets with 1 changes to 1 files
-1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 applying patch from stdin
-patching file a
 summary:     second change
+% hg import in a subdirectory
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 2 changes to 2 files
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+applying ../../../tip.patch
+% message should be 'subdir change'
+summary:     subdir change
+% committer should be 'someoneelse'
+user:        someoneelse
+% should be empty
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-issue322	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,49 @@
+#!/bin/sh
+# http://www.selenic.com/mercurial/bts/issue322
+
+echo % file replaced with directory
+
+hg init a
+cd a       
+echo a > a 
+hg commit -Ama 
+rm a       
+mkdir a    
+echo a > a/a
+
+echo % should fail - would corrupt dirstate
+hg add a/a
+
+cd ..      
+
+echo % directory replaced with file
+
+hg init c
+cd c
+mkdir a
+echo a > a/a
+hg commit -Ama
+
+rm -rf a
+echo a > a
+
+echo % should fail - would corrupt dirstate
+hg add a
+
+cd ..
+
+echo % directory replaced with file
+
+hg init d
+cd d
+mkdir b
+mkdir b/c
+echo a > b/c/d
+hg commit -Ama
+rm -rf b
+echo a > b
+
+echo % should fail - would corrupt dirstate
+hg add b
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-issue322.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,12 @@
+% file replaced with directory
+adding a
+% should fail - would corrupt dirstate
+abort: file named 'a' already in dirstate
+% directory replaced with file
+adding a/a
+% should fail - would corrupt dirstate
+abort: directory named 'a' already in dirstate
+% directory replaced with file
+adding b/c/d
+% should fail - would corrupt dirstate
+abort: directory named 'b' already in dirstate
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-log	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+hg init a
+
+cd a
+echo a > a
+hg ci -Ama -d '1 0'
+
+hg cp a b
+hg ci -mb -d '2 0'
+
+mkdir dir
+hg mv b dir
+hg ci -mc -d '3 0'
+
+hg mv a b
+hg ci -md -d '4 0'
+
+hg mv dir/b e
+hg ci -me -d '5 0'
+
+hg log a
+echo % -f, directory
+hg log -f dir
+echo % -f, but no args
+hg log -f
+echo % one rename
+hg log -vf a
+echo % many renames
+hg log -vf e
+
+# log --follow tests
+hg init ../follow
+cd ../follow
+echo base > base
+hg ci -Ambase -d '1 0'
+
+echo r1 >> base
+hg ci -Amr1 -d '1 0'
+echo r2 >> base
+hg ci -Amr2 -d '1 0'
+
+hg up -C 1
+echo b1 > b1
+hg ci -Amb1 -d '1 0'
+
+echo % log -f
+hg log -f
+
+hg up -C 0
+echo b2 > b2
+hg ci -Amb2 -d '1 0'
+
+echo % log -f -r 1:tip
+hg log -f -r 1:tip
+
+hg up -C 3
+hg merge tip
+hg ci -mm12 -d '1 0'
+
+echo postm >> b1
+hg ci -Amb1.1 -d'1 0'
+
+echo % log --follow-first
+hg log --follow-first
+
+echo % log -P 2
+hg log -P 2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-log.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,177 @@
+adding a
+changeset:   0:8580ff50825a
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     a
+
+% -f, directory
+abort: can only follow copies/renames for explicit file names
+% -f, but no args
+changeset:   4:8c1c8408f737
+tag:         tip
+user:        test
+date:        Thu Jan 01 00:00:05 1970 +0000
+summary:     e
+
+changeset:   3:c4ba038c90ce
+user:        test
+date:        Thu Jan 01 00:00:04 1970 +0000
+summary:     d
+
+changeset:   2:21fba396af4c
+user:        test
+date:        Thu Jan 01 00:00:03 1970 +0000
+summary:     c
+
+changeset:   1:c0296dabce9b
+user:        test
+date:        Thu Jan 01 00:00:02 1970 +0000
+summary:     b
+
+changeset:   0:8580ff50825a
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     a
+
+% one rename
+changeset:   0:8580ff50825a50c8f716709acdf8de0deddcd6ab
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+files:       a
+description:
+a
+
+
+% many renames
+changeset:   4:8c1c8408f7371319750ea2d4fa7969828effbcf4
+tag:         tip
+user:        test
+date:        Thu Jan 01 00:00:05 1970 +0000
+files:       dir/b e
+description:
+e
+
+
+changeset:   2:21fba396af4c801f9717de6c415b6cc9620437e8
+user:        test
+date:        Thu Jan 01 00:00:03 1970 +0000
+files:       b dir/b
+description:
+c
+
+
+changeset:   1:c0296dabce9bf0cd3fdd608de26693c91cd6bbf4
+user:        test
+date:        Thu Jan 01 00:00:02 1970 +0000
+files:       b
+description:
+b
+
+
+changeset:   0:8580ff50825a50c8f716709acdf8de0deddcd6ab
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+files:       a
+description:
+a
+
+
+adding base
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+adding b1
+% log -f
+changeset:   3:e62f78d544b4
+tag:         tip
+parent:      1:3d5bf5654eda
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     b1
+
+changeset:   1:3d5bf5654eda
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     r1
+
+changeset:   0:67e992f2c4f3
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     base
+
+1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+adding b2
+% log -f -r 1:tip
+changeset:   1:3d5bf5654eda
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     r1
+
+changeset:   2:60c670bf5b30
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     r2
+
+changeset:   3:e62f78d544b4
+parent:      1:3d5bf5654eda
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     b1
+
+2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+% log --follow-first
+changeset:   6:2404bbcab562
+tag:         tip
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     b1.1
+
+changeset:   5:302e9dd6890d
+parent:      3:e62f78d544b4
+parent:      4:ddb82e70d1a1
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     m12
+
+changeset:   3:e62f78d544b4
+parent:      1:3d5bf5654eda
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     b1
+
+changeset:   1:3d5bf5654eda
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     r1
+
+changeset:   0:67e992f2c4f3
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     base
+
+% log -P 2
+changeset:   6:2404bbcab562
+tag:         tip
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     b1.1
+
+changeset:   5:302e9dd6890d
+parent:      3:e62f78d544b4
+parent:      4:ddb82e70d1a1
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     m12
+
+changeset:   4:ddb82e70d1a1
+parent:      0:67e992f2c4f3
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     b2
+
+changeset:   3:e62f78d544b4
+parent:      1:3d5bf5654eda
+user:        test
+date:        Thu Jan 01 00:00:01 1970 +0000
+summary:     b1
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-merge-default	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+hg init
+echo a > a
+hg commit -A -ma
+
+echo a >> a
+hg commit -mb
+
+echo a >> a
+hg commit -mc
+
+hg up 1
+echo a >> a
+hg commit -md
+
+hg up 1
+echo a >> a
+hg commit -me
+
+hg up 1
+echo % should fail because not at a head
+hg merge
+
+hg up
+echo % should fail because \> 2 heads
+hg merge
+
+echo % should succeed
+hg merge 2
+hg commit -mm1
+
+echo % should succeed - 2 heads
+hg merge
+hg commit -mm2
+
+echo % should fail because 1 head
+hg merge
+
+true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-merge-default.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,17 @@
+adding a
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% should fail because not at a head
+abort: repo has 3 heads - please merge with an explicit rev
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% should fail because > 2 heads
+abort: repo has 3 heads - please merge with an explicit rev
+% should succeed
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+% should succeed - 2 heads
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+% should fail because 1 head
+abort: there is nothing to merge - use "hg update" instead
--- a/tests/test-merge5.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-merge5.out	Fri Aug 18 21:17:28 2006 -0700
@@ -1,6 +1,3 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 removing b
-this update spans a branch affecting the following files:
- b
-aborting update spanning branches!
-(use 'hg merge' to merge across branches or 'hg update -C' to lose changes)
+abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes
--- a/tests/test-merge7.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-merge7.out	Fri Aug 18 21:17:28 2006 -0700
@@ -22,7 +22,7 @@
 (run 'hg heads' to see heads, 'hg merge' to merge)
 merge: warning: conflicts during merge
 resolving manifests
- force False allow True moddirstate True linear False
+ overwrite None branchmerge True partial False linear False
  ancestor 055d847dd401 local 2eded9ab0a5c remote 84cf5750dd20
  test.txt versions differ, resolve
 merging test.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,155 @@
+#!/bin/sh
+
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "mq=" >> $HGTMP/.hgrc
+
+echo % help
+hg help mq
+
+hg init a
+cd a
+echo a > a
+hg ci -Ama
+
+hg clone . ../k
+
+mkdir b
+echo z > b/z
+hg ci -Ama
+
+echo % qinit
+
+hg qinit
+
+cd ..
+hg init b
+
+echo % -R qinit
+
+hg -R b qinit
+
+hg init c
+
+echo % qinit -c
+
+hg --cwd c qinit -c
+hg -R c/.hg/patches st
+
+echo % qnew implies add
+
+hg -R c qnew test.patch
+hg -R c/.hg/patches st
+
+cd a
+
+echo % qnew -m
+
+hg qnew -m 'foo bar' test.patch
+cat .hg/patches/test.patch
+
+echo % qrefresh
+
+echo a >> a
+hg qrefresh
+sed -e "s/^\(diff -r \)\([a-f0-9]* \)/\1 x/" \
+    -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/test.patch
+
+echo % qpop
+
+hg qpop
+
+echo % qpush
+
+hg qpush
+
+cd ..
+
+echo % pop/push outside repo
+
+hg -R a qpop
+hg -R a qpush
+
+cd a
+hg qnew test2.patch
+
+echo % qrefresh in subdir
+
+cd b
+echo a > a
+hg add a
+hg qrefresh
+
+echo % pop/push -a in subdir
+
+hg qpop -a
+hg --traceback qpush -a
+
+echo % qseries
+hg qseries
+
+echo % qapplied
+hg qapplied
+
+echo % qtop
+hg qtop
+
+echo % qprev
+hg qprev
+
+echo % qnext
+hg qnext
+
+echo % pop, qnext, qprev, qapplied
+hg qpop
+hg qnext
+hg qprev
+hg qapplied
+
+echo % commit should fail
+hg commit
+
+echo % push should fail
+hg push ../../k
+
+echo % qunapplied
+hg qunapplied
+
+echo % push should succeed
+hg qpop -a
+hg push ../../k
+
+echo % strip
+cd ../../b
+echo x>x
+hg ci -Ama
+hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/'
+hg unbundle .hg/strip-backup/*
+
+cat >>$HGTMP/.hgrc <<EOF
+[diff]
+git = True
+EOF
+cd ..
+hg init git
+cd git
+hg qinit
+
+hg qnew -m'new file' new
+echo foo > new
+chmod +x new
+hg add new
+hg qrefresh
+sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/new
+
+hg qnew -m'copy file' copy
+hg cp new copy
+hg qrefresh
+sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/copy
+
+hg qpop
+hg qpush
+hg qdiff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-guards	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "mq=" >> $HGTMP/.hgrc
+
+hg init
+hg qinit
+
+echo x > x
+hg ci -Ama
+
+hg qnew a.patch
+echo a > a
+hg add a
+hg qrefresh
+
+hg qnew b.patch
+echo b > b
+hg add b
+hg qrefresh
+
+hg qnew c.patch
+echo c > c
+hg add c
+hg qrefresh
+
+hg qpop -a
+
+echo % should fail
+hg qguard +fail
+
+hg qpush
+echo % should guard a.patch
+hg qguard +a
+echo % should print +a
+hg qguard
+hg qpop
+
+hg qguard a.patch
+echo % should push b.patch
+hg qpush
+
+hg qpop
+hg qselect a
+echo % should push a.patch
+hg qpush
+
+hg qguard c.patch -a
+echo % should print -a
+hg qguard c.patch
+
+echo % should skip c.patch
+hg qpush -a
+
+hg qguard -n c.patch
+echo % should push c.patch
+hg qpush -a
+
+hg qpop -a
+hg qselect -n
+echo % should push all
+hg qpush -a
+
+hg qpop -a
+hg qguard a.patch +1
+hg qguard b.patch +2
+hg qselect 1
+echo % should push a.patch, not b.patch
+hg qpush
+hg qpush
+hg qpop -a
+
+hg qselect 2
+echo % should push b.patch
+hg qpush
+hg qpop -a
+
+hg qselect 1 2
+echo % should push a.patch, b.patch
+hg qpush
+hg qpush
+hg qpop -a
+
+hg qguard a.patch +1 +2 -3
+hg qselect 1 2 3
+echo % list patches and guards
+hg qguard -l
+echo % list series
+hg qseries -v
+echo % list guards
+hg qselect
+echo % should push b.patch
+hg qpush
+
+hg qpush -a
+hg qselect -n --reapply
+echo % guards in series file: +1 +2 -3
+hg qselect -s
+echo % should show c.patch
+hg qapplied
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-guards.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,84 @@
+adding x
+Patch queue now empty
+% should fail
+abort: no patches applied
+applying a.patch
+Now at: a.patch
+% should guard a.patch
+% should print +a
+a.patch: +a
+Patch queue now empty
+a.patch: +a
+% should push b.patch
+applying b.patch
+Now at: b.patch
+Patch queue now empty
+number of unguarded, unapplied patches has changed from 2 to 3
+% should push a.patch
+applying a.patch
+Now at: a.patch
+% should print -a
+c.patch: -a
+% should skip c.patch
+applying b.patch
+skipping c.patch - guarded by '- a'
+Now at: b.patch
+% should push c.patch
+applying c.patch
+Now at: c.patch
+Patch queue now empty
+guards deactivated
+number of unguarded, unapplied patches has changed from 3 to 2
+% should push all
+applying b.patch
+applying c.patch
+Now at: c.patch
+Patch queue now empty
+number of unguarded, unapplied patches has changed from 1 to 2
+% should push a.patch, not b.patch
+applying a.patch
+Now at: a.patch
+applying c.patch
+Now at: c.patch
+Patch queue now empty
+% should push b.patch
+applying b.patch
+Now at: b.patch
+Patch queue now empty
+number of unguarded, unapplied patches has changed from 2 to 3
+% should push a.patch, b.patch
+applying a.patch
+Now at: a.patch
+applying b.patch
+Now at: b.patch
+Patch queue now empty
+number of unguarded, unapplied patches has changed from 3 to 2
+% list patches and guards
+a.patch: +1 +2 -3
+b.patch: +2
+c.patch: unguarded
+% list series
+0 G a.patch
+1 U b.patch
+2 U c.patch
+% list guards
+1
+2
+3
+% should push b.patch
+applying b.patch
+Now at: b.patch
+applying c.patch
+Now at: c.patch
+guards deactivated
+popping guarded patches
+Patch queue now empty
+reapplying unguarded patches
+applying c.patch
+Now at: c.patch
+% guards in series file: +1 +2 -3
++1
++2
+-3
+% should show c.patch
+c.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qdiff	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "mq=" >> $HGTMP/.hgrc
+
+echo % init
+hg init a
+cd a
+
+echo % commit
+echo 'base' > base
+hg ci -Ambase -d '1 0'
+
+echo % qnew mqbase
+hg qnew -mmqbase mqbase
+
+echo % qrefresh
+echo 'patched' > base
+hg qrefresh
+
+echo % qdiff
+hg qdiff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+               -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+
+echo % qdiff dirname
+hg qdiff . | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
+                 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qdiff.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,19 @@
+% init
+% commit
+adding base
+% qnew mqbase
+% qrefresh
+% qdiff
+diff -r 67e992f2c4f3 base
+--- a/base
++++ b/base
+@@ -1,1 +1,1 @@ base
+-base
++patched
+% qdiff dirname
+diff -r 67e992f2c4f3 base
+--- a/base
++++ b/base
+@@ -1,1 +1,1 @@ base
+-base
++patched
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qnew-twice	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "mq=" >> $HGTMP/.hgrc
+
+hg init a
+cd a
+hg qnew first.patch
+hg qnew first.patch
+
+touch ../first.patch
+hg qimport ../first.patch
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qnew-twice.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,2 @@
+abort: patch "first.patch" already exists
+abort: patch "first.patch" already exists
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qrefresh-replace-log-message	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+# Environement setup for MQ
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "mq=" >> $HGTMP/.hgrc
+
+#Repo init
+hg init
+hg qinit
+
+hg qnew -m "First commit message" first-patch
+echo aaaa > file
+hg add file
+hg qrefresh
+echo =======================
+echo "Should display 'First commit message'"
+hg log -l1 -v | sed -n '/description/,$p'
+echo
+
+# Testing changing message with -m
+echo bbbb > file
+hg qrefresh -m "Second commit message" 
+echo =======================
+echo "Should display 'Second commit message'"
+hg log -l1 -v | sed -n '/description/,$p'
+echo
+
+
+# Testing changing message with -l
+echo "Third commit message" > logfile
+echo " This is the 3rd log message" >> logfile
+echo bbbb > file
+hg qrefresh -l logfile
+echo =======================
+printf "Should display 'Third commit message\\\n This is the 3rd log message'\n"
+hg log -l1 -v | sed -n '/description/,$p'
+echo
+
+# Testing changing message with -l-
+hg qnew -m "First commit message" second-patch
+echo aaaa > file2
+hg add file2
+echo bbbb > file2
+(echo "Fifth commit message" 
+echo " This is the 5th log message" >> logfile) |\
+hg qrefresh -l-
+echo =======================
+printf "Should display 'Fifth commit message\\\n This is the 5th log message'\n"
+hg log -l1 -v | sed -n '/description/,$p'
+echo
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qrefresh-replace-log-message.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,29 @@
+=======================
+Should display 'First commit message'
+description:
+First commit message
+
+
+
+=======================
+Should display 'Second commit message'
+description:
+Second commit message
+
+
+
+=======================
+Should display 'Third commit message\n This is the 3rd log message'
+description:
+Third commit message
+ This is the 3rd log message
+
+
+
+=======================
+Should display 'Fifth commit message\n This is the 5th log message'
+description:
+Fifth commit message
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qsave	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
+echo "[extensions]" >> $HGTMP/.hgrc
+echo "mq=" >> $HGTMP/.hgrc
+
+hg init a
+cd a
+
+echo 'base' > base
+hg ci -Ambase -d '1 0'
+
+hg qnew -mmqbase mqbase
+
+hg qsave
+hg qrestore 2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qsave.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,2 @@
+adding base
+restoring status: hg patches saved state
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq.out	Fri Aug 18 21:17:28 2006 -0700
@@ -0,0 +1,148 @@
+% help
+mq extension - patch management and development
+
+This extension lets you work with a stack of patches in a Mercurial
+repository.  It manages two stacks of patches - all known patches, and
+applied patches (subset of known patches).
+
+Known patches are represented as patch files in the .hg/patches
+directory.  Applied patches are both patch files and changesets.
+
+Common tasks (use "hg help command" for more details):
+
+prepare repository to work with patches   qinit
+create new patch                          qnew
+import existing patch                     qimport
+
+print patch series                        qseries
+print applied patches                     qapplied
+print name of top applied patch           qtop
+
+add known patch to applied stack          qpush
+remove patch from applied stack           qpop
+refresh contents of top applied patch     qrefresh
+
+list of commands (use "hg help -v mq" to show aliases and global options):
+
+ qapplied     print the patches already applied
+ qclone       clone main and patch repository at same time
+ qcommit      commit changes in the queue repository
+ qdelete      remove patches from queue
+ qdiff        diff of the current patch
+ qfold        fold the named patches into the current patch
+ qguard       set or print guards for a patch
+ qheader      Print the header of the topmost or specified patch
+ qimport      import a patch
+ qinit        init a new queue repository
+ qnew         create a new patch
+ qnext        print the name of the next patch
+ qpop         pop the current patch off the stack
+ qprev        print the name of the previous patch
+ qpush        push the next patch onto the stack
+ qrefresh     update the current patch
+ qrename      rename a patch
+ qrestore     restore the queue state saved by a rev
+ qsave        save current queue state
+ qselect      set or print guarded patches to push
+ qseries      print the entire series file
+ qtop         print the name of the current patch
+ qunapplied   print the patches not yet applied
+ strip        strip a revision and all later revs on the same branch
+adding a
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+adding b/z
+% qinit
+% -R qinit
+% qinit -c
+A .hgignore
+A series
+% qnew implies add
+A .hgignore
+A series
+A test.patch
+% qnew -m
+foo bar
+% qrefresh
+foo bar
+
+diff -r  xa
+--- a/a
++++ b/a
+@@ -1,1 +1,2 @@ a
+ a
++a
+% qpop
+Patch queue now empty
+% qpush
+applying test.patch
+Now at: test.patch
+% pop/push outside repo
+Patch queue now empty
+applying test.patch
+Now at: test.patch
+% qrefresh in subdir
+% pop/push -a in subdir
+Patch queue now empty
+applying test.patch
+applying test2.patch
+Now at: test2.patch
+% qseries
+test.patch
+test2.patch
+% qapplied
+test.patch
+test2.patch
+% qtop
+test2.patch
+% qprev
+test.patch
+% qnext
+All patches applied
+% pop, qnext, qprev, qapplied
+Now at: test.patch
+test2.patch
+Only one patch applied
+test.patch
+% commit should fail
+abort: cannot commit over an applied mq patch
+% push should fail
+pushing to ../../k
+abort: source has mq patches applied
+% qunapplied
+test2.patch
+% push should succeed
+Patch queue now empty
+pushing to ../../k
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+% strip
+adding x
+0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+saving bundle to 
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+(run 'hg update' to get a working copy)
+new file
+
+diff --git a/new b/new
+new file mode 100755
+--- /dev/null
++++ b/new
+@@ -0,0 +1,1 @@
++foo
+copy file
+
+diff --git a/new b/copy
+copy from new
+copy to copy
+Now at: new
+applying copy
+Now at: copy
+diff --git a/new b/copy
+copy from new
+copy to copy
--- a/tests/test-push-http	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-push-http	Fri Aug 18 21:17:28 2006 -0700
@@ -36,13 +36,19 @@
 
 echo % expect success
 echo 'allow_push = *' >> .hg/hgrc
+echo '[hooks]' >> .hg/hgrc
+echo 'changegroup = echo changegroup: u=$HG_URL >> $HGTMP/urls' >> .hg/hgrc
 hg serve -p 20059 -d --pid-file=hg.pid
 cat hg.pid >> $DAEMON_PIDS
 hg --cwd ../test2 push http://localhost:20059/
 kill `cat hg.pid`
 hg rollback
 
+sed 's/\(remote:http.*\):.*/\1/' $HGTMP/urls
+
 echo % expect authorization error: all users denied
+echo '[web]' > .hg/hgrc
+echo 'push_ssl = false' >> .hg/hgrc
 echo 'deny_push = *' >> .hg/hgrc
 hg serve -p 20059 -d --pid-file=hg.pid
 cat hg.pid >> $DAEMON_PIDS
--- a/tests/test-push-http.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-push-http.out	Fri Aug 18 21:17:28 2006 -0700
@@ -20,6 +20,7 @@
 adding file changes
 added 1 changesets with 1 changes to 1 files
 rolling back last transaction
+changegroup: u=remote:http
 % expect authorization error: all users denied
 pushing to http://localhost:20059/
 searching for changes
--- a/tests/test-ssh	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-ssh	Fri Aug 18 21:17:28 2006 -0700
@@ -17,6 +17,8 @@
 	exit -1
 fi
 
+SSH_CLIENT='127.0.0.1 1 2'
+export SSH_CLIENT
 echo Got arguments 1:$1 2:$2 3:$3 4:$4 5:$5 >> dummylog
 $2
 EOF
@@ -29,6 +31,8 @@
 hg ci -A -m "init" -d "1000000 0" foo
 echo '[server]' > .hg/hgrc
 echo 'uncompressed = True' >> .hg/hgrc
+echo '[hooks]' >> .hg/hgrc
+echo 'changegroup = echo changegroup in remote: u=$HG_URL >> ../dummylog' >> .hg/hgrc
 
 cd ..
 
@@ -46,6 +50,9 @@
 cd local
 hg verify
 
+echo '[hooks]' >> .hg/hgrc
+echo 'changegroup = echo changegroup in local: u=$HG_URL >> ../dummylog' >> .hg/hgrc
+
 echo "# empty default pull"
 hg paths
 hg pull -e ../dummyssh
--- a/tests/test-ssh.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-ssh.out	Fri Aug 18 21:17:28 2006 -0700
@@ -83,5 +83,7 @@
 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
 Got arguments 1:user@dummy 2:hg -R local serve --stdio 3: 4: 5:
 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
+changegroup in remote: u=remote:ssh:127.0.0.1
 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
 Got arguments 1:user@dummy 2:hg -R remote serve --stdio 3: 4: 5:
+changegroup in remote: u=remote:ssh:127.0.0.1
--- a/tests/test-static-http	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-static-http	Fri Aug 18 21:17:28 2006 -0700
@@ -37,6 +37,14 @@
 cd local
 hg verify
 cat bar
+
+cd ../remote
+echo baz > quux
+hg commit -A -mtest2 -d '100000000 0'
+
+cd ../local
+echo '[hooks]' >> .hg/hgrc
+echo 'changegroup = echo changegroup: u=$HG_URL' >> .hg/hgrc
 http_proxy= hg pull
 
 kill $!
--- a/tests/test-static-http.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-static-http.out	Fri Aug 18 21:17:28 2006 -0700
@@ -19,6 +19,12 @@
 checking files
 1 files, 1 changesets, 1 total revisions
 foo
+adding quux
+changegroup: u=static-http://localhost:20059/remote
 pulling from static-http://localhost:20059/remote
 searching for changes
-no changes found
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+(run 'hg update' to get a working copy)
--- a/tests/test-status	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-status	Fri Aug 18 21:17:28 2006 -0700
@@ -35,3 +35,8 @@
 hg copy modified copied
 echo "hg status -C:"
 hg status -C
+
+echo "hg status -t:"
+hg status -t
+echo "hg status -A:"
+hg status -A
--- a/tests/test-status.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-status.out	Fri Aug 18 21:17:28 2006 -0700
@@ -108,3 +108,50 @@
 R removed
 ! deleted
 ? unknown
+hg status -t:
+hg status: option -t not recognized
+hg status [OPTION]... [FILE]...
+
+show changed files in the working directory
+
+    Show status of files in the repository.  If names are given, only
+    files that match are shown.  Files that are clean or ignored, are
+    not listed unless -c (clean), -i (ignored) or -A is given.
+
+    The codes used to show the status of files are:
+    M = modified
+    A = added
+    R = removed
+    C = clean
+    ! = deleted, but still tracked
+    ? = not tracked
+    I = ignored (not shown by default)
+      = the previous added file was copied from here
+
+aliases: st
+
+options:
+
+ -A --all        show status of all files
+ -m --modified   show only modified files
+ -a --added      show only added files
+ -r --removed    show only removed files
+ -d --deleted    show only deleted (but tracked) files
+ -c --clean      show only files without changes
+ -u --unknown    show only unknown (not tracked) files
+ -i --ignored    show ignored files
+ -n --no-status  hide status prefix
+ -C --copies     show source of copied files
+ -0 --print0     end filenames with NUL, for use with xargs
+ -I --include    include names matching the given patterns
+ -X --exclude    exclude names matching the given patterns
+hg status -A:
+A added
+A copied
+  modified
+R removed
+! deleted
+? unknown
+I ignored
+C .hgignore
+C modified
--- a/tests/test-tag	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-tag	Fri Aug 18 21:17:28 2006 -0700
@@ -19,6 +19,11 @@
 cat .hgtags
 cat .hg/localtags
 
+hg update 0
+hg tag -d "1000000 0" "foobar"
+cat .hgtags
+cat .hg/localtags
+
 hg tag -l 'xx
 newline'
 hg tag -l 'xx:xx'
--- a/tests/test-tag.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-tag.out	Fri Aug 18 21:17:28 2006 -0700
@@ -25,5 +25,8 @@
 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah
 0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah0
 c5c60883086f5526bd3e36814b94a73a4e75e172 bleah1
+0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+0acdaf8983679e0aac16e811534eb49d7ee1f2b4 foobar
+c5c60883086f5526bd3e36814b94a73a4e75e172 bleah1
 abort: '\n' cannot be used in a tag name
 abort: ':' cannot be used in a tag name
--- a/tests/test-up-local-change.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-up-local-change.out	Fri Aug 18 21:17:28 2006 -0700
@@ -15,7 +15,7 @@
 summary:     1
 
 resolving manifests
- force None allow None moddirstate True linear True
+ overwrite False branchmerge False partial False linear True
  ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
  a versions differ, resolve
 remote created b
@@ -31,7 +31,7 @@
 summary:     2
 
 resolving manifests
- force None allow None moddirstate True linear True
+ overwrite False branchmerge False partial False linear True
  ancestor a0c8bcbbb45c local 1165e8bd193e remote a0c8bcbbb45c
 remote deleted b
 removing b
@@ -41,7 +41,7 @@
 date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     1
 
-abort: there is nothing to merge, just use 'hg update' or look at 'hg heads'
+abort: there is nothing to merge - use "hg update" instead
 failed
 changeset:   0:33aaa84a386b
 user:        test
@@ -49,7 +49,7 @@
 summary:     1
 
 resolving manifests
- force None allow None moddirstate True linear True
+ overwrite False branchmerge False partial False linear True
  ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
  a versions differ, resolve
 remote created b
@@ -95,21 +95,12 @@
 date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     2
 
-resolving manifests
- force None allow None moddirstate True linear False
- ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
- a versions differ, resolve
- b versions differ, resolve
-this update spans a branch affecting the following files:
- a (resolve)
- b (resolve)
-aborting update spanning branches!
-(use 'hg merge' to merge across branches or 'hg update -C' to lose changes)
+abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes
 failed
 abort: outstanding uncommitted changes
 failed
 resolving manifests
- force False allow True moddirstate True linear False
+ overwrite False branchmerge True partial False linear False
  ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
  a versions differ, resolve
  b versions differ, resolve
--- a/tests/test-update-reverse.out	Sun Jul 23 09:04:14 2006 -0700
+++ b/tests/test-update-reverse.out	Fri Aug 18 21:17:28 2006 -0700
@@ -40,7 +40,7 @@
 side1
 side2
 resolving manifests
- force 1 allow None moddirstate True linear False
+ overwrite True branchmerge False partial False linear False
  ancestor 8515d4bfda76 local 1c0f48f8ece6 remote 0594b9004bae
 remote deleted side2, clobbering
 remote deleted side1, clobbering