8 #include "UnicodeUtils.h"
11 #include <sys/types.h>
14 typedef CComCritSecLock
<CComCriticalSection
> CAutoLocker
;
18 g_Git.StringAppend(&m_FileName,(BYTE*)entry->name,CP_ACP,Big2lit(entry->flags)&CE_NAMEMASK);\
19 this->m_Flags=Big2lit(entry->flags);\
20 this->m_ModifyTime=Big2lit(entry->mtime.sec);\
21 this->m_IndexHash=(char*)(entry->sha1);
23 int CGitIndex::FillData(ondisk_cache_entry
* entry
)
29 int CGitIndex::FillData(ondisk_cache_entry_extended
* entry
)
32 this->m_Flags
|= ((int)Big2lit(entry
->flags2
))<<16;
36 int CGitIndex::Print()
38 _tprintf(_T("0x%08X 0x%08X %s %s\n"),
39 (int)this->m_ModifyTime
,
41 this->m_IndexHash
.ToString(),
47 CGitIndexList::CGitIndexList()
49 this->m_LastModifyTime
=0;
52 int CGitIndexList::ReadIndex(CString IndexFile
)
54 HANDLE hfile
=INVALID_HANDLE_VALUE
;
66 hfile
= CreateFile(IndexFile
,
68 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
71 FILE_ATTRIBUTE_NORMAL
,
75 if(hfile
== INVALID_HANDLE_VALUE
)
82 DWORD size
=0,filesize
=0;
84 filesize
=GetFileSize(hfile
,NULL
);
86 if(filesize
== INVALID_FILE_SIZE
)
92 buffer
= new BYTE
[filesize
];
99 if(! ReadFile( hfile
, buffer
,filesize
,&size
,NULL
) )
101 ret
= GetLastError();
106 hfile
= INVALID_HANDLE_VALUE
; /* close early to above lock time long*/
108 if (size
!= filesize
)
113 header
= (cache_header
*) buffer
;
114 if( Big2lit(header
->hdr_signature
) != CACHE_SIGNATURE
)
119 p
+= sizeof(cache_header
);
121 int entries
= Big2lit(header
->hdr_entries
);
122 for(int i
=0;i
<entries
;i
++)
124 ondisk_cache_entry
*entry
;
125 ondisk_cache_entry_extended
*entryex
;
126 entry
=(ondisk_cache_entry
*)p
;
127 entryex
=(ondisk_cache_entry_extended
*)p
;
128 int flags
=Big2lit(entry
->flags
);
129 if( flags
& CE_EXTENDED
)
131 GitIndex
.FillData(entryex
);
132 p
+=ondisk_ce_size(entryex
);
136 GitIndex
.FillData(entry
);
137 p
+=ondisk_ce_size(entry
);
140 if(p
>buffer
+filesize
)
145 this->push_back(GitIndex
);
146 this->m_Map
[GitIndex
.m_FileName
]=this->size()-1;
155 if(hfile
!= INVALID_HANDLE_VALUE
)
162 int CGitIndexList::GetFileStatus(CString
&gitdir
,CString
&path
,git_wc_status_kind
*status
,__int64 time
,FIll_STATUS_CALLBACK callback
,void *pData
, CGitHash
*pHash
)
167 if(m_Map
.find(path
) == m_Map
.end() )
169 *status
= git_wc_status_unversioned
;
176 int index
= m_Map
[path
];
182 if( time
== at(index
).m_ModifyTime
)
184 *status
= git_wc_status_normal
;
187 *status
= git_wc_status_modified
;
190 if(at(index
).m_Flags
& CE_STAGEMASK
)
191 *status
= git_wc_status_conflicted
;
192 else if(at(index
).m_Flags
& CE_INTENT_TO_ADD
)
193 *status
= git_wc_status_added
;
196 *pHash
= at(index
).m_IndexHash
;
199 callback(gitdir
+_T("\\")+path
,*status
,false, pData
);
204 int CGitIndexList::GetStatus(CString
&gitdir
,CString
&path
, git_wc_status_kind
*status
,
205 BOOL IsFull
, BOOL IsRecursive
,
206 FIll_STATUS_CALLBACK callback
,void *pData
,
210 git_wc_status_kind dirstatus
= git_wc_status_none
;
217 result
= g_Git
.GetFileModifyTime(gitdir
,&time
,&isDir
);
219 result
= g_Git
.GetFileModifyTime( gitdir
+_T("\\")+path
, &time
, &isDir
);
223 *status
= git_wc_status_deleted
;
225 callback(gitdir
+_T("\\")+path
,git_wc_status_deleted
,false, pData
);
233 if( path
.Right(1) != _T("\\"))
236 int len
=path
.GetLength();
238 for(int i
=0;i
<size();i
++)
240 if( at(i
).m_FileName
.GetLength() > len
)
242 if(at(i
).m_FileName
.Left(len
) == path
)
246 *status
= git_wc_status_normal
;
248 callback(gitdir
+_T("\\")+path
,*status
,false, pData
);
253 result
= g_Git
.GetFileModifyTime( gitdir
+_T("\\")+at(i
).m_FileName
, &time
);
257 *status
= git_wc_status_none
;
258 GetFileStatus(gitdir
,at(i
).m_FileName
,status
,time
,callback
,pData
);
259 if( *status
!= git_wc_status_none
)
261 if( dirstatus
== git_wc_status_none
)
263 dirstatus
= git_wc_status_normal
;
265 if( *status
!= git_wc_status_normal
)
267 dirstatus
= git_wc_status_modified
;
276 if( dirstatus
!= git_wc_status_none
)
282 *status
= git_wc_status_unversioned
;
285 callback(gitdir
+_T("\\")+path
,*status
,false, pData
);
291 GetFileStatus(gitdir
,path
,status
,time
,callback
,pData
,pHash
);
297 int CGitIndexFileMap::CheckAndUpdateIndex(CString
&gitdir
,bool *loaded
)
305 IndexFile
=gitdir
+_T("\\.git\\index");
306 /* Get data associated with "crt_stat.c": */
307 result
= g_Git
.GetFileModifyTime( IndexFile
, &time
);
309 // WIN32_FILE_ATTRIBUTE_DATA FileInfo;
310 // GetFileAttributesEx(_T("D:\\tortoisegit\\src\\gpl.txt"),GetFileExInfoStandard,&FileInfo);
311 // result = _tstat64( _T("D:\\tortoisegit\\src\\gpl.txt"), &buf );
319 if((*this)[gitdir
].m_LastModifyTime
!= time
)
321 if((*this)[gitdir
].ReadIndex(IndexFile
))
326 (*this)[gitdir
].m_LastModifyTime
= time
;
335 int CGitIndexFileMap::GetFileStatus(CString
&gitdir
, CString
&path
, git_wc_status_kind
*status
,BOOL IsFull
, BOOL IsRecursive
,
336 FIll_STATUS_CALLBACK callback
,void *pData
,
342 CheckAndUpdateIndex(gitdir
);
343 (*this)[gitdir
].GetStatus(gitdir
,path
,status
,IsFull
,IsRecursive
,callback
,pData
,pHash
);
352 int CGitIndexFileMap::IsUnderVersionControl(CString
&gitdir
, CString
&path
, bool isDir
,bool *isVersion
)
361 CString subpath
=path
;
362 subpath
.Replace(_T('\\'), _T('/'));
366 CheckAndUpdateIndex(gitdir
);
369 *isVersion
= (SearchInSortVector((*this)[gitdir
], subpath
.GetBuffer(), subpath
.GetLength()) >= 0);
373 *isVersion
= ((*this)[gitdir
].m_Map
.find(subpath
) != (*this)[gitdir
].m_Map
.end());
383 int CGitHeadFileList::GetPackRef(CString
&gitdir
)
385 CString PackRef
= gitdir
;
386 PackRef
+= _T("\\.git\\packed-refs");
389 if( g_Git
.GetFileModifyTime(PackRef
, &mtime
))
391 //packed refs is not existed
392 this->m_PackRefFile
.Empty();
393 this->m_PackRefMap
.clear();
396 }else if(mtime
== m_LastModifyTimePackRef
)
402 this->m_PackRefFile
= PackRef
;
403 this->m_LastModifyTimePackRef
= mtime
;
406 this->m_PackRefMap
.clear();
409 HANDLE hfile
= CreateFile(PackRef
,
411 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
414 FILE_ATTRIBUTE_NORMAL
,
418 if(hfile
== INVALID_HANDLE_VALUE
)
424 int filesize
= GetFileSize(hfile
,NULL
);
427 buff
= new char[filesize
];
429 ReadFile(hfile
,buff
,filesize
,&size
,NULL
);
440 for(int i
=0;i
<filesize
;i
++)
446 while(buff
[i
] != '\n')
457 while(buff
[i
] != ' ')
459 hash
.AppendChar(buff
[i
]);
469 while(buff
[i
] != '\n')
471 ref
.AppendChar(buff
[i
]);
481 this->m_PackRefMap
[ref
] = hash
;
491 if( hfile
!= INVALID_HANDLE_VALUE
)
497 int CGitHeadFileList::ReadHeadHash(CString gitdir
)
499 CString HeadFile
= gitdir
;
500 HeadFile
+= _T("\\.git\\HEAD");
503 HANDLE hfile
=INVALID_HANDLE_VALUE
;
504 HANDLE href
= INVALID_HANDLE_VALUE
;
509 m_HeadFile
= HeadFile
;
511 if( g_Git
.GetFileModifyTime(m_HeadFile
,&m_LastModifyTimeHead
))
518 hfile
= CreateFile(HeadFile
,
520 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
523 FILE_ATTRIBUTE_NORMAL
,
526 if(hfile
== INVALID_HANDLE_VALUE
)
532 DWORD size
=0,filesize
=0;
533 unsigned char buffer
[40] ;
534 ReadFile(hfile
,buffer
,4,&size
,NULL
);
541 if(strcmp((const char*)buffer
,"ref:") == 0)
543 filesize
= GetFileSize(hfile
,NULL
);
545 unsigned char *p
= (unsigned char*)malloc(filesize
-4);
547 ReadFile(hfile
,p
,filesize
-4,&size
,NULL
);
549 m_HeadRefFile
.Empty();
550 g_Git
.StringAppend(&this->m_HeadRefFile
,p
,CP_ACP
,filesize
-4);
551 CString ref
= this->m_HeadRefFile
;
554 ref
=ref
.Tokenize(_T("\n"),start
);
556 m_HeadRefFile
=gitdir
+_T("\\.git\\")+m_HeadRefFile
.Trim();
557 m_HeadRefFile
.Replace(_T('/'),_T('\\'));
560 if(g_Git
.GetFileModifyTime(m_HeadRefFile
,&time
,NULL
))
562 if( GetPackRef(gitdir
))
567 if(this->m_PackRefMap
.find(ref
) == m_PackRefMap
.end())
572 this ->m_Head
= m_PackRefMap
[ref
];
577 href
= CreateFile(m_HeadRefFile
,
579 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
582 FILE_ATTRIBUTE_NORMAL
,
585 if(href
== INVALID_HANDLE_VALUE
)
587 if( GetPackRef(gitdir
))
593 if(this->m_PackRefMap
.find(ref
) == m_PackRefMap
.end())
598 this ->m_Head
= m_PackRefMap
[ref
];
602 ReadFile(href
,buffer
,40,&size
,NULL
);
608 this->m_Head
.ConvertFromStrA((char*)buffer
);
610 this->m_LastModifyTimeRef
= time
;
614 ReadFile(hfile
,buffer
+4,40-4,&size
,NULL
);
620 m_HeadRefFile
.Empty();
622 this->m_Head
.ConvertFromStrA((char*)buffer
);
630 if(hfile
!= INVALID_HANDLE_VALUE
)
632 if(href
!= INVALID_HANDLE_VALUE
)
638 bool CGitHeadFileList::CheckHeadUpdate()
640 if(this->m_HeadFile
.IsEmpty())
645 if( g_Git
.GetFileModifyTime(m_HeadFile
,&mtime
))
648 if(mtime
!= this->m_LastModifyTimeHead
)
651 if(!this->m_HeadRefFile
.IsEmpty())
653 if(g_Git
.GetFileModifyTime(m_HeadRefFile
,&mtime
))
656 if(mtime
!= this->m_LastModifyTimeRef
)
660 if(!this->m_PackRefFile
.IsEmpty())
662 if(g_Git
.GetFileModifyTime(m_PackRefFile
,&mtime
))
665 if(mtime
!= this->m_LastModifyTimePackRef
)
671 int CGitHeadFileList::ReadTree()
674 if( this->m_Head
.IsEmpty())
677 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
679 if(m_Gitdir
!= g_Git
.m_CurrentDir
)
681 g_Git
.SetCurrentDir(m_Gitdir
);
682 SetCurrentDirectory(g_Git
.m_CurrentDir
);
689 ret
= git_read_tree(this->m_Head
.m_hash
,CGitHeadFileList::CallBack
,this);
696 int CGitHeadFileList::CallBack(const unsigned char *sha1
, const char *base
, int baselen
,
697 const char *pathname
, unsigned mode
, int stage
, void *context
)
699 #define S_IFGITLINK 0160000
701 CGitHeadFileList
*p
= (CGitHeadFileList
*)context
;
704 if( (mode
&S_IFMT
) != S_IFGITLINK
)
705 return READ_TREE_RECURSIVE
;
708 unsigned int cur
= p
->size();
709 p
->resize(p
->size()+1);
710 p
->at(cur
).m_Hash
= (char*)sha1
;
711 p
->at(cur
).m_FileName
.Empty();
714 g_Git
.StringAppend(&p
->at(cur
).m_FileName
,(BYTE
*)base
,CP_ACP
,baselen
);
716 g_Git
.StringAppend(&p
->at(cur
).m_FileName
,(BYTE
*)pathname
,CP_ACP
);
718 //p->at(cur).m_FileName.Replace(_T('/'),_T('\\'));
720 p
->m_Map
[p
->at(cur
).m_FileName
]=cur
;
722 if( (mode
&S_IFMT
) == S_IFGITLINK
)
725 return READ_TREE_RECURSIVE
;
728 int CGitIgnoreItem::FetchIgnoreList(CString
&projectroot
, CString
&file
)
730 if(this->m_pExcludeList
)
732 free(m_pExcludeList
);
736 this->m_BaseDir
.Empty();
737 if( projectroot
.GetLength() < file
.GetLength())
739 CString base
= file
.Mid(projectroot
.GetLength()+1);
740 base
.Replace(_T('\\'), _T('/'));
741 if(base
!= _T(".git/info/exclude"))
743 int start
=base
.ReverseFind(_T('/'));
746 base
=base
.Left(start
);
747 this->m_BaseDir
= CUnicodeUtils::GetMulti(base
,CP_ACP
) ;
753 if(g_Git
.GetFileModifyTime(file
,&m_LastModifyTime
))
756 if(git_create_exclude_list(&this->m_pExcludeList
))
760 HANDLE hfile
= CreateFile(file
,
762 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
765 FILE_ATTRIBUTE_NORMAL
,
769 if(hfile
== INVALID_HANDLE_VALUE
)
774 DWORD size
=0,filesize
=0;
776 filesize
=GetFileSize(hfile
,NULL
);
778 if(filesize
== INVALID_FILE_SIZE
)
784 BYTE
*buffer
= new BYTE
[filesize
+1];
792 if(! ReadFile( hfile
, buffer
,filesize
,&size
,NULL
) )
795 return GetLastError();
801 for(int i
=0;i
<size
;i
++)
803 if( buffer
[i
] == '\n' || buffer
[i
] =='\r' || i
==(size
-1) )
805 if (buffer
[i
] == '\n' || buffer
[i
] =='\r')
810 if(p
[0] != '#' && p
[0] != 0)
811 git_add_exclude((const char*)p
,
812 this->m_BaseDir
.GetBuffer(),
813 m_BaseDir
.GetLength(),
814 this->m_pExcludeList
);
819 /* Can't free buffer, exluced list will use this buffer*/
826 bool CGitIgnoreList::CheckFileChanged(CString
&path
)
829 int ret
=g_Git
.GetFileModifyTime(path
, &time
);
831 bool cacheExist
= (m_Map
.find(path
) != m_Map
.end());
833 // both cache and file is not exist
834 if( (ret
!= 0) && (!cacheExist
))
837 // file exist but cache miss
838 if( (ret
== 0) && (!cacheExist
))
841 // file not exist but cache exist
842 if( (ret
!= 0) && (cacheExist
))
846 // file exist and cache exist
847 if( m_Map
[path
].m_LastModifyTime
== time
)
854 bool CGitIgnoreList::CheckIgnoreChanged(CString
&gitdir
,CString
&path
)
861 temp
.Replace(_T('/'), _T('\\'));
863 while(!temp
.IsEmpty())
867 if(CGit::GitPathFileExists(temp
))
869 CString gitignore
=temp
;
870 gitignore
+= _T("ignore");
871 if( CheckFileChanged(gitignore
) )
874 temp
+=_T("\\info\\exclude");
876 if( CheckFileChanged(temp
) )
883 if( CheckFileChanged(temp
) )
889 for( i
=temp
.GetLength() -1;i
>=0;i
--)
891 if(temp
[i
] == _T('\\'))
903 int CGitIgnoreList::LoadAllIgnoreFile(CString
&gitdir
,CString
&path
)
911 temp
.Replace(_T('/'), _T('\\'));
913 while(!temp
.IsEmpty())
917 if(CGit::GitPathFileExists(temp
))
919 CString gitignore
= temp
;
920 gitignore
+= _T("ignore");
921 if( CheckFileChanged(gitignore
) )
923 if(CGit::GitPathFileExists(temp
)) //if .gitignore remove, we need remote cache
925 m_Map
[gitignore
].FetchIgnoreList(gitdir
,gitignore
);
928 m_Map
.erase(gitignore
);
931 temp
+=_T("\\info\\exclude");
933 if( CheckFileChanged(temp
) )
935 if(CGit::GitPathFileExists(temp
)) //if .gitignore remove, we need remote cache
936 return m_Map
[temp
].FetchIgnoreList(gitdir
,temp
);
949 if( CheckFileChanged(temp
) )
951 if(CGit::GitPathFileExists(temp
)) //if .gitignore remove, we need remote cache
952 m_Map
[temp
].FetchIgnoreList(gitdir
,temp
);
960 for( i
=temp
.GetLength() -1;i
>=0;i
--)
962 if(temp
[i
] == _T('\\'))
973 int CGitIgnoreList::GetIgnoreFileChangeTimeList(CString
&path
, std::vector
<__int64
> &timelist
)
981 ignore
+=_T("\\.gitignore");
982 std::map
<CString
, CGitIgnoreItem
>::iterator itMap
;
983 itMap
= m_Map
.find(ignore
);
984 if(itMap
== m_Map
.end())
986 timelist
.push_back(0);
990 timelist
.push_back(itMap
->second
.m_LastModifyTime
);
994 ignore
+= _T("\\.git\\info\\exclude");
996 itMap
= m_Map
.find(ignore
);
997 if(itMap
== m_Map
.end())
1002 timelist
.push_back(itMap
->second
.m_LastModifyTime
);
1007 ignore
+=_T("\\.git");
1009 if(CGit::GitPathFileExists(ignore
))
1012 start
= temp
.ReverseFind(_T('\\'));
1014 temp
=temp
.Left(start
);
1021 bool CGitIgnoreList::IsIgnore(CString
&path
,CString
&projectroot
)
1025 str
.Replace(_T('\\'),_T('/'));
1028 ret
= CheckIgnore(path
, projectroot
);
1031 int start
=str
.ReverseFind(_T('/'));
1035 str
=str
.Left(start
);
1036 ret
= CheckIgnore(str
, projectroot
);
1041 int CGitIgnoreList::CheckIgnore(CString
&path
,CString
&projectroot
)
1045 CString temp
=projectroot
+_T("\\")+path
;
1046 temp
.Replace(_T('/'), _T('\\'));
1050 patha
= CUnicodeUtils::GetMulti(path
,CP_ACP
) ;
1051 patha
.Replace('\\','/');
1053 if(g_Git
.GetFileModifyTime(temp
,&time
,&dir
))
1062 while(!temp
.IsEmpty())
1065 x
=temp
.ReverseFind(_T('\\'));
1069 temp
+=_T("\\.gitignore");
1073 patha
.Replace('\\', '/');
1074 int pos
=patha
.ReverseFind('/');
1075 base
= pos
>=0? patha
.GetBuffer()+pos
+1:patha
.GetBuffer();
1077 if(this->m_Map
.find(temp
) == m_Map
.end() )
1084 if(m_Map
[temp
].m_pExcludeList
)
1085 ret
= git_check_excluded_1( patha
, patha
.GetLength(), base
, &type
, m_Map
[temp
].m_pExcludeList
);
1093 temp
= temp
.Left(temp
.GetLength()-11);
1094 temp
+=_T("\\.git\\info\\exclude");
1096 if(this->m_Map
.find(temp
) == m_Map
.end() )
1103 if(m_Map
[temp
].m_pExcludeList
)
1104 git_check_excluded_1( patha
, patha
.GetLength(), base
, &type
, m_Map
[temp
].m_pExcludeList
);
1113 temp
= temp
.Left(temp
.GetLength()-18);
1121 int CGitStatus::GetStatus(CString
&gitdir
, CString
&path
, git_wc_status_kind
*status
, BOOL IsFull
, BOOL IsRecursive
, FIll_STATUS_CALLBACK callback
, void *pData
)
1127 git_wc_status_kind dirstatus
= git_wc_status_none
;
1130 g_Git
.GetFileModifyTime(path
,&time
,&dir
);
1138 result
= _tstat64( gitdir
, &buf
);
1140 result
= _tstat64( gitdir
+_T("\\")+path
, &buf
);
1145 if(buf
.st_mode
& _S_IFDIR
)
1149 if( path
.Right(1) != _T("\\"))
1152 int len
=path
.GetLength();
1154 for(int i
=0;i
<size();i
++)
1156 if( at(i
).m_FileName
.GetLength() > len
)
1158 if(at(i
).m_FileName
.Left(len
) == path
)
1162 *status
= git_wc_status_normal
;
1164 callback(gitdir
+_T("\\")+path
,*status
,pData
);
1169 result
= _tstat64( gitdir
+_T("\\")+at(i
).m_FileName
, &buf
);
1173 *status
= git_wc_status_none
;
1174 GetFileStatus(gitdir
,at(i
).m_FileName
,status
,buf
,callback
,pData
);
1175 if( *status
!= git_wc_status_none
)
1177 if( dirstatus
== git_wc_status_none
)
1179 dirstatus
= git_wc_status_normal
;
1181 if( *status
!= git_wc_status_normal
)
1183 dirstatus
= git_wc_status_modified
;
1192 if( dirstatus
!= git_wc_status_none
)
1194 *status
= dirstatus
;
1198 *status
= git_wc_status_unversioned
;
1201 callback(gitdir
+_T("\\")+path
,*status
,pData
);
1207 GetFileStatus(gitdir
,path
,status
,buf
,callback
,pData
);