changeset 35267:dbf868623daf

bookmark: add a 'check:bookmarks' bundle2 part This part checks that bookmarks are still at the node they are expected to be. This allows a pushing client to detect push race where the repository was updated between the time it discovered the server state and the time it managed to finish its push. Such checking already exists when pushing bookmark through pushkey. This new part can be inserted at the beginning of the bundle, triggering abort earlier. In addition, we would like to move away from pushey to push bookmark. A step useful to solve issue5165.
author Boris Feld <boris.feld@octobus.net>
date Sun, 15 Oct 2017 15:01:03 +0200
parents 3340d46a5c3f
children ad5f2b923b0d
files mercurial/bundle2.py
diffstat 1 files changed, 29 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/bundle2.py	Sun Oct 15 14:59:55 2017 +0200
+++ b/mercurial/bundle2.py	Sun Oct 15 15:01:03 2017 +0200
@@ -156,6 +156,7 @@
 
 from .i18n import _
 from . import (
+    bookmarks,
     changegroup,
     error,
     node as nodemod,
@@ -1787,6 +1788,34 @@
     replyto = int(inpart.params['in-reply-to'])
     op.records.add('changegroup', {'return': ret}, replyto)
 
+@parthandler('check:bookmarks')
+def handlecheckbookmarks(op, inpart):
+    """check location of bookmarks
+
+    This part is to be used to detect push race regarding bookmark, it
+    contains binary encoded (bookmark, node) tuple. If the local state does
+    not marks the one in the part, a PushRaced exception is raised
+    """
+    bookdata = bookmarks.binarydecode(inpart)
+
+    msgstandard = ('repository changed while pushing - please try again '
+                   '(bookmark "%s" move from %s to %s)')
+    msgmissing = ('repository changed while pushing - please try again '
+                  '(bookmark "%s" is missing, expected %s)')
+    msgexist = ('repository changed while pushing - please try again '
+                '(bookmark "%s" set on %s, expected missing)')
+    for book, node in bookdata:
+        currentnode = op.repo._bookmarks.get(book)
+        if currentnode != node:
+            if node is None:
+                finalmsg = msgexist % (book, nodemod.short(currentnode))
+            elif currentnode is None:
+                finalmsg = msgmissing % (book, nodemod.short(node))
+            else:
+                finalmsg = msgstandard % (book, nodemod.short(node),
+                                          nodemod.short(currentnode))
+            raise error.PushRaced(finalmsg)
+
 @parthandler('check:heads')
 def handlecheckheads(op, inpart):
     """check that head of the repo did not change