diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -407,6 +407,19 @@ def makememctx(repo, parents, text, user
                  date, extra, editor)
     return ctx
 
+def _filterederror(repo, changeid):
+    """build an exception to be raised about a filtered changeid
+
+    This is extracted in a function to help extensions (eg: evolve) to
+    experiment with various message variants."""
+    if repo.filtername.startswith('visible'):
+        msg = _("hidden revision '%s'") % changeid
+        hint = _('use --hidden to access hidden revisions')
+        return error.FilteredRepoLookupError(msg, hint=hint)
+    msg = _("filtered revision '%s' (not in '%s' subset)")
+    msg %= (changeid, repo.filtername)
+    return error.FilteredRepoLookupError(msg)
+
 class changectx(basectx):
     """A changecontext object makes access to data related to a particular
     changeset convenient. It represents a read-only context already present in
@@ -513,13 +526,7 @@ class changectx(basectx):
                 pass
         except (error.FilteredIndexError, error.FilteredLookupError,
                 error.FilteredRepoLookupError):
-            if repo.filtername.startswith('visible'):
-                msg = _("hidden revision '%s'") % changeid
-                hint = _('use --hidden to access hidden revisions')
-                raise error.FilteredRepoLookupError(msg, hint=hint)
-            msg = _("filtered revision '%s' (not in '%s' subset)")
-            msg %= (changeid, repo.filtername)
-            raise error.FilteredRepoLookupError(msg)
+            raise _filterederror(repo, changeid)
         except IndexError:
             pass
         raise error.RepoLookupError(