Fixed issue #1424: Better Indicate submodule diff error
[TortoiseGit.git] / src / TortoiseProc / GitDiff.cpp
blobf93a2e99df4d3d0e45d7a6c3cdc7a4988c37499a
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 CSubmoduleDiffDlg submoduleDiffDlg;
73 submoduleDiffDlg.SetDiff(pPath->GetWinPath(), false, oldhash, oldsub, true, newhash, newsub, toOK);
74 submoduleDiffDlg.DoModal();
76 return 0;
79 if (rev1 != GIT_REV_ZERO)
80 CMessageBox::Show(NULL, _T("ls-tree output format error"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
81 else
82 CMessageBox::Show(NULL, _T("ls-files output format error"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
83 return -1;
86 int CGitDiff::DiffNull(CTGitPath *pPath, git_revnum_t rev1,bool bIsAdd)
88 CString temppath;
89 GetTempPath(temppath);
90 rev1 = g_Git.GetHash(rev1); // make sure we have a HASH here, otherwise filenames might be invalid
91 CString file1;
92 CString nullfile;
93 CString cmd;
95 if(pPath->IsDirectory())
97 git_revnum_t rev2;
98 return SubmoduleDiffNull(pPath,rev1);
101 if(rev1 != GIT_REV_ZERO )
103 TCHAR szTempName[MAX_PATH];
104 GetTempFileName(temppath, pPath->GetBaseFilename(), 0, szTempName);
105 CString temp(szTempName);
106 DeleteFile(szTempName);
107 CreateDirectory(szTempName, NULL);
108 file1.Format(_T("%s\\%s-%s%s"),
109 temp,
110 pPath->GetBaseFilename(),
111 rev1.Left(g_Git.GetShortHASHLength()),
112 pPath->GetFileExtension());
114 g_Git.GetOneFile(rev1,*pPath,file1);
116 else
118 file1=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
121 // preserve FileExtension, needed especially for diffing deleted images (detection on new filename extension)
122 CString tempfile=::GetTempFile() + pPath->GetFileExtension();
123 CStdioFile file(tempfile,CFile::modeReadWrite|CFile::modeCreate );
124 //file.WriteString();
125 file.Close();
126 ::SetFileAttributes(tempfile, FILE_ATTRIBUTE_READONLY);
128 CAppUtils::DiffFlags flags;
130 if(bIsAdd)
131 CAppUtils::StartExtDiff(tempfile,file1,
132 pPath->GetGitPathString(),
133 pPath->GetGitPathString() + _T(":") + rev1.Left(g_Git.GetShortHASHLength())
134 ,flags);
135 else
136 CAppUtils::StartExtDiff(file1,tempfile,
137 pPath->GetGitPathString() + _T(":") + rev1.Left(g_Git.GetShortHASHLength())
138 ,pPath->GetGitPathString(),flags);
140 return 0;
143 int CGitDiff::SubmoduleDiff(CTGitPath * pPath,CTGitPath * /*pPath2*/, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
145 CString oldhash;
146 CString newhash;
147 CString cmd;
148 bool isWorkingCopy = false;
149 if( rev2 == GIT_REV_ZERO || rev1 == GIT_REV_ZERO )
151 oldhash = GIT_REV_ZERO;
152 newhash = GIT_REV_ZERO;
154 CString rev;
155 if( rev2 != GIT_REV_ZERO )
156 rev = rev2;
157 if( rev1 != GIT_REV_ZERO )
158 rev = rev1;
160 isWorkingCopy = true;
162 cmd.Format(_T("git.exe diff %s -- \"%s\""),
163 rev,pPath->GetGitPathString());
165 CString output, err;
166 if (g_Git.Run(cmd, &output, &err, CP_UTF8))
168 CMessageBox::Show(NULL, output + L"\n" + err, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
169 return -1;
171 int start =0;
172 int oldstart = output.Find(_T("-Subproject commit"),start);
173 if(oldstart<0)
175 CMessageBox::Show(NULL,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
176 return -1;
178 oldhash = output.Mid(oldstart+ CString(_T("-Subproject commit")).GetLength()+1,40);
179 start = 0;
180 int newstart = output.Find(_T("+Subproject commit"),start);
181 if(oldstart<0)
183 CMessageBox::Show(NULL,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
184 return -1;
186 newhash = output.Mid(newstart+ CString(_T("+Subproject commit")).GetLength()+1,40);
189 else
191 cmd.Format(_T("git.exe diff-tree -r -z %s %s -- \"%s\""),
192 rev2,rev1,pPath->GetGitPathString());
194 BYTE_VECTOR bytes, errBytes;
195 if(g_Git.Run(cmd, &bytes, &errBytes))
197 CString err;
198 g_Git.StringAppend(&err, &errBytes[0], CP_UTF8);
199 CMessageBox::Show(NULL,err,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
200 return -1;
203 g_Git.StringAppend(&oldhash, &bytes[15], CP_UTF8, 40);
204 g_Git.StringAppend(&newhash, &bytes[15+41], CP_UTF8, 40);
208 CString oldsub;
209 CString newsub;
210 bool oldOK = false, newOK = false;
212 CGit subgit;
213 subgit.m_CurrentDir=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
215 if(pPath->HasAdminDir())
217 int encode=CAppUtils::GetLogOutputEncode(&subgit);
219 if(oldhash != GIT_REV_ZERO)
221 cmd.Format(_T("git log -n1 --pretty=format:\"%%s\" %s"),oldhash);
222 oldOK = !subgit.Run(cmd,&oldsub,encode);
224 if(newsub != GIT_REV_ZERO)
226 cmd.Format(_T("git log -n1 --pretty=format:\"%%s\" %s"),newhash);
227 newOK = !subgit.Run(cmd,&newsub,encode);
231 CSubmoduleDiffDlg submoduleDiffDlg;
232 submoduleDiffDlg.SetDiff(pPath->GetWinPath(), isWorkingCopy, oldhash, oldsub, oldOK, newhash, newsub, newOK);
233 submoduleDiffDlg.DoModal();
235 return 0;
238 int CGitDiff::Diff(CTGitPath * pPath,CTGitPath * pPath2, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
240 CString temppath;
241 GetTempPath(temppath);
243 // make sure we have HASHes here, otherwise filenames might be invalid
244 rev1 = g_Git.GetHash(rev1);
245 rev2 = g_Git.GetHash(rev2);
247 CString file1;
248 CString title1;
249 CString cmd;
251 if(pPath->IsDirectory() || pPath2->IsDirectory())
253 return SubmoduleDiff(pPath,pPath2,rev1,rev2);
256 if(rev1 != GIT_REV_ZERO )
258 TCHAR szTempName[MAX_PATH];
259 GetTempFileName(temppath, pPath->GetBaseFilename(), 0, szTempName);
260 CString temp(szTempName);
261 DeleteFile(szTempName);
262 CreateDirectory(szTempName, NULL);
263 // use original file extension, an external diff tool might need it
264 file1.Format(_T("%s\\%s-%s-right%s"),
265 temp,
266 pPath->GetBaseFilename(),
267 rev1.Left(g_Git.GetShortHASHLength()),
268 pPath->GetFileExtension());
269 title1 = pPath->GetFileOrDirectoryName() + _T(":") + rev1.Left(g_Git.GetShortHASHLength());
270 g_Git.GetOneFile(rev1,*pPath,file1);
271 ::SetFileAttributes(file1, FILE_ATTRIBUTE_READONLY);
273 else
275 file1=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
276 title1.Format( IDS_DIFF_WCNAME, pPath->GetFileOrDirectoryName() );
277 if (!PathFileExists(file1))
279 CString sMsg;
280 sMsg.Format(IDS_PROC_DIFFERROR_FILENOTINWORKINGTREE, file1);
281 if (MessageBox(NULL, sMsg, _T("TortoiseGit"), MB_ICONEXCLAMATION | MB_OKCANCEL) == IDCANCEL)
282 return 1;
283 if (!CCommonAppUtils::FileOpenSave(file1, NULL, IDS_DIFF_WCNAME, IDS_COMMONFILEFILTER, true))
284 return 1;
285 title1.Format(IDS_DIFF_WCNAME, CTGitPath(file1).GetUIFileOrDirectoryName());
289 CString file2;
290 CString title2;
291 if(rev2 != GIT_REV_ZERO)
293 TCHAR szTempName[MAX_PATH];
294 GetTempFileName(temppath, pPath2->GetBaseFilename(), 0, szTempName);
295 CString temp(szTempName);
296 DeleteFile(szTempName);
297 CreateDirectory(szTempName, NULL);
298 CTGitPath fileName = *pPath2;
299 if (rev1 == GIT_REV_ZERO && pPath2->m_Action & CTGitPath::LOGACTIONS_REPLACED)
300 fileName = CTGitPath(pPath2->GetGitOldPathString());
302 // use original file extension, an external diff tool might need it
303 file2.Format(_T("%s\\%s-%s-left%s"),
304 temp,
305 fileName.GetBaseFilename(),
306 rev2.Left(g_Git.GetShortHASHLength()),
307 fileName.GetFileExtension());
308 title2 = fileName.GetFileOrDirectoryName() + _T(":") + rev2.Left(g_Git.GetShortHASHLength());
309 g_Git.GetOneFile(rev2, fileName, file2);
310 ::SetFileAttributes(file2, FILE_ATTRIBUTE_READONLY);
312 else
314 file2=g_Git.m_CurrentDir+_T("\\")+pPath2->GetWinPathString();
315 title2.Format( IDS_DIFF_WCNAME, pPath2->GetFileOrDirectoryName() );
318 if (pPath->m_Action == pPath->LOGACTIONS_ADDED)
320 CGitDiff::DiffNull(pPath, rev1, true);
322 else if (pPath->m_Action == pPath->LOGACTIONS_DELETED)
324 CGitDiff::DiffNull(pPath, rev2, false);
326 else
328 CAppUtils::DiffFlags flags;
329 CAppUtils::StartExtDiff(file2,file1,
330 title2,
331 title1
332 ,flags);
334 return 0;
337 int CGitDiff::DiffCommit(CTGitPath &path, GitRev *r1, GitRev *r2)
339 return DiffCommit(path, path, r1, r2);
342 int CGitDiff::DiffCommit(CTGitPath path1, CTGitPath path2, GitRev *r1, GitRev *r2)
344 if (path1.GetWinPathString().IsEmpty())
346 CFileDiffDlg dlg;
347 dlg.SetDiff(NULL, *r1, *r2);
348 dlg.DoModal();
350 else if (path1.IsDirectory())
352 CFileDiffDlg dlg;
353 dlg.SetDiff(&path1, *r1, *r2);
354 dlg.DoModal();
356 else
358 Diff(&path1, &path2, r1->m_CommitHash.ToString(), r2->m_CommitHash.ToString());
360 return 0;
363 int CGitDiff::DiffCommit(CTGitPath &path, CString r1, CString r2)
365 return DiffCommit(path, path, r1, r2);
369 int CGitDiff::DiffCommit(CTGitPath path1, CTGitPath path2, CString r1, CString r2)
371 if (path1.GetWinPathString().IsEmpty())
373 CFileDiffDlg dlg;
374 dlg.SetDiff(NULL, r1, r2);
375 dlg.DoModal();
377 else if (path1.IsDirectory())
379 CFileDiffDlg dlg;
380 dlg.SetDiff(&path1, r1, r2);
381 dlg.DoModal();
383 else
385 Diff(&path1, &path2, r1, r2);
387 return 0;