For the inline diff, reverse the diff parts for the right view to get a diff consiste...
[TortoiseGit.git] / src / TortoiseMerge / DiffData.cpp
blobf48756a71777a9c17a2406578ed4fd2381c41214
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.
19 #include "stdafx.h"
20 #pragma warning(push)
21 #include "diff.h"
22 #pragma warning(pop)
23 #include "TempFile.h"
24 #include "registry.h"
25 #include "resource.h"
26 #include "DiffData.h"
27 #include "UnicodeUtils.h"
28 #include "svn_dso.h"
29 #include "MovedBlocks.h"
31 #pragma warning(push)
32 #pragma warning(disable: 4702) // unreachable code
33 int CDiffData::abort_on_pool_failure (int /*retcode*/)
35 abort ();
36 return -1;
38 #pragma warning(pop)
40 CDiffData::CDiffData(void)
41 : m_bViewMovedBlocks(false)
42 , m_bPatchRequired(false)
44 apr_initialize();
45 svn_dso_initialize2();
47 m_bBlame = false;
49 m_sPatchOriginal = _T(": original");
50 m_sPatchPatched = _T(": patched");
53 CDiffData::~CDiffData(void)
55 apr_terminate();
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();
70 return count;
73 svn_diff_file_ignore_space_t CDiffData::GetIgnoreSpaceMode(DWORD dwIgnoreWS) const
75 switch (dwIgnoreWS)
77 case 0:
78 return svn_diff_file_ignore_space_none;
79 case 1:
80 return svn_diff_file_ignore_space_all;
81 case 2:
82 return svn_diff_file_ignore_space_change;
83 default:
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);
93 return options;
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;
104 sMsg += _T("\n");
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);
110 return false;
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);
118 int toIndex = m_YourBaseRight.FindLineNumber(to);
119 if (toIndex < 0)
120 return;
121 m_YourBaseLeft.SetMovedIndex(fromIndex, toIndex, true);
122 m_YourBaseRight.SetMovedIndex(toIndex, fromIndex, false);
124 toIndex = m_YourBaseBoth.FindLineNumber(to);
125 if (toIndex < 0)
126 return;
127 while((toIndex < m_YourBaseBoth.GetCount())&&
128 ((m_YourBaseBoth.GetState(toIndex) != DIFFSTATE_ADDED)&&
129 (!m_YourBaseBoth.IsMoved(toIndex))||(m_YourBaseBoth.IsMovedFrom(toIndex))||
130 (m_YourBaseBoth.GetLineNumber(toIndex) != to)))
132 toIndex++;
135 fromIndex = m_YourBaseBoth.FindLineNumber(from);
136 if (fromIndex < 0)
137 return;
138 while((fromIndex < m_YourBaseBoth.GetCount())&&
139 ((m_YourBaseBoth.GetState(fromIndex) != DIFFSTATE_REMOVED)&&
140 (!m_YourBaseBoth.IsMoved(fromIndex))||(!m_YourBaseBoth.IsMovedFrom(fromIndex))||
141 (m_YourBaseBoth.GetLineNumber(fromIndex) != from)))
143 fromIndex++;
145 if ((fromIndex < m_YourBaseBoth.GetCount())&&(toIndex < m_YourBaseBoth.GetCount()))
147 m_YourBaseBoth.SetMovedIndex(fromIndex, toIndex, true);
148 m_YourBaseBoth.SetMovedIndex(toIndex, fromIndex, false);
153 bool CDiffData::CompareWithIgnoreWS(CString s1, CString s2, DWORD dwIgnoreWS) const
155 if (dwIgnoreWS == 2)
157 s1 = s1.TrimLeft(_T(" \t"));
158 s2 = s2.TrimLeft(_T(" \t"));
160 else
162 s1 = s1.TrimRight(_T(" \t"));
163 s2 = s2.TrimRight(_T(" \t"));
166 return s1 != s2;
169 void CDiffData::StickAndSkip(svn_diff_t * &tempdiff, apr_off_t &original_length_sticked, apr_off_t &modified_length_sticked) const
171 if((m_bViewMovedBlocks)&&(tempdiff->type == svn_diff__type_diff_modified))
173 svn_diff_t * nextdiff = tempdiff->next;
174 while((nextdiff)&&(nextdiff->type == svn_diff__type_diff_modified))
176 original_length_sticked += nextdiff->original_length;
177 modified_length_sticked += nextdiff->modified_length;
178 tempdiff = nextdiff;
179 nextdiff = tempdiff->next;
184 BOOL CDiffData::Load()
186 m_arBaseFile.RemoveAll();
187 m_arYourFile.RemoveAll();
188 m_arTheirFile.RemoveAll();
190 m_YourBaseBoth.Clear();
191 m_YourBaseLeft.Clear();
192 m_YourBaseRight.Clear();
194 m_TheirBaseBoth.Clear();
195 m_TheirBaseLeft.Clear();
196 m_TheirBaseRight.Clear();
198 m_Diff3.Clear();
200 CRegDWORD regIgnoreWS = CRegDWORD(_T("Software\\TortoiseGitMerge\\IgnoreWS"));
201 CRegDWORD regIgnoreEOL = CRegDWORD(_T("Software\\TortoiseGitMerge\\IgnoreEOL"), TRUE);
202 CRegDWORD regIgnoreCase = CRegDWORD(_T("Software\\TortoiseGitMerge\\CaseInsensitive"), FALSE);
203 CRegDWORD regIgnoreComments = CRegDWORD(_T("Software\\TortoiseGitMerge\\IgnoreComments"), FALSE);
204 DWORD dwIgnoreWS = regIgnoreWS;
205 bool bIgnoreEOL = ((DWORD)regIgnoreEOL)!=0;
206 BOOL bIgnoreCase = ((DWORD)regIgnoreCase)!=0;
207 bool bIgnoreComments = ((DWORD)regIgnoreComments)!=0;
209 // The Subversion diff API only can ignore whitespaces and eol styles.
210 // It also can only handle one-byte charsets.
211 // To ignore case changes or to handle UTF-16 files, we have to
212 // save the original file in UTF-8 and/or the letters changed to lowercase
213 // so the Subversion diff can handle those.
214 CString sConvertedBaseFilename = m_baseFile.GetFilename();
215 CString sConvertedYourFilename = m_yourFile.GetFilename();
216 CString sConvertedTheirFilename = m_theirFile.GetFilename();
218 m_baseFile.StoreFileAttributes();
219 m_theirFile.StoreFileAttributes();
220 m_yourFile.StoreFileAttributes();
221 //m_mergedFile.StoreFileAttributes();
223 bool bBaseNeedConvert = false;
224 bool bTheirNeedConvert = false;
225 bool bYourNeedConvert = false;
226 bool bBaseIsUtf8 = false;
227 bool bTheirIsUtf8 = false;
228 bool bYourIsUtf8 = false;
230 if (IsBaseFileInUse())
232 if (!m_arBaseFile.Load(m_baseFile.GetFilename()))
234 m_sError = m_arBaseFile.GetErrorString();
235 return FALSE;
237 bBaseNeedConvert = bIgnoreCase || bIgnoreComments || (m_arBaseFile.NeedsConversion()) || !m_rx._Empty();
238 bBaseIsUtf8 = (m_arBaseFile.GetUnicodeType()!=CFileTextLines::ASCII) || bBaseNeedConvert;
241 if (IsTheirFileInUse())
243 // m_arBaseFile.GetCount() is passed as a hint for the number of lines in this file
244 // It's a fair guess that the files will be roughly the same size
245 if (!m_arTheirFile.Load(m_theirFile.GetFilename(), m_arBaseFile.GetCount()))
247 m_sError = m_arTheirFile.GetErrorString();
248 return FALSE;
250 bTheirNeedConvert = bIgnoreCase || bIgnoreComments || (m_arTheirFile.NeedsConversion()) || !m_rx._Empty();
251 bTheirIsUtf8 = (m_arTheirFile.GetUnicodeType()!=CFileTextLines::ASCII) || bTheirNeedConvert;
254 if (IsYourFileInUse())
256 // m_arBaseFile.GetCount() is passed as a hint for the number of lines in this file
257 // It's a fair guess that the files will be roughly the same size
258 if (!m_arYourFile.Load(m_yourFile.GetFilename(), m_arBaseFile.GetCount()))
260 m_sError = m_arYourFile.GetErrorString();
261 return FALSE;
263 bYourNeedConvert = bIgnoreCase || bIgnoreComments || (m_arYourFile.NeedsConversion()) || !m_rx._Empty();
264 bYourIsUtf8 = (m_arYourFile.GetUnicodeType()!=CFileTextLines::ASCII) || bYourNeedConvert;
267 // in case at least one of the files got converted or is UTF8
268 // we have to convert all non UTF8 (ASCII) files
269 // otherwise one file might be in ANSI and the other in UTF8 and we'll end up
270 // with lines marked as different throughout the files even though the lines
271 // would show no change at all in the viewer.
273 // convert all files we need to
274 bool bIsUtf8 = bBaseIsUtf8 || bTheirIsUtf8 || bYourIsUtf8; // any file end as UTF8
275 bBaseNeedConvert |= (IsBaseFileInUse() && !bBaseIsUtf8 && bIsUtf8);
276 if (bBaseNeedConvert)
278 sConvertedBaseFilename = CTempFiles::Instance().GetTempFilePathString();
279 m_baseFile.SetConvertedFileName(sConvertedBaseFilename);
280 m_arBaseFile.Save(sConvertedBaseFilename, true, true, 0, bIgnoreCase, m_bBlame
281 , bIgnoreComments, m_CommentLineStart, m_CommentBlockStart, m_CommentBlockEnd
282 , m_rx, m_replacement);
284 bYourNeedConvert |= (IsYourFileInUse() && !bYourIsUtf8 && bIsUtf8);
285 if (bYourNeedConvert)
287 sConvertedYourFilename = CTempFiles::Instance().GetTempFilePathString();
288 m_yourFile.SetConvertedFileName(sConvertedYourFilename);
289 m_arYourFile.Save(sConvertedYourFilename, true, true, 0, bIgnoreCase, m_bBlame
290 , bIgnoreComments, m_CommentLineStart, m_CommentBlockStart, m_CommentBlockEnd
291 , m_rx, m_replacement);
293 bTheirNeedConvert |= (IsTheirFileInUse() && !bTheirIsUtf8 && bIsUtf8);
294 if (bTheirNeedConvert)
296 sConvertedTheirFilename = CTempFiles::Instance().GetTempFilePathString();
297 m_theirFile.SetConvertedFileName(sConvertedTheirFilename);
298 m_arTheirFile.Save(sConvertedTheirFilename, true, true, 0, bIgnoreCase, m_bBlame
299 , bIgnoreComments, m_CommentLineStart, m_CommentBlockStart, m_CommentBlockEnd
300 , m_rx, m_replacement);
303 // Calculate the number of lines in the largest of the three files
304 int lengthHint = GetLineCount();
308 m_YourBaseBoth.Reserve(lengthHint);
309 m_YourBaseLeft.Reserve(lengthHint);
310 m_YourBaseRight.Reserve(lengthHint);
312 m_TheirBaseBoth.Reserve(lengthHint);
313 m_TheirBaseLeft.Reserve(lengthHint);
314 m_TheirBaseRight.Reserve(lengthHint);
316 catch (CMemoryException* e)
318 e->GetErrorMessage(m_sError.GetBuffer(255), 255);
319 m_sError.ReleaseBuffer();
320 e->Delete();
321 return FALSE;
324 apr_pool_t * pool = NULL;
325 apr_pool_create_ex (&pool, NULL, abort_on_pool_failure, NULL);
327 // Is this a two-way diff?
328 if (IsBaseFileInUse() && IsYourFileInUse() && !IsTheirFileInUse())
330 if (!DoTwoWayDiff(sConvertedBaseFilename, sConvertedYourFilename, dwIgnoreWS, bIgnoreEOL, pool))
332 apr_pool_destroy (pool); // free the allocated memory
333 return FALSE;
337 if (IsBaseFileInUse() && IsTheirFileInUse() && !IsYourFileInUse())
339 ASSERT(FALSE);
342 // Is this a three-way diff?
343 if (IsBaseFileInUse() && IsTheirFileInUse() && IsYourFileInUse())
345 m_Diff3.Reserve(lengthHint);
347 if (!DoThreeWayDiff(sConvertedBaseFilename, sConvertedYourFilename, sConvertedTheirFilename, dwIgnoreWS, bIgnoreEOL, !!bIgnoreCase, pool))
349 apr_pool_destroy (pool); // free the allocated memory
350 return FALSE;
354 // free the allocated memory
355 apr_pool_destroy (pool);
357 m_arBaseFile.RemoveAll();
358 m_arYourFile.RemoveAll();
359 m_arTheirFile.RemoveAll();
361 return TRUE;
364 bool
365 CDiffData::DoTwoWayDiff(const CString& sBaseFilename, const CString& sYourFilename, DWORD dwIgnoreWS, bool bIgnoreEOL, apr_pool_t * pool)
367 svn_diff_file_options_t * options = CreateDiffFileOptions(dwIgnoreWS, bIgnoreEOL, pool);
368 // convert CString filenames (UTF-16 or ANSI) to UTF-8
369 CStringA sBaseFilenameUtf8 = CUnicodeUtils::GetUTF8(sBaseFilename);
370 CStringA sYourFilenameUtf8 = CUnicodeUtils::GetUTF8(sYourFilename);
372 svn_diff_t * diffYourBase = NULL;
373 svn_error_t * svnerr = svn_diff_file_diff_2(&diffYourBase, sBaseFilenameUtf8, sYourFilenameUtf8, options, pool);
375 if (svnerr)
376 return HandleSvnError(svnerr);
378 tsvn_svn_diff_t_extension * movedBlocks = NULL;
379 if(m_bViewMovedBlocks)
380 movedBlocks = MovedBlocksDetect(diffYourBase, dwIgnoreWS, pool); // Side effect is that diffs are now splitted
382 svn_diff_t * tempdiff = diffYourBase;
383 LONG baseline = 0;
384 LONG yourline = 0;
385 while (tempdiff)
387 svn_diff__type_e diffType = tempdiff->type;
388 // Side effect described above overcoming - sticking together
389 apr_off_t original_length_sticked = tempdiff->original_length;
390 apr_off_t modified_length_sticked = tempdiff->modified_length;
391 StickAndSkip(tempdiff, original_length_sticked, modified_length_sticked);
393 for (int i=0; i<original_length_sticked; i++)
395 if (baseline >= m_arBaseFile.GetCount())
397 m_sError.LoadString(IDS_ERR_DIFF_NEWLINES);
398 return false;
400 const CString& sCurrentBaseLine = m_arBaseFile.GetAt(baseline);
401 EOL endingBase = m_arBaseFile.GetLineEnding(baseline);
402 if (diffType == svn_diff__type_common)
404 if (yourline >= m_arYourFile.GetCount())
406 m_sError.LoadString(IDS_ERR_DIFF_NEWLINES);
407 return false;
409 const CString& sCurrentYourLine = m_arYourFile.GetAt(yourline);
410 EOL endingYours = m_arYourFile.GetLineEnding(yourline);
411 if (sCurrentBaseLine != sCurrentYourLine)
413 bool changedWS = false;
414 if (dwIgnoreWS == 2 || dwIgnoreWS == 3)
415 changedWS = CompareWithIgnoreWS(sCurrentBaseLine, sCurrentYourLine, dwIgnoreWS);
416 if (changedWS || dwIgnoreWS == 0)
418 // one-pane view: two lines, one 'removed' and one 'added'
419 m_YourBaseBoth.AddData(sCurrentBaseLine, DIFFSTATE_REMOVEDWHITESPACE, yourline, endingBase, HIDESTATE_SHOWN, -1);
420 m_YourBaseBoth.AddData(sCurrentYourLine, DIFFSTATE_ADDEDWHITESPACE, yourline, endingYours, HIDESTATE_SHOWN, -1);
422 else
424 m_YourBaseBoth.AddData(sCurrentYourLine, DIFFSTATE_NORMAL, yourline, endingBase, HIDESTATE_HIDDEN, -1);
427 else
429 m_YourBaseBoth.AddData(sCurrentYourLine, DIFFSTATE_NORMAL, yourline, endingBase, HIDESTATE_HIDDEN, -1);
431 yourline++; //in both files
433 else
434 { // small trick - we need here a baseline, but we fix it back to yourline at the end of routine
435 m_YourBaseBoth.AddData(sCurrentBaseLine, DIFFSTATE_REMOVED, -1, endingBase, HIDESTATE_SHOWN, -1);
437 baseline++;
439 if (diffType == svn_diff__type_diff_modified)
441 for (int i=0; i<modified_length_sticked; i++)
443 if (m_arYourFile.GetCount() > yourline)
445 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_ADDED, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
447 yourline++;
450 tempdiff = tempdiff->next;
453 HideUnchangedSections(&m_YourBaseBoth, NULL, NULL);
455 tempdiff = diffYourBase;
456 baseline = 0;
457 yourline = 0;
458 while (tempdiff)
460 if (tempdiff->type == svn_diff__type_common)
462 for (int i=0; i<tempdiff->original_length; i++)
464 const CString& sCurrentYourLine = m_arYourFile.GetAt(yourline);
465 EOL endingYours = m_arYourFile.GetLineEnding(yourline);
466 const CString& sCurrentBaseLine = m_arBaseFile.GetAt(baseline);
467 EOL endingBase = m_arBaseFile.GetLineEnding(baseline);
468 if (sCurrentBaseLine != sCurrentYourLine)
470 bool changedWS = false;
471 if (dwIgnoreWS == 2 || dwIgnoreWS == 3)
472 changedWS = CompareWithIgnoreWS(sCurrentBaseLine, sCurrentYourLine, dwIgnoreWS);
473 if (changedWS || dwIgnoreWS == 0)
475 m_YourBaseLeft.AddData(sCurrentBaseLine, DIFFSTATE_WHITESPACE, baseline, endingBase, HIDESTATE_SHOWN, -1);
476 m_YourBaseRight.AddData(sCurrentYourLine, DIFFSTATE_WHITESPACE, yourline, endingYours, HIDESTATE_SHOWN, -1);
478 else
480 m_YourBaseLeft.AddData(sCurrentBaseLine, DIFFSTATE_NORMAL, baseline, endingBase, HIDESTATE_HIDDEN, -1);
481 m_YourBaseRight.AddData(sCurrentYourLine, DIFFSTATE_NORMAL, yourline, endingYours, HIDESTATE_HIDDEN, -1);
484 else
486 m_YourBaseLeft.AddData(sCurrentBaseLine, DIFFSTATE_NORMAL, baseline, endingBase, HIDESTATE_HIDDEN, -1);
487 m_YourBaseRight.AddData(sCurrentYourLine, DIFFSTATE_NORMAL, yourline, endingYours, HIDESTATE_HIDDEN, -1);
489 baseline++;
490 yourline++;
493 if (tempdiff->type == svn_diff__type_diff_modified)
495 // now we trying to stick together parts, that were splitted by MovedBlocks
496 apr_off_t original_length_sticked = tempdiff->original_length;
497 apr_off_t modified_length_sticked = tempdiff->modified_length;
498 StickAndSkip(tempdiff, original_length_sticked, modified_length_sticked);
500 apr_off_t original_length = original_length_sticked;
501 for (int i=0; i<modified_length_sticked; i++)
503 if (m_arYourFile.GetCount() > yourline)
505 EOL endingYours = m_arYourFile.GetLineEnding(yourline);
506 m_YourBaseRight.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_ADDED, yourline, endingYours, HIDESTATE_SHOWN, -1);
507 if (original_length-- <= 0)
509 m_YourBaseLeft.AddEmpty();
511 else
513 EOL endingBase = m_arBaseFile.GetLineEnding(baseline);
514 m_YourBaseLeft.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_REMOVED, baseline, endingBase, HIDESTATE_SHOWN, -1);
515 baseline++;
517 yourline++;
520 apr_off_t modified_length = modified_length_sticked;
521 for (int i=0; i<original_length_sticked; i++)
523 if ((modified_length-- <= 0)&&(m_arBaseFile.GetCount() > baseline))
525 EOL endingBase = m_arBaseFile.GetLineEnding(baseline);
526 m_YourBaseLeft.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_REMOVED, baseline, endingBase, HIDESTATE_SHOWN, -1);
527 m_YourBaseRight.AddEmpty();
528 baseline++;
532 tempdiff = tempdiff->next;
534 // add last (empty) lines if needed - diff don't report those
535 if (m_arBaseFile.GetCount() > baseline)
537 if (m_arYourFile.GetCount() > yourline)
539 // last line is missing in both files add them to end and mark as no diff
540 m_YourBaseLeft.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_NORMAL, baseline, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN, -1);
541 m_YourBaseRight.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_NORMAL, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
542 yourline++;
543 baseline++;
545 else
547 viewdata oViewData(m_arBaseFile.GetAt(baseline), DIFFSTATE_REMOVED, baseline, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN);
548 baseline++;
550 // find first EMPTY line in last blok
551 int nPos = m_YourBaseLeft.GetCount();
552 while (--nPos>=0 && m_YourBaseLeft.GetState(nPos)==DIFFSTATE_EMPTY) ;
553 if (++nPos<m_YourBaseLeft.GetCount())
555 m_YourBaseLeft.SetData(nPos, oViewData);
557 else
559 m_YourBaseLeft.AddData(oViewData);
560 m_YourBaseRight.AddEmpty();
564 else if (m_arYourFile.GetCount() > yourline)
566 viewdata oViewData(m_arYourFile.GetAt(yourline), DIFFSTATE_ADDED, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN);
567 yourline++;
569 // try to move last line higher
570 int nPos = m_YourBaseRight.GetCount();
571 while (--nPos>=0 && m_YourBaseRight.GetState(nPos)==DIFFSTATE_EMPTY) ;
572 if (++nPos<m_YourBaseRight.GetCount())
574 m_YourBaseRight.SetData(nPos, oViewData);
576 else
578 m_YourBaseLeft.AddEmpty();
579 m_YourBaseRight.AddData(oViewData);
584 // Fixing results for conforming moved blocks
586 while(movedBlocks)
588 tempdiff = movedBlocks->base;
589 if(movedBlocks->moved_to != -1)
591 // set states in a block original:length -> moved_to:length
592 TieMovedBlocks((int)tempdiff->original_start, movedBlocks->moved_to, tempdiff->original_length);
594 if(movedBlocks->moved_from != -1)
596 // set states in a block modified:length -> moved_from:length
597 TieMovedBlocks(movedBlocks->moved_from, (int)tempdiff->modified_start, tempdiff->modified_length);
599 movedBlocks = movedBlocks->next;
602 // replace baseline with the yourline in m_YourBaseBoth
603 /* yourline = 0;
604 for(int i=0; i<m_YourBaseBoth.GetCount(); i++)
606 DiffStates state = m_YourBaseBoth.GetState(i);
607 if((state == DIFFSTATE_REMOVED)||(state == DIFFSTATE_MOVED_FROM))
609 m_YourBaseBoth.SetLineNumber(i, -1);
611 else
613 yourline++;
615 }//*/
617 TRACE(_T("done with 2-way diff\n"));
619 HideUnchangedSections(&m_YourBaseLeft, &m_YourBaseRight, NULL);
621 return true;
624 bool
625 CDiffData::DoThreeWayDiff(const CString& sBaseFilename, const CString& sYourFilename, const CString& sTheirFilename, DWORD dwIgnoreWS, bool bIgnoreEOL, bool bIgnoreCase, apr_pool_t * pool)
627 // the following three arrays are used to check for conflicts even in case the
628 // user has ignored spaces/eols.
629 CStdDWORDArray m_arDiff3LinesBase;
630 CStdDWORDArray m_arDiff3LinesYour;
631 CStdDWORDArray m_arDiff3LinesTheir;
632 #define AddLines(baseline, yourline, theirline) m_arDiff3LinesBase.Add(baseline), m_arDiff3LinesYour.Add(yourline), m_arDiff3LinesTheir.Add(theirline)
633 int lengthHint = GetLineCount();
635 m_arDiff3LinesBase.Reserve(lengthHint);
636 m_arDiff3LinesYour.Reserve(lengthHint);
637 m_arDiff3LinesTheir.Reserve(lengthHint);
639 CRegDWORD contextLines = CRegDWORD(_T("Software\\TortoiseGitMerge\\ContextLines"), 3);
640 svn_diff_file_options_t * options = CreateDiffFileOptions(dwIgnoreWS, bIgnoreEOL, pool);
642 // convert CString filenames (UTF-16 or ANSI) to UTF-8
643 CStringA sBaseFilenameUtf8 = CUnicodeUtils::GetUTF8(sBaseFilename);
644 CStringA sYourFilenameUtf8 = CUnicodeUtils::GetUTF8(sYourFilename);
645 CStringA sTheirFilenameUtf8 = CUnicodeUtils::GetUTF8(sTheirFilename);
647 svn_diff_t * diffTheirYourBase = NULL;
648 svn_error_t * svnerr = svn_diff_file_diff3_2(&diffTheirYourBase, sBaseFilenameUtf8, sTheirFilenameUtf8, sYourFilenameUtf8, options, pool);
649 if (svnerr)
650 return HandleSvnError(svnerr);
652 svn_diff_t * tempdiff = diffTheirYourBase;
653 LONG baseline = 0;
654 LONG yourline = 0;
655 LONG theirline = 0;
656 LONG resline = 0;
657 // common viewdata
658 const viewdata emptyConflictEmpty(_T(""), DIFFSTATE_CONFLICTEMPTY, DIFF_EMPTYLINENUMBER, EOL_NOENDING, HIDESTATE_SHOWN);
659 const viewdata emptyIdenticalRemoved(_T(""), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, EOL_NOENDING, HIDESTATE_SHOWN);
660 while (tempdiff)
662 if (tempdiff->type == svn_diff__type_common)
664 ASSERT((tempdiff->latest_length == tempdiff->modified_length) && (tempdiff->modified_length == tempdiff->original_length));
665 for (int i=0; i<tempdiff->original_length; i++)
667 if ((m_arYourFile.GetCount() > yourline)&&(m_arTheirFile.GetCount() > theirline))
669 AddLines(baseline, yourline, theirline);
671 m_Diff3.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_NORMAL, resline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_HIDDEN, -1);
672 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_NORMAL, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_HIDDEN, -1);
673 m_TheirBaseBoth.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_NORMAL, theirline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_HIDDEN, -1);
675 baseline++;
676 yourline++;
677 theirline++;
678 resline++;
682 else if (tempdiff->type == svn_diff__type_diff_common)
684 ASSERT(tempdiff->latest_length == tempdiff->modified_length);
685 //both theirs and yours have lines replaced
686 for (int i=0; i<tempdiff->original_length; i++)
688 if (m_arBaseFile.GetCount() > baseline)
690 AddLines(baseline, yourline, theirline);
692 m_Diff3.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN, -1);
693 m_YourBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, EOL_NOENDING, HIDESTATE_SHOWN, -1);
694 m_TheirBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, EOL_NOENDING, HIDESTATE_SHOWN, -1);
696 baseline++;
699 for (int i=0; i<tempdiff->modified_length; i++)
701 if ((m_arYourFile.GetCount() > yourline)&&(m_arTheirFile.GetCount() > theirline))
703 AddLines(baseline, yourline, theirline);
705 m_Diff3.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_IDENTICALADDED, resline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
706 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_IDENTICALADDED, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
707 m_TheirBaseBoth.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_IDENTICALADDED, resline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_SHOWN, -1);
709 yourline++;
710 theirline++;
711 resline++;
715 else if (tempdiff->type == svn_diff__type_conflict)
717 apr_off_t length = max(tempdiff->original_length, tempdiff->modified_length);
718 length = max(tempdiff->latest_length, length);
719 apr_off_t original = tempdiff->original_length;
720 apr_off_t modified = tempdiff->modified_length;
721 apr_off_t latest = tempdiff->latest_length;
723 apr_off_t originalresolved = 0;
724 apr_off_t modifiedresolved = 0;
725 apr_off_t latestresolved = 0;
727 LONG base = baseline;
728 LONG your = yourline;
729 LONG their = theirline;
730 if (tempdiff->resolved_diff)
732 originalresolved = tempdiff->resolved_diff->original_length;
733 modifiedresolved = tempdiff->resolved_diff->modified_length;
734 latestresolved = tempdiff->resolved_diff->latest_length;
736 for (int i=0; i<length; i++)
738 EOL endingBase = m_arBaseFile.GetCount() > baseline ? m_arBaseFile.GetLineEnding(baseline) : EOL_AUTOLINE;
739 if (original)
741 if (m_arBaseFile.GetCount() > baseline)
743 AddLines(baseline, yourline, theirline);
745 m_Diff3.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, endingBase , HIDESTATE_SHOWN, -1);
746 m_YourBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, endingBase , HIDESTATE_SHOWN, -1);
747 m_TheirBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, endingBase , HIDESTATE_SHOWN, -1);
750 else if ((originalresolved)||((modifiedresolved)&&(latestresolved)))
752 AddLines(baseline, yourline, theirline);
754 m_Diff3.AddData(emptyIdenticalRemoved);
755 if ((latestresolved)&&(modifiedresolved))
757 m_YourBaseBoth.AddData(emptyIdenticalRemoved);
758 m_TheirBaseBoth.AddData(emptyIdenticalRemoved);
761 if (original)
763 original--;
764 baseline++;
766 if (originalresolved)
767 originalresolved--;
769 if (modified)
771 modified--;
772 theirline++;
774 if (modifiedresolved)
775 modifiedresolved--;
776 if (latest)
778 latest--;
779 yourline++;
781 if (latestresolved)
782 latestresolved--;
784 original = tempdiff->original_length;
785 modified = tempdiff->modified_length;
786 latest = tempdiff->latest_length;
787 baseline = base;
788 yourline = your;
789 theirline = their;
790 if (tempdiff->resolved_diff)
792 originalresolved = tempdiff->resolved_diff->original_length;
793 modifiedresolved = tempdiff->resolved_diff->modified_length;
794 latestresolved = tempdiff->resolved_diff->latest_length;
796 for (int i=0; i<length; i++)
798 if ((latest)||(modified))
800 AddLines(baseline, yourline, theirline);
802 m_Diff3.AddData(_T(""), DIFFSTATE_CONFLICTED, resline, EOL_NOENDING, HIDESTATE_SHOWN, -1);
804 resline++;
807 if (latest)
809 if (m_arYourFile.GetCount() > yourline)
811 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_CONFLICTADDED, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
814 else if ((latestresolved)||(modified)||(modifiedresolved))
816 m_YourBaseBoth.AddData(emptyConflictEmpty);
818 if (modified)
820 if (m_arTheirFile.GetCount() > theirline)
822 m_TheirBaseBoth.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_CONFLICTADDED, theirline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_SHOWN, -1);
825 else if ((modifiedresolved)||(latest)||(latestresolved))
827 m_TheirBaseBoth.AddData(emptyConflictEmpty);
829 if (original)
831 original--;
832 baseline++;
834 if (originalresolved)
835 originalresolved--;
836 if (modified)
838 modified--;
839 theirline++;
841 if (modifiedresolved)
842 modifiedresolved--;
843 if (latest)
845 latest--;
846 yourline++;
848 if (latestresolved)
849 latestresolved--;
852 else if (tempdiff->type == svn_diff__type_diff_modified)
854 //deleted!
855 for (int i=0; i<tempdiff->original_length; i++)
857 if ((m_arBaseFile.GetCount() > baseline)&&(m_arYourFile.GetCount() > yourline))
859 AddLines(baseline, yourline, theirline);
861 m_Diff3.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_THEIRSREMOVED, DIFF_EMPTYLINENUMBER, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN, -1);
862 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_NORMAL, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
863 m_TheirBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_THEIRSREMOVED, DIFF_EMPTYLINENUMBER, EOL_NOENDING, HIDESTATE_SHOWN, -1);
865 baseline++;
866 yourline++;
869 //added
870 for (int i=0; i<tempdiff->modified_length; i++)
872 if (m_arTheirFile.GetCount() > theirline)
874 AddLines(baseline, yourline, theirline);
876 m_Diff3.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_THEIRSADDED, resline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_SHOWN, -1);
877 m_YourBaseBoth.AddEmpty();
878 m_TheirBaseBoth.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_THEIRSADDED, theirline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_SHOWN, -1);
880 theirline++;
881 resline++;
885 else if (tempdiff->type == svn_diff__type_diff_latest)
887 //YOURS differs from base
889 for (int i=0; i<tempdiff->original_length; i++)
891 if ((m_arBaseFile.GetCount() > baseline)&&(m_arTheirFile.GetCount() > theirline))
893 AddLines(baseline, yourline, theirline);
895 m_Diff3.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_YOURSREMOVED, DIFF_EMPTYLINENUMBER, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN, -1);
896 m_YourBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_YOURSREMOVED, DIFF_EMPTYLINENUMBER, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN, -1);
897 m_TheirBaseBoth.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_NORMAL, theirline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_HIDDEN, -1);
899 baseline++;
900 theirline++;
903 for (int i=0; i<tempdiff->latest_length; i++)
905 if (m_arYourFile.GetCount() > yourline)
907 AddLines(baseline, yourline, theirline);
909 m_Diff3.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_YOURSADDED, resline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
910 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_IDENTICALADDED, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
911 m_TheirBaseBoth.AddEmpty();
913 yourline++;
914 resline++;
918 else
920 TRACE(_T("something bad happened during diff!\n"));
922 tempdiff = tempdiff->next;
924 } // while (tempdiff)
926 if ((options->ignore_space != svn_diff_file_ignore_space_none)||(bIgnoreCase)||(bIgnoreEOL))
928 // If whitespaces are ignored, a conflict could have been missed
929 // We now go through all lines again and check if they're identical.
930 // If they're not, then that means it is a conflict, and we
931 // mark the conflict with the proper colors.
932 for (long i=0; i<m_Diff3.GetCount(); ++i)
934 DiffStates state1 = m_YourBaseBoth.GetState(i);
935 DiffStates state2 = m_TheirBaseBoth.GetState(i);
937 if (((state1 == DIFFSTATE_IDENTICALADDED)||(state1 == DIFFSTATE_NORMAL))&&
938 ((state2 == DIFFSTATE_IDENTICALADDED)||(state2 == DIFFSTATE_NORMAL)))
940 LONG lineyour = m_arDiff3LinesYour.GetAt(i);
941 LONG linetheir = m_arDiff3LinesTheir.GetAt(i);
942 LONG linebase = m_arDiff3LinesBase.GetAt(i);
943 if ((lineyour < m_arYourFile.GetCount()) &&
944 (linetheir < m_arTheirFile.GetCount()) &&
945 (linebase < m_arBaseFile.GetCount()))
947 if (((m_arYourFile.GetLineEnding(lineyour)!=m_arBaseFile.GetLineEnding(linebase))&&
948 (m_arTheirFile.GetLineEnding(linetheir)!=m_arBaseFile.GetLineEnding(linebase))&&
949 (m_arYourFile.GetLineEnding(lineyour)!=m_arTheirFile.GetLineEnding(linetheir))) ||
950 ((m_arYourFile.GetAt(lineyour).Compare(m_arBaseFile.GetAt(linebase))!=0)&&
951 (m_arTheirFile.GetAt(linetheir).Compare(m_arBaseFile.GetAt(linebase))!=0)&&
952 (m_arYourFile.GetAt(lineyour).Compare(m_arTheirFile.GetAt(linetheir))!=0)))
954 m_Diff3.SetState(i, DIFFSTATE_CONFLICTED_IGNORED);
955 m_YourBaseBoth.SetState(i, DIFFSTATE_CONFLICTADDED);
956 m_TheirBaseBoth.SetState(i, DIFFSTATE_CONFLICTADDED);
962 ASSERT(m_Diff3.GetCount() == m_YourBaseBoth.GetCount());
963 ASSERT(m_TheirBaseBoth.GetCount() == m_YourBaseBoth.GetCount());
965 TRACE(_T("done with 3-way diff\n"));
967 HideUnchangedSections(&m_Diff3, &m_YourBaseBoth, &m_TheirBaseBoth);
969 return true;
972 void CDiffData::HideUnchangedSections(CViewData * data1, CViewData * data2, CViewData * data3) const
974 if (data1 == NULL)
975 return;
977 CRegDWORD contextLines = CRegDWORD(_T("Software\\TortoiseGitMerge\\ContextLines"), 1);
979 if (data1->GetCount() > 1)
981 HIDESTATE lastHideState = data1->GetHideState(0);
982 if (lastHideState == HIDESTATE_HIDDEN)
984 data1->SetLineHideState(0, HIDESTATE_MARKER);
985 if (data2)
986 data2->SetLineHideState(0, HIDESTATE_MARKER);
987 if (data3)
988 data3->SetLineHideState(0, HIDESTATE_MARKER);
990 for (int i = 1; i < data1->GetCount(); ++i)
992 HIDESTATE hideState = data1->GetHideState(i);
993 if (hideState != lastHideState)
995 if (hideState == HIDESTATE_SHOWN)
997 // go back and show the last 'contextLines' lines to "SHOWN"
998 int lineback = i - 1;
999 int stopline = lineback - (int)(DWORD)contextLines;
1000 while ((lineback >= 0)&&(lineback > stopline))
1002 data1->SetLineHideState(lineback, HIDESTATE_SHOWN);
1003 if (data2)
1004 data2->SetLineHideState(lineback, HIDESTATE_SHOWN);
1005 if (data3)
1006 data3->SetLineHideState(lineback, HIDESTATE_SHOWN);
1007 lineback--;
1010 else if ((hideState == HIDESTATE_HIDDEN)&&(lastHideState != HIDESTATE_MARKER))
1012 // go forward and show the next 'contextLines' lines to "SHOWN"
1013 int lineforward = i + 1;
1014 int stopline = lineforward + (int)(DWORD)contextLines;
1015 while ((lineforward < data1->GetCount())&&(lineforward < stopline))
1017 data1->SetLineHideState(lineforward, HIDESTATE_SHOWN);
1018 if (data2)
1019 data2->SetLineHideState(lineforward, HIDESTATE_SHOWN);
1020 if (data3)
1021 data3->SetLineHideState(lineforward, HIDESTATE_SHOWN);
1022 lineforward++;
1024 if ((lineforward < data1->GetCount())&&(data1->GetHideState(lineforward) == HIDESTATE_HIDDEN))
1026 data1->SetLineHideState(lineforward, HIDESTATE_MARKER);
1027 if (data2)
1028 data2->SetLineHideState(lineforward, HIDESTATE_MARKER);
1029 if (data3)
1030 data3->SetLineHideState(lineforward, HIDESTATE_MARKER);
1034 lastHideState = hideState;
1039 void CDiffData::SetCommentTokens( const CString& sLineStart, const CString& sBlockStart, const CString& sBlockEnd )
1041 m_CommentLineStart = sLineStart;
1042 m_CommentBlockStart = sBlockStart;
1043 m_CommentBlockEnd = sBlockEnd;
1046 void CDiffData::SetRegexTokens( const std::wregex& rx, const std::wstring& replacement )
1048 m_rx = rx;
1049 m_replacement = replacement;