OSDN Git Service

Rewrite patch parser for git created patch.
[tortoisegit/TortoiseGitJp.git] / src / TortoiseMerge / Patch.cpp
index 36ad2f4..0f696e5 100644 (file)
@@ -1,6 +1,6 @@
 // TortoiseMerge - a Diff/Patch program\r
 \r
-// Copyright (C) 2004-2008 - TortoiseSVN\r
+// Copyright (C) 2004-2009 - TortoiseSVN\r
 \r
 // This program is free software; you can redistribute it and/or\r
 // modify it under the terms of the GNU General Public License\r
@@ -21,7 +21,7 @@
 #include "UnicodeUtils.h"\r
 #include "DirFileEnum.h"\r
 #include "TortoiseMerge.h"\r
-//#include "svn_wc.h"\r
+#include "svn_wc.h"\r
 #include "GitAdminDir.h"\r
 #include "Patch.h"\r
 \r
@@ -34,6 +34,7 @@ static char THIS_FILE[] = __FILE__;
 CPatch::CPatch(void)\r
 {\r
        m_nStrip = 0;\r
+       m_IsGitPatch = false;\r
 }\r
 \r
 CPatch::~CPatch(void)\r
@@ -56,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
@@ -76,22 +324,41 @@ BOOL CPatch::OpenUnifiedDiffFile(const CString& filename)
        //now we got all the lines of the patch file\r
        //in our array - parsing can start...\r
 \r
+       for(nIndex=0;PatchLines.GetCount();nIndex++)\r
+       {\r
+               sLine = PatchLines.GetAt(nIndex);\r
+               if(sLine.Left(10).Compare(_T("diff --git")) == 0)\r
+               {\r
+                       this->m_IsGitPatch=true;\r
+                       break;\r
+               }\r
+       }\r
+\r
        //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<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
-                       sLine.Replace(_T("="), _T(""));\r
-                       if (sLine.IsEmpty())\r
+                       sLine = PatchLines.GetAt(nIndex);\r
+\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
@@ -99,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
@@ -114,7 +384,7 @@ BOOL CPatch::OpenUnifiedDiffFile(const CString& filename)
                        ending = EOL_AUTOLINE;\r
                if (state == 0)\r
                {\r
-                       if ((sLine.Left(4).Compare(_T("--- "))==0)&&(sLine.Find('\t') >= 0))\r
+                       if ((sLine.Left(4).Compare(_T("--- "))==0)&&((sLine.Find('\t') >= 0)||this->m_IsGitPatch))\r
                        {\r
                                state = 2;\r
                                if (chunks)\r
@@ -124,10 +394,19 @@ BOOL CPatch::OpenUnifiedDiffFile(const CString& filename)
                                        m_arFileDiffs.Add(chunks);\r
                                }\r
                                chunks = new Chunks();\r
+                               \r
                                int nTab = sLine.Find('\t');\r
+\r
+                               int filestart = 4;\r
+                               if(m_IsGitPatch)\r
+                               {\r
+                                       nTab=sLine.GetLength();\r
+                                       filestart = 6;\r
+                               }\r
+\r
                                if (nTab >= 0)\r
                                {\r
-                                       chunks->sFilePath = sLine.Mid(4, nTab-4).Trim();\r
+                                       chunks->sFilePath = sLine.Mid(filestart, nTab-filestart).Trim();\r
                                }\r
                        }\r
                }\r
@@ -609,6 +888,8 @@ BOOL CPatch::PatchFile(const CString& sPath, const CString& sSavePath, const CSt
                                        {\r
                                                if ((lAddLine < PatchLines.GetCount())&&(sPatchLine.Compare(PatchLines.GetAt(lAddLine))==0))\r
                                                        lAddLine++;\r
+                                               else if (((lAddLine + 1) < PatchLines.GetCount())&&(sPatchLine.Compare(PatchLines.GetAt(lAddLine+1))==0))\r
+                                                       lAddLine += 2;\r
                                                else if ((lRemoveLine < PatchLines.GetCount())&&(sPatchLine.Compare(PatchLines.GetAt(lRemoveLine))==0))\r
                                                        lRemoveLine++;\r
                                                else\r
@@ -678,7 +959,7 @@ CString     CPatch::CheckPatchPath(const CString& path)
        {\r
                if (!isDir)\r
                        continue;\r
-               if (g_SVNAdminDir.IsAdminDirPath(subpath))\r
+               if (g_GitAdminDir.IsAdminDirPath(subpath))\r
                        continue;\r
                if (CountMatches(subpath) > (GetNumberOfFiles()/3))\r
                        return subpath;\r