OSDN Git Service

merge original branch.
[tortoisegit/TortoiseGitJp.git] / src / Git / GitRev.cpp
index f1e4f37..9088c7a 100644 (file)
@@ -1,10 +1,29 @@
 #include "StdAfx.h"\r
+#include "ATLComTime.h"\r
 #include "GitRev.h"\r
+#include "Git.h"\r
+#include "GitDLL.h"\r
+#include "UnicodeUtils.h"\r
+\r
+class CException; //Just in case afx.h is not included (cannot be included in every project which uses this file)\r
+\r
+// provide an ASSERT macro for when compiled without MFC\r
+#if !defined ASSERT\r
+       // Don't use _asm here, it isn't supported by x64 version of compiler. In fact, MFC's ASSERT() is the same with _ASSERTE().\r
+       #define ASSERT(x) _ASSERTE(x)\r
+#endif\r
 \r
 \r
 GitRev::GitRev(void)\r
 {\r
        m_Action=0;\r
+       m_IsFull = 0;\r
+       m_IsUpdateing = 0;\r
+       // fetch local machine timezone info\r
+       if ( GetTimeZoneInformation( &m_TimeZone ) == TIME_ZONE_ID_INVALID )\r
+       {\r
+               ASSERT(false);\r
+       }\r
 }\r
 \r
 GitRev::~GitRev(void)\r
@@ -20,27 +39,78 @@ GitRev& GitRev::operator=(GitRev &rev)
        return *this;\r
 }\r
 #endif\r
-int GitRev::ParserFromLog(CString &log)\r
+void GitRev::Clear()\r
+{\r
+       this->m_Action=0;\r
+       this->m_Files.Clear();\r
+       this->m_Action=0;\r
+       this->m_ParentHash.clear();\r
+       m_CommitterName.Empty();\r
+       m_CommitterEmail.Empty();\r
+       m_Body.Empty();\r
+       m_Subject.Empty();\r
+       m_CommitHash.Empty();\r
+       m_Ref.Empty();\r
+       m_RefAction.Empty();\r
+       m_Mark=0;\r
+\r
+}\r
+int GitRev::CopyFrom(GitRev &rev,bool OmitParentAndMark)\r
+{\r
+       m_AuthorName    =rev.m_AuthorName       ;\r
+       m_AuthorEmail   =rev.m_AuthorEmail      ;\r
+       m_AuthorDate    =rev.m_AuthorDate       ;\r
+       m_CommitterName =rev.m_CommitterName    ;\r
+       m_CommitterEmail=rev.m_CommitterEmail;\r
+       m_CommitterDate =rev.m_CommitterDate    ;\r
+       m_Subject               =rev.m_Subject          ;\r
+       m_Body                  =rev.m_Body                     ;\r
+       m_CommitHash    =rev.m_CommitHash       ;\r
+       m_Files                 =rev.m_Files                    ;       \r
+       m_Action                =rev.m_Action           ;\r
+       m_IsFull                =rev.m_IsFull;\r
+\r
+       if(!OmitParentAndMark)\r
+       {\r
+               m_ParentHash    =rev.m_ParentHash       ;\r
+               m_Mark                  =rev.m_Mark;\r
+       }\r
+       return 0;\r
+}\r
+int GitRev::ParserFromLog(BYTE_VECTOR &log,int start)\r
 {\r
-       int pos=0;\r
+       int pos=start;\r
        CString one;\r
        CString key;\r
        CString text;\r
-       CString filelist;\r
-       TCHAR mode;\r
+       BYTE_VECTOR filelist;\r
+       BYTE mode=0;\r
        CTGitPath  path;\r
        this->m_Files.Clear();\r
     m_Action=0;\r
+       int begintime=0;\r
+       int filebegin=-1;\r
 \r
-       while( pos>=0 )\r
+       while( pos < log.size() && pos>=0)\r
        {\r
-               one=log.Tokenize(_T("\n"),pos);\r
-               if(one[0]==_T('#') && one[1] == _T('<') && one[3] == _T('>'))\r
+               \r
+               //one=log.Tokenize(_T("\n"),pos);\r
+               if(log[pos]==_T('#') && log[pos+1] == _T('<') && log[pos+3] == _T('>'))\r
                {\r
-                       text = one.Right(one.GetLength()-4);\r
-                       mode = one[2];\r
+                       //text = one.Right(one.GetLength()-4);\r
+                       text.Empty();\r
+                       g_Git.StringAppend(&text,&log[pos+4],CGit::m_LogEncode);\r
+                       mode = log[pos+2];\r
+                       \r
                        switch(mode)\r
                        {\r
+                       case LOG_REV_ITEM_BEGIN:\r
+                               begintime++;\r
+                               if(begintime>1)\r
+                                       break;\r
+                               else\r
+                                       this->Clear();\r
+                               break;\r
                        case LOG_REV_AUTHOR_NAME:\r
                                this->m_AuthorName = text;\r
                                break;\r
@@ -66,10 +136,25 @@ int GitRev::ParserFromLog(CString &log)
                                this->m_Body = text +_T("\n");\r
                                break;\r
                        case LOG_REV_COMMIT_HASH:\r
-                               this->m_CommitHash = text;\r
+                               this->m_CommitHash = text.Right(40);\r
+                               if(text.GetLength()>40)\r
+                               {\r
+                                       this->m_Mark=text[0];\r
+                               }\r
                                break;\r
                        case LOG_REV_COMMIT_PARENT:\r
-                               this->m_ParentHash.insert(this->m_ParentHash.end(),text);\r
+                               while(text.GetLength()>0)\r
+                               {\r
+                                       this->m_ParentHash.insert(this->m_ParentHash.end(),text.Left(40));\r
+                                       if(text.GetLength()>40)\r
+                                               text=text.Right(text.GetLength()-41);\r
+                                       else\r
+                                               break;\r
+                               }\r
+                               if(m_ParentHash.size()>1)\r
+                               {\r
+                                       int a=1;\r
+                               }\r
                                break;\r
                        case LOG_REV_COMMIT_FILE:\r
                                break;\r
@@ -78,29 +163,274 @@ int GitRev::ParserFromLog(CString &log)
                {\r
                        switch(mode)\r
                        {\r
-                       case LOG_REV_COMMIT_BODY:\r
-                               this->m_Body += one+_T("\n");\r
-                               break;\r
+//                     case LOG_REV_COMMIT_BODY:\r
+//                             this->m_Body += one+_T("\n");\r
+//                             break;\r
                        case LOG_REV_COMMIT_FILE:\r
-                               filelist += one +_T("\n");\r
+                               //filelist += one +_T("\n");\r
+                               //filelist.append(log,pos,log.find(0,pos));\r
+                               if(filebegin<0)\r
+                                       filebegin=pos;\r
                                break;\r
                        }\r
                }\r
+               \r
+               if(begintime>1)\r
+               {\r
+                       break;\r
+               }\r
+\r
+               //find next string start \r
+               pos=log.findNextString(pos);\r
        }\r
        \r
-       this->m_Files.ParserFromLog(filelist);\r
-       this->m_Action=this->m_Files.GetAction();\r
-       return 0;\r
+       if(filebegin>=0)\r
+       {\r
+               \r
+               filelist.append(log,filebegin,pos);     \r
+               this->m_Files.ParserFromLog(filelist);\r
+               this->m_Action=this->m_Files.GetAction();\r
+       }\r
+       return pos;\r
 }\r
 \r
 CTime GitRev::ConverFromString(CString input)\r
 {\r
-       CTime tm(_wtoi(input.Mid(0,4)),\r
-                        _wtoi(input.Mid(5,2)),\r
-                        _wtoi(input.Mid(8,2)),\r
-                        _wtoi(input.Mid(11,2)),\r
-                        _wtoi(input.Mid(14,2)),\r
-                        _wtoi(input.Mid(17,2)),\r
-                        _wtoi(input.Mid(20,4)));\r
-       return tm;\r
+       // pick up date from string\r
+       try\r
+       {\r
+               COleDateTime tm(_wtoi(input.Mid(0,4)),\r
+                                _wtoi(input.Mid(5,2)),\r
+                                _wtoi(input.Mid(8,2)),\r
+                                _wtoi(input.Mid(11,2)),\r
+                                _wtoi(input.Mid(14,2)),\r
+                                _wtoi(input.Mid(17,2)));\r
+               if( tm.GetStatus() != COleDateTime::valid )\r
+                       return CTime();//Error parsing time-string\r
+\r
+               // pick up utc offset\r
+               CString sign = input.Mid(20,1);         // + or -\r
+               int hoursOffset =  _wtoi(input.Mid(21,2));\r
+               int minsOffset = _wtoi(input.Mid(23,2));\r
+               // convert to a fraction of a day\r
+               double offset = (hoursOffset*60 + minsOffset) / 1440.0;         // 1440 mins = 1 day\r
+               if ( sign == "-" )\r
+               {\r
+                       offset = -offset;\r
+               }\r
+               // we have to subtract this from the time given to get UTC\r
+               tm -= offset;\r
+               // get utc time as a SYSTEMTIME\r
+               SYSTEMTIME sysTime;\r
+               tm.GetAsSystemTime( sysTime );\r
+               // and convert to users local time\r
+               SYSTEMTIME local;\r
+               if ( SystemTimeToTzSpecificLocalTime( &m_TimeZone, &sysTime, &local ) )\r
+               {\r
+                       sysTime = local;\r
+               }\r
+               else\r
+               {\r
+                       ASSERT(false);  // this should not happen but leave time in utc if it does\r
+               }\r
+               // convert to CTime and return\r
+               return CTime( sysTime, -1 );;\r
+       }\r
+       catch(CException* e)\r
+       {\r
+               //Probably the date was something like 1970-01-01 00:00:00. _mktime64() doesnt like this.\r
+               //Dont let the application crash on this exception\r
+\r
+#ifdef _AFX //CException classes are only defined when afx.h is included.\r
+                       //When afx.h is not included, the exception is leaked.\r
+                       //This will probably never happen because when CException is not defined, it cannot be thrown.\r
+               e->Delete();\r
+#endif //ifdef _AFX\r
+       }\r
+       return CTime(); //Return an invalid time\r
+}\r
+\r
+int GitRev::SafeFetchFullInfo(CGit *git)\r
+{\r
+       if(InterlockedExchange(&m_IsUpdateing,TRUE) == FALSE)\r
+       {\r
+#if 0\r
+               //GitRev rev;\r
+               BYTE_VECTOR onelog;\r
+               TCHAR oldmark=this->m_Mark;\r
+               CString commithash = m_CommitHash;\r
+               git->GetLog(onelog,commithash,NULL,1,CGit::LOG_INFO_FULL_DIFF|CGit::LOG_INFO_STAT|CGit::LOG_INFO_FILESTATE|CGit::LOG_INFO_DETECT_COPYRENAME|CGit::LOG_INFO_SHOW_MERGEDFILE);\r
+               CString oldhash=m_CommitHash;\r
+               GIT_REV_LIST oldlist=this->m_ParentHash;\r
+               ParserFromLog(onelog);\r
+               \r
+               //ASSERT(oldhash==m_CommitHash);\r
+               if(oldmark!=0)\r
+                       this->m_Mark=oldmark;  //parser full log will cause old mark overwrited. \r
+                                                              //So we need keep old bound mark.\r
+               this->m_ParentHash=oldlist;\r
+               InterlockedExchange(&m_IsUpdateing,FALSE);\r
+               InterlockedExchange(&m_IsFull,TRUE);\r
+               return 0;\r
+#endif\r
+               this->m_Files.Clear();\r
+               git->CheckAndInitDll();\r
+               GIT_COMMIT commit;\r
+               GIT_COMMIT_LIST list;\r
+               GIT_HASH   parent;\r
+               if(git_get_commit_from_hash(&commit, this->m_CommitHash.m_hash))\r
+                       return -1;\r
+\r
+               int i=0;\r
+               bool isRoot = this->m_ParentHash.size()==0;\r
+               git_get_commit_first_parent(&commit,&list);\r
+               while(git_get_commit_next_parent(&list,parent) == 0 || isRoot)\r
+               {\r
+                       GIT_FILE file;\r
+                       int count;\r
+                       if(isRoot)\r
+                               git_root_diff(git->GetGitDiff(), this->m_CommitHash.m_hash, &file, &count);\r
+                       else\r
+                               git_diff(git->GetGitDiff(),parent,commit.m_hash,&file,&count);\r
+                       \r
+                       isRoot = false;\r
+\r
+                       CTGitPath path;\r
+                       CString strnewname;\r
+                       CString stroldname;\r
+                       \r
+                       for(int j=0;j<count;j++)\r
+                       {\r
+                               path.Reset();\r
+                               char *newname;\r
+                               char *oldname;\r
+                               \r
+                               strnewname.Empty();\r
+                               stroldname.Empty();\r
+\r
+                               int mode,IsBin,inc,dec;\r
+                               git_get_diff_file(git->GetGitDiff(),file,j,&newname,&oldname,\r
+                                               &mode,&IsBin,&inc,&dec);\r
+                               \r
+                               git->StringAppend(&strnewname,(BYTE*)newname,CP_ACP);\r
+                               git->StringAppend(&stroldname,(BYTE*)oldname,CP_ACP);\r
+\r
+                               path.m_ParentNo = i;\r
+                               path.SetFromGit(strnewname,&stroldname);\r
+                               path.ParserAction((BYTE)mode);\r
+\r
+                               this->m_Action|=path.m_Action;\r
+\r
+                               if(IsBin)\r
+                               {\r
+                                       path.m_StatAdd=_T("-");\r
+                                       path.m_StatDel=_T("-");\r
+                               }else\r
+                               {\r
+                                       path.m_StatAdd.Format(_T("%d"),inc);\r
+                                       path.m_StatDel.Format(_T("%d"),dec);\r
+                               }\r
+                               m_Files.AddPath(path);\r
+                       }\r
+                       git_diff_flush(git->GetGitDiff());\r
+                       i++;\r
+               }\r
+\r
+               InterlockedExchange(&m_IsUpdateing,FALSE);\r
+               InterlockedExchange(&m_IsFull,TRUE);\r
+\r
+       }\r
+       return -1;\r
+}\r
+\r
+int GitRev::ParserParentFromCommit(GIT_COMMIT *commit)\r
+{\r
+       this->m_ParentHash.clear();\r
+       GIT_COMMIT_LIST list;\r
+       GIT_HASH   parent;\r
+       \r
+       git_get_commit_first_parent(commit,&list);\r
+       while(git_get_commit_next_parent(&list,parent)==0)\r
+       {\r
+               m_ParentHash.push_back(CGitHash((char *)parent));\r
+       }\r
+       return 0;\r
+}\r
+\r
+int GitRev::ParserFromCommit(GIT_COMMIT *commit)\r
+{\r
+       int encode =CP_UTF8;\r
+       \r
+       if(commit->m_Encode != 0 && commit->m_EncodeSize != 0)\r
+       {\r
+               CString str;\r
+               g_Git.StringAppend(&str, (BYTE*)commit->m_Encode, CP_UTF8, commit->m_EncodeSize);\r
+               encode = CUnicodeUtils::GetCPCode(str);\r
+       }\r
+\r
+       this->m_AuthorDate = commit->m_Author.Date;\r
+       \r
+       this->m_AuthorEmail.Empty();\r
+       g_Git.StringAppend(&m_AuthorEmail,(BYTE*)commit->m_Author.Email,CP_UTF8,commit->m_Author.EmailSize);\r
+\r
+       this->m_AuthorName.Empty();\r
+       g_Git.StringAppend(&m_AuthorName,(BYTE*)commit->m_Author.Name,CP_UTF8,commit->m_Author.NameSize);\r
+       \r
+       this->m_Body.Empty();\r
+       g_Git.StringAppend(&m_Body,(BYTE*)commit->m_Body,encode,commit->m_BodySize);\r
+\r
+       this->m_CommitterDate = commit->m_Committer.Date;\r
+       \r
+       this->m_CommitterEmail.Empty();\r
+       g_Git.StringAppend(&m_CommitterEmail, (BYTE*)commit->m_Committer.Email,CP_UTF8, commit->m_Committer.EmailSize);\r
+\r
+       this->m_CommitterName.Empty();\r
+       g_Git.StringAppend(&m_CommitterName, (BYTE*)commit->m_Committer.Name,CP_UTF8, commit->m_Committer.NameSize);\r
+\r
+       this->m_Subject.Empty();\r
+       g_Git.StringAppend(&m_Subject, (BYTE*)commit->m_Subject,encode,commit->m_SubjectSize);\r
+       \r
+       return 0;\r
+}\r
+#ifndef TRACE\r
+#define TRACE(x) 1?0:(x)\r
+#endif\r
+void GitRev::DbgPrint()\r
+{\r
+       TRACE(_T("Commit %s\r\n"), this->m_CommitHash.ToString());\r
+       for(int i=0;i<this->m_ParentHash.size();i++)\r
+       {\r
+               TRACE(_T("Parent %i %s"),i, m_ParentHash[i].ToString());\r
+       }\r
+       TRACE(_T("\n"));\r
+}\r
+\r
+int GitRev::GetCommitFromHash(CGitHash &hash)\r
+{\r
+       g_Git.CheckAndInitDll();\r
+\r
+       GIT_COMMIT commit;\r
+       if(git_get_commit_from_hash( &commit, hash.m_hash))\r
+               return -1;\r
+\r
+       this->ParserFromCommit(&commit);\r
+       git_free_commit(&commit);\r
+\r
+       this->m_CommitHash=hash;\r
+\r
+       return 0;\r
+       \r
 }\r
+\r
+int GitRev::GetCommit(CString &refname)\r
+{\r
+       g_Git.CheckAndInitDll();\r
+       CStringA rev;\r
+       rev= CUnicodeUtils::GetUTF8(refname);\r
+       GIT_HASH sha;\r
+\r
+       if(git_get_sha1(rev.GetBuffer(),sha))\r
+               return -1;\r
+\r
+       GetCommitFromHash(CGitHash((char*)sha));\r
+}
\ No newline at end of file