OSDN Git Service

fixed issues with status (and icon overlays) in root directories
[tortoisegit/TortoiseGitJp.git] / src / TGitCache / CachedDirectory.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // External Cache Copyright (C) 2005-2008 - TortoiseSVN\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "StdAfx.h"\r
20 #include ".\cacheddirectory.h"\r
21 //#include "SVNHelpers.h"\r
22 #include "GitStatusCache.h"\r
23 #include "GitStatus.h"\r
24 #include <set>\r
25 \r
26 CCachedDirectory::CCachedDirectory(void)\r
27 {\r
28         m_indexFileTime = 0;\r
29 //      m_propsFileTime = 0;\r
30         m_currentStatusFetchingPathTicks = 0;\r
31         m_bCurrentFullStatusValid = false;\r
32         m_currentFullStatus = m_mostImportantFileStatus = git_wc_status_none;\r
33         m_bRecursive = true;\r
34 }\r
35 \r
36 CCachedDirectory::~CCachedDirectory(void)\r
37 {\r
38 }\r
39 \r
40 CCachedDirectory::CCachedDirectory(const CTGitPath& directoryPath)\r
41 {\r
42         ATLASSERT(directoryPath.IsDirectory() || !PathFileExists(directoryPath.GetWinPath()));\r
43 \r
44         m_directoryPath = directoryPath;\r
45         m_indexFileTime = 0;\r
46 //      m_propsFileTime = 0;\r
47         m_currentStatusFetchingPathTicks = 0;\r
48         m_bCurrentFullStatusValid = false;\r
49         m_currentFullStatus = m_mostImportantFileStatus = git_wc_status_none;\r
50         m_bRecursive = true;\r
51 }\r
52 \r
53 BOOL CCachedDirectory::SaveToDisk(FILE * pFile)\r
54 {\r
55         AutoLocker lock(m_critSec);\r
56 #define WRITEVALUETOFILE(x) if (fwrite(&x, sizeof(x), 1, pFile)!=1) return false;\r
57 \r
58         unsigned int value = 1;\r
59         WRITEVALUETOFILE(value);        // 'version' of this save-format\r
60         value = (int)m_entryCache.size();\r
61         WRITEVALUETOFILE(value);        // size of the cache map\r
62         // now iterate through the maps and save every entry.\r
63         for (CacheEntryMap::iterator I = m_entryCache.begin(); I != m_entryCache.end(); ++I)\r
64         {\r
65                 const CString& key = I->first;\r
66                 value = key.GetLength();\r
67                 WRITEVALUETOFILE(value);\r
68                 if (value)\r
69                 {\r
70                         if (fwrite((LPCTSTR)key, sizeof(TCHAR), value, pFile)!=value)\r
71                                 return false;\r
72                         if (!I->second.SaveToDisk(pFile))\r
73                                 return false;\r
74                 }\r
75         }\r
76         value = (int)m_childDirectories.size();\r
77         WRITEVALUETOFILE(value);\r
78         for (ChildDirStatus::iterator I = m_childDirectories.begin(); I != m_childDirectories.end(); ++I)\r
79         {\r
80                 const CString& path = I->first.GetWinPathString();\r
81                 value = path.GetLength();\r
82                 WRITEVALUETOFILE(value);\r
83                 if (value)\r
84                 {\r
85                         if (fwrite((LPCTSTR)path, sizeof(TCHAR), value, pFile)!=value)\r
86                                 return false;\r
87                         git_wc_status_kind status = I->second;\r
88                         WRITEVALUETOFILE(status);\r
89                 }\r
90         }\r
91         WRITEVALUETOFILE(m_indexFileTime);\r
92 //      WRITEVALUETOFILE(m_propsFileTime);\r
93         value = m_directoryPath.GetWinPathString().GetLength();\r
94         WRITEVALUETOFILE(value);\r
95         if (value)\r
96         {\r
97                 if (fwrite(m_directoryPath.GetWinPath(), sizeof(TCHAR), value, pFile)!=value)\r
98                         return false;\r
99         }\r
100         if (!m_ownStatus.SaveToDisk(pFile))\r
101                 return false;\r
102         WRITEVALUETOFILE(m_currentFullStatus);\r
103         WRITEVALUETOFILE(m_mostImportantFileStatus);\r
104         return true;\r
105 }\r
106 \r
107 BOOL CCachedDirectory::LoadFromDisk(FILE * pFile)\r
108 {\r
109         AutoLocker lock(m_critSec);\r
110 #define LOADVALUEFROMFILE(x) if (fread(&x, sizeof(x), 1, pFile)!=1) return false;\r
111         try\r
112         {\r
113                 unsigned int value = 0;\r
114                 LOADVALUEFROMFILE(value);\r
115                 if (value != 1)\r
116                         return false;           // not the correct version\r
117                 int mapsize = 0;\r
118                 LOADVALUEFROMFILE(mapsize);\r
119                 for (int i=0; i<mapsize; ++i)\r
120                 {\r
121                         LOADVALUEFROMFILE(value);\r
122                         if (value > MAX_PATH)\r
123                                 return false;\r
124                         if (value)\r
125                         {\r
126                                 CString sKey;\r
127                                 if (fread(sKey.GetBuffer(value+1), sizeof(TCHAR), value, pFile)!=value)\r
128                                 {\r
129                                         sKey.ReleaseBuffer(0);\r
130                                         return false;\r
131                                 }\r
132                                 sKey.ReleaseBuffer(value);\r
133                                 CStatusCacheEntry entry;\r
134                                 if (!entry.LoadFromDisk(pFile))\r
135                                         return false;\r
136                                 m_entryCache[sKey] = entry;\r
137                         }\r
138                 }\r
139                 LOADVALUEFROMFILE(mapsize);\r
140                 for (int i=0; i<mapsize; ++i)\r
141                 {\r
142                         LOADVALUEFROMFILE(value);\r
143                         if (value > MAX_PATH)\r
144                                 return false;\r
145                         if (value)\r
146                         {\r
147                                 CString sPath;\r
148                                 if (fread(sPath.GetBuffer(value), sizeof(TCHAR), value, pFile)!=value)\r
149                                 {\r
150                                         sPath.ReleaseBuffer(0);\r
151                                         return false;\r
152                                 }\r
153                                 sPath.ReleaseBuffer(value);\r
154                                 git_wc_status_kind status;\r
155                                 LOADVALUEFROMFILE(status);\r
156                                 m_childDirectories[CTGitPath(sPath)] = status;\r
157                         }\r
158                 }\r
159                 LOADVALUEFROMFILE(m_indexFileTime);\r
160 //              LOADVALUEFROMFILE(m_propsFileTime);\r
161                 LOADVALUEFROMFILE(value);\r
162                 if (value > MAX_PATH)\r
163                         return false;\r
164                 if (value)\r
165                 {\r
166                         CString sPath;\r
167                         if (fread(sPath.GetBuffer(value+1), sizeof(TCHAR), value, pFile)!=value)\r
168                         {\r
169                                 sPath.ReleaseBuffer(0);\r
170                                 return false;\r
171                         }\r
172                         sPath.ReleaseBuffer(value);\r
173                         m_directoryPath.SetFromWin(sPath);\r
174                 }\r
175                 if (!m_ownStatus.LoadFromDisk(pFile))\r
176                         return false;\r
177 \r
178                 LOADVALUEFROMFILE(m_currentFullStatus);\r
179                 LOADVALUEFROMFILE(m_mostImportantFileStatus);\r
180         }\r
181         catch ( CAtlException )\r
182         {\r
183                 return false;\r
184         }\r
185         return true;\r
186 \r
187 }\r
188 \r
189 CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bool bRecursive,  bool bFetch /* = true */)\r
190 {\r
191         CString strCacheKey;\r
192         bool bThisDirectoryIsUnversioned = false;\r
193         bool bRequestForSelf = false;\r
194         if(path.IsEquivalentToWithoutCase(m_directoryPath))\r
195         {\r
196                 bRequestForSelf = true;\r
197         }\r
198 //OutputDebugStringA("GetStatusForMember: ");OutputDebugStringW(path.GetWinPathString());OutputDebugStringA("\r\n");\r
199         // In all most circumstances, we ask for the status of a member of this directory.\r
200         ATLASSERT(m_directoryPath.IsEquivalentToWithoutCase(path.GetContainingDirectory()) || bRequestForSelf);\r
201 \r
202         CString sProjectRoot;\r
203         const BOOL bIsVersionedPath = m_directoryPath.HasAdminDir(&sProjectRoot);\r
204 \r
205         // Check if the index file has been changed\r
206         CTGitPath indexFilePath(bIsVersionedPath ? sProjectRoot : m_directoryPath);\r
207 //      CTGitPath propsDirPath(m_directoryPath);\r
208         if (g_GitAdminDir.IsVSNETHackActive())\r
209         {\r
210                 indexFilePath.AppendPathString(g_GitAdminDir.GetVSNETAdminDirName() + _T("\\index"));\r
211 //              propsDirPath.AppendPathString(g_GitAdminDir.GetVSNETAdminDirName() + _T("\\dir-props"));\r
212         }\r
213         else\r
214         {\r
215                 indexFilePath.AppendPathString(g_GitAdminDir.GetAdminDirName() + _T("\\index"));\r
216 //              propsDirPath.AppendPathString(g_GitAdminDir.GetAdminDirName() + _T("\\dir-props"));\r
217         }\r
218         if ( (m_indexFileTime == indexFilePath.GetLastWriteTime()) /*&& ((indexFilePath.GetLastWriteTime() == 0) || (m_propsFileTime == propsDirPath.GetLastWriteTime()))*/ )\r
219         {\r
220 //              m_indexFileTime = indexFilePath.GetLastWriteTime();\r
221 //              if (m_indexFileTime)\r
222 //                      m_propsFileTime = propsDirPath.GetLastWriteTime();\r
223 \r
224                 //if(m_indexFileTime == 0)\r
225                 // a newly created project (without commits) has no index file but we still want it to count as versioned\r
226                 if(m_indexFileTime == 0 && !bIsVersionedPath)\r
227                 {\r
228                         // We are a folder which is not in a working copy\r
229                         bThisDirectoryIsUnversioned = true;\r
230                         m_ownStatus.SetStatus(NULL);\r
231 \r
232                         // If a user removes the .git directory, we get here with m_entryCache\r
233                         // not being empty, but still us being unversioned\r
234                         if (!m_entryCache.empty())\r
235                         {\r
236                                 m_entryCache.clear();\r
237                         }\r
238                         ATLASSERT(m_entryCache.empty());\r
239 \r
240                         // However, a member *DIRECTORY* might be the top of WC\r
241                         // so we need to ask them to get their own status\r
242                         if(!path.IsDirectory())\r
243                         {\r
244                                 if ((PathFileExists(path.GetWinPath()))||(bRequestForSelf))\r
245                                         return CStatusCacheEntry();\r
246                                 // the entry doesn't exist anymore! \r
247                                 // but we can't remove it from the cache here:\r
248                                 // the GetStatusForMember() method is called only with a read\r
249                                 // lock and not a write lock!\r
250                                 // So mark it for crawling, and let the crawler remove it\r
251                                 // later\r
252                                 CGitStatusCache::Instance().AddFolderForCrawling(path.GetContainingDirectory());\r
253 \r
254                                 return CStatusCacheEntry();\r
255                         }\r
256                         else\r
257                         {\r
258                                 // If we're in the special case of a directory being asked for its own status\r
259                                 // and this directory is unversioned, then we should just return that here\r
260                                 if(bRequestForSelf)\r
261                                         return CStatusCacheEntry();\r
262                         }\r
263                 }\r
264 \r
265                 if(path.IsDirectory())\r
266                 {\r
267                         // We don't have directory status in our cache\r
268                         // Ask the directory if it knows its own status\r
269                         CCachedDirectory * dirEntry = CGitStatusCache::Instance().GetDirectoryCacheEntry(path);\r
270                         if ((dirEntry)&&(dirEntry->IsOwnStatusValid()))\r
271                         {\r
272                                 // To keep recursive status up to date, we'll request that children are all crawled again\r
273                                 // This will be very quick if nothings changed, because it will all be cache hits\r
274                                 if (bRecursive)\r
275                                 {\r
276                                         AutoLocker lock(dirEntry->m_critSec);\r
277                                         ChildDirStatus::const_iterator it;\r
278                                         for(it = dirEntry->m_childDirectories.begin(); it != dirEntry->m_childDirectories.end(); ++it)\r
279                                         {\r
280                                                 CGitStatusCache::Instance().AddFolderForCrawling(it->first);\r
281                                         }\r
282                                 }\r
283                                 return dirEntry->GetOwnStatus(bRecursive);\r
284                         }\r
285                 }\r
286                 else\r
287                 {\r
288                         {\r
289                                 // if we currently are fetching the status of the directory\r
290                                 // we want the status for, we just return an empty entry here\r
291                                 // and don't wait for that fetching to finish.\r
292                                 // That's because fetching the status can take a *really* long\r
293                                 // time (e.g. if a commit is also in progress on that same\r
294                                 // directory), and we don't want to make the explorer appear\r
295                                 // to hang.\r
296                                 AutoLocker pathlock(m_critSecPath);\r
297                                 if ((!bFetch)&&(!m_currentStatusFetchingPath.IsEmpty()))\r
298                                 {\r
299                                         if ((m_currentStatusFetchingPath.IsAncestorOf(path))&&((m_currentStatusFetchingPathTicks + 1000)<GetTickCount()))\r
300                                         {\r
301                                                 ATLTRACE(_T("returning empty status (status fetch in progress) for %s\n"), path.GetWinPath());\r
302                                                 m_currentFullStatus = m_mostImportantFileStatus = git_wc_status_none;\r
303                                                 return CStatusCacheEntry();\r
304                                         }\r
305                                 }\r
306                         }\r
307                         // Look up a file in our own cache\r
308                         AutoLocker lock(m_critSec);\r
309                         strCacheKey = GetCacheKey(path);\r
310                         CacheEntryMap::iterator itMap = m_entryCache.find(strCacheKey);\r
311                         if(itMap != m_entryCache.end())\r
312                         {\r
313                                 // We've hit the cache - check for timeout\r
314                                 if(!itMap->second.HasExpired((long)GetTickCount()))\r
315                                 {\r
316                                         if(itMap->second.DoesFileTimeMatch(path.GetLastWriteTime()))\r
317                                         {\r
318                                                 if ((itMap->second.GetEffectiveStatus()!=git_wc_status_missing)||(!PathFileExists(path.GetWinPath())))\r
319                                                 {\r
320                                                         // Note: the filetime matches after a modified has been committed too.\r
321                                                         // So in that case, we would return a wrong status (e.g. 'modified' instead\r
322                                                         // of 'normal') here.\r
323                                                         return itMap->second;\r
324                                                 }\r
325                                         }\r
326                                 }\r
327                         }\r
328                 }\r
329         }\r
330         else\r
331         {\r
332                 AutoLocker pathlock(m_critSecPath);\r
333                 if ((!bFetch)&&(!m_currentStatusFetchingPath.IsEmpty()))\r
334                 {\r
335                         if ((m_currentStatusFetchingPath.IsAncestorOf(path))&&((m_currentStatusFetchingPathTicks + 1000)<GetTickCount()))\r
336                         {\r
337                                 ATLTRACE(_T("returning empty status (status fetch in progress) for %s\n"), path.GetWinPath());\r
338                                 m_currentFullStatus = m_mostImportantFileStatus = git_wc_status_none;\r
339                                 return CStatusCacheEntry();\r
340                         }\r
341                 }\r
342                 // if we're fetching the status for the explorer,\r
343                 // we don't refresh the status but use the one\r
344                 // we already have (to save time and make the explorer\r
345                 // more responsive in stress conditions).\r
346                 // We leave the refreshing to the crawler.\r
347                 if ((!bFetch)&&(m_indexFileTime))\r
348                 {\r
349                         CGitStatusCache::Instance().AddFolderForCrawling(path.GetDirectory());\r
350                         return CStatusCacheEntry();\r
351                 }\r
352                 AutoLocker lock(m_critSec);\r
353                 m_indexFileTime = indexFilePath.GetLastWriteTime();\r
354 //              m_propsFileTime = propsDirPath.GetLastWriteTime();\r
355                 m_entryCache.clear();\r
356                 strCacheKey = GetCacheKey(path);\r
357         }\r
358 \r
359 //      svn_opt_revision_t revision;\r
360 //      revision.kind = svn_opt_revision_unspecified;\r
361 \r
362         // We've not got this item in the cache - let's add it\r
363         // We never bother asking SVN for the status of just one file, always for its containing directory\r
364 \r
365         if (g_GitAdminDir.IsAdminDirPath(path.GetWinPathString()))\r
366         {\r
367                 // We're being asked for the status of an .git directory\r
368                 // It's not worth asking for this\r
369                 return CStatusCacheEntry();\r
370         }\r
371 \r
372         {\r
373                 {\r
374                         AutoLocker pathlock(m_critSecPath);\r
375                         if ((!bFetch)&&(!m_currentStatusFetchingPath.IsEmpty()))\r
376                         {\r
377                                 if ((m_currentStatusFetchingPath.IsAncestorOf(path))&&((m_currentStatusFetchingPathTicks + 1000)<GetTickCount()))\r
378                                 {\r
379                                         ATLTRACE(_T("returning empty status (status fetch in progress) for %s\n"), path.GetWinPath());\r
380                                         m_currentFullStatus = m_mostImportantFileStatus = git_wc_status_none;\r
381                                         return CStatusCacheEntry();\r
382                                 }\r
383                         }\r
384                 }\r
385 //              SVNPool subPool(CGitStatusCache::Instance().m_svnHelp.Pool());\r
386                 {\r
387                         AutoLocker lock(m_critSec);\r
388                         m_mostImportantFileStatus = git_wc_status_none;\r
389                         m_childDirectories.clear();\r
390                         m_entryCache.clear();\r
391                         m_ownStatus.SetStatus(NULL);\r
392                         m_bRecursive = bRecursive;\r
393                 }\r
394                 if(!bThisDirectoryIsUnversioned)\r
395                 {\r
396                         {\r
397                                 AutoLocker pathlock(m_critSecPath);\r
398                                 m_currentStatusFetchingPath = m_directoryPath;\r
399                                 m_currentStatusFetchingPathTicks = GetTickCount();\r
400                         }\r
401                         ATLTRACE(_T("git_enum_files for '%s' (req %s)\n"), m_directoryPath.GetWinPath(), path.GetWinPath());\r
402 \r
403                         CString sProjectRoot;\r
404                         m_directoryPath.HasAdminDir(&sProjectRoot);\r
405                         ATLASSERT( !m_directoryPath.IsEmpty() );\r
406 \r
407                         LPCTSTR lpszSubPath = NULL;\r
408                         CString sSubPath;\r
409                         CString s = m_directoryPath.GetDirectory().GetWinPathString();\r
410                         if (s.GetLength() > sProjectRoot.GetLength())\r
411                         {\r
412                                 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());\r
413                                 lpszSubPath = sSubPath;\r
414                                 // skip initial slash if necessary\r
415                                 if (*lpszSubPath == _T('\\'))\r
416                                         lpszSubPath++;\r
417                         }\r
418 //MessageBoxA(NULL, CStringA(sProjectRoot), sSubPath, MB_OK);\r
419 //OutputDebugStringA("###");OutputDebugStringW(sProjectRoot);OutputDebugStringA(" - ");OutputDebugStringA(sSubPath);OutputDebugStringA("\r\n");\r
420                         BOOL pErr = !wgEnumFiles(sProjectRoot, lpszSubPath, WGEFF_NoRecurse|WGEFF_FullPath, &GetStatusCallback, this);\r
421 \r
422                         /*git_error_t* pErr = svn_client_status4 (\r
423                                 NULL,\r
424                                 m_directoryPath.GetSVNApiPath(subPool),\r
425                                 &revision,\r
426                                 GetStatusCallback,\r
427                                 this,\r
428                                 svn_depth_immediates,\r
429                                 TRUE,                                                                   //getall\r
430                                 FALSE,\r
431                                 TRUE,                                                                   //noignore\r
432                                 FALSE,                                                                  //ignore externals\r
433                                 NULL,                                                                   //changelists\r
434                                 CGitStatusCache::Instance().m_svnHelp.ClientContext(),\r
435                                 subPool\r
436                                 );*/\r
437                         {\r
438                                 AutoLocker pathlock(m_critSecPath);\r
439                                 m_currentStatusFetchingPath.Reset();\r
440                         }\r
441                         ATLTRACE(_T("git_enum_files finished for '%s'\n"), m_directoryPath.GetWinPath(), path.GetWinPath());\r
442                         if(pErr)\r
443                         {\r
444                                 // Handle an error\r
445                                 // The most likely error on a folder is that it's not part of a WC\r
446                                 // In most circumstances, this will have been caught earlier,\r
447                                 // but in some situations, we'll get this error.\r
448                                 // If we allow ourselves to fall on through, then folders will be asked\r
449                                 // for their own status, and will set themselves as unversioned, for the \r
450                                 // benefit of future requests\r
451 //                              ATLTRACE("git_enum_files err: '%s'\n", pErr->message);\r
452 //                              svn_error_clear(pErr);\r
453                                 // No assert here! Since we _can_ get here, an assertion is not an option!\r
454                                 // Reasons to get here: \r
455                                 // - renaming a folder with many sub folders --> results in "not a working copy" if the revert\r
456                                 //   happens between our checks and the svn_client_status() call.\r
457                                 // - reverting a move/copy --> results in "not a working copy" (as above)\r
458                                 if (!m_directoryPath.HasAdminDir())\r
459                                 {\r
460                                         m_currentFullStatus = m_mostImportantFileStatus = git_wc_status_none;\r
461                                         return CStatusCacheEntry();\r
462                                 }\r
463                                 else\r
464                                 {\r
465                                         ATLTRACE("git_enum_files error, assume none status\n");\r
466                                         // Since we only assume a none status here due to svn_client_status()\r
467                                         // returning an error, make sure that this status times out soon.\r
468                                         CGitStatusCache::Instance().m_folderCrawler.BlockPath(m_directoryPath, 2000);\r
469                                         CGitStatusCache::Instance().AddFolderForCrawling(m_directoryPath);\r
470                                         return CStatusCacheEntry();\r
471                                 }\r
472                         }\r
473                 }\r
474                 else\r
475                 {\r
476                         ATLTRACE("Skipped git status for unversioned folder\n");\r
477                 }\r
478         }\r
479         // Now that we've refreshed our SVN status, we can see if it's \r
480         // changed the 'most important' status value for this directory.\r
481         // If it has, then we should tell our parent\r
482         UpdateCurrentStatus();\r
483 \r
484         if (path.IsDirectory())\r
485         {\r
486                 CCachedDirectory * dirEntry = CGitStatusCache::Instance().GetDirectoryCacheEntry(path);\r
487                 if ((dirEntry)&&(dirEntry->IsOwnStatusValid()))\r
488                 {\r
489                         CGitStatusCache::Instance().AddFolderForCrawling(path);\r
490                         return dirEntry->GetOwnStatus(bRecursive);\r
491                 }\r
492 \r
493                 // If the status *still* isn't valid here, it means that \r
494                 // the current directory is unversioned, and we shall need to ask its children for info about themselves\r
495                 if (dirEntry)\r
496                         return dirEntry->GetStatusForMember(path,bRecursive);\r
497                 CGitStatusCache::Instance().AddFolderForCrawling(path);\r
498                 return CStatusCacheEntry();\r
499         }\r
500         else\r
501         {\r
502                 CacheEntryMap::iterator itMap = m_entryCache.find(strCacheKey);\r
503                 if(itMap != m_entryCache.end())\r
504                 {\r
505                         return itMap->second;\r
506                 }\r
507         }\r
508 \r
509         AddEntry(path, NULL);\r
510         return CStatusCacheEntry();\r
511 }\r
512 \r
513 void \r
514 CCachedDirectory::AddEntry(const CTGitPath& path, const git_wc_status2_t* pGitStatus, DWORD validuntil /* = 0*/)\r
515 {\r
516         AutoLocker lock(m_critSec);\r
517         if(path.IsDirectory())\r
518         {\r
519                 CCachedDirectory * childDir = CGitStatusCache::Instance().GetDirectoryCacheEntry(path);\r
520                 if (childDir)\r
521                 {\r
522                         if ((childDir->GetCurrentFullStatus() != git_wc_status_missing)||(pGitStatus==NULL)||(pGitStatus->text_status != git_wc_status_unversioned))\r
523                                 childDir->m_ownStatus.SetStatus(pGitStatus);\r
524                         childDir->m_ownStatus.SetKind(git_node_dir);\r
525                 }\r
526         }\r
527         else\r
528         {\r
529                 CString cachekey = GetCacheKey(path);\r
530                 CacheEntryMap::iterator entry_it = m_entryCache.lower_bound(cachekey);\r
531                 if (entry_it != m_entryCache.end() && entry_it->first == cachekey)\r
532                 {\r
533                         if (pGitStatus)\r
534                         {\r
535                                 if (entry_it->second.GetEffectiveStatus() > git_wc_status_none &&\r
536                                         entry_it->second.GetEffectiveStatus() != GitStatus::GetMoreImportant(pGitStatus->prop_status, pGitStatus->text_status))\r
537                                 {\r
538                                         CGitStatusCache::Instance().UpdateShell(path);\r
539                                         ATLTRACE(_T("shell update for %s\n"), path.GetWinPath());\r
540                                 }\r
541                         }\r
542                 }\r
543                 else\r
544                 {\r
545                         entry_it = m_entryCache.insert(entry_it, std::make_pair(cachekey, CStatusCacheEntry()));\r
546                 }\r
547                 entry_it->second = CStatusCacheEntry(pGitStatus, path.GetLastWriteTime(), path.IsReadOnly(), validuntil);\r
548                 // TEMP(?): git status doesn't not have "entry" that contains node type, so manually set as file\r
549                 entry_it->second.SetKind(git_node_file);\r
550         }\r
551 }\r
552 \r
553 \r
554 CString \r
555 CCachedDirectory::GetCacheKey(const CTGitPath& path)\r
556 {\r
557         // All we put into the cache as a key is just the end portion of the pathname\r
558         // There's no point storing the path of the containing directory for every item\r
559         return path.GetWinPathString().Mid(m_directoryPath.GetWinPathString().GetLength());\r
560 }\r
561 \r
562 CString \r
563 CCachedDirectory::GetFullPathString(const CString& cacheKey)\r
564 {\r
565         return m_directoryPath.GetWinPathString() + _T("\\") + cacheKey;\r
566 }\r
567 \r
568 BOOL CCachedDirectory::GetStatusCallback(const struct wgFile_s *pFile, void *pUserData)\r
569 {\r
570         CCachedDirectory* pThis = (CCachedDirectory*)pUserData;\r
571 \r
572         const TCHAR *path = pFile->sFileName;\r
573 \r
574         if (path == NULL)\r
575                 return FALSE;\r
576 \r
577         git_wc_status2_t _status;\r
578         git_wc_status2_t *status = &_status;\r
579 \r
580         if ((pFile->nFlags & WGFF_Directory) && pFile->nStatus == WGFS_Unknown)\r
581                 status->prop_status = status->text_status = git_wc_status_incomplete;\r
582         else\r
583                 status->prop_status = status->text_status = GitStatusFromWingit(pFile->nStatus);\r
584 //if (pFile->nStatus > WGFS_Normal) {CStringA s; s.Format("==>%s %d\r\n",pFile->sFileName,pFile->nStatus); OutputDebugStringA(s);}\r
585         CTGitPath svnPath;\r
586 \r
587 //      if(status->entry)\r
588         {\r
589                 //if ((status->text_status != git_wc_status_none)&&(status->text_status != git_wc_status_missing))\r
590                         svnPath.SetFromGit(path, pFile->nFlags & WGFF_Directory);\r
591                 /*else\r
592                         svnPath.SetFromGit(path);*/\r
593 \r
594                 if (pFile->nFlags & WGFF_Directory)\r
595                 {\r
596                         if ( !svnPath.IsEquivalentToWithoutCase(pThis->m_directoryPath) )\r
597                         {\r
598                                 if (pThis->m_bRecursive)\r
599                                 {\r
600                                         // Add any versioned directory, which is not our 'self' entry, to the list for having its status updated\r
601 //OutputDebugStringA("AddFolderCrawl: ");OutputDebugStringW(svnPath.GetWinPathString());OutputDebugStringA("\r\n");\r
602                                         CGitStatusCache::Instance().AddFolderForCrawling(svnPath);\r
603                                 }\r
604 \r
605                                 // Make sure we know about this child directory\r
606                                 // This initial status value is likely to be overwritten from below at some point\r
607                                 git_wc_status_kind s = GitStatus::GetMoreImportant(status->text_status, status->prop_status);\r
608                                 CCachedDirectory * cdir = CGitStatusCache::Instance().GetDirectoryCacheEntryNoCreate(svnPath);\r
609                                 if (cdir)\r
610                                 {\r
611                                         // This child directory is already in our cache!\r
612                                         // So ask this dir about its recursive status\r
613                                         git_wc_status_kind st = GitStatus::GetMoreImportant(s, cdir->GetCurrentFullStatus());\r
614                                         AutoLocker lock(pThis->m_critSec);\r
615                                         pThis->m_childDirectories[svnPath] = st;\r
616                                 }\r
617                                 else\r
618                                 {\r
619                                         // the child directory is not in the cache. Create a new entry for it in the cache which is\r
620                                         // initially 'unversioned'. But we added that directory to the crawling list above, which\r
621                                         // means the cache will be updated soon.\r
622                                         CGitStatusCache::Instance().GetDirectoryCacheEntry(svnPath);\r
623                                         AutoLocker lock(pThis->m_critSec);\r
624                                         pThis->m_childDirectories[svnPath] = s;\r
625                                 }\r
626                         }\r
627                 }\r
628                 else\r
629                 {\r
630                         // Keep track of the most important status of all the files in this directory\r
631                         // Don't include subdirectories in this figure, because they need to provide their \r
632                         // own 'most important' value\r
633                         pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, status->text_status);\r
634                         pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, status->prop_status);\r
635                         if (((status->text_status == git_wc_status_unversioned)||(status->text_status == git_wc_status_none))\r
636                                 &&(CGitStatusCache::Instance().IsUnversionedAsModified()))\r
637                         {\r
638                                 // treat unversioned files as modified\r
639                                 if (pThis->m_mostImportantFileStatus != git_wc_status_added)\r
640                                         pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, git_wc_status_modified);\r
641                         }\r
642                 }\r
643         }\r
644 #if 0\r
645         else\r
646         {\r
647                 svnPath.SetFromGit(path);\r
648                 // Subversion returns no 'entry' field for versioned folders if they're\r
649                 // part of another working copy (nested layouts).\r
650                 // So we have to make sure that such an 'unversioned' folder really\r
651                 // is unversioned.\r
652                 if (((status->text_status == git_wc_status_unversioned)||(status->text_status == git_wc_status_missing))&&(!svnPath.IsEquivalentToWithoutCase(pThis->m_directoryPath))&&(svnPath.IsDirectory()))\r
653                 {\r
654                         if (svnPath.HasAdminDir())\r
655                         {\r
656                                 CGitStatusCache::Instance().AddFolderForCrawling(svnPath);\r
657                                 // Mark the directory as 'versioned' (status 'normal' for now).\r
658                                 // This initial value will be overwritten from below some time later\r
659                                 {\r
660                                         AutoLocker lock(pThis->m_critSec);\r
661                                         pThis->m_childDirectories[svnPath] = git_wc_status_normal;\r
662                                 }\r
663                                 // Make sure the entry is also in the cache\r
664                                 CGitStatusCache::Instance().GetDirectoryCacheEntry(svnPath);\r
665                                 // also mark the status in the status object as normal\r
666                                 status->text_status = git_wc_status_normal;\r
667                         }\r
668                 }\r
669                 else if (status->text_status == git_wc_status_external)\r
670                 {\r
671                         CGitStatusCache::Instance().AddFolderForCrawling(svnPath);\r
672                         // Mark the directory as 'versioned' (status 'normal' for now).\r
673                         // This initial value will be overwritten from below some time later\r
674                         {\r
675                                 AutoLocker lock(pThis->m_critSec);\r
676                                 pThis->m_childDirectories[svnPath] = git_wc_status_normal;\r
677                         }\r
678                         // we have added a directory to the child-directory list of this\r
679                         // directory. We now must make sure that this directory also has\r
680                         // an entry in the cache.\r
681                         CGitStatusCache::Instance().GetDirectoryCacheEntry(svnPath);\r
682                         // also mark the status in the status object as normal\r
683                         status->text_status = git_wc_status_normal;\r
684                 }\r
685                 else\r
686                 {\r
687                         if (svnPath.IsDirectory())\r
688                         {\r
689                                 AutoLocker lock(pThis->m_critSec);\r
690                                 pThis->m_childDirectories[svnPath] = GitStatus::GetMoreImportant(status->text_status, status->prop_status);\r
691                         }\r
692                         else if ((CGitStatusCache::Instance().IsUnversionedAsModified())&&(status->text_status != git_wc_status_missing))\r
693                         {\r
694                                 // make this unversioned item change the most important status of this\r
695                                 // folder to modified if it doesn't already have another status\r
696                                 if (pThis->m_mostImportantFileStatus != git_wc_status_added)\r
697                                         pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, git_wc_status_modified);\r
698                         }\r
699                 }\r
700         }\r
701 #endif\r
702 \r
703         pThis->AddEntry(svnPath, status);\r
704 \r
705         return FALSE;\r
706 }\r
707 \r
708 #if 0\r
709 git_error_t * CCachedDirectory::GetStatusCallback(void *baton, const char *path, git_wc_status2_t *status)\r
710 {\r
711         CCachedDirectory* pThis = (CCachedDirectory*)baton;\r
712 \r
713         if (path == NULL)\r
714                 return 0;\r
715                 \r
716         CTGitPath svnPath;\r
717 \r
718         if(status->entry)\r
719         {\r
720                 if ((status->text_status != git_wc_status_none)&&(status->text_status != git_wc_status_missing))\r
721                         svnPath.SetFromSVN(path, (status->entry->kind == svn_node_dir));\r
722                 else\r
723                         svnPath.SetFromSVN(path);\r
724 \r
725                 if(svnPath.IsDirectory())\r
726                 {\r
727                         if(!svnPath.IsEquivalentToWithoutCase(pThis->m_directoryPath))\r
728                         {\r
729                                 if (pThis->m_bRecursive)\r
730                                 {\r
731                                         // Add any versioned directory, which is not our 'self' entry, to the list for having its status updated\r
732                                         CGitStatusCache::Instance().AddFolderForCrawling(svnPath);\r
733                                 }\r
734 \r
735                                 // Make sure we know about this child directory\r
736                                 // This initial status value is likely to be overwritten from below at some point\r
737                                 git_wc_status_kind s = GitStatus::GetMoreImportant(status->text_status, status->prop_status);\r
738                                 CCachedDirectory * cdir = CGitStatusCache::Instance().GetDirectoryCacheEntryNoCreate(svnPath);\r
739                                 if (cdir)\r
740                                 {\r
741                                         // This child directory is already in our cache!\r
742                                         // So ask this dir about its recursive status\r
743                                         git_wc_status_kind st = GitStatus::GetMoreImportant(s, cdir->GetCurrentFullStatus());\r
744                                         AutoLocker lock(pThis->m_critSec);\r
745                                         pThis->m_childDirectories[svnPath] = st;\r
746                                 }\r
747                                 else\r
748                                 {\r
749                                         // the child directory is not in the cache. Create a new entry for it in the cache which is\r
750                                         // initially 'unversioned'. But we added that directory to the crawling list above, which\r
751                                         // means the cache will be updated soon.\r
752                                         CGitStatusCache::Instance().GetDirectoryCacheEntry(svnPath);\r
753                                         AutoLocker lock(pThis->m_critSec);\r
754                                         pThis->m_childDirectories[svnPath] = s;\r
755                                 }\r
756                         }\r
757                 }\r
758                 else\r
759                 {\r
760                         // Keep track of the most important status of all the files in this directory\r
761                         // Don't include subdirectories in this figure, because they need to provide their \r
762                         // own 'most important' value\r
763                         pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, status->text_status);\r
764                         pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, status->prop_status);\r
765                         if (((status->text_status == git_wc_status_unversioned)||(status->text_status == git_wc_status_none))\r
766                                 &&(CGitStatusCache::Instance().IsUnversionedAsModified()))\r
767                         {\r
768                                 // treat unversioned files as modified\r
769                                 if (pThis->m_mostImportantFileStatus != git_wc_status_added)\r
770                                         pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, git_wc_status_modified);\r
771                         }\r
772                 }\r
773         }\r
774         else\r
775         {\r
776                 svnPath.SetFromSVN(path);\r
777                 // Subversion returns no 'entry' field for versioned folders if they're\r
778                 // part of another working copy (nested layouts).\r
779                 // So we have to make sure that such an 'unversioned' folder really\r
780                 // is unversioned.\r
781                 if (((status->text_status == git_wc_status_unversioned)||(status->text_status == git_wc_status_missing))&&(!svnPath.IsEquivalentToWithoutCase(pThis->m_directoryPath))&&(svnPath.IsDirectory()))\r
782                 {\r
783                         if (svnPath.HasAdminDir())\r
784                         {\r
785                                 CGitStatusCache::Instance().AddFolderForCrawling(svnPath);\r
786                                 // Mark the directory as 'versioned' (status 'normal' for now).\r
787                                 // This initial value will be overwritten from below some time later\r
788                                 {\r
789                                         AutoLocker lock(pThis->m_critSec);\r
790                                         pThis->m_childDirectories[svnPath] = git_wc_status_normal;\r
791                                 }\r
792                                 // Make sure the entry is also in the cache\r
793                                 CGitStatusCache::Instance().GetDirectoryCacheEntry(svnPath);\r
794                                 // also mark the status in the status object as normal\r
795                                 status->text_status = git_wc_status_normal;\r
796                         }\r
797                 }\r
798                 else if (status->text_status == git_wc_status_external)\r
799                 {\r
800                         CGitStatusCache::Instance().AddFolderForCrawling(svnPath);\r
801                         // Mark the directory as 'versioned' (status 'normal' for now).\r
802                         // This initial value will be overwritten from below some time later\r
803                         {\r
804                                 AutoLocker lock(pThis->m_critSec);\r
805                                 pThis->m_childDirectories[svnPath] = git_wc_status_normal;\r
806                         }\r
807                         // we have added a directory to the child-directory list of this\r
808                         // directory. We now must make sure that this directory also has\r
809                         // an entry in the cache.\r
810                         CGitStatusCache::Instance().GetDirectoryCacheEntry(svnPath);\r
811                         // also mark the status in the status object as normal\r
812                         status->text_status = git_wc_status_normal;\r
813                 }\r
814                 else\r
815                 {\r
816                         if (svnPath.IsDirectory())\r
817                         {\r
818                                 AutoLocker lock(pThis->m_critSec);\r
819                                 pThis->m_childDirectories[svnPath] = GitStatus::GetMoreImportant(status->text_status, status->prop_status);\r
820                         }\r
821                         else if ((CGitStatusCache::Instance().IsUnversionedAsModified())&&(status->text_status != git_wc_status_missing))\r
822                         {\r
823                                 // make this unversioned item change the most important status of this\r
824                                 // folder to modified if it doesn't already have another status\r
825                                 if (pThis->m_mostImportantFileStatus != git_wc_status_added)\r
826                                         pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, git_wc_status_modified);\r
827                         }\r
828                 }\r
829         }\r
830 \r
831         pThis->AddEntry(svnPath, status);\r
832 \r
833         return 0;\r
834 }\r
835 #endif\r
836 \r
837 bool \r
838 CCachedDirectory::IsOwnStatusValid() const\r
839 {\r
840         return m_ownStatus.HasBeenSet() && \r
841                    !m_ownStatus.HasExpired(GetTickCount()) &&\r
842                    // 'external' isn't a valid status. That just\r
843                    // means the folder is not part of the current working\r
844                    // copy but it still has its own 'real' status\r
845                    m_ownStatus.GetEffectiveStatus()!=git_wc_status_external &&\r
846                    m_ownStatus.IsKindKnown();\r
847 }\r
848 \r
849 void CCachedDirectory::Invalidate()\r
850 {\r
851         m_ownStatus.Invalidate();\r
852 }\r
853 \r
854 git_wc_status_kind CCachedDirectory::CalculateRecursiveStatus()\r
855 {\r
856         // Combine our OWN folder status with the most important of our *FILES'* status.\r
857         git_wc_status_kind retVal = GitStatus::GetMoreImportant(m_mostImportantFileStatus, m_ownStatus.GetEffectiveStatus());\r
858 \r
859         // NOTE: TSVN marks dir as modified if it contains added/deleted/missing files, but we prefer the most important\r
860         //       status to propagate upward in its original state\r
861         /*if ((retVal != git_wc_status_modified)&&(retVal != m_ownStatus.GetEffectiveStatus()))\r
862         {\r
863                 if ((retVal == git_wc_status_added)||(retVal == git_wc_status_deleted)||(retVal == git_wc_status_missing))\r
864                         retVal = git_wc_status_modified;\r
865         }*/\r
866 \r
867         // Now combine all our child-directorie's status\r
868         \r
869         AutoLocker lock(m_critSec);\r
870         ChildDirStatus::const_iterator it;\r
871         for(it = m_childDirectories.begin(); it != m_childDirectories.end(); ++it)\r
872         {\r
873                 retVal = GitStatus::GetMoreImportant(retVal, it->second);\r
874                 /*if ((retVal != git_wc_status_modified)&&(retVal != m_ownStatus.GetEffectiveStatus()))\r
875                 {\r
876                         if ((retVal == git_wc_status_added)||(retVal == git_wc_status_deleted)||(retVal == git_wc_status_missing))\r
877                                 retVal = git_wc_status_modified;\r
878                 }*/\r
879         }\r
880         \r
881         return retVal;\r
882 }\r
883 \r
884 // Update our composite status and deal with things if it's changed\r
885 void CCachedDirectory::UpdateCurrentStatus()\r
886 {\r
887         git_wc_status_kind newStatus = CalculateRecursiveStatus();\r
888 \r
889         if ((newStatus != m_currentFullStatus)&&(m_ownStatus.IsVersioned()))\r
890         {\r
891                 if ((m_currentFullStatus != git_wc_status_none)&&(m_ownStatus.GetEffectiveStatus() != git_wc_status_missing))\r
892                 {\r
893                         // Our status has changed - tell the shell\r
894                         ATLTRACE(_T("Dir %s, status change from %d to %d, send shell notification\n"), m_directoryPath.GetWinPath(), m_currentFullStatus, newStatus);           \r
895                         CGitStatusCache::Instance().UpdateShell(m_directoryPath);\r
896                 }\r
897                 if (m_ownStatus.GetEffectiveStatus() != git_wc_status_missing)\r
898                         m_currentFullStatus = newStatus;\r
899                 else\r
900                         m_currentFullStatus = git_wc_status_missing;\r
901         }\r
902         // And tell our parent, if we've got one...\r
903         // we tell our parent *always* about our status, even if it hasn't\r
904         // changed. This is to make sure that the parent has really our current\r
905         // status - the parent can decide itself if our status has changed\r
906         // or not.\r
907         CTGitPath parentPath = m_directoryPath.GetContainingDirectory();\r
908         if(!parentPath.IsEmpty())\r
909         {\r
910                 // We have a parent\r
911                 CCachedDirectory * cachedDir = CGitStatusCache::Instance().GetDirectoryCacheEntry(parentPath);\r
912                 if (cachedDir)\r
913                         cachedDir->UpdateChildDirectoryStatus(m_directoryPath, m_currentFullStatus);\r
914         }\r
915 }\r
916 \r
917 \r
918 // Receive a notification from a child that its status has changed\r
919 void CCachedDirectory::UpdateChildDirectoryStatus(const CTGitPath& childDir, git_wc_status_kind childStatus)\r
920 {\r
921         git_wc_status_kind currentStatus = git_wc_status_none;\r
922         {\r
923                 AutoLocker lock(m_critSec);\r
924                 currentStatus = m_childDirectories[childDir];\r
925         }\r
926         if ((currentStatus != childStatus)||(!IsOwnStatusValid()))\r
927         {\r
928                 {\r
929                         AutoLocker lock(m_critSec);\r
930                         m_childDirectories[childDir] = childStatus;\r
931                 }\r
932                 UpdateCurrentStatus();\r
933         }\r
934 }\r
935 \r
936 CStatusCacheEntry CCachedDirectory::GetOwnStatus(bool bRecursive)\r
937 {\r
938         // Don't return recursive status if we're unversioned ourselves.\r
939         if(bRecursive && m_ownStatus.GetEffectiveStatus() > git_wc_status_unversioned)\r
940         {\r
941                 CStatusCacheEntry recursiveStatus(m_ownStatus);\r
942                 UpdateCurrentStatus();\r
943                 recursiveStatus.ForceStatus(m_currentFullStatus);\r
944                 return recursiveStatus;                         \r
945         }\r
946         else\r
947         {\r
948                 return m_ownStatus;\r
949         }\r
950 }\r
951 \r
952 void CCachedDirectory::RefreshStatus(bool bRecursive)\r
953 {\r
954         // Make sure that our own status is up-to-date\r
955         GetStatusForMember(m_directoryPath,bRecursive);\r
956 \r
957         AutoLocker lock(m_critSec);\r
958         // We also need to check if all our file members have the right date on them\r
959         CacheEntryMap::iterator itMembers;\r
960         std::set<CTGitPath> refreshedpaths;\r
961         DWORD now = GetTickCount();\r
962         if (m_entryCache.size() == 0)\r
963                 return;\r
964         for (itMembers = m_entryCache.begin(); itMembers != m_entryCache.end(); ++itMembers)\r
965         {\r
966                 if (itMembers->first)\r
967                 {\r
968                         CTGitPath filePath(m_directoryPath);\r
969                         filePath.AppendPathString(itMembers->first);\r
970                         std::set<CTGitPath>::iterator refr_it;\r
971                         if ((!filePath.IsEquivalentToWithoutCase(m_directoryPath))&&\r
972                                 (((refr_it = refreshedpaths.lower_bound(filePath)) == refreshedpaths.end()) || !filePath.IsEquivalentToWithoutCase(*refr_it)))\r
973                         {\r
974                                 if ((itMembers->second.HasExpired(now))||(!itMembers->second.DoesFileTimeMatch(filePath.GetLastWriteTime())))\r
975                                 {\r
976                                         lock.Unlock();\r
977                                         // We need to request this item as well\r
978                                         GetStatusForMember(filePath,bRecursive);\r
979                                         // GetStatusForMember now has recreated the m_entryCache map.\r
980                                         // So start the loop again, but add this path to the refreshed paths set\r
981                                         // to make sure we don't refresh this path again. This is to make sure\r
982                                         // that we don't end up in an endless loop.\r
983                                         lock.Lock();\r
984                                         refreshedpaths.insert(refr_it, filePath);\r
985                                         itMembers = m_entryCache.begin();\r
986                                         if (m_entryCache.size()==0)\r
987                                                 return;\r
988                                         continue;\r
989                                 }\r
990                                 else if ((bRecursive)&&(itMembers->second.IsDirectory()))\r
991                                 {\r
992                                         // crawl all sub folders too! Otherwise a change deep inside the\r
993                                         // tree which has changed won't get propagated up the tree.\r
994                                         CGitStatusCache::Instance().AddFolderForCrawling(filePath);\r
995                                 }\r
996                         }\r
997                 }\r
998         }\r
999 }\r
1000 \r
1001 void CCachedDirectory::RefreshMostImportant()\r
1002 {\r
1003         CacheEntryMap::iterator itMembers;\r
1004         git_wc_status_kind newStatus = m_ownStatus.GetEffectiveStatus();\r
1005         for (itMembers = m_entryCache.begin(); itMembers != m_entryCache.end(); ++itMembers)\r
1006         {\r
1007                 newStatus = GitStatus::GetMoreImportant(newStatus, itMembers->second.GetEffectiveStatus());\r
1008                 if (((itMembers->second.GetEffectiveStatus() == git_wc_status_unversioned)||(itMembers->second.GetEffectiveStatus() == git_wc_status_none))\r
1009                         &&(CGitStatusCache::Instance().IsUnversionedAsModified()))\r
1010                 {\r
1011                         // treat unversioned files as modified\r
1012                         if (newStatus != git_wc_status_added)\r
1013                                 newStatus = GitStatus::GetMoreImportant(newStatus, git_wc_status_modified);\r
1014                 }\r
1015         }\r
1016         if (newStatus != m_mostImportantFileStatus)\r
1017         {\r
1018                 ATLTRACE(_T("status change of path %s\n"), m_directoryPath.GetWinPath());\r
1019                 CGitStatusCache::Instance().UpdateShell(m_directoryPath);\r
1020         }\r
1021         m_mostImportantFileStatus = newStatus;\r
1022 }\r