refactored ignore file checking
[TortoiseGit.git] / src / Git / GitIndex.cpp
blobdd42e0570e88972d87e787a791d478d74c596ad8
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2012 - 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"
33 #include "SmartHandle.h"
35 CGitAdminDirMap g_AdminDirMap;
37 #define FILL_DATA() \
38 m_FileName.Empty();\
39 g_Git.StringAppend(&m_FileName, (BYTE*)entry->name, CP_UTF8, Big2lit(entry->flags)&CE_NAMEMASK);\
40 m_FileName.MakeLower(); \
41 this->m_Flags=Big2lit(entry->flags);\
42 this->m_ModifyTime=Big2lit(entry->mtime.sec);\
43 this->m_IndexHash=(char*)(entry->sha1);
45 int CGitIndex::FillData(ondisk_cache_entry * entry)
47 FILL_DATA();
48 return 0;
51 int CGitIndex::FillData(ondisk_cache_entry_extended * entry)
53 FILL_DATA();
54 this->m_Flags |= ((int)Big2lit(entry->flags2))<<16;
55 return 0;
58 int CGitIndex::Print()
60 _tprintf(_T("0x%08X 0x%08X %s %s\n"),
61 (int)this->m_ModifyTime,
62 this->m_Flags,
63 this->m_IndexHash.ToString(),
64 this->m_FileName);
66 return 0;
69 CGitIndexList::CGitIndexList()
71 this->m_LastModifyTime = 0;
74 static bool SortIndex(CGitIndex &Item1, CGitIndex &Item2)
76 return Item1.m_FileName.Compare(Item2.m_FileName) < 0;
79 static bool SortTree(CGitTreeItem &Item1, CGitTreeItem &Item2)
81 return Item1.m_FileName.Compare(Item2.m_FileName) < 0;
84 int CGitIndexList::ReadIndex(CString IndexFile)
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 CAutoFile 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)
111 ret = -1 ;
112 break;
115 CAutoFile hmap = CreateFileMapping(hfile, NULL, PAGE_READONLY, 0, 0, NULL);
116 if (!hmap)
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);
154 else
156 this->at(i).FillData(entry);
157 p += ondisk_ce_size(entry);
161 std::sort(this->begin(), this->end(), SortIndex);
162 g_Git.GetFileModifyTime(IndexFile, &this->m_LastModifyTime);
163 } while(0);
165 catch(...)
167 ret= -1;
170 if (buffer)
171 UnmapViewOfFile(buffer);
173 return ret;
176 int CGitIndexList::GetFileStatus(const CString &gitdir,const CString &pathorg,git_wc_status_kind *status,__int64 time,FIll_STATUS_CALLBACK callback,void *pData, CGitHash *pHash)
178 if(status)
180 CString path = pathorg;
181 path.MakeLower();
183 int start = SearchInSortVector(*this, ((CString&)path).GetBuffer(), -1);
184 ((CString&)path).ReleaseBuffer();
186 if (start < 0)
188 *status = git_wc_status_unversioned;
189 if (pHash)
190 pHash->Empty();
193 else
195 int index = start;
196 if (index <0)
197 return -1;
198 if (index >= size() )
199 return -1;
201 if (time == at(index).m_ModifyTime)
203 *status = git_wc_status_normal;
205 else
207 *status = git_wc_status_modified;
210 if (at(index).m_Flags & CE_STAGEMASK )
211 *status = git_wc_status_conflicted;
212 else if (at(index).m_Flags & CE_INTENT_TO_ADD)
213 *status = git_wc_status_added;
215 if(pHash)
216 *pHash = at(index).m_IndexHash;
221 if(callback && status)
222 callback(gitdir + _T("\\") + pathorg, *status, false, pData);
223 return 0;
226 int CGitIndexList::GetStatus(const CString &gitdir,const CString &pathParam, git_wc_status_kind *status,
227 BOOL IsFull, BOOL IsRecursive,
228 FIll_STATUS_CALLBACK callback,void *pData,
229 CGitHash *pHash)
231 int result;
232 git_wc_status_kind dirstatus = git_wc_status_none;
233 __int64 time;
234 bool isDir = false;
235 CString path = pathParam;
237 if (status)
239 if (path.IsEmpty())
240 result = g_Git.GetFileModifyTime(gitdir, &time, &isDir);
241 else
242 result = g_Git.GetFileModifyTime(gitdir + _T("\\") + path, &time, &isDir);
244 if (result)
246 *status = git_wc_status_deleted;
247 if (callback)
248 callback(gitdir + _T("\\") + path, git_wc_status_deleted, false, pData);
250 return 0;
252 if (isDir)
254 if (!path.IsEmpty())
256 if (path.Right(1) != _T("\\"))
257 path += _T("\\");
259 int len = path.GetLength();
261 for (int i = 0; i < size(); i++)
263 if (at(i).m_FileName.GetLength() > len)
265 if (at(i).m_FileName.Left(len) == path)
267 if (!IsFull)
269 *status = git_wc_status_normal;
270 if (callback)
271 callback(gitdir + _T("\\") + path, *status, false, pData);
272 return 0;
275 else
277 result = g_Git.GetFileModifyTime(gitdir+_T("\\") + at(i).m_FileName, &time);
278 if (result)
279 continue;
281 *status = git_wc_status_none;
282 GetFileStatus(gitdir, at(i).m_FileName, status, time, callback, pData);
283 if (*status != git_wc_status_none)
285 if (dirstatus == git_wc_status_none)
287 dirstatus = git_wc_status_normal;
289 if (*status != git_wc_status_normal)
291 dirstatus = git_wc_status_modified;
298 } /* End For */
300 if (dirstatus != git_wc_status_none)
302 *status = dirstatus;
304 else
306 *status = git_wc_status_unversioned;
308 if(callback)
309 callback(gitdir + _T("\\") + path, *status, false, pData);
311 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 = g_AdminDirMap.GetAdminDir(gitdir) + _T("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 CString IndexFile = g_AdminDirMap.GetAdminDir(gitdir) + _T("index");
366 if(pIndex->ReadIndex(IndexFile))
367 return -1;
369 this->SafeSet(gitdir, pIndex);
371 }catch(...)
373 return -1;
375 return 0;
378 int CGitIndexFileMap::GetFileStatus(const CString &gitdir, const CString &path, git_wc_status_kind *status,BOOL IsFull, BOOL IsRecursive,
379 FIll_STATUS_CALLBACK callback,void *pData,
380 CGitHash *pHash,
381 bool isLoadUpdatedIndex)
385 CheckAndUpdate(gitdir, isLoadUpdatedIndex);
387 SHARED_INDEX_PTR pIndex = this->SafeGet(gitdir);
388 if (pIndex.get() != NULL)
390 pIndex->GetStatus(gitdir, path, status, IsFull, IsRecursive, callback, pData, pHash);
394 catch(...)
396 return -1;
398 return 0;
401 int CGitIndexFileMap::IsUnderVersionControl(const CString &gitdir, const CString &path, bool isDir,bool *isVersion, bool isLoadUpdateIndex)
405 if (path.IsEmpty())
407 *isVersion = true;
408 return 0;
411 CString subpath = path;
412 subpath.Replace(_T('\\'), _T('/'));
413 if(isDir)
414 subpath += _T('/');
416 subpath.MakeLower();
418 CheckAndUpdate(gitdir, isLoadUpdateIndex);
420 SHARED_INDEX_PTR pIndex = this->SafeGet(gitdir);
422 if(pIndex.get())
424 if(isDir)
425 *isVersion = (SearchInSortVector(*pIndex, subpath.GetBuffer(), subpath.GetLength()) >= 0);
426 else
427 *isVersion = (SearchInSortVector(*pIndex, subpath.GetBuffer(), -1) >= 0);
428 subpath.ReleaseBuffer();
431 }catch(...)
433 return -1;
435 return 0;
438 int CGitHeadFileList::GetPackRef(const CString &gitdir)
440 CString PackRef = g_AdminDirMap.GetAdminDir(gitdir) + _T("packed-refs");
442 __int64 mtime;
443 if (g_Git.GetFileModifyTime(PackRef, &mtime))
445 //packed refs is not existed
446 this->m_PackRefFile.Empty();
447 this->m_PackRefMap.clear();
448 return 0;
450 else if(mtime == m_LastModifyTimePackRef)
452 return 0;
454 else
456 this->m_PackRefFile = PackRef;
457 this->m_LastModifyTimePackRef = mtime;
460 int ret = 0;
462 this->m_PackRefMap.clear();
464 CAutoFile hfile = CreateFile(PackRef,
465 GENERIC_READ,
466 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
467 NULL,
468 OPEN_EXISTING,
469 FILE_ATTRIBUTE_NORMAL,
470 NULL);
473 if (!hfile)
475 ret = -1;
476 break;
479 DWORD filesize = GetFileSize(hfile, NULL);
480 DWORD size =0;
481 char *buff;
482 buff = new char[filesize];
484 ReadFile(hfile, buff, filesize, &size, NULL);
486 if (size != filesize)
488 ret = -1;
489 break;
492 CString hash;
493 CString ref;
495 for(DWORD i=0;i<filesize;)
497 hash.Empty();
498 ref.Empty();
499 if (buff[i] == '#' || buff[i] == '^')
501 while (buff[i] != '\n')
503 i++;
504 if (i == filesize)
505 break;
507 i++;
510 if (i >= filesize)
511 break;
513 while (buff[i] != ' ')
515 hash.AppendChar(buff[i]);
516 i++;
517 if (i == filesize)
518 break;
521 i++;
522 if (i >= filesize)
523 break;
525 while (buff[i] != '\n')
527 ref.AppendChar(buff[i]);
528 i++;
529 if (i == filesize)
530 break;
533 if (!ref.IsEmpty() )
535 this->m_PackRefMap[ref] = hash;
538 while (buff[i] == '\n')
540 i++;
541 if (i == filesize)
542 break;
546 delete buff;
548 } while(0);
550 return ret;
553 int CGitHeadFileList::ReadHeadHash(CString gitdir)
555 int ret = 0;
556 m_Gitdir = g_AdminDirMap.GetAdminDir(gitdir);
558 m_HeadFile = m_Gitdir + _T("HEAD");
560 if( g_Git.GetFileModifyTime(m_HeadFile, &m_LastModifyTimeHead))
561 return -1;
567 CAutoFile hfile = CreateFile(m_HeadFile,
568 GENERIC_READ,
569 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
570 NULL,
571 OPEN_EXISTING,
572 FILE_ATTRIBUTE_NORMAL,
573 NULL);
575 if (!hfile)
577 ret = -1;
578 break;
581 DWORD size = 0,filesize = 0;
582 unsigned char buffer[40] ;
583 ReadFile(hfile, buffer, 4, &size, NULL);
584 if (size != 4)
586 ret = -1;
587 break;
589 buffer[4]=0;
590 if (strcmp((const char*)buffer,"ref:") == 0)
592 filesize = GetFileSize(hfile, NULL);
594 unsigned char *p = (unsigned char*)malloc(filesize -4);
596 ReadFile(hfile, p, filesize - 4, &size, NULL);
598 m_HeadRefFile.Empty();
599 g_Git.StringAppend(&this->m_HeadRefFile, p, CP_UTF8, filesize - 4);
600 CString ref = this->m_HeadRefFile;
601 ref = ref.Trim();
602 int start = 0;
603 ref = ref.Tokenize(_T("\n"), start);
604 free(p);
605 m_HeadRefFile = m_Gitdir + m_HeadRefFile.Trim();
606 m_HeadRefFile.Replace(_T('/'),_T('\\'));
608 __int64 time;
609 if (g_Git.GetFileModifyTime(m_HeadRefFile, &time, NULL))
611 m_HeadRefFile.Empty();
612 if (GetPackRef(gitdir))
614 ret = -1;
615 break;
617 if (this->m_PackRefMap.find(ref) == m_PackRefMap.end())
619 ret = -1;
620 break;
622 this ->m_Head = m_PackRefMap[ref];
623 ret = 0;
624 break;
627 CAutoFile href = CreateFile(m_HeadRefFile,
628 GENERIC_READ,
629 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
630 NULL,
631 OPEN_EXISTING,
632 FILE_ATTRIBUTE_NORMAL,
633 NULL);
635 if (!href)
637 m_HeadRefFile.Empty();
639 if (GetPackRef(gitdir))
641 ret = -1;
642 break;
645 if (this->m_PackRefMap.find(ref) == m_PackRefMap.end())
647 ret = -1;
648 break;
650 this ->m_Head = m_PackRefMap[ref];
651 ret = 0;
652 break;
654 ReadFile(href, buffer, 40, &size, NULL);
655 if (size != 40)
657 ret = -1;
658 break;
660 this->m_Head.ConvertFromStrA((char*)buffer);
662 this->m_LastModifyTimeRef = time;
665 else
667 ReadFile(hfile, buffer + 4, 40 - 4, &size, NULL);
668 if(size != 36)
670 ret = -1;
671 break;
673 m_HeadRefFile.Empty();
675 this->m_Head.ConvertFromStrA((char*)buffer);
677 } while(0);
679 catch(...)
681 ret = -1;
684 return ret;
687 bool CGitHeadFileList::CheckHeadUpdate()
689 if (this->m_HeadFile.IsEmpty())
690 return true;
692 __int64 mtime=0;
694 if (g_Git.GetFileModifyTime(m_HeadFile, &mtime))
695 return true;
697 if (mtime != this->m_LastModifyTimeHead)
698 return true;
700 if (!this->m_HeadRefFile.IsEmpty())
702 if (g_Git.GetFileModifyTime(m_HeadRefFile, &mtime))
703 return true;
705 if (mtime != this->m_LastModifyTimeRef)
706 return true;
709 if(!this->m_PackRefFile.IsEmpty())
711 if (g_Git.GetFileModifyTime(m_PackRefFile, &mtime))
712 return true;
714 if (mtime != this->m_LastModifyTimePackRef)
715 return true;
718 // in an empty repo HEAD points to refs/heads/master, but this ref doesn't exist.
719 // So we need to retry again and again until the ref exists - otherwise we will never notice
720 if (this->m_Head.IsEmpty() && this->m_HeadRefFile.IsEmpty() && this->m_PackRefFile.IsEmpty())
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_UTF8, baselen);
779 g_Git.StringAppend(&p->at(cur).m_FileName,(BYTE*)pathname, CP_UTF8);
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 ReadTreeRecursive(git_repository &repo, 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 const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
799 if (entry == NULL)
800 continue;
801 int mode = git_tree_entry_attributes(entry);
802 if( CallBack(git_tree_entry_id(entry)->id,
803 base,
804 base.GetLength(),
805 git_tree_entry_name(entry),
806 mode,
808 data) == READ_TREE_RECURSIVE
811 if(mode&S_IFDIR)
813 git_object *object = NULL;
814 git_tree_entry_to_object(&object, &repo, entry);
815 if (object == NULL)
816 continue;
817 CStringA parent = base;
818 parent += git_tree_entry_name(entry);
819 parent += "/";
820 ReadTreeRecursive(repo, (git_tree*)object, parent, CallBack, data);
821 git_object_free(object);
827 return 0;
830 int CGitHeadFileList::ReadTree()
832 CStringA gitdir = CUnicodeUtils::GetMulti(m_Gitdir, CP_UTF8);
833 git_repository *repository = NULL;
834 git_commit *commit = NULL;
835 git_tree * tree = NULL;
836 int ret = 0;
837 this->clear(); // hack to avoid duplicates in the head list, which are introduced in GitStatus::GetFileStatus when this method is called
840 ret = git_repository_open(&repository, gitdir.GetBuffer());
841 if(ret)
842 break;
843 ret = git_commit_lookup(&commit, repository, (const git_oid*)m_Head.m_hash);
844 if(ret)
845 break;
847 ret = git_commit_tree(&tree, commit);
848 if(ret)
849 break;
851 ret = ReadTreeRecursive(*repository, tree,"", CGitHeadFileList::CallBack,this);
852 if(ret)
853 break;
855 std::sort(this->begin(), this->end(), SortTree);
856 this->m_TreeHash = (char*)(git_commit_id(commit)->id);
858 } while(0);
860 if (tree)
861 git_tree_free(tree);
863 if (commit)
864 git_commit_free(commit);
866 if (repository)
867 git_repository_free(repository);
869 return ret;
872 int CGitIgnoreItem::FetchIgnoreList(const CString &projectroot, const CString &file, bool isGlobal)
874 CAutoWriteLock lock(&this->m_SharedMutex);
876 if (this->m_pExcludeList)
878 free(m_pExcludeList);
879 m_pExcludeList=NULL;
882 this->m_BaseDir.Empty();
883 if (!isGlobal)
885 CString base = file.Mid(projectroot.GetLength());
886 base.Replace(_T('\\'), _T('/'));
888 int start = base.ReverseFind(_T('/'));
889 if(start > 0)
891 base = base.Left(start);
892 this->m_BaseDir = CUnicodeUtils::GetMulti(base, CP_UTF8);
897 if(g_Git.GetFileModifyTime(file, &m_LastModifyTime))
898 return -1;
900 if(git_create_exclude_list(&this->m_pExcludeList))
901 return -1;
904 CAutoFile hfile = CreateFile(file,
905 GENERIC_READ,
906 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
907 NULL,
908 OPEN_EXISTING,
909 FILE_ATTRIBUTE_NORMAL,
910 NULL);
913 if (!hfile)
914 return -1 ;
916 DWORD size=0,filesize=0;
918 filesize=GetFileSize(hfile, NULL);
920 if(filesize == INVALID_FILE_SIZE)
921 return -1;
923 BYTE *buffer = new BYTE[filesize + 1];
925 if(buffer == NULL)
926 return -1;
928 if(! ReadFile(hfile, buffer,filesize,&size,NULL))
929 return GetLastError();
931 BYTE *p = buffer;
932 for (int i = 0; i < size; i++)
934 if (buffer[i] == '\n' || buffer[i] == '\r' || i == (size - 1))
936 if (buffer[i] == '\n' || buffer[i] == '\r')
937 buffer[i] = 0;
938 if (i == size - 1)
939 buffer[size] = 0;
941 if(p[0] != '#' && p[0] != 0)
942 git_add_exclude((const char*)p,
943 this->m_BaseDir.GetBuffer(),
944 m_BaseDir.GetLength(),
945 this->m_pExcludeList);
947 p=buffer+i+1;
950 /* Can't free buffer, exluced list will use this buffer*/
951 //delete buffer;
952 //buffer = NULL;
954 return 0;
957 bool CGitIgnoreList::CheckFileChanged(const CString &path)
959 __int64 time = 0;
961 int ret = g_Git.GetFileModifyTime(path, &time);
963 this->m_SharedMutex.AcquireShared();
964 bool cacheExist = (m_Map.find(path) != m_Map.end());
965 this->m_SharedMutex.ReleaseShared();
967 if (!cacheExist && ret == 0)
969 CAutoWriteLock lock(&this->m_SharedMutex);
970 m_Map[path].m_LastModifyTime = 0;
971 m_Map[path].m_SharedMutex.Init();
973 // both cache and file is not exist
974 if ((ret != 0) && (!cacheExist))
975 return false;
977 // file exist but cache miss
978 if ((ret == 0) && (!cacheExist))
979 return true;
981 // file not exist but cache exist
982 if ((ret != 0) && (cacheExist))
984 return true;
986 // file exist and cache exist
989 CAutoReadLock lock(&this->m_SharedMutex);
990 if (m_Map[path].m_LastModifyTime == time)
991 return false;
993 return true;
996 bool CGitIgnoreList::CheckIgnoreChanged(const CString &gitdir,const CString &path)
998 CString temp;
999 temp = gitdir;
1000 temp += _T("\\");
1001 temp += path;
1003 temp.Replace(_T('/'), _T('\\'));
1005 while(!temp.IsEmpty())
1007 CString tempOrig = temp;
1008 temp += _T("\\.git");
1010 if (CGit::GitPathFileExists(temp))
1012 CString gitignore=temp;
1013 gitignore += _T("ignore");
1014 if (CheckFileChanged(gitignore))
1015 return true;
1017 CString wcglobalgitignore = g_AdminDirMap.GetAdminDir(tempOrig) + _T("info\\exclude");
1018 if (CheckFileChanged(wcglobalgitignore))
1019 return true;
1020 else
1021 return false;
1023 else
1025 temp += _T("ignore");
1026 if (CheckFileChanged(temp))
1027 return true;
1030 int found=0;
1031 int i;
1032 for (i = temp.GetLength() - 1; i >= 0; i--)
1034 if(temp[i] == _T('\\'))
1035 found ++;
1037 if(found == 2)
1038 break;
1041 temp = temp.Left(i);
1043 return true;
1046 int CGitIgnoreList::FetchIgnoreFile(const CString &gitdir, const CString &gitignore, bool isGlobal)
1048 if (CGit::GitPathFileExists(gitignore)) //if .gitignore remove, we need remote cache
1050 CAutoWriteLock lock(&this->m_SharedMutex);
1051 if (m_Map.find(gitignore) == m_Map.end())
1052 m_Map[gitignore].m_SharedMutex.Init();
1054 m_Map[gitignore].FetchIgnoreList(gitdir, gitignore, isGlobal);
1056 else
1058 CAutoWriteLock lock(&this->m_SharedMutex);
1059 if (m_Map.find(gitignore) != m_Map.end())
1060 m_Map[gitignore].m_SharedMutex.Release();
1062 m_Map.erase(gitignore);
1064 return 0;
1067 int CGitIgnoreList::LoadAllIgnoreFile(const CString &gitdir,const CString &path)
1069 CString temp;
1071 temp = gitdir;
1072 temp += _T("\\");
1073 temp += path;
1075 temp.Replace(_T('/'), _T('\\'));
1077 while (!temp.IsEmpty())
1079 CString tempOrig = temp;
1080 temp += _T("\\.git");
1082 if (CGit::GitPathFileExists(temp))
1084 CString gitignore = temp;
1085 gitignore += _T("ignore");
1086 if (CheckFileChanged(gitignore))
1088 FetchIgnoreFile(gitdir, gitignore, false);
1091 CString wcglobalgitignore = g_AdminDirMap.GetAdminDir(tempOrig) + _T("info\\exclude");
1092 if (CheckFileChanged(wcglobalgitignore))
1094 return FetchIgnoreFile(gitdir, wcglobalgitignore, true);
1097 return 0;
1099 else
1101 temp += _T("ignore");
1102 if (CheckFileChanged(temp))
1104 FetchIgnoreFile(gitdir, temp, false);
1108 int found = 0;
1109 int i;
1110 for (i = temp.GetLength() - 1; i >= 0; i--)
1112 if(temp[i] == _T('\\'))
1113 found ++;
1115 if(found == 2)
1116 break;
1119 temp = temp.Left(i);
1121 return 0;
1123 int CGitIgnoreList::GetIgnoreFileChangeTimeList(const CString &path, std::vector<__int64> &timelist)
1125 CString temp = path;
1126 CString ignore = temp;
1127 int start = 0;
1130 CAutoReadLock lock(&this->m_SharedMutex);
1132 ignore=temp;
1133 ignore += _T("\\.gitignore");
1134 std::map<CString, CGitIgnoreItem>::iterator itMap;
1135 itMap = m_Map.find(ignore);
1136 if (itMap == m_Map.end())
1138 timelist.push_back(0);
1140 else
1142 timelist.push_back(itMap->second.m_LastModifyTime);
1145 ignore = g_AdminDirMap.GetAdminDir(temp) + _T("info\\exclude");
1146 itMap = m_Map.find(ignore);
1147 if (itMap == m_Map.end())
1151 else
1153 timelist.push_back(itMap->second.m_LastModifyTime);
1154 return 0;
1157 ignore = temp;
1158 ignore += _T("\\.git");
1160 if (CGit::GitPathFileExists(ignore))
1161 return 0;
1163 start = temp.ReverseFind(_T('\\'));
1164 if (start > 0)
1165 temp=temp.Left(start);
1167 } while(start > 0);
1169 return -1;
1172 bool CGitIgnoreList::IsIgnore(const CString &path,const CString &projectroot)
1174 CString str=path;
1176 str.Replace(_T('\\'),_T('/'));
1178 if (str.GetLength()>0)
1179 if (str[str.GetLength()-1] == _T('/'))
1180 str = str.Left(str.GetLength() - 1);
1182 int ret;
1183 ret = CheckIgnore(str, projectroot);
1184 while (ret < 0)
1186 int start = str.ReverseFind(_T('/'));
1187 if(start < 0)
1188 return (ret == 1);
1190 str = str.Left(start);
1191 ret = CheckIgnore(str, projectroot);
1194 return (ret == 1);
1196 int CGitIgnoreList::CheckFileAgainstIgnoreList(const CString &ignorefile, const CStringA &patha, const char * base, int &type)
1198 if (m_Map.find(ignorefile) != m_Map.end())
1200 int ret = -1;
1201 if(m_Map[ignorefile].m_pExcludeList)
1202 ret = git_check_excluded_1(patha, patha.GetLength(), base, &type, m_Map[ignorefile].m_pExcludeList);
1203 if (ret == 0 || ret == 1)
1204 return ret;
1206 return -1;
1208 int CGitIgnoreList::CheckIgnore(const CString &path,const CString &projectroot)
1210 __int64 time = 0;
1211 bool dir = 0;
1212 CString temp = projectroot + _T("\\") + path;
1213 temp.Replace(_T('/'), _T('\\'));
1215 CStringA patha = CUnicodeUtils::GetMulti(path, CP_UTF8);
1216 patha.Replace('\\', '/');
1218 if(g_Git.GetFileModifyTime(temp, &time, &dir))
1219 return -1;
1221 int type = 0;
1222 if (dir)
1224 type = DT_DIR;
1226 // strip directory name
1227 // we do not need to check for a .ignore file inside a directory we might ignore
1228 int i = temp.ReverseFind(_T('\\'));
1229 if (i >= 0)
1230 temp = temp.Left(i);
1232 else
1233 type = DT_REG;
1235 char * base = NULL;
1236 int pos = patha.ReverseFind('/');
1237 base = pos >= 0 ? patha.GetBuffer() + pos + 1 : patha.GetBuffer();
1239 int ret = -1;
1241 CAutoReadLock lock(&this->m_SharedMutex);
1242 while (!temp.IsEmpty())
1244 CString tempOrig = temp;
1245 temp += _T("\\.git");
1247 if (CGit::GitPathFileExists(temp))
1249 CString gitignore = temp;
1250 gitignore += _T("ignore");
1251 if ((ret = CheckFileAgainstIgnoreList(gitignore, patha, base, type)) != -1)
1252 break;
1254 CString wcglobalgitignore = g_AdminDirMap.GetAdminDir(tempOrig) + _T("info\\exclude");
1255 ret = CheckFileAgainstIgnoreList(wcglobalgitignore, patha, base, type);
1256 break;
1258 else
1260 temp += _T("ignore");
1261 if ((ret = CheckFileAgainstIgnoreList(temp, patha, base, type)) != -1)
1262 break;
1265 int found = 0;
1266 int i;
1267 for (i = temp.GetLength() - 1; i >= 0; i--)
1269 if (temp[i] == _T('\\'))
1270 found++;
1272 if (found == 2)
1273 break;
1276 temp = temp.Left(i);
1279 patha.ReleaseBuffer();
1281 return ret;
1284 bool CGitHeadFileMap::CheckHeadUpdate(const CString &gitdir)
1286 SHARED_TREE_PTR ptr;
1287 ptr = this->SafeGet(gitdir);
1289 if( ptr.get())
1291 return ptr->CheckHeadUpdate();
1293 else
1295 SHARED_TREE_PTR ptr1(new CGitHeadFileList);
1296 ptr1->ReadHeadHash(gitdir);
1298 this->SafeSet(gitdir, ptr1);
1299 return true;
1301 return false;
1304 int CGitHeadFileMap::GetHeadHash(const CString &gitdir, CGitHash &hash)
1306 SHARED_TREE_PTR ptr;
1307 ptr = this->SafeGet(gitdir);
1309 if(ptr.get() == NULL)
1311 SHARED_TREE_PTR ptr1(new CGitHeadFileList());
1312 ptr1->ReadHeadHash(gitdir);
1314 hash = ptr1->m_Head;
1316 this->SafeSet(gitdir, ptr1);
1319 else
1321 if(ptr->CheckHeadUpdate())
1323 SHARED_TREE_PTR ptr1(new CGitHeadFileList());
1324 ptr1->ReadHeadHash(gitdir);
1326 hash = ptr1->m_Head;
1327 this->SafeSet(gitdir, ptr1);
1330 hash = ptr->m_Head;
1332 return 0;
1334 #if 0
1336 int CGitStatus::GetStatus(const CString &gitdir, const CString &path, git_wc_status_kind *status, BOOL IsFull, BOOL IsRecursive , FIll_STATUS_CALLBACK callback , void *pData)
1338 int result;
1339 __int64 time;
1340 bool dir;
1342 git_wc_status_kind dirstatus = git_wc_status_none;
1343 if (status)
1345 g_Git.GetFileModifyTime(path, &time, &dir);
1346 if(path.IsEmpty())
1347 result = _tstat64( gitdir, &buf );
1348 else
1349 result = _tstat64( gitdir+_T("\\")+path, &buf );
1351 if(result)
1352 return -1;
1354 if(buf.st_mode & _S_IFDIR)
1356 if(!path.IsEmpty())
1358 if( path.Right(1) != _T("\\"))
1359 path += _T("\\");
1361 int len = path.GetLength();
1363 for (int i = 0; i < size(); i++)
1365 if (at(i).m_FileName.GetLength() > len)
1367 if (at(i).m_FileName.Left(len) == path)
1369 if(!IsFull)
1371 *status = git_wc_status_normal;
1372 if(callback)
1373 callback(gitdir + _T("\\") + path, *status, pData);
1374 return 0;
1377 else
1379 result = _tstat64(gitdir + _T("\\") + at(i).m_FileName, &buf);
1380 if (result)
1381 continue;
1383 *status = git_wc_status_none;
1384 GetFileStatus(gitdir, at(i).m_FileName, status, buf, callback, pData);
1385 if (*status != git_wc_status_none)
1387 if (dirstatus == git_wc_status_none)
1389 dirstatus = git_wc_status_normal;
1391 if (*status != git_wc_status_normal)
1393 dirstatus = git_wc_status_modified;
1402 if (dirstatus != git_wc_status_none)
1404 *status = dirstatus;
1406 else
1408 *status = git_wc_status_unversioned;
1410 if(callback)
1411 callback(gitdir + _T("\\") + path, *status, pData);
1413 return 0;
1416 else
1418 GetFileStatus(gitdir, path, status, buf, callback, pData);
1421 return 0;
1424 #endif