OSDN Git Service

Add Show Whole Project Button at commitDialog
[tortoisegit/TortoiseGitJp.git] / src / Git / Git.cpp
index 6d84e6f..1bb0531 100644 (file)
@@ -4,6 +4,8 @@
 #include "GitRev.h"\r
 #include "registry.h"\r
 #include "GitConfig.h"\r
+#include <map>\r
+#include "UnicodeUtils.h"\r
 \r
 \r
 static LPTSTR nextpath(LPCTSTR src, LPTSTR dst, UINT maxlen)\r
@@ -106,22 +108,86 @@ static BOOL FindGitPath()
 \r
 \r
 #define MAX_DIRBUFFER 1000\r
+#define CALL_OUTPUT_READ_CHUNK_SIZE 1024\r
+\r
 CString CGit::ms_LastMsysGitDir;\r
 CGit g_Git;\r
-CGit::CGit(void)\r
+BOOL g_IsWingitDllload = TRUE;\r
+\r
+LPBYTE wgGetRevisionID_safe(const char *pszProjectPath, const char *pszName)\r
 {\r
-       GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));\r
+       //if(g_IsWingitDllload)\r
+       //      return wgGetRevisionID(pszProjectPath,pszName);\r
+       //else\r
+       return NULL;\r
+}\r
 \r
-       // make sure git/bin is in PATH before wingit.dll gets (delay) loaded by wgInit()\r
-       if ( !CheckMsysGitDir() )\r
+BOOL wgEnumFiles_safe(const char *pszProjectPath, const char *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)\r
+{\r
+       //if(g_IsWingitDllload)\r
+       //      return wgEnumFiles(pszProjectPath,pszSubPath,nFlags,pEnumCb,pUserData);\r
+       //else\r
+       //      return g_Git.EnumFiles(pszProjectPath,pszSubPath,nFlags,pEnumCb,pUserData);\r
+       return FALSE;\r
+}\r
+\r
+BOOL CGit::IsVista()\r
+{\r
+\r
+       if( CRegStdWORD(_T("Software\\TortoiseGit\\CacheType") ) == 0)\r
        {\r
-               // TODO\r
+               g_IsWingitDllload=FALSE;\r
+               return TRUE;\r
+       }\r
+\r
+       OSVERSIONINFO osvi;\r
+    BOOL bIsWindowsXPorLater;\r
+\r
+    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));\r
+    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);\r
+\r
+    GetVersionEx(&osvi);\r
+       \r
+       if(osvi.dwMajorVersion >= 6)\r
+               return TRUE;\r
+       else\r
+               return FALSE;\r
+}\r
+\r
+static void InitWinGitDll()\r
+{\r
+       __try\r
+       {\r
+\r
+               if( CGit::IsVista () )\r
+               {\r
+                       g_IsWingitDllload=FALSE;\r
+                       return;\r
+               }\r
+               if ( !wgInit() )\r
+               {\r
+                               // TODO\r
+               }\r
+       }\r
+       __except(1)\r
+       {\r
+               g_IsWingitDllload=FALSE;\r
+               return;\r
        }\r
 \r
-       if ( !wgInit() )\r
+}\r
+CGit::CGit(void)\r
+{\r
+#if 0\r
+       GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));\r
+       m_CurrentDir.ReleaseBuffer();\r
+       // make sure git/bin is in PATH before wingit.dll gets (delay) loaded by wgInit()\r
+       if ( !CheckMsysGitDir() )\r
        {\r
                // TODO\r
        }\r
+       InitWinGitDll();\r
+#endif\r
 }\r
 \r
 CGit::~CGit(void)\r
@@ -220,21 +286,25 @@ BOOL CGit::IsInitRepos()
 \r
        return FALSE;\r
 }\r
-int CGit::Run(CString cmd,BYTE_VECTOR *vector)\r
+int CGit::Run(CGitCall* pcall)\r
 {\r
        PROCESS_INFORMATION pi;\r
        HANDLE hRead;\r
-       if(RunAsync(cmd,&pi,&hRead))\r
+       if(RunAsync(pcall->GetCmd(),&pi,&hRead))\r
                return GIT_ERROR_CREATE_PROCESS;\r
 \r
        DWORD readnumber;\r
-       BYTE data;\r
-       while(ReadFile(hRead,&data,1,&readnumber,NULL))\r
+       BYTE data[CALL_OUTPUT_READ_CHUNK_SIZE];\r
+       bool bAborted=false;\r
+       while(ReadFile(hRead,data,CALL_OUTPUT_READ_CHUNK_SIZE,&readnumber,NULL))\r
        {\r
-               //g_Buffer[readnumber]=0;\r
-               vector->push_back(data);\r
-//             StringAppend(output,g_Buffer,codes);\r
+               //Todo: when OnOutputData() returns 'true', abort git-command. Send CTRL-C signal?\r
+               if(!bAborted)//For now, flush output when command aborted.\r
+                       if(pcall->OnOutputData(data,readnumber))\r
+                               bAborted=true;\r
        }\r
+       if(!bAborted)\r
+               pcall->OnEnd();\r
 \r
        \r
        CloseHandle(pi.hThread);\r
@@ -251,7 +321,25 @@ int CGit::Run(CString cmd,BYTE_VECTOR *vector)
 \r
        CloseHandle(hRead);\r
        return exitcode;\r
+}\r
+class CGitCall_ByteVector : public CGitCall\r
+{\r
+public:\r
+       CGitCall_ByteVector(CString cmd,BYTE_VECTOR* pvector):CGitCall(cmd),m_pvector(pvector){}\r
+       virtual bool OnOutputData(const BYTE* data, size_t size)\r
+       {\r
+               size_t oldsize=m_pvector->size();\r
+               m_pvector->resize(m_pvector->size()+size);\r
+               memcpy(&*(m_pvector->begin()+oldsize),data,size);\r
+               return false;\r
+       }\r
+       BYTE_VECTOR* m_pvector;\r
 \r
+};\r
+int CGit::Run(CString cmd,BYTE_VECTOR *vector)\r
+{\r
+       CGitCall_ByteVector call(cmd,vector);\r
+       return Run(&call);\r
 }\r
 int CGit::Run(CString cmd, CString* output,int code)\r
 {\r
@@ -259,13 +347,10 @@ int CGit::Run(CString cmd, CString* output,int code)
        int ret;\r
        ret=Run(cmd,&vector);\r
 \r
-       if(ret)\r
-               return ret;\r
-       \r
        vector.push_back(0);\r
        \r
        StringAppend(output,&(vector[0]),code);\r
-       return 0;\r
+       return ret;\r
 }\r
 \r
 CString CGit::GetUserName(void)\r
@@ -342,6 +427,13 @@ int CGit::BuildOutputFormat(CString &format,bool IsFull)
 \r
 int CGit::GetLog(BYTE_VECTOR& logOut, CString &hash,  CTGitPath *path ,int count,int mask)\r
 {\r
+       CGitCall_ByteVector gitCall(CString(),&logOut);\r
+       return GetLog(&gitCall,hash,path,count,mask);\r
+}\r
+\r
+//int CGit::GetLog(CGitCall* pgitCall, CString &hash,  CTGitPath *path ,int count,int mask)\r
+int CGit::GetLog(CGitCall* pgitCall, CString &hash, CTGitPath *path, int count, int mask)\r
+{\r
 \r
        CString cmd;\r
        CString log;\r
@@ -378,9 +470,18 @@ int CGit::GetLog(BYTE_VECTOR& logOut, CString &hash,  CTGitPath *path ,int count
        if(mask& CGit::LOG_INFO_DETECT_RENAME )\r
                param += _T(" -M ");\r
 \r
+       if(mask& CGit::LOG_INFO_FIRST_PARENT )\r
+               param += _T(" --first-parent ");\r
+       \r
+       if(mask& CGit::LOG_INFO_NO_MERGE )\r
+               param += _T(" --no-merges ");\r
+\r
+       if(mask& CGit::LOG_INFO_FOLLOW)\r
+               param += _T(" --follow ");\r
+\r
        param+=hash;\r
 \r
-       cmd.Format(_T("git.exe log %s -z --topo-order --parents %s --pretty=format:\""),\r
+       cmd.Format(_T("git.exe log %s -z --topo-order %s --parents --pretty=format:\""),\r
                                num,param);\r
 \r
        BuildOutputFormat(log,!(mask&CGit::LOG_INFO_ONLY_HASH));\r
@@ -388,7 +489,10 @@ int CGit::GetLog(BYTE_VECTOR& logOut, CString &hash,  CTGitPath *path ,int count
        cmd += log;\r
        cmd += CString(_T("\"  "))+hash+file;\r
 \r
-       return Run(cmd,&logOut);\r
+       pgitCall->SetCmd(cmd);\r
+\r
+       return Run(pgitCall);\r
+//     return Run(cmd,&logOut);\r
 }\r
 \r
 #if 0\r
@@ -489,7 +593,7 @@ int CGit::RunLogFile(CString cmd,CString &filename)
        CloseHandle(pi.hProcess);\r
        CloseHandle(houtfile);\r
        return GIT_SUCCESS;\r
-       return 0;\r
+//     return 0;\r
 }\r
 \r
 git_revnum_t CGit::GetHash(CString &friendname)\r
@@ -546,12 +650,12 @@ int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)
                CString one;\r
                while( pos>=0 )\r
                {\r
-                       i++;\r
                        one=output.Tokenize(_T("\n"),pos);\r
                        list.push_back(one.Right(one.GetLength()-2));\r
                        if(one[0] == _T('*'))\r
                                if(current)\r
                                        *current=i;\r
+                       i++;\r
                }\r
        }\r
        return ret;\r
@@ -634,6 +738,18 @@ BOOL CGit::CheckMsysGitDir()
                free(home);\r
        }\r
 \r
+       //setup ssh client\r
+       CRegString sshclient=CRegString(_T("Software\\TortoiseGit\\SSH"));\r
+       CString ssh=sshclient;\r
+\r
+       if(!ssh.IsEmpty())\r
+       {\r
+               _tputenv_s(_T("GIT_SSH"),ssh);\r
+       }else\r
+       {\r
+               _tputenv_s(_T("GIT_SSH"),_T(""));\r
+       }\r
+\r
        // search PATH if git/bin directory is alredy present\r
        if ( FindGitPath() )\r
        {\r
@@ -673,7 +789,8 @@ BOOL CGit::CheckMsysGitDir()
 \r
        free(oldpath);\r
 \r
-       if( !FindGitPath() )\r
+\r
+    if( !FindGitPath() )\r
        {\r
                return false;\r
        }\r
@@ -682,4 +799,108 @@ BOOL CGit::CheckMsysGitDir()
                bInitialized = TRUE;\r
                return true;\r
        }\r
-}
\ No newline at end of file
+}\r
+\r
+\r
+class CGitCall_EnumFiles : public CGitCall\r
+{\r
+public:\r
+       CGitCall_EnumFiles(const char *pszProjectPath, const char *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)\r
+       :       m_pszProjectPath(pszProjectPath),\r
+               m_pszSubPath(pszSubPath),\r
+               m_nFlags(nFlags),\r
+               m_pEnumCb(pEnumCb),\r
+               m_pUserData(pUserData)\r
+       {\r
+       }\r
+\r
+       typedef std::map<CStringA,char> TStrCharMap;\r
+\r
+       const char *    m_pszProjectPath;\r
+       const char *    m_pszSubPath;\r
+       unsigned int    m_nFlags;\r
+       WGENUMFILECB *  m_pEnumCb;\r
+       void *                  m_pUserData;\r
+\r
+       BYTE_VECTOR             m_DataCollector;\r
+       TStrCharMap             m_FileStatus;\r
+\r
+       virtual bool    OnOutputData(const BYTE* data, size_t size)\r
+       {\r
+               m_DataCollector.append(data,size);\r
+               while(true)\r
+               {\r
+                       int found=m_DataCollector.findData((const BYTE*)"\n",1);\r
+                       if(found<0)\r
+                               return false;\r
+                       CStringA line;\r
+                       char* pline=line.GetBuffer(found+1);\r
+                       memcpy(pline,&*m_DataCollector.begin(),found);\r
+                       pline[found]='\0';\r
+                       line.ReleaseBuffer();\r
+                       OnSingleLine(line);\r
+                       m_DataCollector.erase(m_DataCollector.begin(),m_DataCollector.begin()+found+1);\r
+               }\r
+               return false;//Should never reach this\r
+       }\r
+       virtual void    OnEnd()\r
+       {\r
+               for(TStrCharMap::iterator itFileStatus=m_FileStatus.begin();itFileStatus!=m_FileStatus.end();++itFileStatus)\r
+               {\r
+                       wgFile_s fileStatus;\r
+                       fileStatus.sFileName=itFileStatus->first;\r
+                       switch(itFileStatus->second)\r
+                       {\r
+                       case 'C':       fileStatus.nStatus=WGFS_Modified;break;\r
+                       case 'H':       fileStatus.nStatus=WGFS_Normal;break;\r
+                       case 'R':       fileStatus.nStatus=WGFS_Deleted;break;\r
+                       case '?':       fileStatus.nStatus=WGFS_Empty;break;//Other?\r
+                       case 'K'://Todo: Killed?\r
+                       case 'M'://Todo: What to do with this state? WGFS_Conflicted?\r
+                       default://Unexpected status. Show as normal.\r
+                               fileStatus.nStatus=WGFS_Normal;\r
+                       }\r
+                       fileStatus.sha1=NULL;//Unknown with this call\r
+                       fileStatus.nFlags=0;//Never a directory with this call. Git doesnt track directories as such.\r
+                       (*m_pEnumCb)(&fileStatus,m_pUserData);\r
+               }\r
+       }\r
+       bool OnSingleLine(CStringA line)\r
+       {\r
+               //Parse single line\r
+               int space=line.Find(' ');\r
+               if(space<0)\r
+                       return false;\r
+               char status=line[0];\r
+               CStringA path=line;\r
+               path=path.Mid(space+1);\r
+               m_FileStatus[path]=status;\r
+               return true;\r
+       }\r
+\r
+\r
+\r
+\r
+};\r
+\r
+BOOL CGit::EnumFiles(const char *pszProjectPath, const char *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)\r
+{\r
+       if(*pszProjectPath=='\0')\r
+               return FALSE;\r
+       CGitCall_EnumFiles W_GitCall(pszProjectPath,pszSubPath,nFlags,pEnumCb,pUserData);\r
+       CString cmd;\r
+\r
+/*     char W_szToDir[MAX_PATH];\r
+       strncpy(W_szToDir,pszProjectPath,sizeof(W_szToDir)-1);\r
+       if(W_szToDir[strlen(W_szToDir)-1]!='\\')\r
+               strncat(W_szToDir,"\\",sizeof(W_szToDir)-1);\r
+\r
+       SetCurrentDirectoryA(W_szToDir);\r
+       GetCurrentDirectoryA(sizeof(W_szToDir)-1,W_szToDir);\r
+*/\r
+       SetCurrentDir(CUnicodeUtils::GetUnicode(pszProjectPath));\r
+       cmd.Format(_T("git.exe ls-files -t -c -d -m"));// -- %s"),CUnicodeUtils::GetUnicode(pszProjectPath));\r
+       W_GitCall.SetCmd(cmd);\r
+       Run(&W_GitCall);\r
+       return TRUE;\r
+}\r