Fixed issue #2495: "Show Reflog" dialog shows empty action for "push" entries
[TortoiseGit.git] / src / Git / GitStatus.cpp
blobaa7de00cb7d29925aa1f520200ff06f0ac94ae8b
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2014 - 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"
29 extern CGitAdminDirMap g_AdminDirMap;
30 extern CGitIndexFileMap g_IndexFileMap;
31 CGitHeadFileMap g_HeadFileMap;
32 CGitIgnoreList g_IgnoreList;
34 GitStatus::GitStatus()
35 : status(NULL)
36 , m_allstatus(git_wc_status_none)
38 m_status.assumeValid = m_status.skipWorktree = false;
39 m_status.prop_status = m_status.text_status = git_wc_status_none;
42 GitStatus::~GitStatus(void)
46 // static method
47 git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t depth, bool * assumeValid, bool * skipWorktree)
49 git_wc_status_kind statuskind;
50 BOOL err;
51 BOOL isDir;
52 CString sProjectRoot;
54 isDir = path.IsDirectory();
55 if (!path.HasAdminDir(&sProjectRoot))
56 return git_wc_status_none;
58 // rev.kind = git_opt_revision_unspecified;
59 statuskind = git_wc_status_none;
61 const BOOL bIsRecursive = (depth == git_depth_infinity || depth == git_depth_unknown); // taken from SVN source
63 CString sSubPath;
64 CString s = path.GetWinPathString();
65 if (s.GetLength() > sProjectRoot.GetLength())
67 if (sProjectRoot.GetLength() == 3 && sProjectRoot[1] == _T(':'))
68 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
69 else
70 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);
73 bool isfull = ((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
74 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
76 if(isDir)
78 err = GetDirStatus(sProjectRoot,sSubPath,&statuskind, isfull,bIsRecursive,isfull,NULL, NULL);
79 // folders must not be displayed as added or deleted only as modified (this is for Shell Overlay-Modes)
80 if (statuskind == git_wc_status_unversioned && sSubPath.IsEmpty())
81 statuskind = git_wc_status_normal;
82 else if (statuskind == git_wc_status_deleted || statuskind == git_wc_status_added)
83 statuskind = git_wc_status_modified;
85 else
87 err = GetFileStatus(sProjectRoot, sSubPath, &statuskind, isfull, false, isfull, NULL, NULL, assumeValid, skipWorktree);
90 return statuskind;
93 // static method
94 git_wc_status_kind GitStatus::GetAllStatusRecursive(const CTGitPath& path)
96 return GetAllStatus(path, git_depth_infinity);
99 // static method
100 git_wc_status_kind GitStatus::GetMoreImportant(git_wc_status_kind status1, git_wc_status_kind status2)
102 if (GetStatusRanking(status1) >= GetStatusRanking(status2))
103 return status1;
104 return status2;
106 // static private method
107 int GitStatus::GetStatusRanking(git_wc_status_kind status)
109 switch (status)
111 case git_wc_status_none:
112 return 0;
113 case git_wc_status_unversioned:
114 return 1;
115 case git_wc_status_ignored:
116 return 2;
117 case git_wc_status_incomplete:
118 return 4;
119 case git_wc_status_normal:
120 case git_wc_status_external:
121 return 5;
122 case git_wc_status_added:
123 return 6;
124 case git_wc_status_missing:
125 return 7;
126 case git_wc_status_deleted:
127 return 8;
128 case git_wc_status_replaced:
129 return 9;
130 case git_wc_status_modified:
131 return 10;
132 case git_wc_status_merged:
133 return 11;
134 case git_wc_status_conflicted:
135 return 12;
136 case git_wc_status_obstructed:
137 return 13;
139 return 0;
142 void GitStatus::GetStatus(const CTGitPath& path, bool /*update*/ /* = false */, bool noignore /* = false */, bool /*noexternals*/ /* = false */)
144 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
145 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
146 // after the call again
148 CString sProjectRoot;
149 if ( !path.HasAdminDir(&sProjectRoot) )
150 return;
152 bool isfull = ((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
153 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
155 int err = 0;
158 LPCTSTR lpszSubPath = NULL;
159 CString sSubPath;
160 CString s = path.GetWinPathString();
161 if (s.GetLength() > sProjectRoot.GetLength())
163 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
164 lpszSubPath = sSubPath;
165 // skip initial slash if necessary
166 if (*lpszSubPath == _T('\\'))
167 ++lpszSubPath;
170 m_status.prop_status = m_status.text_status = git_wc_status_none;
171 m_status.assumeValid = false;
172 m_status.skipWorktree = false;
174 if(path.IsDirectory())
176 err = GetDirStatus(sProjectRoot,lpszSubPath,&m_status.text_status , isfull, false,!noignore, NULL, NULL);
177 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
178 m_status.text_status = git_wc_status_modified;
180 else
182 err = GetFileStatus(sProjectRoot, lpszSubPath, &m_status.text_status ,isfull, false,!noignore, NULL,NULL, &m_status.assumeValid, &m_status.skipWorktree);
186 // Error present if function is not under version control
187 if (err)
189 status = NULL;
190 return;
193 status = &m_status;
196 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
198 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)
202 CString path = pathParam;
204 path.Replace(_T('\\'),_T('/'));
206 CString lowcasepath =path;
207 lowcasepath.MakeLower();
209 if(status)
211 git_wc_status_kind st = git_wc_status_none;
212 CGitHash hash;
214 g_IndexFileMap.GetFileStatus(gitdir, path, &st, IsFull, false, callback, pData, &hash, true, assumeValid, skipWorktree);
216 if( st == git_wc_status_conflicted )
218 *status =st;
219 if (callback && assumeValid && skipWorktree)
220 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
221 return 0;
224 if( st == git_wc_status_unversioned )
226 if(!IsIgnore)
228 *status = git_wc_status_unversioned;
229 if (callback && assumeValid && skipWorktree)
230 callback(gitdir + _T("/") + path, *status, false, pData, *assumeValid, *skipWorktree);
231 return 0;
234 if (g_IgnoreList.CheckIgnoreChanged(gitdir, path, false))
236 g_IgnoreList.LoadAllIgnoreFile(gitdir, path, false);
238 if (g_IgnoreList.IsIgnore(path, gitdir, false))
240 st = git_wc_status_ignored;
242 *status = st;
243 if (callback && assumeValid && skipWorktree)
244 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
246 return 0;
249 if ((st == git_wc_status_normal || st == git_wc_status_modified) && IsFull)
252 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
254 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
256 // Check Head Tree Hash;
258 //add item
260 int start = SearchInSortVector(*treeptr, lowcasepath, -1);
262 if(start<0)
264 *status =st=git_wc_status_added;
265 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": File miss in head tree %s"), path);
266 if (callback && assumeValid && skipWorktree)
267 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
268 return 0;
271 //staged and not commit
272 if( treeptr->at(start).m_Hash != hash )
274 *status =st=git_wc_status_modified;
275 if (callback && assumeValid && skipWorktree)
276 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
277 return 0;
282 *status =st;
283 if (callback && assumeValid && skipWorktree)
284 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
285 return 0;
288 catch(...)
290 if(status)
291 *status = git_wc_status_none;
292 return -1;
295 return 0;
299 bool GitStatus::HasIgnoreFilesChanged(const CString &gitdir, const CString &subpaths, bool isDir)
301 return g_IgnoreList.CheckIgnoreChanged(gitdir, subpaths, isDir);
304 int GitStatus::LoadIgnoreFile(const CString &gitdir, const CString &subpaths, bool isDir)
306 return g_IgnoreList.LoadAllIgnoreFile(gitdir, subpaths, isDir);
308 int GitStatus::IsUnderVersionControl(const CString &gitdir, const CString &path, bool isDir,bool *isVersion)
310 if (g_IndexFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion))
311 return 1;
312 if (!*isVersion)
313 return g_HeadFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion);
314 return 0;
317 __int64 GitStatus::GetIndexFileTime(const CString &gitdir)
319 SHARED_INDEX_PTR ptr=g_IndexFileMap.SafeGet(gitdir);
320 if(ptr.get() == NULL)
321 return 0;
323 return ptr->m_LastModifyTime;
326 int GitStatus::IsIgnore(const CString &gitdir, const CString &path, bool *isIgnore, bool isDir)
328 if (g_IgnoreList.CheckIgnoreChanged(gitdir, path, isDir))
329 g_IgnoreList.LoadAllIgnoreFile(gitdir, path, isDir);
331 *isIgnore = g_IgnoreList.IsIgnore(path, gitdir, isDir);
333 return 0;
336 static bool SortFileName(CGitFileName &Item1, CGitFileName &Item2)
338 return Item1.m_FileName.Compare(Item2.m_FileName)<0;
341 int GitStatus::GetFileList(const CString &gitdir, const CString &subpath, std::vector<CGitFileName> &list)
343 WIN32_FIND_DATA data;
344 HANDLE handle=::FindFirstFile(gitdir+_T("\\")+subpath+_T("\\*.*"), &data);
347 if(_tcscmp(data.cFileName, _T(".git")) == 0)
348 continue;
350 if(_tcscmp(data.cFileName, _T(".")) == 0)
351 continue;
353 if(_tcscmp(data.cFileName, _T("..")) == 0)
354 continue;
356 CGitFileName filename;
358 filename.m_CaseFileName = filename.m_FileName = data.cFileName;
359 filename.m_FileName.MakeLower();
361 if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
363 filename.m_FileName += _T('/');
366 list.push_back(filename);
368 }while(::FindNextFile(handle, &data));
370 FindClose(handle);
372 std::sort(list.begin(), list.end(), SortFileName);
373 return 0;
376 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)
380 CString path =subpath;
382 path.Replace(_T('\\'),_T('/'));
383 if(!path.IsEmpty())
384 if(path[path.GetLength()-1] != _T('/'))
385 path += _T('/'); //Add trail / to show it is directory, not file name.
387 CString lowcasepath = path;
388 lowcasepath.MakeLower();
390 std::vector<CGitFileName> filelist;
391 GetFileList(gitdir, subpath, filelist);
393 if(status)
395 g_IndexFileMap.CheckAndUpdate(gitdir,true);
397 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
399 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
400 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
402 std::vector<CGitFileName>::iterator it;
404 // new git working tree has no index file
405 if (indexptr.get() == NULL)
407 for (it = filelist.begin(); it < filelist.end(); ++it)
409 CString casepath = path + it->m_CaseFileName;
411 bool bIsDir = false;
412 if (it->m_FileName.GetLength() > 0 && it->m_FileName[it->m_FileName.GetLength() - 1] == _T('/'))
413 bIsDir = true;
415 if (IsIgnore)
417 if (g_IgnoreList.CheckIgnoreChanged(gitdir, casepath, bIsDir))
418 g_IgnoreList.LoadAllIgnoreFile(gitdir, casepath, bIsDir);
420 if (g_IgnoreList.IsIgnore(casepath, gitdir, bIsDir))
421 *status = git_wc_status_ignored;
422 else if (bIsDir)
423 continue;
424 else
425 *status = git_wc_status_unversioned;
427 else if (bIsDir)
428 continue;
429 else
430 *status = git_wc_status_unversioned;
432 if(callback)
433 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
435 return 0;
438 CString onepath;
439 CString casepath;
440 for (it = filelist.begin(); it < filelist.end(); ++it)
442 casepath=onepath = path;
443 onepath.MakeLower();
444 onepath += it->m_FileName;
445 casepath += it->m_CaseFileName;
447 bool bIsDir = false;
448 if (!onepath.IsEmpty() && onepath[onepath.GetLength() - 1] == _T('/'))
449 bIsDir = true;
451 int matchLength = -1;
452 if (bIsDir)
453 matchLength = onepath.GetLength();
454 int pos = SearchInSortVector(*indexptr, onepath, matchLength);
455 int posintree = SearchInSortVector(*treeptr, onepath, matchLength);
457 if(pos <0 && posintree<0)
459 if (onepath.IsEmpty())
460 continue;
462 if(!IsIgnore)
464 *status = git_wc_status_unversioned;
465 if(callback)
466 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
467 continue;
470 if (g_IgnoreList.CheckIgnoreChanged(gitdir, casepath, bIsDir))
471 g_IgnoreList.LoadAllIgnoreFile(gitdir, casepath, bIsDir);
473 if (g_IgnoreList.IsIgnore(casepath, gitdir, bIsDir))
474 *status = git_wc_status_ignored;
475 else
476 *status = git_wc_status_unversioned;
478 if(callback)
479 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
482 else if(pos <0 && posintree>=0) /* check if file delete in index */
484 *status = git_wc_status_deleted;
485 if(callback)
486 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
489 else if(pos >=0 && posintree <0) /* Check if file added */
491 *status = git_wc_status_added;
492 if (indexptr->at(pos).m_Flags & GIT_IDXENTRY_STAGEMASK)
493 *status = git_wc_status_conflicted;
494 if(callback)
495 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
497 else
499 if (onepath.IsEmpty())
500 continue;
502 if (bIsDir)
504 *status = git_wc_status_normal;
505 if(callback)
506 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
508 else
510 bool assumeValid = false;
511 bool skipWorktree = false;
512 git_wc_status_kind filestatus;
513 GetFileStatus(gitdir, casepath, &filestatus, IsFul, IsRecursive, IsIgnore, callback, pData, &assumeValid, &skipWorktree);
517 }/*End of For*/
519 /* Check deleted file in system */
520 int start=0, end=0;
521 int pos = SearchInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength()); // match path prefix, (sub)folders end with slash
522 std::map<CString, bool> skipWorktreeMap;
524 if (GetRangeInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength(), &start, &end, pos) == 0)
526 CGitIndexList::iterator it;
527 CString oldstring;
529 for (it = indexptr->begin() + start; it <= indexptr->begin() + end; ++it)
531 int commonPrefixLength = lowcasepath.GetLength();
532 int index = (*it).m_FileName.Find(_T('/'), commonPrefixLength);
533 if(index<0)
534 index = (*it).m_FileName.GetLength();
535 else
536 ++index; // include slash at the end for subfolders, so that we do not match files by mistake
538 CString filename = (*it).m_FileName.Mid(commonPrefixLength, index - commonPrefixLength);
539 if(oldstring != filename)
541 oldstring = filename;
542 if (SearchInSortVector(filelist, filename, filename.GetLength()) < 0)
544 bool skipWorktree = false;
545 *status = git_wc_status_deleted;
546 if (((*it).m_Flags & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
548 skipWorktreeMap[filename] = true;
549 skipWorktree = true;
550 *status = git_wc_status_normal;
552 if(callback)
553 callback(gitdir + _T("/") + (*it).m_FileName, *status, false, pData, false, skipWorktree);
559 start = end =0;
560 pos = SearchInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength()); // match path prefix, (sub)folders end with slash
561 if (GetRangeInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength(), &start, &end, pos) == 0)
563 CGitHeadFileList::iterator it;
564 CString oldstring;
566 for (it = treeptr->begin() + start; it <= treeptr->begin() + end; ++it)
568 int commonPrefixLength = lowcasepath.GetLength();
569 int index = (*it).m_FileName.Find(_T('/'), commonPrefixLength);
570 if(index<0)
571 index = (*it).m_FileName.GetLength();
572 else
573 ++index; // include slash at the end for subfolders, so that we do not match files by mistake
575 CString filename = (*it).m_FileName.Mid(commonPrefixLength, index - commonPrefixLength);
576 if (oldstring != filename && skipWorktreeMap[filename] != true)
578 oldstring = filename;
579 if (SearchInSortVector(filelist, filename, filename.GetLength()) < 0)
581 *status = git_wc_status_deleted;
582 if(callback)
583 callback(gitdir + _T("/") + (*it).m_FileName, *status, false, pData, false, false);
589 }/*End of if status*/
590 }catch(...)
592 return -1;
594 return 0;
597 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)
601 CString path =subpath;
603 path.Replace(_T('\\'),_T('/'));
604 if(!path.IsEmpty())
605 if(path[path.GetLength()-1] != _T('/'))
606 path += _T('/'); //Add trail / to show it is directory, not file name.
608 CString lowcasepath = path;
609 lowcasepath.MakeLower();
611 if(status)
613 g_IndexFileMap.CheckAndUpdate(gitdir, true);
615 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
617 if (indexptr == NULL)
619 *status = git_wc_status_unversioned;
620 return 0;
623 int pos = SearchInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength());
625 //Not In Version Contorl
626 if(pos<0)
628 if(!IsIgnore)
630 *status = git_wc_status_unversioned;
631 if(callback)
632 callback(gitdir + _T("/") + path, *status, false, pData, false, false);
633 return 0;
635 //Check ignore always.
637 if (g_IgnoreList.CheckIgnoreChanged(gitdir, path, true))
638 g_IgnoreList.LoadAllIgnoreFile(gitdir, path, true);
640 if (g_IgnoreList.IsIgnore(path, gitdir, true))
641 *status = git_wc_status_ignored;
642 else
643 *status = git_wc_status_unversioned;
645 g_HeadFileMap.CheckHeadAndUpdate(gitdir, false);
647 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
648 //Check init repository
649 if (treeptr->HeadIsEmpty() && path.IsEmpty())
650 *status = git_wc_status_normal;
654 else // In version control
656 *status = git_wc_status_normal;
658 int start=0;
659 int end=0;
660 if(path.IsEmpty())
662 start=0;
663 end = (int)indexptr->size() - 1;
665 GetRangeInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength(), &start, &end, pos);
666 CGitIndexList::iterator it;
668 it = indexptr->begin()+start;
670 // Check Conflict;
671 for (int i = start; i <= end; ++i)
673 if (((*it).m_Flags & GIT_IDXENTRY_STAGEMASK) !=0)
675 *status = git_wc_status_conflicted;
676 if(callback)
678 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
679 if(dirpos<0 || IsRecursive)
680 callback(gitdir + _T("\\") + it->m_FileName, git_wc_status_conflicted, false, pData, false, false);
682 else
683 break;
685 ++it;
688 if( IsFul && (*status != git_wc_status_conflicted))
690 *status = git_wc_status_normal;
692 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
694 //Check Add
695 it = indexptr->begin()+start;
699 //Check if new init repository
700 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
702 if (!treeptr->empty() || treeptr->HeadIsEmpty())
704 for (int i = start; i<= end; ++i)
706 pos = SearchInSortVector(*treeptr, (*it).m_FileName, -1);
708 if(pos < 0)
710 *status = max(git_wc_status_added, *status); // added file found
711 if(callback)
713 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
714 if(dirpos<0 || IsRecursive)
715 callback(gitdir + _T("\\") + it->m_FileName, git_wc_status_added, false, pData, false, false);
718 else
719 break;
722 if( pos>=0 && treeptr->at(pos).m_Hash != (*it).m_IndexHash)
724 *status = max(git_wc_status_modified, *status); // modified file found
725 if(callback)
727 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
728 if(dirpos<0 || IsRecursive)
729 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);
732 else
733 break;
736 ++it;
739 //Check Delete
740 if( *status == git_wc_status_normal )
742 pos = SearchInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength());
743 if(pos <0)
745 *status = max(git_wc_status_added, *status); // added file found
748 else
750 int hstart,hend;
751 GetRangeInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength(), &hstart, &hend, pos);
752 CGitHeadFileList::iterator hit;
753 hit = treeptr->begin() + hstart;
754 CGitHeadFileList::iterator lastElement = treeptr->end();
755 for (int i = hstart; i <= hend && hit != lastElement; ++i)
757 if (SearchInSortVector(*indexptr, (*hit).m_FileName, -1) < 0)
759 *status = max(git_wc_status_deleted, *status); // deleted file found
760 break;
762 ++hit;
767 }/* End lock*/
769 // If define callback, it need update each file status.
770 // If not define callback, status == git_wc_status_conflicted, needn't check each file status
771 // because git_wc_status_conflicted is highest.s
772 if(callback || (*status != git_wc_status_conflicted))
774 //Check File Time;
775 //if(IsRecursive)
777 CString sub, currentPath;
778 it = indexptr->begin()+start;
779 for (int i = start; i <= end; ++i, ++it)
781 if( !IsRecursive )
783 //skip child directory
784 int pos = (*it).m_FileName.Find(_T('/'), path.GetLength());
786 if( pos > 0)
788 currentPath = (*it).m_FileName.Left(pos);
789 if( callback && (sub != currentPath) )
791 sub = currentPath;
792 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": index subdir %s\n"),sub);
793 if(callback) callback(gitdir + _T("\\") + sub,
794 git_wc_status_normal, true, pData, false, false);
796 continue;
800 git_wc_status_kind filestatus = git_wc_status_none;
801 bool assumeValid = false;
802 bool skipWorktree = false;
803 GetFileStatus(gitdir, (*it).m_FileName, &filestatus, IsFul, IsRecursive, IsIgnore, callback, pData, &assumeValid, &skipWorktree);
809 if(callback) callback(gitdir + _T("/") + subpath, *status, true, pData, false, false);
812 }catch(...)
814 if(status)
815 *status = git_wc_status_none;
816 return -1;
819 return 0;
822 bool GitStatus::IsExistIndexLockFile(const CString &gitdir)
824 CString sDirName= gitdir;
826 if (!PathIsDirectory(sDirName))
828 int x = sDirName.ReverseFind(_T('\\'));
829 if (x < 2)
830 return false;
832 sDirName = sDirName.Left(x);
835 for (;;)
837 if(PathFileExists(sDirName + _T("\\.git")))
839 if(PathFileExists(g_AdminDirMap.GetAdminDir(sDirName) + _T("index.lock")))
840 return true;
841 else
842 return false;
845 int x = sDirName.ReverseFind(_T('\\'));
846 if (x < 2)
847 return false;
849 sDirName = sDirName.Left(x);
853 bool GitStatus::ReleasePath(const CString &gitdir)
855 g_IndexFileMap.SafeClear(gitdir);
856 return true;
859 bool GitStatus::ReleasePathsRecursively(const CString &rootpath)
861 g_IndexFileMap.SafeClearRecursively(rootpath);
862 return true;