CommitDlg: make sure branch name is valid
[TortoiseGit.git] / src / Git / GitIndex.cpp
blob9d03b072763efee076dfb975b29444984abac1b0
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);
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 if (hmap != INVALID_HANDLE_VALUE)
174 CloseHandle(hmap);
176 if (hfile != INVALID_HANDLE_VALUE)
177 CloseHandle(hfile);
179 return ret;
182 int CGitIndexList::GetFileStatus(const CString &gitdir,const CString &pathorg,git_wc_status_kind *status,__int64 time,FIll_STATUS_CALLBACK callback,void *pData, CGitHash *pHash)
184 if(status)
186 CString path = pathorg;
187 path.MakeLower();
189 int start = SearchInSortVector(*this, ((CString&)path).GetBuffer(), -1);
190 ((CString&)path).ReleaseBuffer();
192 if (start < 0)
194 *status = git_wc_status_unversioned;
195 if (pHash)
196 pHash->Empty();
199 else
201 int index = start;
202 if (index <0)
203 return -1;
204 if (index >= size() )
205 return -1;
207 if (time == at(index).m_ModifyTime)
209 *status = git_wc_status_normal;
211 else
213 *status = git_wc_status_modified;
216 if (at(index).m_Flags & CE_STAGEMASK )
217 *status = git_wc_status_conflicted;
218 else if (at(index).m_Flags & CE_INTENT_TO_ADD)
219 *status = git_wc_status_added;
221 if(pHash)
222 *pHash = at(index).m_IndexHash;
227 if(callback && status)
228 callback(gitdir + _T("\\") + pathorg, *status, false, pData);
229 return 0;
232 int CGitIndexList::GetStatus(const CString &gitdir,const CString &pathParam, git_wc_status_kind *status,
233 BOOL IsFull, BOOL IsRecursive,
234 FIll_STATUS_CALLBACK callback,void *pData,
235 CGitHash *pHash)
237 int result;
238 git_wc_status_kind dirstatus = git_wc_status_none;
239 __int64 time;
240 bool isDir = false;
241 CString path = pathParam;
243 if (status)
245 if (path.IsEmpty())
246 result = g_Git.GetFileModifyTime(gitdir, &time, &isDir);
247 else
248 result = g_Git.GetFileModifyTime(gitdir + _T("\\") + path, &time, &isDir);
250 if (result)
252 *status = git_wc_status_deleted;
253 if (callback)
254 callback(gitdir + _T("\\") + path, git_wc_status_deleted, false, pData);
256 return 0;
258 if (isDir)
260 if (!path.IsEmpty())
262 if (path.Right(1) != _T("\\"))
263 path += _T("\\");
265 int len = path.GetLength();
267 for (int i = 0; i < size(); i++)
269 if (at(i).m_FileName.GetLength() > len)
271 if (at(i).m_FileName.Left(len) == path)
273 if (!IsFull)
275 *status = git_wc_status_normal;
276 if (callback)
277 callback(gitdir + _T("\\") + path, *status, false, pData);
278 return 0;
281 else
283 result = g_Git.GetFileModifyTime(gitdir+_T("\\") + at(i).m_FileName, &time);
284 if (result)
285 continue;
287 *status = git_wc_status_none;
288 GetFileStatus(gitdir, at(i).m_FileName, status, time, callback, pData);
289 if (*status != git_wc_status_none)
291 if (dirstatus == git_wc_status_none)
293 dirstatus = git_wc_status_normal;
295 if (*status != git_wc_status_normal)
297 dirstatus = git_wc_status_modified;
304 } /* End For */
306 if (dirstatus != git_wc_status_none)
308 *status = dirstatus;
310 else
312 *status = git_wc_status_unversioned;
314 if(callback)
315 callback(gitdir + _T("\\") + path, *status, false, pData);
317 return 0;
320 else
322 GetFileStatus(gitdir, path, status, time, callback, pData, pHash);
325 return 0;
328 int CGitIndexFileMap::Check(const CString &gitdir, bool *isChanged)
330 __int64 time;
331 int result;
333 CString IndexFile;
334 IndexFile = gitdir + _T("\\.git\\index");
335 /* Get data associated with "crt_stat.c": */
336 result = g_Git.GetFileModifyTime(IndexFile, &time);
338 if (result)
339 return result;
341 SHARED_INDEX_PTR pIndex;
342 pIndex = this->SafeGet(gitdir);
344 if (pIndex.get() == NULL)
346 if(isChanged)
347 *isChanged = true;
348 return 0;
351 if (pIndex->m_LastModifyTime == time)
353 if (isChanged)
354 *isChanged = false;
356 else
358 if (isChanged)
359 *isChanged = true;
361 return 0;
364 int CGitIndexFileMap::LoadIndex(const CString &gitdir)
368 SHARED_INDEX_PTR pIndex(new CGitIndexList);
370 if(pIndex->ReadIndex(gitdir + _T("\\.git\\index")))
371 return -1;
373 this->SafeSet(gitdir, pIndex);
375 }catch(...)
377 return -1;
379 return 0;
382 int CGitIndexFileMap::GetFileStatus(const CString &gitdir, const CString &path, git_wc_status_kind *status,BOOL IsFull, BOOL IsRecursive,
383 FIll_STATUS_CALLBACK callback,void *pData,
384 CGitHash *pHash,
385 bool isLoadUpdatedIndex)
389 CheckAndUpdate(gitdir, isLoadUpdatedIndex);
391 SHARED_INDEX_PTR pIndex = this->SafeGet(gitdir);
392 if (pIndex.get() != NULL)
394 pIndex->GetStatus(gitdir, path, status, IsFull, IsRecursive, callback, pData, pHash);
398 catch(...)
400 return -1;
402 return 0;
405 int CGitIndexFileMap::IsUnderVersionControl(const CString &gitdir, const CString &path, bool isDir,bool *isVersion, bool isLoadUpdateIndex)
409 if (path.IsEmpty())
411 *isVersion = true;
412 return 0;
415 CString subpath = path;
416 subpath.Replace(_T('\\'), _T('/'));
417 if(isDir)
418 subpath += _T('/');
420 subpath.MakeLower();
422 CheckAndUpdate(gitdir, isLoadUpdateIndex);
424 SHARED_INDEX_PTR pIndex = this->SafeGet(gitdir);
426 if(pIndex.get())
428 if(isDir)
429 *isVersion = (SearchInSortVector(*pIndex, subpath.GetBuffer(), subpath.GetLength()) >= 0);
430 else
431 *isVersion = (SearchInSortVector(*pIndex, subpath.GetBuffer(), -1) >= 0);
434 }catch(...)
436 return -1;
438 return 0;
441 int CGitHeadFileList::GetPackRef(const CString &gitdir)
443 CString PackRef = gitdir;
444 PackRef += _T("\\.git\\packed-refs");
446 __int64 mtime;
447 if (g_Git.GetFileModifyTime(PackRef, &mtime))
449 //packed refs is not existed
450 this->m_PackRefFile.Empty();
451 this->m_PackRefMap.clear();
452 return 0;
455 else if(mtime == m_LastModifyTimePackRef)
457 return 0;
460 else
462 this->m_PackRefFile = PackRef;
463 this->m_LastModifyTimePackRef = mtime;
466 int ret = 0;
468 this->m_PackRefMap.clear();
470 HANDLE hfile = CreateFile(PackRef,
471 GENERIC_READ,
472 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
473 NULL,
474 OPEN_EXISTING,
475 FILE_ATTRIBUTE_NORMAL,
476 NULL);
479 if(hfile == INVALID_HANDLE_VALUE)
481 ret = -1;
482 break;
485 DWORD filesize = GetFileSize(hfile, NULL);
486 DWORD size =0;
487 char *buff;
488 buff = new char[filesize];
490 ReadFile(hfile, buff, filesize, &size, NULL);
492 if (size != filesize)
494 ret = -1;
495 break;
498 CString hash;
499 CString ref;
501 for(DWORD i=0;i<filesize;)
503 hash.Empty();
504 ref.Empty();
505 if (buff[i] == '#' || buff[i] == '^')
507 while (buff[i] != '\n')
509 i++;
510 if (i == filesize)
511 break;
513 i++;
516 if (i >= filesize)
517 break;
519 while (buff[i] != ' ')
521 hash.AppendChar(buff[i]);
522 i++;
523 if (i == filesize)
524 break;
527 i++;
528 if (i >= filesize)
529 break;
531 while (buff[i] != '\n')
533 ref.AppendChar(buff[i]);
534 i++;
535 if (i == filesize)
536 break;
539 if (!ref.IsEmpty() )
541 this->m_PackRefMap[ref] = hash;
544 while (buff[i] == '\n')
546 i++;
547 if (i == filesize)
548 break;
552 delete buff;
554 } while(0);
556 if(hfile != INVALID_HANDLE_VALUE)
557 CloseHandle(hfile);
559 return ret;
562 int CGitHeadFileList::ReadHeadHash(CString gitdir)
564 CString HeadFile = gitdir;
565 HeadFile += _T("\\.git\\HEAD");
568 HANDLE hfile=INVALID_HANDLE_VALUE;
569 HANDLE href = INVALID_HANDLE_VALUE;
571 int ret = 0;
572 m_Gitdir = gitdir;
574 m_HeadFile = HeadFile;
576 if( g_Git.GetFileModifyTime(m_HeadFile, &m_LastModifyTimeHead))
577 return -1;
583 hfile = CreateFile(HeadFile,
584 GENERIC_READ,
585 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
586 NULL,
587 OPEN_EXISTING,
588 FILE_ATTRIBUTE_NORMAL,
589 NULL);
591 if(hfile == INVALID_HANDLE_VALUE)
593 ret = -1;
594 break;
597 DWORD size = 0,filesize = 0;
598 unsigned char buffer[40] ;
599 ReadFile(hfile, buffer, 4, &size, NULL);
600 if (size != 4)
602 ret = -1;
603 break;
605 buffer[4]=0;
606 if (strcmp((const char*)buffer,"ref:") == 0)
608 filesize = GetFileSize(hfile, NULL);
610 unsigned char *p = (unsigned char*)malloc(filesize -4);
612 ReadFile(hfile, p, filesize - 4, &size, NULL);
614 m_HeadRefFile.Empty();
615 g_Git.StringAppend(&this->m_HeadRefFile, p, CP_ACP, filesize - 4);
616 CString ref = this->m_HeadRefFile;
617 ref = ref.Trim();
618 int start = 0;
619 ref = ref.Tokenize(_T("\n"), start);
620 free(p);
621 m_HeadRefFile=gitdir+_T("\\.git\\") + m_HeadRefFile.Trim();
622 m_HeadRefFile.Replace(_T('/'),_T('\\'));
624 __int64 time;
625 if (g_Git.GetFileModifyTime(m_HeadRefFile, &time, NULL))
627 m_HeadRefFile.Empty();
628 if (GetPackRef(gitdir))
630 ret = -1;
631 break;
633 if (this->m_PackRefMap.find(ref) == m_PackRefMap.end())
635 ret = -1;
636 break;
638 this ->m_Head = m_PackRefMap[ref];
639 ret = 0;
640 break;
643 href = CreateFile(m_HeadRefFile,
644 GENERIC_READ,
645 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
646 NULL,
647 OPEN_EXISTING,
648 FILE_ATTRIBUTE_NORMAL,
649 NULL);
651 if (href == INVALID_HANDLE_VALUE)
653 m_HeadRefFile.Empty();
655 if (GetPackRef(gitdir))
657 ret = -1;
658 break;
661 if (this->m_PackRefMap.find(ref) == m_PackRefMap.end())
663 ret = -1;
664 break;
666 this ->m_Head = m_PackRefMap[ref];
667 ret = 0;
668 break;
670 ReadFile(href, buffer, 40, &size, NULL);
671 if (size != 40)
673 ret = -1;
674 break;
676 this->m_Head.ConvertFromStrA((char*)buffer);
678 this->m_LastModifyTimeRef = time;
681 else
683 ReadFile(hfile, buffer + 4, 40 - 4, &size, NULL);
684 if(size != 36)
686 ret = -1;
687 break;
689 m_HeadRefFile.Empty();
691 this->m_Head.ConvertFromStrA((char*)buffer);
693 } while(0);
695 catch(...)
697 ret = -1;
700 if (hfile != INVALID_HANDLE_VALUE)
701 CloseHandle(hfile);
702 if (href != INVALID_HANDLE_VALUE)
703 CloseHandle(href);
705 return ret;
708 bool CGitHeadFileList::CheckHeadUpdate()
710 if (this->m_HeadFile.IsEmpty())
711 return true;
713 __int64 mtime=0;
715 if (g_Git.GetFileModifyTime(m_HeadFile, &mtime))
716 return true;
718 if (mtime != this->m_LastModifyTimeHead)
719 return true;
721 if (!this->m_HeadRefFile.IsEmpty())
723 if (g_Git.GetFileModifyTime(m_HeadRefFile, &mtime))
724 return true;
726 if (mtime != this->m_LastModifyTimeRef)
727 return true;
730 if(!this->m_PackRefFile.IsEmpty())
732 if (g_Git.GetFileModifyTime(m_PackRefFile, &mtime))
733 return true;
735 if (mtime != this->m_LastModifyTimePackRef)
736 return true;
738 return false;
740 #if 0
741 int CGitHeadFileList::ReadTree()
743 int ret;
744 if (this->m_Head.IsEmpty())
745 return -1;
749 CAutoLocker lock(g_Git.m_critGitDllSec);
750 CAutoWriteLock lock1(&this->m_SharedMutex);
752 if (m_Gitdir != g_Git.m_CurrentDir)
754 g_Git.SetCurrentDir(m_Gitdir);
755 SetCurrentDirectory(g_Git.m_CurrentDir);
756 git_init();
759 this->m_Map.clear();
760 this->clear();
762 ret = git_read_tree(this->m_Head.m_hash, CGitHeadFileList::CallBack, this);
763 if (!ret)
764 m_TreeHash = m_Head;
766 } catch(...)
768 return -1;
770 return ret;
772 #endif;
774 int CGitHeadFileList::CallBack(const unsigned char *sha1, const char *base, int baselen,
775 const char *pathname, unsigned mode, int /*stage*/, void *context)
777 #define S_IFGITLINK 0160000
779 CGitHeadFileList *p = (CGitHeadFileList*)context;
780 if( mode&S_IFDIR )
782 if( (mode&S_IFMT) != S_IFGITLINK)
783 return READ_TREE_RECURSIVE;
786 unsigned int cur = p->size();
787 p->resize(p->size() + 1);
788 p->at(cur).m_Hash = (char*)sha1;
789 p->at(cur).m_FileName.Empty();
791 if(base)
792 g_Git.StringAppend(&p->at(cur).m_FileName, (BYTE*)base, CP_ACP, baselen);
794 g_Git.StringAppend(&p->at(cur).m_FileName,(BYTE*)pathname, CP_ACP);
796 p->at(cur).m_FileName.MakeLower();
798 //p->at(cur).m_FileName.Replace(_T('/'), _T('\\'));
800 //p->m_Map[p->at(cur).m_FileName] = cur;
802 if( (mode&S_IFMT) == S_IFGITLINK)
803 return 0;
805 return READ_TREE_RECURSIVE;
808 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)
810 size_t count = git_tree_entrycount(tree);
811 for (int i = 0; i < count; i++)
813 const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
814 if (entry == NULL)
815 continue;
816 int mode = git_tree_entry_attributes(entry);
817 if( CallBack(git_tree_entry_id(entry)->id,
818 base,
819 base.GetLength(),
820 git_tree_entry_name(entry),
821 mode,
823 data) == READ_TREE_RECURSIVE
826 if(mode&S_IFDIR)
828 git_object *object = NULL;
829 git_tree_entry_2object(&object, &repo, entry);
830 if (object == NULL)
831 continue;
832 CStringA parent = base;
833 parent += git_tree_entry_name(entry);
834 parent += "/";
835 ReadTreeRecursive(repo, (git_tree*)object, parent, CallBack, data);
841 return 0;
844 int CGitHeadFileList::ReadTree()
846 CStringA gitdir = CUnicodeUtils::GetMulti(m_Gitdir,CP_ACP) ;
847 gitdir += "\\.git";
848 git_repository *repository = NULL;
849 git_commit *commit = NULL;
850 git_tree * tree = NULL;
851 int ret = 0;
854 ret = git_repository_open(&repository, gitdir.GetBuffer());
855 if(ret)
856 break;
857 ret = git_commit_lookup(&commit, repository, (const git_oid*)m_Head.m_hash);
858 if(ret)
859 break;
861 ret = git_commit_tree(&tree, commit);
862 if(ret)
863 break;
865 ret = ReadTreeRecursive(*repository, tree,"", CGitHeadFileList::CallBack,this);
866 if(ret)
867 break;
869 std::sort(this->begin(), this->end(), SortTree);
870 this->m_TreeHash = (char*)(git_commit_id(commit)->id);
872 } while(0);
874 if (repository)
875 git_repository_free(repository);
877 return ret;
880 int CGitIgnoreItem::FetchIgnoreList(const CString &projectroot, const CString &file)
882 CAutoWriteLock lock(&this->m_SharedMutex);
884 if (this->m_pExcludeList)
886 free(m_pExcludeList);
887 m_pExcludeList=NULL;
890 this->m_BaseDir.Empty();
891 if (projectroot.GetLength() < file.GetLength())
893 CString base = file.Mid(projectroot.GetLength() + 1);
894 base.Replace(_T('\\'), _T('/'));
895 if (base != _T(".git/info/exclude"))
897 int start = base.ReverseFind(_T('/'));
898 if(start >= 0)
900 base = base.Left(start);
901 this->m_BaseDir = CUnicodeUtils::GetMulti(base,CP_ACP) ;
907 if(g_Git.GetFileModifyTime(file, &m_LastModifyTime))
908 return -1;
910 if(git_create_exclude_list(&this->m_pExcludeList))
911 return -1;
914 HANDLE hfile = CreateFile(file,
915 GENERIC_READ,
916 FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
917 NULL,
918 OPEN_EXISTING,
919 FILE_ATTRIBUTE_NORMAL,
920 NULL);
923 if(hfile == INVALID_HANDLE_VALUE)
925 return -1 ;
928 DWORD size=0,filesize=0;
930 filesize=GetFileSize(hfile, NULL);
932 if(filesize == INVALID_FILE_SIZE)
934 CloseHandle(hfile);
935 return -1;
938 BYTE *buffer = new BYTE[filesize + 1];
940 if(buffer == NULL)
942 CloseHandle(hfile);
943 return -1;
946 if(! ReadFile(hfile, buffer,filesize,&size,NULL))
948 CloseHandle(hfile);
949 return GetLastError();
952 CloseHandle(hfile);
954 BYTE *p = buffer;
955 for (int i = 0; i < size; i++)
957 if (buffer[i] == '\n' || buffer[i] == '\r' || i == (size - 1))
959 if (buffer[i] == '\n' || buffer[i] == '\r')
960 buffer[i] = 0;
961 if (i == size - 1)
962 buffer[size] = 0;
964 if(p[0] != '#' && p[0] != 0)
965 git_add_exclude((const char*)p,
966 this->m_BaseDir.GetBuffer(),
967 m_BaseDir.GetLength(),
968 this->m_pExcludeList);
970 p=buffer+i+1;
973 /* Can't free buffer, exluced list will use this buffer*/
974 //delete buffer;
975 //buffer = NULL;
977 return 0;
980 bool CGitIgnoreList::CheckFileChanged(const CString &path)
982 __int64 time = 0;
984 int ret = g_Git.GetFileModifyTime(path, &time);
986 this->m_SharedMutex.AcquireShared();
987 bool cacheExist = (m_Map.find(path) != m_Map.end());
988 this->m_SharedMutex.ReleaseShared();
990 if (!cacheExist && ret == 0)
992 CAutoWriteLock lock(&this->m_SharedMutex);
993 m_Map[path].m_LastModifyTime = 0;
994 m_Map[path].m_SharedMutex.Init();
996 // both cache and file is not exist
997 if ((ret != 0) && (!cacheExist))
998 return false;
1000 // file exist but cache miss
1001 if ((ret == 0) && (!cacheExist))
1002 return true;
1004 // file not exist but cache exist
1005 if ((ret != 0) && (cacheExist))
1007 return true;
1009 // file exist and cache exist
1012 CAutoReadLock lock(&this->m_SharedMutex);
1013 if (m_Map[path].m_LastModifyTime == time)
1014 return false;
1016 return true;
1019 bool CGitIgnoreList::CheckIgnoreChanged(const CString &gitdir,const CString &path)
1021 CString temp;
1022 temp = gitdir;
1023 temp += _T("\\");
1024 temp += path;
1026 temp.Replace(_T('/'), _T('\\'));
1028 while(!temp.IsEmpty())
1030 temp += _T("\\.git");
1032 if (CGit::GitPathFileExists(temp))
1034 CString gitignore=temp;
1035 gitignore += _T("ignore");
1036 if (CheckFileChanged(gitignore))
1037 return true;
1039 temp += _T("\\info\\exclude");
1041 if (CheckFileChanged(temp))
1042 return true;
1043 else
1044 return false;
1046 else
1048 temp += _T("ignore");
1049 if (CheckFileChanged(temp))
1050 return true;
1053 int found=0;
1054 int i;
1055 for (i = temp.GetLength() - 1; i >= 0; i--)
1057 if(temp[i] == _T('\\'))
1058 found ++;
1060 if(found == 2)
1061 break;
1064 temp = temp.Left(i);
1066 return true;
1069 int CGitIgnoreList::FetchIgnoreFile(const CString &gitdir, const CString &gitignore)
1071 if (CGit::GitPathFileExists(gitignore)) //if .gitignore remove, we need remote cache
1073 CAutoWriteLock lock(&this->m_SharedMutex);
1074 if (m_Map.find(gitignore) == m_Map.end())
1075 m_Map[gitignore].m_SharedMutex.Init();
1077 m_Map[gitignore].FetchIgnoreList(gitdir,gitignore);
1079 else
1081 CAutoWriteLock lock(&this->m_SharedMutex);
1082 if (m_Map.find(gitignore) != m_Map.end())
1083 m_Map[gitignore].m_SharedMutex.Release();
1085 m_Map.erase(gitignore);
1087 return 0;
1090 int CGitIgnoreList::LoadAllIgnoreFile(const CString &gitdir,const CString &path)
1092 CString temp;
1094 temp = gitdir;
1095 temp += _T("\\");
1096 temp += path;
1098 temp.Replace(_T('/'), _T('\\'));
1100 while (!temp.IsEmpty())
1102 temp += _T("\\.git");
1104 if (CGit::GitPathFileExists(temp))
1106 CString gitignore = temp;
1107 gitignore += _T("ignore");
1108 if (CheckFileChanged(gitignore))
1110 FetchIgnoreFile(gitdir, gitignore);
1113 temp += _T("\\info\\exclude");
1115 if (CheckFileChanged(temp))
1117 return FetchIgnoreFile(gitdir, temp);
1120 return 0;
1122 else
1124 temp += _T("ignore");
1125 if (CheckFileChanged(temp))
1127 FetchIgnoreFile(gitdir, temp);
1131 int found = 0;
1132 int i;
1133 for (i = temp.GetLength() - 1; i >= 0; i--)
1135 if(temp[i] == _T('\\'))
1136 found ++;
1138 if(found == 2)
1139 break;
1142 temp = temp.Left(i);
1144 return 0;
1146 int CGitIgnoreList::GetIgnoreFileChangeTimeList(const CString &path, std::vector<__int64> &timelist)
1148 CString temp = path;
1149 CString ignore = temp;
1150 int start = 0;
1153 CAutoReadLock lock(&this->m_SharedMutex);
1155 ignore=temp;
1156 ignore += _T("\\.gitignore");
1157 std::map<CString, CGitIgnoreItem>::iterator itMap;
1158 itMap = m_Map.find(ignore);
1159 if (itMap == m_Map.end())
1161 timelist.push_back(0);
1163 else
1165 timelist.push_back(itMap->second.m_LastModifyTime);
1168 ignore = temp;
1169 ignore += _T("\\.git\\info\\exclude");
1171 itMap = m_Map.find(ignore);
1172 if (itMap == m_Map.end())
1176 else
1178 timelist.push_back(itMap->second.m_LastModifyTime);
1179 return 0;
1182 ignore = temp;
1183 ignore += _T("\\.git");
1185 if (CGit::GitPathFileExists(ignore))
1186 return 0;
1188 start = temp.ReverseFind(_T('\\'));
1189 if (start > 0)
1190 temp=temp.Left(start);
1192 } while(start > 0);
1194 return -1;
1197 bool CGitIgnoreList::IsIgnore(const CString &path,const CString &projectroot)
1199 CString str=path;
1201 str.Replace(_T('\\'),_T('/'));
1203 if (str.GetLength()>0)
1204 if (str[str.GetLength()-1] == _T('/'))
1205 str = str.Left(str.GetLength() - 1);
1207 int ret;
1208 ret = CheckIgnore(str, projectroot);
1209 while (ret < 0)
1211 int start = str.ReverseFind(_T('/'));
1212 if(start < 0)
1213 return (ret == 1);
1215 str = str.Left(start);
1216 ret = CheckIgnore(str, projectroot);
1219 return (ret == 1);
1221 int CGitIgnoreList::CheckIgnore(const CString &path,const CString &projectroot)
1223 __int64 time = 0;
1224 bool dir = 0;
1225 CString temp = projectroot + _T("\\") + path;
1226 temp.Replace(_T('/'), _T('\\'));
1228 CStringA patha;
1230 patha = CUnicodeUtils::GetMulti(path, CP_ACP) ;
1231 patha.Replace('\\', '/');
1233 if(g_Git.GetFileModifyTime(temp, &time, &dir))
1234 return -1;
1236 int type = 0;
1237 if (dir)
1238 type = DT_DIR;
1239 else
1240 type = DT_REG;
1242 while (!temp.IsEmpty())
1244 int x;
1245 x = temp.ReverseFind(_T('\\'));
1246 if(x < 0)
1247 x=0;
1248 temp=temp.Left(x);
1250 temp += _T("\\.gitignore");
1252 char *base;
1254 patha.Replace('\\', '/');
1255 int pos = patha.ReverseFind('/');
1256 base = pos >= 0 ? patha.GetBuffer() + pos + 1 : patha.GetBuffer();
1258 CAutoReadLock lock(&this->m_SharedMutex);
1260 if(this->m_Map.find(temp) != m_Map.end())
1262 int ret=-1;
1264 if(m_Map[temp].m_pExcludeList)
1265 ret = git_check_excluded_1( patha, patha.GetLength(), base, &type, m_Map[temp].m_pExcludeList);
1267 if(ret == 1)
1268 return 1;
1269 if(ret == 0)
1270 return 0;
1273 temp = temp.Left(temp.GetLength()-11);
1274 temp +=_T("\\.git\\info\\exclude");
1276 if(this->m_Map.find(temp) != m_Map.end())
1278 int ret = -1;
1280 if(m_Map[temp].m_pExcludeList)
1281 ret = git_check_excluded_1(patha, patha.GetLength(), base, &type, m_Map[temp].m_pExcludeList);
1283 if(ret == 1)
1284 return 1;
1285 if(ret == 0)
1286 return 0;
1288 return -1;
1290 temp = temp.Left(temp.GetLength() - 18);
1293 return -1;
1296 bool CGitHeadFileMap::CheckHeadUpdate(const CString &gitdir)
1298 SHARED_TREE_PTR ptr;
1299 ptr = this->SafeGet(gitdir);
1301 if( ptr.get())
1303 return ptr->CheckHeadUpdate();
1305 else
1307 SHARED_TREE_PTR ptr1(new CGitHeadFileList);
1308 if(ptr1->CheckHeadUpdate())
1309 ptr1->ReadHeadHash(gitdir);
1311 this->SafeSet(gitdir, ptr1);
1312 return true;
1314 return false;
1317 int CGitHeadFileMap::GetHeadHash(const CString &gitdir, CGitHash &hash)
1319 SHARED_TREE_PTR ptr;
1320 ptr = this->SafeGet(gitdir);
1322 if(ptr.get() == NULL)
1324 SHARED_TREE_PTR ptr1(new CGitHeadFileList());
1325 if(ptr1->CheckHeadUpdate())
1326 ptr1->ReadHeadHash(gitdir);
1328 hash = ptr1->m_Head;
1330 this->SafeSet(gitdir, ptr1);
1333 else
1335 if(ptr->CheckHeadUpdate())
1337 SHARED_TREE_PTR ptr1(new CGitHeadFileList());
1338 if(ptr1->CheckHeadUpdate())
1339 ptr1->ReadHeadHash(gitdir);
1341 hash = ptr1->m_Head;
1342 this->SafeSet(gitdir, ptr1);
1345 hash = ptr->m_Head;
1347 return 0;
1349 #if 0
1351 int CGitStatus::GetStatus(const CString &gitdir, const CString &path, git_wc_status_kind *status, BOOL IsFull, BOOL IsRecursive , FIll_STATUS_CALLBACK callback , void *pData)
1353 int result;
1354 __int64 time;
1355 bool dir;
1357 git_wc_status_kind dirstatus = git_wc_status_none;
1358 if (status)
1360 g_Git.GetFileModifyTime(path, &time, &dir);
1361 if(path.IsEmpty())
1362 result = _tstat64( gitdir, &buf );
1363 else
1364 result = _tstat64( gitdir+_T("\\")+path, &buf );
1366 if(result)
1367 return -1;
1369 if(buf.st_mode & _S_IFDIR)
1371 if(!path.IsEmpty())
1373 if( path.Right(1) != _T("\\"))
1374 path += _T("\\");
1376 int len = path.GetLength();
1378 for (int i = 0; i < size(); i++)
1380 if (at(i).m_FileName.GetLength() > len)
1382 if (at(i).m_FileName.Left(len) == path)
1384 if(!IsFull)
1386 *status = git_wc_status_normal;
1387 if(callback)
1388 callback(gitdir + _T("\\") + path, *status, pData);
1389 return 0;
1392 else
1394 result = _tstat64(gitdir + _T("\\") + at(i).m_FileName, &buf);
1395 if (result)
1396 continue;
1398 *status = git_wc_status_none;
1399 GetFileStatus(gitdir, at(i).m_FileName, status, buf, callback, pData);
1400 if (*status != git_wc_status_none)
1402 if (dirstatus == git_wc_status_none)
1404 dirstatus = git_wc_status_normal;
1406 if (*status != git_wc_status_normal)
1408 dirstatus = git_wc_status_modified;
1417 if (dirstatus != git_wc_status_none)
1419 *status = dirstatus;
1421 else
1423 *status = git_wc_status_unversioned;
1425 if(callback)
1426 callback(gitdir + _T("\\") + path, *status, pData);
1428 return 0;
1431 else
1433 GetFileStatus(gitdir, path, status, buf, callback, pData);
1436 return 0;
1439 #endif