added new log filter (for subject)
[TortoiseGit.git] / src / TortoiseProc / GitDiff.cpp
blob1f7f94e835ac34eae669ad35cb417658da776586
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
4 // Copyright (C) 2008-2011 - 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"
28 CGitDiff::CGitDiff(void)
32 CGitDiff::~CGitDiff(void)
35 int CGitDiff::Parser(git_revnum_t &rev)
37 if(rev == GIT_REV_ZERO)
38 return 0;
39 if(rev.GetLength() > 40)
41 CString cmd;
42 cmd.Format(_T("git.exe rev-parse %s"),rev);
43 CString output;
44 if (!g_Git.Run(cmd, &output, NULL, CP_UTF8))
46 //int start=output.Find(_T('\n'));
47 rev=output.Left(40);
50 return 0;
53 int CGitDiff::SubmoduleDiffNull(CTGitPath *pPath, git_revnum_t &/*rev1*/)
55 CString oldhash = GIT_REV_ZERO;
56 CString oldsub ;
57 CString newsub;
58 CString newhash;
59 CString workingcopy;
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_ACP))
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 CString msg;
86 msg.Format(_T("Submodule <b>%s</b> Change\r\n\r\n<b>From:</b> %s\r\n\t%s\r\n\r\n<b>To%s:</b> %s\r\n\t\t%s"),
87 pPath->GetWinPath(),
88 oldhash,
89 oldsub ,
90 workingcopy,
91 newhash,
92 newsub);
93 CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);
94 return 0;
97 CMessageBox::Show(NULL,_T("ls-tree output format error"),_T("TortoiseGit"),MB_OK|MB_ICONERROR);
98 return -1;
101 int CGitDiff::DiffNull(CTGitPath *pPath, git_revnum_t rev1,bool bIsAdd)
103 CString temppath;
104 GetTempPath(temppath);
105 Parser(rev1);
106 CString file1;
107 CString nullfile;
108 CString cmd;
110 if(pPath->IsDirectory())
112 git_revnum_t rev2;
113 return SubmoduleDiffNull(pPath,rev1);
116 if(rev1 != GIT_REV_ZERO )
118 TCHAR szTempName[MAX_PATH];
119 GetTempFileName(temppath, pPath->GetBaseFilename(), 0, szTempName);
120 CString temp(szTempName);
121 DeleteFile(szTempName);
122 CreateDirectory(szTempName, NULL);
123 file1.Format(_T("%s\\%s-%s%s"),
124 temp,
125 pPath->GetBaseFilename(),
126 rev1.Left(6),
127 pPath->GetFileExtension());
129 g_Git.GetOneFile(rev1,*pPath,file1);
131 else
133 file1=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
136 // preserve FileExtension, needed especially for diffing deleted images (detection on new filename extension)
137 CString tempfile=::GetTempFile() + pPath->GetFileExtension();
138 CStdioFile file(tempfile,CFile::modeReadWrite|CFile::modeCreate );
139 //file.WriteString();
140 file.Close();
141 ::SetFileAttributes(tempfile, FILE_ATTRIBUTE_READONLY);
143 CAppUtils::DiffFlags flags;
145 if(bIsAdd)
146 CAppUtils::StartExtDiff(tempfile,file1,
147 pPath->GetGitPathString(),
148 pPath->GetGitPathString()+_T(":")+rev1.Left(6)
149 ,flags);
150 else
151 CAppUtils::StartExtDiff(file1,tempfile,
152 pPath->GetGitPathString()+_T(":")+rev1.Left(6)
153 ,pPath->GetGitPathString(),flags);
155 return 0;
158 int CGitDiff::SubmoduleDiff(CTGitPath * pPath,CTGitPath * /*pPath2*/, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
160 CString oldhash;
161 CString newhash;
162 CString cmd;
163 CString workingcopy;
165 if( rev2 == GIT_REV_ZERO || rev1 == GIT_REV_ZERO )
167 oldhash = GIT_REV_ZERO;
168 newhash = GIT_REV_ZERO;
170 CString rev;
171 if( rev2 != GIT_REV_ZERO )
172 rev = rev2;
173 if( rev1 != GIT_REV_ZERO )
174 rev = rev1;
176 workingcopy = _T(" (Work Copy)");
178 cmd.Format(_T("git.exe diff %s -- \"%s\""),
179 rev,pPath->GetGitPathString());
181 CString output, err;
182 if (g_Git.Run(cmd, &output, &err, CP_ACP))
184 CMessageBox::Show(NULL, output + L"\n" + err, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
185 return -1;
187 int start =0;
188 int oldstart = output.Find(_T("-Subproject commit"),start);
189 if(oldstart<0)
191 CMessageBox::Show(NULL,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
192 return -1;
194 oldhash = output.Mid(oldstart+ CString(_T("-Subproject commit")).GetLength()+1,40);
195 start = 0;
196 int newstart = output.Find(_T("+Subproject commit"),start);
197 if(oldstart<0)
199 CMessageBox::Show(NULL,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
200 return -1;
202 newhash = output.Mid(newstart+ CString(_T("+Subproject commit")).GetLength()+1,40);
205 else
207 cmd.Format(_T("git.exe diff-tree -r -z %s %s -- \"%s\""),
208 rev2,rev1,pPath->GetGitPathString());
210 BYTE_VECTOR bytes, errBytes;
211 if(g_Git.Run(cmd, &bytes, &errBytes))
213 CString err;
214 g_Git.StringAppend(&err,&errBytes[0],CP_ACP);
215 CMessageBox::Show(NULL,err,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
216 return -1;
219 g_Git.StringAppend(&oldhash,&bytes[15],CP_ACP,40);
220 g_Git.StringAppend(&newhash,&bytes[15+41],CP_ACP,40);
224 CString oldsub;
225 CString newsub;
227 CGit subgit;
228 subgit.m_CurrentDir=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
230 if(pPath->HasAdminDir())
232 int encode=CAppUtils::GetLogOutputEncode(&subgit);
234 if(oldhash != GIT_REV_ZERO)
236 cmd.Format(_T("git log -n1 --pretty=format:\"%%s\" %s"),oldhash);
237 subgit.Run(cmd,&oldsub,encode);
239 if(newsub != GIT_REV_ZERO)
241 cmd.Format(_T("git log -n1 --pretty=format:\"%%s\" %s"),newhash);
242 subgit.Run(cmd,&newsub,encode);
245 CString msg;
246 msg.Format(_T("Submodule <b>%s</b> Change\r\n\r\n<b>From:</b> %s\r\n\t%s\r\n\r\n<b>To%s:</b> %s\r\n\t\t%s"),
247 pPath->GetWinPath(),
248 oldhash,
249 oldsub ,
250 workingcopy,
251 newhash,
252 newsub);
253 CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);
255 return 0;
258 int CGitDiff::Diff(CTGitPath * pPath,CTGitPath * pPath2, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
260 CString temppath;
261 GetTempPath(temppath);
262 Parser(rev1);
263 Parser(rev2);
264 CString file1;
265 CString title1;
266 CString cmd;
268 if(pPath->IsDirectory() || pPath2->IsDirectory())
270 return SubmoduleDiff(pPath,pPath2,rev1,rev2);
273 if(rev1 != GIT_REV_ZERO )
275 TCHAR szTempName[MAX_PATH];
276 GetTempFileName(temppath, pPath->GetBaseFilename(), 0, szTempName);
277 CString temp(szTempName);
278 DeleteFile(szTempName);
279 CreateDirectory(szTempName, NULL);
280 // use original file extension, an external diff tool might need it
281 file1.Format(_T("%s\\%s-%s-right%s"),
282 temp,
283 pPath->GetBaseFilename(),
284 rev1.Left(6),
285 pPath->GetFileExtension());
286 title1 = pPath->GetFileOrDirectoryName()+_T(":")+rev1.Left(6);
287 g_Git.GetOneFile(rev1,*pPath,file1);
288 ::SetFileAttributes(file1, FILE_ATTRIBUTE_READONLY);
290 else
292 file1=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
293 title1.Format( IDS_DIFF_WCNAME, pPath->GetFileOrDirectoryName() );
296 CString file2;
297 CString title2;
298 if(rev2 != GIT_REV_ZERO)
300 TCHAR szTempName[MAX_PATH];
301 GetTempFileName(temppath, pPath2->GetBaseFilename(), 0, szTempName);
302 CString temp(szTempName);
303 DeleteFile(szTempName);
304 CreateDirectory(szTempName, NULL);
305 CTGitPath fileName = *pPath2;
306 if (rev1 == GIT_REV_ZERO && pPath2->m_Action & CTGitPath::LOGACTIONS_REPLACED)
307 fileName = CTGitPath(pPath2->GetGitOldPathString());
309 // use original file extension, an external diff tool might need it
310 file2.Format(_T("%s\\%s-%s-left%s"),
311 temp,
312 fileName.GetBaseFilename(),
313 rev2.Left(6),
314 fileName.GetFileExtension());
315 title2 = fileName.GetFileOrDirectoryName() + _T(":") + rev2.Left(6);
316 g_Git.GetOneFile(rev2, fileName, file2);
317 ::SetFileAttributes(file2, FILE_ATTRIBUTE_READONLY);
319 else
321 file2=g_Git.m_CurrentDir+_T("\\")+pPath2->GetWinPathString();
322 title2.Format( IDS_DIFF_WCNAME, pPath2->GetFileOrDirectoryName() );
325 if (pPath->m_Action == pPath->LOGACTIONS_ADDED)
327 CGitDiff::DiffNull(pPath, rev1, true);
329 else if (pPath->m_Action == pPath->LOGACTIONS_DELETED)
331 CGitDiff::DiffNull(pPath, rev2, false);
333 else
335 CAppUtils::DiffFlags flags;
336 CAppUtils::StartExtDiff(file2,file1,
337 title2,
338 title1
339 ,flags);
341 return 0;
344 int CGitDiff::DiffCommit(CTGitPath &path, GitRev *r1, GitRev *r2)
346 if (path.GetWinPathString().IsEmpty())
348 CFileDiffDlg dlg;
349 dlg.SetDiff(NULL,*r1,*r2);
350 dlg.DoModal();
352 else if (path.IsDirectory())
354 CFileDiffDlg dlg;
355 dlg.SetDiff(&path,*r1,*r2);
356 dlg.DoModal();
358 else
360 Diff(&path,&path,r1->m_CommitHash.ToString(),r2->m_CommitHash.ToString());
362 return 0;
365 int CGitDiff::DiffCommit(CTGitPath &path, CString r1, CString r2)
367 if (path.GetWinPathString().IsEmpty())
369 CFileDiffDlg dlg;
370 dlg.SetDiff(NULL,r1,r2);
371 dlg.DoModal();
373 else if (path.IsDirectory())
375 CFileDiffDlg dlg;
376 dlg.SetDiff(&path,r1,r2);
377 dlg.DoModal();
379 else
381 Diff(&path,&path,r1,r2);
383 return 0;