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.
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
;
44 cmd
.Format(_T("git.exe ls-tree HEAD -- \"%s\""), pPath
->GetGitPathString());
46 if (g_Git
.Run(cmd
, &output
, &err
, CP_UTF8
))
48 CMessageBox::Show(NULL
, output
+ L
"\n" + err
, _T("TortoiseGit"), MB_OK
|MB_ICONERROR
);
53 start
=output
.Find(_T(' '),start
);
56 start
=output
.Find(_T(' '),start
+1);
58 newhash
=output
.Mid(start
+1, 40);
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();
74 CMessageBox::Show(NULL
,_T("ls-tree output format error"),_T("TortoiseGit"),MB_OK
|MB_ICONERROR
);
78 int CGitDiff::DiffNull(CTGitPath
*pPath
, git_revnum_t rev1
,bool bIsAdd
)
81 GetTempPath(temppath
);
82 rev1
= g_Git
.GetHash(rev1
); // make sure we have a HASH here, otherwise filenames might be invalid
87 if(pPath
->IsDirectory())
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"),
102 pPath
->GetBaseFilename(),
103 rev1
.Left(g_Git
.GetShortHASHLength()),
104 pPath
->GetFileExtension());
106 g_Git
.GetOneFile(rev1
,*pPath
,file1
);
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();
118 ::SetFileAttributes(tempfile
, FILE_ATTRIBUTE_READONLY
);
120 CAppUtils::DiffFlags flags
;
123 CAppUtils::StartExtDiff(tempfile
,file1
,
124 pPath
->GetGitPathString(),
125 pPath
->GetGitPathString() + _T(":") + rev1
.Left(g_Git
.GetShortHASHLength())
128 CAppUtils::StartExtDiff(file1
,tempfile
,
129 pPath
->GetGitPathString() + _T(":") + rev1
.Left(g_Git
.GetShortHASHLength())
130 ,pPath
->GetGitPathString(),flags
);
135 int CGitDiff::SubmoduleDiff(CTGitPath
* pPath
,CTGitPath
* /*pPath2*/, git_revnum_t rev1
, git_revnum_t rev2
, bool /*blame*/, bool /*unified*/)
140 bool isWorkingCopy
= false;
141 if( rev2
== GIT_REV_ZERO
|| rev1
== GIT_REV_ZERO
)
143 oldhash
= GIT_REV_ZERO
;
144 newhash
= GIT_REV_ZERO
;
147 if( rev2
!= GIT_REV_ZERO
)
149 if( rev1
!= GIT_REV_ZERO
)
152 isWorkingCopy
= true;
154 cmd
.Format(_T("git.exe diff %s -- \"%s\""),
155 rev
,pPath
->GetGitPathString());
158 if (g_Git
.Run(cmd
, &output
, &err
, CP_UTF8
))
160 CMessageBox::Show(NULL
, output
+ L
"\n" + err
, _T("TortoiseGit"), MB_OK
|MB_ICONERROR
);
164 int oldstart
= output
.Find(_T("-Subproject commit"),start
);
167 CMessageBox::Show(NULL
,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK
|MB_ICONERROR
);
170 oldhash
= output
.Mid(oldstart
+ CString(_T("-Subproject commit")).GetLength()+1,40);
172 int newstart
= output
.Find(_T("+Subproject commit"),start
);
175 CMessageBox::Show(NULL
,_T("Subproject Diff Format error") ,_T("TortoiseGit"),MB_OK
|MB_ICONERROR
);
178 newhash
= output
.Mid(newstart
+ CString(_T("+Subproject commit")).GetLength()+1,40);
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
))
190 g_Git
.StringAppend(&err
, &errBytes
[0], CP_UTF8
);
191 CMessageBox::Show(NULL
,err
,_T("TortoiseGit"),MB_OK
|MB_ICONERROR
);
195 g_Git
.StringAppend(&oldhash
, &bytes
[15], CP_UTF8
, 40);
196 g_Git
.StringAppend(&newhash
, &bytes
[15+41], CP_UTF8
, 40);
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();
229 int CGitDiff::Diff(CTGitPath
* pPath
,CTGitPath
* pPath2
, git_revnum_t rev1
, git_revnum_t rev2
, bool /*blame*/, bool /*unified*/)
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
);
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"),
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
);
266 file1
=g_Git
.m_CurrentDir
+_T("\\")+pPath
->GetWinPathString();
267 title1
.Format( IDS_DIFF_WCNAME
, pPath
->GetFileOrDirectoryName() );
268 if (!PathFileExists(file1
))
271 sMsg
.Format(IDS_PROC_DIFFERROR_FILENOTINWORKINGTREE
, file1
);
272 if (MessageBox(NULL
, sMsg
, _T("TortoiseGit"), MB_ICONEXCLAMATION
| MB_OKCANCEL
) == IDCANCEL
)
274 if (!CCommonAppUtils::FileOpenSave(file1
, NULL
, IDS_DIFF_WCNAME
, IDS_COMMONFILEFILTER
, true))
276 title1
.Format(IDS_DIFF_WCNAME
, CTGitPath(file1
).GetUIFileOrDirectoryName());
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"),
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
);
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);
319 CAppUtils::DiffFlags flags
;
320 CAppUtils::StartExtDiff(file2
,file1
,
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())
338 dlg
.SetDiff(NULL
, *r1
, *r2
);
341 else if (path1
.IsDirectory())
344 dlg
.SetDiff(&path1
, *r1
, *r2
);
349 Diff(&path1
, &path2
, r1
->m_CommitHash
.ToString(), r2
->m_CommitHash
.ToString());
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())
365 dlg
.SetDiff(NULL
, r1
, r2
);
368 else if (path1
.IsDirectory())
371 dlg
.SetDiff(&path1
, r1
, r2
);
376 Diff(&path1
, &path2
, r1
, r2
);