diff --git a/hgext3rd/absorb/__init__.py b/hgext3rd/absorb/__init__.py
--- a/hgext3rd/absorb/__init__.py
+++ b/hgext3rd/absorb/__init__.py
@@ -735,17 +735,18 @@
         needupdate = [(name, self.replacemap[hsh])
                       for name, hsh in repo._bookmarks.iteritems()
                       if hsh in self.replacemap]
+        changes = []
         for name, hsh in needupdate:
             if hsh:
-                repo._bookmarks[name] = hsh
+                changes.append((name, hsh))
                 if self.ui.verbose:
                     self.ui.write(_('moving bookmark %s to %s\n')
                                   % (name, node.hex(hsh)))
             else:
-                del repo._bookmarks[name]
+                changes.append((name, None))
                 if self.ui.verbose:
                     self.ui.write(_('deleting bookmark %s\n') % name)
-        repo._bookmarks.recordchange(tr)
+        repo._bookmarks.applychanges(repo, tr, changes)
 
     def _moveworkingdirectoryparent(self):
         ctx = self.repo[self.finalnode]
diff --git a/hgext3rd/fbamend/__init__.py b/hgext3rd/fbamend/__init__.py
--- a/hgext3rd/fbamend/__init__.py
+++ b/hgext3rd/fbamend/__init__.py
@@ -290,26 +290,25 @@
                 ui.warn(msg)
                 ui.status(_("(use 'hg restack' to rebase them)\n"))
 
-        newbookmarks = repo._bookmarks
-
+        changes = []
         # move old bookmarks to new node
         for bm in oldbookmarks:
-            newbookmarks[bm] = node
+            changes.append((bm, node))
 
         userestack = ui.configbool('fbamend', 'userestack')
         if not _histediting(repo) and not userestack:
             preamendname = _preamendname(repo, node)
             if haschildren:
-                newbookmarks[preamendname] = old.node()
+                changes.append((preamendname, old.node()))
             elif not active:
                 # update bookmark if it isn't based on the active bookmark name
                 oldname = _preamendname(repo, old.node())
                 if oldname in repo._bookmarks:
-                    newbookmarks[preamendname] = repo._bookmarks[oldname]
-                    del newbookmarks[oldname]
+                    changes.append((preamendname, repo._bookmarks[oldname]))
+                    changes.append((oldname, None)) # delete the old name
 
         tr = repo.transaction('fixupamend')
-        newbookmarks.recordchange(tr)
+        repo._bookmarks.applychanges(repo, tr, changes)
         tr.close()
 
         if rebase and haschildren:
@@ -366,11 +365,11 @@
         if opts['rev'] and opts['rev'][0]:
             rebasemod.rebase(ui, repo, **opts)
 
+        changes = []
         for bookmark in oldbookmarks:
-            repo._bookmarks.pop(bookmark)
-
+            changes.append((bookmark, None)) # delete the bookmark
         tr = repo.transaction('fixupamend')
-        repo._bookmarks.recordchange(tr)
+        repo._bookmarks.applychanges(repo, tr, changes)
 
         if obsolete.isenabled(repo, obsolete.createmarkersopt):
             tr.close()
@@ -478,11 +477,12 @@
     with repo.wlock():
         with repo.lock():
             with repo.transaction('movebookmarks') as tr:
+                changes = []
                 for rev in revs:
                     latest = cl.node(common.latest(repo, rev))
                     for bm in repo.nodebookmarks(cl.node(rev)):
-                        repo._bookmarks[bm] = latest
-                repo._bookmarks.recordchange(tr)
+                        changes.append((bm, latest))
+                repo._bookmarks.applychanges(repo, tr, changes)
 
 ### bookmarks api compatibility layer ###
 def bmactivate(repo, mark):
diff --git a/hgext3rd/fbamend/common.py b/hgext3rd/fbamend/common.py
--- a/hgext3rd/fbamend/common.py
+++ b/hgext3rd/fbamend/common.py
@@ -93,13 +93,14 @@
     def updatebookmarks(newid):
         dirty = False
         for oldid in oldids:
+            changes = []
             oldbookmarks = repo.nodebookmarks(oldid)
             if oldbookmarks:
                 for b in oldbookmarks:
-                    repo._bookmarks[b] = newid
+                    changes.append((b, newid))
                 dirty = True
             if dirty:
-                repo._bookmarks.recordchange(tr)
+                repo._bookmarks.applychanges(repo, tr, changes)
     return updatebookmarks
 
 def rewrite(repo, old, updates, head, newbases, commitopts):
diff --git a/hgext3rd/fbamend/prune.py b/hgext3rd/fbamend/prune.py
--- a/hgext3rd/fbamend/prune.py
+++ b/hgext3rd/fbamend/prune.py
@@ -69,9 +69,10 @@
         wlock = repo.wlock()
         lock = repo.lock()
         tr = repo.transaction('prune')
+        changes = []
         for bookmark in bookmarks:
-            del repomarks[bookmark]
-        repomarks.recordchange(tr)
+            changes.append((bookmark, None)) # delete the bookmark
+        repomarks.applychanges(repo, tr, changes)
         tr.close()
         for bookmark in sorted(bookmarks):
             repo.ui.write(_("bookmark '%s' deleted\n") % bookmark)
@@ -225,8 +226,8 @@
                 movebookmark = bookactive and not bookmarks
                 if movebookmark:
                     bookmarksmod.deactivate(repo)
-                    repo._bookmarks[bookactive] = newnode.node()
-                    repo._bookmarks.recordchange(tr)
+                    changes = [(bookactive, newnode.node())]
+                    repo._bookmarks.applychanges(repo, tr, changes)
                 commands.update(ui, repo, newnode.rev())
                 ui.status(_('working directory now at %s\n')
                           % ui.label(str(newnode), 'evolve.node'))
diff --git a/hgext3rd/fbamend/unamend.py b/hgext3rd/fbamend/unamend.py
--- a/hgext3rd/fbamend/unamend.py
+++ b/hgext3rd/fbamend/unamend.py
@@ -58,7 +58,6 @@
 
     with repo.wlock():
         with repo.lock():
-            repobookmarks = repo._bookmarks
             ctxbookmarks = curctx.bookmarks()
             changedfiles = []
             wctx = repo[None]
@@ -78,8 +77,9 @@
                         dirstate.add(filename)
                     if data[1][0] is None:
                         dirstate.remove(filename)
+            changes = []
             for book in ctxbookmarks:
-                repobookmarks[book] = precnode
-            repobookmarks.recordchange(tr)
+                changes.append((book, precnode))
+            repo._bookmarks.applychanges(repo, tr, changes)
             obsolete.createmarkers(repo, [(curctx, (precctx,))])
             tr.close()
diff --git a/hgext3rd/reset.py b/hgext3rd/reset.py
--- a/hgext3rd/reset.py
+++ b/hgext3rd/reset.py
@@ -193,8 +193,8 @@
         try:
             lock = repo.lock()
             tr = repo.transaction('reset')
-            repo._bookmarks[bookmark] = ctx.node()
-            repo._bookmarks.recordchange(tr)
+            changes = [(bookmark, ctx.node())]
+            repo._bookmarks.applychanges(repo, tr, changes)
             tr.close()
         finally:
             lockmod.release(lock, tr)
diff --git a/hgext3rd/uncommit.py b/hgext3rd/uncommit.py
--- a/hgext3rd/uncommit.py
+++ b/hgext3rd/uncommit.py
@@ -38,9 +38,10 @@
 def _updatebookmarks(repo, oldid, newid, tr):
     oldbookmarks = repo.nodebookmarks(oldid)
     if oldbookmarks:
+        changes = []
         for b in oldbookmarks:
-            repo._bookmarks[b] = newid
-        repo._bookmarks.recordchange(tr)
+            changes.append((b, newid))
+        repo._bookmarks.applychanges(repo, tr, changes)
 
 def _commitfiltered(repo, ctx, match):
     """Recommit ctx with changed files not in match. Return the new
diff --git a/infinitepush/__init__.py b/infinitepush/__init__.py
--- a/infinitepush/__init__.py
+++ b/infinitepush/__init__.py
@@ -743,10 +743,11 @@
     with repo.wlock():
         with repo.lock():
             with repo.transaction('bookmark') as tr:
+                changes = []
                 for scratchbook, node in bookmarks.iteritems():
                     changectx = repo[node]
-                    repo._bookmarks[scratchbook] = changectx.node()
-                repo._bookmarks.recordchange(tr)
+                    changes.append((scratchbook, changectx.node()))
+                repo._bookmarks.applychanges(repo, tr, changes)
 
 def _findcommonincoming(orig, *args, **kwargs):
     common, inc, remoteheads = orig(*args, **kwargs)
diff --git a/infinitepush/backupcommands.py b/infinitepush/backupcommands.py
--- a/infinitepush/backupcommands.py
+++ b/infinitepush/backupcommands.py
@@ -226,13 +226,14 @@
     with repo.wlock():
         with repo.lock():
             with repo.transaction('bookmark') as tr:
+                changes = []
                 for book, hexnode in backupstate.localbookmarks.iteritems():
                     if hexnode in repo:
-                        repo._bookmarks[book] = bin(hexnode)
+                        changes.append((book, bin(hexnode)))
                     else:
                         ui.warn(_('%s not found, not creating %s bookmark') %
                                 (hexnode, book))
-                repo._bookmarks.recordchange(tr)
+                repo._bookmarks.applychanges(repo, tr, changes)
 
     return result
 
diff --git a/tests/test-pushrebase-remotenames.t b/tests/test-pushrebase-remotenames.t
--- a/tests/test-pushrebase-remotenames.t
+++ b/tests/test-pushrebase-remotenames.t
@@ -159,9 +159,8 @@
   >     try:
   >         lock = repo.lock()
   >         tr = repo.transaction("pretxnopen.movebook")
-  >         bm = repo._bookmarks
-  >         bm['bm'] = repo[1].node()
-  >         bm.recordchange(tr)
+  >         changes = [('bm', repo[1].node())]
+  >         repo._bookmarks.applychanges(repo, tr, changes)
   >         tr.close()
   >     finally:
   >         if tr: