Moved #include "git2.h" to stdafx.h
[TortoiseGit.git] / src / Git / GitStatus.cpp
blobf46242b4c441418eb5db440f85e5ae8b66826814
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 "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 "gitindex.h"
38 #include "shellcache.h"
40 extern CGitAdminDirMap g_AdminDirMap;
41 CGitIndexFileMap g_IndexFileMap;
42 CGitHeadFileMap g_HeadFileMap;
43 CGitIgnoreList g_IgnoreList;
45 GitStatus::GitStatus()
46 : status(NULL)
50 GitStatus::~GitStatus(void)
54 // static method
55 git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t depth, bool * assumeValid, bool * skipWorktree)
57 git_wc_status_kind statuskind;
58 BOOL err;
59 BOOL isDir;
60 CString sProjectRoot;
62 isDir = path.IsDirectory();
63 if (!path.HasAdminDir(&sProjectRoot))
64 return git_wc_status_none;
66 // rev.kind = git_opt_revision_unspecified;
67 statuskind = git_wc_status_none;
69 const BOOL bIsRecursive = (depth == git_depth_infinity || depth == git_depth_unknown); // taken from SVN source
71 CString sSubPath;
72 CString s = path.GetWinPathString();
73 if (s.GetLength() > sProjectRoot.GetLength())
75 if (sProjectRoot.GetLength() == 3 && sProjectRoot[1] == _T(':'))
76 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
77 else
78 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);
81 bool isfull = ((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
82 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
84 if(isDir)
86 err = GetDirStatus(sProjectRoot,sSubPath,&statuskind, isfull,bIsRecursive,isfull,NULL, NULL);
87 // folders must not be displayed as added or deleted only as modified (this is for Shell Overlay-Modes)
88 if (statuskind == git_wc_status_unversioned && sSubPath.IsEmpty())
89 statuskind = git_wc_status_normal;
90 else if (statuskind == git_wc_status_deleted || statuskind == git_wc_status_added)
91 statuskind = git_wc_status_modified;
93 else
95 err = GetFileStatus(sProjectRoot, sSubPath, &statuskind, isfull, false, isfull, NULL, NULL, assumeValid, skipWorktree);
98 return statuskind;
101 // static method
102 git_wc_status_kind GitStatus::GetAllStatusRecursive(const CTGitPath& path)
104 return GetAllStatus(path, git_depth_infinity);
107 // static method
108 git_wc_status_kind GitStatus::GetMoreImportant(git_wc_status_kind status1, git_wc_status_kind status2)
110 if (GetStatusRanking(status1) >= GetStatusRanking(status2))
111 return status1;
112 return status2;
114 // static private method
115 int GitStatus::GetStatusRanking(git_wc_status_kind status)
117 switch (status)
119 case git_wc_status_none:
120 return 0;
121 case git_wc_status_unversioned:
122 return 1;
123 case git_wc_status_ignored:
124 return 2;
125 case git_wc_status_incomplete:
126 return 4;
127 case git_wc_status_normal:
128 case git_wc_status_external:
129 return 5;
130 case git_wc_status_added:
131 return 6;
132 case git_wc_status_missing:
133 return 7;
134 case git_wc_status_deleted:
135 return 8;
136 case git_wc_status_replaced:
137 return 9;
138 case git_wc_status_modified:
139 return 10;
140 case git_wc_status_merged:
141 return 11;
142 case git_wc_status_conflicted:
143 return 12;
144 case git_wc_status_obstructed:
145 return 13;
147 return 0;
150 void GitStatus::GetStatus(const CTGitPath& path, bool /*update*/ /* = false */, bool noignore /* = false */, bool /*noexternals*/ /* = false */)
152 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
153 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
154 // after the call again
156 CString sProjectRoot;
157 if ( !path.HasAdminDir(&sProjectRoot) )
158 return;
160 bool isfull = ((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\CacheType"),
161 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
164 LPCTSTR lpszSubPath = NULL;
165 CString sSubPath;
166 CString s = path.GetWinPathString();
167 if (s.GetLength() > sProjectRoot.GetLength())
169 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
170 lpszSubPath = sSubPath;
171 // skip initial slash if necessary
172 if (*lpszSubPath == _T('\\'))
173 ++lpszSubPath;
176 m_status.prop_status = m_status.text_status = git_wc_status_none;
177 m_status.assumeValid = false;
178 m_status.skipWorktree = false;
180 if(path.IsDirectory())
182 m_err = GetDirStatus(sProjectRoot,CString(lpszSubPath),&m_status.text_status , isfull, false,!noignore, NULL, NULL);
185 else
187 m_err = GetFileStatus(sProjectRoot, CString(lpszSubPath), &m_status.text_status ,isfull, false,!noignore, NULL,NULL, &m_status.assumeValid, &m_status.skipWorktree);
191 // Error present if function is not under version control
192 if (m_err)
194 status = NULL;
195 return;
198 status = &m_status;
201 void GitStatus::GetStatusString(git_wc_status_kind status, size_t buflen, TCHAR * string)
203 TCHAR * buf;
204 switch (status)
206 case git_wc_status_none:
207 buf = _T("none\0");
208 break;
209 case git_wc_status_unversioned:
210 buf = _T("unversioned\0");
211 break;
212 case git_wc_status_normal:
213 buf = _T("normal\0");
214 break;
215 case git_wc_status_added:
216 buf = _T("added\0");
217 break;
218 case git_wc_status_missing:
219 buf = _T("missing\0");
220 break;
221 case git_wc_status_deleted:
222 buf = _T("deleted\0");
223 break;
224 case git_wc_status_replaced:
225 buf = _T("replaced\0");
226 break;
227 case git_wc_status_modified:
228 buf = _T("modified\0");
229 break;
230 case git_wc_status_merged:
231 buf = _T("merged\0");
232 break;
233 case git_wc_status_conflicted:
234 buf = _T("conflicted\0");
235 break;
236 case git_wc_status_obstructed:
237 buf = _T("obstructed\0");
238 break;
239 case git_wc_status_ignored:
240 buf = _T("ignored");
241 break;
242 case git_wc_status_external:
243 buf = _T("external");
244 break;
245 case git_wc_status_incomplete:
246 buf = _T("incomplete\0");
247 break;
248 default:
249 buf = _T("\0");
250 break;
252 _stprintf_s(string, buflen, _T("%s"), buf);
255 void GitStatus::GetStatusString(HINSTANCE hInst, git_wc_status_kind status, TCHAR * string, int size, WORD lang)
257 switch (status)
259 case git_wc_status_none:
260 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
261 break;
262 case git_wc_status_unversioned:
263 LoadStringEx(hInst, IDS_STATUSUNVERSIONED, string, size, lang);
264 break;
265 case git_wc_status_normal:
266 LoadStringEx(hInst, IDS_STATUSNORMAL, string, size, lang);
267 break;
268 case git_wc_status_added:
269 LoadStringEx(hInst, IDS_STATUSADDED, string, size, lang);
270 break;
271 case git_wc_status_missing:
272 LoadStringEx(hInst, IDS_STATUSABSENT, string, size, lang);
273 break;
274 case git_wc_status_deleted:
275 LoadStringEx(hInst, IDS_STATUSDELETED, string, size, lang);
276 break;
277 case git_wc_status_replaced:
278 LoadStringEx(hInst, IDS_STATUSREPLACED, string, size, lang);
279 break;
280 case git_wc_status_modified:
281 LoadStringEx(hInst, IDS_STATUSMODIFIED, string, size, lang);
282 break;
283 case git_wc_status_merged:
284 LoadStringEx(hInst, IDS_STATUSMERGED, string, size, lang);
285 break;
286 case git_wc_status_conflicted:
287 LoadStringEx(hInst, IDS_STATUSCONFLICTED, string, size, lang);
288 break;
289 case git_wc_status_ignored:
290 LoadStringEx(hInst, IDS_STATUSIGNORED, string, size, lang);
291 break;
292 case git_wc_status_obstructed:
293 LoadStringEx(hInst, IDS_STATUSOBSTRUCTED, string, size, lang);
294 break;
295 case git_wc_status_external:
296 LoadStringEx(hInst, IDS_STATUSEXTERNAL, string, size, lang);
297 break;
298 case git_wc_status_incomplete:
299 LoadStringEx(hInst, IDS_STATUSINCOMPLETE, string, size, lang);
300 break;
301 default:
302 LoadStringEx(hInst, IDS_STATUSNONE, string, size, lang);
303 break;
307 int GitStatus::LoadStringEx(HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax, WORD wLanguage)
309 const STRINGRESOURCEIMAGE* pImage;
310 const STRINGRESOURCEIMAGE* pImageEnd;
311 ULONG nResourceSize;
312 HGLOBAL hGlobal;
313 UINT iIndex;
314 int ret;
316 HRSRC hResource = FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE(((uID>>4)+1)), wLanguage);
317 if (!hResource)
319 // try the default language before giving up!
320 hResource = FindResource(hInstance, MAKEINTRESOURCE(((uID>>4)+1)), RT_STRING);
321 if (!hResource)
322 return 0;
324 hGlobal = LoadResource(hInstance, hResource);
325 if (!hGlobal)
326 return 0;
327 pImage = (const STRINGRESOURCEIMAGE*)::LockResource(hGlobal);
328 if(!pImage)
329 return 0;
331 nResourceSize = ::SizeofResource(hInstance, hResource);
332 pImageEnd = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+nResourceSize);
333 iIndex = uID&0x000f;
335 while ((iIndex > 0) && (pImage < pImageEnd))
337 pImage = (const STRINGRESOURCEIMAGE*)(LPBYTE(pImage)+(sizeof(STRINGRESOURCEIMAGE)+(pImage->nLength*sizeof(WCHAR))));
338 iIndex--;
340 if (pImage >= pImageEnd)
341 return 0;
342 if (pImage->nLength == 0)
343 return 0;
345 ret = pImage->nLength;
346 if (pImage->nLength > nBufferMax)
348 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength-1);
349 lpBuffer[nBufferMax-1] = 0;
351 else
353 wcsncpy_s(lpBuffer, nBufferMax, pImage->achString, pImage->nLength);
354 lpBuffer[ret] = 0;
356 return ret;
359 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
361 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)
365 CString path = pathParam;
367 TCHAR oldpath[MAX_PATH+1];
368 memset(oldpath,0,MAX_PATH+1);
370 path.Replace(_T('\\'),_T('/'));
372 CString lowcasepath =path;
373 lowcasepath.MakeLower();
375 if(status)
377 git_wc_status_kind st = git_wc_status_none;
378 CGitHash hash;
380 g_IndexFileMap.GetFileStatus(gitdir, path, &st, IsFull, false, callback, pData, &hash, true, assumeValid, skipWorktree);
382 if( st == git_wc_status_conflicted )
384 *status =st;
385 if(callback)
386 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
387 return 0;
390 if( st == git_wc_status_unversioned )
392 if(!IsIgnore)
394 *status = git_wc_status_unversioned;
395 if(callback)
396 callback(gitdir + _T("/") + path, *status, false, pData, *assumeValid, *skipWorktree);
397 return 0;
400 if( g_IgnoreList.CheckIgnoreChanged(gitdir,path))
402 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
404 if( g_IgnoreList.IsIgnore(path, gitdir) )
406 st = git_wc_status_ignored;
408 *status = st;
409 if(callback)
410 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
412 return 0;
415 if( st == git_wc_status_normal && IsFull)
418 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
420 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
422 // Check Head Tree Hash;
424 //add item
426 int start =SearchInSortVector(*treeptr,lowcasepath.GetBuffer(),-1);
427 lowcasepath.ReleaseBuffer();
429 if(start<0)
431 *status =st=git_wc_status_added;
432 ATLTRACE(_T("File miss in head tree %s"), path);
433 if(callback)
434 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
435 return 0;
438 //staged and not commit
439 if( treeptr->at(start).m_Hash != hash )
441 *status =st=git_wc_status_modified;
442 if(callback)
443 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
444 return 0;
449 *status =st;
450 if(callback)
451 callback(gitdir + _T("/") + path, st, false, pData, *assumeValid, *skipWorktree);
452 return 0;
455 catch(...)
457 if(status)
458 *status = git_wc_status_none;
459 return -1;
462 return 0;
466 bool GitStatus::HasIgnoreFilesChanged(const CString &gitdir, const CString &subpaths)
468 return g_IgnoreList.CheckIgnoreChanged(gitdir, subpaths);
471 int GitStatus::LoadIgnoreFile(const CString &gitdir,const CString &subpaths)
473 return g_IgnoreList.LoadAllIgnoreFile(gitdir,subpaths);
475 int GitStatus::IsUnderVersionControl(const CString &gitdir, const CString &path, bool isDir,bool *isVersion)
477 if (g_IndexFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion))
478 return 1;
479 if (!*isVersion)
480 return g_HeadFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion);
481 return 0;
484 __int64 GitStatus::GetIndexFileTime(const CString &gitdir)
486 SHARED_INDEX_PTR ptr=g_IndexFileMap.SafeGet(gitdir);
487 if(ptr.get() == NULL)
488 return 0;
490 return ptr->m_LastModifyTime;
493 int GitStatus::IsIgnore(const CString &gitdir, const CString &path, bool *isIgnore)
495 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,path))
496 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
498 *isIgnore = g_IgnoreList.IsIgnore(path,gitdir);
500 return 0;
503 static bool SortFileName(CGitFileName &Item1, CGitFileName &Item2)
505 return Item1.m_FileName.Compare(Item2.m_FileName)<0;
508 int GitStatus::GetFileList(const CString &gitdir, const CString &subpath, std::vector<CGitFileName> &list)
510 WIN32_FIND_DATA data;
511 HANDLE handle=::FindFirstFile(gitdir+_T("\\")+subpath+_T("\\*.*"), &data);
514 if(_tcscmp(data.cFileName, _T(".git")) == 0)
515 continue;
517 if(_tcscmp(data.cFileName, _T(".")) == 0)
518 continue;
520 if(_tcscmp(data.cFileName, _T("..")) == 0)
521 continue;
523 CGitFileName filename;
525 filename.m_CaseFileName = filename.m_FileName = data.cFileName;
526 filename.m_FileName.MakeLower();
528 if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
530 filename.m_FileName += _T('/');
533 list.push_back(filename);
535 }while(::FindNextFile(handle, &data));
537 FindClose(handle);
539 std::sort(list.begin(), list.end(), SortFileName);
540 return 0;
543 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)
547 TCHAR oldpath[MAX_PATH+1];
548 memset(oldpath,0,MAX_PATH+1);
550 CString path =subpath;
552 path.Replace(_T('\\'),_T('/'));
553 if(!path.IsEmpty())
554 if(path[path.GetLength()-1] != _T('/'))
555 path += _T('/'); //Add trail / to show it is directory, not file name.
557 CString lowcasepath = path;
558 lowcasepath.MakeLower();
560 std::vector<CGitFileName> filelist;
561 GetFileList(gitdir, subpath, filelist);
563 if(status)
565 g_IndexFileMap.CheckAndUpdate(gitdir,true);
567 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
569 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
570 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
572 std::vector<CGitFileName>::iterator it;
574 // new git working tree has no index file
575 if (indexptr.get() == NULL)
577 for (it = filelist.begin(); it < filelist.end(); ++it)
579 CString casepath = path + it->m_CaseFileName;
581 bool bIsDir = false;
582 if (casepath.GetLength() > 0 && casepath[casepath.GetLength() - 1] == _T('/'))
583 bIsDir = true;
585 if (bIsDir) /*check if it is directory*/
587 if (::PathFileExists(gitdir + casepath + _T("\\.git")))
588 { /* That is git submodule */
589 *status = git_wc_status_unknown;
590 if (callback)
591 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
592 continue;
596 if (IsIgnore)
598 if (g_IgnoreList.CheckIgnoreChanged(gitdir, casepath))
599 g_IgnoreList.LoadAllIgnoreFile(gitdir, casepath);
601 if (g_IgnoreList.IsIgnore(casepath, gitdir))
602 *status = git_wc_status_ignored;
603 else if (bIsDir)
604 continue;
605 else
606 *status = git_wc_status_unversioned;
608 else if (bIsDir)
609 continue;
610 else
611 *status = git_wc_status_unversioned;
613 if(callback)
614 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
616 return 0;
619 CString onepath;
620 CString casepath;
621 for (it = filelist.begin(); it < filelist.end(); ++it)
623 casepath=onepath = path;
624 onepath.MakeLower();
625 onepath += it->m_FileName;
626 casepath += it->m_CaseFileName;
628 LPTSTR onepathBuffer = onepath.GetBuffer();
629 int pos = SearchInSortVector(*indexptr, onepathBuffer, onepath.GetLength());
630 int posintree = SearchInSortVector(*treeptr, onepathBuffer, onepath.GetLength());
631 onepath.ReleaseBuffer();
633 bool bIsDir =false;
634 if(onepath.GetLength()>0 && onepath[onepath.GetLength()-1] == _T('/'))
635 bIsDir =true;
637 if(pos <0 && posintree<0)
639 if(onepath.GetLength() ==0)
640 continue;
642 if(bIsDir) /*check if it is directory*/
644 if(::PathFileExists(gitdir+onepath+_T("/.git")))
645 { /* That is git submodule */
646 *status = git_wc_status_unknown;
647 if(callback)
648 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
649 continue;
653 if(!IsIgnore)
655 *status = git_wc_status_unversioned;
656 if(callback)
657 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
658 continue;
661 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,casepath))
662 g_IgnoreList.LoadAllIgnoreFile(gitdir,casepath);
664 if(g_IgnoreList.IsIgnore(casepath,gitdir))
665 *status = git_wc_status_ignored;
666 else
667 *status = git_wc_status_unversioned;
669 if(callback)
670 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
673 else if(pos <0 && posintree>=0) /* check if file delete in index */
675 *status = git_wc_status_deleted;
676 if(callback)
677 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
680 else if(pos >=0 && posintree <0) /* Check if file added */
682 *status = git_wc_status_added;
683 if(callback)
684 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
686 else
688 if(onepath.GetLength() ==0)
689 continue;
691 if(onepath[onepath.GetLength()-1] == _T('/'))
693 *status = git_wc_status_normal;
694 if(callback)
695 callback(gitdir + _T("/") + casepath, *status, bIsDir, pData, false, false);
697 else
699 bool assumeValid = false;
700 bool skipWorktree = false;
701 git_wc_status_kind filestatus;
702 GetFileStatus(gitdir, casepath, &filestatus, IsFul, IsRecursive, IsIgnore, callback, pData, &assumeValid, &skipWorktree);
706 }/*End of For*/
708 /* Check deleted file in system */
709 LPTSTR lowcasepathBuffer = lowcasepath.GetBuffer();
710 int start=0, end=0;
711 int pos=SearchInSortVector(*indexptr, lowcasepathBuffer, lowcasepath.GetLength());
713 if (GetRangeInSortVector(*indexptr, lowcasepathBuffer, lowcasepath.GetLength(), &start, &end, pos) == 0)
715 CGitIndexList::iterator it;
716 CString oldstring;
718 for (it = indexptr->begin() + start; it <= indexptr->begin() + end; ++it)
720 int start = lowcasepath.GetLength();
721 int index = (*it).m_FileName.Find(_T('/'), start);
722 if(index<0)
723 index = (*it).m_FileName.GetLength();
725 CString filename = (*it).m_FileName.Mid(start, index-start);
726 if(oldstring != filename)
728 oldstring = filename;
729 if(SearchInSortVector(filelist, filename.GetBuffer(), filename.GetLength())<0)
731 *status = git_wc_status_deleted;
732 if(callback)
733 callback(gitdir + _T("/") + filename, *status, false, pData, false, false);
739 start = end =0;
740 pos=SearchInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength());
741 if (GetRangeInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength(), &start, &end, pos) == 0)
743 CGitHeadFileList::iterator it;
744 CString oldstring;
746 for (it = treeptr->begin() + start; it <= treeptr->begin() + end; ++it)
748 int start = lowcasepath.GetLength();
749 int index = (*it).m_FileName.Find(_T('/'), start);
750 if(index<0)
751 index = (*it).m_FileName.GetLength();
753 CString filename = (*it).m_FileName.Mid(start, index-start);
754 if(oldstring != filename)
756 oldstring = filename;
757 if(SearchInSortVector(filelist, filename.GetBuffer(), filename.GetLength())<0)
759 *status = git_wc_status_deleted;
760 if(callback)
761 callback(gitdir + _T("/") + (*it).m_FileName, *status, false, pData, false, false);
766 lowcasepath.ReleaseBuffer();
768 }/*End of if status*/
769 }catch(...)
771 return -1;
773 return 0;
776 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)
780 TCHAR oldpath[MAX_PATH+1];
781 memset(oldpath,0,MAX_PATH+1);
783 CString path =subpath;
785 path.Replace(_T('\\'),_T('/'));
786 if(!path.IsEmpty())
787 if(path[path.GetLength()-1] != _T('/'))
788 path += _T('/'); //Add trail / to show it is directory, not file name.
790 CString lowcasepath = path;
791 lowcasepath.MakeLower();
793 if(status)
795 g_IndexFileMap.CheckAndUpdate(gitdir, true);
797 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
799 if (indexptr == NULL)
801 *status = git_wc_status_unversioned;
802 return 0;
805 int pos=SearchInSortVector(*indexptr,lowcasepath.GetBuffer(),lowcasepath.GetLength());
806 lowcasepath.ReleaseBuffer();
808 //Not In Version Contorl
809 if(pos<0)
811 if(!IsIgnore)
813 *status = git_wc_status_unversioned;
814 if(callback)
815 callback(gitdir + _T("/") + path, *status, false, pData, false, false);
816 return 0;
818 //Check ignore always.
820 if(::g_IgnoreList.CheckIgnoreChanged(gitdir,path))
821 g_IgnoreList.LoadAllIgnoreFile(gitdir,path);
823 if(g_IgnoreList.IsIgnore(path,gitdir))
824 *status = git_wc_status_ignored;
825 else
826 *status = git_wc_status_unversioned;
828 g_HeadFileMap.CheckHeadAndUpdate(gitdir, false);
830 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
831 //Check init repository
832 if (treeptr->HeadIsEmpty() && path.IsEmpty())
833 *status = git_wc_status_normal;
837 else // In version control
839 *status = git_wc_status_normal;
841 int start=0;
842 int end=0;
843 if(path.IsEmpty())
845 start=0;
846 end = (int)indexptr->size() - 1;
848 LPTSTR lowcasepathBuffer = lowcasepath.GetBuffer();
849 GetRangeInSortVector(*indexptr, lowcasepathBuffer, lowcasepath.GetLength(), &start, &end, pos);
850 lowcasepath.ReleaseBuffer();
851 CGitIndexList::iterator it;
853 it = indexptr->begin()+start;
855 // Check Conflict;
856 for (int i = start; i <= end; ++i)
858 if (((*it).m_Flags & GIT_IDXENTRY_STAGEMASK) !=0)
860 *status = git_wc_status_conflicted;
861 if(callback)
863 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
864 if(dirpos<0 || IsRecursive)
865 callback(gitdir + _T("\\") + it->m_FileName, git_wc_status_conflicted, false, pData, false, false);
867 else
868 break;
870 ++it;
873 if( IsFul && (*status != git_wc_status_conflicted))
875 *status = git_wc_status_normal;
877 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
879 //Check Add
880 it = indexptr->begin()+start;
884 //Check if new init repository
885 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
887 if (!treeptr->empty() || treeptr->HeadIsEmpty())
889 for (int i = start; i<= end; ++i)
891 pos =SearchInSortVector(*treeptr, (*it).m_FileName.GetBuffer(), -1);
892 (*it).m_FileName.ReleaseBuffer();
894 if(pos < 0)
896 *status = max(git_wc_status_added, *status); // added file found
897 if(callback)
899 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
900 if(dirpos<0 || IsRecursive)
901 callback(gitdir + _T("\\") + it->m_FileName, git_wc_status_added, false, pData, false, false);
904 else
905 break;
908 if( pos>=0 && treeptr->at(pos).m_Hash != (*it).m_IndexHash)
910 *status = max(git_wc_status_modified, *status); // modified file found
911 if(callback)
913 int dirpos = (*it).m_FileName.Find(_T('/'), path.GetLength());
914 if(dirpos<0 || IsRecursive)
915 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);
918 else
919 break;
922 ++it;
925 //Check Delete
926 if( *status == git_wc_status_normal )
928 pos = SearchInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength());
929 if(pos <0)
931 *status = max(git_wc_status_added, *status); // added file found
934 else
936 int hstart,hend;
937 GetRangeInSortVector(*treeptr, lowcasepathBuffer, lowcasepath.GetLength(), &hstart, &hend, pos);
938 CGitHeadFileList::iterator hit;
939 hit = treeptr->begin() + hstart;
940 CGitHeadFileList::iterator lastElement = treeptr->end();
941 for (int i = hstart; i <= hend && hit != lastElement; ++i)
943 if( SearchInSortVector(*indexptr,(*hit).m_FileName.GetBuffer(),-1) < 0)
945 (*hit).m_FileName.ReleaseBuffer();
946 *status = max(git_wc_status_deleted, *status); // deleted file found
947 break;
949 (*hit).m_FileName.ReleaseBuffer();
950 ++hit;
955 }/* End lock*/
957 lowcasepath.ReleaseBuffer();
958 // If define callback, it need update each file status.
959 // If not define callback, status == git_wc_status_conflicted, needn't check each file status
960 // because git_wc_status_conflicted is highest.s
961 if(callback || (*status != git_wc_status_conflicted))
963 //Check File Time;
964 //if(IsRecursive)
966 CString sub, currentPath;
967 it = indexptr->begin()+start;
968 for (int i = start; i <= end; ++i, ++it)
970 if( !IsRecursive )
972 //skip child directory
973 int pos = (*it).m_FileName.Find(_T('/'), path.GetLength());
975 if( pos > 0)
977 currentPath = (*it).m_FileName.Left(pos);
978 if( callback && (sub != currentPath) )
980 sub = currentPath;
981 ATLTRACE(_T("index subdir %s\n"),sub);
982 if(callback) callback(gitdir + _T("\\") + sub,
983 git_wc_status_normal, true, pData, false, false);
985 continue;
989 git_wc_status_kind filestatus = git_wc_status_none;
990 bool assumeValid = false;
991 bool skipWorktree = false;
992 GetFileStatus(gitdir, (*it).m_FileName, &filestatus, IsFul, IsRecursive, IsIgnore, callback, pData, &assumeValid, &skipWorktree);
998 if(callback) callback(gitdir + _T("/") + subpath, *status, true, pData, false, false);
1001 }catch(...)
1003 if(status)
1004 *status = git_wc_status_none;
1005 return -1;
1008 return 0;
1011 bool GitStatus::IsExistIndexLockFile(const CString &gitdir)
1013 CString sDirName= gitdir;
1015 for (;;)
1017 if(PathFileExists(sDirName + _T("\\.git")))
1019 if(PathFileExists(g_AdminDirMap.GetAdminDir(sDirName) + _T("index.lock")))
1020 return true;
1021 else
1022 return false;
1025 int x = sDirName.ReverseFind(_T('\\'));
1026 if (x < 2)
1027 return false;
1029 sDirName = sDirName.Left(x);