Fix typos
[TortoiseGit.git] / src / TGitCache / CachedDirectory.cpp
blob5a12e89c4e3803c7c3d307406e05d9063328e256
1 // TortoiseGit - a Windows shell extension for easy version control
3 // External Cache Copyright (C) 2005-2008 - TortoiseSVN
4 // Copyright (C) 2008-2019, 2021-2023 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "CachedDirectory.h"
22 #include "GitAdminDir.h"
23 #include "GitStatusCache.h"
24 #include "PathUtils.h"
25 #include "GitStatus.h"
26 #include <set>
28 CCachedDirectory::CCachedDirectory()
32 CCachedDirectory::~CCachedDirectory()
36 CCachedDirectory::CCachedDirectory(const CTGitPath& directoryPath)
37 : CCachedDirectory()
39 ATLASSERT(directoryPath.IsDirectory() || !PathFileExists(directoryPath.GetWinPath()));
41 directoryPath.HasAdminDir(); // make sure HasAdminDir is always initialized
42 m_directoryPath = directoryPath;
43 m_directoryPath.UpdateCase();
44 m_directoryPath.GetGitPathString(); // make sure git path string is set
47 BOOL CCachedDirectory::SaveToDisk(FILE * pFile)
49 AutoLocker lock(m_critSec);
50 #define WRITEVALUETOFILE(x) if (fwrite(&x, sizeof(x), 1, pFile)!=1) return false;
52 unsigned int value = GIT_CACHE_VERSION;
53 WRITEVALUETOFILE(value); // 'version' of this save-format
54 value = static_cast<int>(m_entryCache.size());
55 WRITEVALUETOFILE(value); // size of the cache map
56 // now iterate through the maps and save every entry.
57 for (const auto& entry : m_entryCache)
59 const CString& key = entry.first;
60 value = key.GetLength();
61 WRITEVALUETOFILE(value);
62 if (value)
64 if (fwrite(static_cast<LPCWSTR>(key), sizeof(wchar_t), value, pFile) != value)
65 return false;
66 if (!entry.second.SaveToDisk(pFile))
67 return false;
70 value = static_cast<int>(m_childDirectories.size());
71 WRITEVALUETOFILE(value);
72 for (const auto& entry : m_childDirectories)
74 const CString& path = entry.first;
75 value = path.GetLength();
76 WRITEVALUETOFILE(value);
77 if (value)
79 if (fwrite(static_cast<LPCWSTR>(path), sizeof(wchar_t), value, pFile) != value)
80 return false;
81 git_wc_status_kind status = entry.second;
82 WRITEVALUETOFILE(status);
85 value = m_directoryPath.GetWinPathString().GetLength();
86 WRITEVALUETOFILE(value);
87 if (value)
89 if (fwrite(m_directoryPath.GetWinPath(), sizeof(wchar_t), value, pFile) != value)
90 return false;
92 if (!m_ownStatus.SaveToDisk(pFile))
93 return false;
94 WRITEVALUETOFILE(m_currentFullStatus);
95 WRITEVALUETOFILE(m_mostImportantFileStatus);
96 return true;
99 BOOL CCachedDirectory::LoadFromDisk(FILE * pFile)
101 AutoLocker lock(m_critSec);
102 #define LOADVALUEFROMFILE(x) if (fread(&x, sizeof(x), 1, pFile)!=1) return false;
105 unsigned int value = 0;
106 LOADVALUEFROMFILE(value);
107 if (value != GIT_CACHE_VERSION)
108 return false; // not the correct version
109 int mapsize = 0;
110 LOADVALUEFROMFILE(mapsize);
111 for (int i=0; i<mapsize; ++i)
113 LOADVALUEFROMFILE(value);
114 if (value > MAX_PATH)
115 return false;
116 if (value)
118 CString sKey;
119 if (fread(sKey.GetBuffer(value+1), sizeof(wchar_t), value, pFile) != value)
121 sKey.ReleaseBuffer(0);
122 return false;
124 sKey.ReleaseBuffer(value);
125 CStatusCacheEntry entry;
126 if (!entry.LoadFromDisk(pFile))
127 return false;
128 // only read non empty keys (just needed for transition from old TGit clients)
129 if (!sKey.IsEmpty())
130 m_entryCache[sKey] = entry;
133 LOADVALUEFROMFILE(mapsize);
134 for (int i=0; i<mapsize; ++i)
136 LOADVALUEFROMFILE(value);
137 if (value > MAX_PATH)
138 return false;
139 if (value)
141 CString sPath;
142 if (fread(sPath.GetBuffer(value), sizeof(wchar_t), value, pFile) != value)
144 sPath.ReleaseBuffer(0);
145 return false;
147 sPath.ReleaseBuffer(value);
148 git_wc_status_kind status;
149 LOADVALUEFROMFILE(status);
150 m_childDirectories[sPath] = status;
153 LOADVALUEFROMFILE(value);
154 if (value > MAX_PATH)
155 return false;
156 if (value)
158 CString sPath;
159 if (fread(sPath.GetBuffer(value+1), sizeof(wchar_t), value, pFile) != value)
161 sPath.ReleaseBuffer(0);
162 return false;
164 sPath.ReleaseBuffer(value);
165 // make sure paths do not end with backslash (just needed for transition from old TGit clients)
166 if (sPath.GetLength() > 3 && sPath[sPath.GetLength() - 1] == L'\\')
167 sPath.TrimRight(L'\\');
168 m_directoryPath.SetFromWin(sPath);
169 m_directoryPath.GetGitPathString(); // make sure git path string is set
171 if (!m_ownStatus.LoadFromDisk(pFile))
172 return false;
174 LOADVALUEFROMFILE(m_currentFullStatus);
175 LOADVALUEFROMFILE(m_mostImportantFileStatus);
177 catch ( CAtlException )
179 return false;
181 return true;
185 CStatusCacheEntry CCachedDirectory::GetStatusFromCache(const CTGitPath& path, bool bRecursive)
187 if(path.IsDirectory())
189 // We don't have directory status in our cache
190 // Ask the directory if it knows its own status
191 CCachedDirectory * dirEntry = CGitStatusCache::Instance().GetDirectoryCacheEntry(path);
192 if( dirEntry)
194 if (dirEntry->IsOwnStatusValid())
195 return dirEntry->GetOwnStatus(bRecursive);
196 else
198 /* cache have outof date, need crawl again*/
200 CGitStatusCache::Instance().AddFolderForCrawling(path);
201 // also ask parent in case me might have missed some watcher update requests for our parent
202 CTGitPath parentPath = path.GetContainingDirectory();
203 if (!parentPath.IsEmpty())
205 auto parentEntry = CGitStatusCache::Instance().GetDirectoryCacheEntry(parentPath);
206 if (parentEntry && parentEntry->GetCurrentFullStatus() > git_wc_status_unversioned)
207 CGitStatusCache::Instance().AddFolderForCrawling(parentPath);
210 /*Return old status during crawling*/
211 return dirEntry->GetOwnStatus(bRecursive);
214 else
216 CGitStatusCache::Instance().AddFolderForCrawling(path);
217 // also ask parent in case me might have missed some watcher update requests for our parent
218 CTGitPath parentPath = path.GetContainingDirectory();
219 if (!parentPath.IsEmpty())
221 auto parentEntry = CGitStatusCache::Instance().GetDirectoryCacheEntry(parentPath);
222 if (parentEntry && parentEntry->GetCurrentFullStatus() > git_wc_status_unversioned)
223 CGitStatusCache::Instance().AddFolderForCrawling(parentPath);
226 return CStatusCacheEntry();
228 else
230 //All file ignored if under ignore directory
231 if (m_ownStatus.GetEffectiveStatus() == git_wc_status_ignored)
232 return CStatusCacheEntry(git_wc_status_ignored);
234 // Look up a file in our own cache
235 AutoLocker lock(m_critSec);
236 CString strCacheKey = GetCacheKey(path);
237 CacheEntryMap::iterator itMap = m_entryCache.find(strCacheKey);
238 if(itMap != m_entryCache.end())
240 // We've hit the cache - check for timeout
241 if (!itMap->second.HasExpired(static_cast<LONGLONG>(GetTickCount64())))
243 if (itMap->second.GetEffectiveStatus() == git_wc_status_ignored || itMap->second.GetEffectiveStatus() == git_wc_status_unversioned || itMap->second.DoesFileTimeMatch(path.GetLastWriteTime()))
245 // Note: the filetime matches after a modified has been committed too.
246 // So in that case, we would return a wrong status (e.g. 'modified' instead
247 // of 'normal') here.
248 return itMap->second;
253 CGitStatusCache::Instance().AddFolderForCrawling(path.GetContainingDirectory());
254 return CStatusCacheEntry();
258 CStatusCacheEntry CCachedDirectory::GetStatusFromGit(const CTGitPath &path, const CString& sProjectRoot, bool isSelf)
260 CString subpaths;
261 CString s = path.GetGitPathString();
262 if (s.GetLength() > sProjectRoot.GetLength())
264 if (s[sProjectRoot.GetLength()] == L'/')
265 subpaths = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1);
266 else
267 subpaths = s.Right(s.GetLength() - sProjectRoot.GetLength());
270 GitStatus *pGitStatus = &CGitStatusCache::Instance().m_GitStatus;
271 UNREFERENCED_PARAMETER(pGitStatus);
273 if (EnumFiles(path, sProjectRoot, subpaths, isSelf))
275 // there was an error
276 m_ownStatus = git_wc_status_none;
277 m_currentFullStatus = git_wc_status_none;
278 m_mostImportantFileStatus = git_wc_status_none;
280 AutoLocker lock(m_critSec);
281 for (auto it = m_childDirectories.cbegin(); it != m_childDirectories.cend(); ++it)
282 CGitStatusCache::Instance().AddFolderForCrawling(it->first);
283 m_childDirectories.clear();
284 m_entryCache.clear();
286 UpdateCurrentStatus();
287 // make sure that this status times out soon.
288 CGitStatusCache::Instance().m_folderCrawler.BlockPath(m_directoryPath, 20);
289 CGitStatusCache::Instance().AddFolderForCrawling(m_directoryPath);
290 return CStatusCacheEntry();
292 UpdateCurrentStatus();
293 if (!path.IsDirectory())
294 return GetCacheStatusForMember(path);
295 return CStatusCacheEntry(m_ownStatus);
298 /// bFetch is true, fetch all status, call by crawl.
299 /// bFetch is false, get cache status, return quickly.
301 CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bool bRecursive, bool bFetch /* = true */)
303 bool bRequestForSelf = false;
304 if(path.IsEquivalentToWithoutCase(m_directoryPath))
305 bRequestForSelf = true;
307 // In all most circumstances, we ask for the status of a member of this directory.
308 ATLASSERT(m_directoryPath.IsEquivalentToWithoutCase(path.GetContainingDirectory()) || bRequestForSelf);
310 if (GitAdminDir::IsAdminDirPath(path.GetWinPathString()))
312 // We're being asked for the status of an .git directory
313 // It's not worth asking for this
314 return CStatusCacheEntry();
317 if (bFetch)
319 CString sProjectRoot;
321 AutoLocker lock(m_critSec);
322 // HasAdminDir(..., true) might modify m_directoryPath, so we need to do it synchronized (also write access to m_childDirectories, ... requires it)
323 const bool isVersioned = m_directoryPath.HasAdminDir(&sProjectRoot, true);
324 if (!isVersioned && (bRequestForSelf || !path.IsDirectory()))
326 // shortcut if path is not versioned
327 m_ownStatus = git_wc_status_none;
328 m_mostImportantFileStatus = git_wc_status_none;
329 for (auto it = m_childDirectories.cbegin(); it != m_childDirectories.cend(); ++it)
330 CGitStatusCache::Instance().AddFolderForCrawling(it->first);
331 m_childDirectories.clear();
332 m_entryCache.clear();
333 UpdateCurrentStatus();
334 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": %s is not underversion control\n", path.GetWinPath());
335 return CStatusCacheEntry();
339 if (!bRequestForSelf && path.IsDirectory() && !path.HasAdminDir(&sProjectRoot))
341 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": %s is not underversion control\n", path.GetWinPath());
342 return CStatusCacheEntry();
345 return GetStatusFromGit(path, sProjectRoot, bRequestForSelf);
347 else
349 return GetStatusFromCache(path, bRecursive);
353 CString CCachedDirectory::GetProjectRoot() const
355 CString sProjectRoot;
356 m_directoryPath.HasAdminDir(&sProjectRoot, false);
357 return sProjectRoot;
360 CStatusCacheEntry CCachedDirectory::GetCacheStatusForMember(const CTGitPath& path)
362 // no disk access!
363 AutoLocker lock(m_critSec);
364 CacheEntryMap::iterator itMap = m_entryCache.find(GetCacheKey(path));
365 if(itMap != m_entryCache.end())
366 return itMap->second;
368 return CStatusCacheEntry();
371 int CCachedDirectory::EnumFiles(const CTGitPath& path, CString sProjectRoot, const CString& sSubPath, bool isSelf)
373 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": EnumFiles %s\n", path.GetWinPath());
375 // strip "\" at the end, otherwise cache lookups for drives do not work correctly
376 sProjectRoot.TrimRight(L'\\');
378 GitStatus *pStatus = &CGitStatusCache::Instance().m_GitStatus;
379 UNREFERENCED_PARAMETER(pStatus);
381 if (!path.IsDirectory())
383 git_wc_status2_t status = { git_wc_status_none, false, false };
384 if (pStatus->GetFileStatus(sProjectRoot, sSubPath, status, TRUE, true))
386 // we could not get the status of a file, try whole directory after a short delay if the whole directory was not already crawled with an error
387 if (m_currentFullStatus == git_wc_status_none)
388 return -1;
389 CGitStatusCache::Instance().m_folderCrawler.BlockPath(m_directoryPath, 20);
390 CGitStatusCache::Instance().AddFolderForCrawling(m_directoryPath);
391 return 0;
393 GetStatusCallback(path.GetWinPathString(), &status, false, path.GetLastWriteTime(true), this);
394 RefreshMostImportant(false);
396 else
398 if (isSelf)
400 AutoLocker lock(m_critSec);
401 // clear subdirectory status cache
402 m_childDirectories_tmp.clear();
403 // build new files status cache
404 m_entryCache_tmp.clear();
407 m_mostImportantFileStatus = git_wc_status_none;
408 git_wc_status_kind folderstatus = git_wc_status_unversioned;
409 if (pStatus->EnumDirStatus(sProjectRoot, sSubPath, &folderstatus, GetStatusCallback, this))
410 return -1;
412 if (isSelf)
414 AutoLocker lock(m_critSec);
415 // use a tmp files status cache so that we can still use the old cached values
416 // for deciding whether we have to issue a shell notify
417 m_entryCache = std::move(m_entryCache_tmp);
418 m_childDirectories = std::move(m_childDirectories_tmp);
421 RefreshMostImportant(false);
422 // need to update m_ownStatus
423 m_ownStatus = folderstatus;
426 return 0;
428 void
429 CCachedDirectory::AddEntry(const CTGitPath& path, const git_wc_status2_t* pGitStatus, __int64 lastwritetime)
431 if (!path.IsDirectory())
433 AutoLocker lock(m_critSec);
434 CString cachekey = GetCacheKey(path);
435 CacheEntryMap::iterator entry_it = m_entryCache.lower_bound(cachekey);
436 if (entry_it != m_entryCache.end() && entry_it->first == cachekey)
438 if (pGitStatus)
440 if (entry_it->second.GetEffectiveStatus() > git_wc_status_none && entry_it->second.GetEffectiveStatus() != pGitStatus->status)
442 CGitStatusCache::Instance().UpdateShell(path);
443 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": shell update for %s\n", path.GetWinPath());
447 else
448 entry_it = m_entryCache.insert(entry_it, std::make_pair(cachekey, CStatusCacheEntry()));
449 entry_it->second = CStatusCacheEntry(pGitStatus, lastwritetime);
450 m_entryCache_tmp[cachekey] = entry_it->second;
454 CString
455 CCachedDirectory::GetCacheKey(const CTGitPath& path)
457 // All we put into the cache as a key is just the end portion of the pathname
458 // There's no point storing the path of the containing directory for every item
459 return path.GetWinPathString().Mid(m_directoryPath.GetWinPathString().GetLength()).TrimLeft(L'\\');
462 CString
463 CCachedDirectory::GetFullPathString(const CString& cacheKey)
465 CString fullpath(m_directoryPath.GetWinPathString());
466 fullpath += L'\\';
467 fullpath += cacheKey;
468 return fullpath;
471 BOOL CCachedDirectory::GetStatusCallback(const CString& path, const git_wc_status2_t* pGitStatus, bool isDir, __int64 lastwritetime, void* baton)
473 CTGitPath gitPath(path, isDir);
475 auto pThis = reinterpret_cast<CCachedDirectory*>(baton);
478 if (isDir)
479 { /*gitpath is directory*/
480 ATLASSERT(!gitPath.IsEquivalentToWithoutCase(pThis->m_directoryPath)); // this method does not get called four ourself
481 //if ( !gitPath.IsEquivalentToWithoutCase(pThis->m_directoryPath) )
483 if (pThis->m_bRecursive)
485 // Add any versioned directory, which is not our 'self' entry, to the list for having its status updated
486 if (pGitStatus->status >= git_wc_status_normal || (CGitStatusCache::Instance().IsUnversionedAsModified() && pGitStatus->status == git_wc_status_unversioned))
487 CGitStatusCache::Instance().AddFolderForCrawling(gitPath);
490 // deleted subfolders are reported as modified whereas deleted submodules are reported as deleted
491 if (pGitStatus->status == git_wc_status_deleted || pGitStatus->status == git_wc_status_modified)
493 pThis->SetChildStatus(gitPath.GetWinPathString(), pGitStatus->status);
494 return FALSE;
497 // Make sure we know about this child directory
498 // and keep the last known status so that we can use this
499 // to check whether we need to refresh explorer
500 pThis->KeepChildStatus(gitPath.GetWinPathString());
505 pThis->AddEntry(gitPath, pGitStatus, lastwritetime);
507 return FALSE;
510 bool
511 CCachedDirectory::IsOwnStatusValid() const
513 return m_ownStatus.HasBeenSet() && !m_ownStatus.HasExpired(GetTickCount64());
516 void CCachedDirectory::Invalidate()
518 m_ownStatus.Invalidate();
521 git_wc_status_kind CCachedDirectory::CalculateRecursiveStatus()
523 // Combine our OWN folder status with the most important of our *FILES'* status.
524 git_wc_status_kind retVal = GitStatus::GetMoreImportant(m_mostImportantFileStatus, m_ownStatus.GetEffectiveStatus());
526 // Now combine all our child-directorie's status
527 AutoLocker lock(m_critSec);
528 ChildDirStatus::const_iterator it;
529 for(it = m_childDirectories.begin(); it != m_childDirectories.end(); ++it)
531 retVal = GitStatus::GetMoreImportant(retVal, it->second);
534 // folders can only be none, unversioned, normal, modified, and conflicted
535 GitStatus::AdjustFolderStatus(retVal);
537 if (retVal == git_wc_status_ignored && m_ownStatus.GetEffectiveStatus() != git_wc_status_ignored) // hack to show folders which have only ignored files inside but are not ignored themself
538 retVal = git_wc_status_unversioned;
540 return retVal;
543 // Update our composite status and deal with things if it's changed
544 void CCachedDirectory::UpdateCurrentStatus()
546 git_wc_status_kind newStatus = CalculateRecursiveStatus();
547 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": UpdateCurrentStatus %s new:%d old: %d\n",
548 m_directoryPath.GetWinPath(),
549 newStatus, m_currentFullStatus);
551 if (newStatus != m_currentFullStatus && IsOwnStatusValid())
553 m_currentFullStatus = newStatus;
555 // Our status has changed - tell the shell
556 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Dir %s, status change from %d to %d\n", m_directoryPath.GetWinPath(), m_currentFullStatus, newStatus);
557 CGitStatusCache::Instance().UpdateShell(m_directoryPath);
559 // And tell our parent, if we've got one...
560 // we tell our parent *always* about our status, even if it hasn't
561 // changed. This is to make sure that the parent has really our current
562 // status - the parent can decide itself if our status has changed
563 // or not.
564 CTGitPath parentPath = m_directoryPath.GetContainingDirectory();
565 if(!parentPath.IsEmpty())
567 // We have a parent
568 // just version controled directory need to cache.
569 CString root1, root2;
570 if (parentPath.HasAdminDir(&root1) && (CGitStatusCache::Instance().IsRecurseSubmodules() || m_directoryPath.HasAdminDir(&root2) && CPathUtils::ArePathStringsEqualWithCase(root1, root2)))
572 CCachedDirectory * cachedDir = CGitStatusCache::Instance().GetDirectoryCacheEntry(parentPath);
573 if (cachedDir)
574 cachedDir->UpdateChildDirectoryStatus(m_directoryPath, m_currentFullStatus);
579 // Receive a notification from a child that its status has changed
580 void CCachedDirectory::UpdateChildDirectoryStatus(const CTGitPath& childDir, git_wc_status_kind childStatus)
582 git_wc_status_kind currentStatus = git_wc_status_none;
584 AutoLocker lock(m_critSec);
585 currentStatus = m_childDirectories[childDir.GetWinPathString()];
586 m_childDirectories_tmp[childDir.GetWinPathString()] = childStatus;
588 if ((currentStatus != childStatus)||(!IsOwnStatusValid()))
590 SetChildStatus(childDir.GetWinPathString(), childStatus);
591 UpdateCurrentStatus();
595 void CCachedDirectory::KeepChildStatus(const CString& childDir)
597 AutoLocker lock(m_critSec);
598 auto it = m_childDirectories.find(childDir);
599 if (it != m_childDirectories.cend())
601 // if a submodule was deleted, we must not keep the deleted status if it re-appears - the deleted status cannot be reset otherwise
602 // ATM only missing submodules are reported as deleted, so that this check only performed for submodules which were deleted
603 if (it->second == git_wc_status_deleted)
605 CTGitPath child(childDir);
606 CString root1, root2;
607 if (child.HasAdminDir(&root1) && m_directoryPath.HasAdminDir(&root2) && !CPathUtils::ArePathStringsEqualWithCase(root1, root2))
608 return;
610 m_childDirectories_tmp[childDir] = it->second;
614 void CCachedDirectory::SetChildStatus(const CString& childDir, git_wc_status_kind childStatus)
616 AutoLocker lock(m_critSec);
617 m_childDirectories[childDir] = childStatus;
618 m_childDirectories_tmp[childDir] = childStatus;
621 CStatusCacheEntry CCachedDirectory::GetOwnStatus(bool bRecursive)
623 // Don't return recursive status if we're unversioned ourselves.
624 if (bRecursive && m_ownStatus.GetEffectiveStatus() > git_wc_status_none)
626 CStatusCacheEntry recursiveStatus(m_ownStatus);
627 UpdateCurrentStatus();
628 recursiveStatus.ForceStatus(m_currentFullStatus);
629 return recursiveStatus;
631 else
633 return m_ownStatus;
637 void CCachedDirectory::RefreshStatus(bool bRecursive)
640 AutoLocker lock(m_critSec);
641 m_directoryPath.UpdateCase();
644 // Make sure that our own status is up-to-date
645 GetStatusForMember(m_directoryPath,bRecursive);
648 * TSVNCache here checks whether m_entryCache is still up2date with the filesystem and refreshes all child directories.
649 * In the current TGitCache implementation, however, the file status is always fetched from git when GetStatusForMember is called from here (with fetch=true).
650 * Therefore, it is not necessary check whether the cache is still up to date since we just updated it.
654 void CCachedDirectory::RefreshMostImportant(bool bUpdateShell /* = true */)
656 AutoLocker lock(m_critSec);
657 CacheEntryMap::iterator itMembers;
658 git_wc_status_kind newStatus = git_wc_status_unversioned;
659 for (itMembers = m_entryCache.begin(); itMembers != m_entryCache.end(); ++itMembers)
661 newStatus = GitStatus::GetMoreImportant(newStatus, itMembers->second.GetEffectiveStatus());
662 if (((itMembers->second.GetEffectiveStatus() == git_wc_status_unversioned)||(itMembers->second.GetEffectiveStatus() == git_wc_status_none))
663 &&(CGitStatusCache::Instance().IsUnversionedAsModified()))
665 // treat unversioned files as modified
666 if (newStatus != git_wc_status_added)
667 newStatus = GitStatus::GetMoreImportant(newStatus, git_wc_status_modified);
670 if (bUpdateShell && newStatus != m_mostImportantFileStatus)
672 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": status change of path %s\n", m_directoryPath.GetWinPath());
673 CGitStatusCache::Instance().UpdateShell(m_directoryPath);
675 m_mostImportantFileStatus = newStatus;