Can also refresh Rebase dialog by double clicking to delete conflict file
[TortoiseGit.git] / src / Git / GitFolderStatus.cpp
blobab8b408e5a54cad173831cc95756503e32f087c2
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.
20 #include "stdafx.h"
21 #include "ShellExt.h"
22 #include "GitFolderStatus.h"
23 #include "UnicodeUtils.h"
24 #include "..\TGitCache\CacheInterface.h"
25 #include "Git.h"
26 #include "gitindex.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
38 if (value == NULL)
40 return emptyString;
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())
48 // yes -> return it
49 return *iter;
52 // no -> add one
54 const char* newString = _strdup (value);
55 if (newString)
57 pool.insert (newString);
59 else
60 return emptyString;
62 // .. and return it
64 return newString;
67 // clear internal pool
69 void StringPool::clear()
71 // delete all strings
73 for (pool_type::iterator iter = pool.begin(), end = pool.end(); iter != end; ++iter)
75 free((void*)*iter);
78 // remove pointers from pool
80 pool.clear();
83 GitFolderStatus::GitFolderStatus(void)
85 m_TimeStamp = 0;
86 emptyString[0] = 0;
87 invalidstatus.author = emptyString;
88 invalidstatus.askedcounter = -1;
89 invalidstatus.status = git_wc_status_none;
90 invalidstatus.url = emptyString;
91 invalidstatus.assumeValid = FALSE;
92 invalidstatus.skipWorktree = FALSE;
93 // invalidstatus.rev = -1;
94 m_nCounter = 0;
95 dirstatus = NULL;
96 sCacheKey.reserve(MAX_PATH);
98 g_Git.SetCurrentDir(_T(""));
99 m_hInvalidationEvent = CreateEvent(NULL, FALSE, FALSE, _T("TortoiseGitCacheInvalidationEvent"));
102 GitFolderStatus::~GitFolderStatus(void)
106 const FileStatusCacheEntry * GitFolderStatus::BuildCache(const CTGitPath& filepath, const CString& /*sProjectRoot*/, BOOL bIsFolder, BOOL bDirectFolder)
108 //dont' build the cache if an instance of TortoiseProc is running
109 //since this could interfere with svn commands running (concurrent
110 //access of the .git directory).
111 if (g_ShellCache.BlockStatus())
113 CAutoGeneralHandle TGitMutex = ::CreateMutex(NULL, FALSE, _T("TortoiseGitProc.exe"));
114 if (TGitMutex != NULL)
116 if (::GetLastError() == ERROR_ALREADY_EXISTS)
118 return &invalidstatus;
123 ClearCache();
124 authors.clear();
125 urls.clear();
127 if (bIsFolder)
129 if (bDirectFolder)
131 // NOTE: see not in GetFullStatus about project inside another project, we should only get here when
132 // that occurs, and this is not correctly handled yet
134 // initialize record members
135 // dirstat.rev = -1;
136 dirstat.status = git_wc_status_none;
137 dirstat.author = authors.GetString(NULL);
138 dirstat.url = urls.GetString(NULL);
139 dirstat.askedcounter = GITFOLDERSTATUS_CACHETIMES;
140 dirstat.assumeValid = FALSE;
141 dirstat.skipWorktree = FALSE;
143 dirstatus = NULL;
144 // git_revnum_t youngest = GIT_INVALID_REVNUM;
145 // git_opt_revision_t rev;
146 // rev.kind = git_opt_revision_unspecified;
149 if (dirstatus)
151 /* if (dirstatus->entry)
153 dirstat.author = authors.GetString (dirstatus->entry->cmt_author);
154 dirstat.url = authors.GetString (dirstatus->entry->url);
155 dirstat.rev = dirstatus->entry->cmt_rev;
157 dirstat.status = GitStatus::GetMoreImportant(dirstatus->text_status, dirstatus->prop_status);
159 m_cache[filepath.GetWinPath()] = dirstat;
160 m_TimeStamp = GetTickCount();
161 return &dirstat;
163 } // if (bIsFolder)
165 m_nCounter = 0;
167 git_wc_status_kind status;
168 bool assumeValid = false;
169 bool skipWorktree = false;
170 int t1,t2;
171 t2=t1=0;
174 git_depth_t depth = git_depth_infinity;
176 if (g_ShellCache.GetCacheType() == ShellCache::dll)
178 depth = git_depth_empty;
181 t1 = ::GetCurrentTime();
182 status = m_GitStatus.GetAllStatus(filepath, depth, &assumeValid, &skipWorktree);
183 t2 = ::GetCurrentTime();
185 catch ( ... )
187 status = git_wc_status_unknown;
190 ATLTRACE2(_T("building cache for %s - time %d\n"), filepath.GetWinPath(), t2 -t1);
192 m_TimeStamp = GetTickCount();
193 FileStatusCacheEntry * ret = NULL;
195 if (_tcslen(filepath.GetWinPath())==3)
196 ret = &m_cache[(LPCTSTR)filepath.GetWinPathString().Left(2)];
197 else
198 ret = &m_cache[filepath.GetWinPath()];
200 ret->status = status;
201 ret->assumeValid = assumeValid;
202 ret->skipWorktree = skipWorktree;
204 m_mostRecentPath = filepath;
205 m_mostRecentStatus = ret;
207 if (ret)
208 return ret;
209 return &invalidstatus;
212 DWORD GitFolderStatus::GetTimeoutValue()
214 DWORD timeout = GITFOLDERSTATUS_CACHETIMEOUT;
215 DWORD factor = (DWORD)m_cache.size() / 200;
216 if (factor==0)
217 factor = 1;
218 return factor*timeout;
221 const FileStatusCacheEntry * GitFolderStatus::GetFullStatus(const CTGitPath& filepath, BOOL bIsFolder, BOOL bColumnProvider)
223 const FileStatusCacheEntry * ret = NULL;
225 CString sProjectRoot;
226 BOOL bHasAdminDir = g_ShellCache.HasGITAdminDir(filepath.GetWinPath(), bIsFolder, &sProjectRoot);
228 //no overlay for unversioned folders
229 if ((!bColumnProvider)&&(!bHasAdminDir))
230 return &invalidstatus;
231 //for the SVNStatus column, we have to check the cache to see
232 //if it's not just unversioned but ignored
233 ret = GetCachedItem(filepath);
234 if ((ret)&&(ret->status == git_wc_status_unversioned)&&(bIsFolder)&&(bHasAdminDir))
236 // an 'unversioned' folder, but with an ADMIN dir --> nested layout!
237 // NOTE: this could be a sub-project in git, or just some standalone project inside of another, either way a TODO
238 ret = BuildCache(filepath, sProjectRoot, bIsFolder, TRUE);
239 if (ret)
240 return ret;
241 else
242 return &invalidstatus;
244 if (ret)
245 return ret;
247 //if it's not in the cache and has no admin dir, then we assume
248 //it's not ignored too
249 if ((bColumnProvider)&&(!bHasAdminDir))
250 return &invalidstatus;
251 ret = BuildCache(filepath, sProjectRoot, bIsFolder);
252 if (ret)
253 return ret;
254 else
255 return &invalidstatus;
258 const FileStatusCacheEntry * GitFolderStatus::GetCachedItem(const CTGitPath& filepath)
260 sCacheKey.assign(filepath.GetWinPath());
261 FileStatusMap::const_iterator iter;
262 const FileStatusCacheEntry *retVal;
264 if(m_mostRecentPath.IsEquivalentTo(CTGitPath(sCacheKey.c_str())))
266 // We've hit the same result as we were asked for last time
267 ATLTRACE2(_T("fast cache hit for %s\n"), filepath);
268 retVal = m_mostRecentStatus;
270 else if ((iter = m_cache.find(sCacheKey)) != m_cache.end())
272 ATLTRACE2(_T("cache found for %s\n"), filepath);
273 retVal = &iter->second;
274 m_mostRecentStatus = retVal;
275 m_mostRecentPath = CTGitPath(sCacheKey.c_str());
277 else
279 retVal = NULL;
282 if(retVal != NULL)
284 // We found something in a cache - check that the cache is not timed-out or force-invalidated
285 DWORD now = GetTickCount();
287 if ((now >= m_TimeStamp)&&((now - m_TimeStamp) > GetTimeoutValue()))
289 // Cache is timed-out
290 ATLTRACE("Cache timed-out\n");
291 ClearCache();
292 retVal = NULL;
294 else if(WaitForSingleObject(m_hInvalidationEvent, 0) == WAIT_OBJECT_0)
296 // TortoiseProc has just done something which has invalidated the cache
297 ATLTRACE("Cache invalidated\n");
298 ClearCache();
299 retVal = NULL;
301 return retVal;
303 return NULL;
306 void GitFolderStatus::ClearCache()
308 m_cache.clear();
309 m_mostRecentStatus = NULL;
310 m_mostRecentPath.Reset();
311 // If we're about to rebuild the cache, there's no point hanging on to
312 // an event which tells us that it's invalid
313 ResetEvent(m_hInvalidationEvent);