1 // TortoiseMerge - a Diff/Patch program
3 // Copyright (C) 2004-2009 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "UnicodeUtils.h"
22 #include "DirFileEnum.h"
23 #include "TortoiseMerge.h"
25 #include "GitAdminDir.h"
31 static char THIS_FILE
[] = __FILE__
;
45 void CPatch::FreeMemory()
47 for (int i
=0; i
<m_arFileDiffs
.GetCount(); i
++)
49 Chunks
* chunks
= m_arFileDiffs
.GetAt(i
);
50 for (int j
=0; j
<chunks
->chunks
.GetCount(); j
++)
52 delete chunks
->chunks
.GetAt(j
);
54 chunks
->chunks
.RemoveAll();
57 m_arFileDiffs
.RemoveAll();
60 BOOL
CPatch::ParserGitPatch(CFileTextLines
&PatchLines
,int nIndex
)
63 EOL ending
= EOL_NOENDING
;
66 Chunks
* chunks
= NULL
;
68 int nAddLineCount
= 0;
69 int nRemoveLineCount
= 0;
70 int nContextLineCount
= 0;
71 for ( ;nIndex
<PatchLines
.GetCount(); nIndex
++)
73 sLine
= PatchLines
.GetAt(nIndex
);
74 ending
= PatchLines
.GetLineEnding(nIndex
);
75 if (ending
!= EOL_NOENDING
)
76 ending
= EOL_AUTOLINE
;
83 if( sLine
.Find(_T("diff --git"))==0)
87 //this is a new file diff, so add the last one to
89 m_arFileDiffs
.Add(chunks
);
91 chunks
= new Chunks();
96 if( sLine
.Find(_T("index"))==0 )
98 int dotstart
=sLine
.Find(_T(".."));
101 chunks
->sRevision
= sLine
.Mid(dotstart
-7,7);
102 chunks
->sRevision2
= sLine
.Mid(dotstart
+2,7);
107 if( sLine
.Find(_T("--- "))==0 )
109 if (sLine
.Left(3).Compare(_T("---"))!=0)
111 //no starting "---" found
112 //seems to be either garbage or just
113 //a binary file. So start over...
124 sLine
= sLine
.Mid(3); //remove the "---"
126 //at the end of the filepath there's a revision number...
127 int bracket
= sLine
.ReverseFind('(');
129 // some patch files can have another '(' char, especially ones created in Chinese OS
130 bracket
= sLine
.ReverseFind(0xff08);
134 if (chunks
->sFilePath
.IsEmpty())
135 chunks
->sFilePath
= sLine
.Trim();
138 chunks
->sFilePath
= sLine
.Left(bracket
-1).Trim();
140 if (chunks
->sFilePath
.Find('\t')>=0)
142 chunks
->sFilePath
= chunks
->sFilePath
.Left(chunks
->sFilePath
.Find('\t'));
144 if( chunks
->sFilePath
.Find(_T("a/")) == 0 )
145 chunks
->sFilePath
=chunks
->sFilePath
.Mid(2);
147 chunks
->sFilePath
.Replace(_T('/'),_T('\\'));
151 if( sLine
.Find(_T("+++ ")) == 0 )
153 sLine
= sLine
.Mid(3); //remove the "---"
156 //at the end of the filepath there's a revision number...
157 int bracket
= sLine
.ReverseFind('(');
159 // some patch files can have another '(' char, especially ones created in Chinese OS
160 bracket
= sLine
.ReverseFind(0xff08);
163 chunks
->sFilePath2
= sLine
.Trim();
165 chunks
->sFilePath2
= sLine
.Left(bracket
-1).Trim();
166 if (chunks
->sFilePath2
.Find('\t')>=0)
168 chunks
->sFilePath2
= chunks
->sFilePath2
.Left(chunks
->sFilePath2
.Find('\t'));
170 if( chunks
->sFilePath2
.Find(_T("a/")) == 0 )
171 chunks
->sFilePath2
=chunks
->sFilePath2
.Mid(2);
173 chunks
->sFilePath2
.Replace(_T('/'),_T('\\'));
176 //@@ -xxx,xxx +xxx,xxx @@
177 if( sLine
.Find(_T("@@")) == 0 )
179 sLine
= sLine
.Mid(2);
180 sLine
= sLine
.Trim();
182 CString sRemove
= sLine
.Left(sLine
.Find(' '));
183 CString sAdd
= sLine
.Mid(sLine
.Find(' '));
184 chunk
->lRemoveStart
= (-_ttol(sRemove
));
185 if (sRemove
.Find(',')>=0)
187 sRemove
= sRemove
.Mid(sRemove
.Find(',')+1);
188 chunk
->lRemoveLength
= _ttol(sRemove
);
192 chunk
->lRemoveStart
= 0;
193 chunk
->lRemoveLength
= (-_ttol(sRemove
));
195 chunk
->lAddStart
= _ttol(sAdd
);
196 if (sAdd
.Find(',')>=0)
198 sAdd
= sAdd
.Mid(sAdd
.Find(',')+1);
199 chunk
->lAddLength
= _ttol(sAdd
);
203 chunk
->lAddStart
= 1;
204 chunk
->lAddLength
= _ttol(sAdd
);
213 case 5: //[ |+|-] <sourceline>
215 //this line is either a context line (with a ' ' in front)
216 //a line added (with a '+' in front)
217 //or a removed line (with a '-' in front)
222 type
= sLine
.GetAt(0);
225 //it's a context line - we don't use them here right now
226 //but maybe in the future the patch algorithm can be
227 //extended to use those in case the file to patch has
228 //already changed and no base file is around...
229 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
230 chunk
->arLinesStates
.Add(PATCHSTATE_CONTEXT
);
231 chunk
->arEOLs
.push_back(ending
);
234 else if (type
== '\\')
236 //it's a context line (sort of):
237 //warnings start with a '\' char (e.g. "\ No newline at end of file")
238 //so just ignore this...
240 else if (type
== '-')
243 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
244 chunk
->arLinesStates
.Add(PATCHSTATE_REMOVED
);
245 chunk
->arEOLs
.push_back(ending
);
248 else if (type
== '+')
251 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
252 chunk
->arLinesStates
.Add(PATCHSTATE_ADDED
);
253 chunk
->arEOLs
.push_back(ending
);
258 //none of those lines! what the hell happened here?
259 m_sErrorMessage
.Format(IDS_ERR_PATCH_UNKOWNLINETYPE
, nIndex
);
262 if ((chunk
->lAddLength
== (nAddLineCount
+ nContextLineCount
)) &&
263 chunk
->lRemoveLength
== (nRemoveLineCount
+ nContextLineCount
))
267 chunks
->chunks
.Add(chunk
);
272 nContextLineCount
= 0;
273 nRemoveLineCount
= 0;
281 } // for ( ;nIndex<m_PatchLines.GetCount(); nIndex++)
284 m_sErrorMessage
.LoadString(IDS_ERR_PATCH_CHUNKMISMATCH
);
288 m_arFileDiffs
.Add(chunks
);
296 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
298 delete chunks
->chunks
.GetAt(i
);
300 chunks
->chunks
.RemoveAll();
307 BOOL
CPatch::OpenUnifiedDiffFile(const CString
& filename
)
310 EOL ending
= EOL_NOENDING
;
312 INT_PTR nLineCount
= 0;
313 g_crasher
.AddFile((LPCSTR
)(LPCTSTR
)filename
, (LPCSTR
)(LPCTSTR
)_T("unified diff file"));
315 CFileTextLines PatchLines
;
316 if (!PatchLines
.Load(filename
))
318 m_sErrorMessage
= PatchLines
.GetErrorString();
321 m_UnicodeType
= PatchLines
.GetUnicodeType();
323 nLineCount
= PatchLines
.GetCount();
324 //now we got all the lines of the patch file
325 //in our array - parsing can start...
327 for(nIndex
=0;PatchLines
.GetCount();nIndex
++)
329 sLine
= PatchLines
.GetAt(nIndex
);
330 if(sLine
.Left(10).Compare(_T("diff --git")) == 0)
332 this->m_IsGitPatch
=true;
337 //first, skip possible garbage at the beginning
338 //garbage is finished when a line starts with "Index: "
339 //and the next line consists of only "=" characters
342 for (nIndex
=0; nIndex
<PatchLines
.GetCount(); nIndex
++)
344 sLine
= PatchLines
.GetAt(nIndex
);
346 if (sLine
.Left(4).Compare(_T("--- "))==0)
348 if ((nIndex
+1)<PatchLines
.GetCount())
350 sLine
= PatchLines
.GetAt(nIndex
+1);
352 if(sLine
.IsEmpty()&&m_IsGitPatch
)
355 sLine
.Replace(_T("="), _T(""));
362 if ((PatchLines
.GetCount()-nIndex
) < 2)
364 //no file entry found.
365 m_sErrorMessage
.LoadString(IDS_ERR_PATCH_NOINDEX
);
370 return ParserGitPatch(PatchLines
,nIndex
);
372 //from this point on we have the real unified diff data
374 Chunks
* chunks
= NULL
;
375 Chunk
* chunk
= NULL
;
376 int nAddLineCount
= 0;
377 int nRemoveLineCount
= 0;
378 int nContextLineCount
= 0;
379 for ( ;nIndex
<PatchLines
.GetCount(); nIndex
++)
381 sLine
= PatchLines
.GetAt(nIndex
);
382 ending
= PatchLines
.GetLineEnding(nIndex
);
383 if (ending
!= EOL_NOENDING
)
384 ending
= EOL_AUTOLINE
;
387 if ((sLine
.Left(4).Compare(_T("--- "))==0)&&((sLine
.Find('\t') >= 0)||this->m_IsGitPatch
))
392 //this is a new file diff, so add the last one to
394 m_arFileDiffs
.Add(chunks
);
396 chunks
= new Chunks();
398 int nTab
= sLine
.Find('\t');
403 nTab
=sLine
.GetLength();
409 chunks
->sFilePath
= sLine
.Mid(filestart
, nTab
-filestart
).Trim();
415 case 0: //Index: <filepath>
418 if ((nIndex
+1)<PatchLines
.GetCount())
420 nextLine
= PatchLines
.GetAt(nIndex
+1);
421 if (!nextLine
.IsEmpty())
423 nextLine
.Replace(_T("="), _T(""));
424 if (nextLine
.IsEmpty())
428 //this is a new file diff, so add the last one to
430 m_arFileDiffs
.Add(chunks
);
432 chunks
= new Chunks();
433 int nColon
= sLine
.Find(':');
436 chunks
->sFilePath
= sLine
.Mid(nColon
+1).Trim();
437 if (chunks
->sFilePath
.Find('\t')>=0)
438 chunks
->sFilePath
.Left(chunks
->sFilePath
.Find('\t')).TrimRight();
439 if (chunks
->sFilePath
.Right(9).Compare(_T("(deleted)"))==0)
440 chunks
->sFilePath
.Left(chunks
->sFilePath
.GetLength()-9).TrimRight();
441 if (chunks
->sFilePath
.Right(7).Compare(_T("(added)"))==0)
442 chunks
->sFilePath
.Left(chunks
->sFilePath
.GetLength()-7).TrimRight();
458 //was not found at the start of a file diff!
465 case 1: //====================
467 sLine
.Replace(_T("="), _T(""));
470 // if the next line is already the start of the chunk,
471 // then the patch/diff file was not created by svn. But we
472 // still try to use it
473 if (PatchLines
.GetCount() > (nIndex
+ 1))
476 if (PatchLines
.GetAt(nIndex
+1).Left(2).Compare(_T("@@"))==0)
486 //=========================
488 m_sErrorMessage
.Format(IDS_ERR_PATCH_NOEQUATIONCHARLINE
, nIndex
);
493 case 2: //--- <filepath>
495 if (sLine
.Left(3).Compare(_T("---"))!=0)
497 //no starting "---" found
498 //seems to be either garbage or just
499 //a binary file. So start over...
509 sLine
= sLine
.Mid(3); //remove the "---"
511 //at the end of the filepath there's a revision number...
512 int bracket
= sLine
.ReverseFind('(');
514 // some patch files can have another '(' char, especially ones created in Chinese OS
515 bracket
= sLine
.ReverseFind(0xff08);
516 CString num
= sLine
.Mid(bracket
); //num = "(revision xxxxx)"
517 num
= num
.Mid(num
.Find(' '));
518 num
= num
.Trim(_T(" )"));
519 // here again, check for the Chinese bracket
520 num
= num
.Trim(0xff09);
521 chunks
->sRevision
= num
;
524 if (chunks
->sFilePath
.IsEmpty())
525 chunks
->sFilePath
= sLine
.Trim();
528 chunks
->sFilePath
= sLine
.Left(bracket
-1).Trim();
529 if (chunks
->sFilePath
.Find('\t')>=0)
531 chunks
->sFilePath
= chunks
->sFilePath
.Left(chunks
->sFilePath
.Find('\t'));
536 case 3: //+++ <filepath>
538 if (sLine
.Left(3).Compare(_T("+++"))!=0)
540 //no starting "+++" found
541 m_sErrorMessage
.Format(IDS_ERR_PATCH_NOADDFILELINE
, nIndex
);
544 sLine
= sLine
.Mid(3); //remove the "---"
546 //at the end of the filepath there's a revision number...
547 int bracket
= sLine
.ReverseFind('(');
549 // some patch files can have another '(' char, especially ones created in Chinese OS
550 bracket
= sLine
.ReverseFind(0xff08);
551 CString num
= sLine
.Mid(bracket
); //num = "(revision xxxxx)"
552 num
= num
.Mid(num
.Find(' '));
553 num
= num
.Trim(_T(" )"));
554 // here again, check for the Chinese bracket
555 num
= num
.Trim(0xff09);
556 chunks
->sRevision2
= num
;
558 chunks
->sFilePath2
= sLine
.Trim();
560 chunks
->sFilePath2
= sLine
.Left(bracket
-1).Trim();
561 if (chunks
->sFilePath2
.Find('\t')>=0)
563 chunks
->sFilePath2
= chunks
->sFilePath2
.Left(chunks
->sFilePath2
.Find('\t'));
568 case 4: //@@ -xxx,xxx +xxx,xxx @@
570 //start of a new chunk
571 if (sLine
.Left(2).Compare(_T("@@"))!=0)
573 //chunk doesn't start with "@@"
574 //so there's garbage in between two file diffs
582 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
584 delete chunks
->chunks
.GetAt(i
);
586 chunks
->chunks
.RemoveAll();
591 break; //skip the garbage
593 sLine
= sLine
.Mid(2);
594 sLine
= sLine
.Trim();
596 CString sRemove
= sLine
.Left(sLine
.Find(' '));
597 CString sAdd
= sLine
.Mid(sLine
.Find(' '));
598 chunk
->lRemoveStart
= (-_ttol(sRemove
));
599 if (sRemove
.Find(',')>=0)
601 sRemove
= sRemove
.Mid(sRemove
.Find(',')+1);
602 chunk
->lRemoveLength
= _ttol(sRemove
);
606 chunk
->lRemoveStart
= 0;
607 chunk
->lRemoveLength
= (-_ttol(sRemove
));
609 chunk
->lAddStart
= _ttol(sAdd
);
610 if (sAdd
.Find(',')>=0)
612 sAdd
= sAdd
.Mid(sAdd
.Find(',')+1);
613 chunk
->lAddLength
= _ttol(sAdd
);
617 chunk
->lAddStart
= 1;
618 chunk
->lAddLength
= _ttol(sAdd
);
623 case 5: //[ |+|-] <sourceline>
625 //this line is either a context line (with a ' ' in front)
626 //a line added (with a '+' in front)
627 //or a removed line (with a '-' in front)
632 type
= sLine
.GetAt(0);
635 //it's a context line - we don't use them here right now
636 //but maybe in the future the patch algorithm can be
637 //extended to use those in case the file to patch has
638 //already changed and no base file is around...
639 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
640 chunk
->arLinesStates
.Add(PATCHSTATE_CONTEXT
);
641 chunk
->arEOLs
.push_back(ending
);
644 else if (type
== '\\')
646 //it's a context line (sort of):
647 //warnings start with a '\' char (e.g. "\ No newline at end of file")
648 //so just ignore this...
650 else if (type
== '-')
653 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
654 chunk
->arLinesStates
.Add(PATCHSTATE_REMOVED
);
655 chunk
->arEOLs
.push_back(ending
);
658 else if (type
== '+')
661 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
662 chunk
->arLinesStates
.Add(PATCHSTATE_ADDED
);
663 chunk
->arEOLs
.push_back(ending
);
668 //none of those lines! what the hell happened here?
669 m_sErrorMessage
.Format(IDS_ERR_PATCH_UNKOWNLINETYPE
, nIndex
);
672 if ((chunk
->lAddLength
== (nAddLineCount
+ nContextLineCount
)) &&
673 chunk
->lRemoveLength
== (nRemoveLineCount
+ nContextLineCount
))
677 chunks
->chunks
.Add(chunk
);
682 nContextLineCount
= 0;
683 nRemoveLineCount
= 0;
691 } // for ( ;nIndex<m_PatchLines.GetCount(); nIndex++)
694 m_sErrorMessage
.LoadString(IDS_ERR_PATCH_CHUNKMISMATCH
);
698 m_arFileDiffs
.Add(chunks
);
705 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
707 delete chunks
->chunks
.GetAt(i
);
709 chunks
->chunks
.RemoveAll();
716 CString
CPatch::GetFilename(int nIndex
)
720 if (nIndex
< m_arFileDiffs
.GetCount())
722 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
723 CString filepath
= Strip(c
->sFilePath
);
729 CString
CPatch::GetRevision(int nIndex
)
733 if (nIndex
< m_arFileDiffs
.GetCount())
735 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
741 CString
CPatch::GetFilename2(int nIndex
)
745 if (nIndex
< m_arFileDiffs
.GetCount())
747 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
748 CString filepath
= Strip(c
->sFilePath2
);
754 CString
CPatch::GetRevision2(int nIndex
)
758 if (nIndex
< m_arFileDiffs
.GetCount())
760 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
761 return c
->sRevision2
;
766 BOOL
CPatch::PatchFile(const CString
& sPath
, const CString
& sSavePath
, const CString
& sBaseFile
)
768 if (PathIsDirectory(sPath
))
770 m_sErrorMessage
.Format(IDS_ERR_PATCH_INVALIDPATCHFILE
, (LPCTSTR
)sPath
);
773 // find the entry in the patch file which matches the full path given in sPath.
775 // use the longest path that matches
777 for (int i
=0; i
<GetNumberOfFiles(); i
++)
779 CString temppath
= sPath
;
780 CString temp
= GetFilename(i
);
781 temppath
.Replace('/', '\\');
782 temp
.Replace('/', '\\');
783 if (temppath
.Mid(temppath
.GetLength()-temp
.GetLength()-1, 1).CompareNoCase(_T("\\"))==0)
785 temppath
= temppath
.Right(temp
.GetLength());
786 if ((temp
.CompareNoCase(temppath
)==0))
788 if (nMaxMatch
< temp
.GetLength())
790 nMaxMatch
= temp
.GetLength();
795 else if (temppath
.CompareNoCase(temp
)==0)
797 if ((nIndex
< 0)&&(! temp
.IsEmpty()))
805 m_sErrorMessage
.Format(IDS_ERR_PATCH_FILENOTINPATCH
, (LPCTSTR
)sPath
);
810 CString sPatchFile
= sBaseFile
.IsEmpty() ? sPath
: sBaseFile
;
811 if (PathFileExists(sPatchFile
))
813 g_crasher
.AddFile((LPCSTR
)(LPCTSTR
)sPatchFile
, (LPCSTR
)(LPCTSTR
)_T("File to patch"));
815 CFileTextLines PatchLines
;
816 CFileTextLines PatchLinesResult
;
817 PatchLines
.Load(sPatchFile
);
818 PatchLinesResult
= PatchLines
; //.Copy(PatchLines);
819 PatchLines
.CopySettings(&PatchLinesResult
);
821 Chunks
* chunks
= m_arFileDiffs
.GetAt(nIndex
);
823 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
825 Chunk
* chunk
= chunks
->chunks
.GetAt(i
);
826 LONG lRemoveLine
= chunk
->lRemoveStart
;
827 LONG lAddLine
= chunk
->lAddStart
;
828 for (int j
=0; j
<chunk
->arLines
.GetCount(); j
++)
830 CString sPatchLine
= chunk
->arLines
.GetAt(j
);
831 EOL ending
= chunk
->arEOLs
[j
];
832 if ((m_UnicodeType
!= CFileTextLines::UTF8
)&&(m_UnicodeType
!= CFileTextLines::UTF8BOM
))
834 if ((PatchLines
.GetUnicodeType()==CFileTextLines::UTF8
)||(m_UnicodeType
== CFileTextLines::UTF8BOM
))
836 // convert the UTF-8 contents in CString sPatchLine into a CStringA
837 sPatchLine
= CUnicodeUtils::GetUnicode(CStringA(sPatchLine
));
840 int nPatchState
= (int)chunk
->arLinesStates
.GetAt(j
);
843 case PATCHSTATE_REMOVED
:
845 if ((lAddLine
> PatchLines
.GetCount())||(PatchLines
.GetCount()==0))
847 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, _T(""), (LPCTSTR
)sPatchLine
);
852 if ((sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
-1))!=0)&&(!HasExpandedKeyWords(sPatchLine
)))
854 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, (LPCTSTR
)sPatchLine
, (LPCTSTR
)PatchLines
.GetAt(lAddLine
-1));
857 if (lAddLine
> PatchLines
.GetCount())
859 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, (LPCTSTR
)sPatchLine
, _T(""));
862 PatchLines
.RemoveAt(lAddLine
-1);
865 case PATCHSTATE_ADDED
:
869 PatchLines
.InsertAt(lAddLine
-1, sPatchLine
, ending
);
873 case PATCHSTATE_CONTEXT
:
875 if (lAddLine
> PatchLines
.GetCount())
877 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, _T(""), (LPCTSTR
)sPatchLine
);
882 if (lRemoveLine
== 0)
884 if ((sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
-1))!=0) &&
885 (!HasExpandedKeyWords(sPatchLine
)) &&
886 (lRemoveLine
<= PatchLines
.GetCount()) &&
887 (sPatchLine
.Compare(PatchLines
.GetAt(lRemoveLine
-1))!=0))
889 if ((lAddLine
< PatchLines
.GetCount())&&(sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
))==0))
891 else if (((lAddLine
+ 1) < PatchLines
.GetCount())&&(sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
+1))==0))
893 else if ((lRemoveLine
< PatchLines
.GetCount())&&(sPatchLine
.Compare(PatchLines
.GetAt(lRemoveLine
))==0))
897 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, (LPCTSTR
)sPatchLine
, (LPCTSTR
)PatchLines
.GetAt(lAddLine
-1));
908 } // switch (nPatchState)
909 } // for (j=0; j<chunk->arLines.GetCount(); j++)
910 } // for (int i=0; i<chunks->chunks.GetCount(); i++)
911 if (!sSavePath
.IsEmpty())
913 PatchLines
.Save(sSavePath
, false);
918 BOOL
CPatch::HasExpandedKeyWords(const CString
& line
)
920 if (line
.Find(_T("$LastChangedDate"))>=0)
922 if (line
.Find(_T("$Date"))>=0)
924 if (line
.Find(_T("$LastChangedRevision"))>=0)
926 if (line
.Find(_T("$Rev"))>=0)
928 if (line
.Find(_T("$LastChangedBy"))>=0)
930 if (line
.Find(_T("$Author"))>=0)
932 if (line
.Find(_T("$HeadURL"))>=0)
934 if (line
.Find(_T("$URL"))>=0)
936 if (line
.Find(_T("$Id"))>=0)
941 CString
CPatch::CheckPatchPath(const CString
& path
)
943 //first check if the path already matches
944 if (CountMatches(path
) > (GetNumberOfFiles()/3))
946 //now go up the tree and try again
947 CString upperpath
= path
;
948 while (upperpath
.ReverseFind('\\')>0)
950 upperpath
= upperpath
.Left(upperpath
.ReverseFind('\\'));
951 if (CountMatches(upperpath
) > (GetNumberOfFiles()/3))
954 //still no match found. So try sub folders
957 CDirFileEnum
filefinder(path
);
958 while (filefinder
.NextFile(subpath
, &isDir
))
962 if (g_GitAdminDir
.IsAdminDirPath(subpath
))
964 if (CountMatches(subpath
) > (GetNumberOfFiles()/3))
968 // if a patch file only contains newly added files
969 // we can't really find the correct path.
970 // But: we can compare paths strings without the filenames
971 // and check if at least those match
973 while (upperpath
.ReverseFind('\\')>0)
975 upperpath
= upperpath
.Left(upperpath
.ReverseFind('\\'));
976 if (CountDirMatches(upperpath
) > (GetNumberOfFiles()/3))
983 int CPatch::CountMatches(const CString
& path
)
986 for (int i
=0; i
<GetNumberOfFiles(); ++i
)
988 CString temp
= GetFilename(i
);
989 temp
.Replace('/', '\\');
990 if (PathIsRelative(temp
))
991 temp
= path
+ _T("\\")+ temp
;
992 if (PathFileExists(temp
))
998 int CPatch::CountDirMatches(const CString
& path
)
1001 for (int i
=0; i
<GetNumberOfFiles(); ++i
)
1003 CString temp
= GetFilename(i
);
1004 temp
.Replace('/', '\\');
1005 if (PathIsRelative(temp
))
1006 temp
= path
+ _T("\\")+ temp
;
1007 // remove the filename
1008 temp
= temp
.Left(temp
.ReverseFind('\\'));
1009 if (PathFileExists(temp
))
1015 BOOL
CPatch::StripPrefixes(const CString
& path
)
1017 int nSlashesMax
= 0;
1018 for (int i
=0; i
<GetNumberOfFiles(); i
++)
1020 CString filename
= GetFilename(i
);
1021 filename
.Replace('/','\\');
1022 int nSlashes
= filename
.Replace('\\','/');
1023 nSlashesMax
= max(nSlashesMax
,nSlashes
);
1026 for (int nStrip
=1;nStrip
<nSlashesMax
;nStrip
++)
1029 if ( CountMatches(path
) > GetNumberOfFiles()/3 )
1031 // Use current m_nStrip
1036 // Stripping doesn't help so reset it again
1041 CString
CPatch::Strip(const CString
& filename
)
1043 CString s
= filename
;
1046 // Remove windows drive letter "c:"
1047 if ( s
.GetLength()>2 && s
[1]==':')
1052 for (int nStrip
=1;nStrip
<=m_nStrip
;nStrip
++)
1054 // "/home/ts/my-working-copy/dir/file.txt"
1055 // "home/ts/my-working-copy/dir/file.txt"
1056 // "ts/my-working-copy/dir/file.txt"
1057 // "my-working-copy/dir/file.txt"
1059 s
= s
.Mid(s
.FindOneOf(_T("/\\"))+1);
1065 CString
CPatch::RemoveUnicodeBOM(const CString
& str
)
1067 if (str
.GetLength()==0)
1069 if (str
[0] == 0xFEFF)