1 // TortoiseMerge - a Diff/Patch program
3 // Copyright (C) 2009-2012 - TortoiseGit
4 // Copyright (C) 2012 - Sven Strickroth <email@cs-ware.de>
5 // Copyright (C) 2004-2009,2011-2012 - TortoiseSVN
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software Foundation,
19 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "UnicodeUtils.h"
24 #include "DirFileEnum.h"
25 #include "TortoiseMerge.h"
27 #include "GitAdminDir.h"
33 static char THIS_FILE
[] = __FILE__
;
38 , m_UnicodeType(CFileTextLines::AUTOTYPE
)
48 void CPatch::FreeMemory()
50 for (int i
=0; i
<m_arFileDiffs
.GetCount(); i
++)
52 Chunks
* chunks
= m_arFileDiffs
.GetAt(i
);
53 for (int j
=0; j
<chunks
->chunks
.GetCount(); j
++)
55 delete chunks
->chunks
.GetAt(j
);
57 chunks
->chunks
.RemoveAll();
60 m_arFileDiffs
.RemoveAll();
63 BOOL
CPatch::ParserGitPatch(CFileTextLines
&PatchLines
,int nIndex
)
66 EOL ending
= EOL_NOENDING
;
69 Chunks
* chunks
= NULL
;
71 int nAddLineCount
= 0;
72 int nRemoveLineCount
= 0;
73 int nContextLineCount
= 0;
74 for ( ;nIndex
<PatchLines
.GetCount(); nIndex
++)
76 sLine
= PatchLines
.GetAt(nIndex
);
77 ending
= PatchLines
.GetLineEnding(nIndex
);
78 if (ending
!= EOL_NOENDING
)
79 ending
= EOL_AUTOLINE
;
86 if( sLine
.Find(_T("diff --git"))==0)
90 //this is a new file diff, so add the last one to
92 m_arFileDiffs
.Add(chunks
);
94 chunks
= new Chunks();
99 if( sLine
.Find(_T("index"))==0 )
101 int dotstart
=sLine
.Find(_T(".."));
104 chunks
->sRevision
= sLine
.Mid(dotstart
-7,7);
105 chunks
->sRevision2
= sLine
.Mid(dotstart
+2,7);
110 if( sLine
.Find(_T("--- "))==0 )
112 if (sLine
.Left(3).Compare(_T("---"))!=0)
114 //no starting "---" found
115 //seems to be either garbage or just
116 //a binary file. So start over...
127 sLine
= sLine
.Mid(3); //remove the "---"
129 //at the end of the filepath there's a revision number...
130 int bracket
= sLine
.ReverseFind('(');
132 // some patch files can have another '(' char, especially ones created in Chinese OS
133 bracket
= sLine
.ReverseFind(0xff08);
137 if (chunks
->sFilePath
.IsEmpty())
138 chunks
->sFilePath
= sLine
.Trim();
141 chunks
->sFilePath
= sLine
.Left(bracket
-1).Trim();
143 if (chunks
->sFilePath
.Find('\t')>=0)
145 chunks
->sFilePath
= chunks
->sFilePath
.Left(chunks
->sFilePath
.Find('\t'));
147 if (chunks
->sFilePath
.Find(_T('"')) == 0 && chunks
->sFilePath
.ReverseFind(_T('"')) == chunks
->sFilePath
.GetLength() - 1)
148 chunks
->sFilePath
=chunks
->sFilePath
.Mid(1, chunks
->sFilePath
.GetLength() - 2);
149 if( chunks
->sFilePath
.Find(_T("a/")) == 0 )
150 chunks
->sFilePath
=chunks
->sFilePath
.Mid(2);
152 if( chunks
->sFilePath
.Find(_T("b/")) == 0 )
153 chunks
->sFilePath
=chunks
->sFilePath
.Mid(2);
156 chunks
->sFilePath
.Replace(_T('/'),_T('\\'));
158 if (chunks
->sFilePath
== _T("\\dev\\null") || chunks
->sFilePath
== _T("/dev/null"))
159 chunks
->sFilePath
= _T("NUL");
163 if( sLine
.Find(_T("+++ ")) == 0 )
165 sLine
= sLine
.Mid(3); //remove the "---"
168 //at the end of the filepath there's a revision number...
169 int bracket
= sLine
.ReverseFind('(');
171 // some patch files can have another '(' char, especially ones created in Chinese OS
172 bracket
= sLine
.ReverseFind(0xff08);
175 chunks
->sFilePath2
= sLine
.Trim();
177 chunks
->sFilePath2
= sLine
.Left(bracket
-1).Trim();
178 if (chunks
->sFilePath2
.Find('\t')>=0)
180 chunks
->sFilePath2
= chunks
->sFilePath2
.Left(chunks
->sFilePath2
.Find('\t'));
182 if (chunks
->sFilePath2
.Find(_T('"')) == 0 && chunks
->sFilePath2
.ReverseFind(_T('"')) == chunks
->sFilePath2
.GetLength() - 1)
183 chunks
->sFilePath2
=chunks
->sFilePath2
.Mid(1, chunks
->sFilePath2
.GetLength() - 2);
184 if( chunks
->sFilePath2
.Find(_T("a/")) == 0 )
185 chunks
->sFilePath2
=chunks
->sFilePath2
.Mid(2);
187 if( chunks
->sFilePath2
.Find(_T("b/")) == 0 )
188 chunks
->sFilePath2
=chunks
->sFilePath2
.Mid(2);
190 chunks
->sFilePath2
.Replace(_T('/'),_T('\\'));
192 if (chunks
->sFilePath2
== _T("\\dev\\null") || chunks
->sFilePath2
== _T("/dev/null"))
193 chunks
->sFilePath2
= _T("NUL");
196 //@@ -xxx,xxx +xxx,xxx @@
197 if( sLine
.Find(_T("@@")) == 0 )
199 sLine
= sLine
.Mid(2);
200 sLine
= sLine
.Trim();
202 CString sRemove
= sLine
.Left(sLine
.Find(' '));
203 CString sAdd
= sLine
.Mid(sLine
.Find(' '));
204 chunk
->lRemoveStart
= (-_ttol(sRemove
));
205 if (sRemove
.Find(',')>=0)
207 sRemove
= sRemove
.Mid(sRemove
.Find(',')+1);
208 chunk
->lRemoveLength
= _ttol(sRemove
);
212 chunk
->lRemoveStart
= 0;
213 chunk
->lRemoveLength
= (-_ttol(sRemove
));
215 chunk
->lAddStart
= _ttol(sAdd
);
216 if (sAdd
.Find(',')>=0)
218 sAdd
= sAdd
.Mid(sAdd
.Find(',')+1);
219 chunk
->lAddLength
= _ttol(sAdd
);
223 chunk
->lAddStart
= 1;
224 chunk
->lAddLength
= _ttol(sAdd
);
233 case 5: //[ |+|-] <sourceline>
235 //this line is either a context line (with a ' ' in front)
236 //a line added (with a '+' in front)
237 //or a removed line (with a '-' in front)
242 type
= sLine
.GetAt(0);
245 //it's a context line - we don't use them here right now
246 //but maybe in the future the patch algorithm can be
247 //extended to use those in case the file to patch has
248 //already changed and no base file is around...
249 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
250 chunk
->arLinesStates
.Add(PATCHSTATE_CONTEXT
);
251 chunk
->arEOLs
.push_back(ending
);
254 else if (type
== '\\')
256 //it's a context line (sort of):
257 //warnings start with a '\' char (e.g. "\ No newline at end of file")
258 //so just ignore this...
260 else if (type
== '-')
263 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
264 chunk
->arLinesStates
.Add(PATCHSTATE_REMOVED
);
265 chunk
->arEOLs
.push_back(ending
);
268 else if (type
== '+')
271 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
272 chunk
->arLinesStates
.Add(PATCHSTATE_ADDED
);
273 chunk
->arEOLs
.push_back(ending
);
278 //none of those lines! what the hell happened here?
279 m_sErrorMessage
.Format(IDS_ERR_PATCH_UNKOWNLINETYPE
, nIndex
);
282 if ((chunk
->lAddLength
== (nAddLineCount
+ nContextLineCount
)) &&
283 chunk
->lRemoveLength
== (nRemoveLineCount
+ nContextLineCount
))
287 chunks
->chunks
.Add(chunk
);
292 nContextLineCount
= 0;
293 nRemoveLineCount
= 0;
301 } // for ( ;nIndex<m_PatchLines.GetCount(); nIndex++)
304 m_sErrorMessage
.LoadString(IDS_ERR_PATCH_CHUNKMISMATCH
);
308 m_arFileDiffs
.Add(chunks
);
316 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
318 delete chunks
->chunks
.GetAt(i
);
320 chunks
->chunks
.RemoveAll();
327 BOOL
CPatch::OpenUnifiedDiffFile(const CString
& filename
)
330 EOL ending
= EOL_NOENDING
;
332 INT_PTR nLineCount
= 0;
333 CCrashReport::Instance().AddFile2(filename
, NULL
, _T("unified diff file"), CR_AF_MAKE_FILE_COPY
);
335 CFileTextLines PatchLines
;
336 if (!PatchLines
.Load(filename
))
338 m_sErrorMessage
= PatchLines
.GetErrorString();
341 m_UnicodeType
= PatchLines
.GetUnicodeType();
343 nLineCount
= PatchLines
.GetCount();
344 //now we got all the lines of the patch file
345 //in our array - parsing can start...
347 for(nIndex
=0;nIndex
<PatchLines
.GetCount();nIndex
++)
349 sLine
= PatchLines
.GetAt((int)nIndex
);
350 if(sLine
.Left(10).Compare(_T("diff --git")) == 0)
352 this->m_IsGitPatch
=true;
357 //first, skip possible garbage at the beginning
358 //garbage is finished when a line starts with "Index: "
359 //and the next line consists of only "=" characters
362 for (nIndex
=0; nIndex
<PatchLines
.GetCount(); nIndex
++)
364 sLine
= PatchLines
.GetAt((int)nIndex
);
366 if (sLine
.Left(4).Compare(_T("--- "))==0)
368 if ((nIndex
+1)<PatchLines
.GetCount())
370 sLine
= PatchLines
.GetAt((int)nIndex
+ 1);
372 if(sLine
.IsEmpty()&&m_IsGitPatch
)
382 if ((PatchLines
.GetCount()-nIndex
) < 2)
384 //no file entry found.
385 m_sErrorMessage
.LoadString(IDS_ERR_PATCH_NOINDEX
);
390 return ParserGitPatch(PatchLines
, (int)nIndex
);
392 //from this point on we have the real unified diff data
394 Chunks
* chunks
= NULL
;
395 Chunk
* chunk
= NULL
;
396 int nAddLineCount
= 0;
397 int nRemoveLineCount
= 0;
398 int nContextLineCount
= 0;
399 for ( ;nIndex
<PatchLines
.GetCount(); nIndex
++)
401 sLine
= PatchLines
.GetAt((int)nIndex
);
402 ending
= PatchLines
.GetLineEnding((int)nIndex
);
403 if (ending
!= EOL_NOENDING
)
404 ending
= EOL_AUTOLINE
;
407 if ((sLine
.Left(4).Compare(_T("--- "))==0)&&((sLine
.Find('\t') >= 0)||this->m_IsGitPatch
))
412 //this is a new file diff, so add the last one to
414 m_arFileDiffs
.Add(chunks
);
416 chunks
= new Chunks();
418 int nTab
= sLine
.Find('\t');
423 nTab
=sLine
.GetLength();
429 chunks
->sFilePath
= sLine
.Mid(filestart
, nTab
-filestart
).Trim();
435 case 0: //Index: <filepath>
438 if ((nIndex
+1)<PatchLines
.GetCount())
440 nextLine
= PatchLines
.GetAt((int)nIndex
+ 1);
441 if (!nextLine
.IsEmpty())
443 nextLine
.Remove('=');
444 if (nextLine
.IsEmpty())
448 //this is a new file diff, so add the last one to
450 m_arFileDiffs
.Add(chunks
);
452 chunks
= new Chunks();
453 int nColon
= sLine
.Find(':');
456 chunks
->sFilePath
= sLine
.Mid(nColon
+1).Trim();
457 if (chunks
->sFilePath
.Find('\t')>=0)
458 chunks
->sFilePath
.Left(chunks
->sFilePath
.Find('\t')).TrimRight();
459 if (chunks
->sFilePath
.Right(9).Compare(_T("(deleted)"))==0)
460 chunks
->sFilePath
.Left(chunks
->sFilePath
.GetLength()-9).TrimRight();
461 if (chunks
->sFilePath
.Right(7).Compare(_T("(added)"))==0)
462 chunks
->sFilePath
.Left(chunks
->sFilePath
.GetLength()-7).TrimRight();
478 //was not found at the start of a file diff!
485 case 1: //====================
490 // if the next line is already the start of the chunk,
491 // then the patch/diff file was not created by svn. But we
492 // still try to use it
493 if (PatchLines
.GetCount() > (nIndex
+ 1))
496 if (PatchLines
.GetAt((int)nIndex
+ 1).Left(2).Compare(_T("@@"))==0)
506 //=========================
508 m_sErrorMessage
.Format(IDS_ERR_PATCH_NOEQUATIONCHARLINE
, nIndex
);
513 case 2: //--- <filepath>
515 if (sLine
.Left(3).Compare(_T("---"))!=0)
517 //no starting "---" found
518 //seems to be either garbage or just
519 //a binary file. So start over...
529 sLine
= sLine
.Mid(3); //remove the "---"
531 //at the end of the filepath there's a revision number...
532 int bracket
= sLine
.ReverseFind('(');
534 // some patch files can have another '(' char, especially ones created in Chinese OS
535 bracket
= sLine
.ReverseFind(0xff08);
536 CString num
= sLine
.Mid(bracket
); //num = "(revision xxxxx)"
537 num
= num
.Mid(num
.Find(' '));
538 num
= num
.Trim(_T(" )"));
539 // here again, check for the Chinese bracket
540 num
= num
.Trim(0xff09);
541 chunks
->sRevision
= num
;
544 if (chunks
->sFilePath
.IsEmpty())
545 chunks
->sFilePath
= sLine
.Trim();
548 chunks
->sFilePath
= sLine
.Left(bracket
-1).Trim();
549 if (chunks
->sFilePath
.Find('\t')>=0)
551 chunks
->sFilePath
= chunks
->sFilePath
.Left(chunks
->sFilePath
.Find('\t'));
556 case 3: //+++ <filepath>
558 if (sLine
.Left(3).Compare(_T("+++"))!=0)
560 //no starting "+++" found
561 m_sErrorMessage
.Format(IDS_ERR_PATCH_NOADDFILELINE
, nIndex
);
564 sLine
= sLine
.Mid(3); //remove the "---"
566 //at the end of the filepath there's a revision number...
567 int bracket
= sLine
.ReverseFind('(');
569 // some patch files can have another '(' char, especially ones created in Chinese OS
570 bracket
= sLine
.ReverseFind(0xff08);
571 CString num
= sLine
.Mid(bracket
); //num = "(revision xxxxx)"
572 num
= num
.Mid(num
.Find(' '));
573 num
= num
.Trim(_T(" )"));
574 // here again, check for the Chinese bracket
575 num
= num
.Trim(0xff09);
576 chunks
->sRevision2
= num
;
578 chunks
->sFilePath2
= sLine
.Trim();
580 chunks
->sFilePath2
= sLine
.Left(bracket
-1).Trim();
581 if (chunks
->sFilePath2
.Find('\t')>=0)
583 chunks
->sFilePath2
= chunks
->sFilePath2
.Left(chunks
->sFilePath2
.Find('\t'));
588 case 4: //@@ -xxx,xxx +xxx,xxx @@
590 //start of a new chunk
591 if (sLine
.Left(2).Compare(_T("@@"))!=0)
593 //chunk doesn't start with "@@"
594 //so there's garbage in between two file diffs
602 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
604 delete chunks
->chunks
.GetAt(i
);
606 chunks
->chunks
.RemoveAll();
611 break; //skip the garbage
613 sLine
= sLine
.Mid(2);
614 sLine
= sLine
.Trim();
616 CString sRemove
= sLine
.Left(sLine
.Find(' '));
617 CString sAdd
= sLine
.Mid(sLine
.Find(' '));
618 chunk
->lRemoveStart
= (-_ttol(sRemove
));
619 if (sRemove
.Find(',')>=0)
621 sRemove
= sRemove
.Mid(sRemove
.Find(',')+1);
622 chunk
->lRemoveLength
= _ttol(sRemove
);
626 chunk
->lRemoveStart
= 0;
627 chunk
->lRemoveLength
= (-_ttol(sRemove
));
629 chunk
->lAddStart
= _ttol(sAdd
);
630 if (sAdd
.Find(',')>=0)
632 sAdd
= sAdd
.Mid(sAdd
.Find(',')+1);
633 chunk
->lAddLength
= _ttol(sAdd
);
637 chunk
->lAddStart
= 1;
638 chunk
->lAddLength
= _ttol(sAdd
);
643 case 5: //[ |+|-] <sourceline>
645 //this line is either a context line (with a ' ' in front)
646 //a line added (with a '+' in front)
647 //or a removed line (with a '-' in front)
652 type
= sLine
.GetAt(0);
655 //it's a context line - we don't use them here right now
656 //but maybe in the future the patch algorithm can be
657 //extended to use those in case the file to patch has
658 //already changed and no base file is around...
659 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
660 chunk
->arLinesStates
.Add(PATCHSTATE_CONTEXT
);
661 chunk
->arEOLs
.push_back(ending
);
664 else if (type
== '\\')
666 //it's a context line (sort of):
667 //warnings start with a '\' char (e.g. "\ No newline at end of file")
668 //so just ignore this...
670 else if (type
== '-')
673 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
674 chunk
->arLinesStates
.Add(PATCHSTATE_REMOVED
);
675 chunk
->arEOLs
.push_back(ending
);
678 else if (type
== '+')
681 chunk
->arLines
.Add(RemoveUnicodeBOM(sLine
.Mid(1)));
682 chunk
->arLinesStates
.Add(PATCHSTATE_ADDED
);
683 chunk
->arEOLs
.push_back(ending
);
688 //none of those lines! what the hell happened here?
689 m_sErrorMessage
.Format(IDS_ERR_PATCH_UNKOWNLINETYPE
, nIndex
);
692 if ((chunk
->lAddLength
== (nAddLineCount
+ nContextLineCount
)) &&
693 chunk
->lRemoveLength
== (nRemoveLineCount
+ nContextLineCount
))
697 chunks
->chunks
.Add(chunk
);
702 nContextLineCount
= 0;
703 nRemoveLineCount
= 0;
711 } // for ( ;nIndex<m_PatchLines.GetCount(); nIndex++)
714 m_sErrorMessage
.LoadString(IDS_ERR_PATCH_CHUNKMISMATCH
);
718 m_arFileDiffs
.Add(chunks
);
725 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
727 delete chunks
->chunks
.GetAt(i
);
729 chunks
->chunks
.RemoveAll();
736 CString
CPatch::GetFilename(int nIndex
)
740 if (nIndex
< m_arFileDiffs
.GetCount())
742 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
743 CString filepath
= Strip(c
->sFilePath
);
749 CString
CPatch::GetRevision(int nIndex
)
753 if (nIndex
< m_arFileDiffs
.GetCount())
755 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
761 CString
CPatch::GetFilename2(int nIndex
)
765 if (nIndex
< m_arFileDiffs
.GetCount())
767 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
768 CString filepath
= Strip(c
->sFilePath2
);
774 CString
CPatch::GetRevision2(int nIndex
)
778 if (nIndex
< m_arFileDiffs
.GetCount())
780 Chunks
* c
= m_arFileDiffs
.GetAt(nIndex
);
781 return c
->sRevision2
;
786 int CPatch::PatchFile(int nIndex
, const CString
& sPatchPath
, const CString
& sSavePath
, const CString
& sBaseFile
, const bool force
)
788 CString sPath
= GetFullPath(sPatchPath
, nIndex
);
789 if (PathIsDirectory(sPath
))
791 m_sErrorMessage
.Format(IDS_ERR_PATCH_INVALIDPATCHFILE
, (LPCTSTR
)sPath
);
796 m_sErrorMessage
.Format(IDS_ERR_PATCH_FILENOTINPATCH
, (LPCTSTR
)sPath
);
800 if (!force
&& sPath
== _T("NUL") && PathFileExists(GetFullPath(sPatchPath
, nIndex
, 1)))
803 if (GetFullPath(sPatchPath
, nIndex
, 1) == _T("NUL") && !PathFileExists(sPath
))
807 CString sPatchFile
= sBaseFile
.IsEmpty() ? sPath
: sBaseFile
;
808 if (PathFileExists(sPatchFile
))
810 CCrashReport::Instance().AddFile2(sPatchFile
, NULL
, _T("File to patch"), CR_AF_MAKE_FILE_COPY
);
812 CFileTextLines PatchLines
;
813 CFileTextLines PatchLinesResult
;
814 PatchLines
.Load(sPatchFile
);
815 PatchLinesResult
= PatchLines
; //.Copy(PatchLines);
816 PatchLines
.CopySettings(&PatchLinesResult
);
818 Chunks
* chunks
= m_arFileDiffs
.GetAt(nIndex
);
820 for (int i
=0; i
<chunks
->chunks
.GetCount(); i
++)
822 Chunk
* chunk
= chunks
->chunks
.GetAt(i
);
823 LONG lRemoveLine
= chunk
->lRemoveStart
;
824 LONG lAddLine
= chunk
->lAddStart
;
825 for (int j
=0; j
<chunk
->arLines
.GetCount(); j
++)
827 CString sPatchLine
= chunk
->arLines
.GetAt(j
);
828 EOL ending
= chunk
->arEOLs
[j
];
829 if ((m_UnicodeType
!= CFileTextLines::UTF8
)&&(m_UnicodeType
!= CFileTextLines::UTF8BOM
))
831 if ((PatchLines
.GetUnicodeType()==CFileTextLines::UTF8
)||(m_UnicodeType
== CFileTextLines::UTF8BOM
))
833 // convert the UTF-8 contents in CString sPatchLine into a CStringA
834 sPatchLine
= CUnicodeUtils::GetUnicode(CStringA(sPatchLine
));
837 int nPatchState
= (int)chunk
->arLinesStates
.GetAt(j
);
840 case PATCHSTATE_REMOVED
:
842 if ((lAddLine
> PatchLines
.GetCount())||(PatchLines
.GetCount()==0))
844 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, _T(""), (LPCTSTR
)sPatchLine
);
849 if ((sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
-1))!=0)&&(!HasExpandedKeyWords(sPatchLine
)))
851 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, (LPCTSTR
)sPatchLine
, (LPCTSTR
)PatchLines
.GetAt(lAddLine
-1));
854 if (lAddLine
> PatchLines
.GetCount())
856 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, (LPCTSTR
)sPatchLine
, _T(""));
859 PatchLines
.RemoveAt(lAddLine
-1);
862 case PATCHSTATE_ADDED
:
866 PatchLines
.InsertAt(lAddLine
-1, sPatchLine
, ending
);
870 case PATCHSTATE_CONTEXT
:
872 if (lAddLine
> PatchLines
.GetCount())
874 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, _T(""), (LPCTSTR
)sPatchLine
);
879 if (lRemoveLine
== 0)
881 if ((sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
-1))!=0) &&
882 (!HasExpandedKeyWords(sPatchLine
)) &&
883 (lRemoveLine
<= PatchLines
.GetCount()) &&
884 (sPatchLine
.Compare(PatchLines
.GetAt(lRemoveLine
-1))!=0))
886 if ((lAddLine
< PatchLines
.GetCount())&&(sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
))==0))
888 else if (((lAddLine
+ 1) < PatchLines
.GetCount())&&(sPatchLine
.Compare(PatchLines
.GetAt(lAddLine
+1))==0))
890 else if ((lRemoveLine
< PatchLines
.GetCount())&&(sPatchLine
.Compare(PatchLines
.GetAt(lRemoveLine
))==0))
894 m_sErrorMessage
.Format(IDS_ERR_PATCH_DOESNOTMATCH
, (LPCTSTR
)sPatchLine
, (LPCTSTR
)PatchLines
.GetAt(lAddLine
-1));
905 } // switch (nPatchState)
906 } // for (j=0; j<chunk->arLines.GetCount(); j++)
907 } // for (int i=0; i<chunks->chunks.GetCount(); i++)
908 if (!sSavePath
.IsEmpty())
910 PatchLines
.Save(sSavePath
, false);
915 BOOL
CPatch::HasExpandedKeyWords(const CString
& line
)
917 if (line
.Find(_T("$LastChangedDate"))>=0)
919 if (line
.Find(_T("$Date"))>=0)
921 if (line
.Find(_T("$LastChangedRevision"))>=0)
923 if (line
.Find(_T("$Rev"))>=0)
925 if (line
.Find(_T("$LastChangedBy"))>=0)
927 if (line
.Find(_T("$Author"))>=0)
929 if (line
.Find(_T("$HeadURL"))>=0)
931 if (line
.Find(_T("$URL"))>=0)
933 if (line
.Find(_T("$Id"))>=0)
938 CString
CPatch::CheckPatchPath(const CString
& path
)
940 //first check if the path already matches
941 if (CountMatches(path
) > (GetNumberOfFiles()/3))
943 //now go up the tree and try again
944 CString upperpath
= path
;
945 while (upperpath
.ReverseFind('\\')>0)
947 upperpath
= upperpath
.Left(upperpath
.ReverseFind('\\'));
948 if (CountMatches(upperpath
) > (GetNumberOfFiles()/3))
951 //still no match found. So try sub folders
954 CDirFileEnum
filefinder(path
);
955 while (filefinder
.NextFile(subpath
, &isDir
))
959 if (g_GitAdminDir
.IsAdminDirPath(subpath
))
961 if (CountMatches(subpath
) > (GetNumberOfFiles()/3))
965 // if a patch file only contains newly added files
966 // we can't really find the correct path.
967 // But: we can compare paths strings without the filenames
968 // and check if at least those match
970 while (upperpath
.ReverseFind('\\')>0)
972 upperpath
= upperpath
.Left(upperpath
.ReverseFind('\\'));
973 if (CountDirMatches(upperpath
) > (GetNumberOfFiles()/3))
980 int CPatch::CountMatches(const CString
& path
)
983 for (int i
=0; i
<GetNumberOfFiles(); ++i
)
985 CString temp
= GetFilename(i
);
986 temp
.Replace('/', '\\');
987 if (PathIsRelative(temp
))
988 temp
= path
+ _T("\\")+ temp
;
989 if (PathFileExists(temp
))
995 int CPatch::CountDirMatches(const CString
& path
)
998 for (int i
=0; i
<GetNumberOfFiles(); ++i
)
1000 CString temp
= GetFilename(i
);
1001 temp
.Replace('/', '\\');
1002 if (PathIsRelative(temp
))
1003 temp
= path
+ _T("\\")+ temp
;
1004 // remove the filename
1005 temp
= temp
.Left(temp
.ReverseFind('\\'));
1006 if (PathFileExists(temp
))
1012 BOOL
CPatch::StripPrefixes(const CString
& path
)
1014 int nSlashesMax
= 0;
1015 for (int i
=0; i
<GetNumberOfFiles(); i
++)
1017 CString filename
= GetFilename(i
);
1018 filename
.Replace('/','\\');
1019 int nSlashes
= filename
.Replace('\\','/');
1020 nSlashesMax
= max(nSlashesMax
,nSlashes
);
1023 for (int nStrip
=1;nStrip
<nSlashesMax
;nStrip
++)
1026 if ( CountMatches(path
) > GetNumberOfFiles()/3 )
1028 // Use current m_nStrip
1033 // Stripping doesn't help so reset it again
1038 CString
CPatch::Strip(const CString
& filename
)
1040 CString s
= filename
;
1043 // Remove windows drive letter "c:"
1044 if ( s
.GetLength()>2 && s
[1]==':')
1049 for (int nStrip
=1;nStrip
<=m_nStrip
;nStrip
++)
1051 // "/home/ts/my-working-copy/dir/file.txt"
1052 // "home/ts/my-working-copy/dir/file.txt"
1053 // "ts/my-working-copy/dir/file.txt"
1054 // "my-working-copy/dir/file.txt"
1056 s
= s
.Mid(s
.FindOneOf(_T("/\\"))+1);
1062 CString
CPatch::GetFullPath(const CString
& sPath
, int nIndex
, int fileno
/* = 0*/)
1066 temp
= GetFilename(nIndex
);
1068 temp
= GetFilename2(nIndex
);
1070 temp
.Replace('/', '\\');
1071 if(temp
== _T("NUL"))
1074 if (PathIsRelative(temp
))
1076 if (sPath
.Right(1).Compare(_T("\\")) != 0)
1077 temp
= sPath
+ _T("\\") + temp
;
1079 temp
= sPath
+ temp
;
1085 CString
CPatch::RemoveUnicodeBOM(const CString
& str
)
1087 if (str
.GetLength()==0)
1089 if (str
[0] == 0xFEFF)