##// END OF EJS Templates
bisect: avoid adding irrelevant revisions to bisect state...
bisect: avoid adding irrelevant revisions to bisect state When adding new revisions to the bisect state, it only makes sense to add information about revisions that are under consideration (i.e., those that are topologically between the known good and bad revisions). However, if the user passes in a revset (e.g., '!merge()' to exclude merge commits), hg will resolve the revset first and add all matching revisions to the bisect state (which in this case would likely be the majority of revisions in the repo). To avoid this, revisions should only be added to the bisect state if they are between the good and bad revisions (and therefore relevant to the bisection). -- Here are the results of some performance tests using the `mozilla-central` repo (since it is one of the largest freely-available hg repositories in the wild). These tests compare the performance of a locally-built `hg` before and after application of this series. Note that `--noupdate` is passed to avoid including update time (which should not vary across cases). Setup (run between each test): $ hg bisect --reset $ hg bisect --noupdate --bad 56c3ad4bde5c70714b784ccf15d099e0df0f5bde $ hg bisect --noupdate --good 57426696adaf08298af3027fa77486fee0633b13 Test using a revset that returns a very large number of revisions: $ time hg bisect --noupdate --skip '!merge()' > /dev/null Before: real 0m9.398s user 0m9.233s sys 0m0.120s After: real 0m1.513s user 0m1.425s sys 0m0.052s Test using a revset that is expensive to compute: $ time hg bisect --noupdate --skip 'desc("Bug")' > /dev/null Before: real 0m49.853s user 0m49.580s sys 0m0.243s After: real 0m4.120s user 0m4.036s sys 0m0.048s

File last commit:

r50180:56f98406 default
r50337:81623652 default
Show More
test-filecache.py
282 lines | 6.7 KiB | text/x-python | PythonLexer
import os
import stat
import subprocess
import sys
if subprocess.call(
[sys.executable, '%s/hghave' % os.environ['TESTDIR'], 'cacheable']
):
sys.exit(80)
print_ = print
def print(*args, **kwargs):
"""print() wrapper that flushes stdout buffers to avoid py3 buffer issues
We could also just write directly to sys.stdout.buffer the way the
ui object will, but this was easier for porting the test.
"""
print_(*args, **kwargs)
sys.stdout.flush()
from mercurial import (
extensions,
hg,
localrepo,
pycompat,
ui as uimod,
util,
vfs as vfsmod,
)
class fakerepo:
def __init__(self):
self._filecache = {}
class fakevfs:
def join(self, p):
return p
vfs = fakevfs()
def unfiltered(self):
return self
def sjoin(self, p):
return p
@localrepo.repofilecache('x', 'y')
def cached(self):
print('creating')
return 'string from function'
def invalidate(self):
for k in self._filecache:
try:
delattr(self, pycompat.sysstr(k))
except AttributeError:
pass
def basic(repo):
print("* neither file exists")
# calls function
repo.cached
repo.invalidate()
print("* neither file still exists")
# uses cache
repo.cached
# create empty file
f = open('x', 'w')
f.close()
repo.invalidate()
print("* empty file x created")
# should recreate the object
repo.cached
f = open('x', 'w')
f.write('a')
f.close()
repo.invalidate()
print("* file x changed size")
# should recreate the object
repo.cached
repo.invalidate()
print("* nothing changed with either file")
# stats file again, reuses object
repo.cached
# atomic replace file, size doesn't change
# hopefully st_mtime doesn't change as well so this doesn't use the cache
# because of inode change
f = vfsmod.vfs(b'.')(b'x', b'w', atomictemp=True)
f.write(b'b')
f.close()
repo.invalidate()
print("* file x changed inode")
repo.cached
# create empty file y
f = open('y', 'w')
f.close()
repo.invalidate()
print("* empty file y created")
# should recreate the object
repo.cached
f = open('y', 'w')
f.write('A')
f.close()
repo.invalidate()
print("* file y changed size")
# should recreate the object
repo.cached
f = vfsmod.vfs(b'.')(b'y', b'w', atomictemp=True)
f.write(b'B')
f.close()
repo.invalidate()
print("* file y changed inode")
repo.cached
f = vfsmod.vfs(b'.')(b'x', b'w', atomictemp=True)
f.write(b'c')
f.close()
f = vfsmod.vfs(b'.')(b'y', b'w', atomictemp=True)
f.write(b'C')
f.close()
repo.invalidate()
print("* both files changed inode")
repo.cached
def fakeuncacheable():
def wrapcacheable(orig, *args, **kwargs):
return False
def wrapinit(orig, *args, **kwargs):
pass
originit = extensions.wrapfunction(util.cachestat, '__init__', wrapinit)
origcacheable = extensions.wrapfunction(
util.cachestat, 'cacheable', wrapcacheable
)
for fn in ['x', 'y']:
try:
os.remove(fn)
except OSError:
pass
basic(fakerepo())
util.cachestat.cacheable = origcacheable
util.cachestat.__init__ = originit
def test_filecache_synced():
# test old behavior that caused filecached properties to go out of sync
os.system('hg init && echo a >> a && hg ci -qAm.')
repo = hg.repository(uimod.ui.load())
# first rollback clears the filecache, but changelog to stays in __dict__
repo.rollback()
repo.commit(b'.')
# second rollback comes along and touches the changelog externally
# (file is moved)
repo.rollback()
# but since changelog isn't under the filecache control anymore, we don't
# see that it changed, and return the old changelog without reconstructing
# it
repo.commit(b'.')
def setbeforeget(repo):
os.remove('x')
os.remove('y')
repo.__class__.cached.set(repo, 'string set externally')
repo.invalidate()
print("* neither file exists")
print(repo.cached)
repo.invalidate()
f = open('x', 'w')
f.write('a')
f.close()
print("* file x created")
print(repo.cached)
repo.__class__.cached.set(repo, 'string 2 set externally')
repo.invalidate()
print("* string set externally again")
print(repo.cached)
repo.invalidate()
f = open('y', 'w')
f.write('b')
f.close()
print("* file y created")
print(repo.cached)
def antiambiguity():
filename = 'ambigcheck'
# try some times, because reproduction of ambiguity depends on
# "filesystem time"
for i in range(5):
fp = open(filename, 'w')
fp.write('FOO')
fp.close()
oldstat = os.stat(filename)
if oldstat[stat.ST_CTIME] != oldstat[stat.ST_MTIME]:
# subsequent changing never causes ambiguity
continue
repetition = 3
# repeat changing via checkambigatclosing, to examine whether
# st_mtime is advanced multiple times as expected
for i in range(repetition):
# explicit closing
fp = vfsmod.checkambigatclosing(open(filename, 'a'))
fp.write('FOO')
fp.close()
# implicit closing by "with" statement
with vfsmod.checkambigatclosing(open(filename, 'a')) as fp:
fp.write('BAR')
newstat = os.stat(filename)
if oldstat[stat.ST_CTIME] != newstat[stat.ST_CTIME]:
# timestamp ambiguity was naturally avoided while repetition
continue
# st_mtime should be advanced "repetition * 2" times, because
# all changes occurred at same time (in sec)
expected = (oldstat[stat.ST_MTIME] + repetition * 2) & 0x7FFFFFFF
if newstat[stat.ST_MTIME] != expected:
print(
"'newstat[stat.ST_MTIME] %s is not %s (as %s + %s * 2)"
% (
newstat[stat.ST_MTIME],
expected,
oldstat[stat.ST_MTIME],
repetition,
)
)
# no more examination is needed regardless of result
break
else:
# This platform seems too slow to examine anti-ambiguity
# of file timestamp (or test happened to be executed at
# bad timing). Exit silently in this case, because running
# on other faster platforms can detect problems
pass
print('basic:')
print()
basic(fakerepo())
print()
print('fakeuncacheable:')
print()
fakeuncacheable()
test_filecache_synced()
print()
print('setbeforeget:')
print()
setbeforeget(fakerepo())
print()
print('antiambiguity:')
print()
antiambiguity()