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.
22 //#include "resource.h"
23 #include "..\TortoiseShell\resource.h"
24 //#include "git_config.h"
25 #include "GitStatus.h"
26 #include "UnicodeUtils.h"
27 //#include "GitGlobal.h"
28 //#include "GitHelpers.h"
31 //# include "MessageBox.h"
32 //# include "registry.h"
33 //# include "TGitPath.h"
34 //# include "PathUtils.h"
39 #include "shellcache.h"
41 extern CGitAdminDirMap g_AdminDirMap
;
42 CGitIndexFileMap g_IndexFileMap
;
43 CGitHeadFileMap g_HeadFileMap
;
44 CGitIgnoreList g_IgnoreList
;
46 GitStatus::GitStatus()
51 GitStatus::~GitStatus(void)
56 git_wc_status_kind
GitStatus::GetAllStatus(const CTGitPath
& path
, git_depth_t depth
, bool * assumeValid
, bool * skipWorktree
)
58 git_wc_status_kind statuskind
;
63 isDir
= path
.IsDirectory();
64 if (!path
.HasAdminDir(&sProjectRoot
))
65 return git_wc_status_none
;
67 // rev.kind = git_opt_revision_unspecified;
68 statuskind
= git_wc_status_none
;
70 const BOOL bIsRecursive
= (depth
== git_depth_infinity
|| depth
== git_depth_unknown
); // taken from SVN source
73 CString s
= path
.GetWinPathString();
74 if (s
.GetLength() > sProjectRoot
.GetLength())
76 if (sProjectRoot
.GetLength() == 3 && sProjectRoot
[1] == _T(':'))
77 sSubPath
= s
.Right(s
.GetLength() - sProjectRoot
.GetLength());
79 sSubPath
= s
.Right(s
.GetLength() - sProjectRoot
.GetLength() - 1/*otherwise it gets initial slash*/);
82 bool isfull
= ((DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
83 GetSystemMetrics(SM_REMOTESESSION
) ? ShellCache::dll
: ShellCache::exe
) == ShellCache::dllFull
);
87 err
= GetDirStatus(sProjectRoot
,sSubPath
,&statuskind
, isfull
,bIsRecursive
,isfull
,NULL
, NULL
);
88 // folders must not be displayed as added or deleted only as modified (this is for Shell Overlay-Modes)
89 if (statuskind
== git_wc_status_unversioned
&& sSubPath
.IsEmpty())
90 statuskind
= git_wc_status_normal
;
91 else if (statuskind
== git_wc_status_deleted
|| statuskind
== git_wc_status_added
)
92 statuskind
= git_wc_status_modified
;
96 err
= GetFileStatus(sProjectRoot
, sSubPath
, &statuskind
, isfull
, false, isfull
, NULL
, NULL
, assumeValid
, skipWorktree
);
103 git_wc_status_kind
GitStatus::GetAllStatusRecursive(const CTGitPath
& path
)
105 return GetAllStatus(path
, git_depth_infinity
);
109 git_wc_status_kind
GitStatus::GetMoreImportant(git_wc_status_kind status1
, git_wc_status_kind status2
)
111 if (GetStatusRanking(status1
) >= GetStatusRanking(status2
))
115 // static private method
116 int GitStatus::GetStatusRanking(git_wc_status_kind status
)
120 case git_wc_status_none
:
122 case git_wc_status_unversioned
:
124 case git_wc_status_ignored
:
126 case git_wc_status_incomplete
:
128 case git_wc_status_normal
:
129 case git_wc_status_external
:
131 case git_wc_status_added
:
133 case git_wc_status_missing
:
135 case git_wc_status_deleted
:
137 case git_wc_status_replaced
:
139 case git_wc_status_modified
:
141 case git_wc_status_merged
:
143 case git_wc_status_conflicted
:
145 case git_wc_status_obstructed
:
151 void GitStatus::GetStatus(const CTGitPath
& path
, bool /*update*/ /* = false */, bool noignore
/* = false */, bool /*noexternals*/ /* = false */)
153 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
154 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
155 // after the call again
157 CString sProjectRoot
;
158 if ( !path
.HasAdminDir(&sProjectRoot
) )
161 bool isfull
= ((DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
162 GetSystemMetrics(SM_REMOTESESSION
) ? ShellCache::dll
: ShellCache::exe
) == ShellCache::dllFull
);
165 LPCTSTR lpszSubPath
= NULL
;
167 CString s
= path
.GetWinPathString();
168 if (s
.GetLength() > sProjectRoot
.GetLength())
170 sSubPath
= s
.Right(s
.GetLength() - sProjectRoot
.GetLength());
171 lpszSubPath
= sSubPath
;
172 // skip initial slash if necessary
173 if (*lpszSubPath
== _T('\\'))
177 m_status
.prop_status
= m_status
.text_status
= git_wc_status_none
;
178 m_status
.assumeValid
= false;
179 m_status
.skipWorktree
= false;
181 if(path
.IsDirectory())
183 m_err
= GetDirStatus(sProjectRoot
,CString(lpszSubPath
),&m_status
.text_status
, isfull
, false,!noignore
, NULL
, NULL
);
188 m_err
= GetFileStatus(sProjectRoot
, CString(lpszSubPath
), &m_status
.text_status
,isfull
, false,!noignore
, NULL
,NULL
, &m_status
.assumeValid
, &m_status
.skipWorktree
);
192 // Error present if function is not under version control
202 void GitStatus::GetStatusString(git_wc_status_kind status
, size_t buflen
, TCHAR
* string
)
207 case git_wc_status_none
:
210 case git_wc_status_unversioned
:
211 buf
= _T("unversioned\0");
213 case git_wc_status_normal
:
214 buf
= _T("normal\0");
216 case git_wc_status_added
:
219 case git_wc_status_missing
:
220 buf
= _T("missing\0");
222 case git_wc_status_deleted
:
223 buf
= _T("deleted\0");
225 case git_wc_status_replaced
:
226 buf
= _T("replaced\0");
228 case git_wc_status_modified
:
229 buf
= _T("modified\0");
231 case git_wc_status_merged
:
232 buf
= _T("merged\0");
234 case git_wc_status_conflicted
:
235 buf
= _T("conflicted\0");
237 case git_wc_status_obstructed
:
238 buf
= _T("obstructed\0");
240 case git_wc_status_ignored
:
243 case git_wc_status_external
:
244 buf
= _T("external");
246 case git_wc_status_incomplete
:
247 buf
= _T("incomplete\0");
253 _stprintf_s(string
, buflen
, _T("%s"), buf
);
256 void GitStatus::GetStatusString(HINSTANCE hInst
, git_wc_status_kind status
, TCHAR
* string
, int size
, WORD lang
)
260 case git_wc_status_none
:
261 LoadStringEx(hInst
, IDS_STATUSNONE
, string
, size
, lang
);
263 case git_wc_status_unversioned
:
264 LoadStringEx(hInst
, IDS_STATUSUNVERSIONED
, string
, size
, lang
);
266 case git_wc_status_normal
:
267 LoadStringEx(hInst
, IDS_STATUSNORMAL
, string
, size
, lang
);
269 case git_wc_status_added
:
270 LoadStringEx(hInst
, IDS_STATUSADDED
, string
, size
, lang
);
272 case git_wc_status_missing
:
273 LoadStringEx(hInst
, IDS_STATUSABSENT
, string
, size
, lang
);
275 case git_wc_status_deleted
:
276 LoadStringEx(hInst
, IDS_STATUSDELETED
, string
, size
, lang
);
278 case git_wc_status_replaced
:
279 LoadStringEx(hInst
, IDS_STATUSREPLACED
, string
, size
, lang
);
281 case git_wc_status_modified
:
282 LoadStringEx(hInst
, IDS_STATUSMODIFIED
, string
, size
, lang
);
284 case git_wc_status_merged
:
285 LoadStringEx(hInst
, IDS_STATUSMERGED
, string
, size
, lang
);
287 case git_wc_status_conflicted
:
288 LoadStringEx(hInst
, IDS_STATUSCONFLICTED
, string
, size
, lang
);
290 case git_wc_status_ignored
:
291 LoadStringEx(hInst
, IDS_STATUSIGNORED
, string
, size
, lang
);
293 case git_wc_status_obstructed
:
294 LoadStringEx(hInst
, IDS_STATUSOBSTRUCTED
, string
, size
, lang
);
296 case git_wc_status_external
:
297 LoadStringEx(hInst
, IDS_STATUSEXTERNAL
, string
, size
, lang
);
299 case git_wc_status_incomplete
:
300 LoadStringEx(hInst
, IDS_STATUSINCOMPLETE
, string
, size
, lang
);
303 LoadStringEx(hInst
, IDS_STATUSNONE
, string
, size
, lang
);
308 int GitStatus::LoadStringEx(HINSTANCE hInstance
, UINT uID
, LPTSTR lpBuffer
, int nBufferMax
, WORD wLanguage
)
310 const STRINGRESOURCEIMAGE
* pImage
;
311 const STRINGRESOURCEIMAGE
* pImageEnd
;
317 HRSRC hResource
= FindResourceEx(hInstance
, RT_STRING
, MAKEINTRESOURCE(((uID
>>4)+1)), wLanguage
);
320 // try the default language before giving up!
321 hResource
= FindResource(hInstance
, MAKEINTRESOURCE(((uID
>>4)+1)), RT_STRING
);
325 hGlobal
= LoadResource(hInstance
, hResource
);
328 pImage
= (const STRINGRESOURCEIMAGE
*)::LockResource(hGlobal
);
332 nResourceSize
= ::SizeofResource(hInstance
, hResource
);
333 pImageEnd
= (const STRINGRESOURCEIMAGE
*)(LPBYTE(pImage
)+nResourceSize
);
336 while ((iIndex
> 0) && (pImage
< pImageEnd
))
338 pImage
= (const STRINGRESOURCEIMAGE
*)(LPBYTE(pImage
)+(sizeof(STRINGRESOURCEIMAGE
)+(pImage
->nLength
*sizeof(WCHAR
))));
341 if (pImage
>= pImageEnd
)
343 if (pImage
->nLength
== 0)
346 ret
= pImage
->nLength
;
347 if (pImage
->nLength
> nBufferMax
)
349 wcsncpy_s(lpBuffer
, nBufferMax
, pImage
->achString
, pImage
->nLength
-1);
350 lpBuffer
[nBufferMax
-1] = 0;
354 wcsncpy_s(lpBuffer
, nBufferMax
, pImage
->achString
, pImage
->nLength
);
360 typedef CComCritSecLock
<CComCriticalSection
> CAutoLocker
;
362 int GitStatus::GetFileStatus(const CString
&gitdir
, const CString
&pathParam
, git_wc_status_kind
* status
,BOOL IsFull
, BOOL
/*IsRecursive*/,BOOL IsIgnore
, FIll_STATUS_CALLBACK callback
, void *pData
, bool * assumeValid
, bool * skipWorktree
)
366 CString path
= pathParam
;
368 TCHAR oldpath
[MAX_PATH
+1];
369 memset(oldpath
,0,MAX_PATH
+1);
371 path
.Replace(_T('\\'),_T('/'));
373 CString lowcasepath
=path
;
374 lowcasepath
.MakeLower();
378 git_wc_status_kind st
= git_wc_status_none
;
381 g_IndexFileMap
.GetFileStatus(gitdir
, path
, &st
, IsFull
, false, callback
, pData
, &hash
, true, assumeValid
, skipWorktree
);
383 if( st
== git_wc_status_conflicted
)
387 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
391 if( st
== git_wc_status_unversioned
)
395 *status
= git_wc_status_unversioned
;
397 callback(gitdir
+ _T("/") + path
, *status
, false, pData
, *assumeValid
, *skipWorktree
);
401 if( g_IgnoreList
.CheckIgnoreChanged(gitdir
,path
))
403 g_IgnoreList
.LoadAllIgnoreFile(gitdir
,path
);
405 if( g_IgnoreList
.IsIgnore(path
, gitdir
) )
407 st
= git_wc_status_ignored
;
411 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
416 if( st
== git_wc_status_normal
&& IsFull
)
419 g_HeadFileMap
.CheckHeadUpdate(gitdir
);
422 SHARED_TREE_PTR treeptr
;
424 treeptr
=g_HeadFileMap
.SafeGet(gitdir
);
426 b
= treeptr
->m_Head
!= treeptr
->m_TreeHash
;
430 treeptr
->ReadHeadHash(gitdir
);
433 if( treeptr
->m_HeadFile
.IsEmpty() )
435 *status
=st
=git_wc_status_added
;
437 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
440 if(treeptr
->ReadTree())
442 //Check if init repository
443 *status
= treeptr
->m_Head
.IsEmpty()? git_wc_status_added
: st
;
445 callback(gitdir
+ _T("/") + path
, *status
, false, pData
, *assumeValid
, *skipWorktree
);
448 g_HeadFileMap
.SafeSet(gitdir
, treeptr
);
451 // Check Head Tree Hash;
455 int start
=SearchInSortVector(*treeptr
,lowcasepath
.GetBuffer(),-1);
456 lowcasepath
.ReleaseBuffer();
460 *status
=st
=git_wc_status_added
;
461 ATLTRACE(_T("File miss in head tree %s"), path
);
463 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
467 //staged and not commit
468 if( treeptr
->at(start
).m_Hash
!= hash
)
470 *status
=st
=git_wc_status_modified
;
472 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
480 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
487 *status
= git_wc_status_none
;
495 bool GitStatus::IsGitReposChanged(const CString
&gitdir
,const CString
&subpaths
, int mode
)
497 if( mode
& GIT_MODE_INDEX
)
499 return g_IndexFileMap
.CheckAndUpdate(gitdir
, true);
502 if( mode
& GIT_MODE_HEAD
)
504 if(g_HeadFileMap
.CheckHeadUpdate(gitdir
))
508 if( mode
& GIT_MODE_IGNORE
)
510 if(g_IgnoreList
.CheckIgnoreChanged(gitdir
,subpaths
))
516 int GitStatus::LoadIgnoreFile(const CString
&gitdir
,const CString
&subpaths
)
518 return g_IgnoreList
.LoadAllIgnoreFile(gitdir
,subpaths
);
520 int GitStatus::IsUnderVersionControl(const CString
&gitdir
, const CString
&path
, bool isDir
,bool *isVersion
)
522 if (g_IndexFileMap
.IsUnderVersionControl(gitdir
, path
, isDir
, isVersion
))
525 return g_HeadFileMap
.IsUnderVersionControl(gitdir
, path
, isDir
, isVersion
);
529 __int64
GitStatus::GetIndexFileTime(const CString
&gitdir
)
531 SHARED_INDEX_PTR ptr
=g_IndexFileMap
.SafeGet(gitdir
);
532 if(ptr
.get() == NULL
)
535 return ptr
->m_LastModifyTime
;
538 int GitStatus::IsIgnore(const CString
&gitdir
, const CString
&path
, bool *isIgnore
)
540 if(::g_IgnoreList
.CheckIgnoreChanged(gitdir
,path
))
541 g_IgnoreList
.LoadAllIgnoreFile(gitdir
,path
);
543 *isIgnore
= g_IgnoreList
.IsIgnore(path
,gitdir
);
548 static bool SortFileName(CGitFileName
&Item1
, CGitFileName
&Item2
)
550 return Item1
.m_FileName
.Compare(Item2
.m_FileName
)<0;
553 int GitStatus::GetFileList(const CString
&gitdir
, const CString
&subpath
, std::vector
<CGitFileName
> &list
)
555 WIN32_FIND_DATA data
;
556 HANDLE handle
=::FindFirstFile(gitdir
+_T("\\")+subpath
+_T("\\*.*"), &data
);
559 if(_tcscmp(data
.cFileName
, _T(".git")) == 0)
562 if(_tcscmp(data
.cFileName
, _T(".")) == 0)
565 if(_tcscmp(data
.cFileName
, _T("..")) == 0)
568 CGitFileName filename
;
570 filename
.m_CaseFileName
= filename
.m_FileName
= data
.cFileName
;
571 filename
.m_FileName
.MakeLower();
573 if(data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
575 filename
.m_FileName
+= _T('/');
578 list
.push_back(filename
);
580 }while(::FindNextFile(handle
, &data
));
584 std::sort(list
.begin(), list
.end(), SortFileName
);
588 int GitStatus::EnumDirStatus(const CString
&gitdir
,const CString
&subpath
,git_wc_status_kind
* status
,BOOL IsFul
, BOOL IsRecursive
, BOOL IsIgnore
, FIll_STATUS_CALLBACK callback
, void *pData
)
592 TCHAR oldpath
[MAX_PATH
+1];
593 memset(oldpath
,0,MAX_PATH
+1);
595 CString path
=subpath
;
597 path
.Replace(_T('\\'),_T('/'));
599 if(path
[path
.GetLength()-1] != _T('/'))
600 path
+= _T('/'); //Add trail / to show it is directory, not file name.
602 CString lowcasepath
= path
;
603 lowcasepath
.MakeLower();
605 std::vector
<CGitFileName
> filelist
;
606 GetFileList(gitdir
, subpath
, filelist
);
610 g_IndexFileMap
.CheckAndUpdate(gitdir
,true);
612 if(g_HeadFileMap
.CheckHeadUpdate(gitdir
) || g_HeadFileMap
.IsHashChanged(gitdir
))
614 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
615 treeptr
->ReadHeadHash(gitdir
);
616 if(!treeptr
->ReadTree())
618 g_HeadFileMap
.SafeSet(gitdir
, treeptr
);
622 SHARED_INDEX_PTR indexptr
= g_IndexFileMap
.SafeGet(gitdir
);
623 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
625 std::vector
<CGitFileName
>::iterator it
;
627 // new git working tree has no index file
628 if (indexptr
.get() == NULL
)
630 for(it
= filelist
.begin(); it
< filelist
.end(); it
++)
632 CString casepath
= path
+ it
->m_CaseFileName
;
635 if (casepath
.GetLength() > 0 && casepath
[casepath
.GetLength() - 1] == _T('/'))
638 if (bIsDir
) /*check if it is directory*/
640 if (::PathFileExists(gitdir
+ casepath
+ _T("\\.git")))
641 { /* That is git submodule */
642 *status
= git_wc_status_unknown
;
644 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
651 if (g_IgnoreList
.CheckIgnoreChanged(gitdir
, casepath
))
652 g_IgnoreList
.LoadAllIgnoreFile(gitdir
, casepath
);
654 if (g_IgnoreList
.IsIgnore(casepath
, gitdir
))
655 *status
= git_wc_status_ignored
;
659 *status
= git_wc_status_unversioned
;
664 *status
= git_wc_status_unversioned
;
667 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
674 for(it
= filelist
.begin(); it
<filelist
.end();it
++)
676 casepath
=onepath
= path
;
678 onepath
+= it
->m_FileName
;
679 casepath
+= it
->m_CaseFileName
;
681 LPTSTR onepathBuffer
= onepath
.GetBuffer();
682 int pos
= SearchInSortVector(*indexptr
, onepathBuffer
, onepath
.GetLength());
683 int posintree
= SearchInSortVector(*treeptr
, onepathBuffer
, onepath
.GetLength());
684 onepath
.ReleaseBuffer();
687 if(onepath
.GetLength()>0 && onepath
[onepath
.GetLength()-1] == _T('/'))
690 if(pos
<0 && posintree
<0)
692 if(onepath
.GetLength() ==0)
695 if(bIsDir
) /*check if it is directory*/
697 if(::PathFileExists(gitdir
+onepath
+_T("/.git")))
698 { /* That is git submodule */
699 *status
= git_wc_status_unknown
;
701 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
708 *status
= git_wc_status_unversioned
;
710 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
714 if(::g_IgnoreList
.CheckIgnoreChanged(gitdir
,casepath
))
715 g_IgnoreList
.LoadAllIgnoreFile(gitdir
,casepath
);
717 if(g_IgnoreList
.IsIgnore(casepath
,gitdir
))
718 *status
= git_wc_status_ignored
;
720 *status
= git_wc_status_unversioned
;
723 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
726 else if(pos
<0 && posintree
>=0) /* check if file delete in index */
728 *status
= git_wc_status_deleted
;
730 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
733 else if(pos
>=0 && posintree
<0) /* Check if file added */
735 *status
= git_wc_status_added
;
737 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
741 if(onepath
.GetLength() ==0)
744 if(onepath
[onepath
.GetLength()-1] == _T('/'))
746 *status
= git_wc_status_normal
;
748 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
752 bool assumeValid
= false;
753 bool skipWorktree
= false;
754 git_wc_status_kind filestatus
;
755 GetFileStatus(gitdir
, casepath
, &filestatus
, IsFul
, IsRecursive
, IsIgnore
, callback
, pData
, &assumeValid
, &skipWorktree
);
761 /* Check deleted file in system */
762 LPTSTR lowcasepathBuffer
= lowcasepath
.GetBuffer();
764 int pos
=SearchInSortVector(*indexptr
, lowcasepathBuffer
, lowcasepath
.GetLength());
766 if (GetRangeInSortVector(*indexptr
, lowcasepathBuffer
, lowcasepath
.GetLength(), &start
, &end
, pos
))
768 CGitIndexList::iterator it
;
771 for(it
= indexptr
->begin()+start
; it
<= indexptr
->begin()+end
; it
++)
773 int start
= lowcasepath
.GetLength();
774 int index
= (*it
).m_FileName
.Find(_T('/'), start
);
776 index
= (*it
).m_FileName
.GetLength();
778 CString filename
= (*it
).m_FileName
.Mid(start
, index
-start
);
779 if(oldstring
!= filename
)
781 oldstring
= filename
;
782 if(SearchInSortVector(filelist
, filename
.GetBuffer(), filename
.GetLength())<0)
784 *status
= git_wc_status_deleted
;
786 callback(gitdir
+ _T("/") + filename
, *status
, false, pData
, false, false);
793 pos
=SearchInSortVector(*treeptr
, lowcasepathBuffer
, lowcasepath
.GetLength());
794 if (GetRangeInSortVector(*treeptr
, lowcasepathBuffer
, lowcasepath
.GetLength(), &start
, &end
, pos
) == 0)
796 CGitHeadFileList::iterator it
;
799 for(it
= treeptr
->begin()+start
; it
<= treeptr
->begin()+end
; it
++)
801 int start
= lowcasepath
.GetLength();
802 int index
= (*it
).m_FileName
.Find(_T('/'), start
);
804 index
= (*it
).m_FileName
.GetLength();
806 CString filename
= (*it
).m_FileName
.Mid(start
, index
-start
);
807 if(oldstring
!= filename
)
809 oldstring
= filename
;
810 if(SearchInSortVector(filelist
, filename
.GetBuffer(), filename
.GetLength())<0)
812 *status
= git_wc_status_deleted
;
814 callback(gitdir
+ _T("/") + (*it
).m_FileName
, *status
, false, pData
, false, false);
819 lowcasepath
.ReleaseBuffer();
821 }/*End of if status*/
829 int GitStatus::GetDirStatus(const CString
&gitdir
,const CString
&subpath
,git_wc_status_kind
* status
,BOOL IsFul
, BOOL IsRecursive
,BOOL IsIgnore
,FIll_STATUS_CALLBACK callback
,void *pData
)
833 TCHAR oldpath
[MAX_PATH
+1];
834 memset(oldpath
,0,MAX_PATH
+1);
836 CString path
=subpath
;
838 path
.Replace(_T('\\'),_T('/'));
840 if(path
[path
.GetLength()-1] != _T('/'))
841 path
+= _T('/'); //Add trail / to show it is directory, not file name.
843 CString lowcasepath
= path
;
844 lowcasepath
.MakeLower();
848 g_IndexFileMap
.CheckAndUpdate(gitdir
, true);
850 SHARED_INDEX_PTR indexptr
= g_IndexFileMap
.SafeGet(gitdir
);
852 if (indexptr
== NULL
)
854 *status
= git_wc_status_unversioned
;
858 int pos
=SearchInSortVector(*indexptr
,lowcasepath
.GetBuffer(),lowcasepath
.GetLength());
859 lowcasepath
.ReleaseBuffer();
861 //Not In Version Contorl
866 *status
= git_wc_status_unversioned
;
868 callback(gitdir
+ _T("/") + path
, *status
, false, pData
, false, false);
871 //Check ignore always.
873 if(::g_IgnoreList
.CheckIgnoreChanged(gitdir
,path
))
874 g_IgnoreList
.LoadAllIgnoreFile(gitdir
,path
);
876 if(g_IgnoreList
.IsIgnore(path
,gitdir
))
877 *status
= git_wc_status_ignored
;
879 *status
= git_wc_status_unversioned
;
881 g_HeadFileMap
.CheckHeadUpdate(gitdir
);
883 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
884 //Check init repository
885 if(treeptr
->m_Head
.IsEmpty() && path
.IsEmpty())
886 *status
= git_wc_status_normal
;
890 else // In version control
892 *status
= git_wc_status_normal
;
899 end
= (int)indexptr
->size() - 1;
901 LPTSTR lowcasepathBuffer
= lowcasepath
.GetBuffer();
902 GetRangeInSortVector(*indexptr
, lowcasepathBuffer
, lowcasepath
.GetLength(), &start
, &end
, pos
);
903 lowcasepath
.ReleaseBuffer();
904 CGitIndexList::iterator it
;
906 it
= indexptr
->begin()+start
;
909 for(int i
=start
;i
<=end
;i
++)
911 if (((*it
).m_Flags
& GIT_IDXENTRY_STAGEMASK
) !=0)
913 *status
= git_wc_status_conflicted
;
916 int dirpos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
917 if(dirpos
<0 || IsRecursive
)
918 callback(gitdir
+ _T("\\") + it
->m_FileName
, git_wc_status_conflicted
, false, pData
, false, false);
926 if( IsFul
&& (*status
!= git_wc_status_conflicted
))
928 *status
= git_wc_status_normal
;
930 if(g_HeadFileMap
.CheckHeadUpdate(gitdir
))
932 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
934 treeptr
->ReadHeadHash(gitdir
);
936 if(treeptr
->ReadTree())
938 g_HeadFileMap
.SafeSet(gitdir
, treeptr
);
942 it
= indexptr
->begin()+start
;
946 //Check if new init repository
947 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
949 if( treeptr
->size() > 0 || treeptr
->m_Head
.IsEmpty() )
951 for(int i
=start
;i
<=end
;i
++)
953 pos
=SearchInSortVector(*treeptr
, (*it
).m_FileName
.GetBuffer(), -1);
954 (*it
).m_FileName
.ReleaseBuffer();
958 *status
= max(git_wc_status_added
, *status
); // added file found
961 int dirpos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
962 if(dirpos
<0 || IsRecursive
)
963 callback(gitdir
+ _T("\\") + it
->m_FileName
, git_wc_status_added
, false, pData
, false, false);
970 if( pos
>=0 && treeptr
->at(pos
).m_Hash
!= (*it
).m_IndexHash
)
972 *status
= max(git_wc_status_modified
, *status
); // modified file found
975 int dirpos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
976 if(dirpos
<0 || IsRecursive
)
977 callback(gitdir
+ _T("\\") + it
->m_FileName
, git_wc_status_modified
, false, pData
, ((*it
).m_Flags
& GIT_IDXENTRY_VALID
) && !((*it
).m_Flags
& GIT_IDXENTRY_SKIP_WORKTREE
), ((*it
).m_Flags
& GIT_IDXENTRY_SKIP_WORKTREE
) != 0);
988 if( *status
== git_wc_status_normal
)
990 pos
= SearchInSortVector(*treeptr
, lowcasepathBuffer
, lowcasepath
.GetLength());
993 *status
= max(git_wc_status_added
, *status
); // added file found
999 GetRangeInSortVector(*treeptr
, lowcasepathBuffer
, lowcasepath
.GetLength(), &hstart
, &hend
, pos
);
1000 CGitHeadFileList::iterator hit
;
1001 hit
= treeptr
->begin() + hstart
;
1002 CGitHeadFileList::iterator lastElement
= treeptr
->end();
1003 for(int i
=hstart
; i
<= hend
&& hit
!= lastElement
; i
++)
1005 if( SearchInSortVector(*indexptr
,(*hit
).m_FileName
.GetBuffer(),-1) < 0)
1007 (*hit
).m_FileName
.ReleaseBuffer();
1008 *status
= max(git_wc_status_deleted
, *status
); // deleted file found
1011 (*hit
).m_FileName
.ReleaseBuffer();
1019 lowcasepath
.ReleaseBuffer();
1020 // If define callback, it need update each file status.
1021 // If not define callback, status == git_wc_status_conflicted, needn't check each file status
1022 // because git_wc_status_conflicted is highest.s
1023 if(callback
|| (*status
!= git_wc_status_conflicted
))
1028 CString sub
, currentPath
;
1029 it
= indexptr
->begin()+start
;
1030 for(int i
=start
;i
<=end
;i
++,it
++)
1034 //skip child directory
1035 int pos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
1039 currentPath
= (*it
).m_FileName
.Left(pos
);
1040 if( callback
&& (sub
!= currentPath
) )
1043 ATLTRACE(_T("index subdir %s\n"),sub
);
1044 if(callback
) callback(gitdir
+ _T("\\") + sub
,
1045 git_wc_status_normal
, true, pData
, false, false);
1051 git_wc_status_kind filestatus
= git_wc_status_none
;
1052 bool assumeValid
= false;
1053 bool skipWorktree
= false;
1054 GetFileStatus(gitdir
, (*it
).m_FileName
, &filestatus
, IsFul
, IsRecursive
, IsIgnore
, callback
, pData
, &assumeValid
, &skipWorktree
);
1060 if(callback
) callback(gitdir
+ _T("/") + subpath
, *status
, true, pData
, false, false);
1066 *status
= git_wc_status_none
;
1073 bool GitStatus::IsExistIndexLockFile(const CString
&gitdir
)
1075 CString sDirName
= gitdir
;
1079 if(PathFileExists(sDirName
+ _T("\\.git")))
1081 if(PathFileExists(g_AdminDirMap
.GetAdminDir(sDirName
) + _T("index.lock")))
1087 int x
= sDirName
.ReverseFind(_T('\\'));
1091 sDirName
= sDirName
.Left(x
);