changeset 1830:4ced57680ce7

merge with crew.
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Fri, 03 Mar 2006 09:39:37 -0800
parents b0f6af327fd4 (current diff) a9343f9d7365 (diff)
children bf118f39afd7
files mercurial/hgweb.py mercurial/util.py
diffstat 59 files changed, 2978 insertions(+), 1133 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Fri Feb 10 11:25:07 2006 -0800
+++ b/.hgignore	Fri Mar 03 09:39:37 2006 -0800
@@ -12,6 +12,7 @@
 build
 dist
 doc/*.[0-9]
+doc/*.[0-9].gendoc.txt
 doc/*.[0-9].{x,ht}ml
 MANIFEST
 patches
--- a/contrib/bash_completion	Fri Feb 10 11:25:07 2006 -0800
+++ b/contrib/bash_completion	Fri Mar 03 09:39:37 2006 -0800
@@ -3,23 +3,26 @@
 _hg_command_list()
 {
     "$hg" --debug help 2>/dev/null | \
-	awk 'function command_line(line) {
-		gsub(/,/, "", line)
-		gsub(/:.*/, "", line)
-		split(line, aliases)
+	awk -F', ' '/^list of commands:/ {commands=1}
+	    commands==1 && /^ [^ ]/ {
+		line = substr($0, 2)
+		colon = index(line, ":")
+		if (colon > 0)
+		    line = substr(line, 1, colon-1)
+		n = split(line, aliases)
 		command = aliases[1]
-		delete aliases[1]
+		if (index(command, "debug") == 1) {
+		    for (i=1; i<=n; i++)
+			debug[j++] = aliases[i]
+		    next
+		}
 		print command
-		for (i in aliases)
+		for (i=2; i<=n; i++)
 		    if (index(command, aliases[i]) != 1)
 			print aliases[i]
 	    }
-	    /^list of commands:/ {commands=1}
-	    commands && /^ debug/ {a[i++] = $0; next;}
-	    commands && /^ [^ ]/ {command_line($0)}
 	    /^global options:/ {exit 0}
-	    END {for (i in a) command_line(a[i])}'
-
+	    END {for (i in debug) print debug[i]}'
 }
 
 _hg_option_list()
--- a/contrib/convert-repo	Fri Feb 10 11:25:07 2006 -0800
+++ b/contrib/convert-repo	Fri Mar 03 09:39:37 2006 -0800
@@ -21,7 +21,7 @@
 # interrupted and can be run repeatedly to copy new commits.
 
 import sys, os, zlib, sha, time
-from mercurial import hg, ui, util, commands
+from mercurial import hg, ui, util
 
 class convert_git:
     def __init__(self, path):
@@ -113,7 +113,7 @@
         except:
             pass
 
-    def putcommit(self, files, parents, author, date, text):
+    def putcommit(self, files, parents, author, dest, text):
         seen = {}
         pl = []
         for p in parents:
@@ -129,13 +129,8 @@
         while parents:
             p1 = p2
             p2 = parents.pop(0)
-            self.repo.dirstate.setparents(hg.bin(p1), hg.bin(p2))
-            if len(files) > 0:
-                olddir = os.getcwd()
-                os.chdir(self.path)
-                commands.addremove(self.repo.ui, self.repo, *files)
-                os.chdir(olddir)
-            self.repo.commit(files, text, author, date)
+            self.repo.rawcommit(files, text, author, dest,
+                                hg.bin(p1), hg.bin(p2))
             text = "(octopus merge fixup)\n"
             p2 = hg.hex(self.repo.changelog.tip())
 
@@ -265,6 +260,7 @@
         t = self.toposort(parents)
         t = [n for n in t if n not in self.map]
         num = len(t)
+        c = None
 
         for c in t:
             num -= 1
@@ -279,7 +275,7 @@
             if v in self.map:
                 ctags[k] = self.map[v]
 
-        if ctags:
+        if c and ctags:
             nrev = self.dest.puttags(ctags)
             # write another hash correspondence to override the previous
             # one so we don't end up with extra tag heads
--- a/contrib/hbisect.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/contrib/hbisect.py	Fri Mar 03 09:39:37 2006 -0800
@@ -187,7 +187,7 @@
         check_clean(self.ui, self.repo)
         rev = self.next()
         self.ui.write("Now testing %s\n" % hg.hex(rev))
-        return self.repo.update(rev, allow=True, force=True)
+        return self.repo.update(rev, force=True)
 
     def good(self, rev):
         self.goodrevs.append(rev)
@@ -232,7 +232,7 @@
             b.good(new_rev)
             ui.write("it is good\n")
         anc = b.ancestors()
-        repo.update(new_rev, allow=True, force=True)
+        repo.update(new_rev, force=True)
     for v in anc:
         if v != rev:
             ui.warn("fail to found cset! :(\n")
--- a/doc/Makefile	Fri Feb 10 11:25:07 2006 -0800
+++ b/doc/Makefile	Fri Mar 03 09:39:37 2006 -0800
@@ -8,6 +8,12 @@
 
 html: $(HTML)
 
+hg.1.txt: hg.1.gendoc.txt
+	touch hg.1.txt
+
+hg.1.gendoc.txt: ../mercurial/commands.py
+	python gendoc.py > $@
+
 %: %.xml
 	xmlto man $*.xml
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/gendoc.py	Fri Mar 03 09:39:37 2006 -0800
@@ -0,0 +1,92 @@
+import sys, textwrap
+# import from the live mercurial repo
+sys.path.insert(0, "..")
+from mercurial.commands import table, globalopts
+from mercurial.i18n import gettext as _
+
+def get_desc(docstr):
+    if not docstr:
+        return "", ""
+    # sanitize
+    docstr = docstr.strip("\n")
+    docstr = docstr.rstrip()
+    shortdesc = docstr.splitlines()[0].strip()
+
+    i = docstr.find("\n")
+    if i != -1:
+        desc = docstr[i+2:]
+    else:
+        desc = "    %s" % shortdesc
+    return (shortdesc, desc)
+
+def get_opts(opts):
+    for shortopt, longopt, default, desc in opts:
+        allopts = []
+        if shortopt:
+            allopts.append("-%s" % shortopt)
+        if longopt:
+            allopts.append("--%s" % longopt)
+        desc += default and _(" (default: %s)") % default or ""
+        yield(", ".join(allopts), desc)
+
+def get_cmd(cmd):
+    d = {}
+    attr = table[cmd]
+    cmds = cmd.lstrip("^").split("|")
+
+    d['synopsis'] = attr[2]
+    d['cmd'] = cmds[0]
+    d['aliases'] = cmd.split("|")[1:]
+    d['desc'] = get_desc(attr[0].__doc__)
+    d['opts'] = list(get_opts(attr[1]))
+    return d
+
+
+def show_doc(ui):
+    def bold(s, text=""):
+        ui.write("%s\n%s\n%s\n" % (s, "="*len(s), text))
+    def underlined(s, text=""):
+        ui.write("%s\n%s\n%s\n" % (s, "-"*len(s), text))
+
+    # print options
+    underlined(_("OPTIONS"))
+    for optstr, desc in get_opts(globalopts):
+        ui.write("%s::\n    %s\n\n" % (optstr, desc))
+
+    # print cmds
+    underlined(_("COMMANDS"))
+    h = {}
+    for c, attr in table.items():
+            f = c.split("|")[0]
+            f = f.lstrip("^")
+            h[f] = c
+    cmds = h.keys()
+    cmds.sort()
+
+    for f in cmds:
+            if f.startswith("debug"): continue
+            d = get_cmd(h[f])
+            # synopsis
+            ui.write("%s::\n" % d['synopsis'].replace("hg ","", 1))
+            # description
+            ui.write("%s\n\n" % d['desc'][1])
+            # options
+            opt_output = list(d['opts'])
+            if opt_output:
+                opts_len = max([len(line[0]) for line in opt_output])
+                ui.write(_("    options:\n"))
+                for optstr, desc in opt_output:
+                    if desc:
+                        s = "%-*s  %s" % (opts_len, optstr, desc)
+                    else:
+                        s = optstr
+                    s = textwrap.fill(s, initial_indent=4 * " ",
+                                      subsequent_indent=(6 + opts_len) * " ")
+                    ui.write("%s\n" % s)
+                ui.write("\n")
+            # aliases
+            if d['aliases']:
+                ui.write(_("    aliases: %s\n\n") % " ".join(d['aliases']))
+
+if __name__ == "__main__":
+    show_doc(sys.stdout)
--- a/doc/hg.1.txt	Fri Feb 10 11:25:07 2006 -0800
+++ b/doc/hg.1.txt	Fri Mar 03 09:39:37 2006 -0800
@@ -14,42 +14,6 @@
 -----------
 The hg(1) command provides a command line interface to the Mercurial system.
 
-OPTIONS
--------
-
--R, --repository::
-    repository root directory
-
---cwd::
-    change working directory
-
--y, --noninteractive::
-    do not prompt, assume 'yes' for any required answers
-
--q, --quiet::
-    suppress output
-
--v, --verbose::
-    enable additional output
-
---debug::
-    enable debugging output
-
---traceback::
-    print traceback on exception
-
---time::
-    time how long the command takes
-
---profile::
-    print command execution profile
-
---version::
-    output version information and exit
-
--h, --help::
-    display help and exit
-
 COMMAND ELEMENTS
 ----------------
 
@@ -70,581 +34,8 @@
     fast and the old-http:// protocol which is much slower but does not
     require a special server on the web host.
 
-COMMANDS
---------
 
-add [options] [files ...]::
-    Schedule files to be version controlled and added to the repository.
-
-    The files will be added to the repository at the next commit.
-
-    If no names are given, add all files in the current directory and
-    its subdirectories.
-
-addremove [options] [files ...]::
-    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.
-
-annotate [-r <rev> -u -n -c -d] [files ...]::
-    List changes in files, showing the revision id responsible for each line
-
-    This command is useful to discover who did a change or when a change took
-    place.
-
-    Without the -a option, annotate will avoid processing files it
-    detects as binary. With -a, annotate will generate an annotation
-    anyway, probably with undesirable results.
-
-    options:
-    -a, --text            treat all files as text
-    -I, --include <pat>   include names matching the given patterns
-    -X, --exclude <pat>   exclude names matching the given patterns
-    -r, --revision <rev>  annotate the specified revision
-    -u, --user            list the author
-    -d, --date            list the commit date
-    -c, --changeset       list the changeset
-    -n, --number          list the revision number (default)
-
-bundle <file> <other>::
-    (EXPERIMENTAL)
-
-    Generate a compressed changegroup file collecting all changesets
-    not found in the other repository.
-
-    This file can then be transferred using conventional means and
-    applied to another repository with the unbundle command. This is
-    useful when native push and pull are not available or when
-    exporting an entire repository is undesirable. The standard file
-    extension is ".hg".
-
-    Unlike import/export, this exactly preserves all changeset
-    contents including permissions, rename data, and revision history.
-
-cat [options] <file ...>::
-    Print the specified files as they were at the given revision.
-    If no revision is given then the tip is used.
-
-    Output may be to a file, in which case the name of the file is
-    given using a format string.  The formatting rules are the same as
-    for the export command, with the following additions:
-
-    %s   basename of file being printed
-    %d   dirname of file being printed, or '.' if in repo root
-    %p   root-relative path name of file being printed
-
-    options:
-    -I, --include <pat>       include names matching the given patterns
-    -X, --exclude <pat>       exclude names matching the given patterns
-    -o, --output <filespec>   print output to file with formatted name
-    -r, --rev <rev>           print the given revision
-
-clone [options] <source> [dest]::
-    Create a copy of an existing repository in a new directory.
-
-    If no destination directory name is specified, it defaults to the
-    basename of the source.
-
-    The location of the source is added to the new repository's
-    .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.
-
-    See pull for valid source format details.
-
-    options:
-    -U, --noupdate   do not update the new working directory
-    --pull           use pull protocol to copy metadata
-    -e, --ssh        specify ssh command to use
-    --remotecmd      specify hg command to run on the remote side
-
-commit [options] [files...]::
-    Commit changes to the given files into the repository.
-
-    If a list of files is omitted, all changes reported by "hg status"
-    from the root of the repository will be commited.
-
-    The HGEDITOR or EDITOR environment variables are used to start an
-    editor to add a commit comment.
-
-    Options:
-
-    -A, --addremove       run addremove during commit
-    -I, --include <pat>   include names matching the given patterns
-    -X, --exclude <pat>   exclude names matching the given patterns
-    -m, --message <text>  use <text> as commit message
-    -l, --logfile <file>  read the commit message from <file>
-    -d, --date <datecode> record datecode as commit date
-    -u, --user <user>     record user as commiter
-
-    aliases: ci
-
-copy <source ...> <dest>::
-    Mark dest as having copies of source files.  If dest is a
-    directory, copies are put in that directory.  If dest is a file,
-    there can only be one source.
-
-    By default, this command copies the contents of files as they
-    stand in the working directory.  If invoked with --after, the
-    operation is recorded, but no copying is performed.
-
-    This command takes effect in the next commit.
-
-    NOTE: This command should be treated as experimental. While it
-    should properly record copied files, this information is not yet
-    fully used by merge, nor fully reported by log.
-
-    Options:
-    -A, --after           record a copy that has already occurred
-    -I, --include <pat>   include names matching the given patterns
-    -X, --exclude <pat>   exclude names matching the given patterns
-    -f, --force           forcibly copy over an existing managed file
-
-    aliases: cp
-
-diff [-a] [-r revision] [-r revision] [files ...]::
-    Show differences between revisions for the specified files.
-
-    Differences between files are shown using the unified diff format.
-
-    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.
-
-    Without the -a option, diff will avoid generating diffs of files
-    it detects as binary. With -a, diff will generate a diff anyway,
-    probably with undesirable results.
-
-    options:
-    -a, --text           treat all files as text
-    -I, --include <pat>  include names matching the given patterns
-    -X, --exclude <pat>  exclude names matching the given patterns
-
-export [-o filespec] [revision] ...::
-    Print the changeset header and diffs for one or more revisions.
-
-    The information shown in the changeset header is: author,
-    changeset hash, parent and commit comment.
-
-    Output may be to a file, in which case the name of the file is
-    given using a format string.  The formatting rules are as follows:
-
-    %%   literal "%" character
-    %H   changeset hash (40 bytes of hexadecimal)
-    %N   number of patches being generated
-    %R   changeset revision number
-    %b   basename of the exporting repository
-    %h   short-form changeset hash (12 bytes of hexadecimal)
-    %n   zero-padded sequence number, starting at 1
-    %r   zero-padded changeset revision number
-
-    Without the -a option, export will avoid generating diffs of files
-    it detects as binary. With -a, export will generate a diff anyway,
-    probably with undesirable results.
-
-    options:
-    -a, --text                treat all files as text
-    -o, --output <filespec>   print output to file with formatted name
-
-forget [options] [files]::
-    Undo an 'hg add' scheduled for the next commit.
-
-    options:
-    -I, --include <pat>  include names matching the given patterns
-    -X, --exclude <pat>  exclude names matching the given patterns
-
-grep [options] pattern [files]::
-    Search revisions of files for a regular expression.
-
-    This command behaves differently than Unix grep.  It only accepts
-    Python/Perl regexps.  It searches repository history, not the
-    working directory.  It always prints the revision number in which
-    a match appears.
-
-    By default, grep only prints output for the first revision of a
-    file in which it finds a match.  To get it to print every revision
-    that contains a change in match status ("-" for a match that
-    becomes a non-match, or "+" for a non-match that becomes a match),
-    use the --all flag.
-
-    options:
-    -0, --print0              end fields with NUL
-    -I, --include <pat>       include names matching the given patterns
-    -X, --exclude <pat>       exclude names matching the given patterns
-        --all                 print all revisions that match
-    -i, --ignore-case         ignore case when matching
-    -l, --files-with-matches  print only filenames and revs that match
-    -n, --line-number         print matching line numbers
-    -r <rev>, --rev <rev>     search in given revision range
-    -u, --user                print user who committed change
-
-heads::
-    Show all repository head changesets.
-
-    Repository "heads" are changesets that don't have children
-    changesets. They are where development generally takes place and
-    are the usual targets for update and merge operations.
-
-identify::
-    Print a short summary of the current state of the repo.
-
-    This summary identifies the repository state using one or two parent
-    hash identifiers, followed by a "+" if there are uncommitted changes
-    in the working directory, followed by a list of tags for this revision.
-
-    aliases: id
-
-import [-p <n> -b <base> -f] <patches>::
-    Import a list of patches and commit them individually.
-
-    If there are outstanding changes in the working directory, import
-    will abort unless given the -f flag.
-
-    If a patch looks like a mail message (its first line starts with
-    "From " or looks like an RFC822 header), it will not be applied
-    unless the -f option is used.  The importer neither parses nor
-    discards mail headers, so use -f only to override the "mailness"
-    safety check, not to import a real mail message.
-
-    options:
-    -p, --strip <n>   directory strip option for patch. This has the same
-                      meaning as the corresponding patch option
-    -b <path>         base directory to read patches from
-    -f, --force       skip check for outstanding uncommitted changes
-
-    aliases: patch
-
-incoming [-p] [source]::
-    Show new changesets found in the specified repo or the default
-    pull repo. These are the changesets that would be pulled if a pull
-    was requested.
-
-    Currently only local repositories are supported.
-
-    options:
-    -p, --patch           show patch
-
-    aliases: in
-
-init [dest]::
-    Initialize a new repository in the given directory.  If the given
-    directory does not exist, it is created.
-
-    If no directory is given, the current directory is used.
-
-locate [options] [files]::
-    Print all files under Mercurial control whose names match the
-    given patterns.
-
-    This command searches the current directory and its
-    subdirectories.  To search an entire repository, move to the root
-    of the repository.
-
-    If no patterns are given to match, this command prints all file
-    names.
-
-    If you want to feed the output of this command into the "xargs"
-    command, use the "-0" option to both this command and "xargs".
-    This will avoid the problem of "xargs" treating single filenames
-    that contain white space as multiple filenames.
-
-    options:
-
-    -0, --print0         end filenames with NUL, for use with xargs
-    -f, --fullpath       print complete paths from the filesystem root
-    -I, --include <pat>  include names matching the given patterns
-    -r, --rev <rev>      search the repository as it stood at rev
-    -X, --exclude <pat>  exclude names matching the given patterns
-
-log [-r revision ...] [-p] [files]::
-    Print the revision history of the specified files or the entire project.
-
-    By default this command outputs: changeset id and hash, tags,
-    parents, user, date and time, and a summary for each commit. The
-    -v switch adds some more detail, such as changed files, manifest
-    hashes or message signatures.
-
-    options:
-    -I, --include <pat>   include names matching the given patterns
-    -X, --exclude <pat>   exclude names matching the given patterns
-    -r, --rev <A>         show the specified revision or range
-    -p, --patch           show patch
-
-    aliases: history
-
-manifest [revision]::
-    Print a list of version controlled files for the given revision.
-
-    The manifest is the list of files being version controlled. If no revision
-    is given then the tip is used.
-
-outgoing [-p] [dest]::
-    Show changesets not found in the specified destination repo or the
-    default push repo. These are the changesets that would be pushed
-    if a push was requested.
-
-    See pull for valid source format details.
-
-    options:
-    -p, --patch           show patch
-
-    aliases: out
-
-parents::
-    Print the working directory's parent revisions.
-
-paths [NAME]::
-    Show definition of symbolic path name NAME. If no name is given, show
-    definition of available names.
-
-    Path names are defined in the [paths] section of /etc/mercurial/hgrc
-    and $HOME/.hgrc.  If run inside a repository, .hg/hgrc is used, too.
-
-pull <repository path>::
-    Pull changes from a remote repository to a local one.
-
-    This finds all changes from the repository at the specified path
-    or URL and adds them to the local repository. By default, this
-    does not update the copy of the project in the working directory.
-
-    Valid URLs are of the form:
-
-      local/filesystem/path
-      http://[user@]host[:port][/path]
-      https://[user@]host[:port][/path]
-      ssh://[user@]host[:port][/path]
-
-    SSH requires an accessible shell account on the destination machine
-    and a copy of hg in the remote path.  With SSH, paths are relative
-    to the remote user's home directory by default; use two slashes at
-    the start of a path to specify it as relative to the filesystem root.
-
-    options:
-    -u, --update   update the working directory to tip after pull
-    -e, --ssh    specify ssh command to use
-    --remotecmd  specify hg command to run on the remote side
-
-push <destination>::
-    Push changes from the local repository to the given destination.
-
-    This is the symmetrical operation for pull. It helps to move
-    changes from the current repository to a different one. If the
-    destination is local this is identical to a pull in that directory
-    from the current one.
-
-    By default, push will refuse to run if it detects the result would
-    increase the number of remote heads. This generally indicates the
-    the client has forgotten to sync and merge before pushing.
-
-    Valid URLs are of the form:
-
-      local/filesystem/path
-      ssh://[user@]host[:port][/path]
-
-    SSH requires an accessible shell account on the destination
-    machine and a copy of hg in the remote path.
-
-    options:
-
-    -f, --force  force update
-    -e, --ssh    specify ssh command to use
-    --remotecmd  specify hg command to run on the remote side
-
-rawcommit [-p -d -u -F -m -l]::
-    Lowlevel commit, for use in helper scripts. (DEPRECATED)
-
-    This command is not intended to be used by normal users, as it is
-    primarily useful for importing from other SCMs.
-
-    This command is now deprecated and will be removed in a future
-    release, please use debugsetparents and commit instead.
-
-recover::
-    Recover from an interrupted commit or pull.
-
-    This command tries to fix the repository status after an interrupted
-    operation. It should only be necessary when Mercurial suggests it.
-
-remove [options] [files ...]::
-    Schedule the indicated files for removal from the repository.
-
-    This command schedules the files to be removed at the next commit.
-    This only removes files from the current branch, not from the
-    entire project history.  If the files still exist in the working
-    directory, they will be deleted from it.
-
-    aliases: rm
-
-rename <source ...> <dest>::
-    Mark dest as copies of sources; mark sources for deletion.  If
-    dest is a directory, copies are put in that directory.  If dest is
-    a file, there can only be one source.
-
-    By default, this command copies the contents of files as they
-    stand in the working directory.  If invoked with --after, the
-    operation is recorded, but no copying is performed.
-
-    This command takes effect in the next commit.
-
-    NOTE: This command should be treated as experimental. While it
-    should properly record rename files, this information is not yet
-    fully used by merge, nor fully reported by log.
-
-    Options:
-    -A, --after        record a rename that has already occurred
-    -f, --force        forcibly copy over an existing managed file
-
-    aliases: mv
-
-revert [names ...]::
-    The revert command has two modes of operation.
-
-    In its default mode, it reverts any uncommitted modifications made
-    to the named files or directories.  This restores the contents of
-    the affected files to an unmodified state.
-
-    Using the -r option, it reverts the given files or directories to
-    their state as of an earlier revision.  This can be helpful to "roll
-    back" some or all of a change that should not have been committed.
-
-    Revert modifies the working directory.  It does not commit any
-    changes, or change the parent of the current working directory.
-
-    If a file has been deleted, it is recreated.  If the executable
-    mode of a file was changed, it is reset.
-
-    If a directory is given, all files in that directory and its
-    subdirectories are reverted.
-
-    If no arguments are given, all files in the current directory and
-    its subdirectories are reverted.
-
-    options:
-    -r, --rev <rev>       revision to revert to
-    -n, --nonrecursive    do not recurse into subdirectories
-
-root::
-    Print the root directory of the current repository.
-
-serve [options]::
-    Start a local HTTP repository browser and pull server.
-
-    By default, the server logs accesses to stdout and errors to
-    stderr.  Use the "-A" and "-E" options to log to files.
-
-    options:
-    -A, --accesslog <file>   name of access log file to write to
-    -E, --errorlog <file>    name of error log file to write to
-    -a, --address <addr>     address to use
-    -p, --port <n>           port to use (default: 8000)
-    -n, --name <name>        name to show in web pages (default: working dir)
-    -t, --templatedir <path> web templates to use
-    -6, --ipv6               use IPv6 in addition to IPv4
-
-status [options] [files]::
-    Show changed files in the working directory.  If no names are
-    given, all files are shown.  Otherwise, only files matching the
-    given names are shown.
-
-    The codes used to show the status of files are:
-
-    M = changed
-    A = added
-    R = removed
-    ? = not tracked
-
-    options:
-
-    -m, --modified       show only modified files
-    -a, --added          show only added files
-    -r, --removed        show only removed files
-    -u, --unknown        show only unknown (not tracked) files
-    -n, --no-status      hide status prefix
-    -0, --print0         end filenames with NUL, for use with xargs
-    -I, --include <pat>  include names matching the given patterns
-    -X, --exclude <pat>  exclude names matching the given patterns
-
-tag [-l -m <text> -d <datecode> -u <user>] <name> [revision]::
-    Name a particular revision using <name>.
-
-    Tags are used to name particular revisions of the repository and are
-    very useful to compare different revision, to go back to significant
-    earlier versions or to mark branch points as releases, etc.
-
-    If no revision is given, the tip is used.
-
-    To facilitate version control, distribution, and merging of tags,
-    they are stored as a file named ".hgtags" which is managed
-    similarly to other project files and can be hand-edited if
-    necessary.
-
-    options:
-    -l, --local           make the tag local
-    -m, --message <text>  message for tag commit log entry
-    -d, --date <datecode> datecode for commit
-    -u, --user <user>     user for commit
-
-    Note: Local tags are not version-controlled or distributed and are
-    stored in the .hg/localtags file. If there exists a local tag and
-    a public tag with the same name, local tag is used.
-
-tags::
-    List the repository tags.
-
-    This lists both regular and local tags.
-
-tip::
-    Show the tip revision.
-
-unbundle <file>::
-    (EXPERIMENTAL)
-
-    Apply a compressed changegroup file generated by the bundle
-    command.
-
-undo::
-    Undo the last commit or pull transaction.
-
-    Roll back the last pull or commit transaction on the
-    repository, restoring the project to its earlier state.
-
-    This command should be used with care. There is only one level of
-    undo and there is no redo.
-
-    This command is not intended for use on public repositories. Once
-    a change is visible for pull by other users, undoing it locally is
-    ineffective.
-
-update [-m -C] [revision]::
-    Update the working directory to the specified revision.
-
-    By default, update will refuse to run if doing so would require
-    merging or discarding local changes.
-
-    With the -m option, a merge will be performed.
-
-    With the -C option, local changes will be lost.
-
-    options:
-    -m, --merge       allow merging of branches
-    -C, --clean       overwrite locally modified files
-
-    aliases: up checkout co
-
-verify::
-    Verify the integrity of the current repository.
-
-    This will perform an extensive check of the repository's
-    integrity, validating the hashes and checksums of each entry in
-    the changelog, manifest, and tracked files, as well as the
-    integrity of their crosslinks and indices.
+include::hg.1.gendoc.txt[]
 
 FILE NAME PATTERNS
 ------------------
--- a/doc/hgrc.5.txt	Fri Feb 10 11:25:07 2006 -0800
+++ b/doc/hgrc.5.txt	Fri Mar 03 09:39:37 2006 -0800
@@ -141,20 +141,75 @@
 
     [hooks]
     # do not use the site-wide hook
-    commit =
-    commit.email = /my/email/hook
-    commit.autobuild = /my/build/hook
+    incoming =
+    incoming.email = /my/email/hook
+    incoming.autobuild = /my/build/hook
+
+  Most hooks are run with environment variables set that give added
+  useful information.  For each hook below, the environment variables
+  it is passed are listed with names of the form "$HG_foo".
 
   changegroup;;
-    Run after a changegroup has been added via push or pull. Passed
-    the ID of the first new changeset in $NODE.
+    Run after a changegroup has been added via push, pull or
+    unbundle. ID of the first new changeset is in $HG_NODE.
   commit;;
-    Run after a changeset has been created or for each changeset
-    pulled. Passed the ID of the newly created changeset in
-    environment variable $NODE.
+    Run after a changeset has been created in the local repository.
+    ID of the newly created changeset is in $HG_NODE.  Parent
+    changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
+  incoming;;
+    Run after a changeset has been pulled, pushed, or unbundled into
+    the local repository.  The ID of the newly arrived changeset is in
+    $HG_NODE.
+  outgoing;;
+    Run after sending changes from local repository to another.  ID of
+    first changeset sent is in $HG_NODE.  Source of operation is in
+    $HG_SOURCE; see "preoutgoing" hook for description.
+  prechangegroup;;
+    Run before a changegroup is added via push, pull or unbundle.
+    Exit status 0 allows the changegroup to proceed.  Non-zero status
+    will cause the push, pull or unbundle to fail.
   precommit;;
-    Run before starting a commit.  Exit status 0 allows the commit to
-    proceed.  Non-zero status will cause the commit to fail.
+    Run before starting a local commit.  Exit status 0 allows the
+    commit to proceed.  Non-zero status will cause the commit to fail.
+    Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
+  preoutgoing;;
+    Run before computing changes to send from the local repository to
+    another.  Non-zero status will cause failure.  This lets you
+    prevent pull over http or ssh.  Also prevents against local pull,
+    push (outbound) or bundle commands, but not effective, since you
+    can just copy files instead then.  Source of operation is in
+    $HG_SOURCE.  If "serve", operation is happening on behalf of
+    remote ssh or http repository.  If "push", "pull" or "bundle",
+    operation is happening on behalf of repository on same system.
+  pretag;;
+    Run before creating a tag.  Exit status 0 allows the tag to be
+    created.  Non-zero status will cause the tag to fail.  ID of
+    changeset to tag is in $HG_NODE.  Name of tag is in $HG_TAG.  Tag
+    is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
+  pretxnchangegroup;;
+    Run after a changegroup has been added via push, pull or unbundle,
+    but before the transaction has been committed.  Changegroup is
+    visible to hook program.  This lets you validate incoming changes
+    before accepting them.  Passed the ID of the first new changeset
+    in $HG_NODE.  Exit status 0 allows the transaction to commit.
+    Non-zero status will cause the transaction to be rolled back and
+    the push, pull or unbundle will fail.
+  pretxncommit;;
+    Run after a changeset has been created but the transaction not yet
+    committed.  Changeset is visible to hook program.  This lets you
+    validate commit message and changes.  Exit status 0 allows the
+    commit to proceed.  Non-zero status will cause the transaction to
+    be rolled back.  ID of changeset is in $HG_NODE.  Parent changeset
+    IDs are in $HG_PARENT1 and $HG_PARENT2.
+  tag;;
+    Run after a tag is created.  ID of tagged changeset is in
+    $HG_NODE.  Name of tag is in $HG_TAG.  Tag is local if
+    $HG_LOCAL=1, in repo if $HG_LOCAL=0.
+
+  In earlier releases, the names of hook environment variables did not
+  have a "HG_" prefix.  These unprefixed names are still provided in
+  the environment for backwards compatibility, but their use is
+  deprecated, and they will be removed in a future release.
 
 http_proxy::
   Used to access web-based Mercurial repositories through a HTTP
@@ -192,6 +247,9 @@
     remote command to use for clone/push/pull operations. Default is 'hg'.
   ssh;;
     command to use for SSH connections. Default is 'ssh'.
+  timeout;;
+    The timeout used when a lock is held (in seconds), a negative value
+    means no timeout. Default is 600.
   username;;
     The committer of a changeset created when running "commit".
     Typically a person's name and email address, e.g. "Fred Widget
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/mq.py	Fri Mar 03 09:39:37 2006 -0800
@@ -0,0 +1,1308 @@
+#!/usr/bin/env python
+# queue.py - patch queues for mercurial
+#
+# Copyright 2005 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.
+
+from mercurial.demandload import *
+demandload(globals(), "os sys re struct traceback errno bz2")
+from mercurial.i18n import gettext as _
+from mercurial import ui, hg, revlog, commands, util
+
+versionstr = "0.45"
+
+repomap = {}
+
+class queue:
+    def __init__(self, ui, path, patchdir=None):
+        self.opener = util.opener(path)
+        self.basepath = path
+        if patchdir:
+            self.path = patchdir
+        else:
+            self.path = os.path.join(path, "patches")
+        self.ui = ui
+        self.applied = []
+        self.full_series = []
+        self.applied_dirty = 0
+        self.series_dirty = 0
+        self.series_path = os.path.join(self.path, "series")
+        self.status_path = os.path.join(self.path, "status")
+
+        s = self.series_path
+        if os.path.exists(s):
+            self.full_series = self.opener(s).read().splitlines()
+        self.read_series(self.full_series)
+
+        s = self.status_path
+        if os.path.exists(s):
+            self.applied = self.opener(s).read().splitlines()
+
+    def find_series(self, patch):
+        pre = re.compile("(\s*)([^#]+)")
+        index = 0
+        for l in self.full_series:
+            m = pre.match(l)
+            if m:
+                s = m.group(2)
+                s = s.rstrip()
+                if s == patch:
+                    return index
+            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
+        self.series = []
+        self.series = [ x for x in matcher(list) ]
+
+    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 readheaders(self, patch):
+        def eatdiff(lines):
+            while lines:
+                l = lines[-1]
+                if (l.startswith("diff -") or
+                    l.startswith("Index:") or
+                    l.startswith("===========")):
+                    del lines[-1]
+                else:
+                    break
+        def eatempty(lines):
+            while lines:
+                l = lines[-1]
+                if re.match('\s*$', l):
+                    del lines[-1]
+                else:
+                    break
+
+        pf = os.path.join(self.path, patch)
+        message = []
+        comments = []
+        user = None
+        format = None
+        subject = None
+        diffstart = 0
+
+        for line in file(pf):
+            line = line.rstrip()
+            if diffstart:
+                if line.startswith('+++ '):
+                    diffstart = 2
+                break
+            if line.startswith("--- "):
+                diffstart = 1
+                continue
+            elif format == "hgpatch":
+                # parse values when importing the result of an hg export
+                if line.startswith("# User "):
+                    user = line[7:]
+                elif not line.startswith("# ") and line:
+                    message.append(line)
+                    format = None
+            elif line == '# HG changeset patch':
+                format = "hgpatch"
+            elif (format != "tagdone" and (line.startswith("Subject: ") or
+                                           line.startswith("subject: "))):
+                subject = line[9:]
+                format = "tag"
+            elif (format != "tagdone" and (line.startswith("From: ") or
+                                           line.startswith("from: "))):
+                user = line[6:]
+                format = "tag"
+            elif format == "tag" and line == "":
+                # when looking for tags (subject: from: etc) they
+                # end once you find a blank line in the source
+                format = "tagdone"
+            else:
+                message.append(line)
+            comments.append(line)
+
+        eatdiff(message)
+        eatdiff(comments)
+        eatempty(message)
+        eatempty(comments)
+
+        # make sure message isn't empty
+        if format and format.startswith("tag") and subject:
+            message.insert(0, "")
+            message.insert(0, subject)
+        return (message, comments, user, diffstart > 1)
+
+    def mergeone(self, repo, mergeq, head, patch, rev, wlock):
+        # first try just applying the patch
+        (err, n) = self.apply(repo, [ patch ], update_status=False,
+                              strict=True, merge=rev, wlock=wlock)
+
+        if err == 0:
+            return (err, n)
+
+        if n is None:
+            self.ui.warn("apply failed for patch %s\n" % patch)
+            sys.exit(1)
+
+        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)
+        self.strip(repo, n, update=False, backup='strip', wlock=wlock)
+
+        c = repo.changelog.read(rev)
+        ret = repo.update(rev, allow=True, wlock=wlock)
+        if ret:
+            self.ui.warn("update returned %d\n" % ret)
+            sys.exit(1)
+        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)
+        try:
+            message, comments, user, patchfound = mergeq.readheaders(patch)
+        except:
+            self.ui.warn("Unable to read %s\n" % patch)
+            sys.exit(1)
+
+        patchf = self.opener(os.path.join(self.path, patch), "w")
+        if comments:
+            comments = "\n".join(comments) + '\n\n'
+            patchf.write(comments)
+        commands.dodiff(patchf, self.ui, repo, head, n)
+        patchf.close()
+        return (0, n)
+
+    def qparents(self, repo, rev=None):
+        if rev is None:
+            (p1, p2) = repo.dirstate.parents()
+            if p2 == revlog.nullid:
+                return p1
+            if len(self.applied) == 0:
+                return None
+            (top, patch) = self.applied[-1].split(':')
+            top = revlog.bin(top)
+            return top
+        pp = repo.changelog.parents(rev)
+        if pp[1] != revlog.nullid:
+            arevs = [ x.split(':')[0] for x in self.applied ]
+            p0 = revlog.hex(pp[0])
+            p1 = revlog.hex(pp[1])
+            if p0 in arevs:
+                return pp[0]
+            if p1 in arevs:
+                return pp[1]
+            return None
+        return pp[0]
+
+    def mergepatch(self, repo, mergeq, series, wlock):
+        if len(self.applied) == 0:
+            # each of the patches merged in will have two parents.  This
+            # can confuse the qrefresh, qdiff, and strip code because it
+            # needs to know which parent is actually in the patch queue.
+            # so, we insert a merge marker with only one parent.  This way
+            # the first patch in the queue is never a merge patch
+            #
+            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_dirty = 1
+
+        head = self.qparents(repo)
+
+        for patch in series:
+            patch = mergeq.lookup(patch)
+            if not patch:
+                self.ui.warn("patch %s does not exist\n" % patch)
+                return (1, None)
+
+            info = mergeq.isapplied(patch)
+            if not info:
+                self.ui.warn("patch %s is not applied\n" % patch)
+                return (1, None)
+            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_dirty = 1
+            if err:
+                return (err, head)
+        return (0, head)
+
+    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)
+
+            try:
+                message, comments, user, patchfound = self.readheaders(patch)
+            except:
+                self.ui.warn("Unable to read %s\n" % pf)
+                err = 1
+                break
+
+            if not message:
+                message = "imported patch %s\n" % patch
+            else:
+                if list:
+                    message.append("\nimported patch %s" % patch)
+                message = '\n'.join(message)
+
+            try:
+                f = os.popen("patch -p1 --no-backup-if-mismatch < '%s'" % (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()
+
+            if merge and len(files) > 0:
+                # Mark as merged and update dirstate parent info
+                repo.dirstate.update(repo.dirstate.filterfiles(files), '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)
+            n = repo.commit(files, message, user, force=1, lock=lock,
+                            wlock=wlock)
+
+            if n == None:
+                self.ui.warn("repo commit failed\n")
+                sys.exit(1)
+
+            if update_status:
+                self.applied.append(revlog.hex(n) + ":" + patch)
+
+            if patcherr:
+                if not patchfound:
+                    self.ui.warn("patch %s is empty\n" % patch)
+                    err = 0
+                else:
+                    self.ui.warn("patch failed, rejects left in working dir\n")
+                    err = 1
+                break
+
+            if fuzz and strict:
+                self.ui.warn("fuzz found when applying patch, stopping\n")
+                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)
+        self.series_dirty = 1
+
+    def check_toppatch(self, repo):
+        if len(self.applied) > 0:
+            (top, patch) = self.applied[-1].split(':')
+            top = revlog.bin(top)
+            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)
+            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 new(self, repo, patch, msg=None, force=None):
+        if not force:
+            self.check_localchanges(repo)
+        self.check_toppatch(repo)
+        wlock = repo.wlock()
+        insert = self.series_end()
+        if msg:
+            n = repo.commit([], "[mq]: %s" % msg, force=True, wlock=wlock)
+        else:
+            n = repo.commit([],
+                            "New patch: %s" % patch, force=True, wlock=wlock)
+        if n == None:
+            self.ui.warn("repo commit failed\n")
+            sys.exit(1)
+        self.full_series[insert:insert] = [patch]
+        self.applied.append(revlog.hex(n) + ":" + patch)
+        self.read_series(self.full_series)
+        self.series_dirty = 1
+        self.applied_dirty = 1
+        p = self.opener(os.path.join(self.path, patch), "w")
+        if msg:
+            msg = msg + "\n"
+            p.write(msg)
+        p.close()
+        wlock = None
+        r = self.qrepo()
+        if r: r.add([patch])
+
+    def strip(self, repo, rev, update=True, backup="all", wlock=None):
+        def limitheads(chlog, stop):
+            """return the list of all nodes that have no children"""
+            p = {}
+            h = []
+            stoprev = 0
+            if stop in chlog.nodemap:
+                stoprev = chlog.rev(stop)
+
+            for r in range(chlog.count() - 1, -1, -1):
+                n = chlog.node(r)
+                if n not in p:
+                    h.append(n)
+                if n == stop:
+                    break
+                if r < stoprev:
+                    break
+                for pn in chlog.parents(n):
+                    p[pn] = 1
+            return h
+
+        def bundle(cg):
+            backupdir = repo.join("strip-backup")
+            if not os.path.isdir(backupdir):
+                os.mkdir(backupdir)
+            name = os.path.join(backupdir, "%s" % revlog.short(rev))
+            name = savename(name)
+            self.ui.warn("saving bundle to %s\n" % name)
+            # TODO, exclusive open
+            f = open(name, "wb")
+            try:
+                f.write("HG10")
+                z = bz2.BZ2Compressor(9)
+                while 1:
+                    chunk = cg.read(4096)
+                    if not chunk:
+                        break
+                    f.write(z.compress(chunk))
+                f.write(z.flush())
+            except:
+                os.unlink(name)
+                raise
+            f.close()
+            return name
+
+        def stripall(rev, revnum):
+            cl = repo.changelog
+            c = cl.read(rev)
+            mm = repo.manifest.read(c[0])
+            seen = {}
+
+            for x in xrange(revnum, cl.count()):
+                c = cl.read(cl.node(x))
+                for f in c[3]:
+                    if f in seen:
+                        continue
+                    seen[f] = 1
+                    if f in mm:
+                        filerev = mm[f]
+                    else:
+                        filerev = 0
+                    seen[f] = filerev
+            # 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:
+                ff = repo.file(f)
+                filerev = seen[f]
+                if filerev != 0:
+                    if filerev in ff.nodemap:
+                        filerev = ff.rev(filerev)
+                    else:
+                        filerev = 0
+                ff.strip(filerev, revnum)
+
+        if not wlock:
+            wlock = repo.wlock()
+        lock = repo.lock()
+        chlog = repo.changelog
+        # TODO delete the undo files, and handle undo of merge sets
+        pp = chlog.parents(rev)
+        revnum = chlog.rev(rev)
+
+        if update:
+            urev = self.qparents(repo, rev)
+            repo.update(urev, allow=False, force=True, wlock=wlock)
+            repo.dirstate.write()
+
+        # save is a list of all the branches we are truncating away
+        # that we actually want to keep.  changegroup will be used
+        # to preserve them and add them back after the truncate
+        saveheads = []
+        savebases = {}
+
+        tip = chlog.tip()
+        heads = limitheads(chlog, rev)
+        seen = {}
+
+        # search through all the heads, finding those where the revision
+        # we want to strip away is an ancestor.  Also look for merges
+        # that might be turned into new heads by the strip.
+        while heads:
+            h = heads.pop()
+            n = h
+            while True:
+                seen[n] = 1
+                pp = chlog.parents(n)
+                if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
+                    if pp[1] not in seen:
+                        heads.append(pp[1])
+                if pp[0] == revlog.nullid:
+                    break
+                if chlog.rev(pp[0]) < revnum:
+                    break
+                n = pp[0]
+                if n == rev:
+                    break
+            r = chlog.reachable(h, rev)
+            if rev not in r:
+                saveheads.append(h)
+                for x in r:
+                    if chlog.rev(x) > revnum:
+                        savebases[x] = 1
+
+        # create a changegroup for all the branches we need to keep
+        if backup is "all":
+            backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
+            bundle(backupch)
+        if saveheads:
+            backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
+            chgrpfile = bundle(backupch)
+
+        stripall(rev, revnum)
+
+        change = chlog.read(rev)
+        repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
+        chlog.strip(revnum, revnum)
+        if saveheads:
+            self.ui.status("adding branch\n")
+            commands.unbundle(self.ui, repo, chgrpfile, update=False)
+            if backup is not "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])
+        return None
+
+    def lookup(self, patch):
+        if patch == None:
+            return None
+        if patch in self.series:
+            return patch
+        if not os.path.isfile(os.path.join(self.path, 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
+
+    def push(self, repo, patch=None, force=False, list=False,
+             mergeq=None, wlock=None):
+        if not wlock:
+            wlock = repo.wlock()
+        patch = self.lookup(patch)
+        if patch and self.isapplied(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")
+            sys.exit(1)
+        if not force:
+            self.check_localchanges(repo)
+
+        self.applied_dirty = 1;
+        start = self.series_end()
+        if start > 0:
+            self.check_toppatch(repo)
+        if not patch:
+            patch = self.series[start]
+            end = start + 1
+        else:
+            end = self.series.index(patch, start) + 1
+        s = self.series[start:end]
+        if mergeq:
+            ret = self.mergepatch(repo, mergeq, s, wlock)
+        else:
+            ret = self.apply(repo, s, list, wlock=wlock)
+        top = self.applied[-1].split(':')[1]
+        if ret[0]:
+            self.ui.write("Errors during apply, please fix and refresh %s\n" %
+                          top)
+        else:
+            self.ui.write("Now at: %s\n" % top)
+        return ret[0]
+
+    def pop(self, repo, patch=None, force=False, update=True, wlock=None):
+        def getfile(f, rev):
+            t = repo.file(f).read(rev)
+            try:
+                repo.wfile(f, "w").write(t)
+            except IOError:
+                os.makedirs(os.path.dirname(repo.wjoin(f)))
+                repo.wfile(f, "w").write(t)
+
+        if not wlock:
+            wlock = repo.wlock()
+        if patch:
+            # index, rev, patch
+            info = self.isapplied(patch)
+            if not info:
+                patch = self.lookup(patch)
+            info = self.isapplied(patch)
+            if not info:
+                self.ui.warn("patch %s is not applied\n" % patch)
+                sys.exit(1)
+        if len(self.applied) == 0:
+            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 ]
+            for p in parents:
+                if p in rr:
+                    self.ui.warn("qpop: forcing dirstate update\n")
+                    update = True
+
+        if not force and update:
+            self.check_localchanges(repo)
+
+        self.applied_dirty = 1;
+        end = len(self.applied)
+        if not patch:
+            info = [len(self.applied) - 1] + self.applied[-1].split(':')
+        start = info[0]
+        rev = revlog.bin(info[1])
+
+        # we know there are no local changes, so we can make a simplified
+        # form of hg.update.
+        if update:
+            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)
+            if d:
+                raise util.Abort("deletions found between repo revs")
+            for f in c:
+                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')
+            for f in a:
+                try: os.unlink(repo.wjoin(f))
+                except: raise
+                try: os.removedirs(os.path.dirname(repo.wjoin(f)))
+                except: pass
+            if a:
+                repo.dirstate.forget(a)
+            repo.dirstate.setparents(qp, revlog.nullid)
+        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])
+        else:
+            self.ui.write("Patch queue now empty\n")
+
+    def diff(self, repo, files):
+        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)
+
+    def refresh(self, repo, short=False):
+        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 = revlog.bin(top)
+        cparents = repo.changelog.parents(top)
+        patchparent = self.qparents(repo, top)
+        message, comments, user, patchfound = self.readheaders(patch)
+
+        patchf = self.opener(os.path.join(self.path, patch), "w")
+        if comments:
+            comments = "\n".join(comments) + '\n\n'
+            patchf.write(comments)
+
+        tip = repo.changelog.tip()
+        if top == tip:
+            # if the top of our patch queue is also the tip, there is an
+            # optimization here.  We update the dirstate in place and strip
+            # off the tip commit.  Then just commit the current directory
+            # tree.  We can also send repo.commit the list of files
+            # changed to speed up the diff
+            #
+            # in short mode, we only diff the files included in the
+            # patch already
+            #
+            # this should really read:
+            #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
+            # but we do it backwards to take advantage of manifest/chlog
+            # caching against the next repo.changes call
+            #
+            (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
+            if short:
+                filelist = cc + aa + dd
+            else:
+                filelist = None
+            (c, a, r, d, u) = repo.changes(None, None, filelist)
+
+            # 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:
+                if x not in aa:
+                    cc.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)
+                else:
+                    aa.append(x)
+            # make sure any files deleted in the local dirstate
+            # are not in the add or change column of the patch
+            forget = []
+            for x in d + r:
+                if x in aa:
+                    del aa[aa.index(x)]
+                    forget.append(x)
+                    continue
+                elif x in cc:
+                    del cc[cc.index(x)]
+                dd.append(x)
+
+            c = list(util.unique(cc))
+            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))
+            patchf.close()
+
+            changes = repo.changelog.read(tip)
+            repo.dirstate.setparents(*cparents)
+            repo.dirstate.update(a, 'a')
+            repo.dirstate.update(r, 'r')
+            repo.dirstate.update(c, 'n')
+            repo.dirstate.forget(forget)
+
+            if not message:
+                message = "patch queue: %s\n" % patch
+            else:
+                message = "\n".join(message)
+            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_dirty = 1
+        else:
+            commands.dodiff(patchf, self.ui, repo, patchparent, None)
+            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")
+        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)
+        if not patch:
+            start = self.series_end()
+        else:
+            start = self.series.index(patch) + 1
+        for p in self.series[start:]:
+            self.ui.write("%s\n" % p)
+
+    def qseries(self, repo, missing=None):
+        start = self.series_end()
+        if not missing:
+            for p in self.series[:start]:
+                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)
+        else:
+            list = []
+            for root, dirs, files in os.walk(self.path):
+                d = root[len(self.path) + 1:]
+                for f in files:
+                    fl = os.path.join(d, f)
+                    if (fl not in self.series and fl != "status" and
+                        fl != "series" 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)
+
+    def issaveline(self, l):
+        name = l.split(':')[1]
+        if name == '.hg.patches.save.line':
+            return True
+
+    def qrepo(self, create=False):
+        if create or os.path.isdir(os.path.join(self.path, ".hg")):
+            return hg.repository(ui=self.ui, path=self.path, create=create)
+
+    def restore(self, repo, rev, delete=None, qupdate=None):
+        c = repo.changelog.read(rev)
+        desc = c[4].strip()
+        lines = desc.splitlines()
+        i = 0
+        datastart = None
+        series = []
+        applied = []
+        qpp = None
+        for i in xrange(0, len(lines)):
+            if lines[i] == 'Patch Data:':
+                datastart = i + 1
+            elif lines[i].startswith('Dirstate:'):
+                l = lines[i].rstrip()
+                l = l[10:].split(' ')
+                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)
+        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.series_dirty = 1
+        self.applied_dirty = 1
+        heads = repo.changelog.heads()
+        if delete:
+            if rev not in heads:
+                self.ui.warn("save entry has children, leaving it alone\n")
+            else:
+                self.ui.warn("removing save entry %s\n" % hg.short(rev))
+                pp = repo.dirstate.parents()
+                if rev in pp:
+                    update = True
+                else:
+                    update = False
+                self.strip(repo, rev, update=update, backup='strip')
+        if qpp:
+            self.ui.warn("saved queue repository parents: %s %s\n" %
+                         (hg.short(qpp[0]), hg.short(qpp[1])))
+            if qupdate:
+                print "queue directory updating"
+                r = self.qrepo()
+                if not r:
+                    self.ui.warn("Unable to load queue repository\n")
+                    return 1
+                r.update(qpp[0], allow=False, force=True)
+
+    def save(self, repo, msg=None):
+        if len(self.applied) == 0:
+            self.ui.warn("save: no patches applied, exiting\n")
+            return 1
+        if self.issaveline(self.applied[-1]):
+            self.ui.warn("status is already saved\n")
+            return 1
+
+        ar = [ ':' + x for x in self.full_series ]
+        if not msg:
+            msg = "hg patches saved state"
+        else:
+            msg = "hg patches: " + msg.rstrip('\r\n')
+        r = self.qrepo()
+        if r:
+            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 "")
+        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_dirty = 1
+
+    def series_end(self):
+        end = 0
+        if len(self.applied) > 0:
+            (top, p) = self.applied[-1].split(':')
+            try:
+                end = self.series.index(p)
+            except ValueError:
+                return 0
+            return end + 1
+        return 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)
+        if not patch:
+            end = len(self.applied)
+        else:
+            end = self.series.index(patch) + 1
+        for x in xrange(end):
+            p = self.appliedname(x)
+            self.ui.write("%s\n" % p)
+
+    def appliedname(self, index):
+        p = self.applied[index]
+        if not self.ui.verbose:
+            p = p.split(':')[1]
+        return p
+
+    def top(self, repo):
+        if len(self.applied):
+            p = self.appliedname(-1)
+            self.ui.write(p + '\n')
+        else:
+            self.ui.write("No patches applied\n")
+
+    def next(self, repo):
+        end = self.series_end()
+        if end == len(self.series):
+            self.ui.write("All patches applied\n")
+        else:
+            self.ui.write(self.series[end] + '\n')
+
+    def prev(self, repo):
+        if len(self.applied) > 1:
+            p = self.appliedname(-2)
+            self.ui.write(p + '\n')
+        elif len(self.applied) == 1:
+            self.ui.write("Only one patch applied\n")
+        else:
+            self.ui.write("No patches applied\n")
+
+    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)
+        i = 0
+        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)
+            else:
+                try:
+                    text = file(filename).read()
+                except IOError:
+                    self.ui.warn("Unable to read %s\n" % patch)
+                    sys.exit(1)
+                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)
+                patchf = self.opener(os.path.join(self.path, 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
+            self.full_series[index:index] = [patch]
+            self.read_series(self.full_series)
+            self.ui.warn("adding %s to series file\n" % patch)
+            i += 1
+            patch = None
+        self.series_dirty = 1
+
+def delete(ui, repo, patch, **opts):
+    """remove a patch from the series file"""
+    q = repomap[repo]
+    q.delete(repo, patch)
+    q.save_dirty()
+    return 0
+
+def applied(ui, repo, patch=None, **opts):
+    """print the patches already applied"""
+    repomap[repo].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
+
+def qimport(ui, repo, *filename, **opts):
+    """import a patch"""
+    q = repomap[repo]
+    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]
+    r = q.init(repo, create=opts['create_repo'])
+    q.save_dirty()
+    if r:
+        fp = r.wopener('.hgignore', 'w')
+        print >> fp, 'syntax: glob'
+        print >> fp, 'status'
+        fp.close()
+        r.wopener('series', 'w').close()
+        r.add(['.hgignore', 'series'])
+    return 0
+
+def commit(ui, repo, *pats, **opts):
+    q = repomap[repo]
+    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'])
+    return 0
+
+def top(ui, repo, **opts):
+    """print the name of the current patch"""
+    repomap[repo].top(repo)
+    return 0
+
+def next(ui, repo, **opts):
+    """print the name of the next patch"""
+    repomap[repo].next(repo)
+    return 0
+
+def prev(ui, repo, **opts):
+    """print the name of the previous patch"""
+    repomap[repo].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'])
+    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()
+    return 0
+
+def diff(ui, repo, *files, **opts):
+    """diff of the current patch"""
+    repomap[repo].diff(repo, files)
+    return 0
+
+def lastsavename(path):
+    (dir, base) = os.path.split(path)
+    names = os.listdir(dir)
+    namere = re.compile("%s.([0-9]+)" % base)
+    max = 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
+                maxname = f
+    if maxname:
+        return (os.path.join(dir, maxname), max)
+    return (None, None)
+
+def savename(path):
+    (last, index) = lastsavename(path)
+    if last is None:
+        index = 0
+    newpath = path + ".%d" % (index + 1)
+    return newpath
+
+def push(ui, repo, patch=None, **opts):
+    """push the next patch onto the stack"""
+    q = repomap[repo]
+    mergeq = None
+
+    if opts['all']:
+        patch = q.series[-1]
+    if opts['merge']:
+        if opts['name']:
+            newpath = opts['name']
+        else:
+            newpath, i = lastsavename(q.path)
+        if not newpath:
+            ui.warn("no saved queues found, please use -n\n")
+            return 1
+        mergeq = queue(ui, repo.join(""), newpath)
+        ui.warn("merging with queue at: %s\n" % mergeq.path)
+    ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
+                 mergeq=mergeq)
+    q.save_dirty()
+    return ret
+
+def pop(ui, repo, patch=None, **opts):
+    """pop the current patch off the stack"""
+    localupdate = True
+    if opts['name']:
+        q = queue(ui, repo.join(""), repo.join(opts['name']))
+        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.save_dirty()
+    return 0
+
+def restore(ui, repo, rev, **opts):
+    """restore the queue state saved by a rev"""
+    rev = repo.lookup(rev)
+    q = repomap[repo]
+    q.restore(repo, rev, delete=opts['delete'],
+              qupdate=opts['update'])
+    q.save_dirty()
+    return 0
+
+def save(ui, repo, **opts):
+    """save current queue state"""
+    q = repomap[repo]
+    ret = q.save(repo, msg=opts['message'])
+    if ret:
+        return ret
+    q.save_dirty()
+    if opts['copy']:
+        path = q.path
+        if opts['name']:
+            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)
+                if not opts['force']:
+                    ui.warn("destination %s exists, use -f to force\n" %
+                            newpath)
+                    sys.exit(1)
+        else:
+            newpath = savename(path)
+        ui.warn("copy %s to %s\n" % (path, newpath))
+        util.copyfiles(path, newpath)
+    if opts['empty']:
+        try:
+            os.unlink(q.status_path)
+        except:
+            pass
+    return 0
+
+def strip(ui, repo, rev, **opts):
+    """strip a revision and all later revs on the same branch"""
+    rev = repo.lookup(rev)
+    backup = 'all'
+    if opts['backup']:
+        backup = 'strip'
+    elif opts['nobackup']:
+        backup = 'none'
+    repomap[repo].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 reposetup(ui, repo):
+    repomap[repo] = queue(ui, repo.join(""))
+
+cmdtable = {
+    "qapplied": (applied, [], 'hg qapplied [patch]'),
+    "qcommit|qci":
+        (commit,
+         [('A', 'addremove', None, _('run addremove during commit')),
+          ('I', 'include', [], _('include names matching the given patterns')),
+          ('X', 'exclude', [], _('exclude names matching the given patterns')),
+          ('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'))],
+         'hg qcommit [options] [files]'),
+    "^qdiff": (diff, [], 'hg qdiff [files]'),
+    "qdelete": (delete, [], 'hg qdelete [patch]'),
+    "^qimport":
+        (qimport,
+         [('e', 'existing', None, 'import file in patch dir'),
+          ('n', 'name', '', 'patch file name'),
+          ('f', 'force', None, 'overwrite existing files')],
+         'hg qimport'),
+    "^qinit":
+        (init,
+         [('c', 'create-repo', None, 'create patch repository')],
+         'hg [-c] qinit'),
+    "qnew":
+        (new,
+         [('m', 'message', '', 'commit message'),
+          ('f', 'force', None, 'force')],
+         'hg qnew [-m message ] patch'),
+    "qnext": (next, [], 'hg qnext'),
+    "qprev": (prev, [], 'hg qprev'),
+    "^qpop":
+        (pop,
+         [('a', 'all', None, 'pop all patches'),
+          ('n', 'name', '', 'queue name to pop'),
+          ('f', 'force', None, 'forget any local changes')],
+         'hg qpop [options] [patch/index]'),
+    "^qpush":
+        (push,
+         [('f', 'force', None, 'apply if the patch has rejects'),
+          ('l', 'list', None, 'list patch name in commit text'),
+          ('a', 'all', None, 'apply all patches'),
+          ('m', 'merge', None, 'merge from another queue'),
+          ('n', 'name', '', 'merge queue name')],
+         'hg qpush [options] [patch/index]'),
+    "^qrefresh":
+        (refresh,
+         [('s', 'short', None, 'short refresh')],
+         'hg qrefresh'),
+    "qrestore":
+        (restore,
+         [('d', 'delete', None, 'delete save entry'),
+          ('u', 'update', None, 'update queue working dir')],
+         'hg qrestore rev'),
+    "qsave":
+        (save,
+         [('m', 'message', '', 'commit message'),
+          ('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'),
+    "qseries":
+        (series,
+         [('m', 'missing', None, 'print patches not in series')],
+         'hg qseries'),
+    "^strip":
+        (strip,
+         [('f', 'force', None, 'force multi-head removal'),
+          ('b', 'backup', None, 'bundle unrelated changesets'),
+          ('n', 'nobackup', None, 'no backups')],
+         'hg strip rev'),
+    "qtop": (top, [], 'hg qtop'),
+    "qunapplied": (unapplied, [], 'hg qunapplied [patch]'),
+    "qversion": (version, [], 'hg qversion')
+}
+
--- a/hgmerge	Fri Feb 10 11:25:07 2006 -0800
+++ b/hgmerge	Fri Mar 03 09:39:37 2006 -0800
@@ -17,28 +17,32 @@
 
 # find decent versions of our utilities, insisting on the GNU versions where we
 # need to
-MERGE=merge
-DIFF3=gdiff3
-DIFF=gdiff
-PATCH=gpatch
+MERGE="merge"
+DIFF3="gdiff3"
+DIFF="gdiff"
+PATCH="gpatch"
 
-type $MERGE >/dev/null 2>&1 || MERGE=
-type $DIFF3 >/dev/null 2>&1 || DIFF3=diff3
-type $DIFF  >/dev/null 2>&1 || DIFF=diff
-type $PATCH >/dev/null 2>&1 || PATCH=patch
+type "$MERGE" >/dev/null 2>&1 || MERGE=
+type "$DIFF3" >/dev/null 2>&1 || DIFF3="diff3"
 $DIFF3 --version >/dev/null 2>&1 || DIFF3=
+type "$DIFF"  >/dev/null 2>&1 || DIFF="diff"
+type "$DIFF"  >/dev/null 2>&1 || DIFF=
+type "$PATCH" >/dev/null 2>&1 || PATCH="patch"
+type "$PATCH" >/dev/null 2>&1 || PATCH=
 
 # find optional visual utilities
-FILEMERGE='/Developer/Applications/Utilities/FileMerge.app/Contents/MacOS/FileMerge'
-KDIFF3=kdiff3
-TKDIFF=tkdiff
+FILEMERGE="/Developer/Applications/Utilities/FileMerge.app/Contents/MacOS/FileMerge"
+KDIFF3="kdiff3"
+TKDIFF="tkdiff"
+MELD="meld"
 
-type $FILEMERGE >/dev/null 2>&1 || FILEMERGE=
-type $KDIFF3    >/dev/null 2>&1 || KDIFF3=
-type $TKDIFF    >/dev/null 2>&1 || TKDIFF=
+type "$FILEMERGE" >/dev/null 2>&1 || FILEMERGE=
+type "$KDIFF3"    >/dev/null 2>&1 || KDIFF3=
+type "$TKDIFF"    >/dev/null 2>&1 || TKDIFF=
+type "$MELD"      >/dev/null 2>&1 || MELD=
 
 # random part of names
-RAND="$RANDOM.$RANDOM.$RANDOM.$$"
+RAND="$RANDOM$RANDOM"
 
 # temporary directory for diff+patch merge
 HGTMP="${TMPDIR-/tmp}/hgmerge.$RAND"
@@ -68,6 +72,19 @@
     exit 1
 }
 
+# Ask if the merge was successful
+ask_if_merged() {
+    while true; do
+        echo "$LOCAL seems unchanged."
+        echo "Was the merge successful? [y/n]"
+        read answer
+        case "$answer" in
+            y*|Y*) success;;
+            n*|N*) failure;;
+        esac
+    done
+}
+
 # Clean up when interrupted
 trap "failure" 1 2 3 6 15 # HUP INT QUIT ABRT TERM
 
@@ -76,18 +93,16 @@
 cp "$BACKUP" "$LOCAL"
 
 # Attempt to do a non-interactive merge
-if [ -n "$MERGE" ]; then
-    $MERGE "$LOCAL" "$BASE" "$OTHER" 2> /dev/null && success
-    cp "$BACKUP" "$LOCAL"
-elif [ -n "$DIFF3" ]; then
-    echo $DIFF3 -m "$BACKUP" "$BASE" "$OTHER"
-    $DIFF3 -m "$BACKUP" "$BASE" "$OTHER" > "$LOCAL" && success
-    if [ $? -eq 2 ]; then
-        echo "$DIFF3 failed! Exiting." 1>&2
-        cp "$BACKUP" "$LOCAL"
+if [ -n "$MERGE" -o -n "$DIFF3" ]; then
+    if [ -n "$MERGE" ]; then
+        $MERGE "$LOCAL" "$BASE" "$OTHER" 2> /dev/null && success
+    elif [ -n "$DIFF3" ]; then
+        $DIFF3 -m "$BACKUP" "$BASE" "$OTHER" > "$LOCAL" && success
+    fi
+    if [ $? -gt 1 ]; then
+        echo "automatic merge failed! Exiting." 1>&2
         failure
     fi
-    cp "$BACKUP" "$LOCAL"
 fi
 
 # on MacOS X try FileMerge.app, shipped with Apple's developer tools
@@ -97,71 +112,66 @@
     # filemerge prefers the right by default
     $FILEMERGE -left "$OTHER" -right "$LOCAL" -ancestor "$BASE" -merge "$LOCAL"
     [ $? -ne 0 ] && echo "FileMerge failed to launch" && failure
-    if test "$LOCAL" -nt "$CHGTEST"
-    then
-        success
-    else
-        echo "$LOCAL seems unchanged. Was the merge successful?"
-        select answer in yes no
-        do
-            test "$answer" == "yes" && success || failure
-        done
-    fi
-    failure
+    test "$LOCAL" -nt "$CHGTEST" && success || ask_if_merged
 fi
 
 if [ -n "$DISPLAY" ]; then
     # try using kdiff3, which is fairly nice
     if [ -n "$KDIFF3" ]; then
-	$KDIFF3 --auto "$BASE" "$LOCAL" "$OTHER" -o "$LOCAL" || failure
-	success
+        $KDIFF3 --auto "$BASE" "$BACKUP" "$OTHER" -o "$LOCAL" || failure
+        success
     fi
 
     # try using tkdiff, which is a bit less sophisticated
     if [ -n "$TKDIFF" ]; then
-	$TKDIFF "$LOCAL" "$OTHER" -a "$BASE" -o "$LOCAL" || failure
-	success
+        $TKDIFF "$BACKUP" "$OTHER" -a "$BASE" -o "$LOCAL" || failure
+        success
+    fi
+
+    if [ -n "$MELD" ]; then
+        cp "$BACKUP" "$CHGTEST"
+        # protect our feet - meld allows us to save to the left file
+        cp "$BACKUP" "$LOCAL.tmp.$RAND"
+        # Meld doesn't have automatic merging, so to reduce intervention
+        # use the file with conflicts
+        $MELD "$LOCAL.tmp.$RAND" "$LOCAL" "$OTHER" || failure
+        # Also it doesn't return good error code
+        test "$LOCAL" -nt "$CHGTEST" && success || ask_if_merged
     fi
 fi
 
 # Attempt to do a merge with $EDITOR
-if [ -n "$MERGE" ]; then
-    echo "conflicts detected in $LOCAL"
-    $MERGE "$LOCAL" "$BASE" "$OTHER" 2>/dev/null || $EDITOR "$LOCAL"
-    success
-fi
-
-if [ -n "$DIFF3" ]; then
+if [ -n "$MERGE" -o -n "$DIFF3" ]; then
     echo "conflicts detected in $LOCAL"
-    $DIFF3 -m "$BACKUP" "$BASE" "$OTHER" > "$LOCAL" || {
-        case $? in
-            1)
-                $EDITOR "$LOCAL" ;;
-            2)  echo "$DIFF3 failed! Exiting." 1>&2
-                cp "$BACKUP" "$LOCAL"
-                failure ;;
-        esac
-        success
-    }
+    cp "$BACKUP" "$CHGTEST"
+    $EDITOR "$LOCAL" || failure
+    # Some editors do not return meaningful error codes
+    # Do not take any chances
+    test "$LOCAL" -nt "$CHGTEST" && success || ask_if_merged
 fi
 
 # attempt to manually merge with diff and patch
 if [ -n "$DIFF" -a -n "$PATCH" ]; then
 
     (umask 077 && mkdir "$HGTMP") || {
-	echo "Could not create temporary directory $HGTMP" 1>&2
-	failure
+        echo "Could not create temporary directory $HGTMP" 1>&2
+        failure
     }
 
     $DIFF -u "$BASE" "$OTHER" > "$HGTMP/diff" || :
     if $PATCH "$LOCAL" < "$HGTMP/diff"; then
-	success
+        success
     else
-	# If rejects are empty after using the editor, merge was ok
-	$EDITOR "$LOCAL" "$LOCAL.rej" && test -s "$LOCAL.rej" || success
+        # If rejects are empty after using the editor, merge was ok
+        $EDITOR "$LOCAL" "$LOCAL.rej" || failure
+        test -s "$LOCAL.rej" || success
     fi
     failure
 fi
 
-echo "hgmerge: unable to find merge, tkdiff, kdiff3, or diff+patch!"
+echo
+echo "hgmerge: unable to find any merge utility!"
+echo "supported programs:"
+echo "merge, FileMerge, tkdiff, kdiff3, meld, diff+patch"
+echo
 failure
--- a/mercurial/bdiff.c	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/bdiff.c	Fri Mar 03 09:39:37 2006 -0800
@@ -17,6 +17,10 @@
 #define inline
 #endif
 
+#ifdef __SUNPRO_C
+# define inline
+#endif 
+
 #ifdef _WIN32
 #ifdef _MSC_VER
 #define inline __inline
--- a/mercurial/commands.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/commands.py	Fri Mar 03 09:39:37 2006 -0800
@@ -82,6 +82,21 @@
     "iter", rev, None: in-order traversal of the revs earlier iterated
     over with "add" - use to display data'''
 
+    def increasing_windows(start, end, windowsize=8, sizelimit=512):
+        if start < end:
+            while start < end:
+                yield start, min(windowsize, end-start)
+                start += windowsize
+                if windowsize < sizelimit:
+                    windowsize *= 2
+        else:
+            while start > end:
+                yield start, min(windowsize, start-end-1)
+                start -= windowsize
+                if windowsize < sizelimit:
+                    windowsize *= 2
+
+
     files, matchfn, anypats = matchpats(repo, pats, opts)
 
     if repo.changelog.count() == 0:
@@ -90,7 +105,6 @@
     revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
     wanted = {}
     slowpath = anypats
-    window = 300
     fncache = {}
 
     chcache = {}
@@ -106,17 +120,17 @@
     if not slowpath:
         # Only files, no patterns.  Check the history of each file.
         def filerevgen(filelog):
-            for i in xrange(filelog.count() - 1, -1, -window):
+            for i, window in increasing_windows(filelog.count()-1, -1):
                 revs = []
-                for j in xrange(max(0, i - window), i + 1):
+                for j in xrange(i - window, i + 1):
                     revs.append(filelog.linkrev(filelog.node(j)))
                 revs.reverse()
                 for rev in revs:
                     yield rev
 
         minrev, maxrev = min(revs), max(revs)
-        for file in files:
-            filelog = repo.file(file)
+        for file_ in files:
+            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:
@@ -127,13 +141,13 @@
                     if rev < minrev:
                         break
                     fncache.setdefault(rev, [])
-                    fncache[rev].append(file)
+                    fncache[rev].append(file_)
                     wanted[rev] = 1
     if slowpath:
         # The slow path checks files modified in every changeset.
         def changerevgen():
-            for i in xrange(repo.changelog.count() - 1, -1, -window):
-                for j in xrange(max(0, i - window), i + 1):
+            for i, window in increasing_windows(repo.changelog.count()-1, -1):
+                for j in xrange(i - window, i + 1):
                     yield j, getchange(j)[3]
 
         for rev, changefiles in changerevgen():
@@ -143,9 +157,9 @@
                 wanted[rev] = 1
 
     def iterate():
-        for i in xrange(0, len(revs), window):
+        for i, window in increasing_windows(0, len(revs)):
             yield 'window', revs[0] < revs[-1], revs[-1]
-            nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
+            nrevs = [rev for rev in revs[i:i+window]
                      if rev in wanted]
             srevs = list(nrevs)
             srevs.sort()
@@ -261,7 +275,15 @@
                 mode)
 
 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
-           changes=None, text=False):
+           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
@@ -280,8 +302,6 @@
             return repo.file(f).read(mmap2[f])
     else:
         date2 = util.datestr()
-        if not node1:
-            node1 = repo.dirstate.parents()[0]
         def read(f):
             return repo.wread(f)
 
@@ -291,13 +311,9 @@
         hexfunc = ui.verbose and hex or short
         r = [hexfunc(node) for node in [node1, node2] if node]
 
-    change = repo.changelog.read(node1)
-    mmap = repo.manifest.read(change[0])
-    date1 = util.datestr(change[2])
-
     diffopts = ui.diffopts()
-    showfunc = diffopts['showfunc']
-    ignorews = diffopts['ignorews']
+    showfunc = opts.get('show_function') or diffopts['showfunc']
+    ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
     for f in modified:
         to = None
         if f in mmap:
@@ -447,7 +463,6 @@
             f = f.lstrip("^")
             if not ui.debugflag and f.startswith("debug"):
                 continue
-            d = ""
             doc = e[0].__doc__
             if not doc:
                 doc = _("(No help text available)")
@@ -622,7 +637,7 @@
     dest = ui.expandpath(dest, repo.root)
     other = hg.repository(ui, dest)
     o = repo.findoutgoing(other)
-    cg = repo.changegroup(o)
+    cg = repo.changegroup(o, 'bundle')
 
     try:
         f.write("HG10")
@@ -681,6 +696,8 @@
     such as AFS, implement hardlinking incorrectly, but do not report
     errors.  In these cases, use the --pull option to avoid
     hardlinking.
+
+    See pull for valid source format details.
     """
     if dest is None:
         dest = os.path.basename(os.path.normpath(source))
@@ -725,8 +742,8 @@
             # can end up with extra data in the cloned revlogs that's
             # not pointed to by changesets, thus causing verify to
             # fail
-            l1 = lock.lock(os.path.join(source, ".hg", "lock"))
-        except OSError:
+            l1 = other.lock()
+        except lock.LockException:
             copy = False
 
     if copy:
@@ -808,7 +825,8 @@
     except ValueError, inst:
         raise util.Abort(str(inst))
 
-def docopy(ui, repo, pats, opts):
+def docopy(ui, repo, pats, opts, wlock):
+    # called with the repo lock held
     cwd = repo.getcwd()
     errors = 0
     copied = []
@@ -818,14 +836,19 @@
         reasons = {'?': _('is not managed'),
                    'a': _('has been marked for add'),
                    'r': _('has been marked for remove')}
-        reason = reasons.get(repo.dirstate.state(abs))
+        state = repo.dirstate.state(abs)
+        reason = reasons.get(state)
         if reason:
+            if state == 'a':
+                origsrc = repo.dirstate.copied(abs)
+                if origsrc is not None:
+                    return origsrc
             if exact:
                 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
         else:
-            return True
-
-    def copy(abssrc, relsrc, target, exact):
+            return abs
+
+    def copy(origsrc, abssrc, relsrc, target, exact):
         abstarget = util.canonpath(repo.root, cwd, target)
         reltarget = util.pathto(cwd, abstarget)
         prevsrc = targets.get(abstarget)
@@ -849,8 +872,16 @@
             if not os.path.isdir(targetdir):
                 os.makedirs(targetdir)
             try:
-                shutil.copyfile(relsrc, reltarget)
-                shutil.copymode(relsrc, reltarget)
+                restore = repo.dirstate.state(abstarget) == 'r'
+                if restore:
+                    repo.undelete([abstarget], wlock)
+                try:
+                    shutil.copyfile(relsrc, reltarget)
+                    shutil.copymode(relsrc, reltarget)
+                    restore = False
+                finally:
+                    if restore:
+                        repo.remove([abstarget], wlock)
             except shutil.Error, inst:
                 raise util.Abort(str(inst))
             except IOError, inst:
@@ -864,7 +895,8 @@
         if ui.verbose or not exact:
             ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
         targets[abstarget] = abssrc
-        repo.copy(abssrc, abstarget)
+        if abstarget != origsrc:
+            repo.copy(origsrc, abstarget, wlock)
         copied.append((abssrc, relsrc, exact))
 
     def targetpathfn(pat, dest, srcs):
@@ -938,8 +970,9 @@
     for pat in pats:
         srcs = []
         for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
-            if okaytocopy(abssrc, relsrc, exact):
-                srcs.append((abssrc, relsrc, exact))
+            origsrc = okaytocopy(abssrc, relsrc, exact)
+            if origsrc:
+                srcs.append((origsrc, abssrc, relsrc, exact))
         if not srcs:
             continue
         copylist.append((tfn(pat, dest, srcs), srcs))
@@ -947,8 +980,8 @@
         raise util.Abort(_('no files to copy'))
 
     for targetpath, srcs in copylist:
-        for abssrc, relsrc, exact in srcs:
-            copy(abssrc, relsrc, targetpath(abssrc), exact)
+        for origsrc, abssrc, relsrc, exact in srcs:
+            copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
 
     if errors:
         ui.warn(_('(consider using --after)\n'))
@@ -971,7 +1004,12 @@
     should properly record copied files, this information is not yet
     fully used by merge, nor fully reported by log.
     """
-    errs, copied = docopy(ui, repo, pats, opts)
+    try:
+        wlock = repo.wlock(0)
+        errs, copied = docopy(ui, repo, pats, opts, wlock)
+    except lock.LockHeld, inst:
+        ui.warn(_("repository lock held by %s\n") % inst.args[0])
+        errs = 1
     return errs
 
 def debugancestor(ui, index, rev1, rev2):
@@ -980,6 +1018,18 @@
     a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
     ui.write("%d:%s\n" % (r.rev(a), hex(a)))
 
+def debugrebuildstate(ui, repo, rev=None):
+    """rebuild the dirstate as it would look like for the given revision"""
+    if not rev:
+        rev = repo.changelog.tip()
+    else:
+        rev = repo.lookup(rev)
+    change = repo.changelog.read(rev)
+    n = change[0]
+    files = repo.manifest.readflags(n)
+    wlock = repo.wlock()
+    repo.dirstate.rebuild(rev, files.iteritems())
+
 def debugcheckstate(ui, repo):
     """validate the correctness of the current dirstate"""
     parent1, parent2 = repo.dirstate.parents()
@@ -1140,7 +1190,7 @@
     fns, matchfn, anypats = matchpats(repo, pats, opts)
 
     dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
-           text=opts['text'])
+           text=opts['text'], opts=opts)
 
 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
     node = repo.lookup(changeset)
@@ -1284,6 +1334,7 @@
             s = linestate(line, lnum, cstart, cend)
             m[s] = s
 
+    # FIXME: prev isn't used, why ?
     prev = {}
     ucache = {}
     def display(fn, rev, states, prevstates):
@@ -1593,7 +1644,19 @@
                 self.write(*args)
         def __getattr__(self, key):
             return getattr(self.ui, key)
+
     changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
+
+    if opts['limit']:
+        try:
+            limit = int(opts['limit'])
+        except ValueError:
+            raise util.Abort(_('limit must be a positive integer'))
+        if limit <= 0: raise util.Abort(_('limit must be positive'))
+    else:
+        limit = sys.maxint
+    count = 0
+
     for st, rev, fns in changeiter:
         if st == 'window':
             du = dui(ui)
@@ -1607,7 +1670,6 @@
             if opts['only_merges'] and len(parents) != 2:
                 continue
 
-            br = None
             if opts['keyword']:
                 changes = getchange(rev)
                 miss = 0
@@ -1620,7 +1682,8 @@
                 if miss:
                     continue
 
-            if opts['branch']:
+            br = None
+            if opts['branches']:
                 br = repo.branchlookup([repo.changelog.node(rev)])
 
             show_changeset(du, repo, rev, brinfo=br)
@@ -1629,8 +1692,11 @@
                 dodiff(du, du, repo, prev, changenode, match=matchfn)
                 du.write("\n\n")
         elif st == 'iter':
-            for args in du.hunk[rev]:
-                ui.write(*args)
+            if count == limit: break
+            if du.hunk[rev]:
+                count += 1
+                for args in du.hunk[rev]:
+                    ui.write(*args)
 
 def manifest(ui, repo, rev=None):
     """output the latest or given revision of the project manifest
@@ -1664,6 +1730,8 @@
     Show changesets not found in the specified destination repo or the
     default push repo. These are the changesets that would be pushed
     if a push was requested.
+
+    See pull for valid source format details.
     """
     dest = ui.expandpath(dest, repo.root)
     other = hg.repository(ui, dest)
@@ -1681,7 +1749,7 @@
             dodiff(ui, ui, repo, prev, n)
             ui.write("\n")
 
-def parents(ui, repo, rev=None):
+def parents(ui, repo, rev=None, branches=None):
     """show the parents of the working dir or revision
 
     Print the working directory's parent revisions.
@@ -1691,9 +1759,12 @@
     else:
         p = repo.dirstate.parents()
 
+    br = None
+    if branches is not None:
+        br = repo.branchlookup(p)
     for n in p:
         if n != nullid:
-            show_changeset(ui, repo, changenode=n)
+            show_changeset(ui, repo, changenode=n, brinfo=br)
 
 def paths(ui, search=None):
     """show definition of symbolic path names
@@ -1764,7 +1835,7 @@
 
     return r
 
-def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
+def push(ui, repo, dest="default-push", **opts):
     """push changes to the specified destination
 
     Push changes from the local repository to the given destination.
@@ -1789,18 +1860,22 @@
     dest = ui.expandpath(dest, repo.root)
     ui.status('pushing to %s\n' % (dest))
 
-    if ssh:
-        ui.setconfig("ui", "ssh", ssh)
-    if remotecmd:
-        ui.setconfig("ui", "remotecmd", remotecmd)
+    if opts['ssh']:
+        ui.setconfig("ui", "ssh", opts['ssh'])
+    if opts['remotecmd']:
+        ui.setconfig("ui", "remotecmd", opts['remotecmd'])
 
     other = hg.repository(ui, dest)
-    r = repo.push(other, force)
+    revs = None
+    if opts['rev']:
+        revs = [repo.lookup(rev) for rev in opts['rev']]
+    r = repo.push(other, opts['force'], revs=revs)
     return r
 
 def rawcommit(ui, repo, *flist, **rc):
     """raw commit interface (DEPRECATED)
 
+    (DEPRECATED)
     Lowlevel commit, for use in helper scripts.
 
     This command is not intended to be used by normal users, as it is
@@ -1893,21 +1968,33 @@
     should properly record rename files, this information is not yet
     fully used by merge, nor fully reported by log.
     """
-    errs, copied = docopy(ui, repo, pats, opts)
-    names = []
-    for abs, rel, exact in copied:
-        if ui.verbose or not exact:
-            ui.status(_('removing %s\n') % rel)
-        names.append(abs)
-    repo.remove(names, unlink=True)
+    try:
+        wlock = repo.wlock(0)
+        errs, copied = docopy(ui, repo, pats, opts, wlock)
+        names = []
+        for abs, rel, exact in copied:
+            if ui.verbose or not exact:
+                ui.status(_('removing %s\n') % rel)
+            names.append(abs)
+        repo.remove(names, True, wlock)
+    except lock.LockHeld, inst:
+        ui.warn(_("repository lock held by %s\n") % inst.args[0])
+        errs = 1
     return errs
 
 def revert(ui, repo, *pats, **opts):
     """revert modified files or dirs back to their unmodified states
 
-    Revert any uncommitted modifications made to the named files or
-    directories.  This restores the contents of the affected files to
-    an unmodified state.
+    In its default mode, it reverts any uncommitted modifications made
+    to the named files or directories.  This restores the contents of
+    the affected files to an unmodified state.
+
+    Using the -r option, it reverts the given files or directories to
+    their state as of an earlier revision.  This can be helpful to "roll
+    back" some or all of a change that should not have been committed.
+
+    Revert modifies the working directory.  It does not commit any
+    changes, or change the parent of the current working directory.
 
     If a file has been deleted, it is recreated.  If the executable
     mode of a file was changed, it is reset.
@@ -1922,7 +2009,7 @@
     files, choose, anypats = matchpats(repo, pats, opts)
     modified, added, removed, deleted, unknown = repo.changes(match=choose)
     repo.forget(added)
-    repo.undelete(removed + deleted)
+    repo.undelete(removed)
 
     return repo.update(node, False, True, choose, False)
 
@@ -1996,7 +2083,7 @@
                 arg, roots = getarg()
                 nodes = map(bin, roots.split(" "))
 
-                cg = repo.changegroup(nodes)
+                cg = repo.changegroup(nodes, 'serve')
                 while 1:
                     d = cg.read(4096)
                     if not d:
@@ -2019,6 +2106,16 @@
         if opts[o]:
             ui.setconfig("web", o, opts[o])
 
+    if opts['daemon'] and not opts['daemon_pipefds']:
+        rfd, wfd = os.pipe()
+        args = sys.argv[:]
+        args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
+        pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
+                         args[0], args)
+        os.close(wfd)
+        os.read(rfd, 1)
+        os._exit(0)
+
     try:
         httpd = hgweb.create_server(repo)
     except socket.error, inst:
@@ -2037,6 +2134,25 @@
             ui.status(_('listening at http://%s:%d/\n') % (addr, port))
         else:
             ui.status(_('listening at http://%s/\n') % addr)
+
+    if opts['pid_file']:
+        fp = open(opts['pid_file'], 'w')
+        fp.write(str(os.getpid()))
+        fp.close()
+
+    if opts['daemon_pipefds']:
+        rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
+        os.close(rfd)
+        os.write(wfd, 'y')
+        os.close(wfd)
+        sys.stdout.flush()
+        sys.stderr.flush()
+        fd = os.open(util.nulldev, os.O_RDWR)
+        if fd != 0: os.dup2(fd, 0)
+        if fd != 1: os.dup2(fd, 1)
+        if fd != 2: os.dup2(fd, 2)
+        if fd not in (0, 1, 2): os.close(fd)
+
     httpd.serve_forever()
 
 def status(ui, repo, *pats, **opts):
@@ -2113,8 +2229,12 @@
         if name.find(c) >= 0:
             raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
 
+    repo.hook('pretag', throw=True, node=r, tag=name,
+              local=int(not not opts['local']))
+
     if opts['local']:
         repo.opener("localtags", "a").write("%s %s\n" % (r, name))
+        repo.hook('tag', node=r, tag=name, local=1)
         return
 
     for x in repo.changes():
@@ -2130,6 +2250,7 @@
                _("Added tag %s for changeset %s") % (name, r))
     try:
         repo.commit([".hgtags"], message, opts['user'], opts['date'])
+        repo.hook('tag', node=r, tag=name, local=0)
     except ValueError, inst:
         raise util.Abort(str(inst))
 
@@ -2150,13 +2271,18 @@
             r = "    ?:?"
         ui.write("%-30s %s\n" % (t, r))
 
-def tip(ui, repo):
+def tip(ui, repo, **opts):
     """show the tip revision
 
     Show the tip revision.
     """
     n = repo.changelog.tip()
-    show_changeset(ui, repo, changenode=n)
+    br = None
+    if opts['branches']:
+        br = repo.branchlookup([n])
+    show_changeset(ui, repo, changenode=n, brinfo=br)
+    if opts['patch']:
+        dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
 
 def unbundle(ui, repo, fname, **opts):
     """apply a changegroup file
@@ -2273,47 +2399,51 @@
           ('c', 'changeset', None, _('list the changeset')),
           ('I', 'include', [], _('include names matching the given patterns')),
           ('X', 'exclude', [], _('exclude names matching the given patterns'))],
-         _('hg annotate [OPTION]... FILE...')),
+         _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
     "bundle":
         (bundle,
          [],
          _('hg bundle FILE DEST')),
     "cat":
         (cat,
-         [('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns')),
-          ('o', 'output', '', _('print output to file with formatted name')),
-          ('r', 'rev', '', _('print the given revision'))],
+         [('o', 'output', '', _('print output to file with formatted name')),
+          ('r', 'rev', '', _('print the given revision')),
+          ('I', 'include', [], _('include names matching the given patterns')),
+          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
          _('hg cat [OPTION]... FILE...')),
     "^clone":
         (clone,
          [('U', 'noupdate', None, _('do not update the new working directory')),
-          ('e', 'ssh', '', _('specify ssh command to use')),
-          ('', 'pull', None, _('use pull protocol to copy metadata')),
           ('r', 'rev', [],
            _('a changeset you would like to have after cloning')),
+          ('', 'pull', None, _('use pull protocol to copy metadata')),
+          ('e', 'ssh', '', _('specify ssh command to use')),
           ('', 'remotecmd', '',
            _('specify hg command to run on the remote side'))],
          _('hg clone [OPTION]... SOURCE [DEST]')),
     "^commit|ci":
         (commit,
          [('A', 'addremove', None, _('run addremove during commit')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns')),
           ('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'))],
+          ('u', 'user', '', _('record user as commiter')),
+          ('I', 'include', [], _('include names matching the given patterns')),
+          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
          _('hg commit [OPTION]... [FILE]...')),
     "copy|cp":
         (copy,
-         [('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns')),
-          ('A', 'after', None, _('record a copy that has already occurred')),
+         [('A', 'after', None, _('record a copy that has already occurred')),
           ('f', 'force', None,
-           _('forcibly copy over an existing managed file'))],
+           _('forcibly copy over an existing managed file')),
+          ('I', 'include', [], _('include names matching the given patterns')),
+          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
          _('hg copy [OPTION]... [SOURCE]... DEST')),
     "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
+    "debugrebuildstate":
+        (debugrebuildstate,
+         [('r', 'rev', '', _('revision to rebuild to'))],
+         _('debugrebuildstate [-r REV] [REV]')),
     "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
     "debugconfig": (debugconfig, [], _('debugconfig')),
     "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
@@ -2331,6 +2461,10 @@
         (diff,
          [('r', 'rev', [], _('revision')),
           ('a', 'text', None, _('treat all files as text')),
+          ('p', 'show-function', None,
+           _('show which function each change is in')),
+          ('w', 'ignore-all-space', None,
+           _('ignore white space when comparing lines')),
           ('I', 'include', [], _('include names matching the given patterns')),
           ('X', 'exclude', [], _('exclude names matching the given patterns'))],
          _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
@@ -2339,7 +2473,7 @@
          [('o', 'output', '', _('print output to file with formatted name')),
           ('a', 'text', None, _('treat all files as text')),
           ('', 'switch-parent', None, _('diff against the second parent'))],
-         _('hg export [-a] [-o OUTFILE] REV...')),
+         _('hg export [-a] [-o OUTFILESPEC] REV...')),
     "forget":
         (forget,
          [('I', 'include', [], _('include names matching the given patterns')),
@@ -2348,19 +2482,19 @@
     "grep":
         (grep,
          [('0', 'print0', None, _('end fields with NUL')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns')),
           ('', 'all', None, _('print all revisions that match')),
           ('i', 'ignore-case', None, _('ignore case when matching')),
           ('l', 'files-with-matches', None,
            _('print only filenames and revs that match')),
           ('n', 'line-number', None, _('print matching line numbers')),
           ('r', 'rev', [], _('search in given revision range')),
-          ('u', 'user', None, _('print user who committed change'))],
+          ('u', 'user', None, _('print user who committed change')),
+          ('I', 'include', [], _('include names matching the given patterns')),
+          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
          _('hg grep [OPTION]... PATTERN [FILE]...')),
     "heads":
         (heads,
-         [('b', 'branches', None, _('find branch info')),
+         [('b', 'branches', None, _('show branches')),
           ('r', 'rev', '', _('show only heads which are descendants of rev'))],
          _('hg heads [-b] [-r <rev>]')),
     "help": (help_, [], _('hg help [COMMAND]')),
@@ -2370,10 +2504,10 @@
          [('p', 'strip', 1,
            _('directory strip option for patch. This has the same\n') +
            _('meaning as the corresponding patch option')),
+          ('b', 'base', '', _('base path')),
           ('f', 'force', None,
-           _('skip check for outstanding uncommitted changes')),
-          ('b', 'base', '', _('base path'))],
-         _('hg import [-f] [-p NUM] [-b BASE] PATCH...')),
+           _('skip check for outstanding uncommitted changes'))],
+         _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
     "incoming|in": (incoming,
          [('M', 'no-merges', None, _('do not show merges')),
           ('p', 'patch', None, _('show patch')),
@@ -2392,22 +2526,26 @@
          _('hg locate [OPTION]... [PATTERN]...')),
     "^log|history":
         (log,
-         [('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns')),
-          ('b', 'branch', None, _('show branches')),
+         [('b', 'branches', None, _('show branches')),
           ('k', 'keyword', [], _('search for a keyword')),
+          ('l', 'limit', '', _('limit number of changes displayed')),
           ('r', 'rev', [], _('show the specified revision or range')),
           ('M', 'no-merges', None, _('do not show merges')),
           ('m', 'only-merges', None, _('show only merges')),
-          ('p', 'patch', None, _('show patch'))],
-         _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
+          ('p', 'patch', None, _('show patch')),
+          ('I', 'include', [], _('include names matching the given patterns')),
+          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         _('hg log [OPTION]... [FILE]')),
     "manifest": (manifest, [], _('hg manifest [REV]')),
     "outgoing|out": (outgoing,
          [('M', 'no-merges', None, _('do not show merges')),
           ('p', 'patch', None, _('show patch')),
           ('n', 'newest-first', None, _('show newest record first'))],
-         _('hg outgoing [-p] [-n] [-M] [DEST]')),
-    "^parents": (parents, [], _('hg parents [REV]')),
+         _('hg outgoing [-M] [-p] [-n] [DEST]')),
+    "^parents":
+        (parents,
+         [('b', 'branches', None, _('show branches'))],
+         _('hg parents [-b] [REV]')),
     "paths": (paths, [], _('hg paths [NAME]')),
     "^pull":
         (pull,
@@ -2417,15 +2555,16 @@
           ('r', 'rev', [], _('a specific revision you would like to pull')),
           ('', 'remotecmd', '',
            _('specify hg command to run on the remote side'))],
-         _('hg pull [-u] [-e FILE] [-r rev] [--remotecmd FILE] [SOURCE]')),
+         _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
     "^push":
         (push,
          [('f', 'force', None, _('force push')),
           ('e', 'ssh', '', _('specify ssh command to use')),
+          ('r', 'rev', [], _('a specific revision you would like to push')),
           ('', 'remotecmd', '',
            _('specify hg command to run on the remote side'))],
-         _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
-    "rawcommit":
+         _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
+    "debugrawcommit|rawcommit":
         (rawcommit,
          [('p', 'parent', [], _('parent')),
           ('d', 'date', '', _('date code')),
@@ -2433,7 +2572,7 @@
           ('F', 'files', '', _('file list')),
           ('m', 'message', '', _('commit message')),
           ('l', 'logfile', '', _('commit message file'))],
-         _('hg rawcommit [OPTION]... [FILE]...')),
+         _('hg debugrawcommit [OPTION]... [FILE]...')),
     "recover": (recover, [], _('hg recover')),
     "^remove|rm":
         (remove,
@@ -2442,27 +2581,30 @@
          _('hg remove [OPTION]... FILE...')),
     "rename|mv":
         (rename,
-         [('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns')),
-          ('A', 'after', None, _('record a rename that has already occurred')),
+         [('A', 'after', None, _('record a rename that has already occurred')),
           ('f', 'force', None,
-           _('forcibly copy over an existing managed file'))],
+           _('forcibly copy over an existing managed file')),
+          ('I', 'include', [], _('include names matching the given patterns')),
+          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
          _('hg rename [OPTION]... [SOURCE]... DEST')),
     "^revert":
         (revert,
-         [('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns')),
-          ('r', 'rev', '', _('revision to revert to'))],
-         _('hg revert [-n] [-r REV] [NAME]...')),
+         [('r', 'rev', '', _('revision to revert to')),
+          ('I', 'include', [], _('include names matching the given patterns')),
+          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         _('hg revert [-r REV] [NAME]...')),
     "root": (root, [], _('hg root')),
     "^serve":
         (serve,
          [('A', 'accesslog', '', _('name of access log file to write to')),
+          ('d', 'daemon', None, _('run server in background')),
+          ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
           ('E', 'errorlog', '', _('name of error log file to write to')),
           ('p', 'port', 0, _('port to use (default: 8000)')),
           ('a', 'address', '', _('address to use')),
           ('n', 'name', '',
            _('name to show in web pages (default: working dir)')),
+          ('', 'pid-file', '', _('name of file to write process ID to')),
           ('', 'stdio', None, _('for remote clients')),
           ('t', 'templates', '', _('web templates to use')),
           ('', 'style', '', _('template style to use')),
@@ -2488,9 +2630,13 @@
           ('d', 'date', '', _('record datecode as commit date')),
           ('u', 'user', '', _('record user as commiter')),
           ('r', 'rev', '', _('revision to tag'))],
-         _('hg tag [-r REV] [OPTION]... NAME')),
+         _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
     "tags": (tags, [], _('hg tags')),
-    "tip": (tip, [], _('hg tip')),
+    "tip":
+        (tip,
+         [('b', 'branches', None, _('show branches')),
+          ('p', 'patch', None, _('show patch'))],
+         _('hg tip [-b] [-p]')),
     "unbundle":
         (unbundle,
          [('u', 'update', None,
@@ -2530,17 +2676,20 @@
 def find(cmd):
     """Return (aliases, command table entry) for command string."""
     choice = None
+    count = 0
     for e in table.keys():
         aliases = e.lstrip("^").split("|")
         if cmd in aliases:
             return aliases, table[e]
         for a in aliases:
             if a.startswith(cmd):
-                if choice:
-                    raise AmbiguousCommand(cmd)
-                else:
-                    choice = aliases, table[e]
-                    break
+                count += 1
+                choice = aliases, table[e]
+                break
+
+    if count > 1:
+        raise AmbiguousCommand(cmd)
+
     if choice:
         return choice
 
--- a/mercurial/dirstate.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/dirstate.py	Fri Mar 03 09:39:37 2006 -0800
@@ -197,9 +197,24 @@
 
     def clear(self):
         self.map = {}
+        self.copies = {}
+        self.markdirty()
+
+    def rebuild(self, parent, files):
+        self.clear()
+        umask = os.umask(0)
+        os.umask(umask)
+        for f, mode in files:
+            if mode:
+                self.map[f] = ('n', ~umask, -1, 0)
+            else:
+                self.map[f] = ('n', ~umask & 0666, -1, 0)
+        self.pl = (parent, nullid)
         self.markdirty()
 
     def write(self):
+        if not self.dirty:
+            return
         st = self.opener("dirstate", "w", atomic=True)
         st.write("".join(self.pl))
         for f, e in self.map.items():
@@ -270,11 +285,11 @@
         elif not dc:
             dc = self.filterfiles(files)
 
-        def statmatch(file, stat):
-            file = util.pconvert(file)
-            if file not in dc and self.ignore(file):
+        def statmatch(file_, stat):
+            file_ = util.pconvert(file_)
+            if file_ not in dc and self.ignore(file_):
                 return False
-            return match(file)
+            return match(file_)
 
         return self.walkhelper(files=files, statmatch=statmatch, dc=dc)
 
@@ -350,9 +365,9 @@
                 continue
             if stat.S_ISDIR(st.st_mode):
                 cmp1 = (lambda x, y: cmp(x[1], y[1]))
-                sorted = [ x for x in findfiles(f) ]
-                sorted.sort(cmp1)
-                for e in sorted:
+                sorted_ = [ x for x in findfiles(f) ]
+                sorted_.sort(cmp1)
+                for e in sorted_:
                     yield e
             else:
                 ff = util.normpath(ff)
@@ -380,7 +395,7 @@
 
         for src, fn, st in self.statwalk(files, match):
             try:
-                type, mode, size, time = self[fn]
+                type_, mode, size, time = self[fn]
             except KeyError:
                 unknown.append(fn)
                 continue
@@ -399,22 +414,23 @@
                         nonexistent = False
                 # XXX: what to do with file no longer present in the fs
                 # who are not removed in the dirstate ?
-                if nonexistent and type in "nm":
+                if nonexistent and type_ in "nm":
                     deleted.append(fn)
                     continue
             # check the common case first
-            if type == 'n':
+            if type_ == 'n':
                 if not st:
                     st = os.stat(fn)
-                if size != st.st_size or (mode ^ st.st_mode) & 0100:
+                if size >= 0 and (size != st.st_size
+                                  or (mode ^ st.st_mode) & 0100):
                     modified.append(fn)
                 elif time != st.st_mtime:
                     lookup.append(fn)
-            elif type == 'm':
+            elif type_ == 'm':
                 modified.append(fn)
-            elif type == 'a':
+            elif type_ == 'a':
                 added.append(fn)
-            elif type == 'r':
+            elif type_ == 'r':
                 removed.append(fn)
 
         return (lookup, modified, added, removed, deleted, unknown)
--- a/mercurial/hgweb.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/hgweb.py	Fri Mar 03 09:39:37 2006 -0800
@@ -7,6 +7,7 @@
 # of the GNU General Public License, incorporated herein by reference.
 
 import os, cgi, sys, urllib
+import mimetypes
 from demandload import demandload
 demandload(globals(), "mdiff time re socket zlib errno ui hg ConfigParser")
 demandload(globals(), "zipfile tempfile StringIO tarfile BaseHTTPServer util")
@@ -18,7 +19,7 @@
     for f in "templates", "../templates":
         p = os.path.join(os.path.dirname(__file__), f)
         if os.path.isdir(p):
-            return p
+            return os.path.normpath(p)
 
 def age(x):
     def plural(t, c):
@@ -71,6 +72,30 @@
     else:
         return os.stat(hg_path).st_mtime
 
+def staticfile(directory, fname):
+    """return a file inside directory with guessed content-type header
+
+    fname always uses '/' as directory separator and isn't allowed to
+    contain unusual path components.
+    Content-type is guessed using the mimetypes module.
+    Return an empty string if fname is illegal or file not found.
+
+    """
+    parts = fname.split('/')
+    path = directory
+    for part in parts:
+        if (part in ('', os.curdir, os.pardir) or
+            os.sep in part or os.altsep is not None and os.altsep in part):
+            return ""
+        path = os.path.join(path, part)
+    try:
+        os.stat(path)
+        ct = mimetypes.guess_type(path)[0] or "text/plain"
+        return "Content-type: %s\n\n%s" % (ct, file(path).read())
+    except (TypeError, OSError):
+        # illegal fname or unreadable file
+        return ""
+
 class hgrequest(object):
     def __init__(self, inp=None, out=None, env=None):
         self.inp = inp or sys.stdin
@@ -660,9 +685,10 @@
         i = self.repo.tagslist()
         i.reverse()
 
-        def entries(**map):
+        def entries(notip=False, **map):
             parity = 0
             for k,n in i:
+                if notip and k == "tip": continue
                 yield {"parity": parity,
                        "tag": k,
                        "tagmanifest": hex(cl.read(n)[0]),
@@ -672,7 +698,8 @@
 
         yield self.t("tags",
                      manifest=hex(mf),
-                     entries=entries)
+                     entries=lambda **x: entries(False, **x),
+                     entriesnotip=lambda **x: entries(True, **x))
 
     def summary(self):
         cl = self.repo.changelog
@@ -843,6 +870,7 @@
                 'ca': [('cmd', ['archive']), ('node', None)],
                 'tags': [('cmd', ['tags'])],
                 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
+                'static': [('cmd', ['static']), ('file', None)]
             }
 
             for k in shortcuts.iterkeys():
@@ -858,6 +886,7 @@
         expand_form(req.form)
 
         t = self.repo.ui.config("web", "templates", templatepath())
+        static = self.repo.ui.config("web", "static", os.path.join(t,"static"))
         m = os.path.join(t, "map")
         style = self.repo.ui.config("web", "style", "")
         if req.form.has_key('style'):
@@ -962,7 +991,7 @@
                 nodes = map(bin, req.form['roots'][0].split(" "))
 
             z = zlib.compressobj()
-            f = self.repo.changegroup(nodes)
+            f = self.repo.changegroup(nodes, 'serve')
             while 1:
                 chunk = f.read(4096)
                 if not chunk:
@@ -981,6 +1010,11 @@
 
             req.write(self.t("error"))
 
+        elif req.form['cmd'][0] == 'static':
+            fname = req.form['file'][0]
+            req.write(staticfile(static, fname)
+                      or self.t("error", error="%r not found" % fname))
+
         else:
             req.write(self.t("error"))
 
@@ -1152,4 +1186,10 @@
             else:
                 req.write(tmpl("notfound", repo=virtual))
         else:
-            req.write(tmpl("index", entries=entries))
+            if req.form.has_key('static'):
+                static = os.path.join(templatepath(), "static")
+                fname = req.form['static'][0]
+                req.write(staticfile(static, fname)
+                          or tmpl("error", error="%r not found" % fname))
+            else:
+                req.write(tmpl("index", entries=entries))
--- a/mercurial/httprepo.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/httprepo.py	Fri Mar 03 09:39:37 2006 -0800
@@ -119,7 +119,7 @@
             self.ui.warn(_("unexpected response:\n") + d[:400] + "\n...\n")
             raise
 
-    def changegroup(self, nodes):
+    def changegroup(self, nodes, kind):
         n = " ".join(map(hex, nodes))
         f = self.do_cmd("changegroup", roots=n)
         bytes = 0
--- a/mercurial/localrepo.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/localrepo.py	Fri Mar 03 09:39:37 2006 -0800
@@ -13,6 +13,8 @@
 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
 
 class localrepository(object):
+    def __del__(self):
+        self.transhandle = None
     def __init__(self, ui, path=None, create=0):
         if not path:
             p = os.getcwd()
@@ -37,6 +39,7 @@
         self.nodetagscache = None
         self.encodepats = None
         self.decodepats = None
+        self.transhandle = None
 
         if create:
             os.mkdir(self.path)
@@ -48,30 +51,36 @@
         except IOError:
             pass
 
-    def hook(self, name, **args):
+    def hook(self, name, throw=False, **args):
         def runhook(name, cmd):
             self.ui.note(_("running hook %s: %s\n") % (name, cmd))
             old = {}
             for k, v in args.items():
                 k = k.upper()
+                old['HG_' + k] = os.environ.get(k, None)
                 old[k] = os.environ.get(k, None)
-                os.environ[k] = v
+                os.environ['HG_' + k] = str(v)
+                os.environ[k] = str(v)
 
-            # Hooks run in the repository root
-            olddir = os.getcwd()
-            os.chdir(self.root)
-            r = os.system(cmd)
-            os.chdir(olddir)
+            try:
+                # Hooks run in the repository root
+                olddir = os.getcwd()
+                os.chdir(self.root)
+                r = os.system(cmd)
+            finally:
+                for k, v in old.items():
+                    if v is not None:
+                        os.environ[k] = v
+                    else:
+                        del os.environ[k]
 
-            for k, v in old.items():
-                if v != None:
-                    os.environ[k] = v
-                else:
-                    del os.environ[k]
+                os.chdir(olddir)
 
             if r:
-                self.ui.warn(_("abort: %s hook failed with status %d!\n") %
-                             (name, r))
+                desc, r = util.explain_exit(r)
+                if throw:
+                    raise util.Abort(_('%s hook %s') % (name, desc))
+                self.ui.warn(_('error: %s hook %s\n') % (name, desc))
                 return False
             return True
 
@@ -209,6 +218,10 @@
         return self.wopener(filename, 'w').write(data)
 
     def transaction(self):
+        tr = self.transhandle
+        if tr != None and tr.running():
+            return tr.nest()
+
         # save dirstate for undo
         try:
             ds = self.opener("dirstate").read()
@@ -216,21 +229,18 @@
             ds = ""
         self.opener("journal.dirstate", "w").write(ds)
 
-        def after():
-            util.rename(self.join("journal"), self.join("undo"))
-            util.rename(self.join("journal.dirstate"),
-                        self.join("undo.dirstate"))
-
-        return transaction.transaction(self.ui.warn, self.opener,
-                                       self.join("journal"), after)
+        tr = transaction.transaction(self.ui.warn, self.opener,
+                                       self.join("journal"), 
+                                       aftertrans(self.path))
+        self.transhandle = tr
+        return tr
 
     def recover(self):
-        lock = self.lock()
+        l = self.lock()
         if os.path.exists(self.join("journal")):
             self.ui.status(_("rolling back interrupted transaction\n"))
             transaction.rollback(self.opener, self.join("journal"))
-            self.manifest = manifest.manifest(self.opener)
-            self.changelog = changelog.changelog(self.opener)
+            self.reload()
             return True
         else:
             self.ui.warn(_("no interrupted transaction available\n"))
@@ -239,34 +249,70 @@
     def undo(self, wlock=None):
         if not wlock:
             wlock = self.wlock()
-        lock = self.lock()
+        l = self.lock()
         if os.path.exists(self.join("undo")):
             self.ui.status(_("rolling back last transaction\n"))
             transaction.rollback(self.opener, self.join("undo"))
             util.rename(self.join("undo.dirstate"), self.join("dirstate"))
-            self.dirstate.read()
+            self.reload()
+            self.wreload()
         else:
             self.ui.warn(_("no undo information available\n"))
 
-    def lock(self, wait=1):
+    def wreload(self):
+        self.dirstate.read()
+
+    def reload(self):
+        self.changelog.load()
+        self.manifest.load()
+        self.tagscache = None
+        self.nodetagscache = None
+
+    def do_lock(self, lockname, wait, releasefn=None, acquirefn=None):
         try:
-            return lock.lock(self.join("lock"), 0)
-        except lock.LockHeld, inst:
-            if wait:
-                self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
-                return lock.lock(self.join("lock"), wait)
-            raise inst
-
-    def wlock(self, wait=1):
-        try:
-            wlock = lock.lock(self.join("wlock"), 0, self.dirstate.write)
+            l = lock.lock(self.join(lockname), 0, releasefn)
         except lock.LockHeld, inst:
             if not wait:
                 raise inst
             self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
-            wlock = lock.lock(self.join("wlock"), wait, self.dirstate.write)
-        self.dirstate.read()
-        return wlock
+            try:
+                # default to 600 seconds timeout
+                l = lock.lock(self.join(lockname),
+                              int(self.ui.config("ui", "timeout") or 600),
+                              releasefn)
+            except lock.LockHeld, inst:
+                raise util.Abort(_("timeout while waiting for "
+                                   "lock held by %s") % inst.args[0])
+        if acquirefn:
+            acquirefn()
+        return l
+
+    def lock(self, wait=1):
+        return self.do_lock("lock", wait, acquirefn=self.reload)
+
+    def wlock(self, wait=1):
+        return self.do_lock("wlock", wait,
+                            self.dirstate.write,
+                            self.wreload)
+
+    def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
+        "determine whether a new filenode is needed"
+        fp1 = manifest1.get(filename, nullid)
+        fp2 = manifest2.get(filename, nullid)
+
+        if fp2 != nullid:
+            # is one parent an ancestor of the other?
+            fpa = filelog.ancestor(fp1, fp2)
+            if fpa == fp1:
+                fp1, fp2 = fp2, nullid
+            elif fpa == fp2:
+                fp2 = nullid
+
+            # is the file unmodified from the parent? report existing entry
+            if fp2 == nullid and text == filelog.read(fp1):
+                return (fp1, None, None)
+
+        return (None, fp1, fp2)
 
     def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
         orig_parent = self.dirstate.parents()[0] or nullid
@@ -286,7 +332,7 @@
 
         if not wlock:
             wlock = self.wlock()
-        lock = self.lock()
+        l = self.lock()
         tr = self.transaction()
         mm = m1.copy()
         mfm = mf1.copy()
@@ -298,27 +344,10 @@
                 r = self.file(f)
                 mfm[f] = tm
 
-                fp1 = m1.get(f, nullid)
-                fp2 = m2.get(f, nullid)
-
-                # is the same revision on two branches of a merge?
-                if fp2 == fp1:
-                    fp2 = nullid
-
-                if fp2 != nullid:
-                    # is one parent an ancestor of the other?
-                    fpa = r.ancestor(fp1, fp2)
-                    if fpa == fp1:
-                        fp1, fp2 = fp2, nullid
-                    elif fpa == fp2:
-                        fp2 = nullid
-
-                    # is the file unmodified from the parent?
-                    if t == r.read(fp1):
-                        # record the proper existing parent in manifest
-                        # no need to add a revision
-                        mm[f] = fp1
-                        continue
+                (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
+                if entry:
+                    mm[f] = entry
+                    continue
 
                 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
                 changed.append(f)
@@ -342,7 +371,7 @@
             self.dirstate.setparents(n, nullid)
 
     def commit(self, files=None, text="", user=None, date=None,
-               match=util.always, force=False, wlock=None):
+               match=util.always, force=False, lock=None, wlock=None):
         commit = []
         remove = []
         changed = []
@@ -372,12 +401,16 @@
             self.ui.status(_("nothing changed\n"))
             return None
 
-        if not self.hook("precommit"):
-            return None
+        xp1 = hex(p1)
+        if p2 == nullid: xp2 = ''
+        else: xp2 = hex(p2)
+
+        self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
 
         if not wlock:
             wlock = self.wlock()
-        lock = self.lock()
+        if not lock:
+            lock = self.lock()
         tr = self.transaction()
 
         # check in files
@@ -403,22 +436,9 @@
                 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
                 fp1, fp2 = nullid, nullid
             else:
-                fp1 = m1.get(f, nullid)
-                fp2 = m2.get(f, nullid)
-
-            if fp2 != nullid:
-                # is one parent an ancestor of the other?
-                fpa = r.ancestor(fp1, fp2)
-                if fpa == fp1:
-                    fp1, fp2 = fp2, nullid
-                elif fpa == fp2:
-                    fp2 = nullid
-
-                # is the file unmodified from the parent?
-                if not meta and t == r.read(fp1) and fp2 == nullid:
-                    # record the proper existing parent in manifest
-                    # no need to add a revision
-                    new[f] = fp1
+                entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
+                if entry:
+                    new[f] = entry
                     continue
 
             new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
@@ -459,14 +479,15 @@
 
         user = user or self.ui.username()
         n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
+        self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
+                  parent2=xp2)
         tr.close()
 
         self.dirstate.setparents(n)
         self.dirstate.update(new, "n")
         self.dirstate.forget(remove)
 
-        if not self.hook("commit", node=hex(n)):
-            return None
+        self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
         return n
 
     def walk(self, node=None, files=[], match=util.always):
@@ -504,12 +525,18 @@
                     del mf[fn]
             return mf
 
+        if node1:
+            # read the manifest from node1 before the manifest from node2,
+            # so that we'll hit the manifest cache if we're going through
+            # all the revisions in parent->child order.
+            mf1 = mfmatches(node1)
+
         # are we comparing the working directory?
         if not node2:
             if not wlock:
                 try:
                     wlock = self.wlock(wait=0)
-                except lock.LockHeld:
+                except lock.LockException:
                     wlock = None
             lookup, modified, added, removed, deleted, unknown = (
                 self.dirstate.changes(files, match))
@@ -542,8 +569,6 @@
             # flush lists from dirstate before comparing manifests
             modified, added = [], []
 
-            mf1 = mfmatches(node1)
-
             for fn in mf2:
                 if mf1.has_key(fn):
                     if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
@@ -598,7 +623,6 @@
             if os.path.exists(p):
                 self.ui.warn(_("%s still exists!\n") % f)
             elif self.dirstate.state(f) == 'a':
-                self.ui.warn(_("%s never committed!\n") % f)
                 self.dirstate.forget([f])
             elif f not in self.dirstate:
                 self.ui.warn(_("%s not tracked!\n") % f)
@@ -933,7 +957,7 @@
         return subset
 
     def pull(self, remote, heads=None):
-        lock = self.lock()
+        l = self.lock()
 
         # if we have an empty repo, fetch everything
         if self.changelog.tip() == nullid:
@@ -947,12 +971,12 @@
             return 1
 
         if heads is None:
-            cg = remote.changegroup(fetch)
+            cg = remote.changegroup(fetch, 'pull')
         else:
-            cg = remote.changegroupsubset(fetch, heads)
+            cg = remote.changegroupsubset(fetch, heads, 'pull')
         return self.addchangegroup(cg)
 
-    def push(self, remote, force=False):
+    def push(self, remote, force=False, revs=None):
         lock = remote.lock()
 
         base = {}
@@ -964,20 +988,28 @@
             return 1
 
         update = self.findoutgoing(remote, base)
-        if not update:
+        if revs is not None:
+            msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
+        else:
+            bases, heads = update, self.changelog.heads()
+
+        if not bases:
             self.ui.status(_("no changes found\n"))
             return 1
         elif not force:
-            if len(heads) < len(self.changelog.heads()):
+            if len(bases) < len(heads):
                 self.ui.warn(_("abort: push creates new remote branches!\n"))
                 self.ui.status(_("(did you forget to merge?"
                                  " use push -f to force)\n"))
                 return 1
 
-        cg = self.changegroup(update)
+        if revs is None:
+            cg = self.changegroup(update, 'push')
+        else:
+            cg = self.changegroupsubset(update, revs, 'push')
         return remote.addchangegroup(cg)
 
-    def changegroupsubset(self, bases, heads):
+    def changegroupsubset(self, bases, heads, source):
         """This function generates a changegroup consisting of all the nodes
         that are descendents of any of the bases, and ancestors of any of
         the heads.
@@ -989,6 +1021,8 @@
         Another wrinkle is doing the reverse, figuring out which changeset in
         the changegroup a particular filenode or manifestnode belongs to."""
 
+        self.hook('preoutgoing', throw=True, source=source)
+
         # Set up some initial variables
         # Make it easy to refer to self.changelog
         cl = self.changelog
@@ -1241,14 +1275,19 @@
             # Signal that no more groups are left.
             yield struct.pack(">l", 0)
 
+            self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
+
         return util.chunkbuffer(gengroup())
 
-    def changegroup(self, basenodes):
+    def changegroup(self, basenodes, source):
         """Generate a changegroup of all nodes that we have that a recipient
         doesn't.
 
         This is much easier than the previous function as we can assume that
         the recipient has any changenode we aren't sending them."""
+
+        self.hook('preoutgoing', throw=True, source=source)
+
         cl = self.changelog
         nodes = cl.nodesbetween(basenodes, None)[0]
         revset = dict.fromkeys([cl.rev(n) for n in nodes])
@@ -1300,6 +1339,7 @@
                         yield chnk
 
             yield struct.pack(">l", 0)
+            self.hook('outgoing', node=hex(nodes[0]), source=source)
 
         return util.chunkbuffer(gengroup())
 
@@ -1335,6 +1375,9 @@
 
         if not source:
             return
+
+        self.hook('prechangegroup', throw=True)
+
         changesets = files = revisions = 0
 
         tr = self.transaction()
@@ -1377,18 +1420,16 @@
                          " with %d changes to %d files%s\n")
                          % (changesets, revisions, files, heads))
 
+        self.hook('pretxnchangegroup', throw=True,
+                  node=hex(self.changelog.node(cor+1)))
+
         tr.close()
 
         if changesets > 0:
-            if not self.hook("changegroup",
-                             node=hex(self.changelog.node(cor+1))):
-                self.ui.warn(_("abort: changegroup hook returned failure!\n"))
-                return 1
+            self.hook("changegroup", node=hex(self.changelog.node(cor+1)))
 
             for i in range(cor + 1, cnr + 1):
-                self.hook("commit", node=hex(self.changelog.node(i)))
-
-        return
+                self.hook("incoming", node=hex(self.changelog.node(i)))
 
     def update(self, node, allow=False, force=False, choose=None,
                moddirstate=True, forcemerge=False, wlock=None):
@@ -1844,3 +1885,13 @@
         if errors[0]:
             self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
             return 1
+
+# used to avoid circular references so destructors work
+def aftertrans(base):
+    p = base
+    def a():
+        util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
+        util.rename(os.path.join(p, "journal.dirstate"),
+                    os.path.join(p, "undo.dirstate"))
+    return a
+
--- a/mercurial/lock.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/lock.py	Fri Mar 03 09:39:37 2006 -0800
@@ -5,17 +5,21 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
-import os, time
+import errno, os, time
 import util
 
-class LockHeld(Exception):
+class LockException(Exception):
+    pass
+class LockHeld(LockException):
+    pass
+class LockUnavailable(LockException):
     pass
 
 class lock(object):
-    def __init__(self, file, wait=1, releasefn=None):
+    def __init__(self, file, timeout=-1, releasefn=None):
         self.f = file
         self.held = 0
-        self.wait = wait
+        self.timeout = timeout
         self.releasefn = releasefn
         self.lock()
 
@@ -23,13 +27,16 @@
         self.release()
 
     def lock(self):
+        timeout = self.timeout
         while 1:
             try:
                 self.trylock()
                 return 1
             except LockHeld, inst:
-                if self.wait:
+                if timeout != 0:
                     time.sleep(1)
+                    if timeout > 0:
+                        timeout -= 1
                     continue
                 raise inst
 
@@ -38,8 +45,11 @@
         try:
             util.makelock(str(pid), self.f)
             self.held = 1
-        except (OSError, IOError):
-            raise LockHeld(util.readlock(self.f))
+        except (OSError, IOError), why:
+            if why.errno == errno.EEXIST:
+                raise LockHeld(util.readlock(self.f))
+            else:
+                raise LockUnavailable(why)
 
     def release(self):
         if self.held:
--- a/mercurial/mdiff.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/mdiff.py	Fri Mar 03 09:39:37 2006 -0800
@@ -18,16 +18,22 @@
 
     if not text and (util.binary(a) or util.binary(b)):
         l = ['Binary file %s has changed\n' % fn]
-    elif a == None:
+    elif not a:
         b = b.splitlines(1)
-        l1 = "--- %s\t%s\n" % ("/dev/null", epoch)
+        if a is None:
+            l1 = "--- %s\t%s\n" % ("/dev/null", epoch)
+        else:
+            l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
         l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
         l3 = "@@ -0,0 +1,%d @@\n" % len(b)
         l = [l1, l2, l3] + ["+" + e for e in b]
-    elif b == None:
+    elif not b:
         a = a.splitlines(1)
         l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
-        l2 = "+++ %s\t%s\n" % ("/dev/null", epoch)
+        if b is None:
+            l2 = "+++ %s\t%s\n" % ("/dev/null", epoch)
+        else:
+            l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
         l3 = "@@ -1,%d +0,0 @@\n" % len(a)
         l = [l1, l2, l3] + ["-" + e for e in a]
     else:
--- a/mercurial/mpatch.c	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/mpatch.c	Fri Mar 03 09:39:37 2006 -0800
@@ -43,6 +43,7 @@
 #endif
 
 static char mpatch_doc[] = "Efficient binary patching.";
+static PyObject *mpatch_Error;
 
 struct frag {
 	int start, end, len;
@@ -65,8 +66,11 @@
 			a = NULL;
 		} else
 			a->head = a->tail = a->base;
+		return a;
 	}
-	return a;
+	if (!PyErr_Occurred())
+		PyErr_NoMemory();
+	return NULL;
 }
 
 static void lfree(struct flist *a)
@@ -215,6 +219,9 @@
 
 	/* assume worst case size, we won't have many of these lists */
 	l = lalloc(len / 12);
+	if (!l)
+		return NULL;
+
 	lt = l->tail;
 
 	while (bin < end) {
@@ -227,6 +234,13 @@
 		lt++;
 	}
 
+	if (bin != end) {
+		if (!PyErr_Occurred())
+			PyErr_SetString(mpatch_Error, "patch cannot be decoded");
+		lfree(l);
+		return NULL;
+	}
+
 	l->tail = lt;
 	return l;
 }
@@ -238,6 +252,12 @@
 	struct frag *f = l->head;
 
 	while (f != l->tail) {
+		if (f->start < last || f->end > len) {
+			if (!PyErr_Occurred())
+				PyErr_SetString(mpatch_Error,
+				                "invalid patch");
+			return -1;
+		}
 		outlen += f->start - last;
 		last = f->end;
 		outlen += f->len;
@@ -248,13 +268,19 @@
 	return outlen;
 }
 
-static void apply(char *buf, char *orig, int len, struct flist *l)
+static int apply(char *buf, char *orig, int len, struct flist *l)
 {
 	struct frag *f = l->head;
 	int last = 0;
 	char *p = buf;
 
 	while (f != l->tail) {
+		if (f->start < last || f->end > len) {
+			if (!PyErr_Occurred())
+				PyErr_SetString(mpatch_Error,
+				                "invalid patch");
+			return 0;
+		}
 		memcpy(p, orig + last, f->start - last);
 		p += f->start - last;
 		memcpy(p, f->data, f->len);
@@ -263,6 +289,7 @@
 		f++;
 	}
 	memcpy(p, orig + last, len - last);
+	return 1;
 }
 
 /* recursively generate a patch of all bins between start and end */
@@ -304,16 +331,25 @@
 
 	patch = fold(bins, 0, len);
 	if (!patch)
-		return PyErr_NoMemory();
+		return NULL;
 
 	outlen = calcsize(PyString_Size(text), patch);
+	if (outlen < 0) {
+		result = NULL;
+		goto cleanup;
+	}
 	result = PyString_FromStringAndSize(NULL, outlen);
-	if (result) {
-		in = PyString_AsString(text);
-		out = PyString_AsString(result);
-		apply(out, in, PyString_Size(text), patch);
+	if (!result) {
+		result = NULL;
+		goto cleanup;
 	}
-
+	in = PyString_AsString(text);
+	out = PyString_AsString(result);
+	if (!apply(out, in, PyString_Size(text), patch)) {
+		Py_DECREF(result);
+		result = NULL;
+	}
+cleanup:
 	lfree(patch);
 	return result;
 }
@@ -327,5 +363,6 @@
 initmpatch(void)
 {
 	Py_InitModule3("mpatch", methods, mpatch_doc);
+	mpatch_Error = PyErr_NewException("mpatch.mpatchError", NULL, NULL);
 }
 
--- a/mercurial/revlog.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/revlog.py	Fri Mar 03 09:39:37 2006 -0800
@@ -13,7 +13,7 @@
 from node import *
 from i18n import gettext as _
 from demandload import demandload
-demandload(globals(), "binascii errno heapq mdiff sha struct zlib")
+demandload(globals(), "binascii errno heapq mdiff os sha struct zlib")
 
 def hash(text, p1, p2):
     """generate a hash from the given text and its parent hashes
@@ -187,15 +187,33 @@
         self.indexfile = indexfile
         self.datafile = datafile
         self.opener = opener
+
+        self.indexstat = None
         self.cache = None
         self.chunkcache = None
+        self.load()
 
+    def load(self):
         try:
-            i = self.opener(self.indexfile).read()
+            f = self.opener(self.indexfile)
         except IOError, inst:
             if inst.errno != errno.ENOENT:
                 raise
             i = ""
+        else:
+            try:
+                st = os.fstat(f.fileno())
+            except AttributeError, inst:
+                st = None
+            else:
+                oldst = self.indexstat
+                if (oldst and st.st_dev == oldst.st_dev
+                    and st.st_ino == oldst.st_ino
+                    and st.st_mtime == oldst.st_mtime
+                    and st.st_ctime == oldst.st_ctime):
+                    return
+            self.indexstat = st
+            i = f.read()
 
         if i and i[:4] != "\0\0\0\0":
             raise RevlogError(_("incompatible revlog signature on %s") %
@@ -624,12 +642,10 @@
             # we store negative distances because heap returns smallest member
             h = [(-dist[node], node)]
             seen = {}
-            earliest = self.count()
             while h:
                 d, n = heapq.heappop(h)
                 if n not in seen:
                     seen[n] = 1
-                    r = self.rev(n)
                     yield (-d, n)
                     for p in self.parents(n):
                         heapq.heappush(h, (-dist[p], p))
@@ -690,11 +706,6 @@
         p = self.parents(self.node(revs[0]))[0]
         revs.insert(0, self.rev(p))
 
-        # helper to reconstruct intermediate versions
-        def construct(text, base, rev):
-            bins = [self.chunk(r) for r in xrange(base + 1, rev + 1)]
-            return mdiff.patches(text, bins)
-
         # build deltas
         for d in xrange(0, len(revs) - 1):
             a, b = revs[d], revs[d + 1]
@@ -738,10 +749,10 @@
         base = prev = -1
         start = end = measure = 0
         if r:
-            start = self.start(self.base(t))
+            base = self.base(t)
+            start = self.start(base)
             end = self.end(t)
-            measure = self.length(self.base(t))
-            base = self.base(t)
+            measure = self.length(base)
             prev = self.tip()
 
         transaction.add(self.datafile, end)
@@ -793,14 +804,15 @@
                     raise RevlogError(_("consistency error adding group"))
                 measure = len(text)
             else:
-                e = (end, len(cdelta), self.base(t), link, p1, p2, node)
+                e = (end, len(cdelta), base, link, p1, p2, node)
                 self.index.append(e)
                 self.nodemap[node] = r
                 dfh.write(cdelta)
                 ifh.write(struct.pack(indexformat, *e))
 
             t, r, chain, prev = r, r + 1, node, node
-            start = self.start(self.base(t))
+            base = self.base(t)
+            start = self.start(base)
             end = self.end(t)
 
         dfh.close()
--- a/mercurial/sshrepo.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/sshrepo.py	Fri Mar 03 09:39:37 2006 -0800
@@ -110,7 +110,7 @@
         except:
             raise hg.RepoError(_("unexpected response '%s'") % (d[:400] + "..."))
 
-    def changegroup(self, nodes):
+    def changegroup(self, nodes, kind):
         n = " ".join(map(hex, nodes))
         f = self.do_cmd("changegroup", roots=n)
         return self.pipei
--- a/mercurial/statichttprepo.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/statichttprepo.py	Fri Mar 03 09:39:37 2006 -0800
@@ -15,8 +15,10 @@
     def read(self, size=None):
         try:
             return httprangereader.httprangereader.read(self, size)
+        except urllib2.HTTPError, inst:
+            raise IOError(None, inst)
         except urllib2.URLError, inst:
-            raise IOError(None, str(inst))
+            raise IOError(None, inst.reason[1])
 
 def opener(base):
     """return a function that opens files over http"""
--- a/mercurial/transaction.py	Fri Feb 10 11:25:07 2006 -0800
+++ b/mercurial/transaction.py	Fri Mar 03 09:39:37 2006 -0800
@@ -22,6 +22,7 @@
         if os.path.exists(journal):
             raise AssertionError(_("journal already exists - run hg recover"))
 
+        self.count = 1
         self.report = report
         self.opener = opener
         self.after = after
@@ -46,7 +47,17 @@
         self.file.write("%s\0%d\n" % (file, offset))
         self.file.flush()
 
+    def nest(self):
+        self.count += 1
+        return self
+
+    def running(self):
+        return self.count > 0
+
     def close(self):
+        self.count -= 1
+        if self.count != 0:
+            return
         self.file.close()
         self.entries = []
         if self.after: