changeset 11493:cc4e2a7ca23f

Merge with stable
author Martin Geisler <mg@aragost.com>
date Fri, 02 Jul 2010 11:30:57 +0200
parents 8b452fe4bf50 (current diff) c37f35d7f2f5 (diff)
children 2347513f562a
files
diffstat 40 files changed, 373 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/mercurial.spec	Mon Jun 21 17:02:48 2010 -0300
+++ b/contrib/mercurial.spec	Fri Jul 02 11:30:57 2010 +0200
@@ -41,7 +41,6 @@
 make install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{_prefix} MANDIR=%{_mandir}
 
 install contrib/hgk          $RPM_BUILD_ROOT%{_bindir}
-install contrib/convert-repo $RPM_BUILD_ROOT%{_bindir}/mercurial-convert-repo
 install contrib/hg-ssh       $RPM_BUILD_ROOT%{_bindir}
 
 bash_completion_dir=$RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
@@ -74,7 +73,6 @@
 %{_bindir}/hg
 %{_bindir}/hgk
 %{_bindir}/hg-ssh
-%{_bindir}/mercurial-convert-repo
 %dir %{_sysconfdir}/bash_completion.d/
 %dir %{_datadir}/zsh/site-functions/
 %dir %{_sysconfdir}/mercurial
--- a/contrib/tcsh_completion	Mon Jun 21 17:02:48 2010 -0300
+++ b/contrib/tcsh_completion	Fri Jul 02 11:30:57 2010 +0200
@@ -2,7 +2,7 @@
 # tcsh completion for Mercurial
 #
 # This file has been auto-generated by tcsh_completion_build.sh for
-# Mercurial Distributed SCM (version 1.3.1+269-5d8125bbbbf4)
+# Mercurial Distributed SCM (version 1.5.4+154-8b452fe4bf50)
 #
 # Copyright (C) 2005 TK Soh.
 #
@@ -32,18 +32,19 @@
   'p/1/(add addremove annotate blame archive \
     backout bisect branch branches bundle \
     cat clone commit ci copy \
-    cp debugancestor debugcheckstate debugcommands debugcomplete \
-    debugdata debugdate debugfsinfo debugindex debugindexdot \
-    debuginstall debugrebuildstate debugrename debugsetparents debugstate \
-    debugsub debugwalk diff export forget \
-    grep heads help identify id \
-    import patch incoming in init \
-    locate log history manifest merge \
-    outgoing out parents paths pull \
-    push recover remove rm rename \
-    mv resolve revert rollback root \
-    serve showconfig debugconfig status st \
-    summary sum tag tags tip \
-    unbundle update up checkout co \
-    verify version)/'
+    cp debugancestor debugbuilddag debugcheckstate debugcommands \
+    debugcomplete debugdag debugdata debugdate debugfsinfo \
+    debugindex debugindexdot debuginstall debugpushkey debugrebuildstate \
+    debugrename debugrevspec debugsetparents debugstate debugsub \
+    debugwalk diff export forget grep \
+    heads help identify id import \
+    patch incoming in init locate \
+    log history manifest merge outgoing \
+    out parents paths pull push \
+    recover remove rm rename mv \
+    resolve revert rollback root serve \
+    showconfig debugconfig status st summary \
+    sum tag tags tip unbundle \
+    update up checkout co verify \
+    version)/'
 
--- a/hgext/bookmarks.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/hgext/bookmarks.py	Fri Jul 02 11:30:57 2010 +0200
@@ -30,7 +30,7 @@
 
 from mercurial.i18n import _
 from mercurial.node import nullid, nullrev, hex, short
-from mercurial import util, commands, repair, extensions, pushkey, hg
+from mercurial import util, commands, repair, extensions, pushkey, hg, url
 import os
 
 def write(repo):
@@ -53,6 +53,13 @@
         for refspec, node in refs.iteritems():
             file.write("%s %s\n" % (hex(node), refspec))
         file.rename()
+
+        # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
+        try:
+            os.utime(repo.sjoin('00changelog.i'), None)
+        except OSError:
+            pass
+
     finally:
         wlock.release()
 
@@ -327,16 +334,15 @@
                             if r:
                                 self.ui.status(_("updating bookmark %s\n") % k)
                             else:
-                                self.ui.warn(_("failed to update bookmark"
-                                                  " %s!\n") % k)
+                                self.ui.warn(_('updating bookmark %s'
+                                               ' failed!\n') % k)
 
             return result
 
-        def addchangegroup(self, source, srctype, url, emptyok=False):
+        def addchangegroup(self, *args, **kwargs):
             parents = self.dirstate.parents()
 
-            result = super(bookmark_repo, self).addchangegroup(
-                source, srctype, url, emptyok)
+            result = super(bookmark_repo, self).addchangegroup(*args, **kwargs)
             if result > 1:
                 # We have more heads than before
                 return result
@@ -445,6 +451,40 @@
 
     return result
 
+def diffbookmarks(ui, repo, remote):
+    ui.status(_("searching for changes\n"))
+
+    lmarks = repo.listkeys('bookmarks')
+    rmarks = remote.listkeys('bookmarks')
+
+    diff = set(rmarks) - set(lmarks)
+    for k in diff:
+        ui.write("   %-25s %s\n" % (k, rmarks[k][:12]))
+
+    if len(diff) <= 0:
+        ui.status(_("no changes found\n"))
+        return 1
+    return 0
+
+def incoming(oldincoming, ui, repo, source="default", **opts):
+    if opts.get('bookmarks'):
+        source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
+        other = hg.repository(hg.remoteui(repo, opts), source)
+        ui.status(_('comparing with %s\n') % url.hidepassword(source))
+        return diffbookmarks(ui, repo, other)
+    else:
+        return oldincoming(ui, repo, source, **opts)
+
+def outgoing(oldoutgoing, ui, repo, dest=None, **opts):
+    if opts.get('bookmarks'):
+        dest = ui.expandpath(dest or 'default-push', dest or 'default')
+        dest, branches = hg.parseurl(dest, opts.get('branch'))
+        other = hg.repository(hg.remoteui(repo, opts), dest)
+        ui.status(_('comparing with %s\n') % url.hidepassword(dest))
+        return diffbookmarks(ui, other, repo)
+    else:
+        return oldoutgoing(ui, repo, dest, **opts)
+
 def uisetup(ui):
     extensions.wrapfunction(repair, "strip", strip)
     if ui.configbool('bookmarks', 'track.current'):
@@ -456,6 +496,12 @@
     entry = extensions.wrapcommand(commands.table, 'push', push)
     entry[1].append(('B', 'bookmark', [],
                      _("bookmark to export")))
+    entry = extensions.wrapcommand(commands.table, 'incoming', incoming)
+    entry[1].append(('B', 'bookmarks', False,
+                     _("compare bookmark")))
+    entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
+    entry[1].append(('B', 'bookmarks', False,
+                     _("compare bookmark")))
 
     pushkey.register('bookmarks', pushbookmark, listbookmarks)
 
--- a/hgext/graphlog.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/hgext/graphlog.py	Fri Jul 02 11:30:57 2010 +0200
@@ -209,6 +209,8 @@
 def get_revs(repo, rev_opt):
     if rev_opt:
         revs = revrange(repo, rev_opt)
+        if len(revs) == 0:
+            return (nullrev, nullrev)
         return (max(revs), min(revs))
     else:
         return (len(repo) - 1, 0)
--- a/hgext/mq.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/hgext/mq.py	Fri Jul 02 11:30:57 2010 +0200
@@ -250,6 +250,7 @@
         self.ui = ui
         self.applied_dirty = 0
         self.series_dirty = 0
+        self.added = []
         self.series_path = "series"
         self.status_path = "status"
         self.guards_path = "guards"
@@ -1017,7 +1018,7 @@
                             _("cannot push to a previous patch: %s") % patch)
                     self.ui.warn(
                         _('qpush: %s is already at the top\n') % patch)
-                    return
+                    return 0
                 pushable, reason = self.pushable(patch)
                 if not pushable:
                     if reason:
@@ -1046,10 +1047,12 @@
 
             if move:
                 try:
-                    del self.full_series[self.full_series.index(patch, start)]
+                    index = self.series.index(patch, start)
+                    fullpatch = self.full_series[index]
+                    del self.full_series[index]
                 except ValueError:
                     raise util.Abort(_("patch '%s' not found") % patch)
-                self.full_series.insert(start, patch)
+                self.full_series.insert(start, fullpatch)
                 self.parse_series()
                 self.series_dirty = 1
 
@@ -1620,7 +1623,7 @@
         if (len(files) > 1 or len(rev) > 1) and patchname:
             raise util.Abort(_('option "-n" not valid when importing multiple '
                                'patches'))
-        added = []
+        self.added = []
         if rev:
             # If mq patches are applied, we can only import revisions
             # that form a linear path to qbase.
@@ -1670,10 +1673,11 @@
                 se = statusentry(n, patchname)
                 self.applied.insert(0, se)
 
-                added.append(patchname)
+                self.added.append(patchname)
                 patchname = None
             self.parse_series()
             self.applied_dirty = 1
+            self.series_dirty = True
 
         for i, filename in enumerate(files):
             if existing:
@@ -1707,13 +1711,10 @@
                 index = self.full_series_end() + i
                 self.full_series[index:index] = [patchname]
             self.parse_series()
+            self.series_dirty = True
             self.ui.warn(_("adding %s to series file\n") % patchname)
-            added.append(patchname)
+            self.added.append(patchname)
             patchname = None
-        self.series_dirty = 1
-        qrepo = self.qrepo()
-        if qrepo:
-            qrepo[None].add(added)
 
 def delete(ui, repo, *patches, **opts):
     """remove patches from queue
@@ -1803,10 +1804,15 @@
     using the --name flag.
     """
     q = repo.mq
-    q.qimport(repo, filename, patchname=opts['name'],
+    try:
+        q.qimport(repo, filename, patchname=opts['name'],
               existing=opts['existing'], force=opts['force'], rev=opts['rev'],
               git=opts['git'])
-    q.save_dirty()
+    finally:
+        q.save_dirty()
+        qrepo = q.qrepo()
+        if qrepo:
+            qrepo[None].add(q.added)
 
     if opts.get('push') and not opts.get('rev'):
         return q.push(repo, None)
--- a/hgext/progress.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/hgext/progress.py	Fri Jul 02 11:30:57 2010 +0200
@@ -51,6 +51,9 @@
 def spacejoin(*args):
     return ' '.join(s for s in args if s)
 
+def shouldprint(ui):
+    return sys.stderr.isatty() or ui.configbool('progress', 'assume-tty')
+
 class progbar(object):
     def __init__(self, ui):
         self.ui = ui
@@ -69,6 +72,8 @@
             default=['topic', 'bar', 'number'])
 
     def show(self, topic, pos, item, unit, total):
+        if not shouldprint(self.ui):
+            return
         termwidth = self.width()
         self.printed = True
         head = ''
@@ -137,9 +142,13 @@
         sys.stderr.flush()
 
     def clear(self):
+        if not shouldprint(self.ui):
+            return
         sys.stderr.write('\r%s\r' % (' ' * self.width()))
 
     def complete(self):
+        if not shouldprint(self.ui):
+            return
         if self.ui.configbool('progress', 'clear-complete', default=True):
             self.clear()
         else:
@@ -177,8 +186,7 @@
     # setconfig('progress', 'disable', 'True') to disable this extension
     if ui.configbool('progress', 'disable'):
         return
-    if ((sys.stderr.isatty() or ui.configbool('progress', 'assume-tty'))
-        and not ui.debugflag and not ui.quiet):
+    if shouldprint(ui) and not ui.debugflag and not ui.quiet:
         # we instantiate one globally shared progress bar to avoid
         # competing progress bars when multiple UI objects get created
         global sharedprog
--- a/hgext/zeroconf/Zeroconf.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/hgext/zeroconf/Zeroconf.py	Fri Jul 02 11:30:57 2010 +0200
@@ -204,6 +204,13 @@
 class BadTypeInNameException(Exception):
 	pass
 
+class BadDomainName(Exception):
+	def __init__(self, pos):
+		Exception.__init__(self, "at position %s" % pos)
+
+class BadDomainNameCircular(BadDomainName):
+	pass
+
 # implementation classes
 
 class DNSEntry(object):
@@ -598,10 +605,10 @@
 					next = off + 1
 				off = ((len & 0x3F) << 8) | ord(self.data[off])
 				if off >= first:
-					raise "Bad domain name (circular) at " + str(off)
+					raise BadDomainNameCircular(off)
 				first = off
 			else:
-				raise "Bad domain name at " + str(off)
+				raise BadDomainName(off)
 
 		if next >= 0:
 			self.offset = next
--- a/i18n/polib.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/i18n/polib.py	Fri Jul 02 11:30:57 2010 +0200
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
+# no-check-code
 #
 # License: MIT (see LICENSE file provided)
 # vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4:
--- a/mercurial/cmdutil.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/cmdutil.py	Fri Jul 02 11:30:57 2010 +0200
@@ -905,7 +905,9 @@
                 if self.buffered:
                     self.header[ctx.rev()] = h
                 else:
-                    self.ui.write(h)
+                    if self.lastheader != h:
+                        self.lastheader = h
+                        self.ui.write(h)
 
             # write changeset metadata, then patch if requested
             key = types['changeset']
--- a/mercurial/commands.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/commands.py	Fri Jul 02 11:30:57 2010 +0200
@@ -2570,6 +2570,10 @@
     head, the other head is merged with by default. Otherwise, an
     explicit revision with which to merge with must be provided.
 
+    To undo an uncommitted merge, use :hg:`update --clean .` which
+    will check out a clean copy of the original merge parent, losing
+    all changes.
+
     Returns 0 on success, 1 if there are unresolved files.
     """
 
@@ -3015,8 +3019,11 @@
 def revert(ui, repo, *pats, **opts):
     """restore individual files or directories to an earlier state
 
-    (Use update -r to check out earlier revisions, revert does not
-    change the working directory parents.)
+    NOTE: This command is most likely not what you are looking for. revert
+    will partially overwrite content in the working directory without changing
+    the working directory parents. Use :hg:`update -r rev` to check out earlier
+    revisions, or :hg:`update --clean .` to undo a merge which has added
+    another parent.
 
     With no revision specified, revert the named files or directories
     to the contents they had in the parent of the working directory.
@@ -3745,7 +3752,8 @@
         for fname in fnames:
             f = url.open(ui, fname)
             gen = changegroup.readbundle(f, fname)
-            modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
+            modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
+                                           lock=lock)
     finally:
         lock.release()
 
--- a/mercurial/context.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/context.py	Fri Jul 02 11:30:57 2010 +0200
@@ -490,12 +490,16 @@
 
         return zip(hist[f][0], hist[f][1].splitlines(True))
 
-    def ancestor(self, fc2):
+    def ancestor(self, fc2, actx=None):
         """
         find the common ancestor file context, if any, of self, and fc2
+
+        If actx is given, it must be the changectx of the common ancestor
+        of self's and fc2's respective changesets.
         """
 
-        actx = self.changectx().ancestor(fc2.changectx())
+        if actx is None:
+            actx = self.changectx().ancestor(fc2.changectx())
 
         # the trivial case: changesets are unrelated, files must be too
         if not actx:
--- a/mercurial/discovery.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/discovery.py	Fri Jul 02 11:30:57 2010 +0200
@@ -280,7 +280,7 @@
             remotemap = remote.branchmap()
             newbranches = branches - set(remotemap)
             if newbranches and not newbranch: # new branch requires --new-branch
-                branchnames = ', '.join("%s" % b for b in newbranches)
+                branchnames = ', '.join(sorted(newbranches))
                 repo.ui.warn(_("abort: push creates "
                                "new remote branches: %s!\n")
                              % branchnames)
--- a/mercurial/help/glossary.txt	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/help/glossary.txt	Fri Jul 02 11:30:57 2010 +0200
@@ -8,7 +8,7 @@
 Branch
     (Noun) A child changeset that has been created from a parent that
     is not a head. These are known as topological branches, see
-    'Branch, topological'.If a topological branch is named, it becomes
+    'Branch, topological'. If a topological branch is named, it becomes
     a named branch. If a topological branch is not named, it becomes
     an anonymous branch. See 'Branch, anonymous' and 'Branch, named'.
 
@@ -55,8 +55,8 @@
     collection of disjoint subsets. A named branch is not necessarily
     a topological branch. If a new named branch is created from the
     head of another named branch, or the default branch, but no
-    further changesets are added to that previous branch, then the new
-    named branch will be a branch in name only.
+    further changesets are added to that previous branch, then that
+    previous branch will be a branch in name only.
 
 Branch tip
     See 'Tip, branch'.
@@ -151,7 +151,7 @@
     consisting of nodes and edges, where nodes correspond to
     changesets and edges imply a parent -> child relation. This graph
     can be visualized by graphical tools such as :hg:`glog`
-    (graphlog). In mercurial, the DAG is limited by the requirement
+    (graphlog). In Mercurial, the DAG is limited by the requirement
     for children to have at most two parents.
 
 Default branch
@@ -322,7 +322,7 @@
     pointing to the data.
 
 Rewriting history
-    See  'History, rewriting'.
+    See 'History, rewriting'.
 
 Root
     A changeset that has only the null changeset as its parent. Most
@@ -342,7 +342,7 @@
 Update
     (Noun) Another synonym of changeset.
 
-    Example:  "I've pushed an update".
+    Example: "I've pushed an update".
 
     (Verb) This term is usually used to describe updating the state of
     the working directory to that of a specific changeset. See
@@ -354,4 +354,4 @@
     See 'Directory, working'.
 
 Working directory parent
-    See  'Parent, working directory'.
+    See 'Parent, working directory'.
--- a/mercurial/help/revsets.txt	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/help/revsets.txt	Fri Jul 02 11:30:57 2010 +0200
@@ -21,7 +21,7 @@
   A DAG range, meaning all changesets that are descendants of x and
   ancestors of y, including x and y themselves. If the first endpoint
   is left out, this is equivalent to ``ancestors(y)``, if the second
-  is left out it is equivalent to ``descendents(x)``.
+  is left out it is equivalent to ``descendants(x)``.
 
   An alternative syntax is ``x..y``.
 
--- a/mercurial/hgweb/protocol.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/hgweb/protocol.py	Fri Jul 02 11:30:57 2010 +0200
@@ -164,7 +164,7 @@
                           urllib.quote(req.env.get('REMOTE_HOST', '')),
                           urllib.quote(req.env.get('REMOTE_USER', '')))
                     try:
-                        ret = repo.addchangegroup(gen, 'serve', url)
+                        ret = repo.addchangegroup(gen, 'serve', url, lock=lock)
                     except util.Abort, inst:
                         sys.stdout.write("abort: %s\n" % inst)
                         ret = 0
--- a/mercurial/hook.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/hook.py	Fri Jul 02 11:30:57 2010 +0200
@@ -98,7 +98,10 @@
         cwd = repo.root
     else:
         cwd = os.getcwd()
-    r = util.system(cmd, environ=env, cwd=cwd)
+    if 'HG_URL' in env and env['HG_URL'].startswith('remote:http'):
+        r = util.system(cmd, environ=env, cwd=cwd, out=ui)
+    else:
+        r = util.system(cmd, environ=env, cwd=cwd)
     if r:
         desc, r = util.explain_exit(r)
         if throw:
--- a/mercurial/localrepo.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/localrepo.py	Fri Jul 02 11:30:57 2010 +0200
@@ -1199,7 +1199,7 @@
                                        "other repository doesn't support "
                                        "changegroupsubset."))
                 cg = remote.changegroupsubset(fetch, heads, 'pull')
-            return self.addchangegroup(cg, 'pull', remote.url())
+            return self.addchangegroup(cg, 'pull', remote.url(), lock=lock)
         finally:
             lock.release()
 
@@ -1233,8 +1233,8 @@
             ret = discovery.prepush(self, remote, force, revs, newbranch)
             if ret[0] is not None:
                 cg, remote_heads = ret
-                # here, we return an integer indicating remote head count change
-                return remote.addchangegroup(cg, 'push', self.url())
+                # we return an integer indicating remote head count change
+                return remote.addchangegroup(cg, 'push', self.url(), lock=lock)
             # and here we return 0 for "nothing to push" or 1 for
             # "something to push but I refuse"
             return ret[1]
@@ -1620,7 +1620,7 @@
 
         return util.chunkbuffer(gengroup())
 
-    def addchangegroup(self, source, srctype, url, emptyok=False):
+    def addchangegroup(self, source, srctype, url, emptyok=False, lock=None):
         """Add the changegroup returned by source.read() to this repo.
         srctype is a string like 'push', 'pull', or 'unbundle'.  url is
         the URL of the repo where this changegroup is coming from.
@@ -1760,6 +1760,8 @@
             tr.close()
         finally:
             tr.release()
+            if lock:
+                lock.release()
 
         if changesets > 0:
             # forcefully update the on-disk branch cache
--- a/mercurial/merge.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/merge.py	Fri Jul 02 11:30:57 2010 +0200
@@ -23,15 +23,13 @@
     def _read(self):
         self._state = {}
         try:
-            localnode = None
             f = self._repo.opener("merge/state")
             for i, l in enumerate(f):
                 if i == 0:
-                    localnode = l[:-1]
+                    self._local = bin(l[:-1])
                 else:
                     bits = l[:-1].split("\0")
                     self._state[bits[0]] = bits[1:]
-            self._local = bin(localnode)
         except IOError, err:
             if err.errno != errno.ENOENT:
                 raise
@@ -184,7 +182,9 @@
             rflags = fmerge(f, f, f)
             a = ma.get(f, nullid)
             if n == m2[f] or m2[f] == a: # same or local newer
-                if m1.flags(f) != rflags:
+                # is file locally modified or flags need changing?
+                # dirstate flags may need to be made current
+                if m1.flags(f) != rflags or n[20:]:
                     act("update permissions", "e", f, rflags)
             elif n == a: # remote newer
                 act("remote is newer", "g", f, rflags)
@@ -244,8 +244,13 @@
 def actionkey(a):
     return a[1] == 'r' and -1 or 0, a
 
-def applyupdates(repo, action, wctx, mctx):
-    "apply the merge action list to the working directory"
+def applyupdates(repo, action, wctx, mctx, actx):
+    """apply the merge action list to the working directory
+
+    wctx is the working copy context
+    mctx is the context to be merged into the working copy
+    actx is the context of the common ancestor
+    """
 
     updated, merged, removed, unresolved = 0, 0, 0, 0
     ms = mergestate(repo)
@@ -265,7 +270,7 @@
             repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
             fcl = wctx[f]
             fco = mctx[f2]
-            fca = fcl.ancestor(fco) or repo.filectx(f, fileid=nullrev)
+            fca = fcl.ancestor(fco, actx) or repo.filectx(f, fileid=nullrev)
             ms.add(fcl, fco, fca, fd, flags)
             if f != fd and move:
                 moves.append(f)
@@ -507,7 +512,7 @@
         if not partial:
             repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
 
-        stats = applyupdates(repo, action, wc, p2)
+        stats = applyupdates(repo, action, wc, p2, pa)
 
         if not partial:
             repo.dirstate.setparents(fp1, fp2)
--- a/mercurial/minirst.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/minirst.py	Fri Jul 02 11:30:57 2010 +0200
@@ -36,7 +36,13 @@
 """
 
 import re, sys
-import util
+import util, encoding
+
+def replace(text, substs):
+    utext = text.decode(encoding.encoding)
+    for f, t in substs:
+        utext = utext.replace(f, t)
+    return utext.encode(encoding.encoding)
 
 def findblocks(text):
     """Find continuous blocks of lines in text.
@@ -251,21 +257,22 @@
 
 
 def inlineliterals(blocks):
+    substs = [('``', '"')]
     for b in blocks:
         if b['type'] in ('paragraph', 'section'):
-            b['lines'] = [l.replace('``', '"') for l in b['lines']]
+            b['lines'] = [replace(l, substs) for l in b['lines']]
     return blocks
 
 
 def hgrole(blocks):
+    substs = [(':hg:`', '"hg '), ('`', '"')]
     for b in blocks:
         if b['type'] in ('paragraph', 'section'):
             # Turn :hg:`command` into "hg command". This also works
             # when there is a line break in the command and relies on
             # the fact that we have no stray back-quotes in the input
             # (run the blocks through inlineliterals first).
-            b['lines'] = [l.replace(':hg:`', '"hg ').replace('`', '"')
-                          for l in b['lines']]
+            b['lines'] = [replace(l, substs) for l in b['lines']]
     return blocks
 
 
--- a/mercurial/parser.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/parser.py	Fri Jul 02 11:30:57 2010 +0200
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-# see http://effbot.org/zone/simple-top-down-parsing.txt and
+# see http://effbot.org/zone/simple-top-down-parsing.htm and
 # http://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing/
 # for background
 
--- a/mercurial/revset.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/revset.py	Fri Jul 02 11:30:57 2010 +0200
@@ -111,10 +111,6 @@
 
 # operator methods
 
-def negate(repo, subset, x):
-    return getset(repo, subset,
-                  ('string', '-' + getstring(x, _("can't negate that"))))
-
 def stringset(repo, subset, x):
     x = repo[x].rev()
     if x == -1 and len(subset) == len(repo):
@@ -129,11 +125,24 @@
     return stringset(repo, subset, x)
 
 def rangeset(repo, subset, x, y):
-    m = getset(repo, subset, x)[0]
-    n = getset(repo, subset, y)[-1]
+    m = getset(repo, subset, x)
+    if not m:
+        m = getset(repo, range(len(repo)), x)
+
+    n = getset(repo, subset, y)
+    if not n:
+        n = getset(repo, range(len(repo)), y)
+
+    if not m or not n:
+        return []
+    m, n = m[0], n[-1]
+
     if m < n:
-        return range(m, n + 1)
-    return range(m, n - 1, -1)
+        r = range(m, n + 1)
+    else:
+        r = range(m, n - 1, -1)
+    s = set(subset)
+    return [x for x in r if x in s]
 
 def andset(repo, subset, x, y):
     return getset(repo, getset(repo, subset, x), y)
@@ -222,11 +231,15 @@
 
 def ancestors(repo, subset, x):
     args = getset(repo, range(len(repo)), x)
+    if not args:
+        return []
     s = set(repo.changelog.ancestors(*args)) | set(args)
     return [r for r in subset if r in s]
 
 def descendants(repo, subset, x):
     args = getset(repo, range(len(repo)), x)
+    if not args:
+        return []
     s = set(repo.changelog.descendants(*args)) | set(args)
     return [r for r in subset if r in s]
 
@@ -422,7 +435,6 @@
     repo.ui.popbuffer()
     cl = repo.changelog
     o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, None)[0]])
-    print 'out', dest, o
     return [r for r in subset if r in o]
 
 def tagged(repo, subset, x):
@@ -466,7 +478,6 @@
 }
 
 methods = {
-    "negate": negate,
     "range": rangeset,
     "string": stringset,
     "symbol": symbolset,
@@ -499,6 +510,9 @@
         return optimize(('range', ('string', '0'), x[1]), small)
     elif op == 'rangepost':
         return optimize(('range', x[1], ('string', 'tip')), small)
+    elif op == 'negate':
+        return optimize(('string',
+                         '-' + getstring(x[1], _("can't negate that"))), small)
     elif op in 'string symbol negate':
         return smallbonus, x # single revisions are small
     elif op == 'and' or op == 'dagrange':
--- a/mercurial/sshserver.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/sshserver.py	Fri Jul 02 11:30:57 2010 +0200
@@ -161,7 +161,8 @@
             return
 
         self.respond("")
-        r = self.repo.addchangegroup(self.fin, 'serve', self.client_url())
+        r = self.repo.addchangegroup(self.fin, 'serve', self.client_url(),
+                                     lock=self.lock)
         self.respond(str(r))
 
     def client_url(self):
@@ -205,7 +206,8 @@
                 # push can proceed
 
                 fp.seek(0)
-                r = self.repo.addchangegroup(fp, 'serve', self.client_url())
+                r = self.repo.addchangegroup(fp, 'serve', self.client_url(),
+                                             lock=self.lock)
                 self.respond(str(r))
             finally:
                 if not was_locked:
--- a/mercurial/subrepo.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/subrepo.py	Fri Jul 02 11:30:57 2010 +0200
@@ -67,19 +67,21 @@
         repo.ui.debug("  subrepo %s: %s %s\n" % (s, msg, r))
 
     for s, l in s1.items():
+        ld = l # local state with possible dirty flag for compares
         if wctx != actx and wctx.sub(s).dirty():
-            l = (l[0], l[1] + "+")
+            ld = (l[0], l[1] + "+")
+
         a = sa.get(s, nullstate)
         if s in s2:
             r = s2[s]
-            if l == r or r == a: # no change or local is newer
+            if ld == r or r == a: # no change or local is newer
                 sm[s] = l
                 continue
-            elif l == a: # other side changed
+            elif ld == a: # other side changed
                 debug(s, "other changed, get", r)
                 wctx.sub(s).get(r)
                 sm[s] = r
-            elif l[0] != r[0]: # sources differ
+            elif ld[0] != r[0]: # sources differ
                 if repo.ui.promptchoice(
                     _(' subrepository sources for %s differ\n'
                       'use (l)ocal source (%s) or (r)emote source (%s)?')
@@ -88,7 +90,7 @@
                     debug(s, "prompt changed, get", r)
                     wctx.sub(s).get(r)
                     sm[s] = r
-            elif l[1] == a[1]: # local side is unchanged
+            elif ld[1] == a[1]: # local side is unchanged
                 debug(s, "other side changed, get", r)
                 wctx.sub(s).get(r)
                 sm[s] = r
@@ -96,7 +98,7 @@
                 debug(s, "both sides changed, merge with", r)
                 wctx.sub(s).merge(r)
                 sm[s] = l
-        elif l == a: # remote removed, local unchanged
+        elif ld == a: # remote removed, local unchanged
             debug(s, "remote removed, remove")
             wctx.sub(s).remove()
         else:
@@ -379,8 +381,8 @@
             self.get(state)
 
     def push(self, force):
-        # nothing for svn
-        pass
+        # push is a no-op for SVN
+        return True
 
 types = {
     'hg': hgsubrepo,
--- a/mercurial/url.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/url.py	Fri Jul 02 11:30:57 2010 +0200
@@ -542,11 +542,25 @@
             conn.ui = self.ui
             return conn
 
-# In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
-# it doesn't know about the auth type requested.  This can happen if
-# somebody is using BasicAuth and types a bad password.
 class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler):
+    def __init__(self, *args, **kwargs):
+        urllib2.HTTPDigestAuthHandler.__init__(self, *args, **kwargs)
+        self.retried_req = None
+
+    def reset_retry_count(self):
+        # Python 2.6.5 will call this on 401 or 407 errors and thus loop
+        # forever. We disable reset_retry_count completely and reset in
+        # http_error_auth_reqed instead.
+        pass
+
     def http_error_auth_reqed(self, auth_header, host, req, headers):
+        # Reset the retry counter once for each request.
+        if req is not self.retried_req:
+            self.retried_req = req
+            self.retried = 0
+        # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
+        # it doesn't know about the auth type requested. This can happen if
+        # somebody is using BasicAuth and types a bad password.
         try:
             return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
                         self, auth_header, host, req, headers)
@@ -556,13 +570,6 @@
                 return
             raise
 
-    # Python 2.6.5 will keep resetting the retry count on redirects, for
-    # example when the server returns 401 on failing auth (like google code
-    # currently does). We stop the endless recursion by not resetting the
-    # count.
-    def reset_retry_count(self):
-        pass
-
 def getauthinfo(path):
     scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
     if not urlpath:
--- a/mercurial/util.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/mercurial/util.py	Fri Jul 02 11:30:57 2010 +0200
@@ -366,13 +366,16 @@
     global _hgexecutable
     _hgexecutable = path
 
-def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
+def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
     '''enhanced shell command execution.
     run with environment maybe modified, maybe in different dir.
 
     if command fails and onerr is None, return status.  if ui object,
     print error message and return status, else raise onerr object as
-    exception.'''
+    exception.
+
+    if out is specified, it is assumed to be a file-like object that has a
+    write() method. stdout and stderr will be redirected to out.'''
     def py2shell(val):
         'convert python object into string that is useful to shell'
         if val is None or val is False:
@@ -386,8 +389,17 @@
     env = dict(os.environ)
     env.update((k, py2shell(v)) for k, v in environ.iteritems())
     env['HG'] = hgexecutable()
-    rc = subprocess.call(cmd, shell=True, close_fds=closefds,
-                         env=env, cwd=cwd)
+    if out is None:
+        rc = subprocess.call(cmd, shell=True, close_fds=closefds,
+                             env=env, cwd=cwd)
+    else:
+        proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
+                                env=env, cwd=cwd, stdout=subprocess.PIPE,
+                                stderr=subprocess.STDOUT)
+        for line in proc.stdout:
+            out.write(line)
+        proc.wait()
+        rc = proc.returncode
     if sys.platform == 'OpenVMS' and rc & 1:
         rc = 0
     if rc and onerr:
--- a/setup.py	Mon Jun 21 17:02:48 2010 -0300
+++ b/setup.py	Fri Jul 02 11:30:57 2010 +0200
@@ -35,12 +35,15 @@
 import os, subprocess, time
 import shutil
 import tempfile
+from distutils import log
 from distutils.core import setup, Extension
 from distutils.dist import Distribution
 from distutils.command.build import build
+from distutils.command.build_ext import build_ext
 from distutils.command.build_py import build_py
 from distutils.spawn import spawn, find_executable
 from distutils.ccompiler import new_compiler
+from distutils.errors import CCompilerError
 
 scripts = ['hg']
 if os.name == 'nt':
@@ -209,6 +212,17 @@
 Distribution.global_options.append(('pure', None, "use pure (slow) Python "
                                     "code instead of C extensions"))
 
+class hgbuildext(build_ext):
+
+    def build_extension(self, ext):
+        try:
+            build_ext.build_extension(self, ext)
+        except CCompilerError:
+            if not hasattr(ext, 'optional') or not ext.optional:
+                raise
+            log.warn("Failed to build optional extension '%s' (skipping)",
+                     ext.name)
+
 class hgbuildpy(build_py):
 
     def finalize_options(self):
@@ -232,6 +246,7 @@
                 yield module
 
 cmdclass = {'build_mo': hgbuildmo,
+            'build_ext': hgbuildext,
             'build_py': hgbuildpy}
 
 packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert',
@@ -256,10 +271,13 @@
 if sys.platform == 'linux2' and os.uname()[2] > '2.6':
     # The inotify extension is only usable with Linux 2.6 kernels.
     # You also need a reasonably recent C library.
+    # In any case, if it fails to build the error will be skipped ('optional').
     cc = new_compiler()
     if hasfunction(cc, 'inotify_add_watch'):
-        extmodules.append(Extension('hgext.inotify.linux._inotify',
-                                     ['hgext/inotify/linux/_inotify.c']))
+        inotify = Extension('hgext.inotify.linux._inotify',
+                            ['hgext/inotify/linux/_inotify.c'])
+        inotify.optional = True
+        extmodules.append(inotify)
         packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
 
 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
--- a/tests/test-acl	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-acl	Fri Jul 02 11:30:57 2010 +0200
@@ -44,7 +44,7 @@
 [acl]
 sources = push
 [extensions]
-f=$PWD/fakegroups.py
+f=`pwd`/fakegroups.py
 EOF
 }
 
--- a/tests/test-alias	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-alias	Fri Jul 02 11:30:57 2010 +0200
@@ -13,7 +13,7 @@
 shortlog = log --template '{rev} {node|short} | {date|isodate}\n'
 dln = lognull --debug
 nousage = rollback
-put = export -r 0 -o "\$PWD/%R.diff"
+put = export -r 0 -o "\$FOO/%R.diff"
 
 [defaults]
 mylog = -q
@@ -62,5 +62,5 @@
 hg dln
 
 echo '% path expanding'
-hg put
+FOO=`pwd` hg put
 cat 0.diff
--- a/tests/test-command-template	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-command-template	Fri Jul 02 11:30:57 2010 +0200
@@ -100,6 +100,9 @@
 hg log --style=changelog > changelog
 cat changelog
 
+echo '# issue 2130'
+hg heads --style changelog
+
 echo "# keys work"
 for key in author branches date desc file_adds file_dels file_mods \
         file_copies file_copies_switch files \
--- a/tests/test-command-template.out	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-command-template.out	Fri Jul 02 11:30:57 2010 +0200
@@ -437,6 +437,23 @@
 	line 1 line 2
 	[1e4e1b8f71e0]
 
+# issue 2130
+2020-01-01  test  <test>
+
+	* fourth, second, third:
+	third
+	[95c24699272e] [tip]
+
+1970-01-18  person  <person>
+
+	* merge
+	[c7b487c6c50e]
+
+1970-01-17  person  <person>
+
+	* new branch
+	[32a18f097fcc] <foo>
+
 # keys work
 author: test
 author: User Name <user@hostname>
--- a/tests/test-glog	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-glog	Fri Jul 02 11:30:57 2010 +0200
@@ -146,6 +146,9 @@
 echo % unused arguments
 hg glog -q foo bar || echo failed
 
+echo % empty revision range - display nothing
+hg glog -r 1..0
+
 echo % from outer space
 cd ..
 hg glog -l1 repo
--- a/tests/test-glog.out	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-glog.out	Fri Jul 02 11:30:57 2010 +0200
@@ -548,6 +548,7 @@
 
 show revision history alongside an ASCII revision graph
 failed
+% empty revision range - display nothing
 % from outer space
 @  changeset:   34:fea3ac5810e0
 |  tag:         tip
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qimport-fail-cleanup	Fri Jul 02 11:30:57 2010 +0200
@@ -0,0 +1,33 @@
+#!/bin/sh
+#failed qimport of patches from files should cleanup by recording successfully
+#imported patches in series file.
+
+echo "[extensions]" >> $HGRCPATH
+echo "mq=" >> $HGRCPATH
+
+hg init repo
+cd repo
+
+echo a > a
+hg ci -Am'add a'
+
+cat >b.patch<<EOF
+diff --git a/a b/a
+--- a/a
++++ b/a
+@@ -1,1 +1,2 @@
+ a
++b
+EOF
+
+echo
+echo '#empty series'
+hg qseries
+
+echo
+echo '#qimport valid patch followed by invalid patch'
+hg qimport b.patch fakepatch
+
+echo
+echo '#valid patches before fail added to series'
+hg qseries
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qimport-fail-cleanup.out	Fri Jul 02 11:30:57 2010 +0200
@@ -0,0 +1,10 @@
+adding a
+
+#empty series
+
+#qimport valid patch followed by invalid patch
+adding b.patch to series file
+abort: unable to read fakepatch
+
+#valid patches before fail added to series
+b.patch
--- a/tests/test-push-http	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-push-http	Fri Jul 02 11:30:57 2010 +0200
@@ -39,11 +39,9 @@
 echo % expect success
 echo 'allow_push = *' >> .hg/hgrc
 echo '[hooks]' >> .hg/hgrc
-echo 'changegroup = python ../printenv.py changegroup 0 ../urls' >> .hg/hgrc
+echo 'changegroup = python ../printenv.py changegroup 0' >> .hg/hgrc
 req
 
-cat ../urls
-
 hg rollback
 echo % expect authorization error: all users denied
 echo '[web]' > .hg/hgrc
--- a/tests/test-push-http.out	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-push-http.out	Fri Jul 02 11:30:57 2010 +0200
@@ -23,8 +23,8 @@
 remote: adding manifests
 remote: adding file changes
 remote: added 1 changesets with 1 changes to 1 files
+remote: changegroup hook: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_URL=remote:http 
 % serve errors
-changegroup hook: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_URL=remote:http 
 rolling back to revision 0 (undo serve)
 % expect authorization error: all users denied
 abort: authorization failed
--- a/tests/test-resolve	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-resolve	Fri Jul 02 11:30:57 2010 +0200
@@ -25,3 +25,9 @@
 
 echo % resolve -l, should be empty
 hg resolve -l
+
+# test crashed merge with empty mergestate
+mkdir .hg/merge
+touch .hg/merge/state
+echo % resolve -l, should be empty
+hg resolve -l
--- a/tests/test-resolve.out	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-resolve.out	Fri Jul 02 11:30:57 2010 +0200
@@ -6,3 +6,4 @@
 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
 use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon
 % resolve -l, should be empty
+% resolve -l, should be empty
--- a/tests/test-revset	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-revset	Fri Jul 02 11:30:57 2010 +0200
@@ -126,3 +126,11 @@
 log '4:8'
 
 log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
+
+log 'not 0 and 0:2'
+log 'not 1 and 0:2'
+log 'not 2 and 0:2'
+log '(1 and 2)::'
+log '(1 and 2):'
+log '(1 and 2):3'
+log 'sort(head(), -rev)'
--- a/tests/test-revset.out	Mon Jun 21 17:02:48 2010 -0300
+++ b/tests/test-revset.out	Fri Jul 02 11:30:57 2010 +0200
@@ -198,3 +198,25 @@
 4
 2
 5
+% log 'not 0 and 0:2'
+1
+2
+% log 'not 1 and 0:2'
+0
+2
+% log 'not 2 and 0:2'
+0
+1
+% log '(1 and 2)::'
+% log '(1 and 2):'
+% log '(1 and 2):3'
+% log 'sort(head(), -rev)'
+9
+7
+6
+5
+4
+3
+2
+1
+0