PageRenderTime 34ms CodeModel.GetById 37ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Git/gitindex.h

https://gitlab.com/zhaohaiyi/tortoisegit
C Header | 489 lines | 389 code | 73 blank | 27 comment | 56 complexity | 5d501a89bb595f58b0ed17181627ddeb MD5 | raw file
  1. // TortoiseGit - a Windows shell extension for easy version control
  2. // Copyright (C) 2008-2016 - TortoiseGit
  3. // This program is free software; you can redistribute it and/or
  4. // modify it under the terms of the GNU General Public License
  5. // as published by the Free Software Foundation; either version 2
  6. // of the License, or (at your option) any later version.
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU General Public License for more details.
  11. // You should have received a copy of the GNU General Public License
  12. // along with this program; if not, write to the Free Software Foundation,
  13. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  14. //
  15. #include "GitHash.h"
  16. #include "gitdll.h"
  17. #include "GitStatus.h"
  18. #include "UnicodeUtils.h"
  19. #include "ReaderWriterLock.h"
  20. #include "GitAdminDir.h"
  21. #include "StringUtils.h"
  22. class CGitIndex
  23. {
  24. public:
  25. CString m_FileName;
  26. __time64_t m_ModifyTime;
  27. uint16_t m_Flags;
  28. uint16_t m_FlagsExtended;
  29. CGitHash m_IndexHash;
  30. __int64 m_Size;
  31. int Print();
  32. };
  33. class CGitIndexList:public std::vector<CGitIndex>
  34. {
  35. protected:
  36. public:
  37. __time64_t m_LastModifyTime;
  38. BOOL m_bHasConflicts;
  39. CGitIndexList();
  40. ~CGitIndexList();
  41. int ReadIndex(CString file);
  42. 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);
  43. #ifdef GTEST_INCLUDE_GTEST_GTEST_H_
  44. FRIEND_TEST(GitIndexCBasicGitWithTestRepoFixture, GetFileStatus);
  45. #endif
  46. protected:
  47. __int64 m_iMaxCheckSize;
  48. CComCriticalSection m_critRepoSec;
  49. CAutoRepository repository;
  50. 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);
  51. };
  52. typedef std::tr1::shared_ptr<CGitIndexList> SHARED_INDEX_PTR;
  53. typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
  54. class CGitIndexFileMap:public std::map<CString, SHARED_INDEX_PTR>
  55. {
  56. public:
  57. CComCriticalSection m_critIndexSec;
  58. CGitIndexFileMap() { m_critIndexSec.Init(); }
  59. ~CGitIndexFileMap() { m_critIndexSec.Term(); }
  60. SHARED_INDEX_PTR SafeGet(CString thePath)
  61. {
  62. thePath.MakeLower();
  63. CAutoLocker lock(m_critIndexSec);
  64. auto lookup = find(thePath);
  65. if (lookup == cend())
  66. return SHARED_INDEX_PTR();
  67. return lookup->second;
  68. }
  69. void SafeSet(CString thePath, SHARED_INDEX_PTR ptr)
  70. {
  71. thePath.MakeLower();
  72. CAutoLocker lock(m_critIndexSec);
  73. (*this)[thePath] = ptr;
  74. }
  75. bool SafeClear(CString thePath)
  76. {
  77. thePath.MakeLower();
  78. CAutoLocker lock(m_critIndexSec);
  79. auto lookup = find(thePath);
  80. if (lookup == cend())
  81. return false;
  82. erase(lookup);
  83. return true;
  84. }
  85. bool SafeClearRecursively(CString thePath)
  86. {
  87. thePath.MakeLower();
  88. CAutoLocker lock(m_critIndexSec);
  89. std::vector<CString> toRemove;
  90. for (auto it = this->cbegin(); it != this->cend(); ++it)
  91. {
  92. if (CStringUtils::StartsWith((*it).first, thePath))
  93. toRemove.push_back((*it).first);
  94. }
  95. for (auto it = toRemove.cbegin(); it != toRemove.cend(); ++it)
  96. this->erase(*it);
  97. return !toRemove.empty();
  98. }
  99. int Check(const CString &gitdir, bool *isChanged);
  100. int LoadIndex(const CString &gitdir);
  101. bool CheckAndUpdate(const CString &gitdir,bool isLoadUpdatedIndex)
  102. {
  103. bool isChanged=false;
  104. if(isLoadUpdatedIndex && Check(gitdir,&isChanged))
  105. return false;
  106. if(isChanged && isLoadUpdatedIndex)
  107. {
  108. LoadIndex(gitdir);
  109. return true;
  110. }
  111. return false;
  112. }
  113. int GetFileStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,
  114. BOOL IsFull=false, BOOL IsRecursive=false,
  115. FILL_STATUS_CALLBACK callback = nullptr,
  116. void* pData = nullptr, CGitHash* pHash = nullptr,
  117. bool isLoadUpdatedIndex = true, bool* assumeValid = nullptr, bool* skipWorktree = nullptr);
  118. int IsUnderVersionControl(const CString &gitdir,
  119. CString path,
  120. bool isDir,
  121. bool *isVersion,
  122. bool isLoadUpdateIndex=true);
  123. };
  124. class CGitTreeItem
  125. {
  126. public:
  127. CString m_FileName;
  128. CGitHash m_Hash;
  129. int m_Flags;
  130. };
  131. /* After object create, never change field agains
  132. * that needn't lock to get field
  133. */
  134. class CGitHeadFileList:public std::vector<CGitTreeItem>
  135. {
  136. private:
  137. int GetPackRef(const CString &gitdir);
  138. CReaderWriterLock m_SharedMutex;
  139. __time64_t m_LastModifyTimeHead;
  140. __time64_t m_LastModifyTimeRef;
  141. __time64_t m_LastModifyTimePackRef;
  142. CString m_HeadRefFile;
  143. CGitHash m_Head;
  144. CString m_HeadFile;
  145. CString m_Gitdir;
  146. CString m_PackRefFile;
  147. CGitHash m_TreeHash; /* buffered tree hash value */
  148. std::map<CString,CGitHash> m_PackRefMap;
  149. public:
  150. CGitHeadFileList()
  151. {
  152. m_LastModifyTimeHead=0;
  153. m_LastModifyTimeRef=0;
  154. m_LastModifyTimePackRef = 0;
  155. }
  156. int ReadTree();
  157. int ReadHeadHash(const CString& gitdir);
  158. bool CheckHeadUpdate();
  159. bool HeadHashEqualsTreeHash();
  160. bool HeadFileIsEmpty();
  161. bool HeadIsEmpty();
  162. static int CallBack(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);
  163. };
  164. typedef std::tr1::shared_ptr<CGitHeadFileList> SHARED_TREE_PTR;
  165. class CGitHeadFileMap:public std::map<CString,SHARED_TREE_PTR>
  166. {
  167. public:
  168. CComCriticalSection m_critTreeSec;
  169. CGitHeadFileMap() { m_critTreeSec.Init(); }
  170. ~CGitHeadFileMap() { m_critTreeSec.Term(); }
  171. SHARED_TREE_PTR SafeGet(CString thePath, bool allowEmpty = false)
  172. {
  173. thePath.MakeLower();
  174. CAutoLocker lock(m_critTreeSec);
  175. auto lookup = find(thePath);
  176. if (lookup == cend())
  177. {
  178. if (allowEmpty)
  179. return SHARED_TREE_PTR();
  180. return SHARED_TREE_PTR(new CGitHeadFileList);
  181. }
  182. return lookup->second;
  183. }
  184. void SafeSet(CString thePath, SHARED_TREE_PTR ptr)
  185. {
  186. thePath.MakeLower();
  187. CAutoLocker lock(m_critTreeSec);
  188. (*this)[thePath] = ptr;
  189. }
  190. bool SafeClear(CString thePath)
  191. {
  192. thePath.MakeLower();
  193. CAutoLocker lock(m_critTreeSec);
  194. auto lookup = find(thePath);
  195. if (lookup == cend())
  196. return false;
  197. erase(lookup);
  198. return true;
  199. }
  200. bool SafeClearRecursively(CString thePath)
  201. {
  202. thePath.MakeLower();
  203. CAutoLocker lock(m_critTreeSec);
  204. std::vector<CString> toRemove;
  205. for (auto it = this->cbegin(); it != this->cend(); ++it)
  206. {
  207. if (CStringUtils::StartsWith((*it).first, thePath))
  208. toRemove.push_back((*it).first);
  209. }
  210. for (auto it = toRemove.cbegin(); it != toRemove.cend(); ++it)
  211. this->erase(*it);
  212. return !toRemove.empty();
  213. }
  214. int GetFileStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,BOOL IsFull=false, BOOL IsRecursive=false,
  215. FILL_STATUS_CALLBACK callback = nullptr, void *pData = nullptr,
  216. bool isLoaded=false);
  217. bool CheckHeadAndUpdate(const CString &gitdir, bool readTree = true);
  218. int IsUnderVersionControl(const CString& gitdir, CString path, bool isDir, bool* isVersion);
  219. };
  220. class CGitFileName
  221. {
  222. public:
  223. CGitFileName() {}
  224. CGitFileName(const CString& filename)
  225. {
  226. m_CaseFileName = filename;
  227. m_FileName = filename;
  228. m_FileName.MakeLower();
  229. }
  230. CString m_FileName;
  231. CString m_CaseFileName;
  232. };
  233. static bool SortCGitFileName(const CGitFileName& item1, const CGitFileName& item2)
  234. {
  235. return item1.m_FileName.Compare(item2.m_FileName) < 0;
  236. }
  237. class CGitIgnoreItem
  238. {
  239. public:
  240. CGitIgnoreItem()
  241. {
  242. m_LastModifyTime =0;
  243. m_pExcludeList = nullptr;
  244. m_buffer = nullptr;
  245. }
  246. ~CGitIgnoreItem()
  247. {
  248. if(m_pExcludeList)
  249. git_free_exclude_list(m_pExcludeList);
  250. free(m_buffer);
  251. m_pExcludeList= nullptr;
  252. m_buffer = nullptr;
  253. }
  254. __time64_t m_LastModifyTime;
  255. CStringA m_BaseDir;
  256. BYTE *m_buffer;
  257. EXCLUDE_LIST m_pExcludeList;
  258. int FetchIgnoreList(const CString &projectroot, const CString &file, bool isGlobal);
  259. /**
  260. * patha: the filename to be checked whether is is ignored or not
  261. * base: must be a pointer to the beginning of the base filename WITHIN patha
  262. * type: DT_DIR or DT_REG
  263. */
  264. int IsPathIgnored(const CStringA& patha, const char* base, int& type);
  265. #ifdef GTEST_INCLUDE_GTEST_GTEST_H_
  266. int IsPathIgnored(const CStringA& patha, int& type);
  267. #endif
  268. };
  269. class CGitIgnoreList
  270. {
  271. private:
  272. bool CheckFileChanged(const CString &path);
  273. int FetchIgnoreFile(const CString &gitdir, const CString &gitignore, bool isGlobal);
  274. int CheckIgnore(const CString &path, const CString &root, bool isDir);
  275. int CheckFileAgainstIgnoreList(const CString &ignorefile, const CStringA &patha, const char * base, int &type);
  276. // core.excludesfile stuff
  277. std::map<CString, CString> m_CoreExcludesfiles;
  278. CString m_sGitSystemConfigPath;
  279. CString m_sGitProgramDataConfigPath;
  280. ULONGLONG m_dGitSystemConfigPathLastChecked;
  281. CReaderWriterLock m_coreExcludefilesSharedMutex;
  282. // checks if the msysgit path has changed and return true/false
  283. // if the path changed, the cache is update
  284. // force is only ised in constructor
  285. bool CheckAndUpdateGitSystemConfigPath(bool force = true);
  286. bool CheckAndUpdateCoreExcludefile(const CString &adminDir);
  287. const CString GetWindowsHome();
  288. public:
  289. CReaderWriterLock m_SharedMutex;
  290. CGitIgnoreList(){ CheckAndUpdateGitSystemConfigPath(true); }
  291. std::map<CString, CGitIgnoreItem> m_Map;
  292. bool CheckIgnoreChanged(const CString &gitdir,const CString &path, bool isDir);
  293. int LoadAllIgnoreFile(const CString &gitdir, const CString &path, bool isDir);
  294. bool IsIgnore(CString path, const CString& root, bool isDir);
  295. };
  296. template<class T>
  297. int GetRangeInSortVector(const T &vector, LPCTSTR pstr, int len, int *start, int *end, int pos)
  298. {
  299. if( pos < 0)
  300. return -1;
  301. if (start == 0 || !end)
  302. return -1;
  303. *start=*end=-1;
  304. if (vector.empty())
  305. return -1;
  306. if (pos >= (int)vector.size())
  307. return -1;
  308. if( _tcsnccmp(vector[pos].m_FileName, pstr,len) != 0)
  309. return -1;
  310. *start = 0;
  311. *end = (int)vector.size() - 1;
  312. // shortcut, if all entries are going match
  313. if (!len)
  314. return 0;
  315. for (int i = pos; i < (int)vector.size(); ++i)
  316. {
  317. if (_tcsnccmp(vector[i].m_FileName, pstr, len) != 0)
  318. break;
  319. *end = i;
  320. }
  321. for (int i = pos; i >= 0; --i)
  322. {
  323. if (_tcsnccmp(vector[i].m_FileName, pstr, len) != 0)
  324. break;
  325. *start = i;
  326. }
  327. return 0;
  328. }
  329. template<class T>
  330. int SearchInSortVector(const T &vector, LPCTSTR pstr, int len)
  331. {
  332. int end = (int)vector.size() - 1;
  333. int start = 0;
  334. int mid = (start+end)/2;
  335. if (vector.empty())
  336. return -1;
  337. while(!( start == end && start==mid))
  338. {
  339. int cmp;
  340. if(len < 0)
  341. cmp = _tcscmp(vector[mid].m_FileName,pstr);
  342. else
  343. cmp = _tcsnccmp( vector[mid].m_FileName,pstr,len );
  344. if (cmp == 0)
  345. return mid;
  346. else if (cmp < 0)
  347. start = mid + 1;
  348. else // (cmp > 0)
  349. end = mid;
  350. mid=(start +end ) /2;
  351. }
  352. if(len <0)
  353. {
  354. if(_tcscmp(vector[mid].m_FileName,pstr) == 0)
  355. return mid;
  356. }
  357. else
  358. {
  359. if(_tcsnccmp( vector[mid].m_FileName,pstr,len ) == 0)
  360. return mid;
  361. }
  362. return -1;
  363. };
  364. class CGitAdminDirMap:public std::map<CString, CString>
  365. {
  366. public:
  367. CComCriticalSection m_critIndexSec;
  368. std::map<CString, CString> m_reverseLookup;
  369. CGitAdminDirMap() { m_critIndexSec.Init(); }
  370. ~CGitAdminDirMap() { m_critIndexSec.Term(); }
  371. CString GetAdminDir(const CString &path)
  372. {
  373. CString thePath(path);
  374. thePath.MakeLower();
  375. CAutoLocker lock(m_critIndexSec);
  376. auto lookup = find(thePath);
  377. if (lookup == cend())
  378. {
  379. if (PathIsDirectory(path + _T("\\.git")))
  380. {
  381. (*this)[thePath] = path + _T("\\.git\\");
  382. m_reverseLookup[thePath + _T("\\.git")] = path;
  383. return (*this)[thePath];
  384. }
  385. CString result = GitAdminDir::ReadGitLink(path, path + _T("\\.git"));
  386. if (!result.IsEmpty())
  387. {
  388. (*this)[thePath] = result + _T("\\");
  389. m_reverseLookup[result.MakeLower()] = path;
  390. return (*this)[thePath];
  391. }
  392. return path + _T("\\.git\\"); // in case of an error stick to old behavior
  393. }
  394. return lookup->second;
  395. }
  396. CString GetAdminDirConcat(const CString& path, const CString& subpath)
  397. {
  398. CString result(GetAdminDir(path));
  399. result += subpath;
  400. return result;
  401. }
  402. CString GetWorkingCopy(const CString &gitDir)
  403. {
  404. CString path(gitDir);
  405. path.MakeLower();
  406. CAutoLocker lock(m_critIndexSec);
  407. auto lookup = m_reverseLookup.find(path);
  408. if (lookup == m_reverseLookup.cend())
  409. return gitDir;
  410. return lookup->second;
  411. }
  412. };