Use Rebase Dialog For cherry pick ant log dialog actions.
[TortoiseGit.git] / src / TortoiseProc / GitLogListAction.cpp
blob8bafa70748e59e304b3527159e82599c50a8ce37
1 // GitLogList.cpp : implementation file
2 //
3 /*
4 Description: qgit revision list view
6 Author: Marco Costalba (C) 2005-2007
8 Copyright: See COPYING file that comes with this distribution
11 #include "stdafx.h"
12 #include "TortoiseProc.h"
13 #include "GitLogList.h"
14 #include "GitRev.h"
15 //#include "VssStyle.h"
16 #include "IconMenu.h"
17 // CGitLogList
18 #include "cursor.h"
19 #include "InputDlg.h"
20 #include "PropDlg.h"
21 #include "SVNProgressDlg.h"
22 #include "ProgressDlg.h"
23 //#include "RepositoryBrowser.h"
24 //#include "CopyDlg.h"
25 //#include "StatGraphDlg.h"
26 #include "Logdlg.h"
27 #include "MessageBox.h"
28 #include "Registry.h"
29 #include "AppUtils.h"
30 #include "PathUtils.h"
31 #include "StringUtils.h"
32 #include "UnicodeUtils.h"
33 #include "TempFile.h"
34 //#include "GitInfo.h"
35 //#include "GitDiff.h"
36 #include "IconMenu.h"
37 //#include "RevisionRangeDlg.h"
38 //#include "BrowseFolder.h"
39 //#include "BlameDlg.h"
40 //#include "Blame.h"
41 //#include "GitHelpers.h"
42 #include "GitStatus.h"
43 //#include "LogDlgHelper.h"
44 //#include "CachedLogInfo.h"
45 //#include "RepositoryInfo.h"
46 //#include "EditPropertiesDlg.h"
47 #include "FileDiffDlg.h"
48 #include "CommitDlg.h"
49 #include "RebaseDlg.h"
51 IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl)
53 void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect)
55 POSITION pos = GetFirstSelectedItemPosition();
56 int indexNext = GetNextSelectedItem(pos);
57 if (indexNext < 0)
58 return;
60 GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));
62 theApp.DoWaitCursor(1);
63 bool bOpenWith = false;
64 switch (cmd)
66 case ID_GNUDIFF1:
68 CString tempfile=GetTempFile();
69 CString cmd;
70 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
71 cmd.Format(_T("git.exe diff-tree -r -p --stat %s"),r1->m_CommitHash);
72 g_Git.RunLogFile(cmd,tempfile);
73 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r1->m_Subject);
75 break;
77 case ID_GNUDIFF2:
79 CString tempfile=GetTempFile();
80 CString cmd;
81 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
82 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
83 cmd.Format(_T("git.exe diff-tree -r -p --stat %s %s"),r1->m_CommitHash,r2->m_CommitHash);
84 g_Git.RunLogFile(cmd,tempfile);
85 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r2->m_CommitHash.Left(6));
88 break;
90 case ID_COMPARETWO:
92 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
93 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
94 CFileDiffDlg dlg;
95 dlg.SetDiff(NULL,*r1,*r2);
96 dlg.DoModal();
99 break;
102 case ID_COMPARE:
104 GitRev * r1 = &m_wcRev;
105 GitRev * r2 = pSelLogEntry;
106 CFileDiffDlg dlg;
107 dlg.SetDiff(NULL,*r1,*r2);
108 dlg.DoModal();
110 //user clicked on the menu item "compare with working copy"
111 //if (PromptShown())
113 // GitDiff diff(this, m_hWnd, true);
114 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
115 // diff.SetHEADPeg(m_LogRevision);
116 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
118 //else
119 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
121 break;
123 case ID_COMPAREWITHPREVIOUS:
126 CFileDiffDlg dlg;
128 if(pSelLogEntry->m_ParentHash.size()>0)
129 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
131 dlg.SetDiff(NULL,pSelLogEntry->m_CommitHash,pSelLogEntry->m_ParentHash[0]);
132 dlg.DoModal();
133 }else
135 CMessageBox::Show(NULL,_T("No previous version"),_T("TortoiseGit"),MB_OK);
137 //if (PromptShown())
139 // GitDiff diff(this, m_hWnd, true);
140 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
141 // diff.SetHEADPeg(m_LogRevision);
142 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
144 //else
145 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
147 break;
148 case ID_COPYCLIPBOARD:
150 CopySelectionToClipBoard();
152 break;
153 case ID_COPYHASH:
155 CopySelectionToClipBoard(TRUE);
157 break;
158 case ID_EXPORT:
159 CAppUtils::Export(&pSelLogEntry->m_CommitHash);
160 break;
161 case ID_CREATE_BRANCH:
162 CAppUtils::CreateBranchTag(FALSE,&pSelLogEntry->m_CommitHash);
163 ReloadHashMap();
164 Invalidate();
165 break;
166 case ID_CREATE_TAG:
167 CAppUtils::CreateBranchTag(TRUE,&pSelLogEntry->m_CommitHash);
168 ReloadHashMap();
169 Invalidate();
170 break;
171 case ID_SWITCHTOREV:
172 CAppUtils::Switch(&pSelLogEntry->m_CommitHash);
173 ReloadHashMap();
174 Invalidate();
175 break;
176 case ID_RESET:
177 CAppUtils::GitReset(&pSelLogEntry->m_CommitHash);
178 ReloadHashMap();
179 Invalidate();
180 break;
181 case ID_REBASE_PICK:
182 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_PICK);
183 break;
184 case ID_REBASE_EDIT:
185 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_EDIT);
186 break;
187 case ID_REBASE_SQUASH:
188 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SQUASH);
189 break;
190 case ID_REBASE_SKIP:
191 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SKIP);
192 break;
193 case ID_COMBINE_COMMIT:
195 CString head;
196 CString headhash;
198 head.Format(_T("HEAD~%d"),LastSelect);
199 CString hash=g_Git.GetHash(head);
200 hash=hash.Left(40);
202 headhash=g_Git.GetHash(CString(_T("HEAD")));
203 headhash=headhash.Left(40);
205 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
206 if(pLastEntry->m_CommitHash != hash)
208 CMessageBox::Show(NULL,_T("Only combine top continuous commit"),_T("TortoiseGit"),MB_OK);
210 if(!g_Git.CheckCleanWorkTree())
212 CMessageBox::Show(NULL,_T("Combine need clean work tree"),_T("TortoiseGit"),MB_OK);
213 break;
215 CString cmd,out;
217 cmd.Format(_T("git.exe reset --mixed %s"),hash);
218 if(g_Git.Run(cmd,&out,CP_UTF8))
220 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
222 CCommitDlg dlg;
223 for(int i=FirstSelect;i<=LastSelect;i++)
225 GitRev* pRev = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
226 dlg.m_sLogMessage+=pRev->m_Subject+_T("\n")+pRev->m_Body;
227 dlg.m_sLogMessage+=_T("\n");
229 dlg.m_bWholeProject=true;
230 dlg.m_bSelectFilesForCommit = true;
231 dlg.m_bCommitAmend=true;
232 dlg.m_AmendStr=dlg.m_sLogMessage;
234 if (dlg.DoModal() == IDOK)
237 }else
239 cmd.Format(_T("git.exe reset --hard %s"),headhash);
240 out.Empty();
241 if(g_Git.Run(cmd,&out,CP_UTF8))
243 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
246 Refresh();
248 break;
250 case ID_CHERRY_PICK:
251 if(!g_Git.CheckCleanWorkTree())
253 CMessageBox::Show(NULL,_T("Cherry Pick Require Clean Working Tree"),_T("TortoiseGit"),MB_OK);
255 }else
257 CRebaseDlg dlg;
258 dlg.m_IsCherryPick = TRUE;
259 dlg.m_Upstream = this->m_CurrentBranch;
260 POSITION pos = GetFirstSelectedItemPosition();
261 while(pos)
263 int indexNext = GetNextSelectedItem(pos);
264 dlg.m_CommitList.m_logEntries.push_back(*(GitRev*)m_arShownList[indexNext]);
265 dlg.m_CommitList.m_logEntries.at(dlg.m_CommitList.m_logEntries.size()-1).m_Action |= CTGitPath::LOGACTIONS_REBASE_PICK;
268 if(dlg.DoModal() == IDOK)
270 Refresh();
273 break;
274 case ID_REBASE_TO_VERSION:
275 if(!g_Git.CheckCleanWorkTree())
277 CMessageBox::Show(NULL,_T("Rebase Require Clean Working Tree"),_T("TortoiseGit"),MB_OK);
279 }else
281 CRebaseDlg dlg;
282 dlg.m_Upstream = pSelLogEntry->m_CommitHash;
284 if(dlg.DoModal() == IDOK)
286 Refresh();
290 break;
292 default:
293 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
294 break;
295 #if 0
297 case ID_REVERTREV:
299 // we need an URL to complete this command, so error out if we can't get an URL
300 if (pathURL.IsEmpty())
302 CString strMessage;
303 strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
304 CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
305 TRACE(_T("could not retrieve the URL of the folder!\n"));
306 break; //exit
308 CString msg;
309 msg.Format(IDS_LOG_REVERT_CONFIRM, m_path.GetWinPath());
310 if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
312 CGitProgressDlg dlg;
313 dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
314 dlg.SetPathList(CTGitPathList(m_path));
315 dlg.SetUrl(pathURL);
316 dlg.SetSecondUrl(pathURL);
317 revisionRanges.AdjustForMerge(true);
318 dlg.SetRevisionRanges(revisionRanges);
319 dlg.SetPegRevision(m_LogRevision);
320 dlg.DoModal();
323 break;
324 case ID_MERGEREV:
326 // we need an URL to complete this command, so error out if we can't get an URL
327 if (pathURL.IsEmpty())
329 CString strMessage;
330 strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
331 CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
332 TRACE(_T("could not retrieve the URL of the folder!\n"));
333 break; //exit
336 CString path = m_path.GetWinPathString();
337 bool bGotSavePath = false;
338 if ((GetSelectedCount() == 1)&&(!m_path.IsDirectory()))
340 bGotSavePath = CAppUtils::FileOpenSave(path, NULL, IDS_LOG_MERGETO, IDS_COMMONFILEFILTER, true, GetSafeHwnd());
342 else
344 CBrowseFolder folderBrowser;
345 folderBrowser.SetInfo(CString(MAKEINTRESOURCE(IDS_LOG_MERGETO)));
346 bGotSavePath = (folderBrowser.Show(GetSafeHwnd(), path, path) == CBrowseFolder::OK);
348 if (bGotSavePath)
350 CGitProgressDlg dlg;
351 dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
352 dlg.SetPathList(CTGitPathList(CTGitPath(path)));
353 dlg.SetUrl(pathURL);
354 dlg.SetSecondUrl(pathURL);
355 revisionRanges.AdjustForMerge(false);
356 dlg.SetRevisionRanges(revisionRanges);
357 dlg.SetPegRevision(m_LogRevision);
358 dlg.DoModal();
361 break;
362 case ID_REVERTTOREV:
364 // we need an URL to complete this command, so error out if we can't get an URL
365 if (pathURL.IsEmpty())
367 CString strMessage;
368 strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
369 CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
370 TRACE(_T("could not retrieve the URL of the folder!\n"));
371 break; //exit
374 CString msg;
375 msg.Format(IDS_LOG_REVERTTOREV_CONFIRM, m_path.GetWinPath());
376 if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
378 CGitProgressDlg dlg;
379 dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
380 dlg.SetPathList(CTGitPathList(m_path));
381 dlg.SetUrl(pathURL);
382 dlg.SetSecondUrl(pathURL);
383 GitRevRangeArray revarray;
384 revarray.AddRevRange(GitRev::REV_HEAD, revSelected);
385 dlg.SetRevisionRanges(revarray);
386 dlg.SetPegRevision(m_LogRevision);
387 dlg.DoModal();
390 break;
394 case ID_BLAMECOMPARE:
396 //user clicked on the menu item "compare with working copy"
397 //now first get the revision which is selected
398 if (PromptShown())
400 GitDiff diff(this, this->m_hWnd, true);
401 diff.SetHEADPeg(m_LogRevision);
402 diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
404 else
405 CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
407 break;
408 case ID_BLAMETWO:
410 //user clicked on the menu item "compare and blame revisions"
411 if (PromptShown())
413 GitDiff diff(this, this->m_hWnd, true);
414 diff.SetHEADPeg(m_LogRevision);
415 diff.ShowCompare(CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), false, true);
417 else
418 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
420 break;
421 case ID_BLAMEWITHPREVIOUS:
423 //user clicked on the menu item "Compare and Blame with previous revision"
424 if (PromptShown())
426 GitDiff diff(this, this->m_hWnd, true);
427 diff.SetHEADPeg(m_LogRevision);
428 diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
430 else
431 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
433 break;
435 case ID_OPENWITH:
436 bOpenWith = true;
437 case ID_OPEN:
439 CProgressDlg progDlg;
440 progDlg.SetTitle(IDS_APPNAME);
441 progDlg.SetAnimation(IDR_DOWNLOAD);
442 CString sInfoLine;
443 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
444 progDlg.SetLine(1, sInfoLine, true);
445 SetAndClearProgressInfo(&progDlg);
446 progDlg.ShowModeless(m_hWnd);
447 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
448 bool bSuccess = true;
449 if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
451 bSuccess = false;
452 // try again, but with the selected revision as the peg revision
453 if (!Cat(m_path, revSelected, revSelected, tempfile))
455 progDlg.Stop();
456 SetAndClearProgressInfo((HWND)NULL);
457 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
458 EnableOKButton();
459 break;
461 bSuccess = true;
463 if (bSuccess)
465 progDlg.Stop();
466 SetAndClearProgressInfo((HWND)NULL);
467 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
468 int ret = 0;
469 if (!bOpenWith)
470 ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
471 if ((ret <= HINSTANCE_ERROR)||bOpenWith)
473 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
474 cmd += tempfile.GetWinPathString() + _T(" ");
475 CAppUtils::LaunchApplication(cmd, NULL, false);
479 break;
480 case ID_BLAME:
482 CBlameDlg dlg;
483 dlg.EndRev = revSelected;
484 if (dlg.DoModal() == IDOK)
486 CBlame blame;
487 CString tempfile;
488 CString logfile;
489 tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
490 if (!tempfile.IsEmpty())
492 if (dlg.m_bTextView)
494 //open the default text editor for the result file
495 CAppUtils::StartTextViewer(tempfile);
497 else
499 CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
500 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
502 break;
506 else
508 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
512 break;
513 case ID_UPDATE:
515 CString sCmd;
516 CString url = _T("tgit:")+pathURL;
517 sCmd.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
518 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
519 (LPCTSTR)m_path.GetWinPath(), (LONG)revSelected);
520 CAppUtils::LaunchApplication(sCmd, NULL, false);
522 break;
523 case ID_FINDENTRY:
525 m_nSearchIndex = GetSelectionMark();
526 if (m_nSearchIndex < 0)
527 m_nSearchIndex = 0;
528 if (m_pFindDialog)
530 break;
532 else
534 m_pFindDialog = new CFindReplaceDialog();
535 m_pFindDialog->Create(TRUE, NULL, NULL, FR_HIDEUPDOWN | FR_HIDEWHOLEWORD, this);
538 break;
539 case ID_REPOBROWSE:
541 CString sCmd;
542 sCmd.Format(_T("%s /command:repobrowser /path:\"%s\" /rev:%s"),
543 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
544 (LPCTSTR)pathURL, (LPCTSTR)revSelected.ToString());
546 CAppUtils::LaunchApplication(sCmd, NULL, false);
548 break;
549 case ID_EDITLOG:
551 EditLogMessage(selIndex);
553 break;
554 case ID_EDITAUTHOR:
556 EditAuthor(selEntries);
558 break;
559 case ID_REVPROPS:
561 CEditPropertiesDlg dlg;
562 dlg.SetProjectProperties(&m_ProjectProperties);
563 CTGitPathList escapedlist;
564 dlg.SetPathList(CTGitPathList(CTGitPath(pathURL)));
565 dlg.SetRevision(revSelected);
566 dlg.RevProps(true);
567 dlg.DoModal();
569 break;
571 case ID_EXPORT:
573 CString sCmd;
574 sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
575 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
576 (LPCTSTR)pathURL, (LONG)revSelected);
577 CAppUtils::LaunchApplication(sCmd, NULL, false);
579 break;
580 case ID_CHECKOUT:
582 CString sCmd;
583 CString url = _T("tgit:")+pathURL;
584 sCmd.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),
585 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
586 (LPCTSTR)url, (LONG)revSelected);
587 CAppUtils::LaunchApplication(sCmd, NULL, false);
589 break;
590 case ID_VIEWREV:
592 CString url = m_ProjectProperties.sWebViewerRev;
593 url = GetAbsoluteUrlFromRelativeUrl(url);
594 url.Replace(_T("%REVISION%"), revSelected.ToString());
595 if (!url.IsEmpty())
596 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
598 break;
599 case ID_VIEWPATHREV:
601 CString relurl = pathURL;
602 CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
603 relurl = relurl.Mid(sRoot.GetLength());
604 CString url = m_ProjectProperties.sWebViewerPathRev;
605 url = GetAbsoluteUrlFromRelativeUrl(url);
606 url.Replace(_T("%REVISION%"), revSelected.ToString());
607 url.Replace(_T("%PATH%"), relurl);
608 if (!url.IsEmpty())
609 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
611 break;
612 #endif
614 } // switch (cmd)
616 theApp.DoWaitCursor(-1);
619 void CGitLogList::SetSelectedAction(int action)
621 POSITION pos = GetFirstSelectedItemPosition();
622 int index;
623 while(pos)
625 index = GetNextSelectedItem(pos);
626 ((GitRev*)m_arShownList[index])->m_Action = action;
627 CRect rect;
628 this->GetItemRect(index,&rect,LVIR_BOUNDS);
629 this->InvalidateRect(rect);