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 if( chunks
->sFilePath
.Find(_T("b/")) == 0 )
148 chunks
->sFilePath
=chunks
->sFilePath
.Mid(2);
151 chunks
->sFilePath
.Replace(_T('/'),_T('\\'));
153 if(chunks
->sFilePath
== _T("\\dev\\null"))
154 chunks
->sFilePath
= _T("NUL");
158 if( sLine
.Find(_T("+++ ")) == 0 )
160 sLine
= sLine
.Mid(3); //remove the "---"
163 //at the end of the filepath there's a revision number...
164 int bracket
= sLine
.ReverseFind('(');
166 // some patch files can have another '(' char, especially ones created in Chinese OS
167 bracket
= sLine
.ReverseFind(0xff08);
170 chunks
->sFilePath2
= sLine
.Trim();
172 chunks
->sFilePath2
= sLine
.Left(bracket
-1).Trim();
173 if (chunks
->sFilePath2
.Find('\t')>=0)
175 chunks
->sFilePath2
= chunks
->sFilePath2
.Left(chunks
->sFilePath2
.Find('\t'));
177 if( chunks
->sFilePath2
.Find(_T("a/")) == 0 )
178 chunks
->sFilePath2
=chunks
->sFilePath2
.Mid(2);
180 if( chunks
->sFilePath2
.Find(_T("b/")) == 0 )
181 chunks
->sFilePath2
=chunks
->sFilePath2
.Mid(2);
183 chunks
->sFilePath2
.Replace(_T('/'),_T('\\'));
185 if(chunks
->sFilePath2
== _T("\\dev\\null"))
186 chunks
->sFilePath2
= _T("NUL");
189 //@@ -xxx,xxx +xxx,xxx @@
190 if( sLine
.Find(_T("@@")) == 0 )
192 sLine
= sLine
.Mid(2);
193 sLine
= sLine
.Trim();
195 CString sRemove
= sLine
.Left(sLine
.Find(' '));
196 CString sAdd
= sLine
.Mid(sLine
.Find(' '));
197 chunk
->lRemoveStart
= (-_ttol(sRemove
));
198 if (sRemove
.Find(',')>=0)
200 sRemove
= sRemove
.Mid(sRemove
.Find(',')+1);
201 chunk
->lRemoveLength
= _ttol(sRemove
);
205 chunk
->lRemoveStart
= 0;
206 chunk
->lRemoveLength
= (-_ttol(sRemove
));
208 chunk
->lAddStart
= _ttol(sAdd
);
209 if (sAdd
.Find(',')>=0)
211 sAdd
= sAdd
.Mid(sAdd
.Find(',')+1);
212 chunk
->lAddLength
= _ttol(sAdd
);
216 chunk
->lAddStart
= 1;
217 chunk
->lAddLength
= _ttol(sAdd
);
226 case 5: //[ |+|-] <sourceline>
228 //this line is either a context line (with a ' ' in front)
229 //a line added (with a '+' in front)
230 //or a removed line (with a '-' in front)
235 type
= sLine
.GetAt(0);
238 //it's a context line - we don't use them here right now
239 //but maybe in the future the patch algorithm can be
240 //extended to use those in case the file to patch has
241 //already changed and no base file is around...
242 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
243 chunk
->arLinesStates
.Add(PATCHSTATE_CONTEXT
);
244 chunk
->arEOLs
.push_back(ending
);
247 else if (type
== '\\')
249 //it's a context line (sort of):
250 //warnings start with a '\' char (e.g. "\ No newline at end of file")
251 //so just ignore this...
253 else if (type
== '-')
256 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
257 chunk
->arLinesStates
.Add(PATCHSTATE_REMOVED
);
258 chunk
->arEOLs
.push_back(ending
);
261 else if (type
== '+')
264 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
265 chunk
->arLinesStates
.Add(PATCHSTATE_ADDED
);
266 chunk
->arEOLs
.push_back(ending
);
271 //none of those lines! what the hell happened here?
272 m_sErrorMessage
.Format(IDS_ERR_PATCH_UNKOWNLINETYPE
, nIndex
);
275 if ((chunk
->lAddLength
== (nAddLineCount
+ nContextLineCount
)) &&
276 chunk
->lRemoveLength
== (nRemoveLineCount
+ nContextLineCount
))
280 chunks
->chunks
.Add(chunk
);
285 nContextLineCount
= 0;
286 nRemoveLineCount
= 0;
294 } // for ( ;nIndex<m_PatchLines.GetCount(); nIndex++)
297 m_sErrorMessage
.LoadString(IDS_ERR_PATCH_CHUNKMISMATCH
);
301 m_arFileDiffs
.Add(chunks
);
309 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
311 delete chunks
->chunks
.GetAt(i
);
313 chunks
->chunks
.RemoveAll();
320 BOOL
CPatch::OpenUnifiedDiffFile(const CString
& filename
)
323 EOL ending
= EOL_NOENDING
;
325 INT_PTR nLineCount
= 0;
326 g_crasher
.AddFile((LPCSTR
)(LPCTSTR
)filename
, (LPCSTR
)(LPCTSTR
)_T("unified diff file"));
328 CFileTextLines PatchLines
;
329 if (!PatchLines
.Load(filename
))
331 m_sErrorMessage
= PatchLines
.GetErrorString();
334 m_UnicodeType
= PatchLines
.GetUnicodeType();
336 nLineCount
= PatchLines
.GetCount();
337 //now we got all the lines of the patch file
338 //in our array - parsing can start...
340 for(nIndex
=0;PatchLines
.GetCount();nIndex
++)
342 sLine
= PatchLines
.GetAt(nIndex
);
343 if(sLine
.Left(10).Compare(_T("diff --git")) == 0)
345 this->m_IsGitPatch
=true;
350 //first, skip possible garbage at the beginning
351 //garbage is finished when a line starts with "Index: "
352 //and the next line consists of only "=" characters
355 for (nIndex
=0; nIndex
<PatchLines
.GetCount(); nIndex
++)
357 sLine
= PatchLines
.GetAt(nIndex
);
359 if (sLine
.Left(4).Compare(_T("--- "))==0)
361 if ((nIndex
+1)<PatchLines
.GetCount())
363 sLine
= PatchLines
.GetAt(nIndex
+1);
365 if(sLine
.IsEmpty()&&m_IsGitPatch
)
368 sLine
.Replace(_T("="), _T(""));
375 if ((PatchLines
.GetCount()-nIndex
) < 2)
377 //no file entry found.
378 m_sErrorMessage
.LoadString(IDS_ERR_PATCH_NOINDEX
);
383 return ParserGitPatch(PatchLines
,nIndex
);
385 //from this point on we have the real unified diff data
387 Chunks
* chunks
= NULL
;
388 Chunk
* chunk
= NULL
;
389 int nAddLineCount
= 0;
390 int nRemoveLineCount
= 0;
391 int nContextLineCount
= 0;
392 for ( ;nIndex
<PatchLines
.GetCount(); nIndex
++)
394 sLine
= PatchLines
.GetAt(nIndex
);
395 ending
= PatchLines
.GetLineEnding(nIndex
);
396 if (ending
!= EOL_NOENDING
)
397 ending
= EOL_AUTOLINE
;
400 if ((sLine
.Left(4).Compare(_T("--- "))==0)&&((sLine
.Find('\t') >= 0)||this->m_IsGitPatch
))
405 //this is a new file diff, so add the last one to
407 m_arFileDiffs
.Add(chunks
);
409 chunks
= new Chunks();
411 int nTab
= sLine
.Find('\t');
416 nTab
=sLine
.GetLength();
422 chunks
->sFilePath
= sLine
.Mid(filestart
, nTab
-filestart
).Trim();
428 case 0: //Index: <filepath>
431 if ((nIndex
+1)<PatchLines
.GetCount())
433 nextLine
= PatchLines
.GetAt(nIndex
+1);
434 if (!nextLine
.IsEmpty())
436 nextLine
.Replace(_T("="), _T(""));
437 if (nextLine
.IsEmpty())
441 //this is a new file diff, so add the last one to
443 m_arFileDiffs
.Add(chunks
);
445 chunks
= new Chunks();
446 int nColon
= sLine
.Find(':');
449 chunks
->sFilePath
= sLine
.Mid(nColon
+1).Trim();
450 if (chunks
->sFilePath
.Find('\t')>=0)
451 chunks
->sFilePath
.Left(chunks
->sFilePath
.Find('\t')).TrimRight();
452 if (chunks
->sFilePath
.Right(9).Compare(_T("(deleted)"))==0)
453 chunks
->sFilePath
.Left(chunks
->sFilePath
.GetLength()-9).TrimRight();
454 if (chunks
->sFilePath
.Right(7).Compare(_T("(added)"))==0)
455 chunks
->sFilePath
.Left(chunks
->sFilePath
.GetLength()-7).TrimRight();
471 //was not found at the start of a file diff!
478 case 1: //====================
480 sLine
.Replace(_T("="), _T(""));
483 // if the next line is already the start of the chunk,
484 // then the patch/diff file was not created by svn. But we
485 // still try to use it
486 if (PatchLines
.GetCount() > (nIndex
+ 1))
489 if (PatchLines
.GetAt(nIndex
+1).Left(2).Compare(_T("@@"))==0)
499 //=========================
501 m_sErrorMessage
.Format(IDS_ERR_PATCH_NOEQUATIONCHARLINE
, nIndex
);
506 case 2: //--- <filepath>
508 if (sLine
.Left(3).Compare(_T("---"))!=0)
510 //no starting "---" found
511 //seems to be either garbage or just
512 //a binary file. So start over...
522 sLine
= sLine
.Mid(3); //remove the "---"
524 //at the end of the filepath there's a revision number...
525 int bracket
= sLine
.ReverseFind('(');
527 // some patch files can have another '(' char, especially ones created in Chinese OS
528 bracket
= sLine
.ReverseFind(0xff08);
529 CString num
= sLine
.Mid(bracket
); //num = "(revision xxxxx)"
530 num
= num
.Mid(num
.Find(' '));
531 num
= num
.Trim(_T(" )"));
532 // here again, check for the Chinese bracket
533 num
= num
.Trim(0xff09);
534 chunks
->sRevision
= num
;
537 if (chunks
->sFilePath
.IsEmpty())
538 chunks
->sFilePath
= sLine
.Trim();
541 chunks
->sFilePath
= sLine
.Left(bracket
-1).Trim();
542 if (chunks
->sFilePath
.Find('\t')>=0)
544 chunks
->sFilePath
= chunks
->sFilePath
.Left(chunks
->sFilePath
.Find('\t'));
549 case 3: //+++ <filepath>
551 if (sLine
.Left(3).Compare(_T("+++"))!=0)
553 //no starting "+++" found
554 m_sErrorMessage
.Format(IDS_ERR_PATCH_NOADDFILELINE
, nIndex
);
557 sLine
= sLine
.Mid(3); //remove the "---"
559 //at the end of the filepath there's a revision number...
560 int bracket
= sLine
.ReverseFind('(');
562 // some patch files can have another '(' char, especially ones created in Chinese OS
563 bracket
= sLine
.ReverseFind(0xff08);
564 CString num
= sLine
.Mid(bracket
); //num = "(revision xxxxx)"
565 num
= num
.Mid(num
.Find(' '));
566 num
= num
.Trim(_T(" )"));
567 // here again, check for the Chinese bracket
568 num
= num
.Trim(0xff09);
569 chunks
->sRevision2
= num
;
571 chunks
->sFilePath2
= sLine
.Trim();
573 chunks
->sFilePath2
= sLine
.Left(bracket
-1).Trim();
574 if (chunks
->sFilePath2
.Find('\t')>=0)
576 chunks
->sFilePath2
= chunks
->sFilePath2
.Left(chunks
->sFilePath2
.Find('\t'));
581 case 4: //@@ -xxx,xxx +xxx,xxx @@
583 //start of a new chunk
584 if (sLine
.Left(2).Compare(_T("@@"))!=0)
586 //chunk doesn't start with "@@"
587 //so there's garbage in between two file diffs
595 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
597 delete chunks
->chunks
.GetAt(i
);
599 chunks
->chunks
.RemoveAll();
604 break; //skip the garbage
606 sLine
= sLine
.Mid(2);
607 sLine
= sLine
.Trim();
609 CString sRemove
= sLine
.Left(sLine
.Find(' '));
610 CString sAdd
= sLine
.Mid(sLine
.Find(' '));
611 chunk
->lRemoveStart
= (-_ttol(sRemove
));
612 if (sRemove
.Find(',')>=0)
614 sRemove
= sRemove
.Mid(sRemove
.Find(',')+1);
615 chunk
->lRemoveLength
= _ttol(sRemove
);
619 chunk
->lRemoveStart
= 0;
620 chunk
->lRemoveLength
= (-_ttol(sRemove
));
622 chunk
->lAddStart
= _ttol(sAdd
);
623 if (sAdd
.Find(',')>=0)
625 sAdd
= sAdd
.Mid(sAdd
.Find(',')+1);
626 chunk
->lAddLength
= _ttol(sAdd
);
630 chunk
->lAddStart
= 1;
631 chunk
->lAddLength
= _ttol(sAdd
);
636 case 5: //[ |+|-] <sourceline>
638 //this line is either a context line (with a ' ' in front)
639 //a line added (with a '+' in front)
640 //or a removed line (with a '-' in front)
645 type
= sLine
.GetAt(0);
648 //it's a context line - we don't use them here right now
649 //but maybe in the future the patch algorithm can be
650 //extended to use those in case the file to patch has
651 //already changed and no base file is around...
652 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
653 chunk
->arLinesStates
.Add(PATCHSTATE_CONTEXT
);
654 chunk
->arEOLs
.push_back(ending
);
657 else if (type
== '\\')
659 //it's a context line (sort of):
660 //warnings start with a '\' char (e.g. "\ No newline at end of file")
661 //so just ignore this...
663 else if (type
== '-')
666 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
667 chunk
->arLinesStates
.Add(PATCHSTATE_REMOVED
);
668 chunk
->arEOLs
.push_back(ending
);
671 else if (type
== '+')
674 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
675 chunk
->arLinesStates
.Add(PATCHSTATE_ADDED
);
676 chunk
->arEOLs
.push_back(ending
);
681 //none of those lines! what the hell happened here?
682 m_sErrorMessage
.Format(IDS_ERR_PATCH_UNKOWNLINETYPE
, nIndex
);
685 if ((chunk
->lAddLength
== (nAddLineCount
+ nContextLineCount
)) &&
686 chunk
->lRemoveLength
== (nRemoveLineCount
+ nContextLineCount
))
690 chunks
->chunks
.Add(chunk
);
695 nContextLineCount
= 0;
696 nRemoveLineCount
= 0;
704 } // for ( ;nIndex<m_PatchLines.GetCount(); nIndex++)
707 m_sErrorMessage
.LoadString(IDS_ERR_PATCH_CHUNKMISMATCH
);
711 m_arFileDiffs
.Add(chunks
);
718 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
720 delete chunks
->chunks
.GetAt(i
);
722 chunks
->chunks
.RemoveAll();
729 CString
CPatch::GetFilename(int nIndex
)
733 if (nIndex
< m_arFileDiffs
.GetCount())
735 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
736 CString filepath
= Strip(c
->sFilePath
);
742 CString
CPatch::GetRevision(int nIndex
)
746 if (nIndex
< m_arFileDiffs
.GetCount())
748 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
754 CString
CPatch::GetFilename2(int nIndex
)
758 if (nIndex
< m_arFileDiffs
.GetCount())
760 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
761 CString filepath
= Strip(c
->sFilePath2
);
767 CString
CPatch::GetRevision2(int nIndex
)
771 if (nIndex
< m_arFileDiffs
.GetCount())
773 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
774 return c
->sRevision2
;
779 BOOL
CPatch::PatchFile(const CString
& sPath
, const CString
& sSavePath
, const CString
& sBaseFile
)
781 if (PathIsDirectory(sPath
))
783 m_sErrorMessage
.Format(IDS_ERR_PATCH_INVALIDPATCHFILE
, (LPCTSTR
)sPath
);
786 // find the entry in the patch file which matches the full path given in sPath.
788 // use the longest path that matches
790 for (int i
=0; i
<GetNumberOfFiles(); i
++)
792 CString temppath
= sPath
;
793 CString temp
= GetFilename(i
);
794 temppath
.Replace('/', '\\');
795 temp
.Replace('/', '\\');
796 if (temppath
.Mid(temppath
.GetLength()-temp
.GetLength()-1, 1).CompareNoCase(_T("\\"))==0)
798 temppath
= temppath
.Right(temp
.GetLength());
799 if ((temp
.CompareNoCase(temppath
)==0))
801 if (nMaxMatch
< temp
.GetLength())
803 nMaxMatch
= temp
.GetLength();
808 else if (temppath
.CompareNoCase(temp
)==0)
810 if ((nIndex
< 0)&&(! temp
.IsEmpty()))
818 m_sErrorMessage
.Format(IDS_ERR_PATCH_FILENOTINPATCH
, (LPCTSTR
)sPath
);
823 CString sPatchFile
= sBaseFile
.IsEmpty() ? sPath
: sBaseFile
;
824 if (PathFileExists(sPatchFile
))
826 g_crasher
.AddFile((LPCSTR
)(LPCTSTR
)sPatchFile
, (LPCSTR
)(LPCTSTR
)_T("File to patch"));
828 CFileTextLines PatchLines
;
829 CFileTextLines PatchLinesResult
;
830 PatchLines
.Load(sPatchFile
);
831 PatchLinesResult
= PatchLines
; //.Copy(PatchLines);
832 PatchLines
.CopySettings(&PatchLinesResult
);
834 Chunks
* chunks
= m_arFileDiffs
.GetAt(nIndex
);
836 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
838 Chunk
* chunk
= chunks
->chunks
.GetAt(i
);
839 LONG lRemoveLine
= chunk
->lRemoveStart
;
840 LONG lAddLine
= chunk
->lAddStart
;
841 for (int j
=0; j
<chunk
->arLines
.GetCount(); j
++)
843 CString sPatchLine
= chunk
->arLines
.GetAt(j
);
844 EOL ending
= chunk
->arEOLs
[j
];
845 if ((m_UnicodeType
!= CFileTextLines::UTF8
)&&(m_UnicodeType
!= CFileTextLines::UTF8BOM
))
847 if ((PatchLines
.GetUnicodeType()==CFileTextLines::UTF8
)||(m_UnicodeType
== CFileTextLines::UTF8BOM
))
849 // convert the UTF-8 contents in CString sPatchLine into a CStringA
850 sPatchLine
= CUnicodeUtils::GetUnicode(CStringA(sPatchLine
));
853 int nPatchState
= (int)chunk
->arLinesStates
.GetAt(j
);
856 case PATCHSTATE_REMOVED
:
858 if ((lAddLine
> PatchLines
.GetCount())||(PatchLines
.GetCount()==0))
860 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, _T(""), (LPCTSTR
)sPatchLine
);
865 if ((sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
-1))!=0)&&(!HasExpandedKeyWords(sPatchLine
)))
867 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, (LPCTSTR
)sPatchLine
, (LPCTSTR
)PatchLines
.GetAt(lAddLine
-1));
870 if (lAddLine
> PatchLines
.GetCount())
872 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, (LPCTSTR
)sPatchLine
, _T(""));
875 PatchLines
.RemoveAt(lAddLine
-1);
878 case PATCHSTATE_ADDED
:
882 PatchLines
.InsertAt(lAddLine
-1, sPatchLine
, ending
);
886 case PATCHSTATE_CONTEXT
:
888 if (lAddLine
> PatchLines
.GetCount())
890 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, _T(""), (LPCTSTR
)sPatchLine
);
895 if (lRemoveLine
== 0)
897 if ((sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
-1))!=0) &&
898 (!HasExpandedKeyWords(sPatchLine
)) &&
899 (lRemoveLine
<= PatchLines
.GetCount()) &&
900 (sPatchLine
.Compare(PatchLines
.GetAt(lRemoveLine
-1))!=0))
902 if ((lAddLine
< PatchLines
.GetCount())&&(sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
))==0))
904 else if (((lAddLine
+ 1) < PatchLines
.GetCount())&&(sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
+1))==0))
906 else if ((lRemoveLine
< PatchLines
.GetCount())&&(sPatchLine
.Compare(PatchLines
.GetAt(lRemoveLine
))==0))
910 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, (LPCTSTR
)sPatchLine
, (LPCTSTR
)PatchLines
.GetAt(lAddLine
-1));
921 } // switch (nPatchState)
922 } // for (j=0; j<chunk->arLines.GetCount(); j++)
923 } // for (int i=0; i<chunks->chunks.GetCount(); i++)
924 if (!sSavePath
.IsEmpty())
926 PatchLines
.Save(sSavePath
, false);
931 BOOL
CPatch::HasExpandedKeyWords(const CString
& line
)
933 if (line
.Find(_T("$LastChangedDate"))>=0)
935 if (line
.Find(_T("$Date"))>=0)
937 if (line
.Find(_T("$LastChangedRevision"))>=0)
939 if (line
.Find(_T("$Rev"))>=0)
941 if (line
.Find(_T("$LastChangedBy"))>=0)
943 if (line
.Find(_T("$Author"))>=0)
945 if (line
.Find(_T("$HeadURL"))>=0)
947 if (line
.Find(_T("$URL"))>=0)
949 if (line
.Find(_T("$Id"))>=0)
954 CString
CPatch::CheckPatchPath(const CString
& path
)
956 //first check if the path already matches
957 if (CountMatches(path
) > (GetNumberOfFiles()/3))
959 //now go up the tree and try again
960 CString upperpath
= path
;
961 while (upperpath
.ReverseFind('\\')>0)
963 upperpath
= upperpath
.Left(upperpath
.ReverseFind('\\'));
964 if (CountMatches(upperpath
) > (GetNumberOfFiles()/3))
967 //still no match found. So try sub folders
970 CDirFileEnum
filefinder(path
);
971 while (filefinder
.NextFile(subpath
, &isDir
))
975 if (g_GitAdminDir
.IsAdminDirPath(subpath
))
977 if (CountMatches(subpath
) > (GetNumberOfFiles()/3))
981 // if a patch file only contains newly added files
982 // we can't really find the correct path.
983 // But: we can compare paths strings without the filenames
984 // and check if at least those match
986 while (upperpath
.ReverseFind('\\')>0)
988 upperpath
= upperpath
.Left(upperpath
.ReverseFind('\\'));
989 if (CountDirMatches(upperpath
) > (GetNumberOfFiles()/3))
996 int CPatch::CountMatches(const CString
& path
)
999 for (int i
=0; i
<GetNumberOfFiles(); ++i
)
1001 CString temp
= GetFilename(i
);
1002 temp
.Replace('/', '\\');
1003 if (PathIsRelative(temp
))
1004 temp
= path
+ _T("\\")+ temp
;
1005 if (PathFileExists(temp
))
1011 int CPatch::CountDirMatches(const CString
& path
)
1014 for (int i
=0; i
<GetNumberOfFiles(); ++i
)
1016 CString temp
= GetFilename(i
);
1017 temp
.Replace('/', '\\');
1018 if (PathIsRelative(temp
))
1019 temp
= path
+ _T("\\")+ temp
;
1020 // remove the filename
1021 temp
= temp
.Left(temp
.ReverseFind('\\'));
1022 if (PathFileExists(temp
))
1028 BOOL
CPatch::StripPrefixes(const CString
& path
)
1030 int nSlashesMax
= 0;
1031 for (int i
=0; i
<GetNumberOfFiles(); i
++)
1033 CString filename
= GetFilename(i
);
1034 filename
.Replace('/','\\');
1035 int nSlashes
= filename
.Replace('\\','/');
1036 nSlashesMax
= max(nSlashesMax
,nSlashes
);
1039 for (int nStrip
=1;nStrip
<nSlashesMax
;nStrip
++)
1042 if ( CountMatches(path
) > GetNumberOfFiles()/3 )
1044 // Use current m_nStrip
1049 // Stripping doesn't help so reset it again
1054 CString
CPatch::Strip(const CString
& filename
)
1056 CString s
= filename
;
1059 // Remove windows drive letter "c:"
1060 if ( s
.GetLength()>2 && s
[1]==':')
1065 for (int nStrip
=1;nStrip
<=m_nStrip
;nStrip
++)
1067 // "/home/ts/my-working-copy/dir/file.txt"
1068 // "home/ts/my-working-copy/dir/file.txt"
1069 // "ts/my-working-copy/dir/file.txt"
1070 // "my-working-copy/dir/file.txt"
1072 s
= s
.Mid(s
.FindOneOf(_T("/\\"))+1);
1078 CString
CPatch::RemoveUnicodeBOM(const CString
& str
)
1080 if (str
.GetLength()==0)
1082 if (str
[0] == 0xFEFF)