Migrate HintListCtrl to HintCtrl template
[TortoiseGit.git] / src / Git / gitindex.h
blob2cbd76e7b00802b462fac1b018445cd74c08fc52
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - 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"
27 class CGitIndex
29 public:
30 CString m_FileName;
31 __time64_t m_ModifyTime;
32 uint16_t m_Flags;
33 uint16_t m_FlagsExtended;
34 CGitHash m_IndexHash;
35 __int64 m_Size;
37 int Print();
40 class CGitIndexList:public std::vector<CGitIndex>
42 protected:
44 public:
45 __time64_t m_LastModifyTime;
46 BOOL m_bHasConflicts;
48 CGitIndexList();
49 ~CGitIndexList();
51 int ReadIndex(CString file);
52 int GetStatus(const CString& gitdir, CString path, git_wc_status_kind* status, BOOL IsFull = FALSE, BOOL IsRecursive = FALSE, FILL_STATUS_CALLBACK callback = nullptr, void* pData = nullptr, CGitHash* pHash = nullptr, bool* assumeValid = nullptr, bool* skipWorktree = nullptr);
53 #ifdef GTEST_INCLUDE_GTEST_GTEST_H_
54 FRIEND_TEST(GitIndexCBasicGitWithTestRepoFixture, GetFileStatus);
55 #endif
56 protected:
57 __int64 m_iMaxCheckSize;
58 CComCriticalSection m_critRepoSec;
59 CAutoRepository repository;
60 int GetFileStatus(const CString &gitdir, const CString &path, git_wc_status_kind * status, __int64 time, __int64 filesize, FILL_STATUS_CALLBACK callback = nullptr, void *pData = nullptr, CGitHash *pHash = nullptr, bool * assumeValid = nullptr, bool * skipWorktree = nullptr);
63 typedef std::tr1::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 ((*it).first.Find(thePath) == 0)
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 int Check(const CString &gitdir, bool *isChanged);
118 int LoadIndex(const CString &gitdir);
120 bool CheckAndUpdate(const CString &gitdir,bool isLoadUpdatedIndex)
122 bool isChanged=false;
123 if(isLoadUpdatedIndex && Check(gitdir,&isChanged))
124 return false;
126 if(isChanged && isLoadUpdatedIndex)
128 LoadIndex(gitdir);
129 return true;
132 return false;
134 int GetFileStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,
135 BOOL IsFull=false, BOOL IsRecursive=false,
136 FILL_STATUS_CALLBACK callback = nullptr,
137 void* pData = nullptr, CGitHash* pHash = nullptr,
138 bool isLoadUpdatedIndex = true, bool* assumeValid = nullptr, bool* skipWorktree = nullptr);
140 int IsUnderVersionControl(const CString &gitdir,
141 CString path,
142 bool isDir,
143 bool *isVersion,
144 bool isLoadUpdateIndex=true);
148 class CGitTreeItem
150 public:
151 CString m_FileName;
152 CGitHash m_Hash;
153 int m_Flags;
156 /* After object create, never change field agains
157 * that needn't lock to get field
159 class CGitHeadFileList:public std::vector<CGitTreeItem>
161 private:
162 int GetPackRef(const CString &gitdir);
163 CReaderWriterLock m_SharedMutex;
165 __time64_t m_LastModifyTimeHead;
166 __time64_t m_LastModifyTimeRef;
167 __time64_t m_LastModifyTimePackRef;
169 CString m_HeadRefFile;
170 CGitHash m_Head;
171 CString m_HeadFile;
172 CString m_Gitdir;
173 CString m_PackRefFile;
175 CGitHash m_TreeHash; /* buffered tree hash value */
177 std::map<CString,CGitHash> m_PackRefMap;
179 public:
180 CGitHeadFileList()
182 m_LastModifyTimeHead=0;
183 m_LastModifyTimeRef=0;
184 m_LastModifyTimePackRef = 0;
187 int ReadTree();
188 int ReadHeadHash(const CString& gitdir);
189 bool CheckHeadUpdate();
190 bool HeadHashEqualsTreeHash();
191 bool HeadFileIsEmpty();
192 bool HeadIsEmpty();
193 static int CallBack(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);
196 typedef std::tr1::shared_ptr<CGitHeadFileList> SHARED_TREE_PTR;
197 class CGitHeadFileMap:public std::map<CString,SHARED_TREE_PTR>
199 public:
201 CComCriticalSection m_critTreeSec;
203 CGitHeadFileMap() { m_critTreeSec.Init(); }
204 ~CGitHeadFileMap() { m_critTreeSec.Term(); }
206 SHARED_TREE_PTR SafeGet(CString thePath, bool allowEmpty = false)
208 thePath.MakeLower();
209 CAutoLocker lock(m_critTreeSec);
210 auto lookup = find(thePath);
211 if (lookup == cend())
213 if (allowEmpty)
214 return SHARED_TREE_PTR();
215 return SHARED_TREE_PTR(new CGitHeadFileList);
217 return lookup->second;
220 void SafeSet(CString thePath, SHARED_TREE_PTR ptr)
222 thePath.MakeLower();
223 CAutoLocker lock(m_critTreeSec);
224 (*this)[thePath] = ptr;
227 bool SafeClear(CString thePath)
229 thePath.MakeLower();
230 CAutoLocker lock(m_critTreeSec);
231 auto lookup = find(thePath);
232 if (lookup == cend())
233 return false;
234 erase(lookup);
235 return true;
238 bool SafeClearRecursively(CString thePath)
240 thePath.MakeLower();
241 CAutoLocker lock(m_critTreeSec);
242 std::vector<CString> toRemove;
243 for (auto it = this->cbegin(); it != this->cend(); ++it)
245 if ((*it).first.Find(thePath) == 0)
246 toRemove.push_back((*it).first);
248 for (auto it = toRemove.cbegin(); it != toRemove.cend(); ++it)
249 this->erase(*it);
250 return !toRemove.empty();
253 int GetFileStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,BOOL IsFull=false, BOOL IsRecursive=false,
254 FILL_STATUS_CALLBACK callback = nullptr, void *pData = nullptr,
255 bool isLoaded=false);
256 bool CheckHeadAndUpdate(const CString &gitdir, bool readTree = true);
257 int IsUnderVersionControl(const CString& gitdir, CString path, bool isDir, bool* isVersion);
260 class CGitFileName
262 public:
263 CGitFileName() {}
264 CGitFileName(const CString& filename)
266 m_CaseFileName = filename;
267 m_FileName = filename;
268 m_FileName.MakeLower();
270 CString m_FileName;
271 CString m_CaseFileName;
274 static bool SortCGitFileName(const CGitFileName& item1, const CGitFileName& item2)
276 return item1.m_FileName.Compare(item2.m_FileName) < 0;
279 class CGitIgnoreItem
281 public:
282 CGitIgnoreItem()
284 m_LastModifyTime =0;
285 m_pExcludeList = nullptr;
286 m_buffer = nullptr;
288 ~CGitIgnoreItem()
290 if(m_pExcludeList)
291 git_free_exclude_list(m_pExcludeList);
292 free(m_buffer);
293 m_pExcludeList= nullptr;
294 m_buffer = nullptr;
296 __time64_t m_LastModifyTime;
297 CStringA m_BaseDir;
298 BYTE *m_buffer;
299 EXCLUDE_LIST m_pExcludeList;
301 int FetchIgnoreList(const CString &projectroot, const CString &file, bool isGlobal);
304 * patha: the filename to be checked whether is is ignored or not
305 * base: must be a pointer to the beginning of the base filename WITHIN patha
306 * type: DT_DIR or DT_REG
308 int IsPathIgnored(const CStringA& patha, const char* base, int& type);
309 #ifdef GTEST_INCLUDE_GTEST_GTEST_H_
310 int IsPathIgnored(const CStringA& patha, int& type);
311 #endif
314 class CGitIgnoreList
316 private:
317 bool CheckFileChanged(const CString &path);
318 int FetchIgnoreFile(const CString &gitdir, const CString &gitignore, bool isGlobal);
320 int CheckIgnore(const CString &path, const CString &root, bool isDir);
321 int CheckFileAgainstIgnoreList(const CString &ignorefile, const CStringA &patha, const char * base, int &type);
323 // core.excludesfile stuff
324 std::map<CString, CString> m_CoreExcludesfiles;
325 CString m_sGitSystemConfigPath;
326 CString m_sGitProgramDataConfigPath;
327 ULONGLONG m_dGitSystemConfigPathLastChecked;
328 CReaderWriterLock m_coreExcludefilesSharedMutex;
329 // checks if the msysgit path has changed and return true/false
330 // if the path changed, the cache is update
331 // force is only ised in constructor
332 bool CheckAndUpdateGitSystemConfigPath(bool force = true);
333 bool CheckAndUpdateCoreExcludefile(const CString &adminDir);
334 const CString GetWindowsHome();
336 public:
337 CReaderWriterLock m_SharedMutex;
339 CGitIgnoreList(){ CheckAndUpdateGitSystemConfigPath(true); }
341 std::map<CString, CGitIgnoreItem> m_Map;
343 bool CheckIgnoreChanged(const CString &gitdir,const CString &path, bool isDir);
344 int LoadAllIgnoreFile(const CString &gitdir, const CString &path, bool isDir);
345 bool IsIgnore(CString path, const CString& root, bool isDir);
348 template<class T>
349 int GetRangeInSortVector(const T &vector, LPCTSTR pstr, int len, int *start, int *end, int pos)
351 if( pos < 0)
352 return -1;
353 if (start == 0 || !end)
354 return -1;
356 *start=*end=-1;
358 if (vector.empty())
359 return -1;
361 if (pos >= (int)vector.size())
362 return -1;
364 if( _tcsnccmp(vector[pos].m_FileName, pstr,len) != 0)
365 return -1;
367 *start = 0;
368 *end = (int)vector.size() - 1;
370 // shortcut, if all entries are going match
371 if (!len)
372 return 0;
374 for (int i = pos; i < (int)vector.size(); ++i)
376 if (_tcsnccmp(vector[i].m_FileName, pstr, len) != 0)
377 break;
379 *end = i;
381 for (int i = pos; i >= 0; --i)
383 if (_tcsnccmp(vector[i].m_FileName, pstr, len) != 0)
384 break;
386 *start = i;
389 return 0;
392 template<class T>
393 int SearchInSortVector(const T &vector, LPCTSTR pstr, int len)
395 int end = (int)vector.size() - 1;
396 int start = 0;
397 int mid = (start+end)/2;
399 if (vector.empty())
400 return -1;
402 while(!( start == end && start==mid))
404 int cmp;
405 if(len < 0)
406 cmp = _tcscmp(vector[mid].m_FileName,pstr);
407 else
408 cmp = _tcsnccmp( vector[mid].m_FileName,pstr,len );
410 if (cmp == 0)
411 return mid;
412 else if (cmp < 0)
413 start = mid + 1;
414 else // (cmp > 0)
415 end = mid;
417 mid=(start +end ) /2;
420 if(len <0)
422 if(_tcscmp(vector[mid].m_FileName,pstr) == 0)
423 return mid;
425 else
427 if(_tcsnccmp( vector[mid].m_FileName,pstr,len ) == 0)
428 return mid;
430 return -1;
433 class CGitAdminDirMap:public std::map<CString, CString>
435 public:
436 CComCriticalSection m_critIndexSec;
437 std::map<CString, CString> m_reverseLookup;
439 CGitAdminDirMap() { m_critIndexSec.Init(); }
440 ~CGitAdminDirMap() { m_critIndexSec.Term(); }
442 CString GetAdminDir(const CString &path)
444 CString thePath(path);
445 thePath.MakeLower();
446 CAutoLocker lock(m_critIndexSec);
447 auto lookup = find(thePath);
448 if (lookup == cend())
450 if (PathIsDirectory(path + _T("\\.git")))
452 (*this)[thePath] = path + _T("\\.git\\");
453 m_reverseLookup[thePath + _T("\\.git")] = path;
454 return (*this)[thePath];
457 CString result = GitAdminDir::ReadGitLink(path, path + _T("\\.git"));
458 if (!result.IsEmpty())
460 (*this)[thePath] = result + _T("\\");
461 m_reverseLookup[result.MakeLower()] = path;
462 return (*this)[thePath];
465 return path + _T("\\.git\\"); // in case of an error stick to old behavior
468 return lookup->second;
471 CString GetAdminDirConcat(const CString& path, const CString& subpath)
473 CString result(GetAdminDir(path));
474 result += subpath;
475 return result;
478 CString GetWorkingCopy(const CString &gitDir)
480 CString path(gitDir);
481 path.MakeLower();
482 CAutoLocker lock(m_critIndexSec);
483 auto lookup = m_reverseLookup.find(path);
484 if (lookup == m_reverseLookup.cend())
485 return gitDir;
486 return lookup->second;