CStatusCacheEntry CCachedDirectory::GetStatusFromCache(const CTGitPath& path, bool bRecursive) { if(path.IsDirectory()) { // We don't have directory status in our cache // Ask the directory if it knows its own status CCachedDirectory * dirEntry = CGitStatusCache::Instance().GetDirectoryCacheEntry(path); if( dirEntry) { if (dirEntry->IsOwnStatusValid()) return dirEntry->GetOwnStatus(bRecursive); else { /* cache have outof date, need crawl again*/ /*AutoLocker lock(dirEntry->m_critSec); ChildDirStatus::const_iterator it; for(it = dirEntry->m_childDirectories.begin(); it != dirEntry->m_childDirectories.end(); ++it) { CGitStatusCache::Instance().AddFolderForCrawling(it->first); }*/ CGitStatusCache::Instance().AddFolderForCrawling(path); /*Return old status during crawling*/ return dirEntry->GetOwnStatus(bRecursive); } } else { CGitStatusCache::Instance().AddFolderForCrawling(path); } return CStatusCacheEntry(); } else { //All file ignored if under ignore directory if (m_ownStatus.GetEffectiveStatus() == git_wc_status_ignored) return CStatusCacheEntry(git_wc_status_ignored); if (m_ownStatus.GetEffectiveStatus() == git_wc_status_unversioned) return CStatusCacheEntry(git_wc_status_unversioned); // Look up a file in our own cache AutoLocker lock(m_critSec); CString strCacheKey = GetCacheKey(path); CacheEntryMap::iterator itMap = m_entryCache.find(strCacheKey); if(itMap != m_entryCache.end()) { // We've hit the cache - check for timeout if(!itMap->second.HasExpired((long)GetTickCount())) { if(itMap->second.DoesFileTimeMatch(path.GetLastWriteTime())) { if ((itMap->second.GetEffectiveStatus()!=git_wc_status_missing)||(!PathFileExists(path.GetWinPath()))) { // Note: the filetime matches after a modified has been committed too. // So in that case, we would return a wrong status (e.g. 'modified' instead // of 'normal') here. return itMap->second; } } } } CGitStatusCache::Instance().AddFolderForCrawling(path.GetContainingDirectory()); return CStatusCacheEntry(); } }
CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTSVNPath& path, bool bRecursive, bool bFetch /* = true */) { CStringA strCacheKey; bool bThisDirectoryIsUnversioned = false; bool bRequestForSelf = false; if(path.IsEquivalentToWithoutCase(m_directoryPath)) { bRequestForSelf = true; } // In all most circumstances, we ask for the status of a member of this directory. ATLASSERT(m_directoryPath.IsEquivalentToWithoutCase(path.GetContainingDirectory()) || bRequestForSelf); long long dbFileTime = CSVNStatusCache::Instance().WCRoots()->GetDBFileTime(m_directoryPath); bool wcDbFileTimeChanged = (m_wcDbFileTime != dbFileTime); if ( !wcDbFileTimeChanged ) { if(m_wcDbFileTime == 0) { // We are a folder which is not in a working copy bThisDirectoryIsUnversioned = true; m_ownStatus.SetStatus(NULL, false, false); // If a user removes the .svn directory, we get here with m_entryCache // not being empty, but still us being unversioned if (!m_entryCache.empty()) { m_entryCache.clear(); } ATLASSERT(m_entryCache.empty()); // However, a member *DIRECTORY* might be the top of WC // so we need to ask them to get their own status if(!path.IsDirectory()) { if ((PathFileExists(path.GetWinPath()))||(bRequestForSelf)) return CStatusCacheEntry(); // the entry doesn't exist anymore! // but we can't remove it from the cache here: // the GetStatusForMember() method is called only with a read // lock and not a write lock! // So mark it for crawling, and let the crawler remove it // later CSVNStatusCache::Instance().AddFolderForCrawling(path.GetContainingDirectory()); return CStatusCacheEntry(); } else { // If we're in the special case of a directory being asked for its own status // and this directory is unversioned, then we should just return that here if(bRequestForSelf) { return CStatusCacheEntry(); } } } if (CSVNStatusCache::Instance().GetDirectoryCacheEntryNoCreate(path) != NULL) { // We don't have directory status in our cache // Ask the directory if it knows its own status CCachedDirectory * dirEntry = CSVNStatusCache::Instance().GetDirectoryCacheEntry(path); if ((dirEntry)&&(dirEntry->IsOwnStatusValid())) { // To keep recursive status up to date, we'll request that children are all crawled again // We have to do this because the directory watcher isn't very reliable (especially under heavy load) // and also has problems with SUBSTed drives. // If nothing has changed in those directories, this crawling is fast and only // accesses two files for each directory. if (bRecursive) { AutoLocker lock(dirEntry->m_critSec); ChildDirStatus::const_iterator it; for(it = dirEntry->m_childDirectories.begin(); it != dirEntry->m_childDirectories.end(); ++it) { CTSVNPath newpath; CString winPath = CUnicodeUtils::GetUnicode (it->first); newpath.SetFromWin(winPath, true); CSVNStatusCache::Instance().AddFolderForCrawling(newpath); } } return dirEntry->GetOwnStatus(bRecursive); } } else { { // if we currently are fetching the status of the directory // we want the status for, we just return an empty entry here // and don't wait for that fetching to finish. // That's because fetching the status can take a *really* long // time (e.g. if a commit is also in progress on that same // directory), and we don't want to make the explorer appear // to hang. if ((!bFetch)&&(m_FetchingStatus)) { if (m_directoryPath.IsAncestorOf(path)) { m_currentFullStatus = m_mostImportantFileStatus = svn_wc_status_none; return GetCacheStatusForMember(path); } } } // Look up a file in our own cache AutoLocker lock(m_critSec); strCacheKey = GetCacheKey(path); CacheEntryMap::iterator itMap = m_entryCache.find(strCacheKey); if(itMap != m_entryCache.end()) { // We've hit the cache - check for timeout if(!itMap->second.HasExpired((long)GetTickCount())) { if(itMap->second.DoesFileTimeMatch(path.GetLastWriteTime())) { if ((itMap->second.GetEffectiveStatus()!=svn_wc_status_missing)||(!PathFileExists(path.GetWinPath()))) { // Note: the filetime matches after a modified has been committed too. // So in that case, we would return a wrong status (e.g. 'modified' instead // of 'normal') here. return itMap->second; } } } } } } else { if ((!bFetch)&&(m_FetchingStatus)) { if (m_directoryPath.IsAncestorOf(path)) { // returning empty status (status fetch in progress) // also set the status to 'none' to have the status change and // the shell updater invoked in the crawler m_currentFullStatus = m_mostImportantFileStatus = svn_wc_status_none; CSVNStatusCache::Instance().AddFolderForCrawling(m_directoryPath.GetDirectory()); return GetCacheStatusForMember(path); } } // if we're fetching the status for the explorer, // we don't refresh the status but use the one // we already have (to save time and make the explorer // more responsive in stress conditions). // We leave the refreshing to the crawler. if ((!bFetch)&&(m_wcDbFileTime)) { CSVNStatusCache::Instance().AddFolderForCrawling(m_directoryPath.GetDirectory()); return GetCacheStatusForMember(path); } AutoLocker lock(m_critSec); m_entryCache.clear(); strCacheKey = GetCacheKey(path); } // We've not got this item in the cache - let's add it // We never bother asking SVN for the status of just one file, always for its containing directory if (g_SVNAdminDir.IsAdminDirPath(path.GetWinPathString())) { // We're being asked for the status of an .SVN directory // It's not worth asking for this return CStatusCacheEntry(); } { if ((!bFetch)&&(m_FetchingStatus)) { if (m_directoryPath.IsAncestorOf(path)) { m_currentFullStatus = m_mostImportantFileStatus = svn_wc_status_none; return GetCacheStatusForMember(path); } } } { AutoLocker lock(m_critSec); m_mostImportantFileStatus = svn_wc_status_none; m_childDirectories.clear(); m_entryCache.clear(); m_ownStatus.SetStatus(NULL, false, false); } if(!bThisDirectoryIsUnversioned) { if (!SvnUpdateMembersStatus()) { m_wcDbFileTime = 0; return CStatusCacheEntry(); } } // Now that we've refreshed our SVN status, we can see if it's // changed the 'most important' status value for this directory. // If it has, then we should tell our parent UpdateCurrentStatus(); m_wcDbFileTime = dbFileTime; if (path.IsDirectory()) { CCachedDirectory * dirEntry = CSVNStatusCache::Instance().GetDirectoryCacheEntry(path); if ((dirEntry)&&(dirEntry->IsOwnStatusValid())) { //CSVNStatusCache::Instance().AddFolderForCrawling(path); return dirEntry->GetOwnStatus(bRecursive); } // If the status *still* isn't valid here, it means that // the current directory is unversioned, and we shall need to ask its children for info about themselves if ((dirEntry)&&(dirEntry != this)) return dirEntry->GetStatusForMember(path,bRecursive); // add the path for crawling: if it's really unversioned, the crawler will // only check for the admin dir and do nothing more. But if it is // versioned (could happen in a nested layout) the crawler will update its // status correctly CSVNStatusCache::Instance().AddFolderForCrawling(path); return CStatusCacheEntry(); } else { CacheEntryMap::iterator itMap = m_entryCache.find(strCacheKey); if(itMap != m_entryCache.end()) { return itMap->second; } } AddEntry(path, NULL, false, false); return CStatusCacheEntry(); }