06d07c4aaf22e3207c7dc5d9dc2cf966ca7fec65
[TortoiseGit.git] / src / Git / GitStatus.cpp
blob06d07c4aaf22e3207c7dc5d9dc2cf966ca7fec65
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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 CGitIndexFileMap g_IndexFileMap;
31 CGitHeadFileMap g_HeadFileMap;
32 CGitIgnoreList g_IgnoreList;
34 GitStatus::GitStatus()
35 : status(NULL)
39 GitStatus::~GitStatus(void)
43 // static method
44 git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t depth, bool * assumeValid, bool * skipWorktree)
46 git_wc_status_kind statuskind;
47 BOOL err;
48 BOOL isDir;
49 CString sProjectRoot;
51 isDir = path.IsDirectory();
52 if (!path.HasAdminDir(&sProjectRoot))
53 return git_wc_status_none;
55 // rev.kind = git_opt_revision_unspecified;
56 statuskind = git_wc_status_none;
58 const BOOL bIsRecursive = (depth == git_depth_infinity || depth == git_depth_unknown); // taken from SVN source
60 CString sSubPath;
61 CString s = path.GetWinPathString();
62 if (s.GetLength() > sProjectRoot.GetLength())
64 if (sProjectRoot.GetLength() == 3 && sProjectRoot[1] == _T(':'))
65 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
66 else
67 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);
70 bool isfull = ((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
71 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
73 if(isDir)
75 err = GetDirStatus(sProjectRoot,sSubPath,&statuskind, isfull,bIsRecursive,isfull,NULL, NULL);
76 // folders must not be displayed as added or deleted only as modified (this is for Shell Overlay-Modes)
77 if (statuskind == git_wc_status_unversioned && sSubPath.IsEmpty())
78 statuskind = git_wc_status_normal;
79 else if (statuskind == git_wc_status_deleted || statuskind == git_wc_status_added)
80 statuskind = git_wc_status_modified;
82 else
84 err = GetFileStatus(sProjectRoot, sSubPath, &statuskind, isfull, false, isfull, NULL, NULL, assumeValid, skipWorktree);
87 return statuskind;
90 // static method
91 git_wc_status_kind GitStatus::GetAllStatusRecursive(const CTGitPath& path)
93 return GetAllStatus(path, git_depth_infinity);
96 // static method
97 git_wc_status_kind GitStatus::GetMoreImportant(git_wc_status_kind status1, git_wc_status_kind status2)
99 if (GetStatusRanking(status1) >= GetStatusRanking(status2))
100 return status1;
101 return status2;
103 // static private method
104 int GitStatus::GetStatusRanking(git_wc_status_kind status)
106 switch (status)
108 case git_wc_status_none:
109 return 0;
110 case git_wc_status_unversioned:
111 return 1;
112 case git_wc_status_ignored:
113 return 2;
114 case git_wc_status_incomplete:
115 return 4;
116 case git_wc_status_normal:
117 case git_wc_status_external:
118 return 5;
119 case git_wc_status_added:
120 return 6;
121 case git_wc_status_missing:
122 return 7;
123 case git_wc_status_deleted:
124 return 8;
125 case git_wc_status_replaced:
126 return 9;
127 case git_wc_status_modified:
128 return 10;
129 case git_wc_status_merged:
130 return 11;
131 case git_wc_status_conflicted:
132 return 12;
133 case git_wc_status_obstructed:
134 return 13;
136 return 0;
139 void GitStatus::GetStatus(const CTGitPath& path, bool /*update*/ /* = false */, bool noignore /* = false */, bool /*noexternals*/ /* = false */)
141 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
142 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
143 // after the call again
145 CString sProjectRoot;
146 if ( !path.HasAdminDir(&sProjectRoot) )
147 return;
149 bool isfull = ((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
150 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
153 LPCTSTR lpszSubPath = NULL;
154 CString sSubPath;
155 CString s = path.GetWinPathString();
156 if (s.GetLength() > sProjectRoot.GetLength())
158 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
159 lpszSubPath = sSubPath;
160 // skip initial slash if necessary
161 if (*lpszSubPath == _T('\\'))
162 ++lpszSubPath;
165 m_status.prop_status = m_status.text_status = git_wc_status_none;
166 m_status.assumeValid = false;
167 m_status.skipWorktree = false;
169 if(path.IsDirectory())
171 m_err = GetDirStatus(sProjectRoot,CString(lpszSubPath),&m_status.text_status , isfull, false,!noignore, NULL, NULL);
174 else
176 m_err = GetFileStatus(sProjectRoot, CString(lpszSubPath), &m_status.text_status ,isfull, false,!noignore, NULL,NULL, &m_status.assumeValid, &m_status.skipWorktree);
180 // Error present if function is not under version control
181 if (m_err)
183 status = NULL;
184 return;
187 status = &m_status;
190 void GitStatus::GetStatusString(git_wc_status_kind status, size_t buflen, TCHAR * string)
192 TCHAR * buf;
193 switch (status)
195 case git_wc_status_none:
196 buf = _T("none\0");
197 break;
198 case git_wc_status_unversioned:
199 buf = _T("unversioned\0");
200 break;
201 case git_wc_status_normal:
202 buf = _T("normal\0");
203 break;
204 case git_wc_status_added:
205 buf = _T("added\0");
206 break;
207 case git_wc_status_missing:
208 buf = _T("missing\0");
209 break;
210 case git_wc_status_deleted:
211 buf = _T("deleted\0");
212 break;
213 case git_wc_status_replaced:
214 buf = _T("replaced\0");
215 break;
216 case git_wc_status_modified:
217 buf = _T("modified\0");
218 break;
219 case git_wc_status_merged:
220 buf = _T("merged\0");
221 break;
222 case git_wc_status_conflicted:
223 buf = _T("conflicted\0");
224 break;
225 case git_wc_status_obstructed:
226 buf = _T("obstructed\0");
227 break;
228 case git_wc_status_ignored:
229 buf = _T("ignored");
230 break;
231 case git_wc_status_external:
232 buf = _T("external");
233 break;
234 case git_wc_status_incomplete:
235 buf = _T("incomplete\0");
236 break;
237 default:
238 buf = _T("\0");
239 break;
241 _stprintf_s(string, buflen, _T("%s"), buf);
244 void GitStatus::GetStatusString(HINSTANCE hInst, git_wc_status_kind status, TCHAR * string, int size, WORD lang)
246 switch (status)
248 case git_wc_status_none:
249 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
250 break;
251 case git_wc_status_unversioned:
252 LoadStringEx(hInst, IDS_STATUSUNVERSIONED, string, size, lang);
253 break;
254 case git_wc_status_normal:
255 LoadStringEx(hInst, IDS_STATUSNORMAL, string, size, lang);
256 break;
257 case git_wc_status_added:
258 LoadStringEx(hInst, IDS_STATUSADDED, string, size, lang);
259 break;
260 case git_wc_status_missing:
261 LoadStringEx(hInst, IDS_STATUSABSENT, string, size, lang);
262 break;
263 case git_wc_status_deleted:
264 LoadStringEx(hInst, IDS_STATUSDELETED, string, size, lang);
265 break;
266 case git_wc_status_replaced:
267 LoadStringEx(hInst, IDS_STATUSREPLACED, string, size, lang);
268 break;
269 case git_wc_status_modified:
270 LoadStringEx(hInst, IDS_STATUSMODIFIED, string, size, lang);
271 break;
272 case git_wc_status_merged:
273 LoadStringEx(hInst, IDS_STATUSMERGED, string, size, lang);
274 break;
275 case git_wc_status_conflicted:
276 LoadStringEx(hInst, IDS_STATUSCONFLICTED, string, size, lang);
277 break;
278 case git_wc_status_ignored:
279 LoadStringEx(hInst, IDS_STATUSIGNORED, string, size, lang);
280 break;
281 case git_wc_status_obstructed:
282 LoadStringEx(hInst, IDS_STATUSOBSTRUCTED, string, size, lang);
283 break;
284 case git_wc_status_external:
285 LoadStringEx(hInst, IDS_STATUSEXTERNAL, string, size, lang);
286 break;
287 case git_wc_status_incomplete:
288 LoadStringEx(hInst, IDS_STATUSINCOMPLETE, string, size, lang);
289 break;
290 default:
291 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
292 break;
296 int GitStatus::LoadStringEx(HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax, WORD wLanguage)
298 const STRINGRESOURCEIMAGE* pImage;
299 const STRINGRESOURCEIMAGE* pImageEnd;
300 ULONG nResourceSize;
301 HGLOBAL hGlobal;
302 UINT iIndex;
303 int ret;
305 HRSRC hResource = FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE(((uID>>4)+1)), wLanguage);
306 if (!hResource)
308 // try the default language before giving up!
309 hResource = FindResource(hInstance, MAKEINTRESOURCE(((uID>>4)+1)), RT_STRING);
310 if (!hResource)
311 return 0;
313 hGlobal = LoadResource(hInstance, hResource);
314 if (!hGlobal)
315 return 0;
316 pImage = (const STRINGRESOURCEIMAGE*)::LockResource(hGlobal);
317 if(!pImage)
318 return 0;
320 nResourceSize = ::SizeofResource(hInstance, hResource);
321 pImageEnd = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+nResourceSize);
322 iIndex = uID&0x000f;
324 while ((iIndex > 0) && (pImage < pImageEnd))
326 pImage = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+(sizeof(STRINGRESOURCEIMAGE)+(pImage->nLength*sizeof(WCHAR))));
327 iIndex--;
329 if (pImage >= pImageEnd)
330 return 0;
331 if (pImage->nLength == 0)
332 return 0;
334 ret = pImage->nLength;
335 if (pImage->nLength > nBufferMax)
337 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength-1);
338 lpBuffer[nBufferMax-1] = 0;
340 else
342 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength);
343 lpBuffer[ret] = 0;
345 return ret;
348 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
350 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)
354 CString path = pathParam;
356 TCHAR oldpath[MAX_PATH+1];
357 memset(oldpath,0,MAX_PATH+1);
359 path.Replace(_T('\\'),_T('/'));
361 CString lowcasepath =path;
362 lowcasepath.MakeLower();
364 if(status)
366 git_wc_status_kind st = git_wc_status_none;
367 CGitHash hash;
369 g_IndexFileMap.GetFileStatus(gitdir, path, &st, IsFull, false, callback, pData, &hash, true, assumeValid, skipWorktree);
371 if( st == git_wc_status_conflicted )
373 *status =st;
374 if(callback)
375 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
376 return 0;
379 if( st == git_wc_status_unversioned )
381 if(!IsIgnore)
383 *status = git_wc_status_unversioned;
384 if(callback)
385 callback(gitdir + _T("/") + path, *status, false, pData, *assumeValid, *skipWorktree);
386 return 0;
389 if( g_IgnoreList.CheckIgnoreChanged(gitdir,path))
391 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
393 if( g_IgnoreList.IsIgnore(path, gitdir) )
395 st = git_wc_status_ignored;
397 *status = st;
398 if(callback)
399 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
401 return 0;
404 if( st == git_wc_status_normal && IsFull)
407 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
409 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
411 // Check Head Tree Hash;
413 //add item
415 int start =SearchInSortVector(*treeptr,lowcasepath.GetBuffer(),-1);
416 lowcasepath.ReleaseBuffer();
418 if(start<0)
420 *status =st=git_wc_status_added;
421 ATLTRACE(_T("File miss in head tree %s"), path);
422 if(callback)
423 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
424 return 0;
427 //staged and not commit
428 if( treeptr->at(start).m_Hash != hash )
430 *status =st=git_wc_status_modified;
431 if(callback)
432 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
433 return 0;
438 *status =st;
439 if(callback)
440 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
441 return 0;
444 catch(...)
446 if(status)
447 *status = git_wc_status_none;
448 return -1;
451 return 0;
455 bool GitStatus::HasIgnoreFilesChanged(const CString &gitdir, const CString &subpaths)
457 return g_IgnoreList.CheckIgnoreChanged(gitdir, subpaths);
460 int GitStatus::LoadIgnoreFile(const CString &gitdir,const CString &subpaths)
462 return g_IgnoreList.LoadAllIgnoreFile(gitdir,subpaths);
464 int GitStatus::IsUnderVersionControl(const CString &gitdir, const CString &path, bool isDir,bool *isVersion)
466 if (g_IndexFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion))
467 return 1;
468 if (!*isVersion)
469 return g_HeadFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion);
470 return 0;
473 __int64 GitStatus::GetIndexFileTime(const CString &gitdir)
475 SHARED_INDEX_PTR ptr=g_IndexFileMap.SafeGet(gitdir);
476 if(ptr.get() == NULL)
477 return 0;
479 return ptr->m_LastModifyTime;
482 int GitStatus::IsIgnore(const CString &gitdir, const CString &path, bool *isIgnore)
484 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,path))
485 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
487 *isIgnore = g_IgnoreList.IsIgnore(path,gitdir);
489 return 0;
492 static bool SortFileName(CGitFileName &Item1, CGitFileName &Item2)
494 return Item1.m_FileName.Compare(Item2.m_FileName)<0;
497 int GitStatus::GetFileList(const CString &gitdir, const CString &subpath, std::vector<CGitFileName> &list)
499 WIN32_FIND_DATA data;
500 HANDLE handle=::FindFirstFile(gitdir+_T("\\")+subpath+_T("\\*.*"), &data);
503 if(_tcscmp(data.cFileName, _T(".git")) == 0)
504 continue;
506 if(_tcscmp(data.cFileName, _T(".")) == 0)
507 continue;
509 if(_tcscmp(data.cFileName, _T("..")) == 0)
510 continue;
512 CGitFileName filename;
514 filename.m_CaseFileName = filename.m_FileName = data.cFileName;
515 filename.m_FileName.MakeLower();
517 if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
519 filename.m_FileName += _T('/');
522 list.push_back(filename);
524 }while(::FindNextFile(handle, &data));
526 FindClose(handle);
528 std::sort(list.begin(), list.end(), SortFileName);
529 return 0;
532 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)
536 TCHAR oldpath[MAX_PATH+1];
537 memset(oldpath,0,MAX_PATH+1);
539 CString path =subpath;
541 path.Replace(_T('\\'),_T('/'));
542 if(!path.IsEmpty())
543 if(path[path.GetLength()-1] != _T('/'))
544 path += _T('/'); //Add trail / to show it is directory, not file name.
546 CString lowcasepath = path;
547 lowcasepath.MakeLower();
549 std::vector<CGitFileName> filelist;
550 GetFileList(gitdir, subpath, filelist);
552 if(status)
554 g_IndexFileMap.CheckAndUpdate(gitdir,true);
556 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
558 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
559 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
561 std::vector<CGitFileName>::iterator it;
563 // new git working tree has no index file
564 if (indexptr.get() == NULL)
566 for (it = filelist.begin(); it < filelist.end(); ++it)
568 CString casepath = path + it->m_CaseFileName;
570 bool bIsDir = false;
571 if (casepath.GetLength() > 0 && casepath[casepath.GetLength() - 1] == _T('/'))
572 bIsDir = true;
574 if (bIsDir) /*check if it is directory*/
576 if (::PathFileExists(gitdir + casepath + _T("\\.git")))
577 { /* That is git submodule */
578 *status = git_wc_status_unknown;
579 if (callback)
580 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
581 continue;
585 if (IsIgnore)
587 if (g_IgnoreList.CheckIgnoreChanged(gitdir, casepath))
588 g_IgnoreList.LoadAllIgnoreFile(gitdir, casepath);
590 if (g_IgnoreList.IsIgnore(casepath, gitdir))
591 *status = git_wc_status_ignored;
592 else if (bIsDir)
593 continue;
594 else
595 *status = git_wc_status_unversioned;
597 else if (bIsDir)
598 continue;
599 else
600 *status = git_wc_status_unversioned;
602 if(callback)
603 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
605 return 0;
608 CString onepath;
609 CString casepath;
610 for (it = filelist.begin(); it < filelist.end(); ++it)
612 casepath=onepath = path;
613 onepath.MakeLower();
614 onepath += it->m_FileName;
615 casepath += it->m_CaseFileName;
617 LPTSTR onepathBuffer = onepath.GetBuffer();
618 int pos = SearchInSortVector(*indexptr, onepathBuffer, onepath.GetLength());
619 int posintree = SearchInSortVector(*treeptr, onepathBuffer, onepath.GetLength());
620 onepath.ReleaseBuffer();
622 bool bIsDir =false;
623 if(onepath.GetLength()>0 && onepath[onepath.GetLength()-1] == _T('/'))
624 bIsDir =true;
626 if(pos <0 && posintree<0)
628 if(onepath.GetLength() ==0)
629 continue;
631 if(bIsDir) /*check if it is directory*/
633 if(::PathFileExists(gitdir+onepath+_T("/.git")))
634 { /* That is git submodule */
635 *status = git_wc_status_unknown;
636 if(callback)
637 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
638 continue;
642 if(!IsIgnore)
644 *status = git_wc_status_unversioned;
645 if(callback)
646 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
647 continue;
650 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,casepath))
651 g_IgnoreList.LoadAllIgnoreFile(gitdir,casepath);
653 if(g_IgnoreList.IsIgnore(casepath,gitdir))
654 *status = git_wc_status_ignored;
655 else
656 *status = git_wc_status_unversioned;
658 if(callback)
659 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
662 else if(pos <0 && posintree>=0) /* check if file delete in index */
664 *status = git_wc_status_deleted;
665 if(callback)
666 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
669 else if(pos >=0 && posintree <0) /* Check if file added */
671 *status = git_wc_status_added;
672 if(callback)
673 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
675 else
677 if(onepath.GetLength() ==0)
678 continue;
680 if(onepath[onepath.GetLength()-1] == _T('/'))
682 *status = git_wc_status_normal;
683 if(callback)
684 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
686 else
688 bool assumeValid = false;
689 bool skipWorktree = false;
690 git_wc_status_kind filestatus;
691 GetFileStatus(gitdir, casepath, &filestatus, IsFul, IsRecursive, IsIgnore, callback, pData, &assumeValid, &skipWorktree);
695 }/*End of For*/
697 /* Check deleted file in system */
698 LPTSTR lowcasepathBuffer = lowcasepath.GetBuffer();
699 int start=0, end=0;
700 int pos=SearchInSortVector(*indexptr, lowcasepathBuffer, lowcasepath.GetLength());
702 if (GetRangeInSortVector(*indexptr, lowcasepathBuffer, lowcasepath.GetLength(), &start, &end, pos) == 0)
704 CGitIndexList::iterator it;
705 CString oldstring;
707 for (it = indexptr->begin() + start; it <= indexptr->begin() + end; ++it)
709 int start = lowcasepath.GetLength();
710 int index = (*it).m_FileName.Find(_T('/'), start);
711 if(index<0)
712 index = (*it).m_FileName.GetLength();
714 CString filename = (*it).m_FileName.Mid(start, index-start);
715 if(oldstring != filename)
717 oldstring = filename;
718 if(SearchInSortVector(filelist, filename.GetBuffer(), filename.GetLength())<0)
720 *status = git_wc_status_deleted;
721 if(callback)
722 callback(gitdir + _T("/") + filename, *status, false, pData, false, false);
728 start = end =0;
729 pos=SearchInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength());
730 if (GetRangeInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength(), &start, &end, pos) == 0)
732 CGitHeadFileList::iterator it;
733 CString oldstring;
735 for (it = treeptr->begin() + start; it <= treeptr->begin() + end; ++it)
737 int start = lowcasepath.GetLength();
738 int index = (*it).m_FileName.Find(_T('/'), start);
739 if(index<0)
740 index = (*it).m_FileName.GetLength();
742 CString filename = (*it).m_FileName.Mid(start, index-start);
743 if(oldstring != filename)
745 oldstring = filename;
746 if(SearchInSortVector(filelist, filename.GetBuffer(), filename.GetLength())<0)
748 *status = git_wc_status_deleted;
749 if(callback)
750 callback(gitdir + _T("/") + (*it).m_FileName, *status, false, pData, false, false);
755 lowcasepath.ReleaseBuffer();
757 }/*End of if status*/
758 }catch(...)
760 return -1;
762 return 0;
765 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)
769 TCHAR oldpath[MAX_PATH+1];
770 memset(oldpath,0,MAX_PATH+1);
772 CString path =subpath;
774 path.Replace(_T('\\'),_T('/'));
775 if(!path.IsEmpty())
776 if(path[path.GetLength()-1] != _T('/'))
777 path += _T('/'); //Add trail / to show it is directory, not file name.
779 CString lowcasepath = path;
780 lowcasepath.MakeLower();
782 if(status)
784 g_IndexFileMap.CheckAndUpdate(gitdir, true);
786 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
788 if (indexptr == NULL)
790 *status = git_wc_status_unversioned;
791 return 0;
794 int pos=SearchInSortVector(*indexptr,lowcasepath.GetBuffer(),lowcasepath.GetLength());
795 lowcasepath.ReleaseBuffer();
797 //Not In Version Contorl
798 if(pos<0)
800 if(!IsIgnore)
802 *status = git_wc_status_unversioned;
803 if(callback)
804 callback(gitdir + _T("/") + path, *status, false, pData, false, false);
805 return 0;
807 //Check ignore always.
809 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,path))
810 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
812 if(g_IgnoreList.IsIgnore(path,gitdir))
813 *status = git_wc_status_ignored;
814 else
815 *status = git_wc_status_unversioned;
817 g_HeadFileMap.CheckHeadAndUpdate(gitdir, false);
819 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
820 //Check init repository
821 if (treeptr->HeadIsEmpty() && path.IsEmpty())
822 *status = git_wc_status_normal;
826 else // In version control
828 *status = git_wc_status_normal;
830 int start=0;
831 int end=0;
832 if(path.IsEmpty())
834 start=0;
835 end = (int)indexptr->size() - 1;
837 LPTSTR lowcasepathBuffer = lowcasepath.GetBuffer();
838 GetRangeInSortVector(*indexptr, lowcasepathBuffer, lowcasepath.GetLength(), &start, &end, pos);
839 lowcasepath.ReleaseBuffer();
840 CGitIndexList::iterator it;
842 it = indexptr->begin()+start;
844 // Check Conflict;
845 for (int i = start; i <= end; ++i)
847 if (((*it).m_Flags & GIT_IDXENTRY_STAGEMASK) !=0)
849 *status = git_wc_status_conflicted;
850 if(callback)
852 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
853 if(dirpos<0 || IsRecursive)
854 callback(gitdir + _T("\\") + it->m_FileName, git_wc_status_conflicted, false, pData, false, false);
856 else
857 break;
859 ++it;
862 if( IsFul && (*status != git_wc_status_conflicted))
864 *status = git_wc_status_normal;
866 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
868 //Check Add
869 it = indexptr->begin()+start;
873 //Check if new init repository
874 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
876 if (!treeptr->empty() || treeptr->HeadIsEmpty())
878 for (int i = start; i<= end; ++i)
880 pos =SearchInSortVector(*treeptr, (*it).m_FileName.GetBuffer(), -1);
881 (*it).m_FileName.ReleaseBuffer();
883 if(pos < 0)
885 *status = max(git_wc_status_added, *status); // added file found
886 if(callback)
888 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
889 if(dirpos<0 || IsRecursive)
890 callback(gitdir + _T("\\") + it->m_FileName, git_wc_status_added, false, pData, false, false);
893 else
894 break;
897 if( pos>=0 && treeptr->at(pos).m_Hash != (*it).m_IndexHash)
899 *status = max(git_wc_status_modified, *status); // modified file found
900 if(callback)
902 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
903 if(dirpos<0 || IsRecursive)
904 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);
907 else
908 break;
911 ++it;
914 //Check Delete
915 if( *status == git_wc_status_normal )
917 pos = SearchInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength());
918 if(pos <0)
920 *status = max(git_wc_status_added, *status); // added file found
923 else
925 int hstart,hend;
926 GetRangeInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength(), &hstart, &hend, pos);
927 CGitHeadFileList::iterator hit;
928 hit = treeptr->begin() + hstart;
929 CGitHeadFileList::iterator lastElement = treeptr->end();
930 for (int i = hstart; i <= hend && hit != lastElement; ++i)
932 if( SearchInSortVector(*indexptr,(*hit).m_FileName.GetBuffer(),-1) < 0)
934 (*hit).m_FileName.ReleaseBuffer();
935 *status = max(git_wc_status_deleted, *status); // deleted file found
936 break;
938 (*hit).m_FileName.ReleaseBuffer();
939 ++hit;
944 }/* End lock*/
946 lowcasepath.ReleaseBuffer();
947 // If define callback, it need update each file status.
948 // If not define callback, status == git_wc_status_conflicted, needn't check each file status
949 // because git_wc_status_conflicted is highest.s
950 if(callback || (*status != git_wc_status_conflicted))
952 //Check File Time;
953 //if(IsRecursive)
955 CString sub, currentPath;
956 it = indexptr->begin()+start;
957 for (int i = start; i <= end; ++i, ++it)
959 if( !IsRecursive )
961 //skip child directory
962 int pos = (*it).m_FileName.Find(_T('/'), path.GetLength());
964 if( pos > 0)
966 currentPath = (*it).m_FileName.Left(pos);
967 if( callback && (sub != currentPath) )
969 sub = currentPath;
970 ATLTRACE(_T("index subdir %s\n"),sub);
971 if(callback) callback(gitdir + _T("\\") + sub,
972 git_wc_status_normal, true, pData, false, false);
974 continue;
978 git_wc_status_kind filestatus = git_wc_status_none;
979 bool assumeValid = false;
980 bool skipWorktree = false;
981 GetFileStatus(gitdir, (*it).m_FileName, &filestatus, IsFul, IsRecursive, IsIgnore, callback, pData, &assumeValid, &skipWorktree);
987 if(callback) callback(gitdir + _T("/") + subpath, *status, true, pData, false, false);
990 }catch(...)
992 if(status)
993 *status = git_wc_status_none;
994 return -1;
997 return 0;
1000 bool GitStatus::IsExistIndexLockFile(const CString &gitdir)
1002 CString sDirName= gitdir;
1004 for (;;)
1006 if(PathFileExists(sDirName + _T("\\.git")))
1008 if(PathFileExists(g_AdminDirMap.GetAdminDir(sDirName) + _T("index.lock")))
1009 return true;
1010 else
1011 return false;
1014 int x = sDirName.ReverseFind(_T('\\'));
1015 if (x < 2)
1016 return false;
1018 sDirName = sDirName.Left(x);