don't include IconMenu.h twice
[TortoiseGit.git] / src / TortoiseProc / GitLogListAction.cpp
blobc67a1fa8fb56e96fdcd6f116942a836d3d22c826
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 "RevisionRangeDlg.h"
49 //#include "BrowseFolder.h"
50 //#include "BlameDlg.h"
51 //#include "Blame.h"
52 //#include "GitHelpers.h"
53 #include "GitStatus.h"
54 //#include "LogDlgHelper.h"
55 //#include "CachedLogInfo.h"
56 //#include "RepositoryInfo.h"
57 //#include "EditPropertiesDlg.h"
58 #include "FileDiffDlg.h"
59 #include "CommitDlg.h"
60 #include "RebaseDlg.h"
61 #include "GitDiff.h"
63 IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl)
65 int CGitLogList::RevertSelectedCommits()
67 CSysProgressDlg progress;
68 int ret = -1;
70 #if 0
71 if(!g_Git.CheckCleanWorkTree())
73 CMessageBox::Show(NULL,_T("Revert requires a clean working tree"),_T("TortoiseGit"),MB_OK);
76 #endif
78 if (progress.IsValid() && (this->GetSelectedCount() > 1) )
80 progress.SetTitle(_T("Revert Commit"));
81 progress.SetAnimation(IDR_MOVEANI);
82 progress.SetTime(true);
83 progress.ShowModeless(this);
86 POSITION pos = GetFirstSelectedItemPosition();
87 int i=0;
88 while(pos)
90 int index = GetNextSelectedItem(pos);
91 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(index));
93 if (progress.IsValid() && (this->GetSelectedCount() > 1) )
95 progress.FormatPathLine(1, _T("Revert %s"), r1->m_CommitHash.ToString());
96 progress.FormatPathLine(2, _T("%s"), r1->GetSubject());
97 progress.SetProgress(i, this->GetSelectedCount());
99 i++;
101 if(r1->m_CommitHash.IsEmpty())
102 continue;
104 CString cmd, output;
105 cmd.Format(_T("git.exe revert --no-edit --no-commit %s"), r1->m_CommitHash.ToString());
106 if(g_Git.Run(cmd, &output, CP_ACP))
108 CString str;
109 str=_T("Revert fail\n");
110 str+= cmd;
111 str+= _T("\n")+output;
112 if( GetSelectedCount() == 1)
113 CMessageBox::Show(NULL,str, _T("TortoiseGit"),MB_OK|MB_ICONERROR);
114 else
116 if(CMessageBox::Show(NULL, str, _T("TortoiseGit"),2 , IDI_ERROR, _T("&Skip"), _T("&Abort")) == 2)
118 return ret;
122 else
124 ret =0;
127 if ((progress.IsValid())&&(progress.HasUserCancelled()))
128 break;
130 return ret;
132 int CGitLogList::CherryPickFrom(CString from, CString to)
134 CLogDataVector logs(&m_LogCache);
135 if(logs.ParserFromLog(NULL,-1,0,&from,&to))
136 return -1;
138 if(logs.size() == 0)
139 return 0;
141 CSysProgressDlg progress;
142 if (progress.IsValid())
144 progress.SetTitle(_T("Cherry Pick"));
145 progress.SetAnimation(IDR_MOVEANI);
146 progress.SetTime(true);
147 progress.ShowModeless(this);
150 for(int i=logs.size()-1;i>=0;i--)
152 if (progress.IsValid())
154 progress.FormatPathLine(1, _T("Pick up %s"), logs.GetGitRevAt(i).m_CommitHash.ToString());
155 progress.FormatPathLine(2, _T("%s"), logs.GetGitRevAt(i).GetSubject());
156 progress.SetProgress(logs.size()-i, logs.size());
158 if ((progress.IsValid())&&(progress.HasUserCancelled()))
160 //CMessageBox::Show(hwndExplorer, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_ICONINFORMATION);
161 throw std::exception(CUnicodeUtils::GetUTF8(_T("User canceled\r\n\r\n")));
162 return -1;
164 CString cmd,out;
165 cmd.Format(_T("git.exe cherry-pick %s"),logs.GetGitRevAt(i).m_CommitHash.ToString());
166 out.Empty();
167 if(g_Git.Run(cmd,&out,CP_UTF8))
169 throw std::exception(CUnicodeUtils::GetUTF8(CString(_T("Cherry Pick Failure\r\n\r\n"))+out));
170 return -1;
174 return 0;
177 void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect, CMenu *popmenu)
179 POSITION pos = GetFirstSelectedItemPosition();
180 int indexNext = GetNextSelectedItem(pos);
181 if (indexNext < 0)
182 return;
184 GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));
186 theApp.DoWaitCursor(1);
187 switch (cmd&0xFFFF)
189 case ID_COMMIT:
191 CTGitPathList pathlist;
192 CTGitPathList selectedlist;
193 pathlist.AddPath(this->m_Path);
194 bool bSelectFilesForCommit = !!DWORD(CRegStdWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE));
195 CString str;
196 CAppUtils::Commit(CString(),false,str,
197 pathlist,selectedlist,bSelectFilesForCommit);
198 //this->Refresh();
199 this->GetParent()->PostMessage(WM_COMMAND,ID_LOGDLG_REFRESH,0);
201 break;
202 case ID_GNUDIFF1:
204 CString tempfile=GetTempFile();
205 CString command;
206 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
207 if(!r1->m_CommitHash.IsEmpty())
209 CString merge;
210 CString hash2;
211 cmd >>= 16;
212 if( (cmd&0xFFFF) == 0xFFFF)
214 merge=_T("-m");
215 } else if((cmd&0xFFFF) == 0xFFFE)
217 merge=_T("-c");
218 }else
220 if(cmd > r1->m_ParentHash.size())
222 CString str;
223 str.Format(_T("%d parent does not exist"), cmd);
224 CMessageBox::Show(NULL,str,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
225 return;
226 }else
228 if(cmd>0)
229 hash2 = r1->m_ParentHash[cmd-1].ToString();
232 command.Format(_T("git.exe diff-tree %s -r -p --stat %s %s"),merge, r1->m_CommitHash.ToString(), hash2);
233 }else
234 command.Format(_T("git.exe diff -r -p --stat"));
236 g_Git.RunLogFile(command,tempfile);
237 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.ToString().Left(6)+_T(":")+r1->GetSubject());
239 break;
241 case ID_GNUDIFF2:
243 CString tempfile=GetTempFile();
244 CString cmd;
245 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
246 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
248 if( r1->m_CommitHash.IsEmpty()) {
249 cmd.Format(_T("git.exe diff -r -p --stat %s"),r2->m_CommitHash.ToString());
250 }else if( r2->m_CommitHash.IsEmpty()) {
251 cmd.Format(_T("git.exe diff -r -p --stat %s"),r1->m_CommitHash.ToString());
252 }else {
253 cmd.Format(_T("git.exe diff-tree -r -p --stat %s %s"),r2->m_CommitHash.ToString(),r1->m_CommitHash.ToString());
256 g_Git.RunLogFile(cmd,tempfile);
257 CAppUtils::StartUnifiedDiffViewer(tempfile,r2->m_CommitHash.ToString().Left(6)+_T(":")+r1->m_CommitHash.ToString().Left(6));
260 break;
262 case ID_COMPARETWO:
264 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
265 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
266 CGitDiff::DiffCommit(this->m_Path, r1,r2);
269 break;
271 case ID_COMPARE:
273 GitRev * r1 = &m_wcRev;
274 GitRev * r2 = pSelLogEntry;
276 CGitDiff::DiffCommit(this->m_Path, r1,r2);
278 //user clicked on the menu item "compare with working copy"
279 //if (PromptShown())
281 // GitDiff diff(this, m_hWnd, true);
282 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
283 // diff.SetHEADPeg(m_LogRevision);
284 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
286 //else
287 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
289 break;
291 case ID_COMPAREWITHPREVIOUS:
294 CFileDiffDlg dlg;
296 if(pSelLogEntry->m_ParentHash.size()>0)
297 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
299 cmd>>=16;
300 cmd&=0xFFFF;
302 if(cmd == 0)
303 cmd=1;
305 CGitDiff::DiffCommit(this->m_Path, pSelLogEntry->m_CommitHash.ToString(),pSelLogEntry->m_ParentHash[cmd-1].ToString());
307 }else
309 CMessageBox::Show(NULL,_T("No previous version"),_T("TortoiseGit"),MB_OK);
311 //if (PromptShown())
313 // GitDiff diff(this, m_hWnd, true);
314 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
315 // diff.SetHEADPeg(m_LogRevision);
316 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
318 //else
319 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
321 break;
322 case ID_COPYCLIPBOARD:
324 CopySelectionToClipBoard();
326 break;
327 case ID_COPYHASH:
329 CopySelectionToClipBoard(TRUE);
331 break;
332 case ID_EXPORT:
334 CString str=pSelLogEntry->m_CommitHash.ToString();
335 CAppUtils::Export(&str);
337 break;
338 case ID_CREATE_BRANCH:
340 CString str = pSelLogEntry->m_CommitHash.ToString();
341 CAppUtils::CreateBranchTag(FALSE,&str);
342 ReloadHashMap();
343 Invalidate();
345 break;
346 case ID_CREATE_TAG:
348 CString str = pSelLogEntry->m_CommitHash.ToString();
349 CAppUtils::CreateBranchTag(TRUE,&str);
350 ReloadHashMap();
351 Invalidate();
352 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
354 break;
355 case ID_SWITCHTOREV:
357 CString str = pSelLogEntry->m_CommitHash.ToString();
358 CAppUtils::Switch(&str);
360 ReloadHashMap();
361 Invalidate();
362 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
363 break;
364 case ID_SWITCHBRANCH:
365 if(popmenu)
367 CString *branch = (CString*)((CIconMenu*)popmenu)->GetMenuItemData(cmd);
368 if(branch)
371 CProgressDlg progress;
372 CString name;
373 if(branch->Find(_T("refs/heads/")) ==0 )
374 name = branch->Mid(11);
375 else
376 name = *branch;
378 progress.m_GitCmd.Format(_T("git.exe checkout %s"), name.GetString());
379 progress.DoModal();
381 ReloadHashMap();
382 Invalidate();
383 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
385 break;
386 case ID_RESET:
388 CString str = pSelLogEntry->m_CommitHash.ToString();
389 CAppUtils::GitReset(&str);
390 ReloadHashMap();
391 Invalidate();
393 break;
394 case ID_REBASE_PICK:
395 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_PICK);
396 break;
397 case ID_REBASE_EDIT:
398 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_EDIT);
399 break;
400 case ID_REBASE_SQUASH:
401 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SQUASH);
402 break;
403 case ID_REBASE_SKIP:
404 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SKIP);
405 break;
406 case ID_COMBINE_COMMIT:
408 CString head;
409 CGitHash headhash;
410 CGitHash hashFirst,hashLast;
412 int headindex=GetHeadIndex();
413 if(headindex>=0) //incase show all branch, head is not the first commits.
415 head.Format(_T("HEAD~%d"),FirstSelect-headindex);
416 hashFirst=g_Git.GetHash(head);
418 head.Format(_T("HEAD~%d"),LastSelect-headindex);
419 hashLast=g_Git.GetHash(head);
422 GitRev* pFirstEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
423 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
424 if(pFirstEntry->m_CommitHash != hashFirst || pLastEntry->m_CommitHash != hashLast)
426 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);
427 break;
430 headhash=g_Git.GetHash(_T("HEAD"));
432 if(!g_Git.CheckCleanWorkTree())
434 CMessageBox::Show(NULL,_T("Combine needs a clean work tree"),_T("TortoiseGit"),MB_OK);
435 break;
437 CString cmd,out;
439 //Use throw to abort this process (reset back to original HEAD)
442 cmd.Format(_T("git.exe reset --hard %s"),pFirstEntry->m_CommitHash.ToString());
443 if(g_Git.Run(cmd,&out,CP_UTF8))
445 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
446 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to first commit (first step) aborting...\r\n\r\n")+out));
448 cmd.Format(_T("git.exe reset --mixed %s"),hashLast.ToString());
449 if(g_Git.Run(cmd,&out,CP_UTF8))
451 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
452 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to last commit (second step) aborting...\r\n\r\n")+out));
455 CTGitPathList PathList;
456 /* don't why must add --stat to get action status*/
457 /* first -z will be omitted by gitdll*/
458 if(g_Git.GetDiffPath(&PathList,&pFirstEntry->m_CommitHash,&hashLast,"-z --stat -r"))
460 CMessageBox::Show(NULL,_T("Get Diff file list error"),_T("TortoiseGit"),MB_OK);
461 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not get changed file list aborting...\r\n\r\n")+out));
464 for(int i=0;i<PathList.GetCount();i++)
466 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_ADDED)
468 cmd.Format(_T("git.exe add -- \"%s\""), PathList[i].GetGitPathString());
469 if(g_Git.Run(cmd,&out,CP_ACP))
471 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
472 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not add new file aborting...\r\n\r\n")+out));
476 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_DELETED)
478 cmd.Format(_T("git.exe rm -- \"%s\""), PathList[i].GetGitPathString());
479 if(g_Git.Run(cmd,&out,CP_ACP))
481 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
482 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not rm file aborting...\r\n\r\n")+out));
487 CCommitDlg dlg;
488 for(int i=FirstSelect;i<=LastSelect;i++)
490 GitRev* pRev = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
491 dlg.m_sLogMessage+=pRev->GetSubject()+_T("\n")+pRev->GetBody();
492 dlg.m_sLogMessage+=_T("\n");
494 dlg.m_bWholeProject=true;
495 dlg.m_bSelectFilesForCommit = true;
496 dlg.m_bCommitAmend=true;
497 dlg.m_bNoPostActions=true;
498 dlg.m_AmendStr=dlg.m_sLogMessage;
500 if (dlg.DoModal() == IDOK)
502 if(pFirstEntry->m_CommitHash!=headhash)
504 //Commitrange firstEntry..headhash (from top of combine to original head) needs to be 'cherry-picked'
505 //on top of new commit.
506 //Use the rebase --onto command for it.
508 //All this can be done in one step using the following command:
509 //cmd.Format(_T("git.exe format-patch --stdout --binary --full-index -k %s..%s | git am -k -3"),
510 // pFirstEntry->m_CommitHash,
511 // headhash);
512 //But I am not sure if a '|' is going to work in a CreateProcess() call.
514 //Later the progress dialog could be used to execute these steps.
516 if(CherryPickFrom(pFirstEntry->m_CommitHash.ToString(),headhash))
518 CString msg;
519 msg.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));
520 throw std::exception(CUnicodeUtils::GetUTF8(msg));
522 #if 0
523 CString currentBranch=g_Git.GetCurrentBranch();
524 cmd.Format(_T("git.exe rebase --onto \"%s\" %s %s"),
525 currentBranch,
526 pFirstEntry->m_CommitHash,
527 headhash);
528 if(g_Git.Run(cmd,&out,CP_UTF8)!=0)
530 CString msg;
531 msg.Format(_T("Error while rebasing commits on top of combined commits. Aborting.\r\n\r\n%s"),out);
532 // CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);
533 g_Git.Run(_T("git.exe rebase --abort"),&out,CP_UTF8);
534 throw std::exception(CUnicodeUtils::GetUTF8(msg));
537 //HEAD is now on <no branch>.
538 //The following steps are to get HEAD back on the original branch and reset the branch to the new HEAD
539 //To avoid 2 working copy changes, we could use git branch -f <original branch> <hash new head>
540 //And then git checkout <original branch>
541 //But I don't know if 'git branch -f' removes tracking options. So for now, do a checkout and a reset.
543 //Store new HEAD
544 CString newHead=g_Git.GetHash(CString(_T("HEAD")));
546 //Checkout working branch
547 cmd.Format(_T("git.exe checkout -f \"%s\""),currentBranch);
548 if(g_Git.Run(cmd,&out,CP_UTF8))
549 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not checkout original branch. Aborting...\r\n\r\n")+out));
551 //Reset to new HEAD
552 cmd.Format(_T("git.exe reset --hard %s"),newHead);
553 if(g_Git.Run(cmd,&out,CP_UTF8))
554 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to new head. Aborting...\r\n\r\n")+out));
555 #endif
558 else
559 throw std::exception("User aborted the combine process");
561 catch(std::exception& e)
563 CMessageBox::Show(NULL,CUnicodeUtils::GetUnicode(CStringA(e.what())),_T("TortoiseGit: Combine error"),MB_OK|MB_ICONERROR);
564 cmd.Format(_T("git.exe reset --hard %s"),headhash.ToString());
565 out.Empty();
566 if(g_Git.Run(cmd,&out,CP_UTF8))
568 CMessageBox::Show(NULL,_T("Could not reset to original HEAD\r\n\r\n")+out,_T("TortoiseGit"),MB_OK);
571 Refresh();
573 break;
575 case ID_CHERRY_PICK:
576 if(!g_Git.CheckCleanWorkTree())
578 CMessageBox::Show(NULL,_T("Cherry Pick requires a clean working tree"),_T("TortoiseGit"),MB_OK);
580 }else
582 CRebaseDlg dlg;
583 dlg.m_IsCherryPick = TRUE;
584 dlg.m_Upstream = this->m_CurrentBranch;
585 POSITION pos = GetFirstSelectedItemPosition();
586 while(pos)
588 int indexNext = GetNextSelectedItem(pos);
589 dlg.m_CommitList.m_logEntries.push_back( ((GitRev*)m_arShownList[indexNext])->m_CommitHash );
590 dlg.m_CommitList.m_LogCache.m_HashMap[((GitRev*)m_arShownList[indexNext])->m_CommitHash]=*(GitRev*)m_arShownList[indexNext];
591 dlg.m_CommitList.m_logEntries.GetGitRevAt(dlg.m_CommitList.m_logEntries.size()-1).GetAction(this) |= CTGitPath::LOGACTIONS_REBASE_PICK;
594 if(dlg.DoModal() == IDOK)
596 Refresh();
599 break;
600 case ID_REBASE_TO_VERSION:
601 if(!g_Git.CheckCleanWorkTree())
603 CMessageBox::Show(NULL,_T("Rebase requires a clean working tree"),_T("TortoiseGit"),MB_OK);
605 }else
607 CRebaseDlg dlg;
608 dlg.m_Upstream = pSelLogEntry->m_CommitHash;
610 if(dlg.DoModal() == IDOK)
612 Refresh();
616 break;
618 case ID_STASH_APPLY:
619 CAppUtils::StashApply(pSelLogEntry->m_Ref);
620 break;
622 case ID_REFLOG_DEL:
624 CString str;
625 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);
626 if(CMessageBox::Show(NULL, str, _T("TortoiseGit"), 1, IDI_QUESTION, _T("&Delete"), _T("&Abort")) == 1)
628 CString cmd,out;
629 cmd.Format(_T("git.exe reflog delete %s"),pSelLogEntry->m_Ref);
630 if(g_Git.Run(cmd,&out,CP_ACP))
632 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
634 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
637 break;
638 case ID_LOG:
640 CString cmd;
641 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
642 cmd += _T(" /command:log");
643 cmd += _T(" /path:\"")+g_Git.m_CurrentDir+_T("\" ");
644 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
645 cmd += _T(" /endrev:")+r1->m_CommitHash.ToString();
646 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
648 break;
649 case ID_CREATE_PATCH:
651 int select=this->GetSelectedCount();
652 CString cmd;
653 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
654 cmd += _T(" /command:formatpatch");
656 cmd += _T(" /path:\"")+g_Git.m_CurrentDir+_T("\" ");
658 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
659 GitRev * r2 = NULL;
660 if(select == 1)
662 cmd += _T(" /startrev:")+r1->m_CommitHash.ToString();
664 else
666 r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
667 if( this->m_IsOldFirst )
669 cmd += _T(" /startrev:")+r1->m_CommitHash.ToString()+_T("~1");
670 cmd += _T(" /endrev:")+r2->m_CommitHash.ToString();
672 }else
674 cmd += _T(" /startrev:")+r2->m_CommitHash.ToString()+_T("~1");
675 cmd += _T(" /endrev:")+r1->m_CommitHash.ToString();
680 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
682 break;
683 case ID_PUSH:
684 if (CAppUtils::Push())
685 Refresh();
686 break;
687 case ID_DELETE:
689 int index = cmd>>16;
690 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) == m_HashMap.end() )
692 CMessageBox::Show(NULL,IDS_ERROR_NOREF,IDS_APPNAME,MB_OK|MB_ICONERROR);
693 return;
695 if( index >= m_HashMap[pSelLogEntry->m_CommitHash].size())
697 CMessageBox::Show(NULL,IDS_ERROR_INDEX,IDS_APPNAME,MB_OK|MB_ICONERROR);
698 return;
700 CString ref,msg;
701 ref=m_HashMap[pSelLogEntry->m_CommitHash][index];
703 msg=CString(_T("Do you really want to <ct=0x0000FF>delete</ct> <b>"))+ref;
704 msg+=_T("</b>?");
705 if( CMessageBox::Show(NULL, msg, _T("TortoiseGit"), 2, IDI_QUESTION, _T("&Delete"), _T("&Abort")) == 1 )
707 CString shortname;
708 CString cmd;
709 if(this->GetShortName(ref,shortname,_T("refs/heads/")))
711 cmd.Format(_T("git.exe branch -D -- %s"),shortname);
714 if(this->GetShortName(ref,shortname,_T("refs/remotes/")))
716 cmd.Format(_T("git.exe branch -r -D -- %s"),shortname);
719 if(this->GetShortName(ref,shortname,_T("refs/tags/")))
721 cmd.Format(_T("git.exe tag -d -- %s"),shortname);
724 if(this->GetShortName(ref,shortname,_T("refs/stash")))
726 if(CMessageBox::Show(NULL, _T("<ct=0x0000FF>Do you really want to delete <b>ALL</b> stash?</ct>"),
727 _T("TortoiseGit"), 2, IDI_QUESTION, _T("&Delete"), _T("&Abort")) == 1)
728 cmd.Format(_T("git.exe stash clear"));
729 else
730 return;
733 CString out;
734 if(g_Git.Run(cmd,&out,CP_UTF8))
736 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
738 this->ReloadHashMap();
739 CRect rect;
740 this->GetItemRect(FirstSelect,&rect,LVIR_BOUNDS);
741 this->InvalidateRect(rect);
744 break;
746 case ID_FINDENTRY:
748 m_nSearchIndex = GetSelectionMark();
749 if (m_nSearchIndex < 0)
750 m_nSearchIndex = 0;
751 if (m_pFindDialog)
753 break;
755 else
757 m_pFindDialog = new CFindDlg();
758 m_pFindDialog->Create(this);
761 break;
762 case ID_MERGEREV:
764 CString str = pSelLogEntry->m_CommitHash.ToString();
765 // we need an URL to complete this command, so error out if we can't get an URL
766 if(CAppUtils::Merge(&str))
768 this->Refresh();
771 break;
772 case ID_REVERTREV:
774 if(!this->RevertSelectedCommits())
775 this->Refresh();
777 break;
778 case ID_EDITNOTE:
780 CAppUtils::EditNote(pSelLogEntry);
781 this->SetItemState(FirstSelect, 0, LVIS_SELECTED);
782 this->SetItemState(FirstSelect, LVIS_SELECTED, LVIS_SELECTED);
784 break;
785 default:
786 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
787 break;
788 #if 0
790 case ID_BLAMECOMPARE:
792 //user clicked on the menu item "compare with working copy"
793 //now first get the revision which is selected
794 if (PromptShown())
796 GitDiff diff(this, this->m_hWnd, true);
797 diff.SetHEADPeg(m_LogRevision);
798 diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
800 else
801 CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
803 break;
804 case ID_BLAMETWO:
806 //user clicked on the menu item "compare and blame revisions"
807 if (PromptShown())
809 GitDiff diff(this, this->m_hWnd, true);
810 diff.SetHEADPeg(m_LogRevision);
811 diff.ShowCompare(CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), false, true);
813 else
814 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
816 break;
817 case ID_BLAMEWITHPREVIOUS:
819 //user clicked on the menu item "Compare and Blame with previous revision"
820 if (PromptShown())
822 GitDiff diff(this, this->m_hWnd, true);
823 diff.SetHEADPeg(m_LogRevision);
824 diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
826 else
827 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
829 break;
831 case ID_OPENWITH:
832 bOpenWith = true;
833 case ID_OPEN:
835 CProgressDlg progDlg;
836 progDlg.SetTitle(IDS_APPNAME);
837 progDlg.SetAnimation(IDR_DOWNLOAD);
838 CString sInfoLine;
839 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
840 progDlg.SetLine(1, sInfoLine, true);
841 SetAndClearProgressInfo(&progDlg);
842 progDlg.ShowModeless(m_hWnd);
843 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
844 bool bSuccess = true;
845 if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
847 bSuccess = false;
848 // try again, but with the selected revision as the peg revision
849 if (!Cat(m_path, revSelected, revSelected, tempfile))
851 progDlg.Stop();
852 SetAndClearProgressInfo((HWND)NULL);
853 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
854 EnableOKButton();
855 break;
857 bSuccess = true;
859 if (bSuccess)
861 progDlg.Stop();
862 SetAndClearProgressInfo((HWND)NULL);
863 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
864 int ret = 0;
865 if (!bOpenWith)
866 ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
867 if ((ret <= HINSTANCE_ERROR)||bOpenWith)
869 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
870 cmd += tempfile.GetWinPathString() + _T(" ");
871 CAppUtils::LaunchApplication(cmd, NULL, false);
875 break;
876 case ID_BLAME:
878 CBlameDlg dlg;
879 dlg.EndRev = revSelected;
880 if (dlg.DoModal() == IDOK)
882 CBlame blame;
883 CString tempfile;
884 CString logfile;
885 tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
886 if (!tempfile.IsEmpty())
888 if (dlg.m_bTextView)
890 //open the default text editor for the result file
891 CAppUtils::StartTextViewer(tempfile);
893 else
895 CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
896 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
898 break;
902 else
904 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
908 break;
909 case ID_UPDATE:
911 CString sCmd;
912 CString url = _T("tgit:")+pathURL;
913 sCmd.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
914 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
915 (LPCTSTR)m_path.GetWinPath(), (LONG)revSelected);
916 CAppUtils::LaunchApplication(sCmd, NULL, false);
918 break;
920 case ID_REPOBROWSE:
922 CString sCmd;
923 sCmd.Format(_T("%s /command:repobrowser /path:\"%s\" /rev:%s"),
924 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
925 (LPCTSTR)pathURL, (LPCTSTR)revSelected.ToString());
927 CAppUtils::LaunchApplication(sCmd, NULL, false);
929 break;
930 case ID_EDITLOG:
932 EditLogMessage(selIndex);
934 break;
935 case ID_EDITAUTHOR:
937 EditAuthor(selEntries);
939 break;
940 case ID_REVPROPS:
942 CEditPropertiesDlg dlg;
943 dlg.SetProjectProperties(&m_ProjectProperties);
944 CTGitPathList escapedlist;
945 dlg.SetPathList(CTGitPathList(CTGitPath(pathURL)));
946 dlg.SetRevision(revSelected);
947 dlg.RevProps(true);
948 dlg.DoModal();
950 break;
952 case ID_EXPORT:
954 CString sCmd;
955 sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
956 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
957 (LPCTSTR)pathURL, (LONG)revSelected);
958 CAppUtils::LaunchApplication(sCmd, NULL, false);
960 break;
961 case ID_CHECKOUT:
963 CString sCmd;
964 CString url = _T("tgit:")+pathURL;
965 sCmd.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),
966 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
967 (LPCTSTR)url, (LONG)revSelected);
968 CAppUtils::LaunchApplication(sCmd, NULL, false);
970 break;
971 case ID_VIEWREV:
973 CString url = m_ProjectProperties.sWebViewerRev;
974 url = GetAbsoluteUrlFromRelativeUrl(url);
975 url.Replace(_T("%REVISION%"), revSelected.ToString());
976 if (!url.IsEmpty())
977 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
979 break;
980 case ID_VIEWPATHREV:
982 CString relurl = pathURL;
983 CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
984 relurl = relurl.Mid(sRoot.GetLength());
985 CString url = m_ProjectProperties.sWebViewerPathRev;
986 url = GetAbsoluteUrlFromRelativeUrl(url);
987 url.Replace(_T("%REVISION%"), revSelected.ToString());
988 url.Replace(_T("%PATH%"), relurl);
989 if (!url.IsEmpty())
990 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
992 break;
993 #endif
995 } // switch (cmd)
997 theApp.DoWaitCursor(-1);
1000 void CGitLogList::SetSelectedAction(int action)
1002 POSITION pos = GetFirstSelectedItemPosition();
1003 int index;
1004 while(pos)
1006 index = GetNextSelectedItem(pos);
1007 ((GitRev*)m_arShownList[index])->GetAction(this) = action;
1008 CRect rect;
1009 this->GetItemRect(index,&rect,LVIR_BOUNDS);
1010 this->InvalidateRect(rect);