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.
21 #include "CachedDirectory.h"
22 #include "GitAdminDir.h"
23 #include "GitStatusCache.h"
24 #include "PathUtils.h"
25 #include "GitStatus.h"
28 CCachedDirectory::CCachedDirectory()
32 CCachedDirectory::~CCachedDirectory()
36 CCachedDirectory::CCachedDirectory(const CTGitPath
& directoryPath
)
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
);
64 if (fwrite(static_cast<LPCWSTR
>(key
), sizeof(wchar_t), value
, pFile
) != value
)
66 if (!entry
.second
.SaveToDisk(pFile
))
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
);
79 if (fwrite(static_cast<LPCWSTR
>(path
), sizeof(wchar_t), value
, pFile
) != value
)
81 git_wc_status_kind status
= entry
.second
;
82 WRITEVALUETOFILE(status
);
85 value
= m_directoryPath
.GetWinPathString().GetLength();
86 WRITEVALUETOFILE(value
);
89 if (fwrite(m_directoryPath
.GetWinPath(), sizeof(wchar_t), value
, pFile
) != value
)
92 if (!m_ownStatus
.SaveToDisk(pFile
))
94 WRITEVALUETOFILE(m_currentFullStatus
);
95 WRITEVALUETOFILE(m_mostImportantFileStatus
);
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
110 LOADVALUEFROMFILE(mapsize
);
111 for (int i
=0; i
<mapsize
; ++i
)
113 LOADVALUEFROMFILE(value
);
114 if (value
> MAX_PATH
)
119 if (fread(sKey
.GetBuffer(value
+1), sizeof(wchar_t), value
, pFile
) != value
)
121 sKey
.ReleaseBuffer(0);
124 sKey
.ReleaseBuffer(value
);
125 CStatusCacheEntry entry
;
126 if (!entry
.LoadFromDisk(pFile
))
128 // only read non empty keys (just needed for transition from old TGit clients)
130 m_entryCache
[sKey
] = entry
;
133 LOADVALUEFROMFILE(mapsize
);
134 for (int i
=0; i
<mapsize
; ++i
)
136 LOADVALUEFROMFILE(value
);
137 if (value
> MAX_PATH
)
142 if (fread(sPath
.GetBuffer(value
), sizeof(wchar_t), value
, pFile
) != value
)
144 sPath
.ReleaseBuffer(0);
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
)
159 if (fread(sPath
.GetBuffer(value
+1), sizeof(wchar_t), value
, pFile
) != value
)
161 sPath
.ReleaseBuffer(0);
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
))
174 LOADVALUEFROMFILE(m_currentFullStatus
);
175 LOADVALUEFROMFILE(m_mostImportantFileStatus
);
177 catch ( CAtlException
)
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
);
194 if (dirEntry
->IsOwnStatusValid())
195 return dirEntry
->GetOwnStatus(bRecursive
);
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
);
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();
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
)
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);
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();
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
);
349 return GetStatusFromCache(path
, bRecursive
);
353 CString
CCachedDirectory::GetProjectRoot() const
355 CString sProjectRoot
;
356 m_directoryPath
.HasAdminDir(&sProjectRoot
, false);
360 CStatusCacheEntry
CCachedDirectory::GetCacheStatusForMember(const CTGitPath
& path
)
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
)
389 CGitStatusCache::Instance().m_folderCrawler
.BlockPath(m_directoryPath
, 20);
390 CGitStatusCache::Instance().AddFolderForCrawling(m_directoryPath
);
393 GetStatusCallback(path
.GetWinPathString(), &status
, false, path
.GetLastWriteTime(true), this);
394 RefreshMostImportant(false);
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))
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
;
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
)
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());
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
;
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
'\\');
463 CCachedDirectory::GetFullPathString(const CString
& cacheKey
)
465 CString
fullpath(m_directoryPath
.GetWinPathString());
467 fullpath
+= cacheKey
;
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
);
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
);
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
);
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
;
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
564 CTGitPath parentPath
= m_directoryPath
.GetContainingDirectory();
565 if(!parentPath
.IsEmpty())
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
);
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
))
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
;
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
;