Fixed issue #1294: TortoiseGit might crash on concurrent access on CGitHeadFileList
[TortoiseGit.git] / src / Git / gitindex.h
blobd06a67c0f8454fe9d46bacfd62ddc8f2d4341e97
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2012 - 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 "SharedMutex.h"
25 class CGitIndex
27 public:
28 CString m_FileName;
29 __time64_t m_ModifyTime;
30 int m_Flags;
31 CGitHash m_IndexHash;
33 int Print();
36 class CAutoReadLock
38 SharedMutex *m_Lock;
39 public:
40 CAutoReadLock(SharedMutex * lock)
42 m_Lock = lock;
43 lock->AcquireShared();
45 ~CAutoReadLock()
47 m_Lock->ReleaseShared();
51 class CAutoWriteLock
53 SharedMutex *m_Lock;
54 public:
55 CAutoWriteLock(SharedMutex * lock)
57 m_Lock = lock;
58 lock->AcquireExclusive();
60 ~CAutoWriteLock()
62 m_Lock->ReleaseExclusive();
66 class CGitIndexList:public std::vector<CGitIndex>
68 protected:
70 public:
71 __time64_t m_LastModifyTime;
73 CGitIndexList();
75 int ReadIndex(CString file);
76 int GetStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,BOOL IsFull=false, BOOL IsRecursive=false,FIll_STATUS_CALLBACK callback=NULL,void *pData=NULL,CGitHash *pHash=NULL);
77 protected:
78 int GetFileStatus(const CString &gitdir,const CString &path, git_wc_status_kind * status,__int64 time,FIll_STATUS_CALLBACK callback=NULL,void *pData=NULL,CGitHash *pHash=NULL);
79 int GetDirStatus(const CString &gitdir,const CString &path, git_wc_status_kind * status,__int64 time,FIll_STATUS_CALLBACK callback=NULL,void *pData=NULL,CGitHash *pHash=NULL);
82 typedef std::tr1::shared_ptr<CGitIndexList> SHARED_INDEX_PTR;
83 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
85 class CGitIndexFileMap:public std::map<CString, SHARED_INDEX_PTR>
87 public:
88 CComCriticalSection m_critIndexSec;
90 CGitIndexFileMap() { m_critIndexSec.Init(); }
91 ~CGitIndexFileMap() { m_critIndexSec.Term(); }
93 SHARED_INDEX_PTR SafeGet(const CString &path)
95 CString thePath = path;
96 thePath.MakeLower();
97 CAutoLocker lock(m_critIndexSec);
98 if(this->find(thePath) == end())
99 return SHARED_INDEX_PTR();
100 else
101 return (*this)[thePath];
104 void SafeSet(const CString &path, SHARED_INDEX_PTR ptr)
106 CString thePath = path;
107 thePath.MakeLower();
108 CAutoLocker lock(m_critIndexSec);
109 (*this)[thePath] = ptr;
112 int Check(const CString &gitdir, bool *isChanged);
113 int LoadIndex(const CString &gitdir);
115 bool CheckAndUpdate(const CString &gitdir,bool isLoadUpdatedIndex)
117 bool isChanged=false;
118 if(isLoadUpdatedIndex && Check(gitdir,&isChanged))
119 return false;
121 if(isChanged && isLoadUpdatedIndex)
123 LoadIndex(gitdir);
124 return true;
127 return false;
129 int GetFileStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,
130 BOOL IsFull=false, BOOL IsRecursive=false,
131 FIll_STATUS_CALLBACK callback=NULL,
132 void *pData=NULL,CGitHash *pHash=NULL,
133 bool isLoadUpdatedIndex=true);
135 int IsUnderVersionControl(const CString &gitdir,
136 const CString &path,
137 bool isDir,
138 bool *isVersion,
139 bool isLoadUpdateIndex=true);
143 class CGitTreeItem
145 public:
146 CString m_FileName;
147 CGitHash m_Hash;
148 int m_Flags;
151 /* After object create, never change field agains
152 * that needn't lock to get field
154 class CGitHeadFileList:public std::vector<CGitTreeItem>
156 private:
158 int GetPackRef(const CString &gitdir);
159 SharedMutex m_SharedMutex;
161 public:
162 __time64_t m_LastModifyTimeHead;
163 __time64_t m_LastModifyTimeRef;
164 __time64_t m_LastModifyTimePackRef;
166 CString m_HeadRefFile;
167 CGitHash m_Head;
168 CString m_HeadFile;
169 CString m_Gitdir;
170 CString m_PackRefFile;
172 CGitHash m_TreeHash; /* buffered tree hash value */
174 std::map<CString,CGitHash> m_PackRefMap;
176 CGitHeadFileList()
178 m_LastModifyTimeHead=0;
179 m_LastModifyTimeRef=0;
180 m_LastModifyTimePackRef = 0;
181 m_SharedMutex.Init();
184 ~CGitHeadFileList()
186 m_SharedMutex.Release();
189 int ReadTree();
190 int ReadHeadHash(CString gitdir);
191 bool CheckHeadUpdate();
192 static int CallBack(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);
193 //int ReadTree();
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(const CString &path)
208 CString thePath = path;
209 thePath.MakeLower();
210 CAutoLocker lock(m_critTreeSec);
211 if(this->find(thePath) == end())
212 return SHARED_TREE_PTR();
213 else
214 return (*this)[thePath];
217 void SafeSet(const CString &path, SHARED_TREE_PTR ptr)
219 CString thePath = path;
220 thePath.MakeLower();
221 CAutoLocker lock(m_critTreeSec);
222 (*this)[thePath] = ptr;
225 int GetFileStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,BOOL IsFull=false, BOOL IsRecursive=false,
226 FIll_STATUS_CALLBACK callback=NULL,void *pData=NULL,
227 bool isLoaded=false);
228 bool CheckHeadUpdate(const CString &gitdir);
229 int GetHeadHash(const CString &gitdir, CGitHash &hash);
230 int IsUnderVersionControl(const CString &gitdir, const CString &path, bool isDir, bool *isVersion);
233 bool IsHashChanged(const CString &gitdir)
235 SHARED_TREE_PTR ptr = SafeGet(gitdir);
237 if( ptr.get() == NULL)
238 return false;
240 return ptr->m_Head != ptr->m_TreeHash;
245 class CGitFileName
247 public:
248 CString m_FileName;
249 CString m_CaseFileName;
252 class CGitIgnoreItem
254 public:
255 SharedMutex m_SharedMutex;
257 CGitIgnoreItem()
259 m_LastModifyTime =0;
260 m_pExcludeList =NULL;
262 ~CGitIgnoreItem()
264 if(m_pExcludeList)
265 git_free_exclude_list(m_pExcludeList);
266 m_pExcludeList=NULL;
268 __time64_t m_LastModifyTime;
269 CStringA m_BaseDir;
270 EXCLUDE_LIST m_pExcludeList;
271 int FetchIgnoreList(const CString &projectroot, const CString &file, bool isGlobal);
274 class CGitIgnoreList
276 private:
277 bool CheckFileChanged(const CString &path);
278 int FetchIgnoreFile(const CString &gitdir, const CString &gitignore, bool isGlobal);
280 int CheckIgnore(const CString &path,const CString &root);
281 int CheckFileAgainstIgnoreList(const CString &ignorefile, const CStringA &patha, const char * base, int &type);
283 // core.excludesfile stuff
284 std::map<CString, CString> m_CoreExcludesfiles;
285 CString m_sMsysGitBinPath;
286 DWORD m_dMsysGitBinPathLastChecked;
287 SharedMutex m_coreExcludefilesSharedMutex;
288 // checks if the msysgit path has changed and return true/false
289 // if the path changed, the cache is update
290 // force is only ised in constructor
291 bool CheckAndUpdateMsysGitBinpath(bool force = true);
292 bool CheckAndUpdateCoreExcludefile(const CString &adminDir);
293 const CString GetWindowsHome();
295 public:
296 SharedMutex m_SharedMutex;
298 CGitIgnoreList(){ m_SharedMutex.Init(); m_coreExcludefilesSharedMutex.Init(); CheckAndUpdateMsysGitBinpath(true); }
299 ~CGitIgnoreList() { m_SharedMutex.Release(); m_coreExcludefilesSharedMutex.Release(); }
301 std::map<CString, CGitIgnoreItem> m_Map;
303 bool CheckIgnoreChanged(const CString &gitdir,const CString &path);
304 int LoadAllIgnoreFile(const CString &gitdir,const CString &path);
305 bool IsIgnore(const CString &path,const CString &root);
308 template<class T>
309 int GetRangeInSortVector(T &vector,LPTSTR pstr,int len, int *start, int *end, int pos)
311 if( pos < 0)
313 return -1;
315 if(start == 0 || end == NULL)
316 return -1;
318 *start=*end=-1;
320 if(vector.size() ==0)
321 return -1;
323 if(pos >= vector.size())
324 return -1;
326 if( _tcsnccmp(vector[pos].m_FileName, pstr,len) != 0)
328 for(int i=0;i< vector.size();i++)
330 if( _tcsnccmp(vector[i].m_FileName, pstr,len) == 0 )
332 if(*start<0)
333 *start =i;
334 *end =i;
337 return -1;
339 else
341 *start =0;
342 *end = vector.size();
344 for(int i=pos;i<vector.size();i++)
346 if( _tcsnccmp(vector[i].m_FileName, pstr,len) == 0 )
348 *end=i;
350 else
352 break;
355 for(int i=pos;i>=0;i--)
357 if( _tcsnccmp(vector[i].m_FileName, pstr,len) == 0 )
359 *start=i;
361 else
363 break;
367 return 0;
370 template<class T>
371 int SearchInSortVector(T &vector, LPTSTR pstr, int len)
373 int end=vector.size()-1;
374 int start = 0;
375 int mid = (start+end)/2;
377 if(vector.size() == 0)
378 return -1;
380 while(!( start == end && start==mid))
382 int cmp;
383 if(len < 0)
384 cmp = _tcscmp(vector[mid].m_FileName,pstr);
385 else
386 cmp = _tcsnccmp( vector[mid].m_FileName,pstr,len );
388 if(cmp ==0)
389 return mid;
391 if(cmp < 0)
393 start = mid+1;
396 if(cmp > 0)
398 end=mid;
400 mid=(start +end ) /2;
403 if(len <0)
405 if(_tcscmp(vector[mid].m_FileName,pstr) == 0)
406 return mid;
408 else
410 if(_tcsnccmp( vector[mid].m_FileName,pstr,len ) == 0)
411 return mid;
413 return -1;
415 #if 0
417 class CGitStatus
419 protected:
420 int GetFileStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,BOOL IsFull=false, BOOL IsRecursive=false,FIll_STATUS_CALLBACK callback=NULL,void *pData=NULL);
421 public:
422 CGitIgnoreList m_IgnoreList;
423 CGitHeadFileMap m_HeadFilesMap;
424 CGitIndexFileMap m_IndexFilesMap;
426 int GetStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,BOOL IsFull=false, BOOL IsRecursive=false,FIll_STATUS_CALLBACK callback=NULL,void *pData=NULL);
429 #endif
431 class CGitAdminDirMap:public std::map<CString, CString>
433 public:
434 CComCriticalSection m_critIndexSec;
435 std::map<CString, CString> m_reverseLookup;
437 CGitAdminDirMap() { m_critIndexSec.Init(); }
438 ~CGitAdminDirMap() { m_critIndexSec.Term(); }
440 CString GetAdminDir(const CString &path)
442 CString thePath = path;
443 thePath.MakeLower();
444 CAutoLocker lock(m_critIndexSec);
445 if(this->find(thePath) == end())
447 if (PathIsDirectory(thePath + _T("\\.git")))
449 (*this)[thePath] = thePath + _T("\\.git\\");
450 m_reverseLookup[thePath + _T("\\.git")] = thePath;
451 return (*this)[thePath];
453 else
455 FILE * pFile = _tfsopen(thePath + _T("\\.git"), _T("r"), SH_DENYWR);
456 if (pFile)
458 int size = 65536;
459 auto_buffer<char> buffer(size);
460 if (fread(buffer, sizeof(char), size, pFile))
462 fclose(pFile);
463 CString str = CString(buffer);
464 if (str.Left(8) == _T("gitdir: "))
466 str = str.TrimRight().Mid(8);
467 str.Replace(_T("/"), _T("\\"));
468 str.TrimRight(_T("\\"));
469 if (str.GetLength() > 0 && str[0] == _T('.'))
471 str = thePath + _T("\\") + str;
472 CString newPath;
473 PathCanonicalize(newPath.GetBuffer(MAX_PATH), str.GetBuffer());
474 newPath.ReleaseBuffer();
475 str.ReleaseBuffer();
476 str = newPath;
478 (*this)[thePath] = str + _T("\\");
479 m_reverseLookup[str.MakeLower()] = path;
480 return (*this)[thePath];
483 fclose(pFile);
485 return thePath + _T("\\.git\\"); // in case of an error stick to old behavior
488 else
489 return (*this)[thePath];
492 CString GetWorkingCopy(const CString &gitDir)
494 CString path = gitDir;
495 path.MakeLower();
496 CAutoLocker lock(m_critIndexSec);
497 if (m_reverseLookup.find(path) == m_reverseLookup.end())
498 return gitDir;
499 else
500 return m_reverseLookup[path];