[issue2137] API: after unbundle, repo.lookup() does not recognized added changeset IDs

Greg Ward greg-hg at gerg.ca
Tue Apr 13 17:11:27 CDT 2010


On Tue, Apr 13, 2010 at 3:19 PM, Greg Ward <bugs at mercurial.selenic.com> wrote:
>
> New submission from Greg Ward <greg-hg at gerg.ca>:
[...]
> In certain circumstances, the push to target finds no outgoing changesets
> even though unbundle really did add changesets to tmptarget.  At a high
> level, I can illustrate the problem like this:
>
>  tip1 = hgnode.short(tmptarget.changelog.tip())
>  tip2 = hgnode.short(tmptarget.lookup(tip1))
>  assert tip1 == tip2
>
> This actually never gets to the assert -- it raises RepoLookupError in the
> call to tmptarget.lookup(tmp1), which seems like a violation of some pretty
> basic contracts.
>
> I'm still digging.  Will post more when I have more info and (hopefully) a
> reproduction script.

OK, I can reproduce this with a tiny repo and a fairly small Python
script.  The catch is that I have to

1) monkeypatch revlog and set _prereadsize to a small value
2) really patch revlog.py and modify checkinlinesize() so it splits
out 00changelog.d much earlier (like, after 128 bytes instead of 128
kiB)

This is my patch to revlog.py:

--- a/mercurial/revlog.py
+++ b/mercurial/revlog.py
@@ -1010,7 +1010,7 @@
         return text

     def checkinlinesize(self, tr, fp=None):
-        if not self._inline or (self.start(-2) + self.length(-2)) < 131072:
+        if not self._inline or (self.start(-2) + self.length(-2)) < 128:
             return

         trinfo = tr.find(self.indexfile)

Obviously, a small refactoring here would let me tweak that by
monkeypatching, meaning I could reproduce this problem cleanly.

And here is the Python script that demonstrates the problem:

"""
#!/usr/bin/python

# attempt to reproduce hg issue 2137 (revlog not properly updated after
# unbundle, causing repo.lookup() to fail spuriously)

import sys
from mercurial import ui as hgui, hg, commands, node as hgnode, error, revlog

(path, bundle) = sys.argv[1:3]

revlog._prereadsize = 128
ui = hgui.ui()
ui.setconfig('ui', 'verbose', 'true')
#ui.setconfig('ui', 'debug', 'true')
ui.write('opening %s\n' % path)
repo = hg.repository(ui, path)
cl = repo.changelog

ui.write('initial sanity check\n')
assert len(repo) == len(repo.changelog.index) - 1 == repo.changelog.nodemap.p.l
tip = cl.tip()
assert repo.lookup(hgnode.short(tip)) == tip

# apply the bundle
ui.write('applying bundle\n')
commands.unbundle(ui, repo, bundle, update=False)

# demonstrate the bug: first, expose the low-level problem (changelog's
# nodemap has two different idea's of the changelog's length)
ui.write('testing for the bug\n')
cl = repo.changelog
if cl.nodemap.p.l == len(repo):
    print "PASS: cl.nodemap.p.l == len(repo) == %d" % len(repo)
else:
    print "FAIL: len(repo) == %d, cl.nodemap.p.l == %d" \
          % (len(repo), cl.nodemap.p.l)

# and demonstrate it again the high-level way: looking up the abbreviated
# changeset ID of a changeset that is known to be in the changelog fails
tip1 = cl.tip()
try:
    tip2 = repo.lookup(hgnode.short(tip1))
    assert tip1 == tip2
    print "PASS: looked up tip two ways and got consistent results"
except error.RepoLookupError:
    print "FAIL: failed to lookup abbreviated changeset ID of tip"
"""

I'll have a real test script shortly.

Greg


More information about the Mercurial-devel mailing list