diff --git a/mercurial/repoview.py b/mercurial/repoview.py --- a/mercurial/repoview.py +++ b/mercurial/repoview.py @@ -315,7 +315,10 @@ class repoview(object): revs = filterrevs(unfi, self.filtername) cl = self._clcache newkey = (unfilen, unfinode, hash(revs), unfichangelog._delayed) - if cl is not None and newkey != self._clcachekey: + # if cl.index is not unfiindex, unfi.changelog would be + # recreated, and our clcache refers to garbage object + if (cl is not None and + (cl.index is not unfiindex or newkey != self._clcachekey)): cl = None # could have been made None by the previous if if cl is None: diff --git a/tests/test-commandserver.t b/tests/test-commandserver.t --- a/tests/test-commandserver.t +++ b/tests/test-commandserver.t @@ -725,3 +725,133 @@ unix domain socket: [255] #endif + + $ cd .. + +Test that accessing to invalid changelog cache is avoided at +subsequent operations even if repo object is reused even after failure +of transaction (see 0a7610758c42 also) + +"hg log" after failure of transaction is needed to detect invalid +cache in repoview: this can't detect by "hg verify" only. + +Combination of "finalization" and "empty-ness of changelog" (2 x 2 = +4) are tested, because '00changelog.i' are differently changed in each +cases. + + $ cat > $TESTTMP/failafterfinalize.py < # extension to abort transaction after finalization forcibly + > from mercurial import commands, error, extensions, lock as lockmod + > def fail(tr): + > raise error.Abort('fail after finalization') + > def reposetup(ui, repo): + > class failrepo(repo.__class__): + > def commitctx(self, ctx, error=False): + > if self.ui.configbool('failafterfinalize', 'fail'): + > # 'sorted()' by ASCII code on category names causes + > # invoking 'fail' after finalization of changelog + > # using "'cl-%i' % id(self)" as category name + > self.currenttransaction().addfinalize('zzzzzzzz', fail) + > return super(failrepo, self).commitctx(ctx, error) + > repo.__class__ = failrepo + > EOF + + $ hg init repo3 + $ cd repo3 + + $ cat <> $HGRCPATH + > [ui] + > logtemplate = {rev} {desc|firstline} ({files})\n + > + > [extensions] + > failafterfinalize = $TESTTMP/failafterfinalize.py + > EOF + +- test failure with "empty changelog" + + $ echo foo > foo + $ hg add foo + +(failuer before finalization) + + >>> from hgclient import readchannel, runcommand, check + >>> @check + ... def abort(server): + ... readchannel(server) + ... runcommand(server, ['commit', + ... '--config', 'hooks.pretxncommit=false', + ... '-mfoo']) + ... runcommand(server, ['log']) + ... runcommand(server, ['verify', '-q']) + *** runcommand commit --config hooks.pretxncommit=false -mfoo + transaction abort! + rollback completed + abort: pretxncommit hook exited with status 1 + [255] + *** runcommand log + *** runcommand verify -q + +(failuer after finalization) + + >>> from hgclient import readchannel, runcommand, check + >>> @check + ... def abort(server): + ... readchannel(server) + ... runcommand(server, ['commit', + ... '--config', 'failafterfinalize.fail=true', + ... '-mfoo']) + ... runcommand(server, ['log']) + ... runcommand(server, ['verify', '-q']) + *** runcommand commit --config failafterfinalize.fail=true -mfoo + transaction abort! + rollback completed + abort: fail after finalization + [255] + *** runcommand log + *** runcommand verify -q + +- test failure with "not-empty changelog" + + $ echo bar > bar + $ hg add bar + $ hg commit -mbar bar + +(failure before finalization) + + >>> from hgclient import readchannel, runcommand, check + >>> @check + ... def abort(server): + ... readchannel(server) + ... runcommand(server, ['commit', + ... '--config', 'hooks.pretxncommit=false', + ... '-mfoo', 'foo']) + ... runcommand(server, ['log']) + ... runcommand(server, ['verify', '-q']) + *** runcommand commit --config hooks.pretxncommit=false -mfoo foo + transaction abort! + rollback completed + abort: pretxncommit hook exited with status 1 + [255] + *** runcommand log + 0 bar (bar) + *** runcommand verify -q + +(failure after finalization) + + >>> from hgclient import readchannel, runcommand, check + >>> @check + ... def abort(server): + ... readchannel(server) + ... runcommand(server, ['commit', + ... '--config', 'failafterfinalize.fail=true', + ... '-mfoo', 'foo']) + ... runcommand(server, ['log']) + ... runcommand(server, ['verify', '-q']) + *** runcommand commit --config failafterfinalize.fail=true -mfoo foo + transaction abort! + rollback completed + abort: fail after finalization + [255] + *** runcommand log + 0 bar (bar) + *** runcommand verify -q