diff mercurial/localrepo.py @ 7233:9f0e52e1df77

fix pull racing with push/commit (issue1320) changegroup() has a problem when nodes which does not descend from a node in <bases> are added to remote after the discovery phase. If that happens, changegroup() won't send the correct set of nodes, ie. some nodes will be missing. To correct it we have to find the set of nodes that both remote and self have (called <common>), and send all the nodes not in <common>. This fix has some overhead, in the worst case it will re-send a whole branch. A proper fix to avoid this overhead might be to change the protocol so that the <common> nodes are sent (instead of the <bases> of the missing nodes).
author Benoit Boissinot <benoit.boissinot@ens-lyon.org>
date Tue, 21 Oct 2008 17:00:35 +0200
parents 7946503ec76e
children ae70fe6143fc
line wrap: on
line diff
--- a/mercurial/localrepo.py	Thu Oct 23 23:03:09 2008 +0200
+++ b/mercurial/localrepo.py	Tue Oct 21 17:00:35 2008 +0200
@@ -1578,6 +1578,20 @@
         the linkrev.
         """
 
+        if extranodes is None:
+            # can we go through the fast path ?
+            heads.sort()
+            allheads = self.heads()
+            allheads.sort()
+            if heads == allheads:
+                common = []
+                # parents of bases are known from both sides
+                for n in bases:
+                    for p in self.changelog.parents(n):
+                        if p != nullid:
+                            common.append(p)
+                return self._changegroup(common, source)
+
         self.hook('preoutgoing', throw=True, source=source)
 
         # Set up some initial variables
@@ -1854,16 +1868,22 @@
         return util.chunkbuffer(gengroup())
 
     def changegroup(self, basenodes, source):
+        # to avoid a race we use changegroupsubset() (issue1320)
+        return self.changegroupsubset(basenodes, self.heads(), source)
+
+    def _changegroup(self, common, source):
         """Generate a changegroup of all nodes that we have that a recipient
         doesn't.
 
         This is much easier than the previous function as we can assume that
-        the recipient has any changenode we aren't sending them."""
+        the recipient has any changenode we aren't sending them.
+
+        common is the set of common nodes between remote and self"""
 
         self.hook('preoutgoing', throw=True, source=source)
 
         cl = self.changelog
-        nodes = cl.nodesbetween(basenodes, None)[0]
+        nodes = cl.findmissing(common)
         revset = dict.fromkeys([cl.rev(n) for n in nodes])
         self.changegroupinfo(nodes, source)