DeleteRemoteTagDlg: Allow to delete multiple tags at once
[TortoiseGit.git] / src / TortoiseProc / GitDiff.cpp
blob81a6c4f77fbc76414a76aebf5f5701f53b7493f4
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 cmd.Format(_T("git.exe ls-tree HEAD -- \"%s\""), pPath->GetGitPathString());
45 CString output, err;
46 if (g_Git.Run(cmd, &output, &err, CP_UTF8))
48 CMessageBox::Show(NULL, output + L"\n" + err, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
49 return -1;
52 int start=0;
53 start=output.Find(_T(' '),start);
54 if(start>0)
56 start=output.Find(_T(' '),start+1);
57 if(start>0)
58 newhash=output.Mid(start+1, 40);
60 CGit subgit;
61 subgit.m_CurrentDir=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
62 int encode=CAppUtils::GetLogOutputEncode(&subgit);
64 cmd.Format(_T("git.exe log -n1 --pretty=format:\"%%s\" %s"),newhash);
65 subgit.Run(cmd,&newsub,encode);
67 CSubmoduleDiffDlg submoduleDiffDlg;
68 submoduleDiffDlg.SetDiff(pPath->GetWinPath(), false, oldhash, oldsub, newhash, newsub);
69 submoduleDiffDlg.DoModal();
71 return 0;
74 CMessageBox::Show(NULL,_T("ls-tree output format error"),_T("TortoiseGit"),MB_OK|MB_ICONERROR);
75 return -1;
78 int CGitDiff::DiffNull(CTGitPath *pPath, git_revnum_t rev1,bool bIsAdd)
80 CString temppath;
81 GetTempPath(temppath);
82 rev1 = g_Git.GetHash(rev1); // make sure we have a HASH here, otherwise filenames might be invalid
83 CString file1;
84 CString nullfile;
85 CString cmd;
87 if(pPath->IsDirectory())
89 git_revnum_t rev2;
90 return SubmoduleDiffNull(pPath,rev1);
93 if(rev1 != GIT_REV_ZERO )
95 TCHAR szTempName[MAX_PATH];
96 GetTempFileName(temppath, pPath->GetBaseFilename(), 0, szTempName);
97 CString temp(szTempName);
98 DeleteFile(szTempName);
99 CreateDirectory(szTempName, NULL);
100 file1.Format(_T("%s\\%s-%s%s"),
101 temp,
102 pPath->GetBaseFilename(),
103 rev1.Left(g_Git.GetShortHASHLength()),
104 pPath->GetFileExtension());
106 g_Git.GetOneFile(rev1,*pPath,file1);
108 else
110 file1=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
113 // preserve FileExtension, needed especially for diffing deleted images (detection on new filename extension)
114 CString tempfile=::GetTempFile() + pPath->GetFileExtension();
115 CStdioFile file(tempfile,CFile::modeReadWrite|CFile::modeCreate );
116 //file.WriteString();
117 file.Close();
118 ::SetFileAttributes(tempfile, FILE_ATTRIBUTE_READONLY);
120 CAppUtils::DiffFlags flags;
122 if(bIsAdd)
123 CAppUtils::StartExtDiff(tempfile,file1,
124 pPath->GetGitPathString(),
125 pPath->GetGitPathString() + _T(":") + rev1.Left(g_Git.GetShortHASHLength())
126 ,flags);
127 else
128 CAppUtils::StartExtDiff(file1,tempfile,
129 pPath->GetGitPathString() + _T(":") + rev1.Left(g_Git.GetShortHASHLength())
130 ,pPath->GetGitPathString(),flags);
132 return 0;
135 int CGitDiff::SubmoduleDiff(CTGitPath * pPath,CTGitPath * /*pPath2*/, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
137 CString oldhash;
138 CString newhash;
139 CString cmd;
140 bool isWorkingCopy = false;
141 if( rev2 == GIT_REV_ZERO || rev1 == GIT_REV_ZERO )
143 oldhash = GIT_REV_ZERO;
144 newhash = GIT_REV_ZERO;
146 CString rev;
147 if( rev2 != GIT_REV_ZERO )
148 rev = rev2;
149 if( rev1 != GIT_REV_ZERO )
150 rev = rev1;
152 isWorkingCopy = true;
154 cmd.Format(_T("git.exe diff %s -- \"%s\""),
155 rev,pPath->GetGitPathString());
157 CString output, err;
158 if (g_Git.Run(cmd, &output, &err, CP_UTF8))
160 CMessageBox::Show(NULL, output + L"\n" + err, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
161 return -1;
163 int start =0;
164 int oldstart = output.Find(_T("-Subproject commit"),start);
165 if(oldstart<0)
167 CMessageBox::Show(NULL,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
168 return -1;
170 oldhash = output.Mid(oldstart+ CString(_T("-Subproject commit")).GetLength()+1,40);
171 start = 0;
172 int newstart = 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 newhash = output.Mid(newstart+ CString(_T("+Subproject commit")).GetLength()+1,40);
181 else
183 cmd.Format(_T("git.exe diff-tree -r -z %s %s -- \"%s\""),
184 rev2,rev1,pPath->GetGitPathString());
186 BYTE_VECTOR bytes, errBytes;
187 if(g_Git.Run(cmd, &bytes, &errBytes))
189 CString err;
190 g_Git.StringAppend(&err, &errBytes[0], CP_UTF8);
191 CMessageBox::Show(NULL,err,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
192 return -1;
195 g_Git.StringAppend(&oldhash, &bytes[15], CP_UTF8, 40);
196 g_Git.StringAppend(&newhash, &bytes[15+41], CP_UTF8, 40);
200 CString oldsub;
201 CString newsub;
203 CGit subgit;
204 subgit.m_CurrentDir=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
206 if(pPath->HasAdminDir())
208 int encode=CAppUtils::GetLogOutputEncode(&subgit);
210 if(oldhash != GIT_REV_ZERO)
212 cmd.Format(_T("git log -n1 --pretty=format:\"%%s\" %s"),oldhash);
213 subgit.Run(cmd,&oldsub,encode);
215 if(newsub != GIT_REV_ZERO)
217 cmd.Format(_T("git log -n1 --pretty=format:\"%%s\" %s"),newhash);
218 subgit.Run(cmd,&newsub,encode);
222 CSubmoduleDiffDlg submoduleDiffDlg;
223 submoduleDiffDlg.SetDiff(pPath->GetWinPath(), isWorkingCopy, oldhash, oldsub, newhash, newsub);
224 submoduleDiffDlg.DoModal();
226 return 0;
229 int CGitDiff::Diff(CTGitPath * pPath,CTGitPath * pPath2, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
231 CString temppath;
232 GetTempPath(temppath);
234 // make sure we have HASHes here, otherwise filenames might be invalid
235 rev1 = g_Git.GetHash(rev1);
236 rev2 = g_Git.GetHash(rev2);
238 CString file1;
239 CString title1;
240 CString cmd;
242 if(pPath->IsDirectory() || pPath2->IsDirectory())
244 return SubmoduleDiff(pPath,pPath2,rev1,rev2);
247 if(rev1 != GIT_REV_ZERO )
249 TCHAR szTempName[MAX_PATH];
250 GetTempFileName(temppath, pPath->GetBaseFilename(), 0, szTempName);
251 CString temp(szTempName);
252 DeleteFile(szTempName);
253 CreateDirectory(szTempName, NULL);
254 // use original file extension, an external diff tool might need it
255 file1.Format(_T("%s\\%s-%s-right%s"),
256 temp,
257 pPath->GetBaseFilename(),
258 rev1.Left(g_Git.GetShortHASHLength()),
259 pPath->GetFileExtension());
260 title1 = pPath->GetFileOrDirectoryName() + _T(":") + rev1.Left(g_Git.GetShortHASHLength());
261 g_Git.GetOneFile(rev1,*pPath,file1);
262 ::SetFileAttributes(file1, FILE_ATTRIBUTE_READONLY);
264 else
266 file1=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
267 title1.Format( IDS_DIFF_WCNAME, pPath->GetFileOrDirectoryName() );
268 if (!PathFileExists(file1))
270 CString sMsg;
271 sMsg.Format(IDS_PROC_DIFFERROR_FILENOTINWORKINGTREE, file1);
272 if (MessageBox(NULL, sMsg, _T("TortoiseGit"), MB_ICONEXCLAMATION | MB_OKCANCEL) == IDCANCEL)
273 return 1;
274 if (!CCommonAppUtils::FileOpenSave(file1, NULL, IDS_DIFF_WCNAME, IDS_COMMONFILEFILTER, true))
275 return 1;
276 title1.Format(IDS_DIFF_WCNAME, CTGitPath(file1).GetUIFileOrDirectoryName());
280 CString file2;
281 CString title2;
282 if(rev2 != GIT_REV_ZERO)
284 TCHAR szTempName[MAX_PATH];
285 GetTempFileName(temppath, pPath2->GetBaseFilename(), 0, szTempName);
286 CString temp(szTempName);
287 DeleteFile(szTempName);
288 CreateDirectory(szTempName, NULL);
289 CTGitPath fileName = *pPath2;
290 if (rev1 == GIT_REV_ZERO && pPath2->m_Action & CTGitPath::LOGACTIONS_REPLACED)
291 fileName = CTGitPath(pPath2->GetGitOldPathString());
293 // use original file extension, an external diff tool might need it
294 file2.Format(_T("%s\\%s-%s-left%s"),
295 temp,
296 fileName.GetBaseFilename(),
297 rev2.Left(g_Git.GetShortHASHLength()),
298 fileName.GetFileExtension());
299 title2 = fileName.GetFileOrDirectoryName() + _T(":") + rev2.Left(g_Git.GetShortHASHLength());
300 g_Git.GetOneFile(rev2, fileName, file2);
301 ::SetFileAttributes(file2, FILE_ATTRIBUTE_READONLY);
303 else
305 file2=g_Git.m_CurrentDir+_T("\\")+pPath2->GetWinPathString();
306 title2.Format( IDS_DIFF_WCNAME, pPath2->GetFileOrDirectoryName() );
309 if (pPath->m_Action == pPath->LOGACTIONS_ADDED)
311 CGitDiff::DiffNull(pPath, rev1, true);
313 else if (pPath->m_Action == pPath->LOGACTIONS_DELETED)
315 CGitDiff::DiffNull(pPath, rev2, false);
317 else
319 CAppUtils::DiffFlags flags;
320 CAppUtils::StartExtDiff(file2,file1,
321 title2,
322 title1
323 ,flags);
325 return 0;
328 int CGitDiff::DiffCommit(CTGitPath &path, GitRev *r1, GitRev *r2)
330 return DiffCommit(path, path, r1, r2);
333 int CGitDiff::DiffCommit(CTGitPath path1, CTGitPath path2, GitRev *r1, GitRev *r2)
335 if (path1.GetWinPathString().IsEmpty())
337 CFileDiffDlg dlg;
338 dlg.SetDiff(NULL, *r1, *r2);
339 dlg.DoModal();
341 else if (path1.IsDirectory())
343 CFileDiffDlg dlg;
344 dlg.SetDiff(&path1, *r1, *r2);
345 dlg.DoModal();
347 else
349 Diff(&path1, &path2, r1->m_CommitHash.ToString(), r2->m_CommitHash.ToString());
351 return 0;
354 int CGitDiff::DiffCommit(CTGitPath &path, CString r1, CString r2)
356 return DiffCommit(path, path, r1, r2);
360 int CGitDiff::DiffCommit(CTGitPath path1, CTGitPath path2, CString r1, CString r2)
362 if (path1.GetWinPathString().IsEmpty())
364 CFileDiffDlg dlg;
365 dlg.SetDiff(NULL, r1, r2);
366 dlg.DoModal();
368 else if (path1.IsDirectory())
370 CFileDiffDlg dlg;
371 dlg.SetDiff(&path1, r1, r2);
372 dlg.DoModal();
374 else
376 Diff(&path1, &path2, r1, r2);
378 return 0;