Disable warnings and drop prebuild scripts
[TortoiseGit.git] / src / TortoiseProc / GitDiff.cpp
blob3f664ed54c2ea2c065884bc10509ce7122e97eab
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::SubmoduleDiffNull(CTGitPath *pPath, git_revnum_t &rev1)
38 CString oldhash = GIT_REV_ZERO;
39 CString oldsub ;
40 CString newsub;
41 CString newhash;
43 CString cmd;
44 if (rev1 != GIT_REV_ZERO)
45 cmd.Format(_T("git.exe ls-tree \"%s\" -- \"%s\""), rev1, pPath->GetGitPathString());
46 else
47 cmd.Format(_T("git.exe ls-files -s -- \"%s\""), pPath->GetGitPathString());
49 CString output, err;
50 if (g_Git.Run(cmd, &output, &err, CP_UTF8))
52 CMessageBox::Show(NULL, output + L"\n" + err, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
53 return -1;
56 int start=0;
57 start=output.Find(_T(' '),start);
58 if(start>0)
60 if (rev1 != GIT_REV_ZERO) // in ls-files the hash is in the second column; in ls-tree it's in the third one
61 start = output.Find(_T(' '), start + 1);
62 if(start>0)
63 newhash=output.Mid(start+1, 40);
65 CGit subgit;
66 subgit.m_CurrentDir=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
67 int encode=CAppUtils::GetLogOutputEncode(&subgit);
69 cmd.Format(_T("git.exe log -n1 --pretty=format:\"%%s\" %s"),newhash);
70 bool toOK = !subgit.Run(cmd,&newsub,encode);
72 bool dirty = false;
73 if (rev1 == GIT_REV_ZERO)
75 CString dirtyList;
76 subgit.Run(_T("git.exe status --porcelain"), &dirtyList, encode);
77 dirty = !dirtyList.IsEmpty();
80 CSubmoduleDiffDlg submoduleDiffDlg;
81 submoduleDiffDlg.SetDiff(pPath->GetWinPath(), false, oldhash, oldsub, true, newhash, newsub, toOK, dirty, CSubmoduleDiffDlg::NewSubmodule);
82 submoduleDiffDlg.DoModal();
84 return 0;
87 if (rev1 != GIT_REV_ZERO)
88 CMessageBox::Show(NULL, _T("ls-tree output format error"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
89 else
90 CMessageBox::Show(NULL, _T("ls-files output format error"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
91 return -1;
94 int CGitDiff::DiffNull(CTGitPath *pPath, git_revnum_t rev1,bool bIsAdd)
96 CString temppath;
97 GetTempPath(temppath);
98 rev1 = g_Git.GetHash(rev1); // make sure we have a HASH here, otherwise filenames might be invalid
99 CString file1;
100 CString nullfile;
101 CString cmd;
103 if(pPath->IsDirectory())
105 git_revnum_t rev2;
106 return SubmoduleDiffNull(pPath,rev1);
109 if(rev1 != GIT_REV_ZERO )
111 TCHAR szTempName[MAX_PATH];
112 GetTempFileName(temppath, pPath->GetBaseFilename(), 0, szTempName);
113 CString temp(szTempName);
114 DeleteFile(szTempName);
115 CreateDirectory(szTempName, NULL);
116 file1.Format(_T("%s\\%s-%s%s"),
117 temp,
118 pPath->GetBaseFilename(),
119 rev1.Left(g_Git.GetShortHASHLength()),
120 pPath->GetFileExtension());
122 g_Git.GetOneFile(rev1,*pPath,file1);
124 else
126 file1=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
129 // preserve FileExtension, needed especially for diffing deleted images (detection on new filename extension)
130 CString tempfile=::GetTempFile() + pPath->GetFileExtension();
131 CStdioFile file(tempfile,CFile::modeReadWrite|CFile::modeCreate );
132 //file.WriteString();
133 file.Close();
134 ::SetFileAttributes(tempfile, FILE_ATTRIBUTE_READONLY);
136 CAppUtils::DiffFlags flags;
138 if(bIsAdd)
139 CAppUtils::StartExtDiff(tempfile,file1,
140 pPath->GetGitPathString(),
141 pPath->GetGitPathString() + _T(":") + rev1.Left(g_Git.GetShortHASHLength()),
142 g_Git.m_CurrentDir + _T("\\") + pPath->GetWinPathString(), g_Git.m_CurrentDir + _T("\\") + pPath->GetWinPathString(),
143 git_revnum_t(GIT_REV_ZERO), rev1
144 , flags);
145 else
146 CAppUtils::StartExtDiff(file1,tempfile,
147 pPath->GetGitPathString() + _T(":") + rev1.Left(g_Git.GetShortHASHLength()),
148 pPath->GetGitPathString(),
149 g_Git.m_CurrentDir + _T("\\") + pPath->GetWinPathString(), g_Git.m_CurrentDir + _T("\\") + pPath->GetWinPathString(),
150 rev1, git_revnum_t(GIT_REV_ZERO)
151 , flags);
153 return 0;
156 int CGitDiff::SubmoduleDiff(CTGitPath * pPath,CTGitPath * /*pPath2*/, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
158 CString oldhash;
159 CString newhash;
160 bool dirty = false;
161 CString cmd;
162 bool isWorkingCopy = false;
163 if( rev2 == GIT_REV_ZERO || rev1 == GIT_REV_ZERO )
165 oldhash = GIT_REV_ZERO;
166 newhash = GIT_REV_ZERO;
168 CString rev;
169 if( rev2 != GIT_REV_ZERO )
170 rev = rev2;
171 if( rev1 != GIT_REV_ZERO )
172 rev = rev1;
174 isWorkingCopy = true;
176 cmd.Format(_T("git.exe diff %s -- \"%s\""),
177 rev,pPath->GetGitPathString());
179 CString output, err;
180 if (g_Git.Run(cmd, &output, &err, CP_UTF8))
182 CMessageBox::Show(NULL, output + L"\n" + err, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
183 return -1;
186 if (output.IsEmpty())
188 if (CMessageBox::Show(NULL, CString(MAKEINTRESOURCE(IDS_SUBMODULE_EMPTYDIFF)), _T("TortoiseGit"), 1, IDI_QUESTION, CString(MAKEINTRESOURCE(IDS_MSGBOX_YES)), CString(MAKEINTRESOURCE(IDS_MSGBOX_NO))) == 1)
190 CString sCmd;
191 sCmd.Format(_T("/command:subupdate /bkpath:\"%s\""), g_Git.m_CurrentDir);
192 CAppUtils::RunTortoiseGitProc(sCmd);
194 return -1;
197 int start =0;
198 int oldstart = output.Find(_T("-Subproject commit"),start);
199 if(oldstart<0)
201 CMessageBox::Show(NULL,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
202 return -1;
204 oldhash = output.Mid(oldstart+ CString(_T("-Subproject commit")).GetLength()+1,40);
205 start = 0;
206 int newstart = output.Find(_T("+Subproject commit"),start);
207 if(oldstart<0)
209 CMessageBox::Show(NULL,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
210 return -1;
212 newhash = output.Mid(newstart+ CString(_T("+Subproject commit")).GetLength()+1,40);
213 dirty = output.Mid(newstart + CString(_T("+Subproject commit")).GetLength() + 41) == _T("-dirty\n");
215 else
217 cmd.Format(_T("git.exe diff-tree -r -z %s %s -- \"%s\""),
218 rev2,rev1,pPath->GetGitPathString());
220 BYTE_VECTOR bytes, errBytes;
221 if(g_Git.Run(cmd, &bytes, &errBytes))
223 CString err;
224 g_Git.StringAppend(&err, &errBytes[0], CP_UTF8);
225 CMessageBox::Show(NULL,err,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
226 return -1;
229 g_Git.StringAppend(&oldhash, &bytes[15], CP_UTF8, 40);
230 g_Git.StringAppend(&newhash, &bytes[15+41], CP_UTF8, 40);
234 CString oldsub;
235 CString newsub;
236 bool oldOK = false, newOK = false;
238 CGit subgit;
239 subgit.m_CurrentDir=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
240 CSubmoduleDiffDlg::ChangeType changeType = CSubmoduleDiffDlg::Unknown;
242 if(pPath->HasAdminDir())
244 int encode=CAppUtils::GetLogOutputEncode(&subgit);
245 int oldTime = 0, newTime = 0;
247 if(oldhash != GIT_REV_ZERO)
249 CString cmdout, cmderr;
250 cmd.Format(_T("git log -n1 --pretty=format:\"%%ct %%s\" %s"), oldhash);
251 oldOK = !subgit.Run(cmd, &cmdout, &cmderr, encode);
252 if (oldOK)
254 int pos = cmdout.Find(_T(" "));
255 oldTime = _ttoi(cmdout.Left(pos));
256 oldsub = cmdout.Mid(pos + 1);
258 else
259 oldsub = cmderr;
261 if (newhash != GIT_REV_ZERO)
263 CString cmdout, cmderr;
264 cmd.Format(_T("git log -n1 --pretty=format:\"%%ct %%s\" %s"), newhash);
265 newOK = !subgit.Run(cmd, &cmdout, &cmderr, encode);
266 if (newOK)
268 int pos = cmdout.Find(_T(" "));
269 newTime = _ttoi(cmdout.Left(pos));
270 newsub = cmdout.Mid(pos + 1);
272 else
273 newsub = cmderr;
276 if (oldhash == GIT_REV_ZERO)
278 oldOK = true;
279 changeType = CSubmoduleDiffDlg::NewSubmodule;
281 else if (newhash == GIT_REV_ZERO)
283 newOK = true;
284 changeType = CSubmoduleDiffDlg::DeleteSubmodule;
286 else if (oldhash != newhash)
288 bool ffNewer = false, ffOlder = false;
289 ffNewer = subgit.IsFastForward(oldhash, newhash);
290 if (!ffNewer)
292 ffOlder = subgit.IsFastForward(newhash, oldhash);
293 if (!ffOlder)
295 if (newTime > oldTime)
296 changeType = CSubmoduleDiffDlg::NewerTime;
297 else if (newTime < oldTime)
298 changeType = CSubmoduleDiffDlg::OlderTime;
299 else
300 changeType = CSubmoduleDiffDlg::SameTime;
302 else
303 changeType = CSubmoduleDiffDlg::Rewind;
305 else
306 changeType = CSubmoduleDiffDlg::FastForward;
310 CSubmoduleDiffDlg submoduleDiffDlg;
311 submoduleDiffDlg.SetDiff(pPath->GetWinPath(), isWorkingCopy, oldhash, oldsub, oldOK, newhash, newsub, newOK, dirty, changeType);
312 submoduleDiffDlg.DoModal();
314 return 0;
317 int CGitDiff::Diff(CTGitPath * pPath,CTGitPath * pPath2, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
319 CString temppath;
320 GetTempPath(temppath);
322 // make sure we have HASHes here, otherwise filenames might be invalid
323 rev1 = g_Git.GetHash(rev1);
324 rev2 = g_Git.GetHash(rev2);
326 CString file1;
327 CString title1;
328 CString cmd;
330 if(pPath->IsDirectory() || pPath2->IsDirectory())
332 return SubmoduleDiff(pPath,pPath2,rev1,rev2);
335 if(rev1 != GIT_REV_ZERO )
337 TCHAR szTempName[MAX_PATH];
338 GetTempFileName(temppath, pPath->GetBaseFilename(), 0, szTempName);
339 CString temp(szTempName);
340 DeleteFile(szTempName);
341 CreateDirectory(szTempName, NULL);
342 // use original file extension, an external diff tool might need it
343 file1.Format(_T("%s\\%s-%s-right%s"),
344 temp,
345 pPath->GetBaseFilename(),
346 rev1.Left(g_Git.GetShortHASHLength()),
347 pPath->GetFileExtension());
348 title1 = pPath->GetFileOrDirectoryName() + _T(":") + rev1.Left(g_Git.GetShortHASHLength());
349 g_Git.GetOneFile(rev1,*pPath,file1);
350 ::SetFileAttributes(file1, FILE_ATTRIBUTE_READONLY);
352 else
354 file1=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
355 title1.Format( IDS_DIFF_WCNAME, pPath->GetFileOrDirectoryName() );
356 if (!PathFileExists(file1))
358 CString sMsg;
359 sMsg.Format(IDS_PROC_DIFFERROR_FILENOTINWORKINGTREE, file1);
360 if (MessageBox(NULL, sMsg, _T("TortoiseGit"), MB_ICONEXCLAMATION | MB_OKCANCEL) == IDCANCEL)
361 return 1;
362 if (!CCommonAppUtils::FileOpenSave(file1, NULL, IDS_DIFF_WCNAME, IDS_COMMONFILEFILTER, true))
363 return 1;
364 title1.Format(IDS_DIFF_WCNAME, CTGitPath(file1).GetUIFileOrDirectoryName());
368 CString file2;
369 CString title2;
370 if(rev2 != GIT_REV_ZERO)
372 TCHAR szTempName[MAX_PATH];
373 GetTempFileName(temppath, pPath2->GetBaseFilename(), 0, szTempName);
374 CString temp(szTempName);
375 DeleteFile(szTempName);
376 CreateDirectory(szTempName, NULL);
377 CTGitPath fileName = *pPath2;
378 if (rev1 == GIT_REV_ZERO && pPath2->m_Action & CTGitPath::LOGACTIONS_REPLACED)
379 fileName = CTGitPath(pPath2->GetGitOldPathString());
381 // use original file extension, an external diff tool might need it
382 file2.Format(_T("%s\\%s-%s-left%s"),
383 temp,
384 fileName.GetBaseFilename(),
385 rev2.Left(g_Git.GetShortHASHLength()),
386 fileName.GetFileExtension());
387 title2 = fileName.GetFileOrDirectoryName() + _T(":") + rev2.Left(g_Git.GetShortHASHLength());
388 g_Git.GetOneFile(rev2, fileName, file2);
389 ::SetFileAttributes(file2, FILE_ATTRIBUTE_READONLY);
391 else
393 file2=g_Git.m_CurrentDir+_T("\\")+pPath2->GetWinPathString();
394 title2.Format( IDS_DIFF_WCNAME, pPath2->GetFileOrDirectoryName() );
397 if (pPath->m_Action == pPath->LOGACTIONS_ADDED)
399 CGitDiff::DiffNull(pPath, rev1, true);
401 else if (pPath->m_Action == pPath->LOGACTIONS_DELETED)
403 CGitDiff::DiffNull(pPath, rev2, false);
405 else
407 CAppUtils::DiffFlags flags;
408 CAppUtils::StartExtDiff(file2,file1,
409 title2,
410 title1,
411 g_Git.m_CurrentDir + _T("\\") + pPath2->GetWinPathString(),
412 g_Git.m_CurrentDir + _T("\\") + pPath->GetWinPathString(),
413 rev2,
414 rev1,
415 flags);
417 return 0;
420 int CGitDiff::DiffCommit(CTGitPath &path, GitRev *r1, GitRev *r2)
422 return DiffCommit(path, path, r1, r2);
425 int CGitDiff::DiffCommit(CTGitPath path1, CTGitPath path2, GitRev *r1, GitRev *r2)
427 if (path1.GetWinPathString().IsEmpty())
429 CFileDiffDlg dlg;
430 dlg.SetDiff(NULL, *r1, *r2);
431 dlg.DoModal();
433 else if (path1.IsDirectory())
435 CFileDiffDlg dlg;
436 dlg.SetDiff(&path1, *r1, *r2);
437 dlg.DoModal();
439 else
441 Diff(&path1, &path2, r1->m_CommitHash.ToString(), r2->m_CommitHash.ToString());
443 return 0;
446 int CGitDiff::DiffCommit(CTGitPath &path, CString r1, CString r2)
448 return DiffCommit(path, path, r1, r2);
452 int CGitDiff::DiffCommit(CTGitPath path1, CTGitPath path2, CString r1, CString r2)
454 if (path1.GetWinPathString().IsEmpty())
456 CFileDiffDlg dlg;
457 dlg.SetDiff(NULL, r1, r2);
458 dlg.DoModal();
460 else if (path1.IsDirectory())
462 CFileDiffDlg dlg;
463 dlg.SetDiff(&path1, r1, r2);
464 dlg.DoModal();
466 else
468 Diff(&path1, &path2, r1, r2);
470 return 0;