diff --git a/hgext/shelve.py b/hgext/shelve.py
--- a/hgext/shelve.py
+++ b/hgext/shelve.py
@@ -33,6 +33,7 @@ from mercurial import (
     bundlerepo,
     changegroup,
     cmdutil,
+    discovery,
     error,
     exchange,
     hg,
@@ -145,8 +146,11 @@ class shelvedfile(object):
             btype = 'HG20'
             compression = 'BZ'
 
-        cg = changegroup.changegroupsubset(self.repo, bases, [node], 'shelve',
-                                           version=cgversion)
+        outgoing = discovery.outgoing(self.repo, missingroots=bases,
+                                      missingheads=[node])
+        cg = changegroup.makechangegroup(self.repo, outgoing, cgversion,
+                                         'shelve')
+
         bundle2.writebundle(self.ui, cg, self.fname, btype, self.vfs,
                                 compression=compression)
 
diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -940,22 +940,6 @@ def getsubsetraw(repo, outgoing, bundler
     _changegroupinfo(repo, csets, source)
     return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
 
-def changegroupsubset(repo, roots, heads, source, version='01'):
-    """Compute a changegroup consisting of all the nodes that are
-    descendants of any of the roots and ancestors of any of the heads.
-    Return a chunkbuffer object whose read() method will return
-    successive changegroup chunks.
-
-    It is fairly complex as determining which filenodes and which
-    manifest nodes need to be included for the changeset to be complete
-    is non-trivial.
-
-    Another wrinkle is doing the reverse, figuring out which changeset in
-    the changegroup a particular filenode or manifestnode belongs to.
-    """
-    outgoing = discovery.outgoing(repo, missingroots=roots, missingheads=heads)
-    return makechangegroup(repo, outgoing, version, source)
-
 def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None,
                            version='01'):
     """Like getbundle, but taking a discovery.outgoing as an argument.
@@ -985,7 +969,9 @@ def getlocalchangegroup(repo, *args, **k
 
 def changegroup(repo, basenodes, source):
     # to avoid a race we use changegroupsubset() (issue1320)
-    return changegroupsubset(repo, basenodes, repo.heads(), source)
+    outgoing = discovery.outgoing(repo, missingroots=basenodes,
+                                  missingheads=repo.heads())
+    return makechangegroup(repo, outgoing, '01', source)
 
 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
     revisions = 0
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -31,6 +31,7 @@ from . import (
     context,
     dirstate,
     dirstateguard,
+    discovery,
     encoding,
     error,
     exchange,
@@ -289,7 +290,9 @@ class locallegacypeer(repository.legacyp
         return changegroup.changegroup(self._repo, basenodes, source)
 
     def changegroupsubset(self, bases, heads, source):
-        return changegroup.changegroupsubset(self._repo, bases, heads, source)
+        outgoing = discovery.outgoing(self._repo, missingroots=bases,
+                                      missingheads=heads)
+        return changegroup.makechangegroup(self._repo, outgoing, '01', source)
 
     # End of baselegacywirecommands interface.
 
diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -21,6 +21,7 @@ from .node import (
 from . import (
     bundle2,
     changegroup as changegroupmod,
+    discovery,
     encoding,
     error,
     exchange,
@@ -801,7 +802,9 @@ def changegroup(repo, proto, roots):
 def changegroupsubset(repo, proto, bases, heads):
     bases = decodelist(bases)
     heads = decodelist(heads)
-    cg = changegroupmod.changegroupsubset(repo, bases, heads, 'serve')
+    outgoing = discovery.outgoing(repo, missingroots=bases,
+                                  missingheads=heads)
+    cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
     return streamres(reader=cg, v1compressible=True)
 
 @wireprotocommand('debugwireargs', 'one two *')