Refactored: Replace 'count' parameter of CGit::GetLogCmd() with CFilterData::m_Number...
[TortoiseGit.git] / src / Git / GitStatus.cpp
blob26e98fea76dfc08f22e4537e47eafe4bcfa4987a
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.
20 #include "stdafx.h"
21 #include "registry.h"
22 #include "..\TortoiseShell\resource.h"
23 #include "GitStatus.h"
24 #include "UnicodeUtils.h"
25 #include "Git.h"
26 #include "gitindex.h"
27 #include "ShellCache.h"
28 #include "SysInfo.h"
30 extern CGitAdminDirMap g_AdminDirMap;
31 extern CGitIndexFileMap g_IndexFileMap;
32 CGitHeadFileMap g_HeadFileMap;
33 CGitIgnoreList g_IgnoreList;
35 GitStatus::GitStatus()
36 : status(NULL)
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)
47 // static method
48 git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t depth, bool * assumeValid, bool * skipWorktree)
50 git_wc_status_kind statuskind;
51 BOOL err;
52 BOOL isDir;
53 CString sProjectRoot;
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
64 CString sSubPath;
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());
70 else
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);
77 if(isDir)
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;
86 else
88 err = GetFileStatus(sProjectRoot, sSubPath, &statuskind, isfull, false, isfull, NULL, NULL, assumeValid, skipWorktree);
91 return statuskind;
94 // static method
95 git_wc_status_kind GitStatus::GetAllStatusRecursive(const CTGitPath& path)
97 return GetAllStatus(path, git_depth_infinity);
100 // static method
101 git_wc_status_kind GitStatus::GetMoreImportant(git_wc_status_kind status1, git_wc_status_kind status2)
103 if (GetStatusRanking(status1) >= GetStatusRanking(status2))
104 return status1;
105 return status2;
107 // static private method
108 int GitStatus::GetStatusRanking(git_wc_status_kind status)
110 switch (status)
112 case git_wc_status_none:
113 return 0;
114 case git_wc_status_unversioned:
115 return 1;
116 case git_wc_status_ignored:
117 return 2;
118 case git_wc_status_incomplete:
119 return 4;
120 case git_wc_status_normal:
121 case git_wc_status_external:
122 return 5;
123 case git_wc_status_added:
124 return 6;
125 case git_wc_status_missing:
126 return 7;
127 case git_wc_status_deleted:
128 return 8;
129 case git_wc_status_replaced:
130 return 9;
131 case git_wc_status_modified:
132 return 10;
133 case git_wc_status_merged:
134 return 11;
135 case git_wc_status_conflicted:
136 return 12;
137 case git_wc_status_obstructed:
138 return 13;
140 return 0;
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) )
151 return;
153 bool isfull = ((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
154 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
156 int err = 0;
159 LPCTSTR lpszSubPath = NULL;
160 CString sSubPath;
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('\\'))
168 ++lpszSubPath;
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;
181 else
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
188 if (err)
190 status = NULL;
191 return;
194 status = &m_status;
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();
210 if(status)
212 git_wc_status_kind st = git_wc_status_none;
213 CGitHash hash;
215 g_IndexFileMap.GetFileStatus(gitdir, path, &st, IsFull, false, callback, pData, &hash, true, assumeValid, skipWorktree);
217 if( st == git_wc_status_conflicted )
219 *status =st;
220 if (callback && assumeValid && skipWorktree)
221 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
222 return 0;
225 if( st == git_wc_status_unversioned )
227 if(!IsIgnore)
229 *status = git_wc_status_unversioned;
230 if (callback && assumeValid && skipWorktree)
231 callback(gitdir + _T("/") + path, *status, false, pData, *assumeValid, *skipWorktree);
232 return 0;
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;
243 *status = st;
244 if (callback && assumeValid && skipWorktree)
245 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
247 return 0;
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;
259 //add item
261 int start = SearchInSortVector(*treeptr, lowcasepath, -1);
263 if(start<0)
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);
269 return 0;
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);
278 return 0;
283 *status =st;
284 if (callback && assumeValid && skipWorktree)
285 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
286 return 0;
289 catch(...)
291 if(status)
292 *status = git_wc_status_none;
293 return -1;
296 return 0;
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))
312 return 1;
313 if (!*isVersion)
314 return g_HeadFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion);
315 return 0;
318 __int64 GitStatus::GetIndexFileTime(const CString &gitdir)
320 SHARED_INDEX_PTR ptr=g_IndexFileMap.SafeGet(gitdir);
321 if(ptr.get() == NULL)
322 return 0;
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);
334 return 0;
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)
349 continue;
351 if(_tcscmp(data.cFileName, _T(".")) == 0)
352 continue;
354 if(_tcscmp(data.cFileName, _T("..")) == 0)
355 continue;
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));
371 FindClose(handle);
373 std::sort(list.begin(), list.end(), SortFileName);
374 return 0;
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('/'));
384 if(!path.IsEmpty())
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);
394 if(status)
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;
412 bool bIsDir = false;
413 if (it->m_FileName.GetLength() > 0 && it->m_FileName[it->m_FileName.GetLength() - 1] == _T('/'))
414 bIsDir = true;
416 if (IsIgnore)
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;
423 else if (bIsDir)
424 continue;
425 else
426 *status = git_wc_status_unversioned;
428 else if (bIsDir)
429 continue;
430 else
431 *status = git_wc_status_unversioned;
433 if(callback)
434 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
436 return 0;
439 CString onepath;
440 CString casepath;
441 for (it = filelist.begin(); it < filelist.end(); ++it)
443 casepath=onepath = path;
444 onepath.MakeLower();
445 onepath += it->m_FileName;
446 casepath += it->m_CaseFileName;
448 bool bIsDir = false;
449 if (!onepath.IsEmpty() && onepath[onepath.GetLength() - 1] == _T('/'))
450 bIsDir = true;
452 int matchLength = -1;
453 if (bIsDir)
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())
461 continue;
463 if(!IsIgnore)
465 *status = git_wc_status_unversioned;
466 if(callback)
467 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
468 continue;
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;
476 else
477 *status = git_wc_status_unversioned;
479 if(callback)
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;
486 if(callback)
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;
495 if(callback)
496 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
498 else
500 if (onepath.IsEmpty())
501 continue;
503 if (bIsDir)
505 *status = git_wc_status_normal;
506 if(callback)
507 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
509 else
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);
518 }/*End of For*/
520 /* Check deleted file in system */
521 int start=0, end=0;
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;
528 CString oldstring;
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);
534 if(index<0)
535 index = (*it2).m_FileName.GetLength();
536 else
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;
550 skipWorktree = true;
551 *status = git_wc_status_normal;
553 if(callback)
554 callback(gitdir + _T("/") + (*it2).m_FileName, *status, false, pData, false, skipWorktree);
560 start = end =0;
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;
565 CString oldstring;
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);
571 if(index<0)
572 index = (*it2).m_FileName.GetLength();
573 else
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;
583 if(callback)
584 callback(gitdir + _T("/") + (*it2).m_FileName, *status, false, pData, false, false);
590 }/*End of if status*/
591 }catch(...)
593 return -1;
595 return 0;
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('/'));
605 if(!path.IsEmpty())
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();
612 if(status)
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;
621 return 0;
624 int pos = SearchInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength());
626 //Not In Version Contorl
627 if(pos<0)
629 if(!IsIgnore)
631 *status = git_wc_status_unversioned;
632 if(callback)
633 callback(gitdir + _T("/") + path, *status, false, pData, false, false);
634 return 0;
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;
643 else
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;
659 int start=0;
660 int end=0;
661 if(path.IsEmpty())
663 start=0;
664 end = (int)indexptr->size() - 1;
666 GetRangeInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength(), &start, &end, pos);
667 CGitIndexList::iterator it;
669 it = indexptr->begin()+start;
671 // Check Conflict;
672 for (int i = start; i <= end; ++i)
674 if (((*it).m_Flags & GIT_IDXENTRY_STAGEMASK) !=0)
676 *status = git_wc_status_conflicted;
677 if(callback)
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);
683 else
684 break;
686 ++it;
689 if( IsFul && (*status != git_wc_status_conflicted))
691 *status = git_wc_status_normal;
693 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
695 //Check Add
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);
709 if(pos < 0)
711 *status = max(git_wc_status_added, *status); // added file found
712 if(callback)
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);
719 else
720 break;
723 if( pos>=0 && treeptr->at(pos).m_Hash != (*it).m_IndexHash)
725 *status = max(git_wc_status_modified, *status); // modified file found
726 if(callback)
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);
733 else
734 break;
737 ++it;
740 //Check Delete
741 if( *status == git_wc_status_normal )
743 pos = SearchInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength());
744 if(pos <0)
746 *status = max(git_wc_status_added, *status); // added file found
749 else
751 int hstart,hend;
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
761 break;
763 ++hit;
768 }/* End lock*/
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))
775 //Check File Time;
776 //if(IsRecursive)
778 CString sub, currentPath;
779 it = indexptr->begin()+start;
780 for (int i = start; i <= end; ++i, ++it)
782 if( !IsRecursive )
784 //skip child directory
785 int slashpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
787 if (slashpos > 0)
789 currentPath = (*it).m_FileName.Left(slashpos);
790 if( callback && (sub != currentPath) )
792 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);
797 continue;
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);
813 }catch(...)
815 if(status)
816 *status = git_wc_status_none;
817 return -1;
820 return 0;
823 bool GitStatus::IsExistIndexLockFile(const CString &gitdir)
825 CString sDirName= gitdir;
827 if (!PathIsDirectory(sDirName))
829 int x = sDirName.ReverseFind(_T('\\'));
830 if (x < 2)
831 return false;
833 sDirName = sDirName.Left(x);
836 for (;;)
838 if(PathFileExists(sDirName + _T("\\.git")))
840 if(PathFileExists(g_AdminDirMap.GetAdminDir(sDirName) + _T("index.lock")))
841 return true;
842 else
843 return false;
846 int x = sDirName.ReverseFind(_T('\\'));
847 if (x < 2)
848 return false;
850 sDirName = sDirName.Left(x);
854 bool GitStatus::ReleasePath(const CString &gitdir)
856 g_IndexFileMap.SafeClear(gitdir);
857 return true;
860 bool GitStatus::ReleasePathsRecursively(const CString &rootpath)
862 g_IndexFileMap.SafeClearRecursively(rootpath);
863 return true;