diff --git a/mercurial/revlogutils/nodemap.py b/mercurial/revlogutils/nodemap.py
--- a/mercurial/revlogutils/nodemap.py
+++ b/mercurial/revlogutils/nodemap.py
@@ -128,15 +128,20 @@
         notr._postclose[k](None)
 
 
-def persist_nodemap(tr, revlog, pending=False):
+def persist_nodemap(tr, revlog, pending=False, force=False):
     """Write nodemap data on disk for a given revlog"""
     if getattr(revlog, 'filteredrevs', ()):
         raise error.ProgrammingError(
             "cannot persist nodemap of a filtered changelog"
         )
     if revlog.nodemap_file is None:
-        msg = "calling persist nodemap on a revlog without the feature enabled"
-        raise error.ProgrammingError(msg)
+        if force:
+            revlog.nodemap_file = get_nodemap_file(
+                revlog.opener, revlog.indexfile
+            )
+        else:
+            msg = "calling persist nodemap on a revlog without the feature enabled"
+            raise error.ProgrammingError(msg)
 
     can_incremental = util.safehasattr(revlog.index, "nodemap_data_incremental")
     ondisk_docket = revlog._nodemap_docket
diff --git a/mercurial/upgrade_utils/engine.py b/mercurial/upgrade_utils/engine.py
--- a/mercurial/upgrade_utils/engine.py
+++ b/mercurial/upgrade_utils/engine.py
@@ -24,6 +24,7 @@
     util,
     vfs as vfsmod,
 )
+from ..revlogutils import nodemap
 
 
 def _revlogfrompath(repo, path):
@@ -452,6 +453,22 @@
     if upgrade_op.requirements_only:
         ui.status(_(b'upgrading repository requirements\n'))
         scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
+    # if there is only one action and that is persistent nodemap upgrade
+    # directly write the nodemap file and update requirements instead of going
+    # through the whole cloning process
+    elif (
+        len(upgrade_op.upgrade_actions) == 1
+        and b'persistent-nodemap' in upgrade_op._upgrade_actions_names
+        and not upgrade_op.removed_actions
+    ):
+        ui.status(
+            _(b'upgrading repository to use persistent nodemap feature\n')
+        )
+        with srcrepo.transaction(b'upgrade') as tr:
+            unfi = srcrepo.unfiltered()
+            cl = unfi.changelog
+            nodemap.persist_nodemap(tr, cl, force=True)
+        scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
     else:
         with dstrepo.transaction(b'upgrade') as tr:
             _clonerevlogs(
diff --git a/tests/test-persistent-nodemap.t b/tests/test-persistent-nodemap.t
--- a/tests/test-persistent-nodemap.t
+++ b/tests/test-persistent-nodemap.t
@@ -636,28 +636,11 @@
   repository locked and read-only
   creating temporary repository to stage upgraded data: $TESTTMP/test-repo/.hg/upgrade.* (glob)
   (it is safe to interrupt this process any time before data migration completes)
-  migrating 15018 total revisions (5006 in filelogs, 5006 in manifests, 5006 in changelog)
-  migrating 1.74 MB in store; 569 MB tracked data
-  migrating 5004 filelogs containing 5006 revisions (346 KB in store; 28.2 KB tracked data)
-  finished migrating 5006 filelog revisions across 5004 filelogs; change in size: 0 bytes
-  migrating 1 manifests containing 5006 revisions (765 KB in store; 569 MB tracked data)
-  finished migrating 5006 manifest revisions across 1 manifests; change in size: 0 bytes
-  migrating changelog containing 5006 revisions (673 KB in store; 363 KB tracked data)
-  finished migrating 5006 changelog revisions; change in size: 0 bytes
-  finished migrating 15018 total revisions; total change in store size: 0 bytes
-  copying phaseroots
-  data fully upgraded in a temporary repository
-  marking source repository as being upgraded; clients will be unable to read from repository
-  starting in-place swap of repository data
-  replacing store...
-  store replacement complete; repository was inconsistent for *s (glob)
-  finalizing requirements file and making repository readable again
+  upgrading repository to use persistent nodemap feature
   removing temporary repository $TESTTMP/test-repo/.hg/upgrade.* (glob)
   $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
   00changelog-*.nd (glob)
   00changelog.n
-  00manifest-*.nd (glob)
-  00manifest.n
 
   $ hg debugnodemap --metadata
   uid: * (glob)