code cleanup
[TortoiseGit.git] / src / TortoiseProc / GitLogListAction.cpp
blob7ac13c18eb27283f2b9a40f062e210dc209440bd
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::RevertSelectedCommits()
57 CSysProgressDlg progress;
58 int ret = -1;
60 #if 0
61 if(!g_Git.CheckCleanWorkTree())
63 CMessageBox::Show(NULL,_T("Revert requires a clean working tree"),_T("TortoiseGit"),MB_OK);
66 #endif
68 if (progress.IsValid() && (this->GetSelectedCount() > 1) )
70 progress.SetTitle(_T("Revert Commit"));
71 progress.SetAnimation(IDR_MOVEANI);
72 progress.SetTime(true);
73 progress.ShowModeless(this);
76 POSITION pos = GetFirstSelectedItemPosition();
77 int i=0;
78 while(pos)
80 int index = GetNextSelectedItem(pos);
81 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(index));
83 if (progress.IsValid() && (this->GetSelectedCount() > 1) )
85 progress.FormatPathLine(1, _T("Revert %s"), r1->m_CommitHash.ToString());
86 progress.FormatPathLine(2, _T("%s"), r1->GetSubject());
87 progress.SetProgress(i, this->GetSelectedCount());
89 i++;
91 if(r1->m_CommitHash.IsEmpty())
92 continue;
94 CString cmd, output;
95 cmd.Format(_T("git.exe revert --no-edit --no-commit %s"), r1->m_CommitHash.ToString());
96 if(g_Git.Run(cmd, &output, CP_ACP))
98 CString str;
99 str=_T("Revert fail\n");
100 str+= cmd;
101 str+= _T("\n")+output;
102 if( GetSelectedCount() == 1)
103 CMessageBox::Show(NULL,str, _T("TortoiseGit"),MB_OK|MB_ICONERROR);
104 else
106 if(CMessageBox::Show(NULL, str, _T("TortoiseGit"),2, IDI_ERROR, _T("Skip"), _T("Abort")) ==2)
108 return ret;
112 else
114 ret =0;
117 if ((progress.IsValid())&&(progress.HasUserCancelled()))
118 break;
120 return ret;
122 int CGitLogList::CherryPickFrom(CString from, CString to)
124 CLogDataVector logs(&m_LogCache);
125 if(logs.ParserFromLog(NULL,-1,0,&from,&to))
126 return -1;
128 if(logs.size() == 0)
129 return 0;
131 CSysProgressDlg progress;
132 if (progress.IsValid())
134 progress.SetTitle(_T("Cherry Pick"));
135 progress.SetAnimation(IDR_MOVEANI);
136 progress.SetTime(true);
137 progress.ShowModeless(this);
140 for(int i=logs.size()-1;i>=0;i--)
142 if (progress.IsValid())
144 progress.FormatPathLine(1, _T("Pick up %s"), logs.GetGitRevAt(i).m_CommitHash.ToString());
145 progress.FormatPathLine(2, _T("%s"), logs.GetGitRevAt(i).GetSubject());
146 progress.SetProgress(logs.size()-i, logs.size());
148 if ((progress.IsValid())&&(progress.HasUserCancelled()))
150 //CMessageBox::Show(hwndExplorer, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_ICONINFORMATION);
151 throw std::exception(CUnicodeUtils::GetUTF8(_T("User canceled\r\n\r\n")));
152 return -1;
154 CString cmd,out;
155 cmd.Format(_T("git.exe cherry-pick %s"),logs.GetGitRevAt(i).m_CommitHash.ToString());
156 out.Empty();
157 if(g_Git.Run(cmd,&out,CP_UTF8))
159 throw std::exception(CUnicodeUtils::GetUTF8(CString(_T("Cherry Pick Failure\r\n\r\n"))+out));
160 return -1;
164 return 0;
167 void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect, CMenu *popmenu)
169 POSITION pos = GetFirstSelectedItemPosition();
170 int indexNext = GetNextSelectedItem(pos);
171 if (indexNext < 0)
172 return;
174 GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));
176 theApp.DoWaitCursor(1);
177 switch (cmd&0xFFFF)
179 case ID_COMMIT:
181 CTGitPathList pathlist;
182 CTGitPathList selectedlist;
183 pathlist.AddPath(this->m_Path);
184 bool bSelectFilesForCommit = !!DWORD(CRegStdWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE));
185 CString str;
186 CAppUtils::Commit(CString(),false,str,
187 pathlist,selectedlist,bSelectFilesForCommit);
188 //this->Refresh();
189 this->GetParent()->PostMessage(WM_COMMAND,ID_LOGDLG_REFRESH,0);
191 break;
192 case ID_GNUDIFF1:
194 CString tempfile=GetTempFile();
195 CString command;
196 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
197 if(!r1->m_CommitHash.IsEmpty())
199 CString merge;
200 CString hash2;
201 cmd >>= 16;
202 if( (cmd&0xFFFF) == 0xFFFF)
204 merge=_T("-m");
205 } else if((cmd&0xFFFF) == 0xFFFE)
207 merge=_T("-c");
208 }else
210 if(cmd > r1->m_ParentHash.size())
212 CString str;
213 str.Format(_T("%d parent does not exist"), cmd);
214 CMessageBox::Show(NULL,str,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
215 return;
216 }else
218 if(cmd>0)
219 hash2 = r1->m_ParentHash[cmd-1].ToString();
222 command.Format(_T("git.exe diff-tree %s -r -p --stat %s %s"),merge, r1->m_CommitHash.ToString(), hash2);
223 }else
224 command.Format(_T("git.exe diff -r -p --stat"));
226 g_Git.RunLogFile(command,tempfile);
227 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.ToString().Left(6)+_T(":")+r1->GetSubject());
229 break;
231 case ID_GNUDIFF2:
233 CString tempfile=GetTempFile();
234 CString cmd;
235 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
236 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
238 if( r1->m_CommitHash.IsEmpty()) {
239 cmd.Format(_T("git.exe diff -r -p --stat %s"),r2->m_CommitHash.ToString());
240 }else if( r2->m_CommitHash.IsEmpty()) {
241 cmd.Format(_T("git.exe diff -r -p --stat %s"),r1->m_CommitHash.ToString());
242 }else {
243 cmd.Format(_T("git.exe diff-tree -r -p --stat %s %s"),r2->m_CommitHash.ToString(),r1->m_CommitHash.ToString());
246 g_Git.RunLogFile(cmd,tempfile);
247 CAppUtils::StartUnifiedDiffViewer(tempfile,r2->m_CommitHash.ToString().Left(6)+_T(":")+r1->m_CommitHash.ToString().Left(6));
250 break;
252 case ID_COMPARETWO:
254 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
255 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
256 CGitDiff::DiffCommit(this->m_Path, r1,r2);
259 break;
261 case ID_COMPARE:
263 GitRev * r1 = &m_wcRev;
264 GitRev * r2 = pSelLogEntry;
266 CGitDiff::DiffCommit(this->m_Path, r1,r2);
268 //user clicked on the menu item "compare with working copy"
269 //if (PromptShown())
271 // GitDiff diff(this, m_hWnd, true);
272 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
273 // diff.SetHEADPeg(m_LogRevision);
274 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
276 //else
277 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
279 break;
281 case ID_COMPAREWITHPREVIOUS:
284 CFileDiffDlg dlg;
286 if(pSelLogEntry->m_ParentHash.size()>0)
287 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
289 cmd>>=16;
290 cmd&=0xFFFF;
292 if(cmd == 0)
293 cmd=1;
295 CGitDiff::DiffCommit(this->m_Path, pSelLogEntry->m_CommitHash.ToString(),pSelLogEntry->m_ParentHash[cmd-1].ToString());
297 }else
299 CMessageBox::Show(NULL,_T("No previous version"),_T("TortoiseGit"),MB_OK);
301 //if (PromptShown())
303 // GitDiff diff(this, m_hWnd, true);
304 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
305 // diff.SetHEADPeg(m_LogRevision);
306 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
308 //else
309 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
311 break;
312 case ID_COPYCLIPBOARD:
314 CopySelectionToClipBoard();
316 break;
317 case ID_COPYHASH:
319 CopySelectionToClipBoard(TRUE);
321 break;
322 case ID_EXPORT:
323 CAppUtils::Export(&pSelLogEntry->m_CommitHash.ToString());
324 break;
325 case ID_CREATE_BRANCH:
326 CAppUtils::CreateBranchTag(FALSE,&pSelLogEntry->m_CommitHash.ToString());
327 ReloadHashMap();
328 Invalidate();
329 break;
330 case ID_CREATE_TAG:
331 CAppUtils::CreateBranchTag(TRUE,&pSelLogEntry->m_CommitHash.ToString());
332 ReloadHashMap();
333 Invalidate();
334 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
335 break;
336 case ID_SWITCHTOREV:
337 CAppUtils::Switch(&pSelLogEntry->m_CommitHash.ToString());
338 ReloadHashMap();
339 Invalidate();
340 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
341 break;
342 case ID_SWITCHBRANCH:
343 if(popmenu)
345 CString *branch = (CString*)((CIconMenu*)popmenu)->GetMenuItemData(cmd);
346 if(branch)
349 CProgressDlg progress;
350 CString name;
351 if(branch->Find(_T("refs/heads/")) ==0 )
352 name = branch->Mid(11);
353 else
354 name = *branch;
356 progress.m_GitCmd.Format(_T("git.exe checkout %s"), name.GetString());
357 progress.DoModal();
359 ReloadHashMap();
360 Invalidate();
361 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
363 break;
364 case ID_RESET:
365 CAppUtils::GitReset(&pSelLogEntry->m_CommitHash.ToString());
366 ReloadHashMap();
367 Invalidate();
368 break;
369 case ID_REBASE_PICK:
370 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_PICK);
371 break;
372 case ID_REBASE_EDIT:
373 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_EDIT);
374 break;
375 case ID_REBASE_SQUASH:
376 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SQUASH);
377 break;
378 case ID_REBASE_SKIP:
379 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SKIP);
380 break;
381 case ID_COMBINE_COMMIT:
383 CString head;
384 CGitHash headhash;
385 CGitHash hashFirst,hashLast;
387 int headindex=GetHeadIndex();
388 if(headindex>=0) //incase show all branch, head is not the first commits.
390 head.Format(_T("HEAD~%d"),FirstSelect-headindex);
391 hashFirst=g_Git.GetHash(head);
393 head.Format(_T("HEAD~%d"),LastSelect-headindex);
394 hashLast=g_Git.GetHash(head);
397 GitRev* pFirstEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
398 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
399 if(pFirstEntry->m_CommitHash != hashFirst || pLastEntry->m_CommitHash != hashLast)
401 CMessageBox::Show(NULL,_T("Cannot combine commits now.\r\nMake sure you are viewing the log of your current branch and no filters are applied."),_T("TortoiseGit"),MB_OK);
402 break;
405 headhash=g_Git.GetHash(_T("HEAD"));
407 if(!g_Git.CheckCleanWorkTree())
409 CMessageBox::Show(NULL,_T("Combine needs a clean work tree"),_T("TortoiseGit"),MB_OK);
410 break;
412 CString cmd,out;
414 //Use throw to abort this process (reset back to original HEAD)
417 cmd.Format(_T("git.exe reset --hard %s"),pFirstEntry->m_CommitHash.ToString());
418 if(g_Git.Run(cmd,&out,CP_UTF8))
420 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
421 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to first commit (first step) aborting...\r\n\r\n")+out));
423 cmd.Format(_T("git.exe reset --mixed %s"),hashLast.ToString());
424 if(g_Git.Run(cmd,&out,CP_UTF8))
426 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
427 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to last commit (second step) aborting...\r\n\r\n")+out));
430 CTGitPathList PathList;
431 /* don't why must add --stat to get action status*/
432 /* first -z will be omitted by gitdll*/
433 if(g_Git.GetDiffPath(&PathList,&pFirstEntry->m_CommitHash,&hashLast,"-z --stat -r"))
435 CMessageBox::Show(NULL,_T("Get Diff file list error"),_T("TortoiseGit"),MB_OK);
436 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not get changed file list aborting...\r\n\r\n")+out));
439 for(int i=0;i<PathList.GetCount();i++)
441 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_ADDED)
443 cmd.Format(_T("git.exe add \"%s\""), PathList[i].GetGitPathString());
444 if(g_Git.Run(cmd,&out,CP_ACP))
446 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
447 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could add new file aborting...\r\n\r\n")+out));
451 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_DELETED)
453 cmd.Format(_T("git.exe rm --cached \"%s\""), PathList[i].GetGitPathString());
454 if(g_Git.Run(cmd,&out,CP_ACP))
456 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
457 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could add new file aborting...\r\n\r\n")+out));
462 CCommitDlg dlg;
463 for(int i=FirstSelect;i<=LastSelect;i++)
465 GitRev* pRev = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
466 dlg.m_sLogMessage+=pRev->GetSubject()+_T("\n")+pRev->GetBody();
467 dlg.m_sLogMessage+=_T("\n");
469 dlg.m_bWholeProject=true;
470 dlg.m_bSelectFilesForCommit = true;
471 dlg.m_bCommitAmend=true;
472 dlg.m_bNoPostActions=true;
473 dlg.m_AmendStr=dlg.m_sLogMessage;
475 if (dlg.DoModal() == IDOK)
477 if(pFirstEntry->m_CommitHash!=headhash)
479 //Commitrange firstEntry..headhash (from top of combine to original head) needs to be 'cherry-picked'
480 //on top of new commit.
481 //Use the rebase --onto command for it.
483 //All this can be done in one step using the following command:
484 //cmd.Format(_T("git.exe format-patch --stdout --binary --full-index -k %s..%s | git am -k -3"),
485 // pFirstEntry->m_CommitHash,
486 // headhash);
487 //But I am not sure if a '|' is going to work in a CreateProcess() call.
489 //Later the progress dialog could be used to execute these steps.
491 if(CherryPickFrom(pFirstEntry->m_CommitHash.ToString(),headhash))
493 CString msg;
494 msg.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));
495 throw std::exception(CUnicodeUtils::GetUTF8(msg));
497 #if 0
498 CString currentBranch=g_Git.GetCurrentBranch();
499 cmd.Format(_T("git.exe rebase --onto \"%s\" %s %s"),
500 currentBranch,
501 pFirstEntry->m_CommitHash,
502 headhash);
503 if(g_Git.Run(cmd,&out,CP_UTF8)!=0)
505 CString msg;
506 msg.Format(_T("Error while rebasing commits on top of combined commits. Aborting.\r\n\r\n%s"),out);
507 // CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);
508 g_Git.Run(_T("git.exe rebase --abort"),&out,CP_UTF8);
509 throw std::exception(CUnicodeUtils::GetUTF8(msg));
512 //HEAD is now on <no branch>.
513 //The following steps are to get HEAD back on the original branch and reset the branch to the new HEAD
514 //To avoid 2 working copy changes, we could use git branch -f <original branch> <hash new head>
515 //And then git checkout <original branch>
516 //But I don't know if 'git branch -f' removes tracking options. So for now, do a checkout and a reset.
518 //Store new HEAD
519 CString newHead=g_Git.GetHash(CString(_T("HEAD")));
521 //Checkout working branch
522 cmd.Format(_T("git.exe checkout -f \"%s\""),currentBranch);
523 if(g_Git.Run(cmd,&out,CP_UTF8))
524 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not checkout original branch. Aborting...\r\n\r\n")+out));
526 //Reset to new HEAD
527 cmd.Format(_T("git.exe reset --hard %s"),newHead);
528 if(g_Git.Run(cmd,&out,CP_UTF8))
529 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to new head. Aborting...\r\n\r\n")+out));
530 #endif
533 else
534 throw std::exception("User aborted the combine process");
536 catch(std::exception& e)
538 CMessageBox::Show(NULL,CUnicodeUtils::GetUnicode(CStringA(e.what())),_T("TortoiseGit: Combine error"),MB_OK|MB_ICONERROR);
539 cmd.Format(_T("git.exe reset --hard %s"),headhash.ToString());
540 out.Empty();
541 if(g_Git.Run(cmd,&out,CP_UTF8))
543 CMessageBox::Show(NULL,_T("Could not reset to original HEAD\r\n\r\n")+out,_T("TortoiseGit"),MB_OK);
546 Refresh();
548 break;
550 case ID_CHERRY_PICK:
551 if(!g_Git.CheckCleanWorkTree())
553 CMessageBox::Show(NULL,_T("Cherry Pick requires a clean working tree"),_T("TortoiseGit"),MB_OK);
555 }else
557 CRebaseDlg dlg;
558 dlg.m_IsCherryPick = TRUE;
559 dlg.m_Upstream = this->m_CurrentBranch;
560 POSITION pos = GetFirstSelectedItemPosition();
561 while(pos)
563 int indexNext = GetNextSelectedItem(pos);
564 dlg.m_CommitList.m_logEntries.push_back( ((GitRev*)m_arShownList[indexNext])->m_CommitHash );
565 dlg.m_CommitList.m_LogCache.m_HashMap[((GitRev*)m_arShownList[indexNext])->m_CommitHash]=*(GitRev*)m_arShownList[indexNext];
566 dlg.m_CommitList.m_logEntries.GetGitRevAt(dlg.m_CommitList.m_logEntries.size()-1).GetAction(this) |= CTGitPath::LOGACTIONS_REBASE_PICK;
569 if(dlg.DoModal() == IDOK)
571 Refresh();
574 break;
575 case ID_REBASE_TO_VERSION:
576 if(!g_Git.CheckCleanWorkTree())
578 CMessageBox::Show(NULL,_T("Rebase requires a clean working tree"),_T("TortoiseGit"),MB_OK);
580 }else
582 CRebaseDlg dlg;
583 dlg.m_Upstream = pSelLogEntry->m_CommitHash;
585 if(dlg.DoModal() == IDOK)
587 Refresh();
591 break;
593 case ID_STASH_APPLY:
594 CAppUtils::StashApply(pSelLogEntry->m_Ref);
595 break;
597 case ID_REFLOG_DEL:
599 CString str;
600 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);
601 if(CMessageBox::Show(NULL,str,_T("TortoiseGit"),MB_YESNO|MB_ICONWARNING) == IDYES)
603 CString cmd,out;
604 cmd.Format(_T("git.exe reflog delete %s"),pSelLogEntry->m_Ref);
605 if(g_Git.Run(cmd,&out,CP_ACP))
607 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
609 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
612 break;
613 case ID_LOG:
615 CString cmd;
616 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
617 cmd += _T(" /command:log");
618 cmd += _T(" /path:\"")+g_Git.m_CurrentDir+_T("\" ");
619 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
620 cmd += _T(" /endrev:")+r1->m_CommitHash.ToString();
621 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
623 break;
624 case ID_CREATE_PATCH:
626 int select=this->GetSelectedCount();
627 CString cmd;
628 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
629 cmd += _T(" /command:formatpatch");
631 cmd += _T(" /path:\"")+g_Git.m_CurrentDir+_T("\" ");
633 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
634 GitRev * r2 = NULL;
635 if(select == 1)
637 cmd += _T(" /startrev:")+r1->m_CommitHash.ToString();
639 else
641 r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
642 if( this->m_IsOldFirst )
644 cmd += _T(" /startrev:")+r1->m_CommitHash.ToString()+_T("~1");
645 cmd += _T(" /endrev:")+r2->m_CommitHash.ToString();
647 }else
649 cmd += _T(" /startrev:")+r2->m_CommitHash.ToString()+_T("~1");
650 cmd += _T(" /endrev:")+r1->m_CommitHash.ToString();
655 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
657 break;
658 case ID_DELETE:
660 int index = cmd>>16;
661 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) == m_HashMap.end() )
663 CMessageBox::Show(NULL,IDS_ERROR_NOREF,IDS_APPNAME,MB_OK|MB_ICONERROR);
664 return;
666 if( index >= m_HashMap[pSelLogEntry->m_CommitHash].size())
668 CMessageBox::Show(NULL,IDS_ERROR_INDEX,IDS_APPNAME,MB_OK|MB_ICONERROR);
669 return;
671 CString ref,msg;
672 ref=m_HashMap[pSelLogEntry->m_CommitHash][index];
674 msg=CString(_T("<ct=0x0000FF>Delete</ct> <b>"))+ref;
675 msg+=_T("</b>\n\n Are you sure?");
676 if( CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_YESNO) == IDYES )
678 CString shortname;
679 CString cmd;
680 if(this->GetShortName(ref,shortname,_T("refs/heads/")))
682 cmd.Format(_T("git.exe branch -D %s"),shortname);
685 if(this->GetShortName(ref,shortname,_T("refs/remotes/")))
687 cmd.Format(_T("git.exe branch -r -D %s"),shortname);
690 if(this->GetShortName(ref,shortname,_T("refs/tags/")))
692 cmd.Format(_T("git.exe tag -d %s"),shortname);
695 if(this->GetShortName(ref,shortname,_T("refs/stash")))
697 if(CMessageBox::Show(NULL,_T("<ct=0x0000FF>Are you sure remove <b>ALL</b> stash?</ct>"),
698 _T("TortoiseGit"),MB_YESNO)==IDYES)
699 cmd.Format(_T("git.exe stash clear"));
700 else
701 return;
704 CString out;
705 if(g_Git.Run(cmd,&out,CP_UTF8))
707 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
709 this->ReloadHashMap();
710 CRect rect;
711 this->GetItemRect(FirstSelect,&rect,LVIR_BOUNDS);
712 this->InvalidateRect(rect);
715 break;
717 case ID_FINDENTRY:
719 m_nSearchIndex = GetSelectionMark();
720 if (m_nSearchIndex < 0)
721 m_nSearchIndex = 0;
722 if (m_pFindDialog)
724 break;
726 else
728 m_pFindDialog = new CFindDlg();
729 m_pFindDialog->Create(this);
732 break;
733 case ID_MERGEREV:
735 // we need an URL to complete this command, so error out if we can't get an URL
736 if(CAppUtils::Merge(&pSelLogEntry->m_CommitHash.ToString()))
738 this->Refresh();
741 break;
742 case ID_REVERTREV:
744 if(!this->RevertSelectedCommits())
745 this->Refresh();
747 break;
748 case ID_EDITNOTE:
750 CAppUtils::EditNote(pSelLogEntry);
751 this->SetItemState(FirstSelect, 0, LVIS_SELECTED);
752 this->SetItemState(FirstSelect, LVIS_SELECTED, LVIS_SELECTED);
754 break;
755 default:
756 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
757 break;
758 #if 0
760 case ID_BLAMECOMPARE:
762 //user clicked on the menu item "compare with working copy"
763 //now first get the revision which is selected
764 if (PromptShown())
766 GitDiff diff(this, this->m_hWnd, true);
767 diff.SetHEADPeg(m_LogRevision);
768 diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
770 else
771 CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
773 break;
774 case ID_BLAMETWO:
776 //user clicked on the menu item "compare and blame revisions"
777 if (PromptShown())
779 GitDiff diff(this, this->m_hWnd, true);
780 diff.SetHEADPeg(m_LogRevision);
781 diff.ShowCompare(CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), false, true);
783 else
784 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
786 break;
787 case ID_BLAMEWITHPREVIOUS:
789 //user clicked on the menu item "Compare and Blame with previous revision"
790 if (PromptShown())
792 GitDiff diff(this, this->m_hWnd, true);
793 diff.SetHEADPeg(m_LogRevision);
794 diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
796 else
797 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
799 break;
801 case ID_OPENWITH:
802 bOpenWith = true;
803 case ID_OPEN:
805 CProgressDlg progDlg;
806 progDlg.SetTitle(IDS_APPNAME);
807 progDlg.SetAnimation(IDR_DOWNLOAD);
808 CString sInfoLine;
809 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
810 progDlg.SetLine(1, sInfoLine, true);
811 SetAndClearProgressInfo(&progDlg);
812 progDlg.ShowModeless(m_hWnd);
813 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
814 bool bSuccess = true;
815 if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
817 bSuccess = false;
818 // try again, but with the selected revision as the peg revision
819 if (!Cat(m_path, revSelected, revSelected, tempfile))
821 progDlg.Stop();
822 SetAndClearProgressInfo((HWND)NULL);
823 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
824 EnableOKButton();
825 break;
827 bSuccess = true;
829 if (bSuccess)
831 progDlg.Stop();
832 SetAndClearProgressInfo((HWND)NULL);
833 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
834 int ret = 0;
835 if (!bOpenWith)
836 ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
837 if ((ret <= HINSTANCE_ERROR)||bOpenWith)
839 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
840 cmd += tempfile.GetWinPathString() + _T(" ");
841 CAppUtils::LaunchApplication(cmd, NULL, false);
845 break;
846 case ID_BLAME:
848 CBlameDlg dlg;
849 dlg.EndRev = revSelected;
850 if (dlg.DoModal() == IDOK)
852 CBlame blame;
853 CString tempfile;
854 CString logfile;
855 tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
856 if (!tempfile.IsEmpty())
858 if (dlg.m_bTextView)
860 //open the default text editor for the result file
861 CAppUtils::StartTextViewer(tempfile);
863 else
865 CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
866 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
868 break;
872 else
874 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
878 break;
879 case ID_UPDATE:
881 CString sCmd;
882 CString url = _T("tgit:")+pathURL;
883 sCmd.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
884 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
885 (LPCTSTR)m_path.GetWinPath(), (LONG)revSelected);
886 CAppUtils::LaunchApplication(sCmd, NULL, false);
888 break;
890 case ID_REPOBROWSE:
892 CString sCmd;
893 sCmd.Format(_T("%s /command:repobrowser /path:\"%s\" /rev:%s"),
894 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
895 (LPCTSTR)pathURL, (LPCTSTR)revSelected.ToString());
897 CAppUtils::LaunchApplication(sCmd, NULL, false);
899 break;
900 case ID_EDITLOG:
902 EditLogMessage(selIndex);
904 break;
905 case ID_EDITAUTHOR:
907 EditAuthor(selEntries);
909 break;
910 case ID_REVPROPS:
912 CEditPropertiesDlg dlg;
913 dlg.SetProjectProperties(&m_ProjectProperties);
914 CTGitPathList escapedlist;
915 dlg.SetPathList(CTGitPathList(CTGitPath(pathURL)));
916 dlg.SetRevision(revSelected);
917 dlg.RevProps(true);
918 dlg.DoModal();
920 break;
922 case ID_EXPORT:
924 CString sCmd;
925 sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
926 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
927 (LPCTSTR)pathURL, (LONG)revSelected);
928 CAppUtils::LaunchApplication(sCmd, NULL, false);
930 break;
931 case ID_CHECKOUT:
933 CString sCmd;
934 CString url = _T("tgit:")+pathURL;
935 sCmd.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),
936 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
937 (LPCTSTR)url, (LONG)revSelected);
938 CAppUtils::LaunchApplication(sCmd, NULL, false);
940 break;
941 case ID_VIEWREV:
943 CString url = m_ProjectProperties.sWebViewerRev;
944 url = GetAbsoluteUrlFromRelativeUrl(url);
945 url.Replace(_T("%REVISION%"), revSelected.ToString());
946 if (!url.IsEmpty())
947 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
949 break;
950 case ID_VIEWPATHREV:
952 CString relurl = pathURL;
953 CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
954 relurl = relurl.Mid(sRoot.GetLength());
955 CString url = m_ProjectProperties.sWebViewerPathRev;
956 url = GetAbsoluteUrlFromRelativeUrl(url);
957 url.Replace(_T("%REVISION%"), revSelected.ToString());
958 url.Replace(_T("%PATH%"), relurl);
959 if (!url.IsEmpty())
960 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
962 break;
963 #endif
965 } // switch (cmd)
967 theApp.DoWaitCursor(-1);
970 void CGitLogList::SetSelectedAction(int action)
972 POSITION pos = GetFirstSelectedItemPosition();
973 int index;
974 while(pos)
976 index = GetNextSelectedItem(pos);
977 ((GitRev*)m_arShownList[index])->GetAction(this) = action;
978 CRect rect;
979 this->GetItemRect(index,&rect,LVIR_BOUNDS);
980 this->InvalidateRect(rect);