changeset 3047:fcadf7a32425

Merge with mpm
author Josef "Jeff" Sipek <jeffpc@josefsipek.net>
date Sun, 03 Sep 2006 06:06:02 -0400
parents 2a4d4aecb2b4 (current diff) 962b9c7df641 (diff)
children 8d344bc72e68
files
diffstat 151 files changed, 6062 insertions(+), 2452 deletions(-) [+]
line wrap: on
line diff
--- a/CONTRIBUTORS	Sat Aug 19 15:20:54 2006 -0400
+++ b/CONTRIBUTORS	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/MANIFEST.in	Sun Sep 03 06:06:02 2006 -0400
@@ -10,6 +10,7 @@
 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
--- a/contrib/bash_completion	Sat Aug 19 15:20:54 2006 -0400
+++ b/contrib/bash_completion	Sun Sep 03 06:06:02 2006 -0400
@@ -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/darcs2hg.py	Sat Aug 19 15:20:54 2006 -0400
+++ b/contrib/darcs2hg.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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/mercurial.el	Sat Aug 19 15:20:54 2006 -0400
+++ b/contrib/mercurial.el	Sun Sep 03 06:06:02 2006 -0400
@@ -718,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.
--- a/contrib/vim/hgcommand.vim	Sat Aug 19 15:20:54 2006 -0400
+++ b/contrib/vim/hgcommand.vim	Sun Sep 03 06:06:02 2006 -0400
@@ -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/doc/hg.1.txt	Sat Aug 19 15:20:54 2006 -0400
+++ b/doc/hg.1.txt	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/doc/hgmerge.1.txt	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/doc/hgrc.5.txt	Sun Sep 03 06:06:02 2006 -0400
@@ -139,12 +139,13 @@
     Optional.  Email address to use in "From" header and SMTP envelope
     of outgoing messages.
   to;;
-    Optional.  Email address(es) of recipient(s).
+    Optional.  Comma-separated list of recipients' email addresses.
   cc;;
-    Optional.  Email address(es) to send carbon copies to.
+    Optional.  Comma-separated list of carbon copy recipients'
+    email addresses.
   bcc;;
-    Optional.  Email address(es) to send blind carbon copies to.
-    Cannot be set interactively.
+    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 "[smtp]" for
@@ -305,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;;
--- a/doc/ja/hg.1.ja.txt	Sat Aug 19 15:20:54 2006 -0400
+++ b/doc/ja/hg.1.ja.txt	Sun Sep 03 06:06:02 2006 -0400
@@ -862,6 +862,6 @@
 
 著作権情報
 -----
-Copyright (C) 2005 Matt Mackall.
+Copyright (C) 2005, 2006 Matt Mackall.
 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
 認められます。
--- a/doc/ja/hgmerge.1.ja.txt	Sat Aug 19 15:20:54 2006 -0400
+++ b/doc/ja/hgmerge.1.ja.txt	Sun Sep 03 06:06:02 2006 -0400
@@ -32,6 +32,6 @@
 
 著作権情報
 ----
-Copyright (C) 2005 Matt Mackall.
+Copyright (C) 2005, 2006 Matt Mackall.
 このソフトウェアの自由な使用は GNU 一般公有使用許諾 (GPL) のもとで
 認められます。
--- a/hgext/extdiff.py	Sat Aug 19 15:20:54 2006 -0400
+++ b/hgext/extdiff.py	Sun Sep 03 06:06:02 2006 -0400
@@ -5,19 +5,24 @@
 # 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)
@@ -26,16 +31,23 @@
 #   #(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)
@@ -79,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
 
@@ -92,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'))
@@ -104,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
@@ -115,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":
@@ -133,21 +151,25 @@
         if not cmd.startswith('cmd.'): continue
         cmd = cmd[4:]
         if not path: path = cmd
-        def save(cmd, path):
+        diffopts = ui.config('extdiff', 'opts.' + cmd, '')
+        diffopts = diffopts and [diffopts] or []
+        def save(cmd, path, diffopts):
             '''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[cmd] = (save(cmd, path, diffopts),
                          cmdtable['extdiff'][1][1:],
                          _('hg %s [OPT]... [FILE]...') % cmd)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/fetch.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/hgext/gpg.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/hgext/hbisect.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/hgext/hgk.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/hgext/mq.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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,72 +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:]:
-            if self.ui.verbose:
-                self.ui.write("%d " % self.series.index(p))
-            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:
@@ -876,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):
@@ -911,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()
@@ -947,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:
@@ -967,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:
@@ -1000,12 +1224,11 @@
             self.ui.write("%s\n" % p)
 
     def appliedname(self, index):
-        p = self.applied[index]
-        pname = p.split(':')[1]
+        pname = self.applied[index].name
         if not self.ui.verbose:
             p = pname
         else:
-            p = str(self.series.index(pname)) + " " + p
+            p = str(self.series.index(pname)) + " " + pname
         return p
 
     def top(self, repo):
@@ -1036,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)
@@ -1075,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:
@@ -1114,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):
@@ -1187,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']:
@@ -1215,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()
@@ -1233,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()
@@ -1244,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
@@ -1270,49 +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(""))
-    oldtags = repo.tags
+    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 qtags():
-        if repo.tagscache:
-            return repo.tagscache
+        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()
 
-        tagscache = oldtags()
+            q = self.mq
+            if not q.applied:
+                return tagscache
 
-        q = repomap[repo]
-        if len(q.applied) == 0:
+            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
 
-        mqtags = [patch.split(':') 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:
-                repo.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
-            else:
-                tagscache[patch[1]] = revlog.bin(patch[0])
-
-        return tagscache
-
-    repo.tags = qtags
+    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'),
@@ -1325,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":
@@ -1346,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'),
@@ -1355,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,
@@ -1373,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	Sat Aug 19 15:20:54 2006 -0400
+++ b/hgext/notify.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/hgext/patchbomb.py	Sun Sep 03 06:06:02 2006 -0400
@@ -23,28 +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, ...
-# bcc = bcc1, bcc2, ...
+#   [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
@@ -130,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:
@@ -193,8 +235,7 @@
     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)))
@@ -209,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
@@ -267,12 +311,15 @@
             fp.close()
         else:
             ui.status('Sending ', m['Subject'], ' ...\n')
-            mail.sendmail(sender, to + bcc + 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,
-     [('', 'bcc', [], 'email addresses of blind 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'),
--- a/mercurial/archival.py	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/archival.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/bdiff.c	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/bundlerepo.py	Sun Sep 03 06:06:02 2006 -0400
@@ -237,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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/changelog.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sun Sep 03 06:06:02 2006 -0400
@@ -0,0 +1,145 @@
+# cmdutil.py - help for command processing in 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(), 'mdiff 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 findrenames(repo, added=None, removed=None, threshold=0.5):
+    if added is None or removed is None:
+        added, removed = repo.status()[1:3]
+    changes = repo.changelog.read(repo.dirstate.parents()[0])
+    mf = repo.manifest.read(changes[0])
+    for a in added:
+        aa = repo.wread(a)
+        bestscore, bestname = None, None
+        for r in removed:
+            rr = repo.file(r).read(mf[r])
+            delta = mdiff.textdiff(aa, rr)
+            if len(delta) < len(aa):
+                myscore = 1.0 - (float(len(delta)) / len(aa))
+                if bestscore is None or myscore > bestscore:
+                    bestscore, bestname = myscore, r
+        if bestname and bestscore >= threshold:
+            yield bestname, a, bestscore
+
+def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
+              similarity=None):
+    if dry_run is None:
+        dry_run = opts.get('dry_run')
+    if similarity is None:
+        similarity = float(opts.get('similarity') or 0)
+    add, remove = [], []
+    mapping = {}
+    for src, abs, rel, exact in walk(repo, pats, opts):
+        if src == 'f' and repo.dirstate.state(abs) == '?':
+            add.append(abs)
+            mapping[abs] = rel, exact
+            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)
+            mapping[abs] = rel, exact
+            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)
+    if similarity > 0:
+        for old, new, score in findrenames(repo, add, remove, similarity):
+            oldrel, oldexact = mapping[old]
+            newrel, newexact = mapping[new]
+            if repo.ui.verbose or not oldexact or not newexact:
+                repo.ui.status(_('recording removal of %s as rename to %s '
+                                 '(%d%% similar)\n') %
+                               (oldrel, newrel, score * 100))
+            if not dry_run:
+                repo.copy(old, new, wlock=wlock)
--- a/mercurial/commands.py	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/commands.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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)
@@ -466,17 +403,15 @@
         changes = log.read(changenode)
         date = util.datestr(changes[2])
 
-        parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
-                   for p in log.parents(changenode)
+        hexfunc = self.ui.debugflag and hex or short
+
+        parents = [(log.rev(p), hexfunc(p)) for p in log.parents(changenode)
                    if self.ui.debugflag or p != nullid]
         if (not self.ui.debugflag and len(parents) == 1 and
             parents[0][0] == rev-1):
             parents = []
 
-        if self.ui.verbose:
-            self.ui.write(_("changeset:   %d:%s\n") % (rev, hex(changenode)))
-        else:
-            self.ui.write(_("changeset:   %d:%s\n") % (rev, short(changenode)))
+        self.ui.write(_("changeset:   %d:%s\n") % (rev, hexfunc(changenode)))
 
         for tag in self.repo.nodetags(changenode):
             self.ui.status(_("tag:         %s\n") % tag)
@@ -493,7 +428,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 +472,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 +638,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)
@@ -710,33 +652,21 @@
 def addremove(ui, repo, *pats, **opts):
     """add all new files, delete all missing files (DEPRECATED)
 
-    (DEPRECATED)
     Add all new files and remove all missing files from the repository.
 
     New files are ignored if they match any of the patterns in .hgignore. As
     with add, these changes take effect at the next commit.
 
-    This command is now deprecated and will be removed in a future
-    release. Please use add and remove --after instead.
+    Use the -s option to detect renamed files.  With a parameter > 0,
+    this compares every removed file with every added file and records
+    those similar enough as renames.  This option takes a percentage
+    between 0 (disabled) and 100 (files must be identical) as its
+    parameter.  Detecting renamed files this way can be expensive.
     """
-    ui.warn(_('(the addremove command is deprecated; use add and remove '
-              '--after instead)\n'))
-    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)
+    sim = float(opts.get('similarity') or 0)
+    if sim < 0 or sim > 100:
+        raise util.Abort(_('similarity must be between 0 and 100'))
+    return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
 
 def annotate(ui, repo, *pats, **opts):
     """show changeset information per file line
@@ -779,7 +709,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))
@@ -831,10 +762,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 == '-':
@@ -842,7 +773,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)
 
@@ -885,7 +816,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)
@@ -902,11 +833,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
@@ -944,8 +877,9 @@
     %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)
+    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):
@@ -960,10 +894,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.
 
@@ -971,7 +920,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'],
@@ -989,28 +938,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 = []
@@ -1165,7 +1099,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))
@@ -1239,9 +1173,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"""
@@ -1382,7 +1316,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' % (
@@ -1411,37 +1345,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
@@ -1472,15 +1379,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)
@@ -1493,7 +1399,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:
@@ -1550,42 +1456,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:
@@ -1602,6 +1522,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
@@ -1616,20 +1537,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:
@@ -1638,7 +1570,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):
@@ -1675,8 +1608,8 @@
         ui.write(_("unknown\n"))
         return
 
-    hexfunc = ui.verbose and hex or short
-    modified, added, removed, deleted, unknown = repo.changes()
+    hexfunc = ui.debugflag and hex or short
+    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 "")]
@@ -1720,81 +1653,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']
@@ -1806,14 +1681,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)
 
@@ -1830,7 +1700,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"])
@@ -1866,7 +1736,7 @@
             displayer.show(changenode=n)
             if opts['patch']:
                 prev = (parents and parents[0]) or nullid
-                dodiff(ui, ui, other, prev, n)
+                patch.diff(other, prev, n, fp=repo.ui)
                 ui.write("\n")
     finally:
         if hasattr(other, 'close'):
@@ -1886,7 +1756,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):
@@ -1914,8 +1784,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']:
@@ -1926,7 +1796,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
@@ -2006,7 +1887,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
@@ -2037,22 +1918,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
@@ -2064,7 +1967,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']]
@@ -2085,7 +1988,7 @@
         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, file_=None, rev=None, branches=None, **opts):
@@ -2146,7 +2049,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:
@@ -2186,7 +2089,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))
@@ -2224,7 +2127,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))
@@ -2278,7 +2181,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):
@@ -2298,12 +2201,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')
@@ -2410,20 +2313,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'))
@@ -2495,8 +2399,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')
@@ -2631,7 +2534,7 @@
 
     all = opts['all']
     
-    files, matchfn, anypats = matchpats(repo, pats, opts)
+    files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
     cwd = (pats and repo.getcwd()) or ''
     modified, added, removed, deleted, unknown, ignored, clean = [
         [util.pathto(cwd, x) for x in n]
@@ -2681,8 +2584,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"))
@@ -2691,17 +2594,20 @@
     if opts['rev']:
         rev_ = opts['rev']
     if rev_:
-        r = hex(repo.lookup(rev_))
+        r = repo.lookup(rev_)
     else:
         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'])
+        r = p1
+
+    message = opts['message']
+    if not message:
+        message = _('Added tag %s for changeset %s') % (name, short(r))
+
+    repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
 
 def tags(ui, repo):
     """list repository tags
@@ -2713,9 +2619,10 @@
 
     l = repo.tagslist()
     l.reverse()
+    hexfunc = ui.debugflag and hex or short
     for t, n in l:
         try:
-            r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
+            r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
         except KeyError:
             r = "    ?:?"
         if ui.quiet:
@@ -2734,7 +2641,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
@@ -2779,7 +2686,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.
@@ -2794,13 +2701,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 = []
@@ -2808,19 +2719,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
@@ -2832,7 +2743,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
 
@@ -2843,11 +2754,14 @@
           ('X', 'exclude', [], _('exclude names matching the given patterns')),
           ('n', 'dry-run', None, _('do not perform actions, just print output'))],
          _('hg add [OPTION]... [FILE]...')),
-    "debugaddremove|addremove":
+    "addremove":
         (addremove,
          [('I', 'include', [], _('include names matching the given patterns')),
           ('X', 'exclude', [], _('exclude names matching the given patterns')),
-          ('n', 'dry-run', None, _('do not perform actions, just print output'))],
+          ('n', 'dry-run', None,
+           _('do not perform actions, just print output')),
+          ('s', 'similarity', '',
+           _('guess renamed files by similarity (0<=s<=1)'))],
          _('hg addremove [OPTION]... [FILE]...')),
     "^annotate":
         (annotate,
@@ -2953,6 +2867,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,
@@ -2977,6 +2892,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')),
@@ -3013,7 +2930,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', '',
@@ -3039,6 +2956,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')),
@@ -3046,6 +2967,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'))],
@@ -3084,7 +3006,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]')),
@@ -3323,24 +3245,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
@@ -3360,23 +3274,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)
@@ -3428,6 +3355,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
@@ -3435,6 +3363,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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/context.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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.
--- a/mercurial/demandload.py	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/demandload.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/dirstate.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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)
@@ -476,7 +518,7 @@
                 if size >= 0 and (size != st.st_size
                                   or (mode ^ st.st_mode) & 0100):
                     modified.append(fn)
-                elif time != st.st_mtime:
+                elif time != int(st.st_mtime):
                     lookup.append(fn)
                 elif list_clean:
                     clean.append(fn)
--- a/mercurial/filelog.py	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/filelog.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/hg.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/hgweb/common.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/hgweb/hgweb_mod.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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 *
@@ -48,6 +48,7 @@
             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)
 
@@ -128,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:
@@ -175,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
@@ -221,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,
@@ -395,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 = {}
@@ -449,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
@@ -459,7 +452,6 @@
         rev = man.rev(mn)
         changerev = man.linkrev(mn)
         node = self.repo.changelog.node(changerev)
-        mff = man.readflags(mn)
 
         files = {}
 
@@ -493,7 +485,7 @@
                        "filenode": hex(fnode),
                        "parity": self.stripes(parity),
                        "basename": f,
-                       "permissions": mff[full]}
+                       "permissions": mf.execf(full)}
                 parity += 1
 
         def dirlist(**map):
@@ -611,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
@@ -691,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)],
@@ -773,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]))
 
--- a/mercurial/hgweb/hgwebdir_mod.py	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/hgweb/hgwebdir_mod.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/hgweb/request.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/hgweb/server.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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.
--- a/mercurial/httprangereader.py	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/httprangereader.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/httprepo.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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.
@@ -339,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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/i18n.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/localrepo.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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.
@@ -169,7 +169,7 @@
 
     tag_disallowed = ':\r\n'
 
-    def tag(self, name, node, local=False, message=None, user=None, date=None):
+    def tag(self, name, node, message, local, user, date):
         '''tag a revision with a symbolic name.
 
         if local is True, the tag is stored in a per-repository file.
@@ -191,27 +191,24 @@
             if c in name:
                 raise util.Abort(_('%r cannot be used in a tag name') % c)
 
-        self.hook('pretag', throw=True, node=node, tag=name, local=local)
+        self.hook('pretag', throw=True, node=hex(node), tag=name, local=local)
 
         if local:
-            self.opener('localtags', 'a').write('%s %s\n' % (node, name))
-            self.hook('tag', node=node, tag=name, local=local)
+            self.opener('localtags', 'a').write('%s %s\n' % (hex(node), name))
+            self.hook('tag', node=hex(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)'))
 
-        self.wfile('.hgtags', 'ab').write('%s %s\n' % (node, name))
+        self.wfile('.hgtags', 'ab').write('%s %s\n' % (hex(node), name))
         if self.dirstate.state('.hgtags') == '?':
             self.add(['.hgtags'])
 
-        if not message:
-            message = _('Added tag %s for changeset %s') % (name, node)
-
         self.commit(['.hgtags'], message, user, date)
-        self.hook('tag', node=node, tag=name, local=local)
+        self.hook('tag', node=hex(node), tag=name, local=local)
 
     def tags(self):
         '''return a mapping of tag to node'''
@@ -292,6 +289,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:
@@ -466,8 +467,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 = []
 
@@ -480,36 +480,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()
@@ -533,15 +529,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:
@@ -567,7 +562,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)
@@ -594,12 +589,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
@@ -671,8 +665,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)
@@ -714,8 +707,10 @@
                     for f in lookup:
                         if fcmp(f, mf2):
                             modified.append(f)
-                        elif wlock is not None:
-                            self.dirstate.update([f], "n")
+                        else:
+                            clean.append(f)
+                            if wlock is not None:
+                                self.dirstate.update([f], "n")
             else:
                 # we are comparing working dir against non-parent
                 # generate a pseudo-manifest for the working dir
@@ -754,16 +749,6 @@
             l.sort()
         return (modified, added, removed, deleted, unknown, ignored, clean)
 
-    def changes(self, node1=None, node2=None, files=[], match=util.always,
-                wlock=None, list_ignored=False, list_clean=False):
-        '''DEPRECATED - use status instead'''
-        marduit = self.status(node1, node2, files, match, wlock,
-                              list_ignored, list_clean)
-        if list_ignored:
-            return marduit[:-1]
-        else:
-            return marduit[:-2]
-
     def add(self, list, wlock=None):
         if not wlock:
             wlock = self.wlock()
@@ -812,7 +797,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()
@@ -822,7 +806,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):
@@ -1118,7 +1102,7 @@
             else:
                 raise util.Abort(_("repository is unrelated"))
 
-        self.ui.note(_("found new changesets starting at ") +
+        self.ui.debug(_("found new changesets starting at ") +
                      " ".join([short(f) for f in fetch]) + "\n")
 
         self.ui.debug(_("%d total queries\n") % reqcnt)
@@ -1173,22 +1157,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', remote.url())
+            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:
@@ -1693,530 +1684,6 @@
 
         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()
@@ -2242,7 +1709,7 @@
                         util.bytecount(total_bytes / elapsed)))
         self.reload()
         return len(self.heads()) + 1
-        
+
     def clone(self, remote, heads=[], stream=False):
         '''clone remote repository.
 
@@ -2271,3 +1738,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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/lock.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sun Sep 03 06:06:02 2006 -0400
@@ -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))
+    ui.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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/manifest.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/mdiff.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sun Sep 03 06:06:02 2006 -0400
@@ -0,0 +1,327 @@
+# 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)
+
+    # are we going backwards?
+    backwards = (pa == 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 linear_path and not (overwrite or 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).copy()
+    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)))
+
+    action = {}
+    forget = []
+
+    # update m1 from working dir
+    umap = dict.fromkeys(unknown)
+
+    for f in added + modified + unknown:
+        m1[f] = m1.get(f, nullid) + "+"
+        m1.set(f, util.is_exec(repo.wjoin(f), m1.execf(f)))
+
+    for f in deleted + removed:
+        del m1[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)
+
+    if partial:
+        for f in m1.keys():
+            if not partial(f): del m1[f]
+        for f in m2.keys():
+            if not partial(f): del m2[f]
+
+    # Compare manifests
+    for f, n in m1.iteritems():
+        if f in m2:
+            queued = 0
+
+            # are files different?
+            if n != m2[f]:
+                a = ma.get(f, nullid)
+                # are both different from the ancestor?
+                if not overwrite and n != a and m2[f] != a:
+                    repo.ui.debug(_(" %s versions differ, resolve\n") % f)
+                    action[f] = (fmerge(f, m1, m2, ma), n[:20], m2[f])
+                    queued = 1
+                # are we clobbering?
+                # is remote's version newer?
+                # or are we going back in time and clean?
+                elif overwrite or m2[f] != a or (backwards and not n[20:]):
+                    repo.ui.debug(_(" remote %s is newer, get\n") % f)
+                    action[f] = (m2.execf(f), m2[f], None)
+                    queued = 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
+                action[f] = (m2.execf(f), m2[f], None)
+
+            # do we still need to look at mode bits?
+            if not queued and m1.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, m1, m2, ma) != m1.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:
+                    r = repo.ui.prompt(
+                        (_(" local changed %s which remote deleted\n") % f) +
+                         _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
+                if r == _("d"):
+                    action[f] = (None, None, None)
+            else:
+                repo.ui.debug(_("other deleted %s\n") % f)
+                action[f] = (None, None, None)
+        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)
+                action[f] = (None, None, None)
+            elif not n[20:]: # same as parent
+                if backwards:
+                    repo.ui.debug(_("remote deleted %s\n") % f)
+                    action[f] = (None, None, None)
+                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 f[0] == "/":
+            continue
+        if f in ma and n != ma[f]:
+            r = _("k")
+            if not overwrite:
+                r = repo.ui.prompt(
+                    (_("remote changed %s which local deleted\n") % f) +
+                     _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
+            if r == _("k"):
+                action[f] = (m2.execf(f), n, None)
+        elif f not in ma:
+            repo.ui.debug(_("remote created %s\n") % f)
+            action[f] = (m2.execf(f), n, None)
+        else:
+            if overwrite or backwards:
+                repo.ui.debug(_("local deleted %s, recreating\n") % f)
+                action[f] = (m2.execf(f), n, None)
+            else:
+                repo.ui.debug(_("local deleted %s\n") % f)
+
+    del m1, m2, ma
+
+    ### apply phase
+
+    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)
+
+    # update files
+    unresolved = []
+    updated, merged, removed = 0, 0, 0
+    files = action.keys()
+    files.sort()
+    for f in files:
+        flag, my, other = action[f]
+        if f[0] == "/":
+            continue
+        if not my:
+            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))
+            removed +=1
+        elif other:
+            repo.ui.status(_("merging %s\n") % f)
+            if merge3(repo, f, my, other, xp1, xp2):
+                unresolved.append(f)
+            util.set_exec(repo.wjoin(f), flag)
+            merged += 1
+        else:
+            repo.ui.note(_("getting %s\n") % f)
+            t = repo.file(f).read(my)
+            repo.wwrite(f, t)
+            util.set_exec(repo.wjoin(f), flag)
+            updated += 1
+
+    # update dirstate
+    if not partial:
+        repo.dirstate.setparents(p1, p2)
+        repo.dirstate.forget(forget)
+        files = action.keys()
+        files.sort()
+        for f in files:
+            flag, my, other = action[f]
+            if not my:
+                if branchmerge:
+                    repo.dirstate.update([f], 'r')
+                else:
+                    repo.dirstate.forget([f])
+            elif not other:
+                if branchmerge:
+                    repo.dirstate.update([f], 'n', st_mtime=-1)
+                else:
+                    repo.dirstate.update([f], 'n')
+            else:
+                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 = ((updated, _("updated")),
+                 (merged - len(unresolved), _("merged")),
+                 (removed, _("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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/mpatch.c	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/node.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/packagescan.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/remoterepo.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/repo.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/revlog.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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.
@@ -744,8 +744,6 @@
 
     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):
             return self.node(id)
         try:
@@ -760,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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/sshrepo.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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.
@@ -204,3 +204,5 @@
 
     def stream_out(self):
         return self.do_cmd('stream_out')
+
+instance = sshrepository
--- a/mercurial/sshserver.py	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/sshserver.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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.
--- a/mercurial/statichttprepo.py	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/statichttprepo.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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):
@@ -50,3 +51,14 @@
 
     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	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/templater.py	Sun Sep 03 06:06:02 2006 -0400
@@ -459,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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/transaction.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/ui.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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.
 
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/util.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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.
@@ -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]:
@@ -607,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
 
@@ -696,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:
@@ -996,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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/verify.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/mercurial/version.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/changelog-gitweb.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/changelog.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/changeset-gitweb.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/changeset.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/error-gitweb.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/fileannotate-gitweb.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/fileannotate.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/filediff.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/filelog-gitweb.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/filelog.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/filerevision-gitweb.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/filerevision.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/manifest-gitweb.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/manifest.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/map	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/search-gitweb.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/search.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/shortlog-gitweb.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/static/style.css	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/summary-gitweb.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/tags-gitweb.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/templates/tags.tmpl	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/tests/README	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sat Aug 19 15:20:54 2006 -0400
+++ b/tests/run-tests.py	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sun Sep 03 06:06:02 2006 -0400
@@ -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
--- a/tests/test-addremove	Sat Aug 19 15:20:54 2006 -0400
+++ b/tests/test-addremove	Sun Sep 03 06:06:02 2006 -0400
@@ -10,3 +10,17 @@
 touch ../foo_2 bar_2
 hg -v addremove
 hg -v commit -m "add 2" -d "1000000 0"
+
+cd ..
+hg init sim
+cd sim
+echo a > a
+echo a >> a
+echo a >> a
+echo c > c
+hg commit -Ama
+mv a b
+rm c
+echo d > d
+hg addremove -s 0.5
+hg commit -mb
--- a/tests/test-addremove.out	Sat Aug 19 15:20:54 2006 -0400
+++ b/tests/test-addremove.out	Sun Sep 03 06:06:02 2006 -0400
@@ -1,10 +1,15 @@
-(the addremove command is deprecated; use add and remove --after instead)
 adding dir/bar
 adding foo
 dir/bar
 foo
-(the addremove command is deprecated; use add and remove --after instead)
 adding dir/bar_2
 adding foo_2
 dir/bar_2
 foo_2
+adding a
+adding c
+adding b
+adding d
+removing a
+removing c
+recording removal of a as rename to b (100% similar)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-annotate	Sun Sep 03 06:06:02 2006 -0400
@@ -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	Sun Sep 03 06:06:02 2006 -0400
@@ -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-archive.out	Sat Aug 19 15:20:54 2006 -0400