OSDN Git Service

Impliment commit command at log dialog when choose work copy
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / GitLogListAction.cpp
index 5f5f6fa..9585438 100644 (file)
@@ -20,6 +20,7 @@
 #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
@@ -59,9 +107,18 @@ void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect)
 \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
@@ -85,7 +142,7 @@ void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect)
 \r
                        }\r
                        break;\r
-\r
+  \r
                case ID_COMPARETWO:\r
                        {\r
                                GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));\r
@@ -159,23 +216,329 @@ void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect)
                        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
@@ -501,4 +864,20 @@ void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect)
                } // 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