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"
26 #include "StringUtils.h"
32 __time64_t m_ModifyTime
;
34 uint16_t m_FlagsExtended
;
41 class CGitIndexList
:public std::vector
<CGitIndex
>
46 __time64_t m_LastModifyTime
;
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
);
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::shared_ptr
<CGitIndexList
> SHARED_INDEX_PTR
;
65 typedef CComCritSecLock
<CComCriticalSection
> CAutoLocker
;
67 class CGitIndexFileMap
:public std::map
<CString
, SHARED_INDEX_PTR
>
70 CComCriticalSection m_critIndexSec
;
72 CGitIndexFileMap() { m_critIndexSec
.Init(); }
73 ~CGitIndexFileMap() { m_critIndexSec
.Term(); }
75 SHARED_INDEX_PTR
SafeGet(CString thePath
)
78 CAutoLocker
lock(m_critIndexSec
);
79 auto lookup
= find(thePath
);
81 return SHARED_INDEX_PTR();
82 return lookup
->second
;
85 void SafeSet(CString thePath
, SHARED_INDEX_PTR ptr
)
88 CAutoLocker
lock(m_critIndexSec
);
89 (*this)[thePath
] = ptr
;
92 bool SafeClear(CString thePath
)
95 CAutoLocker
lock(m_critIndexSec
);
96 auto lookup
= find(thePath
);
103 bool SafeClearRecursively(CString thePath
)
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
)
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
))
127 if(isChanged
&& isLoadUpdatedIndex
)
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
,
145 bool isLoadUpdateIndex
=true);
157 /* After object create, never change field agains
158 * that needn't lock to get field
160 class CGitHeadFileList
:public std::vector
<CGitTreeItem
>
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
;
174 CString m_PackRefFile
;
176 CGitHash m_TreeHash
; /* buffered tree hash value */
178 std::map
<CString
,CGitHash
> m_PackRefMap
;
183 m_LastModifyTimeHead
=0;
184 m_LastModifyTimeRef
=0;
185 m_LastModifyTimePackRef
= 0;
189 int ReadHeadHash(const CString
& gitdir
);
190 bool CheckHeadUpdate();
191 bool HeadHashEqualsTreeHash();
192 bool HeadFileIsEmpty();
194 static int CallBack(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);
197 typedef std::shared_ptr
<CGitHeadFileList
> SHARED_TREE_PTR
;
198 class CGitHeadFileMap
:public std::map
<CString
,SHARED_TREE_PTR
>
202 CComCriticalSection m_critTreeSec
;
204 CGitHeadFileMap() { m_critTreeSec
.Init(); }
205 ~CGitHeadFileMap() { m_critTreeSec
.Term(); }
207 SHARED_TREE_PTR
SafeGet(CString thePath
, bool allowEmpty
= false)
210 CAutoLocker
lock(m_critTreeSec
);
211 auto lookup
= find(thePath
);
212 if (lookup
== cend())
215 return SHARED_TREE_PTR();
216 return std::make_shared
<CGitHeadFileList
>();
218 return lookup
->second
;
221 void SafeSet(CString thePath
, SHARED_TREE_PTR ptr
)
224 CAutoLocker
lock(m_critTreeSec
);
225 (*this)[thePath
] = ptr
;
228 bool SafeClear(CString thePath
)
231 CAutoLocker
lock(m_critTreeSec
);
232 auto lookup
= find(thePath
);
233 if (lookup
== cend())
239 bool SafeClearRecursively(CString thePath
)
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
)
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
);
265 CGitFileName(const CString
& filename
)
267 m_CaseFileName
= filename
;
268 m_FileName
= filename
;
269 m_FileName
.MakeLower();
272 CString m_CaseFileName
;
275 static bool SortCGitFileName(const CGitFileName
& item1
, const CGitFileName
& item2
)
277 return item1
.m_FileName
.Compare(item2
.m_FileName
) < 0;
286 m_pExcludeList
= nullptr;
292 git_free_exclude_list(m_pExcludeList
);
294 m_pExcludeList
= nullptr;
297 __time64_t m_LastModifyTime
;
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
);
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();
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 static const size_t NPOS
= (size_t)-1; // bad/missing length/position
350 static_assert(MAXSIZE_T
== NPOS
, "NPOS must equal MAXSIZE_T");
351 #pragma warning(push)
352 #pragma warning(disable: 4310)
353 static_assert(-1 == (int)NPOS
, "NPOS must equal -1");
357 int GetRangeInSortVector(const T
& vector
, LPCTSTR pstr
, size_t len
, size_t* start
, size_t* end
, size_t pos
)
364 *start
= *end
= NPOS
;
369 if (pos
>= vector
.size())
372 if (wcsncmp(vector
[pos
].m_FileName
, pstr
, len
) != 0)
376 *end
= vector
.size() - 1;
378 // shortcut, if all entries are going match
382 for (size_t i
= pos
; i
< vector
.size(); ++i
)
384 if (wcsncmp(vector
[i
].m_FileName
, pstr
, len
) != 0)
389 for (size_t i
= pos
+ 1; i
-- > 0;)
391 if (wcsncmp(vector
[i
].m_FileName
, pstr
, len
) != 0)
401 size_t SearchInSortVector(const T
& vector
, LPCTSTR pstr
, int len
)
403 size_t end
= vector
.size() - 1;
405 size_t mid
= (start
+ end
) / 2;
410 while(!( start
== end
&& start
==mid
))
414 cmp
= wcscmp(vector
[mid
].m_FileName
, pstr
);
416 cmp
= wcsncmp(vector
[mid
].m_FileName
, pstr
, len
);
425 mid
=(start
+end
) /2;
430 if (wcscmp(vector
[mid
].m_FileName
, pstr
) == 0)
435 if (wcsncmp(vector
[mid
].m_FileName
, pstr
, len
) == 0)
441 class CGitAdminDirMap
:public std::map
<CString
, CString
>
444 CComCriticalSection m_critIndexSec
;
445 std::map
<CString
, CString
> m_reverseLookup
;
447 CGitAdminDirMap() { m_critIndexSec
.Init(); }
448 ~CGitAdminDirMap() { m_critIndexSec
.Term(); }
450 CString
GetAdminDir(const CString
&path
)
452 CString
thePath(path
);
454 CAutoLocker
lock(m_critIndexSec
);
455 auto lookup
= find(thePath
);
456 if (lookup
== cend())
458 if (PathIsDirectory(path
+ L
"\\.git"))
460 (*this)[thePath
] = path
+ L
"\\.git\\";
461 m_reverseLookup
[thePath
+ L
"\\.git"] = path
;
462 return (*this)[thePath
];
465 CString result
= GitAdminDir::ReadGitLink(path
, path
+ L
"\\.git");
466 if (!result
.IsEmpty())
468 (*this)[thePath
] = result
+ L
'\\';
469 m_reverseLookup
[result
.MakeLower()] = path
;
470 return (*this)[thePath
];
473 return path
+ L
"\\.git\\"; // in case of an error stick to old behavior
476 return lookup
->second
;
479 CString
GetAdminDirConcat(const CString
& path
, const CString
& subpath
)
481 CString
result(GetAdminDir(path
));
486 CString
GetWorkingCopy(const CString
&gitDir
)
488 CString
path(gitDir
);
490 CAutoLocker
lock(m_critIndexSec
);
491 auto lookup
= m_reverseLookup
.find(path
);
492 if (lookup
== m_reverseLookup
.cend())
494 return lookup
->second
;