Pass const reference to path
[TortoiseGit.git] / src / Git / gitindex.h
blobbbd71ce0e8eebb3d51a27f17b68a171adfe8a981
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2017 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "GitHash.h"
21 #include "gitdll.h"
22 #include "GitStatus.h"
23 #include "UnicodeUtils.h"
24 #include "ReaderWriterLock.h"
25 #include "GitAdminDir.h"
26 #include "StringUtils.h"
27 #include "PathUtils.h"
29 class CGitIndex
31 public:
32 CString m_FileName;
33 __time64_t m_ModifyTime;
34 uint16_t m_Flags;
35 uint16_t m_FlagsExtended;
36 CGitHash m_IndexHash;
37 __int64 m_Size;
39 int Print();
42 class CGitIndexList:public std::vector<CGitIndex>
44 public:
45 __time64_t m_LastModifyTime;
46 BOOL m_bHasConflicts;
48 CGitIndexList();
49 ~CGitIndexList();
51 int ReadIndex(CString dotgitdir);
52 int GetFileStatus(const CString& gitdir, const CString& path, git_wc_status2_t& status, CGitHash* pHash = nullptr);
53 int GetFileStatus(CAutoRepository& repository, const CString& gitdir, CGitIndex& entry, git_wc_status2_t& status, __int64 time, __int64 filesize);
54 #ifdef GTEST_INCLUDE_GTEST_GTEST_H_
55 FRIEND_TEST(GitIndexCBasicGitWithTestRepoFixture, GetFileStatus);
56 #endif
57 protected:
58 __int64 m_iMaxCheckSize;
59 CAutoConfig config;
60 int GetFileStatus(const CString& gitdir, const CString& path, git_wc_status2_t& status, __int64 time, __int64 filesize, CGitHash* pHash = nullptr);
63 typedef std::shared_ptr<CGitIndexList> SHARED_INDEX_PTR;
64 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
66 class CGitIndexFileMap:public std::map<CString, SHARED_INDEX_PTR>
68 public:
69 CComCriticalSection m_critIndexSec;
71 CGitIndexFileMap() { m_critIndexSec.Init(); }
72 ~CGitIndexFileMap() { m_critIndexSec.Term(); }
74 SHARED_INDEX_PTR SafeGet(CString thePath)
76 thePath.MakeLower();
77 CAutoLocker lock(m_critIndexSec);
78 auto lookup = find(thePath);
79 if (lookup == cend())
80 return SHARED_INDEX_PTR();
81 return lookup->second;
84 void SafeSet(CString thePath, SHARED_INDEX_PTR ptr)
86 thePath.MakeLower();
87 CAutoLocker lock(m_critIndexSec);
88 (*this)[thePath] = ptr;
91 bool SafeClear(CString thePath)
93 thePath.MakeLower();
94 CAutoLocker lock(m_critIndexSec);
95 auto lookup = find(thePath);
96 if (lookup == cend())
97 return false;
98 erase(lookup);
99 return true;
102 bool SafeClearRecursively(CString thePath)
104 thePath.MakeLower();
105 CAutoLocker lock(m_critIndexSec);
106 std::vector<CString> toRemove;
107 for (auto it = this->cbegin(); it != this->cend(); ++it)
109 if (CStringUtils::StartsWith((*it).first, thePath))
110 toRemove.push_back((*it).first);
112 for (auto it = toRemove.cbegin(); it != toRemove.cend(); ++it)
113 this->erase(*it);
114 return !toRemove.empty();
117 bool HasIndexChangedOnDisk(const CString& gitdir);
118 int LoadIndex(const CString &gitdir);
120 void CheckAndUpdate(const CString& gitdir)
122 if (HasIndexChangedOnDisk(gitdir))
123 LoadIndex(gitdir);
127 class CGitTreeItem
129 public:
130 CString m_FileName;
131 CGitHash m_Hash;
132 int m_Flags;
135 /* After object create, never change field agains
136 * that needn't lock to get field
138 class CGitHeadFileList:public std::vector<CGitTreeItem>
140 private:
141 int GetPackRef(const CString &gitdir);
143 __time64_t m_LastModifyTimeHead;
144 __time64_t m_LastModifyTimeRef;
145 __time64_t m_LastModifyTimePackRef;
147 CString m_HeadRefFile;
148 CGitHash m_Head;
149 CString m_HeadFile;
150 CString m_Gitdir;
151 CString m_PackRefFile;
153 CGitHash m_TreeHash; /* buffered tree hash value */
155 std::map<CString,CGitHash> m_PackRefMap;
157 public:
158 CGitHeadFileList()
159 : m_LastModifyTimeHead(0)
160 , m_LastModifyTimeRef(0)
161 , m_LastModifyTimePackRef(0)
165 int ReadTree();
166 int ReadHeadHash(const CString& gitdir);
167 bool CheckHeadUpdate();
168 bool HeadHashEqualsTreeHash();
169 static int CallBack(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);
172 typedef std::shared_ptr<CGitHeadFileList> SHARED_TREE_PTR;
173 class CGitHeadFileMap:public std::map<CString,SHARED_TREE_PTR>
175 public:
177 CComCriticalSection m_critTreeSec;
179 CGitHeadFileMap() { m_critTreeSec.Init(); }
180 ~CGitHeadFileMap() { m_critTreeSec.Term(); }
182 SHARED_TREE_PTR SafeGet(CString thePath)
184 thePath.MakeLower();
185 CAutoLocker lock(m_critTreeSec);
186 auto lookup = find(thePath);
187 if (lookup == cend())
188 return SHARED_TREE_PTR();
189 return lookup->second;
192 void SafeSet(CString thePath, SHARED_TREE_PTR ptr)
194 thePath.MakeLower();
195 CAutoLocker lock(m_critTreeSec);
196 (*this)[thePath] = ptr;
199 bool SafeClear(CString thePath)
201 thePath.MakeLower();
202 CAutoLocker lock(m_critTreeSec);
203 auto lookup = find(thePath);
204 if (lookup == cend())
205 return false;
206 erase(lookup);
207 return true;
210 bool SafeClearRecursively(CString thePath)
212 thePath.MakeLower();
213 CAutoLocker lock(m_critTreeSec);
214 std::vector<CString> toRemove;
215 for (auto it = this->cbegin(); it != this->cend(); ++it)
217 if (CStringUtils::StartsWith((*it).first, thePath))
218 toRemove.push_back((*it).first);
220 for (auto it = toRemove.cbegin(); it != toRemove.cend(); ++it)
221 this->erase(*it);
222 return !toRemove.empty();
224 void CheckHeadAndUpdate(const CString& gitdir);
227 class CGitFileName
229 public:
230 CGitFileName() {}
231 CGitFileName(LPCTSTR filename, __int64 size, __int64 lastmodified)
232 : m_FileName(filename)
233 , m_Size(size)
234 , m_LastModified(lastmodified)
237 CString m_FileName;
238 __int64 m_Size;
239 __int64 m_LastModified;
242 static bool SortCGitFileName(const CGitFileName& item1, const CGitFileName& item2)
244 return item1.m_FileName.Compare(item2.m_FileName) < 0;
247 class CGitIgnoreItem
249 public:
250 CGitIgnoreItem()
251 : m_LastModifyTime(0)
252 , m_pExcludeList(nullptr)
253 , m_buffer(nullptr)
254 , m_iIgnoreCase(nullptr)
258 ~CGitIgnoreItem()
260 if(m_pExcludeList)
261 git_free_exclude_list(m_pExcludeList);
262 free(m_buffer);
265 __time64_t m_LastModifyTime;
266 CStringA m_BaseDir;
267 BYTE *m_buffer;
268 EXCLUDE_LIST m_pExcludeList;
269 int* m_iIgnoreCase;
271 int FetchIgnoreList(const CString& projectroot, const CString& file, bool isGlobal, int* ignoreCase);
274 * patha: the filename to be checked whether is is ignored or not
275 * base: must be a pointer to the beginning of the base filename WITHIN patha
276 * type: DT_DIR or DT_REG
278 int IsPathIgnored(const CStringA& patha, const char* base, int& type);
279 #ifdef GTEST_INCLUDE_GTEST_GTEST_H_
280 int IsPathIgnored(const CStringA& patha, int& type);
281 #endif
284 class CGitIgnoreList
286 private:
287 bool CheckFileChanged(const CString &path);
288 int FetchIgnoreFile(const CString &gitdir, const CString &gitignore, bool isGlobal);
290 int CheckIgnore(const CString &path, const CString &root, bool isDir);
291 int CheckFileAgainstIgnoreList(const CString &ignorefile, const CStringA &patha, const char * base, int &type);
293 // core.excludesfile stuff
294 std::map<CString, CString> m_CoreExcludesfiles;
295 std::map<CString, int> m_IgnoreCase;
296 CString m_sGitSystemConfigPath;
297 CString m_sGitProgramDataConfigPath;
298 ULONGLONG m_dGitSystemConfigPathLastChecked;
299 CReaderWriterLock m_coreExcludefilesSharedMutex;
300 // checks if the msysgit path has changed and return true/false
301 // if the path changed, the cache is update
302 // force is only ised in constructor
303 bool CheckAndUpdateGitSystemConfigPath(bool force = true);
304 bool CheckAndUpdateCoreExcludefile(const CString &adminDir);
305 const CString GetWindowsHome();
307 public:
308 CReaderWriterLock m_SharedMutex;
310 CGitIgnoreList(){ CheckAndUpdateGitSystemConfigPath(true); }
312 std::map<CString, CGitIgnoreItem> m_Map;
314 bool CheckAndUpdateIgnoreFiles(const CString& gitdir, const CString& path, bool isDir);
315 bool IsIgnore(CString path, const CString& root, bool isDir);
318 static const size_t NPOS = (size_t)-1; // bad/missing length/position
319 static_assert(MAXSIZE_T == NPOS, "NPOS must equal MAXSIZE_T");
320 #pragma warning(push)
321 #pragma warning(disable: 4310)
322 static_assert(-1 == (int)NPOS, "NPOS must equal -1");
323 #pragma warning(pop)
325 template<class T>
326 int GetRangeInSortVector(const T& vector, LPCTSTR pstr, size_t len, size_t* start, size_t* end, size_t pos)
328 if (pos == NPOS)
329 return -1;
330 if (!start || !end)
331 return -1;
333 *start = *end = NPOS;
335 if (vector.empty())
336 return -1;
338 if (pos >= vector.size())
339 return -1;
341 if (wcsncmp(vector[pos].m_FileName, pstr, len) != 0)
342 return -1;
344 *start = 0;
345 *end = vector.size() - 1;
347 // shortcut, if all entries are going match
348 if (!len)
349 return 0;
351 for (size_t i = pos; i < vector.size(); ++i)
353 if (wcsncmp(vector[i].m_FileName, pstr, len) != 0)
354 break;
356 *end = i;
358 for (size_t i = pos + 1; i-- > 0;)
360 if (wcsncmp(vector[i].m_FileName, pstr, len) != 0)
361 break;
363 *start = i;
366 return 0;
369 template<class T>
370 size_t SearchInSortVector(const T& vector, LPCTSTR pstr, int len)
372 size_t end = vector.size() - 1;
373 size_t start = 0;
374 size_t mid = (start + end) / 2;
376 if (vector.empty())
377 return NPOS;
379 while(!( start == end && start==mid))
381 int cmp;
382 if(len < 0)
383 cmp = wcscmp(vector[mid].m_FileName, pstr);
384 else
385 cmp = wcsncmp(vector[mid].m_FileName, pstr, len);
387 if (cmp == 0)
388 return mid;
389 else if (cmp < 0)
390 start = mid + 1;
391 else // (cmp > 0)
392 end = mid;
394 mid=(start +end ) /2;
397 if(len <0)
399 if (wcscmp(vector[mid].m_FileName, pstr) == 0)
400 return mid;
402 else
404 if (wcsncmp(vector[mid].m_FileName, pstr, len) == 0)
405 return mid;
407 return NPOS;
410 class CGitAdminDirMap:public std::map<CString, CString>
412 public:
413 CComCriticalSection m_critIndexSec;
414 std::map<CString, CString> m_reverseLookup;
415 std::map<CString, CString> m_WorktreeAdminDirLookup;
417 CGitAdminDirMap() { m_critIndexSec.Init(); }
418 ~CGitAdminDirMap() { m_critIndexSec.Term(); }
420 CString GetAdminDir(const CString &path)
422 CString thePath(CPathUtils::NormalizePath(path));
423 CAutoLocker lock(m_critIndexSec);
424 auto lookup = find(thePath);
425 if (lookup == cend())
427 CString adminDir;
428 GitAdminDir::GetAdminDirPath(thePath, adminDir);
429 if (PathIsDirectory(adminDir))
431 adminDir = CPathUtils::BuildPathWithPathDelimiter(CPathUtils::NormalizePath(adminDir));
432 (*this)[thePath] = adminDir;
433 m_reverseLookup[adminDir] = thePath;
434 return (*this)[thePath];
436 return thePath + L".git\\"; // in case of an error stick to old behavior
439 return lookup->second;
442 void ResetAdminDirCache(const CString& path)
444 CString thePath(path);
445 thePath.MakeLower();
446 CAutoLocker lock(m_critIndexSec);
447 auto lookup = find(thePath);
448 if (lookup == cend())
449 return;
450 m_reverseLookup.erase(lookup->second.MakeLower().TrimRight(L'\\'));
451 erase(lookup);
454 CString GetAdminDirConcat(const CString& path, const CString& subpath)
456 CString result(GetAdminDir(path));
457 result += subpath;
458 return result;
461 CString GetWorktreeAdminDir(const CString& path)
463 CString thePath(CPathUtils::NormalizePath(path));
464 CAutoLocker lock(m_critIndexSec);
465 auto lookup = m_WorktreeAdminDirLookup.find(thePath);
466 if (lookup == m_WorktreeAdminDirLookup.cend())
468 CString wtadmindir;
469 GitAdminDir::GetWorktreeAdminDirPath(thePath, wtadmindir);
470 if (PathIsDirectory(wtadmindir))
472 wtadmindir = CPathUtils::BuildPathWithPathDelimiter(CPathUtils::NormalizePath(wtadmindir));
473 m_WorktreeAdminDirLookup[thePath] = wtadmindir;
474 m_reverseLookup[wtadmindir] = thePath;
475 return m_WorktreeAdminDirLookup[thePath];
477 ATLASSERT(false);
478 return thePath + L".git\\"; // we should never get here
480 return lookup->second;
483 CString GetWorktreeAdminDirConcat(const CString& path, const CString& subpath)
485 CString result(GetWorktreeAdminDir(path));
486 result += subpath;
487 return result;
490 CString GetWorkingCopy(const CString &gitDir)
492 CString path(CPathUtils::BuildPathWithPathDelimiter(CPathUtils::NormalizePath(gitDir)));
493 CAutoLocker lock(m_critIndexSec);
494 auto lookup = m_reverseLookup.find(path);
495 if (lookup == m_reverseLookup.cend())
496 return gitDir;
497 return lookup->second;