# cachethg.py - overlay/status cache## Copyright 2008 Steve Borho <steve@borho.org>## This software may be used and distributed according to the terms of the# GNU General Public License version 2, incorporated herein by reference.importosimportsysfrommercurialimporthg,util,ui,node,merge,errorfromtortoisehg.utilimportpaths,debugthg,hglibdebugging=Falseenabled=Truelocalonly=Falseincludepaths=[]excludepaths=[]try:from_winregimportHKEY_CURRENT_USER,OpenKey,QueryValueExfromwin32apiimportGetTickCountCACHE_TIMEOUT=5000try:hkey=OpenKey(HKEY_CURRENT_USER,r"Software\TortoiseHg")enabled=QueryValueEx(hkey,'EnableOverlays')[0]in('1','True')localonly=QueryValueEx(hkey,'LocalDisksOnly')[0]in('1','True')incs=QueryValueEx(hkey,'IncludePath')[0]excs=QueryValueEx(hkey,'ExcludePath')[0]debugging=QueryValueEx(hkey,'OverlayDebug')[0]in('1','True')forpinincs.split(';'):path=p.strip()ifpath:includepaths.append(path)forpinexcs.split(';'):path=p.strip()ifpath:excludepath.append(path)exceptEnvironmentError:passexceptImportError:fromtimeimporttimeasGetTickCountCACHE_TIMEOUT=5.0debugging=debugthg.debug('O')ifdebugging:debugf=debugthg.debugfdebugf('Enabled %s',enabled)debugf('LocalDisksOnly %s',localonly)debugf('IncludePaths %s',includepaths)debugf('ExcludePaths %s',excludepaths)else:debugf=debugthg.debugf_NoSTATUS_STATES='MAR!?IC'MODIFIED,ADDED,REMOVED,DELETED,UNKNOWN,IGNORED,UNCHANGED=STATUS_STATESNOT_IN_REPO=' 'ROOT="r"UNRESOLVED='U'# file status cacheoverlay_cache={}cache_tick_count=0cache_root=Nonecache_pdir=Nonedefadd_dirs(list):dirs=set()iflist:dirs.add('')forfinlist:pdir=os.path.dirname(f)ifpdirindirs:continuewhilepdir:dirs.add(pdir)pdir=os.path.dirname(pdir)list.extend(dirs)defget_state(upath,repo=None):""" Get the state of a given path in source control. """states=get_states(upath,repo)returnstatesandstates[0]orNOT_IN_REPOdefget_states(upath,repo=None):""" Get the states of a given path in source control. """globaloverlay_cache,cache_tick_countglobalcache_root,cache_pdirglobalenabled,localonlyglobalincludepaths,excludepaths#debugf("called: _get_state(%s)", path)tc=GetTickCount()try:# handle some Asian charsetspath=upath.encode('mbcs')except:path=upath# check if path is cachedpdir=os.path.dirname(path)status=overlay_cache.get(path,'')ifoverlay_cacheand(cache_pdir==pdirorcache_pdirandstatusnotin' r'andpath.startswith(cache_pdir)):#use cached data when pdir has not changed or when the cached state is a repo stateiftc-cache_tick_count<CACHE_TIMEOUT:ifnotstatus:ifos.path.isdir(os.path.join(path,'.hg')):add(path,ROOT)status=ROOTelse:status=overlay_cache.get(pdir+'*',NOT_IN_REPO)add(path,status)debugf("%s: %s (cached~)",(path,status))else:debugf("%s: %s (cached)",(path,status))returnstatuselse:debugf("Timed out!!")overlay_cache.clear()cache_tick_count=GetTickCount()# path is a driveifpath.endswith(":\\"):add(path,NOT_IN_REPO)returnNOT_IN_REPO# open repoifcache_pdir==pdir:root=cache_rootelse:debugf("find new root")root=paths.find_root(path)ifroot==path:ifnotoverlay_cache:cache_root=pdiradd(path,ROOT)debugf("%s: r",path)returnROOTcache_root=rootcache_pdir=pdirifrootisNone:debugf("_get_state: not in repo")overlay_cache={None:None}cache_tick_count=GetTickCount()returnNOT_IN_REPOdebugf("_get_state: root = "+root)hgdir=os.path.join(root,'.hg','')ifpdir==hgdir[:-1]orpdir.startswith(hgdir):add(pdir,NOT_IN_REPO)returnNOT_IN_REPOtry:ifnotenabled:overlay_cache={None:None}cache_tick_count=GetTickCount()debugf("overlayicons disabled")returnNOT_IN_REPOiflocalonlyandpaths.netdrive_status(path):debugf("%s: is a network drive",path)overlay_cache={None:None}cache_tick_count=GetTickCount()returnNOT_IN_REPOifincludepaths:forpinincludepaths:ifpath.startswith(p):breakelse:debugf("%s: is not in an include path",path)overlay_cache={None:None}cache_tick_count=GetTickCount()returnNOT_IN_REPOforpinexcludepaths:ifpath.startswith(p):debugf("%s: is in an exclude path",path)overlay_cache={None:None}cache_tick_count=GetTickCount()returnNOT_IN_REPOtc1=GetTickCount()real=os.path.realpath#only test if necessary (symlink in path)ifnotrepoor(repo.root!=rootandrepo.root!=real(root)):repo=hg.repository(ui.ui(),path=root)debugf("hg.repository() took %g ticks",(GetTickCount()-tc1))excepterror.RepoError:# We aren't in a working treedebugf("%s: not in repo",pdir)add(pdir+'*',IGNORED)returnIGNOREDexceptException,e:debugf("error while handling %s:",pdir)debugf(e)add(pdir+'*',UNKNOWN)returnUNKNOWN# get file statustc1=GetTickCount()try:matcher=hglib.match(repo[None],[pdir])repostate=repo.status(match=matcher,ignored=True,clean=True,unknown=True)exceptutil.Abort,inst:debugf("abort: %s",inst)debugf("treat as unknown : %s",path)returnUNKNOWNdebugf("status() took %g ticks",(GetTickCount()-tc1))mergestate=repo.dirstate.parents()[1]!=node.nullidand \
hasattr(merge,'mergestate')# cached file infotc=GetTickCount()overlay_cache={}add(root,ROOT)add(os.path.join(root,'.hg'),NOT_IN_REPO)states=STATUS_STATESifmergestate:mstate=merge.mergestate(repo)unresolved=[fforfinmstateifmstate[f]=='u']ifunresolved:modified=repostate[0]modified[:]=set(modified)-set(unresolved)repostate.insert(0,unresolved)states=[UNRESOLVED]+statesstates=zip(repostate,states)states[-1],states[-2]=states[-2],states[-1]#clean before ignoredforgrp,stinstates:add_dirs(grp)forfingrp:fpath=os.path.join(root,os.path.normpath(f))add(fpath,st)status=overlay_cache.get(path,UNKNOWN)debugf("%s: %s",(path,status))cache_tick_count=GetTickCount()returnstatusdefadd(path,state):overlay_cache[path]=overlay_cache.get(path,'')+state