X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;ds=sidebyside;f=src%2FTortoiseProc%2FGitLogListAction.cpp;h=7bf709b1e4fad54108b8dd1ce1dc652be7e262e7;hb=c0d70c39ba6321000bf1803a4dab2baf72350ab0;hp=0b3d00375c74cd741599645bd1780f15fc0afe75;hpb=85cc56c0f7aa31bf8c0127a933e183e9f928f519;p=tortoisegit%2FTortoiseGitJp.git diff --git a/src/TortoiseProc/GitLogListAction.cpp b/src/TortoiseProc/GitLogListAction.cpp index 0b3d003..7bf709b 100644 --- a/src/TortoiseProc/GitLogListAction.cpp +++ b/src/TortoiseProc/GitLogListAction.cpp @@ -20,6 +20,7 @@ #include "PropDlg.h" #include "SVNProgressDlg.h" #include "ProgressDlg.h" +#include "SysProgressDlg.h" //#include "RepositoryBrowser.h" //#include "CopyDlg.h" //#include "StatGraphDlg.h" @@ -45,9 +46,56 @@ //#include "RepositoryInfo.h" //#include "EditPropertiesDlg.h" #include "FileDiffDlg.h" +#include "CommitDlg.h" +#include "RebaseDlg.h" IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl) +int CGitLogList::CherryPickFrom(CString from, CString to) +{ + CLogDataVector logs; + if(logs.ParserFromLog(NULL,-1,0,&from,&to)) + return -1; + + if(logs.size() == 0) + return 0; + + CSysProgressDlg progress; + if (progress.IsValid()) + { + progress.SetTitle(_T("Cherry Pick")); + progress.SetAnimation(IDR_MOVEANI); + progress.SetTime(true); + progress.ShowModeless(this); + } + + for(int i=logs.size()-1;i>=0;i--) + { + if (progress.IsValid()) + { + progress.FormatPathLine(1, _T("Pick up %s"), logs[i].m_CommitHash); + progress.FormatPathLine(2, _T("%s"), logs[i].m_Subject); + progress.SetProgress(logs.size()-i, logs.size()); + } + if ((progress.IsValid())&&(progress.HasUserCancelled())) + { + //CMessageBox::Show(hwndExplorer, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_ICONINFORMATION); + throw std::exception(CUnicodeUtils::GetUTF8(_T("User canceled\r\n\r\n"))); + return -1; + } + CString cmd,out; + cmd.Format(_T("git.exe cherry-pick %s"),logs[i].m_CommitHash); + out.Empty(); + if(g_Git.Run(cmd,&out,CP_UTF8)) + { + throw std::exception(CUnicodeUtils::GetUTF8(CString(_T("Cherry Pick Failure\r\n\r\n"))+out)); + return -1; + } + } + + return 0; +} + void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect) { POSITION pos = GetFirstSelectedItemPosition(); @@ -59,7 +107,7 @@ void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect) theApp.DoWaitCursor(1); bool bOpenWith = false; - switch (cmd) + switch (cmd&0xFFFF) { case ID_GNUDIFF1: { @@ -188,6 +236,290 @@ void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect) case ID_REBASE_SKIP: SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SKIP); break; + case ID_COMBINE_COMMIT: + { + CString head; + CString headhash; + CString hashFirst,hashLast; + + int headindex=GetHeadIndex(); + if(headindex>=0) //incase show all branch, head is not the first commits. + { + head.Format(_T("HEAD~%d"),FirstSelect-headindex); + hashFirst=g_Git.GetHash(head); + + head.Format(_T("HEAD~%d"),LastSelect-headindex); + hashLast=g_Git.GetHash(head); + } + + GitRev* pFirstEntry = reinterpret_cast(m_arShownList.GetAt(FirstSelect)); + GitRev* pLastEntry = reinterpret_cast(m_arShownList.GetAt(LastSelect)); + if(pFirstEntry->m_CommitHash != hashFirst || pLastEntry->m_CommitHash != hashLast) + { + CMessageBox::Show(NULL,_T( + "Cannot combine commits now.\r\n\ + Make sure you are viewing the log of your current branch and \ + no filters are applied."),_T("TortoiseGit"),MB_OK); + break; + } + + headhash=g_Git.GetHash(CString(_T("HEAD"))); + + if(!g_Git.CheckCleanWorkTree()) + { + CMessageBox::Show(NULL,_T("Combine needs a clean work tree"),_T("TortoiseGit"),MB_OK); + break; + } + CString cmd,out; + + //Use throw to abort this process (reset back to original HEAD) + try + { + cmd.Format(_T("git.exe reset --hard %s"),pFirstEntry->m_CommitHash); + if(g_Git.Run(cmd,&out,CP_UTF8)) + { + CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK); + throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to first commit (first step) aborting...\r\n\r\n")+out)); + } + cmd.Format(_T("git.exe reset --mixed %s"),hashLast); + if(g_Git.Run(cmd,&out,CP_UTF8)) + { + CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK); + throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to last commit (second step) aborting...\r\n\r\n")+out)); + } + CCommitDlg dlg; + for(int i=FirstSelect;i<=LastSelect;i++) + { + GitRev* pRev = reinterpret_cast(m_arShownList.GetAt(i)); + dlg.m_sLogMessage+=pRev->m_Subject+_T("\n")+pRev->m_Body; + dlg.m_sLogMessage+=_T("\n"); + } + dlg.m_bWholeProject=true; + dlg.m_bSelectFilesForCommit = true; + dlg.m_bCommitAmend=true; + dlg.m_AmendStr=dlg.m_sLogMessage; + + bool abort=false; + if (dlg.DoModal() == IDOK) + { + if(pFirstEntry->m_CommitHash!=headhash) + { + //Commitrange firstEntry..headhash (from top of combine to original head) needs to be 'cherry-picked' + //on top of new commit. + //Use the rebase --onto command for it. + // + //All this can be done in one step using the following command: + //cmd.Format(_T("git.exe format-patch --stdout --binary --full-index -k %s..%s | git am -k -3"), + // pFirstEntry->m_CommitHash, + // headhash); + //But I am not sure if a '|' is going to work in a CreateProcess() call. + // + //Later the progress dialog could be used to execute these steps. + + if(CherryPickFrom(pFirstEntry->m_CommitHash,headhash)) + { + CString msg; + msg.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n")); + throw std::exception(CUnicodeUtils::GetUTF8(msg)); + } +#if 0 + CString currentBranch=g_Git.GetCurrentBranch(); + cmd.Format(_T("git.exe rebase --onto \"%s\" %s %s"), + currentBranch, + pFirstEntry->m_CommitHash, + headhash); + if(g_Git.Run(cmd,&out,CP_UTF8)!=0) + { + CString msg; + msg.Format(_T("Error while rebasing commits on top of combined commits. Aborting.\r\n\r\n%s"),out); +// CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK); + g_Git.Run(_T("git.exe rebase --abort"),&out,CP_UTF8); + throw std::exception(CUnicodeUtils::GetUTF8(msg)); + } + + //HEAD is now on . + //The following steps are to get HEAD back on the original branch and reset the branch to the new HEAD + //To avoid 2 working copy changes, we could use git branch -f + //And then git checkout + //But I don't know if 'git branch -f' removes tracking options. So for now, do a checkout and a reset. + + //Store new HEAD + CString newHead=g_Git.GetHash(CString(_T("HEAD"))); + + //Checkout working branch + cmd.Format(_T("git.exe checkout -f \"%s\""),currentBranch); + if(g_Git.Run(cmd,&out,CP_UTF8)) + throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not checkout original branch. Aborting...\r\n\r\n")+out)); + + //Reset to new HEAD + cmd.Format(_T("git.exe reset --hard %s"),newHead); + if(g_Git.Run(cmd,&out,CP_UTF8)) + throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to new head. Aborting...\r\n\r\n")+out)); +#endif + } + } + else + throw std::exception("User aborted the combine process"); + } + catch(std::exception& e) + { + CMessageBox::Show(NULL,CUnicodeUtils::GetUnicode(CStringA(e.what())),_T("TortoiseGit: Combine error"),MB_OK|MB_ICONERROR); + cmd.Format(_T("git.exe reset --hard %s"),headhash); + out.Empty(); + if(g_Git.Run(cmd,&out,CP_UTF8)) + { + CMessageBox::Show(NULL,_T("Could not reset to original HEAD\r\n\r\n")+out,_T("TortoiseGit"),MB_OK); + } + } + Refresh(); + } + break; + + case ID_CHERRY_PICK: + if(!g_Git.CheckCleanWorkTree()) + { + CMessageBox::Show(NULL,_T("Cherry Pick requires a clean working tree"),_T("TortoiseGit"),MB_OK); + + }else + { + CRebaseDlg dlg; + dlg.m_IsCherryPick = TRUE; + dlg.m_Upstream = this->m_CurrentBranch; + POSITION pos = GetFirstSelectedItemPosition(); + while(pos) + { + int indexNext = GetNextSelectedItem(pos); + dlg.m_CommitList.m_logEntries.push_back(*(GitRev*)m_arShownList[indexNext]); + dlg.m_CommitList.m_logEntries.at(dlg.m_CommitList.m_logEntries.size()-1).m_Action |= CTGitPath::LOGACTIONS_REBASE_PICK; + } + + if(dlg.DoModal() == IDOK) + { + Refresh(); + } + } + break; + case ID_REBASE_TO_VERSION: + if(!g_Git.CheckCleanWorkTree()) + { + CMessageBox::Show(NULL,_T("Rebase requires a clean working tree"),_T("TortoiseGit"),MB_OK); + + }else + { + CRebaseDlg dlg; + dlg.m_Upstream = pSelLogEntry->m_CommitHash; + + if(dlg.DoModal() == IDOK) + { + Refresh(); + } + } + + break; + + case ID_STASH_APPLY: + CAppUtils::StashApply(pSelLogEntry->m_Ref); + break; + + case ID_REFLOG_DEL: + { + CString str; + str.Format(_T("Warning: %s will be permanently deleted. It can NOT be recovered!\r\n \r\n Are you sure you want to continue?"),pSelLogEntry->m_Ref); + if(CMessageBox::Show(NULL,str,_T("TortoiseGit"),MB_YESNO|MB_ICONWARNING) == IDYES) + { + CString cmd,out; + cmd.Format(_T("git.exe reflog delete %s"),pSelLogEntry->m_Ref); + if(g_Git.Run(cmd,&out,CP_ACP)) + { + CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK); + } + ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0); + } + } + break; + case ID_CREATE_PATCH: + { + int select=this->GetSelectedCount(); + CString cmd; + cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe"); + cmd += _T(" /command:formatpatch"); + + cmd += _T(" /path:")+g_Git.m_CurrentDir+_T(" "); + + GitRev * r1 = reinterpret_cast(m_arShownList.GetAt(FirstSelect)); + GitRev * r2 = NULL; + if(select == 1) + { + cmd += _T(" /startrev:")+r1->m_CommitHash; + } + else + { + r2 = reinterpret_cast(m_arShownList.GetAt(LastSelect)); + if( this->m_IsOldFirst ) + { + cmd += _T(" /startrev:")+r1->m_CommitHash+_T("~1"); + cmd += _T(" /endrev:")+r2->m_CommitHash; + + }else + { + cmd += _T(" /startrev:")+r2->m_CommitHash+_T("~1"); + cmd += _T(" /endrev:")+r1->m_CommitHash; + } + + } + + CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false); + } + break; + case ID_DELETE: + { + int index = cmd>>16; + if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) == m_HashMap.end() ) + { + CMessageBox::Show(NULL,IDS_ERROR_NOREF,IDS_APPNAME,MB_OK|MB_ICONERROR); + return; + } + if( index >= m_HashMap[pSelLogEntry->m_CommitHash].size()) + { + CMessageBox::Show(NULL,IDS_ERROR_INDEX,IDS_APPNAME,MB_OK|MB_ICONERROR); + return; + } + CString ref,msg; + ref=m_HashMap[pSelLogEntry->m_CommitHash][index]; + + msg=CString(_T("Delete "))+ref; + msg+=_T("\n\n Are you sure?"); + if( CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_YESNO) == IDYES ) + { + CString shortname; + CString cmd; + if(this->GetShortName(ref,shortname,_T("refs/heads/"))) + { + cmd.Format(_T("git.exe branch -D %s"),shortname); + } + + if(this->GetShortName(ref,shortname,_T("refs/remotes/"))) + { + cmd.Format(_T("git.exe branch -r -D %s"),shortname); + } + + if(this->GetShortName(ref,shortname,_T("refs/tags/"))) + { + cmd.Format(_T("git.exe tag -d %s"),shortname); + } + + CString out; + if(g_Git.Run(cmd,&out,CP_UTF8)) + { + CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK); + } + this->ReloadHashMap(); + CRect rect; + this->GetItemRect(FirstSelect,&rect,LVIR_BOUNDS); + this->InvalidateRect(rect); + } + } + break; default: //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK); break;