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
;
37 int CGitIndex::Print()
39 _tprintf(_T("0x%08X 0x%08X %s %s\n"),
40 (int)this->m_ModifyTime
,
42 this->m_IndexHash
.ToString(),
48 CGitIndexList::CGitIndexList()
50 this->m_LastModifyTime
= 0;
53 static bool SortIndex(CGitIndex
&Item1
, CGitIndex
&Item2
)
55 return Item1
.m_FileName
.Compare(Item2
.m_FileName
) < 0;
58 static bool SortTree(CGitTreeItem
&Item1
, CGitTreeItem
&Item2
)
60 return Item1
.m_FileName
.Compare(Item2
.m_FileName
) < 0;
63 int CGitIndexList::ReadIndex(CString dgitdir
)
67 CStringA gitdir
= CUnicodeUtils::GetMulti(dgitdir
, CP_UTF8
);
68 git_repository
*repository
= NULL
;
69 git_index
*index
= NULL
;
71 int ret
= git_repository_open(&repository
, gitdir
.GetBuffer());
72 gitdir
.ReleaseBuffer();
76 if (git_repository_index(&index
, repository
))
78 git_repository_free(repository
);
82 unsigned int ecount
= git_index_entrycount(index
);
84 for (unsigned int i
= 0; i
< ecount
; ++i
)
86 git_index_entry
*e
= git_index_get(index
, i
);
88 this->at(i
).m_FileName
.Empty();
89 g_Git
.StringAppend(&this->at(i
).m_FileName
, (BYTE
*)e
->path
, CP_UTF8
);
90 this->at(i
).m_FileName
.MakeLower();
91 this->at(i
).m_ModifyTime
= e
->mtime
.seconds
;
92 this->at(i
).m_Flags
= e
->flags
;
93 this->at(i
).m_IndexHash
= (char *) e
->oid
.id
;
96 git_index_free(index
);
97 git_repository_free(repository
);
99 g_Git
.GetFileModifyTime(dgitdir
+ _T("index"), &this->m_LastModifyTime
);
100 std::sort(this->begin(), this->end(), SortIndex
);
105 int CGitIndexList::GetFileStatus(const CString
&gitdir
,const CString
&pathorg
,git_wc_status_kind
*status
,__int64 time
,FIll_STATUS_CALLBACK callback
,void *pData
, CGitHash
*pHash
)
109 CString path
= pathorg
;
112 int start
= SearchInSortVector(*this, ((CString
&)path
).GetBuffer(), -1);
113 ((CString
&)path
).ReleaseBuffer();
117 *status
= git_wc_status_unversioned
;
127 if (index
>= size() )
130 if (at(index
).m_Flags
& GIT_IDXENTRY_VALID
)
132 *status
= git_wc_status_normal
;
134 else if (time
== at(index
).m_ModifyTime
)
136 *status
= git_wc_status_normal
;
140 *status
= git_wc_status_modified
;
143 if (at(index
).m_Flags
& GIT_IDXENTRY_STAGEMASK
)
144 *status
= git_wc_status_conflicted
;
145 else if (at(index
).m_Flags
& GIT_IDXENTRY_INTENT_TO_ADD
)
146 *status
= git_wc_status_added
;
149 *pHash
= at(index
).m_IndexHash
;
154 if(callback
&& status
)
155 callback(gitdir
+ _T("\\") + pathorg
, *status
, false, pData
);
159 int CGitIndexList::GetStatus(const CString
&gitdir
,const CString
&pathParam
, git_wc_status_kind
*status
,
160 BOOL IsFull
, BOOL IsRecursive
,
161 FIll_STATUS_CALLBACK callback
,void *pData
,
165 git_wc_status_kind dirstatus
= git_wc_status_none
;
168 CString path
= pathParam
;
173 result
= g_Git
.GetFileModifyTime(gitdir
, &time
, &isDir
);
175 result
= g_Git
.GetFileModifyTime(gitdir
+ _T("\\") + path
, &time
, &isDir
);
179 *status
= git_wc_status_deleted
;
181 callback(gitdir
+ _T("\\") + path
, git_wc_status_deleted
, false, pData
);
189 if (path
.Right(1) != _T("\\"))
192 int len
= path
.GetLength();
194 for (int i
= 0; i
< size(); i
++)
196 if (at(i
).m_FileName
.GetLength() > len
)
198 if (at(i
).m_FileName
.Left(len
) == path
)
202 *status
= git_wc_status_normal
;
204 callback(gitdir
+ _T("\\") + path
, *status
, false, pData
);
210 result
= g_Git
.GetFileModifyTime(gitdir
+_T("\\") + at(i
).m_FileName
, &time
);
214 *status
= git_wc_status_none
;
215 GetFileStatus(gitdir
, at(i
).m_FileName
, status
, time
, callback
, pData
);
216 if (*status
!= git_wc_status_none
)
218 if (dirstatus
== git_wc_status_none
)
220 dirstatus
= git_wc_status_normal
;
222 if (*status
!= git_wc_status_normal
)
224 dirstatus
= git_wc_status_modified
;
233 if (dirstatus
!= git_wc_status_none
)
239 *status
= git_wc_status_unversioned
;
242 callback(gitdir
+ _T("\\") + path
, *status
, false, pData
);
249 GetFileStatus(gitdir
, path
, status
, time
, callback
, pData
, pHash
);
255 int CGitIndexFileMap::Check(const CString
&gitdir
, bool *isChanged
)
260 CString IndexFile
= g_AdminDirMap
.GetAdminDir(gitdir
) + _T("index");
262 /* Get data associated with "crt_stat.c": */
263 result
= g_Git
.GetFileModifyTime(IndexFile
, &time
);
268 SHARED_INDEX_PTR pIndex
;
269 pIndex
= this->SafeGet(gitdir
);
271 if (pIndex
.get() == NULL
)
278 if (pIndex
->m_LastModifyTime
== time
)
291 int CGitIndexFileMap::LoadIndex(const CString
&gitdir
)
295 SHARED_INDEX_PTR
pIndex(new CGitIndexList
);
297 if(pIndex
->ReadIndex(g_AdminDirMap
.GetAdminDir(gitdir
)))
300 this->SafeSet(gitdir
, pIndex
);
309 int CGitIndexFileMap::GetFileStatus(const CString
&gitdir
, const CString
&path
, git_wc_status_kind
*status
,BOOL IsFull
, BOOL IsRecursive
,
310 FIll_STATUS_CALLBACK callback
,void *pData
,
312 bool isLoadUpdatedIndex
)
316 CheckAndUpdate(gitdir
, isLoadUpdatedIndex
);
318 SHARED_INDEX_PTR pIndex
= this->SafeGet(gitdir
);
319 if (pIndex
.get() != NULL
)
321 pIndex
->GetStatus(gitdir
, path
, status
, IsFull
, IsRecursive
, callback
, pData
, pHash
);
332 int CGitIndexFileMap::IsUnderVersionControl(const CString
&gitdir
, const CString
&path
, bool isDir
,bool *isVersion
, bool isLoadUpdateIndex
)
342 CString subpath
= path
;
343 subpath
.Replace(_T('\\'), _T('/'));
349 CheckAndUpdate(gitdir
, isLoadUpdateIndex
);
351 SHARED_INDEX_PTR pIndex
= this->SafeGet(gitdir
);
356 *isVersion
= (SearchInSortVector(*pIndex
, subpath
.GetBuffer(), subpath
.GetLength()) >= 0);
358 *isVersion
= (SearchInSortVector(*pIndex
, subpath
.GetBuffer(), -1) >= 0);
359 subpath
.ReleaseBuffer();
369 int CGitHeadFileList::GetPackRef(const CString
&gitdir
)
371 CString PackRef
= g_AdminDirMap
.GetAdminDir(gitdir
) + _T("packed-refs");
374 if (g_Git
.GetFileModifyTime(PackRef
, &mtime
))
376 CAutoWriteLock
lock(&this->m_SharedMutex
);
377 //packed refs is not existed
378 this->m_PackRefFile
.Empty();
379 this->m_PackRefMap
.clear();
382 else if(mtime
== m_LastModifyTimePackRef
)
388 CAutoWriteLock
lock(&this->m_SharedMutex
);
389 this->m_PackRefFile
= PackRef
;
390 this->m_LastModifyTimePackRef
= mtime
;
395 CAutoWriteLock
lock(&this->m_SharedMutex
);
396 this->m_PackRefMap
.clear();
398 CAutoFile hfile
= CreateFile(PackRef
,
400 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
403 FILE_ATTRIBUTE_NORMAL
,
413 DWORD filesize
= GetFileSize(hfile
, NULL
);
416 buff
= new char[filesize
];
418 ReadFile(hfile
, buff
, filesize
, &size
, NULL
);
420 if (size
!= filesize
)
429 for(DWORD i
=0;i
<filesize
;)
433 if (buff
[i
] == '#' || buff
[i
] == '^')
435 while (buff
[i
] != '\n')
447 while (buff
[i
] != ' ')
449 hash
.AppendChar(buff
[i
]);
459 while (buff
[i
] != '\n')
461 ref
.AppendChar(buff
[i
]);
469 this->m_PackRefMap
[ref
] = hash
;
472 while (buff
[i
] == '\n')
487 int CGitHeadFileList::ReadHeadHash(CString gitdir
)
490 CAutoWriteLock
lock(&this->m_SharedMutex
);
491 m_Gitdir
= g_AdminDirMap
.GetAdminDir(gitdir
);
493 m_HeadFile
= m_Gitdir
+ _T("HEAD");
495 if( g_Git
.GetFileModifyTime(m_HeadFile
, &m_LastModifyTimeHead
))
502 CAutoFile hfile
= CreateFile(m_HeadFile
,
504 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
507 FILE_ATTRIBUTE_NORMAL
,
516 DWORD size
= 0,filesize
= 0;
517 unsigned char buffer
[40] ;
518 ReadFile(hfile
, buffer
, 4, &size
, NULL
);
525 if (strcmp((const char*)buffer
,"ref:") == 0)
527 filesize
= GetFileSize(hfile
, NULL
);
529 unsigned char *p
= (unsigned char*)malloc(filesize
-4);
531 ReadFile(hfile
, p
, filesize
- 4, &size
, NULL
);
533 m_HeadRefFile
.Empty();
534 g_Git
.StringAppend(&this->m_HeadRefFile
, p
, CP_UTF8
, filesize
- 4);
535 CString ref
= this->m_HeadRefFile
;
538 ref
= ref
.Tokenize(_T("\n"), start
);
540 m_HeadRefFile
= m_Gitdir
+ m_HeadRefFile
.Trim();
541 m_HeadRefFile
.Replace(_T('/'),_T('\\'));
544 if (g_Git
.GetFileModifyTime(m_HeadRefFile
, &time
, NULL
))
546 m_HeadRefFile
.Empty();
547 if (GetPackRef(gitdir
))
552 if (this->m_PackRefMap
.find(ref
) == m_PackRefMap
.end())
557 this ->m_Head
= m_PackRefMap
[ref
];
562 CAutoFile href
= CreateFile(m_HeadRefFile
,
564 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
567 FILE_ATTRIBUTE_NORMAL
,
572 m_HeadRefFile
.Empty();
574 if (GetPackRef(gitdir
))
580 if (this->m_PackRefMap
.find(ref
) == m_PackRefMap
.end())
585 this ->m_Head
= m_PackRefMap
[ref
];
589 ReadFile(href
, buffer
, 40, &size
, NULL
);
595 this->m_Head
.ConvertFromStrA((char*)buffer
);
597 this->m_LastModifyTimeRef
= time
;
602 ReadFile(hfile
, buffer
+ 4, 40 - 4, &size
, NULL
);
608 m_HeadRefFile
.Empty();
610 this->m_Head
.ConvertFromStrA((char*)buffer
);
622 bool CGitHeadFileList::CheckHeadUpdate()
624 CAutoReadLock
lock(&m_SharedMutex
);
625 if (this->m_HeadFile
.IsEmpty())
630 if (g_Git
.GetFileModifyTime(m_HeadFile
, &mtime
))
633 if (mtime
!= this->m_LastModifyTimeHead
)
636 if (!this->m_HeadRefFile
.IsEmpty())
638 if (g_Git
.GetFileModifyTime(m_HeadRefFile
, &mtime
))
641 if (mtime
!= this->m_LastModifyTimeRef
)
645 if(!this->m_PackRefFile
.IsEmpty())
647 if (g_Git
.GetFileModifyTime(m_PackRefFile
, &mtime
))
650 if (mtime
!= this->m_LastModifyTimePackRef
)
654 // in an empty repo HEAD points to refs/heads/master, but this ref doesn't exist.
655 // So we need to retry again and again until the ref exists - otherwise we will never notice
656 if (this->m_Head
.IsEmpty() && this->m_HeadRefFile
.IsEmpty() && this->m_PackRefFile
.IsEmpty())
662 int CGitHeadFileList::ReadTree()
665 if (this->m_Head
.IsEmpty())
670 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
671 CAutoWriteLock
lock1(&this->m_SharedMutex
);
673 if (m_Gitdir
!= g_Git
.m_CurrentDir
)
675 g_Git
.SetCurrentDir(m_Gitdir
);
676 SetCurrentDirectory(g_Git
.m_CurrentDir
);
683 ret
= git_read_tree(this->m_Head
.m_hash
, CGitHeadFileList::CallBack
, this);
695 int CGitHeadFileList::CallBack(const unsigned char *sha1
, const char *base
, int baselen
,
696 const char *pathname
, unsigned mode
, int /*stage*/, void *context
)
698 #define S_IFGITLINK 0160000
700 CGitHeadFileList
*p
= (CGitHeadFileList
*)context
;
703 if( (mode
&S_IFMT
) != S_IFGITLINK
)
704 return READ_TREE_RECURSIVE
;
707 unsigned int cur
= p
->size();
708 p
->resize(p
->size() + 1);
709 p
->at(cur
).m_Hash
= (char*)sha1
;
710 p
->at(cur
).m_FileName
.Empty();
713 g_Git
.StringAppend(&p
->at(cur
).m_FileName
, (BYTE
*)base
, CP_UTF8
, baselen
);
715 g_Git
.StringAppend(&p
->at(cur
).m_FileName
,(BYTE
*)pathname
, CP_UTF8
);
717 p
->at(cur
).m_FileName
.MakeLower();
719 //p->at(cur).m_FileName.Replace(_T('/'), _T('\\'));
721 //p->m_Map[p->at(cur).m_FileName] = cur;
723 if( (mode
&S_IFMT
) == S_IFGITLINK
)
726 return READ_TREE_RECURSIVE
;
729 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
)
731 size_t count
= git_tree_entrycount(tree
);
732 for (int i
= 0; i
< count
; i
++)
734 const git_tree_entry
*entry
= git_tree_entry_byindex(tree
, i
);
737 int mode
= git_tree_entry_attributes(entry
);
738 if( CallBack(git_tree_entry_id(entry
)->id
,
741 git_tree_entry_name(entry
),
744 data
) == READ_TREE_RECURSIVE
749 git_object
*object
= NULL
;
750 git_tree_entry_to_object(&object
, &repo
, entry
);
753 CStringA parent
= base
;
754 parent
+= git_tree_entry_name(entry
);
756 ReadTreeRecursive(repo
, (git_tree
*)object
, parent
, CallBack
, data
);
757 git_object_free(object
);
766 int CGitHeadFileList::ReadTree()
768 CAutoWriteLock
lock(&m_SharedMutex
);
769 CStringA gitdir
= CUnicodeUtils::GetMulti(m_Gitdir
, CP_UTF8
);
770 git_repository
*repository
= NULL
;
771 git_commit
*commit
= NULL
;
772 git_tree
* tree
= NULL
;
774 this->clear(); // hack to avoid duplicates in the head list, which are introduced in GitStatus::GetFileStatus when this method is called
777 ret
= git_repository_open(&repository
, gitdir
.GetBuffer());
778 gitdir
.ReleaseBuffer();
781 ret
= git_commit_lookup(&commit
, repository
, (const git_oid
*)m_Head
.m_hash
);
785 ret
= git_commit_tree(&tree
, commit
);
789 ret
= ReadTreeRecursive(*repository
, tree
,"", CGitHeadFileList::CallBack
,this);
793 std::sort(this->begin(), this->end(), SortTree
);
794 this->m_TreeHash
= (char*)(git_commit_id(commit
)->id
);
802 git_commit_free(commit
);
805 git_repository_free(repository
);
810 int CGitIgnoreItem::FetchIgnoreList(const CString
&projectroot
, const CString
&file
, bool isGlobal
)
812 CAutoWriteLock
lock(&this->m_SharedMutex
);
814 if (this->m_pExcludeList
)
816 free(m_pExcludeList
);
820 this->m_BaseDir
.Empty();
823 CString base
= file
.Mid(projectroot
.GetLength() + 1);
824 base
.Replace(_T('\\'), _T('/'));
826 int start
= base
.ReverseFind(_T('/'));
829 base
= base
.Left(start
);
830 this->m_BaseDir
= CUnicodeUtils::GetMulti(base
, CP_UTF8
) + "/";
835 if(g_Git
.GetFileModifyTime(file
, &m_LastModifyTime
))
838 if(git_create_exclude_list(&this->m_pExcludeList
))
842 CAutoFile hfile
= CreateFile(file
,
844 FILE_SHARE_READ
|FILE_SHARE_DELETE
|FILE_SHARE_WRITE
,
847 FILE_ATTRIBUTE_NORMAL
,
854 DWORD size
=0,filesize
=0;
856 filesize
=GetFileSize(hfile
, NULL
);
858 if(filesize
== INVALID_FILE_SIZE
)
861 BYTE
*buffer
= new BYTE
[filesize
+ 1];
866 if(! ReadFile(hfile
, buffer
,filesize
,&size
,NULL
))
867 return GetLastError();
870 for (int i
= 0; i
< size
; i
++)
872 if (buffer
[i
] == '\n' || buffer
[i
] == '\r' || i
== (size
- 1))
874 if (buffer
[i
] == '\n' || buffer
[i
] == '\r')
879 if(p
[0] != '#' && p
[0] != 0)
880 git_add_exclude((const char*)p
,
881 this->m_BaseDir
.GetBuffer(),
882 m_BaseDir
.GetLength(),
883 this->m_pExcludeList
);
888 /* Can't free buffer, exluced list will use this buffer*/
895 bool CGitIgnoreList::CheckFileChanged(const CString
&path
)
899 int ret
= g_Git
.GetFileModifyTime(path
, &time
);
901 this->m_SharedMutex
.AcquireShared();
902 bool cacheExist
= (m_Map
.find(path
) != m_Map
.end());
903 this->m_SharedMutex
.ReleaseShared();
905 if (!cacheExist
&& ret
== 0)
907 CAutoWriteLock
lock(&this->m_SharedMutex
);
908 m_Map
[path
].m_LastModifyTime
= 0;
909 m_Map
[path
].m_SharedMutex
.Init();
911 // both cache and file is not exist
912 if ((ret
!= 0) && (!cacheExist
))
915 // file exist but cache miss
916 if ((ret
== 0) && (!cacheExist
))
919 // file not exist but cache exist
920 if ((ret
!= 0) && (cacheExist
))
924 // file exist and cache exist
927 CAutoReadLock
lock(&this->m_SharedMutex
);
928 if (m_Map
[path
].m_LastModifyTime
== time
)
934 bool CGitIgnoreList::CheckIgnoreChanged(const CString
&gitdir
,const CString
&path
)
941 temp
.Replace(_T('/'), _T('\\'));
943 while(!temp
.IsEmpty())
945 CString tempOrig
= temp
;
946 temp
+= _T("\\.git");
948 if (CGit::GitPathFileExists(temp
))
950 CString gitignore
=temp
;
951 gitignore
+= _T("ignore");
952 if (CheckFileChanged(gitignore
))
955 CString adminDir
= g_AdminDirMap
.GetAdminDir(tempOrig
);
956 CString wcglobalgitignore
= adminDir
+ _T("info\\exclude");
957 if (CheckFileChanged(wcglobalgitignore
))
960 if (CheckAndUpdateCoreExcludefile(adminDir
))
967 temp
+= _T("ignore");
968 if (CheckFileChanged(temp
))
974 for (i
= temp
.GetLength() - 1; i
>= 0; i
--)
976 if(temp
[i
] == _T('\\'))
988 int CGitIgnoreList::FetchIgnoreFile(const CString
&gitdir
, const CString
&gitignore
, bool isGlobal
)
990 if (CGit::GitPathFileExists(gitignore
)) //if .gitignore remove, we need remote cache
992 CAutoWriteLock
lock(&this->m_SharedMutex
);
993 if (m_Map
.find(gitignore
) == m_Map
.end())
994 m_Map
[gitignore
].m_SharedMutex
.Init();
996 m_Map
[gitignore
].FetchIgnoreList(gitdir
, gitignore
, isGlobal
);
1000 CAutoWriteLock
lock(&this->m_SharedMutex
);
1001 if (m_Map
.find(gitignore
) != m_Map
.end())
1002 m_Map
[gitignore
].m_SharedMutex
.Release();
1004 m_Map
.erase(gitignore
);
1009 int CGitIgnoreList::LoadAllIgnoreFile(const CString
&gitdir
,const CString
&path
)
1017 temp
.Replace(_T('/'), _T('\\'));
1019 while (!temp
.IsEmpty())
1021 CString tempOrig
= temp
;
1022 temp
+= _T("\\.git");
1024 if (CGit::GitPathFileExists(temp
))
1026 CString gitignore
= temp
;
1027 gitignore
+= _T("ignore");
1028 if (CheckFileChanged(gitignore
))
1030 FetchIgnoreFile(gitdir
, gitignore
, false);
1033 CString adminDir
= g_AdminDirMap
.GetAdminDir(tempOrig
);
1034 CString wcglobalgitignore
= adminDir
+ _T("info\\exclude");
1035 if (CheckFileChanged(wcglobalgitignore
))
1037 FetchIgnoreFile(gitdir
, wcglobalgitignore
, true);
1040 if (CheckAndUpdateCoreExcludefile(adminDir
))
1042 m_SharedMutex
.AcquireShared();
1043 CString excludesFile
= m_CoreExcludesfiles
[adminDir
];
1044 m_SharedMutex
.ReleaseShared();
1045 if (!excludesFile
.IsEmpty())
1046 FetchIgnoreFile(gitdir
, excludesFile
, true);
1053 temp
+= _T("ignore");
1054 if (CheckFileChanged(temp
))
1056 FetchIgnoreFile(gitdir
, temp
, false);
1062 for (i
= temp
.GetLength() - 1; i
>= 0; i
--)
1064 if(temp
[i
] == _T('\\'))
1071 temp
= temp
.Left(i
);
1075 bool CGitIgnoreList::CheckAndUpdateMsysGitBinpath(bool force
)
1077 // recheck every 30 seconds
1078 if (GetTickCount() - m_dMsysGitBinPathLastChecked
> 30000 || force
)
1080 m_dMsysGitBinPathLastChecked
= GetTickCount();
1081 CString
msysGitBinPath(CRegString(REG_MSYSGIT_PATH
, _T(""), FALSE
));
1082 if (msysGitBinPath
!= m_sMsysGitBinPath
)
1084 m_sMsysGitBinPath
= msysGitBinPath
;
1090 bool CGitIgnoreList::CheckAndUpdateCoreExcludefile(const CString
&adminDir
)
1092 bool hasChanged
= false;
1094 CString projectConfig
= adminDir
+ _T("config");
1095 CString globalConfig
= GetWindowsHome() + _T("\\.gitconfig");
1097 CAutoWriteLock
lock(&m_coreExcludefilesSharedMutex
);
1098 hasChanged
= CheckAndUpdateMsysGitBinpath();
1099 CString systemConfig
= m_sMsysGitBinPath
+ _T("\\..\\etc\\gitconfig");
1101 hasChanged
= hasChanged
|| CheckFileChanged(projectConfig
);
1102 hasChanged
= hasChanged
|| CheckFileChanged(globalConfig
);
1103 if (!m_sMsysGitBinPath
.IsEmpty())
1104 hasChanged
= hasChanged
|| CheckFileChanged(systemConfig
);
1106 m_SharedMutex
.AcquireShared();
1107 CString excludesFile
= m_CoreExcludesfiles
[adminDir
];
1108 m_SharedMutex
.ReleaseShared();
1109 if (!excludesFile
.IsEmpty())
1110 hasChanged
= hasChanged
|| CheckFileChanged(excludesFile
);
1115 git_config
* config
;
1116 git_config_new(&config
);
1117 CStringA projectConfigA
= CUnicodeUtils::GetMulti(projectConfig
, CP_UTF8
);
1118 git_config_add_file_ondisk(config
, projectConfigA
.GetBuffer(), 3);
1119 projectConfigA
.ReleaseBuffer();
1120 CStringA globalConfigA
= CUnicodeUtils::GetMulti(globalConfig
, CP_UTF8
);
1121 git_config_add_file_ondisk(config
, globalConfigA
.GetBuffer(), 2);
1122 globalConfigA
.ReleaseBuffer();
1123 if (!m_sMsysGitBinPath
.IsEmpty())
1125 CStringA systemConfigA
= CUnicodeUtils::GetMulti(systemConfig
, CP_UTF8
);
1126 git_config_add_file_ondisk(config
, systemConfigA
.GetBuffer(), 1);
1127 systemConfigA
.ReleaseBuffer();
1129 const char * out
= NULL
;
1130 CStringA
name(_T("core.excludesfile"));
1131 git_config_get_string(&out
, config
, name
.GetBuffer());
1132 name
.ReleaseBuffer();
1133 CStringA
excludesFileA(out
);
1134 excludesFile
= CUnicodeUtils::GetUnicode(excludesFileA
);
1135 if (excludesFile
.Find(_T("~/")) == 0)
1136 excludesFile
= GetWindowsHome() + excludesFile
.Mid(1);
1137 git_config_free(config
);
1139 CAutoWriteLock
lockMap(&m_SharedMutex
);
1140 g_Git
.GetFileModifyTime(projectConfig
, &m_Map
[projectConfig
].m_LastModifyTime
);
1141 g_Git
.GetFileModifyTime(globalConfig
, &m_Map
[globalConfig
].m_LastModifyTime
);
1142 if (m_Map
[globalConfig
].m_LastModifyTime
== 0)
1144 m_Map
[globalConfig
].m_SharedMutex
.Release();
1145 m_Map
.erase(globalConfig
);
1147 if (!m_sMsysGitBinPath
.IsEmpty())
1148 g_Git
.GetFileModifyTime(systemConfig
, &m_Map
[systemConfig
].m_LastModifyTime
);
1149 if (m_Map
[systemConfig
].m_LastModifyTime
== 0 || m_sMsysGitBinPath
.IsEmpty())
1151 m_Map
[systemConfig
].m_SharedMutex
.Release();
1152 m_Map
.erase(systemConfig
);
1154 m_CoreExcludesfiles
[adminDir
] = excludesFile
;
1158 const CString
CGitIgnoreList::GetWindowsHome()
1160 static CString
sWindowsHome(g_Git
.GetHomeDirectory());
1161 return sWindowsHome
;
1163 bool CGitIgnoreList::IsIgnore(const CString
&path
,const CString
&projectroot
)
1167 str
.Replace(_T('\\'),_T('/'));
1169 if (str
.GetLength()>0)
1170 if (str
[str
.GetLength()-1] == _T('/'))
1171 str
= str
.Left(str
.GetLength() - 1);
1174 ret
= CheckIgnore(str
, projectroot
);
1177 int start
= str
.ReverseFind(_T('/'));
1181 str
= str
.Left(start
);
1182 ret
= CheckIgnore(str
, projectroot
);
1187 int CGitIgnoreList::CheckFileAgainstIgnoreList(const CString
&ignorefile
, const CStringA
&patha
, const char * base
, int &type
)
1189 if (m_Map
.find(ignorefile
) != m_Map
.end())
1192 if(m_Map
[ignorefile
].m_pExcludeList
)
1193 ret
= git_check_excluded_1(patha
, patha
.GetLength(), base
, &type
, m_Map
[ignorefile
].m_pExcludeList
);
1194 if (ret
== 0 || ret
== 1)
1199 int CGitIgnoreList::CheckIgnore(const CString
&path
,const CString
&projectroot
)
1203 CString temp
= projectroot
+ _T("\\") + path
;
1204 temp
.Replace(_T('/'), _T('\\'));
1206 CStringA patha
= CUnicodeUtils::GetMulti(path
, CP_UTF8
);
1207 patha
.Replace('\\', '/');
1209 if(g_Git
.GetFileModifyTime(temp
, &time
, &dir
))
1217 // strip directory name
1218 // we do not need to check for a .ignore file inside a directory we might ignore
1219 int i
= temp
.ReverseFind(_T('\\'));
1221 temp
= temp
.Left(i
);
1227 int pos
= patha
.ReverseFind('/');
1228 base
= pos
>= 0 ? patha
.GetBuffer() + pos
+ 1 : patha
.GetBuffer();
1232 CAutoReadLock
lock(&this->m_SharedMutex
);
1233 while (!temp
.IsEmpty())
1235 CString tempOrig
= temp
;
1236 temp
+= _T("\\.git");
1238 if (CGit::GitPathFileExists(temp
))
1240 CString gitignore
= temp
;
1241 gitignore
+= _T("ignore");
1242 if ((ret
= CheckFileAgainstIgnoreList(gitignore
, patha
, base
, type
)) != -1)
1245 CString adminDir
= g_AdminDirMap
.GetAdminDir(tempOrig
);
1246 CString wcglobalgitignore
= adminDir
+ _T("info\\exclude");
1247 if ((ret
= CheckFileAgainstIgnoreList(wcglobalgitignore
, patha
, base
, type
)) != -1)
1250 m_SharedMutex
.AcquireShared();
1251 CString excludesFile
= m_CoreExcludesfiles
[adminDir
];
1252 m_SharedMutex
.ReleaseShared();
1253 if (!excludesFile
.IsEmpty())
1254 ret
= CheckFileAgainstIgnoreList(excludesFile
, 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::IsUnderVersionControl(const CString
&gitdir
, const CString
&path
, bool isDir
, bool *isVersion
)
1314 CString subpath
= path
;
1315 subpath
.Replace(_T('\\'), _T('/'));
1319 subpath
.MakeLower();
1321 CheckHeadUpdate(gitdir
);
1323 SHARED_TREE_PTR treeptr
;
1324 treeptr
= SafeGet(gitdir
);
1326 if (treeptr
->m_Head
!= treeptr
->m_TreeHash
)
1328 treeptr
->ReadHeadHash(gitdir
);
1331 if (treeptr
->m_HeadFile
.IsEmpty())
1336 else if (treeptr
->ReadTree())
1338 treeptr
->m_LastModifyTimeHead
= 0;
1342 SafeSet(gitdir
, treeptr
);
1346 *isVersion
= (SearchInSortVector(*treeptr
, subpath
.GetBuffer(), subpath
.GetLength()) >= 0);
1348 *isVersion
= (SearchInSortVector(*treeptr
, subpath
.GetBuffer(), -1) >= 0);
1349 subpath
.ReleaseBuffer();
1359 int CGitHeadFileMap::GetHeadHash(const CString
&gitdir
, CGitHash
&hash
)
1361 SHARED_TREE_PTR ptr
;
1362 ptr
= this->SafeGet(gitdir
);
1364 if(ptr
.get() == NULL
)
1366 SHARED_TREE_PTR
ptr1(new CGitHeadFileList());
1367 ptr1
->ReadHeadHash(gitdir
);
1369 hash
= ptr1
->m_Head
;
1371 this->SafeSet(gitdir
, ptr1
);
1376 if(ptr
->CheckHeadUpdate())
1378 SHARED_TREE_PTR
ptr1(new CGitHeadFileList());
1379 ptr1
->ReadHeadHash(gitdir
);
1381 hash
= ptr1
->m_Head
;
1382 this->SafeSet(gitdir
, ptr1
);