changeset 52100:19ae7730636a

branching: merge stable into default
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Sat, 26 Oct 2024 00:58:01 +0200
parents 31076a2301f1 (current diff) b5efb7a7d2a5 (diff)
children 720d9849dcf9
files hgext/show.py i18n/de.po i18n/ja.po mercurial/helptext/hgweb.txt mercurial/hg.py mercurial/pycompat.py mercurial/windows.py tests/test-alias.t tests/test-bad-extension.t tests/test-convert.t tests/test-encoding-textwrap.t tests/test-extdiff.t tests/test-extension.t tests/test-fix.t tests/test-help.t tests/test-http-bad-server.t tests/test-lfs-serve-access.t tests/test-mq-qrefresh-interactive.t tests/test-qrecord.t tests/test-record.t tests/test-shelve.t tests/test-show.t tests/test-uncommit.t
diffstat 9 files changed, 190 insertions(+), 157 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/hg.py	Thu Oct 24 15:23:52 2024 +0200
+++ b/mercurial/hg.py	Sat Oct 26 00:58:01 2024 +0200
@@ -1427,60 +1427,6 @@
     )
 
 
-def _outgoing(ui, repo, dests, opts, subpath=None):
-    out = set()
-    others = []
-    for path in urlutil.get_push_paths(repo, ui, dests):
-        dest = path.loc
-        if subpath is not None:
-            subpath = urlutil.url(subpath)
-            if subpath.isabs():
-                dest = bytes(subpath)
-            else:
-                p = urlutil.url(dest)
-                if p.islocal():
-                    normpath = os.path.normpath
-                else:
-                    normpath = posixpath.normpath
-                p.path = normpath(b'%s/%s' % (p.path, subpath))
-                dest = bytes(p)
-        branches = path.branch, opts.get(b'branch') or []
-
-        ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(dest))
-        revs, checkout = addbranchrevs(repo, repo, branches, opts.get(b'rev'))
-        if revs:
-            revs = [repo[rev].node() for rev in logcmdutil.revrange(repo, revs)]
-
-        other = peer(repo, opts, dest)
-        try:
-            outgoing = discovery.findcommonoutgoing(
-                repo, other, revs, force=opts.get(b'force')
-            )
-            o = outgoing.missing
-            out.update(o)
-            if not o:
-                scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
-            others.append(other)
-        except:  # re-raises
-            other.close()
-            raise
-    # make sure this is ordered by revision number
-    outgoing_revs = list(out)
-    cl = repo.changelog
-    outgoing_revs.sort(key=cl.rev)
-    return outgoing_revs, others
-
-
-def _outgoing_recurse(ui, repo, dests, opts):
-    ret = 1
-    if opts.get(b'subrepos'):
-        ctx = repo[None]
-        for subpath in sorted(ctx.substate):
-            sub = ctx.sub(subpath)
-            ret = min(ret, sub.outgoing(ui, dests, opts))
-    return ret
-
-
 def _outgoing_filter(repo, revs, opts):
     """apply revision filtering/ordering option for outgoing"""
     limit = logcmdutil.getlimit(opts)
@@ -1504,37 +1450,94 @@
         yield n
 
 
+def _outgoing_recurse(ui, repo, dests, opts):
+    ret = 1
+    if opts.get(b'subrepos'):
+        ctx = repo[None]
+        for subpath in sorted(ctx.substate):
+            sub = ctx.sub(subpath)
+            ret = min(ret, sub.outgoing(ui, dests, opts))
+    return ret
+
+
+def display_outgoing_revs(ui, repo, o, opts):
+    # make sure this is ordered by revision number
+    cl = repo.changelog
+    o.sort(key=cl.rev)
+    if opts.get(b'graph'):
+        revdag = logcmdutil.graphrevs(repo, o, opts)
+        ui.pager(b'outgoing')
+        displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
+        logcmdutil.displaygraph(
+            ui, repo, revdag, displayer, graphmod.asciiedges
+        )
+    else:
+        ui.pager(b'outgoing')
+        displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
+        for n in _outgoing_filter(repo, o, opts):
+            displayer.show(repo[n])
+            displayer.close()
+
+
+_no_subtoppath = object()
+
+
 def outgoing(ui, repo, dests, opts, subpath=None):
     if opts.get(b'graph'):
         logcmdutil.checkunsupportedgraphflags([], opts)
-    o, others = _outgoing(ui, repo, dests, opts, subpath=subpath)
     ret = 1
-    try:
-        if o:
-            ret = 0
+    for path in urlutil.get_push_paths(repo, ui, dests):
+        dest = path.loc
+        prev_subtopath = getattr(repo, "_subtoppath", _no_subtoppath)
+        try:
+            repo._subtoppath = dest
+            if subpath is not None:
+                subpath = urlutil.url(subpath)
+                if subpath.isabs():
+                    dest = bytes(subpath)
+                else:
+                    p = urlutil.url(dest)
+                    if p.islocal():
+                        normpath = os.path.normpath
+                    else:
+                        normpath = posixpath.normpath
+                    p.path = normpath(b'%s/%s' % (p.path, subpath))
+                    dest = bytes(p)
+            branches = path.branch, opts.get(b'branch') or []
 
-            if opts.get(b'graph'):
-                revdag = logcmdutil.graphrevs(repo, o, opts)
-                ui.pager(b'outgoing')
-                displayer = logcmdutil.changesetdisplayer(
-                    ui, repo, opts, buffered=True
+            ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(dest))
+            revs, checkout = addbranchrevs(
+                repo, repo, branches, opts.get(b'rev')
+            )
+            if revs:
+                revs = [
+                    repo[rev].node() for rev in logcmdutil.revrange(repo, revs)
+                ]
+
+            other = peer(repo, opts, dest)
+            try:
+                outgoing = discovery.findcommonoutgoing(
+                    repo, other, revs, force=opts.get(b'force')
                 )
-                logcmdutil.displaygraph(
-                    ui, repo, revdag, displayer, graphmod.asciiedges
-                )
+                o = outgoing.missing
+                if not o:
+                    scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
+                else:
+                    ret = 0
+                    display_outgoing_revs(ui, repo, o, opts)
+
+                cmdutil.outgoinghooks(ui, repo, other, opts, o)
+                ret = min(ret, _outgoing_recurse(ui, repo, dests, opts))
+            except:  # re-raises
+                raise
+            finally:
+                other.close()
+        finally:
+            if prev_subtopath is _no_subtoppath:
+                del repo._subtoppath
             else:
-                ui.pager(b'outgoing')
-                displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
-                for n in _outgoing_filter(repo, o, opts):
-                    displayer.show(repo[n])
-                displayer.close()
-        for oth in others:
-            cmdutil.outgoinghooks(ui, repo, oth, opts, o)
-            ret = min(ret, _outgoing_recurse(ui, repo, dests, opts))
-        return ret  # exit code is zero since we found outgoing changes
-    finally:
-        for oth in others:
-            oth.close()
+                repo._subtoppath = prev_subtopath
+    return ret
 
 
 def verify(repo, level=None):
--- a/mercurial/thirdparty/zope/interface/interface.py	Thu Oct 24 15:23:52 2024 +0200
+++ b/mercurial/thirdparty/zope/interface/interface.py	Sat Oct 26 00:58:01 2024 +0200
@@ -352,7 +352,14 @@
         # Make sure that all recorded attributes (and methods) are of type
         # `Attribute` and `Method`
         for name, attr in list(attrs.items()):
-            if name in ('__locals__', '__qualname__', '__annotations__'):
+            compiler_attributes = (
+                '__locals__',
+                '__qualname__',
+                '__annotations__',
+                '__firstlineno__',
+                '__static_attributes__',
+            )
+            if name in compiler_attributes:
                 # __locals__: Python 3 sometimes adds this.
                 # __qualname__: PEP 3155 (Python 3.3+)
                 # __annotations__: PEP 3107 (Python 3.0+)
--- a/mercurial/windows.py	Thu Oct 24 15:23:52 2024 +0200
+++ b/mercurial/windows.py	Sat Oct 26 00:58:01 2024 +0200
@@ -19,6 +19,7 @@
 import winreg  # pytype: disable=import-error
 
 from typing import (
+    Any,
     AnyStr,
     BinaryIO,
     Iterable,
@@ -676,13 +677,38 @@
 
 
 class cachestat:
-    stat: Optional[os.stat_result]
+    stat: os.stat_result
 
     def __init__(self, path: bytes) -> None:
-        self.stat = None
+        self.stat = os.stat(path)
 
     def cacheable(self) -> bool:
-        return False
+        return bool(self.stat.st_ino)
+
+    __hash__ = object.__hash__
+
+    def __eq__(self, other: Any) -> bool:
+        try:
+            # Only dev, ino, size, mtime and atime are likely to change. Out
+            # of these, we shouldn't compare atime but should compare the
+            # rest. However, one of the other fields changing indicates
+            # something fishy going on, so return False if anything but atime
+            # changes.
+            return (
+                self.stat.st_ino == other.stat.st_ino
+                and self.stat.st_dev == other.stat.st_dev
+                and self.stat.st_nlink == other.stat.st_nlink
+                and self.stat.st_uid == other.stat.st_uid
+                and self.stat.st_gid == other.stat.st_gid
+                and self.stat.st_size == other.stat.st_size
+                and self.stat[stat.ST_MTIME] == other.stat[stat.ST_MTIME]
+                and self.stat[stat.ST_CTIME] == other.stat[stat.ST_CTIME]
+            )
+        except AttributeError:
+            return False
+
+    def __ne__(self, other: Any) -> bool:
+        return not self == other
 
 
 def lookupreg(
--- a/tests/test-bad-extension.t	Thu Oct 24 15:23:52 2024 +0200
+++ b/tests/test-bad-extension.t	Sat Oct 26 00:58:01 2024 +0200
@@ -23,6 +23,7 @@
     File "*/mercurial/dispatch.py", line *, in _runexithandlers (glob) (no-pyoxidizer !)
     File "mercurial.dispatch", line *, in _runexithandlers (glob) (pyoxidizer !)
       func(*args, **kwargs)
+      ~~~~^^^^^^^^^^^^^^^^^ (py313 !)
     File "$TESTTMP/bailatexit.py", line *, in bail (glob)
       raise RuntimeError('ui.atexit handler exception')
   RuntimeError: ui.atexit handler exception
--- a/tests/test-exchange-multi-source.t	Thu Oct 24 15:23:52 2024 +0200
+++ b/tests/test-exchange-multi-source.t	Sat Oct 26 00:58:01 2024 +0200
@@ -133,10 +133,26 @@
   $ hg out -G -R test-repo-bare ./branch-E-push ./branch-G-push ./branch-H-push
   comparing with ./branch-E-push
   searching for changes
+  o  changeset:   7:40faebb2ec45
+     tag:         tip
+     parent:      2:f838bfaca5c7
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     H
+  
+  o  changeset:   6:c521a06b234b
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     G
+  |
+  o  changeset:   5:2f3a4c5c1417
+     parent:      1:27547f69f254
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     F
+  
   comparing with ./branch-G-push
   searching for changes
-  comparing with ./branch-H-push
-  searching for changes
   o  changeset:   7:40faebb2ec45
   |  tag:         tip
   |  parent:      2:f838bfaca5c7
@@ -144,17 +160,6 @@
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     H
   |
-  | o  changeset:   6:c521a06b234b
-  | |  user:        test
-  | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  summary:     G
-  | |
-  | o  changeset:   5:2f3a4c5c1417
-  |    parent:      1:27547f69f254
-  |    user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     F
-  |
   | o  changeset:   4:a603bfb5a83e
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
@@ -170,6 +175,29 @@
      date:        Thu Jan 01 00:00:00 1970 +0000
      summary:     C
   
+  comparing with ./branch-H-push
+  searching for changes
+  o  changeset:   6:c521a06b234b
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     G
+  |
+  o  changeset:   5:2f3a4c5c1417
+     parent:      1:27547f69f254
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     F
+  
+  o  changeset:   4:a603bfb5a83e
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     E
+  |
+  o  changeset:   3:b3325c91a4d9
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     D
+  
   $ hg bundle -R test-repo-bare bundle.hg ./branch-E-push ./branch-G-push ./branch-H-push
   searching for changes
   searching for changes
@@ -339,11 +367,15 @@
   $ hg out -G -R test-repo-bare ./branch-E-push ./branch-G-push ./branch-H-push --rev default
   comparing with ./branch-E-push
   searching for changes
+  o  changeset:   7:40faebb2ec45
+     tag:         tip
+     parent:      2:f838bfaca5c7
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     H
+  
   comparing with ./branch-G-push
   searching for changes
-  comparing with ./branch-H-push
-  searching for changes
-  no changes found
   o  changeset:   7:40faebb2ec45
   |  tag:         tip
   |  parent:      2:f838bfaca5c7
@@ -356,6 +388,9 @@
      date:        Thu Jan 01 00:00:00 1970 +0000
      summary:     C
   
+  comparing with ./branch-H-push
+  searching for changes
+  no changes found
   $ hg bundle -R test-repo-bare bundle.hg ./branch-E-push ./branch-G-push ./branch-H-push --rev default
   searching for changes
   searching for changes
@@ -422,11 +457,6 @@
   $ hg out -G -R test-repo-bare ./branch-G-push ./branch-H-push ./branch-E-push --rev default
   comparing with ./branch-G-push
   searching for changes
-  comparing with ./branch-H-push
-  searching for changes
-  no changes found
-  comparing with ./branch-E-push
-  searching for changes
   o  changeset:   7:40faebb2ec45
   |  tag:         tip
   |  parent:      2:f838bfaca5c7
@@ -439,6 +469,18 @@
      date:        Thu Jan 01 00:00:00 1970 +0000
      summary:     C
   
+  comparing with ./branch-H-push
+  searching for changes
+  no changes found
+  comparing with ./branch-E-push
+  searching for changes
+  o  changeset:   7:40faebb2ec45
+     tag:         tip
+     parent:      2:f838bfaca5c7
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     H
+  
   $ hg bundle -R test-repo-bare bundle.hg ./branch-G-push ./branch-H-push ./branch-E-push --rev default
   searching for changes
   searching for changes
--- a/tests/test-extension.t	Thu Oct 24 15:23:52 2024 +0200
+++ b/tests/test-extension.t	Sat Oct 26 00:58:01 2024 +0200
@@ -596,6 +596,7 @@
     File "*/mercurial/extensions.py", line *, in _runuisetup (glob) (no-pyoxidizer !)
     File "mercurial.extensions", line *, in _runuisetup (glob) (pyoxidizer !)
       uisetup(ui)
+      ~~~~~~~^^^^ (py313 !)
     File "$TESTTMP/baduisetup.py", line 2, in uisetup
       1 / 0
       ~~^~~ (py311 !)
--- a/tests/test-http-bad-server.t	Thu Oct 24 15:23:52 2024 +0200
+++ b/tests/test-http-bad-server.t	Sat Oct 26 00:58:01 2024 +0200
@@ -246,6 +246,8 @@
   read limit reached; closing socket
   $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/?cmd=batch': (glob)
   Traceback (most recent call last):
+          rctx, req, res, self.check_perm (py313 !)
+      ) (py313 !)
   Exception: connection closed after receiving N bytes
   
 
--- a/tests/test-lfs-serve-access.t	Thu Oct 24 15:23:52 2024 +0200
+++ b/tests/test-lfs-serve-access.t	Sat Oct 26 00:58:01 2024 +0200
@@ -83,6 +83,7 @@
   [50]
 
   $ hg -R httpclone update default --config lfs.url=http://localhost:$HGPORT2/missing
+  abort: LFS error: *onnection *refused* (glob) (?)
   abort: LFS error: $ECONNREFUSED$ (?)
   abort: LFS error: $EADDRNOTAVAIL$ (glob) (?)
   abort: LFS error: No route to host (?)
@@ -336,60 +337,10 @@
   $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
   $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 422 - (glob)
 
-  $ grep -v '  File "' $TESTTMP/errors.log
-  $LOCALIP - - [$ERRDATE$] HG error:  Exception happened while processing request '/.git/info/lfs/objects/batch': (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:  Traceback (most recent call last): (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:      verifies = store.verify(oid) (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:                 ^^^^^^^^^^^^^^^^^ (glob) (py311 !)
-  $LOCALIP - - [$ERRDATE$] HG error:      raise IOError(errno.EIO, r'%s: I/O error' % oid.decode("utf-8")) (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:  *Error: [Errno *] f03217a32529a28a42d03b1244fe09b6e0f9fd06d7b966d4d50567be2abe6c0e: I/O error (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:   (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:  Exception happened while processing request '/.git/info/lfs/objects/batch': (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:  Traceback (most recent call last): (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:      verifies = store.verify(oid) (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:                 ^^^^^^^^^^^^^^^^^ (glob) (py311 !)
-  $LOCALIP - - [$ERRDATE$] HG error:      raise IOError(errno.EIO, r'%s: I/O error' % oid.decode("utf-8")) (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:  *Error: [Errno *] b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c: I/O error (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:   (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:  Exception happened while processing request '/.hg/lfs/objects/b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c': (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:  Traceback (most recent call last): (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:      localstore.download(oid, req.bodyfh, req.headers[b'Content-Length'])
-  $LOCALIP - - [$ERRDATE$] HG error:      super(badstore, self).download(oid, src, contentlength)
-  $LOCALIP - - [$ERRDATE$] HG error:      raise LfsCorruptionError( (glob) (py38 !)
-  $LOCALIP - - [$ERRDATE$] HG error:      _(b'corrupt remote lfs object: %s') % oid (glob) (no-py38 !)
+  $ grep 'hgext.lfs.blobstore.LfsCorruptionError' $TESTTMP/errors.log
   $LOCALIP - - [$ERRDATE$] HG error:  hgext.lfs.blobstore.LfsCorruptionError: corrupt remote lfs object: b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:   (glob)
-  $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d': (glob)
-  Traceback (most recent call last):
-      self.do_write()
-      self.do_hgweb()
-      for chunk in self.server.application(env, self._start_response):
-      for r in self._runwsgi(req, res, repo):
-               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (py311 !)
-      handled = wireprotoserver.handlewsgirequest( (py38 !)
-                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (py311 !)
-      return _processbasictransfer( (py38 !)
-             ^^^^^^^^^^^^^^^^^^^^^^ (py311 !)
-      rctx, req, res, self.check_perm (no-py38 !)
-      rctx.repo, req, res, lambda perm: checkperm(rctx, req, perm) (no-py38 !)
-      res.setbodybytes(localstore.read(oid))
-                       ^^^^^^^^^^^^^^^^^^^^ (py311 !)
-      blob = self._read(self.vfs, oid, verify)
-             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (py311 !)
-      raise IOError(errno.EIO, r'%s: I/O error' % oid.decode("utf-8"))
-  *Error: [Errno *] 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d: I/O error (glob)
-  
-  $LOCALIP - - [$ERRDATE$] HG error:  Exception happened while processing request '/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d': (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:  Traceback (most recent call last): (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:      res.setbodybytes(localstore.read(oid)) (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:                       ^^^^^^^^^^^^^^^^^^^^ (glob) (py311 !)
-  $LOCALIP - - [$ERRDATE$] HG error:      blob = self._read(self.vfs, oid, verify) (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (glob) (py311 !)
-  $LOCALIP - - [$ERRDATE$] HG error:      blobstore._verify(oid, b'dummy content') (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:      raise LfsCorruptionError( (glob) (py38 !)
-  $LOCALIP - - [$ERRDATE$] HG error:      hint=_(b'run hg verify'), (glob) (no-py38 !)
   $LOCALIP - - [$ERRDATE$] HG error:  hgext.lfs.blobstore.LfsCorruptionError: detected corrupt lfs object: 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d (glob)
-  $LOCALIP - - [$ERRDATE$] HG error:   (glob)
+
 
 Basic Authorization headers are returned by the Batch API, and sent back with
 the GET/PUT request.
--- a/tests/test-patchbomb.t	Thu Oct 24 15:23:52 2024 +0200
+++ b/tests/test-patchbomb.t	Sat Oct 26 00:58:01 2024 +0200
@@ -2414,7 +2414,7 @@
   
 
 test multi-address parsing:
-  $ hg email --date '1980-1-1 0:1' -m tmp.mbox -f quux -t 'spam<spam><eggs>' \
+  $ hg email --date '1980-1-1 0:1' -m tmp.mbox -f quux -t 'spam<spam>' \
   >  -t toast -c 'foo,bar@example.com' -c '"A, B <>" <a@example.com>' -s test -r 0 \
   >  --config email.bcc='"Quux, A." <quux>'
   this patch series consists of 1 patches.
@@ -2435,7 +2435,7 @@
   User-Agent: Mercurial-patchbomb/* (glob)
   Date: Tue, 01 Jan 1980 00:01:00 +0000
   From: quux
-  To: =?iso-8859-1?q?spam?= <spam>, eggs, toast
+  To: =?iso-8859-1?q?spam?= <spam>, toast
   Cc: foo, bar@example.com, =?iso-8859-1?q?A=2C_B_=3C=3E?= <a@example.com>
   Bcc: =?iso-8859-1?q?Quux=2C_A=2E?= <quux>