Return failure in case we got an invalid handle
[TortoiseGit.git] / src / Git / GitStatus.cpp
blobc23d769e8024a8850072886084fcda245c97ff17
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "registry.h"
22 #include "..\TortoiseShell\resource.h"
23 #include "GitStatus.h"
24 #include "UnicodeUtils.h"
25 #include "Git.h"
26 #include "gitindex.h"
27 #include "ShellCache.h"
28 #include "SysInfo.h"
29 #include "SmartHandle.h"
31 extern CGitAdminDirMap g_AdminDirMap;
32 extern CGitIndexFileMap g_IndexFileMap;
33 CGitHeadFileMap g_HeadFileMap;
34 CGitIgnoreList g_IgnoreList;
36 GitStatus::GitStatus()
37 : status(nullptr)
39 m_status.assumeValid = m_status.skipWorktree = false;
40 m_status.prop_status = m_status.text_status = git_wc_status_none;
43 // static method
44 #ifndef TGITCACHE
45 git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t depth, bool * assumeValid, bool * skipWorktree)
47 git_wc_status_kind statuskind;
48 BOOL err;
49 BOOL isDir;
50 CString sProjectRoot;
52 isDir = path.IsDirectory();
53 if (!path.HasAdminDir(&sProjectRoot))
54 return git_wc_status_none;
56 // rev.kind = git_opt_revision_unspecified;
57 statuskind = git_wc_status_none;
59 const BOOL bIsRecursive = (depth == git_depth_infinity || depth == git_depth_unknown); // taken from SVN source
61 CString sSubPath;
62 CString s = path.GetWinPathString();
63 if (s.GetLength() > sProjectRoot.GetLength())
65 if (sProjectRoot.GetLength() == 3 && sProjectRoot[1] == L':')
66 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
67 else
68 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/);
71 bool isfull = ((DWORD)CRegStdDWORD(L"Software\\TortoiseGit\\CacheType",
72 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
74 if(isDir)
76 err = GetDirStatus(sProjectRoot, sSubPath, &statuskind, isfull, bIsRecursive, isfull);
77 // folders must not be displayed as added or deleted only as modified (this is for Shell Overlay-Modes)
78 if (statuskind == git_wc_status_unversioned && sSubPath.IsEmpty())
79 statuskind = git_wc_status_normal;
80 else if (statuskind == git_wc_status_deleted || statuskind == git_wc_status_added)
81 statuskind = git_wc_status_modified;
83 else
84 err = GetFileStatus(sProjectRoot, sSubPath, &statuskind, isfull, false, isfull, nullptr, nullptr, assumeValid, skipWorktree);
86 return statuskind;
88 #endif
90 // static method
91 git_wc_status_kind GitStatus::GetMoreImportant(git_wc_status_kind status1, git_wc_status_kind status2)
93 if (GetStatusRanking(status1) >= GetStatusRanking(status2))
94 return status1;
95 return status2;
97 // static private method
98 int GitStatus::GetStatusRanking(git_wc_status_kind status)
100 switch (status)
102 case git_wc_status_none:
103 return 0;
104 case git_wc_status_unversioned:
105 return 1;
106 case git_wc_status_ignored:
107 return 2;
108 case git_wc_status_incomplete:
109 return 4;
110 case git_wc_status_normal:
111 case git_wc_status_external:
112 return 5;
113 case git_wc_status_added:
114 return 6;
115 case git_wc_status_missing:
116 return 7;
117 case git_wc_status_deleted:
118 return 8;
119 case git_wc_status_replaced:
120 return 9;
121 case git_wc_status_modified:
122 return 10;
123 case git_wc_status_merged:
124 return 11;
125 case git_wc_status_conflicted:
126 return 12;
127 case git_wc_status_obstructed:
128 return 13;
130 return 0;
133 #ifndef TGITCACHE
134 void GitStatus::GetStatus(const CTGitPath& path, bool /*update*/ /* = false */, bool noignore /* = false */, bool /*noexternals*/ /* = false */)
136 // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of
137 // Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right
138 // after the call again
140 CString sProjectRoot;
141 if ( !path.HasAdminDir(&sProjectRoot) )
142 return;
144 bool isfull = ((DWORD)CRegStdDWORD(L"Software\\TortoiseGit\\CacheType",
145 GetSystemMetrics(SM_REMOTESESSION) ? ShellCache::dll : ShellCache::exe) == ShellCache::dllFull);
147 int err = 0;
149 LPCTSTR lpszSubPath = nullptr;
150 CString sSubPath;
151 CString s = path.GetWinPathString();
152 if (s.GetLength() > sProjectRoot.GetLength())
154 sSubPath = s.Right(s.GetLength() - sProjectRoot.GetLength());
155 lpszSubPath = sSubPath;
156 // skip initial slash if necessary
157 if (*lpszSubPath == L'\\')
158 ++lpszSubPath;
161 m_status.prop_status = m_status.text_status = git_wc_status_none;
162 m_status.assumeValid = false;
163 m_status.skipWorktree = false;
165 if (path.IsDirectory())
167 err = GetDirStatus(sProjectRoot, lpszSubPath, &m_status.text_status, isfull, false, !noignore);
168 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
169 m_status.text_status = git_wc_status_modified;
171 else
172 err = GetFileStatus(sProjectRoot, lpszSubPath, &m_status.text_status, isfull, false, !noignore, nullptr, nullptr, &m_status.assumeValid, &m_status.skipWorktree);
174 // Error present if function is not under version control
175 if (err)
177 status = nullptr;
178 return;
181 status = &m_status;
183 #endif
185 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
187 int GitStatus::GetFileStatus(const CString& gitdir, CString path, git_wc_status_kind* status, BOOL IsFull, BOOL /*IsRecursive*/, BOOL IsIgnore, FILL_STATUS_CALLBACK callback, void* pData, bool* assumeValid, bool* skipWorktree)
189 if (!status)
190 return 0;
192 path.Replace(L'\\', L'/');
194 CString lowcasepath = path;
195 lowcasepath.MakeLower();
197 git_wc_status_kind st = git_wc_status_none;
198 CGitHash hash;
200 g_IndexFileMap.GetFileStatus(gitdir, path, &st, IsFull, false, callback, pData, &hash, true, assumeValid, skipWorktree);
202 if (st == git_wc_status_conflicted)
204 *status = st;
205 if (callback && assumeValid && skipWorktree)
206 callback(CombinePath(gitdir, path), st, false, pData, *assumeValid, *skipWorktree);
207 return 0;
210 if (st == git_wc_status_unversioned)
212 if (!IsIgnore)
214 *status = git_wc_status_unversioned;
215 if (callback && assumeValid && skipWorktree)
216 callback(CombinePath(gitdir, path), *status, false, pData, *assumeValid, *skipWorktree);
217 return 0;
220 if (g_IgnoreList.CheckIgnoreChanged(gitdir, path, false))
221 g_IgnoreList.LoadAllIgnoreFile(gitdir, path, false);
222 if (g_IgnoreList.IsIgnore(path, gitdir, false))
223 st = git_wc_status_ignored;
225 *status = st;
226 if (callback && assumeValid && skipWorktree)
227 callback(CombinePath(gitdir, path), st, false, pData, *assumeValid, *skipWorktree);
229 return 0;
232 if ((st == git_wc_status_normal || st == git_wc_status_modified) && IsFull)
234 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
236 // Check Head Tree Hash
237 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
239 //add item
240 size_t start = SearchInSortVector(*treeptr, lowcasepath, -1);
241 if (start == NPOS)
243 *status = st = git_wc_status_added;
244 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": File miss in head tree %s", (LPCTSTR)path);
245 if (callback && assumeValid && skipWorktree)
246 callback(CombinePath(gitdir, path), st, false, pData, *assumeValid, *skipWorktree);
247 return 0;
250 // staged and not commit
251 if ((*treeptr)[start].m_Hash != hash)
253 *status = st = git_wc_status_modified;
254 if (callback && assumeValid && skipWorktree)
255 callback(CombinePath(gitdir, path), st, false, pData, *assumeValid, *skipWorktree);
256 return 0;
259 *status = st;
260 if (callback && assumeValid && skipWorktree)
261 callback(CombinePath(gitdir, path), st, false, pData, *assumeValid, *skipWorktree);
262 return 0;
265 #ifdef TGITCACHE
266 bool GitStatus::HasIgnoreFilesChanged(const CString &gitdir, const CString &subpaths, bool isDir)
268 return g_IgnoreList.CheckIgnoreChanged(gitdir, subpaths, isDir);
271 int GitStatus::LoadIgnoreFile(const CString &gitdir, const CString &subpaths, bool isDir)
273 return g_IgnoreList.LoadAllIgnoreFile(gitdir, subpaths, isDir);
275 int GitStatus::IsUnderVersionControl(const CString &gitdir, const CString &path, bool isDir,bool *isVersion)
277 if (g_IndexFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion))
278 return 1;
279 if (!*isVersion)
280 return g_HeadFileMap.IsUnderVersionControl(gitdir, path, isDir, isVersion);
281 return 0;
284 int GitStatus::IsIgnore(const CString &gitdir, const CString &path, bool *isIgnore, bool isDir)
286 if (g_IgnoreList.CheckIgnoreChanged(gitdir, path, isDir))
287 g_IgnoreList.LoadAllIgnoreFile(gitdir, path, isDir);
289 *isIgnore = g_IgnoreList.IsIgnore(path, gitdir, isDir);
291 return 0;
294 int GitStatus::GetFileList(CString path, std::vector<CGitFileName> &list)
296 path += L"\\*.*";
297 WIN32_FIND_DATA data;
298 CAutoFindFile handle = ::FindFirstFileEx(path, SysInfo::Instance().IsWin7OrLater() ? FindExInfoBasic : FindExInfoStandard, &data, FindExSearchNameMatch, nullptr, SysInfo::Instance().IsWin7OrLater() ? FIND_FIRST_EX_LARGE_FETCH : 0);
299 if (!handle)
300 return -1;
303 if (wcscmp(data.cFileName, L".git") == 0)
304 continue;
306 if (wcscmp(data.cFileName, L".") == 0)
307 continue;
309 if (wcscmp(data.cFileName, L"..") == 0)
310 continue;
312 CGitFileName filename;
314 filename.m_CaseFileName = filename.m_FileName = data.cFileName;
315 filename.m_FileName.MakeLower();
317 if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
318 filename.m_FileName += L'/';
320 list.push_back(filename);
322 }while(::FindNextFile(handle, &data));
324 handle.CloseHandle(); // manually close handle here in order to keep handles open as short as possible
326 std::sort(list.begin(), list.end(), SortCGitFileName);
327 return 0;
330 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)
332 if (!status)
333 return 0;
335 CString path = subpath;
337 path.Replace(L'\\', L'/');
338 if (!path.IsEmpty() && path[path.GetLength() - 1] != L'/')
339 path += L'/'; // Add trail / to show it is directory, not file name.
341 std::vector<CGitFileName> filelist;
342 GetFileList(CombinePath(gitdir, subpath), filelist);
344 g_IndexFileMap.CheckAndUpdate(gitdir,true);
346 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
348 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
349 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
351 // new git working tree has no index file
352 if (!indexptr.get())
354 for (auto it = filelist.cbegin(); it != filelist.cend(); ++it)
356 CString casepath = path;
357 casepath += it->m_CaseFileName;
359 bool bIsDir = false;
360 if (!it->m_FileName.IsEmpty() && it->m_FileName[it->m_FileName.GetLength() - 1] == L'/')
361 bIsDir = true;
363 if (IsIgnore)
365 if (g_IgnoreList.CheckIgnoreChanged(gitdir, casepath, bIsDir))
366 g_IgnoreList.LoadAllIgnoreFile(gitdir, casepath, bIsDir);
368 if (g_IgnoreList.IsIgnore(casepath, gitdir, bIsDir))
369 *status = git_wc_status_ignored;
370 else if (bIsDir)
371 continue;
372 else
373 *status = git_wc_status_unversioned;
375 else if (bIsDir)
376 continue;
377 else
378 *status = git_wc_status_unversioned;
380 if (callback)
381 callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false);
383 return 0;
386 CString lowcasepath = path;
387 lowcasepath.MakeLower();
389 for (auto it = filelist.cbegin(), itend = filelist.cend(); it != itend; ++it)
391 CString onepath(lowcasepath);
392 onepath += it->m_FileName;
393 CString casepath(path);
394 casepath += it->m_CaseFileName;
396 bool bIsDir = false;
397 if (!onepath.IsEmpty() && onepath[onepath.GetLength() - 1] == L'/')
398 bIsDir = true;
400 int matchLength = -1;
401 if (bIsDir)
402 matchLength = onepath.GetLength();
403 size_t pos = SearchInSortVector(*indexptr, onepath, matchLength);
404 size_t posintree = SearchInSortVector(*treeptr, onepath, matchLength);
406 if (pos == NPOS && posintree == NPOS)
408 if (onepath.IsEmpty())
409 continue;
411 if (!IsIgnore)
413 *status = git_wc_status_unversioned;
414 if (callback)
415 callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false);
416 continue;
419 if (g_IgnoreList.CheckIgnoreChanged(gitdir, casepath, bIsDir))
420 g_IgnoreList.LoadAllIgnoreFile(gitdir, casepath, bIsDir);
422 if (g_IgnoreList.IsIgnore(casepath, gitdir, bIsDir))
423 *status = git_wc_status_ignored;
424 else
425 *status = git_wc_status_unversioned;
427 if (callback)
428 callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false);
430 else if (pos == NPOS && posintree != NPOS) /* check if file delete in index */
432 *status = git_wc_status_deleted;
433 if (callback)
434 callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false);
436 else if (pos != NPOS && posintree == NPOS) /* Check if file added */
438 *status = git_wc_status_added;
439 if ((*indexptr)[pos].m_Flags & GIT_IDXENTRY_STAGEMASK)
440 *status = git_wc_status_conflicted;
441 if (callback)
442 callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false);
444 else
446 if (onepath.IsEmpty())
447 continue;
449 if (bIsDir)
451 *status = git_wc_status_normal;
452 if (callback)
453 callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false);
455 else
457 bool assumeValid = false;
458 bool skipWorktree = false;
459 git_wc_status_kind filestatus;
460 GetFileStatus(gitdir, casepath, &filestatus, IsFul, IsRecursive, IsIgnore, callback, pData, &assumeValid, &skipWorktree);
463 }/*End of For*/
465 /* Check deleted file in system */
466 size_t start = 0, end = 0;
467 size_t pos = SearchInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength()); // match path prefix, (sub)folders end with slash
468 std::set<CString> skipWorktreeSet;
470 if (GetRangeInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength(), &start, &end, pos) == 0)
472 CString oldstring;
473 for (auto it = indexptr->cbegin() + start, itlast = indexptr->cbegin() + end; it <= itlast; ++it)
475 auto& entry = *it;
476 int commonPrefixLength = lowcasepath.GetLength();
477 int index = entry.m_FileName.Find(L'/', commonPrefixLength);
478 if (index < 0)
479 index = entry.m_FileName.GetLength();
480 else
481 ++index; // include slash at the end for subfolders, so that we do not match files by mistake
483 CString filename = entry.m_FileName.Mid(commonPrefixLength, index - commonPrefixLength);
484 if (oldstring != filename)
486 oldstring = filename;
487 if (SearchInSortVector(filelist, filename, filename.GetLength()) == NPOS)
489 bool skipWorktree = false;
490 *status = git_wc_status_deleted;
491 if ((entry.m_FlagsExtended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
493 skipWorktreeSet.insert(filename);
494 skipWorktree = true;
495 *status = git_wc_status_normal;
497 if (callback)
498 callback(CombinePath(gitdir, entry.m_FileName), *status, false, pData, false, skipWorktree);
504 start = end = 0;
505 pos = SearchInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength()); // match path prefix, (sub)folders end with slash
506 if (GetRangeInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength(), &start, &end, pos) == 0)
508 CString oldstring;
509 for (auto it = treeptr->cbegin() + start, itlast = treeptr->cbegin() + end; it <= itlast; ++it)
511 auto& entry = *it;
512 int commonPrefixLength = lowcasepath.GetLength();
513 int index = entry.m_FileName.Find(L'/', commonPrefixLength);
514 if (index < 0)
515 index = entry.m_FileName.GetLength();
516 else
517 ++index; // include slash at the end for subfolders, so that we do not match files by mistake
519 CString filename = entry.m_FileName.Mid(commonPrefixLength, index - commonPrefixLength);
520 if (oldstring != filename && skipWorktreeSet.find(filename) == skipWorktreeSet.cend())
522 oldstring = filename;
523 if (SearchInSortVector(filelist, filename, filename.GetLength()) == NPOS)
525 *status = git_wc_status_deleted;
526 if (callback)
527 callback(CombinePath(gitdir, entry.m_FileName), *status, false, pData, false, false);
532 return 0;
534 #endif
536 #ifndef TGITCACHE
537 int GitStatus::GetDirStatus(const CString& gitdir, const CString& subpath, git_wc_status_kind* status, BOOL IsFul, BOOL IsRecursive, BOOL IsIgnore)
539 if (!status)
540 return 0;
542 CString path = subpath;
544 path.Replace(L'\\', L'/');
545 if (!path.IsEmpty() && path[path.GetLength() - 1] != L'/')
546 path += L'/'; //Add trail / to show it is directory, not file name.
548 g_IndexFileMap.CheckAndUpdate(gitdir, true);
550 SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir);
552 if (!indexptr)
554 *status = git_wc_status_unversioned;
555 return 0;
558 CString lowcasepath = path;
559 lowcasepath.MakeLower();
561 size_t pos = SearchInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength());
563 // Not In Version Contorl
564 if (pos == NPOS)
566 if (!IsIgnore)
568 *status = git_wc_status_unversioned;
569 return 0;
572 // Check ignore always.
573 if (g_IgnoreList.CheckIgnoreChanged(gitdir, path, true))
574 g_IgnoreList.LoadAllIgnoreFile(gitdir, path, true);
576 if (g_IgnoreList.IsIgnore(path, gitdir, true))
577 *status = git_wc_status_ignored;
578 else
579 *status = git_wc_status_unversioned;
581 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
583 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
584 // Check init repository
585 if (treeptr->HeadIsEmpty() && path.IsEmpty())
586 *status = git_wc_status_normal;
587 // check if only one file in repository is deleted in index
588 else if (path.IsEmpty() && !treeptr->empty())
589 *status = git_wc_status_deleted;
591 return 0;
594 // In version control
595 *status = git_wc_status_normal;
597 size_t start = 0;
598 size_t end = 0;
600 GetRangeInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength(), &start, &end, pos);
602 // Check Conflict;
603 for (auto it = indexptr->cbegin() + start, itlast = indexptr->cbegin() + end; indexptr->m_bHasConflicts && it <= itlast; ++it)
605 if (((*it).m_Flags & GIT_IDXENTRY_STAGEMASK) != 0)
607 *status = git_wc_status_conflicted;
608 break;
612 if (IsFul && (*status != git_wc_status_conflicted))
614 *status = git_wc_status_normal;
616 g_HeadFileMap.CheckHeadAndUpdate(gitdir);
618 // Check Add
620 // Check if new init repository
621 SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir);
623 if (!treeptr->empty() || treeptr->HeadIsEmpty())
625 for (auto it = indexptr->cbegin() + start, itlast = indexptr->cbegin() + end; it <= itlast; ++it)
627 pos = SearchInSortVector(*treeptr, (*it).m_FileName, -1);
629 if (pos == NPOS)
631 *status = max(git_wc_status_added, *status); // added file found
632 break;
635 if ((*treeptr)[pos].m_Hash != (*it).m_IndexHash)
637 *status = max(git_wc_status_modified, *status); // modified file found
638 break;
642 // Check Delete
643 if (*status == git_wc_status_normal)
645 pos = SearchInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength());
646 if (pos == NPOS)
647 *status = max(git_wc_status_added, *status); // added file found
648 else
650 size_t hstart, hend;
651 // we know that pos exists in treeptr
652 GetRangeInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength(), &hstart, &hend, pos);
653 for (auto hit = treeptr->cbegin() + hstart, lastElement = treeptr->cbegin() + hend; hit <= lastElement; ++hit)
655 if (SearchInSortVector(*indexptr, (*hit).m_FileName, -1) == NPOS)
657 *status = max(git_wc_status_deleted, *status); // deleted file found
658 break;
664 } /* End lock*/
667 // When status == git_wc_status_conflicted, needn't check each file status
668 // because git_wc_status_conflicted is highest.s
669 if (*status == git_wc_status_conflicted)
670 return 0;
672 for (auto it = indexptr->cbegin() + start, itlast = indexptr->cbegin() + end; it <= itlast; ++it)
674 //skip child directory
675 if (!IsRecursive && (*it).m_FileName.Find(L'/', path.GetLength()) > 0)
676 continue;
678 git_wc_status_kind filestatus = git_wc_status_none;
679 bool assumeValid = false;
680 bool skipWorktree = false;
681 GetFileStatus(gitdir, (*it).m_FileName, &filestatus, IsFul, IsRecursive, IsIgnore, nullptr, nullptr, &assumeValid, &skipWorktree);
682 switch (filestatus)
684 case git_wc_status_added:
685 case git_wc_status_modified:
686 case git_wc_status_deleted:
687 case git_wc_status_conflicted:
688 *status = GetMoreImportant(filestatus, *status);
692 return 0;
694 #endif
696 #ifdef TGITCACHE
697 bool GitStatus::IsExistIndexLockFile(CString sDirName)
699 if (!PathIsDirectory(sDirName))
701 int x = sDirName.ReverseFind(L'\\');
702 if (x < 2)
703 return false;
705 sDirName.Truncate(x);
708 for (;;)
710 if (PathFileExists(CombinePath(sDirName, L".git")))
712 if (PathFileExists(g_AdminDirMap.GetAdminDirConcat(sDirName, L"index.lock")))
713 return true;
715 return false;
718 int x = sDirName.ReverseFind(L'\\');
719 if (x < 2)
720 return false;
722 sDirName.Truncate(x);
725 #endif
727 bool GitStatus::ReleasePath(const CString &gitdir)
729 g_IndexFileMap.SafeClear(gitdir);
730 g_HeadFileMap.SafeClear(gitdir);
731 return true;
734 bool GitStatus::ReleasePathsRecursively(const CString &rootpath)
736 g_IndexFileMap.SafeClearRecursively(rootpath);
737 g_HeadFileMap.SafeClearRecursively(rootpath);
738 return true;