1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2012 - TortoiseGit
4 // Copyright (C) 2011-2012 Sven Strickroth <email@cs-ware.de>
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.
22 #include "GitBlameLogList.h"
24 #include "TortoiseGitBlameDoc.h"
25 #include "TortoiseGitBlameView.h"
27 #include "CommonAppUtils.h"
29 IMPLEMENT_DYNAMIC(CGitBlameLogList
, CHintListCtrl
)
31 void CGitBlameLogList::hideUnimplementedCommands()
34 GetContextMenuBit(ID_COMPAREWITHPREVIOUS
) |
35 GetContextMenuBit(ID_COPYCLIPBOARD
) |
36 GetContextMenuBit(ID_COPYHASH
) |
37 GetContextMenuBit(ID_EXPORT
) |
38 GetContextMenuBit(ID_CREATE_BRANCH
) |
39 GetContextMenuBit(ID_CREATE_TAG
) |
40 GetContextMenuBit(ID_SWITCHTOREV
)
42 m_ContextMenuMask
|= GetContextMenuBit(ID_LOG
) | GetContextMenuBit(ID_BLAME
) | GetContextMenuBit(ID_REPOBROWSE
);
45 void CGitBlameLogList::ContextMenuAction(int cmd
,int FirstSelect
, int LastSelect
,CMenu
* menu
)
47 POSITION pos
= GetFirstSelectedItemPosition();
48 int indexNext
= GetNextSelectedItem(pos
);
54 GitRev
* pSelLogEntry
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(indexNext
));
56 bool bOpenWith
= false;
58 procCmd
+= _T("/path:\"");
59 procCmd
+= ((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName();
61 procCmd
+= _T(" /rev:")+this->m_logEntries
.GetGitRevAt(indexNext
).m_CommitHash
.ToString();
63 procCmd
+= _T(" /command:");
68 procCmd
+= _T("diff /udiff");
74 CString tempfile
=GetTempFile();
76 GitRev
* r1
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(FirstSelect
));
77 GitRev
* r2
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(LastSelect
));
78 cmd
.Format(_T("git.exe diff-tree -r -p --stat %s %s"),r1
->m_CommitHash
,r2
->m_CommitHash
);
79 g_Git
.RunLogFile(cmd
,tempfile
);
80 CAppUtils::StartUnifiedDiffViewer(tempfile
, r1
->m_CommitHash
.Left(g_Git
.GetShortHASHLength()) + _T(":") + r2
->m_CommitHash
.Left(g_Git
.GetShortHASHLength()));
88 GitRev
* r1
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(FirstSelect
));
89 GitRev
* r2
= reinterpret_cast<GitRev
*>(m_arShownList
.GetAt(LastSelect
));
91 dlg
.SetDiff(NULL
,*r1
,*r2
);
100 GitRev
* r1
= &m_wcRev
;
101 GitRev
* r2
= pSelLogEntry
;
103 dlg
.SetDiff(NULL
,*r1
,*r2
);
106 //user clicked on the menu item "compare with working copy"
109 // GitDiff diff(this, m_hWnd, true);
110 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
111 // diff.SetHEADPeg(m_LogRevision);
112 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
115 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
119 procCmd
+=CString(_T("diff \rev1:"))+CString(GIT_REV_ZERO
)+CString(_T(" \rev2:"))+this->m_logEntries
.GetGitRevAt(indexNext
).m_CommitHash
.ToString();
122 case ID_COMPAREWITHPREVIOUS
:
123 if (indexNext
+ 1 < m_logEntries
.size()) // cannot diff previous revision in first revision
125 procCmd
+=CString(_T("diff /startrev:"))+this->m_logEntries
.GetGitRevAt(indexNext
).m_CommitHash
.ToString()+CString(_T(" /endrev:"))+this->m_logEntries
.GetGitRevAt(indexNext
+1).m_CommitHash
.ToString();
132 case ID_COPYCLIPBOARD
:
134 CopySelectionToClipBoard();
139 CopySelectionToClipBoard(TRUE
);
143 procCmd
+= _T("export");
145 case ID_CREATE_BRANCH
:
146 procCmd
+= _T("branch");
149 procCmd
+= _T("tag");
152 procCmd
+= _T("switch");
155 procCmd
+= _T("blame");
156 procCmd
+= _T(" /endrev:") + this->m_logEntries
.GetGitRevAt(indexNext
).m_CommitHash
.ToString();
159 procCmd
+= _T("log");
162 procCmd
.Format(_T("/command:repobrowser /path:\"%s\" /rev:%s"), g_Git
.m_CurrentDir
, this->m_logEntries
.GetGitRevAt(indexNext
).m_CommitHash
.ToString());
165 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
172 // we need an URL to complete this command, so error out if we can't get an URL
173 if (pathURL
.IsEmpty())
176 strMessage
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)(m_path
.GetUIPathString()));
177 CMessageBox::Show(this->m_hWnd
, strMessage
, _T("TortoiseGit"), MB_ICONERROR
);
178 TRACE(_T("could not retrieve the URL of the folder!\n"));
182 msg
.Format(IDS_LOG_REVERT_CONFIRM
, m_path
.GetWinPath());
183 if (CMessageBox::Show(this->m_hWnd
, msg
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
) == IDYES
)
186 dlg
.SetCommand(CGitProgressDlg::GitProgress_Merge
);
187 dlg
.SetPathList(CTGitPathList(m_path
));
189 dlg
.SetSecondUrl(pathURL
);
190 revisionRanges
.AdjustForMerge(true);
191 dlg
.SetRevisionRanges(revisionRanges
);
192 dlg
.SetPegRevision(m_LogRevision
);
199 // we need an URL to complete this command, so error out if we can't get an URL
200 if (pathURL
.IsEmpty())
203 strMessage
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)(m_path
.GetUIPathString()));
204 CMessageBox::Show(this->m_hWnd
, strMessage
, _T("TortoiseGit"), MB_ICONERROR
);
205 TRACE(_T("could not retrieve the URL of the folder!\n"));
209 CString path
= m_path
.GetWinPathString();
210 bool bGotSavePath
= false;
211 if ((GetSelectedCount() == 1)&&(!m_path
.IsDirectory()))
213 bGotSavePath
= CAppUtils::FileOpenSave(path
, NULL
, IDS_LOG_MERGETO
, IDS_COMMONFILEFILTER
, true, GetSafeHwnd());
217 CBrowseFolder folderBrowser
;
218 folderBrowser
.SetInfo(CString(MAKEINTRESOURCE(IDS_LOG_MERGETO
)));
219 bGotSavePath
= (folderBrowser
.Show(GetSafeHwnd(), path
, path
) == CBrowseFolder::OK
);
224 dlg
.SetCommand(CGitProgressDlg::GitProgress_Merge
);
225 dlg
.SetPathList(CTGitPathList(CTGitPath(path
)));
227 dlg
.SetSecondUrl(pathURL
);
228 revisionRanges
.AdjustForMerge(false);
229 dlg
.SetRevisionRanges(revisionRanges
);
230 dlg
.SetPegRevision(m_LogRevision
);
237 // we need an URL to complete this command, so error out if we can't get an URL
238 if (pathURL
.IsEmpty())
241 strMessage
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)(m_path
.GetUIPathString()));
242 CMessageBox::Show(this->m_hWnd
, strMessage
, _T("TortoiseGit"), MB_ICONERROR
);
243 TRACE(_T("could not retrieve the URL of the folder!\n"));
248 msg
.Format(IDS_LOG_REVERTTOREV_CONFIRM
, m_path
.GetWinPath());
249 if (CMessageBox::Show(this->m_hWnd
, msg
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
) == IDYES
)
252 dlg
.SetCommand(CGitProgressDlg::GitProgress_Merge
);
253 dlg
.SetPathList(CTGitPathList(m_path
));
255 dlg
.SetSecondUrl(pathURL
);
256 GitRevRangeArray revarray
;
257 revarray
.AddRevRange(GitRev::REV_HEAD
, revSelected
);
258 dlg
.SetRevisionRanges(revarray
);
259 dlg
.SetPegRevision(m_LogRevision
);
265 case ID_BLAMECOMPARE
:
267 //user clicked on the menu item "compare with working copy"
268 //now first get the revision which is selected
271 GitDiff
diff(this, this->m_hWnd
, true);
272 diff
.SetHEADPeg(m_LogRevision
);
273 diff
.ShowCompare(m_path
, GitRev::REV_BASE
, m_path
, revSelected
, GitRev(), false, true);
276 CAppUtils::StartShowCompare(m_hWnd
, m_path
, GitRev::REV_BASE
, m_path
, revSelected
, GitRev(), m_LogRevision
, false, false, true);
281 //user clicked on the menu item "compare and blame revisions"
284 GitDiff
diff(this, this->m_hWnd
, true);
285 diff
.SetHEADPeg(m_LogRevision
);
286 diff
.ShowCompare(CTGitPath(pathURL
), revSelected2
, CTGitPath(pathURL
), revSelected
, GitRev(), false, true);
289 CAppUtils::StartShowCompare(m_hWnd
, CTGitPath(pathURL
), revSelected2
, CTGitPath(pathURL
), revSelected
, GitRev(), m_LogRevision
, false, false, true);
292 case ID_BLAMEWITHPREVIOUS
:
294 //user clicked on the menu item "Compare and Blame with previous revision"
297 GitDiff
diff(this, this->m_hWnd
, true);
298 diff
.SetHEADPeg(m_LogRevision
);
299 diff
.ShowCompare(CTGitPath(pathURL
), revPrevious
, CTGitPath(pathURL
), revSelected
, GitRev(), false, true);
302 CAppUtils::StartShowCompare(m_hWnd
, CTGitPath(pathURL
), revPrevious
, CTGitPath(pathURL
), revSelected
, GitRev(), m_LogRevision
, false, false, true);
310 CProgressDlg progDlg
;
311 progDlg
.SetTitle(IDS_APPNAME
);
312 progDlg
.SetAnimation(IDR_DOWNLOAD
);
314 sInfoLine
.Format(IDS_PROGRESSGETFILEREVISION
, m_path
.GetWinPath(), (LPCTSTR
)revSelected
.ToString());
315 progDlg
.SetLine(1, sInfoLine
, true);
316 SetAndClearProgressInfo(&progDlg
);
317 progDlg
.ShowModeless(m_hWnd
);
318 CTGitPath tempfile
= CTempFiles::Instance().GetTempFilePath(false, m_path
, revSelected
);
319 bool bSuccess
= true;
320 if (!Cat(m_path
, GitRev(GitRev::REV_HEAD
), revSelected
, tempfile
))
323 // try again, but with the selected revision as the peg revision
324 if (!Cat(m_path
, revSelected
, revSelected
, tempfile
))
327 SetAndClearProgressInfo((HWND
)NULL
);
328 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
337 SetAndClearProgressInfo((HWND
)NULL
);
338 SetFileAttributes(tempfile
.GetWinPath(), FILE_ATTRIBUTE_READONLY
);
341 ret
= (int)ShellExecute(this->m_hWnd
, NULL
, tempfile
.GetWinPath(), NULL
, NULL
, SW_SHOWNORMAL
);
342 if ((ret
<= HINSTANCE_ERROR
)||bOpenWith
)
344 CString cmd
= _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
345 cmd
+= tempfile
.GetWinPathString() + _T(" ");
346 CAppUtils::LaunchApplication(cmd
, NULL
, false);
354 dlg
.EndRev
= revSelected
;
355 if (dlg
.DoModal() == IDOK
)
360 tempfile
= blame
.BlameToTempFile(m_path
, dlg
.StartRev
, dlg
.EndRev
, dlg
.EndRev
, logfile
, _T(""), dlg
.m_bIncludeMerge
, TRUE
, TRUE
);
361 if (!tempfile
.IsEmpty())
365 //open the default text editor for the result file
366 CAppUtils::StartTextViewer(tempfile
);
370 CString sParams
= _T("/path:\"") + m_path
.GetGitPathString() + _T("\" ");
371 if(!CAppUtils::LaunchTortoiseBlame(tempfile
, logfile
, CPathUtils::GetFileNameFromPath(m_path
.GetFileOrDirectoryName()),sParams
))
379 CMessageBox::Show(this->m_hWnd
, blame
.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
387 CString url
= _T("tgit:")+pathURL
;
388 sCmd
.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
389 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
390 (LPCTSTR
)m_path
.GetWinPath(), (LONG
)revSelected
);
391 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
396 m_nSearchIndex
= GetSelectionMark();
397 if (m_nSearchIndex
< 0)
405 m_pFindDialog
= new CFindReplaceDialog();
406 m_pFindDialog
->Create(TRUE
, NULL
, NULL
, FR_HIDEUPDOWN
| FR_HIDEWHOLEWORD
, this);
412 EditLogMessage(selIndex
);
417 EditAuthor(selEntries
);
422 CEditPropertiesDlg dlg
;
423 dlg
.SetProjectProperties(&m_ProjectProperties
);
424 CTGitPathList escapedlist
;
425 dlg
.SetPathList(CTGitPathList(CTGitPath(pathURL
)));
426 dlg
.SetRevision(revSelected
);
435 sCmd
.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
436 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
437 (LPCTSTR
)pathURL
, (LONG
)revSelected
);
438 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
444 CString url
= _T("tgit:")+pathURL
;
445 sCmd
.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),
446 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
447 (LPCTSTR
)url
, (LONG
)revSelected
);
448 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
453 CString url
= m_ProjectProperties
.sWebViewerRev
;
454 url
= GetAbsoluteUrlFromRelativeUrl(url
);
455 url
.Replace(_T("%REVISION%"), revSelected
.ToString());
457 ShellExecute(this->m_hWnd
, _T("open"), url
, NULL
, NULL
, SW_SHOWDEFAULT
);
462 CString relurl
= pathURL
;
463 CString sRoot
= GetRepositoryRoot(CTGitPath(relurl
));
464 relurl
= relurl
.Mid(sRoot
.GetLength());
465 CString url
= m_ProjectProperties
.sWebViewerPathRev
;
466 url
= GetAbsoluteUrlFromRelativeUrl(url
);
467 url
.Replace(_T("%REVISION%"), revSelected
.ToString());
468 url
.Replace(_T("%PATH%"), relurl
);
470 ShellExecute(this->m_hWnd
, _T("open"), url
, NULL
, NULL
, SW_SHOWDEFAULT
);
477 CCommonAppUtils::RunTortoiseProc(procCmd
);