int GitStatus::GetDirStatus(const CString& gitdir, const CString& subpath, git_wc_status_kind* status, BOOL IsFul, BOOL IsRecursive, BOOL IsIgnore) { if (!status) return 0; CString path = subpath; path.Replace(_T('\\'), _T('/')); if (!path.IsEmpty() && path[path.GetLength() - 1] != _T('/')) path += _T('/'); //Add trail / to show it is directory, not file name. g_IndexFileMap.CheckAndUpdate(gitdir, true); SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir); if (!indexptr) { *status = git_wc_status_unversioned; return 0; } CString lowcasepath = path; lowcasepath.MakeLower(); int pos = SearchInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength()); // Not In Version Contorl if (pos < 0) { if (!IsIgnore) { *status = git_wc_status_unversioned; return 0; } // Check ignore always. if (g_IgnoreList.CheckIgnoreChanged(gitdir, path, true)) g_IgnoreList.LoadAllIgnoreFile(gitdir, path, true); if (g_IgnoreList.IsIgnore(path, gitdir, true)) *status = git_wc_status_ignored; else *status = git_wc_status_unversioned; g_HeadFileMap.CheckHeadAndUpdate(gitdir); SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir); // Check init repository if (treeptr->HeadIsEmpty() && path.IsEmpty()) *status = git_wc_status_normal; // check if only one file in repository is deleted in index else if (path.IsEmpty() && !treeptr->empty()) *status = git_wc_status_deleted; return 0; } // In version control *status = git_wc_status_normal; int start = 0; int end = 0; GetRangeInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength(), &start, &end, pos); // Check Conflict; for (auto it = indexptr->cbegin() + start, itlast = indexptr->cbegin() + end; indexptr->m_bHasConflicts && it <= itlast; ++it) { if (((*it).m_Flags & GIT_IDXENTRY_STAGEMASK) != 0) { *status = git_wc_status_conflicted; break; } } if (IsFul && (*status != git_wc_status_conflicted)) { *status = git_wc_status_normal; g_HeadFileMap.CheckHeadAndUpdate(gitdir); // Check Add { // Check if new init repository SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir); if (!treeptr->empty() || treeptr->HeadIsEmpty()) { for (auto it = indexptr->cbegin() + start, itlast = indexptr->cbegin() + end; it <= itlast; ++it) { pos = SearchInSortVector(*treeptr, (*it).m_FileName, -1); if (pos < 0) { *status = max(git_wc_status_added, *status); // added file found break; } if (pos >= 0 && treeptr->at(pos).m_Hash != (*it).m_IndexHash) { *status = max(git_wc_status_modified, *status); // modified file found break; } } // Check Delete if (*status == git_wc_status_normal) { pos = SearchInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength()); if (pos < 0) *status = max(git_wc_status_added, *status); // added file found else { int hstart, hend; GetRangeInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength(), &hstart, &hend, pos); for (auto hit = treeptr->cbegin() + hstart, lastElement = treeptr->cbegin() + hend; hit <= lastElement; ++hit) { if (SearchInSortVector(*indexptr, (*hit).m_FileName, -1) < 0) { *status = max(git_wc_status_deleted, *status); // deleted file found break; } } } } } } /* End lock*/ } // When status == git_wc_status_conflicted, needn't check each file status // because git_wc_status_conflicted is highest.s if (*status == git_wc_status_conflicted) return 0; for (auto it = indexptr->cbegin() + start, itlast = indexptr->cbegin() + end; it <= itlast; ++it) { //skip child directory if (!IsRecursive && (*it).m_FileName.Find(_T('/'), path.GetLength()) > 0) continue; git_wc_status_kind filestatus = git_wc_status_none; bool assumeValid = false; bool skipWorktree = false; GetFileStatus(gitdir, (*it).m_FileName, &filestatus, IsFul, IsRecursive, IsIgnore, nullptr, nullptr, &assumeValid, &skipWorktree); switch (filestatus) { case git_wc_status_added: case git_wc_status_modified: case git_wc_status_deleted: case git_wc_status_conflicted: *status = GetMoreImportant(filestatus, *status); } } return 0; }
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) { if (!status) return 0; CString path = subpath; path.Replace(_T('\\'), _T('/')); if (!path.IsEmpty() && path[path.GetLength() - 1] != _T('/')) path += _T('/'); // Add trail / to show it is directory, not file name. std::vector<CGitFileName> filelist; GetFileList(CombinePath(gitdir, subpath), filelist); g_IndexFileMap.CheckAndUpdate(gitdir,true); g_HeadFileMap.CheckHeadAndUpdate(gitdir); SHARED_INDEX_PTR indexptr = g_IndexFileMap.SafeGet(gitdir); SHARED_TREE_PTR treeptr = g_HeadFileMap.SafeGet(gitdir); // new git working tree has no index file if (!indexptr.get()) { for (auto it = filelist.cbegin(); it != filelist.cend(); ++it) { CString casepath = path; casepath += it->m_CaseFileName; bool bIsDir = false; if (!it->m_FileName.IsEmpty() && it->m_FileName[it->m_FileName.GetLength() - 1] == _T('/')) bIsDir = true; if (IsIgnore) { if (g_IgnoreList.CheckIgnoreChanged(gitdir, casepath, bIsDir)) g_IgnoreList.LoadAllIgnoreFile(gitdir, casepath, bIsDir); if (g_IgnoreList.IsIgnore(casepath, gitdir, bIsDir)) *status = git_wc_status_ignored; else if (bIsDir) continue; else *status = git_wc_status_unversioned; } else if (bIsDir) continue; else *status = git_wc_status_unversioned; if (callback) callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false); } return 0; } CString lowcasepath = path; lowcasepath.MakeLower(); for (auto it = filelist.cbegin(), itend = filelist.cend(); it != itend; ++it) { CString onepath(lowcasepath); onepath += it->m_FileName; CString casepath(path); casepath += it->m_CaseFileName; bool bIsDir = false; if (!onepath.IsEmpty() && onepath[onepath.GetLength() - 1] == _T('/')) bIsDir = true; int matchLength = -1; if (bIsDir) matchLength = onepath.GetLength(); int pos = SearchInSortVector(*indexptr, onepath, matchLength); int posintree = SearchInSortVector(*treeptr, onepath, matchLength); if (pos < 0 && posintree < 0) { if (onepath.IsEmpty()) continue; if (!IsIgnore) { *status = git_wc_status_unversioned; if (callback) callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false); continue; } if (g_IgnoreList.CheckIgnoreChanged(gitdir, casepath, bIsDir)) g_IgnoreList.LoadAllIgnoreFile(gitdir, casepath, bIsDir); if (g_IgnoreList.IsIgnore(casepath, gitdir, bIsDir)) *status = git_wc_status_ignored; else *status = git_wc_status_unversioned; if (callback) callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false); } else if (pos < 0 && posintree >= 0) /* check if file delete in index */ { *status = git_wc_status_deleted; if (callback) callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false); } else if (pos >= 0 && posintree < 0) /* Check if file added */ { *status = git_wc_status_added; if (indexptr->at(pos).m_Flags & GIT_IDXENTRY_STAGEMASK) *status = git_wc_status_conflicted; if (callback) callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false); } else { if (onepath.IsEmpty()) continue; if (bIsDir) { *status = git_wc_status_normal; if (callback) callback(CombinePath(gitdir, casepath), *status, bIsDir, pData, false, false); } else { bool assumeValid = false; bool skipWorktree = false; git_wc_status_kind filestatus; GetFileStatus(gitdir, casepath, &filestatus, IsFul, IsRecursive, IsIgnore, callback, pData, &assumeValid, &skipWorktree); } } }/*End of For*/ /* Check deleted file in system */ int start = 0, end = 0; int pos = SearchInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength()); // match path prefix, (sub)folders end with slash std::map<CString, bool> skipWorktreeMap; if (GetRangeInSortVector(*indexptr, lowcasepath, lowcasepath.GetLength(), &start, &end, pos) == 0) { CString oldstring; for (auto it = indexptr->cbegin() + start, itlast = indexptr->cbegin() + end; it <= itlast; ++it) { int commonPrefixLength = lowcasepath.GetLength(); int index = (*it).m_FileName.Find(_T('/'), commonPrefixLength); if (index < 0) index = (*it).m_FileName.GetLength(); else ++index; // include slash at the end for subfolders, so that we do not match files by mistake CString filename = (*it).m_FileName.Mid(commonPrefixLength, index - commonPrefixLength); if (oldstring != filename) { oldstring = filename; if (SearchInSortVector(filelist, filename, filename.GetLength()) < 0) { bool skipWorktree = false; *status = git_wc_status_deleted; if (((*it).m_Flags & GIT_IDXENTRY_SKIP_WORKTREE) != 0) { skipWorktreeMap[filename] = true; skipWorktree = true; *status = git_wc_status_normal; } if (callback) callback(CombinePath(gitdir, (*it).m_FileName), *status, false, pData, false, skipWorktree); } } } } start = end = 0; pos = SearchInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength()); // match path prefix, (sub)folders end with slash if (GetRangeInSortVector(*treeptr, lowcasepath, lowcasepath.GetLength(), &start, &end, pos) == 0) { CString oldstring; for (auto it = treeptr->cbegin() + start, itlast = treeptr->cbegin() + end; it <= itlast; ++it) { int commonPrefixLength = lowcasepath.GetLength(); int index = (*it).m_FileName.Find(_T('/'), commonPrefixLength); if (index < 0) index = (*it).m_FileName.GetLength(); else ++index; // include slash at the end for subfolders, so that we do not match files by mistake CString filename = (*it).m_FileName.Mid(commonPrefixLength, index - commonPrefixLength); if (oldstring != filename && skipWorktreeMap[filename] != true) { oldstring = filename; if (SearchInSortVector(filelist, filename, filename.GetLength()) < 0) { *status = git_wc_status_deleted; if (callback) callback(CombinePath(gitdir, (*it).m_FileName), *status, false, pData, false, false); } } } } return 0; }