Fix possible problems with "intend to add" staged files
[TortoiseGit.git] / src / Git / gitindex.h
blob3db2286bab26d21b9edf8727e92c977c6f8379d2
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=NULL,CGitHash *pHash=NULL,
138 bool isLoadUpdatedIndex = true, bool * assumeValid = NULL, bool * skipWorktree = NULL);
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 =NULL;
286 m_buffer = NULL;
288 ~CGitIgnoreItem()
290 if(m_pExcludeList)
291 git_free_exclude_list(m_pExcludeList);
292 free(m_buffer);
293 m_pExcludeList=NULL;
294 m_buffer = NULL;
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 ULONGLONG m_dGitSystemConfigPathLastChecked;
327 CReaderWriterLock m_coreExcludefilesSharedMutex;
328 // checks if the msysgit path has changed and return true/false
329 // if the path changed, the cache is update
330 // force is only ised in constructor
331 bool CheckAndUpdateGitSystemConfigPath(bool force = true);
332 bool CheckAndUpdateCoreExcludefile(const CString &adminDir);
333 const CString GetWindowsHome();
335 public:
336 CReaderWriterLock m_SharedMutex;
338 CGitIgnoreList(){ CheckAndUpdateGitSystemConfigPath(true); }
340 std::map<CString, CGitIgnoreItem> m_Map;
342 bool CheckIgnoreChanged(const CString &gitdir,const CString &path, bool isDir);
343 int LoadAllIgnoreFile(const CString &gitdir, const CString &path, bool isDir);
344 bool IsIgnore(CString path, const CString& root, bool isDir);
347 template<class T>
348 int GetRangeInSortVector(const T &vector, LPCTSTR pstr, int len, int *start, int *end, int pos)
350 if( pos < 0)
352 return -1;
354 if(start == 0 || end == NULL)
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;