Fixed issue #2495: "Show Reflog" dialog shows empty action for "push" entries
[TortoiseGit.git] / src / Git / GitFolderStatus.cpp
blob9f4fe67846b4bccb93fbc46c6b4b5c3edba86e5e
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008,2011 - TortoiseSVN
4 // Copyright (C) 2008-2014 - 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 "ShellCache.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 GitFolderStatus::GitFolderStatus(void)
32 m_TimeStamp = 0;
33 invalidstatus.askedcounter = -1;
34 invalidstatus.status = git_wc_status_none;
35 invalidstatus.assumeValid = FALSE;
36 invalidstatus.skipWorktree = FALSE;
37 dirstat.askedcounter = -1;
38 dirstat.assumeValid = dirstat.skipWorktree = false;
39 dirstat.status = git_wc_status_none;
40 m_nCounter = 0;
41 dirstatus = NULL;
42 m_mostRecentStatus = NULL;
43 sCacheKey.reserve(MAX_PATH);
45 g_Git.SetCurrentDir(_T(""));
46 m_hInvalidationEvent = CreateEvent(NULL, FALSE, FALSE, _T("TortoiseGitCacheInvalidationEvent")); // no need to explicitely close m_hInvalidationEvent in ~GitFolderStatus as it is CAutoGeneralHandle
49 GitFolderStatus::~GitFolderStatus(void)
53 const FileStatusCacheEntry * GitFolderStatus::BuildCache(const CTGitPath& filepath, const CString& /*sProjectRoot*/, BOOL bIsFolder, BOOL bDirectFolder)
55 //dont' build the cache if an instance of TortoiseGitProc is running
56 //since this could interfere with svn commands running (concurrent
57 //access of the .git directory).
58 if (g_ShellCache.BlockStatus())
60 CAutoGeneralHandle TGitMutex = ::CreateMutex(NULL, FALSE, _T("TortoiseGitProc.exe"));
61 if (TGitMutex != NULL)
63 if (::GetLastError() == ERROR_ALREADY_EXISTS)
65 return &invalidstatus;
70 ClearCache();
72 if (bIsFolder)
74 if (bDirectFolder)
76 // NOTE: see not in GetFullStatus about project inside another project, we should only get here when
77 // that occurs, and this is not correctly handled yet
79 // initialize record members
80 dirstat.status = git_wc_status_none;
81 dirstat.askedcounter = GITFOLDERSTATUS_CACHETIMES;
82 dirstat.assumeValid = FALSE;
83 dirstat.skipWorktree = FALSE;
85 dirstatus = NULL;
86 // rev.kind = git_opt_revision_unspecified;
89 if (dirstatus)
91 /* if (dirstatus->entry)
93 dirstat.author = authors.GetString (dirstatus->entry->cmt_author);
94 dirstat.url = authors.GetString (dirstatus->entry->url);
95 dirstat.rev = dirstatus->entry->cmt_rev;
96 }*/
97 dirstat.status = GitStatus::GetMoreImportant(dirstatus->text_status, dirstatus->prop_status);
99 m_cache[filepath.GetWinPath()] = dirstat;
100 m_TimeStamp = GetTickCount();
101 return &dirstat;
103 } // if (bIsFolder)
105 m_nCounter = 0;
107 git_wc_status_kind status;
108 bool assumeValid = false;
109 bool skipWorktree = false;
110 int t1,t2;
111 t2=t1=0;
114 git_depth_t depth = git_depth_infinity;
116 if (g_ShellCache.GetCacheType() == ShellCache::dll)
118 depth = git_depth_empty;
121 t1 = ::GetCurrentTime();
122 status = m_GitStatus.GetAllStatus(filepath, depth, &assumeValid, &skipWorktree);
123 t2 = ::GetCurrentTime();
125 catch ( ... )
127 status = git_wc_status_unknown;
130 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": building cache for %s - time %d\n"), filepath.GetWinPath(), t2 - t1);
132 m_TimeStamp = GetTickCount();
133 FileStatusCacheEntry * ret = NULL;
135 if (_tcslen(filepath.GetWinPath())==3)
136 ret = &m_cache[(LPCTSTR)filepath.GetWinPathString().Left(2)];
137 else
138 ret = &m_cache[filepath.GetWinPath()];
140 if (ret)
142 ret->status = status;
143 ret->assumeValid = assumeValid;
144 ret->skipWorktree = skipWorktree;
147 m_mostRecentPath = filepath;
148 m_mostRecentStatus = ret;
150 if (ret)
151 return ret;
152 return &invalidstatus;
155 DWORD GitFolderStatus::GetTimeoutValue()
157 DWORD timeout = GITFOLDERSTATUS_CACHETIMEOUT;
158 DWORD factor = (DWORD)m_cache.size() / 200;
159 if (factor==0)
160 factor = 1;
161 return factor*timeout;
164 const FileStatusCacheEntry * GitFolderStatus::GetFullStatus(const CTGitPath& filepath, BOOL bIsFolder)
166 const FileStatusCacheEntry * ret = NULL;
168 CString sProjectRoot;
169 BOOL bHasAdminDir = g_ShellCache.HasGITAdminDir(filepath.GetWinPath(), bIsFolder, &sProjectRoot);
171 //no overlay for unversioned folders
172 if (!bHasAdminDir)
173 return &invalidstatus;
174 //for the SVNStatus column, we have to check the cache to see
175 //if it's not just unversioned but ignored
176 ret = GetCachedItem(filepath);
177 if ((ret)&&(ret->status == git_wc_status_unversioned)&&(bIsFolder)&&(bHasAdminDir))
179 // an 'unversioned' folder, but with an ADMIN dir --> nested layout!
180 // NOTE: this could be a sub-project in git, or just some standalone project inside of another, either way a TODO
181 ret = BuildCache(filepath, sProjectRoot, bIsFolder, TRUE);
182 if (ret)
183 return ret;
184 else
185 return &invalidstatus;
187 if (ret)
188 return ret;
190 //if it's not in the cache and has no admin dir, then we assume
191 //it's not ignored too
192 ret = BuildCache(filepath, sProjectRoot, bIsFolder);
193 if (ret)
194 return ret;
195 else
196 return &invalidstatus;
199 const FileStatusCacheEntry * GitFolderStatus::GetCachedItem(const CTGitPath& filepath)
201 sCacheKey.assign(filepath.GetWinPath());
202 FileStatusMap::const_iterator iter;
203 const FileStatusCacheEntry *retVal;
205 if(m_mostRecentPath.IsEquivalentTo(CTGitPath(sCacheKey.c_str())))
207 // We've hit the same result as we were asked for last time
208 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": fast cache hit for %s\n"), filepath);
209 retVal = m_mostRecentStatus;
211 else if ((iter = m_cache.find(sCacheKey)) != m_cache.end())
213 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": cache found for %s\n"), filepath);
214 retVal = &iter->second;
215 m_mostRecentStatus = retVal;
216 m_mostRecentPath = CTGitPath(sCacheKey.c_str());
218 else
220 retVal = NULL;
223 if(retVal != NULL)
225 // We found something in a cache - check that the cache is not timed-out or force-invalidated
226 DWORD now = GetTickCount();
228 if ((now >= m_TimeStamp)&&((now - m_TimeStamp) > GetTimeoutValue()))
230 // Cache is timed-out
231 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Cache timed-out\n");
232 ClearCache();
233 retVal = NULL;
235 else if(WaitForSingleObject(m_hInvalidationEvent, 0) == WAIT_OBJECT_0)
237 // TortoiseGitProc has just done something which has invalidated the cache
238 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Cache invalidated\n");
239 ClearCache();
240 retVal = NULL;
242 return retVal;
244 return NULL;
247 void GitFolderStatus::ClearCache()
249 m_cache.clear();
250 m_mostRecentStatus = NULL;
251 m_mostRecentPath.Reset();
252 // If we're about to rebuild the cache, there's no point hanging on to
253 // an event which tells us that it's invalid
254 ResetEvent(m_hInvalidationEvent);