Fix TGitStatus wrong when filenameA is substring of filenameB
[TortoiseGit.git] / src / Git / GitIndex.cpp
blob1dc83f0da318b2288fa7e5b219de720271c9f8f7
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 "StdAfx.h"
21 #include "Git.h"
22 #include "atlconv.h"
23 #include "GitRev.h"
24 #include "registry.h"
25 #include "GitConfig.h"
26 #include <map>
27 #include "UnicodeUtils.h"
28 #include "TGitPath.h"
29 #include "gitindex.h"
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include "git2.h"
35 #define FILL_DATA() \
36 m_FileName.Empty();\
37 g_Git.StringAppend(&m_FileName,(BYTE*)entry->name,CP_ACP,Big2lit(entry->flags)&CE_NAMEMASK);\
38 m_FileName.MakeLower(); \
39 this->m_Flags=Big2lit(entry->flags);\
40 this->m_ModifyTime=Big2lit(entry->mtime.sec);\
41 this->m_IndexHash=(char*)(entry->sha1);
43 int CGitIndex::FillData(ondisk_cache_entry * entry)
45 FILL_DATA();
46 return 0;
49 int CGitIndex::FillData(ondisk_cache_entry_extended * entry)
51 FILL_DATA();
52 this->m_Flags |= ((int)Big2lit(entry->flags2))<<16;
53 return 0;
56 int CGitIndex::Print()
58 _tprintf(_T("0x%08X 0x%08X %s %s\n"),
59 (int)this->m_ModifyTime,
60 this->m_Flags,
61 this->m_IndexHash.ToString(),
62 this->m_FileName);
64 return 0;
67 CGitIndexList::CGitIndexList()
69 this->m_LastModifyTime=0;
72 static bool SortIndex(CGitIndex &Item1, CGitIndex &Item2)
74 return Item1.m_FileName.Compare(Item2.m_FileName)<0;
77 static bool SortTree(CGitTreeItem &Item1, CGitTreeItem &Item2)
79 return Item1.m_FileName.Compare(Item2.m_FileName)<0;
82 int CGitIndexList::ReadIndex(CString IndexFile)
84 HANDLE hfile=INVALID_HANDLE_VALUE;
85 HANDLE hmap = INVALID_HANDLE_VALUE;
86 int ret=0;
87 BYTE *buffer=NULL,*p;
88 CGitIndex GitIndex;
90 #ifdef DEBUG
91 m_GitFile = IndexFile;
92 #endif
94 try
98 this->clear();
100 hfile = CreateFile(IndexFile,
101 GENERIC_READ,
102 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
103 NULL,
104 OPEN_EXISTING,
105 FILE_ATTRIBUTE_NORMAL,
106 NULL);
109 if(hfile == INVALID_HANDLE_VALUE)
111 ret = -1 ;
112 break;
115 hmap = CreateFileMapping(hfile, NULL, PAGE_READONLY,0,0,NULL);
116 if(hmap == INVALID_HANDLE_VALUE)
118 ret =-1;
119 break;
122 p = buffer = (BYTE*)MapViewOfFile(hmap,FILE_MAP_READ,0,0,0);
123 if(buffer == NULL)
125 ret = -1;
126 break;
129 cache_header *header;
130 header = (cache_header *) buffer;
132 if( Big2lit(header->hdr_signature) != CACHE_SIGNATURE )
134 ret = -1;
135 break;
137 p+= sizeof(cache_header);
139 int entries = Big2lit(header->hdr_entries);
140 resize(entries);
142 for(int i=0;i<entries;i++)
144 ondisk_cache_entry *entry;
145 ondisk_cache_entry_extended *entryex;
146 entry=(ondisk_cache_entry*)p;
147 entryex=(ondisk_cache_entry_extended*)p;
148 int flags=Big2lit(entry->flags);
149 if( flags & CE_EXTENDED)
151 this->at(i).FillData(entryex);
152 p+=ondisk_ce_size(entryex);
153 }else
155 this->at(i).FillData(entry);
156 p+=ondisk_ce_size(entry);
160 std::sort(this->begin(), this->end(), SortIndex);
161 g_Git.GetFileModifyTime(IndexFile, &this->m_LastModifyTime);
162 }while(0);
163 }catch(...)
165 ret= -1;
168 if(buffer)
169 UnmapViewOfFile(buffer);
171 if(hmap != INVALID_HANDLE_VALUE)
172 CloseHandle(hmap);
174 if(hfile != INVALID_HANDLE_VALUE)
175 CloseHandle(hfile);
177 return ret;
180 int CGitIndexList::GetFileStatus(const CString &gitdir,const CString &pathorg,git_wc_status_kind *status,__int64 time,FIll_STATUS_CALLBACK callback,void *pData, CGitHash *pHash)
182 if(status)
184 CString path = pathorg;
185 path.MakeLower();
187 int start=SearchInSortVector(*this, ((CString&)path).GetBuffer(),-1);
188 ((CString&)path).ReleaseBuffer();
190 if( start<0 )
192 *status = git_wc_status_unversioned;
193 if(pHash)
194 pHash->Empty();
196 }else
198 int index = start;
199 if(index <0)
200 return -1;
201 if(index >= size() )
202 return -1;
204 if( time == at(index).m_ModifyTime )
206 *status = git_wc_status_normal;
207 }else
209 *status = git_wc_status_modified;
212 if(at(index).m_Flags & CE_STAGEMASK )
213 *status = git_wc_status_conflicted;
214 else if(at(index).m_Flags & CE_INTENT_TO_ADD)
215 *status = git_wc_status_added;
217 if(pHash)
218 *pHash = at(index).m_IndexHash;
223 if(callback && status)
224 callback(gitdir+_T("\\")+pathorg,*status,false, pData);
225 return 0;
228 int CGitIndexList::GetStatus(const CString &gitdir,const CString &pathParam, git_wc_status_kind *status,
229 BOOL IsFull, BOOL IsRecursive,
230 FIll_STATUS_CALLBACK callback,void *pData,
231 CGitHash *pHash)
233 int result;
234 git_wc_status_kind dirstatus = git_wc_status_none;
235 __int64 time;
236 bool isDir=false;
237 CString path = pathParam;
239 if(status)
241 if(path.IsEmpty())
242 result = g_Git.GetFileModifyTime(gitdir,&time,&isDir);
243 else
244 result = g_Git.GetFileModifyTime( gitdir+_T("\\")+path, &time, &isDir );
246 if(result)
248 *status = git_wc_status_deleted;
249 if(callback)
250 callback(gitdir+_T("\\")+path,git_wc_status_deleted,false, pData);
252 return 0;
254 if(isDir)
256 if(!path.IsEmpty())
258 if( path.Right(1) != _T("\\"))
259 path+=_T("\\");
261 int len =path.GetLength();
263 for(int i=0;i<size();i++)
265 if( at(i).m_FileName.GetLength() > len )
267 if(at(i).m_FileName.Left(len) == path)
269 if( !IsFull )
271 *status = git_wc_status_normal;
272 if(callback)
273 callback(gitdir+_T("\\")+path,*status,false, pData);
274 return 0;
276 }else
278 result = g_Git.GetFileModifyTime( gitdir+_T("\\")+at(i).m_FileName , &time);
279 if(result)
280 continue;
282 *status = git_wc_status_none;
283 GetFileStatus(gitdir,at(i).m_FileName,status,time,callback,pData);
284 if( *status != git_wc_status_none )
286 if( dirstatus == git_wc_status_none)
288 dirstatus = git_wc_status_normal;
290 if( *status != git_wc_status_normal )
292 dirstatus = git_wc_status_modified;
299 } /* End For */
301 if( dirstatus != git_wc_status_none )
303 *status = dirstatus;
305 else
307 *status = git_wc_status_unversioned;
309 if(callback)
310 callback(gitdir+_T("\\")+path,*status,false, pData);
312 return 0;
314 }else
316 GetFileStatus(gitdir,path,status,time,callback,pData,pHash);
319 return 0;
322 int CGitIndexFileMap::Check(const CString &gitdir, bool *isChanged)
324 __int64 time;
325 int result;
327 CString IndexFile;
328 IndexFile=gitdir+_T("\\.git\\index");
329 /* Get data associated with "crt_stat.c": */
330 result = g_Git.GetFileModifyTime( IndexFile, &time );
332 if(result)
333 return result;
335 SHARED_INDEX_PTR pIndex;
336 pIndex = this->SafeGet(gitdir);
338 if(pIndex.get() == NULL)
340 if(isChanged)
341 *isChanged = true;
342 return 0;
345 if(pIndex->m_LastModifyTime == time)
347 if(isChanged)
348 *isChanged = false;
350 else
352 if(isChanged)
353 *isChanged = true;
355 return 0;
358 int CGitIndexFileMap::LoadIndex(const CString &gitdir)
362 SHARED_INDEX_PTR pIndex(new CGitIndexList);
364 if(pIndex->ReadIndex(gitdir+_T("\\.git\\index")))
365 return -1;
367 this->SafeSet(gitdir,pIndex);
369 }catch(...)
371 return -1;
373 return 0;
376 int CGitIndexFileMap::GetFileStatus(const CString &gitdir, const CString &path, git_wc_status_kind *status,BOOL IsFull, BOOL IsRecursive,
377 FIll_STATUS_CALLBACK callback,void *pData,
378 CGitHash *pHash,
379 bool isLoadUpdatedIndex)
383 CheckAndUpdate(gitdir, isLoadUpdatedIndex);
385 SHARED_INDEX_PTR pIndex = this->SafeGet(gitdir);
386 if(pIndex.get() != NULL)
388 pIndex->GetStatus(gitdir,path,status,IsFull,IsRecursive,callback,pData,pHash);
391 }catch(...)
393 return -1;
395 return 0;
398 int CGitIndexFileMap::IsUnderVersionControl(const CString &gitdir, const CString &path, bool isDir,bool *isVersion, bool isLoadUpdateIndex)
402 if(path.IsEmpty())
404 *isVersion =true;
405 return 0;
408 CString subpath=path;
409 subpath.Replace(_T('\\'), _T('/'));
410 if(isDir)
411 subpath+=_T('/');
413 subpath.MakeLower();
415 CheckAndUpdate(gitdir, isLoadUpdateIndex);
417 SHARED_INDEX_PTR pIndex = this->SafeGet(gitdir);
419 if(pIndex.get())
421 if(isDir)
422 *isVersion = (SearchInSortVector(*pIndex, subpath.GetBuffer(), subpath.GetLength()) >= 0);
423 else
424 *isVersion = (SearchInSortVector(*pIndex, subpath.GetBuffer(), -1) >= 0);
427 }catch(...)
429 return -1;
431 return 0;
434 int CGitHeadFileList::GetPackRef(const CString &gitdir)
436 CString PackRef = gitdir;
437 PackRef += _T("\\.git\\packed-refs");
439 __int64 mtime;
440 if( g_Git.GetFileModifyTime(PackRef, &mtime))
442 //packed refs is not existed
443 this->m_PackRefFile.Empty();
444 this->m_PackRefMap.clear();
445 return 0;
447 }else if(mtime == m_LastModifyTimePackRef)
449 return 0;
451 }else
453 this->m_PackRefFile = PackRef;
454 this->m_LastModifyTimePackRef = mtime;
457 int ret =0;
459 this->m_PackRefMap.clear();
461 HANDLE hfile = CreateFile(PackRef,
462 GENERIC_READ,
463 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
464 NULL,
465 OPEN_EXISTING,
466 FILE_ATTRIBUTE_NORMAL,
467 NULL);
470 if(hfile == INVALID_HANDLE_VALUE)
472 ret = -1;
473 break;
476 DWORD filesize = GetFileSize(hfile,NULL);
477 DWORD size =0;
478 char *buff;
479 buff = new char[filesize];
481 ReadFile(hfile,buff,filesize,&size,NULL);
483 if(size != filesize)
485 ret = -1;
486 break;
489 CString hash;
490 CString ref;
492 for(DWORD i=0;i<filesize;i++)
494 hash.Empty();
495 ref.Empty();
496 if(buff[i] == '#')
498 while(buff[i] != '\n')
499 { i++;
500 if( i==filesize)
501 break;
503 i++;
506 if( i== filesize)
507 break;
509 while(buff[i] != ' ')
511 hash.AppendChar(buff[i]);
512 i++;
513 if(i==filesize)
514 break;
517 i++;
518 if( i== filesize)
519 break;
521 while(buff[i] != '\n')
523 ref.AppendChar(buff[i]);
524 i++;
525 if( i== filesize)
526 break;
529 i++;
531 if( !ref.IsEmpty() )
533 this->m_PackRefMap[ref] = hash;
539 delete buff;
541 }while(0);
543 if( hfile != INVALID_HANDLE_VALUE)
544 CloseHandle(hfile);
546 return ret;
549 int CGitHeadFileList::ReadHeadHash(CString gitdir)
551 CString HeadFile = gitdir;
552 HeadFile += _T("\\.git\\HEAD");
555 HANDLE hfile=INVALID_HANDLE_VALUE;
556 HANDLE href = INVALID_HANDLE_VALUE;
558 int ret = 0;
559 m_Gitdir = gitdir;
561 m_HeadFile = HeadFile;
563 if( g_Git.GetFileModifyTime(m_HeadFile,&m_LastModifyTimeHead))
564 return -1;
570 hfile = CreateFile(HeadFile,
571 GENERIC_READ,
572 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
573 NULL,
574 OPEN_EXISTING,
575 FILE_ATTRIBUTE_NORMAL,
576 NULL);
578 if(hfile == INVALID_HANDLE_VALUE)
580 ret = -1;
581 break;
584 DWORD size=0,filesize=0;
585 unsigned char buffer[40] ;
586 ReadFile(hfile,buffer,4,&size,NULL);
587 if(size !=4)
589 ret = -1;
590 break;
592 buffer[4]=0;
593 if(strcmp((const char*)buffer,"ref:") == 0)
595 filesize = GetFileSize(hfile,NULL);
597 unsigned char *p = (unsigned char*)malloc(filesize -4);
599 ReadFile(hfile,p,filesize-4,&size,NULL);
601 m_HeadRefFile.Empty();
602 g_Git.StringAppend(&this->m_HeadRefFile,p,CP_ACP,filesize-4);
603 CString ref = this->m_HeadRefFile;
604 ref=ref.Trim();
605 int start =0;
606 ref=ref.Tokenize(_T("\n"),start);
607 free(p);
608 m_HeadRefFile=gitdir+_T("\\.git\\")+m_HeadRefFile.Trim();
609 m_HeadRefFile.Replace(_T('/'),_T('\\'));
611 __int64 time;
612 if(g_Git.GetFileModifyTime(m_HeadRefFile,&time,NULL))
614 m_HeadRefFile.Empty();
615 if( GetPackRef(gitdir))
617 ret = -1;
618 break;
620 if(this->m_PackRefMap.find(ref) == m_PackRefMap.end())
622 ret = -1;
623 break;
625 this ->m_Head = m_PackRefMap[ref];
626 ret =0;
627 break;
630 href = CreateFile(m_HeadRefFile,
631 GENERIC_READ,
632 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
633 NULL,
634 OPEN_EXISTING,
635 FILE_ATTRIBUTE_NORMAL,
636 NULL);
638 if(href == INVALID_HANDLE_VALUE)
640 m_HeadRefFile.Empty();
642 if( GetPackRef(gitdir))
644 ret = -1;
645 break;
648 if(this->m_PackRefMap.find(ref) == m_PackRefMap.end())
650 ret = -1;
651 break;
653 this ->m_Head = m_PackRefMap[ref];
654 ret =0;
655 break;
657 ReadFile(href,buffer,40,&size,NULL);
658 if(size != 40)
660 ret =-1;
661 break;
663 this->m_Head.ConvertFromStrA((char*)buffer);
665 this->m_LastModifyTimeRef = time;
667 }else
669 ReadFile(hfile,buffer+4,40-4,&size,NULL);
670 if(size !=36)
672 ret =-1;
673 break;
675 m_HeadRefFile.Empty();
677 this->m_Head.ConvertFromStrA((char*)buffer);
679 }while(0);
680 }catch(...)
682 ret = -1;
685 if(hfile != INVALID_HANDLE_VALUE)
686 CloseHandle(hfile);
687 if(href != INVALID_HANDLE_VALUE)
688 CloseHandle(href);
690 return ret;
693 bool CGitHeadFileList::CheckHeadUpdate()
695 if(this->m_HeadFile.IsEmpty())
696 return true;
698 __int64 mtime=0;
700 if( g_Git.GetFileModifyTime(m_HeadFile,&mtime))
701 return true;
703 if(mtime != this->m_LastModifyTimeHead)
704 return true;
706 if(!this->m_HeadRefFile.IsEmpty())
708 if(g_Git.GetFileModifyTime(m_HeadRefFile,&mtime))
709 return true;
711 if(mtime != this->m_LastModifyTimeRef)
712 return true;
715 if(!this->m_PackRefFile.IsEmpty())
717 if(g_Git.GetFileModifyTime(m_PackRefFile,&mtime))
718 return true;
720 if(mtime != this->m_LastModifyTimePackRef)
721 return true;
723 return false;
725 #if 0
726 int CGitHeadFileList::ReadTree()
728 int ret;
729 if( this->m_Head.IsEmpty())
730 return -1;
734 CAutoLocker lock(g_Git.m_critGitDllSec);
735 CAutoWriteLock lock1(&this->m_SharedMutex);
737 if(m_Gitdir != g_Git.m_CurrentDir)
739 g_Git.SetCurrentDir(m_Gitdir);
740 SetCurrentDirectory(g_Git.m_CurrentDir);
741 git_init();
744 this->m_Map.clear();
745 this->clear();
747 ret = git_read_tree(this->m_Head.m_hash,CGitHeadFileList::CallBack,this);
748 if(!ret)
749 m_TreeHash = m_Head;
751 } catch(...)
753 return -1;
755 return ret;
757 #endif;
759 int CGitHeadFileList::CallBack(const unsigned char *sha1, const char *base, int baselen,
760 const char *pathname, unsigned mode, int /*stage*/, void *context)
762 #define S_IFGITLINK 0160000
764 CGitHeadFileList *p = (CGitHeadFileList*)context;
765 if( mode&S_IFDIR )
767 if( (mode&S_IFMT) != S_IFGITLINK)
768 return READ_TREE_RECURSIVE;
771 unsigned int cur = p->size();
772 p->resize(p->size()+1);
773 p->at(cur).m_Hash = (char*)sha1;
774 p->at(cur).m_FileName.Empty();
776 if(base)
777 g_Git.StringAppend(&p->at(cur).m_FileName,(BYTE*)base,CP_ACP,baselen);
779 g_Git.StringAppend(&p->at(cur).m_FileName,(BYTE*)pathname,CP_ACP);
781 p->at(cur).m_FileName.MakeLower();
783 //p->at(cur).m_FileName.Replace(_T('/'),_T('\\'));
785 //p->m_Map[p->at(cur).m_FileName]=cur;
787 if( (mode&S_IFMT) == S_IFGITLINK)
788 return 0;
790 return READ_TREE_RECURSIVE;
793 int ReadTreeRecurive(git_tree * tree, CStringA base, int (*CallBack) (const unsigned char *, const char *, int, const char *, unsigned int, int, void *),void *data)
795 size_t count = git_tree_entrycount(tree);
796 for(int i=0; i<count; i++)
798 git_tree_entry *entry = git_tree_entry_byindex(tree, i);
799 int mode = git_tree_entry_attributes(entry);
800 if( CallBack(git_tree_entry_id(entry)->id,
801 base,
802 base.GetLength(),
803 git_tree_entry_name(entry),
804 mode,
806 data) == READ_TREE_RECURSIVE
809 if(mode&S_IFDIR)
811 git_object *object;
812 git_tree_entry_2object(&object, entry);
813 CStringA parent = base;
814 parent += git_tree_entry_name(entry);
815 parent += "/";
816 ReadTreeRecurive((git_tree*)object,parent, CallBack,data);
822 return 0;
825 int CGitHeadFileList::ReadTree()
827 CStringA gitdir = CUnicodeUtils::GetMulti(m_Gitdir,CP_ACP) ;
828 gitdir += "\\.git";
829 git_repository *repository = NULL;
830 git_commit *commit = NULL;
831 git_tree * tree = NULL;
832 int ret =0;
835 ret = git_repository_open(&repository, gitdir.GetBuffer());
836 if(ret)
837 break;
838 ret = git_commit_lookup(&commit, repository, (const git_oid*)m_Head.m_hash);
839 if(ret)
840 break;
842 tree = (git_tree*)git_commit_tree(commit);
844 ret = ReadTreeRecurive(tree,"", CGitHeadFileList::CallBack,this);
845 if(ret)
846 break;
848 std::sort(this->begin(), this->end(), SortTree);
849 this->m_TreeHash = (char*)(git_commit_id(commit)->id);
851 }while(0);
853 if(repository)
854 git_repository_free(repository);
856 return ret;
859 int CGitIgnoreItem::FetchIgnoreList(const CString &projectroot, const CString &file)
861 CAutoWriteLock lock(&this->m_SharedMutex);
863 if(this->m_pExcludeList)
865 free(m_pExcludeList);
866 m_pExcludeList=NULL;
869 this->m_BaseDir.Empty();
870 if( projectroot.GetLength() < file.GetLength())
872 CString base = file.Mid(projectroot.GetLength()+1);
873 base.Replace(_T('\\'), _T('/'));
874 if(base != _T(".git/info/exclude"))
876 int start=base.ReverseFind(_T('/'));
877 if(start>=0)
879 base=base.Left(start);
880 this->m_BaseDir = CUnicodeUtils::GetMulti(base,CP_ACP) ;
886 if(g_Git.GetFileModifyTime(file,&m_LastModifyTime))
887 return -1;
889 if(git_create_exclude_list(&this->m_pExcludeList))
890 return -1;
893 HANDLE hfile = CreateFile(file,
894 GENERIC_READ,
895 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
896 NULL,
897 OPEN_EXISTING,
898 FILE_ATTRIBUTE_NORMAL,
899 NULL);
902 if(hfile == INVALID_HANDLE_VALUE)
904 return -1 ;
907 DWORD size=0,filesize=0;
909 filesize=GetFileSize(hfile,NULL);
911 if(filesize == INVALID_FILE_SIZE )
913 CloseHandle(hfile);
914 return -1;
917 BYTE *buffer = new BYTE[filesize+1];
919 if(buffer == NULL)
921 CloseHandle(hfile);
922 return -1;
925 if(! ReadFile( hfile, buffer,filesize,&size,NULL) )
927 CloseHandle(hfile);
928 return GetLastError();
931 CloseHandle(hfile);
933 BYTE *p = buffer;
934 for(int i=0;i<size;i++)
936 if( buffer[i] == '\n' || buffer[i] =='\r' || i==(size-1) )
938 if (buffer[i] == '\n' || buffer[i] =='\r')
939 buffer[i]=0;
940 if( i== size-1)
941 buffer[size]=0;
943 if(p[0] != '#' && p[0] != 0)
944 git_add_exclude((const char*)p,
945 this->m_BaseDir.GetBuffer(),
946 m_BaseDir.GetLength(),
947 this->m_pExcludeList);
949 p=buffer+i+1;
952 /* Can't free buffer, exluced list will use this buffer*/
953 //delete buffer;
954 //buffer=NULL;
956 return 0;
959 bool CGitIgnoreList::CheckFileChanged(const CString &path)
961 __int64 time=0;
963 int ret=g_Git.GetFileModifyTime(path, &time);
965 this->m_SharedMutex.AcquireShared();
966 bool cacheExist = (m_Map.find(path) != m_Map.end());
967 this->m_SharedMutex.ReleaseShared();
969 if (!cacheExist && ret == 0)
971 CAutoWriteLock lock(&this->m_SharedMutex);
972 m_Map[path].m_LastModifyTime = 0;
973 m_Map[path].m_SharedMutex.Init();
975 // both cache and file is not exist
976 if( (ret != 0) && (!cacheExist))
977 return false;
979 // file exist but cache miss
980 if( (ret == 0) && (!cacheExist))
981 return true;
983 // file not exist but cache exist
984 if( (ret != 0) && (cacheExist))
986 return true;
988 // file exist and cache exist
991 CAutoReadLock lock(&this->m_SharedMutex);
992 if( m_Map[path].m_LastModifyTime == time )
993 return false;
995 return true;
999 bool CGitIgnoreList::CheckIgnoreChanged(const CString &gitdir,const CString &path)
1001 CString temp;
1002 temp=gitdir;
1003 temp+=_T("\\");
1004 temp+=path;
1006 temp.Replace(_T('/'), _T('\\'));
1008 while(!temp.IsEmpty())
1010 temp+=_T("\\.git");
1012 if(CGit::GitPathFileExists(temp))
1014 CString gitignore=temp;
1015 gitignore += _T("ignore");
1016 if( CheckFileChanged(gitignore) )
1017 return true;
1019 temp+=_T("\\info\\exclude");
1021 if( CheckFileChanged(temp) )
1022 return true;
1023 else
1024 return false;
1025 }else
1027 temp+=_T("ignore");
1028 if( CheckFileChanged(temp) )
1029 return true;
1032 int found=0;
1033 int i;
1034 for( i=temp.GetLength() -1;i>=0;i--)
1036 if(temp[i] == _T('\\'))
1037 found ++;
1039 if(found == 2)
1040 break;
1043 temp = temp.Left(i);
1045 return true;
1048 int CGitIgnoreList::FetchIgnoreFile(const CString &gitdir, const CString &gitignore)
1050 if(CGit::GitPathFileExists(gitignore)) //if .gitignore remove, we need remote cache
1052 CAutoWriteLock lock(&this->m_SharedMutex);
1053 if(m_Map.find(gitignore) == m_Map.end())
1054 m_Map[gitignore].m_SharedMutex.Init();
1056 m_Map[gitignore].FetchIgnoreList(gitdir,gitignore);
1058 else
1060 CAutoWriteLock lock(&this->m_SharedMutex);
1061 if(m_Map.find(gitignore) != m_Map.end())
1062 m_Map[gitignore].m_SharedMutex.Release();
1064 m_Map.erase(gitignore);
1066 return 0;
1069 int CGitIgnoreList::LoadAllIgnoreFile(const CString &gitdir,const CString &path)
1071 CString temp;
1073 temp=gitdir;
1074 temp+=_T("\\");
1075 temp+=path;
1077 temp.Replace(_T('/'), _T('\\'));
1079 while(!temp.IsEmpty())
1081 temp+=_T("\\.git");
1083 if(CGit::GitPathFileExists(temp))
1085 CString gitignore = temp;
1086 gitignore += _T("ignore");
1087 if( CheckFileChanged(gitignore) )
1089 FetchIgnoreFile(gitdir,gitignore);
1092 temp+=_T("\\info\\exclude");
1094 if( CheckFileChanged(temp) )
1096 return FetchIgnoreFile(gitdir,temp);
1099 return 0;
1101 }else
1103 temp+=_T("ignore");
1104 if( CheckFileChanged(temp) )
1106 FetchIgnoreFile(gitdir,temp);
1110 int found=0;
1111 int i;
1112 for( i=temp.GetLength() -1;i>=0;i--)
1114 if(temp[i] == _T('\\'))
1115 found ++;
1117 if(found == 2)
1118 break;
1121 temp = temp.Left(i);
1123 return 0;
1125 int CGitIgnoreList::GetIgnoreFileChangeTimeList(const CString &path, std::vector<__int64> &timelist)
1127 CString temp=path;
1128 CString ignore=temp;
1129 int start =0;
1132 CAutoReadLock lock(&this->m_SharedMutex);
1134 ignore=temp;
1135 ignore+=_T("\\.gitignore");
1136 std::map<CString, CGitIgnoreItem>::iterator itMap;
1137 itMap = m_Map.find(ignore);
1138 if(itMap == m_Map.end())
1140 timelist.push_back(0);
1142 }else
1144 timelist.push_back(itMap->second.m_LastModifyTime);
1147 ignore =temp;
1148 ignore += _T("\\.git\\info\\exclude");
1150 itMap = m_Map.find(ignore);
1151 if(itMap == m_Map.end())
1154 }else
1156 timelist.push_back(itMap->second.m_LastModifyTime);
1157 return 0;
1160 ignore=temp;
1161 ignore+=_T("\\.git");
1163 if(CGit::GitPathFileExists(ignore))
1164 return 0;
1166 start = temp.ReverseFind(_T('\\'));
1167 if(start>0)
1168 temp=temp.Left(start);
1170 }while(start>0);
1172 return -1;
1175 bool CGitIgnoreList::IsIgnore(const CString &path,const CString &projectroot)
1177 CString str=path;
1179 str.Replace(_T('\\'),_T('/'));
1181 int ret;
1182 ret = CheckIgnore(path, projectroot);
1183 while(ret < 0)
1185 int start=str.ReverseFind(_T('/'));
1186 if(start<0)
1187 return (ret == 1);
1189 str=str.Left(start);
1190 ret = CheckIgnore(str, projectroot);
1193 return (ret == 1);
1195 int CGitIgnoreList::CheckIgnore(const CString &path,const CString &projectroot)
1197 __int64 time=0;
1198 bool dir=0;
1199 CString temp=projectroot+_T("\\")+path;
1200 temp.Replace(_T('/'), _T('\\'));
1202 CStringA patha;
1204 patha = CUnicodeUtils::GetMulti(path,CP_ACP) ;
1205 patha.Replace('\\','/');
1207 if(g_Git.GetFileModifyTime(temp,&time,&dir))
1208 return -1;
1210 int type=0;
1211 if( dir )
1212 type = DT_DIR;
1213 else
1214 type = DT_REG;
1216 while(!temp.IsEmpty())
1218 int x;
1219 x=temp.ReverseFind(_T('\\'));
1220 if(x<0) x=0;
1221 temp=temp.Left(x);
1223 temp+=_T("\\.gitignore");
1225 char *base;
1227 patha.Replace('\\', '/');
1228 int pos=patha.ReverseFind('/');
1229 base = pos>=0? patha.GetBuffer()+pos+1:patha.GetBuffer();
1231 CAutoReadLock lock(&this->m_SharedMutex);
1233 if(this->m_Map.find(temp) == m_Map.end() )
1236 }else
1238 int ret=-1;
1240 if(m_Map[temp].m_pExcludeList)
1241 ret = git_check_excluded_1( patha, patha.GetLength(), base, &type, m_Map[temp].m_pExcludeList);
1243 if(ret == 1)
1244 return 1;
1245 if(ret == 0)
1246 return 0;
1249 temp = temp.Left(temp.GetLength()-11);
1250 temp +=_T("\\.git\\info\\exclude");
1252 if(this->m_Map.find(temp) == m_Map.end() )
1255 }else
1257 int ret=-1;
1259 if(m_Map[temp].m_pExcludeList)
1260 ret = git_check_excluded_1( patha, patha.GetLength(), base, &type, m_Map[temp].m_pExcludeList);
1262 if(ret == 1)
1263 return 1;
1264 if(ret == 0)
1265 return 0;
1267 return -1;
1269 temp = temp.Left(temp.GetLength()-18);
1272 return -1;
1275 int CGitHeadFileMap::CheckHeadUpdate(const CString &gitdir)
1277 SHARED_TREE_PTR ptr;
1278 ptr = this->SafeGet(gitdir);
1280 if( ptr.get())
1282 ptr->CheckHeadUpdate();
1284 else
1286 SHARED_TREE_PTR ptr1(new CGitHeadFileList);
1287 this->SafeSet(gitdir, ptr1);
1289 return 0;
1292 int CGitHeadFileMap::GetHeadHash(const CString &gitdir, CGitHash &hash)
1294 SHARED_TREE_PTR ptr;
1295 ptr = this->SafeGet(gitdir);
1297 if(ptr.get() == NULL)
1299 SHARED_TREE_PTR ptr1(new CGitHeadFileList());
1300 if(ptr1->CheckHeadUpdate())
1301 ptr1->ReadHeadHash(gitdir);
1303 hash = ptr1->m_Head;
1305 this->SafeSet(gitdir, ptr1);
1307 }else
1309 if(ptr->CheckHeadUpdate())
1311 SHARED_TREE_PTR ptr1(new CGitHeadFileList());
1312 if(ptr1->CheckHeadUpdate())
1313 ptr1->ReadHeadHash(gitdir);
1315 hash = ptr1->m_Head;
1316 this->SafeSet(gitdir, ptr1);
1319 hash = ptr->m_Head;
1321 return 0;
1323 #if 0
1325 int CGitStatus::GetStatus(const CString &gitdir, const CString &path, git_wc_status_kind *status, BOOL IsFull, BOOL IsRecursive , FIll_STATUS_CALLBACK callback , void *pData)
1327 int result;
1328 __int64 time;
1329 bool dir;
1331 git_wc_status_kind dirstatus = git_wc_status_none;
1332 if(status)
1334 g_Git.GetFileModifyTime(path,&time,&dir);
1335 if( dir)
1337 }else
1341 if(path.IsEmpty())
1342 result = _tstat64( gitdir, &buf );
1343 else
1344 result = _tstat64( gitdir+_T("\\")+path, &buf );
1346 if(result)
1347 return -1;
1349 if(buf.st_mode & _S_IFDIR)
1351 if(!path.IsEmpty())
1353 if( path.Right(1) != _T("\\"))
1354 path+=_T("\\");
1356 int len =path.GetLength();
1358 for(int i=0;i<size();i++)
1360 if( at(i).m_FileName.GetLength() > len )
1362 if(at(i).m_FileName.Left(len) == path)
1364 if( !IsFull )
1366 *status = git_wc_status_normal;
1367 if(callback)
1368 callback(gitdir+_T("\\")+path,*status,pData);
1369 return 0;
1371 }else
1373 result = _tstat64( gitdir+_T("\\")+at(i).m_FileName, &buf );
1374 if(result)
1375 continue;
1377 *status = git_wc_status_none;
1378 GetFileStatus(gitdir,at(i).m_FileName,status,buf,callback,pData);
1379 if( *status != git_wc_status_none )
1381 if( dirstatus == git_wc_status_none)
1383 dirstatus = git_wc_status_normal;
1385 if( *status != git_wc_status_normal )
1387 dirstatus = git_wc_status_modified;
1396 if( dirstatus != git_wc_status_none )
1398 *status = dirstatus;
1400 else
1402 *status = git_wc_status_unversioned;
1404 if(callback)
1405 callback(gitdir+_T("\\")+path,*status,pData);
1407 return 0;
1409 }else
1411 GetFileStatus(gitdir,path,status,buf,callback,pData);
1414 return 0;
1417 #endif