TGitCache: fixed ignore overlays for working copies with separate-git-dir
[TortoiseGit.git] / src / Git / GitIndex.cpp
blobbab993ad53c38aa5af2400ba34e9654da51123b2
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_ACP, 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);
430 }catch(...)
432 return -1;
434 return 0;
437 int CGitHeadFileList::GetPackRef(const CString &gitdir)
439 CString PackRef = g_AdminDirMap.GetAdminDir(gitdir) + _T("packed-refs");
441 __int64 mtime;
442 if (g_Git.GetFileModifyTime(PackRef, &mtime))
444 //packed refs is not existed
445 this->m_PackRefFile.Empty();
446 this->m_PackRefMap.clear();
447 return 0;
449 else if(mtime == m_LastModifyTimePackRef)
451 return 0;
453 else
455 this->m_PackRefFile = PackRef;
456 this->m_LastModifyTimePackRef = mtime;
459 int ret = 0;
461 this->m_PackRefMap.clear();
463 CAutoFile hfile = CreateFile(PackRef,
464 GENERIC_READ,
465 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
466 NULL,
467 OPEN_EXISTING,
468 FILE_ATTRIBUTE_NORMAL,
469 NULL);
472 if (!hfile)
474 ret = -1;
475 break;
478 DWORD filesize = GetFileSize(hfile, NULL);
479 DWORD size =0;
480 char *buff;
481 buff = new char[filesize];
483 ReadFile(hfile, buff, filesize, &size, NULL);
485 if (size != filesize)
487 ret = -1;
488 break;
491 CString hash;
492 CString ref;
494 for(DWORD i=0;i<filesize;)
496 hash.Empty();
497 ref.Empty();
498 if (buff[i] == '#' || buff[i] == '^')
500 while (buff[i] != '\n')
502 i++;
503 if (i == filesize)
504 break;
506 i++;
509 if (i >= filesize)
510 break;
512 while (buff[i] != ' ')
514 hash.AppendChar(buff[i]);
515 i++;
516 if (i == filesize)
517 break;
520 i++;
521 if (i >= filesize)
522 break;
524 while (buff[i] != '\n')
526 ref.AppendChar(buff[i]);
527 i++;
528 if (i == filesize)
529 break;
532 if (!ref.IsEmpty() )
534 this->m_PackRefMap[ref] = hash;
537 while (buff[i] == '\n')
539 i++;
540 if (i == filesize)
541 break;
545 delete buff;
547 } while(0);
549 return ret;
552 int CGitHeadFileList::ReadHeadHash(CString gitdir)
554 int ret = 0;
555 m_Gitdir = g_AdminDirMap.GetAdminDir(gitdir);
557 m_HeadFile = m_Gitdir + _T("HEAD");
559 if( g_Git.GetFileModifyTime(m_HeadFile, &m_LastModifyTimeHead))
560 return -1;
566 CAutoFile hfile = CreateFile(m_HeadFile,
567 GENERIC_READ,
568 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
569 NULL,
570 OPEN_EXISTING,
571 FILE_ATTRIBUTE_NORMAL,
572 NULL);
574 if (!hfile)
576 ret = -1;
577 break;
580 DWORD size = 0,filesize = 0;
581 unsigned char buffer[40] ;
582 ReadFile(hfile, buffer, 4, &size, NULL);
583 if (size != 4)
585 ret = -1;
586 break;
588 buffer[4]=0;
589 if (strcmp((const char*)buffer,"ref:") == 0)
591 filesize = GetFileSize(hfile, NULL);
593 unsigned char *p = (unsigned char*)malloc(filesize -4);
595 ReadFile(hfile, p, filesize - 4, &size, NULL);
597 m_HeadRefFile.Empty();
598 g_Git.StringAppend(&this->m_HeadRefFile, p, CP_ACP, filesize - 4);
599 CString ref = this->m_HeadRefFile;
600 ref = ref.Trim();
601 int start = 0;
602 ref = ref.Tokenize(_T("\n"), start);
603 free(p);
604 m_HeadRefFile = m_Gitdir + m_HeadRefFile.Trim();
605 m_HeadRefFile.Replace(_T('/'),_T('\\'));
607 __int64 time;
608 if (g_Git.GetFileModifyTime(m_HeadRefFile, &time, NULL))
610 m_HeadRefFile.Empty();
611 if (GetPackRef(gitdir))
613 ret = -1;
614 break;
616 if (this->m_PackRefMap.find(ref) == m_PackRefMap.end())
618 ret = -1;
619 break;
621 this ->m_Head = m_PackRefMap[ref];
622 ret = 0;
623 break;
626 CAutoFile href = CreateFile(m_HeadRefFile,
627 GENERIC_READ,
628 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
629 NULL,
630 OPEN_EXISTING,
631 FILE_ATTRIBUTE_NORMAL,
632 NULL);
634 if (!href)
636 m_HeadRefFile.Empty();
638 if (GetPackRef(gitdir))
640 ret = -1;
641 break;
644 if (this->m_PackRefMap.find(ref) == m_PackRefMap.end())
646 ret = -1;
647 break;
649 this ->m_Head = m_PackRefMap[ref];
650 ret = 0;
651 break;
653 ReadFile(href, buffer, 40, &size, NULL);
654 if (size != 40)
656 ret = -1;
657 break;
659 this->m_Head.ConvertFromStrA((char*)buffer);
661 this->m_LastModifyTimeRef = time;
664 else
666 ReadFile(hfile, buffer + 4, 40 - 4, &size, NULL);
667 if(size != 36)
669 ret = -1;
670 break;
672 m_HeadRefFile.Empty();
674 this->m_Head.ConvertFromStrA((char*)buffer);
676 } while(0);
678 catch(...)
680 ret = -1;
683 return ret;
686 bool CGitHeadFileList::CheckHeadUpdate()
688 if (this->m_HeadFile.IsEmpty())
689 return true;
691 __int64 mtime=0;
693 if (g_Git.GetFileModifyTime(m_HeadFile, &mtime))
694 return true;
696 if (mtime != this->m_LastModifyTimeHead)
697 return true;
699 if (!this->m_HeadRefFile.IsEmpty())
701 if (g_Git.GetFileModifyTime(m_HeadRefFile, &mtime))
702 return true;
704 if (mtime != this->m_LastModifyTimeRef)
705 return true;
708 if(!this->m_PackRefFile.IsEmpty())
710 if (g_Git.GetFileModifyTime(m_PackRefFile, &mtime))
711 return true;
713 if (mtime != this->m_LastModifyTimePackRef)
714 return true;
717 // in an empty repo HEAD points to refs/heads/master, but this ref doesn't exist.
718 // So we need to retry again and again until the ref exists - otherwise we will never notice
719 if (this->m_Head.IsEmpty() && this->m_HeadRefFile.IsEmpty() && this->m_PackRefFile.IsEmpty())
720 return true;
722 return false;
724 #if 0
725 int CGitHeadFileList::ReadTree()
727 int ret;
728 if (this->m_Head.IsEmpty())
729 return -1;
733 CAutoLocker lock(g_Git.m_critGitDllSec);
734 CAutoWriteLock lock1(&this->m_SharedMutex);
736 if (m_Gitdir != g_Git.m_CurrentDir)
738 g_Git.SetCurrentDir(m_Gitdir);
739 SetCurrentDirectory(g_Git.m_CurrentDir);
740 git_init();
743 this->m_Map.clear();
744 this->clear();
746 ret = git_read_tree(this->m_Head.m_hash, CGitHeadFileList::CallBack, this);
747 if (!ret)
748 m_TreeHash = m_Head;
750 } catch(...)
752 return -1;
754 return ret;
756 #endif;
758 int CGitHeadFileList::CallBack(const unsigned char *sha1, const char *base, int baselen,
759 const char *pathname, unsigned mode, int /*stage*/, void *context)
761 #define S_IFGITLINK 0160000
763 CGitHeadFileList *p = (CGitHeadFileList*)context;
764 if( mode&S_IFDIR )
766 if( (mode&S_IFMT) != S_IFGITLINK)
767 return READ_TREE_RECURSIVE;
770 unsigned int cur = p->size();
771 p->resize(p->size() + 1);
772 p->at(cur).m_Hash = (char*)sha1;
773 p->at(cur).m_FileName.Empty();
775 if(base)
776 g_Git.StringAppend(&p->at(cur).m_FileName, (BYTE*)base, CP_ACP, baselen);
778 g_Git.StringAppend(&p->at(cur).m_FileName,(BYTE*)pathname, CP_ACP);
780 p->at(cur).m_FileName.MakeLower();
782 //p->at(cur).m_FileName.Replace(_T('/'), _T('\\'));
784 //p->m_Map[p->at(cur).m_FileName] = cur;
786 if( (mode&S_IFMT) == S_IFGITLINK)
787 return 0;
789 return READ_TREE_RECURSIVE;
792 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)
794 size_t count = git_tree_entrycount(tree);
795 for (int i = 0; i < count; i++)
797 const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
798 if (entry == NULL)
799 continue;
800 int mode = git_tree_entry_attributes(entry);
801 if( CallBack(git_tree_entry_id(entry)->id,
802 base,
803 base.GetLength(),
804 git_tree_entry_name(entry),
805 mode,
807 data) == READ_TREE_RECURSIVE
810 if(mode&S_IFDIR)
812 git_object *object = NULL;
813 git_tree_entry_2object(&object, &repo, entry);
814 if (object == NULL)
815 continue;
816 CStringA parent = base;
817 parent += git_tree_entry_name(entry);
818 parent += "/";
819 ReadTreeRecursive(repo, (git_tree*)object, parent, CallBack, data);
820 git_object_free(object);
826 return 0;
829 int CGitHeadFileList::ReadTree()
831 CStringA gitdir = CUnicodeUtils::GetMulti(m_Gitdir, CP_ACP);
832 git_repository *repository = NULL;
833 git_commit *commit = NULL;
834 git_tree * tree = NULL;
835 int ret = 0;
836 this->clear(); // hack to avoid duplicates in the head list, which are introduced in GitStatus::GetFileStatus when this method is called
839 ret = git_repository_open(&repository, gitdir.GetBuffer());
840 if(ret)
841 break;
842 ret = git_commit_lookup(&commit, repository, (const git_oid*)m_Head.m_hash);
843 if(ret)
844 break;
846 ret = git_commit_tree(&tree, commit);
847 if(ret)
848 break;
850 ret = ReadTreeRecursive(*repository, tree,"", CGitHeadFileList::CallBack,this);
851 if(ret)
852 break;
854 std::sort(this->begin(), this->end(), SortTree);
855 this->m_TreeHash = (char*)(git_commit_id(commit)->id);
857 } while(0);
859 if (tree)
860 git_tree_free(tree);
862 if (commit)
863 git_commit_free(commit);
865 if (repository)
866 git_repository_free(repository);
868 return ret;
871 int CGitIgnoreItem::FetchIgnoreList(const CString &projectroot, const CString &file)
873 CAutoWriteLock lock(&this->m_SharedMutex);
875 if (this->m_pExcludeList)
877 free(m_pExcludeList);
878 m_pExcludeList=NULL;
881 this->m_BaseDir.Empty();
882 CString gitDir = g_AdminDirMap.GetAdminDir(projectroot);
883 if (gitDir.GetLength() < file.GetLength())
885 CString base = file.Mid(gitDir.GetLength());
886 base.Replace(_T('\\'), _T('/'));
887 if (base != _T("info/exclude"))
889 int start = base.ReverseFind(_T('/'));
890 if(start >= 0)
892 base = base.Left(start);
893 this->m_BaseDir = CUnicodeUtils::GetMulti(base,CP_ACP) ;
899 if(g_Git.GetFileModifyTime(file, &m_LastModifyTime))
900 return -1;
902 if(git_create_exclude_list(&this->m_pExcludeList))
903 return -1;
906 CAutoFile hfile = CreateFile(file,
907 GENERIC_READ,
908 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
909 NULL,
910 OPEN_EXISTING,
911 FILE_ATTRIBUTE_NORMAL,
912 NULL);
915 if (!hfile)
916 return -1 ;
918 DWORD size=0,filesize=0;
920 filesize=GetFileSize(hfile, NULL);
922 if(filesize == INVALID_FILE_SIZE)
923 return -1;
925 BYTE *buffer = new BYTE[filesize + 1];
927 if(buffer == NULL)
928 return -1;
930 if(! ReadFile(hfile, buffer,filesize,&size,NULL))
931 return GetLastError();
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;
998 bool CGitIgnoreList::CheckIgnoreChanged(const CString &gitdir,const CString &path)
1000 CString temp;
1001 temp = gitdir;
1002 temp += _T("\\");
1003 temp += path;
1005 temp.Replace(_T('/'), _T('\\'));
1007 while(!temp.IsEmpty())
1009 CString tempOrig = temp;
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 CString wcglobalgitignore = g_AdminDirMap.GetAdminDir(tempOrig) + _T("info\\exclude");
1020 if (CheckFileChanged(wcglobalgitignore))
1021 return true;
1022 else
1023 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 CString tempOrig = temp;
1082 temp += _T("\\.git");
1084 if (CGit::GitPathFileExists(temp))
1086 CString gitignore = temp;
1087 gitignore += _T("ignore");
1088 if (CheckFileChanged(gitignore))
1090 FetchIgnoreFile(gitdir, gitignore);
1093 CString wcglobalgitignore = g_AdminDirMap.GetAdminDir(tempOrig) + _T("info\\exclude");
1094 if (CheckFileChanged(wcglobalgitignore))
1096 return FetchIgnoreFile(gitdir, wcglobalgitignore);
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 = g_AdminDirMap.GetAdminDir(temp) + _T("info\\exclude");
1148 itMap = m_Map.find(ignore);
1149 if (itMap == m_Map.end())
1153 else
1155 timelist.push_back(itMap->second.m_LastModifyTime);
1156 return 0;
1159 ignore = temp;
1160 ignore += _T("\\.git");
1162 if (CGit::GitPathFileExists(ignore))
1163 return 0;
1165 start = temp.ReverseFind(_T('\\'));
1166 if (start > 0)
1167 temp=temp.Left(start);
1169 } while(start > 0);
1171 return -1;
1174 bool CGitIgnoreList::IsIgnore(const CString &path,const CString &projectroot)
1176 CString str=path;
1178 str.Replace(_T('\\'),_T('/'));
1180 if (str.GetLength()>0)
1181 if (str[str.GetLength()-1] == _T('/'))
1182 str = str.Left(str.GetLength() - 1);
1184 int ret;
1185 ret = CheckIgnore(str, projectroot);
1186 while (ret < 0)
1188 int start = str.ReverseFind(_T('/'));
1189 if(start < 0)
1190 return (ret == 1);
1192 str = str.Left(start);
1193 ret = CheckIgnore(str, projectroot);
1196 return (ret == 1);
1198 int CGitIgnoreList::CheckIgnore(const CString &path,const CString &projectroot)
1200 __int64 time = 0;
1201 bool dir = 0;
1202 CString temp = projectroot + _T("\\") + path;
1203 temp.Replace(_T('/'), _T('\\'));
1205 CStringA patha;
1207 patha = CUnicodeUtils::GetMulti(path, CP_ACP) ;
1208 patha.Replace('\\', '/');
1210 if(g_Git.GetFileModifyTime(temp, &time, &dir))
1211 return -1;
1213 int type = 0;
1214 if (dir)
1215 type = DT_DIR;
1216 else
1217 type = DT_REG;
1219 while (!temp.IsEmpty())
1221 int x;
1222 x = temp.ReverseFind(_T('\\'));
1223 if(x < 0)
1224 x=0;
1225 temp=temp.Left(x);
1227 temp += _T("\\.gitignore");
1229 char *base;
1231 patha.Replace('\\', '/');
1232 int pos = patha.ReverseFind('/');
1233 base = pos >= 0 ? patha.GetBuffer() + pos + 1 : patha.GetBuffer();
1235 CAutoReadLock lock(&this->m_SharedMutex);
1237 if(this->m_Map.find(temp) != m_Map.end())
1239 int ret=-1;
1241 if(m_Map[temp].m_pExcludeList)
1242 ret = git_check_excluded_1( patha, patha.GetLength(), base, &type, m_Map[temp].m_pExcludeList);
1244 if(ret == 1)
1245 return 1;
1246 if(ret == 0)
1247 return 0;
1250 temp = g_AdminDirMap.GetAdminDir(temp.Left(temp.GetLength() - 11)) + _T("info\\exclude");
1252 if(this->m_Map.find(temp) != m_Map.end())
1254 int ret = -1;
1256 if(m_Map[temp].m_pExcludeList)
1257 ret = git_check_excluded_1(patha, patha.GetLength(), base, &type, m_Map[temp].m_pExcludeList);
1259 if(ret == 1)
1260 return 1;
1261 if(ret == 0)
1262 return 0;
1264 return -1;
1266 temp = temp.Left(temp.GetLength() - 18);
1269 return -1;
1272 bool CGitHeadFileMap::CheckHeadUpdate(const CString &gitdir)
1274 SHARED_TREE_PTR ptr;
1275 ptr = this->SafeGet(gitdir);
1277 if( ptr.get())
1279 return ptr->CheckHeadUpdate();
1281 else
1283 SHARED_TREE_PTR ptr1(new CGitHeadFileList);
1284 ptr1->ReadHeadHash(gitdir);
1286 this->SafeSet(gitdir, ptr1);
1287 return true;
1289 return false;
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 ptr1->ReadHeadHash(gitdir);
1302 hash = ptr1->m_Head;
1304 this->SafeSet(gitdir, ptr1);
1307 else
1309 if(ptr->CheckHeadUpdate())
1311 SHARED_TREE_PTR ptr1(new CGitHeadFileList());
1312 ptr1->ReadHeadHash(gitdir);
1314 hash = ptr1->m_Head;
1315 this->SafeSet(gitdir, ptr1);
1318 hash = ptr->m_Head;
1320 return 0;
1322 #if 0
1324 int CGitStatus::GetStatus(const CString &gitdir, const CString &path, git_wc_status_kind *status, BOOL IsFull, BOOL IsRecursive , FIll_STATUS_CALLBACK callback , void *pData)
1326 int result;
1327 __int64 time;
1328 bool dir;
1330 git_wc_status_kind dirstatus = git_wc_status_none;
1331 if (status)
1333 g_Git.GetFileModifyTime(path, &time, &dir);
1334 if(path.IsEmpty())
1335 result = _tstat64( gitdir, &buf );
1336 else
1337 result = _tstat64( gitdir+_T("\\")+path, &buf );
1339 if(result)
1340 return -1;
1342 if(buf.st_mode & _S_IFDIR)
1344 if(!path.IsEmpty())
1346 if( path.Right(1) != _T("\\"))
1347 path += _T("\\");
1349 int len = path.GetLength();
1351 for (int i = 0; i < size(); i++)
1353 if (at(i).m_FileName.GetLength() > len)
1355 if (at(i).m_FileName.Left(len) == path)
1357 if(!IsFull)
1359 *status = git_wc_status_normal;
1360 if(callback)
1361 callback(gitdir + _T("\\") + path, *status, pData);
1362 return 0;
1365 else
1367 result = _tstat64(gitdir + _T("\\") + at(i).m_FileName, &buf);
1368 if (result)
1369 continue;
1371 *status = git_wc_status_none;
1372 GetFileStatus(gitdir, at(i).m_FileName, status, buf, callback, pData);
1373 if (*status != git_wc_status_none)
1375 if (dirstatus == git_wc_status_none)
1377 dirstatus = git_wc_status_normal;
1379 if (*status != git_wc_status_normal)
1381 dirstatus = git_wc_status_modified;
1390 if (dirstatus != git_wc_status_none)
1392 *status = dirstatus;
1394 else
1396 *status = git_wc_status_unversioned;
1398 if(callback)
1399 callback(gitdir + _T("\\") + path, *status, pData);
1401 return 0;
1404 else
1406 GetFileStatus(gitdir, path, status, buf, callback, pData);
1409 return 0;
1412 #endif