Fix TGitStatus wrong when filenameA is substring of filenameB
[TortoiseGit.git] / src / Git / gitindex.h
blob5f1d698c14532076cb7fc422d0c6cc5583548624
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2011 - 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 /* Copy from Git cache.h*/
26 #define FLEX_ARRAY 4
28 #pragma pack(push)
29 #pragma pack(1)
30 //#pragma pack(show)
31 #define CACHE_SIGNATURE 0x44495243 /* "DIRC" */
32 struct cache_header {
33 unsigned int hdr_signature;
34 unsigned int hdr_version;
35 unsigned int hdr_entries;
39 * The "cache_time" is just the low 32 bits of the
40 * time. It doesn't matter if it overflows - we only
41 * check it for equality in the 32 bits we save.
43 struct cache_time {
44 UINT32 sec;
45 UINT32 nsec;
49 * dev/ino/uid/gid/size are also just tracked to the low 32 bits
50 * Again - this is just a (very strong in practice) heuristic that
51 * the inode hasn't changed.
53 * We save the fields in big-endian order to allow using the
54 * index file over NFS transparently.
56 struct ondisk_cache_entry {
57 struct cache_time ctime;
58 struct cache_time mtime;
59 UINT32 dev;
60 UINT32 ino;
61 UINT32 mode;
62 UINT32 uid;
63 UINT32 gid;
64 UINT32 size;
65 BYTE sha1[20];
66 UINT16 flags;
67 char name[FLEX_ARRAY]; /* more */
71 * This struct is used when CE_EXTENDED bit is 1
72 * The struct must match ondisk_cache_entry exactly from
73 * ctime till flags
75 struct ondisk_cache_entry_extended {
76 struct cache_time ctime;
77 struct cache_time mtime;
78 UINT32 dev;
79 UINT32 ino;
80 UINT32 mode;
81 UINT32 uid;
82 UINT32 gid;
83 UINT32 size;
84 BYTE sha1[20];
85 UINT16 flags;
86 UINT16 flags2;
87 char name[FLEX_ARRAY]; /* more */
90 #pragma pack(pop)
92 #define CE_NAMEMASK (0x0fff)
93 #define CE_STAGEMASK (0x3000)
94 #define CE_EXTENDED (0x4000)
95 #define CE_VALID (0x8000)
96 #define CE_STAGESHIFT 12
98 * Range 0xFFFF0000 in ce_flags is divided into
99 * two parts: in-memory flags and on-disk ones.
100 * Flags in CE_EXTENDED_FLAGS will get saved on-disk
101 * if you want to save a new flag, add it in
102 * CE_EXTENDED_FLAGS
104 * In-memory only flags
106 #define CE_UPDATE (0x10000)
107 #define CE_REMOVE (0x20000)
108 #define CE_UPTODATE (0x40000)
109 #define CE_ADDED (0x80000)
111 #define CE_HASHED (0x100000)
112 #define CE_UNHASHED (0x200000)
115 * Extended on-disk flags
117 #define CE_INTENT_TO_ADD 0x20000000
118 /* CE_EXTENDED2 is for future extension */
119 #define CE_EXTENDED2 0x80000000
121 #define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)
124 * Safeguard to avoid saving wrong flags:
125 * - CE_EXTENDED2 won't get saved until its semantic is known
126 * - Bits in 0x0000FFFF have been saved in ce_flags already
127 * - Bits in 0x003F0000 are currently in-memory flags
129 #if CE_EXTENDED_FLAGS & 0x803FFFFF
130 #error "CE_EXTENDED_FLAGS out of range"
131 #endif
134 * Copy the sha1 and stat state of a cache entry from one to
135 * another. But we never change the name, or the hash state!
137 #define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
139 template<class T>
140 T Big2lit(T data)
142 T ret;
143 BYTE *p1=(BYTE*)&data;
144 BYTE *p2=(BYTE*)&ret;
145 for(int i=0;i<sizeof(T);i++)
147 p2[sizeof(T)-i-1] = p1[i];
149 return ret;
152 template<class T>
153 static inline size_t ce_namelen(T *ce)
155 size_t len = Big2lit(ce->flags) & CE_NAMEMASK;
156 if (len < CE_NAMEMASK)
157 return len;
158 return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK;
161 #define flexible_size(STRUCT,len) ((offsetof(STRUCT,name) + (len) + 8) & ~7)
163 //#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
164 //#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
166 //#define ondisk_ce_size(ce) (((ce)->flags & CE_EXTENDED) ? \
167 // ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
168 // ondisk_cache_entry_size(ce_namelen(ce)))
170 template<class T>
171 static inline size_t ondisk_ce_size(T *ce)
173 return flexible_size(T,ce_namelen(ce));
176 class CGitIndex
178 public:
179 CString m_FileName;
180 __time64_t m_ModifyTime;
181 int m_Flags;
182 //int m_Status;
183 CGitHash m_IndexHash;
185 int FillData(ondisk_cache_entry* entry);
186 int FillData(ondisk_cache_entry_extended* entry);
187 int Print();
191 class CAutoReadLock
193 SharedMutex *m_Lock;
194 public:
195 CAutoReadLock(SharedMutex * lock)
197 m_Lock = lock;
198 lock->AcquireShared();
200 ~CAutoReadLock()
202 m_Lock->ReleaseShared();
206 class CAutoWriteLock
208 SharedMutex *m_Lock;
209 public:
210 CAutoWriteLock(SharedMutex * lock)
212 m_Lock = lock;
213 lock->AcquireExclusive();
215 ~CAutoWriteLock()
217 m_Lock->ReleaseExclusive();
221 class CGitIndexList:public std::vector<CGitIndex>
223 protected:
225 public:
226 __time64_t m_LastModifyTime;
228 #ifdef DEBUG
229 CString m_GitFile;
230 ~CGitIndexList()
232 //TRACE(_T("Free Index List 0x%x %s"),this, m_GitFile);
234 #endif
236 CGitIndexList();
238 int ReadIndex(CString file);
239 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);
240 protected:
241 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);
242 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);
245 typedef std::tr1::shared_ptr<CGitIndexList> SHARED_INDEX_PTR;
246 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
248 class CGitIndexFileMap:public std::map<CString, SHARED_INDEX_PTR>
250 public:
251 CComCriticalSection m_critIndexSec;
253 CGitIndexFileMap() { m_critIndexSec.Init(); }
254 ~CGitIndexFileMap() { m_critIndexSec.Term(); }
256 SHARED_INDEX_PTR SafeGet(const CString &path)
258 CAutoLocker lock(m_critIndexSec);
259 if(this->find(path) == end())
260 return SHARED_INDEX_PTR();
261 else
262 return (*this)[path];
265 void SafeSet(const CString &path, SHARED_INDEX_PTR ptr)
267 CAutoLocker lock(m_critIndexSec);
268 (*this)[path] = ptr;
271 int Check(const CString &gitdir, bool *isChanged);
272 int LoadIndex(const CString &gitdir);
274 bool CheckAndUpdate(const CString &gitdir,bool isLoadUpdatedIndex)
276 bool isChanged=false;
277 if(isLoadUpdatedIndex && Check(gitdir,&isChanged))
278 return false;
280 if(isChanged && isLoadUpdatedIndex)
282 LoadIndex(gitdir);
283 return true;
286 return false;
288 int GetFileStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,
289 BOOL IsFull=false, BOOL IsRecursive=false,
290 FIll_STATUS_CALLBACK callback=NULL,
291 void *pData=NULL,CGitHash *pHash=NULL,
292 bool isLoadUpdatedIndex=true);
294 int IsUnderVersionControl(const CString &gitdir,
295 const CString &path,
296 bool isDir,
297 bool *isVersion,
298 bool isLoadUpdateIndex=true);
302 class CGitTreeItem
304 public:
305 CString m_FileName;
306 CGitHash m_Hash;
307 int m_Flags;
310 /* After object create, never change field agains
311 * that needn't lock to get field
313 class CGitHeadFileList:public std::vector<CGitTreeItem>
315 private:
317 int GetPackRef(const CString &gitdir);
319 public:
320 __time64_t m_LastModifyTimeHead;
321 __time64_t m_LastModifyTimeRef;
322 __time64_t m_LastModifyTimePackRef;
324 CString m_HeadRefFile;
325 CGitHash m_Head;
326 CString m_HeadFile;
327 CString m_Gitdir;
328 CString m_PackRefFile;
330 CGitHash m_TreeHash; /* buffered tree hash value */
332 std::map<CString,CGitHash> m_PackRefMap;
334 CGitHeadFileList()
336 m_LastModifyTimeHead=0;
337 m_LastModifyTimeRef=0;
338 m_LastModifyTimePackRef = 0;
341 #ifdef DEBUG
342 CString m_GitFile;
343 ~CGitHeadFileList()
345 //TRACE(_T("Free Index List 0x%x %s"),this, m_GitFile);
347 #endif
349 int ReadTree();
350 int ReadHeadHash(CString gitdir);
351 bool CheckHeadUpdate();
352 static int CallBack(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);
353 //int ReadTree();
356 typedef std::tr1::shared_ptr<CGitHeadFileList> SHARED_TREE_PTR;
357 class CGitHeadFileMap:public std::map<CString,SHARED_TREE_PTR>
359 public:
361 CComCriticalSection m_critTreeSec;
363 CGitHeadFileMap() { m_critTreeSec.Init(); }
364 ~CGitHeadFileMap() { m_critTreeSec.Term(); }
366 SHARED_TREE_PTR SafeGet(const CString &path)
368 CAutoLocker lock(m_critTreeSec);
369 if(this->find(path) == end())
370 return SHARED_TREE_PTR();
371 else
372 return (*this)[path];
375 void SafeSet(const CString &path, SHARED_TREE_PTR ptr)
377 CAutoLocker lock(m_critTreeSec);
378 (*this)[path] = ptr;
381 int GetFileStatus(const CString &gitdir,const CString &path,git_wc_status_kind * status,BOOL IsFull=false, BOOL IsRecursive=false,
382 FIll_STATUS_CALLBACK callback=NULL,void *pData=NULL,
383 bool isLoaded=false);
384 int CheckHeadUpdate(const CString &gitdir);
385 int GetHeadHash(const CString &gitdir, CGitHash &hash);
388 bool IsHashChanged(const CString &gitdir)
390 SHARED_TREE_PTR ptr = SafeGet(gitdir);
392 if( ptr.get() == NULL)
393 return false;
395 return ptr->m_Head != ptr->m_TreeHash;
402 class CGitIgnoreItem
404 public:
405 SharedMutex m_SharedMutex;
407 CGitIgnoreItem()
409 m_LastModifyTime =0;
410 m_pExcludeList =NULL;
412 ~CGitIgnoreItem()
414 if(m_pExcludeList)
415 git_free_exclude_list(m_pExcludeList);
416 m_pExcludeList=NULL;
418 __time64_t m_LastModifyTime;
419 CStringA m_BaseDir;
420 EXCLUDE_LIST m_pExcludeList;
421 int FetchIgnoreList(const CString &projectroot, const CString &file);
424 class CGitIgnoreList
426 private:
427 bool CheckFileChanged(const CString &path);
428 int FetchIgnoreFile(const CString &gitdir, const CString &gitignore);
430 int CheckIgnore(const CString &path,const CString &root);
432 public:
433 SharedMutex m_SharedMutex;
435 CGitIgnoreList(){ m_SharedMutex.Init(); }
436 ~CGitIgnoreList() { m_SharedMutex.Release(); }
438 std::map<CString, CGitIgnoreItem> m_Map;
440 int GetIgnoreFileChangeTimeList(const CString &dir, std::vector<__int64> &timelist);
441 bool CheckIgnoreChanged(const CString &gitdir,const CString &path);
442 int LoadAllIgnoreFile(const CString &gitdir,const CString &path);
443 bool IsIgnore(const CString &path,const CString &root);
446 template<class T>
447 int GetRangeInSortVector(T &vector,LPTSTR pstr,int len, int *start, int *end, int pos)
449 if( pos < 0)
451 return -1;
453 if(start == 0 || end == NULL)
454 return -1;
456 *start=*end=-1;
457 if( _tcsnccmp(vector[pos].m_FileName, pstr,len) != 0)
459 for(int i=0;i< vector.size();i++)
461 if( _tcsnccmp(vector[i].m_FileName, pstr,len) == 0 )
463 if(*start<0)
464 *start =i;
465 *end =i;
468 return -1;
469 }else
471 *start =0;
472 *end = vector.size();
474 for(int i=pos;i<vector.size();i++)
476 if( _tcsnccmp(vector[i].m_FileName, pstr,len) == 0 )
478 *end=i;
479 }else
481 break;
484 for(int i=pos;i>=0;i--)
486 if( _tcsnccmp(vector[i].m_FileName, pstr,len) == 0 )
488 *start=i;
489 }else
491 break;
495 return 0;
498 template<class T>
499 int SearchInSortVector(T &vector, LPTSTR pstr, int len)
501 int end=vector.size()-1;
502 int start = 0;
503 int mid = (start+end)/2;
505 if(vector.size() == 0)
506 return -1;
508 while(!( start == end && start==mid))
510 int cmp;
511 if(len < 0)
512 cmp = _tcscmp(vector[mid].m_FileName,pstr);
513 else
514 cmp = _tcsnccmp( vector[mid].m_FileName,pstr,len );
516 if(cmp ==0)
517 return mid;
519 if(cmp < 0)
521 start = mid+1;
524 if(cmp > 0)
526 end=mid;
528 mid=(start +end ) /2;
531 if(len <0)
533 if(_tcscmp(vector[mid].m_FileName,pstr) == 0)
534 return mid;
536 else
538 if(_tcsnccmp( vector[mid].m_FileName,pstr,len ) == 0)
539 return mid;
541 return -1;
543 #if 0
545 class CGitStatus
547 protected:
548 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);
549 public:
550 CGitIgnoreList m_IgnoreList;
551 CGitHeadFileMap m_HeadFilesMap;
552 CGitIndexFileMap m_IndexFilesMap;
554 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);
557 #endif