doc: updated images
[TortoiseGit.git] / src / TortoiseProc / GitDiff.cpp
blob4906a6bfa78d5ed97d408fa2b59d992f00843856
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.
19 #include "StdAfx.h"
20 #include "GitDiff.h"
21 #include "AppUtils.h"
22 #include "git.h"
23 #include "gittype.h"
24 #include "resource.h"
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)
39 return 0;
40 if(rev.GetLength() > 40)
42 CString cmd;
43 cmd.Format(_T("git.exe rev-parse %s"),rev);
44 CString output;
45 if (!g_Git.Run(cmd, &output, NULL, CP_UTF8))
47 //int start=output.Find(_T('\n'));
48 rev=output.Left(40);
51 return 0;
54 int CGitDiff::SubmoduleDiffNull(CTGitPath *pPath, git_revnum_t &/*rev1*/)
56 CString oldhash = GIT_REV_ZERO;
57 CString oldsub ;
58 CString newsub;
59 CString newhash;
61 CString cmd;
62 cmd.Format(_T("git.exe ls-tree HEAD -- \"%s\""), pPath->GetGitPathString());
63 CString output, err;
64 if (g_Git.Run(cmd, &output, &err, CP_UTF8))
66 CMessageBox::Show(NULL, output + L"\n" + err, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
67 return -1;
70 int start=0;
71 start=output.Find(_T(' '),start);
72 if(start>0)
74 start=output.Find(_T(' '),start+1);
75 if(start>0)
76 newhash=output.Mid(start+1, 40);
78 CGit subgit;
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();
89 return 0;
92 CMessageBox::Show(NULL,_T("ls-tree output format error"),_T("TortoiseGit"),MB_OK|MB_ICONERROR);
93 return -1;
96 int CGitDiff::DiffNull(CTGitPath *pPath, git_revnum_t rev1,bool bIsAdd)
98 CString temppath;
99 GetTempPath(temppath);
100 Parser(rev1);
101 CString file1;
102 CString nullfile;
103 CString cmd;
105 if(pPath->IsDirectory())
107 git_revnum_t rev2;
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"),
119 temp,
120 pPath->GetBaseFilename(),
121 rev1.Left(g_Git.GetShortHASHLength()),
122 pPath->GetFileExtension());
124 g_Git.GetOneFile(rev1,*pPath,file1);
126 else
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();
135 file.Close();
136 ::SetFileAttributes(tempfile, FILE_ATTRIBUTE_READONLY);
138 CAppUtils::DiffFlags flags;
140 if(bIsAdd)
141 CAppUtils::StartExtDiff(tempfile,file1,
142 pPath->GetGitPathString(),
143 pPath->GetGitPathString() + _T(":") + rev1.Left(g_Git.GetShortHASHLength())
144 ,flags);
145 else
146 CAppUtils::StartExtDiff(file1,tempfile,
147 pPath->GetGitPathString() + _T(":") + rev1.Left(g_Git.GetShortHASHLength())
148 ,pPath->GetGitPathString(),flags);
150 return 0;
153 int CGitDiff::SubmoduleDiff(CTGitPath * pPath,CTGitPath * /*pPath2*/, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
155 CString oldhash;
156 CString newhash;
157 CString cmd;
158 bool isWorkingCopy = false;
159 if( rev2 == GIT_REV_ZERO || rev1 == GIT_REV_ZERO )
161 oldhash = GIT_REV_ZERO;
162 newhash = GIT_REV_ZERO;
164 CString rev;
165 if( rev2 != GIT_REV_ZERO )
166 rev = rev2;
167 if( rev1 != GIT_REV_ZERO )
168 rev = rev1;
170 isWorkingCopy = true;
172 cmd.Format(_T("git.exe diff %s -- \"%s\""),
173 rev,pPath->GetGitPathString());
175 CString output, err;
176 if (g_Git.Run(cmd, &output, &err, CP_UTF8))
178 CMessageBox::Show(NULL, output + L"\n" + err, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
179 return -1;
181 int start =0;
182 int oldstart = output.Find(_T("-Subproject commit"),start);
183 if(oldstart<0)
185 CMessageBox::Show(NULL,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
186 return -1;
188 oldhash = output.Mid(oldstart+ CString(_T("-Subproject commit")).GetLength()+1,40);
189 start = 0;
190 int newstart = output.Find(_T("+Subproject commit"),start);
191 if(oldstart<0)
193 CMessageBox::Show(NULL,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
194 return -1;
196 newhash = output.Mid(newstart+ CString(_T("+Subproject commit")).GetLength()+1,40);
199 else
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))
207 CString err;
208 g_Git.StringAppend(&err, &errBytes[0], CP_UTF8);
209 CMessageBox::Show(NULL,err,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
210 return -1;
213 g_Git.StringAppend(&oldhash, &bytes[15], CP_UTF8, 40);
214 g_Git.StringAppend(&newhash, &bytes[15+41], CP_UTF8, 40);
218 CString oldsub;
219 CString newsub;
221 CGit subgit;
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();
244 return 0;
247 int CGitDiff::Diff(CTGitPath * pPath,CTGitPath * pPath2, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
249 CString temppath;
250 GetTempPath(temppath);
251 Parser(rev1);
252 Parser(rev2);
253 CString file1;
254 CString title1;
255 CString cmd;
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"),
271 temp,
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);
279 else
281 file1=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
282 title1.Format( IDS_DIFF_WCNAME, pPath->GetFileOrDirectoryName() );
283 if (!PathFileExists(file1))
285 CString sMsg;
286 sMsg.Format(IDS_PROC_DIFFERROR_FILENOTINWORKINGTREE, file1);
287 if (MessageBox(NULL, sMsg, _T("TortoiseGit"), MB_ICONEXCLAMATION | MB_OKCANCEL) == IDCANCEL)
288 return 1;
289 if (!CCommonAppUtils::FileOpenSave(file1, NULL, IDS_DIFF_WCNAME, IDS_COMMONFILEFILTER, true))
290 return 1;
291 title1.Format(IDS_DIFF_WCNAME, CTGitPath(file1).GetUIFileOrDirectoryName());
295 CString file2;
296 CString title2;
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"),
310 temp,
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);
318 else
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);
332 else
334 CAppUtils::DiffFlags flags;
335 CAppUtils::StartExtDiff(file2,file1,
336 title2,
337 title1
338 ,flags);
340 return 0;
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())
352 CFileDiffDlg dlg;
353 dlg.SetDiff(NULL, *r1, *r2);
354 dlg.DoModal();
356 else if (path1.IsDirectory())
358 CFileDiffDlg dlg;
359 dlg.SetDiff(&path1, *r1, *r2);
360 dlg.DoModal();
362 else
364 Diff(&path1, &path2, r1->m_CommitHash.ToString(), r2->m_CommitHash.ToString());
366 return 0;
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())
379 CFileDiffDlg dlg;
380 dlg.SetDiff(NULL, r1, r2);
381 dlg.DoModal();
383 else if (path1.IsDirectory())
385 CFileDiffDlg dlg;
386 dlg.SetDiff(&path1, r1, r2);
387 dlg.DoModal();
389 else
391 Diff(&path1, &path2, r1, r2);
393 return 0;