Patchwork [2,of,2] obsolete: reports the number of local changeset obsoleted when unbundling

login
register
mail settings
Submitter Pierre-Yves David
Date July 1, 2017, 11:03 p.m.
Message ID <56079875cf347a283833.1498950224@nodosa.octopoid.net>
Download mbox | patch
Permalink /patch/21863/
State Accepted
Headers show

Comments

Pierre-Yves David - July 1, 2017, 11:03 p.m.
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@octobus.net>
# Date 1498614859 -7200
#      Wed Jun 28 03:54:19 2017 +0200
# Node ID 56079875cf347a283833e89296310cdac78a211c
# Parent  7f2c9fb4ce2aa427e01ccf02641d218966b580d4
# EXP-Topic tr.changes
# Available At https://www.mercurial-scm.org/repo/users/marmoute/mercurial/
#              hg pull https://www.mercurial-scm.org/repo/users/marmoute/mercurial/ -r 56079875cf34
obsolete: reports the number of local changeset obsoleted when unbundling

This is a first basic visible usage of the changes tracking in the transaction.
We adds a new function computing the pre-existing changesets obsoleted by a
transaction and a transaction call back displaying this information.

Example output:

  added 1 changesets with 1 changes to 1 files (+1 heads)
  3 new obsolescence markers
  obsoleted 1 changesets

The goal is to evolve the transaction summary into something bigger, gathering
existing output there and adding new useful one. This patch is a good first step
on this road. The new output is basic but give a user to the content of
tr.changes['obsmarkers'] and give an idea of the new options we haves. I expect
to revisit the message soon.

The caller recording the transaction summary should also be moved into a more
generic location but further refactoring is needed before it can happen.
Sean Farley - July 1, 2017, 11:49 p.m.
Pierre-Yves David <pierre-yves.david@ens-lyon.org> writes:

> # HG changeset patch
> # User Pierre-Yves David <pierre-yves.david@octobus.net>
> # Date 1498614859 -7200
> #      Wed Jun 28 03:54:19 2017 +0200
> # Node ID 56079875cf347a283833e89296310cdac78a211c
> # Parent  7f2c9fb4ce2aa427e01ccf02641d218966b580d4
> # EXP-Topic tr.changes
> # Available At https://www.mercurial-scm.org/repo/users/marmoute/mercurial/
> #              hg pull https://www.mercurial-scm.org/repo/users/marmoute/mercurial/ -r 56079875cf34
> obsolete: reports the number of local changeset obsoleted when unbundling
>
> This is a first basic visible usage of the changes tracking in the transaction.
> We adds a new function computing the pre-existing changesets obsoleted by a
> transaction and a transaction call back displaying this information.
>
> Example output:
>
>   added 1 changesets with 1 changes to 1 files (+1 heads)
>   3 new obsolescence markers
>   obsoleted 1 changesets
>
> The goal is to evolve the transaction summary into something bigger, gathering
> existing output there and adding new useful one. This patch is a good first step
> on this road. The new output is basic but give a user to the content of
> tr.changes['obsmarkers'] and give an idea of the new options we haves. I expect
> to revisit the message soon.
>
> The caller recording the transaction summary should also be moved into a more
> generic location but further refactoring is needed before it can happen.

Ah, cool! I can use this to improve our webhook system at Bitbucket :-)

Nits below.

> diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
> --- a/mercurial/bundle2.py
> +++ b/mercurial/bundle2.py
> @@ -161,6 +161,7 @@ from . import (
>      phases,
>      pushkey,
>      pycompat,
> +    scmutil,
>      tags,
>      url,
>      util,
> @@ -1810,6 +1811,7 @@ def handleobsmarker(op, inpart):
>      if new:
>          op.repo.ui.status(_('%i new obsolescence markers\n') % new)
>      op.records.add('obsmarkers', {'new': new})
> +    scmutil.registersummarycallback(op.repo, tr)
>      if op.reply is not None:
>          rpart = op.reply.newpart('reply:obsmarkers')
>          rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
> diff --git a/mercurial/obsutil.py b/mercurial/obsutil.py
> --- a/mercurial/obsutil.py
> +++ b/mercurial/obsutil.py
> @@ -7,6 +7,10 @@
>  
>  from __future__ import absolute_import
>  
> +from . import (
> +    phases,
> +)
> +
>  class marker(object):
>      """Wrap obsolete marker raw data"""
>  
> @@ -285,6 +289,28 @@ def foreground(repo, nodes):
>              foreground = set(repo.set('%ln::', known))
>      return set(c.node() for c in foreground)
>  
> +def getobsoleted(repo, tr):
> +    """return the set of pre-existing revisions obsoleted by a transaction"""
> +    torev = repo.unfiltered().changelog.nodemap.get
> +    phase = repo._phasecache.phase
> +    succsmarkers = repo.obsstore.successors.get
> +    public = phases.public

Sometimes I really dislike Python.

> +    addedmarkers = tr.changes.get('obsmarkers')
> +    addedrevs = tr.changes.get('revs')
> +    seenrevs = set(addedrevs)
> +    obsoleted = set()
> +    for mark in addedmarkers:
> +        node = mark[0]
> +        rev = torev(node)
> +        if rev is None or rev in seenrevs:
> +            continue
> +        seenrevs.add(rev)
> +        if phase(repo, rev) == public:
> +            continue
> +        if set(succsmarkers(node)).issubset(addedmarkers):
> +            obsoleted.add(rev)
> +    return obsoleted
> +
>  def successorssets(repo, initialnode, cache=None):
>      """Return set of all latest successors of initial nodes
>  
> diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
> --- a/mercurial/scmutil.py
> +++ b/mercurial/scmutil.py
> @@ -13,6 +13,7 @@ import hashlib
>  import os
>  import re
>  import socket
> +import weakref
>  
>  from .i18n import _
>  from .node import (
> @@ -22,11 +23,13 @@ from .node import (
>      wdirrev,
>  )
>  
> +from .i18n import _
>  from . import (
>      encoding,
>      error,
>      match as matchmod,
>      obsolete,
> +    obsutil,
>      pathutil,
>      phases,
>      pycompat,
> @@ -1059,3 +1062,15 @@ class simplekeyvaluefile(object):
>              lines.append("%s=%s\n" % (k, v))
>          with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
>              fp.write(''.join(lines))
> +
> +def registersummarycallback(repo, otr):
> +    """register a callback to issue a summary after the transaction is closed
> +    """
> +    reporef = weakref.ref(repo)

Is a weakref the best we can do here? I really dislike them but I don't
know anything else off the top of my head.
Pierre-Yves David - July 1, 2017, 11:58 p.m.
On 07/02/2017 01:49 AM, Sean Farley wrote:
> 
> Pierre-Yves David <pierre-yves.david@ens-lyon.org> writes:
> 
>> # HG changeset patch
>> # User Pierre-Yves David <pierre-yves.david@octobus.net>
>> # Date 1498614859 -7200
>> #      Wed Jun 28 03:54:19 2017 +0200
>> # Node ID 56079875cf347a283833e89296310cdac78a211c
>> # Parent  7f2c9fb4ce2aa427e01ccf02641d218966b580d4
>> # EXP-Topic tr.changes
>> # Available At https://www.mercurial-scm.org/repo/users/marmoute/mercurial/
>> #              hg pull https://www.mercurial-scm.org/repo/users/marmoute/mercurial/ -r 56079875cf34
>> obsolete: reports the number of local changeset obsoleted when unbundling
>>
>> This is a first basic visible usage of the changes tracking in the transaction.
>> We adds a new function computing the pre-existing changesets obsoleted by a
>> transaction and a transaction call back displaying this information.
>>
>> Example output:
>>
>>    added 1 changesets with 1 changes to 1 files (+1 heads)
>>    3 new obsolescence markers
>>    obsoleted 1 changesets
>>
>> The goal is to evolve the transaction summary into something bigger, gathering
>> existing output there and adding new useful one. This patch is a good first step
>> on this road. The new output is basic but give a user to the content of
>> tr.changes['obsmarkers'] and give an idea of the new options we haves. I expect
>> to revisit the message soon.
>>
>> The caller recording the transaction summary should also be moved into a more
>> generic location but further refactoring is needed before it can happen.
> 
> Ah, cool! I can use this to improve our webhook system at Bitbucket :-)

Yep, one of the goal of tr.changes goal is to be able to have simple and 
accurate hooks for all relevant events.

> 
> Nits below.
> 
>> diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
>> --- a/mercurial/bundle2.py
>> +++ b/mercurial/bundle2.py
>> @@ -161,6 +161,7 @@ from . import (
>>       phases,
>>       pushkey,
>>       pycompat,
>> +    scmutil,
>>       tags,
>>       url,
>>       util,
>> @@ -1810,6 +1811,7 @@ def handleobsmarker(op, inpart):
>>       if new:
>>           op.repo.ui.status(_('%i new obsolescence markers\n') % new)
>>       op.records.add('obsmarkers', {'new': new})
>> +    scmutil.registersummarycallback(op.repo, tr)
>>       if op.reply is not None:
>>           rpart = op.reply.newpart('reply:obsmarkers')
>>           rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
>> diff --git a/mercurial/obsutil.py b/mercurial/obsutil.py
>> --- a/mercurial/obsutil.py
>> +++ b/mercurial/obsutil.py
>> @@ -7,6 +7,10 @@
>>   
>>   from __future__ import absolute_import
>>   
>> +from . import (
>> +    phases,
>> +)
>> +
>>   class marker(object):
>>       """Wrap obsolete marker raw data"""
>>   
>> @@ -285,6 +289,28 @@ def foreground(repo, nodes):
>>               foreground = set(repo.set('%ln::', known))
>>       return set(c.node() for c in foreground)
>>   
>> +def getobsoleted(repo, tr):
>> +    """return the set of pre-existing revisions obsoleted by a transaction"""
>> +    torev = repo.unfiltered().changelog.nodemap.get
>> +    phase = repo._phasecache.phase
>> +    succsmarkers = repo.obsstore.successors.get
>> +    public = phases.public
> 
> Sometimes I really dislike Python.

Yep :-)


>> +    addedmarkers = tr.changes.get('obsmarkers')
>> +    addedrevs = tr.changes.get('revs')
>> +    seenrevs = set(addedrevs)
>> +    obsoleted = set()
>> +    for mark in addedmarkers:
>> +        node = mark[0]
>> +        rev = torev(node)
>> +        if rev is None or rev in seenrevs:
>> +            continue
>> +        seenrevs.add(rev)
>> +        if phase(repo, rev) == public:
>> +            continue
>> +        if set(succsmarkers(node)).issubset(addedmarkers):
>> +            obsoleted.add(rev)
>> +    return obsoleted
>> +
>>   def successorssets(repo, initialnode, cache=None):
>>       """Return set of all latest successors of initial nodes
>>   
>> diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
>> --- a/mercurial/scmutil.py
>> +++ b/mercurial/scmutil.py
>> @@ -13,6 +13,7 @@ import hashlib
>>   import os
>>   import re
>>   import socket
>> +import weakref
>>   
>>   from .i18n import _
>>   from .node import (
>> @@ -22,11 +23,13 @@ from .node import (
>>       wdirrev,
>>   )
>>   
>> +from .i18n import _
>>   from . import (
>>       encoding,
>>       error,
>>       match as matchmod,
>>       obsolete,
>> +    obsutil,
>>       pathutil,
>>       phases,
>>       pycompat,
>> @@ -1059,3 +1062,15 @@ class simplekeyvaluefile(object):
>>               lines.append("%s=%s\n" % (k, v))
>>           with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
>>               fp.write(''.join(lines))
>> +
>> +def registersummarycallback(repo, otr):
>> +    """register a callback to issue a summary after the transaction is closed
>> +    """
>> +    reporef = weakref.ref(repo)
> 
> Is a weakref the best we can do here? I really dislike them but I don't
> know anything else off the top of my head.

We need a reference to the full repository in this. When it comes to 
transaction and callback with it, we use weakref to avoid cycle. You can 
find one example here:

 
https://www.mercurial-scm.org/repo/hg/file/4d780d510b44/mercurial/localrepo.py#l979

Cheers,
Sean Farley - July 5, 2017, 3:32 a.m.
Pierre-Yves David <pierre-yves.david@ens-lyon.org> writes:

> On 07/02/2017 01:49 AM, Sean Farley wrote:
>>
>> Pierre-Yves David <pierre-yves.david@ens-lyon.org> writes:
>>
>>> # HG changeset patch
>>> # User Pierre-Yves David <pierre-yves.david@octobus.net>
>>> # Date 1498614859 -7200
>>> #      Wed Jun 28 03:54:19 2017 +0200
>>> # Node ID 56079875cf347a283833e89296310cdac78a211c
>>> # Parent  7f2c9fb4ce2aa427e01ccf02641d218966b580d4
>>> # EXP-Topic tr.changes
>>> # Available At https://www.mercurial-scm.org/repo/users/marmoute/mercurial/
>>> #              hg pull https://www.mercurial-scm.org/repo/users/marmoute/mercurial/ -r 56079875cf34
>>> obsolete: reports the number of local changeset obsoleted when unbundling
>>>
>>> This is a first basic visible usage of the changes tracking in the transaction.
>>> We adds a new function computing the pre-existing changesets obsoleted by a
>>> transaction and a transaction call back displaying this information.
>>>
>>> Example output:
>>>
>>>    added 1 changesets with 1 changes to 1 files (+1 heads)
>>>    3 new obsolescence markers
>>>    obsoleted 1 changesets
>>>
>>> The goal is to evolve the transaction summary into something bigger, gathering
>>> existing output there and adding new useful one. This patch is a good first step
>>> on this road. The new output is basic but give a user to the content of
>>> tr.changes['obsmarkers'] and give an idea of the new options we haves. I expect
>>> to revisit the message soon.
>>>
>>> The caller recording the transaction summary should also be moved into a more
>>> generic location but further refactoring is needed before it can happen.
>>
>> Ah, cool! I can use this to improve our webhook system at Bitbucket :-)
>
> Yep, one of the goal of tr.changes goal is to be able to have simple and
> accurate hooks for all relevant events.
>
>>
>> Nits below.
>>
>>> diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
>>> --- a/mercurial/bundle2.py
>>> +++ b/mercurial/bundle2.py
>>> @@ -161,6 +161,7 @@ from . import (
>>>       phases,
>>>       pushkey,
>>>       pycompat,
>>> +    scmutil,
>>>       tags,
>>>       url,
>>>       util,
>>> @@ -1810,6 +1811,7 @@ def handleobsmarker(op, inpart):
>>>       if new:
>>>           op.repo.ui.status(_('%i new obsolescence markers\n') % new)
>>>       op.records.add('obsmarkers', {'new': new})
>>> +    scmutil.registersummarycallback(op.repo, tr)
>>>       if op.reply is not None:
>>>           rpart = op.reply.newpart('reply:obsmarkers')
>>>           rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
>>> diff --git a/mercurial/obsutil.py b/mercurial/obsutil.py
>>> --- a/mercurial/obsutil.py
>>> +++ b/mercurial/obsutil.py
>>> @@ -7,6 +7,10 @@
>>>     from __future__ import absolute_import
>>>   +from . import (
>>> +    phases,
>>> +)
>>> +
>>>   class marker(object):
>>>       """Wrap obsolete marker raw data"""
>>>   @@ -285,6 +289,28 @@ def foreground(repo, nodes):
>>>               foreground = set(repo.set('%ln::', known))
>>>       return set(c.node() for c in foreground)
>>>   +def getobsoleted(repo, tr):
>>> +    """return the set of pre-existing revisions obsoleted by a transaction"""
>>> +    torev = repo.unfiltered().changelog.nodemap.get
>>> +    phase = repo._phasecache.phase
>>> +    succsmarkers = repo.obsstore.successors.get
>>> +    public = phases.public
>>
>> Sometimes I really dislike Python.
>
> Yep :-)
>
>
>>> +    addedmarkers = tr.changes.get('obsmarkers')
>>> +    addedrevs = tr.changes.get('revs')
>>> +    seenrevs = set(addedrevs)
>>> +    obsoleted = set()
>>> +    for mark in addedmarkers:
>>> +        node = mark[0]
>>> +        rev = torev(node)
>>> +        if rev is None or rev in seenrevs:
>>> +            continue
>>> +        seenrevs.add(rev)
>>> +        if phase(repo, rev) == public:
>>> +            continue
>>> +        if set(succsmarkers(node)).issubset(addedmarkers):
>>> +            obsoleted.add(rev)
>>> +    return obsoleted
>>> +
>>>   def successorssets(repo, initialnode, cache=None):
>>>       """Return set of all latest successors of initial nodes
>>>   diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
>>> --- a/mercurial/scmutil.py
>>> +++ b/mercurial/scmutil.py
>>> @@ -13,6 +13,7 @@ import hashlib
>>>   import os
>>>   import re
>>>   import socket
>>> +import weakref
>>>     from .i18n import _
>>>   from .node import (
>>> @@ -22,11 +23,13 @@ from .node import (
>>>       wdirrev,
>>>   )
>>>   +from .i18n import _
>>>   from . import (
>>>       encoding,
>>>       error,
>>>       match as matchmod,
>>>       obsolete,
>>> +    obsutil,
>>>       pathutil,
>>>       phases,
>>>       pycompat,
>>> @@ -1059,3 +1062,15 @@ class simplekeyvaluefile(object):
>>>               lines.append("%s=%s\n" % (k, v))
>>>           with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
>>>               fp.write(''.join(lines))
>>> +
>>> +def registersummarycallback(repo, otr):
>>> +    """register a callback to issue a summary after the transaction is closed
>>> +    """
>>> +    reporef = weakref.ref(repo)
>>
>> Is a weakref the best we can do here? I really dislike them but I don't
>> know anything else off the top of my head.
>
> We need a reference to the full repository in this. When it comes to
> transaction and callback with it, we use weakref to avoid cycle. You can find
> one example here:
>
>
> https://www.mercurial-scm.org/repo/hg/file/4d780d510b44/mercurial/localrepo.py#l979

Ok, makes sense. I've gone ahead and queued this series as well.

Patch

diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -161,6 +161,7 @@  from . import (
     phases,
     pushkey,
     pycompat,
+    scmutil,
     tags,
     url,
     util,
@@ -1810,6 +1811,7 @@  def handleobsmarker(op, inpart):
     if new:
         op.repo.ui.status(_('%i new obsolescence markers\n') % new)
     op.records.add('obsmarkers', {'new': new})
+    scmutil.registersummarycallback(op.repo, tr)
     if op.reply is not None:
         rpart = op.reply.newpart('reply:obsmarkers')
         rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
diff --git a/mercurial/obsutil.py b/mercurial/obsutil.py
--- a/mercurial/obsutil.py
+++ b/mercurial/obsutil.py
@@ -7,6 +7,10 @@ 
 
 from __future__ import absolute_import
 
+from . import (
+    phases,
+)
+
 class marker(object):
     """Wrap obsolete marker raw data"""
 
@@ -285,6 +289,28 @@  def foreground(repo, nodes):
             foreground = set(repo.set('%ln::', known))
     return set(c.node() for c in foreground)
 
+def getobsoleted(repo, tr):
+    """return the set of pre-existing revisions obsoleted by a transaction"""
+    torev = repo.unfiltered().changelog.nodemap.get
+    phase = repo._phasecache.phase
+    succsmarkers = repo.obsstore.successors.get
+    public = phases.public
+    addedmarkers = tr.changes.get('obsmarkers')
+    addedrevs = tr.changes.get('revs')
+    seenrevs = set(addedrevs)
+    obsoleted = set()
+    for mark in addedmarkers:
+        node = mark[0]
+        rev = torev(node)
+        if rev is None or rev in seenrevs:
+            continue
+        seenrevs.add(rev)
+        if phase(repo, rev) == public:
+            continue
+        if set(succsmarkers(node)).issubset(addedmarkers):
+            obsoleted.add(rev)
+    return obsoleted
+
 def successorssets(repo, initialnode, cache=None):
     """Return set of all latest successors of initial nodes
 
diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py
--- a/mercurial/scmutil.py
+++ b/mercurial/scmutil.py
@@ -13,6 +13,7 @@  import hashlib
 import os
 import re
 import socket
+import weakref
 
 from .i18n import _
 from .node import (
@@ -22,11 +23,13 @@  from .node import (
     wdirrev,
 )
 
+from .i18n import _
 from . import (
     encoding,
     error,
     match as matchmod,
     obsolete,
+    obsutil,
     pathutil,
     phases,
     pycompat,
@@ -1059,3 +1062,15 @@  class simplekeyvaluefile(object):
             lines.append("%s=%s\n" % (k, v))
         with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
             fp.write(''.join(lines))
+
+def registersummarycallback(repo, otr):
+    """register a callback to issue a summary after the transaction is closed
+    """
+    reporef = weakref.ref(repo)
+    def reportsummary(tr):
+        """the actual callback reporting the summary"""
+        repo = reporef()
+        obsoleted = obsutil.getobsoleted(repo, tr)
+        if obsoleted:
+            repo.ui.status(_('obsoleted %i changesets\n') % len(obsoleted))
+    otr.addpostclose('00-txnreport', reportsummary)
diff --git a/tests/test-bookmarks-pushpull.t b/tests/test-bookmarks-pushpull.t
--- a/tests/test-bookmarks-pushpull.t
+++ b/tests/test-bookmarks-pushpull.t
@@ -484,6 +484,7 @@  Update to a successor works
   remote: adding file changes
   remote: added 2 changesets with 2 changes to 1 files (+1 heads)
   remote: 2 new obsolescence markers
+  remote: obsoleted 1 changesets
   updating bookmark Y
   $ hg -R ../a book
      @                         1:0d2164f0ce0d
diff --git a/tests/test-exchange-obsmarkers-case-A3.t b/tests/test-exchange-obsmarkers-case-A3.t
--- a/tests/test-exchange-obsmarkers-case-A3.t
+++ b/tests/test-exchange-obsmarkers-case-A3.t
@@ -212,6 +212,7 @@  test obsmarkers exchange.
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files (+1 heads)
   remote: 1 new obsolescence markers
+  remote: obsoleted 1 changesets
   ## post push state
   # obstore: main
   28b51eb45704506b5c603decd6bf7ac5e0f6a52f e5ea8f9c73143125d36658e90ef70c6d2027a5b7 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
@@ -227,6 +228,7 @@  test obsmarkers exchange.
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   1 new obsolescence markers
+  obsoleted 1 changesets
   (run 'hg heads' to see heads, 'hg merge' to merge)
   ## post pull state
   # obstore: main
diff --git a/tests/test-exchange-obsmarkers-case-A6.t b/tests/test-exchange-obsmarkers-case-A6.t
--- a/tests/test-exchange-obsmarkers-case-A6.t
+++ b/tests/test-exchange-obsmarkers-case-A6.t
@@ -97,6 +97,7 @@  Actual Test (explicit push version)
   searching for changes
   no changes found
   remote: 1 new obsolescence markers
+  remote: obsoleted 1 changesets
   ## post push state
   # obstore: main
   28b51eb45704506b5c603decd6bf7ac5e0f6a52f e5ea8f9c73143125d36658e90ef70c6d2027a5b7 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
@@ -107,6 +108,7 @@  Actual Test (explicit push version)
   pulling from main
   no changes found
   1 new obsolescence markers
+  obsoleted 1 changesets
   ## post pull state
   # obstore: main
   28b51eb45704506b5c603decd6bf7ac5e0f6a52f e5ea8f9c73143125d36658e90ef70c6d2027a5b7 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
@@ -130,6 +132,7 @@  Actual Test (bare push version)
   searching for changes
   no changes found
   remote: 1 new obsolescence markers
+  remote: obsoleted 1 changesets
   ## post push state
   # obstore: main
   28b51eb45704506b5c603decd6bf7ac5e0f6a52f e5ea8f9c73143125d36658e90ef70c6d2027a5b7 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
@@ -141,6 +144,7 @@  Actual Test (bare push version)
   searching for changes
   no changes found
   1 new obsolescence markers
+  obsoleted 1 changesets
   ## post pull state
   # obstore: main
   28b51eb45704506b5c603decd6bf7ac5e0f6a52f e5ea8f9c73143125d36658e90ef70c6d2027a5b7 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
diff --git a/tests/test-obsmarker-template.t b/tests/test-obsmarker-template.t
--- a/tests/test-obsmarker-template.t
+++ b/tests/test-obsmarker-template.t
@@ -735,6 +735,7 @@  Test setup
   adding file changes
   added 1 changesets with 0 changes to 1 files (+1 heads)
   2 new obsolescence markers
+  obsoleted 1 changesets
   (run 'hg heads' to see heads, 'hg merge' to merge)
   $ hg log --hidden -G
   o  changeset:   2:7a230b46bf61
diff --git a/tests/test-obsolete-bundle-strip.t b/tests/test-obsolete-bundle-strip.t
--- a/tests/test-obsolete-bundle-strip.t
+++ b/tests/test-obsolete-bundle-strip.t
@@ -205,6 +205,7 @@  Actual testing
   # unbundling: adding file changes
   # unbundling: added 1 changesets with 1 changes to 1 files (+1 heads)
   # unbundling: 2 new obsolescence markers
+  # unbundling: obsoleted 1 changesets
   # unbundling: (run 'hg heads' to see heads)
 
   $ testrevs 'desc("C-A")'
@@ -366,6 +367,7 @@  problematic)
   # unbundling: adding file changes
   # unbundling: added 1 changesets with 1 changes to 1 files (+1 heads)
   # unbundling: 1 new obsolescence markers
+  # unbundling: obsoleted 1 changesets
   # unbundling: (run 'hg heads' to see heads)
 
 bundling multiple revisions
@@ -957,6 +959,7 @@  Actual testing
   # unbundling: adding file changes
   # unbundling: added 1 changesets with 1 changes to 1 files (+1 heads)
   # unbundling: 6 new obsolescence markers
+  # unbundling: obsoleted 3 changesets
   # unbundling: (run 'hg heads' to see heads)
 
 Bundle multiple revisions
@@ -1057,6 +1060,7 @@  Bundle multiple revisions
   # unbundling: adding file changes
   # unbundling: added 2 changesets with 2 changes to 2 files (+2 heads)
   # unbundling: 7 new obsolescence markers
+  # unbundling: obsoleted 2 changesets
   # unbundling: (run 'hg heads' to see heads)
 
 * top one and initial precursors
@@ -1122,6 +1126,7 @@  Bundle multiple revisions
   # unbundling: adding file changes
   # unbundling: added 2 changesets with 2 changes to 2 files (+2 heads)
   # unbundling: 6 new obsolescence markers
+  # unbundling: obsoleted 3 changesets
   # unbundling: (run 'hg heads' to see heads)
 
 * top one and one of the split
@@ -1189,6 +1194,7 @@  Bundle multiple revisions
   # unbundling: adding file changes
   # unbundling: added 2 changesets with 2 changes to 2 files (+2 heads)
   # unbundling: 7 new obsolescence markers
+  # unbundling: obsoleted 2 changesets
   # unbundling: (run 'hg heads' to see heads)
 
 * all
diff --git a/tests/test-obsolete.t b/tests/test-obsolete.t
--- a/tests/test-obsolete.t
+++ b/tests/test-obsolete.t
@@ -613,6 +613,7 @@  Do not warn about new head when the new 
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   1 new obsolescence markers
+  obsoleted 1 changesets
 
 test relevance computation
 ---------------------------------------
diff --git a/tests/test-push-checkheads-pruned-B1.t b/tests/test-push-checkheads-pruned-B1.t
--- a/tests/test-push-checkheads-pruned-B1.t
+++ b/tests/test-push-checkheads-pruned-B1.t
@@ -68,5 +68,6 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   1 new obsolescence markers
+  obsoleted 1 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-pruned-B2.t b/tests/test-push-checkheads-pruned-B2.t
--- a/tests/test-push-checkheads-pruned-B2.t
+++ b/tests/test-push-checkheads-pruned-B2.t
@@ -81,5 +81,6 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   2 new obsolescence markers
+  obsoleted 2 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-pruned-B3.t b/tests/test-push-checkheads-pruned-B3.t
--- a/tests/test-push-checkheads-pruned-B3.t
+++ b/tests/test-push-checkheads-pruned-B3.t
@@ -81,6 +81,7 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   2 new obsolescence markers
+  obsoleted 2 changesets
 
   $ cd ../..
 
diff --git a/tests/test-push-checkheads-pruned-B4.t b/tests/test-push-checkheads-pruned-B4.t
--- a/tests/test-push-checkheads-pruned-B4.t
+++ b/tests/test-push-checkheads-pruned-B4.t
@@ -82,5 +82,6 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   2 new obsolescence markers
+  obsoleted 2 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-pruned-B5.t b/tests/test-push-checkheads-pruned-B5.t
--- a/tests/test-push-checkheads-pruned-B5.t
+++ b/tests/test-push-checkheads-pruned-B5.t
@@ -88,5 +88,6 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   3 new obsolescence markers
+  obsoleted 3 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-pruned-B6.t b/tests/test-push-checkheads-pruned-B6.t
--- a/tests/test-push-checkheads-pruned-B6.t
+++ b/tests/test-push-checkheads-pruned-B6.t
@@ -74,5 +74,6 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   2 new obsolescence markers
+  obsoleted 1 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-pruned-B7.t b/tests/test-push-checkheads-pruned-B7.t
--- a/tests/test-push-checkheads-pruned-B7.t
+++ b/tests/test-push-checkheads-pruned-B7.t
@@ -73,5 +73,6 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   2 new obsolescence markers
+  obsoleted 1 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-pruned-B8.t b/tests/test-push-checkheads-pruned-B8.t
--- a/tests/test-push-checkheads-pruned-B8.t
+++ b/tests/test-push-checkheads-pruned-B8.t
@@ -94,5 +94,6 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   4 new obsolescence markers
+  obsoleted 2 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-superceed-A1.t b/tests/test-push-checkheads-superceed-A1.t
--- a/tests/test-push-checkheads-superceed-A1.t
+++ b/tests/test-push-checkheads-superceed-A1.t
@@ -65,5 +65,6 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   1 new obsolescence markers
+  obsoleted 1 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-superceed-A2.t b/tests/test-push-checkheads-superceed-A2.t
--- a/tests/test-push-checkheads-superceed-A2.t
+++ b/tests/test-push-checkheads-superceed-A2.t
@@ -83,5 +83,6 @@  Actual testing
   adding file changes
   added 2 changesets with 2 changes to 2 files (+1 heads)
   2 new obsolescence markers
+  obsoleted 2 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-superceed-A3.t b/tests/test-push-checkheads-superceed-A3.t
--- a/tests/test-push-checkheads-superceed-A3.t
+++ b/tests/test-push-checkheads-superceed-A3.t
@@ -86,5 +86,6 @@  Actual testing
   adding file changes
   added 2 changesets with 2 changes to 2 files (+1 heads)
   2 new obsolescence markers
+  obsoleted 2 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-superceed-A4.t b/tests/test-push-checkheads-superceed-A4.t
--- a/tests/test-push-checkheads-superceed-A4.t
+++ b/tests/test-push-checkheads-superceed-A4.t
@@ -70,5 +70,6 @@  Actual testing
   adding file changes
   added 2 changesets with 2 changes to 2 files (+1 heads)
   1 new obsolescence markers
+  obsoleted 1 changesets
 
   $ cd ../../
diff --git a/tests/test-push-checkheads-superceed-A5.t b/tests/test-push-checkheads-superceed-A5.t
--- a/tests/test-push-checkheads-superceed-A5.t
+++ b/tests/test-push-checkheads-superceed-A5.t
@@ -70,6 +70,7 @@  Actual testing
   adding file changes
   added 2 changesets with 2 changes to 2 files (+1 heads)
   1 new obsolescence markers
+  obsoleted 1 changesets
 
   $ cd ../..
 
diff --git a/tests/test-push-checkheads-superceed-A6.t b/tests/test-push-checkheads-superceed-A6.t
--- a/tests/test-push-checkheads-superceed-A6.t
+++ b/tests/test-push-checkheads-superceed-A6.t
@@ -94,5 +94,6 @@  Actual testing
   adding file changes
   added 2 changesets with 2 changes to 2 files (+1 heads)
   2 new obsolescence markers
+  obsoleted 2 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-superceed-A7.t b/tests/test-push-checkheads-superceed-A7.t
--- a/tests/test-push-checkheads-superceed-A7.t
+++ b/tests/test-push-checkheads-superceed-A7.t
@@ -94,5 +94,6 @@  Actual testing
   adding file changes
   added 2 changesets with 2 changes to 2 files (+1 heads)
   2 new obsolescence markers
+  obsoleted 2 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-superceed-A8.t b/tests/test-push-checkheads-superceed-A8.t
--- a/tests/test-push-checkheads-superceed-A8.t
+++ b/tests/test-push-checkheads-superceed-A8.t
@@ -75,5 +75,6 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   2 new obsolescence markers
+  obsoleted 1 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-unpushed-D4.t b/tests/test-push-checkheads-unpushed-D4.t
--- a/tests/test-push-checkheads-unpushed-D4.t
+++ b/tests/test-push-checkheads-unpushed-D4.t
@@ -118,5 +118,6 @@  Actual testing (existing branch only)
   adding file changes
   added 1 changesets with 1 changes to 1 files
   1 new obsolescence markers
+  obsoleted 1 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-unpushed-D5.t b/tests/test-push-checkheads-unpushed-D5.t
--- a/tests/test-push-checkheads-unpushed-D5.t
+++ b/tests/test-push-checkheads-unpushed-D5.t
@@ -103,5 +103,6 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files
   1 new obsolescence markers
+  obsoleted 1 changesets
 
   $ cd ../..
diff --git a/tests/test-push-checkheads-unpushed-D7.t b/tests/test-push-checkheads-unpushed-D7.t
--- a/tests/test-push-checkheads-unpushed-D7.t
+++ b/tests/test-push-checkheads-unpushed-D7.t
@@ -92,5 +92,6 @@  Actual testing
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   3 new obsolescence markers
+  obsoleted 1 changesets
 
   $ cd ../..
diff --git a/tests/test-push-race.t b/tests/test-push-race.t
--- a/tests/test-push-race.t
+++ b/tests/test-push-race.t
@@ -1665,6 +1665,7 @@  racing commit push a new head obsoleting
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   1 new obsolescence markers
+  obsoleted 1 changesets
   (run 'hg heads .' to see heads, 'hg merge' to merge)
   $ hg -R ./client-other pull
   pulling from ssh://user@dummy/server
@@ -1674,6 +1675,7 @@  racing commit push a new head obsoleting
   adding file changes
   added 1 changesets with 1 changes to 1 files (+1 heads)
   1 new obsolescence markers
+  obsoleted 1 changesets
   (run 'hg heads .' to see heads, 'hg merge' to merge)
   $ hg -R ./client-racy pull
   pulling from ssh://user@dummy/server
@@ -1770,6 +1772,7 @@  Pushing
   remote: adding file changes
   remote: added 1 changesets with 0 changes to 1 files (+1 heads)
   remote: 1 new obsolescence markers
+  remote: obsoleted 1 changesets
 
   $ release $TESTTMP/watchfile