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).
--- 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)
--- a/mercurial/revlog.py Thu Oct 23 23:03:09 2008 +0200
+++ b/mercurial/revlog.py Tue Oct 21 17:00:35 2008 +0200
@@ -590,6 +590,46 @@
yield i
break
+ def findmissing(self, common=None, heads=None):
+ '''
+ returns the topologically sorted list of nodes from the set:
+ missing = (ancestors(heads) \ ancestors(common))
+
+ where ancestors() is the set of ancestors from heads, heads included
+
+ if heads is None, the heads of the revlog are used
+ if common is None, nullid is assumed to be a common node
+ '''
+ if common is None:
+ common = [nullid]
+ if heads is None:
+ heads = self.heads()
+
+ common = [self.rev(n) for n in common]
+ heads = [self.rev(n) for n in heads]
+
+ # we want the ancestors, but inclusive
+ has = dict.fromkeys(self.ancestors(*common))
+ has[nullrev] = None
+ for r in common:
+ has[r] = None
+
+ # take all ancestors from heads that aren't in has
+ missing = {}
+ visit = [r for r in heads if r not in has]
+ while visit:
+ r = visit.pop(0)
+ if r in missing:
+ continue
+ else:
+ missing[r] = None
+ for p in self.parentrevs(r):
+ if p not in has:
+ visit.append(p)
+ missing = missing.keys()
+ missing.sort()
+ return [self.node(r) for r in missing]
+
def nodesbetween(self, roots=None, heads=None):
"""Return a tuple containing three elements. Elements 1 and 2 contain
a final list bases and heads after all the unreachable ones have been
--- a/tests/test-clone-cgi.out Thu Oct 23 23:03:09 2008 +0200
+++ b/tests/test-clone-cgi.out Tue Oct 21 17:00:35 2008 +0200
@@ -2,4 +2,4 @@
adding a
% try hgweb request
0
-54086fe9a47b47d83204f38bda0b90c2 page1
+1f424bb22ec05c3c6bc866b6e67efe43 page1
--- a/tests/test-fetch.out Thu Oct 23 23:03:09 2008 +0200
+++ b/tests/test-fetch.out Tue Oct 21 17:00:35 2008 +0200
@@ -100,7 +100,7 @@
adding changesets
adding manifests
adding file changes
-added 1 changesets with 1 changes to 1 files
+added 1 changesets with 1 changes to 2 files
% parent should be 2 (no automatic update)
2
Binary file tests/test-hgweb-commands.out has changed
--- a/tests/test-push-warn.out Thu Oct 23 23:03:09 2008 +0200
+++ b/tests/test-push-warn.out Tue Oct 21 17:00:35 2008 +0200
@@ -22,7 +22,7 @@
adding changesets
adding manifests
adding file changes
-added 2 changesets with 1 changes to 1 files
+added 2 changesets with 1 changes to 2 files
adding foo
updating working directory
1 files updated, 0 files merged, 0 files removed, 0 files unresolved