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.
22 #include "GitStatus.h"
23 #include "UnicodeUtils.h"
24 #include "ReaderWriterLock.h"
25 #include "GitAdminDir.h"
31 __time64_t m_ModifyTime
;
33 uint16_t m_FlagsExtended
;
40 class CGitIndexList
:public std::vector
<CGitIndex
>
45 __time64_t m_LastModifyTime
;
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
);
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
>
69 CComCriticalSection m_critIndexSec
;
71 CGitIndexFileMap() { m_critIndexSec
.Init(); }
72 ~CGitIndexFileMap() { m_critIndexSec
.Term(); }
74 SHARED_INDEX_PTR
SafeGet(CString thePath
)
77 CAutoLocker
lock(m_critIndexSec
);
78 auto lookup
= find(thePath
);
80 return SHARED_INDEX_PTR();
81 return lookup
->second
;
84 void SafeSet(CString thePath
, SHARED_INDEX_PTR ptr
)
87 CAutoLocker
lock(m_critIndexSec
);
88 (*this)[thePath
] = ptr
;
91 bool SafeClear(CString thePath
)
94 CAutoLocker
lock(m_critIndexSec
);
95 auto lookup
= find(thePath
);
102 bool SafeClearRecursively(CString thePath
)
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
)
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
))
126 if(isChanged
&& isLoadUpdatedIndex
)
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
,
144 bool isLoadUpdateIndex
=true);
156 /* After object create, never change field agains
157 * that needn't lock to get field
159 class CGitHeadFileList
:public std::vector
<CGitTreeItem
>
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
;
173 CString m_PackRefFile
;
175 CGitHash m_TreeHash
; /* buffered tree hash value */
177 std::map
<CString
,CGitHash
> m_PackRefMap
;
182 m_LastModifyTimeHead
=0;
183 m_LastModifyTimeRef
=0;
184 m_LastModifyTimePackRef
= 0;
188 int ReadHeadHash(const CString
& gitdir
);
189 bool CheckHeadUpdate();
190 bool HeadHashEqualsTreeHash();
191 bool HeadFileIsEmpty();
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
>
201 CComCriticalSection m_critTreeSec
;
203 CGitHeadFileMap() { m_critTreeSec
.Init(); }
204 ~CGitHeadFileMap() { m_critTreeSec
.Term(); }
206 SHARED_TREE_PTR
SafeGet(CString thePath
, bool allowEmpty
= false)
209 CAutoLocker
lock(m_critTreeSec
);
210 auto lookup
= find(thePath
);
211 if (lookup
== cend())
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
)
223 CAutoLocker
lock(m_critTreeSec
);
224 (*this)[thePath
] = ptr
;
227 bool SafeClear(CString thePath
)
230 CAutoLocker
lock(m_critTreeSec
);
231 auto lookup
= find(thePath
);
232 if (lookup
== cend())
238 bool SafeClearRecursively(CString thePath
)
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
)
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
);
264 CGitFileName(const CString
& filename
)
266 m_CaseFileName
= filename
;
267 m_FileName
= filename
;
268 m_FileName
.MakeLower();
271 CString m_CaseFileName
;
274 static bool SortCGitFileName(const CGitFileName
& item1
, const CGitFileName
& item2
)
276 return item1
.m_FileName
.Compare(item2
.m_FileName
) < 0;
285 m_pExcludeList
= nullptr;
291 git_free_exclude_list(m_pExcludeList
);
293 m_pExcludeList
= nullptr;
296 __time64_t m_LastModifyTime
;
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
);
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();
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
);
349 int GetRangeInSortVector(const T
&vector
, LPCTSTR pstr
, int len
, int *start
, int *end
, int pos
)
353 if (start
== 0 || !end
)
361 if (pos
>= (int)vector
.size())
364 if( _tcsnccmp(vector
[pos
].m_FileName
, pstr
,len
) != 0)
368 *end
= (int)vector
.size() - 1;
370 // shortcut, if all entries are going match
374 for (int i
= pos
; i
< (int)vector
.size(); ++i
)
376 if (_tcsnccmp(vector
[i
].m_FileName
, pstr
, len
) != 0)
381 for (int i
= pos
; i
>= 0; --i
)
383 if (_tcsnccmp(vector
[i
].m_FileName
, pstr
, len
) != 0)
393 int SearchInSortVector(const T
&vector
, LPCTSTR pstr
, int len
)
395 int end
= (int)vector
.size() - 1;
397 int mid
= (start
+end
)/2;
402 while(!( start
== end
&& start
==mid
))
406 cmp
= _tcscmp(vector
[mid
].m_FileName
,pstr
);
408 cmp
= _tcsnccmp( vector
[mid
].m_FileName
,pstr
,len
);
417 mid
=(start
+end
) /2;
422 if(_tcscmp(vector
[mid
].m_FileName
,pstr
) == 0)
427 if(_tcsnccmp( vector
[mid
].m_FileName
,pstr
,len
) == 0)
433 class CGitAdminDirMap
:public std::map
<CString
, CString
>
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
);
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
));
478 CString
GetWorkingCopy(const CString
&gitDir
)
480 CString
path(gitDir
);
482 CAutoLocker
lock(m_critIndexSec
);
483 auto lookup
= m_reverseLookup
.find(path
);
484 if (lookup
== m_reverseLookup
.cend())
486 return lookup
->second
;