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.
22 #include "gitstatus.h"
23 #include "SharedMutex.h"
25 /* Copy from Git cache.h*/
31 #define CACHE_SIGNATURE 0x44495243 /* "DIRC" */
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.
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
;
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
75 struct ondisk_cache_entry_extended
{
76 struct cache_time ctime
;
77 struct cache_time mtime
;
87 char name
[FLEX_ARRAY
]; /* more */
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
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"
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)
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
];
153 static inline size_t ce_namelen(T
*ce
)
155 size_t len
= Big2lit(ce
->flags
) & CE_NAMEMASK
;
156 if (len
< CE_NAMEMASK
)
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)))
171 static inline size_t ondisk_ce_size(T
*ce
)
173 return flexible_size(T
,ce_namelen(ce
));
180 __time64_t m_ModifyTime
;
183 CGitHash m_IndexHash
;
185 int FillData(ondisk_cache_entry
* entry
);
186 int FillData(ondisk_cache_entry_extended
* entry
);
195 CAutoReadLock(SharedMutex
* lock
)
198 lock
->AcquireShared();
202 m_Lock
->ReleaseShared();
210 CAutoWriteLock(SharedMutex
* lock
)
213 lock
->AcquireExclusive();
217 m_Lock
->ReleaseExclusive();
221 class CGitIndexList
:public std::vector
<CGitIndex
>
226 __time64_t m_LastModifyTime
;
232 //TRACE(_T("Free Index List 0x%x %s"),this, m_GitFile);
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
);
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
>
251 CComCriticalSection m_critIndexSec
;
253 CGitIndexFileMap() { m_critIndexSec
.Init(); }
254 ~CGitIndexFileMap() { m_critIndexSec
.Term(); }
256 SHARED_INDEX_PTR
SafeGet(const CString
&path
)
258 CString thePath
= path
;
260 CAutoLocker
lock(m_critIndexSec
);
261 if(this->find(thePath
) == end())
262 return SHARED_INDEX_PTR();
264 return (*this)[thePath
];
267 void SafeSet(const CString
&path
, SHARED_INDEX_PTR ptr
)
269 CString thePath
= path
;
271 CAutoLocker
lock(m_critIndexSec
);
272 (*this)[thePath
] = ptr
;
275 int Check(const CString
&gitdir
, bool *isChanged
);
276 int LoadIndex(const CString
&gitdir
);
278 bool CheckAndUpdate(const CString
&gitdir
,bool isLoadUpdatedIndex
)
280 bool isChanged
=false;
281 if(isLoadUpdatedIndex
&& Check(gitdir
,&isChanged
))
284 if(isChanged
&& isLoadUpdatedIndex
)
292 int GetFileStatus(const CString
&gitdir
,const CString
&path
,git_wc_status_kind
* status
,
293 BOOL IsFull
=false, BOOL IsRecursive
=false,
294 FIll_STATUS_CALLBACK callback
=NULL
,
295 void *pData
=NULL
,CGitHash
*pHash
=NULL
,
296 bool isLoadUpdatedIndex
=true);
298 int IsUnderVersionControl(const CString
&gitdir
,
302 bool isLoadUpdateIndex
=true);
314 /* After object create, never change field agains
315 * that needn't lock to get field
317 class CGitHeadFileList
:public std::vector
<CGitTreeItem
>
321 int GetPackRef(const CString
&gitdir
);
324 __time64_t m_LastModifyTimeHead
;
325 __time64_t m_LastModifyTimeRef
;
326 __time64_t m_LastModifyTimePackRef
;
328 CString m_HeadRefFile
;
332 CString m_PackRefFile
;
334 CGitHash m_TreeHash
; /* buffered tree hash value */
336 std::map
<CString
,CGitHash
> m_PackRefMap
;
340 m_LastModifyTimeHead
=0;
341 m_LastModifyTimeRef
=0;
342 m_LastModifyTimePackRef
= 0;
349 //TRACE(_T("Free Index List 0x%x %s"),this, m_GitFile);
354 int ReadHeadHash(CString gitdir
);
355 bool CheckHeadUpdate();
356 static int CallBack(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);
360 typedef std::tr1::shared_ptr
<CGitHeadFileList
> SHARED_TREE_PTR
;
361 class CGitHeadFileMap
:public std::map
<CString
,SHARED_TREE_PTR
>
365 CComCriticalSection m_critTreeSec
;
367 CGitHeadFileMap() { m_critTreeSec
.Init(); }
368 ~CGitHeadFileMap() { m_critTreeSec
.Term(); }
370 SHARED_TREE_PTR
SafeGet(const CString
&path
)
372 CString thePath
= path
;
374 CAutoLocker
lock(m_critTreeSec
);
375 if(this->find(thePath
) == end())
376 return SHARED_TREE_PTR();
378 return (*this)[thePath
];
381 void SafeSet(const CString
&path
, SHARED_TREE_PTR ptr
)
383 CString thePath
= path
;
385 CAutoLocker
lock(m_critTreeSec
);
386 (*this)[thePath
] = ptr
;
389 int GetFileStatus(const CString
&gitdir
,const CString
&path
,git_wc_status_kind
* status
,BOOL IsFull
=false, BOOL IsRecursive
=false,
390 FIll_STATUS_CALLBACK callback
=NULL
,void *pData
=NULL
,
391 bool isLoaded
=false);
392 bool CheckHeadUpdate(const CString
&gitdir
);
393 int GetHeadHash(const CString
&gitdir
, CGitHash
&hash
);
396 bool IsHashChanged(const CString
&gitdir
)
398 SHARED_TREE_PTR ptr
= SafeGet(gitdir
);
400 if( ptr
.get() == NULL
)
403 return ptr
->m_Head
!= ptr
->m_TreeHash
;
412 CString m_CaseFileName
;
418 SharedMutex m_SharedMutex
;
423 m_pExcludeList
=NULL
;
428 git_free_exclude_list(m_pExcludeList
);
431 __time64_t m_LastModifyTime
;
433 EXCLUDE_LIST m_pExcludeList
;
434 int FetchIgnoreList(const CString
&projectroot
, const CString
&file
);
440 bool CheckFileChanged(const CString
&path
);
441 int FetchIgnoreFile(const CString
&gitdir
, const CString
&gitignore
);
443 int CheckIgnore(const CString
&path
,const CString
&root
);
446 SharedMutex m_SharedMutex
;
448 CGitIgnoreList(){ m_SharedMutex
.Init(); }
449 ~CGitIgnoreList() { m_SharedMutex
.Release(); }
451 std::map
<CString
, CGitIgnoreItem
> m_Map
;
453 int GetIgnoreFileChangeTimeList(const CString
&dir
, std::vector
<__int64
> &timelist
);
454 bool CheckIgnoreChanged(const CString
&gitdir
,const CString
&path
);
455 int LoadAllIgnoreFile(const CString
&gitdir
,const CString
&path
);
456 bool IsIgnore(const CString
&path
,const CString
&root
);
460 int GetRangeInSortVector(T
&vector
,LPTSTR pstr
,int len
, int *start
, int *end
, int pos
)
466 if(start
== 0 || end
== NULL
)
471 if(vector
.size() ==0)
474 if(pos
>= vector
.size())
477 if( _tcsnccmp(vector
[pos
].m_FileName
, pstr
,len
) != 0)
479 for(int i
=0;i
< vector
.size();i
++)
481 if( _tcsnccmp(vector
[i
].m_FileName
, pstr
,len
) == 0 )
493 *end
= vector
.size();
495 for(int i
=pos
;i
<vector
.size();i
++)
497 if( _tcsnccmp(vector
[i
].m_FileName
, pstr
,len
) == 0 )
506 for(int i
=pos
;i
>=0;i
--)
508 if( _tcsnccmp(vector
[i
].m_FileName
, pstr
,len
) == 0 )
522 int SearchInSortVector(T
&vector
, LPTSTR pstr
, int len
)
524 int end
=vector
.size()-1;
526 int mid
= (start
+end
)/2;
528 if(vector
.size() == 0)
531 while(!( start
== end
&& start
==mid
))
535 cmp
= _tcscmp(vector
[mid
].m_FileName
,pstr
);
537 cmp
= _tcsnccmp( vector
[mid
].m_FileName
,pstr
,len
);
551 mid
=(start
+end
) /2;
556 if(_tcscmp(vector
[mid
].m_FileName
,pstr
) == 0)
561 if(_tcsnccmp( vector
[mid
].m_FileName
,pstr
,len
) == 0)
571 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
);
573 CGitIgnoreList m_IgnoreList
;
574 CGitHeadFileMap m_HeadFilesMap
;
575 CGitIndexFileMap m_IndexFilesMap
;
577 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
);