1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013 - 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"
38 #include "shellcache.h"
40 extern CGitAdminDirMap g_AdminDirMap
;
41 CGitIndexFileMap g_IndexFileMap
;
42 CGitHeadFileMap g_HeadFileMap
;
43 CGitIgnoreList g_IgnoreList
;
45 GitStatus::GitStatus()
50 GitStatus::~GitStatus(void)
55 git_wc_status_kind
GitStatus::GetAllStatus(const CTGitPath
& path
, git_depth_t depth
, bool * assumeValid
, bool * skipWorktree
)
57 git_wc_status_kind statuskind
;
62 isDir
= path
.IsDirectory();
63 if (!path
.HasAdminDir(&sProjectRoot
))
64 return git_wc_status_none
;
66 // rev.kind = git_opt_revision_unspecified;
67 statuskind
= git_wc_status_none
;
69 const BOOL bIsRecursive
= (depth
== git_depth_infinity
|| depth
== git_depth_unknown
); // taken from SVN source
72 CString s
= path
.GetWinPathString();
73 if (s
.GetLength() > sProjectRoot
.GetLength())
75 if (sProjectRoot
.GetLength() == 3 && sProjectRoot
[1] == _T(':'))
76 sSubPath
= s
.Right(s
.GetLength() - sProjectRoot
.GetLength());
78 sSubPath
= s
.Right(s
.GetLength() - sProjectRoot
.GetLength() - 1/*otherwise it gets initial slash*/);
81 bool isfull
= ((DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
82 GetSystemMetrics(SM_REMOTESESSION
) ? ShellCache::dll
: ShellCache::exe
) == ShellCache::dllFull
);
86 err
= GetDirStatus(sProjectRoot
,sSubPath
,&statuskind
, isfull
,bIsRecursive
,isfull
,NULL
, NULL
);
87 // folders must not be displayed as added or deleted only as modified (this is for Shell Overlay-Modes)
88 if (statuskind
== git_wc_status_unversioned
&& sSubPath
.IsEmpty())
89 statuskind
= git_wc_status_normal
;
90 else if (statuskind
== git_wc_status_deleted
|| statuskind
== git_wc_status_added
)
91 statuskind
= git_wc_status_modified
;
95 err
= GetFileStatus(sProjectRoot
, sSubPath
, &statuskind
, isfull
, false, isfull
, NULL
, NULL
, assumeValid
, skipWorktree
);
102 git_wc_status_kind
GitStatus::GetAllStatusRecursive(const CTGitPath
& path
)
104 return GetAllStatus(path
, git_depth_infinity
);
108 git_wc_status_kind
GitStatus::GetMoreImportant(git_wc_status_kind status1
, git_wc_status_kind status2
)
110 if (GetStatusRanking(status1
) >= GetStatusRanking(status2
))
114 // static private method
115 int GitStatus::GetStatusRanking(git_wc_status_kind status
)
119 case git_wc_status_none
:
121 case git_wc_status_unversioned
:
123 case git_wc_status_ignored
:
125 case git_wc_status_incomplete
:
127 case git_wc_status_normal
:
128 case git_wc_status_external
:
130 case git_wc_status_added
:
132 case git_wc_status_missing
:
134 case git_wc_status_deleted
:
136 case git_wc_status_replaced
:
138 case git_wc_status_modified
:
140 case git_wc_status_merged
:
142 case git_wc_status_conflicted
:
144 case git_wc_status_obstructed
:
150 void GitStatus::GetStatus(const CTGitPath
& path
, bool /*update*/ /* = false */, bool noignore
/* = false */, bool /*noexternals*/ /* = false */)
152 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
153 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
154 // after the call again
156 CString sProjectRoot
;
157 if ( !path
.HasAdminDir(&sProjectRoot
) )
160 bool isfull
= ((DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
161 GetSystemMetrics(SM_REMOTESESSION
) ? ShellCache::dll
: ShellCache::exe
) == ShellCache::dllFull
);
164 LPCTSTR lpszSubPath
= NULL
;
166 CString s
= path
.GetWinPathString();
167 if (s
.GetLength() > sProjectRoot
.GetLength())
169 sSubPath
= s
.Right(s
.GetLength() - sProjectRoot
.GetLength());
170 lpszSubPath
= sSubPath
;
171 // skip initial slash if necessary
172 if (*lpszSubPath
== _T('\\'))
176 m_status
.prop_status
= m_status
.text_status
= git_wc_status_none
;
177 m_status
.assumeValid
= false;
178 m_status
.skipWorktree
= false;
180 if(path
.IsDirectory())
182 m_err
= GetDirStatus(sProjectRoot
,CString(lpszSubPath
),&m_status
.text_status
, isfull
, false,!noignore
, NULL
, NULL
);
187 m_err
= GetFileStatus(sProjectRoot
, CString(lpszSubPath
), &m_status
.text_status
,isfull
, false,!noignore
, NULL
,NULL
, &m_status
.assumeValid
, &m_status
.skipWorktree
);
191 // Error present if function is not under version control
201 void GitStatus::GetStatusString(git_wc_status_kind status
, size_t buflen
, TCHAR
* string
)
206 case git_wc_status_none
:
209 case git_wc_status_unversioned
:
210 buf
= _T("unversioned\0");
212 case git_wc_status_normal
:
213 buf
= _T("normal\0");
215 case git_wc_status_added
:
218 case git_wc_status_missing
:
219 buf
= _T("missing\0");
221 case git_wc_status_deleted
:
222 buf
= _T("deleted\0");
224 case git_wc_status_replaced
:
225 buf
= _T("replaced\0");
227 case git_wc_status_modified
:
228 buf
= _T("modified\0");
230 case git_wc_status_merged
:
231 buf
= _T("merged\0");
233 case git_wc_status_conflicted
:
234 buf
= _T("conflicted\0");
236 case git_wc_status_obstructed
:
237 buf
= _T("obstructed\0");
239 case git_wc_status_ignored
:
242 case git_wc_status_external
:
243 buf
= _T("external");
245 case git_wc_status_incomplete
:
246 buf
= _T("incomplete\0");
252 _stprintf_s(string
, buflen
, _T("%s"), buf
);
255 void GitStatus::GetStatusString(HINSTANCE hInst
, git_wc_status_kind status
, TCHAR
* string
, int size
, WORD lang
)
259 case git_wc_status_none
:
260 LoadStringEx(hInst
, IDS_STATUSNONE
, string
, size
, lang
);
262 case git_wc_status_unversioned
:
263 LoadStringEx(hInst
, IDS_STATUSUNVERSIONED
, string
, size
, lang
);
265 case git_wc_status_normal
:
266 LoadStringEx(hInst
, IDS_STATUSNORMAL
, string
, size
, lang
);
268 case git_wc_status_added
:
269 LoadStringEx(hInst
, IDS_STATUSADDED
, string
, size
, lang
);
271 case git_wc_status_missing
:
272 LoadStringEx(hInst
, IDS_STATUSABSENT
, string
, size
, lang
);
274 case git_wc_status_deleted
:
275 LoadStringEx(hInst
, IDS_STATUSDELETED
, string
, size
, lang
);
277 case git_wc_status_replaced
:
278 LoadStringEx(hInst
, IDS_STATUSREPLACED
, string
, size
, lang
);
280 case git_wc_status_modified
:
281 LoadStringEx(hInst
, IDS_STATUSMODIFIED
, string
, size
, lang
);
283 case git_wc_status_merged
:
284 LoadStringEx(hInst
, IDS_STATUSMERGED
, string
, size
, lang
);
286 case git_wc_status_conflicted
:
287 LoadStringEx(hInst
, IDS_STATUSCONFLICTED
, string
, size
, lang
);
289 case git_wc_status_ignored
:
290 LoadStringEx(hInst
, IDS_STATUSIGNORED
, string
, size
, lang
);
292 case git_wc_status_obstructed
:
293 LoadStringEx(hInst
, IDS_STATUSOBSTRUCTED
, string
, size
, lang
);
295 case git_wc_status_external
:
296 LoadStringEx(hInst
, IDS_STATUSEXTERNAL
, string
, size
, lang
);
298 case git_wc_status_incomplete
:
299 LoadStringEx(hInst
, IDS_STATUSINCOMPLETE
, string
, size
, lang
);
302 LoadStringEx(hInst
, IDS_STATUSNONE
, string
, size
, lang
);
307 int GitStatus::LoadStringEx(HINSTANCE hInstance
, UINT uID
, LPTSTR lpBuffer
, int nBufferMax
, WORD wLanguage
)
309 const STRINGRESOURCEIMAGE
* pImage
;
310 const STRINGRESOURCEIMAGE
* pImageEnd
;
316 HRSRC hResource
= FindResourceEx(hInstance
, RT_STRING
, MAKEINTRESOURCE(((uID
>>4)+1)), wLanguage
);
319 // try the default language before giving up!
320 hResource
= FindResource(hInstance
, MAKEINTRESOURCE(((uID
>>4)+1)), RT_STRING
);
324 hGlobal
= LoadResource(hInstance
, hResource
);
327 pImage
= (const STRINGRESOURCEIMAGE
*)::LockResource(hGlobal
);
331 nResourceSize
= ::SizeofResource(hInstance
, hResource
);
332 pImageEnd
= (const STRINGRESOURCEIMAGE
*)(LPBYTE(pImage
)+nResourceSize
);
335 while ((iIndex
> 0) && (pImage
< pImageEnd
))
337 pImage
= (const STRINGRESOURCEIMAGE
*)(LPBYTE(pImage
)+(sizeof(STRINGRESOURCEIMAGE
)+(pImage
->nLength
*sizeof(WCHAR
))));
340 if (pImage
>= pImageEnd
)
342 if (pImage
->nLength
== 0)
345 ret
= pImage
->nLength
;
346 if (pImage
->nLength
> nBufferMax
)
348 wcsncpy_s(lpBuffer
, nBufferMax
, pImage
->achString
, pImage
->nLength
-1);
349 lpBuffer
[nBufferMax
-1] = 0;
353 wcsncpy_s(lpBuffer
, nBufferMax
, pImage
->achString
, pImage
->nLength
);
359 typedef CComCritSecLock
<CComCriticalSection
> CAutoLocker
;
361 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
)
365 CString path
= pathParam
;
367 TCHAR oldpath
[MAX_PATH
+1];
368 memset(oldpath
,0,MAX_PATH
+1);
370 path
.Replace(_T('\\'),_T('/'));
372 CString lowcasepath
=path
;
373 lowcasepath
.MakeLower();
377 git_wc_status_kind st
= git_wc_status_none
;
380 g_IndexFileMap
.GetFileStatus(gitdir
, path
, &st
, IsFull
, false, callback
, pData
, &hash
, true, assumeValid
, skipWorktree
);
382 if( st
== git_wc_status_conflicted
)
386 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
390 if( st
== git_wc_status_unversioned
)
394 *status
= git_wc_status_unversioned
;
396 callback(gitdir
+ _T("/") + path
, *status
, false, pData
, *assumeValid
, *skipWorktree
);
400 if( g_IgnoreList
.CheckIgnoreChanged(gitdir
,path
))
402 g_IgnoreList
.LoadAllIgnoreFile(gitdir
,path
);
404 if( g_IgnoreList
.IsIgnore(path
, gitdir
) )
406 st
= git_wc_status_ignored
;
410 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
415 if( st
== git_wc_status_normal
&& IsFull
)
418 g_HeadFileMap
.CheckHeadAndUpdate(gitdir
);
420 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
422 // Check Head Tree Hash;
426 int start
=SearchInSortVector(*treeptr
,lowcasepath
.GetBuffer(),-1);
427 lowcasepath
.ReleaseBuffer();
431 *status
=st
=git_wc_status_added
;
432 ATLTRACE(_T("File miss in head tree %s"), path
);
434 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
438 //staged and not commit
439 if( treeptr
->at(start
).m_Hash
!= hash
)
441 *status
=st
=git_wc_status_modified
;
443 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
451 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
458 *status
= git_wc_status_none
;
466 bool GitStatus::HasIgnoreFilesChanged(const CString
&gitdir
, const CString
&subpaths
)
468 return g_IgnoreList
.CheckIgnoreChanged(gitdir
, subpaths
);
471 int GitStatus::LoadIgnoreFile(const CString
&gitdir
,const CString
&subpaths
)
473 return g_IgnoreList
.LoadAllIgnoreFile(gitdir
,subpaths
);
475 int GitStatus::IsUnderVersionControl(const CString
&gitdir
, const CString
&path
, bool isDir
,bool *isVersion
)
477 if (g_IndexFileMap
.IsUnderVersionControl(gitdir
, path
, isDir
, isVersion
))
480 return g_HeadFileMap
.IsUnderVersionControl(gitdir
, path
, isDir
, isVersion
);
484 __int64
GitStatus::GetIndexFileTime(const CString
&gitdir
)
486 SHARED_INDEX_PTR ptr
=g_IndexFileMap
.SafeGet(gitdir
);
487 if(ptr
.get() == NULL
)
490 return ptr
->m_LastModifyTime
;
493 int GitStatus::IsIgnore(const CString
&gitdir
, const CString
&path
, bool *isIgnore
)
495 if(::g_IgnoreList
.CheckIgnoreChanged(gitdir
,path
))
496 g_IgnoreList
.LoadAllIgnoreFile(gitdir
,path
);
498 *isIgnore
= g_IgnoreList
.IsIgnore(path
,gitdir
);
503 static bool SortFileName(CGitFileName
&Item1
, CGitFileName
&Item2
)
505 return Item1
.m_FileName
.Compare(Item2
.m_FileName
)<0;
508 int GitStatus::GetFileList(const CString
&gitdir
, const CString
&subpath
, std::vector
<CGitFileName
> &list
)
510 WIN32_FIND_DATA data
;
511 HANDLE handle
=::FindFirstFile(gitdir
+_T("\\")+subpath
+_T("\\*.*"), &data
);
514 if(_tcscmp(data
.cFileName
, _T(".git")) == 0)
517 if(_tcscmp(data
.cFileName
, _T(".")) == 0)
520 if(_tcscmp(data
.cFileName
, _T("..")) == 0)
523 CGitFileName filename
;
525 filename
.m_CaseFileName
= filename
.m_FileName
= data
.cFileName
;
526 filename
.m_FileName
.MakeLower();
528 if(data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
530 filename
.m_FileName
+= _T('/');
533 list
.push_back(filename
);
535 }while(::FindNextFile(handle
, &data
));
539 std::sort(list
.begin(), list
.end(), SortFileName
);
543 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
)
547 TCHAR oldpath
[MAX_PATH
+1];
548 memset(oldpath
,0,MAX_PATH
+1);
550 CString path
=subpath
;
552 path
.Replace(_T('\\'),_T('/'));
554 if(path
[path
.GetLength()-1] != _T('/'))
555 path
+= _T('/'); //Add trail / to show it is directory, not file name.
557 CString lowcasepath
= path
;
558 lowcasepath
.MakeLower();
560 std::vector
<CGitFileName
> filelist
;
561 GetFileList(gitdir
, subpath
, filelist
);
565 g_IndexFileMap
.CheckAndUpdate(gitdir
,true);
567 g_HeadFileMap
.CheckHeadAndUpdate(gitdir
);
569 SHARED_INDEX_PTR indexptr
= g_IndexFileMap
.SafeGet(gitdir
);
570 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
572 std::vector
<CGitFileName
>::iterator it
;
574 // new git working tree has no index file
575 if (indexptr
.get() == NULL
)
577 for (it
= filelist
.begin(); it
< filelist
.end(); ++it
)
579 CString casepath
= path
+ it
->m_CaseFileName
;
582 if (casepath
.GetLength() > 0 && casepath
[casepath
.GetLength() - 1] == _T('/'))
585 if (bIsDir
) /*check if it is directory*/
587 if (::PathFileExists(gitdir
+ casepath
+ _T("\\.git")))
588 { /* That is git submodule */
589 *status
= git_wc_status_unknown
;
591 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
598 if (g_IgnoreList
.CheckIgnoreChanged(gitdir
, casepath
))
599 g_IgnoreList
.LoadAllIgnoreFile(gitdir
, casepath
);
601 if (g_IgnoreList
.IsIgnore(casepath
, gitdir
))
602 *status
= git_wc_status_ignored
;
606 *status
= git_wc_status_unversioned
;
611 *status
= git_wc_status_unversioned
;
614 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
621 for (it
= filelist
.begin(); it
< filelist
.end(); ++it
)
623 casepath
=onepath
= path
;
625 onepath
+= it
->m_FileName
;
626 casepath
+= it
->m_CaseFileName
;
628 LPTSTR onepathBuffer
= onepath
.GetBuffer();
629 int pos
= SearchInSortVector(*indexptr
, onepathBuffer
, onepath
.GetLength());
630 int posintree
= SearchInSortVector(*treeptr
, onepathBuffer
, onepath
.GetLength());
631 onepath
.ReleaseBuffer();
634 if(onepath
.GetLength()>0 && onepath
[onepath
.GetLength()-1] == _T('/'))
637 if(pos
<0 && posintree
<0)
639 if(onepath
.GetLength() ==0)
642 if(bIsDir
) /*check if it is directory*/
644 if(::PathFileExists(gitdir
+onepath
+_T("/.git")))
645 { /* That is git submodule */
646 *status
= git_wc_status_unknown
;
648 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
655 *status
= git_wc_status_unversioned
;
657 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
661 if(::g_IgnoreList
.CheckIgnoreChanged(gitdir
,casepath
))
662 g_IgnoreList
.LoadAllIgnoreFile(gitdir
,casepath
);
664 if(g_IgnoreList
.IsIgnore(casepath
,gitdir
))
665 *status
= git_wc_status_ignored
;
667 *status
= git_wc_status_unversioned
;
670 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
673 else if(pos
<0 && posintree
>=0) /* check if file delete in index */
675 *status
= git_wc_status_deleted
;
677 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
680 else if(pos
>=0 && posintree
<0) /* Check if file added */
682 *status
= git_wc_status_added
;
684 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
688 if(onepath
.GetLength() ==0)
691 if(onepath
[onepath
.GetLength()-1] == _T('/'))
693 *status
= git_wc_status_normal
;
695 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
699 bool assumeValid
= false;
700 bool skipWorktree
= false;
701 git_wc_status_kind filestatus
;
702 GetFileStatus(gitdir
, casepath
, &filestatus
, IsFul
, IsRecursive
, IsIgnore
, callback
, pData
, &assumeValid
, &skipWorktree
);
708 /* Check deleted file in system */
709 LPTSTR lowcasepathBuffer
= lowcasepath
.GetBuffer();
711 int pos
=SearchInSortVector(*indexptr
, lowcasepathBuffer
, lowcasepath
.GetLength());
713 if (GetRangeInSortVector(*indexptr
, lowcasepathBuffer
, lowcasepath
.GetLength(), &start
, &end
, pos
) == 0)
715 CGitIndexList::iterator it
;
718 for (it
= indexptr
->begin() + start
; it
<= indexptr
->begin() + end
; ++it
)
720 int start
= lowcasepath
.GetLength();
721 int index
= (*it
).m_FileName
.Find(_T('/'), start
);
723 index
= (*it
).m_FileName
.GetLength();
725 CString filename
= (*it
).m_FileName
.Mid(start
, index
-start
);
726 if(oldstring
!= filename
)
728 oldstring
= filename
;
729 if(SearchInSortVector(filelist
, filename
.GetBuffer(), filename
.GetLength())<0)
731 *status
= git_wc_status_deleted
;
733 callback(gitdir
+ _T("/") + filename
, *status
, false, pData
, false, false);
740 pos
=SearchInSortVector(*treeptr
, lowcasepathBuffer
, lowcasepath
.GetLength());
741 if (GetRangeInSortVector(*treeptr
, lowcasepathBuffer
, lowcasepath
.GetLength(), &start
, &end
, pos
) == 0)
743 CGitHeadFileList::iterator it
;
746 for (it
= treeptr
->begin() + start
; it
<= treeptr
->begin() + end
; ++it
)
748 int start
= lowcasepath
.GetLength();
749 int index
= (*it
).m_FileName
.Find(_T('/'), start
);
751 index
= (*it
).m_FileName
.GetLength();
753 CString filename
= (*it
).m_FileName
.Mid(start
, index
-start
);
754 if(oldstring
!= filename
)
756 oldstring
= filename
;
757 if(SearchInSortVector(filelist
, filename
.GetBuffer(), filename
.GetLength())<0)
759 *status
= git_wc_status_deleted
;
761 callback(gitdir
+ _T("/") + (*it
).m_FileName
, *status
, false, pData
, false, false);
766 lowcasepath
.ReleaseBuffer();
768 }/*End of if status*/
776 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
)
780 TCHAR oldpath
[MAX_PATH
+1];
781 memset(oldpath
,0,MAX_PATH
+1);
783 CString path
=subpath
;
785 path
.Replace(_T('\\'),_T('/'));
787 if(path
[path
.GetLength()-1] != _T('/'))
788 path
+= _T('/'); //Add trail / to show it is directory, not file name.
790 CString lowcasepath
= path
;
791 lowcasepath
.MakeLower();
795 g_IndexFileMap
.CheckAndUpdate(gitdir
, true);
797 SHARED_INDEX_PTR indexptr
= g_IndexFileMap
.SafeGet(gitdir
);
799 if (indexptr
== NULL
)
801 *status
= git_wc_status_unversioned
;
805 int pos
=SearchInSortVector(*indexptr
,lowcasepath
.GetBuffer(),lowcasepath
.GetLength());
806 lowcasepath
.ReleaseBuffer();
808 //Not In Version Contorl
813 *status
= git_wc_status_unversioned
;
815 callback(gitdir
+ _T("/") + path
, *status
, false, pData
, false, false);
818 //Check ignore always.
820 if(::g_IgnoreList
.CheckIgnoreChanged(gitdir
,path
))
821 g_IgnoreList
.LoadAllIgnoreFile(gitdir
,path
);
823 if(g_IgnoreList
.IsIgnore(path
,gitdir
))
824 *status
= git_wc_status_ignored
;
826 *status
= git_wc_status_unversioned
;
828 g_HeadFileMap
.CheckHeadAndUpdate(gitdir
, false);
830 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
831 //Check init repository
832 if (treeptr
->HeadIsEmpty() && path
.IsEmpty())
833 *status
= git_wc_status_normal
;
837 else // In version control
839 *status
= git_wc_status_normal
;
846 end
= (int)indexptr
->size() - 1;
848 LPTSTR lowcasepathBuffer
= lowcasepath
.GetBuffer();
849 GetRangeInSortVector(*indexptr
, lowcasepathBuffer
, lowcasepath
.GetLength(), &start
, &end
, pos
);
850 lowcasepath
.ReleaseBuffer();
851 CGitIndexList::iterator it
;
853 it
= indexptr
->begin()+start
;
856 for (int i
= start
; i
<= end
; ++i
)
858 if (((*it
).m_Flags
& GIT_IDXENTRY_STAGEMASK
) !=0)
860 *status
= git_wc_status_conflicted
;
863 int dirpos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
864 if(dirpos
<0 || IsRecursive
)
865 callback(gitdir
+ _T("\\") + it
->m_FileName
, git_wc_status_conflicted
, false, pData
, false, false);
873 if( IsFul
&& (*status
!= git_wc_status_conflicted
))
875 *status
= git_wc_status_normal
;
877 g_HeadFileMap
.CheckHeadAndUpdate(gitdir
);
880 it
= indexptr
->begin()+start
;
884 //Check if new init repository
885 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
887 if (!treeptr
->empty() || treeptr
->HeadIsEmpty())
889 for (int i
= start
; i
<= end
; ++i
)
891 pos
=SearchInSortVector(*treeptr
, (*it
).m_FileName
.GetBuffer(), -1);
892 (*it
).m_FileName
.ReleaseBuffer();
896 *status
= max(git_wc_status_added
, *status
); // added file found
899 int dirpos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
900 if(dirpos
<0 || IsRecursive
)
901 callback(gitdir
+ _T("\\") + it
->m_FileName
, git_wc_status_added
, false, pData
, false, false);
908 if( pos
>=0 && treeptr
->at(pos
).m_Hash
!= (*it
).m_IndexHash
)
910 *status
= max(git_wc_status_modified
, *status
); // modified file found
913 int dirpos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
914 if(dirpos
<0 || IsRecursive
)
915 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);
926 if( *status
== git_wc_status_normal
)
928 pos
= SearchInSortVector(*treeptr
, lowcasepathBuffer
, lowcasepath
.GetLength());
931 *status
= max(git_wc_status_added
, *status
); // added file found
937 GetRangeInSortVector(*treeptr
, lowcasepathBuffer
, lowcasepath
.GetLength(), &hstart
, &hend
, pos
);
938 CGitHeadFileList::iterator hit
;
939 hit
= treeptr
->begin() + hstart
;
940 CGitHeadFileList::iterator lastElement
= treeptr
->end();
941 for (int i
= hstart
; i
<= hend
&& hit
!= lastElement
; ++i
)
943 if( SearchInSortVector(*indexptr
,(*hit
).m_FileName
.GetBuffer(),-1) < 0)
945 (*hit
).m_FileName
.ReleaseBuffer();
946 *status
= max(git_wc_status_deleted
, *status
); // deleted file found
949 (*hit
).m_FileName
.ReleaseBuffer();
957 lowcasepath
.ReleaseBuffer();
958 // If define callback, it need update each file status.
959 // If not define callback, status == git_wc_status_conflicted, needn't check each file status
960 // because git_wc_status_conflicted is highest.s
961 if(callback
|| (*status
!= git_wc_status_conflicted
))
966 CString sub
, currentPath
;
967 it
= indexptr
->begin()+start
;
968 for (int i
= start
; i
<= end
; ++i
, ++it
)
972 //skip child directory
973 int pos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
977 currentPath
= (*it
).m_FileName
.Left(pos
);
978 if( callback
&& (sub
!= currentPath
) )
981 ATLTRACE(_T("index subdir %s\n"),sub
);
982 if(callback
) callback(gitdir
+ _T("\\") + sub
,
983 git_wc_status_normal
, true, pData
, false, false);
989 git_wc_status_kind filestatus
= git_wc_status_none
;
990 bool assumeValid
= false;
991 bool skipWorktree
= false;
992 GetFileStatus(gitdir
, (*it
).m_FileName
, &filestatus
, IsFul
, IsRecursive
, IsIgnore
, callback
, pData
, &assumeValid
, &skipWorktree
);
998 if(callback
) callback(gitdir
+ _T("/") + subpath
, *status
, true, pData
, false, false);
1004 *status
= git_wc_status_none
;
1011 bool GitStatus::IsExistIndexLockFile(const CString
&gitdir
)
1013 CString sDirName
= gitdir
;
1017 if(PathFileExists(sDirName
+ _T("\\.git")))
1019 if(PathFileExists(g_AdminDirMap
.GetAdminDir(sDirName
) + _T("index.lock")))
1025 int x
= sDirName
.ReverseFind(_T('\\'));
1029 sDirName
= sDirName
.Left(x
);