Use Rebase Dialog For cherry pick ant log dialog actions.
[TortoiseGit.git] / src / TortoiseProc / RebaseDlg.cpp
blob4cdfcdf48bca7c4a5c162b08ebd695e377f70ccf
1 // RebaseDlg.cpp : implementation file
2 //
4 #include "stdafx.h"
5 #include "TortoiseProc.h"
6 #include "RebaseDlg.h"
7 #include "AppUtils.h"
8 #include "MessageBox.h"
9 #include "UnicodeUtils.h"
10 // CRebaseDlg dialog
12 IMPLEMENT_DYNAMIC(CRebaseDlg, CResizableStandAloneDialog)
14 CRebaseDlg::CRebaseDlg(CWnd* pParent /*=NULL*/)
15 : CResizableStandAloneDialog(CRebaseDlg::IDD, pParent)
16 , m_bPickAll(FALSE)
17 , m_bSquashAll(FALSE)
18 , m_bEditAll(FALSE)
20 m_RebaseStage=CHOOSE_BRANCH;
21 m_CurrentRebaseIndex=-1;
22 m_bThreadRunning =FALSE;
23 this->m_IsCherryPick = FALSE;
26 CRebaseDlg::~CRebaseDlg()
30 void CRebaseDlg::DoDataExchange(CDataExchange* pDX)
32 CDialog::DoDataExchange(pDX);
33 DDX_Control(pDX, IDC_REBASE_PROGRESS, m_ProgressBar);
34 DDX_Control(pDX, IDC_STATUS_STATIC, m_CtrlStatusText);
35 DDX_Check(pDX, IDC_PICK_ALL, m_bPickAll);
36 DDX_Check(pDX, IDC_SQUASH_ALL, m_bSquashAll);
37 DDX_Check(pDX, IDC_EDIT_ALL, m_bEditAll);
38 DDX_Control(pDX, IDC_REBASE_SPLIT, m_wndSplitter);
39 DDX_Control(pDX,IDC_COMMIT_LIST,m_CommitList);
40 DDX_Control(pDX,IDC_REBASE_COMBOXEX_BRANCH, this->m_BranchCtrl);
41 DDX_Control(pDX,IDC_REBASE_COMBOXEX_UPSTREAM, this->m_UpstreamCtrl);
46 BEGIN_MESSAGE_MAP(CRebaseDlg, CResizableStandAloneDialog)
47 ON_BN_CLICKED(IDC_PICK_ALL, &CRebaseDlg::OnBnClickedPickAll)
48 ON_BN_CLICKED(IDC_SQUASH_ALL, &CRebaseDlg::OnBnClickedSquashAll)
49 ON_BN_CLICKED(IDC_EDIT_ALL, &CRebaseDlg::OnBnClickedEditAll)
50 ON_BN_CLICKED(IDC_REBASE_SPLIT, &CRebaseDlg::OnBnClickedRebaseSplit)
51 ON_BN_CLICKED(IDC_REBASE_CONTINUE,OnBnClickedContinue)
52 ON_BN_CLICKED(IDC_REBASE_ABORT, OnBnClickedAbort)
53 ON_WM_SIZE()
54 ON_CBN_SELCHANGE(IDC_REBASE_COMBOXEX_BRANCH, &CRebaseDlg::OnCbnSelchangeBranch)
55 ON_CBN_SELCHANGE(IDC_REBASE_COMBOXEX_UPSTREAM, &CRebaseDlg::OnCbnSelchangeUpstream)
56 ON_MESSAGE(MSG_REBASE_UPDATE_UI, OnRebaseUpdateUI)
57 END_MESSAGE_MAP()
59 void CRebaseDlg::AddRebaseAnchor()
61 AddAnchor(IDC_REBASE_TAB,TOP_LEFT,BOTTOM_RIGHT);
62 AddAnchor(IDC_COMMIT_LIST,TOP_LEFT, TOP_RIGHT);
63 AddAnchor(IDC_REBASE_SPLIT,TOP_LEFT, TOP_RIGHT);
64 AddAnchor(IDC_STATUS_STATIC, BOTTOM_LEFT,BOTTOM_RIGHT);
65 AddAnchor(IDC_REBASE_CONTINUE,BOTTOM_RIGHT);
66 AddAnchor(IDC_REBASE_ABORT, BOTTOM_RIGHT);
67 AddAnchor(IDC_REBASE_PROGRESS,BOTTOM_LEFT, BOTTOM_RIGHT);
68 AddAnchor(IDC_PICK_ALL,TOP_LEFT);
69 AddAnchor(IDC_SQUASH_ALL,TOP_LEFT);
70 AddAnchor(IDC_EDIT_ALL,TOP_LEFT);
71 AddAnchor(IDC_REBASE_COMBOXEX_UPSTREAM,TOP_LEFT);
72 AddAnchor(IDC_REBASE_COMBOXEX_BRANCH,TOP_LEFT);
73 AddAnchor(IDC_REBASE_STATIC_UPSTREAM,TOP_LEFT);
74 AddAnchor(IDC_REBASE_STATIC_BRANCH,TOP_LEFT);
78 BOOL CRebaseDlg::OnInitDialog()
80 CResizableStandAloneDialog::OnInitDialog();
82 CRect rectDummy;
83 //IDC_REBASE_DUMY_TAB
85 GetClientRect(m_DlgOrigRect);
86 m_CommitList.GetClientRect(m_CommitListOrigRect);
88 CWnd *pwnd=this->GetDlgItem(IDC_REBASE_DUMY_TAB);
89 pwnd->GetWindowRect(&rectDummy);
91 rectDummy.top-=20;
92 rectDummy.bottom-=20;
94 rectDummy.left-=5;
95 rectDummy.right-=5;
97 if (!m_ctrlTabCtrl.Create(CMFCTabCtrl::STYLE_FLAT, rectDummy, this, IDC_REBASE_TAB))
99 TRACE0("Failed to create output tab window\n");
100 return FALSE; // fail to create
102 m_ctrlTabCtrl.SetResizeMode(CMFCTabCtrl::RESIZE_NO);
103 // Create output panes:
104 //const DWORD dwStyle = LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL;
105 DWORD dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP |LVS_SINGLESEL |WS_CHILD | WS_VISIBLE;
107 if (! this->m_FileListCtrl.Create(dwStyle,rectDummy,&this->m_ctrlTabCtrl,0) )
109 TRACE0("Failed to create output windows\n");
110 return FALSE; // fail to create
113 if( ! this->m_LogMessageCtrl.Create(_T("Scintilla"),_T("source"),0,rectDummy,&m_ctrlTabCtrl,0,0) )
115 TRACE0("Failed to create log message control");
116 return FALSE;
118 m_LogMessageCtrl.Init(0);
120 dwStyle = LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL;
122 if (!m_wndOutputRebase.Create(_T("Scintilla"),_T("source"),0,rectDummy, &m_ctrlTabCtrl, 0,0) )
124 TRACE0("Failed to create output windows\n");
125 return -1; // fail to create
127 m_wndOutputRebase.Init(0);
128 m_wndOutputRebase.Call(SCI_SETREADONLY, TRUE);
130 m_tooltips.Create(this);
132 m_FileListCtrl.Init(SVNSLC_COLEXT | SVNSLC_COLSTATUS |IDS_STATUSLIST_COLADD|IDS_STATUSLIST_COLDEL , _T("RebaseDlg"),(SVNSLC_POPALL ^ SVNSLC_POPCOMMIT),false);
134 m_ctrlTabCtrl.AddTab(&m_FileListCtrl,_T("Conflict File"));
135 m_ctrlTabCtrl.AddTab(&m_LogMessageCtrl,_T("Commit Message"),1);
136 m_ctrlTabCtrl.AddTab(&m_wndOutputRebase,_T("Log"),2);
137 AddRebaseAnchor();
140 EnableSaveRestore(_T("RebaseDlg"));
142 DWORD yPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RebaseDlgSizer"));
143 RECT rcDlg, rcLogMsg, rcFileList;
144 GetClientRect(&rcDlg);
145 m_CommitList.GetWindowRect(&rcLogMsg);
146 ScreenToClient(&rcLogMsg);
147 this->m_ctrlTabCtrl.GetWindowRect(&rcFileList);
148 ScreenToClient(&rcFileList);
149 if (yPos)
151 RECT rectSplitter;
152 m_wndSplitter.GetWindowRect(&rectSplitter);
153 ScreenToClient(&rectSplitter);
154 int delta = yPos - rectSplitter.top;
155 if ((rcLogMsg.bottom + delta > rcLogMsg.top)&&(rcLogMsg.bottom + delta < rcFileList.bottom - 30))
157 m_wndSplitter.SetWindowPos(NULL, 0, yPos, 0, 0, SWP_NOSIZE);
158 DoSize(delta);
162 if( this->m_RebaseStage == CHOOSE_BRANCH)
164 this->LoadBranchInfo();
166 }else
168 this->m_BranchCtrl.EnableWindow(FALSE);
169 this->m_UpstreamCtrl.EnableWindow(FALSE);
172 m_CommitList.m_IsIDReplaceAction = TRUE;
173 // m_CommitList.m_IsOldFirst = TRUE;
174 m_CommitList.m_IsRebaseReplaceGraph = TRUE;
176 m_CommitList.InsertGitColumn();
178 this->SetControlEnable();
180 if(m_IsCherryPick)
182 this->m_BranchCtrl.SetCurSel(-1);
183 this->m_BranchCtrl.EnableWindow(FALSE);
184 this->m_UpstreamCtrl.EnableWindow(FALSE);
185 this->SetWindowText(_T("Cherry Pick"));
186 this->m_CommitList.StartFilter();
188 }else
190 SetContinueButtonText();
191 m_CommitList.DeleteAllItems();
192 FetchLogList();
195 m_CommitList.m_ContextMenuMask &= ~(m_CommitList.GetContextMenuBit(CGitLogListBase::ID_CHERRY_PICK)|
196 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_SWITCHTOREV)|
197 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_RESET)|
198 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REVERTREV)|
199 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_TO_VERSION)|
200 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REVERTTOREV));
202 return TRUE;
204 // CRebaseDlg message handlers
206 void CRebaseDlg::OnBnClickedPickAll()
208 // TODO: Add your control notification handler code here
209 this->UpdateData();
210 if(this->m_bPickAll)
211 this->SetAllRebaseAction(CTGitPath::LOGACTIONS_REBASE_PICK);
213 this->m_bEditAll=FALSE;
214 this->m_bSquashAll=FALSE;
215 this->UpdateData(FALSE);
219 void CRebaseDlg::OnBnClickedSquashAll()
221 // TODO: Add your control notification handler code here
222 this->UpdateData();
223 if(this->m_bSquashAll)
224 this->SetAllRebaseAction(CTGitPath::LOGACTIONS_REBASE_SQUASH);
226 this->m_bEditAll=FALSE;
227 this->m_bPickAll=FALSE;
228 this->UpdateData(FALSE);
232 void CRebaseDlg::OnBnClickedEditAll()
234 // TODO: Add your control notification handler code here
235 this->UpdateData();
236 if( this->m_bEditAll )
237 this->SetAllRebaseAction(CTGitPath::LOGACTIONS_REBASE_EDIT);
239 this->m_bPickAll=FALSE;
240 this->m_bSquashAll=FALSE;
241 this->UpdateData(FALSE);
245 void CRebaseDlg::SetAllRebaseAction(int action)
247 for(int i=0;i<this->m_CommitList.m_logEntries.size();i++)
249 m_CommitList.m_logEntries[i].m_Action=action;
251 m_CommitList.Invalidate();
254 void CRebaseDlg::OnBnClickedRebaseSplit()
256 this->UpdateData();
257 // TODO: Add your control notification handler code here
260 LRESULT CRebaseDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
262 switch (message) {
263 case WM_NOTIFY:
264 if (wParam == IDC_REBASE_SPLIT)
266 SPC_NMHDR* pHdr = (SPC_NMHDR*) lParam;
267 DoSize(pHdr->delta);
269 break;
272 return __super::DefWindowProc(message, wParam, lParam);
275 void CRebaseDlg::DoSize(int delta)
278 this->RemoveAllAnchors();
280 CSplitterControl::ChangeHeight(GetDlgItem(IDC_COMMIT_LIST), delta, CW_TOPALIGN);
281 //CSplitterControl::ChangeHeight(GetDlgItem(), delta, CW_TOPALIGN);
282 CSplitterControl::ChangeHeight(GetDlgItem(IDC_REBASE_TAB), -delta, CW_BOTTOMALIGN);
283 //CSplitterControl::ChangeHeight(GetDlgItem(), -delta, CW_BOTTOMALIGN);
284 CSplitterControl::ChangePos(GetDlgItem(IDC_SQUASH_ALL),0,delta);
285 CSplitterControl::ChangePos(GetDlgItem(IDC_PICK_ALL),0,delta);
286 CSplitterControl::ChangePos(GetDlgItem(IDC_EDIT_ALL),0,delta);
288 this->AddRebaseAnchor();
289 // adjust the minimum size of the dialog to prevent the resizing from
290 // moving the list control too far down.
291 CRect rcLogMsg;
292 m_CommitList.GetClientRect(rcLogMsg);
293 SetMinTrackSize(CSize(m_DlgOrigRect.Width(), m_DlgOrigRect.Height()-m_CommitListOrigRect.Height()+rcLogMsg.Height()));
295 SetSplitterRange();
296 // m_CommitList.Invalidate();
298 // GetDlgItem(IDC_LOGMESSAGE)->Invalidate();
300 this->m_ctrlTabCtrl.Invalidate();
301 this->m_CommitList.Invalidate();
302 this->m_FileListCtrl.Invalidate();
303 this->m_LogMessageCtrl.Invalidate();
307 void CRebaseDlg::SetSplitterRange()
309 if ((m_CommitList)&&(m_ctrlTabCtrl))
311 CRect rcTop;
312 m_CommitList.GetWindowRect(rcTop);
313 ScreenToClient(rcTop);
314 CRect rcMiddle;
315 m_ctrlTabCtrl.GetWindowRect(rcMiddle);
316 ScreenToClient(rcMiddle);
317 if (rcMiddle.Height() && rcMiddle.Width())
318 m_wndSplitter.SetRange(rcTop.top+60, rcMiddle.bottom-80);
322 void CRebaseDlg::OnSize(UINT nType,int cx, int cy)
324 // first, let the resizing take place
325 __super::OnSize(nType, cx, cy);
327 //set range
328 SetSplitterRange();
331 void CRebaseDlg::SaveSplitterPos()
333 if (!IsIconic())
335 CRegDWORD regPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RebaseDlgSizer"));
336 RECT rectSplitter;
337 m_wndSplitter.GetWindowRect(&rectSplitter);
338 ScreenToClient(&rectSplitter);
339 regPos = rectSplitter.top;
343 void CRebaseDlg::LoadBranchInfo()
345 m_BranchCtrl.SetMaxHistoryItems(0x7FFFFFFF);
346 m_UpstreamCtrl.SetMaxHistoryItems(0x7FFFFFFF);
348 STRING_VECTOR list;
349 list.clear();
350 int current;
351 g_Git.GetBranchList(list,&current,CGit::BRANCH_ALL);
352 m_BranchCtrl.AddString(list);
353 m_UpstreamCtrl.AddString(list);
355 m_BranchCtrl.SetCurSel(current);
357 AddBranchToolTips(&m_BranchCtrl);
358 AddBranchToolTips(&m_UpstreamCtrl);
360 if(!m_Upstream.IsEmpty())
362 m_UpstreamCtrl.AddString(m_Upstream);
363 m_UpstreamCtrl.SetCurSel(m_UpstreamCtrl.GetCount()-1);
367 void CRebaseDlg::OnCbnSelchangeBranch()
369 FetchLogList();
372 void CRebaseDlg::OnCbnSelchangeUpstream()
374 FetchLogList();
377 void CRebaseDlg::FetchLogList()
379 m_CommitList.Clear();
380 this->m_CommitList.FillGitLog(NULL,0,&m_UpstreamCtrl.GetString(),&m_BranchCtrl.GetString());
381 if( m_CommitList.GetItemCount() == 0 )
382 m_CommitList.ShowText(_T("Nothing Rebase"));
384 CString hash=g_Git.GetHash(m_UpstreamCtrl.GetString());
386 #if 0
387 if(m_CommitList.m_logEntries[m_CommitList.m_logEntries.size()-1].m_ParentHash.size() >=0 )
389 if(hash == m_CommitList.m_logEntries[m_CommitList.m_logEntries.size()-1].m_ParentHash[0])
391 m_CommitList.Clear();
392 m_CommitList.ShowText(_T("Nothing Rebase"));
395 #endif
397 m_tooltips.Pop();
398 AddBranchToolTips(&this->m_BranchCtrl);
399 AddBranchToolTips(&this->m_UpstreamCtrl);
401 for(int i=0;i<m_CommitList.m_logEntries.size();i++)
403 m_CommitList.m_logEntries[i].m_Action = CTGitPath::LOGACTIONS_REBASE_PICK;
406 m_CommitList.Invalidate();
408 if(m_CommitList.m_IsOldFirst)
409 this->m_CurrentRebaseIndex = -1;
410 else
411 this->m_CurrentRebaseIndex = m_CommitList.m_logEntries.size();
415 void CRebaseDlg::AddBranchToolTips(CHistoryCombo *pBranch)
417 if(pBranch)
419 CString text=pBranch->GetString();
420 CString tooltip;
421 BYTE_VECTOR data;
422 g_Git.GetLog(data,text,NULL,1,0);
423 GitRev rev;
424 rev.ParserFromLog(data);
425 tooltip.Format(_T("CommitHash:%s\nCommit by: %s %s\n <b>%s</b> \n %s"),
426 rev.m_CommitHash,
427 rev.m_AuthorName,
428 CAppUtils::FormatDateAndTime(rev.m_AuthorDate,DATE_LONGDATE),
429 rev.m_Subject,
430 rev.m_Body);
432 pBranch->DisableTooltip();
433 this->m_tooltips.AddTool(pBranch->GetComboBoxCtrl(),tooltip);
437 BOOL CRebaseDlg::PreTranslateMessage(MSG*pMsg)
439 m_tooltips.RelayEvent(pMsg);
440 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
442 int CRebaseDlg::CheckRebaseCondition()
444 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
446 if( !g_Git.CheckCleanWorkTree() )
448 CMessageBox::Show(NULL,_T("Rebase Need Clean Working Tree"),_T("TortoiseGit"),MB_OK);
449 return -1;
451 //Todo Check $REBASE_ROOT
452 //Todo Check $DOTEST
454 CString cmd;
455 cmd=_T("git.exe var GIT_COMMITTER_IDENT");
456 if(g_Git.Run(cmd,NULL,CP_UTF8))
457 return -1;
459 //Todo call pre_rebase_hook
461 int CRebaseDlg::StartRebase()
463 CString cmd,out;
464 //Todo call comment_for_reflog
465 cmd.Format(_T("git.exe checkout %s"),this->m_BranchCtrl.GetString());
466 this->AddLogString(cmd);
468 if(g_Git.Run(cmd,&out,CP_UTF8))
469 return -1;
471 this->AddLogString(out);
473 cmd=_T("git.exe rev-parse --verify HEAD");
474 if(g_Git.Run(cmd,&out,CP_UTF8))
476 AddLogString(_T("No Head"));
477 return -1;
479 //Todo
480 //git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null ||
481 // echo "detached HEAD" > "$DOTEST"/head-name
483 cmd.Format(_T("git.exe update-ref ORIG_HEAD HEAD"));
484 if(g_Git.Run(cmd,&out,CP_UTF8))
486 AddLogString(_T("update ORIG_HEAD Fail"));
487 return -1;
490 cmd.Format(_T("git.exe update-ref ORIG_HEAD HEAD"));
492 cmd.Format(_T("git.exe checkout %s"),this->m_UpstreamCtrl.GetString());
493 this->AddLogString(cmd);
495 out.Empty();
496 if(g_Git.Run(cmd,&out,CP_UTF8))
498 return -1;
501 cmd.Format(_T("git.exe rev-parse %s"),this->m_UpstreamCtrl.GetString());
502 if(g_Git.Run(cmd,&this->m_OrigUpstreamHash,CP_UTF8))
504 this->AddLogString(m_OrigUpstreamHash);
505 return -1;
508 cmd.Format(_T("git.exe rev-parse %s"),this->m_BranchCtrl.GetString());
509 if(g_Git.Run(cmd,&this->m_OrigBranchHash,CP_UTF8))
511 this->AddLogString(m_OrigBranchHash);
512 return -1;
515 this->AddLogString(_T("Start Rebase\r\n"));
516 return 0;
518 int CRebaseDlg::VerifyNoConflict()
520 CTGitPathList list;
521 if(g_Git.ListConflictFile(list))
523 AddLogString(_T("Get conflict files fail"));
524 return -1;
526 if( list.GetCount() != 0 )
528 CMessageBox::Show(NULL,_T("There are conflict file, you should mark it resolve"),_T("TortoiseGit"),MB_OK);
529 return -1;
531 return 0;
534 void CRebaseDlg::OnBnClickedContinue()
536 if( m_RebaseStage == CHOOSE_BRANCH|| m_RebaseStage == CHOOSE_COMMIT_PICK_MODE )
538 if(CheckRebaseCondition())
539 return ;
540 m_RebaseStage = REBASE_START;
543 if( m_RebaseStage == REBASE_FINISH )
545 CString cmd,out;
546 cmd.Format(_T("git branch -f %s"),this->m_BranchCtrl.GetString());
547 if(g_Git.Run(cmd,&out,CP_UTF8))
549 AddLogString(out);
550 return ;
552 cmd.Format(_T("git reset --hard %s"),this->m_OrigUpstreamHash);
553 if(g_Git.Run(cmd,&out,CP_UTF8))
555 AddLogString(out);
556 return ;
558 OnOK();
561 if( m_RebaseStage == REBASE_SQUASH_CONFLICT)
563 if(VerifyNoConflict())
564 return;
565 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
566 if(this->CheckNextCommitIsSquash())
567 {//next commit is not squash;
568 m_RebaseStage = REBASE_SQUASH_EDIT;
569 this->OnRebaseUpdateUI(0,0);
570 this->UpdateCurrentStatus();
571 return ;
574 m_RebaseStage=REBASE_CONTINUE;
575 curRev->m_Action|=CTGitPath::LOGACTIONS_REBASE_DONE;
576 this->UpdateCurrentStatus();
580 if( m_RebaseStage == REBASE_CONFLICT )
582 if(VerifyNoConflict())
583 return;
585 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
587 CString out =_T("");
588 CString cmd;
589 cmd.Format(_T("git.exe commit -C %s"), curRev->m_CommitHash);
591 if(g_Git.Run(cmd,&out,CP_UTF8))
593 if(!g_Git.CheckCleanWorkTree())
595 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
596 return;
600 AddLogString(out);
601 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
602 if( curRev->m_Action & CTGitPath::LOGACTIONS_REBASE_EDIT)
604 m_RebaseStage=REBASE_EDIT;
605 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_MESSAGE);
606 this->UpdateCurrentStatus();
607 return;
609 else
611 m_RebaseStage=REBASE_CONTINUE;
612 curRev->m_Action|=CTGitPath::LOGACTIONS_REBASE_DONE;
613 this->UpdateCurrentStatus();
618 if( m_RebaseStage == REBASE_EDIT || m_RebaseStage == REBASE_SQUASH_EDIT )
620 CString str;
621 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
623 str=this->m_LogMessageCtrl.GetText();
624 if(str.Trim().IsEmpty())
626 CMessageBox::Show(NULL,_T("Commit Message Is Empty"),_T("TortoiseGit"),MB_OK|MB_ICONERROR);
627 return;
630 CString tempfile=::GetTempFile();
631 CFile file(tempfile,CFile::modeReadWrite|CFile::modeCreate );
632 CStringA log=CUnicodeUtils::GetUTF8( str);
633 file.Write(log,log.GetLength());
634 //file.WriteString(m_sLogMessage);
635 file.Close();
637 CString out,cmd;
639 if( m_RebaseStage == REBASE_SQUASH_EDIT )
640 cmd.Format(_T("git.exe commit -F \"%s\""), tempfile);
641 else
642 cmd.Format(_T("git.exe commit --amend -F \"%s\""), tempfile);
644 if(g_Git.Run(cmd,&out,CP_UTF8))
646 if(!g_Git.CheckCleanWorkTree())
648 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
649 return;
653 CFile::Remove(tempfile);
654 AddLogString(out);
655 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
656 m_RebaseStage=REBASE_CONTINUE;
657 curRev->m_Action|=CTGitPath::LOGACTIONS_REBASE_DONE;
658 this->UpdateCurrentStatus();
662 InterlockedExchange(&m_bThreadRunning, TRUE);
663 SetControlEnable();
665 if (AfxBeginThread(RebaseThreadEntry, this)==NULL)
667 InterlockedExchange(&m_bThreadRunning, FALSE);
668 CMessageBox::Show(NULL, _T("Create Rebase Thread Fail"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
669 SetControlEnable();
672 int CRebaseDlg::CheckNextCommitIsSquash()
674 int index;
675 if(m_CommitList.m_IsOldFirst)
676 index=m_CurrentRebaseIndex+1;
677 else
678 index=m_CurrentRebaseIndex-1;
680 GitRev *curRev;
683 curRev=(GitRev*)m_CommitList.m_arShownList[index];
685 if( curRev->m_Action&CTGitPath::LOGACTIONS_REBASE_SQUASH )
686 return 0;
687 if( curRev->m_Action&CTGitPath::LOGACTIONS_REBASE_SKIP)
689 if(m_CommitList.m_IsOldFirst)
690 index++;
691 else
692 index--;
693 }else
694 return -1;
696 if(index<0)
697 return -1;
698 if(index>= m_CommitList.GetItemCount())
699 return -1;
701 }while(curRev->m_Action&CTGitPath::LOGACTIONS_REBASE_SKIP);
703 return -1;
706 int CRebaseDlg::GoNext()
708 if(m_CommitList.m_IsOldFirst)
709 m_CurrentRebaseIndex++;
710 else
711 m_CurrentRebaseIndex--;
712 return 0;
715 int CRebaseDlg::StateAction()
717 switch(this->m_RebaseStage)
719 case CHOOSE_BRANCH:
720 case CHOOSE_COMMIT_PICK_MODE:
721 if(StartRebase())
722 return -1;
723 m_RebaseStage = REBASE_START;
724 GoNext();
725 break;
728 return 0;
730 void CRebaseDlg::SetContinueButtonText()
732 CString Text;
733 switch(this->m_RebaseStage)
735 case CHOOSE_BRANCH:
736 case CHOOSE_COMMIT_PICK_MODE:
737 Text = _T("Start");
738 break;
740 case REBASE_START:
741 case REBASE_CONTINUE:
742 case REBASE_SQUASH_CONFLICT:
743 Text = _T("Continue");
744 break;
746 case REBASE_CONFLICT:
747 Text = _T("Commit");
748 break;
749 case REBASE_EDIT:
750 Text = _T("Amend");
751 break;
753 case REBASE_SQUASH_EDIT:
754 Text = _T("Commit");
755 break;
757 case REBASE_ABORT:
758 case REBASE_FINISH:
759 Text = _T("Finish");
760 break;
762 this->GetDlgItem(IDC_REBASE_CONTINUE)->SetWindowText(Text);
765 void CRebaseDlg::SetControlEnable()
767 switch(this->m_RebaseStage)
769 case CHOOSE_BRANCH:
770 case CHOOSE_COMMIT_PICK_MODE:
772 this->GetDlgItem(IDC_PICK_ALL)->EnableWindow(TRUE);
773 this->GetDlgItem(IDC_EDIT_ALL)->EnableWindow(TRUE);
774 this->GetDlgItem(IDC_SQUASH_ALL)->EnableWindow(TRUE);
775 if(!m_IsCherryPick)
777 this->GetDlgItem(IDC_REBASE_COMBOXEX_BRANCH)->EnableWindow(TRUE);
778 this->GetDlgItem(IDC_REBASE_COMBOXEX_UPSTREAM)->EnableWindow(TRUE);
780 //this->m_CommitList.m_IsEnableRebaseMenu=TRUE;
781 this->m_CommitList.m_ContextMenuMask |= m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_PICK)|
782 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SQUASH)|
783 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_EDIT)|
784 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SKIP);
785 break;
787 case REBASE_START:
788 case REBASE_CONTINUE:
789 case REBASE_ABORT:
790 case REBASE_FINISH:
791 case REBASE_CONFLICT:
792 case REBASE_EDIT:
793 case REBASE_SQUASH_CONFLICT:
794 this->GetDlgItem(IDC_PICK_ALL)->EnableWindow(FALSE);
795 this->GetDlgItem(IDC_EDIT_ALL)->EnableWindow(FALSE);
796 this->GetDlgItem(IDC_SQUASH_ALL)->EnableWindow(FALSE);
797 this->GetDlgItem(IDC_REBASE_COMBOXEX_BRANCH)->EnableWindow(FALSE);
798 this->GetDlgItem(IDC_REBASE_COMBOXEX_UPSTREAM)->EnableWindow(FALSE);
799 //this->m_CommitList.m_IsEnableRebaseMenu=FALSE;
800 this->m_CommitList.m_ContextMenuMask &= ~(m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_PICK)|
801 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SQUASH)|
802 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_EDIT)|
803 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SKIP));
804 break;
807 if(m_bThreadRunning)
809 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(FALSE);
810 this->GetDlgItem(IDC_REBASE_ABORT)->EnableWindow(FALSE);
812 }else
814 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(TRUE);
815 this->GetDlgItem(IDC_REBASE_ABORT)->EnableWindow(TRUE);
819 void CRebaseDlg::UpdateProgress()
821 int index;
822 CRect rect;
824 if(m_CommitList.m_IsOldFirst)
825 index = m_CurrentRebaseIndex+1;
826 else
827 index = m_CommitList.GetItemCount()-m_CurrentRebaseIndex;
829 m_ProgressBar.SetRange(1,m_CommitList.GetItemCount());
830 m_ProgressBar.SetPos(index);
832 if(m_CurrentRebaseIndex>0 && m_CurrentRebaseIndex< m_CommitList.GetItemCount())
834 CString text;
835 text.Format(_T("Rebasing...(%d/%d)"),index,m_CommitList.GetItemCount());
836 m_CtrlStatusText.SetWindowText(text);
840 GitRev *prevRev=NULL, *curRev=NULL;
842 if( m_CurrentRebaseIndex >= 0 && m_CurrentRebaseIndex< m_CommitList.m_arShownList.GetSize())
844 curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
847 for(int i=0;i<m_CommitList.m_arShownList.GetSize();i++)
849 prevRev=(GitRev*)m_CommitList.m_arShownList[i];
850 if(prevRev->m_Action & CTGitPath::LOGACTIONS_REBASE_CURRENT)
852 prevRev->m_Action &= ~ CTGitPath::LOGACTIONS_REBASE_CURRENT;
853 m_CommitList.GetItemRect(i,&rect,LVIR_BOUNDS);
854 m_CommitList.InvalidateRect(rect);
858 if(curRev)
860 curRev->m_Action |= CTGitPath::LOGACTIONS_REBASE_CURRENT;
861 m_CommitList.GetItemRect(m_CurrentRebaseIndex,&rect,LVIR_BOUNDS);
862 m_CommitList.InvalidateRect(rect);
864 m_CommitList.EnsureVisible(m_CurrentRebaseIndex,FALSE);
868 void CRebaseDlg::UpdateCurrentStatus()
870 if( m_CurrentRebaseIndex < 0)
872 if(m_CommitList.m_IsOldFirst)
873 m_RebaseStage = CRebaseDlg::REBASE_START;
874 else
875 m_RebaseStage = CRebaseDlg::REBASE_FINISH;
878 if( m_CurrentRebaseIndex == m_CommitList.m_arShownList.GetSize())
880 if(m_CommitList.m_IsOldFirst)
881 m_RebaseStage = CRebaseDlg::REBASE_FINISH;
882 else
883 m_RebaseStage = CRebaseDlg::REBASE_START;
886 SetContinueButtonText();
887 SetControlEnable();
888 UpdateProgress();
891 void CRebaseDlg::AddLogString(CString str)
893 this->m_wndOutputRebase.SendMessage(SCI_SETREADONLY, FALSE);
894 CStringA sTextA = m_wndOutputRebase.StringForControl(str);//CUnicodeUtils::GetUTF8(str);
895 this->m_wndOutputRebase.SendMessage(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)sTextA);
896 this->m_wndOutputRebase.SendMessage(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)"\n");
897 this->m_wndOutputRebase.SendMessage(SCI_SETREADONLY, TRUE);
900 int CRebaseDlg::GetCurrentCommitID()
902 if(m_CommitList.m_IsOldFirst)
904 return this->m_CurrentRebaseIndex+1;
906 }else
908 return m_CommitList.GetItemCount()-m_CurrentRebaseIndex;
912 int CRebaseDlg::DoRebase()
914 CString cmd,out;
915 if(m_CurrentRebaseIndex <0)
916 return 0;
917 if(m_CurrentRebaseIndex >= m_CommitList.GetItemCount() )
918 return 0;
920 GitRev *pRev = (GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
921 int mode=pRev->m_Action & CTGitPath::LOGACTIONS_REBASE_MODE_MASK;
922 CString nocommit;
924 if( mode== CTGitPath::LOGACTIONS_REBASE_SKIP)
926 pRev->m_Action|= CTGitPath::LOGACTIONS_REBASE_DONE;
927 return 0;
930 if( mode != CTGitPath::LOGACTIONS_REBASE_PICK )
932 this->m_SquashMessage+= pRev->m_Subject;
933 this->m_SquashMessage+= _T("\n");
934 this->m_SquashMessage+= pRev->m_Body;
936 else
937 this->m_SquashMessage.Empty();
939 if(mode == CTGitPath::LOGACTIONS_REBASE_SQUASH)
940 nocommit=_T(" --no-commit ");
942 CString log;
943 log.Format(_T("%s %d:%s"),CTGitPath::GetActionName(mode),this->GetCurrentCommitID(),pRev->m_CommitHash);
944 AddLogString(log);
945 AddLogString(pRev->m_Subject);
946 cmd.Format(_T("git.exe cherry-pick %s %s"),nocommit,pRev->m_CommitHash);
948 if(g_Git.Run(cmd,&out,CP_UTF8))
950 AddLogString(out);
951 CTGitPathList list;
952 if(g_Git.ListConflictFile(list))
954 AddLogString(_T("Get conflict files fail"));
955 return -1;
957 if(list.GetCount() == 0 )
959 if(mode == CTGitPath::LOGACTIONS_REBASE_PICK)
961 pRev->m_Action|= CTGitPath::LOGACTIONS_REBASE_DONE;
962 return 0;
964 if(mode == CTGitPath::LOGACTIONS_REBASE_EDIT)
965 return -1; // Edit return -1 to stop rebase.
967 // Squash Case
968 if(CheckNextCommitIsSquash())
969 { // no squash
970 // let user edit last commmit message
971 this->m_RebaseStage = REBASE_SQUASH_EDIT;
972 return -1;
975 if(mode == CTGitPath::LOGACTIONS_REBASE_SQUASH)
976 m_RebaseStage = REBASE_SQUASH_CONFLICT;
977 else
978 m_RebaseStage = REBASE_CONFLICT;
979 return -1;
981 }else
983 AddLogString(out);
984 if(mode == CTGitPath::LOGACTIONS_REBASE_PICK)
986 pRev->m_Action|= CTGitPath::LOGACTIONS_REBASE_DONE;
987 return 0;
989 if(mode == CTGitPath::LOGACTIONS_REBASE_EDIT)
990 return -1; // Edit return -1 to stop rebase.
992 // Squash Case
993 if(CheckNextCommitIsSquash())
994 { // no squash
995 // let user edit last commmit message
996 this->m_RebaseStage = REBASE_SQUASH_EDIT;
997 return -1;
1001 return 0;
1004 BOOL CRebaseDlg::IsEnd()
1006 if(m_CommitList.m_IsOldFirst)
1007 return m_CurrentRebaseIndex>= this->m_CommitList.GetItemCount();
1008 else
1009 return m_CurrentRebaseIndex<0;
1012 int CRebaseDlg::RebaseThread()
1014 int ret=0;
1015 while(1)
1017 if( m_RebaseStage == REBASE_START )
1019 if( this->StartRebase() )
1021 InterlockedExchange(&m_bThreadRunning, FALSE);
1022 ret = -1;
1023 break;
1025 m_RebaseStage = REBASE_CONTINUE;
1027 }else if( m_RebaseStage == REBASE_CONTINUE )
1029 this->GoNext();
1030 if(IsEnd())
1032 ret = 0;
1033 m_RebaseStage = REBASE_FINISH;
1034 break;
1037 ret = DoRebase();
1039 if( ret )
1041 break;
1044 }else
1045 break;
1046 this->PostMessage(MSG_REBASE_UPDATE_UI);
1047 //this->UpdateCurrentStatus();
1050 InterlockedExchange(&m_bThreadRunning, FALSE);
1051 this->PostMessage(MSG_REBASE_UPDATE_UI);
1052 return ret;
1055 void CRebaseDlg::ListConflictFile()
1057 this->m_FileListCtrl.Clear();
1058 CTGitPathList list;
1059 CTGitPath path;
1060 list.AddPath(path);
1062 this->m_FileListCtrl.GetStatus(&list,true);
1063 this->m_FileListCtrl.Show(CTGitPath::LOGACTIONS_UNMERGED|CTGitPath::LOGACTIONS_MODIFIED,CTGitPath::LOGACTIONS_UNMERGED);
1064 if( this->m_FileListCtrl.GetItemCount() == 0 )
1070 LRESULT CRebaseDlg::OnRebaseUpdateUI(WPARAM,LPARAM)
1072 UpdateCurrentStatus();
1073 if(m_CurrentRebaseIndex <0)
1074 return 0;
1075 if(m_CurrentRebaseIndex >= m_CommitList.GetItemCount() )
1076 return 0;
1077 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1079 switch(m_RebaseStage)
1081 case REBASE_CONFLICT:
1082 case REBASE_SQUASH_CONFLICT:
1083 ListConflictFile();
1084 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_CONFLICT);
1085 this->m_LogMessageCtrl.SetText(curRev->m_Subject+_T("\n")+curRev->m_Body);
1086 break;
1087 case REBASE_EDIT:
1088 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_MESSAGE);
1089 this->m_LogMessageCtrl.SetText(curRev->m_Subject+_T("\n")+curRev->m_Body);
1090 break;
1091 case REBASE_SQUASH_EDIT:
1092 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_MESSAGE);
1093 this->m_LogMessageCtrl.SetText(this->m_SquashMessage);
1094 break;
1095 default:
1096 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1098 return 0;
1100 void CRebaseDlg::OnCancel()
1102 OnBnClickedAbort();
1104 void CRebaseDlg::OnBnClickedAbort()
1106 CString cmd,out;
1107 if(m_OrigUpstreamHash.IsEmpty())
1109 __super::OnCancel();
1112 if(m_RebaseStage == CHOOSE_BRANCH || m_RebaseStage== CHOOSE_COMMIT_PICK_MODE)
1114 return;
1117 if(CMessageBox::Show(NULL,_T("Are you sure abort rebase"),_T("TortoiseGit"),MB_YESNO) != IDYES)
1118 return;
1120 cmd.Format(_T("git.exe reset --hard %s"),this->m_OrigUpstreamHash.Left(40));
1121 if(g_Git.Run(cmd,&out,CP_UTF8))
1123 AddLogString(out);
1124 return ;
1127 cmd.Format(_T("git checkout -f %s"),this->m_BranchCtrl.GetString());
1128 if(g_Git.Run(cmd,&out,CP_UTF8))
1130 AddLogString(out);
1131 return ;
1134 __super::OnCancel();