TortoiseSVN -> TortoiseGit
[TortoiseGit.git] / src / TortoiseProc / GitDiff.cpp
blob3c25bca8fa8d2393fa4badafc998cc1ff5435c2a
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,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;
64 if(g_Git.Run(cmd,&output,CP_ACP))
66 CMessageBox::Show(NULL,output,_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();
142 CAppUtils::DiffFlags flags;
144 if(bIsAdd)
145 CAppUtils::StartExtDiff(tempfile,file1,
146 pPath->GetGitPathString(),
147 pPath->GetGitPathString()+_T(":")+rev1.Left(6)
148 ,flags);
149 else
150 CAppUtils::StartExtDiff(file1,tempfile,
151 pPath->GetGitPathString()+_T(":")+rev1.Left(6)
152 ,pPath->GetGitPathString(),flags);
154 return 0;
157 int CGitDiff::SubmoduleDiff(CTGitPath * pPath,CTGitPath * /*pPath2*/, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
159 CString oldhash;
160 CString newhash;
161 CString cmd,err;
162 CString workingcopy;
164 if( rev2 == GIT_REV_ZERO || rev1 == GIT_REV_ZERO )
166 oldhash = GIT_REV_ZERO;
167 newhash = GIT_REV_ZERO;
169 CString rev;
170 if( rev2 != GIT_REV_ZERO )
171 rev = rev2;
172 if( rev1 != GIT_REV_ZERO )
173 rev = rev1;
175 workingcopy = _T(" (Work Copy)");
177 cmd.Format(_T("git.exe diff %s -- \"%s\""),
178 rev,pPath->GetGitPathString());
180 CString output;
181 if(g_Git.Run(cmd,&output,CP_ACP))
183 CMessageBox::Show(NULL,output,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
184 return -1;
186 int start =0;
187 int oldstart = output.Find(_T("-Subproject commit"),start);
188 if(oldstart<0)
190 CMessageBox::Show(NULL,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
191 return -1;
193 oldhash = output.Mid(oldstart+ CString(_T("-Subproject commit")).GetLength()+1,40);
194 start = 0;
195 int newstart = output.Find(_T("+Subproject commit"),start);
196 if(oldstart<0)
198 CMessageBox::Show(NULL,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
199 return -1;
201 newhash = output.Mid(newstart+ CString(_T("+Subproject commit")).GetLength()+1,40);
204 else
206 cmd.Format(_T("git.exe diff-tree -r -z %s %s -- \"%s\""),
207 rev2,rev1,pPath->GetGitPathString());
209 BYTE_VECTOR bytes;
210 if(g_Git.Run(cmd,&bytes))
212 CString err;
213 g_Git.StringAppend(&err,&bytes[0],CP_ACP);
214 CMessageBox::Show(NULL,err,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
215 return -1;
218 g_Git.StringAppend(&oldhash,&bytes[15],CP_ACP,40);
219 g_Git.StringAppend(&newhash,&bytes[15+41],CP_ACP,40);
223 CString oldsub;
224 CString newsub;
226 CGit subgit;
227 subgit.m_CurrentDir=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
229 if(pPath->HasAdminDir())
231 int encode=CAppUtils::GetLogOutputEncode(&subgit);
233 if(oldhash != GIT_REV_ZERO)
235 cmd.Format(_T("git log -n1 --pretty=format:\"%%s\" %s"),oldhash);
236 subgit.Run(cmd,&oldsub,encode);
238 if(newsub != GIT_REV_ZERO)
240 cmd.Format(_T("git log -n1 --pretty=format:\"%%s\" %s"),newhash);
241 subgit.Run(cmd,&newsub,encode);
244 CString msg;
245 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"),
246 pPath->GetWinPath(),
247 oldhash,
248 oldsub ,
249 workingcopy,
250 newhash,
251 newsub);
252 CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);
254 return 0;
257 int CGitDiff::Diff(CTGitPath * pPath,CTGitPath * pPath2, git_revnum_t rev1, git_revnum_t rev2, bool /*blame*/, bool /*unified*/)
259 CString temppath;
260 GetTempPath(temppath);
261 Parser(rev1);
262 Parser(rev2);
263 CString file1;
264 CString title1;
265 CString cmd;
267 if(pPath->IsDirectory() || pPath2->IsDirectory())
269 return SubmoduleDiff(pPath,pPath2,rev1,rev2);
272 if(rev1 != GIT_REV_ZERO )
274 TCHAR szTempName[MAX_PATH];
275 GetTempFileName(temppath, pPath->GetBaseFilename(), 0, szTempName);
276 CString temp(szTempName);
277 DeleteFile(szTempName);
278 CreateDirectory(szTempName, NULL);
279 // use original file extension, an external diff tool might need it
280 file1.Format(_T("%s\\%s-%s-right%s"),
281 temp,
282 pPath->GetBaseFilename(),
283 rev1.Left(6),
284 pPath->GetFileExtension());
285 title1 = pPath->GetFileOrDirectoryName()+_T(":")+rev1.Left(6);
286 g_Git.GetOneFile(rev1,*pPath,file1);
289 else
291 file1=g_Git.m_CurrentDir+_T("\\")+pPath->GetWinPathString();
292 title1.Format( IDS_DIFF_WCNAME, pPath->GetFileOrDirectoryName() );
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 // use original file extension, an external diff tool might need it
305 file2.Format(_T("%s\\%s-%s-left%s"),
306 temp,
307 pPath2->GetBaseFilename(),
308 rev2.Left(6),
309 pPath2->GetFileExtension());
310 title2 = pPath2->GetFileOrDirectoryName()+_T(":")+rev2.Left(6);
311 g_Git.GetOneFile(rev2,*pPath2,file2);
313 else
315 file2=g_Git.m_CurrentDir+_T("\\")+pPath2->GetWinPathString();
316 title2.Format( IDS_DIFF_WCNAME, pPath2->GetFileOrDirectoryName() );
319 if (pPath->m_Action == pPath->LOGACTIONS_ADDED)
321 CGitDiff::DiffNull(pPath, rev1, true);
323 else if (pPath->m_Action == pPath->LOGACTIONS_DELETED)
325 CGitDiff::DiffNull(pPath, rev2, false);
327 else
329 CAppUtils::DiffFlags flags;
330 CAppUtils::StartExtDiff(file2,file1,
331 title2,
332 title1
333 ,flags);
335 return 0;
338 int CGitDiff::DiffCommit(CTGitPath &path, GitRev *r1, GitRev *r2)
340 if( path.GetWinPathString().IsEmpty() || path.IsDirectory() )
342 CFileDiffDlg dlg;
343 dlg.SetDiff(NULL,*r1,*r2);
344 dlg.DoModal();
346 else
348 Diff(&path,&path,r1->m_CommitHash.ToString(),r2->m_CommitHash.ToString());
350 return 0;
353 int CGitDiff::DiffCommit(CTGitPath &path, CString r1, CString r2)
355 if( path.GetWinPathString().IsEmpty() || path.IsDirectory() )
357 CFileDiffDlg dlg;
358 dlg.SetDiff(NULL,r1,r2);
359 dlg.DoModal();
361 else
363 Diff(&path,&path,r1,r2);
365 return 0;