Avoid creation of intermediates
[TortoiseGit.git] / src / Git / gitindex.h
blob876b18bd6eaf9cd707dcccb5102a874f3f4a00ca
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"
26 #include "StringUtils.h"
28 class CGitIndex
30 public:
31 CString m_FileName;
32 __time64_t m_ModifyTime;
33 uint16_t m_Flags;
34 uint16_t m_FlagsExtended;
35 CGitHash m_IndexHash;
36 __int64 m_Size;
38 int Print();
41 class CGitIndexList:public std::vector<CGitIndex>
43 protected:
45 public:
46 __time64_t m_LastModifyTime;
47 BOOL m_bHasConflicts;
49 CGitIndexList();
50 ~CGitIndexList();
52 int ReadIndex(CString file);
53 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);
54 #ifdef GTEST_INCLUDE_GTEST_GTEST_H_
55 FRIEND_TEST(GitIndexCBasicGitWithTestRepoFixture, GetFileStatus);
56 #endif
57 protected:
58 __int64 m_iMaxCheckSize;
59 CComCriticalSection m_critRepoSec;
60 CAutoRepository repository;
61 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);
64 typedef std::tr1::shared_ptr<CGitIndexList> SHARED_INDEX_PTR;
65 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
67 class CGitIndexFileMap:public std::map<CString, SHARED_INDEX_PTR>
69 public:
70 CComCriticalSection m_critIndexSec;
72 CGitIndexFileMap() { m_critIndexSec.Init(); }
73 ~CGitIndexFileMap() { m_critIndexSec.Term(); }
75 SHARED_INDEX_PTR SafeGet(CString thePath)
77 thePath.MakeLower();
78 CAutoLocker lock(m_critIndexSec);
79 auto lookup = find(thePath);
80 if (lookup == cend())
81 return SHARED_INDEX_PTR();
82 return lookup->second;
85 void SafeSet(CString thePath, SHARED_INDEX_PTR ptr)
87 thePath.MakeLower();
88 CAutoLocker lock(m_critIndexSec);
89 (*this)[thePath] = ptr;
92 bool SafeClear(CString thePath)
94 thePath.MakeLower();
95 CAutoLocker lock(m_critIndexSec);
96 auto lookup = find(thePath);
97 if (lookup == cend())
98 return false;
99 erase(lookup);
100 return true;
103 bool SafeClearRecursively(CString thePath)
105 thePath.MakeLower();
106 CAutoLocker lock(m_critIndexSec);
107 std::vector<CString> toRemove;
108 for (auto it = this->cbegin(); it != this->cend(); ++it)
110 if (CStringUtils::StartsWith((*it).first, thePath))
111 toRemove.push_back((*it).first);
113 for (auto it = toRemove.cbegin(); it != toRemove.cend(); ++it)
114 this->erase(*it);
115 return !toRemove.empty();
118 int Check(const CString &gitdir, bool *isChanged);
119 int LoadIndex(const CString &gitdir);
121 bool CheckAndUpdate(const CString &gitdir,bool isLoadUpdatedIndex)
123 bool isChanged=false;
124 if(isLoadUpdatedIndex && Check(gitdir,&isChanged))
125 return false;
127 if(isChanged && isLoadUpdatedIndex)
129 LoadIndex(gitdir);
130 return true;
133 return false;
135 int GetFileStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,
136 BOOL IsFull=false, BOOL IsRecursive=false,
137 FILL_STATUS_CALLBACK callback = nullptr,
138 void* pData = nullptr, CGitHash* pHash = nullptr,
139 bool isLoadUpdatedIndex = true, bool* assumeValid = nullptr, bool* skipWorktree = nullptr);
141 int IsUnderVersionControl(const CString &gitdir,
142 CString path,
143 bool isDir,
144 bool *isVersion,
145 bool isLoadUpdateIndex=true);
149 class CGitTreeItem
151 public:
152 CString m_FileName;
153 CGitHash m_Hash;
154 int m_Flags;
157 /* After object create, never change field agains
158 * that needn't lock to get field
160 class CGitHeadFileList:public std::vector<CGitTreeItem>
162 private:
163 int GetPackRef(const CString &gitdir);
164 CReaderWriterLock m_SharedMutex;
166 __time64_t m_LastModifyTimeHead;
167 __time64_t m_LastModifyTimeRef;
168 __time64_t m_LastModifyTimePackRef;
170 CString m_HeadRefFile;
171 CGitHash m_Head;
172 CString m_HeadFile;
173 CString m_Gitdir;
174 CString m_PackRefFile;
176 CGitHash m_TreeHash; /* buffered tree hash value */
178 std::map<CString,CGitHash> m_PackRefMap;
180 public:
181 CGitHeadFileList()
183 m_LastModifyTimeHead=0;
184 m_LastModifyTimeRef=0;
185 m_LastModifyTimePackRef = 0;
188 int ReadTree();
189 int ReadHeadHash(const CString& gitdir);
190 bool CheckHeadUpdate();
191 bool HeadHashEqualsTreeHash();
192 bool HeadFileIsEmpty();
193 bool HeadIsEmpty();
194 static int CallBack(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);
197 typedef std::tr1::shared_ptr<CGitHeadFileList> SHARED_TREE_PTR;
198 class CGitHeadFileMap:public std::map<CString,SHARED_TREE_PTR>
200 public:
202 CComCriticalSection m_critTreeSec;
204 CGitHeadFileMap() { m_critTreeSec.Init(); }
205 ~CGitHeadFileMap() { m_critTreeSec.Term(); }
207 SHARED_TREE_PTR SafeGet(CString thePath, bool allowEmpty = false)
209 thePath.MakeLower();
210 CAutoLocker lock(m_critTreeSec);
211 auto lookup = find(thePath);
212 if (lookup == cend())
214 if (allowEmpty)
215 return SHARED_TREE_PTR();
216 return SHARED_TREE_PTR(new CGitHeadFileList);
218 return lookup->second;
221 void SafeSet(CString thePath, SHARED_TREE_PTR ptr)
223 thePath.MakeLower();
224 CAutoLocker lock(m_critTreeSec);
225 (*this)[thePath] = ptr;
228 bool SafeClear(CString thePath)
230 thePath.MakeLower();
231 CAutoLocker lock(m_critTreeSec);
232 auto lookup = find(thePath);
233 if (lookup == cend())
234 return false;
235 erase(lookup);
236 return true;
239 bool SafeClearRecursively(CString thePath)
241 thePath.MakeLower();
242 CAutoLocker lock(m_critTreeSec);
243 std::vector<CString> toRemove;
244 for (auto it = this->cbegin(); it != this->cend(); ++it)
246 if (CStringUtils::StartsWith((*it).first, thePath))
247 toRemove.push_back((*it).first);
249 for (auto it = toRemove.cbegin(); it != toRemove.cend(); ++it)
250 this->erase(*it);
251 return !toRemove.empty();
254 int GetFileStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,BOOL IsFull=false, BOOL IsRecursive=false,
255 FILL_STATUS_CALLBACK callback = nullptr, void *pData = nullptr,
256 bool isLoaded=false);
257 bool CheckHeadAndUpdate(const CString &gitdir, bool readTree = true);
258 int IsUnderVersionControl(const CString& gitdir, CString path, bool isDir, bool* isVersion);
261 class CGitFileName
263 public:
264 CGitFileName() {}
265 CGitFileName(const CString& filename)
267 m_CaseFileName = filename;
268 m_FileName = filename;
269 m_FileName.MakeLower();
271 CString m_FileName;
272 CString m_CaseFileName;
275 static bool SortCGitFileName(const CGitFileName& item1, const CGitFileName& item2)
277 return item1.m_FileName.Compare(item2.m_FileName) < 0;
280 class CGitIgnoreItem
282 public:
283 CGitIgnoreItem()
285 m_LastModifyTime =0;
286 m_pExcludeList = nullptr;
287 m_buffer = nullptr;
289 ~CGitIgnoreItem()
291 if(m_pExcludeList)
292 git_free_exclude_list(m_pExcludeList);
293 free(m_buffer);
294 m_pExcludeList= nullptr;
295 m_buffer = nullptr;
297 __time64_t m_LastModifyTime;
298 CStringA m_BaseDir;
299 BYTE *m_buffer;
300 EXCLUDE_LIST m_pExcludeList;
302 int FetchIgnoreList(const CString &projectroot, const CString &file, bool isGlobal);
305 * patha: the filename to be checked whether is is ignored or not
306 * base: must be a pointer to the beginning of the base filename WITHIN patha
307 * type: DT_DIR or DT_REG
309 int IsPathIgnored(const CStringA& patha, const char* base, int& type);
310 #ifdef GTEST_INCLUDE_GTEST_GTEST_H_
311 int IsPathIgnored(const CStringA& patha, int& type);
312 #endif
315 class CGitIgnoreList
317 private:
318 bool CheckFileChanged(const CString &path);
319 int FetchIgnoreFile(const CString &gitdir, const CString &gitignore, bool isGlobal);
321 int CheckIgnore(const CString &path, const CString &root, bool isDir);
322 int CheckFileAgainstIgnoreList(const CString &ignorefile, const CStringA &patha, const char * base, int &type);
324 // core.excludesfile stuff
325 std::map<CString, CString> m_CoreExcludesfiles;
326 CString m_sGitSystemConfigPath;
327 CString m_sGitProgramDataConfigPath;
328 ULONGLONG m_dGitSystemConfigPathLastChecked;
329 CReaderWriterLock m_coreExcludefilesSharedMutex;
330 // checks if the msysgit path has changed and return true/false
331 // if the path changed, the cache is update
332 // force is only ised in constructor
333 bool CheckAndUpdateGitSystemConfigPath(bool force = true);
334 bool CheckAndUpdateCoreExcludefile(const CString &adminDir);
335 const CString GetWindowsHome();
337 public:
338 CReaderWriterLock m_SharedMutex;
340 CGitIgnoreList(){ CheckAndUpdateGitSystemConfigPath(true); }
342 std::map<CString, CGitIgnoreItem> m_Map;
344 bool CheckIgnoreChanged(const CString &gitdir,const CString &path, bool isDir);
345 int LoadAllIgnoreFile(const CString &gitdir, const CString &path, bool isDir);
346 bool IsIgnore(CString path, const CString& root, bool isDir);
349 template<class T>
350 int GetRangeInSortVector(const T &vector, LPCTSTR pstr, int len, int *start, int *end, int pos)
352 if( pos < 0)
353 return -1;
354 if (start == 0 || !end)
355 return -1;
357 *start=*end=-1;
359 if (vector.empty())
360 return -1;
362 if (pos >= (int)vector.size())
363 return -1;
365 if( _tcsnccmp(vector[pos].m_FileName, pstr,len) != 0)
366 return -1;
368 *start = 0;
369 *end = (int)vector.size() - 1;
371 // shortcut, if all entries are going match
372 if (!len)
373 return 0;
375 for (int i = pos; i < (int)vector.size(); ++i)
377 if (_tcsnccmp(vector[i].m_FileName, pstr, len) != 0)
378 break;
380 *end = i;
382 for (int i = pos; i >= 0; --i)
384 if (_tcsnccmp(vector[i].m_FileName, pstr, len) != 0)
385 break;
387 *start = i;
390 return 0;
393 template<class T>
394 int SearchInSortVector(const T &vector, LPCTSTR pstr, int len)
396 int end = (int)vector.size() - 1;
397 int start = 0;
398 int mid = (start+end)/2;
400 if (vector.empty())
401 return -1;
403 while(!( start == end && start==mid))
405 int cmp;
406 if(len < 0)
407 cmp = _tcscmp(vector[mid].m_FileName,pstr);
408 else
409 cmp = _tcsnccmp( vector[mid].m_FileName,pstr,len );
411 if (cmp == 0)
412 return mid;
413 else if (cmp < 0)
414 start = mid + 1;
415 else // (cmp > 0)
416 end = mid;
418 mid=(start +end ) /2;
421 if(len <0)
423 if(_tcscmp(vector[mid].m_FileName,pstr) == 0)
424 return mid;
426 else
428 if(_tcsnccmp( vector[mid].m_FileName,pstr,len ) == 0)
429 return mid;
431 return -1;
434 class CGitAdminDirMap:public std::map<CString, CString>
436 public:
437 CComCriticalSection m_critIndexSec;
438 std::map<CString, CString> m_reverseLookup;
440 CGitAdminDirMap() { m_critIndexSec.Init(); }
441 ~CGitAdminDirMap() { m_critIndexSec.Term(); }
443 CString GetAdminDir(const CString &path)
445 CString thePath(path);
446 thePath.MakeLower();
447 CAutoLocker lock(m_critIndexSec);
448 auto lookup = find(thePath);
449 if (lookup == cend())
451 if (PathIsDirectory(path + _T("\\.git")))
453 (*this)[thePath] = path + _T("\\.git\\");
454 m_reverseLookup[thePath + _T("\\.git")] = path;
455 return (*this)[thePath];
458 CString result = GitAdminDir::ReadGitLink(path, path + _T("\\.git"));
459 if (!result.IsEmpty())
461 (*this)[thePath] = result + _T("\\");
462 m_reverseLookup[result.MakeLower()] = path;
463 return (*this)[thePath];
466 return path + _T("\\.git\\"); // in case of an error stick to old behavior
469 return lookup->second;
472 CString GetAdminDirConcat(const CString& path, const CString& subpath)
474 CString result(GetAdminDir(path));
475 result += subpath;
476 return result;
479 CString GetWorkingCopy(const CString &gitDir)
481 CString path(gitDir);
482 path.MakeLower();
483 CAutoLocker lock(m_critIndexSec);
484 auto lookup = m_reverseLookup.find(path);
485 if (lookup == m_reverseLookup.cend())
486 return gitDir;
487 return lookup->second;