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.
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
)
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
.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
)))
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
)))
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
)
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
)
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 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();
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();
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();
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();
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
331 if (IsBaseFileInUse() && IsTheirFileInUse() && !IsYourFileInUse())
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
348 // free the allocated memory
349 apr_pool_destroy (pool
);
351 m_arBaseFile
.RemoveAll();
352 m_arYourFile
.RemoveAll();
353 m_arTheirFile
.RemoveAll();
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
);
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
;
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
);
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
);
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);
418 m_YourBaseBoth
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingBase
, HIDESTATE_HIDDEN
, -1);
423 m_YourBaseBoth
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingBase
, HIDESTATE_HIDDEN
, -1);
425 yourline
++; //in both files
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);
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);
444 tempdiff
= tempdiff
->next
;
447 HideUnchangedSections(&m_YourBaseBoth
, NULL
, NULL
);
449 tempdiff
= diffYourBase
;
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);
474 m_YourBaseLeft
.AddData(sCurrentBaseLine
, DIFFSTATE_NORMAL
, baseline
, endingBase
, HIDESTATE_HIDDEN
, -1);
475 m_YourBaseRight
.AddData(sCurrentYourLine
, DIFFSTATE_NORMAL
, yourline
, endingYours
, HIDESTATE_HIDDEN
, -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);
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();
507 EOL endingBase
= m_arBaseFile
.GetLineEnding(baseline
);
508 m_YourBaseLeft
.AddData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_REMOVED
, baseline
, endingBase
, HIDESTATE_SHOWN
, -1);
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();
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);
541 viewdata
oViewData(m_arBaseFile
.GetAt(baseline
), DIFFSTATE_REMOVED
, baseline
, m_arBaseFile
.GetLineEnding(baseline
), HIDESTATE_SHOWN
, -1);
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
);
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);
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
);
572 m_YourBaseLeft
.AddEmpty();
573 m_YourBaseRight
.AddData(oViewData
);
578 // Fixing results for conforming moved blocks
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
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
);
611 TRACE(_T("done with 2-way diff\n"));
613 HideUnchangedSections(&m_YourBaseLeft
, &m_YourBaseRight
, NULL
);
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
);
644 return HandleSvnError(svnerr
);
646 svn_diff_t
* tempdiff
= diffTheirYourBase
;
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);
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);
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);
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);
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
;
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
);
760 if (originalresolved
)
768 if (modifiedresolved
)
778 original
= tempdiff
->original_length
;
779 modified
= tempdiff
->modified_length
;
780 latest
= tempdiff
->latest_length
;
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);
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
);
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
);
828 if (originalresolved
)
835 if (modifiedresolved
)
846 else if (tempdiff
->type
== svn_diff__type_diff_modified
)
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);
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);
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);
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();
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
);
966 void CDiffData::HideUnchangedSections(CViewData
* data1
, CViewData
* data2
, CViewData
* data3
)
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
);
980 data2
->SetLineHideState(0, HIDESTATE_MARKER
);
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
);
998 data2
->SetLineHideState(lineback
, HIDESTATE_SHOWN
);
1000 data3
->SetLineHideState(lineback
, HIDESTATE_SHOWN
);
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
);
1013 data2
->SetLineHideState(lineforward
, HIDESTATE_SHOWN
);
1015 data3
->SetLineHideState(lineforward
, HIDESTATE_SHOWN
);
1018 if ((lineforward
< data1
->GetCount())&&(data1
->GetHideState(lineforward
) == HIDESTATE_HIDDEN
))
1020 data1
->SetLineHideState(lineforward
, HIDESTATE_MARKER
);
1022 data2
->SetLineHideState(lineforward
, HIDESTATE_MARKER
);
1024 data3
->SetLineHideState(lineforward
, HIDESTATE_MARKER
);
1028 lastHideState
= hideState
;