Correct detect del file.
[TortoiseGit.git] / src / Git / GitIndex.cpp
blob3efcf8e70c6d30cdb67b0830b4cf4aa8b78abae7
1 #include "StdAfx.h"
2 #include "Git.h"
3 #include "atlconv.h"
4 #include "GitRev.h"
5 #include "registry.h"
6 #include "GitConfig.h"
7 #include <map>
8 #include "UnicodeUtils.h"
9 #include "TGitPath.h"
10 #include "gitindex.h"
11 #include <sys/types.h>
12 #include <sys/stat.h>
14 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
16 #define FILL_DATA() \
17 m_FileName.Empty();\
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)
25 FILL_DATA();
26 return 0;
29 int CGitIndex::FillData(ondisk_cache_entry_extended * entry)
31 FILL_DATA();
32 this->m_Flags |= ((int)Big2lit(entry->flags2))<<16;
33 return 0;
36 int CGitIndex::Print()
38 _tprintf(_T("0x%08X 0x%08X %s %s\n"),
39 (int)this->m_ModifyTime,
40 this->m_Flags,
41 this->m_IndexHash.ToString(),
42 this->m_FileName);
44 return 0;
47 CGitIndexList::CGitIndexList()
49 this->m_LastModifyTime=0;
52 int CGitIndexList::ReadIndex(CString IndexFile)
54 HANDLE hfile=0;
55 int ret=0;
56 BYTE *buffer=NULL,*p;
57 CGitIndex GitIndex;
59 try
63 this->clear();
64 this->m_Map.clear();
66 hfile = CreateFile(IndexFile,
67 GENERIC_READ,
68 FILE_SHARE_READ,
69 NULL,
70 OPEN_EXISTING,
71 FILE_ATTRIBUTE_NORMAL,
72 NULL);
75 if(hfile == INVALID_HANDLE_VALUE)
77 ret = -1 ;
78 break;
81 cache_header *header;
82 DWORD size=0,filesize=0;
84 filesize=GetFileSize(hfile,NULL);
86 if(filesize == INVALID_FILE_SIZE )
88 ret =-1;
89 break;
92 buffer = new BYTE[filesize];
93 p=buffer;
94 if(buffer == NULL)
96 ret = -1;
97 break;
99 if(! ReadFile( hfile, buffer,filesize,&size,NULL) )
101 ret = GetLastError();
102 break;
105 if (size != filesize)
107 ret = -1;
108 break;
110 header = (cache_header *) buffer;
111 if( Big2lit(header->hdr_signature) != CACHE_SIGNATURE )
113 ret = -1;
114 break;
116 p+= sizeof(cache_header);
118 int entries = Big2lit(header->hdr_entries);
119 for(int i=0;i<entries;i++)
121 ondisk_cache_entry *entry;
122 ondisk_cache_entry_extended *entryex;
123 entry=(ondisk_cache_entry*)p;
124 entryex=(ondisk_cache_entry_extended*)p;
125 int flags=Big2lit(entry->flags);
126 if( flags & CE_EXTENDED)
128 GitIndex.FillData(entryex);
129 p+=ondisk_ce_size(entryex);
131 }else
133 GitIndex.FillData(entry);
134 p+=ondisk_ce_size(entry);
137 if(p>buffer+filesize)
139 ret = -1;
140 break;
142 this->push_back(GitIndex);
143 this->m_Map[GitIndex.m_FileName]=this->size()-1;
146 }while(0);
147 }catch(...)
149 ret= -1;
152 if(hfile != INVALID_HANDLE_VALUE)
153 CloseHandle(hfile);
154 if(buffer)
155 delete buffer;
156 return ret;
159 int CGitIndexList::GetFileStatus(CString &gitdir,CString &path,git_wc_status_kind *status,__int64 time,FIll_STATUS_CALLBACK callback,void *pData, CGitHash *pHash)
162 if(status)
164 if(m_Map.find(path) == m_Map.end() )
166 *status = git_wc_status_unversioned;
168 if(pHash)
169 pHash->Empty();
171 }else
173 int index = m_Map[path];
174 if(index <0)
175 return -1;
176 if(index >= size() )
177 return -1;
179 if( time == at(index).m_ModifyTime )
181 *status = git_wc_status_normal;
182 }else
184 *status = git_wc_status_modified;
187 if(at(index).m_Flags & CE_STAGEMASK )
188 *status = git_wc_status_conflicted;
189 else if(at(index).m_Flags & CE_INTENT_TO_ADD)
190 *status = git_wc_status_added;
192 if(pHash)
193 *pHash = at(index).m_IndexHash;
195 if(callback)
196 callback(gitdir+_T("\\")+path,*status,false, pData);
198 return 0;
201 int CGitIndexList::GetStatus(CString &gitdir,CString &path, git_wc_status_kind *status,
202 BOOL IsFull, BOOL IsRecursive,
203 FIll_STATUS_CALLBACK callback,void *pData,
204 CGitHash *pHash)
206 int result;
207 git_wc_status_kind dirstatus = git_wc_status_none;
208 __int64 time;
209 bool isDir=false;
211 if(status)
213 if(path.IsEmpty())
214 result = g_Git.GetFileModifyTime(gitdir,&time,&isDir);
215 else
216 result = g_Git.GetFileModifyTime( gitdir+_T("\\")+path, &time, &isDir );
218 if(result)
220 *status = git_wc_status_deleted;
221 if(callback)
222 callback(gitdir+_T("\\")+path,git_wc_status_deleted,false, pData);
224 return 0;
226 if(isDir)
228 if(!path.IsEmpty())
230 if( path.Right(1) != _T("\\"))
231 path+=_T("\\");
233 int len =path.GetLength();
235 for(int i=0;i<size();i++)
237 if( at(i).m_FileName.GetLength() > len )
239 if(at(i).m_FileName.Left(len) == path)
241 if( !IsFull )
243 *status = git_wc_status_normal;
244 if(callback)
245 callback(gitdir+_T("\\")+path,*status,false, pData);
246 return 0;
248 }else
250 result = g_Git.GetFileModifyTime( gitdir+_T("\\")+at(i).m_FileName , &time);
251 if(result)
252 continue;
254 *status = git_wc_status_none;
255 GetFileStatus(gitdir,at(i).m_FileName,status,time,callback,pData);
256 if( *status != git_wc_status_none )
258 if( dirstatus == git_wc_status_none)
260 dirstatus = git_wc_status_normal;
262 if( *status != git_wc_status_normal )
264 dirstatus = git_wc_status_modified;
273 if( dirstatus != git_wc_status_none )
275 *status = dirstatus;
277 else
279 *status = git_wc_status_unversioned;
281 if(callback)
282 callback(gitdir+_T("\\")+path,*status,false, pData);
284 return 0;
286 }else
288 GetFileStatus(gitdir,path,status,time,callback,pData,pHash);
291 return 0;
294 int CGitIndexFileMap::CheckAndUpdateIndex(CString &gitdir,bool *loaded)
296 __int64 time;
297 int result;
301 CString IndexFile;
302 IndexFile=gitdir+_T("\\.git\\index");
303 /* Get data associated with "crt_stat.c": */
304 result = g_Git.GetFileModifyTime( IndexFile, &time );
306 // WIN32_FILE_ATTRIBUTE_DATA FileInfo;
307 // GetFileAttributesEx(_T("D:\\tortoisegit\\src\\gpl.txt"),GetFileExInfoStandard,&FileInfo);
308 // result = _tstat64( _T("D:\\tortoisegit\\src\\gpl.txt"), &buf );
310 if(loaded)
311 *loaded = false;
313 if(result)
314 return result;
316 if((*this)[gitdir].m_LastModifyTime != time )
318 if((*this)[gitdir].ReadIndex(IndexFile))
319 return -1;
320 if(loaded)
321 *loaded =true;
323 (*this)[gitdir].m_LastModifyTime = time;
325 }catch(...)
327 return -1;
329 return 0;
332 int CGitIndexFileMap::GetFileStatus(CString &gitdir, CString &path, git_wc_status_kind *status,BOOL IsFull, BOOL IsRecursive,
333 FIll_STATUS_CALLBACK callback,void *pData,
334 CGitHash *pHash)
336 int result;
339 CheckAndUpdateIndex(gitdir);
340 (*this)[gitdir].GetStatus(gitdir,path,status,IsFull,IsRecursive,callback,pData,pHash);
342 }catch(...)
344 return -1;
346 return 0;
349 int CGitIndexFileMap::IsUnderVersionControl(CString &gitdir, CString &path, bool isDir,bool *isVersion)
353 if(path.IsEmpty())
355 *isVersion =true;
356 return 0;
358 CString subpath=path;
359 subpath.Replace(_T('\\'), _T('/'));
360 if(isDir)
361 subpath+=_T('/');
363 CheckAndUpdateIndex(gitdir);
364 if(isDir)
366 *isVersion = (SearchInSortVector((*this)[gitdir], subpath.GetBuffer(), subpath.GetLength()) >= 0);
368 else
370 *isVersion = ((*this)[gitdir].m_Map.find(subpath) != (*this)[gitdir].m_Map.end());
373 }catch(...)
375 return -1;
377 return 0;
379 int CGitHeadFileList::ReadHeadHash(CString gitdir)
381 CString HeadFile = gitdir;
382 HeadFile += _T("\\.git\\HEAD");
383 HANDLE hfile;
385 m_Gitdir = gitdir;
387 m_HeadFile = HeadFile;
389 if( g_Git.GetFileModifyTime(m_HeadFile,&m_LastModifyTimeHead))
390 return -1;
392 hfile = CreateFile(HeadFile,
393 GENERIC_READ,
394 FILE_SHARE_READ,
395 NULL,
396 OPEN_EXISTING,
397 FILE_ATTRIBUTE_NORMAL,
398 NULL);
400 if(hfile == INVALID_HANDLE_VALUE)
401 return -1;
403 DWORD size=0,filesize=0;
404 unsigned char buffer[40] ;
405 ReadFile(hfile,buffer,4,&size,NULL);
406 if(size !=4)
407 return -1;
409 buffer[4]=0;
410 if(strcmp((const char*)buffer,"ref:") == 0)
412 filesize = GetFileSize(hfile,NULL);
414 unsigned char *p = (unsigned char*)malloc(filesize -4);
416 ReadFile(hfile,p,filesize-4,&size,NULL);
418 m_HeadRefFile.Empty();
419 g_Git.StringAppend(&this->m_HeadRefFile,p,CP_ACP,filesize-4);
420 free(p);
421 m_HeadRefFile=gitdir+_T("\\.git\\")+m_HeadRefFile.Trim();
422 m_HeadRefFile.Replace(_T('/'),_T('\\'));
424 __int64 time;
425 if(g_Git.GetFileModifyTime(m_HeadRefFile,&time,NULL))
426 return -1;
429 HANDLE href;
430 href = CreateFile(m_HeadRefFile,
431 GENERIC_READ,
432 FILE_SHARE_READ,
433 NULL,
434 OPEN_EXISTING,
435 FILE_ATTRIBUTE_NORMAL,
436 NULL);
438 if(href == INVALID_HANDLE_VALUE)
439 return -1;
441 ReadFile(href,buffer,40,&size,NULL);
442 if(size != 40)
443 return -1;
445 this->m_Head.ConvertFromStrA((char*)buffer);
446 CloseHandle(href);
447 this->m_LastModifyTimeRef = time;
449 }else
451 ReadFile(hfile,buffer+4,40-4,&size,NULL);
452 if(size !=36)
453 return -1;
455 m_HeadRefFile.Empty();
457 this->m_Head.ConvertFromStrA((char*)buffer);
460 CloseHandle(hfile);
463 bool CGitHeadFileList::CheckHeadUpdate()
465 if(this->m_HeadFile.IsEmpty())
466 return true;
468 __int64 mtime=0;
470 if( g_Git.GetFileModifyTime(m_HeadFile,&mtime))
471 return true;
473 if(mtime != this->m_LastModifyTimeHead)
474 return true;
476 if(!this->m_HeadRefFile.IsEmpty())
478 if(g_Git.GetFileModifyTime(m_HeadRefFile,&mtime))
479 return true;
481 if(mtime != this->m_LastModifyTimeRef)
482 return true;
485 return false;
488 int CGitHeadFileList::ReadTree()
490 if( this->m_Head.IsEmpty())
491 return -1;
493 CAutoLocker lock(g_Git.m_critGitDllSec);
495 if(m_Gitdir != g_Git.m_CurrentDir)
497 g_Git.SetCurrentDir(m_Gitdir);
498 SetCurrentDirectory(g_Git.m_CurrentDir);
499 git_init();
501 return git_read_tree(this->m_Head.m_hash,CGitHeadFileList::CallBack,this);
504 int CGitHeadFileList::CallBack(const unsigned char *sha1, const char *base, int baselen,
505 const char *pathname, unsigned mode, int stage, void *context)
507 #define S_IFGITLINK 0160000
509 CGitHeadFileList *p = (CGitHeadFileList*)context;
510 if( mode&S_IFDIR )
512 if( (mode&S_IFMT) != S_IFGITLINK)
513 return READ_TREE_RECURSIVE;
516 unsigned int cur = p->size();
517 p->resize(p->size()+1);
518 p->at(cur).m_Hash = (char*)sha1;
519 p->at(cur).m_FileName.Empty();
521 if(base)
522 g_Git.StringAppend(&p->at(cur).m_FileName,(BYTE*)base,CP_ACP,baselen);
524 g_Git.StringAppend(&p->at(cur).m_FileName,(BYTE*)pathname,CP_ACP);
526 //p->at(cur).m_FileName.Replace(_T('/'),_T('\\'));
528 p->m_Map[p->at(cur).m_FileName]=cur;
530 if( (mode&S_IFMT) == S_IFGITLINK)
531 return 0;
533 return READ_TREE_RECURSIVE;
536 int CGitIgnoreItem::FetchIgnoreList(CString &file)
538 if(this->m_pExcludeList)
540 free(m_pExcludeList);
541 m_pExcludeList=NULL;
545 if(g_Git.GetFileModifyTime(file,&m_LastModifyTime))
546 return -1;
548 if(git_create_exclude_list(&this->m_pExcludeList))
549 return -1;
552 HANDLE hfile = CreateFile(file,
553 GENERIC_READ,
554 FILE_SHARE_READ,
555 NULL,
556 OPEN_EXISTING,
557 FILE_ATTRIBUTE_NORMAL,
558 NULL);
561 if(hfile == INVALID_HANDLE_VALUE)
563 return -1 ;
566 DWORD size=0,filesize=0;
568 filesize=GetFileSize(hfile,NULL);
570 if(filesize == INVALID_FILE_SIZE )
572 return -1;
575 BYTE *buffer = new BYTE[filesize+1];
577 if(buffer == NULL)
579 return -1;
582 if(! ReadFile( hfile, buffer,filesize,&size,NULL) )
584 return GetLastError();
587 BYTE *p = buffer;
588 for(int i=0;i<size;i++)
590 if( buffer[i] == '\n' || buffer[i] =='\r' || i==(size-1) )
592 if( i== size-1)
593 buffer[size]=0;
594 else
595 buffer[i]=0;
597 if(p[0] != '#' && p[0] != 0)
598 git_add_exclude((const char*)p, 0,0,this->m_pExcludeList);
600 p=buffer+i+1;
603 //delete buffer;
604 //buffer=NULL;
608 bool CGitIgnoreList::CheckFileChanged(CString &path)
610 __int64 time=0;
611 int ret=g_Git.GetFileModifyTime(path, &time);
613 bool cacheExist = (m_Map.find(path) != m_Map.end());
614 // both cache and file is not exist
615 if( (ret != 0) && (!cacheExist))
616 return false;
618 // file exist but cache miss
619 if( (ret == 0) && (!cacheExist))
620 return true;
622 // file not exist but cache exist
623 if( (ret != 0) && (cacheExist))
624 return true;
626 // file exist and cache exist
627 if( m_Map[path].m_LastModifyTime == time )
628 return false;
630 return true;
634 bool CGitIgnoreList::CheckIgnoreChanged(CString &gitdir,CString &path)
636 CString temp;
637 temp=gitdir;
638 temp+=_T("\\");
639 temp+=path;
641 while(!temp.IsEmpty())
643 temp+=_T("\\.git");
645 if(PathFileExists(temp))
647 CString gitignore=temp;
648 gitignore += _T("ignore");
649 if( CheckFileChanged(gitignore) )
650 return true;
652 temp+=_T("\\info\\exclude");
654 if( CheckFileChanged(temp) )
655 return true;
656 else
657 return false;
658 }else
660 temp+=_T("ignore");
661 if( CheckFileChanged(temp) )
662 return true;
665 int found=0;
666 int i;
667 for( i=temp.GetLength() -1;i>=0;i--)
669 if(temp[i] == _T('\\'))
670 found ++;
672 if(found == 2)
673 break;
676 temp = temp.Left(i);
678 return true;
681 int CGitIgnoreList::LoadAllIgnoreFile(CString &gitdir,CString &path)
683 CString temp;
685 temp=gitdir;
686 temp+=_T("\\");
687 temp+=path;
689 while(!temp.IsEmpty())
691 temp+=_T("\\.git");
693 if(PathFileExists(temp))
695 CString gitignore = temp;
696 gitignore += _T("ignore");
697 if( CheckFileChanged(gitignore) )
699 m_Map[gitignore].FetchIgnoreList(gitignore);
702 temp+=_T("\\info\\exclude");
704 if( CheckFileChanged(temp) )
706 return m_Map[temp].FetchIgnoreList(temp);
709 }else
711 temp+=_T("ignore");
712 if( CheckFileChanged(temp) )
714 m_Map[temp].FetchIgnoreList(temp);
718 int found=0;
719 int i;
720 for( i=temp.GetLength() -1;i>=0;i--)
722 if(temp[i] == _T('\\'))
723 found ++;
725 if(found == 2)
726 break;
729 temp = temp.Left(i);
731 return true;
733 bool CGitIgnoreList::IsIgnore(CString &path,CString &projectroot)
735 CString str=path;
737 str.Replace(_T('\\'),_T('/'));
739 int ret;
740 ret = CheckIgnore(path, projectroot);
741 while(ret < 0)
743 int start=str.ReverseFind(_T('/'));
744 if(start<0)
745 return (ret == 1);
747 str=str.Left(start);
748 ret = CheckIgnore(str, projectroot);
751 return (ret == 1);
753 int CGitIgnoreList::CheckIgnore(CString &path,CString &projectroot)
755 __int64 time=0;
756 bool dir=0;
757 CString temp=projectroot+_T("\\")+path;
758 CStringA patha;
760 patha = CUnicodeUtils::GetMulti(path,CP_ACP) ;
761 patha.Replace('\\','/');
763 if(g_Git.GetFileModifyTime(temp,&time,&dir))
764 return -1;
766 int type=0;
767 if( dir )
768 type = DT_DIR;
769 else
770 type = DT_REG;
772 while(!temp.IsEmpty())
774 int x;
775 x=temp.ReverseFind(_T('\\'));
776 if(x<0) x=0;
777 temp=temp.Left(x);
779 temp+=_T("\\.gitignore");
780 if(this->m_Map.find(temp) == m_Map.end() )
783 }else
785 char *base;
787 patha.Replace('\\', '/');
788 int pos=patha.ReverseFind('/');
789 base = pos>=0? patha.GetBuffer()+pos+1:patha.GetBuffer();
791 int ret=git_check_excluded_1( patha, patha.GetLength(), base, &type, m_Map[temp].m_pExcludeList);
792 if(ret == 1)
793 return 1;
794 if(ret == 0)
795 return 0;
798 temp = temp.Left(temp.GetLength()-11);
799 temp +=_T("\\.git\\info\\exclude");
801 if(this->m_Map.find(temp) == m_Map.end() )
804 }else
806 int ret=git_check_excluded_1( patha, patha.GetLength(), NULL,&type, m_Map[temp].m_pExcludeList);
807 if(ret == 1)
808 return 1;
809 if(ret == 0)
810 return 0;
812 return -1;
814 temp = temp.Left(temp.GetLength()-18);
817 return -1;
820 #if 0
822 int CGitStatus::GetStatus(CString &gitdir, CString &path, git_wc_status_kind *status, BOOL IsFull, BOOL IsRecursive , FIll_STATUS_CALLBACK callback , void *pData)
824 int result;
825 __int64 time;
826 bool dir;
828 git_wc_status_kind dirstatus = git_wc_status_none;
829 if(status)
831 g_Git.GetFileModifyTime(path,&time,&dir);
832 if( dir)
834 }else
838 if(path.IsEmpty())
839 result = _tstat64( gitdir, &buf );
840 else
841 result = _tstat64( gitdir+_T("\\")+path, &buf );
843 if(result)
844 return -1;
846 if(buf.st_mode & _S_IFDIR)
848 if(!path.IsEmpty())
850 if( path.Right(1) != _T("\\"))
851 path+=_T("\\");
853 int len =path.GetLength();
855 for(int i=0;i<size();i++)
857 if( at(i).m_FileName.GetLength() > len )
859 if(at(i).m_FileName.Left(len) == path)
861 if( !IsFull )
863 *status = git_wc_status_normal;
864 if(callback)
865 callback(gitdir+_T("\\")+path,*status,pData);
866 return 0;
868 }else
870 result = _tstat64( gitdir+_T("\\")+at(i).m_FileName, &buf );
871 if(result)
872 continue;
874 *status = git_wc_status_none;
875 GetFileStatus(gitdir,at(i).m_FileName,status,buf,callback,pData);
876 if( *status != git_wc_status_none )
878 if( dirstatus == git_wc_status_none)
880 dirstatus = git_wc_status_normal;
882 if( *status != git_wc_status_normal )
884 dirstatus = git_wc_status_modified;
893 if( dirstatus != git_wc_status_none )
895 *status = dirstatus;
897 else
899 *status = git_wc_status_unversioned;
901 if(callback)
902 callback(gitdir+_T("\\")+path,*status,pData);
904 return 0;
906 }else
908 GetFileStatus(gitdir,path,status,buf,callback,pData);
911 return 0;
914 #endif