1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
4 // Copyright (C) 2008-2012 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "MessageBox.h"
26 #include "FileDiffDlg.h"
27 #include "SubmoduleDiffDlg.h"
29 CGitDiff::CGitDiff(void)
33 CGitDiff::~CGitDiff(void)
36 int CGitDiff::Parser(git_revnum_t
&rev
)
38 if(rev
== GIT_REV_ZERO
)
40 if(rev
.GetLength() > 40)
43 cmd
.Format(_T("git.exe rev-parse %s"),rev
);
45 if (!g_Git
.Run(cmd
, &output
, NULL
, CP_UTF8
))
47 //int start=output.Find(_T('\n'));
54 int CGitDiff::SubmoduleDiffNull(CTGitPath
*pPath
, git_revnum_t
&/*rev1*/)
56 CString oldhash
= GIT_REV_ZERO
;
62 cmd
.Format(_T("git.exe ls-tree HEAD -- \"%s\""), pPath
->GetGitPathString());
64 if (g_Git
.Run(cmd
, &output
, &err
, CP_UTF8
))
66 CMessageBox::Show(NULL
, output
+ L
"\n" + err
, _T("TortoiseGit"), MB_OK
|MB_ICONERROR
);
71 start
=output
.Find(_T(' '),start
);
74 start
=output
.Find(_T(' '),start
+1);
76 newhash
=output
.Mid(start
+1, 40);
79 subgit
.m_CurrentDir
=g_Git
.m_CurrentDir
+_T("\\")+pPath
->GetWinPathString();
80 int encode
=CAppUtils::GetLogOutputEncode(&subgit
);
82 cmd
.Format(_T("git.exe log -n1 --pretty=format:\"%%s\" %s"),newhash
);
83 subgit
.Run(cmd
,&newsub
,encode
);
85 CSubmoduleDiffDlg submoduleDiffDlg
;
86 submoduleDiffDlg
.SetDiff(pPath
->GetWinPath(), false, oldhash
, oldsub
, newhash
, newsub
);
87 submoduleDiffDlg
.DoModal();
92 CMessageBox::Show(NULL
,_T("ls-tree output format error"),_T("TortoiseGit"),MB_OK
|MB_ICONERROR
);
96 int CGitDiff::DiffNull(CTGitPath
*pPath
, git_revnum_t rev1
,bool bIsAdd
)
99 GetTempPath(temppath
);
105 if(pPath
->IsDirectory())
108 return SubmoduleDiffNull(pPath
,rev1
);
111 if(rev1
!= GIT_REV_ZERO
)
113 TCHAR szTempName
[MAX_PATH
];
114 GetTempFileName(temppath
, pPath
->GetBaseFilename(), 0, szTempName
);
115 CString
temp(szTempName
);
116 DeleteFile(szTempName
);
117 CreateDirectory(szTempName
, NULL
);
118 file1
.Format(_T("%s\\%s-%s%s"),
120 pPath
->GetBaseFilename(),
121 rev1
.Left(g_Git
.GetShortHASHLength()),
122 pPath
->GetFileExtension());
124 g_Git
.GetOneFile(rev1
,*pPath
,file1
);
128 file1
=g_Git
.m_CurrentDir
+_T("\\")+pPath
->GetWinPathString();
131 // preserve FileExtension, needed especially for diffing deleted images (detection on new filename extension)
132 CString tempfile
=::GetTempFile() + pPath
->GetFileExtension();
133 CStdioFile
file(tempfile
,CFile::modeReadWrite
|CFile::modeCreate
);
134 //file.WriteString();
136 ::SetFileAttributes(tempfile
, FILE_ATTRIBUTE_READONLY
);
138 CAppUtils::DiffFlags flags
;
141 CAppUtils::StartExtDiff(tempfile
,file1
,
142 pPath
->GetGitPathString(),
143 pPath
->GetGitPathString() + _T(":") + rev1
.Left(g_Git
.GetShortHASHLength())
146 CAppUtils::StartExtDiff(file1
,tempfile
,
147 pPath
->GetGitPathString() + _T(":") + rev1
.Left(g_Git
.GetShortHASHLength())
148 ,pPath
->GetGitPathString(),flags
);
153 int CGitDiff::SubmoduleDiff(CTGitPath
* pPath
,CTGitPath
* /*pPath2*/, git_revnum_t rev1
, git_revnum_t rev2
, bool /*blame*/, bool /*unified*/)
158 bool isWorkingCopy
= false;
159 if( rev2
== GIT_REV_ZERO
|| rev1
== GIT_REV_ZERO
)
161 oldhash
= GIT_REV_ZERO
;
162 newhash
= GIT_REV_ZERO
;
165 if( rev2
!= GIT_REV_ZERO
)
167 if( rev1
!= GIT_REV_ZERO
)
170 isWorkingCopy
= true;
172 cmd
.Format(_T("git.exe diff %s -- \"%s\""),
173 rev
,pPath
->GetGitPathString());
176 if (g_Git
.Run(cmd
, &output
, &err
, CP_UTF8
))
178 CMessageBox::Show(NULL
, output
+ L
"\n" + err
, _T("TortoiseGit"), MB_OK
|MB_ICONERROR
);
182 int oldstart
= output
.Find(_T("-Subproject commit"),start
);
185 CMessageBox::Show(NULL
,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK
|MB_ICONERROR
);
188 oldhash
= output
.Mid(oldstart
+ CString(_T("-Subproject commit")).GetLength()+1,40);
190 int newstart
= output
.Find(_T("+Subproject commit"),start
);
193 CMessageBox::Show(NULL
,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK
|MB_ICONERROR
);
196 newhash
= output
.Mid(newstart
+ CString(_T("+Subproject commit")).GetLength()+1,40);
201 cmd
.Format(_T("git.exe diff-tree -r -z %s %s -- \"%s\""),
202 rev2
,rev1
,pPath
->GetGitPathString());
204 BYTE_VECTOR bytes
, errBytes
;
205 if(g_Git
.Run(cmd
, &bytes
, &errBytes
))
208 g_Git
.StringAppend(&err
, &errBytes
[0], CP_UTF8
);
209 CMessageBox::Show(NULL
,err
,_T("TortoiseGit"),MB_OK
|MB_ICONERROR
);
213 g_Git
.StringAppend(&oldhash
, &bytes
[15], CP_UTF8
, 40);
214 g_Git
.StringAppend(&newhash
, &bytes
[15+41], CP_UTF8
, 40);
222 subgit
.m_CurrentDir
=g_Git
.m_CurrentDir
+_T("\\")+pPath
->GetWinPathString();
224 if(pPath
->HasAdminDir())
226 int encode
=CAppUtils::GetLogOutputEncode(&subgit
);
228 if(oldhash
!= GIT_REV_ZERO
)
230 cmd
.Format(_T("git log -n1 --pretty=format:\"%%s\" %s"),oldhash
);
231 subgit
.Run(cmd
,&oldsub
,encode
);
233 if(newsub
!= GIT_REV_ZERO
)
235 cmd
.Format(_T("git log -n1 --pretty=format:\"%%s\" %s"),newhash
);
236 subgit
.Run(cmd
,&newsub
,encode
);
240 CSubmoduleDiffDlg submoduleDiffDlg
;
241 submoduleDiffDlg
.SetDiff(pPath
->GetWinPath(), isWorkingCopy
, oldhash
, oldsub
, newhash
, newsub
);
242 submoduleDiffDlg
.DoModal();
247 int CGitDiff::Diff(CTGitPath
* pPath
,CTGitPath
* pPath2
, git_revnum_t rev1
, git_revnum_t rev2
, bool /*blame*/, bool /*unified*/)
250 GetTempPath(temppath
);
257 if(pPath
->IsDirectory() || pPath2
->IsDirectory())
259 return SubmoduleDiff(pPath
,pPath2
,rev1
,rev2
);
262 if(rev1
!= GIT_REV_ZERO
)
264 TCHAR szTempName
[MAX_PATH
];
265 GetTempFileName(temppath
, pPath
->GetBaseFilename(), 0, szTempName
);
266 CString
temp(szTempName
);
267 DeleteFile(szTempName
);
268 CreateDirectory(szTempName
, NULL
);
269 // use original file extension, an external diff tool might need it
270 file1
.Format(_T("%s\\%s-%s-right%s"),
272 pPath
->GetBaseFilename(),
273 rev1
.Left(g_Git
.GetShortHASHLength()),
274 pPath
->GetFileExtension());
275 title1
= pPath
->GetFileOrDirectoryName() + _T(":") + rev1
.Left(g_Git
.GetShortHASHLength());
276 g_Git
.GetOneFile(rev1
,*pPath
,file1
);
277 ::SetFileAttributes(file1
, FILE_ATTRIBUTE_READONLY
);
281 file1
=g_Git
.m_CurrentDir
+_T("\\")+pPath
->GetWinPathString();
282 title1
.Format( IDS_DIFF_WCNAME
, pPath
->GetFileOrDirectoryName() );
283 if (!PathFileExists(file1
))
286 sMsg
.Format(IDS_PROC_DIFFERROR_FILENOTINWORKINGTREE
, file1
);
287 if (MessageBox(NULL
, sMsg
, _T("TortoiseGit"), MB_ICONEXCLAMATION
| MB_OKCANCEL
) == IDCANCEL
)
289 if (!CCommonAppUtils::FileOpenSave(file1
, NULL
, IDS_DIFF_WCNAME
, IDS_COMMONFILEFILTER
, true))
291 title1
.Format(IDS_DIFF_WCNAME
, CTGitPath(file1
).GetUIFileOrDirectoryName());
297 if(rev2
!= GIT_REV_ZERO
)
299 TCHAR szTempName
[MAX_PATH
];
300 GetTempFileName(temppath
, pPath2
->GetBaseFilename(), 0, szTempName
);
301 CString
temp(szTempName
);
302 DeleteFile(szTempName
);
303 CreateDirectory(szTempName
, NULL
);
304 CTGitPath fileName
= *pPath2
;
305 if (rev1
== GIT_REV_ZERO
&& pPath2
->m_Action
& CTGitPath::LOGACTIONS_REPLACED
)
306 fileName
= CTGitPath(pPath2
->GetGitOldPathString());
308 // use original file extension, an external diff tool might need it
309 file2
.Format(_T("%s\\%s-%s-left%s"),
311 fileName
.GetBaseFilename(),
312 rev2
.Left(g_Git
.GetShortHASHLength()),
313 fileName
.GetFileExtension());
314 title2
= fileName
.GetFileOrDirectoryName() + _T(":") + rev2
.Left(g_Git
.GetShortHASHLength());
315 g_Git
.GetOneFile(rev2
, fileName
, file2
);
316 ::SetFileAttributes(file2
, FILE_ATTRIBUTE_READONLY
);
320 file2
=g_Git
.m_CurrentDir
+_T("\\")+pPath2
->GetWinPathString();
321 title2
.Format( IDS_DIFF_WCNAME
, pPath2
->GetFileOrDirectoryName() );
324 if (pPath
->m_Action
== pPath
->LOGACTIONS_ADDED
)
326 CGitDiff::DiffNull(pPath
, rev1
, true);
328 else if (pPath
->m_Action
== pPath
->LOGACTIONS_DELETED
)
330 CGitDiff::DiffNull(pPath
, rev2
, false);
334 CAppUtils::DiffFlags flags
;
335 CAppUtils::StartExtDiff(file2
,file1
,
343 int CGitDiff::DiffCommit(CTGitPath
&path
, GitRev
*r1
, GitRev
*r2
)
345 return DiffCommit(path
, path
, r1
, r2
);
348 int CGitDiff::DiffCommit(CTGitPath
&path1
, CTGitPath
&path2
, GitRev
*r1
, GitRev
*r2
)
350 if (path1
.GetWinPathString().IsEmpty())
353 dlg
.SetDiff(NULL
, *r1
, *r2
);
356 else if (path1
.IsDirectory())
359 dlg
.SetDiff(&path1
, *r1
, *r2
);
364 Diff(&path1
, &path2
, r1
->m_CommitHash
.ToString(), r2
->m_CommitHash
.ToString());
369 int CGitDiff::DiffCommit(CTGitPath
&path
, CString r1
, CString r2
)
371 return DiffCommit(path
, path
, r1
, r2
);
375 int CGitDiff::DiffCommit(CTGitPath
&path1
, CTGitPath
&path2
, CString r1
, CString r2
)
377 if (path1
.GetWinPathString().IsEmpty())
380 dlg
.SetDiff(NULL
, r1
, r2
);
383 else if (path1
.IsDirectory())
386 dlg
.SetDiff(&path1
, r1
, r2
);
391 Diff(&path1
, &path2
, r1
, r2
);