TortoiseGitBlame: Diff shown in wrong order
[TortoiseGit.git] / src / TGitCache / CachedDirectory.cpp
blobee7769daa45cf1fe2f9b7222deb47493502c4d3d
1 // TortoiseGit - a Windows shell extension for easy version control
3 // External Cache Copyright (C) 2005-2008 - TortoiseSVN
4 // Copyright (C) 2008-2017 - 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(void)
29 : m_currentFullStatus(git_wc_status_none)
30 , m_mostImportantFileStatus(git_wc_status_none)
31 , m_bRecursive(true)
35 CCachedDirectory::~CCachedDirectory(void)
39 CCachedDirectory::CCachedDirectory(const CTGitPath& directoryPath)
40 : CCachedDirectory()
42 ATLASSERT(directoryPath.IsDirectory() || !PathFileExists(directoryPath.GetWinPath()));
44 directoryPath.HasAdminDir(); // make sure HasAdminDir is always initialized
45 m_directoryPath = directoryPath;
46 m_directoryPath.GetGitPathString(); // make sure git path string is set
49 BOOL CCachedDirectory::SaveToDisk(FILE * pFile)
51 AutoLocker lock(m_critSec);
52 #define WRITEVALUETOFILE(x) if (fwrite(&x, sizeof(x), 1, pFile)!=1) return false;
54 unsigned int value = GIT_CACHE_VERSION;
55 WRITEVALUETOFILE(value); // 'version' of this save-format
56 value = (int)m_entryCache.size();
57 WRITEVALUETOFILE(value); // size of the cache map
58 // now iterate through the maps and save every entry.
59 for (const auto& entry : m_entryCache)
61 const CString& key = entry.first;
62 value = key.GetLength();
63 WRITEVALUETOFILE(value);
64 if (value)
66 if (fwrite((LPCTSTR)key, sizeof(TCHAR), value, pFile)!=value)
67 return false;
68 if (!entry.second.SaveToDisk(pFile))
69 return false;
72 value = (int)m_childDirectories.size();
73 WRITEVALUETOFILE(value);
74 for (const auto& entry : m_childDirectories)
76 const CString& path = entry.first;
77 value = path.GetLength();
78 WRITEVALUETOFILE(value);
79 if (value)
81 if (fwrite((LPCTSTR)path, sizeof(TCHAR), value, pFile)!=value)
82 return false;
83 git_wc_status_kind status = entry.second;
84 WRITEVALUETOFILE(status);
87 value = m_directoryPath.GetWinPathString().GetLength();
88 WRITEVALUETOFILE(value);
89 if (value)
91 if (fwrite(m_directoryPath.GetWinPath(), sizeof(TCHAR), value, pFile)!=value)
92 return false;
94 if (!m_ownStatus.SaveToDisk(pFile))
95 return false;
96 WRITEVALUETOFILE(m_currentFullStatus);
97 WRITEVALUETOFILE(m_mostImportantFileStatus);
98 return true;
101 BOOL CCachedDirectory::LoadFromDisk(FILE * pFile)
103 AutoLocker lock(m_critSec);
104 #define LOADVALUEFROMFILE(x) if (fread(&x, sizeof(x), 1, pFile)!=1) return false;
107 unsigned int value = 0;
108 LOADVALUEFROMFILE(value);
109 if (value != GIT_CACHE_VERSION)
110 return false; // not the correct version
111 int mapsize = 0;
112 LOADVALUEFROMFILE(mapsize);
113 for (int i=0; i<mapsize; ++i)
115 LOADVALUEFROMFILE(value);
116 if (value > MAX_PATH)
117 return false;
118 if (value)
120 CString sKey;
121 if (fread(sKey.GetBuffer(value+1), sizeof(TCHAR), value, pFile)!=value)
123 sKey.ReleaseBuffer(0);
124 return false;
126 sKey.ReleaseBuffer(value);
127 CStatusCacheEntry entry;
128 if (!entry.LoadFromDisk(pFile))
129 return false;
130 // only read non empty keys (just needed for transition from old TGit clients)
131 if (!sKey.IsEmpty())
132 m_entryCache[sKey] = entry;
135 LOADVALUEFROMFILE(mapsize);
136 for (int i=0; i<mapsize; ++i)
138 LOADVALUEFROMFILE(value);
139 if (value > MAX_PATH)
140 return false;
141 if (value)
143 CString sPath;
144 if (fread(sPath.GetBuffer(value), sizeof(TCHAR), value, pFile)!=value)
146 sPath.ReleaseBuffer(0);
147 return false;
149 sPath.ReleaseBuffer(value);
150 git_wc_status_kind status;
151 LOADVALUEFROMFILE(status);
152 m_childDirectories[sPath] = status;
155 LOADVALUEFROMFILE(value);
156 if (value > MAX_PATH)
157 return false;
158 if (value)
160 CString sPath;
161 if (fread(sPath.GetBuffer(value+1), sizeof(TCHAR), value, pFile)!=value)
163 sPath.ReleaseBuffer(0);
164 return false;
166 sPath.ReleaseBuffer(value);
167 // make sure paths do not end with backslash (just needed for transition from old TGit clients)
168 if (sPath.GetLength() > 3 && sPath[sPath.GetLength() - 1] == L'\\')
169 sPath.TrimRight(L'\\');
170 m_directoryPath.SetFromWin(sPath);
171 m_directoryPath.GetGitPathString(); // make sure git path string is set
173 if (!m_ownStatus.LoadFromDisk(pFile))
174 return false;
176 LOADVALUEFROMFILE(m_currentFullStatus);
177 LOADVALUEFROMFILE(m_mostImportantFileStatus);
179 catch ( CAtlException )
181 return false;
183 return true;
187 CStatusCacheEntry CCachedDirectory::GetStatusFromCache(const CTGitPath& path, bool bRecursive)
189 if(path.IsDirectory())
191 // We don't have directory status in our cache
192 // Ask the directory if it knows its own status
193 CCachedDirectory * dirEntry = CGitStatusCache::Instance().GetDirectoryCacheEntry(path);
194 if( dirEntry)
196 if (dirEntry->IsOwnStatusValid())
197 return dirEntry->GetOwnStatus(bRecursive);
198 else
200 /* cache have outof date, need crawl again*/
202 CGitStatusCache::Instance().AddFolderForCrawling(path);
203 // also ask parent in case me might have missed some watcher update requests for our parent
204 CTGitPath parentPath = path.GetContainingDirectory();
205 if (!parentPath.IsEmpty())
207 auto parentEntry = CGitStatusCache::Instance().GetDirectoryCacheEntry(parentPath);
208 if (parentEntry && parentEntry->GetCurrentFullStatus() > git_wc_status_unversioned)
209 CGitStatusCache::Instance().AddFolderForCrawling(parentPath);
212 /*Return old status during crawling*/
213 return dirEntry->GetOwnStatus(bRecursive);
216 else
218 CGitStatusCache::Instance().AddFolderForCrawling(path);
219 // also ask parent in case me might have missed some watcher update requests for our parent
220 CTGitPath parentPath = path.GetContainingDirectory();
221 if (!parentPath.IsEmpty())
223 auto parentEntry = CGitStatusCache::Instance().GetDirectoryCacheEntry(parentPath);
224 if (parentEntry && parentEntry->GetCurrentFullStatus() > git_wc_status_unversioned)
225 CGitStatusCache::Instance().AddFolderForCrawling(parentPath);
228 return CStatusCacheEntry();
230 else
232 //All file ignored if under ignore directory
233 if (m_ownStatus.GetEffectiveStatus() == git_wc_status_ignored)
234 return CStatusCacheEntry(git_wc_status_ignored);
236 // Look up a file in our own cache
237 AutoLocker lock(m_critSec);
238 CString strCacheKey = GetCacheKey(path);
239 CacheEntryMap::iterator itMap = m_entryCache.find(strCacheKey);
240 if(itMap != m_entryCache.end())
242 // We've hit the cache - check for timeout
243 if (!itMap->second.HasExpired((LONGLONG)GetTickCount64()))
245 if (itMap->second.GetEffectiveStatus() == git_wc_status_ignored || itMap->second.GetEffectiveStatus() == git_wc_status_unversioned || itMap->second.DoesFileTimeMatch(path.GetLastWriteTime()))
247 // Note: the filetime matches after a modified has been committed too.
248 // So in that case, we would return a wrong status (e.g. 'modified' instead
249 // of 'normal') here.
250 return itMap->second;
255 CGitStatusCache::Instance().AddFolderForCrawling(path.GetContainingDirectory());
256 return CStatusCacheEntry();
260 CStatusCacheEntry CCachedDirectory::GetStatusFromGit(const CTGitPath &path, const CString& sProjectRoot, bool isSelf)
262 CString subpaths;
263 CString s = path.GetGitPathString();
264 if (s.GetLength() > sProjectRoot.GetLength())
266 if (s[sProjectRoot.GetLength()] == L'/')
267 subpaths = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1);
268 else
269 subpaths = s.Right(s.GetLength() - sProjectRoot.GetLength());
272 GitStatus *pGitStatus = &CGitStatusCache::Instance().m_GitStatus;
273 UNREFERENCED_PARAMETER(pGitStatus);
275 if (EnumFiles(path, sProjectRoot, subpaths, isSelf))
277 // there was an error
278 m_ownStatus = git_wc_status_none;
279 m_currentFullStatus = git_wc_status_none;
280 m_mostImportantFileStatus = git_wc_status_none;
282 AutoLocker lock(m_critSec);
283 for (auto it = m_childDirectories.cbegin(); it != m_childDirectories.cend(); ++it)
284 CGitStatusCache::Instance().AddFolderForCrawling(it->first);
285 m_childDirectories.clear();
286 m_entryCache.clear();
288 UpdateCurrentStatus();
289 // make sure that this status times out soon.
290 CGitStatusCache::Instance().m_folderCrawler.BlockPath(m_directoryPath, 20);
291 CGitStatusCache::Instance().AddFolderForCrawling(m_directoryPath);
292 return CStatusCacheEntry();
294 UpdateCurrentStatus();
295 if (!path.IsDirectory())
296 return GetCacheStatusForMember(path);
297 return CStatusCacheEntry(m_ownStatus);
300 /// bFetch is true, fetch all status, call by crawl.
301 /// bFetch is false, get cache status, return quickly.
303 CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bool bRecursive, bool bFetch /* = true */)
305 CString sProjectRoot;
306 bool bIsVersionedPath;
308 bool bRequestForSelf = false;
309 if(path.IsEquivalentToWithoutCase(m_directoryPath))
311 bRequestForSelf = true;
312 AutoLocker lock(m_critSec);
313 // HasAdminDir might modify m_directoryPath, so we need to do it synchronized
314 bIsVersionedPath = m_directoryPath.HasAdminDir(&sProjectRoot);
316 else
317 bIsVersionedPath = path.HasAdminDir(&sProjectRoot);
319 // In all most circumstances, we ask for the status of a member of this directory.
320 ATLASSERT(m_directoryPath.IsEquivalentToWithoutCase(path.GetContainingDirectory()) || bRequestForSelf);
322 //If is not version control path
323 if( !bIsVersionedPath)
325 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": %s is not underversion control\n", path.GetWinPath());
326 return CStatusCacheEntry();
329 // We've not got this item in the cache - let's add it
330 // We never bother asking SVN for the status of just one file, always for its containing directory
332 if (GitAdminDir::IsAdminDirPath(path.GetWinPathString()))
334 // We're being asked for the status of an .git directory
335 // It's not worth asking for this
336 return CStatusCacheEntry();
340 if(bFetch)
342 return GetStatusFromGit(path, sProjectRoot, bRequestForSelf);
344 else
346 return GetStatusFromCache(path, bRecursive);
350 CStatusCacheEntry CCachedDirectory::GetCacheStatusForMember(const CTGitPath& path)
352 // no disk access!
353 AutoLocker lock(m_critSec);
354 CacheEntryMap::iterator itMap = m_entryCache.find(GetCacheKey(path));
355 if(itMap != m_entryCache.end())
356 return itMap->second;
358 return CStatusCacheEntry();
361 int CCachedDirectory::EnumFiles(const CTGitPath& path, CString sProjectRoot, const CString& sSubPath, bool isSelf)
363 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": EnumFiles %s\n", path.GetWinPath());
365 // strip "\" at the end, otherwise cache lookups for drives do not work correctly
366 sProjectRoot.TrimRight(L'\\');
368 GitStatus *pStatus = &CGitStatusCache::Instance().m_GitStatus;
369 UNREFERENCED_PARAMETER(pStatus);
371 if (!path.IsDirectory())
373 git_wc_status2_t status = { git_wc_status_none, false, false };
374 if (pStatus->GetFileStatus(sProjectRoot, sSubPath, status, TRUE, true))
376 // 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
377 if (m_currentFullStatus == git_wc_status_none)
378 return -1;
379 CGitStatusCache::Instance().m_folderCrawler.BlockPath(m_directoryPath, 20);
380 CGitStatusCache::Instance().AddFolderForCrawling(m_directoryPath);
381 return 0;
383 GetStatusCallback(path.GetWinPathString(), &status, false, path.GetLastWriteTime(true), this);
384 RefreshMostImportant(false);
386 else
388 if (isSelf)
390 AutoLocker lock(m_critSec);
391 // clear subdirectory status cache
392 m_childDirectories_tmp.clear();
393 // build new files status cache
394 m_entryCache_tmp.clear();
397 m_mostImportantFileStatus = git_wc_status_none;
398 git_wc_status_kind folderstatus = git_wc_status_unversioned;
399 if (pStatus->EnumDirStatus(sProjectRoot, sSubPath, &folderstatus, GetStatusCallback, this))
400 return -1;
402 if (isSelf)
404 AutoLocker lock(m_critSec);
405 // use a tmp files status cache so that we can still use the old cached values
406 // for deciding whether we have to issue a shell notify
407 m_entryCache = std::move(m_entryCache_tmp);
408 m_childDirectories = std::move(m_childDirectories_tmp);
411 RefreshMostImportant(false);
412 // need to update m_ownStatus
413 m_ownStatus = folderstatus;
416 return 0;
418 void
419 CCachedDirectory::AddEntry(const CTGitPath& path, const git_wc_status2_t* pGitStatus, __int64 lastwritetime)
421 if (!path.IsDirectory())
423 AutoLocker lock(m_critSec);
424 CString cachekey = GetCacheKey(path);
425 CacheEntryMap::iterator entry_it = m_entryCache.lower_bound(cachekey);
426 if (entry_it != m_entryCache.end() && entry_it->first == cachekey)
428 if (pGitStatus)
430 if (entry_it->second.GetEffectiveStatus() > git_wc_status_none && entry_it->second.GetEffectiveStatus() != pGitStatus->status)
432 CGitStatusCache::Instance().UpdateShell(path);
433 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": shell update for %s\n", path.GetWinPath());
437 else
438 entry_it = m_entryCache.insert(entry_it, std::make_pair(cachekey, CStatusCacheEntry()));
439 entry_it->second = CStatusCacheEntry(pGitStatus, lastwritetime);
440 m_entryCache_tmp[cachekey] = entry_it->second;
444 CString
445 CCachedDirectory::GetCacheKey(const CTGitPath& path)
447 // All we put into the cache as a key is just the end portion of the pathname
448 // There's no point storing the path of the containing directory for every item
449 return path.GetWinPathString().Mid(m_directoryPath.GetWinPathString().GetLength()).TrimLeft(L'\\');
452 CString
453 CCachedDirectory::GetFullPathString(const CString& cacheKey)
455 CString fullpath(m_directoryPath.GetWinPathString());
456 fullpath += L'\\';
457 fullpath += cacheKey;
458 return fullpath;
461 BOOL CCachedDirectory::GetStatusCallback(const CString& path, const git_wc_status2_t* pGitStatus, bool isDir, __int64 lastwritetime, void* baton)
463 CTGitPath gitPath(path, isDir);
465 auto pThis = reinterpret_cast<CCachedDirectory*>(baton);
468 if (isDir)
469 { /*gitpath is directory*/
470 ATLASSERT(!gitPath.IsEquivalentToWithoutCase(pThis->m_directoryPath)); // this method does not get called four ourself
471 //if ( !gitPath.IsEquivalentToWithoutCase(pThis->m_directoryPath) )
473 if (pThis->m_bRecursive)
475 // Add any versioned directory, which is not our 'self' entry, to the list for having its status updated
476 if (pGitStatus->status >= git_wc_status_normal || (CGitStatusCache::Instance().IsUnversionedAsModified() && pGitStatus->status == git_wc_status_unversioned))
477 CGitStatusCache::Instance().AddFolderForCrawling(gitPath);
480 // deleted subfolders are reported as modified whereas deleted submodules are reported as deleted
481 if (pGitStatus->status == git_wc_status_deleted || pGitStatus->status == git_wc_status_modified)
483 pThis->SetChildStatus(gitPath.GetWinPathString(), pGitStatus->status);
484 return FALSE;
487 // Make sure we know about this child directory
488 // and keep the last known status so that we can use this
489 // to check whether we need to refresh explorer
490 pThis->KeepChildStatus(gitPath.GetWinPathString());
495 pThis->AddEntry(gitPath, pGitStatus, lastwritetime);
497 return FALSE;
500 bool
501 CCachedDirectory::IsOwnStatusValid() const
503 return m_ownStatus.HasBeenSet() && !m_ownStatus.HasExpired(GetTickCount64());
506 void CCachedDirectory::Invalidate()
508 m_ownStatus.Invalidate();
511 git_wc_status_kind CCachedDirectory::CalculateRecursiveStatus()
513 // Combine our OWN folder status with the most important of our *FILES'* status.
514 git_wc_status_kind retVal = GitStatus::GetMoreImportant(m_mostImportantFileStatus, m_ownStatus.GetEffectiveStatus());
516 // Now combine all our child-directorie's status
517 AutoLocker lock(m_critSec);
518 ChildDirStatus::const_iterator it;
519 for(it = m_childDirectories.begin(); it != m_childDirectories.end(); ++it)
521 retVal = GitStatus::GetMoreImportant(retVal, it->second);
524 // folders can only be none, unversioned, normal, modified, and conflicted
525 GitStatus::AdjustFolderStatus(retVal);
527 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
528 retVal = git_wc_status_unversioned;
530 return retVal;
533 // Update our composite status and deal with things if it's changed
534 void CCachedDirectory::UpdateCurrentStatus()
536 git_wc_status_kind newStatus = CalculateRecursiveStatus();
537 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": UpdateCurrentStatus %s new:%d old: %d\n",
538 m_directoryPath.GetWinPath(),
539 newStatus, m_currentFullStatus);
541 if (newStatus != m_currentFullStatus && IsOwnStatusValid())
543 m_currentFullStatus = newStatus;
545 // Our status has changed - tell the shell
546 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Dir %s, status change from %d to %d\n", m_directoryPath.GetWinPath(), m_currentFullStatus, newStatus);
547 CGitStatusCache::Instance().UpdateShell(m_directoryPath);
549 // And tell our parent, if we've got one...
550 // we tell our parent *always* about our status, even if it hasn't
551 // changed. This is to make sure that the parent has really our current
552 // status - the parent can decide itself if our status has changed
553 // or not.
554 CTGitPath parentPath = m_directoryPath.GetContainingDirectory();
555 if(!parentPath.IsEmpty())
557 // We have a parent
558 // just version controled directory need to cache.
559 CString root1, root2;
560 if (parentPath.HasAdminDir(&root1) && (CGitStatusCache::Instance().IsRecurseSubmodules() || m_directoryPath.HasAdminDir(&root2) && CPathUtils::ArePathStringsEqualWithCase(root1, root2)))
562 CCachedDirectory * cachedDir = CGitStatusCache::Instance().GetDirectoryCacheEntry(parentPath);
563 if (cachedDir)
564 cachedDir->UpdateChildDirectoryStatus(m_directoryPath, m_currentFullStatus);
569 // Receive a notification from a child that its status has changed
570 void CCachedDirectory::UpdateChildDirectoryStatus(const CTGitPath& childDir, git_wc_status_kind childStatus)
572 git_wc_status_kind currentStatus = git_wc_status_none;
574 AutoLocker lock(m_critSec);
575 currentStatus = m_childDirectories[childDir.GetWinPathString()];
576 m_childDirectories_tmp[childDir.GetWinPathString()] = childStatus;
578 if ((currentStatus != childStatus)||(!IsOwnStatusValid()))
580 SetChildStatus(childDir.GetWinPathString(), childStatus);
581 UpdateCurrentStatus();
585 void CCachedDirectory::KeepChildStatus(const CString& childDir)
587 AutoLocker lock(m_critSec);
588 auto it = m_childDirectories.find(childDir);
589 if (it != m_childDirectories.cend())
591 // if a submodule was deleted, we must not keep the deleted status if it re-appears - the deleted status cannot be reset otherwise
592 // ATM only missing submodules are reported as deleted, so that this check only performed for submodules which were deleted
593 if (it->second == git_wc_status_deleted)
595 CTGitPath child(childDir);
596 CString root1, root2;
597 if (child.HasAdminDir(&root1) && m_directoryPath.HasAdminDir(&root2) && !CPathUtils::ArePathStringsEqualWithCase(root1, root2))
598 return;
600 m_childDirectories_tmp[childDir] = it->second;
604 void CCachedDirectory::SetChildStatus(const CString& childDir, git_wc_status_kind childStatus)
606 AutoLocker lock(m_critSec);
607 m_childDirectories[childDir] = childStatus;
608 m_childDirectories_tmp[childDir] = childStatus;
611 CStatusCacheEntry CCachedDirectory::GetOwnStatus(bool bRecursive)
613 // Don't return recursive status if we're unversioned ourselves.
614 if (bRecursive && m_ownStatus.GetEffectiveStatus() > git_wc_status_none)
616 CStatusCacheEntry recursiveStatus(m_ownStatus);
617 UpdateCurrentStatus();
618 recursiveStatus.ForceStatus(m_currentFullStatus);
619 return recursiveStatus;
621 else
623 return m_ownStatus;
627 void CCachedDirectory::RefreshStatus(bool bRecursive)
630 AutoLocker lock(m_critSec);
631 m_directoryPath.UpdateCase();
632 m_directoryPath.HasAdminDir(nullptr, true);
635 // Make sure that our own status is up-to-date
636 GetStatusForMember(m_directoryPath,bRecursive);
639 * TSVNCache here checks whether m_entryCache is still up2date with the filesystem and refreshes all child directories.
640 * In the current TGitCache implementation, however, the file status is always fetched from git when GetStatusForMember is called from here (with fetch=true).
641 * Therefore, it is not necessary check whether the cache is still up to date since we just updated it.
645 void CCachedDirectory::RefreshMostImportant(bool bUpdateShell /* = true */)
647 AutoLocker lock(m_critSec);
648 CacheEntryMap::iterator itMembers;
649 git_wc_status_kind newStatus = git_wc_status_unversioned;
650 for (itMembers = m_entryCache.begin(); itMembers != m_entryCache.end(); ++itMembers)
652 newStatus = GitStatus::GetMoreImportant(newStatus, itMembers->second.GetEffectiveStatus());
653 if (((itMembers->second.GetEffectiveStatus() == git_wc_status_unversioned)||(itMembers->second.GetEffectiveStatus() == git_wc_status_none))
654 &&(CGitStatusCache::Instance().IsUnversionedAsModified()))
656 // treat unversioned files as modified
657 if (newStatus != git_wc_status_added)
658 newStatus = GitStatus::GetMoreImportant(newStatus, git_wc_status_modified);
661 if (bUpdateShell && newStatus != m_mostImportantFileStatus)
663 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": status change of path %s\n", m_directoryPath.GetWinPath());
664 CGitStatusCache::Instance().UpdateShell(m_directoryPath);
666 m_mostImportantFileStatus = newStatus;