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
);
118 int toIndex
= m_YourBaseRight
.FindLineNumber(to
);
121 m_YourBaseLeft
.SetMovedIndex(fromIndex
, toIndex
, true);
122 m_YourBaseRight
.SetMovedIndex(toIndex
, fromIndex
, false);
124 toIndex
= m_YourBaseBoth
.FindLineNumber(to
);
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
)))
135 fromIndex
= m_YourBaseBoth
.FindLineNumber(from
);
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
)))
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
157 s1
= s1
.TrimLeft(_T(" \t"));
158 s2
= s2
.TrimLeft(_T(" \t"));
162 s1
= s1
.TrimRight(_T(" \t"));
163 s2
= s2
.TrimRight(_T(" \t"));
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
;
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();
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();
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();
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();
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();
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
337 if (IsBaseFileInUse() && IsTheirFileInUse() && !IsYourFileInUse())
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
354 // free the allocated memory
355 apr_pool_destroy (pool
);
357 m_arBaseFile
.RemoveAll();
358 m_arYourFile
.RemoveAll();
359 m_arTheirFile
.RemoveAll();
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
);
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
;
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
);
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
);
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);
424 m_YourBaseBoth
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingBase
, HIDESTATE_HIDDEN
, -1);
429 m_YourBaseBoth
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingBase
, HIDESTATE_HIDDEN
, -1);
431 yourline
++; //in both files
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);
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);
450 tempdiff
= tempdiff
->next
;
453 HideUnchangedSections(&m_YourBaseBoth
, NULL
, NULL
);
455 tempdiff
= diffYourBase
;
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);
480 m_YourBaseLeft
.AddData(sCurrentBaseLine
, DIFFSTATE_NORMAL
, baseline
, endingBase
, HIDESTATE_HIDDEN
, -1);
481 m_YourBaseRight
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingYours
, HIDESTATE_HIDDEN
, -1);
486 m_YourBaseLeft
.AddData(sCurrentBaseLine
, DIFFSTATE_NORMAL
, baseline
, endingBase
, HIDESTATE_HIDDEN
, -1);
487 m_YourBaseRight
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingYours
, HIDESTATE_HIDDEN
, -1);
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();
513 EOL endingBase
= m_arBaseFile
.GetLineEnding(baseline
);
514 m_YourBaseLeft
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_REMOVED
, baseline
, endingBase
, HIDESTATE_SHOWN
, -1);
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();
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);
547 viewdata
oViewData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_REMOVED
, baseline
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
);
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
);
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
);
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
);
578 m_YourBaseLeft
.AddEmpty();
579 m_YourBaseRight
.AddData(oViewData
);
584 // Fixing results for conforming moved blocks
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
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);
617 TRACE(_T("done with 2-way diff\n"));
619 HideUnchangedSections(&m_YourBaseLeft
, &m_YourBaseRight
, NULL
);
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
);
650 return HandleSvnError(svnerr
);
652 svn_diff_t
* tempdiff
= diffTheirYourBase
;
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
);
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);
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);
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);
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
;
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
);
766 if (originalresolved
)
774 if (modifiedresolved
)
784 original
= tempdiff
->original_length
;
785 modified
= tempdiff
->modified_length
;
786 latest
= tempdiff
->latest_length
;
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);
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
);
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
);
834 if (originalresolved
)
841 if (modifiedresolved
)
852 else if (tempdiff
->type
== svn_diff__type_diff_modified
)
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);
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);
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);
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();
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
);
972 void CDiffData::HideUnchangedSections(CViewData
* data1
, CViewData
* data2
, CViewData
* data3
) const
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
);
986 data2
->SetLineHideState(0, HIDESTATE_MARKER
);
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
);
1004 data2
->SetLineHideState(lineback
, HIDESTATE_SHOWN
);
1006 data3
->SetLineHideState(lineback
, HIDESTATE_SHOWN
);
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
);
1019 data2
->SetLineHideState(lineforward
, HIDESTATE_SHOWN
);
1021 data3
->SetLineHideState(lineforward
, HIDESTATE_SHOWN
);
1024 if ((lineforward
< data1
->GetCount())&&(data1
->GetHideState(lineforward
) == HIDESTATE_HIDDEN
))
1026 data1
->SetLineHideState(lineforward
, HIDESTATE_MARKER
);
1028 data2
->SetLineHideState(lineforward
, HIDESTATE_MARKER
);
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
)
1049 m_replacement
= replacement
;