OSDN Git Service

initial TGitCache support added
[tortoisegit/TortoiseGitJp.git] / src / TGitCache / CachedDirectory.cpp
index b77f0ad..3edd90b 100644 (file)
@@ -25,8 +25,8 @@
 \r
 CCachedDirectory::CCachedDirectory(void)\r
 {\r
-       m_entriesFileTime = 0;\r
-       m_propsFileTime = 0;\r
+       m_indexFileTime = 0;\r
+//     m_propsFileTime = 0;\r
        m_currentStatusFetchingPathTicks = 0;\r
        m_bCurrentFullStatusValid = false;\r
        m_currentFullStatus = m_mostImportantFileStatus = git_wc_status_none;\r
@@ -42,8 +42,8 @@ CCachedDirectory::CCachedDirectory(const CTGitPath& directoryPath)
        ATLASSERT(directoryPath.IsDirectory() || !PathFileExists(directoryPath.GetWinPath()));\r
 \r
        m_directoryPath = directoryPath;\r
-       m_entriesFileTime = 0;\r
-       m_propsFileTime = 0;\r
+       m_indexFileTime = 0;\r
+//     m_propsFileTime = 0;\r
        m_currentStatusFetchingPathTicks = 0;\r
        m_bCurrentFullStatusValid = false;\r
        m_currentFullStatus = m_mostImportantFileStatus = git_wc_status_none;\r
@@ -88,8 +88,8 @@ BOOL CCachedDirectory::SaveToDisk(FILE * pFile)
                        WRITEVALUETOFILE(status);\r
                }\r
        }\r
-       WRITEVALUETOFILE(m_entriesFileTime);\r
-       WRITEVALUETOFILE(m_propsFileTime);\r
+       WRITEVALUETOFILE(m_indexFileTime);\r
+//     WRITEVALUETOFILE(m_propsFileTime);\r
        value = m_directoryPath.GetWinPathString().GetLength();\r
        WRITEVALUETOFILE(value);\r
        if (value)\r
@@ -156,8 +156,8 @@ BOOL CCachedDirectory::LoadFromDisk(FILE * pFile)
                                m_childDirectories[CTGitPath(sPath)] = status;\r
                        }\r
                }\r
-               LOADVALUEFROMFILE(m_entriesFileTime);\r
-               LOADVALUEFROMFILE(m_propsFileTime);\r
+               LOADVALUEFROMFILE(m_indexFileTime);\r
+//             LOADVALUEFROMFILE(m_propsFileTime);\r
                LOADVALUEFROMFILE(value);\r
                if (value > MAX_PATH)\r
                        return false;\r
@@ -195,43 +195,48 @@ CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bo
        {\r
                bRequestForSelf = true;\r
        }\r
-\r
+//OutputDebugStringA("GetStatusForMember: ");OutputDebugStringW(path.GetWinPathString());OutputDebugStringA("\r\n");\r
        // In all most circumstances, we ask for the status of a member of this directory.\r
        ATLASSERT(m_directoryPath.IsEquivalentToWithoutCase(path.GetContainingDirectory()) || bRequestForSelf);\r
 \r
-       // Check if the entries file has been changed\r
-       CTGitPath entriesFilePath(m_directoryPath);\r
-       CTGitPath propsDirPath(m_directoryPath);\r
+       CString sProjectRoot;\r
+       const BOOL bIsVersionedPath = m_directoryPath.HasAdminDir(&sProjectRoot);\r
+\r
+       // Check if the index file has been changed\r
+       CTGitPath indexFilePath(bIsVersionedPath ? sProjectRoot : m_directoryPath);\r
+//     CTGitPath propsDirPath(m_directoryPath);\r
        if (g_GitAdminDir.IsVSNETHackActive())\r
        {\r
-               entriesFilePath.AppendPathString(g_GitAdminDir.GetVSNETAdminDirName() + _T("\\entries"));\r
-               propsDirPath.AppendPathString(g_GitAdminDir.GetVSNETAdminDirName() + _T("\\dir-props"));\r
+               indexFilePath.AppendPathString(g_GitAdminDir.GetVSNETAdminDirName() + _T("\\index"));\r
+//             propsDirPath.AppendPathString(g_GitAdminDir.GetVSNETAdminDirName() + _T("\\dir-props"));\r
        }\r
        else\r
        {\r
-               entriesFilePath.AppendPathString(g_GitAdminDir.GetAdminDirName() + _T("\\entries"));\r
-               propsDirPath.AppendPathString(g_GitAdminDir.GetAdminDirName() + _T("\\dir-props"));\r
+               indexFilePath.AppendPathString(g_GitAdminDir.GetAdminDirName() + _T("\\index"));\r
+//             propsDirPath.AppendPathString(g_GitAdminDir.GetAdminDirName() + _T("\\dir-props"));\r
        }\r
-       if ( (m_entriesFileTime == entriesFilePath.GetLastWriteTime()) && ((entriesFilePath.GetLastWriteTime() == 0) || (m_propsFileTime == propsDirPath.GetLastWriteTime())) )\r
+       if ( (m_indexFileTime == indexFilePath.GetLastWriteTime()) /*&& ((indexFilePath.GetLastWriteTime() == 0) || (m_propsFileTime == propsDirPath.GetLastWriteTime()))*/ )\r
        {\r
-               m_entriesFileTime = entriesFilePath.GetLastWriteTime();\r
-               if (m_entriesFileTime)\r
-                       m_propsFileTime = propsDirPath.GetLastWriteTime();\r
+//             m_indexFileTime = indexFilePath.GetLastWriteTime();\r
+//             if (m_indexFileTime)\r
+//                     m_propsFileTime = propsDirPath.GetLastWriteTime();\r
 \r
-               if(m_entriesFileTime == 0)\r
+               //if(m_indexFileTime == 0)\r
+               // a newly created project (without commits) has no index file but we still want it to count as versioned\r
+               if(m_indexFileTime == 0 && !bIsVersionedPath)\r
                {\r
                        // We are a folder which is not in a working copy\r
                        bThisDirectoryIsUnversioned = true;\r
                        m_ownStatus.SetStatus(NULL);\r
 \r
-                       // If a user removes the .svn directory, we get here with m_entryCache\r
+                       // If a user removes the .git directory, we get here with m_entryCache\r
                        // not being empty, but still us being unversioned\r
                        if (!m_entryCache.empty())\r
                        {\r
                                m_entryCache.clear();\r
                        }\r
                        ATLASSERT(m_entryCache.empty());\r
-                       \r
+\r
                        // However, a member *DIRECTORY* might be the top of WC\r
                        // so we need to ask them to get their own status\r
                        if(!path.IsDirectory())\r
@@ -245,6 +250,7 @@ CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bo
                                // So mark it for crawling, and let the crawler remove it\r
                                // later\r
                                CGitStatusCache::Instance().AddFolderForCrawling(path.GetContainingDirectory());\r
+\r
                                return CStatusCacheEntry();\r
                        }\r
                        else\r
@@ -252,9 +258,7 @@ CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bo
                                // If we're in the special case of a directory being asked for its own status\r
                                // and this directory is unversioned, then we should just return that here\r
                                if(bRequestForSelf)\r
-                               {\r
                                        return CStatusCacheEntry();\r
-                               }\r
                        }\r
                }\r
 \r
@@ -276,7 +280,6 @@ CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bo
                                                CGitStatusCache::Instance().AddFolderForCrawling(it->first);\r
                                        }\r
                                }\r
-\r
                                return dirEntry->GetOwnStatus(bRecursive);\r
                        }\r
                }\r
@@ -341,14 +344,14 @@ CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bo
                // we already have (to save time and make the explorer\r
                // more responsive in stress conditions).\r
                // We leave the refreshing to the crawler.\r
-               if ((!bFetch)&&(m_entriesFileTime))\r
+               if ((!bFetch)&&(m_indexFileTime))\r
                {\r
                        CGitStatusCache::Instance().AddFolderForCrawling(path.GetDirectory());\r
                        return CStatusCacheEntry();\r
                }\r
                AutoLocker lock(m_critSec);\r
-               m_entriesFileTime = entriesFilePath.GetLastWriteTime();\r
-               m_propsFileTime = propsDirPath.GetLastWriteTime();\r
+               m_indexFileTime = indexFilePath.GetLastWriteTime();\r
+//             m_propsFileTime = propsDirPath.GetLastWriteTime();\r
                m_entryCache.clear();\r
                strCacheKey = GetCacheKey(path);\r
        }\r
@@ -361,7 +364,7 @@ CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bo
 \r
        if (g_GitAdminDir.IsAdminDirPath(path.GetWinPathString()))\r
        {\r
-               // We're being asked for the status of an .SVN directory\r
+               // We're being asked for the status of an .git directory\r
                // It's not worth asking for this\r
                return CStatusCacheEntry();\r
        }\r
@@ -395,9 +398,23 @@ CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bo
                                m_currentStatusFetchingPath = m_directoryPath;\r
                                m_currentStatusFetchingPathTicks = GetTickCount();\r
                        }\r
-                       ATLTRACE(_T("svn_cli_stat for '%s' (req %s)\n"), m_directoryPath.GetWinPath(), path.GetWinPath());\r
+                       ATLTRACE(_T("git_enum_files for '%s' (req %s)\n"), m_directoryPath.GetWinPath(), path.GetWinPath());\r
 \r
-                       BOOL pErr = 0;//!wgEnumFiles(CStringA(sProjectRoot), lpszSubPath, WGEFF_NoRecurse|WGEFF_FullPath|WGEFF_DirStatusAll, &fillstatusmap, this);\r
+                       CString sProjectRoot;\r
+                       m_directoryPath.HasAdminDir(&sProjectRoot);\r
+                       ATLASSERT( !m_directoryPath.IsEmpty() );\r
+\r
+                       LPCSTR lpszSubPath = NULL;\r
+                       CStringA sSubPath;\r
+                       CString s = m_directoryPath.GetDirectory().GetWinPathString();\r
+                       if (s.GetLength() > sProjectRoot.GetLength())\r
+                       {\r
+                               sSubPath = CStringA(s.Right(s.GetLength() - sProjectRoot.GetLength() - 1/*otherwise it gets initial slash*/));\r
+                               lpszSubPath = sSubPath;\r
+                       }\r
+//MessageBoxA(NULL, CStringA(sProjectRoot), sSubPath, MB_OK);\r
+//OutputDebugStringA("###");OutputDebugStringW(sProjectRoot);OutputDebugStringA(" - ");OutputDebugStringA(sSubPath);OutputDebugStringA("\r\n");\r
+                       BOOL pErr = !wgEnumFiles(CStringA(sProjectRoot), lpszSubPath, WGEFF_NoRecurse|WGEFF_FullPath, &GetStatusCallback, this);\r
 \r
                        /*git_error_t* pErr = svn_client_status4 (\r
                                NULL,\r
@@ -418,7 +435,7 @@ CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bo
                                AutoLocker pathlock(m_critSecPath);\r
                                m_currentStatusFetchingPath.Reset();\r
                        }\r
-                       ATLTRACE(_T("svn_cli_stat finished for '%s'\n"), m_directoryPath.GetWinPath(), path.GetWinPath());\r
+                       ATLTRACE(_T("git_enum_files finished for '%s'\n"), m_directoryPath.GetWinPath(), path.GetWinPath());\r
                        if(pErr)\r
                        {\r
                                // Handle an error\r
@@ -428,7 +445,7 @@ CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bo
                                // If we allow ourselves to fall on through, then folders will be asked\r
                                // for their own status, and will set themselves as unversioned, for the \r
                                // benefit of future requests\r
-//                             ATLTRACE("svn_cli_stat err: '%s'\n", pErr->message);\r
+//                             ATLTRACE("git_enum_files err: '%s'\n", pErr->message);\r
 //                             svn_error_clear(pErr);\r
                                // No assert here! Since we _can_ get here, an assertion is not an option!\r
                                // Reasons to get here: \r
@@ -442,7 +459,7 @@ CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bo
                                }\r
                                else\r
                                {\r
-                                       ATLTRACE("svn_cli_stat error, assume none status\n");\r
+                                       ATLTRACE("git_enum_files error, assume none status\n");\r
                                        // Since we only assume a none status here due to svn_client_status()\r
                                        // returning an error, make sure that this status times out soon.\r
                                        CGitStatusCache::Instance().m_folderCrawler.BlockPath(m_directoryPath, 2000);\r
@@ -453,7 +470,7 @@ CStatusCacheEntry CCachedDirectory::GetStatusForMember(const CTGitPath& path, bo
                }\r
                else\r
                {\r
-                       ATLTRACE("Skipped SVN status for unversioned folder\n");\r
+                       ATLTRACE("Skipped git status for unversioned folder\n");\r
                }\r
        }\r
        // Now that we've refreshed our SVN status, we can see if it's \r
@@ -501,7 +518,7 @@ CCachedDirectory::AddEntry(const CTGitPath& path, const git_wc_status2_t* pGitSt
                {\r
                        if ((childDir->GetCurrentFullStatus() != git_wc_status_missing)||(pGitStatus==NULL)||(pGitStatus->text_status != git_wc_status_unversioned))\r
                                childDir->m_ownStatus.SetStatus(pGitStatus);\r
-//                     childDir->m_ownStatus.SetKind(svn_node_dir);\r
+                       childDir->m_ownStatus.SetKind(git_node_dir);\r
                }\r
        }\r
        else\r
@@ -525,6 +542,8 @@ CCachedDirectory::AddEntry(const CTGitPath& path, const git_wc_status2_t* pGitSt
                        entry_it = m_entryCache.insert(entry_it, std::make_pair(cachekey, CStatusCacheEntry()));\r
                }\r
                entry_it->second = CStatusCacheEntry(pGitStatus, path.GetLastWriteTime(), path.IsReadOnly(), validuntil);\r
+               // TEMP(?): git status doesn't not have "entry" that contains node type, so manually set as file\r
+               entry_it->second.SetKind(git_node_file);\r
        }\r
 }\r
 \r
@@ -543,6 +562,147 @@ CCachedDirectory::GetFullPathString(const CString& cacheKey)
        return m_directoryPath.GetWinPathString() + _T("\\") + cacheKey;\r
 }\r
 \r
+BOOL CCachedDirectory::GetStatusCallback(const struct wgFile_s *pFile, void *pUserData)\r
+{\r
+       CCachedDirectory* pThis = (CCachedDirectory*)pUserData;\r
+\r
+       const char *path = pFile->sFileName;\r
+\r
+       if (path == NULL)\r
+               return FALSE;\r
+\r
+       git_wc_status2_t _status;\r
+       git_wc_status2_t *status = &_status;\r
+\r
+       if ((pFile->nFlags & WGFF_Directory) && pFile->nStatus == WGFS_Unknown)\r
+               status->prop_status = status->text_status = git_wc_status_incomplete;\r
+       else\r
+               status->prop_status = status->text_status = GitStatusFromWingit(pFile->nStatus);\r
+//if (pFile->nStatus > WGFS_Normal) {CStringA s; s.Format("==>%s %d\r\n",pFile->sFileName,pFile->nStatus); OutputDebugStringA(s);}\r
+       CTGitPath svnPath;\r
+\r
+//     if(status->entry)\r
+       {\r
+               //if ((status->text_status != git_wc_status_none)&&(status->text_status != git_wc_status_missing))\r
+                       svnPath.SetFromGit(path, pFile->nFlags & WGFF_Directory);\r
+               /*else\r
+                       svnPath.SetFromGit(path);*/\r
+\r
+               if (pFile->nFlags & WGFF_Directory)\r
+               {\r
+                       if ( !svnPath.IsEquivalentToWithoutCase(pThis->m_directoryPath) )\r
+                       {\r
+                               if (pThis->m_bRecursive)\r
+                               {\r
+                                       // Add any versioned directory, which is not our 'self' entry, to the list for having its status updated\r
+//OutputDebugStringA("AddFolderCrawl: ");OutputDebugStringW(svnPath.GetWinPathString());OutputDebugStringA("\r\n");\r
+                                       CGitStatusCache::Instance().AddFolderForCrawling(svnPath);\r
+                               }\r
+\r
+                               // Make sure we know about this child directory\r
+                               // This initial status value is likely to be overwritten from below at some point\r
+                               git_wc_status_kind s = GitStatus::GetMoreImportant(status->text_status, status->prop_status);\r
+                               CCachedDirectory * cdir = CGitStatusCache::Instance().GetDirectoryCacheEntryNoCreate(svnPath);\r
+                               if (cdir)\r
+                               {\r
+                                       // This child directory is already in our cache!\r
+                                       // So ask this dir about its recursive status\r
+                                       git_wc_status_kind st = GitStatus::GetMoreImportant(s, cdir->GetCurrentFullStatus());\r
+                                       AutoLocker lock(pThis->m_critSec);\r
+                                       pThis->m_childDirectories[svnPath] = st;\r
+                               }\r
+                               else\r
+                               {\r
+                                       // the child directory is not in the cache. Create a new entry for it in the cache which is\r
+                                       // initially 'unversioned'. But we added that directory to the crawling list above, which\r
+                                       // means the cache will be updated soon.\r
+                                       CGitStatusCache::Instance().GetDirectoryCacheEntry(svnPath);\r
+                                       AutoLocker lock(pThis->m_critSec);\r
+                                       pThis->m_childDirectories[svnPath] = s;\r
+                               }\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       // Keep track of the most important status of all the files in this directory\r
+                       // Don't include subdirectories in this figure, because they need to provide their \r
+                       // own 'most important' value\r
+                       pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, status->text_status);\r
+                       pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, status->prop_status);\r
+                       if (((status->text_status == git_wc_status_unversioned)||(status->text_status == git_wc_status_none))\r
+                               &&(CGitStatusCache::Instance().IsUnversionedAsModified()))\r
+                       {\r
+                               // treat unversioned files as modified\r
+                               if (pThis->m_mostImportantFileStatus != git_wc_status_added)\r
+                                       pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, git_wc_status_modified);\r
+                       }\r
+               }\r
+       }\r
+#if 0\r
+       else\r
+       {\r
+               svnPath.SetFromGit(path);\r
+               // Subversion returns no 'entry' field for versioned folders if they're\r
+               // part of another working copy (nested layouts).\r
+               // So we have to make sure that such an 'unversioned' folder really\r
+               // is unversioned.\r
+               if (((status->text_status == git_wc_status_unversioned)||(status->text_status == git_wc_status_missing))&&(!svnPath.IsEquivalentToWithoutCase(pThis->m_directoryPath))&&(svnPath.IsDirectory()))\r
+               {\r
+                       if (svnPath.HasAdminDir())\r
+                       {\r
+                               CGitStatusCache::Instance().AddFolderForCrawling(svnPath);\r
+                               // Mark the directory as 'versioned' (status 'normal' for now).\r
+                               // This initial value will be overwritten from below some time later\r
+                               {\r
+                                       AutoLocker lock(pThis->m_critSec);\r
+                                       pThis->m_childDirectories[svnPath] = git_wc_status_normal;\r
+                               }\r
+                               // Make sure the entry is also in the cache\r
+                               CGitStatusCache::Instance().GetDirectoryCacheEntry(svnPath);\r
+                               // also mark the status in the status object as normal\r
+                               status->text_status = git_wc_status_normal;\r
+                       }\r
+               }\r
+               else if (status->text_status == git_wc_status_external)\r
+               {\r
+                       CGitStatusCache::Instance().AddFolderForCrawling(svnPath);\r
+                       // Mark the directory as 'versioned' (status 'normal' for now).\r
+                       // This initial value will be overwritten from below some time later\r
+                       {\r
+                               AutoLocker lock(pThis->m_critSec);\r
+                               pThis->m_childDirectories[svnPath] = git_wc_status_normal;\r
+                       }\r
+                       // we have added a directory to the child-directory list of this\r
+                       // directory. We now must make sure that this directory also has\r
+                       // an entry in the cache.\r
+                       CGitStatusCache::Instance().GetDirectoryCacheEntry(svnPath);\r
+                       // also mark the status in the status object as normal\r
+                       status->text_status = git_wc_status_normal;\r
+               }\r
+               else\r
+               {\r
+                       if (svnPath.IsDirectory())\r
+                       {\r
+                               AutoLocker lock(pThis->m_critSec);\r
+                               pThis->m_childDirectories[svnPath] = GitStatus::GetMoreImportant(status->text_status, status->prop_status);\r
+                       }\r
+                       else if ((CGitStatusCache::Instance().IsUnversionedAsModified())&&(status->text_status != git_wc_status_missing))\r
+                       {\r
+                               // make this unversioned item change the most important status of this\r
+                               // folder to modified if it doesn't already have another status\r
+                               if (pThis->m_mostImportantFileStatus != git_wc_status_added)\r
+                                       pThis->m_mostImportantFileStatus = GitStatus::GetMoreImportant(pThis->m_mostImportantFileStatus, git_wc_status_modified);\r
+                       }\r
+               }\r
+       }\r
+#endif\r
+\r
+       pThis->AddEntry(svnPath, status);\r
+\r
+       return FALSE;\r
+}\r
+\r
+#if 0\r
 git_error_t * CCachedDirectory::GetStatusCallback(void *baton, const char *path, git_wc_status2_t *status)\r
 {\r
        CCachedDirectory* pThis = (CCachedDirectory*)baton;\r
@@ -552,7 +712,6 @@ git_error_t * CCachedDirectory::GetStatusCallback(void *baton, const char *path,
                \r
        CTGitPath svnPath;\r
 \r
-#if 0\r
        if(status->entry)\r
        {\r
                if ((status->text_status != git_wc_status_none)&&(status->text_status != git_wc_status_missing))\r
@@ -665,11 +824,12 @@ git_error_t * CCachedDirectory::GetStatusCallback(void *baton, const char *path,
                        }\r
                }\r
        }\r
-#endif\r
+\r
        pThis->AddEntry(svnPath, status);\r
 \r
        return 0;\r
 }\r
+#endif\r
 \r
 bool \r
 CCachedDirectory::IsOwnStatusValid() const\r