# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@ens-lyon.org>
# Date 2012-07-06 17:34:09
# Node ID c621f84dbb35ddab18b82aa25eb49395f0931dc3
# Parent  12fdaa30063a6694492c35f6c39bae90c3a4c1ac

obsolete: compute extinct changesets

`extinct` changesets are obsolete changesets with obsolete descendants only. They
are of no interest anymore and can be:

- exclude from exchange
- hidden to the user in most situation
- safely garbage collected

This changeset just allows mercurial to detect them.

The implementation is a bit naive, as for unstable changesets. We better use a
simple revset query and a cache, but simple version comes first.

diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -235,6 +235,21 @@ class changectx(object):
         return (self.node() in self._repo.obsstore.precursors
                 and self.phase() > phases.public)
 
+    def extinct(self):
+        """True if the changeset is extinct"""
+        # We should just compute a cache a check againts it.
+        # see revset implementation for details
+        #
+        # But this naive implementation does not require cache
+        if self.phase() <= phases.public:
+            return False
+        if not self.obsolete():
+            return False
+        for desc in self.descendants():
+            if not desc.obsolete():
+                return False
+        return True
+
     def unstable(self):
         """True if the changeset is not obsolete but it's ancestor are"""
         # We should just compute /(obsolete()::) - obsolete()/
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -568,6 +568,13 @@ def draft(repo, subset, x):
     pc = repo._phasecache
     return [r for r in subset if pc.phase(repo, r) == phases.draft]
 
+def extinct(repo, subset, x):
+    """``extinct()``
+    obsolete changeset with obsolete descendant only."""
+    getargs(x, 0, 0, _("obsolete takes no arguments"))
+    extinctset = set(repo.revs('(obsolete()::) - (::(not obsolete()))'))
+    return [r for r in subset if r in extinctset]
+
 def extra(repo, subset, x):
     """``extra(label, [value])``
     Changesets with the given label in the extra metadata, with the given
@@ -1365,6 +1372,7 @@ symbols = {
     "descendants": descendants,
     "_firstdescendants": _firstdescendants,
     "draft": draft,
+    "extinct": extinct,
     "extra": extra,
     "file": hasfile,
     "filelog": filelog,
diff --git a/tests/test-obsolete.t b/tests/test-obsolete.t
--- a/tests/test-obsolete.t
+++ b/tests/test-obsolete.t
@@ -320,3 +320,13 @@ detect outgoing obsolete and unstable
   searching for changes
   abort: push includes an unstable changeset: 7878242aeece!
   [255]
+
+Test that extinct changeset are properly detected
+
+  $ hg log -r 'extinct()'
+  changeset:   3:cdbce2fbb163
+  parent:      1:7c3bad9141dc
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     add new_c
+