Open the explorer with the converted temp file selected on a Ctrl+trippleclick on...
[TortoiseGit.git] / src / TortoiseMerge / DiffData.cpp
blob7d2cb51464374d87d15362727150fe2a2029b053
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2006-2013 - 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)
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 m_YourBaseLeft.SetState(fromIndex, DIFFSTATE_MOVED_FROM);
120 m_YourBaseLeft.SetMovedIndex(fromIndex, toIndex);
121 m_YourBaseRight.SetState(toIndex, DIFFSTATE_MOVED_TO);
122 m_YourBaseRight.SetMovedIndex(toIndex, fromIndex);
124 toIndex = m_YourBaseBoth.FindLineNumber(to);
125 while((toIndex < m_YourBaseBoth.GetCount())&&
126 ((m_YourBaseBoth.GetState(toIndex) != DIFFSTATE_ADDED)&&
127 (m_YourBaseBoth.GetState(toIndex) != DIFFSTATE_MOVED_TO)||
128 (m_YourBaseBoth.GetLineNumber(toIndex) != to)))
130 toIndex++;
133 fromIndex = m_YourBaseBoth.FindLineNumber(from);
134 while((fromIndex < m_YourBaseBoth.GetCount())&&
135 ((m_YourBaseBoth.GetState(fromIndex) != DIFFSTATE_REMOVED)&&
136 (m_YourBaseBoth.GetState(fromIndex) != DIFFSTATE_MOVED_FROM)||
137 (m_YourBaseBoth.GetLineNumber(fromIndex) != from)))
139 fromIndex++;
141 if ((fromIndex < m_YourBaseBoth.GetCount())&&(toIndex < m_YourBaseBoth.GetCount()))
143 m_YourBaseBoth.SetState(fromIndex, DIFFSTATE_MOVED_FROM);
144 m_YourBaseBoth.SetMovedIndex(fromIndex, toIndex);
145 m_YourBaseBoth.SetState(toIndex, DIFFSTATE_MOVED_TO);
146 m_YourBaseBoth.SetMovedIndex(toIndex, fromIndex);
151 bool CDiffData::CompareWithIgnoreWS(CString s1, CString s2, DWORD dwIgnoreWS)
153 if (dwIgnoreWS == 2)
155 s1 = s1.TrimLeft(_T(" \t"));
156 s2 = s2.TrimLeft(_T(" \t"));
158 else
160 s1 = s1.TrimRight(_T(" \t"));
161 s2 = s2.TrimRight(_T(" \t"));
164 return s1 != s2;
167 void CDiffData::StickAndSkip(svn_diff_t * &tempdiff, apr_off_t &original_length_sticked, apr_off_t &modified_length_sticked)
169 if((m_bViewMovedBlocks)&&(tempdiff->type == svn_diff__type_diff_modified))
171 svn_diff_t * nextdiff = tempdiff->next;
172 while((nextdiff)&&(nextdiff->type == svn_diff__type_diff_modified))
174 original_length_sticked += nextdiff->original_length;
175 modified_length_sticked += nextdiff->modified_length;
176 tempdiff = nextdiff;
177 nextdiff = tempdiff->next;
182 BOOL CDiffData::Load()
184 m_arBaseFile.RemoveAll();
185 m_arYourFile.RemoveAll();
186 m_arTheirFile.RemoveAll();
188 m_YourBaseBoth.Clear();
189 m_YourBaseLeft.Clear();
190 m_YourBaseRight.Clear();
192 m_TheirBaseBoth.Clear();
193 m_TheirBaseLeft.Clear();
194 m_TheirBaseRight.Clear();
196 m_Diff3.Clear();
198 CRegDWORD regIgnoreWS = CRegDWORD(_T("Software\\TortoiseGitMerge\\IgnoreWS"));
199 CRegDWORD regIgnoreEOL = CRegDWORD(_T("Software\\TortoiseGitMerge\\IgnoreEOL"), TRUE);
200 CRegDWORD regIgnoreCase = CRegDWORD(_T("Software\\TortoiseGitMerge\\CaseInsensitive"), FALSE);
201 DWORD dwIgnoreWS = regIgnoreWS;
202 bool bIgnoreEOL = ((DWORD)regIgnoreEOL)!=0;
203 BOOL bIgnoreCase = ((DWORD)regIgnoreCase)!=0;
205 // The Subversion diff API only can ignore whitespaces and eol styles.
206 // It also can only handle one-byte charsets.
207 // To ignore case changes or to handle UTF-16 files, we have to
208 // save the original file in UTF-8 and/or the letters changed to lowercase
209 // so the Subversion diff can handle those.
210 CString sConvertedBaseFilename = m_baseFile.GetFilename();
211 CString sConvertedYourFilename = m_yourFile.GetFilename();
212 CString sConvertedTheirFilename = m_theirFile.GetFilename();
214 m_baseFile.StoreFileAttributes();
215 m_theirFile.StoreFileAttributes();
216 m_yourFile.StoreFileAttributes();
217 //m_mergedFile.StoreFileAttributes();
219 bool bBaseNeedConvert = false;
220 bool bTheirNeedConvert = false;
221 bool bYourNeedConvert = false;
222 bool bBaseIsUtf8 = false;
223 bool bTheirIsUtf8 = false;
224 bool bYourIsUtf8 = false;
226 // in case at least one of the files got converted or is UTF8
227 // we have to convert all non UTF8 (ASCII) files
228 // otherwise one file might be in ANSI and the other in UTF8 and we'll end up
229 // with lines marked as different throughout the files even though the lines
230 // would show no change at all in the viewer.
231 bool bIsNotUtf8 = false; // Any non UTF8 file ?
233 if (IsBaseFileInUse())
235 if (!m_arBaseFile.Load(m_baseFile.GetFilename()))
237 m_sError = m_arBaseFile.GetErrorString();
238 return FALSE;
240 bBaseNeedConvert = bIgnoreCase || (m_arBaseFile.NeedsConversion());
241 bBaseIsUtf8 = (m_arBaseFile.GetUnicodeType()!=CFileTextLines::ASCII) || bBaseNeedConvert;
242 bIsNotUtf8 |= !bBaseIsUtf8;
245 if (IsTheirFileInUse())
247 // m_arBaseFile.GetCount() is passed as a hint for the number of lines in this file
248 // It's a fair guess that the files will be roughly the same size
249 if (!m_arTheirFile.Load(m_theirFile.GetFilename(), m_arBaseFile.GetCount()))
251 m_sError = m_arTheirFile.GetErrorString();
252 return FALSE;
254 bTheirNeedConvert = bIgnoreCase || (m_arTheirFile.NeedsConversion());
255 bTheirIsUtf8 = (m_arTheirFile.GetUnicodeType()!=CFileTextLines::ASCII) || bTheirNeedConvert;
256 bIsNotUtf8 |= !bTheirIsUtf8;
259 if (IsYourFileInUse())
261 // m_arBaseFile.GetCount() is passed as a hint for the number of lines in this file
262 // It's a fair guess that the files will be roughly the same size
263 if (!m_arYourFile.Load(m_yourFile.GetFilename(), m_arBaseFile.GetCount()))
265 m_sError = m_arYourFile.GetErrorString();
266 return FALSE;
268 bYourNeedConvert = bIgnoreCase || (m_arYourFile.NeedsConversion());
269 bYourIsUtf8 = (m_arYourFile.GetUnicodeType()!=CFileTextLines::ASCII) || bYourNeedConvert;
270 bIsNotUtf8 |= !bYourIsUtf8;
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);
282 bYourNeedConvert |= (IsYourFileInUse() && !bYourIsUtf8 && bIsUtf8);
283 if (bYourNeedConvert)
285 sConvertedYourFilename = CTempFiles::Instance().GetTempFilePathString();
286 m_yourFile.SetConvertedFileName(sConvertedYourFilename);
287 m_arYourFile.Save(sConvertedYourFilename, true, true, 0, bIgnoreCase, m_bBlame);
289 bTheirNeedConvert |= (IsTheirFileInUse() && !bTheirIsUtf8 && bIsUtf8);
290 if (bTheirNeedConvert)
292 sConvertedTheirFilename = CTempFiles::Instance().GetTempFilePathString();
293 m_theirFile.SetConvertedFileName(sConvertedTheirFilename);
294 m_arTheirFile.Save(sConvertedTheirFilename, true, true, 0, bIgnoreCase, m_bBlame);
297 // Calculate the number of lines in the largest of the three files
298 int lengthHint = GetLineCount();
302 m_YourBaseBoth.Reserve(lengthHint);
303 m_YourBaseLeft.Reserve(lengthHint);
304 m_YourBaseRight.Reserve(lengthHint);
306 m_TheirBaseBoth.Reserve(lengthHint);
307 m_TheirBaseLeft.Reserve(lengthHint);
308 m_TheirBaseRight.Reserve(lengthHint);
310 catch (CMemoryException* e)
312 e->GetErrorMessage(m_sError.GetBuffer(255), 255);
313 m_sError.ReleaseBuffer();
314 e->Delete();
315 return FALSE;
318 apr_pool_t * pool = NULL;
319 apr_pool_create_ex (&pool, NULL, abort_on_pool_failure, NULL);
321 // Is this a two-way diff?
322 if (IsBaseFileInUse() && IsYourFileInUse() && !IsTheirFileInUse())
324 if (!DoTwoWayDiff(sConvertedBaseFilename, sConvertedYourFilename, dwIgnoreWS, bIgnoreEOL, pool))
326 apr_pool_destroy (pool); // free the allocated memory
327 return FALSE;
331 if (IsBaseFileInUse() && IsTheirFileInUse() && !IsYourFileInUse())
333 ASSERT(FALSE);
336 // Is this a three-way diff?
337 if (IsBaseFileInUse() && IsTheirFileInUse() && IsYourFileInUse())
339 m_Diff3.Reserve(lengthHint);
341 if (!DoThreeWayDiff(sConvertedBaseFilename, sConvertedYourFilename, sConvertedTheirFilename, dwIgnoreWS, bIgnoreEOL, !!bIgnoreCase, pool))
343 apr_pool_destroy (pool); // free the allocated memory
344 return FALSE;
348 // free the allocated memory
349 apr_pool_destroy (pool);
351 m_arBaseFile.RemoveAll();
352 m_arYourFile.RemoveAll();
353 m_arTheirFile.RemoveAll();
355 return TRUE;
358 bool
359 CDiffData::DoTwoWayDiff(const CString& sBaseFilename, const CString& sYourFilename, DWORD dwIgnoreWS, bool bIgnoreEOL, apr_pool_t * pool)
361 svn_diff_file_options_t * options = CreateDiffFileOptions(dwIgnoreWS, bIgnoreEOL, pool);
362 // convert CString filenames (UTF-16 or ANSI) to UTF-8
363 CStringA sBaseFilenameUtf8 = CUnicodeUtils::GetUTF8(sBaseFilename);
364 CStringA sYourFilenameUtf8 = CUnicodeUtils::GetUTF8(sYourFilename);
366 svn_diff_t * diffYourBase = NULL;
367 svn_error_t * svnerr = svn_diff_file_diff_2(&diffYourBase, sBaseFilenameUtf8, sYourFilenameUtf8, options, pool);
369 if (svnerr)
370 return HandleSvnError(svnerr);
372 tsvn_svn_diff_t_extension * movedBlocks = NULL;
373 if(m_bViewMovedBlocks)
374 movedBlocks = MovedBlocksDetect(diffYourBase, dwIgnoreWS, pool); // Side effect is that diffs are now splitted
376 svn_diff_t * tempdiff = diffYourBase;
377 LONG baseline = 0;
378 LONG yourline = 0;
379 while (tempdiff)
381 svn_diff__type_e diffType = tempdiff->type;
382 // Side effect described above overcoming - sticking together
383 apr_off_t original_length_sticked = tempdiff->original_length;
384 apr_off_t modified_length_sticked = tempdiff->modified_length;
385 StickAndSkip(tempdiff, original_length_sticked, modified_length_sticked);
387 for (int i=0; i<original_length_sticked; i++)
389 if (baseline >= m_arBaseFile.GetCount())
391 m_sError.LoadString(IDS_ERR_DIFF_NEWLINES);
392 return false;
394 const CString& sCurrentBaseLine = m_arBaseFile.GetAt(baseline);
395 EOL endingBase = m_arBaseFile.GetLineEnding(baseline);
396 if (diffType == svn_diff__type_common)
398 if (yourline >= m_arYourFile.GetCount())
400 m_sError.LoadString(IDS_ERR_DIFF_NEWLINES);
401 return false;
403 const CString& sCurrentYourLine = m_arYourFile.GetAt(yourline);
404 EOL endingYours = m_arYourFile.GetLineEnding(yourline);
405 if (sCurrentBaseLine != sCurrentYourLine)
407 bool changedWS = false;
408 if (dwIgnoreWS == 2 || dwIgnoreWS == 3)
409 changedWS = CompareWithIgnoreWS(sCurrentBaseLine, sCurrentYourLine, dwIgnoreWS);
410 if (changedWS || dwIgnoreWS == 0)
412 // one-pane view: two lines, one 'removed' and one 'added'
413 m_YourBaseBoth.AddData(sCurrentBaseLine, DIFFSTATE_REMOVEDWHITESPACE, yourline, endingBase, HIDESTATE_SHOWN, -1);
414 m_YourBaseBoth.AddData(sCurrentYourLine, DIFFSTATE_ADDEDWHITESPACE, yourline, endingYours, HIDESTATE_SHOWN, -1);
416 else
418 m_YourBaseBoth.AddData(sCurrentYourLine, DIFFSTATE_NORMAL, yourline, endingBase, HIDESTATE_HIDDEN, -1);
421 else
423 m_YourBaseBoth.AddData(sCurrentYourLine, DIFFSTATE_NORMAL, yourline, endingBase, HIDESTATE_HIDDEN, -1);
425 yourline++; //in both files
427 else
428 { // small trick - we need here a baseline, but we fix it back to yourline at the end of routine
429 m_YourBaseBoth.AddData(sCurrentBaseLine, DIFFSTATE_REMOVED, baseline, endingBase, HIDESTATE_SHOWN, -1);
431 baseline++;
433 if (diffType == svn_diff__type_diff_modified)
435 for (int i=0; i<modified_length_sticked; i++)
437 if (m_arYourFile.GetCount() > yourline)
439 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_ADDED, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
441 yourline++;
444 tempdiff = tempdiff->next;
447 HideUnchangedSections(&m_YourBaseBoth, NULL, NULL);
449 tempdiff = diffYourBase;
450 baseline = 0;
451 yourline = 0;
452 while (tempdiff)
454 if (tempdiff->type == svn_diff__type_common)
456 for (int i=0; i<tempdiff->original_length; i++)
458 const CString& sCurrentYourLine = m_arYourFile.GetAt(yourline);
459 EOL endingYours = m_arYourFile.GetLineEnding(yourline);
460 const CString& sCurrentBaseLine = m_arBaseFile.GetAt(baseline);
461 EOL endingBase = m_arBaseFile.GetLineEnding(baseline);
462 if (sCurrentBaseLine != sCurrentYourLine)
464 bool changedWS = false;
465 if (dwIgnoreWS == 2 || dwIgnoreWS == 3)
466 changedWS = CompareWithIgnoreWS(sCurrentBaseLine, sCurrentYourLine, dwIgnoreWS);
467 if (changedWS || dwIgnoreWS == 0)
469 m_YourBaseLeft.AddData(sCurrentBaseLine, DIFFSTATE_WHITESPACE, baseline, endingBase, HIDESTATE_SHOWN, -1);
470 m_YourBaseRight.AddData(sCurrentYourLine, DIFFSTATE_WHITESPACE, yourline, endingYours, HIDESTATE_SHOWN, -1);
472 else
474 m_YourBaseLeft.AddData(sCurrentBaseLine, DIFFSTATE_NORMAL, baseline, endingBase, HIDESTATE_HIDDEN, -1);
475 m_YourBaseRight.AddData(sCurrentYourLine, DIFFSTATE_NORMAL, yourline, endingYours, HIDESTATE_HIDDEN, -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);
483 baseline++;
484 yourline++;
487 if (tempdiff->type == svn_diff__type_diff_modified)
489 // now we trying to stick together parts, that were splitted by MovedBlocks
490 apr_off_t original_length_sticked = tempdiff->original_length;
491 apr_off_t modified_length_sticked = tempdiff->modified_length;
492 StickAndSkip(tempdiff, original_length_sticked, modified_length_sticked);
494 apr_off_t original_length = original_length_sticked;
495 for (int i=0; i<modified_length_sticked; i++)
497 if (m_arYourFile.GetCount() > yourline)
499 EOL endingYours = m_arYourFile.GetLineEnding(yourline);
500 m_YourBaseRight.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_ADDED, yourline, endingYours, HIDESTATE_SHOWN, -1);
501 if (original_length-- <= 0)
503 m_YourBaseLeft.AddEmpty();
505 else
507 EOL endingBase = m_arBaseFile.GetLineEnding(baseline);
508 m_YourBaseLeft.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_REMOVED, baseline, endingBase, HIDESTATE_SHOWN, -1);
509 baseline++;
511 yourline++;
514 apr_off_t modified_length = modified_length_sticked;
515 for (int i=0; i<original_length_sticked; i++)
517 if ((modified_length-- <= 0)&&(m_arBaseFile.GetCount() > baseline))
519 EOL endingBase = m_arBaseFile.GetLineEnding(baseline);
520 m_YourBaseLeft.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_REMOVED, baseline, endingBase, HIDESTATE_SHOWN, -1);
521 m_YourBaseRight.AddEmpty();
522 baseline++;
526 tempdiff = tempdiff->next;
528 // add last (empty) lines if needed - diff don't report those
529 if (m_arBaseFile.GetCount() > baseline)
531 if (m_arYourFile.GetCount() > yourline)
533 // last line is missing in both files add them to end and mark as no diff
534 m_YourBaseLeft.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_NORMAL, baseline, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN, -1);
535 m_YourBaseRight.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_NORMAL, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
536 yourline++;
537 baseline++;
539 else
541 viewdata oViewData(m_arBaseFile.GetAt(baseline), DIFFSTATE_REMOVED, baseline, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN, -1);
542 baseline++;
544 // find first EMPTY line in last blok
545 int nPos = m_YourBaseLeft.GetCount();
546 while (--nPos>=0 && m_YourBaseLeft.GetState(nPos)==DIFFSTATE_EMPTY) ;
547 if (++nPos<m_YourBaseLeft.GetCount())
549 m_YourBaseLeft.SetData(nPos, oViewData);
551 else
553 m_YourBaseLeft.AddData(oViewData);
554 m_YourBaseRight.AddEmpty();
558 else if (m_arYourFile.GetCount() > yourline)
560 viewdata oViewData(m_arYourFile.GetAt(yourline), DIFFSTATE_ADDED, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
561 yourline++;
563 // try to move last line higher
564 int nPos = m_YourBaseRight.GetCount();
565 while (--nPos>=0 && m_YourBaseRight.GetState(nPos)==DIFFSTATE_EMPTY) ;
566 if (++nPos<m_YourBaseRight.GetCount())
568 m_YourBaseRight.SetData(nPos, oViewData);
570 else
572 m_YourBaseLeft.AddEmpty();
573 m_YourBaseRight.AddData(oViewData);
578 // Fixing results for conforming moved blocks
580 while(movedBlocks)
582 tempdiff = movedBlocks->base;
583 if(movedBlocks->moved_to != -1)
585 // set states in a block original:length -> moved_to:length
586 TieMovedBlocks((int)tempdiff->original_start, movedBlocks->moved_to, tempdiff->original_length);
588 if(movedBlocks->moved_from != -1)
590 // set states in a block modified:length -> moved_from:length
591 TieMovedBlocks(movedBlocks->moved_from, (int)tempdiff->modified_start, tempdiff->modified_length);
593 movedBlocks = movedBlocks->next;
596 // replace baseline with the yourline in m_YourBaseBoth
597 yourline = 0;
598 for(int i=0; i<m_YourBaseBoth.GetCount(); i++)
600 DiffStates state = m_YourBaseBoth.GetState(i);
601 if((state == DIFFSTATE_REMOVED)||(state == DIFFSTATE_MOVED_FROM))
603 m_YourBaseBoth.SetLineNumber(i, yourline);
605 else
607 yourline++;
611 TRACE(_T("done with 2-way diff\n"));
613 HideUnchangedSections(&m_YourBaseLeft, &m_YourBaseRight, NULL);
615 return true;
618 bool
619 CDiffData::DoThreeWayDiff(const CString& sBaseFilename, const CString& sYourFilename, const CString& sTheirFilename, DWORD dwIgnoreWS, bool bIgnoreEOL, bool bIgnoreCase, apr_pool_t * pool)
621 // the following three arrays are used to check for conflicts even in case the
622 // user has ignored spaces/eols.
623 CStdDWORDArray m_arDiff3LinesBase;
624 CStdDWORDArray m_arDiff3LinesYour;
625 CStdDWORDArray m_arDiff3LinesTheir;
626 #define AddLines(baseline, yourline, theirline) m_arDiff3LinesBase.Add(baseline), m_arDiff3LinesYour.Add(yourline), m_arDiff3LinesTheir.Add(theirline)
627 int lengthHint = GetLineCount();
629 m_arDiff3LinesBase.Reserve(lengthHint);
630 m_arDiff3LinesYour.Reserve(lengthHint);
631 m_arDiff3LinesTheir.Reserve(lengthHint);
633 CRegDWORD contextLines = CRegDWORD(_T("Software\\TortoiseGitMerge\\ContextLines"), 3);
634 svn_diff_file_options_t * options = CreateDiffFileOptions(dwIgnoreWS, bIgnoreEOL, pool);
636 // convert CString filenames (UTF-16 or ANSI) to UTF-8
637 CStringA sBaseFilenameUtf8 = CUnicodeUtils::GetUTF8(sBaseFilename);
638 CStringA sYourFilenameUtf8 = CUnicodeUtils::GetUTF8(sYourFilename);
639 CStringA sTheirFilenameUtf8 = CUnicodeUtils::GetUTF8(sTheirFilename);
641 svn_diff_t * diffTheirYourBase = NULL;
642 svn_error_t * svnerr = svn_diff_file_diff3_2(&diffTheirYourBase, sBaseFilenameUtf8, sTheirFilenameUtf8, sYourFilenameUtf8, options, pool);
643 if (svnerr)
644 return HandleSvnError(svnerr);
646 svn_diff_t * tempdiff = diffTheirYourBase;
647 LONG baseline = 0;
648 LONG yourline = 0;
649 LONG theirline = 0;
650 LONG resline = 0;
651 // common viewdata
652 const viewdata emptyConflictEmpty(_T(""), DIFFSTATE_CONFLICTEMPTY, DIFF_EMPTYLINENUMBER, EOL_NOENDING, HIDESTATE_SHOWN, -1);
653 const viewdata emptyIdenticalRemoved(_T(""), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, EOL_NOENDING, HIDESTATE_SHOWN, -1);
654 while (tempdiff)
656 if (tempdiff->type == svn_diff__type_common)
658 ASSERT((tempdiff->latest_length == tempdiff->modified_length) && (tempdiff->modified_length == tempdiff->original_length));
659 for (int i=0; i<tempdiff->original_length; i++)
661 if ((m_arYourFile.GetCount() > yourline)&&(m_arTheirFile.GetCount() > theirline))
663 AddLines(baseline, yourline, theirline);
665 m_Diff3.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_NORMAL, resline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_HIDDEN, -1);
666 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_NORMAL, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_HIDDEN, -1);
667 m_TheirBaseBoth.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_NORMAL, theirline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_HIDDEN, -1);
669 baseline++;
670 yourline++;
671 theirline++;
672 resline++;
676 else if (tempdiff->type == svn_diff__type_diff_common)
678 ASSERT(tempdiff->latest_length == tempdiff->modified_length);
679 //both theirs and yours have lines replaced
680 for (int i=0; i<tempdiff->original_length; i++)
682 if (m_arBaseFile.GetCount() > baseline)
684 AddLines(baseline, yourline, theirline);
686 m_Diff3.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN, -1);
687 m_YourBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, EOL_NOENDING, HIDESTATE_SHOWN, -1);
688 m_TheirBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, EOL_NOENDING, HIDESTATE_SHOWN, -1);
690 baseline++;
693 for (int i=0; i<tempdiff->modified_length; i++)
695 if ((m_arYourFile.GetCount() > yourline)&&(m_arTheirFile.GetCount() > theirline))
697 AddLines(baseline, yourline, theirline);
699 m_Diff3.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_IDENTICALADDED, resline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
700 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_IDENTICALADDED, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
701 m_TheirBaseBoth.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_IDENTICALADDED, resline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_SHOWN, -1);
703 yourline++;
704 theirline++;
705 resline++;
709 else if (tempdiff->type == svn_diff__type_conflict)
711 apr_off_t length = max(tempdiff->original_length, tempdiff->modified_length);
712 length = max(tempdiff->latest_length, length);
713 apr_off_t original = tempdiff->original_length;
714 apr_off_t modified = tempdiff->modified_length;
715 apr_off_t latest = tempdiff->latest_length;
717 apr_off_t originalresolved = 0;
718 apr_off_t modifiedresolved = 0;
719 apr_off_t latestresolved = 0;
721 LONG base = baseline;
722 LONG your = yourline;
723 LONG their = theirline;
724 if (tempdiff->resolved_diff)
726 originalresolved = tempdiff->resolved_diff->original_length;
727 modifiedresolved = tempdiff->resolved_diff->modified_length;
728 latestresolved = tempdiff->resolved_diff->latest_length;
730 for (int i=0; i<length; i++)
732 EOL endingBase = m_arBaseFile.GetCount() > baseline ? m_arBaseFile.GetLineEnding(baseline) : EOL_AUTOLINE;
733 if (original)
735 if (m_arBaseFile.GetCount() > baseline)
737 AddLines(baseline, yourline, theirline);
739 m_Diff3.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, endingBase , HIDESTATE_SHOWN, -1);
740 m_YourBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, endingBase , HIDESTATE_SHOWN, -1);
741 m_TheirBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_IDENTICALREMOVED, DIFF_EMPTYLINENUMBER, endingBase , HIDESTATE_SHOWN, -1);
744 else if ((originalresolved)||((modifiedresolved)&&(latestresolved)))
746 AddLines(baseline, yourline, theirline);
748 m_Diff3.AddData(emptyIdenticalRemoved);
749 if ((latestresolved)&&(modifiedresolved))
751 m_YourBaseBoth.AddData(emptyIdenticalRemoved);
752 m_TheirBaseBoth.AddData(emptyIdenticalRemoved);
755 if (original)
757 original--;
758 baseline++;
760 if (originalresolved)
761 originalresolved--;
763 if (modified)
765 modified--;
766 theirline++;
768 if (modifiedresolved)
769 modifiedresolved--;
770 if (latest)
772 latest--;
773 yourline++;
775 if (latestresolved)
776 latestresolved--;
778 original = tempdiff->original_length;
779 modified = tempdiff->modified_length;
780 latest = tempdiff->latest_length;
781 baseline = base;
782 yourline = your;
783 theirline = their;
784 if (tempdiff->resolved_diff)
786 originalresolved = tempdiff->resolved_diff->original_length;
787 modifiedresolved = tempdiff->resolved_diff->modified_length;
788 latestresolved = tempdiff->resolved_diff->latest_length;
790 for (int i=0; i<length; i++)
792 if ((latest)||(modified))
794 AddLines(baseline, yourline, theirline);
796 m_Diff3.AddData(_T(""), DIFFSTATE_CONFLICTED, resline, EOL_NOENDING, HIDESTATE_SHOWN, -1);
798 resline++;
801 if (latest)
803 if (m_arYourFile.GetCount() > yourline)
805 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_CONFLICTADDED, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
808 else if ((latestresolved)||(modified)||(modifiedresolved))
810 m_YourBaseBoth.AddData(emptyConflictEmpty);
812 if (modified)
814 if (m_arTheirFile.GetCount() > theirline)
816 m_TheirBaseBoth.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_CONFLICTADDED, theirline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_SHOWN, -1);
819 else if ((modifiedresolved)||(latest)||(latestresolved))
821 m_TheirBaseBoth.AddData(emptyConflictEmpty);
823 if (original)
825 original--;
826 baseline++;
828 if (originalresolved)
829 originalresolved--;
830 if (modified)
832 modified--;
833 theirline++;
835 if (modifiedresolved)
836 modifiedresolved--;
837 if (latest)
839 latest--;
840 yourline++;
842 if (latestresolved)
843 latestresolved--;
846 else if (tempdiff->type == svn_diff__type_diff_modified)
848 //deleted!
849 for (int i=0; i<tempdiff->original_length; i++)
851 if ((m_arBaseFile.GetCount() > baseline)&&(m_arYourFile.GetCount() > yourline))
853 AddLines(baseline, yourline, theirline);
855 m_Diff3.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_THEIRSREMOVED, DIFF_EMPTYLINENUMBER, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN, -1);
856 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_NORMAL, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
857 m_TheirBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_THEIRSREMOVED, DIFF_EMPTYLINENUMBER, EOL_NOENDING, HIDESTATE_SHOWN, -1);
859 baseline++;
860 yourline++;
863 //added
864 for (int i=0; i<tempdiff->modified_length; i++)
866 if (m_arTheirFile.GetCount() > theirline)
868 AddLines(baseline, yourline, theirline);
870 m_Diff3.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_THEIRSADDED, resline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_SHOWN, -1);
871 m_YourBaseBoth.AddEmpty();
872 m_TheirBaseBoth.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_THEIRSADDED, theirline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_SHOWN, -1);
874 theirline++;
875 resline++;
879 else if (tempdiff->type == svn_diff__type_diff_latest)
881 //YOURS differs from base
883 for (int i=0; i<tempdiff->original_length; i++)
885 if ((m_arBaseFile.GetCount() > baseline)&&(m_arTheirFile.GetCount() > theirline))
887 AddLines(baseline, yourline, theirline);
889 m_Diff3.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_YOURSREMOVED, DIFF_EMPTYLINENUMBER, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN, -1);
890 m_YourBaseBoth.AddData(m_arBaseFile.GetAt(baseline), DIFFSTATE_YOURSREMOVED, DIFF_EMPTYLINENUMBER, m_arBaseFile.GetLineEnding(baseline), HIDESTATE_SHOWN, -1);
891 m_TheirBaseBoth.AddData(m_arTheirFile.GetAt(theirline), DIFFSTATE_NORMAL, theirline, m_arTheirFile.GetLineEnding(theirline), HIDESTATE_HIDDEN, -1);
893 baseline++;
894 theirline++;
897 for (int i=0; i<tempdiff->latest_length; i++)
899 if (m_arYourFile.GetCount() > yourline)
901 AddLines(baseline, yourline, theirline);
903 m_Diff3.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_YOURSADDED, resline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
904 m_YourBaseBoth.AddData(m_arYourFile.GetAt(yourline), DIFFSTATE_IDENTICALADDED, yourline, m_arYourFile.GetLineEnding(yourline), HIDESTATE_SHOWN, -1);
905 m_TheirBaseBoth.AddEmpty();
907 yourline++;
908 resline++;
912 else
914 TRACE(_T("something bad happened during diff!\n"));
916 tempdiff = tempdiff->next;
918 } // while (tempdiff)
920 if ((options->ignore_space != svn_diff_file_ignore_space_none)||(bIgnoreCase)||(bIgnoreEOL))
922 // If whitespaces are ignored, a conflict could have been missed
923 // We now go through all lines again and check if they're identical.
924 // If they're not, then that means it is a conflict, and we
925 // mark the conflict with the proper colors.
926 for (long i=0; i<m_Diff3.GetCount(); ++i)
928 DiffStates state1 = m_YourBaseBoth.GetState(i);
929 DiffStates state2 = m_TheirBaseBoth.GetState(i);
931 if (((state1 == DIFFSTATE_IDENTICALADDED)||(state1 == DIFFSTATE_NORMAL))&&
932 ((state2 == DIFFSTATE_IDENTICALADDED)||(state2 == DIFFSTATE_NORMAL)))
934 LONG lineyour = m_arDiff3LinesYour.GetAt(i);
935 LONG linetheir = m_arDiff3LinesTheir.GetAt(i);
936 LONG linebase = m_arDiff3LinesBase.GetAt(i);
937 if ((lineyour < m_arYourFile.GetCount()) &&
938 (linetheir < m_arTheirFile.GetCount()) &&
939 (linebase < m_arBaseFile.GetCount()))
941 if (((m_arYourFile.GetLineEnding(lineyour)!=m_arBaseFile.GetLineEnding(linebase))&&
942 (m_arTheirFile.GetLineEnding(linetheir)!=m_arBaseFile.GetLineEnding(linebase))&&
943 (m_arYourFile.GetLineEnding(lineyour)!=m_arTheirFile.GetLineEnding(linetheir))) ||
944 ((m_arYourFile.GetAt(lineyour).Compare(m_arBaseFile.GetAt(linebase))!=0)&&
945 (m_arTheirFile.GetAt(linetheir).Compare(m_arBaseFile.GetAt(linebase))!=0)&&
946 (m_arYourFile.GetAt(lineyour).Compare(m_arTheirFile.GetAt(linetheir))!=0)))
948 m_Diff3.SetState(i, DIFFSTATE_CONFLICTED_IGNORED);
949 m_YourBaseBoth.SetState(i, DIFFSTATE_CONFLICTADDED);
950 m_TheirBaseBoth.SetState(i, DIFFSTATE_CONFLICTADDED);
956 ASSERT(m_Diff3.GetCount() == m_YourBaseBoth.GetCount());
957 ASSERT(m_TheirBaseBoth.GetCount() == m_YourBaseBoth.GetCount());
959 TRACE(_T("done with 3-way diff\n"));
961 HideUnchangedSections(&m_Diff3, &m_YourBaseBoth, &m_TheirBaseBoth);
963 return true;
966 void CDiffData::HideUnchangedSections(CViewData * data1, CViewData * data2, CViewData * data3)
968 if (data1 == NULL)
969 return;
971 CRegDWORD contextLines = CRegDWORD(_T("Software\\TortoiseGitMerge\\ContextLines"), 1);
973 if (data1->GetCount() > 1)
975 HIDESTATE lastHideState = data1->GetHideState(0);
976 if (lastHideState == HIDESTATE_HIDDEN)
978 data1->SetLineHideState(0, HIDESTATE_MARKER);
979 if (data2)
980 data2->SetLineHideState(0, HIDESTATE_MARKER);
981 if (data3)
982 data3->SetLineHideState(0, HIDESTATE_MARKER);
984 for (int i = 1; i < data1->GetCount(); ++i)
986 HIDESTATE hideState = data1->GetHideState(i);
987 if (hideState != lastHideState)
989 if (hideState == HIDESTATE_SHOWN)
991 // go back and show the last 'contextLines' lines to "SHOWN"
992 int lineback = i - 1;
993 int stopline = lineback - (int)(DWORD)contextLines;
994 while ((lineback >= 0)&&(lineback > stopline))
996 data1->SetLineHideState(lineback, HIDESTATE_SHOWN);
997 if (data2)
998 data2->SetLineHideState(lineback, HIDESTATE_SHOWN);
999 if (data3)
1000 data3->SetLineHideState(lineback, HIDESTATE_SHOWN);
1001 lineback--;
1004 else if ((hideState == HIDESTATE_HIDDEN)&&(lastHideState != HIDESTATE_MARKER))
1006 // go forward and show the next 'contextLines' lines to "SHOWN"
1007 int lineforward = i + 1;
1008 int stopline = lineforward + (int)(DWORD)contextLines;
1009 while ((lineforward < data1->GetCount())&&(lineforward < stopline))
1011 data1->SetLineHideState(lineforward, HIDESTATE_SHOWN);
1012 if (data2)
1013 data2->SetLineHideState(lineforward, HIDESTATE_SHOWN);
1014 if (data3)
1015 data3->SetLineHideState(lineforward, HIDESTATE_SHOWN);
1016 lineforward++;
1018 if ((lineforward < data1->GetCount())&&(data1->GetHideState(lineforward) == HIDESTATE_HIDDEN))
1020 data1->SetLineHideState(lineforward, HIDESTATE_MARKER);
1021 if (data2)
1022 data2->SetLineHideState(lineforward, HIDESTATE_MARKER);
1023 if (data3)
1024 data3->SetLineHideState(lineforward, HIDESTATE_MARKER);
1028 lastHideState = hideState;