1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008,2011 - TortoiseSVN
4 // Copyright (C) 2008-2012 - 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.
22 #include "GitFolderStatus.h"
23 #include "UnicodeUtils.h"
24 #include "..\TGitCache\CacheInterface.h"
28 extern ShellCache g_ShellCache
;
30 //extern CGitIndexFileMap g_IndexFileMap;
32 // get / auto-alloc a string "copy"
34 const char* StringPool::GetString (const char* value
)
36 // special case: NULL pointer
43 // do we already have a string with the desired value?
45 pool_type::const_iterator iter
= pool
.find (value
);
46 if (iter
!= pool
.end())
54 const char* newString
= _strdup (value
);
57 pool
.insert (newString
);
67 // clear internal pool
69 void StringPool::clear()
73 for (pool_type::iterator iter
= pool
.begin(), end
= pool
.end(); iter
!= end
; ++iter
)
78 // remove pointers from pool
83 GitFolderStatus::GitFolderStatus(void)
87 invalidstatus
.author
= emptyString
;
88 invalidstatus
.askedcounter
= -1;
89 invalidstatus
.status
= git_wc_status_none
;
90 invalidstatus
.url
= emptyString
;
91 // invalidstatus.rev = -1;
94 sCacheKey
.reserve(MAX_PATH
);
96 g_Git
.SetCurrentDir(_T(""));
97 m_hInvalidationEvent
= CreateEvent(NULL
, FALSE
, FALSE
, _T("TortoiseGitCacheInvalidationEvent"));
100 GitFolderStatus::~GitFolderStatus(void)
104 const FileStatusCacheEntry
* GitFolderStatus::BuildCache(const CTGitPath
& filepath
, const CString
& sProjectRoot
, BOOL bIsFolder
, BOOL bDirectFolder
)
106 //dont' build the cache if an instance of TortoiseProc is running
107 //since this could interfere with svn commands running (concurrent
108 //access of the .git directory).
109 if (g_ShellCache
.BlockStatus())
111 CAutoGeneralHandle TGitMutex
= ::CreateMutex(NULL
, FALSE
, _T("TortoiseGitProc.exe"));
112 if (TGitMutex
!= NULL
)
114 if (::GetLastError() == ERROR_ALREADY_EXISTS
)
116 return &invalidstatus
;
129 // NOTE: see not in GetFullStatus about project inside another project, we should only get here when
130 // that occurs, and this is not correctly handled yet
132 // initialize record members
134 dirstat
.status
= git_wc_status_none
;
135 dirstat
.author
= authors
.GetString(NULL
);
136 dirstat
.url
= urls
.GetString(NULL
);
137 dirstat
.askedcounter
= GITFOLDERSTATUS_CACHETIMES
;
140 // git_revnum_t youngest = GIT_INVALID_REVNUM;
141 // git_opt_revision_t rev;
142 // rev.kind = git_opt_revision_unspecified;
147 /* if (dirstatus->entry)
149 dirstat.author = authors.GetString (dirstatus->entry->cmt_author);
150 dirstat.url = authors.GetString (dirstatus->entry->url);
151 dirstat.rev = dirstatus->entry->cmt_rev;
153 dirstat
.status
= GitStatus::GetMoreImportant(dirstatus
->text_status
, dirstatus
->prop_status
);
155 m_cache
[filepath
.GetWinPath()] = dirstat
;
156 m_TimeStamp
= GetTickCount();
163 git_wc_status_kind status
;
168 git_depth_t depth
= git_depth_infinity
;
170 if (g_ShellCache
.GetCacheType() == ShellCache::dll
)
172 depth
= git_depth_empty
;
175 t1
= ::GetCurrentTime();
176 status
= m_GitStatus
.GetAllStatus(filepath
, depth
);
177 t2
= ::GetCurrentTime();
183 ATLTRACE2(_T("building cache for %s - time %d\n"), filepath
.GetWinPath(), t2
-t1
);
185 m_TimeStamp
= GetTickCount();
186 FileStatusCacheEntry
* ret
= NULL
;
188 if (_tcslen(filepath
.GetWinPath())==3)
189 ret
= &m_cache
[(LPCTSTR
)filepath
.GetWinPathString().Left(2)];
191 ret
= &m_cache
[filepath
.GetWinPath()];
193 ret
->status
= status
;
195 m_mostRecentPath
= filepath
;
196 m_mostRecentStatus
= ret
;
200 return &invalidstatus
;
203 DWORD
GitFolderStatus::GetTimeoutValue()
205 DWORD timeout
= GITFOLDERSTATUS_CACHETIMEOUT
;
206 DWORD factor
= m_cache
.size()/200;
209 return factor
*timeout
;
212 const FileStatusCacheEntry
* GitFolderStatus::GetFullStatus(const CTGitPath
& filepath
, BOOL bIsFolder
, BOOL bColumnProvider
)
214 const FileStatusCacheEntry
* ret
= NULL
;
216 CString sProjectRoot
;
217 BOOL bHasAdminDir
= g_ShellCache
.HasGITAdminDir(filepath
.GetWinPath(), bIsFolder
, &sProjectRoot
);
219 //no overlay for unversioned folders
220 if ((!bColumnProvider
)&&(!bHasAdminDir
))
221 return &invalidstatus
;
222 //for the SVNStatus column, we have to check the cache to see
223 //if it's not just unversioned but ignored
224 ret
= GetCachedItem(filepath
);
225 if ((ret
)&&(ret
->status
== git_wc_status_unversioned
)&&(bIsFolder
)&&(bHasAdminDir
))
227 // an 'unversioned' folder, but with an ADMIN dir --> nested layout!
228 // NOTE: this could be a sub-project in git, or just some standalone project inside of another, either way a TODO
229 ret
= BuildCache(filepath
, sProjectRoot
, bIsFolder
, TRUE
);
233 return &invalidstatus
;
238 //if it's not in the cache and has no admin dir, then we assume
239 //it's not ignored too
240 if ((bColumnProvider
)&&(!bHasAdminDir
))
241 return &invalidstatus
;
242 ret
= BuildCache(filepath
, sProjectRoot
, bIsFolder
);
246 return &invalidstatus
;
249 const FileStatusCacheEntry
* GitFolderStatus::GetCachedItem(const CTGitPath
& filepath
)
251 sCacheKey
.assign(filepath
.GetWinPath());
252 FileStatusMap::const_iterator iter
;
253 const FileStatusCacheEntry
*retVal
;
255 if(m_mostRecentPath
.IsEquivalentTo(CTGitPath(sCacheKey
.c_str())))
257 // We've hit the same result as we were asked for last time
258 ATLTRACE2(_T("fast cache hit for %s\n"), filepath
);
259 retVal
= m_mostRecentStatus
;
261 else if ((iter
= m_cache
.find(sCacheKey
)) != m_cache
.end())
263 ATLTRACE2(_T("cache found for %s\n"), filepath
);
264 retVal
= &iter
->second
;
265 m_mostRecentStatus
= retVal
;
266 m_mostRecentPath
= CTGitPath(sCacheKey
.c_str());
275 // We found something in a cache - check that the cache is not timed-out or force-invalidated
276 DWORD now
= GetTickCount();
278 if ((now
>= m_TimeStamp
)&&((now
- m_TimeStamp
) > GetTimeoutValue()))
280 // Cache is timed-out
281 ATLTRACE("Cache timed-out\n");
285 else if(WaitForSingleObject(m_hInvalidationEvent
, 0) == WAIT_OBJECT_0
)
287 // TortoiseProc has just done something which has invalidated the cache
288 ATLTRACE("Cache invalidated\n");
297 void GitFolderStatus::ClearCache()
300 m_mostRecentStatus
= NULL
;
301 m_mostRecentPath
.Reset();
302 // If we're about to rebuild the cache, there's no point hanging on to
303 // an event which tells us that it's invalid
304 ResetEvent(m_hInvalidationEvent
);