OSDN Git Service

added some GetStatus functionality in GitStatus changed so empty (or only containing...
authorMyagi <snowcoder@gmail.com>
Mon, 26 Jan 2009 02:39:22 +0000 (03:39 +0100)
committerFrank Li <lznuaa@gmail.com>
Wed, 28 Jan 2009 03:14:34 +0000 (11:14 +0800)
ext/wingit/wingit.dll
ext/wingit/wingit.h
ext/wingit/wingit.lib
src/Git/Git.cpp
src/Git/GitFolderStatus.cpp
src/Git/GitStatus.cpp
src/Git/GitStatus.h
src/TortoiseShell/IconOverlay.cpp

index f70a7a9..81681c6 100644 (file)
Binary files a/ext/wingit/wingit.dll and b/ext/wingit/wingit.dll differ
index 346f5bb..4693207 100644 (file)
@@ -13,7 +13,7 @@
 #ifndef _WINGIT_H_\r
 #define _WINGIT_H_\r
 \r
-#define WG_VERSION "0.1.3"\r
+#define WG_VERSION "0.1.6"\r
 \r
 \r
 #define DLLIMPORT __declspec(dllimport) __stdcall\r
@@ -35,10 +35,21 @@ enum WGENUMFILEFLAGS
 {\r
        WGEFF_NoRecurse         = (1<<0),       // only enumerate files directly in the specified path\r
        WGEFF_FullPath          = (1<<1),       // enumerated filenames are specified with full path (instead of relative to proj root)\r
-       WGEFF_DirStatusDelta= (1<<2),   // include directories, in enumeration, that have a recursive status != WGFS_Normal\r
-       WGEFF_DirStatusAll      = (1<<3)        // include directories, in enumeration, with recursive status\r
+       WGEFF_DirStatusDelta= (1<<2),   // include directories, in enumeration, that have a recursive status != WGFS_Normal (may have a slightly better performance than WGEFF_DirStatusAll)\r
+       WGEFF_DirStatusAll      = (1<<3),       // include directories, in enumeration, with recursive status\r
+       WGEFF_EmptyAsNormal     = (1<<4),       // report sub-directories, with no versioned files, as WGFS_Normal instead of WGFS_Empty\r
+       WGEFF_SingleFile        = (1<<5)        // indicates that the status of a single file or dir, specified by pszSubPath, is wanted\r
 };\r
 \r
+// NOTE: Special behavior for directories when specifying WGEFF_SingleFile:\r
+//\r
+//       * when combined with WGEFF_SingleFile the returned status will only reflect the immediate files in the dir,\r
+//         NOT the recusrive status of immediate sub-dirs\r
+//       * unlike a normal enumeration where the project root dir always is returned as WGFS_Normal regardless\r
+//         of WGEFF_EmptyAsNormal, the project root will return WGFS_Empty if no immediate versioned files\r
+//         unless WGEFF_EmptyAsNormal is specified\r
+//       * WGEFF_DirStatusDelta and WGEFF_DirStatusAll are ignored and can be omitted even for dirs\r
+\r
 \r
 // File status\r
 enum WGFILESTATUS\r
@@ -47,7 +58,8 @@ enum WGFILESTATUS
        WGFS_Modified,\r
        WGFS_Deleted,\r
 \r
-       WGFS_Unknown = -1\r
+       WGFS_Unknown = -1,\r
+       WGFS_Empty = -2\r
 };\r
 \r
 \r
@@ -64,10 +76,13 @@ struct wgFile_s
        int nStatus;                                    // the WGFILESTATUS of the file\r
        int nStage;                                             // the stage number of the file (0 if unstaged)\r
        int nFlags;                                             // a combination of WGFILEFLAGS\r
+\r
+       const BYTE* sha1;                               // points to the BYTE[20] sha1 (NULL for directories, WGFF_Directory)\r
 };\r
 \r
 \r
 // Application-defined callback function for wgEnumFiles, returns TRUE to abort enumeration\r
+// NOTE: do NOT store the pFile pointer or any pointers in wgFile_s for later use, the data is only valid for a single callback call\r
 typedef BOOL (__cdecl WGENUMFILECB)(const struct wgFile_s *pFile, void *pUserData);\r
 \r
 \r
@@ -84,6 +99,10 @@ LPCSTR WINGIT_API wgGetGitVersion(void);
 // Ex: wgEnumFiles("C:\\Projects\\MyProject", "src/core", WGEFF_NoRecurse, MyEnumFunc, NULL)\r
 BOOL WINGIT_API wgEnumFiles(const char *pszProjectPath, const char *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData);\r
 \r
+// Get the SHA1 of pszName (NULL is same as "HEAD", "HEAD^" etc. expression allowed), returns NULL on failure\r
+// NOTE: do not store returned pointer for later used, data must be used/copied right away before further wg calls\r
+LPBYTE WINGIT_API wgGetRevisionID(const char *pszProjectPath, const char *pszName);\r
+\r
 \r
 #ifdef __cplusplus\r
 }\r
index e51b2fa..fdb1784 100644 (file)
Binary files a/ext/wingit/wingit.lib and b/ext/wingit/wingit.lib differ
index 0e9d1e3..10cb7fb 100644 (file)
@@ -389,6 +389,8 @@ int CGit::RunLogFile(CString cmd,CString &filename)
 \r
 git_revnum_t CGit::GetHash(CString &friendname)\r
 {\r
+       // NOTE: could replace this with wgGetRevisionID call\r
+\r
        CString cmd;\r
        CString out;\r
        cmd.Format(_T("git.exe rev-parse %s" ),friendname);\r
index c73ca54..df2d45f 100644 (file)
@@ -241,7 +241,7 @@ const FileStatusCacheEntry * GitFolderStatus::BuildCache(const CTGitPath& filepa
 \r
 //if (lpszSubPath) MessageBoxA(NULL, lpszSubPath, "BuildCache", MB_OK);\r
 //MessageBoxA(NULL, CStringA(sProjectRoot), sSubPath, MB_OK);\r
-               err = !wgEnumFiles(CStringA(sProjectRoot), lpszSubPath, WGEFF_NoRecurse|WGEFF_FullPath|WGEFF_DirStatusDelta, &fillstatusmap, this);\r
+               err = !wgEnumFiles(CStringA(sProjectRoot), lpszSubPath, WGEFF_NoRecurse|WGEFF_FullPath|WGEFF_DirStatusAll, &fillstatusmap, this);\r
 \r
                /*err = svn_client_status4 (&youngest,\r
                        filepath.GetDirectory().GetSVNApiPath(pool),\r
index 0d47c48..67ddc28 100644 (file)
@@ -212,12 +212,7 @@ git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t de
 //     rev.kind = git_opt_revision_unspecified;\r
        statuskind = git_wc_status_none;\r
 \r
-       // TODO: not sure what to do with recursivenes, it's very unclear exactly what svn does, wingit will however return\r
-       //       the correct (recursive) status for folders, so WGEFF_NoRecurse can be specified to avoid unecessary processing\r
-       //const BOOL bIsRecursive = (depth == git_depth_infinity || depth == git_depth_unknown); // taken from SVN source\r
-       UINT nFlags = WGEFF_DirStatusAll;\r
-       //if (!bIsRecursive)\r
-               nFlags |= WGEFF_NoRecurse;\r
+       const BOOL bIsRecursive = (depth == git_depth_infinity || depth == git_depth_unknown); // taken from SVN source\r
 \r
        LPCSTR lpszSubPath = NULL;\r
        CStringA sSubPath;\r
@@ -228,6 +223,21 @@ git_wc_status_kind GitStatus::GetAllStatus(const CTGitPath& path, git_depth_t de
                lpszSubPath = sSubPath;\r
        }\r
 \r
+#if 1\r
+       // when recursion enabled, let wingit determine the recursive status for folders instead of enumerating all files here\r
+       UINT nFlags = WGEFF_SingleFile;\r
+       if (!bIsRecursive)\r
+               nFlags |= WGEFF_NoRecurse;\r
+       if (!lpszSubPath)\r
+               // report root dir as normal (otherwise it could be considered git_wc_status_unversioned, which would be wrong?)\r
+               nFlags |= WGEFF_EmptyAsNormal;\r
+#else\r
+       // enumerate all files, recursively if requested\r
+       UINT nFlags = 0;\r
+       if (!bIsRecursive)\r
+               nFlags |= WGEFF_NoRecurse;\r
+#endif\r
+\r
        err = !wgEnumFiles(CStringA(sProjectRoot), lpszSubPath, nFlags, &getallstatus, &statuskind);\r
 \r
        /*err = git_client_status4 (&youngest,\r
@@ -308,23 +318,51 @@ int GitStatus::GetStatusRanking(git_wc_status_kind status)
 \r
 git_revnum_t GitStatus::GetStatus(const CTGitPath& path, bool update /* = false */, bool noignore /* = false */, bool noexternals /* = false */)\r
 {\r
-#if 0\r
-       apr_hash_t *                            statushash;\r
-       apr_hash_t *                            exthash;\r
-       apr_array_header_t *            statusarray;\r
-       const sort_item*                        item;\r
+       // NOTE: unlike the SVN version this one does not cache the enumerated files, because in practice no code in all of\r
+       //       Tortoise uses this, all places that call GetStatus create a temp GitStatus object which gets destroyed right\r
+       //       after the call again\r
+\r
+//     apr_hash_t *                            statushash;\r
+//     apr_hash_t *                            exthash;\r
+//     apr_array_header_t *            statusarray;\r
+//     const sort_item*                        item;\r
        \r
-       git_error_clear(m_err);\r
-       statushash = apr_hash_make(m_pool);\r
-       exthash = apr_hash_make(m_pool);\r
-       git_revnum_t youngest = Git_INVALID_REVNUM;\r
-       git_opt_revision_t rev;\r
-       rev.kind = git_opt_revision_unspecified;\r
+//     git_error_clear(m_err);\r
+//     statushash = apr_hash_make(m_pool);\r
+//     exthash = apr_hash_make(m_pool);\r
+       git_revnum_t youngest = GIT_INVALID_REVNUM;\r
+//     git_opt_revision_t rev;\r
+//     rev.kind = git_opt_revision_unspecified;\r
+\r
+       CString sProjectRoot;\r
+       if ( !path.HasAdminDir(&sProjectRoot) )\r
+               return youngest;\r
+\r
        struct hashbaton_t hashbaton;\r
-       hashbaton.hash = statushash;\r
-       hashbaton.exthash = exthash;\r
+//     hashbaton.hash = statushash;\r
+//     hashbaton.exthash = exthash;\r
        hashbaton.pThis = this;\r
-       m_err = git_client_status4 (&youngest,\r
+\r
+       LPCSTR lpszSubPath = NULL;\r
+       CStringA sSubPath;\r
+       CString s = path.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
+\r
+       // when recursion enabled, let wingit determine the recursive status for folders instead of enumerating all files here\r
+       UINT nFlags = WGEFF_SingleFile | WGEFF_NoRecurse;\r
+       if (!lpszSubPath)\r
+               // report root dir as normal (otherwise it could be considered git_wc_status_unversioned, which would be wrong?)\r
+               nFlags |= WGEFF_EmptyAsNormal;\r
+\r
+       m_status.prop_status = m_status.text_status = git_wc_status_none;\r
+\r
+       m_err = !wgEnumFiles(CStringA(sProjectRoot), lpszSubPath, nFlags, &getstatus, &m_status);\r
+\r
+       /*m_err = git_client_status4 (&youngest,\r
                                                        path.GetGitApiPath(m_pool),\r
                                                        &rev,\r
                                                        getstatushash,\r
@@ -336,29 +374,36 @@ git_revnum_t GitStatus::GetStatus(const CTGitPath& path, bool update /* = false
                                                        noexternals,\r
                                                        NULL,\r
                                                        ctx,\r
-                                                       m_pool);\r
+                                                       m_pool);*/\r
 \r
 \r
        // Error present if function is not under version control\r
-       if ((m_err != NULL) || (apr_hash_count(statushash) == 0))\r
+       if ((m_err != NULL) || /*(apr_hash_count(statushash) == 0)*/m_status.prop_status == git_wc_status_none)\r
        {\r
                status = NULL;\r
-               return -2;      \r
+//             return -2;      \r
+               return GIT_INVALID_REVNUM;\r
        }\r
 \r
        // Convert the unordered hash to an ordered, sorted array\r
-       statusarray = sort_hash (statushash,\r
+       /*statusarray = sort_hash (statushash,\r
                                                          sort_compare_items_as_paths,\r
-                                                         m_pool);\r
+                                                         m_pool);*/\r
 \r
        // only the first entry is needed (no recurse)\r
-       item = &APR_ARRAY_IDX (statusarray, 0, const sort_item);\r
-       \r
-       status = (git_wc_status2_t *) item->value;\r
+//     item = &APR_ARRAY_IDX (statusarray, 0, const sort_item);\r
        \r
+//     status = (git_wc_status2_t *) item->value;\r
+       status = &m_status;\r
+\r
+       if (update)\r
+       {\r
+               const BYTE *sha1 = wgGetRevisionID(CStringA(sProjectRoot), NULL);\r
+               if (sha1)\r
+                       youngest = ConvertHashToRevnum(sha1);\r
+       }\r
+\r
        return youngest;\r
-#endif\r
-       return CString("");\r
 }\r
 \r
 git_wc_status2_t * GitStatus::GetFirstFileStatus(const CTGitPath& path, CTGitPath& retPath, bool update, git_depth_t depth, bool bNoIgnore /* = true */, bool bNoExternals /* = false */)\r
@@ -743,6 +788,13 @@ BOOL GitStatus::getallstatus(const struct wgFile_s *pFile, void *pUserData)
        return FALSE;\r
 }\r
 \r
+BOOL GitStatus::getstatus(const struct wgFile_s *pFile, void *pUserData)\r
+{\r
+       git_wc_status2_t * s = (git_wc_status2_t*)pUserData;\r
+       s->prop_status = s->text_status = GitStatus::GetMoreImportant(s->prop_status, GitStatusFromWingit(pFile->nStatus));\r
+       return FALSE;\r
+}\r
+\r
 #if 0\r
 git_error_t * GitStatus::getallstatus(void * baton, const char * /*path*/, git_wc_status2_t * status, apr_pool_t * /*pool*/)\r
 {\r
index f5d1ffc..ea566e2 100644 (file)
@@ -47,6 +47,7 @@ typedef enum
 \r
 \r
 #define GIT_REV_ZERO _T("0000000000000000000000000000000000000000")\r
+#define GIT_INVALID_REVNUM _T("")\r
 typedef CString git_revnum_t;\r
 typedef int git_error_t;\r
 \r
@@ -70,11 +71,34 @@ inline static git_wc_status_kind GitStatusFromWingit(int nStatus)
        case WGFS_Normal: return git_wc_status_normal;\r
        case WGFS_Modified: return git_wc_status_modified;\r
        case WGFS_Deleted: return git_wc_status_deleted;\r
+\r
+       case WGFS_Empty: return git_wc_status_unversioned;\r
        }\r
 \r
        return git_wc_status_none;\r
 }\r
 \r
+// convert 20 byte sha1 hash to the git_revnum_t type\r
+inline static git_revnum_t ConvertHashToRevnum(const BYTE *sha1)\r
+{\r
+       if (!sha1)\r
+               return GIT_INVALID_REVNUM;\r
+\r
+       char s[41];\r
+       char *p = s;\r
+       for (int i=0; i<20; i++)\r
+       {\r
+#pragma warning(push)\r
+#pragma warning(disable: 4996)\r
+               sprintf(p, "%02x", (UINT)*sha1);\r
+#pragma warning(pop)\r
+               p += 2;\r
+               sha1++;\r
+       }\r
+\r
+       return CString(s);\r
+}\r
+\r
 \r
 /**\r
  * \ingroup Git\r
@@ -233,7 +257,10 @@ private:
 \r
 //     git_client_ctx_t *                      ctx;\r
        git_wc_status_kind                      m_allstatus;    ///< used by GetAllStatus and GetAllStatusRecursive\r
-       git_error_t *                           m_err;                  ///< Subversion error baton\r
+//     git_error_t *                           m_err;                  ///< Subversion error baton\r
+       BOOL                                            m_err;\r
+\r
+       git_wc_status2_t                        m_status;               // used for GetStatus\r
 \r
 #ifdef _MFC_VER\r
 //     GitPrompt                                       m_prompt;\r
@@ -250,6 +277,7 @@ private:
         */\r
        //static git_error_t * getallstatus (void *baton, const char *path, git_wc_status2_t *status, apr_pool_t *pool);\r
        static BOOL getallstatus(const struct wgFile_s *pFile, void *pUserData);\r
+       static BOOL getstatus(const struct wgFile_s *pFile, void *pUserData);\r
 \r
        /**\r
         * Callback function which stores the raw status from a Git_client_status() function call\r
index 55b56b7..91acf49 100644 (file)
@@ -178,9 +178,15 @@ STDMETHODIMP CShellExt::IsMemberOf(LPCWSTR pwszPath, DWORD /*dwAttrib*/)
                                                        {\r
                                                                const FileStatusCacheEntry * s = m_CachedStatus.GetFullStatus(CTGitPath(pPath), TRUE);\r
                                                                status = s->status;\r
-                                                               // if get status fails then display status as 'normal' on folder (since it contains .git)\r
-                                                               // TODO: works for svn since each folder has .svn, not sure if git needs additinoal processing\r
-                                                               status = GitStatus::GetMoreImportant(git_wc_status_normal, status);\r
+                                                               // GitFolderStatus does not list unversioned files/dir so they would always end up as normal below\r
+                                                               // so let's assume file/dir is unversioned if not found in cache\r
+                                                               /*// sub-dirs that are empty (or contain no versioned files) are reported as unversioned (and should be kept as such)\r
+                                                               if (status != git_wc_status_unversioned)\r
+                                                               {\r
+                                                                       // if get status fails then display status as 'normal' on folder (since it contains .git)\r
+                                                                       // TODO: works for svn since each folder has .svn, not sure if git needs additinoal processing\r
+                                                                       status = GitStatus::GetMoreImportant(git_wc_status_normal, status);\r
+                                                               }*/\r
                                                        }\r
                                                }\r
                                                else\r