1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2011 - TortoiseGit
4 // Copyright (C) 2011 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 "PathUtils.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_BLAME
);
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(6)+_T(":")+r2
->m_CommitHash
.Left(6));
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");
152 procCmd
+=_T("switch");
155 procCmd
+=_T("blame");
156 procCmd
+=_T(" /endrev:") + this->m_logEntries
.GetGitRevAt(indexNext
).m_CommitHash
.ToString();
159 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
166 // we need an URL to complete this command, so error out if we can't get an URL
167 if (pathURL
.IsEmpty())
170 strMessage
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)(m_path
.GetUIPathString()));
171 CMessageBox::Show(this->m_hWnd
, strMessage
, _T("TortoiseGit"), MB_ICONERROR
);
172 TRACE(_T("could not retrieve the URL of the folder!\n"));
176 msg
.Format(IDS_LOG_REVERT_CONFIRM
, m_path
.GetWinPath());
177 if (CMessageBox::Show(this->m_hWnd
, msg
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
) == IDYES
)
180 dlg
.SetCommand(CGitProgressDlg::GitProgress_Merge
);
181 dlg
.SetPathList(CTGitPathList(m_path
));
183 dlg
.SetSecondUrl(pathURL
);
184 revisionRanges
.AdjustForMerge(true);
185 dlg
.SetRevisionRanges(revisionRanges
);
186 dlg
.SetPegRevision(m_LogRevision
);
193 // we need an URL to complete this command, so error out if we can't get an URL
194 if (pathURL
.IsEmpty())
197 strMessage
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)(m_path
.GetUIPathString()));
198 CMessageBox::Show(this->m_hWnd
, strMessage
, _T("TortoiseGit"), MB_ICONERROR
);
199 TRACE(_T("could not retrieve the URL of the folder!\n"));
203 CString path
= m_path
.GetWinPathString();
204 bool bGotSavePath
= false;
205 if ((GetSelectedCount() == 1)&&(!m_path
.IsDirectory()))
207 bGotSavePath
= CAppUtils::FileOpenSave(path
, NULL
, IDS_LOG_MERGETO
, IDS_COMMONFILEFILTER
, true, GetSafeHwnd());
211 CBrowseFolder folderBrowser
;
212 folderBrowser
.SetInfo(CString(MAKEINTRESOURCE(IDS_LOG_MERGETO
)));
213 bGotSavePath
= (folderBrowser
.Show(GetSafeHwnd(), path
, path
) == CBrowseFolder::OK
);
218 dlg
.SetCommand(CGitProgressDlg::GitProgress_Merge
);
219 dlg
.SetPathList(CTGitPathList(CTGitPath(path
)));
221 dlg
.SetSecondUrl(pathURL
);
222 revisionRanges
.AdjustForMerge(false);
223 dlg
.SetRevisionRanges(revisionRanges
);
224 dlg
.SetPegRevision(m_LogRevision
);
231 // we need an URL to complete this command, so error out if we can't get an URL
232 if (pathURL
.IsEmpty())
235 strMessage
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)(m_path
.GetUIPathString()));
236 CMessageBox::Show(this->m_hWnd
, strMessage
, _T("TortoiseGit"), MB_ICONERROR
);
237 TRACE(_T("could not retrieve the URL of the folder!\n"));
242 msg
.Format(IDS_LOG_REVERTTOREV_CONFIRM
, m_path
.GetWinPath());
243 if (CMessageBox::Show(this->m_hWnd
, msg
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
) == IDYES
)
246 dlg
.SetCommand(CGitProgressDlg::GitProgress_Merge
);
247 dlg
.SetPathList(CTGitPathList(m_path
));
249 dlg
.SetSecondUrl(pathURL
);
250 GitRevRangeArray revarray
;
251 revarray
.AddRevRange(GitRev::REV_HEAD
, revSelected
);
252 dlg
.SetRevisionRanges(revarray
);
253 dlg
.SetPegRevision(m_LogRevision
);
259 case ID_BLAMECOMPARE
:
261 //user clicked on the menu item "compare with working copy"
262 //now first get the revision which is selected
265 GitDiff
diff(this, this->m_hWnd
, true);
266 diff
.SetHEADPeg(m_LogRevision
);
267 diff
.ShowCompare(m_path
, GitRev::REV_BASE
, m_path
, revSelected
, GitRev(), false, true);
270 CAppUtils::StartShowCompare(m_hWnd
, m_path
, GitRev::REV_BASE
, m_path
, revSelected
, GitRev(), m_LogRevision
, false, false, true);
275 //user clicked on the menu item "compare and blame revisions"
278 GitDiff
diff(this, this->m_hWnd
, true);
279 diff
.SetHEADPeg(m_LogRevision
);
280 diff
.ShowCompare(CTGitPath(pathURL
), revSelected2
, CTGitPath(pathURL
), revSelected
, GitRev(), false, true);
283 CAppUtils::StartShowCompare(m_hWnd
, CTGitPath(pathURL
), revSelected2
, CTGitPath(pathURL
), revSelected
, GitRev(), m_LogRevision
, false, false, true);
286 case ID_BLAMEWITHPREVIOUS
:
288 //user clicked on the menu item "Compare and Blame with previous revision"
291 GitDiff
diff(this, this->m_hWnd
, true);
292 diff
.SetHEADPeg(m_LogRevision
);
293 diff
.ShowCompare(CTGitPath(pathURL
), revPrevious
, CTGitPath(pathURL
), revSelected
, GitRev(), false, true);
296 CAppUtils::StartShowCompare(m_hWnd
, CTGitPath(pathURL
), revPrevious
, CTGitPath(pathURL
), revSelected
, GitRev(), m_LogRevision
, false, false, true);
304 CProgressDlg progDlg
;
305 progDlg
.SetTitle(IDS_APPNAME
);
306 progDlg
.SetAnimation(IDR_DOWNLOAD
);
308 sInfoLine
.Format(IDS_PROGRESSGETFILEREVISION
, m_path
.GetWinPath(), (LPCTSTR
)revSelected
.ToString());
309 progDlg
.SetLine(1, sInfoLine
, true);
310 SetAndClearProgressInfo(&progDlg
);
311 progDlg
.ShowModeless(m_hWnd
);
312 CTGitPath tempfile
= CTempFiles::Instance().GetTempFilePath(false, m_path
, revSelected
);
313 bool bSuccess
= true;
314 if (!Cat(m_path
, GitRev(GitRev::REV_HEAD
), revSelected
, tempfile
))
317 // try again, but with the selected revision as the peg revision
318 if (!Cat(m_path
, revSelected
, revSelected
, tempfile
))
321 SetAndClearProgressInfo((HWND
)NULL
);
322 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
331 SetAndClearProgressInfo((HWND
)NULL
);
332 SetFileAttributes(tempfile
.GetWinPath(), FILE_ATTRIBUTE_READONLY
);
335 ret
= (int)ShellExecute(this->m_hWnd
, NULL
, tempfile
.GetWinPath(), NULL
, NULL
, SW_SHOWNORMAL
);
336 if ((ret
<= HINSTANCE_ERROR
)||bOpenWith
)
338 CString cmd
= _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
339 cmd
+= tempfile
.GetWinPathString() + _T(" ");
340 CAppUtils::LaunchApplication(cmd
, NULL
, false);
348 dlg
.EndRev
= revSelected
;
349 if (dlg
.DoModal() == IDOK
)
354 tempfile
= blame
.BlameToTempFile(m_path
, dlg
.StartRev
, dlg
.EndRev
, dlg
.EndRev
, logfile
, _T(""), dlg
.m_bIncludeMerge
, TRUE
, TRUE
);
355 if (!tempfile
.IsEmpty())
359 //open the default text editor for the result file
360 CAppUtils::StartTextViewer(tempfile
);
364 CString sParams
= _T("/path:\"") + m_path
.GetGitPathString() + _T("\" ");
365 if(!CAppUtils::LaunchTortoiseBlame(tempfile
, logfile
, CPathUtils::GetFileNameFromPath(m_path
.GetFileOrDirectoryName()),sParams
))
373 CMessageBox::Show(this->m_hWnd
, blame
.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
381 CString url
= _T("tgit:")+pathURL
;
382 sCmd
.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
383 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
384 (LPCTSTR
)m_path
.GetWinPath(), (LONG
)revSelected
);
385 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
390 m_nSearchIndex
= GetSelectionMark();
391 if (m_nSearchIndex
< 0)
399 m_pFindDialog
= new CFindReplaceDialog();
400 m_pFindDialog
->Create(TRUE
, NULL
, NULL
, FR_HIDEUPDOWN
| FR_HIDEWHOLEWORD
, this);
407 sCmd
.Format(_T("%s /command:repobrowser /path:\"%s\" /rev:%s"),
408 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
409 (LPCTSTR
)pathURL
, (LPCTSTR
)revSelected
.ToString());
411 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
416 EditLogMessage(selIndex
);
421 EditAuthor(selEntries
);
426 CEditPropertiesDlg dlg
;
427 dlg
.SetProjectProperties(&m_ProjectProperties
);
428 CTGitPathList escapedlist
;
429 dlg
.SetPathList(CTGitPathList(CTGitPath(pathURL
)));
430 dlg
.SetRevision(revSelected
);
439 sCmd
.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
440 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
441 (LPCTSTR
)pathURL
, (LONG
)revSelected
);
442 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
448 CString url
= _T("tgit:")+pathURL
;
449 sCmd
.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),
450 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
451 (LPCTSTR
)url
, (LONG
)revSelected
);
452 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
457 CString url
= m_ProjectProperties
.sWebViewerRev
;
458 url
= GetAbsoluteUrlFromRelativeUrl(url
);
459 url
.Replace(_T("%REVISION%"), revSelected
.ToString());
461 ShellExecute(this->m_hWnd
, _T("open"), url
, NULL
, NULL
, SW_SHOWDEFAULT
);
466 CString relurl
= pathURL
;
467 CString sRoot
= GetRepositoryRoot(CTGitPath(relurl
));
468 relurl
= relurl
.Mid(sRoot
.GetLength());
469 CString url
= m_ProjectProperties
.sWebViewerPathRev
;
470 url
= GetAbsoluteUrlFromRelativeUrl(url
);
471 url
.Replace(_T("%REVISION%"), revSelected
.ToString());
472 url
.Replace(_T("%PATH%"), relurl
);
474 ShellExecute(this->m_hWnd
, _T("open"), url
, NULL
, NULL
, SW_SHOWDEFAULT
);
483 PROCESS_INFORMATION process
;
484 memset(&startup
, 0, sizeof(startup
));
485 startup
.cb
= sizeof(startup
);
486 memset(&process
, 0, sizeof(process
));
487 CString tortoiseProcPath
= CPathUtils::GetAppDirectory() + _T("TortoiseProc.exe");
489 if (CreateProcess(tortoiseProcPath
, procCmd
.GetBuffer(), NULL
, NULL
, FALSE
, 0, 0, 0, &startup
, &process
))
491 CloseHandle(process
.hThread
);
492 CloseHandle(process
.hProcess
);