Fixed issue #139: "Format patch" with a range of revisions doesn't export the first...
[TortoiseGit.git] / src / TortoiseProc / GitLogListAction.cpp
blob7bf709b1e4fad54108b8dd1ce1dc652be7e262e7
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"
52 IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl)
54 int CGitLogList::CherryPickFrom(CString from, CString to)
56 CLogDataVector logs;
57 if(logs.ParserFromLog(NULL,-1,0,&from,&to))
58 return -1;
60 if(logs.size() == 0)
61 return 0;
63 CSysProgressDlg progress;
64 if (progress.IsValid())
66 progress.SetTitle(_T("Cherry Pick"));
67 progress.SetAnimation(IDR_MOVEANI);
68 progress.SetTime(true);
69 progress.ShowModeless(this);
72 for(int i=logs.size()-1;i>=0;i--)
74 if (progress.IsValid())
76 progress.FormatPathLine(1, _T("Pick up %s"), logs[i].m_CommitHash);
77 progress.FormatPathLine(2, _T("%s"), logs[i].m_Subject);
78 progress.SetProgress(logs.size()-i, logs.size());
80 if ((progress.IsValid())&&(progress.HasUserCancelled()))
82 //CMessageBox::Show(hwndExplorer, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_ICONINFORMATION);
83 throw std::exception(CUnicodeUtils::GetUTF8(_T("User canceled\r\n\r\n")));
84 return -1;
86 CString cmd,out;
87 cmd.Format(_T("git.exe cherry-pick %s"),logs[i].m_CommitHash);
88 out.Empty();
89 if(g_Git.Run(cmd,&out,CP_UTF8))
91 throw std::exception(CUnicodeUtils::GetUTF8(CString(_T("Cherry Pick Failure\r\n\r\n"))+out));
92 return -1;
96 return 0;
99 void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect)
101 POSITION pos = GetFirstSelectedItemPosition();
102 int indexNext = GetNextSelectedItem(pos);
103 if (indexNext < 0)
104 return;
106 GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));
108 theApp.DoWaitCursor(1);
109 bool bOpenWith = false;
110 switch (cmd&0xFFFF)
112 case ID_GNUDIFF1:
114 CString tempfile=GetTempFile();
115 CString cmd;
116 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
117 cmd.Format(_T("git.exe diff-tree -r -p --stat %s"),r1->m_CommitHash);
118 g_Git.RunLogFile(cmd,tempfile);
119 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r1->m_Subject);
121 break;
123 case ID_GNUDIFF2:
125 CString tempfile=GetTempFile();
126 CString cmd;
127 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
128 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
129 cmd.Format(_T("git.exe diff-tree -r -p --stat %s %s"),r1->m_CommitHash,r2->m_CommitHash);
130 g_Git.RunLogFile(cmd,tempfile);
131 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r2->m_CommitHash.Left(6));
134 break;
136 case ID_COMPARETWO:
138 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
139 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
140 CFileDiffDlg dlg;
141 dlg.SetDiff(NULL,*r1,*r2);
142 dlg.DoModal();
145 break;
148 case ID_COMPARE:
150 GitRev * r1 = &m_wcRev;
151 GitRev * r2 = pSelLogEntry;
152 CFileDiffDlg dlg;
153 dlg.SetDiff(NULL,*r1,*r2);
154 dlg.DoModal();
156 //user clicked on the menu item "compare with working copy"
157 //if (PromptShown())
159 // GitDiff diff(this, m_hWnd, true);
160 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
161 // diff.SetHEADPeg(m_LogRevision);
162 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
164 //else
165 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
167 break;
169 case ID_COMPAREWITHPREVIOUS:
172 CFileDiffDlg dlg;
174 if(pSelLogEntry->m_ParentHash.size()>0)
175 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
177 dlg.SetDiff(NULL,pSelLogEntry->m_CommitHash,pSelLogEntry->m_ParentHash[0]);
178 dlg.DoModal();
179 }else
181 CMessageBox::Show(NULL,_T("No previous version"),_T("TortoiseGit"),MB_OK);
183 //if (PromptShown())
185 // GitDiff diff(this, m_hWnd, true);
186 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
187 // diff.SetHEADPeg(m_LogRevision);
188 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
190 //else
191 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
193 break;
194 case ID_COPYCLIPBOARD:
196 CopySelectionToClipBoard();
198 break;
199 case ID_COPYHASH:
201 CopySelectionToClipBoard(TRUE);
203 break;
204 case ID_EXPORT:
205 CAppUtils::Export(&pSelLogEntry->m_CommitHash);
206 break;
207 case ID_CREATE_BRANCH:
208 CAppUtils::CreateBranchTag(FALSE,&pSelLogEntry->m_CommitHash);
209 ReloadHashMap();
210 Invalidate();
211 break;
212 case ID_CREATE_TAG:
213 CAppUtils::CreateBranchTag(TRUE,&pSelLogEntry->m_CommitHash);
214 ReloadHashMap();
215 Invalidate();
216 break;
217 case ID_SWITCHTOREV:
218 CAppUtils::Switch(&pSelLogEntry->m_CommitHash);
219 ReloadHashMap();
220 Invalidate();
221 break;
222 case ID_RESET:
223 CAppUtils::GitReset(&pSelLogEntry->m_CommitHash);
224 ReloadHashMap();
225 Invalidate();
226 break;
227 case ID_REBASE_PICK:
228 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_PICK);
229 break;
230 case ID_REBASE_EDIT:
231 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_EDIT);
232 break;
233 case ID_REBASE_SQUASH:
234 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SQUASH);
235 break;
236 case ID_REBASE_SKIP:
237 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SKIP);
238 break;
239 case ID_COMBINE_COMMIT:
241 CString head;
242 CString headhash;
243 CString hashFirst,hashLast;
245 int headindex=GetHeadIndex();
246 if(headindex>=0) //incase show all branch, head is not the first commits.
248 head.Format(_T("HEAD~%d"),FirstSelect-headindex);
249 hashFirst=g_Git.GetHash(head);
251 head.Format(_T("HEAD~%d"),LastSelect-headindex);
252 hashLast=g_Git.GetHash(head);
255 GitRev* pFirstEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
256 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
257 if(pFirstEntry->m_CommitHash != hashFirst || pLastEntry->m_CommitHash != hashLast)
259 CMessageBox::Show(NULL,_T(
260 "Cannot combine commits now.\r\n\
261 Make sure you are viewing the log of your current branch and \
262 no filters are applied."),_T("TortoiseGit"),MB_OK);
263 break;
266 headhash=g_Git.GetHash(CString(_T("HEAD")));
268 if(!g_Git.CheckCleanWorkTree())
270 CMessageBox::Show(NULL,_T("Combine needs a clean work tree"),_T("TortoiseGit"),MB_OK);
271 break;
273 CString cmd,out;
275 //Use throw to abort this process (reset back to original HEAD)
278 cmd.Format(_T("git.exe reset --hard %s"),pFirstEntry->m_CommitHash);
279 if(g_Git.Run(cmd,&out,CP_UTF8))
281 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
282 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to first commit (first step) aborting...\r\n\r\n")+out));
284 cmd.Format(_T("git.exe reset --mixed %s"),hashLast);
285 if(g_Git.Run(cmd,&out,CP_UTF8))
287 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
288 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to last commit (second step) aborting...\r\n\r\n")+out));
290 CCommitDlg dlg;
291 for(int i=FirstSelect;i<=LastSelect;i++)
293 GitRev* pRev = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
294 dlg.m_sLogMessage+=pRev->m_Subject+_T("\n")+pRev->m_Body;
295 dlg.m_sLogMessage+=_T("\n");
297 dlg.m_bWholeProject=true;
298 dlg.m_bSelectFilesForCommit = true;
299 dlg.m_bCommitAmend=true;
300 dlg.m_AmendStr=dlg.m_sLogMessage;
302 bool abort=false;
303 if (dlg.DoModal() == IDOK)
305 if(pFirstEntry->m_CommitHash!=headhash)
307 //Commitrange firstEntry..headhash (from top of combine to original head) needs to be 'cherry-picked'
308 //on top of new commit.
309 //Use the rebase --onto command for it.
311 //All this can be done in one step using the following command:
312 //cmd.Format(_T("git.exe format-patch --stdout --binary --full-index -k %s..%s | git am -k -3"),
313 // pFirstEntry->m_CommitHash,
314 // headhash);
315 //But I am not sure if a '|' is going to work in a CreateProcess() call.
317 //Later the progress dialog could be used to execute these steps.
319 if(CherryPickFrom(pFirstEntry->m_CommitHash,headhash))
321 CString msg;
322 msg.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));
323 throw std::exception(CUnicodeUtils::GetUTF8(msg));
325 #if 0
326 CString currentBranch=g_Git.GetCurrentBranch();
327 cmd.Format(_T("git.exe rebase --onto \"%s\" %s %s"),
328 currentBranch,
329 pFirstEntry->m_CommitHash,
330 headhash);
331 if(g_Git.Run(cmd,&out,CP_UTF8)!=0)
333 CString msg;
334 msg.Format(_T("Error while rebasing commits on top of combined commits. Aborting.\r\n\r\n%s"),out);
335 // CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);
336 g_Git.Run(_T("git.exe rebase --abort"),&out,CP_UTF8);
337 throw std::exception(CUnicodeUtils::GetUTF8(msg));
340 //HEAD is now on <no branch>.
341 //The following steps are to get HEAD back on the original branch and reset the branch to the new HEAD
342 //To avoid 2 working copy changes, we could use git branch -f <original branch> <hash new head>
343 //And then git checkout <original branch>
344 //But I don't know if 'git branch -f' removes tracking options. So for now, do a checkout and a reset.
346 //Store new HEAD
347 CString newHead=g_Git.GetHash(CString(_T("HEAD")));
349 //Checkout working branch
350 cmd.Format(_T("git.exe checkout -f \"%s\""),currentBranch);
351 if(g_Git.Run(cmd,&out,CP_UTF8))
352 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not checkout original branch. Aborting...\r\n\r\n")+out));
354 //Reset to new HEAD
355 cmd.Format(_T("git.exe reset --hard %s"),newHead);
356 if(g_Git.Run(cmd,&out,CP_UTF8))
357 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to new head. Aborting...\r\n\r\n")+out));
358 #endif
361 else
362 throw std::exception("User aborted the combine process");
364 catch(std::exception& e)
366 CMessageBox::Show(NULL,CUnicodeUtils::GetUnicode(CStringA(e.what())),_T("TortoiseGit: Combine error"),MB_OK|MB_ICONERROR);
367 cmd.Format(_T("git.exe reset --hard %s"),headhash);
368 out.Empty();
369 if(g_Git.Run(cmd,&out,CP_UTF8))
371 CMessageBox::Show(NULL,_T("Could not reset to original HEAD\r\n\r\n")+out,_T("TortoiseGit"),MB_OK);
374 Refresh();
376 break;
378 case ID_CHERRY_PICK:
379 if(!g_Git.CheckCleanWorkTree())
381 CMessageBox::Show(NULL,_T("Cherry Pick requires a clean working tree"),_T("TortoiseGit"),MB_OK);
383 }else
385 CRebaseDlg dlg;
386 dlg.m_IsCherryPick = TRUE;
387 dlg.m_Upstream = this->m_CurrentBranch;
388 POSITION pos = GetFirstSelectedItemPosition();
389 while(pos)
391 int indexNext = GetNextSelectedItem(pos);
392 dlg.m_CommitList.m_logEntries.push_back(*(GitRev*)m_arShownList[indexNext]);
393 dlg.m_CommitList.m_logEntries.at(dlg.m_CommitList.m_logEntries.size()-1).m_Action |= CTGitPath::LOGACTIONS_REBASE_PICK;
396 if(dlg.DoModal() == IDOK)
398 Refresh();
401 break;
402 case ID_REBASE_TO_VERSION:
403 if(!g_Git.CheckCleanWorkTree())
405 CMessageBox::Show(NULL,_T("Rebase requires a clean working tree"),_T("TortoiseGit"),MB_OK);
407 }else
409 CRebaseDlg dlg;
410 dlg.m_Upstream = pSelLogEntry->m_CommitHash;
412 if(dlg.DoModal() == IDOK)
414 Refresh();
418 break;
420 case ID_STASH_APPLY:
421 CAppUtils::StashApply(pSelLogEntry->m_Ref);
422 break;
424 case ID_REFLOG_DEL:
426 CString str;
427 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);
428 if(CMessageBox::Show(NULL,str,_T("TortoiseGit"),MB_YESNO|MB_ICONWARNING) == IDYES)
430 CString cmd,out;
431 cmd.Format(_T("git.exe reflog delete %s"),pSelLogEntry->m_Ref);
432 if(g_Git.Run(cmd,&out,CP_ACP))
434 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
436 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
439 break;
440 case ID_CREATE_PATCH:
442 int select=this->GetSelectedCount();
443 CString cmd;
444 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
445 cmd += _T(" /command:formatpatch");
447 cmd += _T(" /path:")+g_Git.m_CurrentDir+_T(" ");
449 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
450 GitRev * r2 = NULL;
451 if(select == 1)
453 cmd += _T(" /startrev:")+r1->m_CommitHash;
455 else
457 r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
458 if( this->m_IsOldFirst )
460 cmd += _T(" /startrev:")+r1->m_CommitHash+_T("~1");
461 cmd += _T(" /endrev:")+r2->m_CommitHash;
463 }else
465 cmd += _T(" /startrev:")+r2->m_CommitHash+_T("~1");
466 cmd += _T(" /endrev:")+r1->m_CommitHash;
471 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
473 break;
474 case ID_DELETE:
476 int index = cmd>>16;
477 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) == m_HashMap.end() )
479 CMessageBox::Show(NULL,IDS_ERROR_NOREF,IDS_APPNAME,MB_OK|MB_ICONERROR);
480 return;
482 if( index >= m_HashMap[pSelLogEntry->m_CommitHash].size())
484 CMessageBox::Show(NULL,IDS_ERROR_INDEX,IDS_APPNAME,MB_OK|MB_ICONERROR);
485 return;
487 CString ref,msg;
488 ref=m_HashMap[pSelLogEntry->m_CommitHash][index];
490 msg=CString(_T("<ct=0x0000FF>Delete</ct> <b>"))+ref;
491 msg+=_T("</b>\n\n Are you sure?");
492 if( CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_YESNO) == IDYES )
494 CString shortname;
495 CString cmd;
496 if(this->GetShortName(ref,shortname,_T("refs/heads/")))
498 cmd.Format(_T("git.exe branch -D %s"),shortname);
501 if(this->GetShortName(ref,shortname,_T("refs/remotes/")))
503 cmd.Format(_T("git.exe branch -r -D %s"),shortname);
506 if(this->GetShortName(ref,shortname,_T("refs/tags/")))
508 cmd.Format(_T("git.exe tag -d %s"),shortname);
511 CString out;
512 if(g_Git.Run(cmd,&out,CP_UTF8))
514 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
516 this->ReloadHashMap();
517 CRect rect;
518 this->GetItemRect(FirstSelect,&rect,LVIR_BOUNDS);
519 this->InvalidateRect(rect);
522 break;
523 default:
524 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
525 break;
526 #if 0
528 case ID_REVERTREV:
530 // we need an URL to complete this command, so error out if we can't get an URL
531 if (pathURL.IsEmpty())
533 CString strMessage;
534 strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
535 CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
536 TRACE(_T("could not retrieve the URL of the folder!\n"));
537 break; //exit
539 CString msg;
540 msg.Format(IDS_LOG_REVERT_CONFIRM, m_path.GetWinPath());
541 if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
543 CGitProgressDlg dlg;
544 dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
545 dlg.SetPathList(CTGitPathList(m_path));
546 dlg.SetUrl(pathURL);
547 dlg.SetSecondUrl(pathURL);
548 revisionRanges.AdjustForMerge(true);
549 dlg.SetRevisionRanges(revisionRanges);
550 dlg.SetPegRevision(m_LogRevision);
551 dlg.DoModal();
554 break;
555 case ID_MERGEREV:
557 // we need an URL to complete this command, so error out if we can't get an URL
558 if (pathURL.IsEmpty())
560 CString strMessage;
561 strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
562 CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
563 TRACE(_T("could not retrieve the URL of the folder!\n"));
564 break; //exit
567 CString path = m_path.GetWinPathString();
568 bool bGotSavePath = false;
569 if ((GetSelectedCount() == 1)&&(!m_path.IsDirectory()))
571 bGotSavePath = CAppUtils::FileOpenSave(path, NULL, IDS_LOG_MERGETO, IDS_COMMONFILEFILTER, true, GetSafeHwnd());
573 else
575 CBrowseFolder folderBrowser;
576 folderBrowser.SetInfo(CString(MAKEINTRESOURCE(IDS_LOG_MERGETO)));
577 bGotSavePath = (folderBrowser.Show(GetSafeHwnd(), path, path) == CBrowseFolder::OK);
579 if (bGotSavePath)
581 CGitProgressDlg dlg;
582 dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
583 dlg.SetPathList(CTGitPathList(CTGitPath(path)));
584 dlg.SetUrl(pathURL);
585 dlg.SetSecondUrl(pathURL);
586 revisionRanges.AdjustForMerge(false);
587 dlg.SetRevisionRanges(revisionRanges);
588 dlg.SetPegRevision(m_LogRevision);
589 dlg.DoModal();
592 break;
593 case ID_REVERTTOREV:
595 // we need an URL to complete this command, so error out if we can't get an URL
596 if (pathURL.IsEmpty())
598 CString strMessage;
599 strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
600 CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
601 TRACE(_T("could not retrieve the URL of the folder!\n"));
602 break; //exit
605 CString msg;
606 msg.Format(IDS_LOG_REVERTTOREV_CONFIRM, m_path.GetWinPath());
607 if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
609 CGitProgressDlg dlg;
610 dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
611 dlg.SetPathList(CTGitPathList(m_path));
612 dlg.SetUrl(pathURL);
613 dlg.SetSecondUrl(pathURL);
614 GitRevRangeArray revarray;
615 revarray.AddRevRange(GitRev::REV_HEAD, revSelected);
616 dlg.SetRevisionRanges(revarray);
617 dlg.SetPegRevision(m_LogRevision);
618 dlg.DoModal();
621 break;
625 case ID_BLAMECOMPARE:
627 //user clicked on the menu item "compare with working copy"
628 //now first get the revision which is selected
629 if (PromptShown())
631 GitDiff diff(this, this->m_hWnd, true);
632 diff.SetHEADPeg(m_LogRevision);
633 diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
635 else
636 CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
638 break;
639 case ID_BLAMETWO:
641 //user clicked on the menu item "compare and blame revisions"
642 if (PromptShown())
644 GitDiff diff(this, this->m_hWnd, true);
645 diff.SetHEADPeg(m_LogRevision);
646 diff.ShowCompare(CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), false, true);
648 else
649 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
651 break;
652 case ID_BLAMEWITHPREVIOUS:
654 //user clicked on the menu item "Compare and Blame with previous revision"
655 if (PromptShown())
657 GitDiff diff(this, this->m_hWnd, true);
658 diff.SetHEADPeg(m_LogRevision);
659 diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
661 else
662 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
664 break;
666 case ID_OPENWITH:
667 bOpenWith = true;
668 case ID_OPEN:
670 CProgressDlg progDlg;
671 progDlg.SetTitle(IDS_APPNAME);
672 progDlg.SetAnimation(IDR_DOWNLOAD);
673 CString sInfoLine;
674 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
675 progDlg.SetLine(1, sInfoLine, true);
676 SetAndClearProgressInfo(&progDlg);
677 progDlg.ShowModeless(m_hWnd);
678 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
679 bool bSuccess = true;
680 if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
682 bSuccess = false;
683 // try again, but with the selected revision as the peg revision
684 if (!Cat(m_path, revSelected, revSelected, tempfile))
686 progDlg.Stop();
687 SetAndClearProgressInfo((HWND)NULL);
688 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
689 EnableOKButton();
690 break;
692 bSuccess = true;
694 if (bSuccess)
696 progDlg.Stop();
697 SetAndClearProgressInfo((HWND)NULL);
698 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
699 int ret = 0;
700 if (!bOpenWith)
701 ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
702 if ((ret <= HINSTANCE_ERROR)||bOpenWith)
704 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
705 cmd += tempfile.GetWinPathString() + _T(" ");
706 CAppUtils::LaunchApplication(cmd, NULL, false);
710 break;
711 case ID_BLAME:
713 CBlameDlg dlg;
714 dlg.EndRev = revSelected;
715 if (dlg.DoModal() == IDOK)
717 CBlame blame;
718 CString tempfile;
719 CString logfile;
720 tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
721 if (!tempfile.IsEmpty())
723 if (dlg.m_bTextView)
725 //open the default text editor for the result file
726 CAppUtils::StartTextViewer(tempfile);
728 else
730 CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
731 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
733 break;
737 else
739 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
743 break;
744 case ID_UPDATE:
746 CString sCmd;
747 CString url = _T("tgit:")+pathURL;
748 sCmd.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
749 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
750 (LPCTSTR)m_path.GetWinPath(), (LONG)revSelected);
751 CAppUtils::LaunchApplication(sCmd, NULL, false);
753 break;
754 case ID_FINDENTRY:
756 m_nSearchIndex = GetSelectionMark();
757 if (m_nSearchIndex < 0)
758 m_nSearchIndex = 0;
759 if (m_pFindDialog)
761 break;
763 else
765 m_pFindDialog = new CFindReplaceDialog();
766 m_pFindDialog->Create(TRUE, NULL, NULL, FR_HIDEUPDOWN | FR_HIDEWHOLEWORD, this);
769 break;
770 case ID_REPOBROWSE:
772 CString sCmd;
773 sCmd.Format(_T("%s /command:repobrowser /path:\"%s\" /rev:%s"),
774 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
775 (LPCTSTR)pathURL, (LPCTSTR)revSelected.ToString());
777 CAppUtils::LaunchApplication(sCmd, NULL, false);
779 break;
780 case ID_EDITLOG:
782 EditLogMessage(selIndex);
784 break;
785 case ID_EDITAUTHOR:
787 EditAuthor(selEntries);
789 break;
790 case ID_REVPROPS:
792 CEditPropertiesDlg dlg;
793 dlg.SetProjectProperties(&m_ProjectProperties);
794 CTGitPathList escapedlist;
795 dlg.SetPathList(CTGitPathList(CTGitPath(pathURL)));
796 dlg.SetRevision(revSelected);
797 dlg.RevProps(true);
798 dlg.DoModal();
800 break;
802 case ID_EXPORT:
804 CString sCmd;
805 sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
806 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
807 (LPCTSTR)pathURL, (LONG)revSelected);
808 CAppUtils::LaunchApplication(sCmd, NULL, false);
810 break;
811 case ID_CHECKOUT:
813 CString sCmd;
814 CString url = _T("tgit:")+pathURL;
815 sCmd.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),
816 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
817 (LPCTSTR)url, (LONG)revSelected);
818 CAppUtils::LaunchApplication(sCmd, NULL, false);
820 break;
821 case ID_VIEWREV:
823 CString url = m_ProjectProperties.sWebViewerRev;
824 url = GetAbsoluteUrlFromRelativeUrl(url);
825 url.Replace(_T("%REVISION%"), revSelected.ToString());
826 if (!url.IsEmpty())
827 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
829 break;
830 case ID_VIEWPATHREV:
832 CString relurl = pathURL;
833 CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
834 relurl = relurl.Mid(sRoot.GetLength());
835 CString url = m_ProjectProperties.sWebViewerPathRev;
836 url = GetAbsoluteUrlFromRelativeUrl(url);
837 url.Replace(_T("%REVISION%"), revSelected.ToString());
838 url.Replace(_T("%PATH%"), relurl);
839 if (!url.IsEmpty())
840 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
842 break;
843 #endif
845 } // switch (cmd)
847 theApp.DoWaitCursor(-1);
850 void CGitLogList::SetSelectedAction(int action)
852 POSITION pos = GetFirstSelectedItemPosition();
853 int index;
854 while(pos)
856 index = GetNextSelectedItem(pos);
857 ((GitRev*)m_arShownList[index])->m_Action = action;
858 CRect rect;
859 this->GetItemRect(index,&rect,LVIR_BOUNDS);
860 this->InvalidateRect(rect);