Drop unnecessary code
[TortoiseGit.git] / src / Git / GitStatus.cpp
blobae58aea2e39d057f191bbbd22087575518e69609
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2012 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "registry.h"
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"
29 #ifdef _MFC_VER
30 //# include "Git.h"
31 //# include "MessageBox.h"
32 //# include "registry.h"
33 //# include "TGitPath.h"
34 //# include "PathUtils.h"
35 #endif
36 #include "git.h"
37 #include "git2.h"
38 #include "gitindex.h"
39 #include "shellcache.h"
41 extern CGitAdminDirMap g_AdminDirMap;
42 CGitIndexFileMap g_IndexFileMap;
43 CGitHeadFileMap g_HeadFileMap;
44 CGitIgnoreList g_IgnoreList;
46 GitStatus::GitStatus()
47 : status(NULL)
51 GitStatus::~GitStatus(void)
55 // static method
56 git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t depth, bool * assumeValid, bool * skipWorktree)
58 git_wc_status_kind statuskind;
59 BOOL err;
60 BOOL isDir;
61 CString sProjectRoot;
63 isDir = path.IsDirectory();
64 if (!path.HasAdminDir(&sProjectRoot))
65 return git_wc_status_none;
67 // rev.kind = git_opt_revision_unspecified;
68 statuskind = git_wc_status_none;
70 const BOOL bIsRecursive = (depth == git_depth_infinity || depth == git_depth_unknown); // taken from SVN source
72 CString sSubPath;
73 CString s = path.GetWinPathString();
74 if (s.GetLength() > sProjectRoot.GetLength())
76 if (sProjectRoot.GetLength() == 3 && sProjectRoot[1] == _T(':'))
77 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
78 else
79 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);
82 bool isfull = ((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
83 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
85 if(isDir)
87 err = GetDirStatus(sProjectRoot,sSubPath,&statuskind, isfull,bIsRecursive,isfull,NULL, NULL);
88 // folders must not be displayed as added or deleted only as modified (this is for Shell Overlay-Modes)
89 if (statuskind == git_wc_status_unversioned && sSubPath.IsEmpty())
90 statuskind = git_wc_status_normal;
91 else if (statuskind == git_wc_status_deleted || statuskind == git_wc_status_added)
92 statuskind = git_wc_status_modified;
94 else
96 err = GetFileStatus(sProjectRoot, sSubPath, &statuskind, isfull, false, isfull, NULL, NULL, assumeValid, skipWorktree);
99 return statuskind;
102 // static method
103 git_wc_status_kind GitStatus::GetAllStatusRecursive(const CTGitPath& path)
105 return GetAllStatus(path, git_depth_infinity);
108 // static method
109 git_wc_status_kind GitStatus::GetMoreImportant(git_wc_status_kind status1, git_wc_status_kind status2)
111 if (GetStatusRanking(status1) >= GetStatusRanking(status2))
112 return status1;
113 return status2;
115 // static private method
116 int GitStatus::GetStatusRanking(git_wc_status_kind status)
118 switch (status)
120 case git_wc_status_none:
121 return 0;
122 case git_wc_status_unversioned:
123 return 1;
124 case git_wc_status_ignored:
125 return 2;
126 case git_wc_status_incomplete:
127 return 4;
128 case git_wc_status_normal:
129 case git_wc_status_external:
130 return 5;
131 case git_wc_status_added:
132 return 6;
133 case git_wc_status_missing:
134 return 7;
135 case git_wc_status_deleted:
136 return 8;
137 case git_wc_status_replaced:
138 return 9;
139 case git_wc_status_modified:
140 return 10;
141 case git_wc_status_merged:
142 return 11;
143 case git_wc_status_conflicted:
144 return 12;
145 case git_wc_status_obstructed:
146 return 13;
148 return 0;
151 void GitStatus::GetStatus(const CTGitPath& path, bool /*update*/ /* = false */, bool noignore /* = false */, bool /*noexternals*/ /* = false */)
153 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
154 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
155 // after the call again
157 CString sProjectRoot;
158 if ( !path.HasAdminDir(&sProjectRoot) )
159 return;
161 bool isfull = ((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
162 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
165 LPCTSTR lpszSubPath = NULL;
166 CString sSubPath;
167 CString s = path.GetWinPathString();
168 if (s.GetLength() > sProjectRoot.GetLength())
170 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
171 lpszSubPath = sSubPath;
172 // skip initial slash if necessary
173 if (*lpszSubPath == _T('\\'))
174 lpszSubPath++;
177 m_status.prop_status = m_status.text_status = git_wc_status_none;
178 m_status.assumeValid = false;
179 m_status.skipWorktree = false;
181 if(path.IsDirectory())
183 m_err = GetDirStatus(sProjectRoot,CString(lpszSubPath),&m_status.text_status , isfull, false,!noignore, NULL, NULL);
186 else
188 m_err = GetFileStatus(sProjectRoot, CString(lpszSubPath), &m_status.text_status ,isfull, false,!noignore, NULL,NULL, &m_status.assumeValid, &m_status.skipWorktree);
192 // Error present if function is not under version control
193 if (m_err)
195 status = NULL;
196 return;
199 status = &m_status;
202 void GitStatus::GetStatusString(git_wc_status_kind status, size_t buflen, TCHAR * string)
204 TCHAR * buf;
205 switch (status)
207 case git_wc_status_none:
208 buf = _T("none\0");
209 break;
210 case git_wc_status_unversioned:
211 buf = _T("unversioned\0");
212 break;
213 case git_wc_status_normal:
214 buf = _T("normal\0");
215 break;
216 case git_wc_status_added:
217 buf = _T("added\0");
218 break;
219 case git_wc_status_missing:
220 buf = _T("missing\0");
221 break;
222 case git_wc_status_deleted:
223 buf = _T("deleted\0");
224 break;
225 case git_wc_status_replaced:
226 buf = _T("replaced\0");
227 break;
228 case git_wc_status_modified:
229 buf = _T("modified\0");
230 break;
231 case git_wc_status_merged:
232 buf = _T("merged\0");
233 break;
234 case git_wc_status_conflicted:
235 buf = _T("conflicted\0");
236 break;
237 case git_wc_status_obstructed:
238 buf = _T("obstructed\0");
239 break;
240 case git_wc_status_ignored:
241 buf = _T("ignored");
242 break;
243 case git_wc_status_external:
244 buf = _T("external");
245 break;
246 case git_wc_status_incomplete:
247 buf = _T("incomplete\0");
248 break;
249 default:
250 buf = _T("\0");
251 break;
253 _stprintf_s(string, buflen, _T("%s"), buf);
256 void GitStatus::GetStatusString(HINSTANCE hInst, git_wc_status_kind status, TCHAR * string, int size, WORD lang)
258 switch (status)
260 case git_wc_status_none:
261 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
262 break;
263 case git_wc_status_unversioned:
264 LoadStringEx(hInst, IDS_STATUSUNVERSIONED, string, size, lang);
265 break;
266 case git_wc_status_normal:
267 LoadStringEx(hInst, IDS_STATUSNORMAL, string, size, lang);
268 break;
269 case git_wc_status_added:
270 LoadStringEx(hInst, IDS_STATUSADDED, string, size, lang);
271 break;
272 case git_wc_status_missing:
273 LoadStringEx(hInst, IDS_STATUSABSENT, string, size, lang);
274 break;
275 case git_wc_status_deleted:
276 LoadStringEx(hInst, IDS_STATUSDELETED, string, size, lang);
277 break;
278 case git_wc_status_replaced:
279 LoadStringEx(hInst, IDS_STATUSREPLACED, string, size, lang);
280 break;
281 case git_wc_status_modified:
282 LoadStringEx(hInst, IDS_STATUSMODIFIED, string, size, lang);
283 break;
284 case git_wc_status_merged:
285 LoadStringEx(hInst, IDS_STATUSMERGED, string, size, lang);
286 break;
287 case git_wc_status_conflicted:
288 LoadStringEx(hInst, IDS_STATUSCONFLICTED, string, size, lang);
289 break;
290 case git_wc_status_ignored:
291 LoadStringEx(hInst, IDS_STATUSIGNORED, string, size, lang);
292 break;
293 case git_wc_status_obstructed:
294 LoadStringEx(hInst, IDS_STATUSOBSTRUCTED, string, size, lang);
295 break;
296 case git_wc_status_external:
297 LoadStringEx(hInst, IDS_STATUSEXTERNAL, string, size, lang);
298 break;
299 case git_wc_status_incomplete:
300 LoadStringEx(hInst, IDS_STATUSINCOMPLETE, string, size, lang);
301 break;
302 default:
303 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
304 break;
308 int GitStatus::LoadStringEx(HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax, WORD wLanguage)
310 const STRINGRESOURCEIMAGE* pImage;
311 const STRINGRESOURCEIMAGE* pImageEnd;
312 ULONG nResourceSize;
313 HGLOBAL hGlobal;
314 UINT iIndex;
315 int ret;
317 HRSRC hResource = FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE(((uID>>4)+1)), wLanguage);
318 if (!hResource)
320 // try the default language before giving up!
321 hResource = FindResource(hInstance, MAKEINTRESOURCE(((uID>>4)+1)), RT_STRING);
322 if (!hResource)
323 return 0;
325 hGlobal = LoadResource(hInstance, hResource);
326 if (!hGlobal)
327 return 0;
328 pImage = (const STRINGRESOURCEIMAGE*)::LockResource(hGlobal);
329 if(!pImage)
330 return 0;
332 nResourceSize = ::SizeofResource(hInstance, hResource);
333 pImageEnd = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+nResourceSize);
334 iIndex = uID&0x000f;
336 while ((iIndex > 0) && (pImage < pImageEnd))
338 pImage = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+(sizeof(STRINGRESOURCEIMAGE)+(pImage->nLength*sizeof(WCHAR))));
339 iIndex--;
341 if (pImage >= pImageEnd)
342 return 0;
343 if (pImage->nLength == 0)
344 return 0;
346 ret = pImage->nLength;
347 if (pImage->nLength > nBufferMax)
349 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength-1);
350 lpBuffer[nBufferMax-1] = 0;
352 else
354 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength);
355 lpBuffer[ret] = 0;
357 return ret;
360 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
362 int GitStatus::GetFileStatus(const CString &gitdir, const CString &pathParam, git_wc_status_kind * status,BOOL IsFull, BOOL /*IsRecursive*/,BOOL IsIgnore, FIll_STATUS_CALLBACK callback, void *pData, bool * assumeValid, bool * skipWorktree)
366 CString path = pathParam;
368 TCHAR oldpath[MAX_PATH+1];
369 memset(oldpath,0,MAX_PATH+1);
371 path.Replace(_T('\\'),_T('/'));
373 CString lowcasepath =path;
374 lowcasepath.MakeLower();
376 if(status)
378 git_wc_status_kind st = git_wc_status_none;
379 CGitHash hash;
381 g_IndexFileMap.GetFileStatus(gitdir, path, &st, IsFull, false, callback, pData, &hash, true, assumeValid, skipWorktree);
383 if( st == git_wc_status_conflicted )
385 *status =st;
386 if(callback)
387 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
388 return 0;
391 if( st == git_wc_status_unversioned )
393 if(!IsIgnore)
395 *status = git_wc_status_unversioned;
396 if(callback)
397 callback(gitdir + _T("/") + path, *status, false, pData, *assumeValid, *skipWorktree);
398 return 0;
401 if( g_IgnoreList.CheckIgnoreChanged(gitdir,path))
403 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
405 if( g_IgnoreList.IsIgnore(path, gitdir) )
407 st = git_wc_status_ignored;
409 *status = st;
410 if(callback)
411 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
413 return 0;
416 if( st == git_wc_status_normal && IsFull)
419 g_HeadFileMap.CheckHeadUpdate(gitdir);
420 bool b=false;
422 SHARED_TREE_PTR treeptr;
424 treeptr=g_HeadFileMap.SafeGet(gitdir);
426 b = treeptr->m_Head != treeptr->m_TreeHash;
428 if(b)
430 treeptr->ReadHeadHash(gitdir);
432 // Init Repository
433 if( treeptr->m_HeadFile.IsEmpty() )
435 *status =st=git_wc_status_added;
436 if(callback)
437 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
438 return 0;
440 if(treeptr->ReadTree())
442 //Check if init repository
443 *status = treeptr->m_Head.IsEmpty()? git_wc_status_added: st;
444 if(callback)
445 callback(gitdir + _T("/") + path, *status, false, pData, *assumeValid, *skipWorktree);
446 return 0;
448 g_HeadFileMap.SafeSet(gitdir, treeptr);
451 // Check Head Tree Hash;
453 //add item
455 int start =SearchInSortVector(*treeptr,lowcasepath.GetBuffer(),-1);
456 lowcasepath.ReleaseBuffer();
458 if(start<0)
460 *status =st=git_wc_status_added;
461 ATLTRACE(_T("File miss in head tree %s"), path);
462 if(callback)
463 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
464 return 0;
467 //staged and not commit
468 if( treeptr->at(start).m_Hash != hash )
470 *status =st=git_wc_status_modified;
471 if(callback)
472 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
473 return 0;
478 *status =st;
479 if(callback)
480 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
481 return 0;
484 catch(...)
486 if(status)
487 *status = git_wc_status_none;
488 return -1;
491 return 0;
495 bool GitStatus::IsGitReposChanged(const CString &gitdir,const CString &subpaths, int mode)
497 if( mode & GIT_MODE_INDEX)
499 return g_IndexFileMap.CheckAndUpdate(gitdir, true);
502 if( mode & GIT_MODE_HEAD)
504 if(g_HeadFileMap.CheckHeadUpdate(gitdir))
505 return true;
508 if( mode & GIT_MODE_IGNORE)
510 if(g_IgnoreList.CheckIgnoreChanged(gitdir,subpaths))
511 return true;
513 return false;
516 int GitStatus::LoadIgnoreFile(const CString &gitdir,const CString &subpaths)
518 return g_IgnoreList.LoadAllIgnoreFile(gitdir,subpaths);
520 int GitStatus::IsUnderVersionControl(const CString &gitdir, const CString &path, bool isDir,bool *isVersion)
522 if (g_IndexFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion))
523 return 1;
524 if (!*isVersion)
525 return g_HeadFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion);
526 return 0;
529 __int64 GitStatus::GetIndexFileTime(const CString &gitdir)
531 SHARED_INDEX_PTR ptr=g_IndexFileMap.SafeGet(gitdir);
532 if(ptr.get() == NULL)
533 return 0;
535 return ptr->m_LastModifyTime;
538 int GitStatus::IsIgnore(const CString &gitdir, const CString &path, bool *isIgnore)
540 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,path))
541 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
543 *isIgnore = g_IgnoreList.IsIgnore(path,gitdir);
545 return 0;
548 static bool SortFileName(CGitFileName &Item1, CGitFileName &Item2)
550 return Item1.m_FileName.Compare(Item2.m_FileName)<0;
553 int GitStatus::GetFileList(const CString &gitdir, const CString &subpath, std::vector<CGitFileName> &list)
555 WIN32_FIND_DATA data;
556 HANDLE handle=::FindFirstFile(gitdir+_T("\\")+subpath+_T("\\*.*"), &data);
559 if(_tcscmp(data.cFileName, _T(".git")) == 0)
560 continue;
562 if(_tcscmp(data.cFileName, _T(".")) == 0)
563 continue;
565 if(_tcscmp(data.cFileName, _T("..")) == 0)
566 continue;
568 CGitFileName filename;
570 filename.m_CaseFileName = filename.m_FileName = data.cFileName;
571 filename.m_FileName.MakeLower();
573 if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
575 filename.m_FileName += _T('/');
578 list.push_back(filename);
580 }while(::FindNextFile(handle, &data));
582 FindClose(handle);
584 std::sort(list.begin(), list.end(), SortFileName);
585 return 0;
588 int GitStatus::EnumDirStatus(const CString &gitdir,const CString &subpath,git_wc_status_kind * status,BOOL IsFul, BOOL IsRecursive, BOOL IsIgnore, FIll_STATUS_CALLBACK callback, void *pData)
592 TCHAR oldpath[MAX_PATH+1];
593 memset(oldpath,0,MAX_PATH+1);
595 CString path =subpath;
597 path.Replace(_T('\\'),_T('/'));
598 if(!path.IsEmpty())
599 if(path[path.GetLength()-1] != _T('/'))
600 path += _T('/'); //Add trail / to show it is directory, not file name.
602 CString lowcasepath = path;
603 lowcasepath.MakeLower();
605 std::vector<CGitFileName> filelist;
606 GetFileList(gitdir, subpath, filelist);
608 if(status)
610 g_IndexFileMap.CheckAndUpdate(gitdir,true);
612 if(g_HeadFileMap.CheckHeadUpdate(gitdir) || g_HeadFileMap.IsHashChanged(gitdir))
614 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
615 treeptr->ReadHeadHash(gitdir);
616 if(!treeptr->ReadTree())
618 g_HeadFileMap.SafeSet(gitdir, treeptr);
622 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
623 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
625 std::vector<CGitFileName>::iterator it;
627 // new git working tree has no index file
628 if (indexptr.get() == NULL)
630 for(it = filelist.begin(); it < filelist.end(); it++)
632 CString casepath = path + it->m_CaseFileName;
634 bool bIsDir = false;
635 if (casepath.GetLength() > 0 && casepath[casepath.GetLength() - 1] == _T('/'))
636 bIsDir = true;
638 if (bIsDir) /*check if it is directory*/
640 if (::PathFileExists(gitdir + casepath + _T("\\.git")))
641 { /* That is git submodule */
642 *status = git_wc_status_unknown;
643 if (callback)
644 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
645 continue;
649 if (IsIgnore)
651 if (g_IgnoreList.CheckIgnoreChanged(gitdir, casepath))
652 g_IgnoreList.LoadAllIgnoreFile(gitdir, casepath);
654 if (g_IgnoreList.IsIgnore(casepath, gitdir))
655 *status = git_wc_status_ignored;
656 else if (bIsDir)
657 continue;
658 else
659 *status = git_wc_status_unversioned;
661 else if (bIsDir)
662 continue;
663 else
664 *status = git_wc_status_unversioned;
666 if(callback)
667 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
669 return 0;
672 CString onepath;
673 CString casepath;
674 for(it = filelist.begin(); it<filelist.end();it++)
676 casepath=onepath = path;
677 onepath.MakeLower();
678 onepath += it->m_FileName;
679 casepath += it->m_CaseFileName;
681 LPTSTR onepathBuffer = onepath.GetBuffer();
682 int pos = SearchInSortVector(*indexptr, onepathBuffer, onepath.GetLength());
683 int posintree = SearchInSortVector(*treeptr, onepathBuffer, onepath.GetLength());
684 onepath.ReleaseBuffer();
686 bool bIsDir =false;
687 if(onepath.GetLength()>0 && onepath[onepath.GetLength()-1] == _T('/'))
688 bIsDir =true;
690 if(pos <0 && posintree<0)
692 if(onepath.GetLength() ==0)
693 continue;
695 if(bIsDir) /*check if it is directory*/
697 if(::PathFileExists(gitdir+onepath+_T("/.git")))
698 { /* That is git submodule */
699 *status = git_wc_status_unknown;
700 if(callback)
701 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
702 continue;
706 if(!IsIgnore)
708 *status = git_wc_status_unversioned;
709 if(callback)
710 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
711 continue;
714 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,casepath))
715 g_IgnoreList.LoadAllIgnoreFile(gitdir,casepath);
717 if(g_IgnoreList.IsIgnore(casepath,gitdir))
718 *status = git_wc_status_ignored;
719 else
720 *status = git_wc_status_unversioned;
722 if(callback)
723 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
726 else if(pos <0 && posintree>=0) /* check if file delete in index */
728 *status = git_wc_status_deleted;
729 if(callback)
730 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
733 else if(pos >=0 && posintree <0) /* Check if file added */
735 *status = git_wc_status_added;
736 if(callback)
737 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
739 else
741 if(onepath.GetLength() ==0)
742 continue;
744 if(onepath[onepath.GetLength()-1] == _T('/'))
746 *status = git_wc_status_normal;
747 if(callback)
748 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
750 else
752 bool assumeValid = false;
753 bool skipWorktree = false;
754 git_wc_status_kind filestatus;
755 GetFileStatus(gitdir, casepath, &filestatus, IsFul, IsRecursive, IsIgnore, callback, pData, &assumeValid, &skipWorktree);
759 }/*End of For*/
761 /* Check deleted file in system */
762 LPTSTR lowcasepathBuffer = lowcasepath.GetBuffer();
763 int start=0, end=0;
764 int pos=SearchInSortVector(*indexptr, lowcasepathBuffer, lowcasepath.GetLength());
766 if (GetRangeInSortVector(*indexptr, lowcasepathBuffer, lowcasepath.GetLength(), &start, &end, pos))
768 CGitIndexList::iterator it;
769 CString oldstring;
771 for(it = indexptr->begin()+start; it <= indexptr->begin()+end; it++)
773 int start = lowcasepath.GetLength();
774 int index = (*it).m_FileName.Find(_T('/'), start);
775 if(index<0)
776 index = (*it).m_FileName.GetLength();
778 CString filename = (*it).m_FileName.Mid(start, index-start);
779 if(oldstring != filename)
781 oldstring = filename;
782 if(SearchInSortVector(filelist, filename.GetBuffer(), filename.GetLength())<0)
784 *status = git_wc_status_deleted;
785 if(callback)
786 callback(gitdir + _T("/") + filename, *status, false, pData, false, false);
792 start = end =0;
793 pos=SearchInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength());
794 if (GetRangeInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength(), &start, &end, pos) == 0)
796 CGitHeadFileList::iterator it;
797 CString oldstring;
799 for(it = treeptr->begin()+start; it <= treeptr->begin()+end; it++)
801 int start = lowcasepath.GetLength();
802 int index = (*it).m_FileName.Find(_T('/'), start);
803 if(index<0)
804 index = (*it).m_FileName.GetLength();
806 CString filename = (*it).m_FileName.Mid(start, index-start);
807 if(oldstring != filename)
809 oldstring = filename;
810 if(SearchInSortVector(filelist, filename.GetBuffer(), filename.GetLength())<0)
812 *status = git_wc_status_deleted;
813 if(callback)
814 callback(gitdir + _T("/") + (*it).m_FileName, *status, false, pData, false, false);
819 lowcasepath.ReleaseBuffer();
821 }/*End of if status*/
822 }catch(...)
824 return -1;
826 return 0;
829 int GitStatus::GetDirStatus(const CString &gitdir,const CString &subpath,git_wc_status_kind * status,BOOL IsFul, BOOL IsRecursive,BOOL IsIgnore,FIll_STATUS_CALLBACK callback,void *pData)
833 TCHAR oldpath[MAX_PATH+1];
834 memset(oldpath,0,MAX_PATH+1);
836 CString path =subpath;
838 path.Replace(_T('\\'),_T('/'));
839 if(!path.IsEmpty())
840 if(path[path.GetLength()-1] != _T('/'))
841 path += _T('/'); //Add trail / to show it is directory, not file name.
843 CString lowcasepath = path;
844 lowcasepath.MakeLower();
846 if(status)
848 g_IndexFileMap.CheckAndUpdate(gitdir, true);
850 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
852 if (indexptr == NULL)
854 *status = git_wc_status_unversioned;
855 return 0;
858 int pos=SearchInSortVector(*indexptr,lowcasepath.GetBuffer(),lowcasepath.GetLength());
859 lowcasepath.ReleaseBuffer();
861 //Not In Version Contorl
862 if(pos<0)
864 if(!IsIgnore)
866 *status = git_wc_status_unversioned;
867 if(callback)
868 callback(gitdir + _T("/") + path, *status, false, pData, false, false);
869 return 0;
871 //Check ignore always.
873 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,path))
874 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
876 if(g_IgnoreList.IsIgnore(path,gitdir))
877 *status = git_wc_status_ignored;
878 else
879 *status = git_wc_status_unversioned;
881 g_HeadFileMap.CheckHeadUpdate(gitdir);
883 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
884 //Check init repository
885 if(treeptr->m_Head.IsEmpty() && path.IsEmpty())
886 *status = git_wc_status_normal;
890 else // In version control
892 *status = git_wc_status_normal;
894 int start=0;
895 int end=0;
896 if(path.IsEmpty())
898 start=0;
899 end = (int)indexptr->size() - 1;
901 LPTSTR lowcasepathBuffer = lowcasepath.GetBuffer();
902 GetRangeInSortVector(*indexptr, lowcasepathBuffer, lowcasepath.GetLength(), &start, &end, pos);
903 lowcasepath.ReleaseBuffer();
904 CGitIndexList::iterator it;
906 it = indexptr->begin()+start;
908 // Check Conflict;
909 for(int i=start;i<=end;i++)
911 if (((*it).m_Flags & GIT_IDXENTRY_STAGEMASK) !=0)
913 *status = git_wc_status_conflicted;
914 if(callback)
916 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
917 if(dirpos<0 || IsRecursive)
918 callback(gitdir + _T("\\") + it->m_FileName, git_wc_status_conflicted, false, pData, false, false);
920 else
921 break;
923 it++;
926 if( IsFul && (*status != git_wc_status_conflicted))
928 *status = git_wc_status_normal;
930 if(g_HeadFileMap.CheckHeadUpdate(gitdir))
932 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
934 treeptr->ReadHeadHash(gitdir);
936 if(treeptr->ReadTree())
938 g_HeadFileMap.SafeSet(gitdir, treeptr);
941 //Check Add
942 it = indexptr->begin()+start;
946 //Check if new init repository
947 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
949 if( treeptr->size() > 0 || treeptr->m_Head.IsEmpty() )
951 for(int i=start;i<=end;i++)
953 pos =SearchInSortVector(*treeptr, (*it).m_FileName.GetBuffer(), -1);
954 (*it).m_FileName.ReleaseBuffer();
956 if(pos < 0)
958 *status = max(git_wc_status_added, *status); // added file found
959 if(callback)
961 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
962 if(dirpos<0 || IsRecursive)
963 callback(gitdir + _T("\\") + it->m_FileName, git_wc_status_added, false, pData, false, false);
966 else
967 break;
970 if( pos>=0 && treeptr->at(pos).m_Hash != (*it).m_IndexHash)
972 *status = max(git_wc_status_modified, *status); // modified file found
973 if(callback)
975 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
976 if(dirpos<0 || IsRecursive)
977 callback(gitdir + _T("\\") + it->m_FileName, git_wc_status_modified, false, pData, ((*it).m_Flags & GIT_IDXENTRY_VALID) && !((*it).m_Flags & GIT_IDXENTRY_SKIP_WORKTREE), ((*it).m_Flags & GIT_IDXENTRY_SKIP_WORKTREE) != 0);
980 else
981 break;
984 it++;
987 //Check Delete
988 if( *status == git_wc_status_normal )
990 pos = SearchInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength());
991 if(pos <0)
993 *status = max(git_wc_status_added, *status); // added file found
996 else
998 int hstart,hend;
999 GetRangeInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength(), &hstart, &hend, pos);
1000 CGitHeadFileList::iterator hit;
1001 hit = treeptr->begin() + hstart;
1002 CGitHeadFileList::iterator lastElement = treeptr->end();
1003 for(int i=hstart; i <= hend && hit != lastElement; i++)
1005 if( SearchInSortVector(*indexptr,(*hit).m_FileName.GetBuffer(),-1) < 0)
1007 (*hit).m_FileName.ReleaseBuffer();
1008 *status = max(git_wc_status_deleted, *status); // deleted file found
1009 break;
1011 (*hit).m_FileName.ReleaseBuffer();
1012 hit++;
1017 }/* End lock*/
1019 lowcasepath.ReleaseBuffer();
1020 // If define callback, it need update each file status.
1021 // If not define callback, status == git_wc_status_conflicted, needn't check each file status
1022 // because git_wc_status_conflicted is highest.s
1023 if(callback || (*status != git_wc_status_conflicted))
1025 //Check File Time;
1026 //if(IsRecursive)
1028 CString sub, currentPath;
1029 it = indexptr->begin()+start;
1030 for(int i=start;i<=end;i++,it++)
1032 if( !IsRecursive )
1034 //skip child directory
1035 int pos = (*it).m_FileName.Find(_T('/'), path.GetLength());
1037 if( pos > 0)
1039 currentPath = (*it).m_FileName.Left(pos);
1040 if( callback && (sub != currentPath) )
1042 sub = currentPath;
1043 ATLTRACE(_T("index subdir %s\n"),sub);
1044 if(callback) callback(gitdir + _T("\\") + sub,
1045 git_wc_status_normal, true, pData, false, false);
1047 continue;
1051 git_wc_status_kind filestatus = git_wc_status_none;
1052 bool assumeValid = false;
1053 bool skipWorktree = false;
1054 GetFileStatus(gitdir, (*it).m_FileName, &filestatus, IsFul, IsRecursive, IsIgnore, callback, pData, &assumeValid, &skipWorktree);
1060 if(callback) callback(gitdir + _T("/") + subpath, *status, true, pData, false, false);
1063 }catch(...)
1065 if(status)
1066 *status = git_wc_status_none;
1067 return -1;
1070 return 0;
1073 bool GitStatus::IsExistIndexLockFile(const CString &gitdir)
1075 CString sDirName= gitdir;
1077 for (;;)
1079 if(PathFileExists(sDirName + _T("\\.git")))
1081 if(PathFileExists(g_AdminDirMap.GetAdminDir(sDirName) + _T("index.lock")))
1082 return true;
1083 else
1084 return false;
1087 int x = sDirName.ReverseFind(_T('\\'));
1088 if (x < 2)
1089 return false;
1091 sDirName = sDirName.Left(x);