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.
25 #include "GitConfig.h"
27 #include "UnicodeUtils.h"
30 #include <sys/types.h>
33 #include "SmartHandle.h"
35 CGitAdminDirMap g_AdminDirMap
;
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
)
51 int CGitIndex::FillData(ondisk_cache_entry_extended
* entry
)
54 this->m_Flags
|= ((int)Big2lit(entry
->flags2
))<<16;
58 int CGitIndex::Print()
60 _tprintf(_T("0x%08X 0x%08X %s %s\n"),
61 (int)this->m_ModifyTime
,
63 this->m_IndexHash
.ToString(),
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
)
87 BYTE
*buffer
= NULL
, *p
;
91 m_GitFile
= IndexFile
;
100 CAutoFile hfile
= CreateFile(IndexFile
,
102 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
105 FILE_ATTRIBUTE_NORMAL
,
115 CAutoFile hmap
= CreateFileMapping(hfile
, NULL
, PAGE_READONLY
, 0, 0, NULL
);
122 p
= buffer
= (BYTE
*)MapViewOfFile(hmap
, FILE_MAP_READ
, 0, 0, 0);
129 cache_header
*header
;
130 header
= (cache_header
*) buffer
;
132 if (Big2lit(header
->hdr_signature
) != CACHE_SIGNATURE
)
137 p
+= sizeof(cache_header
);
139 int entries
= Big2lit(header
->hdr_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
);
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
);
171 UnmapViewOfFile(buffer
);
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
)
180 CString path
= pathorg
;
183 int start
= SearchInSortVector(*this, ((CString
&)path
).GetBuffer(), -1);
184 ((CString
&)path
).ReleaseBuffer();
188 *status
= git_wc_status_unversioned
;
198 if (index
>= size() )
201 if (time
== at(index
).m_ModifyTime
)
203 *status
= git_wc_status_normal
;
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
;
216 *pHash
= at(index
).m_IndexHash
;
221 if(callback
&& status
)
222 callback(gitdir
+ _T("\\") + pathorg
, *status
, false, pData
);
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
,
232 git_wc_status_kind dirstatus
= git_wc_status_none
;
235 CString path
= pathParam
;
240 result
= g_Git
.GetFileModifyTime(gitdir
, &time
, &isDir
);
242 result
= g_Git
.GetFileModifyTime(gitdir
+ _T("\\") + path
, &time
, &isDir
);
246 *status
= git_wc_status_deleted
;
248 callback(gitdir
+ _T("\\") + path
, git_wc_status_deleted
, false, pData
);
256 if (path
.Right(1) != _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
)
269 *status
= git_wc_status_normal
;
271 callback(gitdir
+ _T("\\") + path
, *status
, false, pData
);
277 result
= g_Git
.GetFileModifyTime(gitdir
+_T("\\") + at(i
).m_FileName
, &time
);
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
;
300 if (dirstatus
!= git_wc_status_none
)
306 *status
= git_wc_status_unversioned
;
309 callback(gitdir
+ _T("\\") + path
, *status
, false, pData
);
316 GetFileStatus(gitdir
, path
, status
, time
, callback
, pData
, pHash
);
322 int CGitIndexFileMap::Check(const CString
&gitdir
, bool *isChanged
)
327 CString IndexFile
= g_AdminDirMap
.GetAdminDir(gitdir
) + _T("index");
329 /* Get data associated with "crt_stat.c": */
330 result
= g_Git
.GetFileModifyTime(IndexFile
, &time
);
335 SHARED_INDEX_PTR pIndex
;
336 pIndex
= this->SafeGet(gitdir
);
338 if (pIndex
.get() == NULL
)
345 if (pIndex
->m_LastModifyTime
== time
)
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
))
369 this->SafeSet(gitdir
, pIndex
);
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
,
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
);
401 int CGitIndexFileMap::IsUnderVersionControl(const CString
&gitdir
, const CString
&path
, bool isDir
,bool *isVersion
, bool isLoadUpdateIndex
)
411 CString subpath
= path
;
412 subpath
.Replace(_T('\\'), _T('/'));
418 CheckAndUpdate(gitdir
, isLoadUpdateIndex
);
420 SHARED_INDEX_PTR pIndex
= this->SafeGet(gitdir
);
425 *isVersion
= (SearchInSortVector(*pIndex
, subpath
.GetBuffer(), subpath
.GetLength()) >= 0);
427 *isVersion
= (SearchInSortVector(*pIndex
, subpath
.GetBuffer(), -1) >= 0);
428 subpath
.ReleaseBuffer();
438 int CGitHeadFileList::GetPackRef(const CString
&gitdir
)
440 CString PackRef
= g_AdminDirMap
.GetAdminDir(gitdir
) + _T("packed-refs");
443 if (g_Git
.GetFileModifyTime(PackRef
, &mtime
))
445 //packed refs is not existed
446 this->m_PackRefFile
.Empty();
447 this->m_PackRefMap
.clear();
450 else if(mtime
== m_LastModifyTimePackRef
)
456 this->m_PackRefFile
= PackRef
;
457 this->m_LastModifyTimePackRef
= mtime
;
462 this->m_PackRefMap
.clear();
464 CAutoFile hfile
= CreateFile(PackRef
,
466 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
469 FILE_ATTRIBUTE_NORMAL
,
479 DWORD filesize
= GetFileSize(hfile
, NULL
);
482 buff
= new char[filesize
];
484 ReadFile(hfile
, buff
, filesize
, &size
, NULL
);
486 if (size
!= filesize
)
495 for(DWORD i
=0;i
<filesize
;)
499 if (buff
[i
] == '#' || buff
[i
] == '^')
501 while (buff
[i
] != '\n')
513 while (buff
[i
] != ' ')
515 hash
.AppendChar(buff
[i
]);
525 while (buff
[i
] != '\n')
527 ref
.AppendChar(buff
[i
]);
535 this->m_PackRefMap
[ref
] = hash
;
538 while (buff
[i
] == '\n')
553 int CGitHeadFileList::ReadHeadHash(CString gitdir
)
556 m_Gitdir
= g_AdminDirMap
.GetAdminDir(gitdir
);
558 m_HeadFile
= m_Gitdir
+ _T("HEAD");
560 if( g_Git
.GetFileModifyTime(m_HeadFile
, &m_LastModifyTimeHead
))
567 CAutoFile hfile
= CreateFile(m_HeadFile
,
569 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
572 FILE_ATTRIBUTE_NORMAL
,
581 DWORD size
= 0,filesize
= 0;
582 unsigned char buffer
[40] ;
583 ReadFile(hfile
, buffer
, 4, &size
, NULL
);
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
;
603 ref
= ref
.Tokenize(_T("\n"), start
);
605 m_HeadRefFile
= m_Gitdir
+ m_HeadRefFile
.Trim();
606 m_HeadRefFile
.Replace(_T('/'),_T('\\'));
609 if (g_Git
.GetFileModifyTime(m_HeadRefFile
, &time
, NULL
))
611 m_HeadRefFile
.Empty();
612 if (GetPackRef(gitdir
))
617 if (this->m_PackRefMap
.find(ref
) == m_PackRefMap
.end())
622 this ->m_Head
= m_PackRefMap
[ref
];
627 CAutoFile href
= CreateFile(m_HeadRefFile
,
629 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
632 FILE_ATTRIBUTE_NORMAL
,
637 m_HeadRefFile
.Empty();
639 if (GetPackRef(gitdir
))
645 if (this->m_PackRefMap
.find(ref
) == m_PackRefMap
.end())
650 this ->m_Head
= m_PackRefMap
[ref
];
654 ReadFile(href
, buffer
, 40, &size
, NULL
);
660 this->m_Head
.ConvertFromStrA((char*)buffer
);
662 this->m_LastModifyTimeRef
= time
;
667 ReadFile(hfile
, buffer
+ 4, 40 - 4, &size
, NULL
);
673 m_HeadRefFile
.Empty();
675 this->m_Head
.ConvertFromStrA((char*)buffer
);
687 bool CGitHeadFileList::CheckHeadUpdate()
689 if (this->m_HeadFile
.IsEmpty())
694 if (g_Git
.GetFileModifyTime(m_HeadFile
, &mtime
))
697 if (mtime
!= this->m_LastModifyTimeHead
)
700 if (!this->m_HeadRefFile
.IsEmpty())
702 if (g_Git
.GetFileModifyTime(m_HeadRefFile
, &mtime
))
705 if (mtime
!= this->m_LastModifyTimeRef
)
709 if(!this->m_PackRefFile
.IsEmpty())
711 if (g_Git
.GetFileModifyTime(m_PackRefFile
, &mtime
))
714 if (mtime
!= this->m_LastModifyTimePackRef
)
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())
726 int CGitHeadFileList::ReadTree()
729 if (this->m_Head
.IsEmpty())
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
);
747 ret
= git_read_tree(this->m_Head
.m_hash
, CGitHeadFileList::CallBack
, this);
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
;
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();
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
)
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
);
801 int mode
= git_tree_entry_attributes(entry
);
802 if( CallBack(git_tree_entry_id(entry
)->id
,
805 git_tree_entry_name(entry
),
808 data
) == READ_TREE_RECURSIVE
813 git_object
*object
= NULL
;
814 git_tree_entry_to_object(&object
, &repo
, entry
);
817 CStringA parent
= base
;
818 parent
+= git_tree_entry_name(entry
);
820 ReadTreeRecursive(repo
, (git_tree
*)object
, parent
, CallBack
, data
);
821 git_object_free(object
);
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
;
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());
843 ret
= git_commit_lookup(&commit
, repository
, (const git_oid
*)m_Head
.m_hash
);
847 ret
= git_commit_tree(&tree
, commit
);
851 ret
= ReadTreeRecursive(*repository
, tree
,"", CGitHeadFileList::CallBack
,this);
855 std::sort(this->begin(), this->end(), SortTree
);
856 this->m_TreeHash
= (char*)(git_commit_id(commit
)->id
);
864 git_commit_free(commit
);
867 git_repository_free(repository
);
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
);
882 this->m_BaseDir
.Empty();
885 CString base
= file
.Mid(projectroot
.GetLength());
886 base
.Replace(_T('\\'), _T('/'));
888 int start
= base
.ReverseFind(_T('/'));
891 base
= base
.Left(start
);
892 this->m_BaseDir
= CUnicodeUtils::GetMulti(base
, CP_UTF8
);
897 if(g_Git
.GetFileModifyTime(file
, &m_LastModifyTime
))
900 if(git_create_exclude_list(&this->m_pExcludeList
))
904 CAutoFile hfile
= CreateFile(file
,
906 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
909 FILE_ATTRIBUTE_NORMAL
,
916 DWORD size
=0,filesize
=0;
918 filesize
=GetFileSize(hfile
, NULL
);
920 if(filesize
== INVALID_FILE_SIZE
)
923 BYTE
*buffer
= new BYTE
[filesize
+ 1];
928 if(! ReadFile(hfile
, buffer
,filesize
,&size
,NULL
))
929 return GetLastError();
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')
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
);
950 /* Can't free buffer, exluced list will use this buffer*/
957 bool CGitIgnoreList::CheckFileChanged(const CString
&path
)
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
))
977 // file exist but cache miss
978 if ((ret
== 0) && (!cacheExist
))
981 // file not exist but cache exist
982 if ((ret
!= 0) && (cacheExist
))
986 // file exist and cache exist
989 CAutoReadLock
lock(&this->m_SharedMutex
);
990 if (m_Map
[path
].m_LastModifyTime
== time
)
996 bool CGitIgnoreList::CheckIgnoreChanged(const CString
&gitdir
,const CString
&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
))
1017 CString wcglobalgitignore
= g_AdminDirMap
.GetAdminDir(tempOrig
) + _T("info\\exclude");
1018 if (CheckFileChanged(wcglobalgitignore
))
1025 temp
+= _T("ignore");
1026 if (CheckFileChanged(temp
))
1032 for (i
= temp
.GetLength() - 1; i
>= 0; i
--)
1034 if(temp
[i
] == _T('\\'))
1041 temp
= temp
.Left(i
);
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
);
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
);
1067 int CGitIgnoreList::LoadAllIgnoreFile(const CString
&gitdir
,const CString
&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);
1101 temp
+= _T("ignore");
1102 if (CheckFileChanged(temp
))
1104 FetchIgnoreFile(gitdir
, temp
, false);
1110 for (i
= temp
.GetLength() - 1; i
>= 0; i
--)
1112 if(temp
[i
] == _T('\\'))
1119 temp
= temp
.Left(i
);
1123 int CGitIgnoreList::GetIgnoreFileChangeTimeList(const CString
&path
, std::vector
<__int64
> &timelist
)
1125 CString temp
= path
;
1126 CString ignore
= temp
;
1130 CAutoReadLock
lock(&this->m_SharedMutex
);
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);
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())
1153 timelist
.push_back(itMap
->second
.m_LastModifyTime
);
1158 ignore
+= _T("\\.git");
1160 if (CGit::GitPathFileExists(ignore
))
1163 start
= temp
.ReverseFind(_T('\\'));
1165 temp
=temp
.Left(start
);
1172 bool CGitIgnoreList::IsIgnore(const CString
&path
,const CString
&projectroot
)
1176 str
.Replace(_T('\\'),_T('/'));
1178 if (str
.GetLength()>0)
1179 if (str
[str
.GetLength()-1] == _T('/'))
1180 str
= str
.Left(str
.GetLength() - 1);
1183 ret
= CheckIgnore(str
, projectroot
);
1186 int start
= str
.ReverseFind(_T('/'));
1190 str
= str
.Left(start
);
1191 ret
= CheckIgnore(str
, projectroot
);
1196 int CGitIgnoreList::CheckFileAgainstIgnoreList(const CString
&ignorefile
, const CStringA
&patha
, const char * base
, int &type
)
1198 if (m_Map
.find(ignorefile
) != m_Map
.end())
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)
1208 int CGitIgnoreList::CheckIgnore(const CString
&path
,const CString
&projectroot
)
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
))
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('\\'));
1230 temp
= temp
.Left(i
);
1236 int pos
= patha
.ReverseFind('/');
1237 base
= pos
>= 0 ? patha
.GetBuffer() + pos
+ 1 : patha
.GetBuffer();
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)
1254 CString wcglobalgitignore
= g_AdminDirMap
.GetAdminDir(tempOrig
) + _T("info\\exclude");
1255 ret
= CheckFileAgainstIgnoreList(wcglobalgitignore
, patha
, base
, type
);
1260 temp
+= _T("ignore");
1261 if ((ret
= CheckFileAgainstIgnoreList(temp
, patha
, base
, type
)) != -1)
1267 for (i
= temp
.GetLength() - 1; i
>= 0; i
--)
1269 if (temp
[i
] == _T('\\'))
1276 temp
= temp
.Left(i
);
1279 patha
.ReleaseBuffer();
1284 bool CGitHeadFileMap::CheckHeadUpdate(const CString
&gitdir
)
1286 SHARED_TREE_PTR ptr
;
1287 ptr
= this->SafeGet(gitdir
);
1291 return ptr
->CheckHeadUpdate();
1295 SHARED_TREE_PTR
ptr1(new CGitHeadFileList
);
1296 ptr1
->ReadHeadHash(gitdir
);
1298 this->SafeSet(gitdir
, ptr1
);
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
);
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
);
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
)
1342 git_wc_status_kind dirstatus
= git_wc_status_none
;
1345 g_Git
.GetFileModifyTime(path
, &time
, &dir
);
1347 result
= _tstat64( gitdir
, &buf
);
1349 result
= _tstat64( gitdir
+_T("\\")+path
, &buf
);
1354 if(buf
.st_mode
& _S_IFDIR
)
1358 if( path
.Right(1) != _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
)
1371 *status
= git_wc_status_normal
;
1373 callback(gitdir
+ _T("\\") + path
, *status
, pData
);
1379 result
= _tstat64(gitdir
+ _T("\\") + at(i
).m_FileName
, &buf
);
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
;
1408 *status
= git_wc_status_unversioned
;
1411 callback(gitdir
+ _T("\\") + path
, *status
, pData
);
1418 GetFileStatus(gitdir
, path
, status
, buf
, callback
, pData
);