diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -157,6 +157,7 @@ from .i18n import _
 from . import (
     changegroup,
     error,
+    node as nodemod,
     obsolete,
     phases,
     pushkey,
@@ -1749,6 +1750,27 @@ def handlecheckupdatedheads(op, inpart):
             raise error.PushRaced('repository changed while pushing - '
                                   'please try again')
 
+@parthandler('check:phases')
+def handlecheckphases(op, inpart):
+    """check that phase boundaries of the repository did not change
+
+    This is used to detect a push race.
+    """
+    phasetonodes = phases.binarydecode(inpart)
+    unfi = op.repo.unfiltered()
+    cl = unfi.changelog
+    phasecache = unfi._phasecache
+    msg = ('repository changed while pushing - please try again '
+           '(%s is %s expected %s)')
+    for expectedphase, nodes in enumerate(phasetonodes):
+        for n in nodes:
+            actualphase = phasecache.phase(unfi, cl.rev(n))
+            if actualphase != expectedphase:
+                finalmsg = msg % (nodemod.short(n),
+                                  phases.phasenames[actualphase],
+                                  phases.phasenames[expectedphase])
+                raise error.PushRaced(finalmsg)
+
 @parthandler('output')
 def handleoutput(op, inpart):
     """forward output captured on the server to the client"""