#include "PropDlg.h"\r
#include "SVNProgressDlg.h"\r
#include "ProgressDlg.h"\r
+#include "SysProgressDlg.h"\r
//#include "RepositoryBrowser.h"\r
//#include "CopyDlg.h"\r
//#include "StatGraphDlg.h"\r
//#include "RepositoryInfo.h"\r
//#include "EditPropertiesDlg.h"\r
#include "FileDiffDlg.h"\r
+#include "CommitDlg.h"\r
+#include "RebaseDlg.h"\r
\r
IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl)\r
\r
+int CGitLogList::CherryPickFrom(CString from, CString to)\r
+{\r
+ CLogDataVector logs;\r
+ if(logs.ParserFromLog(NULL,-1,0,&from,&to))\r
+ return -1;\r
+\r
+ if(logs.size() == 0)\r
+ return 0;\r
+\r
+ CSysProgressDlg progress;\r
+ if (progress.IsValid())\r
+ {\r
+ progress.SetTitle(_T("Cherry Pick"));\r
+ progress.SetAnimation(IDR_MOVEANI);\r
+ progress.SetTime(true);\r
+ progress.ShowModeless(this);\r
+ }\r
+\r
+ for(int i=logs.size()-1;i>=0;i--)\r
+ {\r
+ if (progress.IsValid())\r
+ {\r
+ progress.FormatPathLine(1, _T("Pick up %s"), logs[i].m_CommitHash);\r
+ progress.FormatPathLine(2, _T("%s"), logs[i].m_Subject);\r
+ progress.SetProgress(logs.size()-i, logs.size());\r
+ }\r
+ if ((progress.IsValid())&&(progress.HasUserCancelled()))\r
+ {\r
+ //CMessageBox::Show(hwndExplorer, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_ICONINFORMATION);\r
+ throw std::exception(CUnicodeUtils::GetUTF8(_T("User canceled\r\n\r\n")));\r
+ return -1;\r
+ }\r
+ CString cmd,out;\r
+ cmd.Format(_T("git.exe cherry-pick %s"),logs[i].m_CommitHash);\r
+ out.Empty();\r
+ if(g_Git.Run(cmd,&out,CP_UTF8))\r
+ {\r
+ throw std::exception(CUnicodeUtils::GetUTF8(CString(_T("Cherry Pick Failure\r\n\r\n"))+out));\r
+ return -1;\r
+ }\r
+ }\r
+ \r
+ return 0;\r
+}\r
+\r
void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect)\r
{ \r
POSITION pos = GetFirstSelectedItemPosition();\r
\r
theApp.DoWaitCursor(1);\r
bool bOpenWith = false;\r
-\r
- switch (cmd)\r
+ switch (cmd&0xFFFF)\r
{\r
+ case ID_COMMIT:\r
+ {\r
+ CTGitPathList pathlist;\r
+ bool bSelectFilesForCommit = !!DWORD(CRegStdWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE));\r
+ CAppUtils::Commit(CString(),true,CString(),\r
+ pathlist,pathlist,bSelectFilesForCommit);\r
+ this->Refresh();\r
+ \r
+ }\r
+ break;\r
case ID_GNUDIFF1:\r
{\r
CString tempfile=GetTempFile();\r
\r
}\r
break;\r
-\r
+ \r
case ID_COMPARETWO:\r
{\r
GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));\r
break;\r
case ID_CREATE_BRANCH:\r
CAppUtils::CreateBranchTag(FALSE,&pSelLogEntry->m_CommitHash);\r
- m_HashMap.clear();\r
- g_Git.GetMapHashToFriendName(m_HashMap);\r
+ ReloadHashMap();\r
Invalidate(); \r
break;\r
case ID_CREATE_TAG:\r
CAppUtils::CreateBranchTag(TRUE,&pSelLogEntry->m_CommitHash);\r
- m_HashMap.clear();\r
- g_Git.GetMapHashToFriendName(m_HashMap);\r
+ ReloadHashMap();\r
Invalidate();\r
break;\r
case ID_SWITCHTOREV:\r
CAppUtils::Switch(&pSelLogEntry->m_CommitHash);\r
- m_HashMap.clear();\r
- g_Git.GetMapHashToFriendName(m_HashMap);\r
+ ReloadHashMap();\r
+ Invalidate();\r
+ break;\r
+ case ID_RESET:\r
+ CAppUtils::GitReset(&pSelLogEntry->m_CommitHash);\r
+ ReloadHashMap();\r
Invalidate();\r
break;\r
+ case ID_REBASE_PICK:\r
+ SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_PICK);\r
+ break;\r
+ case ID_REBASE_EDIT:\r
+ SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_EDIT);\r
+ break;\r
+ case ID_REBASE_SQUASH:\r
+ SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SQUASH);\r
+ break;\r
+ case ID_REBASE_SKIP:\r
+ SetSelectedAction(CTGitPath::LOGACTIONS_REBASE_SKIP);\r
+ break;\r
+ case ID_COMBINE_COMMIT:\r
+ {\r
+ CString head;\r
+ CString headhash;\r
+ CString hashFirst,hashLast;\r
+\r
+ int headindex=GetHeadIndex();\r
+ if(headindex>=0) //incase show all branch, head is not the first commits. \r
+ {\r
+ head.Format(_T("HEAD~%d"),FirstSelect-headindex);\r
+ hashFirst=g_Git.GetHash(head);\r
+\r
+ head.Format(_T("HEAD~%d"),LastSelect-headindex);\r
+ hashLast=g_Git.GetHash(head);\r
+ }\r
+ \r
+ GitRev* pFirstEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));\r
+ GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));\r
+ if(pFirstEntry->m_CommitHash != hashFirst || pLastEntry->m_CommitHash != hashLast)\r
+ {\r
+ CMessageBox::Show(NULL,_T(\r
+ "Cannot combine commits now.\r\n\\r
+ Make sure you are viewing the log of your current branch and \\r
+ no filters are applied."),_T("TortoiseGit"),MB_OK);\r
+ break;\r
+ }\r
+ \r
+ headhash=g_Git.GetHash(CString(_T("HEAD")));\r
+ \r
+ if(!g_Git.CheckCleanWorkTree())\r
+ {\r
+ CMessageBox::Show(NULL,_T("Combine needs a clean work tree"),_T("TortoiseGit"),MB_OK);\r
+ break;\r
+ }\r
+ CString cmd,out;\r
+\r
+ //Use throw to abort this process (reset back to original HEAD)\r
+ try\r
+ {\r
+ cmd.Format(_T("git.exe reset --hard %s"),pFirstEntry->m_CommitHash);\r
+ if(g_Git.Run(cmd,&out,CP_UTF8))\r
+ {\r
+ CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);\r
+ throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to first commit (first step) aborting...\r\n\r\n")+out));\r
+ }\r
+ cmd.Format(_T("git.exe reset --mixed %s"),hashLast);\r
+ if(g_Git.Run(cmd,&out,CP_UTF8))\r
+ {\r
+ CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);\r
+ throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to last commit (second step) aborting...\r\n\r\n")+out));\r
+ }\r
+ CCommitDlg dlg;\r
+ for(int i=FirstSelect;i<=LastSelect;i++)\r
+ {\r
+ GitRev* pRev = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));\r
+ dlg.m_sLogMessage+=pRev->m_Subject+_T("\n")+pRev->m_Body;\r
+ dlg.m_sLogMessage+=_T("\n");\r
+ }\r
+ dlg.m_bWholeProject=true;\r
+ dlg.m_bSelectFilesForCommit = true;\r
+ dlg.m_bCommitAmend=true;\r
+ dlg.m_AmendStr=dlg.m_sLogMessage;\r
+\r
+ bool abort=false;\r
+ if (dlg.DoModal() == IDOK)\r
+ {\r
+ if(pFirstEntry->m_CommitHash!=headhash)\r
+ {\r
+ //Commitrange firstEntry..headhash (from top of combine to original head) needs to be 'cherry-picked'\r
+ //on top of new commit.\r
+ //Use the rebase --onto command for it.\r
+ //\r
+ //All this can be done in one step using the following command:\r
+ //cmd.Format(_T("git.exe format-patch --stdout --binary --full-index -k %s..%s | git am -k -3"),\r
+ // pFirstEntry->m_CommitHash,\r
+ // headhash);\r
+ //But I am not sure if a '|' is going to work in a CreateProcess() call.\r
+ //\r
+ //Later the progress dialog could be used to execute these steps.\r
+\r
+ if(CherryPickFrom(pFirstEntry->m_CommitHash,headhash))\r
+ {\r
+ CString msg;\r
+ msg.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));\r
+ throw std::exception(CUnicodeUtils::GetUTF8(msg));\r
+ }\r
+#if 0\r
+ CString currentBranch=g_Git.GetCurrentBranch();\r
+ cmd.Format(_T("git.exe rebase --onto \"%s\" %s %s"),\r
+ currentBranch,\r
+ pFirstEntry->m_CommitHash,\r
+ headhash);\r
+ if(g_Git.Run(cmd,&out,CP_UTF8)!=0)\r
+ {\r
+ CString msg;\r
+ msg.Format(_T("Error while rebasing commits on top of combined commits. Aborting.\r\n\r\n%s"),out);\r
+// CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_OK);\r
+ g_Git.Run(_T("git.exe rebase --abort"),&out,CP_UTF8);\r
+ throw std::exception(CUnicodeUtils::GetUTF8(msg));\r
+ }\r
+\r
+ //HEAD is now on <no branch>. \r
+ //The following steps are to get HEAD back on the original branch and reset the branch to the new HEAD\r
+ //To avoid 2 working copy changes, we could use git branch -f <original branch> <hash new head> \r
+ //And then git checkout <original branch>\r
+ //But I don't know if 'git branch -f' removes tracking options. So for now, do a checkout and a reset.\r
+ \r
+ //Store new HEAD\r
+ CString newHead=g_Git.GetHash(CString(_T("HEAD")));\r
+\r
+ //Checkout working branch\r
+ cmd.Format(_T("git.exe checkout -f \"%s\""),currentBranch);\r
+ if(g_Git.Run(cmd,&out,CP_UTF8))\r
+ throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not checkout original branch. Aborting...\r\n\r\n")+out));\r
+\r
+ //Reset to new HEAD\r
+ cmd.Format(_T("git.exe reset --hard %s"),newHead);\r
+ if(g_Git.Run(cmd,&out,CP_UTF8))\r
+ throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not reset to new head. Aborting...\r\n\r\n")+out));\r
+#endif\r
+ }\r
+ }\r
+ else\r
+ throw std::exception("User aborted the combine process");\r
+ }\r
+ catch(std::exception& e)\r
+ {\r
+ CMessageBox::Show(NULL,CUnicodeUtils::GetUnicode(CStringA(e.what())),_T("TortoiseGit: Combine error"),MB_OK|MB_ICONERROR);\r
+ cmd.Format(_T("git.exe reset --hard %s"),headhash);\r
+ out.Empty();\r
+ if(g_Git.Run(cmd,&out,CP_UTF8))\r
+ {\r
+ CMessageBox::Show(NULL,_T("Could not reset to original HEAD\r\n\r\n")+out,_T("TortoiseGit"),MB_OK);\r
+ }\r
+ }\r
+ Refresh();\r
+ }\r
+ break;\r
+\r
+ case ID_CHERRY_PICK:\r
+ if(!g_Git.CheckCleanWorkTree())\r
+ {\r
+ CMessageBox::Show(NULL,_T("Cherry Pick requires a clean working tree"),_T("TortoiseGit"),MB_OK);\r
+ \r
+ }else\r
+ {\r
+ CRebaseDlg dlg;\r
+ dlg.m_IsCherryPick = TRUE;\r
+ dlg.m_Upstream = this->m_CurrentBranch;\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ while(pos)\r
+ {\r
+ int indexNext = GetNextSelectedItem(pos);\r
+ dlg.m_CommitList.m_logEntries.push_back(*(GitRev*)m_arShownList[indexNext]);\r
+ dlg.m_CommitList.m_logEntries.at(dlg.m_CommitList.m_logEntries.size()-1).m_Action |= CTGitPath::LOGACTIONS_REBASE_PICK;\r
+ }\r
+ \r
+ if(dlg.DoModal() == IDOK)\r
+ {\r
+ Refresh();\r
+ }\r
+ }\r
+ break;\r
+ case ID_REBASE_TO_VERSION:\r
+ if(!g_Git.CheckCleanWorkTree())\r
+ {\r
+ CMessageBox::Show(NULL,_T("Rebase requires a clean working tree"),_T("TortoiseGit"),MB_OK);\r
+ \r
+ }else\r
+ {\r
+ CRebaseDlg dlg;\r
+ dlg.m_Upstream = pSelLogEntry->m_CommitHash;\r
+\r
+ if(dlg.DoModal() == IDOK)\r
+ {\r
+ Refresh();\r
+ }\r
+ }\r
+\r
+ break;\r
+\r
+ case ID_STASH_APPLY:\r
+ CAppUtils::StashApply(pSelLogEntry->m_Ref);\r
+ break;\r
+ \r
+ case ID_REFLOG_DEL:\r
+ { \r
+ CString str;\r
+ 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
+ if(CMessageBox::Show(NULL,str,_T("TortoiseGit"),MB_YESNO|MB_ICONWARNING) == IDYES)\r
+ {\r
+ CString cmd,out;\r
+ cmd.Format(_T("git.exe reflog delete %s"),pSelLogEntry->m_Ref);\r
+ if(g_Git.Run(cmd,&out,CP_ACP))\r
+ {\r
+ CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);\r
+ } \r
+ ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);\r
+ }\r
+ }\r
+ break;\r
+ case ID_CREATE_PATCH:\r
+ {\r
+ int select=this->GetSelectedCount();\r
+ CString cmd;\r
+ cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");\r
+ cmd += _T(" /command:formatpatch");\r
+\r
+ cmd += _T(" /path:")+g_Git.m_CurrentDir+_T(" ");\r
+\r
+ GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));\r
+ GitRev * r2 = NULL;\r
+ if(select == 1)\r
+ {\r
+ cmd += _T(" /startrev:")+r1->m_CommitHash;\r
+ }\r
+ else \r
+ {\r
+ r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));\r
+ if( this->m_IsOldFirst )\r
+ { \r
+ cmd += _T(" /startrev:")+r1->m_CommitHash+_T("~1");\r
+ cmd += _T(" /endrev:")+r2->m_CommitHash;\r
+ \r
+ }else\r
+ { \r
+ cmd += _T(" /startrev:")+r2->m_CommitHash+_T("~1");\r
+ cmd += _T(" /endrev:")+r1->m_CommitHash; \r
+ } \r
+ \r
+ }\r
\r
+ CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);\r
+ }\r
+ break;\r
+ case ID_DELETE:\r
+ {\r
+ int index = cmd>>16;\r
+ if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) == m_HashMap.end() )\r
+ {\r
+ CMessageBox::Show(NULL,IDS_ERROR_NOREF,IDS_APPNAME,MB_OK|MB_ICONERROR);\r
+ return;\r
+ }\r
+ if( index >= m_HashMap[pSelLogEntry->m_CommitHash].size())\r
+ {\r
+ CMessageBox::Show(NULL,IDS_ERROR_INDEX,IDS_APPNAME,MB_OK|MB_ICONERROR);\r
+ return; \r
+ }\r
+ CString ref,msg;\r
+ ref=m_HashMap[pSelLogEntry->m_CommitHash][index];\r
+ \r
+ msg=CString(_T("<ct=0x0000FF>Delete</ct> <b>"))+ref;\r
+ msg+=_T("</b>\n\n Are you sure?");\r
+ if( CMessageBox::Show(NULL,msg,_T("TortoiseGit"),MB_YESNO) == IDYES )\r
+ {\r
+ CString shortname;\r
+ CString cmd;\r
+ if(this->GetShortName(ref,shortname,_T("refs/heads/")))\r
+ {\r
+ cmd.Format(_T("git.exe branch -D %s"),shortname);\r
+ }\r
+\r
+ if(this->GetShortName(ref,shortname,_T("refs/remotes/")))\r
+ {\r
+ cmd.Format(_T("git.exe branch -r -D %s"),shortname);\r
+ }\r
+\r
+ if(this->GetShortName(ref,shortname,_T("refs/tags/")))\r
+ {\r
+ cmd.Format(_T("git.exe tag -d %s"),shortname);\r
+ }\r
+\r
+ if(this->GetShortName(ref,shortname,_T("refs/stash")))\r
+ {\r
+ if(CMessageBox::Show(NULL,_T("<ct=0x0000FF>Are you sure remove <b>ALL</b> stash?</ct>"),\r
+ _T("TortoiseGit"),MB_YESNO)==IDYES)\r
+ cmd.Format(_T("git.exe stash clear"));\r
+ else\r
+ return;\r
+ }\r
+\r
+ CString out;\r
+ if(g_Git.Run(cmd,&out,CP_UTF8))\r
+ {\r
+ CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);\r
+ }\r
+ this->ReloadHashMap();\r
+ CRect rect;\r
+ this->GetItemRect(FirstSelect,&rect,LVIR_BOUNDS);\r
+ this->InvalidateRect(rect);\r
+ }\r
+ }\r
+ break;\r
default:\r
//CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);\r
break;\r
} // switch (cmd)\r
\r
theApp.DoWaitCursor(-1);\r
+}\r
+\r
+void CGitLogList::SetSelectedAction(int action)\r
+{\r
+ POSITION pos = GetFirstSelectedItemPosition();\r
+ int index;\r
+ while(pos)\r
+ {\r
+ index = GetNextSelectedItem(pos);\r
+ ((GitRev*)m_arShownList[index])->m_Action = action;\r
+ CRect rect;\r
+ this->GetItemRect(index,&rect,LVIR_BOUNDS);\r
+ this->InvalidateRect(rect);\r
+\r
+ }\r
+\r
}
\ No newline at end of file