Fix typos
[TortoiseGit.git] / src / Git / GitFolderStatus.cpp
blob793c2f48176e9ecf931454009b416df8f9f78ecc
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008,2011, 2014 - TortoiseSVN
4 // Copyright (C) 2008-2017, 2019, 2023 - 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()
32 sCacheKey.reserve(MAX_PATH);
34 g_Git.SetCurrentDir(L"");
35 m_hInvalidationEvent = CreateEvent(nullptr, FALSE, FALSE, L"TortoiseGitCacheInvalidationEvent"); // no need to explicitly close m_hInvalidationEvent in ~GitFolderStatus as it is CAutoGeneralHandle
38 GitFolderStatus::~GitFolderStatus()
42 const FileStatusCacheEntry * GitFolderStatus::BuildCache(const CTGitPath& filepath, const CString& /*sProjectRoot*/, BOOL bIsFolder, BOOL bDirectFolder)
44 //dont' build the cache if an instance of TortoiseGitProc is running
45 //since this could interfere with svn commands running (concurrent
46 //access of the .git directory).
47 if (g_ShellCache.BlockStatus())
49 CAutoGeneralHandle TGitMutex = ::CreateMutex(nullptr, FALSE, L"TortoiseGitProc.exe");
50 if (TGitMutex != nullptr)
52 if (::GetLastError() == ERROR_ALREADY_EXISTS)
53 return &invalidstatus;
57 ClearCache();
59 if (bIsFolder)
61 if (bDirectFolder)
63 // NOTE: see not in GetFullStatus about project inside another project, we should only get here when
64 // that occurs, and this is not correctly handled yet
66 // initialize record members
67 dirstat.status = git_wc_status_none;
68 dirstat.askedcounter = GITFOLDERSTATUS_CACHETIMES;
69 dirstat.assumeValid = FALSE;
70 dirstat.skipWorktree = FALSE;
72 dirstatus = nullptr;
73 // rev.kind = git_opt_revision_unspecified;
76 if (dirstatus)
77 dirstat.status = dirstatus->status;
78 m_cache[filepath.GetWinPath()] = dirstat;
79 m_TimeStamp = GetTickCount64();
80 return &dirstat;
82 } // if (bIsFolder)
84 git_wc_status2_t status = { git_wc_status_none, false, false };
85 int t1,t2;
86 t2=t1=0;
87 try
89 t1 = ::GetCurrentTime();
90 if (m_GitStatus.GetAllStatus(filepath, g_ShellCache.GetCacheType() != ShellCache::dll, status))
91 status = { git_wc_status_none, false, false };
92 t2 = ::GetCurrentTime();
94 catch ( ... )
96 status = { git_wc_status_none, false, false };
99 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": building cache for %s - time %d\n", filepath.GetWinPath(), t2 - t1);
101 m_TimeStamp = GetTickCount64();
102 FileStatusCacheEntry* ret = nullptr;
104 if (wcslen(filepath.GetWinPath()) == 3)
105 ret = &m_cache[static_cast<LPCWSTR>(filepath.GetWinPathString().Left(2))];
106 else
107 ret = &m_cache[filepath.GetWinPath()];
109 if (ret)
111 ret->status = status.status;
112 ret->assumeValid = status.assumeValid;
113 ret->skipWorktree = status.skipWorktree;
116 m_mostRecentPath = filepath;
117 m_mostRecentStatus = ret;
119 if (ret)
120 return ret;
121 return &invalidstatus;
124 ULONGLONG GitFolderStatus::GetTimeoutValue()
126 ULONGLONG timeout = GITFOLDERSTATUS_CACHETIMEOUT;
127 ULONGLONG factor = static_cast<ULONGLONG>(m_cache.size()) / 200UL;
128 if (factor==0)
129 factor = 1;
130 return factor*timeout;
133 const FileStatusCacheEntry * GitFolderStatus::GetFullStatus(const CTGitPath& filepath, BOOL bIsFolder)
135 CString sProjectRoot;
136 BOOL bHasAdminDir = g_ShellCache.HasGITAdminDir(filepath.GetWinPath(), bIsFolder, &sProjectRoot);
138 //no overlay for unversioned folders
139 if (!bHasAdminDir)
140 return &invalidstatus;
141 //for the SVNStatus column, we have to check the cache to see
142 //if it's not just unversioned but ignored
143 const FileStatusCacheEntry* ret = GetCachedItem(filepath);
144 if ((ret)&&(ret->status == git_wc_status_unversioned)&&(bIsFolder)&&(bHasAdminDir))
146 // an 'unversioned' folder, but with an ADMIN dir --> nested layout!
147 // NOTE: this could be a sub-project in git, or just some standalone project inside of another, either way a TODO
148 ret = BuildCache(filepath, sProjectRoot, bIsFolder, TRUE);
149 if (ret)
150 return ret;
151 else
152 return &invalidstatus;
154 if (ret)
155 return ret;
157 //if it's not in the cache and has no admin dir, then we assume
158 //it's not ignored too
159 ret = BuildCache(filepath, sProjectRoot, bIsFolder);
160 if (ret)
161 return ret;
162 else
163 return &invalidstatus;
166 const FileStatusCacheEntry * GitFolderStatus::GetCachedItem(const CTGitPath& filepath)
168 sCacheKey.assign(filepath.GetWinPath());
169 FileStatusMap::const_iterator iter;
170 const FileStatusCacheEntry* retVal = nullptr;
172 if(m_mostRecentPath.IsEquivalentTo(CTGitPath(sCacheKey.c_str())))
174 // We've hit the same result as we were asked for last time
175 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": fast cache hit for %s\n", filepath.GetWinPath());
176 retVal = m_mostRecentStatus;
178 else if ((iter = m_cache.find(sCacheKey)) != m_cache.end())
180 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": cache found for %s\n", filepath.GetWinPath());
181 retVal = &iter->second;
182 m_mostRecentStatus = retVal;
183 m_mostRecentPath = CTGitPath(sCacheKey.c_str());
186 if (!retVal)
187 return nullptr;
189 // We found something in a cache - check that the cache is not timed-out or force-invalidated
190 ULONGLONG now = GetTickCount64();
192 if ((now >= m_TimeStamp) && ((now - m_TimeStamp) > GetTimeoutValue()))
194 // Cache is timed-out
195 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Cache timed-out\n");
196 ClearCache();
197 return nullptr;
199 else if (WaitForSingleObject(m_hInvalidationEvent, 0) == WAIT_OBJECT_0)
201 // TortoiseGitProc has just done something which has invalidated the cache
202 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Cache invalidated\n");
203 ClearCache();
204 return nullptr;
206 return retVal;
209 void GitFolderStatus::ClearCache()
211 m_cache.clear();
212 m_mostRecentStatus = nullptr;
213 m_mostRecentPath.Reset();
214 // If we're about to rebuild the cache, there's no point hanging on to
215 // an event which tells us that it's invalid
216 ResetEvent(m_hInvalidationEvent);