1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2006-2015 - 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.
27 #include "UnicodeUtils.h"
29 #include "MovedBlocks.h"
32 #pragma warning(disable: 4702) // unreachable code
33 int CDiffData::abort_on_pool_failure (int /*retcode*/)
40 CDiffData::CDiffData(void)
41 : m_bViewMovedBlocks(false)
42 , m_bPatchRequired(false)
45 svn_dso_initialize2();
49 m_sPatchOriginal
= _T(": original");
50 m_sPatchPatched
= _T(": patched");
53 CDiffData::~CDiffData(void)
58 void CDiffData::SetMovedBlocks(bool bViewMovedBlocks
/* = true*/)
60 m_bViewMovedBlocks
= bViewMovedBlocks
;
63 int CDiffData::GetLineCount() const
65 int count
= (int)m_arBaseFile
.GetCount();
66 if (count
< m_arTheirFile
.GetCount())
67 count
= m_arTheirFile
.GetCount();
68 if (count
< m_arYourFile
.GetCount())
69 count
= m_arYourFile
.GetCount();
73 svn_diff_file_ignore_space_t
CDiffData::GetIgnoreSpaceMode(DWORD dwIgnoreWS
) const
78 return svn_diff_file_ignore_space_none
;
80 return svn_diff_file_ignore_space_all
;
82 return svn_diff_file_ignore_space_change
;
84 return svn_diff_file_ignore_space_none
;
88 svn_diff_file_options_t
* CDiffData::CreateDiffFileOptions(DWORD dwIgnoreWS
, bool bIgnoreEOL
, apr_pool_t
* pool
)
90 svn_diff_file_options_t
* options
= svn_diff_file_options_create(pool
);
91 options
->ignore_eol_style
= bIgnoreEOL
;
92 options
->ignore_space
= GetIgnoreSpaceMode(dwIgnoreWS
);
96 bool CDiffData::HandleSvnError(svn_error_t
* svnerr
)
98 TRACE(_T("diff-error in CDiffData::Load()\n"));
99 TRACE(_T("diff-error in CDiffData::Load()\n"));
100 CStringA sMsg
= CStringA(svnerr
->message
);
101 while (svnerr
->child
)
103 svnerr
= svnerr
->child
;
105 sMsg
+= CStringA(svnerr
->message
);
107 CString readableMsg
= CUnicodeUtils::GetUnicode(sMsg
);
108 m_sError
.Format(IDS_ERR_DIFF_DIFF
, (LPCTSTR
)readableMsg
);
109 svn_error_clear(svnerr
);
113 void CDiffData::TieMovedBlocks(int from
, int to
, apr_off_t length
)
115 for(int i
=0; i
<length
; i
++, to
++, from
++)
117 int fromIndex
= m_YourBaseLeft
.FindLineNumber(from
);
120 int toIndex
= m_YourBaseRight
.FindLineNumber(to
);
123 m_YourBaseLeft
.SetMovedIndex(fromIndex
, toIndex
, true);
124 m_YourBaseRight
.SetMovedIndex(toIndex
, fromIndex
, false);
126 toIndex
= m_YourBaseBoth
.FindLineNumber(to
);
129 while((toIndex
< m_YourBaseBoth
.GetCount())&&
130 ((m_YourBaseBoth
.GetState(toIndex
) != DIFFSTATE_ADDED
)&&
131 (!m_YourBaseBoth
.IsMoved(toIndex
))||(m_YourBaseBoth
.IsMovedFrom(toIndex
))||
132 (m_YourBaseBoth
.GetLineNumber(toIndex
) != to
)))
137 fromIndex
= m_YourBaseBoth
.FindLineNumber(from
);
140 while((fromIndex
< m_YourBaseBoth
.GetCount())&&
141 ((m_YourBaseBoth
.GetState(fromIndex
) != DIFFSTATE_REMOVED
)&&
142 (!m_YourBaseBoth
.IsMoved(fromIndex
))||(!m_YourBaseBoth
.IsMovedFrom(fromIndex
))||
143 (m_YourBaseBoth
.GetLineNumber(fromIndex
) != from
)))
147 if ((fromIndex
< m_YourBaseBoth
.GetCount())&&(toIndex
< m_YourBaseBoth
.GetCount()))
149 m_YourBaseBoth
.SetMovedIndex(fromIndex
, toIndex
, true);
150 m_YourBaseBoth
.SetMovedIndex(toIndex
, fromIndex
, false);
155 bool CDiffData::CompareWithIgnoreWS(CString s1
, CString s2
, DWORD dwIgnoreWS
) const
159 s1
= s1
.TrimLeft(_T(" \t"));
160 s2
= s2
.TrimLeft(_T(" \t"));
164 s1
= s1
.TrimRight(_T(" \t"));
165 s2
= s2
.TrimRight(_T(" \t"));
171 void CDiffData::StickAndSkip(svn_diff_t
* &tempdiff
, apr_off_t
&original_length_sticked
, apr_off_t
&modified_length_sticked
) const
173 if((m_bViewMovedBlocks
)&&(tempdiff
->type
== svn_diff__type_diff_modified
))
175 svn_diff_t
* nextdiff
= tempdiff
->next
;
176 while((nextdiff
)&&(nextdiff
->type
== svn_diff__type_diff_modified
))
178 original_length_sticked
+= nextdiff
->original_length
;
179 modified_length_sticked
+= nextdiff
->modified_length
;
181 nextdiff
= tempdiff
->next
;
186 BOOL
CDiffData::Load()
188 m_arBaseFile
.RemoveAll();
189 m_arYourFile
.RemoveAll();
190 m_arTheirFile
.RemoveAll();
192 m_YourBaseBoth
.Clear();
193 m_YourBaseLeft
.Clear();
194 m_YourBaseRight
.Clear();
196 m_TheirBaseBoth
.Clear();
197 m_TheirBaseLeft
.Clear();
198 m_TheirBaseRight
.Clear();
202 CRegDWORD regIgnoreWS
= CRegDWORD(_T("Software\\TortoiseGitMerge\\IgnoreWS"));
203 CRegDWORD regIgnoreEOL
= CRegDWORD(_T("Software\\TortoiseGitMerge\\IgnoreEOL"), TRUE
);
204 CRegDWORD regIgnoreCase
= CRegDWORD(_T("Software\\TortoiseGitMerge\\CaseInsensitive"), FALSE
);
205 CRegDWORD regIgnoreComments
= CRegDWORD(_T("Software\\TortoiseGitMerge\\IgnoreComments"), FALSE
);
206 DWORD dwIgnoreWS
= regIgnoreWS
;
207 bool bIgnoreEOL
= ((DWORD
)regIgnoreEOL
)!=0;
208 BOOL bIgnoreCase
= ((DWORD
)regIgnoreCase
)!=0;
209 bool bIgnoreComments
= ((DWORD
)regIgnoreComments
)!=0;
211 // The Subversion diff API only can ignore whitespaces and eol styles.
212 // It also can only handle one-byte charsets.
213 // To ignore case changes or to handle UTF-16 files, we have to
214 // save the original file in UTF-8 and/or the letters changed to lowercase
215 // so the Subversion diff can handle those.
216 CString sConvertedBaseFilename
= m_baseFile
.GetFilename();
217 CString sConvertedYourFilename
= m_yourFile
.GetFilename();
218 CString sConvertedTheirFilename
= m_theirFile
.GetFilename();
220 m_baseFile
.StoreFileAttributes();
221 m_theirFile
.StoreFileAttributes();
222 m_yourFile
.StoreFileAttributes();
223 //m_mergedFile.StoreFileAttributes();
225 bool bBaseNeedConvert
= false;
226 bool bTheirNeedConvert
= false;
227 bool bYourNeedConvert
= false;
228 bool bBaseIsUtf8
= false;
229 bool bTheirIsUtf8
= false;
230 bool bYourIsUtf8
= false;
232 if (IsBaseFileInUse())
234 if (!m_arBaseFile
.Load(m_baseFile
.GetFilename()))
236 m_sError
= m_arBaseFile
.GetErrorString();
239 bBaseNeedConvert
= bIgnoreCase
|| bIgnoreComments
|| (m_arBaseFile
.NeedsConversion()) || !m_rx
._Empty();
240 bBaseIsUtf8
= (m_arBaseFile
.GetUnicodeType()!=CFileTextLines::ASCII
) || bBaseNeedConvert
;
243 if (IsTheirFileInUse())
245 // m_arBaseFile.GetCount() is passed as a hint for the number of lines in this file
246 // It's a fair guess that the files will be roughly the same size
247 if (!m_arTheirFile
.Load(m_theirFile
.GetFilename(), m_arBaseFile
.GetCount()))
249 m_sError
= m_arTheirFile
.GetErrorString();
252 bTheirNeedConvert
= bIgnoreCase
|| bIgnoreComments
|| (m_arTheirFile
.NeedsConversion()) || !m_rx
._Empty();
253 bTheirIsUtf8
= (m_arTheirFile
.GetUnicodeType()!=CFileTextLines::ASCII
) || bTheirNeedConvert
;
256 if (IsYourFileInUse())
258 // m_arBaseFile.GetCount() is passed as a hint for the number of lines in this file
259 // It's a fair guess that the files will be roughly the same size
260 if (!m_arYourFile
.Load(m_yourFile
.GetFilename(), m_arBaseFile
.GetCount()))
262 m_sError
= m_arYourFile
.GetErrorString();
265 bYourNeedConvert
= bIgnoreCase
|| bIgnoreComments
|| (m_arYourFile
.NeedsConversion()) || !m_rx
._Empty();
266 bYourIsUtf8
= (m_arYourFile
.GetUnicodeType()!=CFileTextLines::ASCII
) || bYourNeedConvert
;
269 // in case at least one of the files got converted or is UTF8
270 // we have to convert all non UTF8 (ASCII) files
271 // otherwise one file might be in ANSI and the other in UTF8 and we'll end up
272 // with lines marked as different throughout the files even though the lines
273 // would show no change at all in the viewer.
275 // convert all files we need to
276 bool bIsUtf8
= bBaseIsUtf8
|| bTheirIsUtf8
|| bYourIsUtf8
; // any file end as UTF8
277 bBaseNeedConvert
|= (IsBaseFileInUse() && !bBaseIsUtf8
&& bIsUtf8
);
278 if (bBaseNeedConvert
)
280 sConvertedBaseFilename
= CTempFiles::Instance().GetTempFilePathString();
281 m_baseFile
.SetConvertedFileName(sConvertedBaseFilename
);
282 m_arBaseFile
.Save(sConvertedBaseFilename
, true, true, 0, bIgnoreCase
, m_bBlame
283 , bIgnoreComments
, m_CommentLineStart
, m_CommentBlockStart
, m_CommentBlockEnd
284 , m_rx
, m_replacement
);
286 bYourNeedConvert
|= (IsYourFileInUse() && !bYourIsUtf8
&& bIsUtf8
);
287 if (bYourNeedConvert
)
289 sConvertedYourFilename
= CTempFiles::Instance().GetTempFilePathString();
290 m_yourFile
.SetConvertedFileName(sConvertedYourFilename
);
291 m_arYourFile
.Save(sConvertedYourFilename
, true, true, 0, bIgnoreCase
, m_bBlame
292 , bIgnoreComments
, m_CommentLineStart
, m_CommentBlockStart
, m_CommentBlockEnd
293 , m_rx
, m_replacement
);
295 bTheirNeedConvert
|= (IsTheirFileInUse() && !bTheirIsUtf8
&& bIsUtf8
);
296 if (bTheirNeedConvert
)
298 sConvertedTheirFilename
= CTempFiles::Instance().GetTempFilePathString();
299 m_theirFile
.SetConvertedFileName(sConvertedTheirFilename
);
300 m_arTheirFile
.Save(sConvertedTheirFilename
, true, true, 0, bIgnoreCase
, m_bBlame
301 , bIgnoreComments
, m_CommentLineStart
, m_CommentBlockStart
, m_CommentBlockEnd
302 , m_rx
, m_replacement
);
305 // Calculate the number of lines in the largest of the three files
306 int lengthHint
= GetLineCount();
310 m_YourBaseBoth
.Reserve(lengthHint
);
311 m_YourBaseLeft
.Reserve(lengthHint
);
312 m_YourBaseRight
.Reserve(lengthHint
);
314 m_TheirBaseBoth
.Reserve(lengthHint
);
315 m_TheirBaseLeft
.Reserve(lengthHint
);
316 m_TheirBaseRight
.Reserve(lengthHint
);
318 catch (CMemoryException
* e
)
320 e
->GetErrorMessage(m_sError
.GetBuffer(255), 255);
321 m_sError
.ReleaseBuffer();
326 apr_pool_t
* pool
= NULL
;
327 apr_pool_create_ex (&pool
, NULL
, abort_on_pool_failure
, NULL
);
329 // Is this a two-way diff?
330 if (IsBaseFileInUse() && IsYourFileInUse() && !IsTheirFileInUse())
332 if (!DoTwoWayDiff(sConvertedBaseFilename
, sConvertedYourFilename
, dwIgnoreWS
, bIgnoreEOL
, pool
))
334 apr_pool_destroy (pool
); // free the allocated memory
339 if (IsBaseFileInUse() && IsTheirFileInUse() && !IsYourFileInUse())
344 // Is this a three-way diff?
345 if (IsBaseFileInUse() && IsTheirFileInUse() && IsYourFileInUse())
347 m_Diff3
.Reserve(lengthHint
);
349 if (!DoThreeWayDiff(sConvertedBaseFilename
, sConvertedYourFilename
, sConvertedTheirFilename
, dwIgnoreWS
, bIgnoreEOL
, !!bIgnoreCase
, pool
))
351 apr_pool_destroy (pool
); // free the allocated memory
356 // free the allocated memory
357 apr_pool_destroy (pool
);
359 m_arBaseFile
.RemoveAll();
360 m_arYourFile
.RemoveAll();
361 m_arTheirFile
.RemoveAll();
367 CDiffData::DoTwoWayDiff(const CString
& sBaseFilename
, const CString
& sYourFilename
, DWORD dwIgnoreWS
, bool bIgnoreEOL
, apr_pool_t
* pool
)
369 svn_diff_file_options_t
* options
= CreateDiffFileOptions(dwIgnoreWS
, bIgnoreEOL
, pool
);
370 // convert CString filenames (UTF-16 or ANSI) to UTF-8
371 CStringA sBaseFilenameUtf8
= CUnicodeUtils::GetUTF8(sBaseFilename
);
372 CStringA sYourFilenameUtf8
= CUnicodeUtils::GetUTF8(sYourFilename
);
374 svn_diff_t
* diffYourBase
= NULL
;
375 svn_error_t
* svnerr
= svn_diff_file_diff_2(&diffYourBase
, sBaseFilenameUtf8
, sYourFilenameUtf8
, options
, pool
);
378 return HandleSvnError(svnerr
);
380 tsvn_svn_diff_t_extension
* movedBlocks
= NULL
;
381 if(m_bViewMovedBlocks
)
382 movedBlocks
= MovedBlocksDetect(diffYourBase
, dwIgnoreWS
, pool
); // Side effect is that diffs are now splitted
384 svn_diff_t
* tempdiff
= diffYourBase
;
389 svn_diff__type_e diffType
= tempdiff
->type
;
390 // Side effect described above overcoming - sticking together
391 apr_off_t original_length_sticked
= tempdiff
->original_length
;
392 apr_off_t modified_length_sticked
= tempdiff
->modified_length
;
393 StickAndSkip(tempdiff
, original_length_sticked
, modified_length_sticked
);
395 for (int i
=0; i
<original_length_sticked
; i
++)
397 if (baseline
>= m_arBaseFile
.GetCount())
399 m_sError
.LoadString(IDS_ERR_DIFF_NEWLINES
);
402 const CString
& sCurrentBaseLine
= m_arBaseFile
.GetAt(baseline
);
403 EOL endingBase
= m_arBaseFile
.GetLineEnding(baseline
);
404 if (diffType
== svn_diff__type_common
)
406 if (yourline
>= m_arYourFile
.GetCount())
408 m_sError
.LoadString(IDS_ERR_DIFF_NEWLINES
);
411 const CString
& sCurrentYourLine
= m_arYourFile
.GetAt(yourline
);
412 EOL endingYours
= m_arYourFile
.GetLineEnding(yourline
);
413 if (sCurrentBaseLine
!= sCurrentYourLine
)
415 bool changedWS
= false;
416 if (dwIgnoreWS
== 2 || dwIgnoreWS
== 3)
417 changedWS
= CompareWithIgnoreWS(sCurrentBaseLine
, sCurrentYourLine
, dwIgnoreWS
);
418 if (changedWS
|| dwIgnoreWS
== 0)
420 // one-pane view: two lines, one 'removed' and one 'added'
421 m_YourBaseBoth
.AddData(sCurrentBaseLine
, DIFFSTATE_REMOVEDWHITESPACE
, yourline
, endingBase
, HIDESTATE_SHOWN
, -1);
422 m_YourBaseBoth
.AddData(sCurrentYourLine
, DIFFSTATE_ADDEDWHITESPACE
, yourline
, endingYours
, HIDESTATE_SHOWN
, -1);
426 m_YourBaseBoth
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingBase
, HIDESTATE_HIDDEN
, -1);
431 m_YourBaseBoth
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingBase
, HIDESTATE_HIDDEN
, -1);
433 yourline
++; //in both files
436 { // small trick - we need here a baseline, but we fix it back to yourline at the end of routine
437 m_YourBaseBoth
.AddData(sCurrentBaseLine
, DIFFSTATE_REMOVED
, -1, endingBase
, HIDESTATE_SHOWN
, -1);
441 if (diffType
== svn_diff__type_diff_modified
)
443 for (int i
=0; i
<modified_length_sticked
; i
++)
445 if (m_arYourFile
.GetCount() > yourline
)
447 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_ADDED
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
452 tempdiff
= tempdiff
->next
;
455 HideUnchangedSections(&m_YourBaseBoth
, NULL
, NULL
);
457 tempdiff
= diffYourBase
;
461 // arbitrary length of 500
462 static const int maxstringlengthforwhitespacecheck
= 500;
463 auto s1
= std::unique_ptr
<wchar_t>(new wchar_t[maxstringlengthforwhitespacecheck
]);
466 if (tempdiff
->type
== svn_diff__type_common
)
468 for (int i
= 0; i
<tempdiff
->original_length
; i
++)
470 const CString
& sCurrentYourLine
= m_arYourFile
.GetAt(yourline
);
471 EOL endingYours
= m_arYourFile
.GetLineEnding(yourline
);
472 const CString
& sCurrentBaseLine
= m_arBaseFile
.GetAt(baseline
);
473 EOL endingBase
= m_arBaseFile
.GetLineEnding(baseline
);
474 if (sCurrentBaseLine
!= sCurrentYourLine
)
476 bool changedWS
= false;
477 bool bWhiteSpaceChanges
= true;
478 if (dwIgnoreWS
== 2 || dwIgnoreWS
== 3)
479 changedWS
= CompareWithIgnoreWS(sCurrentBaseLine
, sCurrentYourLine
, dwIgnoreWS
);
480 else if (dwIgnoreWS
== 0)
482 // the strings could be identical in relation to the regex filter.
483 // so to find out if there are whitespace changes, we have to strip the strings
484 // of all non-whitespace chars and then compare them.
485 // Note: this is not really fast! So we only do that if the lines are not too long (arbitrary value)
486 if ((sCurrentBaseLine
.GetLength() < maxstringlengthforwhitespacecheck
) &&
487 (sCurrentYourLine
.GetLength() < maxstringlengthforwhitespacecheck
))
489 auto pLine1
= (LPCWSTR
)sCurrentBaseLine
;
490 auto pLine2
= (LPCWSTR
)sCurrentYourLine
;
494 if ((*pLine1
== ' ') || (*pLine1
== '\t'))
506 if ((*pLine2
== ' ') || (*pLine2
== '\t'))
510 bWhiteSpaceChanges
= false;
518 if (changedWS
|| ((dwIgnoreWS
== 0) && bWhiteSpaceChanges
))
520 m_YourBaseLeft
.AddData(sCurrentBaseLine
, DIFFSTATE_WHITESPACE
, baseline
, endingBase
, HIDESTATE_SHOWN
, -1);
521 m_YourBaseRight
.AddData(sCurrentYourLine
, DIFFSTATE_WHITESPACE
, yourline
, endingYours
, HIDESTATE_SHOWN
, -1);
525 m_YourBaseLeft
.AddData(sCurrentBaseLine
, DIFFSTATE_NORMAL
, baseline
, endingBase
, HIDESTATE_HIDDEN
, -1);
526 m_YourBaseRight
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingYours
, HIDESTATE_HIDDEN
, -1);
531 m_YourBaseLeft
.AddData(sCurrentBaseLine
, DIFFSTATE_NORMAL
, baseline
, endingBase
, HIDESTATE_HIDDEN
, -1);
532 m_YourBaseRight
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingYours
, HIDESTATE_HIDDEN
, -1);
538 if (tempdiff
->type
== svn_diff__type_diff_modified
)
540 // now we trying to stick together parts, that were splitted by MovedBlocks
541 apr_off_t original_length_sticked
= tempdiff
->original_length
;
542 apr_off_t modified_length_sticked
= tempdiff
->modified_length
;
543 StickAndSkip(tempdiff
, original_length_sticked
, modified_length_sticked
);
545 apr_off_t original_length
= original_length_sticked
;
546 for (int i
=0; i
<modified_length_sticked
; i
++)
548 if (m_arYourFile
.GetCount() > yourline
)
550 EOL endingYours
= m_arYourFile
.GetLineEnding(yourline
);
551 m_YourBaseRight
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_ADDED
, yourline
, endingYours
, HIDESTATE_SHOWN
, -1);
552 if (original_length
-- <= 0)
554 m_YourBaseLeft
.AddEmpty();
558 EOL endingBase
= m_arBaseFile
.GetLineEnding(baseline
);
559 m_YourBaseLeft
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_REMOVED
, baseline
, endingBase
, HIDESTATE_SHOWN
, -1);
565 apr_off_t modified_length
= modified_length_sticked
;
566 for (int i
=0; i
<original_length_sticked
; i
++)
568 if ((modified_length
-- <= 0)&&(m_arBaseFile
.GetCount() > baseline
))
570 EOL endingBase
= m_arBaseFile
.GetLineEnding(baseline
);
571 m_YourBaseLeft
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_REMOVED
, baseline
, endingBase
, HIDESTATE_SHOWN
, -1);
572 m_YourBaseRight
.AddEmpty();
577 tempdiff
= tempdiff
->next
;
579 // add last (empty) lines if needed - diff don't report those
580 if (m_arBaseFile
.GetCount() > baseline
)
582 if (m_arYourFile
.GetCount() > yourline
)
584 // last line is missing in both files add them to end and mark as no diff
585 m_YourBaseLeft
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_NORMAL
, baseline
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
, -1);
586 m_YourBaseRight
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_NORMAL
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
592 viewdata
oViewData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_REMOVED
, baseline
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
);
595 // find first EMPTY line in last blok
596 int nPos
= m_YourBaseLeft
.GetCount();
597 while (--nPos
>=0 && m_YourBaseLeft
.GetState(nPos
)==DIFFSTATE_EMPTY
) ;
598 if (++nPos
<m_YourBaseLeft
.GetCount())
600 m_YourBaseLeft
.SetData(nPos
, oViewData
);
604 m_YourBaseLeft
.AddData(oViewData
);
605 m_YourBaseRight
.AddEmpty();
609 else if (m_arYourFile
.GetCount() > yourline
)
611 viewdata
oViewData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_ADDED
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
);
614 // try to move last line higher
615 int nPos
= m_YourBaseRight
.GetCount();
616 while (--nPos
>=0 && m_YourBaseRight
.GetState(nPos
)==DIFFSTATE_EMPTY
) ;
617 if (++nPos
<m_YourBaseRight
.GetCount())
619 m_YourBaseRight
.SetData(nPos
, oViewData
);
623 m_YourBaseLeft
.AddEmpty();
624 m_YourBaseRight
.AddData(oViewData
);
629 // Fixing results for conforming moved blocks
633 tempdiff
= movedBlocks
->base
;
634 if(movedBlocks
->moved_to
!= -1)
636 // set states in a block original:length -> moved_to:length
637 TieMovedBlocks((int)tempdiff
->original_start
, movedBlocks
->moved_to
, tempdiff
->original_length
);
639 if(movedBlocks
->moved_from
!= -1)
641 // set states in a block modified:length -> moved_from:length
642 TieMovedBlocks(movedBlocks
->moved_from
, (int)tempdiff
->modified_start
, tempdiff
->modified_length
);
644 movedBlocks
= movedBlocks
->next
;
647 // replace baseline with the yourline in m_YourBaseBoth
649 for(int i=0; i<m_YourBaseBoth.GetCount(); i++)
651 DiffStates state = m_YourBaseBoth.GetState(i);
652 if((state == DIFFSTATE_REMOVED)||(state == DIFFSTATE_MOVED_FROM))
654 m_YourBaseBoth.SetLineNumber(i, -1);
662 TRACE(_T("done with 2-way diff\n"));
664 HideUnchangedSections(&m_YourBaseLeft
, &m_YourBaseRight
, NULL
);
670 CDiffData::DoThreeWayDiff(const CString
& sBaseFilename
, const CString
& sYourFilename
, const CString
& sTheirFilename
, DWORD dwIgnoreWS
, bool bIgnoreEOL
, bool bIgnoreCase
, apr_pool_t
* pool
)
672 // the following three arrays are used to check for conflicts even in case the
673 // user has ignored spaces/eols.
674 CStdDWORDArray m_arDiff3LinesBase
;
675 CStdDWORDArray m_arDiff3LinesYour
;
676 CStdDWORDArray m_arDiff3LinesTheir
;
677 #define AddLines(baseline, yourline, theirline) m_arDiff3LinesBase.Add(baseline), m_arDiff3LinesYour.Add(yourline), m_arDiff3LinesTheir.Add(theirline)
678 int lengthHint
= GetLineCount();
680 m_arDiff3LinesBase
.Reserve(lengthHint
);
681 m_arDiff3LinesYour
.Reserve(lengthHint
);
682 m_arDiff3LinesTheir
.Reserve(lengthHint
);
684 CRegDWORD contextLines
= CRegDWORD(_T("Software\\TortoiseGitMerge\\ContextLines"), 3);
685 svn_diff_file_options_t
* options
= CreateDiffFileOptions(dwIgnoreWS
, bIgnoreEOL
, pool
);
687 // convert CString filenames (UTF-16 or ANSI) to UTF-8
688 CStringA sBaseFilenameUtf8
= CUnicodeUtils::GetUTF8(sBaseFilename
);
689 CStringA sYourFilenameUtf8
= CUnicodeUtils::GetUTF8(sYourFilename
);
690 CStringA sTheirFilenameUtf8
= CUnicodeUtils::GetUTF8(sTheirFilename
);
692 svn_diff_t
* diffTheirYourBase
= NULL
;
693 svn_error_t
* svnerr
= svn_diff_file_diff3_2(&diffTheirYourBase
, sBaseFilenameUtf8
, sTheirFilenameUtf8
, sYourFilenameUtf8
, options
, pool
);
695 return HandleSvnError(svnerr
);
697 svn_diff_t
* tempdiff
= diffTheirYourBase
;
703 const viewdata
emptyConflictEmpty(_T(""), DIFFSTATE_CONFLICTEMPTY
, DIFF_EMPTYLINENUMBER
, EOL_NOENDING
, HIDESTATE_SHOWN
);
704 const viewdata
emptyIdenticalRemoved(_T(""), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, EOL_NOENDING
, HIDESTATE_SHOWN
);
707 if (tempdiff
->type
== svn_diff__type_common
)
709 ASSERT((tempdiff
->latest_length
== tempdiff
->modified_length
) && (tempdiff
->modified_length
== tempdiff
->original_length
));
710 for (int i
=0; i
<tempdiff
->original_length
; i
++)
712 if ((m_arYourFile
.GetCount() > yourline
)&&(m_arTheirFile
.GetCount() > theirline
))
714 AddLines(baseline
, yourline
, theirline
);
716 m_Diff3
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_NORMAL
, resline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_HIDDEN
, -1);
717 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_NORMAL
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_HIDDEN
, -1);
718 m_TheirBaseBoth
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_NORMAL
, theirline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_HIDDEN
, -1);
727 else if (tempdiff
->type
== svn_diff__type_diff_common
)
729 ASSERT(tempdiff
->latest_length
== tempdiff
->modified_length
);
730 //both theirs and yours have lines replaced
731 for (int i
=0; i
<tempdiff
->original_length
; i
++)
733 if (m_arBaseFile
.GetCount() > baseline
)
735 AddLines(baseline
, yourline
, theirline
);
737 m_Diff3
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
, -1);
738 m_YourBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, EOL_NOENDING
, HIDESTATE_SHOWN
, -1);
739 m_TheirBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, EOL_NOENDING
, HIDESTATE_SHOWN
, -1);
744 for (int i
=0; i
<tempdiff
->modified_length
; i
++)
746 if ((m_arYourFile
.GetCount() > yourline
)&&(m_arTheirFile
.GetCount() > theirline
))
748 AddLines(baseline
, yourline
, theirline
);
750 m_Diff3
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_IDENTICALADDED
, resline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
751 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_IDENTICALADDED
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
752 m_TheirBaseBoth
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_IDENTICALADDED
, resline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_SHOWN
, -1);
760 else if (tempdiff
->type
== svn_diff__type_conflict
)
762 apr_off_t length
= max(tempdiff
->original_length
, tempdiff
->modified_length
);
763 length
= max(tempdiff
->latest_length
, length
);
764 apr_off_t original
= tempdiff
->original_length
;
765 apr_off_t modified
= tempdiff
->modified_length
;
766 apr_off_t latest
= tempdiff
->latest_length
;
768 apr_off_t originalresolved
= 0;
769 apr_off_t modifiedresolved
= 0;
770 apr_off_t latestresolved
= 0;
772 LONG base
= baseline
;
773 LONG your
= yourline
;
774 LONG their
= theirline
;
775 if (tempdiff
->resolved_diff
)
777 originalresolved
= tempdiff
->resolved_diff
->original_length
;
778 modifiedresolved
= tempdiff
->resolved_diff
->modified_length
;
779 latestresolved
= tempdiff
->resolved_diff
->latest_length
;
781 for (int i
=0; i
<length
; i
++)
783 EOL endingBase
= m_arBaseFile
.GetCount() > baseline
? m_arBaseFile
.GetLineEnding(baseline
) : EOL_AUTOLINE
;
786 if (m_arBaseFile
.GetCount() > baseline
)
788 AddLines(baseline
, yourline
, theirline
);
790 m_Diff3
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, endingBase
, HIDESTATE_SHOWN
, -1);
791 m_YourBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, endingBase
, HIDESTATE_SHOWN
, -1);
792 m_TheirBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, endingBase
, HIDESTATE_SHOWN
, -1);
795 else if ((originalresolved
)||((modifiedresolved
)&&(latestresolved
)))
797 AddLines(baseline
, yourline
, theirline
);
799 m_Diff3
.AddData(emptyIdenticalRemoved
);
800 if ((latestresolved
)&&(modifiedresolved
))
802 m_YourBaseBoth
.AddData(emptyIdenticalRemoved
);
803 m_TheirBaseBoth
.AddData(emptyIdenticalRemoved
);
811 if (originalresolved
)
819 if (modifiedresolved
)
829 original
= tempdiff
->original_length
;
830 modified
= tempdiff
->modified_length
;
831 latest
= tempdiff
->latest_length
;
835 if (tempdiff
->resolved_diff
)
837 originalresolved
= tempdiff
->resolved_diff
->original_length
;
838 modifiedresolved
= tempdiff
->resolved_diff
->modified_length
;
839 latestresolved
= tempdiff
->resolved_diff
->latest_length
;
841 for (int i
=0; i
<length
; i
++)
843 if ((latest
)||(modified
))
845 AddLines(baseline
, yourline
, theirline
);
847 m_Diff3
.AddData(_T(""), DIFFSTATE_CONFLICTED
, resline
, EOL_NOENDING
, HIDESTATE_SHOWN
, -1);
854 if (m_arYourFile
.GetCount() > yourline
)
856 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_CONFLICTADDED
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
859 else if ((latestresolved
)||(modified
)||(modifiedresolved
))
861 m_YourBaseBoth
.AddData(emptyConflictEmpty
);
865 if (m_arTheirFile
.GetCount() > theirline
)
867 m_TheirBaseBoth
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_CONFLICTADDED
, theirline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_SHOWN
, -1);
870 else if ((modifiedresolved
)||(latest
)||(latestresolved
))
872 m_TheirBaseBoth
.AddData(emptyConflictEmpty
);
879 if (originalresolved
)
886 if (modifiedresolved
)
897 else if (tempdiff
->type
== svn_diff__type_diff_modified
)
900 for (int i
=0; i
<tempdiff
->original_length
; i
++)
902 if ((m_arBaseFile
.GetCount() > baseline
)&&(m_arYourFile
.GetCount() > yourline
))
904 AddLines(baseline
, yourline
, theirline
);
906 m_Diff3
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_THEIRSREMOVED
, DIFF_EMPTYLINENUMBER
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
, -1);
907 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_NORMAL
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
908 m_TheirBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_THEIRSREMOVED
, DIFF_EMPTYLINENUMBER
, EOL_NOENDING
, HIDESTATE_SHOWN
, -1);
915 for (int i
=0; i
<tempdiff
->modified_length
; i
++)
917 if (m_arTheirFile
.GetCount() > theirline
)
919 AddLines(baseline
, yourline
, theirline
);
921 m_Diff3
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_THEIRSADDED
, resline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_SHOWN
, -1);
922 m_YourBaseBoth
.AddEmpty();
923 m_TheirBaseBoth
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_THEIRSADDED
, theirline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_SHOWN
, -1);
930 else if (tempdiff
->type
== svn_diff__type_diff_latest
)
932 //YOURS differs from base
934 for (int i
=0; i
<tempdiff
->original_length
; i
++)
936 if ((m_arBaseFile
.GetCount() > baseline
)&&(m_arTheirFile
.GetCount() > theirline
))
938 AddLines(baseline
, yourline
, theirline
);
940 m_Diff3
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_YOURSREMOVED
, DIFF_EMPTYLINENUMBER
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
, -1);
941 m_YourBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_YOURSREMOVED
, DIFF_EMPTYLINENUMBER
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
, -1);
942 m_TheirBaseBoth
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_NORMAL
, theirline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_HIDDEN
, -1);
948 for (int i
=0; i
<tempdiff
->latest_length
; i
++)
950 if (m_arYourFile
.GetCount() > yourline
)
952 AddLines(baseline
, yourline
, theirline
);
954 m_Diff3
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_YOURSADDED
, resline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
955 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_IDENTICALADDED
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
956 m_TheirBaseBoth
.AddEmpty();
965 TRACE(_T("something bad happened during diff!\n"));
967 tempdiff
= tempdiff
->next
;
969 } // while (tempdiff)
971 if ((options
->ignore_space
!= svn_diff_file_ignore_space_none
)||(bIgnoreCase
)||(bIgnoreEOL
))
973 // If whitespaces are ignored, a conflict could have been missed
974 // We now go through all lines again and check if they're identical.
975 // If they're not, then that means it is a conflict, and we
976 // mark the conflict with the proper colors.
977 for (long i
=0; i
<m_Diff3
.GetCount(); ++i
)
979 DiffStates state1
= m_YourBaseBoth
.GetState(i
);
980 DiffStates state2
= m_TheirBaseBoth
.GetState(i
);
982 if (((state1
== DIFFSTATE_IDENTICALADDED
)||(state1
== DIFFSTATE_NORMAL
))&&
983 ((state2
== DIFFSTATE_IDENTICALADDED
)||(state2
== DIFFSTATE_NORMAL
)))
985 LONG lineyour
= m_arDiff3LinesYour
.GetAt(i
);
986 LONG linetheir
= m_arDiff3LinesTheir
.GetAt(i
);
987 LONG linebase
= m_arDiff3LinesBase
.GetAt(i
);
988 if ((lineyour
< m_arYourFile
.GetCount()) &&
989 (linetheir
< m_arTheirFile
.GetCount()) &&
990 (linebase
< m_arBaseFile
.GetCount()))
992 if (((m_arYourFile
.GetLineEnding(lineyour
)!=m_arBaseFile
.GetLineEnding(linebase
))&&
993 (m_arTheirFile
.GetLineEnding(linetheir
)!=m_arBaseFile
.GetLineEnding(linebase
))&&
994 (m_arYourFile
.GetLineEnding(lineyour
)!=m_arTheirFile
.GetLineEnding(linetheir
))) ||
995 ((m_arYourFile
.GetAt(lineyour
).Compare(m_arBaseFile
.GetAt(linebase
))!=0)&&
996 (m_arTheirFile
.GetAt(linetheir
).Compare(m_arBaseFile
.GetAt(linebase
))!=0)&&
997 (m_arYourFile
.GetAt(lineyour
).Compare(m_arTheirFile
.GetAt(linetheir
))!=0)))
999 m_Diff3
.SetState(i
, DIFFSTATE_CONFLICTED_IGNORED
);
1000 m_YourBaseBoth
.SetState(i
, DIFFSTATE_CONFLICTADDED
);
1001 m_TheirBaseBoth
.SetState(i
, DIFFSTATE_CONFLICTADDED
);
1007 ASSERT(m_Diff3
.GetCount() == m_YourBaseBoth
.GetCount());
1008 ASSERT(m_TheirBaseBoth
.GetCount() == m_YourBaseBoth
.GetCount());
1010 TRACE(_T("done with 3-way diff\n"));
1012 HideUnchangedSections(&m_Diff3
, &m_YourBaseBoth
, &m_TheirBaseBoth
);
1017 void CDiffData::HideUnchangedSections(CViewData
* data1
, CViewData
* data2
, CViewData
* data3
) const
1022 CRegDWORD contextLines
= CRegDWORD(_T("Software\\TortoiseGitMerge\\ContextLines"), 1);
1024 if (data1
->GetCount() > 1)
1026 HIDESTATE lastHideState
= data1
->GetHideState(0);
1027 if (lastHideState
== HIDESTATE_HIDDEN
)
1029 data1
->SetLineHideState(0, HIDESTATE_MARKER
);
1031 data2
->SetLineHideState(0, HIDESTATE_MARKER
);
1033 data3
->SetLineHideState(0, HIDESTATE_MARKER
);
1035 for (int i
= 1; i
< data1
->GetCount(); ++i
)
1037 HIDESTATE hideState
= data1
->GetHideState(i
);
1038 if (hideState
!= lastHideState
)
1040 if (hideState
== HIDESTATE_SHOWN
)
1042 // go back and show the last 'contextLines' lines to "SHOWN"
1043 int lineback
= i
- 1;
1044 int stopline
= lineback
- (int)(DWORD
)contextLines
;
1045 while ((lineback
>= 0)&&(lineback
> stopline
))
1047 data1
->SetLineHideState(lineback
, HIDESTATE_SHOWN
);
1049 data2
->SetLineHideState(lineback
, HIDESTATE_SHOWN
);
1051 data3
->SetLineHideState(lineback
, HIDESTATE_SHOWN
);
1055 else if ((hideState
== HIDESTATE_HIDDEN
)&&(lastHideState
!= HIDESTATE_MARKER
))
1057 // go forward and show the next 'contextLines' lines to "SHOWN"
1058 int lineforward
= i
+ 1;
1059 int stopline
= lineforward
+ (int)(DWORD
)contextLines
;
1060 while ((lineforward
< data1
->GetCount())&&(lineforward
< stopline
))
1062 data1
->SetLineHideState(lineforward
, HIDESTATE_SHOWN
);
1064 data2
->SetLineHideState(lineforward
, HIDESTATE_SHOWN
);
1066 data3
->SetLineHideState(lineforward
, HIDESTATE_SHOWN
);
1069 if ((lineforward
< data1
->GetCount())&&(data1
->GetHideState(lineforward
) == HIDESTATE_HIDDEN
))
1071 data1
->SetLineHideState(lineforward
, HIDESTATE_MARKER
);
1073 data2
->SetLineHideState(lineforward
, HIDESTATE_MARKER
);
1075 data3
->SetLineHideState(lineforward
, HIDESTATE_MARKER
);
1079 lastHideState
= hideState
;
1084 void CDiffData::SetCommentTokens( const CString
& sLineStart
, const CString
& sBlockStart
, const CString
& sBlockEnd
)
1086 m_CommentLineStart
= sLineStart
;
1087 m_CommentBlockStart
= sBlockStart
;
1088 m_CommentBlockEnd
= sBlockEnd
;
1091 void CDiffData::SetRegexTokens( const std::wregex
& rx
, const std::wstring
& replacement
)
1094 m_replacement
= replacement
;