diff --git a/hgext3rd/undo.py b/hgext3rd/undo.py --- a/hgext3rd/undo.py +++ b/hgext3rd/undo.py @@ -21,6 +21,7 @@ hg, localrepo, lock as lockmod, + merge, obsolete, obsutil, phases, @@ -63,38 +64,50 @@ if 'CHGINTERNALMARK' in os.environ: return orig(lui, repo, cmd, fullargs, *args) - # This wrapper executes whenever a command is run. - # Some commands (eg hg sl) don't actually modify anything - # ie can't be undone, but the command doesn't know this. command = [cmd] + fullargs - # Check wether undolog is consistent - # ie check wether the undo ext was - # off before this command - if '_undologactive' not in os.environ: - changes = safelog(repo, [""]) - if changes: - _recordnewgap(repo) + # Only write undo log if we know a command is going to do some writes. This + # saves time calculating visible heads if the command is read-only (ex. + # status). + # + # To detect a write command, wrap all possible entries: + # - transaction.__init__ + # - merge.update + triggered = [] + def trigger(orig, *args, **kwargs): + # trigger a log of the initial state of a repo before a command tries + # to modify that state. + if not triggered: + triggered.append(True) - # prevent nested calls + # Check wether undolog is consistent + # ie check wether the undo ext was + # off before this command + changes = safelog(repo, [""]) + if changes: + _recordnewgap(repo) + + return orig(*args, **kwargs) + + # If this hg process is started by another hg process, do not log from the + # inner one, since the inner one is expected to be part of the outer one. + # Note: chg server's case is handled above so "hg serve" won't reach here. if '_undologactive' not in os.environ: os.environ['_undologactive'] = "active" - rootlog = True - else: - rootlog = False + extensions.wrapfunction(transaction.transaction, '__init__', trigger) + extensions.wrapfunction(merge, 'update', trigger) try: result = orig(lui, repo, cmd, fullargs, *args) finally: - if repo is not None: - # mercurial is bad with caches - # eg update to hidden commit will leave false cache + # record changes to repo + if triggered: + if repo is not None: + # mercurial is bad with caches + # eg update to hidden commit will leave false cache repo.invalidatevolatilesets() - - # record changes to repo - if rootlog: safelog(repo, command) - del os.environ['_undologactive'] + del os.environ['_undologactive'] return result diff --git a/tests/test-undo.t b/tests/test-undo.t --- a/tests/test-undo.t +++ b/tests/test-undo.t @@ -210,10 +210,32 @@ $ hg log -G -r 'olddraft(1) and public()' -T compact Test undolog lock - $ hg log --config hooks.duringundologlock="sleep 1" > /dev/null & - $ sleep 0.1 - $ hg st --time - time: real [1-9]*\..* (re) + $ cat > $TESTTMP/noop.py < from __future__ import absolute_import + > from mercurial import registrar + > cmdtable = {} + > command = registrar.command(cmdtable) + > @command('noop') + > def noop(ui, repo): + > with repo.lock(), repo.transaction('noop'): + > # transaction triggers undolog + > pass + > EOF + $ cat >> $HGRCPATH < [extensions] + > noop=$TESTTMP/noop.py + > EOF + $ hg noop --config hooks.duringundologlock="sleep 1" > /dev/null & + $ sleep 0.2 + + "hg status" does not trigger undo log writing + $ hg status --time + time: real 0* (glob) + + remove lock intentionally to test the undolog lock during transaction init + $ rm -f .hg/store/lock + $ hg noop --time + time: real [1-9]\..* (re) hg undo command tests $ hg undo @@ -431,12 +453,8 @@ File corruption handling $ echo 111corruptedrevlog > .hg/undolog/index.i -#if chg -(note: chg has issues with the below test) -#else - $ hg st --debug + $ hg noop --debug caught revlog error. undolog/index.i was probably corrupted -#endif $ hg debugundohistory -l 0: -- gap in log --