Fixed issue #354: What about revert range of revisions from Log window?
[TortoiseGit.git] / src / TortoiseProc / GitLogListAction.cpp
bloba0ce7f939553e76ef9b2cc8f4785446f277e2f93
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 "SysProgressDlg.h"
24 //#include "RepositoryBrowser.h"
25 //#include "CopyDlg.h"
26 //#include "StatGraphDlg.h"
27 #include "Logdlg.h"
28 #include "MessageBox.h"
29 #include "Registry.h"
30 #include "AppUtils.h"
31 #include "PathUtils.h"
32 #include "StringUtils.h"
33 #include "UnicodeUtils.h"
34 #include "TempFile.h"
35 //#include "GitInfo.h"
36 //#include "GitDiff.h"
37 #include "IconMenu.h"
38 //#include "RevisionRangeDlg.h"
39 //#include "BrowseFolder.h"
40 //#include "BlameDlg.h"
41 //#include "Blame.h"
42 //#include "GitHelpers.h"
43 #include "GitStatus.h"
44 //#include "LogDlgHelper.h"
45 //#include "CachedLogInfo.h"
46 //#include "RepositoryInfo.h"
47 //#include "EditPropertiesDlg.h"
48 #include "FileDiffDlg.h"
49 #include "CommitDlg.h"
50 #include "RebaseDlg.h"
51 #include "GitDiff.h"
53 IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl)
55 int CGitLogList::CherryPickFrom(CString from, CString to)
57 CLogDataVector logs(&m_LogCache);
58 if(logs.ParserFromLog(NULL,-1,0,&from,&to))
59 return -1;
61 if(logs.size() == 0)
62 return 0;
64 CSysProgressDlg progress;
65 if (progress.IsValid())
67 progress.SetTitle(_T("Cherry Pick"));
68 progress.SetAnimation(IDR_MOVEANI);
69 progress.SetTime(true);
70 progress.ShowModeless(this);
73 for(int i=logs.size()-1;i>=0;i--)
75 if (progress.IsValid())
77 progress.FormatPathLine(1, _T("Pick up %s"), logs.GetGitRevAt(i).m_CommitHash.ToString());
78 progress.FormatPathLine(2, _T("%s"), logs.GetGitRevAt(i).m_Subject);
79 progress.SetProgress(logs.size()-i, logs.size());
81 if ((progress.IsValid())&&(progress.HasUserCancelled()))
83 //CMessageBox::Show(hwndExplorer, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_ICONINFORMATION);
84 throw std::exception(CUnicodeUtils::GetUTF8(_T("User canceled\r\n\r\n")));
85 return -1;
87 CString cmd,out;
88 cmd.Format(_T("git.exe cherry-pick %s"),logs.GetGitRevAt(i).m_CommitHash.ToString());
89 out.Empty();
90 if(g_Git.Run(cmd,&out,CP_UTF8))
92 throw std::exception(CUnicodeUtils::GetUTF8(CString(_T("Cherry Pick Failure\r\n\r\n"))+out));
93 return -1;
97 return 0;
100 void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect)
102 POSITION pos = GetFirstSelectedItemPosition();
103 int indexNext = GetNextSelectedItem(pos);
104 if (indexNext < 0)
105 return;
107 GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));
109 theApp.DoWaitCursor(1);
110 bool bOpenWith = false;
111 switch (cmd&0xFFFF)
113 case ID_COMMIT:
115 CTGitPathList pathlist;
116 bool bSelectFilesForCommit = !!DWORD(CRegStdWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE));
117 CAppUtils::Commit(CString(),true,CString(),
118 pathlist,pathlist,bSelectFilesForCommit);
119 //this->Refresh();
120 this->GetParent()->PostMessage(WM_COMMAND,ID_LOGDLG_REFRESH,0);
122 break;
123 case ID_GNUDIFF1:
125 CString tempfile=GetTempFile();
126 CString cmd;
127 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
128 if(!r1->m_CommitHash.IsEmpty())
130 cmd.Format(_T("git.exe diff-tree -r -p --stat %s"),r1->m_CommitHash.ToString());
131 }else
132 cmd.Format(_T("git.exe diff -r -p --stat"));
134 g_Git.RunLogFile(cmd,tempfile);
135 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.ToString().Left(6)+_T(":")+r1->m_Subject);
137 break;
139 case ID_GNUDIFF2:
141 CString tempfile=GetTempFile();
142 CString cmd;
143 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
144 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
146 if( r1->m_CommitHash.IsEmpty())
148 cmd.Format(_T("git.exe diff -r -p --stat %s"),r2->m_CommitHash.ToString());
149 }else if( r2->m_CommitHash.IsEmpty())
151 cmd.Format(_T("git.exe diff -r -p --stat %s"),r1->m_CommitHash.ToString());
152 }else
153 cmd.Format(_T("git.exe diff-tree -r -p --stat %s %s"),r1->m_CommitHash.ToString(),r2->m_CommitHash.ToString());
155 g_Git.RunLogFile(cmd,tempfile);
156 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.ToString().Left(6)+_T(":")+r2->m_CommitHash.ToString().Left(6));
159 break;
161 case ID_COMPARETWO:
163 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
164 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
165 CGitDiff::DiffCommit(this->m_Path, r1,r2);
168 break;
171 case ID_COMPARE:
173 GitRev * r1 = &m_wcRev;
174 GitRev * r2 = pSelLogEntry;
176 CGitDiff::DiffCommit(this->m_Path, r1,r2);
178 //user clicked on the menu item "compare with working copy"
179 //if (PromptShown())
181 // GitDiff diff(this, m_hWnd, true);
182 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
183 // diff.SetHEADPeg(m_LogRevision);
184 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
186 //else
187 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
189 break;
191 case ID_COMPAREWITHPREVIOUS:
194 CFileDiffDlg dlg;
196 if(pSelLogEntry->m_ParentHash.size()>0)
197 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
199 CGitDiff::DiffCommit(this->m_Path, pSelLogEntry->m_CommitHash.ToString(),pSelLogEntry->m_ParentHash[0].ToString());
201 }else
203 CMessageBox::Show(NULL,_T("No previous version"),_T("TortoiseGit"),MB_OK);
205 //if (PromptShown())
207 // GitDiff diff(this, m_hWnd, true);
208 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
209 // diff.SetHEADPeg(m_LogRevision);
210 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
212 //else
213 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
215 break;
216 case ID_COPYCLIPBOARD:
218 CopySelectionToClipBoard();
220 break;
221 case ID_COPYHASH:
223 CopySelectionToClipBoard(TRUE);
225 break;
226 case ID_EXPORT:
227 CAppUtils::Export(&pSelLogEntry->m_CommitHash.ToString());
228 break;
229 case ID_CREATE_BRANCH:
230 CAppUtils::CreateBranchTag(FALSE,&pSelLogEntry->m_CommitHash.ToString());
231 ReloadHashMap();
232 Invalidate();
233 break;
234 case ID_CREATE_TAG:
235 CAppUtils::CreateBranchTag(TRUE,&pSelLogEntry->m_CommitHash.ToString());
236 ReloadHashMap();
237 Invalidate();
238 break;
239 case ID_SWITCHTOREV:
240 CAppUtils::Switch(&pSelLogEntry->m_CommitHash.ToString());
241 ReloadHashMap();
242 Invalidate();
243 break;
244 case ID_RESET:
245 CAppUtils::GitReset(&pSelLogEntry->m_CommitHash.ToString());
246 ReloadHashMap();
247 Invalidate();
248 break;
249 case ID_REBASE_PICK:
250 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_PICK);
251 break;
252 case ID_REBASE_EDIT:
253 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_EDIT);
254 break;
255 case ID_REBASE_SQUASH:
256 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SQUASH);
257 break;
258 case ID_REBASE_SKIP:
259 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SKIP);
260 break;
261 case ID_COMBINE_COMMIT:
263 CString head;
264 CGitHash headhash;
265 CGitHash hashFirst,hashLast;
267 int headindex=GetHeadIndex();
268 if(headindex>=0) //incase show all branch, head is not the first commits.
270 head.Format(_T("HEAD~%d"),FirstSelect-headindex);
271 hashFirst=g_Git.GetHash(head);
273 head.Format(_T("HEAD~%d"),LastSelect-headindex);
274 hashLast=g_Git.GetHash(head);
277 GitRev* pFirstEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
278 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
279 if(pFirstEntry->m_CommitHash != hashFirst || pLastEntry->m_CommitHash != hashLast)
281 CMessageBox::Show(NULL,_T(
282 "Cannot combine commits now.\r\n\
283 Make sure you are viewing the log of your current branch and \
284 no filters are applied."),_T("TortoiseGit"),MB_OK);
285 break;
288 headhash=g_Git.GetHash(CString(_T("HEAD")));
290 if(!g_Git.CheckCleanWorkTree())
292 CMessageBox::Show(NULL,_T("Combine needs a clean work tree"),_T("TortoiseGit"),MB_OK);
293 break;
295 CString cmd,out;
297 //Use throw to abort this process (reset back to original HEAD)
300 cmd.Format(_T("git.exe reset --hard %s"),pFirstEntry->m_CommitHash.ToString());
301 if(g_Git.Run(cmd,&out,CP_UTF8))
303 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
304 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to first commit (first step) aborting...\r\n\r\n")+out));
306 cmd.Format(_T("git.exe reset --mixed %s"),hashLast.ToString());
307 if(g_Git.Run(cmd,&out,CP_UTF8))
309 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
310 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to last commit (second step) aborting...\r\n\r\n")+out));
312 CCommitDlg dlg;
313 for(int i=FirstSelect;i<=LastSelect;i++)
315 GitRev* pRev = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
316 dlg.m_sLogMessage+=pRev->m_Subject+_T("\n")+pRev->m_Body;
317 dlg.m_sLogMessage+=_T("\n");
319 dlg.m_bWholeProject=true;
320 dlg.m_bSelectFilesForCommit = true;
321 dlg.m_bCommitAmend=true;
322 dlg.m_AmendStr=dlg.m_sLogMessage;
324 bool abort=false;
325 if (dlg.DoModal() == IDOK)
327 if(pFirstEntry->m_CommitHash!=headhash)
329 //Commitrange firstEntry..headhash (from top of combine to original head) needs to be 'cherry-picked'
330 //on top of new commit.
331 //Use the rebase --onto command for it.
333 //All this can be done in one step using the following command:
334 //cmd.Format(_T("git.exe format-patch --stdout --binary --full-index -k %s..%s | git am -k -3"),
335 // pFirstEntry->m_CommitHash,
336 // headhash);
337 //But I am not sure if a '|' is going to work in a CreateProcess() call.
339 //Later the progress dialog could be used to execute these steps.
341 if(CherryPickFrom(pFirstEntry->m_CommitHash.ToString(),headhash))
343 CString msg;
344 msg.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));
345 throw std::exception(CUnicodeUtils::GetUTF8(msg));
347 #if 0
348 CString currentBranch=g_Git.GetCurrentBranch();
349 cmd.Format(_T("git.exe rebase --onto \"%s\" %s %s"),
350 currentBranch,
351 pFirstEntry->m_CommitHash,
352 headhash);
353 if(g_Git.Run(cmd,&out,CP_UTF8)!=0)
355 CString msg;
356 msg.Format(_T("Error while rebasing commits on top of combined commits. Aborting.\r\n\r\n%s"),out);
357 // CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);
358 g_Git.Run(_T("git.exe rebase --abort"),&out,CP_UTF8);
359 throw std::exception(CUnicodeUtils::GetUTF8(msg));
362 //HEAD is now on <no branch>.
363 //The following steps are to get HEAD back on the original branch and reset the branch to the new HEAD
364 //To avoid 2 working copy changes, we could use git branch -f <original branch> <hash new head>
365 //And then git checkout <original branch>
366 //But I don't know if 'git branch -f' removes tracking options. So for now, do a checkout and a reset.
368 //Store new HEAD
369 CString newHead=g_Git.GetHash(CString(_T("HEAD")));
371 //Checkout working branch
372 cmd.Format(_T("git.exe checkout -f \"%s\""),currentBranch);
373 if(g_Git.Run(cmd,&out,CP_UTF8))
374 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not checkout original branch. Aborting...\r\n\r\n")+out));
376 //Reset to new HEAD
377 cmd.Format(_T("git.exe reset --hard %s"),newHead);
378 if(g_Git.Run(cmd,&out,CP_UTF8))
379 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to new head. Aborting...\r\n\r\n")+out));
380 #endif
383 else
384 throw std::exception("User aborted the combine process");
386 catch(std::exception& e)
388 CMessageBox::Show(NULL,CUnicodeUtils::GetUnicode(CStringA(e.what())),_T("TortoiseGit: Combine error"),MB_OK|MB_ICONERROR);
389 cmd.Format(_T("git.exe reset --hard %s"),headhash.ToString());
390 out.Empty();
391 if(g_Git.Run(cmd,&out,CP_UTF8))
393 CMessageBox::Show(NULL,_T("Could not reset to original HEAD\r\n\r\n")+out,_T("TortoiseGit"),MB_OK);
396 Refresh();
398 break;
400 case ID_CHERRY_PICK:
401 if(!g_Git.CheckCleanWorkTree())
403 CMessageBox::Show(NULL,_T("Cherry Pick requires a clean working tree"),_T("TortoiseGit"),MB_OK);
405 }else
407 CRebaseDlg dlg;
408 dlg.m_IsCherryPick = TRUE;
409 dlg.m_Upstream = this->m_CurrentBranch;
410 POSITION pos = GetFirstSelectedItemPosition();
411 while(pos)
413 int indexNext = GetNextSelectedItem(pos);
414 dlg.m_CommitList.m_logEntries.push_back( ((GitRev*)m_arShownList[indexNext])->m_CommitHash );
415 dlg.m_CommitList.m_LogCache.m_HashMap[((GitRev*)m_arShownList[indexNext])->m_CommitHash]=*(GitRev*)m_arShownList[indexNext];
416 dlg.m_CommitList.m_logEntries.GetGitRevAt(dlg.m_CommitList.m_logEntries.size()-1).m_Action |= CTGitPath::LOGACTIONS_REBASE_PICK;
419 if(dlg.DoModal() == IDOK)
421 Refresh();
424 break;
425 case ID_REBASE_TO_VERSION:
426 if(!g_Git.CheckCleanWorkTree())
428 CMessageBox::Show(NULL,_T("Rebase requires a clean working tree"),_T("TortoiseGit"),MB_OK);
430 }else
432 CRebaseDlg dlg;
433 dlg.m_Upstream = pSelLogEntry->m_CommitHash;
435 if(dlg.DoModal() == IDOK)
437 Refresh();
441 break;
443 case ID_STASH_APPLY:
444 CAppUtils::StashApply(pSelLogEntry->m_Ref);
445 break;
447 case ID_REFLOG_DEL:
449 CString str;
450 str.Format(_T("Warning: %s will be permanently deleted. It can <ct=0x0000FF><b>NOT</b></ct> be recovered!\r\n \r\n Are you sure you want to continue?"),pSelLogEntry->m_Ref);
451 if(CMessageBox::Show(NULL,str,_T("TortoiseGit"),MB_YESNO|MB_ICONWARNING) == IDYES)
453 CString cmd,out;
454 cmd.Format(_T("git.exe reflog delete %s"),pSelLogEntry->m_Ref);
455 if(g_Git.Run(cmd,&out,CP_ACP))
457 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
459 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
462 break;
463 case ID_CREATE_PATCH:
465 int select=this->GetSelectedCount();
466 CString cmd;
467 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
468 cmd += _T(" /command:formatpatch");
470 cmd += _T(" /path:")+g_Git.m_CurrentDir+_T(" ");
472 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
473 GitRev * r2 = NULL;
474 if(select == 1)
476 cmd += _T(" /startrev:")+r1->m_CommitHash.ToString();
478 else
480 r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
481 if( this->m_IsOldFirst )
483 cmd += _T(" /startrev:")+r1->m_CommitHash.ToString()+_T("~1");
484 cmd += _T(" /endrev:")+r2->m_CommitHash.ToString();
486 }else
488 cmd += _T(" /startrev:")+r2->m_CommitHash.ToString()+_T("~1");
489 cmd += _T(" /endrev:")+r1->m_CommitHash.ToString();
494 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
496 break;
497 case ID_DELETE:
499 int index = cmd>>16;
500 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) == m_HashMap.end() )
502 CMessageBox::Show(NULL,IDS_ERROR_NOREF,IDS_APPNAME,MB_OK|MB_ICONERROR);
503 return;
505 if( index >= m_HashMap[pSelLogEntry->m_CommitHash].size())
507 CMessageBox::Show(NULL,IDS_ERROR_INDEX,IDS_APPNAME,MB_OK|MB_ICONERROR);
508 return;
510 CString ref,msg;
511 ref=m_HashMap[pSelLogEntry->m_CommitHash][index];
513 msg=CString(_T("<ct=0x0000FF>Delete</ct> <b>"))+ref;
514 msg+=_T("</b>\n\n Are you sure?");
515 if( CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_YESNO) == IDYES )
517 CString shortname;
518 CString cmd;
519 if(this->GetShortName(ref,shortname,_T("refs/heads/")))
521 cmd.Format(_T("git.exe branch -D %s"),shortname);
524 if(this->GetShortName(ref,shortname,_T("refs/remotes/")))
526 cmd.Format(_T("git.exe branch -r -D %s"),shortname);
529 if(this->GetShortName(ref,shortname,_T("refs/tags/")))
531 cmd.Format(_T("git.exe tag -d %s"),shortname);
534 if(this->GetShortName(ref,shortname,_T("refs/stash")))
536 if(CMessageBox::Show(NULL,_T("<ct=0x0000FF>Are you sure remove <b>ALL</b> stash?</ct>"),
537 _T("TortoiseGit"),MB_YESNO)==IDYES)
538 cmd.Format(_T("git.exe stash clear"));
539 else
540 return;
543 CString out;
544 if(g_Git.Run(cmd,&out,CP_UTF8))
546 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
548 this->ReloadHashMap();
549 CRect rect;
550 this->GetItemRect(FirstSelect,&rect,LVIR_BOUNDS);
551 this->InvalidateRect(rect);
554 break;
556 case ID_FINDENTRY:
558 m_nSearchIndex = GetSelectionMark();
559 if (m_nSearchIndex < 0)
560 m_nSearchIndex = 0;
561 if (m_pFindDialog)
563 break;
565 else
567 m_pFindDialog = new CFindReplaceDialog();
568 m_pFindDialog->Create(TRUE, NULL, NULL, FR_HIDEUPDOWN | FR_HIDEWHOLEWORD, this);
571 break;
572 case ID_MERGEREV:
574 // we need an URL to complete this command, so error out if we can't get an URL
575 if(CAppUtils::Merge(&pSelLogEntry->m_CommitHash.ToString()))
577 this->Refresh();
580 break;
581 case ID_REVERTREV:
583 if(!g_Git.CheckCleanWorkTree())
585 CMessageBox::Show(NULL,_T("Revert requires a clean working tree"),_T("TortoiseGit"),MB_OK);
587 }else
589 CString cmd, output;
590 cmd.Format(_T("git.exe revert --no-edit --no-commit %s"), pSelLogEntry->m_CommitHash.ToString());
591 if(g_Git.Run(cmd, &output, CP_ACP))
593 CString str;
594 str=_T("Revert fail\n");
595 str+= cmd;
596 str+= _T("\n")+output;
597 CMessageBox::Show(NULL,str, _T("TortoiseGit"),MB_OK|MB_ICONERROR);
599 else
601 Refresh();
605 break;
606 default:
607 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
608 break;
609 #if 0
611 case ID_BLAMECOMPARE:
613 //user clicked on the menu item "compare with working copy"
614 //now first get the revision which is selected
615 if (PromptShown())
617 GitDiff diff(this, this->m_hWnd, true);
618 diff.SetHEADPeg(m_LogRevision);
619 diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
621 else
622 CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
624 break;
625 case ID_BLAMETWO:
627 //user clicked on the menu item "compare and blame revisions"
628 if (PromptShown())
630 GitDiff diff(this, this->m_hWnd, true);
631 diff.SetHEADPeg(m_LogRevision);
632 diff.ShowCompare(CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), false, true);
634 else
635 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
637 break;
638 case ID_BLAMEWITHPREVIOUS:
640 //user clicked on the menu item "Compare and Blame with previous revision"
641 if (PromptShown())
643 GitDiff diff(this, this->m_hWnd, true);
644 diff.SetHEADPeg(m_LogRevision);
645 diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
647 else
648 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
650 break;
652 case ID_OPENWITH:
653 bOpenWith = true;
654 case ID_OPEN:
656 CProgressDlg progDlg;
657 progDlg.SetTitle(IDS_APPNAME);
658 progDlg.SetAnimation(IDR_DOWNLOAD);
659 CString sInfoLine;
660 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
661 progDlg.SetLine(1, sInfoLine, true);
662 SetAndClearProgressInfo(&progDlg);
663 progDlg.ShowModeless(m_hWnd);
664 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
665 bool bSuccess = true;
666 if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
668 bSuccess = false;
669 // try again, but with the selected revision as the peg revision
670 if (!Cat(m_path, revSelected, revSelected, tempfile))
672 progDlg.Stop();
673 SetAndClearProgressInfo((HWND)NULL);
674 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
675 EnableOKButton();
676 break;
678 bSuccess = true;
680 if (bSuccess)
682 progDlg.Stop();
683 SetAndClearProgressInfo((HWND)NULL);
684 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
685 int ret = 0;
686 if (!bOpenWith)
687 ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
688 if ((ret <= HINSTANCE_ERROR)||bOpenWith)
690 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
691 cmd += tempfile.GetWinPathString() + _T(" ");
692 CAppUtils::LaunchApplication(cmd, NULL, false);
696 break;
697 case ID_BLAME:
699 CBlameDlg dlg;
700 dlg.EndRev = revSelected;
701 if (dlg.DoModal() == IDOK)
703 CBlame blame;
704 CString tempfile;
705 CString logfile;
706 tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
707 if (!tempfile.IsEmpty())
709 if (dlg.m_bTextView)
711 //open the default text editor for the result file
712 CAppUtils::StartTextViewer(tempfile);
714 else
716 CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
717 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
719 break;
723 else
725 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
729 break;
730 case ID_UPDATE:
732 CString sCmd;
733 CString url = _T("tgit:")+pathURL;
734 sCmd.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
735 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
736 (LPCTSTR)m_path.GetWinPath(), (LONG)revSelected);
737 CAppUtils::LaunchApplication(sCmd, NULL, false);
739 break;
741 case ID_REPOBROWSE:
743 CString sCmd;
744 sCmd.Format(_T("%s /command:repobrowser /path:\"%s\" /rev:%s"),
745 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
746 (LPCTSTR)pathURL, (LPCTSTR)revSelected.ToString());
748 CAppUtils::LaunchApplication(sCmd, NULL, false);
750 break;
751 case ID_EDITLOG:
753 EditLogMessage(selIndex);
755 break;
756 case ID_EDITAUTHOR:
758 EditAuthor(selEntries);
760 break;
761 case ID_REVPROPS:
763 CEditPropertiesDlg dlg;
764 dlg.SetProjectProperties(&m_ProjectProperties);
765 CTGitPathList escapedlist;
766 dlg.SetPathList(CTGitPathList(CTGitPath(pathURL)));
767 dlg.SetRevision(revSelected);
768 dlg.RevProps(true);
769 dlg.DoModal();
771 break;
773 case ID_EXPORT:
775 CString sCmd;
776 sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
777 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
778 (LPCTSTR)pathURL, (LONG)revSelected);
779 CAppUtils::LaunchApplication(sCmd, NULL, false);
781 break;
782 case ID_CHECKOUT:
784 CString sCmd;
785 CString url = _T("tgit:")+pathURL;
786 sCmd.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),
787 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
788 (LPCTSTR)url, (LONG)revSelected);
789 CAppUtils::LaunchApplication(sCmd, NULL, false);
791 break;
792 case ID_VIEWREV:
794 CString url = m_ProjectProperties.sWebViewerRev;
795 url = GetAbsoluteUrlFromRelativeUrl(url);
796 url.Replace(_T("%REVISION%"), revSelected.ToString());
797 if (!url.IsEmpty())
798 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
800 break;
801 case ID_VIEWPATHREV:
803 CString relurl = pathURL;
804 CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
805 relurl = relurl.Mid(sRoot.GetLength());
806 CString url = m_ProjectProperties.sWebViewerPathRev;
807 url = GetAbsoluteUrlFromRelativeUrl(url);
808 url.Replace(_T("%REVISION%"), revSelected.ToString());
809 url.Replace(_T("%PATH%"), relurl);
810 if (!url.IsEmpty())
811 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
813 break;
814 #endif
816 } // switch (cmd)
818 theApp.DoWaitCursor(-1);
821 void CGitLogList::SetSelectedAction(int action)
823 POSITION pos = GetFirstSelectedItemPosition();
824 int index;
825 while(pos)
827 index = GetNextSelectedItem(pos);
828 ((GitRev*)m_arShownList[index])->m_Action = action;
829 CRect rect;
830 this->GetItemRect(index,&rect,LVIR_BOUNDS);
831 this->InvalidateRect(rect);