Rename a lot of SVN references to GIT
[TortoiseGit.git] / src / TortoiseProc / GitLogListAction.cpp
blobb73dc3b4aa77de7e7cb09a0648c3e3d8c94988e8
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 "GITProgressDlg.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(CRegStdDWORD(_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");
216 else if((cmd&0xFFFF) == 0xFFFE)
218 merge=_T("-c");
220 else
222 if(cmd > r1->m_ParentHash.size())
224 CString str;
225 str.Format(_T("Parent %d does not exist"), cmd);
226 CMessageBox::Show(NULL,str,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
227 return;
229 else
231 if(cmd>0)
232 hash2 = r1->m_ParentHash[cmd-1].ToString();
235 command.Format(_T("git.exe diff-tree %s -r -p --stat %s %s"), merge, hash2, r1->m_CommitHash.ToString());
237 else
238 command.Format(_T("git.exe diff -r -p --stat"));
240 g_Git.RunLogFile(command,tempfile);
241 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.ToString().Left(6)+_T(":")+r1->GetSubject());
243 break;
245 case ID_GNUDIFF2:
247 CString tempfile=GetTempFile();
248 CString cmd;
249 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
250 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
252 if( r1->m_CommitHash.IsEmpty()) {
253 cmd.Format(_T("git.exe diff -r -p --stat %s"),r2->m_CommitHash.ToString());
255 else if( r2->m_CommitHash.IsEmpty()) {
256 cmd.Format(_T("git.exe diff -r -p --stat %s"),r1->m_CommitHash.ToString());
258 else
260 cmd.Format(_T("git.exe diff-tree -r -p --stat %s %s"),r2->m_CommitHash.ToString(),r1->m_CommitHash.ToString());
263 g_Git.RunLogFile(cmd,tempfile);
264 CAppUtils::StartUnifiedDiffViewer(tempfile,r2->m_CommitHash.ToString().Left(6)+_T(":")+r1->m_CommitHash.ToString().Left(6));
267 break;
269 case ID_COMPARETWO:
271 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
272 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
273 CGitDiff::DiffCommit(this->m_Path, r1,r2);
276 break;
278 case ID_COMPARE:
280 GitRev * r1 = &m_wcRev;
281 GitRev * r2 = pSelLogEntry;
283 CGitDiff::DiffCommit(this->m_Path, r1,r2);
285 //user clicked on the menu item "compare with working copy"
286 //if (PromptShown())
288 // GitDiff diff(this, m_hWnd, true);
289 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
290 // diff.SetHEADPeg(m_LogRevision);
291 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
293 //else
294 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
296 break;
298 case ID_COMPAREWITHPREVIOUS:
301 CFileDiffDlg dlg;
303 if(pSelLogEntry->m_ParentHash.size()>0)
304 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
306 cmd>>=16;
307 cmd&=0xFFFF;
309 if(cmd == 0)
310 cmd=1;
312 CGitDiff::DiffCommit(this->m_Path, pSelLogEntry->m_CommitHash.ToString(),pSelLogEntry->m_ParentHash[cmd-1].ToString());
315 else
317 CMessageBox::Show(NULL,_T("No previous version"),_T("TortoiseGit"),MB_OK);
319 //if (PromptShown())
321 // GitDiff diff(this, m_hWnd, true);
322 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
323 // diff.SetHEADPeg(m_LogRevision);
324 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
326 //else
327 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
329 break;
330 case ID_COPYCLIPBOARD:
332 CopySelectionToClipBoard();
334 break;
335 case ID_COPYHASH:
337 CopySelectionToClipBoard(TRUE);
339 break;
340 case ID_EXPORT:
342 CString str=pSelLogEntry->m_CommitHash.ToString();
343 CAppUtils::Export(&str);
345 break;
346 case ID_CREATE_BRANCH:
348 CString str = pSelLogEntry->m_CommitHash.ToString();
349 CAppUtils::CreateBranchTag(FALSE,&str);
350 ReloadHashMap();
351 Invalidate();
353 break;
354 case ID_CREATE_TAG:
356 CString str = pSelLogEntry->m_CommitHash.ToString();
357 CAppUtils::CreateBranchTag(TRUE,&str);
358 ReloadHashMap();
359 Invalidate();
360 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
362 break;
363 case ID_SWITCHTOREV:
365 CString str = pSelLogEntry->m_CommitHash.ToString();
366 CAppUtils::Switch(&str);
368 ReloadHashMap();
369 Invalidate();
370 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
371 break;
372 case ID_SWITCHBRANCH:
373 if(popmenu)
375 CString *branch = (CString*)((CIconMenu*)popmenu)->GetMenuItemData(cmd);
376 if(branch)
378 CString name;
379 if(branch->Find(_T("refs/heads/")) ==0 )
380 name = branch->Mid(11);
381 else
382 name = *branch;
384 CAppUtils::PerformSwitch(name);
386 ReloadHashMap();
387 Invalidate();
388 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
390 break;
391 case ID_RESET:
393 CString str = pSelLogEntry->m_CommitHash.ToString();
394 CAppUtils::GitReset(&str);
395 ReloadHashMap();
396 Invalidate();
398 break;
399 case ID_REBASE_PICK:
400 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_PICK);
401 break;
402 case ID_REBASE_EDIT:
403 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_EDIT);
404 break;
405 case ID_REBASE_SQUASH:
406 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SQUASH);
407 break;
408 case ID_REBASE_SKIP:
409 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SKIP);
410 break;
411 case ID_COMBINE_COMMIT:
413 CString head;
414 CGitHash headhash;
415 CGitHash hashFirst,hashLast;
417 int headindex=GetHeadIndex();
418 if(headindex>=0) //incase show all branch, head is not the first commits.
420 head.Format(_T("HEAD~%d"),FirstSelect-headindex);
421 hashFirst=g_Git.GetHash(head);
423 head.Format(_T("HEAD~%d"),LastSelect-headindex);
424 hashLast=g_Git.GetHash(head);
427 GitRev* pFirstEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
428 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
429 if(pFirstEntry->m_CommitHash != hashFirst || pLastEntry->m_CommitHash != hashLast)
431 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);
432 break;
435 headhash=g_Git.GetHash(_T("HEAD"));
437 if(!g_Git.CheckCleanWorkTree())
439 CMessageBox::Show(NULL,_T("Combine needs a clean work tree"),_T("TortoiseGit"),MB_OK);
440 break;
442 CString cmd,out;
444 //Use throw to abort this process (reset back to original HEAD)
447 cmd.Format(_T("git.exe reset --hard %s"),pFirstEntry->m_CommitHash.ToString());
448 if(g_Git.Run(cmd,&out,CP_UTF8))
450 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
451 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to first commit (first step) aborting...\r\n\r\n")+out));
453 cmd.Format(_T("git.exe reset --mixed %s"),hashLast.ToString());
454 if(g_Git.Run(cmd,&out,CP_UTF8))
456 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
457 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to last commit (second step) aborting...\r\n\r\n")+out));
460 CTGitPathList PathList;
461 /* don't why must add --stat to get action status*/
462 /* first -z will be omitted by gitdll*/
463 if(g_Git.GetDiffPath(&PathList,&pFirstEntry->m_CommitHash,&hashLast,"-z --stat -r"))
465 CMessageBox::Show(NULL,_T("Get Diff file list error"),_T("TortoiseGit"),MB_OK);
466 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not get changed file list aborting...\r\n\r\n")+out));
469 for(int i=0;i<PathList.GetCount();i++)
471 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_ADDED)
473 cmd.Format(_T("git.exe add -- \"%s\""), PathList[i].GetGitPathString());
474 if(g_Git.Run(cmd,&out,CP_ACP))
476 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
477 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not add new file aborting...\r\n\r\n")+out));
481 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_DELETED)
483 cmd.Format(_T("git.exe rm -- \"%s\""), PathList[i].GetGitPathString());
484 if(g_Git.Run(cmd,&out,CP_ACP))
486 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
487 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not rm file aborting...\r\n\r\n")+out));
492 CCommitDlg dlg;
493 for(int i=FirstSelect;i<=LastSelect;i++)
495 GitRev* pRev = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
496 dlg.m_sLogMessage+=pRev->GetSubject()+_T("\n")+pRev->GetBody();
497 dlg.m_sLogMessage+=_T("\n");
499 dlg.m_bWholeProject=true;
500 dlg.m_bSelectFilesForCommit = true;
501 dlg.m_bCommitAmend=true;
502 dlg.m_bNoPostActions=true;
503 dlg.m_AmendStr=dlg.m_sLogMessage;
505 if (dlg.DoModal() == IDOK)
507 if(pFirstEntry->m_CommitHash!=headhash)
509 //Commitrange firstEntry..headhash (from top of combine to original head) needs to be 'cherry-picked'
510 //on top of new commit.
511 //Use the rebase --onto command for it.
513 //All this can be done in one step using the following command:
514 //cmd.Format(_T("git.exe format-patch --stdout --binary --full-index -k %s..%s | git am -k -3"),
515 // pFirstEntry->m_CommitHash,
516 // headhash);
517 //But I am not sure if a '|' is going to work in a CreateProcess() call.
519 //Later the progress dialog could be used to execute these steps.
521 if(CherryPickFrom(pFirstEntry->m_CommitHash.ToString(),headhash))
523 CString msg;
524 msg.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));
525 throw std::exception(CUnicodeUtils::GetUTF8(msg));
527 #if 0
528 CString currentBranch=g_Git.GetCurrentBranch();
529 cmd.Format(_T("git.exe rebase --onto \"%s\" %s %s"),
530 currentBranch,
531 pFirstEntry->m_CommitHash,
532 headhash);
533 if(g_Git.Run(cmd,&out,CP_UTF8)!=0)
535 CString msg;
536 msg.Format(_T("Error while rebasing commits on top of combined commits. Aborting.\r\n\r\n%s"),out);
537 // CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);
538 g_Git.Run(_T("git.exe rebase --abort"),&out,CP_UTF8);
539 throw std::exception(CUnicodeUtils::GetUTF8(msg));
542 //HEAD is now on <no branch>.
543 //The following steps are to get HEAD back on the original branch and reset the branch to the new HEAD
544 //To avoid 2 working copy changes, we could use git branch -f <original branch> <hash new head>
545 //And then git checkout <original branch>
546 //But I don't know if 'git branch -f' removes tracking options. So for now, do a checkout and a reset.
548 //Store new HEAD
549 CString newHead=g_Git.GetHash(CString(_T("HEAD")));
551 //Checkout working branch
552 cmd.Format(_T("git.exe checkout -f \"%s\""),currentBranch);
553 if(g_Git.Run(cmd,&out,CP_UTF8))
554 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not checkout original branch. Aborting...\r\n\r\n")+out));
556 //Reset to new HEAD
557 cmd.Format(_T("git.exe reset --hard %s"),newHead);
558 if(g_Git.Run(cmd,&out,CP_UTF8))
559 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to new head. Aborting...\r\n\r\n")+out));
560 #endif
563 else
564 throw std::exception("User aborted the combine process");
566 catch(std::exception& e)
568 CMessageBox::Show(NULL,CUnicodeUtils::GetUnicode(CStringA(e.what())),_T("TortoiseGit: Combine error"),MB_OK|MB_ICONERROR);
569 cmd.Format(_T("git.exe reset --hard %s"),headhash.ToString());
570 out.Empty();
571 if(g_Git.Run(cmd,&out,CP_UTF8))
573 CMessageBox::Show(NULL,_T("Could not reset to original HEAD\r\n\r\n")+out,_T("TortoiseGit"),MB_OK);
576 Refresh();
578 break;
580 case ID_CHERRY_PICK:
581 if(!g_Git.CheckCleanWorkTree())
583 CMessageBox::Show(NULL,_T("Cherry Pick requires a clean working tree"),_T("TortoiseGit"),MB_OK);
586 else
588 CRebaseDlg dlg;
589 dlg.m_IsCherryPick = TRUE;
590 dlg.m_Upstream = this->m_CurrentBranch;
591 POSITION pos = GetFirstSelectedItemPosition();
592 while(pos)
594 int indexNext = GetNextSelectedItem(pos);
595 dlg.m_CommitList.m_logEntries.push_back( ((GitRev*)m_arShownList[indexNext])->m_CommitHash );
596 dlg.m_CommitList.m_LogCache.m_HashMap[((GitRev*)m_arShownList[indexNext])->m_CommitHash]=*(GitRev*)m_arShownList[indexNext];
597 dlg.m_CommitList.m_logEntries.GetGitRevAt(dlg.m_CommitList.m_logEntries.size()-1).GetAction(this) |= CTGitPath::LOGACTIONS_REBASE_PICK;
600 if(dlg.DoModal() == IDOK)
602 Refresh();
605 break;
606 case ID_REBASE_TO_VERSION:
607 if(!g_Git.CheckCleanWorkTree())
609 CMessageBox::Show(NULL,_T("Rebase requires a clean working tree"),_T("TortoiseGit"),MB_OK);
612 else
614 CRebaseDlg dlg;
615 dlg.m_Upstream = pSelLogEntry->m_CommitHash;
617 if(dlg.DoModal() == IDOK)
619 Refresh();
623 break;
625 case ID_STASH_APPLY:
626 CAppUtils::StashApply(pSelLogEntry->m_Ref);
627 break;
629 case ID_REFLOG_DEL:
631 CString ref = pSelLogEntry->m_Ref;
632 if (ref.Find(_T("refs/")) == 0)
633 ref = ref.Mid(5);
634 int pos = ref.ReverseFind('{');
635 if (pos > 0 && ref.Mid(pos, 2) != _T("@{"))
636 ref = ref.Left(pos) + _T("@")+ ref.Mid(pos);
637 CString str;
638 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?"), ref);
639 if(CMessageBox::Show(NULL, str, _T("TortoiseGit"), 1, IDI_QUESTION, _T("&Delete"), _T("&Abort")) == 1)
641 CString cmd,out;
642 if (ref.Find(_T("stash")) == 0)
644 cmd.Format(_T("git.exe stash drop %s"), ref);
646 else
647 cmd.Format(_T("git.exe reflog delete %s"), ref);
648 if(g_Git.Run(cmd,&out,CP_ACP))
650 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
652 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
655 break;
656 case ID_LOG:
658 CString cmd;
659 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
660 cmd += _T(" /command:log");
661 cmd += _T(" /path:\"")+g_Git.m_CurrentDir+_T("\" ");
662 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
663 cmd += _T(" /endrev:")+r1->m_CommitHash.ToString();
664 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
666 break;
667 case ID_CREATE_PATCH:
669 int select=this->GetSelectedCount();
670 CString cmd;
671 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
672 cmd += _T(" /command:formatpatch");
674 cmd += _T(" /path:\"")+g_Git.m_CurrentDir+_T("\" ");
676 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
677 GitRev * r2 = NULL;
678 if(select == 1)
680 cmd += _T(" /startrev:")+r1->m_CommitHash.ToString();
682 else
684 r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
685 if( this->m_IsOldFirst )
687 cmd += _T(" /startrev:")+r1->m_CommitHash.ToString()+_T("~1");
688 cmd += _T(" /endrev:")+r2->m_CommitHash.ToString();
691 else
693 cmd += _T(" /startrev:")+r2->m_CommitHash.ToString()+_T("~1");
694 cmd += _T(" /endrev:")+r1->m_CommitHash.ToString();
699 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
701 break;
702 case ID_PUSH:
704 CString guessAssociatedBranch;
705 if (m_HashMap[pSelLogEntry->m_CommitHash].size() > 0)
706 guessAssociatedBranch = m_HashMap[pSelLogEntry->m_CommitHash].at(0);
707 if (CAppUtils::Push(guessAssociatedBranch))
708 Refresh();
710 break;
711 case ID_DELETE:
713 int index = cmd>>16;
714 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) == m_HashMap.end() )
716 CMessageBox::Show(NULL,IDS_ERROR_NOREF,IDS_APPNAME,MB_OK|MB_ICONERROR);
717 return;
719 if( index >= m_HashMap[pSelLogEntry->m_CommitHash].size())
721 CMessageBox::Show(NULL,IDS_ERROR_INDEX,IDS_APPNAME,MB_OK|MB_ICONERROR);
722 return;
724 CString ref,msg;
725 ref=m_HashMap[pSelLogEntry->m_CommitHash][index];
727 msg=CString(_T("Do you really want to <ct=0x0000FF>delete</ct> <b>"))+ref;
728 msg+=_T("</b>?");
729 if( CMessageBox::Show(NULL, msg, _T("TortoiseGit"), 2, IDI_QUESTION, _T("&Delete"), _T("&Abort")) == 1 )
731 CString shortname;
732 CString cmd;
733 if(this->GetShortName(ref,shortname,_T("refs/heads/")))
735 cmd.Format(_T("git.exe branch -D -- %s"),shortname);
738 if(this->GetShortName(ref,shortname,_T("refs/remotes/")))
740 cmd.Format(_T("git.exe branch -r -D -- %s"),shortname);
743 if(this->GetShortName(ref,shortname,_T("refs/tags/")))
745 cmd.Format(_T("git.exe tag -d -- %s"),shortname);
748 if(this->GetShortName(ref,shortname,_T("refs/stash")))
750 if(CMessageBox::Show(NULL, _T("<ct=0x0000FF>Do you really want to delete <b>ALL</b> stash?</ct>"),
751 _T("TortoiseGit"), 2, IDI_QUESTION, _T("&Delete"), _T("&Abort")) == 1)
752 cmd.Format(_T("git.exe stash clear"));
753 else
754 return;
757 CString out;
758 if(g_Git.Run(cmd,&out,CP_UTF8))
760 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
762 this->ReloadHashMap();
763 CRect rect;
764 this->GetItemRect(FirstSelect,&rect,LVIR_BOUNDS);
765 this->InvalidateRect(rect);
768 break;
770 case ID_FINDENTRY:
772 m_nSearchIndex = GetSelectionMark();
773 if (m_nSearchIndex < 0)
774 m_nSearchIndex = 0;
775 if (m_pFindDialog)
777 break;
779 else
781 m_pFindDialog = new CFindDlg();
782 m_pFindDialog->Create(this);
785 break;
786 case ID_MERGEREV:
788 CString str = pSelLogEntry->m_CommitHash.ToString();
789 if (m_HashMap[pSelLogEntry->m_CommitHash].size() > 0)
790 str = m_HashMap[pSelLogEntry->m_CommitHash].at(0);
791 // we need an URL to complete this command, so error out if we can't get an URL
792 if(CAppUtils::Merge(&str))
794 this->Refresh();
797 break;
798 case ID_REVERTREV:
800 if(!this->RevertSelectedCommits())
801 this->Refresh();
803 break;
804 case ID_EDITNOTE:
806 CAppUtils::EditNote(pSelLogEntry);
807 this->SetItemState(FirstSelect, 0, LVIS_SELECTED);
808 this->SetItemState(FirstSelect, LVIS_SELECTED, LVIS_SELECTED);
810 break;
811 default:
812 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
813 break;
814 #if 0
816 case ID_BLAMECOMPARE:
818 //user clicked on the menu item "compare with working copy"
819 //now first get the revision which is selected
820 if (PromptShown())
822 GitDiff diff(this, this->m_hWnd, true);
823 diff.SetHEADPeg(m_LogRevision);
824 diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
826 else
827 CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
829 break;
830 case ID_BLAMETWO:
832 //user clicked on the menu item "compare and blame revisions"
833 if (PromptShown())
835 GitDiff diff(this, this->m_hWnd, true);
836 diff.SetHEADPeg(m_LogRevision);
837 diff.ShowCompare(CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), false, true);
839 else
840 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
842 break;
843 case ID_BLAMEWITHPREVIOUS:
845 //user clicked on the menu item "Compare and Blame with previous revision"
846 if (PromptShown())
848 GitDiff diff(this, this->m_hWnd, true);
849 diff.SetHEADPeg(m_LogRevision);
850 diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
852 else
853 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
855 break;
857 case ID_OPENWITH:
858 bOpenWith = true;
859 case ID_OPEN:
861 CProgressDlg progDlg;
862 progDlg.SetTitle(IDS_APPNAME);
863 progDlg.SetAnimation(IDR_DOWNLOAD);
864 CString sInfoLine;
865 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
866 progDlg.SetLine(1, sInfoLine, true);
867 SetAndClearProgressInfo(&progDlg);
868 progDlg.ShowModeless(m_hWnd);
869 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
870 bool bSuccess = true;
871 if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
873 bSuccess = false;
874 // try again, but with the selected revision as the peg revision
875 if (!Cat(m_path, revSelected, revSelected, tempfile))
877 progDlg.Stop();
878 SetAndClearProgressInfo((HWND)NULL);
879 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
880 EnableOKButton();
881 break;
883 bSuccess = true;
885 if (bSuccess)
887 progDlg.Stop();
888 SetAndClearProgressInfo((HWND)NULL);
889 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
890 int ret = 0;
891 if (!bOpenWith)
892 ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
893 if ((ret <= HINSTANCE_ERROR)||bOpenWith)
895 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
896 cmd += tempfile.GetWinPathString() + _T(" ");
897 CAppUtils::LaunchApplication(cmd, NULL, false);
901 break;
902 case ID_BLAME:
904 CBlameDlg dlg;
905 dlg.EndRev = revSelected;
906 if (dlg.DoModal() == IDOK)
908 CBlame blame;
909 CString tempfile;
910 CString logfile;
911 tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
912 if (!tempfile.IsEmpty())
914 if (dlg.m_bTextView)
916 //open the default text editor for the result file
917 CAppUtils::StartTextViewer(tempfile);
919 else
921 CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
922 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
924 break;
928 else
930 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
934 break;
935 case ID_UPDATE:
937 CString sCmd;
938 CString url = _T("tgit:")+pathURL;
939 sCmd.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
940 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
941 (LPCTSTR)m_path.GetWinPath(), (LONG)revSelected);
942 CAppUtils::LaunchApplication(sCmd, NULL, false);
944 break;
946 case ID_EDITLOG:
948 EditLogMessage(selIndex);
950 break;
951 case ID_EDITAUTHOR:
953 EditAuthor(selEntries);
955 break;
956 case ID_REVPROPS:
958 CEditPropertiesDlg dlg;
959 dlg.SetProjectProperties(&m_ProjectProperties);
960 CTGitPathList escapedlist;
961 dlg.SetPathList(CTGitPathList(CTGitPath(pathURL)));
962 dlg.SetRevision(revSelected);
963 dlg.RevProps(true);
964 dlg.DoModal();
966 break;
968 case ID_EXPORT:
970 CString sCmd;
971 sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
972 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
973 (LPCTSTR)pathURL, (LONG)revSelected);
974 CAppUtils::LaunchApplication(sCmd, NULL, false);
976 break;
977 case ID_VIEWREV:
979 CString url = m_ProjectProperties.sWebViewerRev;
980 url = GetAbsoluteUrlFromRelativeUrl(url);
981 url.Replace(_T("%REVISION%"), revSelected.ToString());
982 if (!url.IsEmpty())
983 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
985 break;
986 case ID_VIEWPATHREV:
988 CString relurl = pathURL;
989 CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
990 relurl = relurl.Mid(sRoot.GetLength());
991 CString url = m_ProjectProperties.sWebViewerPathRev;
992 url = GetAbsoluteUrlFromRelativeUrl(url);
993 url.Replace(_T("%REVISION%"), revSelected.ToString());
994 url.Replace(_T("%PATH%"), relurl);
995 if (!url.IsEmpty())
996 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
998 break;
999 #endif
1001 } // switch (cmd)
1003 theApp.DoWaitCursor(-1);
1006 void CGitLogList::SetSelectedAction(int action)
1008 POSITION pos = GetFirstSelectedItemPosition();
1009 int index;
1010 while(pos)
1012 index = GetNextSelectedItem(pos);
1013 ((GitRev*)m_arShownList[index])->GetAction(this) = action;
1014 CRect rect;
1015 this->GetItemRect(index,&rect,LVIR_BOUNDS);
1016 this->InvalidateRect(rect);
1020 void CGitLogList::ShiftSelectedAction()
1022 POSITION pos = GetFirstSelectedItemPosition();
1023 int index;
1024 while(pos)
1026 index = GetNextSelectedItem(pos);
1027 int action = ((GitRev*)m_arShownList[index])->GetAction(this);
1028 switch (action)
1030 case CTGitPath::LOGACTIONS_REBASE_PICK:
1031 action = CTGitPath::LOGACTIONS_REBASE_SKIP;
1032 break;
1033 case CTGitPath::LOGACTIONS_REBASE_SKIP:
1034 action= CTGitPath::LOGACTIONS_REBASE_EDIT;
1035 break;
1036 case CTGitPath::LOGACTIONS_REBASE_EDIT:
1037 action = CTGitPath::LOGACTIONS_REBASE_SQUASH;
1038 break;
1039 case CTGitPath::LOGACTIONS_REBASE_SQUASH:
1040 action= CTGitPath::LOGACTIONS_REBASE_PICK;
1041 break;
1043 ((GitRev*)m_arShownList[index])->GetAction(this) = action;
1044 CRect rect;
1045 this->GetItemRect(index, &rect, LVIR_BOUNDS);
1046 this->InvalidateRect(rect);