diff --git a/mercurial/fileset.py b/mercurial/fileset.py
--- a/mercurial/fileset.py
+++ b/mercurial/fileset.py
@@ -463,6 +463,32 @@ def revs(mctx, x):
                 result.append(f)
     return result
 
+@predicate('status(base, rev, pattern)')
+def status(mctx, x):
+    """``status(base, rev, revspec)``
+
+    Evaluate predicate using status change between ``base`` and
+    ``rev``. Examples:
+
+    - ``status(3, 7, added())`` - matches files added from "3" to "7"
+    """
+    repo = mctx.ctx.repo()
+    # i18n: "status" is a keyword
+    b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
+    # i18n: "status" is a keyword
+    baseerr = _("first argument to status must be a revision")
+    baserevspec = getstring(b, baseerr)
+    if not baserevspec:
+        raise error.ParseError(baseerr)
+    reverr = _("second argument to status must be a revision")
+    revspec = getstring(r, reverr)
+    if not revspec:
+        raise error.ParseError(reverr)
+    basenode, node = scmutil.revpair(repo, [baserevspec, revspec])
+    basectx = repo[basenode]
+    ctx = repo[node]
+    return getset(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
+
 @predicate('subrepo([pattern])')
 def subrepo(mctx, x):
     """Subrepositories whose paths match the given pattern.
@@ -538,6 +564,7 @@ class fullmatchctx(matchctx):
 # filesets using matchctx.switch()
 _switchcallers = [
     'revs',
+    'status',
 ]
 
 def _intree(funcs, tree):
diff --git a/tests/test-fileset.t b/tests/test-fileset.t
--- a/tests/test-fileset.t
+++ b/tests/test-fileset.t
@@ -521,3 +521,101 @@ overlapping set
 
   $ fileset "revs('1+2', modified())"
   b2
+
+test 'status(...)'
+=================
+
+Simple case
+-----------
+
+  $ fileset "status(3, 4, added())"
+  .hgsub
+  .hgsubstate
+
+use rev to restrict matched file
+-----------------------------------------
+
+  $ hg status --removed --rev 0 --rev 1
+  R a2
+  $ fileset "status(0, 1, removed())"
+  a2
+  $ fileset "* and status(0, 1, removed())"
+  $ fileset -r 4 "status(0, 1, removed())"
+  a2
+  $ fileset -r 4 "* and status(0, 1, removed())"
+  $ fileset "revs('4', * and status(0, 1, removed()))"
+  $ fileset "revs('0', * and status(0, 1, removed()))"
+  a2
+
+check wdir()
+------------
+
+  $ hg status --removed  --rev 4
+  R con.xml
+  $ fileset "status(4, 'wdir()', removed())"
+  con.xml
+
+  $ hg status --removed --rev 2
+  R a2
+  $ fileset "status('2', 'wdir()', removed())"
+  a2
+
+test backward status
+--------------------
+
+  $ hg status --removed --rev 0 --rev 4
+  R a2
+  $ hg status --added --rev 4 --rev 0
+  A a2
+  $ fileset "status(4, 0, added())"
+  a2
+
+test cross branch status
+------------------------
+
+  $ hg status --added --rev 1 --rev 2
+  A a2
+  $ fileset "status(1, 2, added())"
+  a2
+
+test with multi revs revset
+---------------------------
+  $ hg status --added --rev 0:1 --rev 3:4
+  A .hgsub
+  A .hgsubstate
+  A 1k
+  A 2k
+  A b2link
+  A bin
+  A c1
+  A con.xml
+  $ fileset "status('0:1', '3:4', added())"
+  .hgsub
+  .hgsubstate
+  1k
+  2k
+  b2link
+  bin
+  c1
+  con.xml
+
+tests with empty value
+----------------------
+
+Fully empty revset
+
+  $ fileset "status('', '4', added())"
+  hg: parse error: first argument to status must be a revision
+  [255]
+  $ fileset "status('2', '', added())"
+  hg: parse error: second argument to status must be a revision
+  [255]
+
+Empty revset will error at the revset layer
+
+  $ fileset "status(' ', '4', added())"
+  hg: parse error at 1: not a prefix: end
+  [255]
+  $ fileset "status('2', ' ', added())"
+  hg: parse error at 1: not a prefix: end
+  [255]