1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013 - 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 "SharedMutex.h"
24 #include "UnicodeUtils.h"
30 __time64_t m_ModifyTime
;
31 unsigned short m_Flags
;
37 class CGitIndexList
:public std::vector
<CGitIndex
>
42 __time64_t m_LastModifyTime
;
47 int ReadIndex(CString file
);
48 int GetStatus(const CString
&gitdir
, const 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);
51 CComCriticalSection m_critRepoSec
;
52 git_repository
* repository
;
53 int GetFileStatus(const CString
&gitdir
, const CString
&path
, git_wc_status_kind
* status
, __int64 time
, FILL_STATUS_CALLBACK callback
= nullptr, void *pData
= nullptr, CGitHash
*pHash
= nullptr, bool * assumeValid
= nullptr, bool * skipWorktree
= nullptr);
54 int GetDirStatus(const CString
&gitdir
, const CString
&path
, git_wc_status_kind
* status
,__int64 time
, FILL_STATUS_CALLBACK callback
= nullptr, void *pData
= nullptr,CGitHash
*pHash
= nullptr);
57 typedef std::tr1::shared_ptr
<CGitIndexList
> SHARED_INDEX_PTR
;
58 typedef CComCritSecLock
<CComCriticalSection
> CAutoLocker
;
60 class CGitIndexFileMap
:public std::map
<CString
, SHARED_INDEX_PTR
>
63 CComCriticalSection m_critIndexSec
;
65 CGitIndexFileMap() { m_critIndexSec
.Init(); }
66 ~CGitIndexFileMap() { m_critIndexSec
.Term(); }
68 SHARED_INDEX_PTR
SafeGet(const CString
&path
)
70 CString thePath
= path
;
72 CAutoLocker
lock(m_critIndexSec
);
73 if(this->find(thePath
) == end())
74 return SHARED_INDEX_PTR();
76 return (*this)[thePath
];
79 void SafeSet(const CString
&path
, SHARED_INDEX_PTR ptr
)
81 CString thePath
= path
;
83 CAutoLocker
lock(m_critIndexSec
);
84 (*this)[thePath
] = ptr
;
87 int Check(const CString
&gitdir
, bool *isChanged
);
88 int LoadIndex(const CString
&gitdir
);
90 bool CheckAndUpdate(const CString
&gitdir
,bool isLoadUpdatedIndex
)
93 if(isLoadUpdatedIndex
&& Check(gitdir
,&isChanged
))
96 if(isChanged
&& isLoadUpdatedIndex
)
104 int GetFileStatus(const CString
&gitdir
,const CString
&path
,git_wc_status_kind
* status
,
105 BOOL IsFull
=false, BOOL IsRecursive
=false,
106 FILL_STATUS_CALLBACK callback
= nullptr,
107 void *pData
=NULL
,CGitHash
*pHash
=NULL
,
108 bool isLoadUpdatedIndex
= true, bool * assumeValid
= NULL
, bool * skipWorktree
= NULL
);
110 int IsUnderVersionControl(const CString
&gitdir
,
114 bool isLoadUpdateIndex
=true);
126 /* After object create, never change field agains
127 * that needn't lock to get field
129 class CGitHeadFileList
:public std::vector
<CGitTreeItem
>
132 int GetPackRef(const CString
&gitdir
);
133 SharedMutex m_SharedMutex
;
135 __time64_t m_LastModifyTimeHead
;
136 __time64_t m_LastModifyTimeRef
;
137 __time64_t m_LastModifyTimePackRef
;
139 CString m_HeadRefFile
;
143 CString m_PackRefFile
;
145 CGitHash m_TreeHash
; /* buffered tree hash value */
147 std::map
<CString
,CGitHash
> m_PackRefMap
;
152 m_LastModifyTimeHead
=0;
153 m_LastModifyTimeRef
=0;
154 m_LastModifyTimePackRef
= 0;
155 m_SharedMutex
.Init();
160 m_SharedMutex
.Release();
164 int ReadHeadHash(CString gitdir
);
165 bool CheckHeadUpdate();
166 bool HeadHashEqualsTreeHash();
167 bool HeadFileIsEmpty();
169 static int CallBack(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);
172 typedef std::tr1::shared_ptr
<CGitHeadFileList
> SHARED_TREE_PTR
;
173 class CGitHeadFileMap
:public std::map
<CString
,SHARED_TREE_PTR
>
177 CComCriticalSection m_critTreeSec
;
179 CGitHeadFileMap() { m_critTreeSec
.Init(); }
180 ~CGitHeadFileMap() { m_critTreeSec
.Term(); }
182 SHARED_TREE_PTR
SafeGet(const CString
&path
)
184 CString thePath
= path
;
186 CAutoLocker
lock(m_critTreeSec
);
187 if(this->find(thePath
) == end())
188 return SHARED_TREE_PTR();
190 return (*this)[thePath
];
193 void SafeSet(const CString
&path
, SHARED_TREE_PTR ptr
)
195 CString thePath
= path
;
197 CAutoLocker
lock(m_critTreeSec
);
198 (*this)[thePath
] = ptr
;
201 int GetFileStatus(const CString
&gitdir
,const CString
&path
,git_wc_status_kind
* status
,BOOL IsFull
=false, BOOL IsRecursive
=false,
202 FILL_STATUS_CALLBACK callback
= nullptr, void *pData
= nullptr,
203 bool isLoaded
=false);
204 bool CheckHeadAndUpdate(const CString
&gitdir
, bool readTree
= true);
205 int IsUnderVersionControl(const CString
&gitdir
, const CString
&path
, bool isDir
, bool *isVersion
);
212 CString m_CaseFileName
;
218 SharedMutex m_SharedMutex
;
223 m_pExcludeList
=NULL
;
229 git_free_exclude_list(m_pExcludeList
);
235 __time64_t m_LastModifyTime
;
238 EXCLUDE_LIST m_pExcludeList
;
240 int FetchIgnoreList(const CString
&projectroot
, const CString
&file
, bool isGlobal
);
246 bool CheckFileChanged(const CString
&path
);
247 int FetchIgnoreFile(const CString
&gitdir
, const CString
&gitignore
, bool isGlobal
);
249 int CheckIgnore(const CString
&path
,const CString
&root
);
250 int CheckFileAgainstIgnoreList(const CString
&ignorefile
, const CStringA
&patha
, const char * base
, int &type
);
252 // core.excludesfile stuff
253 std::map
<CString
, CString
> m_CoreExcludesfiles
;
254 CString m_sMsysGitBinPath
;
255 DWORD m_dMsysGitBinPathLastChecked
;
256 SharedMutex m_coreExcludefilesSharedMutex
;
257 // checks if the msysgit path has changed and return true/false
258 // if the path changed, the cache is update
259 // force is only ised in constructor
260 bool CheckAndUpdateMsysGitBinpath(bool force
= true);
261 bool CheckAndUpdateCoreExcludefile(const CString
&adminDir
);
262 const CString
GetWindowsHome();
265 SharedMutex m_SharedMutex
;
267 CGitIgnoreList(){ m_SharedMutex
.Init(); m_coreExcludefilesSharedMutex
.Init(); CheckAndUpdateMsysGitBinpath(true); }
268 ~CGitIgnoreList() { m_SharedMutex
.Release(); m_coreExcludefilesSharedMutex
.Release(); }
270 std::map
<CString
, CGitIgnoreItem
> m_Map
;
272 bool CheckIgnoreChanged(const CString
&gitdir
,const CString
&path
);
273 int LoadAllIgnoreFile(const CString
&gitdir
,const CString
&path
);
274 bool IsIgnore(const CString
&path
,const CString
&root
);
278 int GetRangeInSortVector(T
&vector
,LPTSTR pstr
,int len
, int *start
, int *end
, int pos
)
284 if(start
== 0 || end
== NULL
)
292 if (pos
>= (int)vector
.size())
295 if( _tcsnccmp(vector
[pos
].m_FileName
, pstr
,len
) != 0)
297 for (int i
= 0; i
< (int)vector
.size(); ++i
)
299 if( _tcsnccmp(vector
[i
].m_FileName
, pstr
,len
) == 0 )
311 *end
= (int)vector
.size();
313 for (int i
= pos
; i
< (int)vector
.size(); ++i
)
315 if( _tcsnccmp(vector
[i
].m_FileName
, pstr
,len
) == 0 )
324 for(int i
=pos
;i
>=0;i
--)
326 if( _tcsnccmp(vector
[i
].m_FileName
, pstr
,len
) == 0 )
340 int SearchInSortVector(T
&vector
, LPTSTR pstr
, int len
)
342 int end
= (int)vector
.size() - 1;
344 int mid
= (start
+end
)/2;
349 while(!( start
== end
&& start
==mid
))
353 cmp
= _tcscmp(vector
[mid
].m_FileName
,pstr
);
355 cmp
= _tcsnccmp( vector
[mid
].m_FileName
,pstr
,len
);
364 mid
=(start
+end
) /2;
369 if(_tcscmp(vector
[mid
].m_FileName
,pstr
) == 0)
374 if(_tcsnccmp( vector
[mid
].m_FileName
,pstr
,len
) == 0)
380 class CGitAdminDirMap
:public std::map
<CString
, CString
>
383 CComCriticalSection m_critIndexSec
;
384 std::map
<CString
, CString
> m_reverseLookup
;
386 CGitAdminDirMap() { m_critIndexSec
.Init(); }
387 ~CGitAdminDirMap() { m_critIndexSec
.Term(); }
389 CString
GetAdminDir(const CString
&path
)
391 CString thePath
= path
;
393 CAutoLocker
lock(m_critIndexSec
);
394 if(this->find(thePath
) == end())
396 if (PathIsDirectory(path
+ _T("\\.git")))
398 (*this)[thePath
] = path
+ _T("\\.git\\");
399 m_reverseLookup
[thePath
+ _T("\\.git")] = path
;
400 return (*this)[thePath
];
404 FILE * pFile
= _tfsopen(path
+ _T("\\.git"), _T("r"), SH_DENYWR
);
408 std::unique_ptr
<char[]> buffer(new char[size
]);
410 if ((length
= (int)fread(buffer
.get(), sizeof(char), size
, pFile
)) >= 8)
413 CStringA
strA(buffer
.get(), length
);
414 if (strA
.Left(8) == "gitdir: ")
416 CString str
= CUnicodeUtils::GetUnicode(strA
.Trim().Mid(8)); // 8 = len("gitdir: ")
417 str
.Replace(_T("/"), _T("\\"));
418 str
.TrimRight(_T("\\"));
419 if (str
.GetLength() > 0 && str
[0] == _T('.'))
421 str
= path
+ _T("\\") + str
;
423 PathCanonicalize(newPath
.GetBuffer(MAX_PATH
), str
.GetBuffer());
424 newPath
.ReleaseBuffer();
428 (*this)[thePath
] = str
+ _T("\\");
429 m_reverseLookup
[str
.MakeLower()] = path
;
430 return (*this)[thePath
];
436 return path
+ _T("\\.git\\"); // in case of an error stick to old behavior
440 return (*this)[thePath
];
443 CString
GetWorkingCopy(const CString
&gitDir
)
445 CString path
= gitDir
;
447 CAutoLocker
lock(m_critIndexSec
);
448 if (m_reverseLookup
.find(path
) == m_reverseLookup
.end())
451 return m_reverseLookup
[path
];