1 // GitLogList.cpp : implementation file
\r
4 Description: qgit revision list view
\r
6 Author: Marco Costalba (C) 2005-2007
\r
8 Copyright: See COPYING file that comes with this distribution
\r
12 #include "TortoiseProc.h"
\r
13 #include "GitLogList.h"
\r
15 //#include "VssStyle.h"
\r
16 #include "IconMenu.h"
\r
19 #include "InputDlg.h"
\r
20 #include "PropDlg.h"
\r
21 #include "SVNProgressDlg.h"
\r
22 #include "ProgressDlg.h"
\r
23 #include "SysProgressDlg.h"
\r
24 //#include "RepositoryBrowser.h"
\r
25 //#include "CopyDlg.h"
\r
26 //#include "StatGraphDlg.h"
\r
28 #include "MessageBox.h"
\r
29 #include "Registry.h"
\r
30 #include "AppUtils.h"
\r
31 #include "PathUtils.h"
\r
32 #include "StringUtils.h"
\r
33 #include "UnicodeUtils.h"
\r
34 #include "TempFile.h"
\r
35 //#include "GitInfo.h"
\r
36 //#include "GitDiff.h"
\r
37 #include "IconMenu.h"
\r
38 //#include "RevisionRangeDlg.h"
\r
39 //#include "BrowseFolder.h"
\r
40 //#include "BlameDlg.h"
\r
41 //#include "Blame.h"
\r
42 //#include "GitHelpers.h"
\r
43 #include "GitStatus.h"
\r
44 //#include "LogDlgHelper.h"
\r
45 //#include "CachedLogInfo.h"
\r
46 //#include "RepositoryInfo.h"
\r
47 //#include "EditPropertiesDlg.h"
\r
48 #include "FileDiffDlg.h"
\r
49 #include "CommitDlg.h"
\r
50 #include "RebaseDlg.h"
\r
52 IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl)
\r
54 int CGitLogList::CherryPickFrom(CString from, CString to)
\r
56 CLogDataVector logs;
\r
57 if(logs.ParserFromLog(NULL,-1,0,&from,&to))
\r
60 if(logs.size() == 0)
\r
63 CSysProgressDlg progress;
\r
64 if (progress.IsValid())
\r
66 progress.SetTitle(_T("Cherry Pick"));
\r
67 progress.SetAnimation(IDR_MOVEANI);
\r
68 progress.SetTime(true);
\r
69 progress.ShowModeless(this);
\r
72 for(int i=logs.size()-1;i>=0;i--)
\r
74 if (progress.IsValid())
\r
76 progress.FormatPathLine(1, _T("Pick up %s"), logs[i].m_CommitHash);
\r
77 progress.FormatPathLine(2, _T("%s"), logs[i].m_Subject);
\r
78 progress.SetProgress(logs.size()-i, logs.size());
\r
80 if ((progress.IsValid())&&(progress.HasUserCancelled()))
\r
82 //CMessageBox::Show(hwndExplorer, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_ICONINFORMATION);
\r
83 throw std::exception(CUnicodeUtils::GetUTF8(_T("User canceled\r\n\r\n")));
\r
87 cmd.Format(_T("git.exe cherry-pick %s"),logs[i].m_CommitHash);
\r
89 if(g_Git.Run(cmd,&out,CP_UTF8))
\r
91 throw std::exception(CUnicodeUtils::GetUTF8(CString(_T("Cherry Pick Failure\r\n\r\n"))+out));
\r
99 void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect)
\r
101 POSITION pos = GetFirstSelectedItemPosition();
\r
102 int indexNext = GetNextSelectedItem(pos);
\r
106 GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));
\r
108 theApp.DoWaitCursor(1);
\r
109 bool bOpenWith = false;
\r
110 switch (cmd&0xFFFF)
\r
114 CTGitPathList pathlist;
\r
115 bool bSelectFilesForCommit = !!DWORD(CRegStdWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE));
\r
116 CAppUtils::Commit(CString(),true,CString(),
\r
117 pathlist,pathlist,bSelectFilesForCommit);
\r
119 this->GetParent()->PostMessage(WM_COMMAND,ID_LOGDLG_REFRESH,0);
\r
124 CString tempfile=GetTempFile();
\r
126 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
\r
127 if(r1->m_CommitHash != GIT_REV_ZERO)
\r
129 cmd.Format(_T("git.exe diff-tree -r -p --stat %s"),r1->m_CommitHash);
\r
131 cmd.Format(_T("git.exe diff -r -p --stat"));
\r
133 g_Git.RunLogFile(cmd,tempfile);
\r
134 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r1->m_Subject);
\r
140 CString tempfile=GetTempFile();
\r
142 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
\r
143 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
\r
145 if( r1->m_CommitHash == GIT_REV_ZERO)
\r
147 cmd.Format(_T("git.exe diff -r -p --stat %s"),r2->m_CommitHash);
\r
148 }else if( r2->m_CommitHash == GIT_REV_ZERO)
\r
150 cmd.Format(_T("git.exe diff -r -p --stat %s"),r1->m_CommitHash);
\r
152 cmd.Format(_T("git.exe diff-tree -r -p --stat %s %s"),r1->m_CommitHash,r2->m_CommitHash);
\r
154 g_Git.RunLogFile(cmd,tempfile);
\r
155 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r2->m_CommitHash.Left(6));
\r
160 case ID_COMPARETWO:
\r
162 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
\r
163 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
\r
165 dlg.SetDiff(NULL,*r1,*r2);
\r
174 GitRev * r1 = &m_wcRev;
\r
175 GitRev * r2 = pSelLogEntry;
\r
177 dlg.SetDiff(NULL,*r1,*r2);
\r
180 //user clicked on the menu item "compare with working copy"
\r
181 //if (PromptShown())
\r
183 // GitDiff diff(this, m_hWnd, true);
\r
184 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
\r
185 // diff.SetHEADPeg(m_LogRevision);
\r
186 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
\r
189 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
\r
193 case ID_COMPAREWITHPREVIOUS:
\r
198 if(pSelLogEntry->m_ParentHash.size()>0)
\r
199 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
\r
201 dlg.SetDiff(NULL,pSelLogEntry->m_CommitHash,pSelLogEntry->m_ParentHash[0]);
\r
205 CMessageBox::Show(NULL,_T("No previous version"),_T("TortoiseGit"),MB_OK);
\r
207 //if (PromptShown())
\r
209 // GitDiff diff(this, m_hWnd, true);
\r
210 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
\r
211 // diff.SetHEADPeg(m_LogRevision);
\r
212 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
\r
215 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
\r
218 case ID_COPYCLIPBOARD:
\r
220 CopySelectionToClipBoard();
\r
225 CopySelectionToClipBoard(TRUE);
\r
229 CAppUtils::Export(&pSelLogEntry->m_CommitHash);
\r
231 case ID_CREATE_BRANCH:
\r
232 CAppUtils::CreateBranchTag(FALSE,&pSelLogEntry->m_CommitHash);
\r
236 case ID_CREATE_TAG:
\r
237 CAppUtils::CreateBranchTag(TRUE,&pSelLogEntry->m_CommitHash);
\r
241 case ID_SWITCHTOREV:
\r
242 CAppUtils::Switch(&pSelLogEntry->m_CommitHash);
\r
247 CAppUtils::GitReset(&pSelLogEntry->m_CommitHash);
\r
251 case ID_REBASE_PICK:
\r
252 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_PICK);
\r
254 case ID_REBASE_EDIT:
\r
255 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_EDIT);
\r
257 case ID_REBASE_SQUASH:
\r
258 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SQUASH);
\r
260 case ID_REBASE_SKIP:
\r
261 SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SKIP);
\r
263 case ID_COMBINE_COMMIT:
\r
267 CString hashFirst,hashLast;
\r
269 int headindex=GetHeadIndex();
\r
270 if(headindex>=0) //incase show all branch, head is not the first commits.
\r
272 head.Format(_T("HEAD~%d"),FirstSelect-headindex);
\r
273 hashFirst=g_Git.GetHash(head);
\r
275 head.Format(_T("HEAD~%d"),LastSelect-headindex);
\r
276 hashLast=g_Git.GetHash(head);
\r
279 GitRev* pFirstEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
\r
280 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
\r
281 if(pFirstEntry->m_CommitHash != hashFirst || pLastEntry->m_CommitHash != hashLast)
\r
283 CMessageBox::Show(NULL,_T(
\r
284 "Cannot combine commits now.\r\n\
\r
285 Make sure you are viewing the log of your current branch and \
\r
286 no filters are applied."),_T("TortoiseGit"),MB_OK);
\r
290 headhash=g_Git.GetHash(CString(_T("HEAD")));
\r
292 if(!g_Git.CheckCleanWorkTree())
\r
294 CMessageBox::Show(NULL,_T("Combine needs a clean work tree"),_T("TortoiseGit"),MB_OK);
\r
299 //Use throw to abort this process (reset back to original HEAD)
\r
302 cmd.Format(_T("git.exe reset --hard %s"),pFirstEntry->m_CommitHash);
\r
303 if(g_Git.Run(cmd,&out,CP_UTF8))
\r
305 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
\r
306 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to first commit (first step) aborting...\r\n\r\n")+out));
\r
308 cmd.Format(_T("git.exe reset --mixed %s"),hashLast);
\r
309 if(g_Git.Run(cmd,&out,CP_UTF8))
\r
311 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
\r
312 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to last commit (second step) aborting...\r\n\r\n")+out));
\r
315 for(int i=FirstSelect;i<=LastSelect;i++)
\r
317 GitRev* pRev = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
\r
318 dlg.m_sLogMessage+=pRev->m_Subject+_T("\n")+pRev->m_Body;
\r
319 dlg.m_sLogMessage+=_T("\n");
\r
321 dlg.m_bWholeProject=true;
\r
322 dlg.m_bSelectFilesForCommit = true;
\r
323 dlg.m_bCommitAmend=true;
\r
324 dlg.m_AmendStr=dlg.m_sLogMessage;
\r
327 if (dlg.DoModal() == IDOK)
\r
329 if(pFirstEntry->m_CommitHash!=headhash)
\r
331 //Commitrange firstEntry..headhash (from top of combine to original head) needs to be 'cherry-picked'
\r
332 //on top of new commit.
\r
333 //Use the rebase --onto command for it.
\r
335 //All this can be done in one step using the following command:
\r
336 //cmd.Format(_T("git.exe format-patch --stdout --binary --full-index -k %s..%s | git am -k -3"),
\r
337 // pFirstEntry->m_CommitHash,
\r
339 //But I am not sure if a '|' is going to work in a CreateProcess() call.
\r
341 //Later the progress dialog could be used to execute these steps.
\r
343 if(CherryPickFrom(pFirstEntry->m_CommitHash,headhash))
\r
346 msg.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));
\r
347 throw std::exception(CUnicodeUtils::GetUTF8(msg));
\r
350 CString currentBranch=g_Git.GetCurrentBranch();
\r
351 cmd.Format(_T("git.exe rebase --onto \"%s\" %s %s"),
\r
353 pFirstEntry->m_CommitHash,
\r
355 if(g_Git.Run(cmd,&out,CP_UTF8)!=0)
\r
358 msg.Format(_T("Error while rebasing commits on top of combined commits. Aborting.\r\n\r\n%s"),out);
\r
359 // CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);
\r
360 g_Git.Run(_T("git.exe rebase --abort"),&out,CP_UTF8);
\r
361 throw std::exception(CUnicodeUtils::GetUTF8(msg));
\r
364 //HEAD is now on <no branch>.
\r
365 //The following steps are to get HEAD back on the original branch and reset the branch to the new HEAD
\r
366 //To avoid 2 working copy changes, we could use git branch -f <original branch> <hash new head>
\r
367 //And then git checkout <original branch>
\r
368 //But I don't know if 'git branch -f' removes tracking options. So for now, do a checkout and a reset.
\r
371 CString newHead=g_Git.GetHash(CString(_T("HEAD")));
\r
373 //Checkout working branch
\r
374 cmd.Format(_T("git.exe checkout -f \"%s\""),currentBranch);
\r
375 if(g_Git.Run(cmd,&out,CP_UTF8))
\r
376 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not checkout original branch. Aborting...\r\n\r\n")+out));
\r
378 //Reset to new HEAD
\r
379 cmd.Format(_T("git.exe reset --hard %s"),newHead);
\r
380 if(g_Git.Run(cmd,&out,CP_UTF8))
\r
381 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to new head. Aborting...\r\n\r\n")+out));
\r
386 throw std::exception("User aborted the combine process");
\r
388 catch(std::exception& e)
\r
390 CMessageBox::Show(NULL,CUnicodeUtils::GetUnicode(CStringA(e.what())),_T("TortoiseGit: Combine error"),MB_OK|MB_ICONERROR);
\r
391 cmd.Format(_T("git.exe reset --hard %s"),headhash);
\r
393 if(g_Git.Run(cmd,&out,CP_UTF8))
\r
395 CMessageBox::Show(NULL,_T("Could not reset to original HEAD\r\n\r\n")+out,_T("TortoiseGit"),MB_OK);
\r
402 case ID_CHERRY_PICK:
\r
403 if(!g_Git.CheckCleanWorkTree())
\r
405 CMessageBox::Show(NULL,_T("Cherry Pick requires a clean working tree"),_T("TortoiseGit"),MB_OK);
\r
410 dlg.m_IsCherryPick = TRUE;
\r
411 dlg.m_Upstream = this->m_CurrentBranch;
\r
412 POSITION pos = GetFirstSelectedItemPosition();
\r
415 int indexNext = GetNextSelectedItem(pos);
\r
416 dlg.m_CommitList.m_logEntries.push_back(*(GitRev*)m_arShownList[indexNext]);
\r
417 dlg.m_CommitList.m_logEntries.at(dlg.m_CommitList.m_logEntries.size()-1).m_Action |= CTGitPath::LOGACTIONS_REBASE_PICK;
\r
420 if(dlg.DoModal() == IDOK)
\r
426 case ID_REBASE_TO_VERSION:
\r
427 if(!g_Git.CheckCleanWorkTree())
\r
429 CMessageBox::Show(NULL,_T("Rebase requires a clean working tree"),_T("TortoiseGit"),MB_OK);
\r
434 dlg.m_Upstream = pSelLogEntry->m_CommitHash;
\r
436 if(dlg.DoModal() == IDOK)
\r
444 case ID_STASH_APPLY:
\r
445 CAppUtils::StashApply(pSelLogEntry->m_Ref);
\r
448 case ID_REFLOG_DEL:
\r
451 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);
\r
452 if(CMessageBox::Show(NULL,str,_T("TortoiseGit"),MB_YESNO|MB_ICONWARNING) == IDYES)
\r
455 cmd.Format(_T("git.exe reflog delete %s"),pSelLogEntry->m_Ref);
\r
456 if(g_Git.Run(cmd,&out,CP_ACP))
\r
458 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
\r
460 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
\r
464 case ID_CREATE_PATCH:
\r
466 int select=this->GetSelectedCount();
\r
468 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
\r
469 cmd += _T(" /command:formatpatch");
\r
471 cmd += _T(" /path:")+g_Git.m_CurrentDir+_T(" ");
\r
473 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
\r
474 GitRev * r2 = NULL;
\r
477 cmd += _T(" /startrev:")+r1->m_CommitHash;
\r
481 r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
\r
482 if( this->m_IsOldFirst )
\r
484 cmd += _T(" /startrev:")+r1->m_CommitHash+_T("~1");
\r
485 cmd += _T(" /endrev:")+r2->m_CommitHash;
\r
489 cmd += _T(" /startrev:")+r2->m_CommitHash+_T("~1");
\r
490 cmd += _T(" /endrev:")+r1->m_CommitHash;
\r
495 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
\r
500 int index = cmd>>16;
\r
501 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) == m_HashMap.end() )
\r
503 CMessageBox::Show(NULL,IDS_ERROR_NOREF,IDS_APPNAME,MB_OK|MB_ICONERROR);
\r
506 if( index >= m_HashMap[pSelLogEntry->m_CommitHash].size())
\r
508 CMessageBox::Show(NULL,IDS_ERROR_INDEX,IDS_APPNAME,MB_OK|MB_ICONERROR);
\r
512 ref=m_HashMap[pSelLogEntry->m_CommitHash][index];
\r
514 msg=CString(_T("<ct=0x0000FF>Delete</ct> <b>"))+ref;
\r
515 msg+=_T("</b>\n\n Are you sure?");
\r
516 if( CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_YESNO) == IDYES )
\r
520 if(this->GetShortName(ref,shortname,_T("refs/heads/")))
\r
522 cmd.Format(_T("git.exe branch -D %s"),shortname);
\r
525 if(this->GetShortName(ref,shortname,_T("refs/remotes/")))
\r
527 cmd.Format(_T("git.exe branch -r -D %s"),shortname);
\r
530 if(this->GetShortName(ref,shortname,_T("refs/tags/")))
\r
532 cmd.Format(_T("git.exe tag -d %s"),shortname);
\r
535 if(this->GetShortName(ref,shortname,_T("refs/stash")))
\r
537 if(CMessageBox::Show(NULL,_T("<ct=0x0000FF>Are you sure remove <b>ALL</b> stash?</ct>"),
\r
538 _T("TortoiseGit"),MB_YESNO)==IDYES)
\r
539 cmd.Format(_T("git.exe stash clear"));
\r
545 if(g_Git.Run(cmd,&out,CP_UTF8))
\r
547 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
\r
549 this->ReloadHashMap();
\r
551 this->GetItemRect(FirstSelect,&rect,LVIR_BOUNDS);
\r
552 this->InvalidateRect(rect);
\r
557 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
\r
563 // we need an URL to complete this command, so error out if we can't get an URL
\r
564 if (pathURL.IsEmpty())
\r
566 CString strMessage;
\r
567 strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
\r
568 CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
\r
569 TRACE(_T("could not retrieve the URL of the folder!\n"));
\r
573 msg.Format(IDS_LOG_REVERT_CONFIRM, m_path.GetWinPath());
\r
574 if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
\r
576 CGitProgressDlg dlg;
\r
577 dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
\r
578 dlg.SetPathList(CTGitPathList(m_path));
\r
579 dlg.SetUrl(pathURL);
\r
580 dlg.SetSecondUrl(pathURL);
\r
581 revisionRanges.AdjustForMerge(true);
\r
582 dlg.SetRevisionRanges(revisionRanges);
\r
583 dlg.SetPegRevision(m_LogRevision);
\r
590 // we need an URL to complete this command, so error out if we can't get an URL
\r
591 if (pathURL.IsEmpty())
\r
593 CString strMessage;
\r
594 strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
\r
595 CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
\r
596 TRACE(_T("could not retrieve the URL of the folder!\n"));
\r
600 CString path = m_path.GetWinPathString();
\r
601 bool bGotSavePath = false;
\r
602 if ((GetSelectedCount() == 1)&&(!m_path.IsDirectory()))
\r
604 bGotSavePath = CAppUtils::FileOpenSave(path, NULL, IDS_LOG_MERGETO, IDS_COMMONFILEFILTER, true, GetSafeHwnd());
\r
608 CBrowseFolder folderBrowser;
\r
609 folderBrowser.SetInfo(CString(MAKEINTRESOURCE(IDS_LOG_MERGETO)));
\r
610 bGotSavePath = (folderBrowser.Show(GetSafeHwnd(), path, path) == CBrowseFolder::OK);
\r
614 CGitProgressDlg dlg;
\r
615 dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
\r
616 dlg.SetPathList(CTGitPathList(CTGitPath(path)));
\r
617 dlg.SetUrl(pathURL);
\r
618 dlg.SetSecondUrl(pathURL);
\r
619 revisionRanges.AdjustForMerge(false);
\r
620 dlg.SetRevisionRanges(revisionRanges);
\r
621 dlg.SetPegRevision(m_LogRevision);
\r
626 case ID_REVERTTOREV:
\r
628 // we need an URL to complete this command, so error out if we can't get an URL
\r
629 if (pathURL.IsEmpty())
\r
631 CString strMessage;
\r
632 strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
\r
633 CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
\r
634 TRACE(_T("could not retrieve the URL of the folder!\n"));
\r
639 msg.Format(IDS_LOG_REVERTTOREV_CONFIRM, m_path.GetWinPath());
\r
640 if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
\r
642 CGitProgressDlg dlg;
\r
643 dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
\r
644 dlg.SetPathList(CTGitPathList(m_path));
\r
645 dlg.SetUrl(pathURL);
\r
646 dlg.SetSecondUrl(pathURL);
\r
647 GitRevRangeArray revarray;
\r
648 revarray.AddRevRange(GitRev::REV_HEAD, revSelected);
\r
649 dlg.SetRevisionRanges(revarray);
\r
650 dlg.SetPegRevision(m_LogRevision);
\r
658 case ID_BLAMECOMPARE:
\r
660 //user clicked on the menu item "compare with working copy"
\r
661 //now first get the revision which is selected
\r
664 GitDiff diff(this, this->m_hWnd, true);
\r
665 diff.SetHEADPeg(m_LogRevision);
\r
666 diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
\r
669 CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
\r
674 //user clicked on the menu item "compare and blame revisions"
\r
677 GitDiff diff(this, this->m_hWnd, true);
\r
678 diff.SetHEADPeg(m_LogRevision);
\r
679 diff.ShowCompare(CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), false, true);
\r
682 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
\r
685 case ID_BLAMEWITHPREVIOUS:
\r
687 //user clicked on the menu item "Compare and Blame with previous revision"
\r
690 GitDiff diff(this, this->m_hWnd, true);
\r
691 diff.SetHEADPeg(m_LogRevision);
\r
692 diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
\r
695 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
\r
703 CProgressDlg progDlg;
\r
704 progDlg.SetTitle(IDS_APPNAME);
\r
705 progDlg.SetAnimation(IDR_DOWNLOAD);
\r
707 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
\r
708 progDlg.SetLine(1, sInfoLine, true);
\r
709 SetAndClearProgressInfo(&progDlg);
\r
710 progDlg.ShowModeless(m_hWnd);
\r
711 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
\r
712 bool bSuccess = true;
\r
713 if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
\r
716 // try again, but with the selected revision as the peg revision
\r
717 if (!Cat(m_path, revSelected, revSelected, tempfile))
\r
720 SetAndClearProgressInfo((HWND)NULL);
\r
721 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
\r
730 SetAndClearProgressInfo((HWND)NULL);
\r
731 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
\r
734 ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
\r
735 if ((ret <= HINSTANCE_ERROR)||bOpenWith)
\r
737 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
\r
738 cmd += tempfile.GetWinPathString() + _T(" ");
\r
739 CAppUtils::LaunchApplication(cmd, NULL, false);
\r
747 dlg.EndRev = revSelected;
\r
748 if (dlg.DoModal() == IDOK)
\r
753 tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
\r
754 if (!tempfile.IsEmpty())
\r
756 if (dlg.m_bTextView)
\r
758 //open the default text editor for the result file
\r
759 CAppUtils::StartTextViewer(tempfile);
\r
763 CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
\r
764 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
\r
772 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
\r
780 CString url = _T("tgit:")+pathURL;
\r
781 sCmd.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
\r
782 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
\r
783 (LPCTSTR)m_path.GetWinPath(), (LONG)revSelected);
\r
784 CAppUtils::LaunchApplication(sCmd, NULL, false);
\r
789 m_nSearchIndex = GetSelectionMark();
\r
790 if (m_nSearchIndex < 0)
\r
791 m_nSearchIndex = 0;
\r
798 m_pFindDialog = new CFindReplaceDialog();
\r
799 m_pFindDialog->Create(TRUE, NULL, NULL, FR_HIDEUPDOWN | FR_HIDEWHOLEWORD, this);
\r
803 case ID_REPOBROWSE:
\r
806 sCmd.Format(_T("%s /command:repobrowser /path:\"%s\" /rev:%s"),
\r
807 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
\r
808 (LPCTSTR)pathURL, (LPCTSTR)revSelected.ToString());
\r
810 CAppUtils::LaunchApplication(sCmd, NULL, false);
\r
815 EditLogMessage(selIndex);
\r
818 case ID_EDITAUTHOR:
\r
820 EditAuthor(selEntries);
\r
825 CEditPropertiesDlg dlg;
\r
826 dlg.SetProjectProperties(&m_ProjectProperties);
\r
827 CTGitPathList escapedlist;
\r
828 dlg.SetPathList(CTGitPathList(CTGitPath(pathURL)));
\r
829 dlg.SetRevision(revSelected);
\r
830 dlg.RevProps(true);
\r
838 sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
\r
839 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
\r
840 (LPCTSTR)pathURL, (LONG)revSelected);
\r
841 CAppUtils::LaunchApplication(sCmd, NULL, false);
\r
847 CString url = _T("tgit:")+pathURL;
\r
848 sCmd.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),
\r
849 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
\r
850 (LPCTSTR)url, (LONG)revSelected);
\r
851 CAppUtils::LaunchApplication(sCmd, NULL, false);
\r
856 CString url = m_ProjectProperties.sWebViewerRev;
\r
857 url = GetAbsoluteUrlFromRelativeUrl(url);
\r
858 url.Replace(_T("%REVISION%"), revSelected.ToString());
\r
859 if (!url.IsEmpty())
\r
860 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
\r
863 case ID_VIEWPATHREV:
\r
865 CString relurl = pathURL;
\r
866 CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
\r
867 relurl = relurl.Mid(sRoot.GetLength());
\r
868 CString url = m_ProjectProperties.sWebViewerPathRev;
\r
869 url = GetAbsoluteUrlFromRelativeUrl(url);
\r
870 url.Replace(_T("%REVISION%"), revSelected.ToString());
\r
871 url.Replace(_T("%PATH%"), relurl);
\r
872 if (!url.IsEmpty())
\r
873 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
\r
880 theApp.DoWaitCursor(-1);
\r
883 void CGitLogList::SetSelectedAction(int action)
\r
885 POSITION pos = GetFirstSelectedItemPosition();
\r
889 index = GetNextSelectedItem(pos);
\r
890 ((GitRev*)m_arShownList[index])->m_Action = action;
\r
892 this->GetItemRect(index,&rect,LVIR_BOUNDS);
\r
893 this->InvalidateRect(rect);
\r