1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2015 - 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 "..\TortoiseShell\resource.h"
23 #include "GitStatus.h"
24 #include "UnicodeUtils.h"
27 #include "ShellCache.h"
30 extern CGitAdminDirMap g_AdminDirMap
;
31 extern CGitIndexFileMap g_IndexFileMap
;
32 CGitHeadFileMap g_HeadFileMap
;
33 CGitIgnoreList g_IgnoreList
;
35 GitStatus::GitStatus()
37 , m_allstatus(git_wc_status_none
)
39 m_status
.assumeValid
= m_status
.skipWorktree
= false;
40 m_status
.prop_status
= m_status
.text_status
= git_wc_status_none
;
43 GitStatus::~GitStatus(void)
48 git_wc_status_kind
GitStatus::GetAllStatus(const CTGitPath
& path
, git_depth_t depth
, bool * assumeValid
, bool * skipWorktree
)
50 git_wc_status_kind statuskind
;
55 isDir
= path
.IsDirectory();
56 if (!path
.HasAdminDir(&sProjectRoot
))
57 return git_wc_status_none
;
59 // rev.kind = git_opt_revision_unspecified;
60 statuskind
= git_wc_status_none
;
62 const BOOL bIsRecursive
= (depth
== git_depth_infinity
|| depth
== git_depth_unknown
); // taken from SVN source
65 CString s
= path
.GetWinPathString();
66 if (s
.GetLength() > sProjectRoot
.GetLength())
68 if (sProjectRoot
.GetLength() == 3 && sProjectRoot
[1] == _T(':'))
69 sSubPath
= s
.Right(s
.GetLength() - sProjectRoot
.GetLength());
71 sSubPath
= s
.Right(s
.GetLength() - sProjectRoot
.GetLength() - 1/*otherwise it gets initial slash*/);
74 bool isfull
= ((DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
75 GetSystemMetrics(SM_REMOTESESSION
) ? ShellCache::dll
: ShellCache::exe
) == ShellCache::dllFull
);
79 err
= GetDirStatus(sProjectRoot
,sSubPath
,&statuskind
, isfull
,bIsRecursive
,isfull
,NULL
, NULL
);
80 // folders must not be displayed as added or deleted only as modified (this is for Shell Overlay-Modes)
81 if (statuskind
== git_wc_status_unversioned
&& sSubPath
.IsEmpty())
82 statuskind
= git_wc_status_normal
;
83 else if (statuskind
== git_wc_status_deleted
|| statuskind
== git_wc_status_added
)
84 statuskind
= git_wc_status_modified
;
88 err
= GetFileStatus(sProjectRoot
, sSubPath
, &statuskind
, isfull
, false, isfull
, NULL
, NULL
, assumeValid
, skipWorktree
);
95 git_wc_status_kind
GitStatus::GetAllStatusRecursive(const CTGitPath
& path
)
97 return GetAllStatus(path
, git_depth_infinity
);
101 git_wc_status_kind
GitStatus::GetMoreImportant(git_wc_status_kind status1
, git_wc_status_kind status2
)
103 if (GetStatusRanking(status1
) >= GetStatusRanking(status2
))
107 // static private method
108 int GitStatus::GetStatusRanking(git_wc_status_kind status
)
112 case git_wc_status_none
:
114 case git_wc_status_unversioned
:
116 case git_wc_status_ignored
:
118 case git_wc_status_incomplete
:
120 case git_wc_status_normal
:
121 case git_wc_status_external
:
123 case git_wc_status_added
:
125 case git_wc_status_missing
:
127 case git_wc_status_deleted
:
129 case git_wc_status_replaced
:
131 case git_wc_status_modified
:
133 case git_wc_status_merged
:
135 case git_wc_status_conflicted
:
137 case git_wc_status_obstructed
:
143 void GitStatus::GetStatus(const CTGitPath
& path
, bool /*update*/ /* = false */, bool noignore
/* = false */, bool /*noexternals*/ /* = false */)
145 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
146 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
147 // after the call again
149 CString sProjectRoot
;
150 if ( !path
.HasAdminDir(&sProjectRoot
) )
153 bool isfull
= ((DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
154 GetSystemMetrics(SM_REMOTESESSION
) ? ShellCache::dll
: ShellCache::exe
) == ShellCache::dllFull
);
159 LPCTSTR lpszSubPath
= NULL
;
161 CString s
= path
.GetWinPathString();
162 if (s
.GetLength() > sProjectRoot
.GetLength())
164 sSubPath
= s
.Right(s
.GetLength() - sProjectRoot
.GetLength());
165 lpszSubPath
= sSubPath
;
166 // skip initial slash if necessary
167 if (*lpszSubPath
== _T('\\'))
171 m_status
.prop_status
= m_status
.text_status
= git_wc_status_none
;
172 m_status
.assumeValid
= false;
173 m_status
.skipWorktree
= false;
175 if(path
.IsDirectory())
177 err
= GetDirStatus(sProjectRoot
,lpszSubPath
,&m_status
.text_status
, isfull
, false,!noignore
, NULL
, NULL
);
178 if (m_status
.text_status
== git_wc_status_added
|| m_status
.text_status
== git_wc_status_deleted
) // fix for issue #1769; a folder is either modified, conflicted or normal
179 m_status
.text_status
= git_wc_status_modified
;
183 err
= GetFileStatus(sProjectRoot
, lpszSubPath
, &m_status
.text_status
,isfull
, false,!noignore
, NULL
,NULL
, &m_status
.assumeValid
, &m_status
.skipWorktree
);
187 // Error present if function is not under version control
197 typedef CComCritSecLock
<CComCriticalSection
> CAutoLocker
;
199 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
)
203 CString path
= pathParam
;
205 path
.Replace(_T('\\'),_T('/'));
207 CString lowcasepath
=path
;
208 lowcasepath
.MakeLower();
212 git_wc_status_kind st
= git_wc_status_none
;
215 g_IndexFileMap
.GetFileStatus(gitdir
, path
, &st
, IsFull
, false, callback
, pData
, &hash
, true, assumeValid
, skipWorktree
);
217 if( st
== git_wc_status_conflicted
)
220 if (callback
&& assumeValid
&& skipWorktree
)
221 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
225 if( st
== git_wc_status_unversioned
)
229 *status
= git_wc_status_unversioned
;
230 if (callback
&& assumeValid
&& skipWorktree
)
231 callback(gitdir
+ _T("/") + path
, *status
, false, pData
, *assumeValid
, *skipWorktree
);
235 if (g_IgnoreList
.CheckIgnoreChanged(gitdir
, path
, false))
237 g_IgnoreList
.LoadAllIgnoreFile(gitdir
, path
, false);
239 if (g_IgnoreList
.IsIgnore(path
, gitdir
, false))
241 st
= git_wc_status_ignored
;
244 if (callback
&& assumeValid
&& skipWorktree
)
245 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
250 if ((st
== git_wc_status_normal
|| st
== git_wc_status_modified
) && IsFull
)
253 g_HeadFileMap
.CheckHeadAndUpdate(gitdir
);
255 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
257 // Check Head Tree Hash;
261 int start
= SearchInSortVector(*treeptr
, lowcasepath
, -1);
265 *status
=st
=git_wc_status_added
;
266 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": File miss in head tree %s"), (LPCTSTR
)path
);
267 if (callback
&& assumeValid
&& skipWorktree
)
268 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
272 //staged and not commit
273 if( treeptr
->at(start
).m_Hash
!= hash
)
275 *status
=st
=git_wc_status_modified
;
276 if (callback
&& assumeValid
&& skipWorktree
)
277 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
284 if (callback
&& assumeValid
&& skipWorktree
)
285 callback(gitdir
+ _T("/") + path
, st
, false, pData
, *assumeValid
, *skipWorktree
);
292 *status
= git_wc_status_none
;
300 bool GitStatus::HasIgnoreFilesChanged(const CString
&gitdir
, const CString
&subpaths
, bool isDir
)
302 return g_IgnoreList
.CheckIgnoreChanged(gitdir
, subpaths
, isDir
);
305 int GitStatus::LoadIgnoreFile(const CString
&gitdir
, const CString
&subpaths
, bool isDir
)
307 return g_IgnoreList
.LoadAllIgnoreFile(gitdir
, subpaths
, isDir
);
309 int GitStatus::IsUnderVersionControl(const CString
&gitdir
, const CString
&path
, bool isDir
,bool *isVersion
)
311 if (g_IndexFileMap
.IsUnderVersionControl(gitdir
, path
, isDir
, isVersion
))
314 return g_HeadFileMap
.IsUnderVersionControl(gitdir
, path
, isDir
, isVersion
);
318 __int64
GitStatus::GetIndexFileTime(const CString
&gitdir
)
320 SHARED_INDEX_PTR ptr
=g_IndexFileMap
.SafeGet(gitdir
);
321 if(ptr
.get() == NULL
)
324 return ptr
->m_LastModifyTime
;
327 int GitStatus::IsIgnore(const CString
&gitdir
, const CString
&path
, bool *isIgnore
, bool isDir
)
329 if (g_IgnoreList
.CheckIgnoreChanged(gitdir
, path
, isDir
))
330 g_IgnoreList
.LoadAllIgnoreFile(gitdir
, path
, isDir
);
332 *isIgnore
= g_IgnoreList
.IsIgnore(path
, gitdir
, isDir
);
337 static bool SortFileName(CGitFileName
&Item1
, CGitFileName
&Item2
)
339 return Item1
.m_FileName
.Compare(Item2
.m_FileName
)<0;
342 int GitStatus::GetFileList(const CString
&gitdir
, const CString
&subpath
, std::vector
<CGitFileName
> &list
)
344 WIN32_FIND_DATA data
;
345 HANDLE handle
= ::FindFirstFileEx(gitdir
+ _T("\\") + subpath
+ _T("\\*.*"), SysInfo::Instance().IsWin7OrLater() ? FindExInfoBasic
: FindExInfoStandard
, &data
, FindExSearchNameMatch
, nullptr, SysInfo::Instance().IsWin7OrLater() ? FIND_FIRST_EX_LARGE_FETCH
: 0);
348 if(_tcscmp(data
.cFileName
, _T(".git")) == 0)
351 if(_tcscmp(data
.cFileName
, _T(".")) == 0)
354 if(_tcscmp(data
.cFileName
, _T("..")) == 0)
357 CGitFileName filename
;
359 filename
.m_CaseFileName
= filename
.m_FileName
= data
.cFileName
;
360 filename
.m_FileName
.MakeLower();
362 if(data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
364 filename
.m_FileName
+= _T('/');
367 list
.push_back(filename
);
369 }while(::FindNextFile(handle
, &data
));
373 std::sort(list
.begin(), list
.end(), SortFileName
);
377 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
)
381 CString path
=subpath
;
383 path
.Replace(_T('\\'),_T('/'));
385 if(path
[path
.GetLength()-1] != _T('/'))
386 path
+= _T('/'); //Add trail / to show it is directory, not file name.
388 CString lowcasepath
= path
;
389 lowcasepath
.MakeLower();
391 std::vector
<CGitFileName
> filelist
;
392 GetFileList(gitdir
, subpath
, filelist
);
396 g_IndexFileMap
.CheckAndUpdate(gitdir
,true);
398 g_HeadFileMap
.CheckHeadAndUpdate(gitdir
);
400 SHARED_INDEX_PTR indexptr
= g_IndexFileMap
.SafeGet(gitdir
);
401 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
403 std::vector
<CGitFileName
>::iterator it
;
405 // new git working tree has no index file
406 if (indexptr
.get() == NULL
)
408 for (it
= filelist
.begin(); it
< filelist
.end(); ++it
)
410 CString casepath
= path
+ it
->m_CaseFileName
;
413 if (it
->m_FileName
.GetLength() > 0 && it
->m_FileName
[it
->m_FileName
.GetLength() - 1] == _T('/'))
418 if (g_IgnoreList
.CheckIgnoreChanged(gitdir
, casepath
, bIsDir
))
419 g_IgnoreList
.LoadAllIgnoreFile(gitdir
, casepath
, bIsDir
);
421 if (g_IgnoreList
.IsIgnore(casepath
, gitdir
, bIsDir
))
422 *status
= git_wc_status_ignored
;
426 *status
= git_wc_status_unversioned
;
431 *status
= git_wc_status_unversioned
;
434 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
441 for (it
= filelist
.begin(); it
< filelist
.end(); ++it
)
443 casepath
=onepath
= path
;
445 onepath
+= it
->m_FileName
;
446 casepath
+= it
->m_CaseFileName
;
449 if (!onepath
.IsEmpty() && onepath
[onepath
.GetLength() - 1] == _T('/'))
452 int matchLength
= -1;
454 matchLength
= onepath
.GetLength();
455 int pos
= SearchInSortVector(*indexptr
, onepath
, matchLength
);
456 int posintree
= SearchInSortVector(*treeptr
, onepath
, matchLength
);
458 if(pos
<0 && posintree
<0)
460 if (onepath
.IsEmpty())
465 *status
= git_wc_status_unversioned
;
467 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
471 if (g_IgnoreList
.CheckIgnoreChanged(gitdir
, casepath
, bIsDir
))
472 g_IgnoreList
.LoadAllIgnoreFile(gitdir
, casepath
, bIsDir
);
474 if (g_IgnoreList
.IsIgnore(casepath
, gitdir
, bIsDir
))
475 *status
= git_wc_status_ignored
;
477 *status
= git_wc_status_unversioned
;
480 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
483 else if(pos
<0 && posintree
>=0) /* check if file delete in index */
485 *status
= git_wc_status_deleted
;
487 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
490 else if(pos
>=0 && posintree
<0) /* Check if file added */
492 *status
= git_wc_status_added
;
493 if (indexptr
->at(pos
).m_Flags
& GIT_IDXENTRY_STAGEMASK
)
494 *status
= git_wc_status_conflicted
;
496 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
500 if (onepath
.IsEmpty())
505 *status
= git_wc_status_normal
;
507 callback(gitdir
+ _T("/") + casepath
, *status
, bIsDir
, pData
, false, false);
511 bool assumeValid
= false;
512 bool skipWorktree
= false;
513 git_wc_status_kind filestatus
;
514 GetFileStatus(gitdir
, casepath
, &filestatus
, IsFul
, IsRecursive
, IsIgnore
, callback
, pData
, &assumeValid
, &skipWorktree
);
520 /* Check deleted file in system */
522 int pos
= SearchInSortVector(*indexptr
, lowcasepath
, lowcasepath
.GetLength()); // match path prefix, (sub)folders end with slash
523 std::map
<CString
, bool> skipWorktreeMap
;
525 if (GetRangeInSortVector(*indexptr
, lowcasepath
, lowcasepath
.GetLength(), &start
, &end
, pos
) == 0)
527 CGitIndexList::iterator it2
;
530 for (it2
= indexptr
->begin() + start
; it2
<= indexptr
->begin() + end
; ++it2
)
532 int commonPrefixLength
= lowcasepath
.GetLength();
533 int index
= (*it2
).m_FileName
.Find(_T('/'), commonPrefixLength
);
535 index
= (*it2
).m_FileName
.GetLength();
537 ++index
; // include slash at the end for subfolders, so that we do not match files by mistake
539 CString filename
= (*it2
).m_FileName
.Mid(commonPrefixLength
, index
- commonPrefixLength
);
540 if(oldstring
!= filename
)
542 oldstring
= filename
;
543 if (SearchInSortVector(filelist
, filename
, filename
.GetLength()) < 0)
545 bool skipWorktree
= false;
546 *status
= git_wc_status_deleted
;
547 if (((*it2
).m_Flags
& GIT_IDXENTRY_SKIP_WORKTREE
) != 0)
549 skipWorktreeMap
[filename
] = true;
551 *status
= git_wc_status_normal
;
554 callback(gitdir
+ _T("/") + (*it2
).m_FileName
, *status
, false, pData
, false, skipWorktree
);
561 pos
= SearchInSortVector(*treeptr
, lowcasepath
, lowcasepath
.GetLength()); // match path prefix, (sub)folders end with slash
562 if (GetRangeInSortVector(*treeptr
, lowcasepath
, lowcasepath
.GetLength(), &start
, &end
, pos
) == 0)
564 CGitHeadFileList::iterator it2
;
567 for (it2
= treeptr
->begin() + start
; it2
<= treeptr
->begin() + end
; ++it2
)
569 int commonPrefixLength
= lowcasepath
.GetLength();
570 int index
= (*it2
).m_FileName
.Find(_T('/'), commonPrefixLength
);
572 index
= (*it2
).m_FileName
.GetLength();
574 ++index
; // include slash at the end for subfolders, so that we do not match files by mistake
576 CString filename
= (*it2
).m_FileName
.Mid(commonPrefixLength
, index
- commonPrefixLength
);
577 if (oldstring
!= filename
&& skipWorktreeMap
[filename
] != true)
579 oldstring
= filename
;
580 if (SearchInSortVector(filelist
, filename
, filename
.GetLength()) < 0)
582 *status
= git_wc_status_deleted
;
584 callback(gitdir
+ _T("/") + (*it2
).m_FileName
, *status
, false, pData
, false, false);
590 }/*End of if status*/
598 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
)
602 CString path
=subpath
;
604 path
.Replace(_T('\\'),_T('/'));
606 if(path
[path
.GetLength()-1] != _T('/'))
607 path
+= _T('/'); //Add trail / to show it is directory, not file name.
609 CString lowcasepath
= path
;
610 lowcasepath
.MakeLower();
614 g_IndexFileMap
.CheckAndUpdate(gitdir
, true);
616 SHARED_INDEX_PTR indexptr
= g_IndexFileMap
.SafeGet(gitdir
);
618 if (indexptr
== NULL
)
620 *status
= git_wc_status_unversioned
;
624 int pos
= SearchInSortVector(*indexptr
, lowcasepath
, lowcasepath
.GetLength());
626 //Not In Version Contorl
631 *status
= git_wc_status_unversioned
;
633 callback(gitdir
+ _T("/") + path
, *status
, false, pData
, false, false);
636 //Check ignore always.
638 if (g_IgnoreList
.CheckIgnoreChanged(gitdir
, path
, true))
639 g_IgnoreList
.LoadAllIgnoreFile(gitdir
, path
, true);
641 if (g_IgnoreList
.IsIgnore(path
, gitdir
, true))
642 *status
= git_wc_status_ignored
;
644 *status
= git_wc_status_unversioned
;
646 g_HeadFileMap
.CheckHeadAndUpdate(gitdir
, false);
648 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
649 //Check init repository
650 if (treeptr
->HeadIsEmpty() && path
.IsEmpty())
651 *status
= git_wc_status_normal
;
655 else // In version control
657 *status
= git_wc_status_normal
;
664 end
= (int)indexptr
->size() - 1;
666 GetRangeInSortVector(*indexptr
, lowcasepath
, lowcasepath
.GetLength(), &start
, &end
, pos
);
667 CGitIndexList::iterator it
;
669 it
= indexptr
->begin()+start
;
672 for (int i
= start
; i
<= end
; ++i
)
674 if (((*it
).m_Flags
& GIT_IDXENTRY_STAGEMASK
) !=0)
676 *status
= git_wc_status_conflicted
;
679 int dirpos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
680 if(dirpos
<0 || IsRecursive
)
681 callback(gitdir
+ _T("\\") + it
->m_FileName
, git_wc_status_conflicted
, false, pData
, false, false);
689 if( IsFul
&& (*status
!= git_wc_status_conflicted
))
691 *status
= git_wc_status_normal
;
693 g_HeadFileMap
.CheckHeadAndUpdate(gitdir
);
696 it
= indexptr
->begin()+start
;
700 //Check if new init repository
701 SHARED_TREE_PTR treeptr
= g_HeadFileMap
.SafeGet(gitdir
);
703 if (!treeptr
->empty() || treeptr
->HeadIsEmpty())
705 for (int i
= start
; i
<= end
; ++i
)
707 pos
= SearchInSortVector(*treeptr
, (*it
).m_FileName
, -1);
711 *status
= max(git_wc_status_added
, *status
); // added file found
714 int dirpos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
715 if(dirpos
<0 || IsRecursive
)
716 callback(gitdir
+ _T("\\") + it
->m_FileName
, git_wc_status_added
, false, pData
, false, false);
723 if( pos
>=0 && treeptr
->at(pos
).m_Hash
!= (*it
).m_IndexHash
)
725 *status
= max(git_wc_status_modified
, *status
); // modified file found
728 int dirpos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
729 if(dirpos
<0 || IsRecursive
)
730 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);
741 if( *status
== git_wc_status_normal
)
743 pos
= SearchInSortVector(*treeptr
, lowcasepath
, lowcasepath
.GetLength());
746 *status
= max(git_wc_status_added
, *status
); // added file found
752 GetRangeInSortVector(*treeptr
, lowcasepath
, lowcasepath
.GetLength(), &hstart
, &hend
, pos
);
753 CGitHeadFileList::iterator hit
;
754 hit
= treeptr
->begin() + hstart
;
755 CGitHeadFileList::iterator lastElement
= treeptr
->end();
756 for (int i
= hstart
; i
<= hend
&& hit
!= lastElement
; ++i
)
758 if (SearchInSortVector(*indexptr
, (*hit
).m_FileName
, -1) < 0)
760 *status
= max(git_wc_status_deleted
, *status
); // deleted file found
770 // If define callback, it need update each file status.
771 // If not define callback, status == git_wc_status_conflicted, needn't check each file status
772 // because git_wc_status_conflicted is highest.s
773 if(callback
|| (*status
!= git_wc_status_conflicted
))
778 CString sub
, currentPath
;
779 it
= indexptr
->begin()+start
;
780 for (int i
= start
; i
<= end
; ++i
, ++it
)
784 //skip child directory
785 int slashpos
= (*it
).m_FileName
.Find(_T('/'), path
.GetLength());
789 currentPath
= (*it
).m_FileName
.Left(slashpos
);
790 if( callback
&& (sub
!= currentPath
) )
793 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": index subdir %s\n"), (LPCTSTR
)sub
);
794 if(callback
) callback(gitdir
+ _T("\\") + sub
,
795 git_wc_status_normal
, true, pData
, false, false);
801 git_wc_status_kind filestatus
= git_wc_status_none
;
802 bool assumeValid
= false;
803 bool skipWorktree
= false;
804 GetFileStatus(gitdir
, (*it
).m_FileName
, &filestatus
, IsFul
, IsRecursive
, IsIgnore
, callback
, pData
, &assumeValid
, &skipWorktree
);
810 if(callback
) callback(gitdir
+ _T("/") + subpath
, *status
, true, pData
, false, false);
816 *status
= git_wc_status_none
;
823 bool GitStatus::IsExistIndexLockFile(const CString
&gitdir
)
825 CString sDirName
= gitdir
;
827 if (!PathIsDirectory(sDirName
))
829 int x
= sDirName
.ReverseFind(_T('\\'));
833 sDirName
= sDirName
.Left(x
);
838 if(PathFileExists(sDirName
+ _T("\\.git")))
840 if(PathFileExists(g_AdminDirMap
.GetAdminDir(sDirName
) + _T("index.lock")))
846 int x
= sDirName
.ReverseFind(_T('\\'));
850 sDirName
= sDirName
.Left(x
);
854 bool GitStatus::ReleasePath(const CString
&gitdir
)
856 g_IndexFileMap
.SafeClear(gitdir
);
860 bool GitStatus::ReleasePathsRecursively(const CString
&rootpath
)
862 g_IndexFileMap
.SafeClearRecursively(rootpath
);