Merge Benoit's .hg/store support
authorMatt Mackall <mpm@selenic.com>
Sun, 10 Dec 2006 02:11:02 -0600
changeset 3854 4f6db0233606
parent 3848 8cbf060f637e (diff)
parent 3853 c0b449154a90 (current diff)
child 3855 b9cdd6f2aa43
Merge Benoit's .hg/store support
mercurial/hg.py
mercurial/localrepo.py
mercurial/util.py
--- a/README
+++ b/README
@@ -26,7 +26,8 @@ Setting up Mercurial:
 
  And finally:
 
- $ hg                                    # test installation, show help
+ $ hg debuginstall                       # run some basic tests
+ $ hg                                    # show help
 
  If you get complaints about missing modules, you probably haven't set
  PYTHONPATH correctly.
--- a/contrib/convert-repo
+++ b/contrib/convert-repo
@@ -21,8 +21,20 @@
 # interrupted and can be run repeatedly to copy new commits.
 
 import sys, os, zlib, sha, time
+
+os.environ["HGENCODING"] = "utf-8"
+
 from mercurial import hg, ui, util
 
+def recode(s):
+    try:
+        return s.decode("utf-8").encode("utf-8")
+    except:
+        try:
+            return s.decode("latin-1").encode("utf-8")
+        except:
+            return s.decode("utf-8", "replace").encode("utf-8")
+
 class convert_git:
     def __init__(self, path):
         self.path = path
@@ -55,6 +67,7 @@ class convert_git:
         c = self.catfile(version, "commit") # read the commit hash
         end = c.find("\n\n")
         message = c[end+2:]
+        message = recode(message)
         l = c[:end].splitlines()
         manifest = l[0].split()[1]
         parents = []
@@ -65,11 +78,13 @@ class convert_git:
                 tm, tz = p[-2:]
                 author = " ".join(p[:-2])
                 if author[0] == "<": author = author[1:-1]
+                author = recode(author)
             if n == "committer":
                 p = v.split()
                 tm, tz = p[-2:]
                 committer = " ".join(p[:-2])
                 if committer[0] == "<": committer = committer[1:-1]
+                committer = recode(committer)
                 message += "\ncommitter: %s\n" % v
             if n == "parent": parents.append(v)
 
--- a/contrib/zsh_completion
+++ b/contrib/zsh_completion
@@ -178,7 +178,7 @@ typeset -A _hg_cmd_globals
 }
 
 _hg_status() {
-  status_files=(${(ps:\0:)"$(_hg_cmd status -0n$1 . 2>/dev/null)"})
+  status_files=(${(ps:\0:)"$(_hg_cmd status -0n$1 ./$PREFIX 2>/dev/null)"})
 }
 
 _hg_unknown() {
@@ -619,7 +619,6 @@ typeset -A _hg_cmd_globals
 _hg_cmd_update() {
   _arguments -s -w : $_hg_global_opts \
   '(--clean -C)'{-C,--clean}'[overwrite locally modified files]' \
-  '(--force -f)'{-f,--force}'[force a merge with outstanding changes]' \
   ':revision:_hg_tags'
 }
 
--- a/doc/gendoc.py
+++ b/doc/gendoc.py
@@ -3,6 +3,7 @@ import sys, textwrap
 sys.path.insert(0, "..")
 from mercurial.commands import table, globalopts
 from mercurial.i18n import gettext as _
+from mercurial.help import helptable
 
 def get_desc(docstr):
     if not docstr:
@@ -88,5 +89,16 @@ def show_doc(ui):
             if d['aliases']:
                 ui.write(_("    aliases: %s\n\n") % " ".join(d['aliases']))
 
+    # print topics
+    for t in helptable:
+        l = t.split("|")
+        section = l[-1]
+        underlined(_(section).upper())
+        doc = helptable[t]
+        if callable(doc):
+            doc = doc()
+        ui.write(_(doc))
+        ui.write("\n")
+
 if __name__ == "__main__":
     show_doc(sys.stdout)
--- a/doc/hg.1.txt
+++ b/doc/hg.1.txt
@@ -37,52 +37,6 @@ repository path::
 
 include::hg.1.gendoc.txt[]
 
-FILE NAME PATTERNS
-------------------
-
-    Mercurial accepts several notations for identifying one or more
-    files at a time.
-
-    By default, Mercurial treats filenames as shell-style extended
-    glob patterns.
-
-    Alternate pattern notations must be specified explicitly.
-
-    To use a plain path name without any pattern matching, start a
-    name with "path:".  These path names must match completely, from
-    the root of the current repository.
-
-    To use an extended glob, start a name with "glob:".  Globs are
-    rooted at the current directory; a glob such as "*.c" will match
-    files ending in ".c" in the current directory only.
-
-    The supported glob syntax extensions are "**" to match any string
-    across path separators, and "{a,b}" to mean "a or b".
-
-    To use a Perl/Python regular expression, start a name with "re:".
-    Regexp pattern matching is anchored at the root of the repository.
-
-    Plain examples:
-
-    path:foo/bar   a name bar in a directory named foo in the root of
-                   the repository
-    path:path:name a file or directory named "path:name"
-
-    Glob examples:
-
-    glob:*.c       any name ending in ".c" in the current directory
-    *.c            any name ending in ".c" in the current directory
-    **.c           any name ending in ".c" in the current directory, or
-                   any subdirectory
-    foo/*.c        any name ending in ".c" in the directory foo
-    foo/**.c       any name ending in ".c" in the directory foo, or any
-                   subdirectory
-
-    Regexp examples:
-
-    re:.*\.c$      any name ending in ".c", anywhere in the repository
-
-
 SPECIFYING SINGLE REVISIONS
 ---------------------------
 
@@ -127,105 +81,6 @@ SPECIFYING MULTIPLE REVISIONS
     A range acts as a closed interval.  This means that a range of 3:5
     gives 3, 4 and 5.  Similarly, a range of 4:2 gives 4, 3, and 2.
 
-DATE FORMATS
-------------
-
-    Some commands (backout, commit, tag) allow the user to specify a date.
-    Possible formats for dates are:
-
-YYYY-mm-dd \HH:MM[:SS] [(+|-)NNNN]::
-    This is a subset of ISO 8601, allowing just the recommended notations
-    for date and time. The last part represents the timezone; if omitted,
-    local time is assumed. Examples:
-
-    "2005-08-22 03:27 -0700"
-
-    "2006-04-19 21:39:51"
-
-aaa bbb dd HH:MM:SS YYYY [(+|-)NNNN]::
-    This is the date format used by the C library. Here, aaa stands for
-    abbreviated weekday name and bbb for abbreviated month name. The last
-    part represents the timezone; if omitted, local time is assumed.
-    Examples:
-
-    "Mon Aug 22 03:27:00 2005 -0700"
-
-    "Wed Apr 19 21:39:51 2006"
-
-unixtime offset::
-    This is the internal representation format for dates. unixtime is
-    the number of seconds since the epoch (1970-01-01 00:00 UTC). offset
-    is the offset of the local timezone, in seconds west of UTC (negative
-    if the timezone is east of UTC).
-    Examples:
-
-    "1124706420 25200" (2005-08-22 03:27:00 -0700)
-
-    "1145475591 -7200" (2006-04-19 21:39:51 +0200)
-
-ENVIRONMENT VARIABLES
----------------------
-
-HGEDITOR::
-    This is the name of the editor to use when committing. Defaults to the
-    value of EDITOR.
-
-    (deprecated, use .hgrc)
-
-HGENCODING::
-    This overrides the default locale setting detected by Mercurial.
-    This setting is used to convert data including usernames,
-    changeset descriptions, tag names, and branches. This setting can
-    be overridden with the --encoding command-line option.
-
-HGENCODINGMODE::
-    This sets Mercurial's behavior for handling unknown characters
-    while transcoding user inputs. The default is "strict", which
-    causes Mercurial to abort if it can't translate a character. Other
-    settings include "replace", which replaces unknown characters, and
-    "ignore", which drops them. This setting can be overridden with
-    the --encodingmode command-line option.
-
-HGMERGE::
-    An executable to use for resolving merge conflicts. The program
-    will be executed with three arguments: local file, remote file,
-    ancestor file.
-
-    The default program is "hgmerge", which is a shell script provided
-    by Mercurial with some sensible defaults.
-
-    (deprecated, use .hgrc)
-
-HGRCPATH::
-    A list of files or directories to search for hgrc files.  Item
-    separator is ":" on Unix, ";" on Windows.  If HGRCPATH is not set,
-    platform default search path is used.  If empty, only .hg/hgrc of
-    current repository is read.
-
-    For each element in path, if a directory, all entries in directory
-    ending with ".rc" are added to path.  Else, element itself is
-    added to path.
-
-HGUSER::
-    This is the string used for the author of a commit.
-
-    (deprecated, use .hgrc)
-
-EMAIL::
-    If HGUSER is not set, this will be used as the author for a commit.
-
-LOGNAME::
-    If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
-    '@hostname' appended) as the author value for a commit.
-
-EDITOR::
-    This is the name of the editor used in the hgmerge script. It will be
-    used for commit messages if HGEDITOR isn't set. Defaults to 'vi'.
-
-PYTHONPATH::
-    This is used by Python to find imported modules and may need to be set
-    appropriately if Mercurial is not installed system-wide.
-
 FILES
 -----
  .hgignore::
--- a/doc/hgrc.5.txt
+++ b/doc/hgrc.5.txt
@@ -388,6 +388,9 @@ ui::
     Print debugging information.  True or False.  Default is False.
   editor;;
     The editor to use during a commit.  Default is $EDITOR or "vi".
+  fallbackencoding;;
+    Encoding to try if it's not possible to decode the changelog using
+    UTF-8.  Default is ISO-8859-1.
   ignore;;
     A file to read per-user ignore patterns from. This file should be in
     the same format as a repository-wide .hgignore file. This option
--- a/hgext/hgk.py
+++ b/hgext/hgk.py
@@ -285,7 +285,7 @@ def view(ui, repo, *etc, **opts):
     os.system(cmd)
 
 cmdtable = {
-    "view": (view,
+    "^view": (view,
              [('l', 'limit', '', 'limit number of changes displayed')],
              'hg view [-l LIMIT] [REVRANGE]'),
     "debug-diff-tree": (difftree, [('p', 'patch', None, 'generate patch'),
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -2043,13 +2043,10 @@ def reposetup(ui, repo):
 
             return tagscache
 
-        def branchtags(self):
-            if self.branchcache != None:
-                return self.branchcache
-
+        def _branchtags(self):
             q = self.mq
             if not q.applied:
-                return super(mqrepo, self).branchtags()
+                return super(mqrepo, self)._branchtags()
 
             self.branchcache = {} # avoid recursion in changectx
             cl = self.changelog
@@ -2069,8 +2066,7 @@ def reposetup(ui, repo):
             # update the cache up to the tip
             self._updatebranchcache(partial, start, cl.count())
 
-            self.branchcache = partial
-            return self.branchcache
+            return partial
 
     if repo.local():
         repo.__class__ = mqrepo
--- a/hgext/transplant.py
+++ b/hgext/transplant.py
@@ -178,8 +178,7 @@ class transplanter:
         fp.write("# HG changeset patch\n")
         fp.write("# User %s\n" % user)
         fp.write("# Date %d %d\n" % date)
-        fp.write(changelog[4].rstrip())
-        fp.write("\n\n")
+        fp.write(changelog[4])
         fp.close()
 
         try:
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -18,7 +18,7 @@ def revpair(repo, revs):
     be None, meaning use working dir.'''
 
     def revfix(repo, val, defval):
-        if not val and val != 0:
+        if not val and val != 0 and defval is not None:
             val = defval
         return repo.lookup(val)
 
@@ -261,6 +261,7 @@ class changeset_printer(object):
         self.ui.write(_("changeset:   %d:%s\n") % (rev, hexfunc(changenode)))
 
         if branch:
+            branch = util.tolocal(branch)
             self.ui.write(_("branch:      %s\n") % branch)
         for tag in self.repo.nodetags(changenode):
             self.ui.write(_("tag:         %s\n") % tag)
@@ -313,7 +314,7 @@ class changeset_printer(object):
     def showpatch(self, node):
         if self.patch:
             prev = self.repo.changelog.parents(node)[0]
-            patch.diff(self.repo, prev, node, fp=self.ui)
+            patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui)
             self.ui.write("\n")
 
 class changeset_templater(changeset_printer):
@@ -404,6 +405,7 @@ class changeset_templater(changeset_prin
         def showbranches(**args):
             branch = changes[5].get("branch")
             if branch:
+                branch = util.tolocal(branch)
                 return showlist('branch', [branch], plural='branches', **args)
             # add old style branches if requested
             if self.brinfo:
@@ -508,7 +510,7 @@ class changeset_templater(changeset_prin
         except SyntaxError, inst:
             raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
 
-def show_changeset(ui, repo, opts, buffered=False):
+def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
     """show one changeset using template or regular display.
 
     Display format will be the first non-empty hit of:
@@ -520,7 +522,10 @@ def show_changeset(ui, repo, opts, buffe
     regular display via changeset_printer() is done.
     """
     # options
-    patch = opts.get('patch')
+    patch = False
+    if opts.get('patch'):
+        patch = matchfn or util.always
+
     br = None
     if opts.get('branches'):
         ui.warn(_("the --branches option is deprecated, "
@@ -554,6 +559,25 @@ def show_changeset(ui, repo, opts, buffe
         return t
     return changeset_printer(ui, repo, patch, br, buffered)
 
+def finddate(ui, repo, date):
+    """Find the tipmost changeset that matches the given date spec"""
+    df = util.matchdate(date + " to " + date)
+    get = util.cachefunc(lambda r: repo.changectx(r).changeset())
+    changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
+    results = {}
+    for st, rev, fns in changeiter:
+        if st == 'add':
+            d = get(rev)[2]
+            if df(d[0]):
+                results[rev] = d
+        elif st == 'iter':
+            if rev in results:
+                ui.status("Found revision %s from %s\n" %
+                          (rev, util.datestr(results[rev])))
+                return str(rev)
+
+    raise util.Abort(_("revision matching date not found"))
+
 def walkchangerevs(ui, repo, pats, change, opts):
     '''Iterate over files and the revs they changed in.
 
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -10,7 +10,7 @@ from node import *
 from i18n import gettext as _
 demandload(globals(), "bisect os re sys signal imp urllib pdb shlex stat")
 demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo")
-demandload(globals(), "difflib patch time")
+demandload(globals(), "difflib patch time help mdiff tempfile")
 demandload(globals(), "traceback errno version atexit")
 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
 
@@ -57,7 +57,8 @@ def add(ui, repo, *pats, **opts):
 
     Schedule files to be version controlled and added to the repository.
 
-    The files will be added to the repository at the next commit.
+    The files will be added to the repository at the next commit. To
+    undo an add before that, see hg revert.
 
     If no names are given, add all files in the repository.
     """
@@ -222,6 +223,7 @@ def backout(ui, repo, rev, **opts):
         parent = p1
     hg.clean(repo, node, show_stats=False)
     revert_opts = opts.copy()
+    revert_opts['date'] = None
     revert_opts['all'] = True
     revert_opts['rev'] = hex(parent)
     revert(ui, repo, **revert_opts)
@@ -649,7 +651,8 @@ def copy(ui, repo, *pats, **opts):
     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.
+    This command takes effect in the next commit. To undo a copy
+    before that, see hg revert.
     """
     wlock = repo.wlock(0)
     errs, copied = docopy(ui, repo, pats, opts, wlock)
@@ -788,6 +791,18 @@ def debugdata(ui, file_, rev):
     except KeyError:
         raise util.Abort(_('invalid revision identifier %s') % rev)
 
+def debugdate(ui, date, range=None, **opts):
+    """parse and display a date"""
+    if opts["extended"]:
+        d = util.parsedate(date, util.extendeddateformats)
+    else:
+        d = util.parsedate(date)
+    ui.write("internal: %s %s\n" % d)
+    ui.write("standard: %s\n" % util.datestr(d))
+    if range:
+        m = util.matchdate(range)
+        ui.write("match: %s\n" % m(d[0]))
+
 def debugindex(ui, file_):
     """dump the contents of an index file"""
     r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
@@ -812,6 +827,156 @@ def debugindexdot(ui, file_):
             ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
     ui.write("}\n")
 
+def debuginstall(ui):
+    '''test Mercurial installation'''
+
+    def writetemp(contents):
+        (fd, name) = tempfile.mkstemp()
+        f = os.fdopen(fd, "wb")
+        f.write(contents)
+        f.close()
+        return name
+
+    problems = 0
+
+    # encoding
+    ui.status(_("Checking encoding (%s)...\n") % util._encoding)
+    try:
+        util.fromlocal("test")
+    except util.Abort, inst:
+        ui.write(" %s\n" % inst)
+        ui.write(_(" (check that your locale is properly set)\n"))
+        problems += 1
+
+    # compiled modules
+    ui.status(_("Checking extensions...\n"))
+    try:
+        import bdiff, mpatch, base85
+    except Exception, inst:
+        ui.write(" %s\n" % inst)
+        ui.write(_(" One or more extensions could not be found"))
+        ui.write(_(" (check that you compiled the extensions)\n"))
+        problems += 1
+
+    # templates
+    ui.status(_("Checking templates...\n"))
+    try:
+        import templater
+        t = templater.templater(templater.templatepath("map-cmdline.default"))
+    except Exception, inst:
+        ui.write(" %s\n" % inst)
+        ui.write(_(" (templates seem to have been installed incorrectly)\n"))
+        problems += 1
+
+    # patch
+    ui.status(_("Checking patch...\n"))
+    path = os.environ.get('PATH', '')
+    patcher = util.find_in_path('gpatch', path,
+                                util.find_in_path('patch', path, None))
+    if not patcher:
+        ui.write(_(" Can't find patch or gpatch in PATH\n"))
+        ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
+        problems += 1
+    else:
+        # actually attempt a patch here
+        a = "1\n2\n3\n4\n"
+        b = "1\n2\n3\ninsert\n4\n"
+        d = mdiff.unidiff(a, None, b, None, "a")
+        fa = writetemp(a)
+        fd = writetemp(d)
+        fp = os.popen('%s %s %s' % (patcher, fa, fd))
+        files = []
+        output = ""
+        for line in fp:
+            output += line
+            if line.startswith('patching file '):
+                pf = util.parse_patch_output(line.rstrip())
+                files.append(pf)
+        if files != [fa]:
+            ui.write(_(" unexpected patch output!"))
+            ui.write(_(" (you may have an incompatible version of patch)\n"))
+            ui.write(data)
+            problems += 1
+        a = file(fa).read()
+        if a != b:
+            ui.write(_(" patch test failed!"))
+            ui.write(_(" (you may have an incompatible version of patch)\n"))
+            problems += 1
+        os.unlink(fa)
+        os.unlink(fd)
+
+    # merge helper
+    ui.status(_("Checking merge helper...\n"))
+    cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
+           or "hgmerge")
+    cmdpath = util.find_in_path(cmd, path)
+    if not cmdpath:
+        cmdpath = util.find_in_path(cmd.split()[0], path)
+    if not cmdpath:
+        if cmd == 'hgmerge':
+            ui.write(_(" No merge helper set and can't find default"
+                       " hgmerge script in PATH\n"))
+            ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
+        else:
+            ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
+            ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
+            problems += 1
+    else:
+        # actually attempt a patch here
+        fa = writetemp("1\n2\n3\n4\n")
+        fl = writetemp("1\n2\n3\ninsert\n4\n")
+        fr = writetemp("begin\n1\n2\n3\n4\n")
+        r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
+        if r:
+            ui.write(_(" got unexpected merge error %d!") % r)
+            problems += 1
+        m = file(fl).read()
+        if m != "begin\n1\n2\n3\ninsert\n4\n":
+            ui.write(_(" got unexpected merge results!") % r)
+            ui.write(_(" (your merge helper may have the"
+                       " wrong argument order)\n"))
+            ui.write(m)
+        os.unlink(fa)
+        os.unlink(fl)
+        os.unlink(fr)
+
+    # editor
+    ui.status(_("Checking commit editor...\n"))
+    editor = (os.environ.get("HGEDITOR") or
+              ui.config("ui", "editor") or
+              os.environ.get("EDITOR", "vi"))
+    cmdpath = util.find_in_path(editor, path)
+    if not cmdpath:
+        cmdpath = util.find_in_path(editor.split()[0], path)
+    if not cmdpath:
+        if cmd == 'vi':
+            ui.write(_(" No commit editor set and can't find vi in PATH\n"))
+            ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
+        else:
+            ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
+            ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
+            problems += 1
+
+    # check username
+    ui.status(_("Checking username...\n"))
+    user = os.environ.get("HGUSER")
+    if user is None:
+        user = ui.config("ui", "username")
+    if user is None:
+        user = os.environ.get("EMAIL")
+    if not user:
+        ui.warn(" ")
+        ui.username()
+        ui.write(_(" (specify a username in your .hgrc file)\n"))
+
+    if not problems:
+        ui.status(_("No problems detected\n"))
+    else:
+        ui.write(_("%s problems detected,"
+                   " please check your install!\n") % problems)
+
+    return problems
+
 def debugrename(ui, repo, file1, *pats, **opts):
     """dump rename information"""
 
@@ -843,6 +1008,10 @@ def diff(ui, repo, *pats, **opts):
 
     Differences between files are shown using the unified diff format.
 
+    NOTE: diff may generate unexpected results for merges, as it will
+    default to comparing against the working directory's first parent
+    changeset if no revisions are specified.
+
     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
@@ -866,7 +1035,10 @@ def export(ui, repo, *changesets, **opts
     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.
+    changeset hash, parent(s) and commit comment.
+
+    NOTE: export may generate unexpected diff output for merge changesets,
+    as it will compare the merge changeset against its first parent only.
 
     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:
@@ -1141,6 +1313,26 @@ def help_(ui, name=None, with_version=Fa
             else:
                 ui.write(' %-*s   %s\n' % (m, f, h[f]))
 
+    def helptopic(name):
+        v = None
+        for i in help.helptable:
+            l = i.split('|')
+            if name in l:
+                v = i
+                header = l[-1]
+        if not v:
+            raise UnknownCommand(name)
+
+        # description
+        doc = help.helptable[v]
+        if not doc:
+            doc = _("(No help text available)")
+        if callable(doc):
+            doc = doc()
+
+        ui.write("%s\n" % header)
+        ui.write("%s\n" % doc.rstrip())
+
     def helpext(name):
         try:
             mod = findext(name)
@@ -1163,10 +1355,16 @@ def help_(ui, name=None, with_version=Fa
         helplist(modcmds.has_key)
 
     if name and name != 'shortlist':
-        try:
-            helpcmd(name)
-        except UnknownCommand:
-            helpext(name)
+        i = None
+        for f in (helpcmd, helptopic, helpext):
+            try:
+                f(name)
+                i = None
+                break
+            except UnknownCommand, inst:
+                i = inst
+        if i:
+            raise i
 
     else:
         # program name
@@ -1438,6 +1636,12 @@ def log(ui, repo, *pats, **opts):
     non-trivial parents, user, date and time, and a summary for each
     commit. When the -v/--verbose switch is used, the list of changed
     files and full commit message is shown.
+
+    NOTE: log -p may generate unexpected diff output for merge
+    changesets, as it will compare the merge changeset against its
+    first parent only. Also, the files: list will only reflect files
+    that are different from BOTH parents.
+
     """
 
     get = util.cachefunc(lambda r: repo.changectx(r).changeset())
@@ -1489,7 +1693,12 @@ def log(ui, repo, *pats, **opts):
             return ncache[fn].get(dcache[1][fn])
         return None
 
-    displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
+    df = False
+    if opts["date"]:
+        df = util.matchdate(opts["date"])
+
+
+    displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
     for st, rev, fns in changeiter:
         if st == 'add':
             changenode = repo.changelog.node(rev)
@@ -1500,6 +1709,11 @@ def log(ui, repo, *pats, **opts):
             if opts['only_merges'] and len(parents) != 2:
                 continue
 
+            if df:
+                changes = get(rev)
+                if not df(changes[2][0]):
+                    continue
+
             if opts['keyword']:
                 changes = get(rev)
                 miss = 0
@@ -1799,12 +2013,14 @@ def remove(ui, repo, *pats, **opts):
 
     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.  If invoked with --after,
     files that have been manually deleted are marked as removed.
 
+    This command schedules the files to be removed at the next commit.
+    To undo a remove before that, see hg revert.
+
     Modified files and added files are not removed by default.  To
     remove them, use the -f/--force option.
     """
@@ -1852,7 +2068,8 @@ def rename(ui, repo, *pats, **opts):
     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.
+    This command takes effect in the next commit. To undo a rename
+    before that, see hg revert.
     """
     wlock = repo.wlock(0)
     errs, copied = docopy(ui, repo, pats, opts, wlock)
@@ -1871,8 +2088,9 @@ def revert(ui, repo, *pats, **opts):
     With no revision specified, revert the named files or directories
     to the contents they had in the parent of the working directory.
     This restores the contents of the affected files to an unmodified
-    state.  If the working directory has two parents, you must
-    explicitly specify the revision to revert to.
+    state and unschedules adds, removes, copies, and renames. If the
+    working directory has two parents, you must explicitly specify the
+    revision to revert to.
 
     Modified files are saved with a .orig suffix before reverting.
     To disable these backups, use --no-backup.
@@ -1895,6 +2113,11 @@ def revert(ui, repo, *pats, **opts):
     If no arguments are given, no files are reverted.
     """
 
+    if opts["date"]:
+        if opts["rev"]:
+            raise util.Abort(_("you can't specify a revision and a date"))
+        opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
+
     if not pats and not opts['all']:
         raise util.Abort(_('no files or directories specified; '
                            'use --all to revert the whole repo'))
@@ -2120,6 +2343,11 @@ def status(ui, repo, *pats, **opts):
     files that match are shown.  Files that are clean or ignored, are
     not listed unless -c (clean), -i (ignored) or -A is given.
 
+    NOTE: status may appear to disagree with diff if permissions have
+    changed or a merge has occurred. The standard diff format does not
+    report permission changes and diff only reports changes relative
+    to one merge parent.
+
     If one revision is given, it is used as the base revision.
     If two revisions are given, the difference between them is shown.
 
@@ -2249,7 +2477,7 @@ def unbundle(ui, repo, fname, **opts):
     modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
     return postincoming(ui, repo, modheads, opts['update'])
 
-def update(ui, repo, node=None, clean=False, branch=None):
+def update(ui, repo, node=None, clean=False, branch=None, date=None):
     """update or merge working directory
 
     Update the working directory to the specified revision.
@@ -2264,6 +2492,11 @@ def update(ui, repo, node=None, clean=Fa
     By default, update will refuse to run if doing so would require
     merging or discarding local changes.
     """
+    if date:
+        if node:
+            raise util.Abort(_("you can't specify a revision and a date"))
+        node = cmdutil.finddate(ui, repo, date)
+
     node = _lookup(repo, node, branch)
     if clean:
         return hg.clean(repo, node)
@@ -2354,10 +2587,7 @@ walkopts = [
 ]
 
 table = {
-    "^add":
-        (add,
-         walkopts + dryrunopts,
-         _('hg add [OPTION]... [FILE]...')),
+    "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
     "addremove":
         (addremove,
          [('s', 'similarity', '',
@@ -2444,6 +2674,7 @@ table = {
         (debugcomplete,
          [('o', 'options', None, _('show the command options'))],
          _('debugcomplete [-o] CMD')),
+    "debuginstall": (debuginstall, [], _('debuginstall')),
     "debugrebuildstate":
         (debugrebuildstate,
          [('r', 'rev', '', _('revision to rebuild to'))],
@@ -2451,12 +2682,15 @@ table = {
     "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
     "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
     "debugstate": (debugstate, [], _('debugstate')),
+    "debugdate":
+        (debugdate,
+         [('e', 'extended', None, _('try extended date formats'))],
+         _('debugdate [-e] DATE [RANGE]')),
     "debugdata": (debugdata, [], _('debugdata FILE REV')),
     "debugindex": (debugindex, [], _('debugindex FILE')),
     "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
     "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
-    "debugwalk":
-        (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
+    "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
     "^diff":
         (diff,
          [('r', 'rev', [], _('revision')),
@@ -2545,6 +2779,7 @@ table = {
            _('follow changeset history, or file history across copies and renames')),
           ('', 'follow-first', None,
            _('only follow the first parent of merge changesets')),
+          ('d', 'date', '', _('show revs matching date spec')),
           ('C', 'copies', None, _('show copied files')),
           ('k', 'keyword', [], _('search for a keyword')),
           ('l', 'limit', '', _('limit number of changes displayed')),
@@ -2624,6 +2859,7 @@ table = {
     "^revert":
         (revert,
          [('a', 'all', None, _('revert all changes when no arguments given')),
+          ('d', 'date', '', _('tipmost revision matching date')),
           ('r', 'rev', '', _('revision to revert to')),
           ('', 'no-backup', None, _('do not save backup copies of files')),
          ] + walkopts + dryrunopts,
@@ -2694,14 +2930,15 @@ table = {
         (update,
          [('b', 'branch', '',
            _('checkout the head of a specific branch (DEPRECATED)')),
-          ('C', 'clean', None, _('overwrite locally modified files'))],
+          ('C', 'clean', None, _('overwrite locally modified files')),
+          ('d', 'date', '', _('tipmost revision matching date'))],
          _('hg update [-C] [REV]')),
     "verify": (verify, [], _('hg verify')),
     "version": (version_, [], _('hg version')),
 }
 
 norepo = ("clone init version help debugancestor debugcomplete debugdata"
-          " debugindex debugindexdot")
+          " debugindex debugindexdot debugdate debuginstall")
 optionalrepo = ("paths serve showconfig")
 
 def findpossible(ui, cmd):
@@ -2905,11 +3142,7 @@ def dispatch(args):
 
         try:
             if options['cwd']:
-                try:
-                    os.chdir(options['cwd'])
-                except OSError, inst:
-                    raise util.Abort('%s: %s' %
-                                     (options['cwd'], inst.strerror))
+                os.chdir(options['cwd'])
 
             u.updateopts(options["verbose"], options["debug"], options["quiet"],
                          not options["noninteractive"], options["traceback"],
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -384,7 +384,10 @@ class workingctx(changectx):
         for i, l in (("a", added), ("m", modified), ("u", unknown)):
             for f in l:
                 man[f] = man.get(copied.get(f, f), nullid) + i
-                man.set(f, util.is_exec(self._repo.wjoin(f), man.execf(f)))
+                try:
+                    man.set(f, util.is_exec(self._repo.wjoin(f), man.execf(f)))
+                except OSError:
+                    pass
 
         for f in deleted + removed:
             if f in man:
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -286,13 +286,11 @@ class dirstate(object):
 
     def rebuild(self, parent, files):
         self.clear()
-        umask = os.umask(0)
-        os.umask(umask)
         for f in files:
             if files.execf(f):
-                self.map[f] = ('n', ~umask, -1, 0)
+                self.map[f] = ('n', 0777, -1, 0)
             else:
-                self.map[f] = ('n', ~umask & 0666, -1, 0)
+                self.map[f] = ('n', 0666, -1, 0)
         self.pl = (parent, nullid)
         self.markdirty()
 
new file mode 100644
--- /dev/null
+++ b/mercurial/help.py
@@ -0,0 +1,147 @@
+# help.py - help data for mercurial
+#
+# Copyright 2006 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+helptable = {
+    "dates|Date Formats":
+    r'''
+    Some commands (backout, commit, tag) allow the user to specify a date.
+    Many date formats are acceptible. Here are some examples:
+
+    "Wed Dec 6 13:18:29 2006" (local timezone assumed)
+    "Dec 6 13:18 -0600" (year assumed, time offset provided)
+    "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000)
+    "Dec 6" (midnight)
+    "13:18" (today assumed)
+    "3:39" (3:39AM assumed)
+    "3:39pm" (15:39)
+    "2006-12-6 13:18:29" (ISO 8601 format)
+    "2006-12-6 13:18"
+    "2006-12-6"
+    "12-6"
+    "12/6"
+    "12/6/6" (Dec 6 2006)
+    "" (Jan 1 00:00:00 1970 UTC)
+
+    Lastly, there is Mercurial's internal format:
+
+    "1165432709 0" (Wed Dec 6 13:18:29 2006 UTC)
+
+    This is the internal representation format for dates. unixtime is
+    the number of seconds since the epoch (1970-01-01 00:00 UTC). offset
+    is the offset of the local timezone, in seconds west of UTC (negative
+    if the timezone is east of UTC).
+    ''',
+
+    'environment|env|Environment Variables':
+    r'''
+HGEDITOR::
+    This is the name of the editor to use when committing. Defaults to the
+    value of EDITOR.
+
+    (deprecated, use .hgrc)
+
+HGENCODING::
+    This overrides the default locale setting detected by Mercurial.
+    This setting is used to convert data including usernames,
+    changeset descriptions, tag names, and branches. This setting can
+    be overridden with the --encoding command-line option.
+
+HGENCODINGMODE::
+    This sets Mercurial's behavior for handling unknown characters
+    while transcoding user inputs. The default is "strict", which
+    causes Mercurial to abort if it can't translate a character. Other
+    settings include "replace", which replaces unknown characters, and
+    "ignore", which drops them. This setting can be overridden with
+    the --encodingmode command-line option.
+
+HGMERGE::
+    An executable to use for resolving merge conflicts. The program
+    will be executed with three arguments: local file, remote file,
+    ancestor file.
+
+    The default program is "hgmerge", which is a shell script provided
+    by Mercurial with some sensible defaults.
+
+    (deprecated, use .hgrc)
+
+HGRCPATH::
+    A list of files or directories to search for hgrc files.  Item
+    separator is ":" on Unix, ";" on Windows.  If HGRCPATH is not set,
+    platform default search path is used.  If empty, only .hg/hgrc of
+    current repository is read.
+
+    For each element in path, if a directory, all entries in directory
+    ending with ".rc" are added to path.  Else, element itself is
+    added to path.
+
+HGUSER::
+    This is the string used for the author of a commit.
+
+    (deprecated, use .hgrc)
+
+EMAIL::
+    If HGUSER is not set, this will be used as the author for a commit.
+
+LOGNAME::
+    If neither HGUSER nor EMAIL is set, LOGNAME will be used (with
+    '@hostname' appended) as the author value for a commit.
+
+EDITOR::
+    This is the name of the editor used in the hgmerge script. It will be
+    used for commit messages if HGEDITOR isn't set. Defaults to 'vi'.
+
+PYTHONPATH::
+    This is used by Python to find imported modules and may need to be set
+    appropriately if Mercurial is not installed system-wide.
+    ''',
+
+    "patterns|File Name Patterns": r'''
+    Mercurial accepts several notations for identifying one or more
+    files at a time.
+
+    By default, Mercurial treats filenames as shell-style extended
+    glob patterns.
+
+    Alternate pattern notations must be specified explicitly.
+
+    To use a plain path name without any pattern matching, start a
+    name with "path:".  These path names must match completely, from
+    the root of the current repository.
+
+    To use an extended glob, start a name with "glob:".  Globs are
+    rooted at the current directory; a glob such as "*.c" will match
+    files ending in ".c" in the current directory only.
+
+    The supported glob syntax extensions are "**" to match any string
+    across path separators, and "{a,b}" to mean "a or b".
+
+    To use a Perl/Python regular expression, start a name with "re:".
+    Regexp pattern matching is anchored at the root of the repository.
+
+    Plain examples:
+
+    path:foo/bar   a name bar in a directory named foo in the root of
+                   the repository
+    path:path:name a file or directory named "path:name"
+
+    Glob examples:
+
+    glob:*.c       any name ending in ".c" in the current directory
+    *.c            any name ending in ".c" in the current directory
+    **.c           any name ending in ".c" in the current directory, or
+                   any subdirectory
+    foo/*.c        any name ending in ".c" in the directory foo
+    foo/**.c       any name ending in ".c" in the directory foo, or any
+                   subdirectory
+
+    Regexp examples:
+
+    re:.*\.c$      any name ending in ".c", anywhere in the repository
+
+''',
+}
+
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -103,6 +103,7 @@ def clone(ui, source, dest=None, pull=Fa
 
     if dest is None:
         dest = defaultdest(source)
+        ui.status(_("destination directory: %s\n") % dest)
 
     def localpath(path):
         if path.startswith('file://'):
--- a/mercurial/hgweb/server.py
+++ b/mercurial/hgweb/server.py
@@ -201,8 +201,8 @@ def create_server(ui, repo):
             self.reqmaker = wsgiapplication(self.make_handler)
             self.daemon_threads = True
 
-            addr, port = self.socket.getsockname()
-            if addr == '0.0.0.0':
+            addr, port = self.socket.getsockname()[:2]
+            if addr in ('0.0.0.0', '::'):
                 addr = socket.gethostname()
             else:
                 try:
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -100,6 +100,10 @@ class localrepository(repo.repository):
         self.manifest = manifest.manifest(self.sopener, v)
         self.changelog = changelog.changelog(self.sopener, v)
 
+        fallback = self.ui.config('ui', 'fallbackencoding')
+        if fallback:
+            util._fallbackencoding = fallback
+
         # the changelog might not have the inline index flag
         # on.  If the format of the changelog is the same as found in
         # .hgrc, apply any flags found in the .hgrc as well.
@@ -340,12 +344,7 @@ class localrepository(repo.repository):
                 self.nodetagscache.setdefault(n, []).append(t)
         return self.nodetagscache.get(node, [])
 
-    def branchtags(self):
-        if self.branchcache != None:
-            return self.branchcache
-
-        self.branchcache = {} # avoid recursion in changectx
-
+    def _branchtags(self):
         partial, last, lrev = self._readbranchcache()
 
         tiprev = self.changelog.count() - 1
@@ -353,6 +352,15 @@ class localrepository(repo.repository):
             self._updatebranchcache(partial, lrev+1, tiprev+1)
             self._writebranchcache(partial, self.changelog.tip(), tiprev)
 
+        return partial
+
+    def branchtags(self):
+        if self.branchcache is not None:
+            return self.branchcache
+
+        self.branchcache = {} # avoid recursion in changectx
+        partial = self._branchtags()
+
         # the branch cache is stored on disk as UTF-8, but in the local
         # charset internally
         for k, v in partial.items():
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -17,8 +17,10 @@ from demandload import *
 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
 demandload(globals(), "os threading time calendar ConfigParser locale")
 
-_encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding()
+_encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \
+            or "ascii"
 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
+_fallbackencoding = 'ISO-8859-1'
 
 def tolocal(s):
     """
@@ -30,10 +32,12 @@ def tolocal(s):
     using UTF-8, then Latin-1, and failing that, we use UTF-8 and
     replace unknown characters.
     """
-    for e in "utf-8 latin1".split():
+    for e in ('UTF-8', _fallbackencoding):
         try:
             u = s.decode(e) # attempt strict decoding
             return u.encode(_encoding, "replace")
+        except LookupError, k:
+            raise Abort(_("%s, please check your locale settings") % k)
         except UnicodeDecodeError:
             pass
     u = s.decode("utf-8", "replace") # last ditch
@@ -53,7 +57,9 @@ def fromlocal(s):
         return s.decode(_encoding, _encodingmode).encode("utf-8")
     except UnicodeDecodeError, inst:
         sub = s[max(0, inst.start-10):inst.start+10]
-        raise Abort("decoding near '%s': %s!\n" % (sub, inst))
+        raise Abort("decoding near '%s': %s!" % (sub, inst))
+    except LookupError, k:
+        raise Abort(_("%s, please check your locale settings") % k)
 
 def locallen(s):
     """Find the length in characters of a local string"""
@@ -69,11 +75,41 @@ def localsub(s, a, b=None):
         return u.encode(_encoding, _encodingmode)
     except UnicodeDecodeError, inst:
         sub = s[max(0, inst.start-10), inst.start+10]
-        raise Abort("decoding near '%s': %s!\n" % (sub, inst))
+        raise Abort(_("decoding near '%s': %s!\n") % (sub, inst))
 
 # used by parsedate
-defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
-                      '%a %b %d %H:%M:%S %Y')
+defaultdateformats = (
+    '%Y-%m-%d %H:%M:%S',
+    '%Y-%m-%d %I:%M:%S%p',
+    '%Y-%m-%d %H:%M',
+    '%Y-%m-%d %I:%M%p',
+    '%Y-%m-%d',
+    '%m-%d',
+    '%m/%d',
+    '%m/%d/%y',
+    '%m/%d/%Y',
+    '%a %b %d %H:%M:%S %Y',
+    '%a %b %d %I:%M:%S%p %Y',
+    '%b %d %H:%M:%S %Y',
+    '%b %d %I:%M:%S%p %Y',
+    '%b %d %H:%M:%S',
+    '%b %d %I:%M:%S%p',
+    '%b %d %H:%M',
+    '%b %d %I:%M%p',
+    '%b %d %Y',
+    '%b %d',
+    '%H:%M:%S',
+    '%I:%M:%SP',
+    '%H:%M',
+    '%I:%M%p',
+)
+
+extendeddateformats = defaultdateformats + (
+    "%Y",
+    "%Y-%m",
+    "%b",
+    "%b %Y",
+    )
 
 class SignalInterrupt(Exception):
     """Exception raised on SIGTERM and SIGHUP."""
@@ -1069,21 +1105,31 @@ def datestr(date=None, format='%a %b %d 
         s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
     return s
 
-def strdate(string, format='%a %b %d %H:%M:%S %Y'):
+def strdate(string, format, defaults):
     """parse a localized time string and return a (unixtime, offset) tuple.
     if the string cannot be parsed, ValueError is raised."""
-    def hastimezone(string):
-        return (string[-4:].isdigit() and
-               (string[-5] == '+' or string[-5] == '-') and
-               string[-6].isspace())
+    def timezone(string):
+        tz = string.split()[-1]
+        if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
+            tz = int(tz)
+            offset = - 3600 * (tz / 100) - 60 * (tz % 100)
+            return offset
+        if tz == "GMT" or tz == "UTC":
+            return 0
+        return None
 
     # NOTE: unixtime = localunixtime + offset
-    if hastimezone(string):
-        date, tz = string[:-6], string[-5:]
-        tz = int(tz)
-        offset = - 3600 * (tz / 100) - 60 * (tz % 100)
-    else:
-        date, offset = string, None
+    offset, date = timezone(string), string
+    if offset != None:
+        date = " ".join(string.split()[:-1])
+
+    # add missing elements from defaults
+    for part in defaults:
+        found = [True for p in part if ("%"+p) in format]
+        if not found:
+            date += "@" + defaults[part]
+            format += "@%" + part[0]
+
     timetuple = time.strptime(date, format)
     localunixtime = int(calendar.timegm(timetuple))
     if offset is None:
@@ -1094,36 +1140,98 @@ def strdate(string, format='%a %b %d %H:
         unixtime = localunixtime + offset
     return unixtime, offset
 
-def parsedate(string, formats=None):
+def parsedate(string, formats=None, defaults=None):
     """parse a localized time string and return a (unixtime, offset) tuple.
     The date may be a "unixtime offset" string or in one of the specified
     formats."""
+    if not string:
+        return 0, 0
     if not formats:
         formats = defaultdateformats
+    string = string.strip()
     try:
         when, offset = map(int, string.split(' '))
     except ValueError:
+        # fill out defaults
+        if not defaults:
+            defaults = {}
+        now = makedate()
+        for part in "d mb yY HI M S".split():
+            if part not in defaults:
+                if part[0] in "HMS":
+                    defaults[part] = "00"
+                elif part[0] in "dm":
+                    defaults[part] = "1"
+                else:
+                    defaults[part] = datestr(now, "%" + part[0], False)
+
         for format in formats:
             try:
-                when, offset = strdate(string, format)
+                when, offset = strdate(string, format, defaults)
             except ValueError:
                 pass
             else:
                 break
         else:
-            raise ValueError(_('invalid date: %r '
-                               'see hg(1) manual page for details')
-                             % string)
+            raise Abort(_('invalid date: %r ') % string)
     # validate explicit (probably user-specified) date and
     # time zone offset. values must fit in signed 32 bits for
     # current 32-bit linux runtimes. timezones go from UTC-12
     # to UTC+14
     if abs(when) > 0x7fffffff:
-        raise ValueError(_('date exceeds 32 bits: %d') % when)
+        raise Abort(_('date exceeds 32 bits: %d') % when)
     if offset < -50400 or offset > 43200:
-        raise ValueError(_('impossible time zone offset: %d') % offset)
+        raise Abort(_('impossible time zone offset: %d') % offset)
     return when, offset
 
+def matchdate(date):
+    """Return a function that matches a given date match specifier
+
+    Formats include:
+
+    '{date}' match a given date to the accuracy provided
+
+    '<{date}' on or before a given date
+
+    '>{date}' on or after a given date
+
+    """
+
+    def lower(date):
+        return parsedate(date, extendeddateformats)[0]
+
+    def upper(date):
+        d = dict(mb="12", HI="23", M="59", S="59")
+        for days in "31 30 29".split():
+            try:
+                d["d"] = days
+                return parsedate(date, extendeddateformats, d)[0]
+            except:
+                pass
+        d["d"] = "28"
+        return parsedate(date, extendeddateformats, d)[0]
+
+    if date[0] == "<":
+        when = upper(date[1:])
+        return lambda x: x <= when
+    elif date[0] == ">":
+        when = lower(date[1:])
+        return lambda x: x >= when
+    elif date[0] == "-":
+        try:
+            days = int(date[1:])
+        except ValueError:
+            raise Abort(_("invalid day spec: %s") % date[1:])
+        when = makedate()[0] - days * 3600 * 24
+        return lambda x: x >= when
+    elif " to " in date:
+        a, b = date.split(" to ")
+        start, stop = lower(a), upper(b)
+        return lambda x: x >= start and x <= stop
+    else:
+        start, stop = lower(date), upper(date)
+        return lambda x: x >= start and x <= stop
+
 def shortuser(user):
     """Return a short representation of a user name or email address."""
     f = user.find('@')
index d050be8ebfef9771e5e1dffdd1cb98be3a51765a..b3b9b25304ced2aba95f885f08b3117190181ea0
GIT binary patch
literal 548
zc$@(w0^9vaM=>x$T4*^jL0KkKS=^;|T>t<R|Nq~c)k9RifBJ+Fkxc*Z{%Hg_kPS;>
zwXP9CR%FN_O|~!rnPAKkrB72vjUJ;Q(?HO`gAmXLngB8~0ie@BGBjxQ8aAezQ`1mt
zrqf1)Q)nd6Xvhs4P|yGYri~2%&;Sg8XaEC1NlGE=X`mV!0B9OCG-w8Z0001JVl>DB
zp_4!ctjBG*f@HCcrrH`3tggLra?CbR&`zKf(83tRS~1jATOqm~J8ZHXU9s~uoY68l
z6SR1lD4$mp!4?QuLm#Uim)2BOzKB)?LV(#DkyH#Gaov8nl24(QC^+9bzmSmHMyo4C
z6}8f$JnVzBEtI+-31kA)_USxWXl)Lr4%*s8aEDDw1}Vy7xReHfVI~uGaxA+{BnlTS
z@DQ1pK~9+_fIy*E6OKqQ%w}`dLys9pR#n#+GP|;2q9Ent16oKDzJk^{pi+(nw_rqO
z>4I~p=S&x(M97gNLW+jB`NVhFv{cK&1Uj|^D(ZvLfElum+DA<37Q7)vAkArD`(}NY
z+K}?6c7c<a;_Vs8VWpuc+z`K2D5829i|#;;yhsT_2B)pXRcP@KP|j3sBk2!F!3nwM
zLeP%XF!%9{NDq$bBcw@1svqp40z8jYaH5m)SrSzB`SmbzAES{`K{{e!{RKxV-YilW
mFce%_(N#5&P!=gJf%#OUo-b~y1{mo-#oUoj6eKq(U6()xH14nf
--- a/tests/test-clone-failure.out
+++ b/tests/test-clone-failure.out
@@ -8,4 +8,5 @@ abort: destination '../a' already exists
 255
 abort: repository a not found!
 255
+destination directory: q
 abort: destination 'q' already exists
--- a/tests/test-clone.out
+++ b/tests/test-clone.out
@@ -11,5 +11,6 @@ checking manifests
 crosschecking files in changesets and manifests
 checking files
 1 files, 1 changesets, 1 total revisions
+destination directory: a
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 a
--- a/tests/test-commit.out
+++ b/tests/test-commit.out
@@ -2,18 +2,13 @@
 abort: impossible time zone offset: 4444444
 transaction abort!
 rollback completed
-abort: invalid date: '1\t15.1' see hg(1) manual page for details
-transaction abort!
-rollback completed
-abort: invalid date: 'foo bar' see hg(1) manual page for details
+abort: invalid date: '1\t15.1' 
 transaction abort!
 rollback completed
-abort: invalid date: ' 1 4444' see hg(1) manual page for details
+abort: invalid date: 'foo bar' 
 transaction abort!
 rollback completed
-abort: date exceeds 32 bits: 111111111111
-transaction abort!
-rollback completed
+nothing changed
 % partial commit test
 trouble committing bar!
 abort: No such file or directory: .../test/bar
--- a/tests/test-debugcomplete.out
+++ b/tests/test-debugcomplete.out
@@ -61,8 +61,10 @@ debugcheckstate
 debugcomplete
 debugconfig
 debugdata
+debugdate
 debugindex
 debugindexdot
+debuginstall
 debugrawcommit
 debugrebuildstate
 debugrename
--- a/tests/test-diffdir
+++ b/tests/test-diffdir
@@ -13,3 +13,8 @@ hg diff --nodates -r tip
 
 echo foo > a
 hg diff --nodates
+
+hg diff -r ""
+hg diff -r tip -r ""
+
+true
--- a/tests/test-diffdir.out
+++ b/tests/test-diffdir.out
@@ -18,3 +18,5 @@ diff -r acd8075edac9 b
 +++ b/b
 @@ -0,0 +1,1 @@
 +123
+abort: Ambiguous identifier!
+abort: Ambiguous identifier!
--- a/tests/test-encoding
+++ b/tests/test-encoding
@@ -16,15 +16,18 @@ EOF
 echo % should fail with encoding error
 echo "plain old ascii" > a
 hg st
-HGENCODING=ascii hg ci -l latin-1 -d "0 0"
+HGENCODING=ascii hg ci -l latin-1 -d "1000000 0"
 
 echo % these should work
 echo "latin-1" > a
-HGENCODING=latin-1 hg ci -l latin-1 -d "0 0"
+HGENCODING=latin-1 hg ci -l latin-1 -d "1000000 0"
 echo "utf-8" > a
-HGENCODING=utf-8 hg ci -l utf-8 -d "0 0"
+HGENCODING=utf-8 hg ci -l utf-8 -d "1000000 0"
 
-HGENCODING=latin-1 hg tag -d "0 0" `cat latin-1-tag`
+HGENCODING=latin-1 hg tag -d "1000000 0" `cat latin-1-tag`
+cp latin-1-tag .hg/branch
+HGENCODING=latin-1 hg ci -d "1000000 0" -m 'latin1 branch'
+rm .hg/branch
 
 echo % ascii
 hg --encoding ascii log
@@ -38,3 +41,18 @@ echo % latin-1
 HGENCODING=latin-1 hg tags
 echo % utf-8
 HGENCODING=utf-8 hg tags
+echo % ascii
+HGENCODING=ascii hg branches
+echo % latin-1
+HGENCODING=latin-1 hg branches
+echo % utf-8
+HGENCODING=utf-8 hg branches
+
+echo '[ui]' >> .hg/hgrc
+echo 'fallbackencoding = koi8-r' >> .hg/hgrc
+echo % utf-8
+HGENCODING=utf-8 hg log
+
+HGENCODING=dolphin hg log
+
+exit 0
--- a/tests/test-encoding.out
+++ b/tests/test-encoding.out
@@ -1,7 +1,7 @@
 adding changesets
 adding manifests
 adding file changes
-added 1 changesets with 1 changes to 1 files
+added 2 changesets with 2 changes to 1 files
 (run 'hg update' to get a working copy)
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % should fail with encoding error
@@ -10,85 +10,158 @@ M a
 ? latin-1-tag
 ? utf-8
 abort: decoding near ' encoded: é': 'ascii' codec can't decode byte 0xe9 in position 20: ordinal not in range(128)!
-
 transaction abort!
 rollback completed
 % these should work
 % ascii
-changeset:   3:5edfc7acb541
+changeset:   5:db5520b4645f
+branch:      ?
 tag:         tip
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
-summary:     Added tag ? for changeset 91878608adb3
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     latin1 branch
 
-changeset:   2:91878608adb3
+changeset:   4:9cff3c980b58
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     Added tag ? for changeset 770b9b11621d
+
+changeset:   3:770b9b11621d
 tag:         ?
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
+date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     utf-8 e' encoded: ?
 
-changeset:   1:6355cacf842e
+changeset:   2:0572af48b948
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
+date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     latin-1 e' encoded: ?
 
-changeset:   0:60aad1dd20a9
+changeset:   1:0e5b7e3f9c4a
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
-summary:     latin-1 e': ?
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     koi8-r: ????? = u'\u0440\u0442\u0443\u0442\u044c'
+
+changeset:   0:1e78a93102a3
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     latin-1 e': ? = u'\xe9'
 
 % latin-1
-changeset:   3:5edfc7acb541
+changeset:   5:db5520b4645f
+branch:      é
 tag:         tip
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
-summary:     Added tag é for changeset 91878608adb3
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     latin1 branch
 
-changeset:   2:91878608adb3
+changeset:   4:9cff3c980b58
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     Added tag é for changeset 770b9b11621d
+
+changeset:   3:770b9b11621d
 tag:         é
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
+date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     utf-8 e' encoded: é
 
-changeset:   1:6355cacf842e
+changeset:   2:0572af48b948
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
+date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     latin-1 e' encoded: é
 
-changeset:   0:60aad1dd20a9
+changeset:   1:0e5b7e3f9c4a
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
-summary:     latin-1 e': é
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     koi8-r: ÒÔÕÔØ = u'\u0440\u0442\u0443\u0442\u044c'
+
+changeset:   0:1e78a93102a3
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     latin-1 e': é = u'\xe9'
 
 % utf-8
-changeset:   3:5edfc7acb541
+changeset:   5:db5520b4645f
+branch:      é
 tag:         tip
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
-summary:     Added tag é for changeset 91878608adb3
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     latin1 branch
 
-changeset:   2:91878608adb3
+changeset:   4:9cff3c980b58
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     Added tag é for changeset 770b9b11621d
+
+changeset:   3:770b9b11621d
 tag:         é
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
+date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     utf-8 e' encoded: é
 
-changeset:   1:6355cacf842e
+changeset:   2:0572af48b948
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
+date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     latin-1 e' encoded: é
 
-changeset:   0:60aad1dd20a9
+changeset:   1:0e5b7e3f9c4a
 user:        test
-date:        Thu Jan 01 00:00:00 1970 +0000
-summary:     latin-1 e': é
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     koi8-r: ÒÔÕÔØ = u'\u0440\u0442\u0443\u0442\u044c'
+
+changeset:   0:1e78a93102a3
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     latin-1 e': é = u'\xe9'
 
 % ascii
-tip                                3:5edfc7acb541
-?                                  2:91878608adb3
+tip                                5:db5520b4645f
+?                                  3:770b9b11621d
+% latin-1
+tip                                5:db5520b4645f
+é                                  3:770b9b11621d
+% utf-8
+tip                                5:db5520b4645f
+é                                  3:770b9b11621d
+% ascii
+?                              5:db5520b4645f
 % latin-1
-tip                                3:5edfc7acb541
-é                                  2:91878608adb3
+é                              5:db5520b4645f
+% utf-8
+é                              5:db5520b4645f
 % utf-8
-tip                                3:5edfc7acb541
-é                                  2:91878608adb3
+changeset:   5:db5520b4645f
+branch:      é
+tag:         tip
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     latin1 branch
+
+changeset:   4:9cff3c980b58
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     Added tag é for changeset 770b9b11621d
+
+changeset:   3:770b9b11621d
+tag:         é
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     utf-8 e' encoded: é
+
+changeset:   2:0572af48b948
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     latin-1 e' encoded: é
+
+changeset:   1:0e5b7e3f9c4a
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     koi8-r: ртуть = u'\u0440\u0442\u0443\u0442\u044c'
+
+changeset:   0:1e78a93102a3
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     latin-1 e': И = u'\xe9'
+
+abort: unknown encoding: dolphin, please check your locale settings
--- a/tests/test-help.out
+++ b/tests/test-help.out
@@ -136,7 +136,8 @@ add the specified files on the next comm
 
     Schedule files to be version controlled and added to the repository.
 
-    The files will be added to the repository at the next commit.
+    The files will be added to the repository at the next commit. To
+    undo an add before that, see hg revert.
 
     If no names are given, add all files in the repository.
 
@@ -152,7 +153,8 @@ add the specified files on the next comm
 
     Schedule files to be version controlled and added to the repository.
 
-    The files will be added to the repository at the next commit.
+    The files will be added to the repository at the next commit. To
+    undo an add before that, see hg revert.
 
     If no names are given, add all files in the repository.
 
@@ -169,6 +171,10 @@ diff repository (or selected files)
 
     Differences between files are shown using the unified diff format.
 
+    NOTE: diff may generate unexpected results for merges, as it will
+    default to comparing against the working directory's first parent
+    changeset if no revisions are specified.
+
     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
@@ -199,6 +205,11 @@ show changed files in the working direct
     files that match are shown.  Files that are clean or ignored, are
     not listed unless -c (clean), -i (ignored) or -A is given.
 
+    NOTE: status may appear to disagree with diff if permissions have
+    changed or a merge has occurred. The standard diff format does not
+    report permission changes and diff only reports changes relative
+    to one merge parent.
+
     If one revision is given, it is used as the base revision.
     If two revisions are given, the difference between them is shown.
 
new file mode 100755
--- /dev/null
+++ b/tests/test-install
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+HGMERGE=merge hg debuginstall
new file mode 100644
--- /dev/null
+++ b/tests/test-install.out
@@ -0,0 +1,8 @@
+Checking encoding (ascii)...
+Checking extensions...
+Checking templates...
+Checking patch...
+Checking merge helper...
+Checking commit editor...
+Checking username...
+No problems detected
--- a/tests/test-log
+++ b/tests/test-log
@@ -14,6 +14,8 @@ hg mv b dir
 hg ci -mc -d '3 0'
 
 hg mv a b
+echo a > d
+hg add d
 hg ci -md -d '4 0'
 
 hg mv dir/b e
@@ -39,6 +41,9 @@ echo foo > foo
 hg ci -Ame2 -d '6 0'
 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r 5
 
+echo '% log -p d'
+hg log -pv d
+
 # log --follow tests
 hg init ../follow
 cd ../follow
--- a/tests/test-log.out
+++ b/tests/test-log.out
@@ -7,13 +7,13 @@ summary:     a
 % -f, directory
 abort: can only follow copies/renames for explicit file names
 % -f, but no args
-changeset:   4:8c1c8408f737
+changeset:   4:b30c444c7c84
 tag:         tip
 user:        test
 date:        Thu Jan 01 00:00:05 1970 +0000
 summary:     e
 
-changeset:   3:c4ba038c90ce
+changeset:   3:16b60bf3f99a
 user:        test
 date:        Thu Jan 01 00:00:04 1970 +0000
 summary:     d
@@ -43,7 +43,7 @@ a
 
 
 % many renames
-changeset:   4:8c1c8408f737
+changeset:   4:b30c444c7c84
 tag:         tip
 user:        test
 date:        Thu Jan 01 00:00:05 1970 +0000
@@ -86,6 +86,21 @@ 0
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 adding foo
 5 e (dir/b)
+% log -p d
+changeset:   3:16b60bf3f99a
+user:        test
+date:        Thu Jan 01 00:00:04 1970 +0000
+files:       a b d
+description:
+d
+
+
+diff -r 21fba396af4c -r 16b60bf3f99a d
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/d	Thu Jan 01 00:00:04 1970 +0000
+@@ -0,0 +1,1 @@
++a
+
 adding base
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding b1
--- a/tests/test-parse-date.out
+++ b/tests/test-parse-date.out
@@ -3,7 +3,7 @@ changeset 3:107ce1ee2b43 backs out chang
 merging with changeset 2:e6c3abc120e7
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
-abort: invalid date: 'should fail' see hg(1) manual page for details
+abort: invalid date: 'should fail' 
 transaction abort!
 rollback completed
 abort: date exceeds 32 bits: 100000000000000000
--- a/tests/test-status
+++ b/tests/test-status
@@ -35,8 +35,5 @@ hg status modified added removed deleted
 hg copy modified copied
 echo "hg status -C:"
 hg status -C
-
-echo "hg status -t:"
-hg status -t
 echo "hg status -A:"
 hg status -A
--- a/tests/test-status.out
+++ b/tests/test-status.out
@@ -108,47 +108,6 @@ A copied
 R removed
 ! deleted
 ? unknown
-hg status -t:
-hg status: option -t not recognized
-hg status [OPTION]... [FILE]...
-
-show changed files in the working directory
-
-    Show status of files in the repository.  If names are given, only
-    files that match are shown.  Files that are clean or ignored, are
-    not listed unless -c (clean), -i (ignored) or -A is given.
-
-    If one revision is given, it is used as the base revision.
-    If two revisions are given, the difference between them is shown.
-
-    The codes used to show the status of files are:
-    M = modified
-    A = added
-    R = removed
-    C = clean
-    ! = deleted, but still tracked
-    ? = not tracked
-    I = ignored (not shown by default)
-      = the previous added file was copied from here
-
-aliases: st
-
-options:
-
- -A --all        show status of all files
- -m --modified   show only modified files
- -a --added      show only added files
- -r --removed    show only removed files
- -d --deleted    show only deleted (but tracked) files
- -c --clean      show only files without changes
- -u --unknown    show only unknown (not tracked) files
- -i --ignored    show ignored files
- -n --no-status  hide status prefix
- -C --copies     show source of copied files
- -0 --print0     end filenames with NUL, for use with xargs
-    --rev        show difference from revision
- -I --include    include names matching the given patterns
- -X --exclude    exclude names matching the given patterns
 hg status -A:
 A added
 A copied