OSDN Git Service

Rewrite patch parser for git created patch.
authorFrank Li <lznuaa@gmail.com>
Thu, 23 Apr 2009 02:42:29 +0000 (10:42 +0800)
committerFrank Li <lznuaa@gmail.com>
Thu, 23 Apr 2009 02:42:29 +0000 (10:42 +0800)
Signed-off-by: Frank Li <lznuaa@gmail.com>
src/TortoiseMerge/Patch.cpp
src/TortoiseMerge/Patch.h

index c13c515..0f696e5 100644 (file)
@@ -57,6 +57,253 @@ void CPatch::FreeMemory()
        m_arFileDiffs.RemoveAll();\r
 }\r
 \r
+BOOL CPatch::ParserGitPatch(CFileTextLines &PatchLines,int nIndex)\r
+{\r
+       CString sLine;\r
+       EOL ending = EOL_NOENDING;\r
+\r
+       int state = 0;\r
+       Chunks * chunks = NULL;\r
+       Chunk * chunk = NULL;\r
+       int nAddLineCount = 0;\r
+       int nRemoveLineCount = 0;\r
+       int nContextLineCount = 0;\r
+       for ( ;nIndex<PatchLines.GetCount(); nIndex++)\r
+       {\r
+               sLine = PatchLines.GetAt(nIndex);\r
+               ending = PatchLines.GetLineEnding(nIndex);\r
+               if (ending != EOL_NOENDING)\r
+                       ending = EOL_AUTOLINE;\r
+               \r
+               switch (state)\r
+               {\r
+                       case 0: \r
+                       {\r
+                               // diff --git\r
+                               if( sLine.Find(_T("diff --git"))==0)\r
+                               {\r
+                                       if (chunks)\r
+                                       {\r
+                                               //this is a new file diff, so add the last one to \r
+                                               //our array.\r
+                                               m_arFileDiffs.Add(chunks);\r
+                                       }\r
+                                       chunks = new Chunks();\r
+\r
+                               }\r
+                               \r
+                               //index\r
+                               if( sLine.Find(_T("index"))==0 )\r
+                               {\r
+                                       int dotstart=sLine.Find(_T(".."));\r
+                                       if(dotstart>=0)\r
+                                       {\r
+                                               chunks->sRevision = sLine.Mid(dotstart-7,7);\r
+                                               chunks->sRevision2 = sLine.Mid(dotstart+2,7);\r
+                                       }\r
+                               }\r
+\r
+                               //---\r
+                               if( sLine.Find(_T("--- "))==0 )\r
+                               {\r
+                                       if (sLine.Left(3).Compare(_T("---"))!=0)\r
+                                       {\r
+                                               //no starting "---" found\r
+                                               //seems to be either garbage or just\r
+                                               //a binary file. So start over...\r
+                                               state = 0;\r
+                                               nIndex--;\r
+                                               if (chunks)\r
+                                               {\r
+                                                       delete chunks;\r
+                                                       chunks = NULL;\r
+                                               }\r
+                                               break;\r
+                                       }\r
+                               \r
+                                       sLine = sLine.Mid(3);   //remove the "---"\r
+                                       sLine =sLine.Trim();\r
+                                       //at the end of the filepath there's a revision number...\r
+                                       int bracket = sLine.ReverseFind('(');\r
+                                       if (bracket < 0)\r
+                                       // some patch files can have another '(' char, especially ones created in Chinese OS\r
+                                               bracket = sLine.ReverseFind(0xff08);\r
+                               \r
+                                       if (bracket < 0)\r
+                                       {\r
+                                               if (chunks->sFilePath.IsEmpty())\r
+                                                       chunks->sFilePath = sLine.Trim();\r
+                                       }\r
+                                       else\r
+                                               chunks->sFilePath = sLine.Left(bracket-1).Trim();\r
+                                       \r
+                                       if (chunks->sFilePath.Find('\t')>=0)\r
+                                       {\r
+                                               chunks->sFilePath = chunks->sFilePath.Left(chunks->sFilePath.Find('\t'));\r
+                                       }\r
+                                       if( chunks->sFilePath.Find(_T("a/")) == 0 )\r
+                                               chunks->sFilePath=chunks->sFilePath.Mid(2);\r
+\r
+                                       chunks->sFilePath.Replace(_T('/'),_T('\\'));\r
+                               }\r
+                               \r
+                               // +++\r
+                               if( sLine.Find(_T("+++ ")) == 0 )\r
+                               {\r
+                                       sLine = sLine.Mid(3);   //remove the "---"\r
+                                       sLine =sLine.Trim();\r
+                               \r
+                                       //at the end of the filepath there's a revision number...\r
+                                       int bracket = sLine.ReverseFind('(');\r
+                                       if (bracket < 0)\r
+                                       // some patch files can have another '(' char, especially ones created in Chinese OS\r
+                                               bracket = sLine.ReverseFind(0xff08);\r
+\r
+                                       if (bracket < 0)\r
+                                               chunks->sFilePath2 = sLine.Trim();\r
+                                       else\r
+                                               chunks->sFilePath2 = sLine.Left(bracket-1).Trim();\r
+                                       if (chunks->sFilePath2.Find('\t')>=0)\r
+                                       {\r
+                                               chunks->sFilePath2 = chunks->sFilePath2.Left(chunks->sFilePath2.Find('\t'));\r
+                                       }\r
+                                       if( chunks->sFilePath2.Find(_T("a/")) == 0 )\r
+                                               chunks->sFilePath2=chunks->sFilePath2.Mid(2);\r
+\r
+                                       chunks->sFilePath2.Replace(_T('/'),_T('\\'));\r
+                               }\r
+                               \r
+                               //@@ -xxx,xxx +xxx,xxx @@\r
+                               if( sLine.Find(_T("@@")) == 0 )\r
+                               {\r
+                                       sLine = sLine.Mid(2);\r
+                                       sLine = sLine.Trim();\r
+                                       chunk = new Chunk();\r
+                                       CString sRemove = sLine.Left(sLine.Find(' '));\r
+                                       CString sAdd = sLine.Mid(sLine.Find(' '));\r
+                                       chunk->lRemoveStart = (-_ttol(sRemove));\r
+                                       if (sRemove.Find(',')>=0)\r
+                                       {\r
+                                               sRemove = sRemove.Mid(sRemove.Find(',')+1);\r
+                                               chunk->lRemoveLength = _ttol(sRemove);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               chunk->lRemoveStart = 0;\r
+                                               chunk->lRemoveLength = (-_ttol(sRemove));\r
+                                       }\r
+                                       chunk->lAddStart = _ttol(sAdd);\r
+                                       if (sAdd.Find(',')>=0)\r
+                                       {\r
+                                               sAdd = sAdd.Mid(sAdd.Find(',')+1);\r
+                                               chunk->lAddLength = _ttol(sAdd);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               chunk->lAddStart = 1;\r
+                                               chunk->lAddLength = _ttol(sAdd);\r
+                                       }\r
+                                       \r
+                                       state =5;\r
+                               }\r
+                       } \r
+               break;\r
+               \r
+               \r
+               case 5: //[ |+|-] <sourceline>\r
+                       {\r
+                               //this line is either a context line (with a ' ' in front)\r
+                               //a line added (with a '+' in front)\r
+                               //or a removed line (with a '-' in front)\r
+                               TCHAR type;\r
+                               if (sLine.IsEmpty())\r
+                                       type = ' ';\r
+                               else\r
+                                       type = sLine.GetAt(0);\r
+                               if (type == ' ')\r
+                               {\r
+                                       //it's a context line - we don't use them here right now\r
+                                       //but maybe in the future the patch algorithm can be\r
+                                       //extended to use those in case the file to patch has\r
+                                       //already changed and no base file is around...\r
+                                       chunk->arLines.Add(RemoveUnicodeBOM(sLine.Mid(1)));\r
+                                       chunk->arLinesStates.Add(PATCHSTATE_CONTEXT);\r
+                                       chunk->arEOLs.push_back(ending);\r
+                                       nContextLineCount++;\r
+                               }\r
+                               else if (type == '\\')\r
+                               {\r
+                                       //it's a context line (sort of): \r
+                                       //warnings start with a '\' char (e.g. "\ No newline at end of file")\r
+                                       //so just ignore this...\r
+                               }\r
+                               else if (type == '-')\r
+                               {\r
+                                       //a removed line\r
+                                       chunk->arLines.Add(RemoveUnicodeBOM(sLine.Mid(1)));\r
+                                       chunk->arLinesStates.Add(PATCHSTATE_REMOVED);\r
+                                       chunk->arEOLs.push_back(ending);\r
+                                       nRemoveLineCount++;\r
+                               }\r
+                               else if (type == '+')\r
+                               {\r
+                                       //an added line\r
+                                       chunk->arLines.Add(RemoveUnicodeBOM(sLine.Mid(1)));\r
+                                       chunk->arLinesStates.Add(PATCHSTATE_ADDED);\r
+                                       chunk->arEOLs.push_back(ending);\r
+                                       nAddLineCount++;\r
+                               }\r
+                               else\r
+                               {\r
+                                       //none of those lines! what the hell happened here?\r
+                                       m_sErrorMessage.Format(IDS_ERR_PATCH_UNKOWNLINETYPE, nIndex);\r
+                                       goto errorcleanup;\r
+                               }\r
+                               if ((chunk->lAddLength == (nAddLineCount + nContextLineCount)) &&\r
+                                       chunk->lRemoveLength == (nRemoveLineCount + nContextLineCount))\r
+                               {\r
+                                       //chunk is finished\r
+                                       if (chunks)\r
+                                               chunks->chunks.Add(chunk);\r
+                                       else\r
+                                               delete chunk;\r
+                                       chunk = NULL;\r
+                                       nAddLineCount = 0;\r
+                                       nContextLineCount = 0;\r
+                                       nRemoveLineCount = 0;\r
+                                       state = 0;\r
+                               }\r
+                       } \r
+               break;\r
+               default:\r
+                       ASSERT(FALSE);\r
+               } // switch (state) \r
+       } // for ( ;nIndex<m_PatchLines.GetCount(); nIndex++) \r
+       if (chunk)\r
+       {\r
+               m_sErrorMessage.LoadString(IDS_ERR_PATCH_CHUNKMISMATCH);\r
+               goto errorcleanup;\r
+       }\r
+       if (chunks)\r
+               m_arFileDiffs.Add(chunks);\r
+       return TRUE;\r
+\r
+errorcleanup:\r
+       if (chunk)\r
+               delete chunk;\r
+       if (chunks)\r
+       {\r
+               for (int i=0; i<chunks->chunks.GetCount(); i++)\r
+               {\r
+                       delete chunks->chunks.GetAt(i);\r
+               }\r
+               chunks->chunks.RemoveAll();\r
+               delete chunks;\r
+       }\r
+       FreeMemory();\r
+       return FALSE;\r
+}\r
+\r
 BOOL CPatch::OpenUnifiedDiffFile(const CString& filename)\r
 {\r
        CString sLine;\r
@@ -90,23 +337,28 @@ BOOL CPatch::OpenUnifiedDiffFile(const CString& filename)
        //first, skip possible garbage at the beginning\r
        //garbage is finished when a line starts with "Index: "\r
        //and the next line consists of only "=" characters\r
-       for (nIndex=0; nIndex<PatchLines.GetCount(); nIndex++)\r
+       if( !m_IsGitPatch )\r
        {\r
-               sLine = PatchLines.GetAt(nIndex);\r
-               if (sLine.Left(4).Compare(_T("--- "))==0)\r
-                       break;\r
-               if ((nIndex+1)<PatchLines.GetCount())\r
+               for (nIndex=0; nIndex<PatchLines.GetCount(); nIndex++)\r
                {\r
-                       sLine = PatchLines.GetAt(nIndex+1);\r
-\r
-                       if(sLine.IsEmpty()&&m_IsGitPatch)\r
-                               continue;\r
+                       sLine = PatchLines.GetAt(nIndex);\r
 \r
-                       sLine.Replace(_T("="), _T(""));\r
-                       if (sLine.IsEmpty())\r
+                       if (sLine.Left(4).Compare(_T("--- "))==0)\r
                                break;\r
+                       if ((nIndex+1)<PatchLines.GetCount())\r
+                       {\r
+                               sLine = PatchLines.GetAt(nIndex+1);\r
+\r
+                               if(sLine.IsEmpty()&&m_IsGitPatch)\r
+                                       continue;\r
+\r
+                               sLine.Replace(_T("="), _T(""));\r
+                               if (sLine.IsEmpty())\r
+                                       break;\r
+                       }\r
                }\r
        }\r
+\r
        if ((PatchLines.GetCount()-nIndex) < 2)\r
        {\r
                //no file entry found.\r
@@ -114,6 +366,9 @@ BOOL CPatch::OpenUnifiedDiffFile(const CString& filename)
                return FALSE;\r
        }\r
 \r
+       if( m_IsGitPatch )\r
+               return ParserGitPatch(PatchLines,nIndex);\r
+\r
        //from this point on we have the real unified diff data\r
        int state = 0;\r
        Chunks * chunks = NULL;\r
index 1c01452..366b83a 100644 (file)
@@ -59,6 +59,8 @@ protected:
        int                     CountDirMatches(const CString& path);\r
        CString         RemoveUnicodeBOM(const CString& str);\r
 \r
+       BOOL            ParserGitPatch(CFileTextLines &PatchLines,int nIndex);\r
+\r
        /**\r
         * Strips the filename by removing m_nStrip prefixes.\r
         */\r