1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2006-2014 - 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
);
119 m_YourBaseLeft
.SetMovedIndex(fromIndex
, toIndex
, true);
120 m_YourBaseRight
.SetMovedIndex(toIndex
, fromIndex
, false);
122 toIndex
= m_YourBaseBoth
.FindLineNumber(to
);
125 while((toIndex
< m_YourBaseBoth
.GetCount())&&
126 ((m_YourBaseBoth
.GetState(toIndex
) != DIFFSTATE_ADDED
)&&
127 (!m_YourBaseBoth
.IsMoved(toIndex
))||(m_YourBaseBoth
.IsMovedFrom(toIndex
))||
128 (m_YourBaseBoth
.GetLineNumber(toIndex
) != to
)))
133 fromIndex
= m_YourBaseBoth
.FindLineNumber(from
);
136 while((fromIndex
< m_YourBaseBoth
.GetCount())&&
137 ((m_YourBaseBoth
.GetState(fromIndex
) != DIFFSTATE_REMOVED
)&&
138 (!m_YourBaseBoth
.IsMoved(fromIndex
))||(!m_YourBaseBoth
.IsMovedFrom(fromIndex
))||
139 (m_YourBaseBoth
.GetLineNumber(fromIndex
) != from
)))
143 if ((fromIndex
< m_YourBaseBoth
.GetCount())&&(toIndex
< m_YourBaseBoth
.GetCount()))
145 m_YourBaseBoth
.SetMovedIndex(fromIndex
, toIndex
, true);
146 m_YourBaseBoth
.SetMovedIndex(toIndex
, fromIndex
, false);
151 bool CDiffData::CompareWithIgnoreWS(CString s1
, CString s2
, DWORD dwIgnoreWS
) const
155 s1
= s1
.TrimLeft(_T(" \t"));
156 s2
= s2
.TrimLeft(_T(" \t"));
160 s1
= s1
.TrimRight(_T(" \t"));
161 s2
= s2
.TrimRight(_T(" \t"));
167 void CDiffData::StickAndSkip(svn_diff_t
* &tempdiff
, apr_off_t
&original_length_sticked
, apr_off_t
&modified_length_sticked
) const
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
;
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();
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 CRegDWORD regIgnoreComments
= CRegDWORD(_T("Software\\TortoiseGitMerge\\IgnoreComments"), FALSE
);
202 DWORD dwIgnoreWS
= regIgnoreWS
;
203 bool bIgnoreEOL
= ((DWORD
)regIgnoreEOL
)!=0;
204 BOOL bIgnoreCase
= ((DWORD
)regIgnoreCase
)!=0;
205 bool bIgnoreComments
= ((DWORD
)regIgnoreComments
)!=0;
207 // The Subversion diff API only can ignore whitespaces and eol styles.
208 // It also can only handle one-byte charsets.
209 // To ignore case changes or to handle UTF-16 files, we have to
210 // save the original file in UTF-8 and/or the letters changed to lowercase
211 // so the Subversion diff can handle those.
212 CString sConvertedBaseFilename
= m_baseFile
.GetFilename();
213 CString sConvertedYourFilename
= m_yourFile
.GetFilename();
214 CString sConvertedTheirFilename
= m_theirFile
.GetFilename();
216 m_baseFile
.StoreFileAttributes();
217 m_theirFile
.StoreFileAttributes();
218 m_yourFile
.StoreFileAttributes();
219 //m_mergedFile.StoreFileAttributes();
221 bool bBaseNeedConvert
= false;
222 bool bTheirNeedConvert
= false;
223 bool bYourNeedConvert
= false;
224 bool bBaseIsUtf8
= false;
225 bool bTheirIsUtf8
= false;
226 bool bYourIsUtf8
= false;
228 // in case at least one of the files got converted or is UTF8
229 // we have to convert all non UTF8 (ASCII) files
230 // otherwise one file might be in ANSI and the other in UTF8 and we'll end up
231 // with lines marked as different throughout the files even though the lines
232 // would show no change at all in the viewer.
233 bool bIsNotUtf8
= false; // Any non UTF8 file ?
235 if (IsBaseFileInUse())
237 if (!m_arBaseFile
.Load(m_baseFile
.GetFilename()))
239 m_sError
= m_arBaseFile
.GetErrorString();
242 bBaseNeedConvert
= bIgnoreCase
|| bIgnoreComments
|| (m_arBaseFile
.NeedsConversion()) || !m_rx
._Empty();
243 bBaseIsUtf8
= (m_arBaseFile
.GetUnicodeType()!=CFileTextLines::ASCII
) || bBaseNeedConvert
;
244 bIsNotUtf8
|= !bBaseIsUtf8
;
247 if (IsTheirFileInUse())
249 // m_arBaseFile.GetCount() is passed as a hint for the number of lines in this file
250 // It's a fair guess that the files will be roughly the same size
251 if (!m_arTheirFile
.Load(m_theirFile
.GetFilename(), m_arBaseFile
.GetCount()))
253 m_sError
= m_arTheirFile
.GetErrorString();
256 bTheirNeedConvert
= bIgnoreCase
|| bIgnoreComments
|| (m_arTheirFile
.NeedsConversion()) || !m_rx
._Empty();
257 bTheirIsUtf8
= (m_arTheirFile
.GetUnicodeType()!=CFileTextLines::ASCII
) || bTheirNeedConvert
;
258 bIsNotUtf8
|= !bTheirIsUtf8
;
261 if (IsYourFileInUse())
263 // m_arBaseFile.GetCount() is passed as a hint for the number of lines in this file
264 // It's a fair guess that the files will be roughly the same size
265 if (!m_arYourFile
.Load(m_yourFile
.GetFilename(), m_arBaseFile
.GetCount()))
267 m_sError
= m_arYourFile
.GetErrorString();
270 bYourNeedConvert
= bIgnoreCase
|| bIgnoreComments
|| (m_arYourFile
.NeedsConversion()) || !m_rx
._Empty();
271 bYourIsUtf8
= (m_arYourFile
.GetUnicodeType()!=CFileTextLines::ASCII
) || bYourNeedConvert
;
272 bIsNotUtf8
|= !bYourIsUtf8
;
275 // convert all files we need to
276 bool bIsUtf8
= bBaseIsUtf8
|| bTheirIsUtf8
|| bYourIsUtf8
; // any file end as UTF8
277 bBaseNeedConvert
|= (IsBaseFileInUse() && !bBaseIsUtf8
&& bIsUtf8
);
278 if (bBaseNeedConvert
)
280 sConvertedBaseFilename
= CTempFiles::Instance().GetTempFilePathString();
281 m_baseFile
.SetConvertedFileName(sConvertedBaseFilename
);
282 m_arBaseFile
.Save(sConvertedBaseFilename
, true, true, 0, bIgnoreCase
, m_bBlame
283 , bIgnoreComments
, m_CommentLineStart
, m_CommentBlockStart
, m_CommentBlockEnd
284 , m_rx
, m_replacement
);
286 bYourNeedConvert
|= (IsYourFileInUse() && !bYourIsUtf8
&& bIsUtf8
);
287 if (bYourNeedConvert
)
289 sConvertedYourFilename
= CTempFiles::Instance().GetTempFilePathString();
290 m_yourFile
.SetConvertedFileName(sConvertedYourFilename
);
291 m_arYourFile
.Save(sConvertedYourFilename
, true, true, 0, bIgnoreCase
, m_bBlame
292 , bIgnoreComments
, m_CommentLineStart
, m_CommentBlockStart
, m_CommentBlockEnd
293 , m_rx
, m_replacement
);
295 bTheirNeedConvert
|= (IsTheirFileInUse() && !bTheirIsUtf8
&& bIsUtf8
);
296 if (bTheirNeedConvert
)
298 sConvertedTheirFilename
= CTempFiles::Instance().GetTempFilePathString();
299 m_theirFile
.SetConvertedFileName(sConvertedTheirFilename
);
300 m_arTheirFile
.Save(sConvertedTheirFilename
, true, true, 0, bIgnoreCase
, m_bBlame
301 , bIgnoreComments
, m_CommentLineStart
, m_CommentBlockStart
, m_CommentBlockEnd
302 , m_rx
, m_replacement
);
305 // Calculate the number of lines in the largest of the three files
306 int lengthHint
= GetLineCount();
310 m_YourBaseBoth
.Reserve(lengthHint
);
311 m_YourBaseLeft
.Reserve(lengthHint
);
312 m_YourBaseRight
.Reserve(lengthHint
);
314 m_TheirBaseBoth
.Reserve(lengthHint
);
315 m_TheirBaseLeft
.Reserve(lengthHint
);
316 m_TheirBaseRight
.Reserve(lengthHint
);
318 catch (CMemoryException
* e
)
320 e
->GetErrorMessage(m_sError
.GetBuffer(255), 255);
321 m_sError
.ReleaseBuffer();
326 apr_pool_t
* pool
= NULL
;
327 apr_pool_create_ex (&pool
, NULL
, abort_on_pool_failure
, NULL
);
329 // Is this a two-way diff?
330 if (IsBaseFileInUse() && IsYourFileInUse() && !IsTheirFileInUse())
332 if (!DoTwoWayDiff(sConvertedBaseFilename
, sConvertedYourFilename
, dwIgnoreWS
, bIgnoreEOL
, pool
))
334 apr_pool_destroy (pool
); // free the allocated memory
339 if (IsBaseFileInUse() && IsTheirFileInUse() && !IsYourFileInUse())
344 // Is this a three-way diff?
345 if (IsBaseFileInUse() && IsTheirFileInUse() && IsYourFileInUse())
347 m_Diff3
.Reserve(lengthHint
);
349 if (!DoThreeWayDiff(sConvertedBaseFilename
, sConvertedYourFilename
, sConvertedTheirFilename
, dwIgnoreWS
, bIgnoreEOL
, !!bIgnoreCase
, pool
))
351 apr_pool_destroy (pool
); // free the allocated memory
356 // free the allocated memory
357 apr_pool_destroy (pool
);
359 m_arBaseFile
.RemoveAll();
360 m_arYourFile
.RemoveAll();
361 m_arTheirFile
.RemoveAll();
367 CDiffData::DoTwoWayDiff(const CString
& sBaseFilename
, const CString
& sYourFilename
, DWORD dwIgnoreWS
, bool bIgnoreEOL
, apr_pool_t
* pool
)
369 svn_diff_file_options_t
* options
= CreateDiffFileOptions(dwIgnoreWS
, bIgnoreEOL
, pool
);
370 // convert CString filenames (UTF-16 or ANSI) to UTF-8
371 CStringA sBaseFilenameUtf8
= CUnicodeUtils::GetUTF8(sBaseFilename
);
372 CStringA sYourFilenameUtf8
= CUnicodeUtils::GetUTF8(sYourFilename
);
374 svn_diff_t
* diffYourBase
= NULL
;
375 svn_error_t
* svnerr
= svn_diff_file_diff_2(&diffYourBase
, sBaseFilenameUtf8
, sYourFilenameUtf8
, options
, pool
);
378 return HandleSvnError(svnerr
);
380 tsvn_svn_diff_t_extension
* movedBlocks
= NULL
;
381 if(m_bViewMovedBlocks
)
382 movedBlocks
= MovedBlocksDetect(diffYourBase
, dwIgnoreWS
, pool
); // Side effect is that diffs are now splitted
384 svn_diff_t
* tempdiff
= diffYourBase
;
389 svn_diff__type_e diffType
= tempdiff
->type
;
390 // Side effect described above overcoming - sticking together
391 apr_off_t original_length_sticked
= tempdiff
->original_length
;
392 apr_off_t modified_length_sticked
= tempdiff
->modified_length
;
393 StickAndSkip(tempdiff
, original_length_sticked
, modified_length_sticked
);
395 for (int i
=0; i
<original_length_sticked
; i
++)
397 if (baseline
>= m_arBaseFile
.GetCount())
399 m_sError
.LoadString(IDS_ERR_DIFF_NEWLINES
);
402 const CString
& sCurrentBaseLine
= m_arBaseFile
.GetAt(baseline
);
403 EOL endingBase
= m_arBaseFile
.GetLineEnding(baseline
);
404 if (diffType
== svn_diff__type_common
)
406 if (yourline
>= m_arYourFile
.GetCount())
408 m_sError
.LoadString(IDS_ERR_DIFF_NEWLINES
);
411 const CString
& sCurrentYourLine
= m_arYourFile
.GetAt(yourline
);
412 EOL endingYours
= m_arYourFile
.GetLineEnding(yourline
);
413 if (sCurrentBaseLine
!= sCurrentYourLine
)
415 bool changedWS
= false;
416 if (dwIgnoreWS
== 2 || dwIgnoreWS
== 3)
417 changedWS
= CompareWithIgnoreWS(sCurrentBaseLine
, sCurrentYourLine
, dwIgnoreWS
);
418 if (changedWS
|| dwIgnoreWS
== 0)
420 // one-pane view: two lines, one 'removed' and one 'added'
421 m_YourBaseBoth
.AddData(sCurrentBaseLine
, DIFFSTATE_REMOVEDWHITESPACE
, yourline
, endingBase
, HIDESTATE_SHOWN
, -1);
422 m_YourBaseBoth
.AddData(sCurrentYourLine
, DIFFSTATE_ADDEDWHITESPACE
, yourline
, endingYours
, HIDESTATE_SHOWN
, -1);
426 m_YourBaseBoth
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingBase
, HIDESTATE_HIDDEN
, -1);
431 m_YourBaseBoth
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingBase
, HIDESTATE_HIDDEN
, -1);
433 yourline
++; //in both files
436 { // small trick - we need here a baseline, but we fix it back to yourline at the end of routine
437 m_YourBaseBoth
.AddData(sCurrentBaseLine
, DIFFSTATE_REMOVED
, -1, endingBase
, HIDESTATE_SHOWN
, -1);
441 if (diffType
== svn_diff__type_diff_modified
)
443 for (int i
=0; i
<modified_length_sticked
; i
++)
445 if (m_arYourFile
.GetCount() > yourline
)
447 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_ADDED
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
452 tempdiff
= tempdiff
->next
;
455 HideUnchangedSections(&m_YourBaseBoth
, NULL
, NULL
);
457 tempdiff
= diffYourBase
;
462 if (tempdiff
->type
== svn_diff__type_common
)
464 for (int i
=0; i
<tempdiff
->original_length
; i
++)
466 const CString
& sCurrentYourLine
= m_arYourFile
.GetAt(yourline
);
467 EOL endingYours
= m_arYourFile
.GetLineEnding(yourline
);
468 const CString
& sCurrentBaseLine
= m_arBaseFile
.GetAt(baseline
);
469 EOL endingBase
= m_arBaseFile
.GetLineEnding(baseline
);
470 if (sCurrentBaseLine
!= sCurrentYourLine
)
472 bool changedWS
= false;
473 if (dwIgnoreWS
== 2 || dwIgnoreWS
== 3)
474 changedWS
= CompareWithIgnoreWS(sCurrentBaseLine
, sCurrentYourLine
, dwIgnoreWS
);
475 if (changedWS
|| dwIgnoreWS
== 0)
477 m_YourBaseLeft
.AddData(sCurrentBaseLine
, DIFFSTATE_WHITESPACE
, baseline
, endingBase
, HIDESTATE_SHOWN
, -1);
478 m_YourBaseRight
.AddData(sCurrentYourLine
, DIFFSTATE_WHITESPACE
, yourline
, endingYours
, HIDESTATE_SHOWN
, -1);
482 m_YourBaseLeft
.AddData(sCurrentBaseLine
, DIFFSTATE_NORMAL
, baseline
, endingBase
, HIDESTATE_HIDDEN
, -1);
483 m_YourBaseRight
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingYours
, HIDESTATE_HIDDEN
, -1);
488 m_YourBaseLeft
.AddData(sCurrentBaseLine
, DIFFSTATE_NORMAL
, baseline
, endingBase
, HIDESTATE_HIDDEN
, -1);
489 m_YourBaseRight
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingYours
, HIDESTATE_HIDDEN
, -1);
495 if (tempdiff
->type
== svn_diff__type_diff_modified
)
497 // now we trying to stick together parts, that were splitted by MovedBlocks
498 apr_off_t original_length_sticked
= tempdiff
->original_length
;
499 apr_off_t modified_length_sticked
= tempdiff
->modified_length
;
500 StickAndSkip(tempdiff
, original_length_sticked
, modified_length_sticked
);
502 apr_off_t original_length
= original_length_sticked
;
503 for (int i
=0; i
<modified_length_sticked
; i
++)
505 if (m_arYourFile
.GetCount() > yourline
)
507 EOL endingYours
= m_arYourFile
.GetLineEnding(yourline
);
508 m_YourBaseRight
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_ADDED
, yourline
, endingYours
, HIDESTATE_SHOWN
, -1);
509 if (original_length
-- <= 0)
511 m_YourBaseLeft
.AddEmpty();
515 EOL endingBase
= m_arBaseFile
.GetLineEnding(baseline
);
516 m_YourBaseLeft
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_REMOVED
, baseline
, endingBase
, HIDESTATE_SHOWN
, -1);
522 apr_off_t modified_length
= modified_length_sticked
;
523 for (int i
=0; i
<original_length_sticked
; i
++)
525 if ((modified_length
-- <= 0)&&(m_arBaseFile
.GetCount() > baseline
))
527 EOL endingBase
= m_arBaseFile
.GetLineEnding(baseline
);
528 m_YourBaseLeft
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_REMOVED
, baseline
, endingBase
, HIDESTATE_SHOWN
, -1);
529 m_YourBaseRight
.AddEmpty();
534 tempdiff
= tempdiff
->next
;
536 // add last (empty) lines if needed - diff don't report those
537 if (m_arBaseFile
.GetCount() > baseline
)
539 if (m_arYourFile
.GetCount() > yourline
)
541 // last line is missing in both files add them to end and mark as no diff
542 m_YourBaseLeft
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_NORMAL
, baseline
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
, -1);
543 m_YourBaseRight
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_NORMAL
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
549 viewdata
oViewData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_REMOVED
, baseline
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
);
552 // find first EMPTY line in last blok
553 int nPos
= m_YourBaseLeft
.GetCount();
554 while (--nPos
>=0 && m_YourBaseLeft
.GetState(nPos
)==DIFFSTATE_EMPTY
) ;
555 if (++nPos
<m_YourBaseLeft
.GetCount())
557 m_YourBaseLeft
.SetData(nPos
, oViewData
);
561 m_YourBaseLeft
.AddData(oViewData
);
562 m_YourBaseRight
.AddEmpty();
566 else if (m_arYourFile
.GetCount() > yourline
)
568 viewdata
oViewData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_ADDED
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
);
571 // try to move last line higher
572 int nPos
= m_YourBaseRight
.GetCount();
573 while (--nPos
>=0 && m_YourBaseRight
.GetState(nPos
)==DIFFSTATE_EMPTY
) ;
574 if (++nPos
<m_YourBaseRight
.GetCount())
576 m_YourBaseRight
.SetData(nPos
, oViewData
);
580 m_YourBaseLeft
.AddEmpty();
581 m_YourBaseRight
.AddData(oViewData
);
586 // Fixing results for conforming moved blocks
590 tempdiff
= movedBlocks
->base
;
591 if(movedBlocks
->moved_to
!= -1)
593 // set states in a block original:length -> moved_to:length
594 TieMovedBlocks((int)tempdiff
->original_start
, movedBlocks
->moved_to
, tempdiff
->original_length
);
596 if(movedBlocks
->moved_from
!= -1)
598 // set states in a block modified:length -> moved_from:length
599 TieMovedBlocks(movedBlocks
->moved_from
, (int)tempdiff
->modified_start
, tempdiff
->modified_length
);
601 movedBlocks
= movedBlocks
->next
;
604 // replace baseline with the yourline in m_YourBaseBoth
606 for(int i=0; i<m_YourBaseBoth.GetCount(); i++)
608 DiffStates state = m_YourBaseBoth.GetState(i);
609 if((state == DIFFSTATE_REMOVED)||(state == DIFFSTATE_MOVED_FROM))
611 m_YourBaseBoth.SetLineNumber(i, -1);
619 TRACE(_T("done with 2-way diff\n"));
621 HideUnchangedSections(&m_YourBaseLeft
, &m_YourBaseRight
, NULL
);
627 CDiffData::DoThreeWayDiff(const CString
& sBaseFilename
, const CString
& sYourFilename
, const CString
& sTheirFilename
, DWORD dwIgnoreWS
, bool bIgnoreEOL
, bool bIgnoreCase
, apr_pool_t
* pool
)
629 // the following three arrays are used to check for conflicts even in case the
630 // user has ignored spaces/eols.
631 CStdDWORDArray m_arDiff3LinesBase
;
632 CStdDWORDArray m_arDiff3LinesYour
;
633 CStdDWORDArray m_arDiff3LinesTheir
;
634 #define AddLines(baseline, yourline, theirline) m_arDiff3LinesBase.Add(baseline), m_arDiff3LinesYour.Add(yourline), m_arDiff3LinesTheir.Add(theirline)
635 int lengthHint
= GetLineCount();
637 m_arDiff3LinesBase
.Reserve(lengthHint
);
638 m_arDiff3LinesYour
.Reserve(lengthHint
);
639 m_arDiff3LinesTheir
.Reserve(lengthHint
);
641 CRegDWORD contextLines
= CRegDWORD(_T("Software\\TortoiseGitMerge\\ContextLines"), 3);
642 svn_diff_file_options_t
* options
= CreateDiffFileOptions(dwIgnoreWS
, bIgnoreEOL
, pool
);
644 // convert CString filenames (UTF-16 or ANSI) to UTF-8
645 CStringA sBaseFilenameUtf8
= CUnicodeUtils::GetUTF8(sBaseFilename
);
646 CStringA sYourFilenameUtf8
= CUnicodeUtils::GetUTF8(sYourFilename
);
647 CStringA sTheirFilenameUtf8
= CUnicodeUtils::GetUTF8(sTheirFilename
);
649 svn_diff_t
* diffTheirYourBase
= NULL
;
650 svn_error_t
* svnerr
= svn_diff_file_diff3_2(&diffTheirYourBase
, sBaseFilenameUtf8
, sTheirFilenameUtf8
, sYourFilenameUtf8
, options
, pool
);
652 return HandleSvnError(svnerr
);
654 svn_diff_t
* tempdiff
= diffTheirYourBase
;
660 const viewdata
emptyConflictEmpty(_T(""), DIFFSTATE_CONFLICTEMPTY
, DIFF_EMPTYLINENUMBER
, EOL_NOENDING
, HIDESTATE_SHOWN
);
661 const viewdata
emptyIdenticalRemoved(_T(""), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, EOL_NOENDING
, HIDESTATE_SHOWN
);
664 if (tempdiff
->type
== svn_diff__type_common
)
666 ASSERT((tempdiff
->latest_length
== tempdiff
->modified_length
) && (tempdiff
->modified_length
== tempdiff
->original_length
));
667 for (int i
=0; i
<tempdiff
->original_length
; i
++)
669 if ((m_arYourFile
.GetCount() > yourline
)&&(m_arTheirFile
.GetCount() > theirline
))
671 AddLines(baseline
, yourline
, theirline
);
673 m_Diff3
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_NORMAL
, resline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_HIDDEN
, -1);
674 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_NORMAL
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_HIDDEN
, -1);
675 m_TheirBaseBoth
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_NORMAL
, theirline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_HIDDEN
, -1);
684 else if (tempdiff
->type
== svn_diff__type_diff_common
)
686 ASSERT(tempdiff
->latest_length
== tempdiff
->modified_length
);
687 //both theirs and yours have lines replaced
688 for (int i
=0; i
<tempdiff
->original_length
; i
++)
690 if (m_arBaseFile
.GetCount() > baseline
)
692 AddLines(baseline
, yourline
, theirline
);
694 m_Diff3
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
, -1);
695 m_YourBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, EOL_NOENDING
, HIDESTATE_SHOWN
, -1);
696 m_TheirBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, EOL_NOENDING
, HIDESTATE_SHOWN
, -1);
701 for (int i
=0; i
<tempdiff
->modified_length
; i
++)
703 if ((m_arYourFile
.GetCount() > yourline
)&&(m_arTheirFile
.GetCount() > theirline
))
705 AddLines(baseline
, yourline
, theirline
);
707 m_Diff3
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_IDENTICALADDED
, resline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
708 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_IDENTICALADDED
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
709 m_TheirBaseBoth
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_IDENTICALADDED
, resline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_SHOWN
, -1);
717 else if (tempdiff
->type
== svn_diff__type_conflict
)
719 apr_off_t length
= max(tempdiff
->original_length
, tempdiff
->modified_length
);
720 length
= max(tempdiff
->latest_length
, length
);
721 apr_off_t original
= tempdiff
->original_length
;
722 apr_off_t modified
= tempdiff
->modified_length
;
723 apr_off_t latest
= tempdiff
->latest_length
;
725 apr_off_t originalresolved
= 0;
726 apr_off_t modifiedresolved
= 0;
727 apr_off_t latestresolved
= 0;
729 LONG base
= baseline
;
730 LONG your
= yourline
;
731 LONG their
= theirline
;
732 if (tempdiff
->resolved_diff
)
734 originalresolved
= tempdiff
->resolved_diff
->original_length
;
735 modifiedresolved
= tempdiff
->resolved_diff
->modified_length
;
736 latestresolved
= tempdiff
->resolved_diff
->latest_length
;
738 for (int i
=0; i
<length
; i
++)
740 EOL endingBase
= m_arBaseFile
.GetCount() > baseline
? m_arBaseFile
.GetLineEnding(baseline
) : EOL_AUTOLINE
;
743 if (m_arBaseFile
.GetCount() > baseline
)
745 AddLines(baseline
, yourline
, theirline
);
747 m_Diff3
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, endingBase
, HIDESTATE_SHOWN
, -1);
748 m_YourBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, endingBase
, HIDESTATE_SHOWN
, -1);
749 m_TheirBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_IDENTICALREMOVED
, DIFF_EMPTYLINENUMBER
, endingBase
, HIDESTATE_SHOWN
, -1);
752 else if ((originalresolved
)||((modifiedresolved
)&&(latestresolved
)))
754 AddLines(baseline
, yourline
, theirline
);
756 m_Diff3
.AddData(emptyIdenticalRemoved
);
757 if ((latestresolved
)&&(modifiedresolved
))
759 m_YourBaseBoth
.AddData(emptyIdenticalRemoved
);
760 m_TheirBaseBoth
.AddData(emptyIdenticalRemoved
);
768 if (originalresolved
)
776 if (modifiedresolved
)
786 original
= tempdiff
->original_length
;
787 modified
= tempdiff
->modified_length
;
788 latest
= tempdiff
->latest_length
;
792 if (tempdiff
->resolved_diff
)
794 originalresolved
= tempdiff
->resolved_diff
->original_length
;
795 modifiedresolved
= tempdiff
->resolved_diff
->modified_length
;
796 latestresolved
= tempdiff
->resolved_diff
->latest_length
;
798 for (int i
=0; i
<length
; i
++)
800 if ((latest
)||(modified
))
802 AddLines(baseline
, yourline
, theirline
);
804 m_Diff3
.AddData(_T(""), DIFFSTATE_CONFLICTED
, resline
, EOL_NOENDING
, HIDESTATE_SHOWN
, -1);
811 if (m_arYourFile
.GetCount() > yourline
)
813 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_CONFLICTADDED
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
816 else if ((latestresolved
)||(modified
)||(modifiedresolved
))
818 m_YourBaseBoth
.AddData(emptyConflictEmpty
);
822 if (m_arTheirFile
.GetCount() > theirline
)
824 m_TheirBaseBoth
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_CONFLICTADDED
, theirline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_SHOWN
, -1);
827 else if ((modifiedresolved
)||(latest
)||(latestresolved
))
829 m_TheirBaseBoth
.AddData(emptyConflictEmpty
);
836 if (originalresolved
)
843 if (modifiedresolved
)
854 else if (tempdiff
->type
== svn_diff__type_diff_modified
)
857 for (int i
=0; i
<tempdiff
->original_length
; i
++)
859 if ((m_arBaseFile
.GetCount() > baseline
)&&(m_arYourFile
.GetCount() > yourline
))
861 AddLines(baseline
, yourline
, theirline
);
863 m_Diff3
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_THEIRSREMOVED
, DIFF_EMPTYLINENUMBER
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
, -1);
864 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_NORMAL
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
865 m_TheirBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_THEIRSREMOVED
, DIFF_EMPTYLINENUMBER
, EOL_NOENDING
, HIDESTATE_SHOWN
, -1);
872 for (int i
=0; i
<tempdiff
->modified_length
; i
++)
874 if (m_arTheirFile
.GetCount() > theirline
)
876 AddLines(baseline
, yourline
, theirline
);
878 m_Diff3
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_THEIRSADDED
, resline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_SHOWN
, -1);
879 m_YourBaseBoth
.AddEmpty();
880 m_TheirBaseBoth
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_THEIRSADDED
, theirline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_SHOWN
, -1);
887 else if (tempdiff
->type
== svn_diff__type_diff_latest
)
889 //YOURS differs from base
891 for (int i
=0; i
<tempdiff
->original_length
; i
++)
893 if ((m_arBaseFile
.GetCount() > baseline
)&&(m_arTheirFile
.GetCount() > theirline
))
895 AddLines(baseline
, yourline
, theirline
);
897 m_Diff3
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_YOURSREMOVED
, DIFF_EMPTYLINENUMBER
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
, -1);
898 m_YourBaseBoth
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_YOURSREMOVED
, DIFF_EMPTYLINENUMBER
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
, -1);
899 m_TheirBaseBoth
.AddData(m_arTheirFile
.GetAt(theirline
), DIFFSTATE_NORMAL
, theirline
, m_arTheirFile
.GetLineEnding(theirline
), HIDESTATE_HIDDEN
, -1);
905 for (int i
=0; i
<tempdiff
->latest_length
; i
++)
907 if (m_arYourFile
.GetCount() > yourline
)
909 AddLines(baseline
, yourline
, theirline
);
911 m_Diff3
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_YOURSADDED
, resline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
912 m_YourBaseBoth
.AddData(m_arYourFile
.GetAt(yourline
), DIFFSTATE_IDENTICALADDED
, yourline
, m_arYourFile
.GetLineEnding(yourline
), HIDESTATE_SHOWN
, -1);
913 m_TheirBaseBoth
.AddEmpty();
922 TRACE(_T("something bad happened during diff!\n"));
924 tempdiff
= tempdiff
->next
;
926 } // while (tempdiff)
928 if ((options
->ignore_space
!= svn_diff_file_ignore_space_none
)||(bIgnoreCase
)||(bIgnoreEOL
))
930 // If whitespaces are ignored, a conflict could have been missed
931 // We now go through all lines again and check if they're identical.
932 // If they're not, then that means it is a conflict, and we
933 // mark the conflict with the proper colors.
934 for (long i
=0; i
<m_Diff3
.GetCount(); ++i
)
936 DiffStates state1
= m_YourBaseBoth
.GetState(i
);
937 DiffStates state2
= m_TheirBaseBoth
.GetState(i
);
939 if (((state1
== DIFFSTATE_IDENTICALADDED
)||(state1
== DIFFSTATE_NORMAL
))&&
940 ((state2
== DIFFSTATE_IDENTICALADDED
)||(state2
== DIFFSTATE_NORMAL
)))
942 LONG lineyour
= m_arDiff3LinesYour
.GetAt(i
);
943 LONG linetheir
= m_arDiff3LinesTheir
.GetAt(i
);
944 LONG linebase
= m_arDiff3LinesBase
.GetAt(i
);
945 if ((lineyour
< m_arYourFile
.GetCount()) &&
946 (linetheir
< m_arTheirFile
.GetCount()) &&
947 (linebase
< m_arBaseFile
.GetCount()))
949 if (((m_arYourFile
.GetLineEnding(lineyour
)!=m_arBaseFile
.GetLineEnding(linebase
))&&
950 (m_arTheirFile
.GetLineEnding(linetheir
)!=m_arBaseFile
.GetLineEnding(linebase
))&&
951 (m_arYourFile
.GetLineEnding(lineyour
)!=m_arTheirFile
.GetLineEnding(linetheir
))) ||
952 ((m_arYourFile
.GetAt(lineyour
).Compare(m_arBaseFile
.GetAt(linebase
))!=0)&&
953 (m_arTheirFile
.GetAt(linetheir
).Compare(m_arBaseFile
.GetAt(linebase
))!=0)&&
954 (m_arYourFile
.GetAt(lineyour
).Compare(m_arTheirFile
.GetAt(linetheir
))!=0)))
956 m_Diff3
.SetState(i
, DIFFSTATE_CONFLICTED_IGNORED
);
957 m_YourBaseBoth
.SetState(i
, DIFFSTATE_CONFLICTADDED
);
958 m_TheirBaseBoth
.SetState(i
, DIFFSTATE_CONFLICTADDED
);
964 ASSERT(m_Diff3
.GetCount() == m_YourBaseBoth
.GetCount());
965 ASSERT(m_TheirBaseBoth
.GetCount() == m_YourBaseBoth
.GetCount());
967 TRACE(_T("done with 3-way diff\n"));
969 HideUnchangedSections(&m_Diff3
, &m_YourBaseBoth
, &m_TheirBaseBoth
);
974 void CDiffData::HideUnchangedSections(CViewData
* data1
, CViewData
* data2
, CViewData
* data3
) const
979 CRegDWORD contextLines
= CRegDWORD(_T("Software\\TortoiseGitMerge\\ContextLines"), 1);
981 if (data1
->GetCount() > 1)
983 HIDESTATE lastHideState
= data1
->GetHideState(0);
984 if (lastHideState
== HIDESTATE_HIDDEN
)
986 data1
->SetLineHideState(0, HIDESTATE_MARKER
);
988 data2
->SetLineHideState(0, HIDESTATE_MARKER
);
990 data3
->SetLineHideState(0, HIDESTATE_MARKER
);
992 for (int i
= 1; i
< data1
->GetCount(); ++i
)
994 HIDESTATE hideState
= data1
->GetHideState(i
);
995 if (hideState
!= lastHideState
)
997 if (hideState
== HIDESTATE_SHOWN
)
999 // go back and show the last 'contextLines' lines to "SHOWN"
1000 int lineback
= i
- 1;
1001 int stopline
= lineback
- (int)(DWORD
)contextLines
;
1002 while ((lineback
>= 0)&&(lineback
> stopline
))
1004 data1
->SetLineHideState(lineback
, HIDESTATE_SHOWN
);
1006 data2
->SetLineHideState(lineback
, HIDESTATE_SHOWN
);
1008 data3
->SetLineHideState(lineback
, HIDESTATE_SHOWN
);
1012 else if ((hideState
== HIDESTATE_HIDDEN
)&&(lastHideState
!= HIDESTATE_MARKER
))
1014 // go forward and show the next 'contextLines' lines to "SHOWN"
1015 int lineforward
= i
+ 1;
1016 int stopline
= lineforward
+ (int)(DWORD
)contextLines
;
1017 while ((lineforward
< data1
->GetCount())&&(lineforward
< stopline
))
1019 data1
->SetLineHideState(lineforward
, HIDESTATE_SHOWN
);
1021 data2
->SetLineHideState(lineforward
, HIDESTATE_SHOWN
);
1023 data3
->SetLineHideState(lineforward
, HIDESTATE_SHOWN
);
1026 if ((lineforward
< data1
->GetCount())&&(data1
->GetHideState(lineforward
) == HIDESTATE_HIDDEN
))
1028 data1
->SetLineHideState(lineforward
, HIDESTATE_MARKER
);
1030 data2
->SetLineHideState(lineforward
, HIDESTATE_MARKER
);
1032 data3
->SetLineHideState(lineforward
, HIDESTATE_MARKER
);
1036 lastHideState
= hideState
;
1041 void CDiffData::SetCommentTokens( const CString
& sLineStart
, const CString
& sBlockStart
, const CString
& sBlockEnd
)
1043 m_CommentLineStart
= sLineStart
;
1044 m_CommentBlockStart
= sBlockStart
;
1045 m_CommentBlockEnd
= sBlockEnd
;
1048 void CDiffData::SetRegexTokens( const std::wregex
& rx
, const std::wstring
& replacement
)
1051 m_replacement
= replacement
;