improve messageboxes
[TortoiseGit.git] / src / TortoiseProc / GitLogListAction.cpp
blobe3dd203809b9f5f35c4b7dfd3a9d9c0ab9d0eb17
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2011 - TortoiseGit
4 // Copyright (C) 2005-2007 Marco Costalba
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.
20 // GitLogList.cpp : implementation file
22 #include "stdafx.h"
23 #include "TortoiseProc.h"
24 #include "GitLogList.h"
25 #include "GitRev.h"
26 //#include "VssStyle.h"
27 #include "IconMenu.h"
28 // CGitLogList
29 #include "cursor.h"
30 #include "InputDlg.h"
31 #include "PropDlg.h"
32 #include "SVNProgressDlg.h"
33 #include "ProgressDlg.h"
34 #include "SysProgressDlg.h"
35 //#include "RepositoryBrowser.h"
36 //#include "CopyDlg.h"
37 //#include "StatGraphDlg.h"
38 #include "Logdlg.h"
39 #include "MessageBox.h"
40 #include "Registry.h"
41 #include "AppUtils.h"
42 #include "PathUtils.h"
43 #include "StringUtils.h"
44 #include "UnicodeUtils.h"
45 #include "TempFile.h"
46 //#include "GitInfo.h"
47 //#include "GitDiff.h"
48 #include "IconMenu.h"
49 //#include "RevisionRangeDlg.h"
50 //#include "BrowseFolder.h"
51 //#include "BlameDlg.h"
52 //#include "Blame.h"
53 //#include "GitHelpers.h"
54 #include "GitStatus.h"
55 //#include "LogDlgHelper.h"
56 //#include "CachedLogInfo.h"
57 //#include "RepositoryInfo.h"
58 //#include "EditPropertiesDlg.h"
59 #include "FileDiffDlg.h"
60 #include "CommitDlg.h"
61 #include "RebaseDlg.h"
62 #include "GitDiff.h"
64 IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl)
66 int CGitLogList::RevertSelectedCommits()
68 CSysProgressDlg progress;
69 int ret = -1;
71 #if 0
72 if(!g_Git.CheckCleanWorkTree())
74 CMessageBox::Show(NULL,_T("Revert requires a clean working tree"),_T("TortoiseGit"),MB_OK);
77 #endif
79 if (progress.IsValid() && (this->GetSelectedCount() > 1) )
81 progress.SetTitle(_T("Revert Commit"));
82 progress.SetAnimation(IDR_MOVEANI);
83 progress.SetTime(true);
84 progress.ShowModeless(this);
87 POSITION pos = GetFirstSelectedItemPosition();
88 int i=0;
89 while(pos)
91 int index = GetNextSelectedItem(pos);
92 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(index));
94 if (progress.IsValid() && (this->GetSelectedCount() > 1) )
96 progress.FormatPathLine(1, _T("Revert %s"), r1->m_CommitHash.ToString());
97 progress.FormatPathLine(2, _T("%s"), r1->GetSubject());
98 progress.SetProgress(i, this->GetSelectedCount());
100 i++;
102 if(r1->m_CommitHash.IsEmpty())
103 continue;
105 CString cmd, output;
106 cmd.Format(_T("git.exe revert --no-edit --no-commit %s"), r1->m_CommitHash.ToString());
107 if(g_Git.Run(cmd, &output, CP_ACP))
109 CString str;
110 str=_T("Revert fail\n");
111 str+= cmd;
112 str+= _T("\n")+output;
113 if( GetSelectedCount() == 1)
114 CMessageBox::Show(NULL,str, _T("TortoiseGit"),MB_OK|MB_ICONERROR);
115 else
117 if(CMessageBox::Show(NULL, str, _T("TortoiseGit"),2 , IDI_ERROR, _T("&Skip"), _T("&Abort")) == 2)
119 return ret;
123 else
125 ret =0;
128 if ((progress.IsValid())&&(progress.HasUserCancelled()))
129 break;
131 return ret;
133 int CGitLogList::CherryPickFrom(CString from, CString to)
135 CLogDataVector logs(&m_LogCache);
136 if(logs.ParserFromLog(NULL,-1,0,&from,&to))
137 return -1;
139 if(logs.size() == 0)
140 return 0;
142 CSysProgressDlg progress;
143 if (progress.IsValid())
145 progress.SetTitle(_T("Cherry Pick"));
146 progress.SetAnimation(IDR_MOVEANI);
147 progress.SetTime(true);
148 progress.ShowModeless(this);
151 for(int i=logs.size()-1;i>=0;i--)
153 if (progress.IsValid())
155 progress.FormatPathLine(1, _T("Pick up %s"), logs.GetGitRevAt(i).m_CommitHash.ToString());
156 progress.FormatPathLine(2, _T("%s"), logs.GetGitRevAt(i).GetSubject());
157 progress.SetProgress(logs.size()-i, logs.size());
159 if ((progress.IsValid())&&(progress.HasUserCancelled()))
161 //CMessageBox::Show(hwndExplorer, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_ICONINFORMATION);
162 throw std::exception(CUnicodeUtils::GetUTF8(_T("User canceled\r\n\r\n")));
163 return -1;
165 CString cmd,out;
166 cmd.Format(_T("git.exe cherry-pick %s"),logs.GetGitRevAt(i).m_CommitHash.ToString());
167 out.Empty();
168 if(g_Git.Run(cmd,&out,CP_UTF8))
170 throw std::exception(CUnicodeUtils::GetUTF8(CString(_T("Cherry Pick Failure\r\n\r\n"))+out));
171 return -1;
175 return 0;
178 void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect, CMenu *popmenu)
180 POSITION pos = GetFirstSelectedItemPosition();
181 int indexNext = GetNextSelectedItem(pos);
182 if (indexNext < 0)
183 return;
185 GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));
187 theApp.DoWaitCursor(1);
188 switch (cmd&0xFFFF)
190 case ID_COMMIT:
192 CTGitPathList pathlist;
193 CTGitPathList selectedlist;
194 pathlist.AddPath(this->m_Path);
195 bool bSelectFilesForCommit = !!DWORD(CRegStdWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE));
196 CString str;
197 CAppUtils::Commit(CString(),false,str,
198 pathlist,selectedlist,bSelectFilesForCommit);
199 //this->Refresh();
200 this->GetParent()->PostMessage(WM_COMMAND,ID_LOGDLG_REFRESH,0);
202 break;
203 case ID_GNUDIFF1:
205 CString tempfile=GetTempFile();
206 CString command;
207 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
208 if(!r1->m_CommitHash.IsEmpty())
210 CString merge;
211 CString hash2;
212 cmd >>= 16;
213 if( (cmd&0xFFFF) == 0xFFFF)
215 merge=_T("-m");
216 } else if((cmd&0xFFFF) == 0xFFFE)
218 merge=_T("-c");
219 }else
221 if(cmd > r1->m_ParentHash.size())
223 CString str;
224 str.Format(_T("%d parent does not exist"), cmd);
225 CMessageBox::Show(NULL,str,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
226 return;
227 }else
229 if(cmd>0)
230 hash2 = r1->m_ParentHash[cmd-1].ToString();
233 command.Format(_T("git.exe diff-tree %s -r -p --stat %s %s"),merge, r1->m_CommitHash.ToString(), hash2);
234 }else
235 command.Format(_T("git.exe diff -r -p --stat"));
237 g_Git.RunLogFile(command,tempfile);
238 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.ToString().Left(6)+_T(":")+r1->GetSubject());
240 break;
242 case ID_GNUDIFF2:
244 CString tempfile=GetTempFile();
245 CString cmd;
246 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
247 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
249 if( r1->m_CommitHash.IsEmpty()) {
250 cmd.Format(_T("git.exe diff -r -p --stat %s"),r2->m_CommitHash.ToString());
251 }else if( r2->m_CommitHash.IsEmpty()) {
252 cmd.Format(_T("git.exe diff -r -p --stat %s"),r1->m_CommitHash.ToString());
253 }else {
254 cmd.Format(_T("git.exe diff-tree -r -p --stat %s %s"),r2->m_CommitHash.ToString(),r1->m_CommitHash.ToString());
257 g_Git.RunLogFile(cmd,tempfile);
258 CAppUtils::StartUnifiedDiffViewer(tempfile,r2->m_CommitHash.ToString().Left(6)+_T(":")+r1->m_CommitHash.ToString().Left(6));
261 break;
263 case ID_COMPARETWO:
265 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
266 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
267 CGitDiff::DiffCommit(this->m_Path, r1,r2);
270 break;
272 case ID_COMPARE:
274 GitRev * r1 = &m_wcRev;
275 GitRev * r2 = pSelLogEntry;
277 CGitDiff::DiffCommit(this->m_Path, r1,r2);
279 //user clicked on the menu item "compare with working copy"
280 //if (PromptShown())
282 // GitDiff diff(this, m_hWnd, true);
283 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
284 // diff.SetHEADPeg(m_LogRevision);
285 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
287 //else
288 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
290 break;
292 case ID_COMPAREWITHPREVIOUS:
295 CFileDiffDlg dlg;
297 if(pSelLogEntry->m_ParentHash.size()>0)
298 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
300 cmd>>=16;
301 cmd&=0xFFFF;
303 if(cmd == 0)
304 cmd=1;
306 CGitDiff::DiffCommit(this->m_Path, pSelLogEntry->m_CommitHash.ToString(),pSelLogEntry->m_ParentHash[cmd-1].ToString());
308 }else
310 CMessageBox::Show(NULL,_T("No previous version"),_T("TortoiseGit"),MB_OK);
312 //if (PromptShown())
314 // GitDiff diff(this, m_hWnd, true);
315 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
316 // diff.SetHEADPeg(m_LogRevision);
317 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
319 //else
320 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
322 break;
323 case ID_COPYCLIPBOARD:
325 CopySelectionToClipBoard();
327 break;
328 case ID_COPYHASH:
330 CopySelectionToClipBoard(TRUE);
332 break;
333 case ID_EXPORT:
335 CString str=pSelLogEntry->m_CommitHash.ToString();
336 CAppUtils::Export(&str);
338 break;
339 case ID_CREATE_BRANCH:
341 CString str = pSelLogEntry->m_CommitHash.ToString();
342 CAppUtils::CreateBranchTag(FALSE,&str);
343 ReloadHashMap();
344 Invalidate();
346 break;
347 case ID_CREATE_TAG:
349 CString str = pSelLogEntry->m_CommitHash.ToString();
350 CAppUtils::CreateBranchTag(TRUE,&str);
351 ReloadHashMap();
352 Invalidate();
353 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
355 break;
356 case ID_SWITCHTOREV:
358 CString str = pSelLogEntry->m_CommitHash.ToString();
359 CAppUtils::Switch(&str);
361 ReloadHashMap();
362 Invalidate();
363 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
364 break;
365 case ID_SWITCHBRANCH:
366 if(popmenu)
368 CString *branch = (CString*)((CIconMenu*)popmenu)->GetMenuItemData(cmd);
369 if(branch)
372 CProgressDlg progress;
373 CString name;
374 if(branch->Find(_T("refs/heads/")) ==0 )
375 name = branch->Mid(11);
376 else
377 name = *branch;
379 progress.m_GitCmd.Format(_T("git.exe checkout %s"), name.GetString());
380 progress.DoModal();
382 ReloadHashMap();
383 Invalidate();
384 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
386 break;
387 case ID_RESET:
389 CString str = pSelLogEntry->m_CommitHash.ToString();
390 CAppUtils::GitReset(&str);
391 ReloadHashMap();
392 Invalidate();
394 break;
395 case ID_REBASE_PICK:
396 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_PICK);
397 break;
398 case ID_REBASE_EDIT:
399 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_EDIT);
400 break;
401 case ID_REBASE_SQUASH:
402 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SQUASH);
403 break;
404 case ID_REBASE_SKIP:
405 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SKIP);
406 break;
407 case ID_COMBINE_COMMIT:
409 CString head;
410 CGitHash headhash;
411 CGitHash hashFirst,hashLast;
413 int headindex=GetHeadIndex();
414 if(headindex>=0) //incase show all branch, head is not the first commits.
416 head.Format(_T("HEAD~%d"),FirstSelect-headindex);
417 hashFirst=g_Git.GetHash(head);
419 head.Format(_T("HEAD~%d"),LastSelect-headindex);
420 hashLast=g_Git.GetHash(head);
423 GitRev* pFirstEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
424 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
425 if(pFirstEntry->m_CommitHash != hashFirst || pLastEntry->m_CommitHash != hashLast)
427 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);
428 break;
431 headhash=g_Git.GetHash(_T("HEAD"));
433 if(!g_Git.CheckCleanWorkTree())
435 CMessageBox::Show(NULL,_T("Combine needs a clean work tree"),_T("TortoiseGit"),MB_OK);
436 break;
438 CString cmd,out;
440 //Use throw to abort this process (reset back to original HEAD)
443 cmd.Format(_T("git.exe reset --hard %s"),pFirstEntry->m_CommitHash.ToString());
444 if(g_Git.Run(cmd,&out,CP_UTF8))
446 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
447 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to first commit (first step) aborting...\r\n\r\n")+out));
449 cmd.Format(_T("git.exe reset --mixed %s"),hashLast.ToString());
450 if(g_Git.Run(cmd,&out,CP_UTF8))
452 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
453 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to last commit (second step) aborting...\r\n\r\n")+out));
456 CTGitPathList PathList;
457 /* don't why must add --stat to get action status*/
458 /* first -z will be omitted by gitdll*/
459 if(g_Git.GetDiffPath(&PathList,&pFirstEntry->m_CommitHash,&hashLast,"-z --stat -r"))
461 CMessageBox::Show(NULL,_T("Get Diff file list error"),_T("TortoiseGit"),MB_OK);
462 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not get changed file list aborting...\r\n\r\n")+out));
465 for(int i=0;i<PathList.GetCount();i++)
467 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_ADDED)
469 cmd.Format(_T("git.exe add -- \"%s\""), PathList[i].GetGitPathString());
470 if(g_Git.Run(cmd,&out,CP_ACP))
472 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
473 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not add new file aborting...\r\n\r\n")+out));
477 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_DELETED)
479 cmd.Format(_T("git.exe rm -- \"%s\""), PathList[i].GetGitPathString());
480 if(g_Git.Run(cmd,&out,CP_ACP))
482 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
483 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not rm file aborting...\r\n\r\n")+out));
488 CCommitDlg dlg;
489 for(int i=FirstSelect;i<=LastSelect;i++)
491 GitRev* pRev = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
492 dlg.m_sLogMessage+=pRev->GetSubject()+_T("\n")+pRev->GetBody();
493 dlg.m_sLogMessage+=_T("\n");
495 dlg.m_bWholeProject=true;
496 dlg.m_bSelectFilesForCommit = true;
497 dlg.m_bCommitAmend=true;
498 dlg.m_bNoPostActions=true;
499 dlg.m_AmendStr=dlg.m_sLogMessage;
501 if (dlg.DoModal() == IDOK)
503 if(pFirstEntry->m_CommitHash!=headhash)
505 //Commitrange firstEntry..headhash (from top of combine to original head) needs to be 'cherry-picked'
506 //on top of new commit.
507 //Use the rebase --onto command for it.
509 //All this can be done in one step using the following command:
510 //cmd.Format(_T("git.exe format-patch --stdout --binary --full-index -k %s..%s | git am -k -3"),
511 // pFirstEntry->m_CommitHash,
512 // headhash);
513 //But I am not sure if a '|' is going to work in a CreateProcess() call.
515 //Later the progress dialog could be used to execute these steps.
517 if(CherryPickFrom(pFirstEntry->m_CommitHash.ToString(),headhash))
519 CString msg;
520 msg.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));
521 throw std::exception(CUnicodeUtils::GetUTF8(msg));
523 #if 0
524 CString currentBranch=g_Git.GetCurrentBranch();
525 cmd.Format(_T("git.exe rebase --onto \"%s\" %s %s"),
526 currentBranch,
527 pFirstEntry->m_CommitHash,
528 headhash);
529 if(g_Git.Run(cmd,&out,CP_UTF8)!=0)
531 CString msg;
532 msg.Format(_T("Error while rebasing commits on top of combined commits. Aborting.\r\n\r\n%s"),out);
533 // CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);
534 g_Git.Run(_T("git.exe rebase --abort"),&out,CP_UTF8);
535 throw std::exception(CUnicodeUtils::GetUTF8(msg));
538 //HEAD is now on <no branch>.
539 //The following steps are to get HEAD back on the original branch and reset the branch to the new HEAD
540 //To avoid 2 working copy changes, we could use git branch -f <original branch> <hash new head>
541 //And then git checkout <original branch>
542 //But I don't know if 'git branch -f' removes tracking options. So for now, do a checkout and a reset.
544 //Store new HEAD
545 CString newHead=g_Git.GetHash(CString(_T("HEAD")));
547 //Checkout working branch
548 cmd.Format(_T("git.exe checkout -f \"%s\""),currentBranch);
549 if(g_Git.Run(cmd,&out,CP_UTF8))
550 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not checkout original branch. Aborting...\r\n\r\n")+out));
552 //Reset to new HEAD
553 cmd.Format(_T("git.exe reset --hard %s"),newHead);
554 if(g_Git.Run(cmd,&out,CP_UTF8))
555 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to new head. Aborting...\r\n\r\n")+out));
556 #endif
559 else
560 throw std::exception("User aborted the combine process");
562 catch(std::exception& e)
564 CMessageBox::Show(NULL,CUnicodeUtils::GetUnicode(CStringA(e.what())),_T("TortoiseGit: Combine error"),MB_OK|MB_ICONERROR);
565 cmd.Format(_T("git.exe reset --hard %s"),headhash.ToString());
566 out.Empty();
567 if(g_Git.Run(cmd,&out,CP_UTF8))
569 CMessageBox::Show(NULL,_T("Could not reset to original HEAD\r\n\r\n")+out,_T("TortoiseGit"),MB_OK);
572 Refresh();
574 break;
576 case ID_CHERRY_PICK:
577 if(!g_Git.CheckCleanWorkTree())
579 CMessageBox::Show(NULL,_T("Cherry Pick requires a clean working tree"),_T("TortoiseGit"),MB_OK);
581 }else
583 CRebaseDlg dlg;
584 dlg.m_IsCherryPick = TRUE;
585 dlg.m_Upstream = this->m_CurrentBranch;
586 POSITION pos = GetFirstSelectedItemPosition();
587 while(pos)
589 int indexNext = GetNextSelectedItem(pos);
590 dlg.m_CommitList.m_logEntries.push_back( ((GitRev*)m_arShownList[indexNext])->m_CommitHash );
591 dlg.m_CommitList.m_LogCache.m_HashMap[((GitRev*)m_arShownList[indexNext])->m_CommitHash]=*(GitRev*)m_arShownList[indexNext];
592 dlg.m_CommitList.m_logEntries.GetGitRevAt(dlg.m_CommitList.m_logEntries.size()-1).GetAction(this) |= CTGitPath::LOGACTIONS_REBASE_PICK;
595 if(dlg.DoModal() == IDOK)
597 Refresh();
600 break;
601 case ID_REBASE_TO_VERSION:
602 if(!g_Git.CheckCleanWorkTree())
604 CMessageBox::Show(NULL,_T("Rebase requires a clean working tree"),_T("TortoiseGit"),MB_OK);
606 }else
608 CRebaseDlg dlg;
609 dlg.m_Upstream = pSelLogEntry->m_CommitHash;
611 if(dlg.DoModal() == IDOK)
613 Refresh();
617 break;
619 case ID_STASH_APPLY:
620 CAppUtils::StashApply(pSelLogEntry->m_Ref);
621 break;
623 case ID_REFLOG_DEL:
625 CString str;
626 str.Format(_T("Warning: %s will be permanently deleted. It can <ct=0x0000FF><b>NOT</b></ct> be recovered!\r\n\r\nDo you really want to continue?"),pSelLogEntry->m_Ref);
627 if(CMessageBox::Show(NULL, str, _T("TortoiseGit"), 1, IDI_QUESTION, _T("&Delete"), _T("&Abort")) == 1)
629 CString cmd,out;
630 cmd.Format(_T("git.exe reflog delete %s"),pSelLogEntry->m_Ref);
631 if(g_Git.Run(cmd,&out,CP_ACP))
633 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
635 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
638 break;
639 case ID_LOG:
641 CString cmd;
642 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
643 cmd += _T(" /command:log");
644 cmd += _T(" /path:\"")+g_Git.m_CurrentDir+_T("\" ");
645 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
646 cmd += _T(" /endrev:")+r1->m_CommitHash.ToString();
647 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
649 break;
650 case ID_CREATE_PATCH:
652 int select=this->GetSelectedCount();
653 CString cmd;
654 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
655 cmd += _T(" /command:formatpatch");
657 cmd += _T(" /path:\"")+g_Git.m_CurrentDir+_T("\" ");
659 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
660 GitRev * r2 = NULL;
661 if(select == 1)
663 cmd += _T(" /startrev:")+r1->m_CommitHash.ToString();
665 else
667 r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
668 if( this->m_IsOldFirst )
670 cmd += _T(" /startrev:")+r1->m_CommitHash.ToString()+_T("~1");
671 cmd += _T(" /endrev:")+r2->m_CommitHash.ToString();
673 }else
675 cmd += _T(" /startrev:")+r2->m_CommitHash.ToString()+_T("~1");
676 cmd += _T(" /endrev:")+r1->m_CommitHash.ToString();
681 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
683 break;
684 case ID_DELETE:
686 int index = cmd>>16;
687 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) == m_HashMap.end() )
689 CMessageBox::Show(NULL,IDS_ERROR_NOREF,IDS_APPNAME,MB_OK|MB_ICONERROR);
690 return;
692 if( index >= m_HashMap[pSelLogEntry->m_CommitHash].size())
694 CMessageBox::Show(NULL,IDS_ERROR_INDEX,IDS_APPNAME,MB_OK|MB_ICONERROR);
695 return;
697 CString ref,msg;
698 ref=m_HashMap[pSelLogEntry->m_CommitHash][index];
700 msg=CString(_T("Do you really want to <ct=0x0000FF>delete</ct> <b>"))+ref;
701 msg+=_T("</b>?");
702 if( CMessageBox::Show(NULL, msg, _T("TortoiseGit"), 2, IDI_QUESTION, _T("&Delete"), _T("&Abort")) == 1 )
704 CString shortname;
705 CString cmd;
706 if(this->GetShortName(ref,shortname,_T("refs/heads/")))
708 cmd.Format(_T("git.exe branch -D %s"),shortname);
711 if(this->GetShortName(ref,shortname,_T("refs/remotes/")))
713 cmd.Format(_T("git.exe branch -r -D %s"),shortname);
716 if(this->GetShortName(ref,shortname,_T("refs/tags/")))
718 cmd.Format(_T("git.exe tag -d %s"),shortname);
721 if(this->GetShortName(ref,shortname,_T("refs/stash")))
723 if(CMessageBox::Show(NULL, _T("<ct=0x0000FF>Do you really want to delete <b>ALL</b> stash?</ct>"),
724 _T("TortoiseGit"), 2, IDI_QUESTION, _T("&Delete"), _T("&Abort")) == 2)
725 cmd.Format(_T("git.exe stash clear"));
726 else
727 return;
730 CString out;
731 if(g_Git.Run(cmd,&out,CP_UTF8))
733 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
735 this->ReloadHashMap();
736 CRect rect;
737 this->GetItemRect(FirstSelect,&rect,LVIR_BOUNDS);
738 this->InvalidateRect(rect);
741 break;
743 case ID_FINDENTRY:
745 m_nSearchIndex = GetSelectionMark();
746 if (m_nSearchIndex < 0)
747 m_nSearchIndex = 0;
748 if (m_pFindDialog)
750 break;
752 else
754 m_pFindDialog = new CFindDlg();
755 m_pFindDialog->Create(this);
758 break;
759 case ID_MERGEREV:
761 CString str = pSelLogEntry->m_CommitHash.ToString();
762 // we need an URL to complete this command, so error out if we can't get an URL
763 if(CAppUtils::Merge(&str))
765 this->Refresh();
768 break;
769 case ID_REVERTREV:
771 if(!this->RevertSelectedCommits())
772 this->Refresh();
774 break;
775 case ID_EDITNOTE:
777 CAppUtils::EditNote(pSelLogEntry);
778 this->SetItemState(FirstSelect, 0, LVIS_SELECTED);
779 this->SetItemState(FirstSelect, LVIS_SELECTED, LVIS_SELECTED);
781 break;
782 default:
783 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
784 break;
785 #if 0
787 case ID_BLAMECOMPARE:
789 //user clicked on the menu item "compare with working copy"
790 //now first get the revision which is selected
791 if (PromptShown())
793 GitDiff diff(this, this->m_hWnd, true);
794 diff.SetHEADPeg(m_LogRevision);
795 diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
797 else
798 CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
800 break;
801 case ID_BLAMETWO:
803 //user clicked on the menu item "compare and blame revisions"
804 if (PromptShown())
806 GitDiff diff(this, this->m_hWnd, true);
807 diff.SetHEADPeg(m_LogRevision);
808 diff.ShowCompare(CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), false, true);
810 else
811 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
813 break;
814 case ID_BLAMEWITHPREVIOUS:
816 //user clicked on the menu item "Compare and Blame with previous revision"
817 if (PromptShown())
819 GitDiff diff(this, this->m_hWnd, true);
820 diff.SetHEADPeg(m_LogRevision);
821 diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
823 else
824 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
826 break;
828 case ID_OPENWITH:
829 bOpenWith = true;
830 case ID_OPEN:
832 CProgressDlg progDlg;
833 progDlg.SetTitle(IDS_APPNAME);
834 progDlg.SetAnimation(IDR_DOWNLOAD);
835 CString sInfoLine;
836 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
837 progDlg.SetLine(1, sInfoLine, true);
838 SetAndClearProgressInfo(&progDlg);
839 progDlg.ShowModeless(m_hWnd);
840 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
841 bool bSuccess = true;
842 if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
844 bSuccess = false;
845 // try again, but with the selected revision as the peg revision
846 if (!Cat(m_path, revSelected, revSelected, tempfile))
848 progDlg.Stop();
849 SetAndClearProgressInfo((HWND)NULL);
850 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
851 EnableOKButton();
852 break;
854 bSuccess = true;
856 if (bSuccess)
858 progDlg.Stop();
859 SetAndClearProgressInfo((HWND)NULL);
860 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
861 int ret = 0;
862 if (!bOpenWith)
863 ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
864 if ((ret <= HINSTANCE_ERROR)||bOpenWith)
866 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
867 cmd += tempfile.GetWinPathString() + _T(" ");
868 CAppUtils::LaunchApplication(cmd, NULL, false);
872 break;
873 case ID_BLAME:
875 CBlameDlg dlg;
876 dlg.EndRev = revSelected;
877 if (dlg.DoModal() == IDOK)
879 CBlame blame;
880 CString tempfile;
881 CString logfile;
882 tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
883 if (!tempfile.IsEmpty())
885 if (dlg.m_bTextView)
887 //open the default text editor for the result file
888 CAppUtils::StartTextViewer(tempfile);
890 else
892 CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
893 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
895 break;
899 else
901 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
905 break;
906 case ID_UPDATE:
908 CString sCmd;
909 CString url = _T("tgit:")+pathURL;
910 sCmd.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
911 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
912 (LPCTSTR)m_path.GetWinPath(), (LONG)revSelected);
913 CAppUtils::LaunchApplication(sCmd, NULL, false);
915 break;
917 case ID_REPOBROWSE:
919 CString sCmd;
920 sCmd.Format(_T("%s /command:repobrowser /path:\"%s\" /rev:%s"),
921 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
922 (LPCTSTR)pathURL, (LPCTSTR)revSelected.ToString());
924 CAppUtils::LaunchApplication(sCmd, NULL, false);
926 break;
927 case ID_EDITLOG:
929 EditLogMessage(selIndex);
931 break;
932 case ID_EDITAUTHOR:
934 EditAuthor(selEntries);
936 break;
937 case ID_REVPROPS:
939 CEditPropertiesDlg dlg;
940 dlg.SetProjectProperties(&m_ProjectProperties);
941 CTGitPathList escapedlist;
942 dlg.SetPathList(CTGitPathList(CTGitPath(pathURL)));
943 dlg.SetRevision(revSelected);
944 dlg.RevProps(true);
945 dlg.DoModal();
947 break;
949 case ID_EXPORT:
951 CString sCmd;
952 sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
953 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
954 (LPCTSTR)pathURL, (LONG)revSelected);
955 CAppUtils::LaunchApplication(sCmd, NULL, false);
957 break;
958 case ID_CHECKOUT:
960 CString sCmd;
961 CString url = _T("tgit:")+pathURL;
962 sCmd.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),
963 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
964 (LPCTSTR)url, (LONG)revSelected);
965 CAppUtils::LaunchApplication(sCmd, NULL, false);
967 break;
968 case ID_VIEWREV:
970 CString url = m_ProjectProperties.sWebViewerRev;
971 url = GetAbsoluteUrlFromRelativeUrl(url);
972 url.Replace(_T("%REVISION%"), revSelected.ToString());
973 if (!url.IsEmpty())
974 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
976 break;
977 case ID_VIEWPATHREV:
979 CString relurl = pathURL;
980 CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
981 relurl = relurl.Mid(sRoot.GetLength());
982 CString url = m_ProjectProperties.sWebViewerPathRev;
983 url = GetAbsoluteUrlFromRelativeUrl(url);
984 url.Replace(_T("%REVISION%"), revSelected.ToString());
985 url.Replace(_T("%PATH%"), relurl);
986 if (!url.IsEmpty())
987 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
989 break;
990 #endif
992 } // switch (cmd)
994 theApp.DoWaitCursor(-1);
997 void CGitLogList::SetSelectedAction(int action)
999 POSITION pos = GetFirstSelectedItemPosition();
1000 int index;
1001 while(pos)
1003 index = GetNextSelectedItem(pos);
1004 ((GitRev*)m_arShownList[index])->GetAction(this) = action;
1005 CRect rect;
1006 this->GetItemRect(index,&rect,LVIR_BOUNDS);
1007 this->InvalidateRect(rect);