Pick Ref: Prepare ref picker to be able to leave out some ref types.
[TortoiseGit.git] / src / TortoiseProc / RebaseDlg.cpp
blob465ab4b03ff2f63c45ba7e815f049192e29022bb
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 #include "BrowseRefsDlg.h"
11 // CRebaseDlg dialog
13 IMPLEMENT_DYNAMIC(CRebaseDlg, CResizableStandAloneDialog)
15 CRebaseDlg::CRebaseDlg(CWnd* pParent /*=NULL*/)
16 : CResizableStandAloneDialog(CRebaseDlg::IDD, pParent)
17 , m_bPickAll(FALSE)
18 , m_bSquashAll(FALSE)
19 , m_bEditAll(FALSE)
21 m_RebaseStage=CHOOSE_BRANCH;
22 m_CurrentRebaseIndex=-1;
23 m_bThreadRunning =FALSE;
24 this->m_IsCherryPick = FALSE;
27 CRebaseDlg::~CRebaseDlg()
31 void CRebaseDlg::DoDataExchange(CDataExchange* pDX)
33 CDialog::DoDataExchange(pDX);
34 DDX_Control(pDX, IDC_REBASE_PROGRESS, m_ProgressBar);
35 DDX_Control(pDX, IDC_STATUS_STATIC, m_CtrlStatusText);
36 DDX_Check(pDX, IDC_PICK_ALL, m_bPickAll);
37 DDX_Check(pDX, IDC_SQUASH_ALL, m_bSquashAll);
38 DDX_Check(pDX, IDC_EDIT_ALL, m_bEditAll);
39 DDX_Control(pDX, IDC_REBASE_SPLIT, m_wndSplitter);
40 DDX_Control(pDX,IDC_COMMIT_LIST,m_CommitList);
41 DDX_Control(pDX,IDC_REBASE_COMBOXEX_BRANCH, this->m_BranchCtrl);
42 DDX_Control(pDX,IDC_REBASE_COMBOXEX_UPSTREAM, this->m_UpstreamCtrl);
47 BEGIN_MESSAGE_MAP(CRebaseDlg, CResizableStandAloneDialog)
48 ON_BN_CLICKED(IDC_PICK_ALL, &CRebaseDlg::OnBnClickedPickAll)
49 ON_BN_CLICKED(IDC_SQUASH_ALL, &CRebaseDlg::OnBnClickedSquashAll)
50 ON_BN_CLICKED(IDC_EDIT_ALL, &CRebaseDlg::OnBnClickedEditAll)
51 ON_BN_CLICKED(IDC_REBASE_SPLIT, &CRebaseDlg::OnBnClickedRebaseSplit)
52 ON_BN_CLICKED(IDC_REBASE_CONTINUE,OnBnClickedContinue)
53 ON_BN_CLICKED(IDC_REBASE_ABORT, OnBnClickedAbort)
54 ON_WM_SIZE()
55 ON_CBN_SELCHANGE(IDC_REBASE_COMBOXEX_BRANCH, &CRebaseDlg::OnCbnSelchangeBranch)
56 ON_CBN_SELCHANGE(IDC_REBASE_COMBOXEX_UPSTREAM, &CRebaseDlg::OnCbnSelchangeUpstream)
57 ON_MESSAGE(MSG_REBASE_UPDATE_UI, OnRebaseUpdateUI)
58 ON_BN_CLICKED(IDC_BUTTON_BROWSE, &CRebaseDlg::OnBnClickedButtonBrowse)
59 END_MESSAGE_MAP()
61 void CRebaseDlg::AddRebaseAnchor()
63 AddAnchor(IDC_REBASE_TAB,TOP_LEFT,BOTTOM_RIGHT);
64 AddAnchor(IDC_COMMIT_LIST,TOP_LEFT, TOP_RIGHT);
65 AddAnchor(IDC_REBASE_SPLIT,TOP_LEFT, TOP_RIGHT);
66 AddAnchor(IDC_STATUS_STATIC, BOTTOM_LEFT,BOTTOM_RIGHT);
67 AddAnchor(IDC_REBASE_CONTINUE,BOTTOM_RIGHT);
68 AddAnchor(IDC_REBASE_ABORT, BOTTOM_RIGHT);
69 AddAnchor(IDC_REBASE_PROGRESS,BOTTOM_LEFT, BOTTOM_RIGHT);
70 AddAnchor(IDC_PICK_ALL,TOP_LEFT);
71 AddAnchor(IDC_SQUASH_ALL,TOP_LEFT);
72 AddAnchor(IDC_EDIT_ALL,TOP_LEFT);
73 AddAnchor(IDC_REBASE_COMBOXEX_UPSTREAM,TOP_LEFT);
74 AddAnchor(IDC_REBASE_COMBOXEX_BRANCH,TOP_LEFT);
75 AddAnchor(IDC_REBASE_STATIC_UPSTREAM,TOP_LEFT);
76 AddAnchor(IDC_REBASE_STATIC_BRANCH,TOP_LEFT);
77 this->AddOthersToAnchor();
80 BOOL CRebaseDlg::OnInitDialog()
82 CResizableStandAloneDialog::OnInitDialog();
84 CRect rectDummy;
85 //IDC_REBASE_DUMY_TAB
87 GetClientRect(m_DlgOrigRect);
88 m_CommitList.GetClientRect(m_CommitListOrigRect);
90 CWnd *pwnd=this->GetDlgItem(IDC_REBASE_DUMY_TAB);
91 pwnd->GetWindowRect(&rectDummy);
92 this->ScreenToClient(rectDummy);
94 if (!m_ctrlTabCtrl.Create(CMFCTabCtrl::STYLE_FLAT, rectDummy, this, IDC_REBASE_TAB))
96 TRACE0("Failed to create output tab window\n");
97 return FALSE; // fail to create
99 m_ctrlTabCtrl.SetResizeMode(CMFCTabCtrl::RESIZE_NO);
100 // Create output panes:
101 //const DWORD dwStyle = LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL;
102 DWORD dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP |LVS_SINGLESEL |WS_CHILD | WS_VISIBLE;
104 if (! this->m_FileListCtrl.Create(dwStyle,rectDummy,&this->m_ctrlTabCtrl,0) )
106 TRACE0("Failed to create output windows\n");
107 return FALSE; // fail to create
110 if( ! this->m_LogMessageCtrl.Create(_T("Scintilla"),_T("source"),0,rectDummy,&m_ctrlTabCtrl,0,0) )
112 TRACE0("Failed to create log message control");
113 return FALSE;
115 m_LogMessageCtrl.Init(0);
117 dwStyle = LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL;
119 if (!m_wndOutputRebase.Create(_T("Scintilla"),_T("source"),0,rectDummy, &m_ctrlTabCtrl, 0,0) )
121 TRACE0("Failed to create output windows\n");
122 return -1; // fail to create
124 m_wndOutputRebase.Init(0);
125 m_wndOutputRebase.Call(SCI_SETREADONLY, TRUE);
127 m_tooltips.Create(this);
129 m_FileListCtrl.Init(SVNSLC_COLEXT | SVNSLC_COLSTATUS |SVNSLC_COLADD|SVNSLC_COLDEL , _T("RebaseDlg"),(SVNSLC_POPALL ^ SVNSLC_POPCOMMIT),false);
131 m_ctrlTabCtrl.AddTab(&m_FileListCtrl,_T("Conflict File"));
132 m_ctrlTabCtrl.AddTab(&m_LogMessageCtrl,_T("Commit Message"),1);
133 m_ctrlTabCtrl.AddTab(&m_wndOutputRebase,_T("Log"),2);
134 AddRebaseAnchor();
137 EnableSaveRestore(_T("RebaseDlg"));
139 DWORD yPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RebaseDlgSizer"));
140 RECT rcDlg, rcLogMsg, rcFileList;
141 GetClientRect(&rcDlg);
142 m_CommitList.GetWindowRect(&rcLogMsg);
143 ScreenToClient(&rcLogMsg);
144 this->m_ctrlTabCtrl.GetWindowRect(&rcFileList);
145 ScreenToClient(&rcFileList);
146 if (yPos)
148 RECT rectSplitter;
149 m_wndSplitter.GetWindowRect(&rectSplitter);
150 ScreenToClient(&rectSplitter);
151 int delta = yPos - rectSplitter.top;
152 if ((rcLogMsg.bottom + delta > rcLogMsg.top)&&(rcLogMsg.bottom + delta < rcFileList.bottom - 30))
154 m_wndSplitter.SetWindowPos(NULL, 0, yPos, 0, 0, SWP_NOSIZE);
155 DoSize(delta);
159 if( this->m_RebaseStage == CHOOSE_BRANCH)
161 this->LoadBranchInfo();
163 }else
165 this->m_BranchCtrl.EnableWindow(FALSE);
166 this->m_UpstreamCtrl.EnableWindow(FALSE);
169 m_CommitList.m_IsIDReplaceAction = TRUE;
170 // m_CommitList.m_IsOldFirst = TRUE;
171 m_CommitList.m_IsRebaseReplaceGraph = TRUE;
173 m_CommitList.InsertGitColumn();
175 this->SetControlEnable();
177 if(m_IsCherryPick)
179 this->m_BranchCtrl.SetCurSel(-1);
180 this->m_BranchCtrl.EnableWindow(FALSE);
181 this->m_UpstreamCtrl.EnableWindow(FALSE);
182 this->SetWindowText(_T("Cherry Pick"));
183 this->m_CommitList.StartFilter();
185 }else
187 SetContinueButtonText();
188 m_CommitList.DeleteAllItems();
189 FetchLogList();
192 m_CommitList.m_ContextMenuMask &= ~(m_CommitList.GetContextMenuBit(CGitLogListBase::ID_CHERRY_PICK)|
193 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_SWITCHTOREV)|
194 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_RESET)|
195 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REVERTREV)|
196 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_TO_VERSION)|
197 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REVERTTOREV)|
198 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_COMBINE_COMMIT));
200 if(m_CommitList.m_IsOldFirst)
201 this->m_CurrentRebaseIndex = -1;
202 else
203 this->m_CurrentRebaseIndex = m_CommitList.m_logEntries.size();
206 return TRUE;
208 // CRebaseDlg message handlers
210 void CRebaseDlg::OnBnClickedPickAll()
212 // TODO: Add your control notification handler code here
213 this->UpdateData();
214 if(this->m_bPickAll)
215 this->SetAllRebaseAction(CTGitPath::LOGACTIONS_REBASE_PICK);
217 this->m_bEditAll=FALSE;
218 this->m_bSquashAll=FALSE;
219 this->UpdateData(FALSE);
223 void CRebaseDlg::OnBnClickedSquashAll()
225 // TODO: Add your control notification handler code here
226 this->UpdateData();
227 if(this->m_bSquashAll)
228 this->SetAllRebaseAction(CTGitPath::LOGACTIONS_REBASE_SQUASH);
230 this->m_bEditAll=FALSE;
231 this->m_bPickAll=FALSE;
232 this->UpdateData(FALSE);
236 void CRebaseDlg::OnBnClickedEditAll()
238 // TODO: Add your control notification handler code here
239 this->UpdateData();
240 if( this->m_bEditAll )
241 this->SetAllRebaseAction(CTGitPath::LOGACTIONS_REBASE_EDIT);
243 this->m_bPickAll=FALSE;
244 this->m_bSquashAll=FALSE;
245 this->UpdateData(FALSE);
249 void CRebaseDlg::SetAllRebaseAction(int action)
251 for(int i=0;i<this->m_CommitList.m_logEntries.size();i++)
253 m_CommitList.m_logEntries[i].m_Action=action;
255 m_CommitList.Invalidate();
258 void CRebaseDlg::OnBnClickedRebaseSplit()
260 this->UpdateData();
261 // TODO: Add your control notification handler code here
264 LRESULT CRebaseDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
266 switch (message) {
267 case WM_NOTIFY:
268 if (wParam == IDC_REBASE_SPLIT)
270 SPC_NMHDR* pHdr = (SPC_NMHDR*) lParam;
271 DoSize(pHdr->delta);
273 break;
276 return __super::DefWindowProc(message, wParam, lParam);
279 void CRebaseDlg::DoSize(int delta)
282 this->RemoveAllAnchors();
284 CSplitterControl::ChangeHeight(GetDlgItem(IDC_COMMIT_LIST), delta, CW_TOPALIGN);
285 //CSplitterControl::ChangeHeight(GetDlgItem(), delta, CW_TOPALIGN);
286 CSplitterControl::ChangeHeight(GetDlgItem(IDC_REBASE_TAB), -delta, CW_BOTTOMALIGN);
287 //CSplitterControl::ChangeHeight(GetDlgItem(), -delta, CW_BOTTOMALIGN);
288 CSplitterControl::ChangePos(GetDlgItem(IDC_SQUASH_ALL),0,delta);
289 CSplitterControl::ChangePos(GetDlgItem(IDC_PICK_ALL),0,delta);
290 CSplitterControl::ChangePos(GetDlgItem(IDC_EDIT_ALL),0,delta);
292 this->AddRebaseAnchor();
293 // adjust the minimum size of the dialog to prevent the resizing from
294 // moving the list control too far down.
295 CRect rcLogMsg;
296 m_CommitList.GetClientRect(rcLogMsg);
297 SetMinTrackSize(CSize(m_DlgOrigRect.Width(), m_DlgOrigRect.Height()-m_CommitListOrigRect.Height()+rcLogMsg.Height()));
299 SetSplitterRange();
300 // m_CommitList.Invalidate();
302 // GetDlgItem(IDC_LOGMESSAGE)->Invalidate();
304 this->m_ctrlTabCtrl.Invalidate();
305 this->m_CommitList.Invalidate();
306 this->m_FileListCtrl.Invalidate();
307 this->m_LogMessageCtrl.Invalidate();
311 void CRebaseDlg::SetSplitterRange()
313 if ((m_CommitList)&&(m_ctrlTabCtrl))
315 CRect rcTop;
316 m_CommitList.GetWindowRect(rcTop);
317 ScreenToClient(rcTop);
318 CRect rcMiddle;
319 m_ctrlTabCtrl.GetWindowRect(rcMiddle);
320 ScreenToClient(rcMiddle);
321 if (rcMiddle.Height() && rcMiddle.Width())
322 m_wndSplitter.SetRange(rcTop.top+60, rcMiddle.bottom-80);
326 void CRebaseDlg::OnSize(UINT nType,int cx, int cy)
328 // first, let the resizing take place
329 __super::OnSize(nType, cx, cy);
331 //set range
332 SetSplitterRange();
335 void CRebaseDlg::SaveSplitterPos()
337 if (!IsIconic())
339 CRegDWORD regPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RebaseDlgSizer"));
340 RECT rectSplitter;
341 m_wndSplitter.GetWindowRect(&rectSplitter);
342 ScreenToClient(&rectSplitter);
343 regPos = rectSplitter.top;
347 void CRebaseDlg::LoadBranchInfo()
349 m_BranchCtrl.SetMaxHistoryItems(0x7FFFFFFF);
350 m_UpstreamCtrl.SetMaxHistoryItems(0x7FFFFFFF);
352 STRING_VECTOR list;
353 list.clear();
354 int current;
355 g_Git.GetBranchList(list,&current,CGit::BRANCH_ALL);
356 m_BranchCtrl.AddString(list);
357 m_UpstreamCtrl.AddString(list);
359 m_BranchCtrl.SetCurSel(current);
361 AddBranchToolTips(&m_BranchCtrl);
362 AddBranchToolTips(&m_UpstreamCtrl);
364 if(!m_Upstream.IsEmpty())
366 m_UpstreamCtrl.AddString(m_Upstream);
367 m_UpstreamCtrl.SetCurSel(m_UpstreamCtrl.GetCount()-1);
371 void CRebaseDlg::OnCbnSelchangeBranch()
373 FetchLogList();
376 void CRebaseDlg::OnCbnSelchangeUpstream()
378 FetchLogList();
381 void CRebaseDlg::FetchLogList()
383 m_CommitList.Clear();
384 this->m_CommitList.FillGitLog(NULL,0,&m_UpstreamCtrl.GetString(),&m_BranchCtrl.GetString());
385 if( m_CommitList.GetItemCount() == 0 )
386 m_CommitList.ShowText(_T("Nothing Rebase"));
388 CString hash=g_Git.GetHash(m_UpstreamCtrl.GetString());
390 #if 0
391 if(m_CommitList.m_logEntries[m_CommitList.m_logEntries.size()-1].m_ParentHash.size() >=0 )
393 if(hash == m_CommitList.m_logEntries[m_CommitList.m_logEntries.size()-1].m_ParentHash[0])
395 m_CommitList.Clear();
396 m_CommitList.ShowText(_T("Nothing Rebase"));
399 #endif
401 m_tooltips.Pop();
402 AddBranchToolTips(&this->m_BranchCtrl);
403 AddBranchToolTips(&this->m_UpstreamCtrl);
405 for(int i=0;i<m_CommitList.m_logEntries.size();i++)
407 m_CommitList.m_logEntries[i].m_Action = CTGitPath::LOGACTIONS_REBASE_PICK;
410 m_CommitList.Invalidate();
412 if(m_CommitList.m_IsOldFirst)
413 this->m_CurrentRebaseIndex = -1;
414 else
415 this->m_CurrentRebaseIndex = m_CommitList.m_logEntries.size();
419 void CRebaseDlg::AddBranchToolTips(CHistoryCombo *pBranch)
421 if(pBranch)
423 CString text=pBranch->GetString();
424 CString tooltip;
425 BYTE_VECTOR data;
426 g_Git.GetLog(data,text,NULL,1,0);
427 GitRev rev;
428 rev.ParserFromLog(data);
429 tooltip.Format(_T("CommitHash:%s\nCommit by: %s %s\n <b>%s</b> \n %s"),
430 rev.m_CommitHash,
431 rev.m_AuthorName,
432 CAppUtils::FormatDateAndTime(rev.m_AuthorDate,DATE_LONGDATE),
433 rev.m_Subject,
434 rev.m_Body);
436 pBranch->DisableTooltip();
437 this->m_tooltips.AddTool(pBranch->GetComboBoxCtrl(),tooltip);
441 BOOL CRebaseDlg::PreTranslateMessage(MSG*pMsg)
443 m_tooltips.RelayEvent(pMsg);
444 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
446 int CRebaseDlg::CheckRebaseCondition()
448 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
450 if( !g_Git.CheckCleanWorkTree() )
452 CMessageBox::Show(NULL,_T("Rebase Need Clean Working Tree"),_T("TortoiseGit"),MB_OK);
453 return -1;
455 //Todo Check $REBASE_ROOT
456 //Todo Check $DOTEST
458 CString cmd;
459 cmd=_T("git.exe var GIT_COMMITTER_IDENT");
460 if(g_Git.Run(cmd,NULL,CP_UTF8))
461 return -1;
463 //Todo call pre_rebase_hook
464 return 0;
466 int CRebaseDlg::StartRebase()
468 CString cmd,out;
470 if(!this->m_IsCherryPick)
472 //Todo call comment_for_reflog
473 cmd.Format(_T("git.exe checkout %s"),this->m_BranchCtrl.GetString());
474 this->AddLogString(cmd);
476 if(g_Git.Run(cmd,&out,CP_UTF8))
477 return -1;
479 this->AddLogString(out);
482 cmd=_T("git.exe rev-parse --verify HEAD");
483 if(g_Git.Run(cmd,&out,CP_UTF8))
485 AddLogString(_T("No Head"));
486 return -1;
488 //Todo
489 //git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null ||
490 // echo "detached HEAD" > "$DOTEST"/head-name
492 cmd.Format(_T("git.exe update-ref ORIG_HEAD HEAD"));
493 if(g_Git.Run(cmd,&out,CP_UTF8))
495 AddLogString(_T("update ORIG_HEAD Fail"));
496 return -1;
499 if( !this->m_IsCherryPick )
501 cmd.Format(_T("git.exe checkout %s"),this->m_UpstreamCtrl.GetString());
502 this->AddLogString(cmd);
504 out.Empty();
505 if(g_Git.Run(cmd,&out,CP_UTF8))
507 return -1;
511 m_OrigUpstreamHash.Empty();
512 m_OrigUpstreamHash= g_Git.GetHash(this->m_UpstreamCtrl.GetString());
513 if(m_OrigUpstreamHash.IsEmpty())
515 this->AddLogString(m_OrigUpstreamHash);
516 return -1;
519 if( !this->m_IsCherryPick )
521 cmd.Format(_T("git.exe rev-parse %s"),this->m_BranchCtrl.GetString());
522 if(g_Git.Run(cmd,&this->m_OrigBranchHash,CP_UTF8))
524 this->AddLogString(m_OrigBranchHash);
525 return -1;
527 this->AddLogString(_T("Start Rebase\r\n"));
529 }else
530 this->AddLogString(_T("Start Cherry-pick\r\n"));
532 return 0;
534 int CRebaseDlg::VerifyNoConflict()
536 CTGitPathList list;
537 if(g_Git.ListConflictFile(list))
539 AddLogString(_T("Get conflict files fail"));
540 return -1;
542 if( list.GetCount() != 0 )
544 CMessageBox::Show(NULL,_T("There are conflict file, you should mark it resolve"),_T("TortoiseGit"),MB_OK);
545 return -1;
547 return 0;
550 int CRebaseDlg::FinishRebase()
552 if(this->m_IsCherryPick) //cherry pick mode no "branch", working at upstream branch
553 return 0;
555 CString cmd,out;
556 cmd.Format(_T("git.exe branch -f %s"),this->m_BranchCtrl.GetString());
557 if(g_Git.Run(cmd,&out,CP_UTF8))
559 AddLogString(out);
560 return -1;
562 out.Empty();
563 cmd.Format(_T("git.exe reset --hard %s"),this->m_OrigUpstreamHash);
564 if(g_Git.Run(cmd,&out,CP_UTF8))
566 AddLogString(out);
567 return -1;
569 out.Empty();
570 cmd.Format(_T("git.exe checkout -f %s"),this->m_BranchCtrl.GetString());
571 if(g_Git.Run(cmd,&out,CP_UTF8))
573 AddLogString(out);
574 return -1;
576 return 0;
578 void CRebaseDlg::OnBnClickedContinue()
580 if( m_RebaseStage == CHOOSE_BRANCH|| m_RebaseStage == CHOOSE_COMMIT_PICK_MODE )
582 if(CheckRebaseCondition())
583 return ;
584 m_RebaseStage = REBASE_START;
587 if( m_RebaseStage == REBASE_DONE)
589 OnOK();
592 if( m_RebaseStage == REBASE_FINISH )
594 if(FinishRebase())
595 return ;
597 OnOK();
600 if( m_RebaseStage == REBASE_SQUASH_CONFLICT)
602 if(VerifyNoConflict())
603 return;
604 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
605 if(this->CheckNextCommitIsSquash())
606 {//next commit is not squash;
607 m_RebaseStage = REBASE_SQUASH_EDIT;
608 this->OnRebaseUpdateUI(0,0);
609 this->UpdateCurrentStatus();
610 return ;
613 m_RebaseStage=REBASE_CONTINUE;
614 curRev->m_Action|=CTGitPath::LOGACTIONS_REBASE_DONE;
615 this->UpdateCurrentStatus();
619 if( m_RebaseStage == REBASE_CONFLICT )
621 if(VerifyNoConflict())
622 return;
624 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
626 CString out =_T("");
627 CString cmd;
628 cmd.Format(_T("git.exe commit -C %s"), curRev->m_CommitHash);
630 if(g_Git.Run(cmd,&out,CP_UTF8))
632 if(!g_Git.CheckCleanWorkTree())
634 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
635 return;
639 AddLogString(out);
640 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
641 if( curRev->m_Action & CTGitPath::LOGACTIONS_REBASE_EDIT)
643 m_RebaseStage=REBASE_EDIT;
644 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_MESSAGE);
645 this->UpdateCurrentStatus();
646 return;
648 else
650 m_RebaseStage=REBASE_CONTINUE;
651 curRev->m_Action|=CTGitPath::LOGACTIONS_REBASE_DONE;
652 this->UpdateCurrentStatus();
657 if( m_RebaseStage == REBASE_EDIT || m_RebaseStage == REBASE_SQUASH_EDIT )
659 CString str;
660 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
662 str=this->m_LogMessageCtrl.GetText();
663 if(str.Trim().IsEmpty())
665 CMessageBox::Show(NULL,_T("Commit Message Is Empty"),_T("TortoiseGit"),MB_OK|MB_ICONERROR);
666 return;
669 CString tempfile=::GetTempFile();
670 CFile file(tempfile,CFile::modeReadWrite|CFile::modeCreate );
671 CStringA log=CUnicodeUtils::GetUTF8( str);
672 file.Write(log,log.GetLength());
673 //file.WriteString(m_sLogMessage);
674 file.Close();
676 CString out,cmd;
678 if( m_RebaseStage == REBASE_SQUASH_EDIT )
679 cmd.Format(_T("git.exe commit -F \"%s\""), tempfile);
680 else
681 cmd.Format(_T("git.exe commit --amend -F \"%s\""), tempfile);
683 if(g_Git.Run(cmd,&out,CP_UTF8))
685 if(!g_Git.CheckCleanWorkTree())
687 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
688 return;
692 CFile::Remove(tempfile);
693 AddLogString(out);
694 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
695 m_RebaseStage=REBASE_CONTINUE;
696 curRev->m_Action|=CTGitPath::LOGACTIONS_REBASE_DONE;
697 this->UpdateCurrentStatus();
701 InterlockedExchange(&m_bThreadRunning, TRUE);
702 SetControlEnable();
704 if (AfxBeginThread(RebaseThreadEntry, this)==NULL)
706 InterlockedExchange(&m_bThreadRunning, FALSE);
707 CMessageBox::Show(NULL, _T("Create Rebase Thread Fail"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
708 SetControlEnable();
711 int CRebaseDlg::CheckNextCommitIsSquash()
713 int index;
714 if(m_CommitList.m_IsOldFirst)
715 index=m_CurrentRebaseIndex+1;
716 else
717 index=m_CurrentRebaseIndex-1;
719 GitRev *curRev;
722 if(index<0)
723 return -1;
724 if(index>= m_CommitList.GetItemCount())
725 return -1;
727 curRev=(GitRev*)m_CommitList.m_arShownList[index];
729 if( curRev->m_Action&CTGitPath::LOGACTIONS_REBASE_SQUASH )
730 return 0;
731 if( curRev->m_Action&CTGitPath::LOGACTIONS_REBASE_SKIP)
733 if(m_CommitList.m_IsOldFirst)
734 index++;
735 else
736 index--;
737 }else
738 return -1;
740 }while(curRev->m_Action&CTGitPath::LOGACTIONS_REBASE_SKIP);
742 return -1;
745 int CRebaseDlg::GoNext()
747 if(m_CommitList.m_IsOldFirst)
748 m_CurrentRebaseIndex++;
749 else
750 m_CurrentRebaseIndex--;
751 return 0;
754 int CRebaseDlg::StateAction()
756 switch(this->m_RebaseStage)
758 case CHOOSE_BRANCH:
759 case CHOOSE_COMMIT_PICK_MODE:
760 if(StartRebase())
761 return -1;
762 m_RebaseStage = REBASE_START;
763 GoNext();
764 break;
767 return 0;
769 void CRebaseDlg::SetContinueButtonText()
771 CString Text;
772 switch(this->m_RebaseStage)
774 case CHOOSE_BRANCH:
775 case CHOOSE_COMMIT_PICK_MODE:
776 Text = _T("Start");
777 break;
779 case REBASE_START:
780 case REBASE_CONTINUE:
781 case REBASE_SQUASH_CONFLICT:
782 Text = _T("Continue");
783 break;
785 case REBASE_CONFLICT:
786 Text = _T("Commit");
787 break;
788 case REBASE_EDIT:
789 Text = _T("Amend");
790 break;
792 case REBASE_SQUASH_EDIT:
793 Text = _T("Commit");
794 break;
796 case REBASE_ABORT:
797 case REBASE_FINISH:
798 Text = _T("Finish");
799 break;
801 case REBASE_DONE:
802 Text = _T("Done");
803 break;
805 this->GetDlgItem(IDC_REBASE_CONTINUE)->SetWindowText(Text);
808 void CRebaseDlg::SetControlEnable()
810 switch(this->m_RebaseStage)
812 case CHOOSE_BRANCH:
813 case CHOOSE_COMMIT_PICK_MODE:
815 this->GetDlgItem(IDC_PICK_ALL)->EnableWindow(TRUE);
816 this->GetDlgItem(IDC_EDIT_ALL)->EnableWindow(TRUE);
817 this->GetDlgItem(IDC_SQUASH_ALL)->EnableWindow(TRUE);
818 if(!m_IsCherryPick)
820 this->GetDlgItem(IDC_REBASE_COMBOXEX_BRANCH)->EnableWindow(TRUE);
821 this->GetDlgItem(IDC_REBASE_COMBOXEX_UPSTREAM)->EnableWindow(TRUE);
823 //this->m_CommitList.m_IsEnableRebaseMenu=TRUE;
824 this->m_CommitList.m_ContextMenuMask |= m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_PICK)|
825 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SQUASH)|
826 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_EDIT)|
827 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SKIP);
828 break;
830 case REBASE_START:
831 case REBASE_CONTINUE:
832 case REBASE_ABORT:
833 case REBASE_FINISH:
834 case REBASE_CONFLICT:
835 case REBASE_EDIT:
836 case REBASE_SQUASH_CONFLICT:
837 case REBASE_DONE:
838 this->GetDlgItem(IDC_PICK_ALL)->EnableWindow(FALSE);
839 this->GetDlgItem(IDC_EDIT_ALL)->EnableWindow(FALSE);
840 this->GetDlgItem(IDC_SQUASH_ALL)->EnableWindow(FALSE);
841 this->GetDlgItem(IDC_REBASE_COMBOXEX_BRANCH)->EnableWindow(FALSE);
842 this->GetDlgItem(IDC_REBASE_COMBOXEX_UPSTREAM)->EnableWindow(FALSE);
843 //this->m_CommitList.m_IsEnableRebaseMenu=FALSE;
844 this->m_CommitList.m_ContextMenuMask &= ~(m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_PICK)|
845 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SQUASH)|
846 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_EDIT)|
847 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SKIP));
848 break;
851 if(m_bThreadRunning)
853 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(FALSE);
854 this->GetDlgItem(IDC_REBASE_ABORT)->EnableWindow(FALSE);
856 }else
858 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(TRUE);
859 this->GetDlgItem(IDC_REBASE_ABORT)->EnableWindow(TRUE);
863 void CRebaseDlg::UpdateProgress()
865 int index;
866 CRect rect;
868 if(m_CommitList.m_IsOldFirst)
869 index = m_CurrentRebaseIndex+1;
870 else
871 index = m_CommitList.GetItemCount()-m_CurrentRebaseIndex;
873 m_ProgressBar.SetRange(1,m_CommitList.GetItemCount());
874 m_ProgressBar.SetPos(index);
876 if(m_CurrentRebaseIndex>=0 && m_CurrentRebaseIndex< m_CommitList.GetItemCount())
878 CString text;
879 text.Format(_T("Rebasing...(%d/%d)"),index,m_CommitList.GetItemCount());
880 m_CtrlStatusText.SetWindowText(text);
884 GitRev *prevRev=NULL, *curRev=NULL;
886 if( m_CurrentRebaseIndex >= 0 && m_CurrentRebaseIndex< m_CommitList.m_arShownList.GetSize())
888 curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
891 for(int i=0;i<m_CommitList.m_arShownList.GetSize();i++)
893 prevRev=(GitRev*)m_CommitList.m_arShownList[i];
894 if(prevRev->m_Action & CTGitPath::LOGACTIONS_REBASE_CURRENT)
896 prevRev->m_Action &= ~ CTGitPath::LOGACTIONS_REBASE_CURRENT;
897 m_CommitList.GetItemRect(i,&rect,LVIR_BOUNDS);
898 m_CommitList.InvalidateRect(rect);
902 if(curRev)
904 curRev->m_Action |= CTGitPath::LOGACTIONS_REBASE_CURRENT;
905 m_CommitList.GetItemRect(m_CurrentRebaseIndex,&rect,LVIR_BOUNDS);
906 m_CommitList.InvalidateRect(rect);
908 m_CommitList.EnsureVisible(m_CurrentRebaseIndex,FALSE);
912 void CRebaseDlg::UpdateCurrentStatus()
914 if( m_CurrentRebaseIndex < 0 && m_RebaseStage!= REBASE_DONE)
916 if(m_CommitList.m_IsOldFirst)
917 m_RebaseStage = CRebaseDlg::REBASE_START;
918 else
919 m_RebaseStage = CRebaseDlg::REBASE_FINISH;
922 if( m_CurrentRebaseIndex == m_CommitList.m_arShownList.GetSize() && m_RebaseStage!= REBASE_DONE)
924 if(m_CommitList.m_IsOldFirst)
925 m_RebaseStage = CRebaseDlg::REBASE_DONE;
926 else
927 m_RebaseStage = CRebaseDlg::REBASE_FINISH;
930 SetContinueButtonText();
931 SetControlEnable();
932 UpdateProgress();
935 void CRebaseDlg::AddLogString(CString str)
937 this->m_wndOutputRebase.SendMessage(SCI_SETREADONLY, FALSE);
938 CStringA sTextA = m_wndOutputRebase.StringForControl(str);//CUnicodeUtils::GetUTF8(str);
939 this->m_wndOutputRebase.SendMessage(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)sTextA);
940 this->m_wndOutputRebase.SendMessage(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)"\n");
941 this->m_wndOutputRebase.SendMessage(SCI_SETREADONLY, TRUE);
944 int CRebaseDlg::GetCurrentCommitID()
946 if(m_CommitList.m_IsOldFirst)
948 return this->m_CurrentRebaseIndex+1;
950 }else
952 return m_CommitList.GetItemCount()-m_CurrentRebaseIndex;
956 int CRebaseDlg::DoRebase()
958 CString cmd,out;
959 if(m_CurrentRebaseIndex <0)
960 return 0;
961 if(m_CurrentRebaseIndex >= m_CommitList.GetItemCount() )
962 return 0;
964 GitRev *pRev = (GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
965 int mode=pRev->m_Action & CTGitPath::LOGACTIONS_REBASE_MODE_MASK;
966 CString nocommit;
968 if( mode== CTGitPath::LOGACTIONS_REBASE_SKIP)
970 pRev->m_Action|= CTGitPath::LOGACTIONS_REBASE_DONE;
971 return 0;
974 if( mode != CTGitPath::LOGACTIONS_REBASE_PICK )
976 this->m_SquashMessage+= pRev->m_Subject;
977 this->m_SquashMessage+= _T("\n");
978 this->m_SquashMessage+= pRev->m_Body;
980 else
981 this->m_SquashMessage.Empty();
983 if(mode == CTGitPath::LOGACTIONS_REBASE_SQUASH)
984 nocommit=_T(" --no-commit ");
986 CString log;
987 log.Format(_T("%s %d:%s"),CTGitPath::GetActionName(mode),this->GetCurrentCommitID(),pRev->m_CommitHash);
988 AddLogString(log);
989 AddLogString(pRev->m_Subject);
990 cmd.Format(_T("git.exe cherry-pick %s %s"),nocommit,pRev->m_CommitHash);
992 if(g_Git.Run(cmd,&out,CP_UTF8))
994 AddLogString(out);
995 CTGitPathList list;
996 if(g_Git.ListConflictFile(list))
998 AddLogString(_T("Get conflict files fail"));
999 return -1;
1001 if(list.GetCount() == 0 )
1003 if(mode == CTGitPath::LOGACTIONS_REBASE_PICK)
1005 pRev->m_Action|= CTGitPath::LOGACTIONS_REBASE_DONE;
1006 return 0;
1008 if(mode == CTGitPath::LOGACTIONS_REBASE_EDIT)
1010 this->m_RebaseStage = REBASE_EDIT ;
1011 return -1; // Edit return -1 to stop rebase.
1013 // Squash Case
1014 if(CheckNextCommitIsSquash())
1015 { // no squash
1016 // let user edit last commmit message
1017 this->m_RebaseStage = REBASE_SQUASH_EDIT;
1018 return -1;
1021 if(mode == CTGitPath::LOGACTIONS_REBASE_SQUASH)
1022 m_RebaseStage = REBASE_SQUASH_CONFLICT;
1023 else
1024 m_RebaseStage = REBASE_CONFLICT;
1025 return -1;
1027 }else
1029 AddLogString(out);
1030 if(mode == CTGitPath::LOGACTIONS_REBASE_PICK)
1032 pRev->m_Action|= CTGitPath::LOGACTIONS_REBASE_DONE;
1033 return 0;
1035 if(mode == CTGitPath::LOGACTIONS_REBASE_EDIT)
1037 this->m_RebaseStage = REBASE_EDIT ;
1038 return -1; // Edit return -1 to stop rebase.
1041 // Squash Case
1042 if(CheckNextCommitIsSquash())
1043 { // no squash
1044 // let user edit last commmit message
1045 this->m_RebaseStage = REBASE_SQUASH_EDIT;
1046 return -1;
1050 return 0;
1053 BOOL CRebaseDlg::IsEnd()
1055 if(m_CommitList.m_IsOldFirst)
1056 return m_CurrentRebaseIndex>= this->m_CommitList.GetItemCount();
1057 else
1058 return m_CurrentRebaseIndex<0;
1061 int CRebaseDlg::RebaseThread()
1063 int ret=0;
1064 while(1)
1066 if( m_RebaseStage == REBASE_START )
1068 if( this->StartRebase() )
1070 InterlockedExchange(&m_bThreadRunning, FALSE);
1071 ret = -1;
1072 break;
1074 m_RebaseStage = REBASE_CONTINUE;
1076 }else if( m_RebaseStage == REBASE_CONTINUE )
1078 this->GoNext();
1079 if(IsEnd())
1081 ret = 0;
1082 m_RebaseStage = REBASE_FINISH;
1084 }else
1086 ret = DoRebase();
1088 if( ret )
1090 break;
1094 }else if( m_RebaseStage == REBASE_FINISH )
1096 FinishRebase();
1097 m_RebaseStage = REBASE_DONE;
1098 break;
1100 }else
1102 break;
1104 this->PostMessage(MSG_REBASE_UPDATE_UI);
1105 //this->UpdateCurrentStatus();
1108 InterlockedExchange(&m_bThreadRunning, FALSE);
1109 this->PostMessage(MSG_REBASE_UPDATE_UI);
1110 return ret;
1113 void CRebaseDlg::ListConflictFile()
1115 this->m_FileListCtrl.Clear();
1116 CTGitPathList list;
1117 CTGitPath path;
1118 list.AddPath(path);
1120 this->m_FileListCtrl.GetStatus(&list,true);
1121 this->m_FileListCtrl.Show(CTGitPath::LOGACTIONS_UNMERGED|CTGitPath::LOGACTIONS_MODIFIED|CTGitPath::LOGACTIONS_ADDED|CTGitPath::LOGACTIONS_DELETED,
1122 CTGitPath::LOGACTIONS_UNMERGED);
1123 if( this->m_FileListCtrl.GetItemCount() == 0 )
1129 LRESULT CRebaseDlg::OnRebaseUpdateUI(WPARAM,LPARAM)
1131 UpdateCurrentStatus();
1132 if(m_CurrentRebaseIndex <0)
1133 return 0;
1134 if(m_CurrentRebaseIndex >= m_CommitList.GetItemCount() )
1135 return 0;
1136 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1138 switch(m_RebaseStage)
1140 case REBASE_CONFLICT:
1141 case REBASE_SQUASH_CONFLICT:
1142 ListConflictFile();
1143 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_CONFLICT);
1144 this->m_LogMessageCtrl.SetText(curRev->m_Subject+_T("\n")+curRev->m_Body);
1145 break;
1146 case REBASE_EDIT:
1147 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_MESSAGE);
1148 this->m_LogMessageCtrl.SetText(curRev->m_Subject+_T("\n")+curRev->m_Body);
1149 break;
1150 case REBASE_SQUASH_EDIT:
1151 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_MESSAGE);
1152 this->m_LogMessageCtrl.SetText(this->m_SquashMessage);
1153 break;
1154 default:
1155 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1157 return 0;
1159 void CRebaseDlg::OnCancel()
1161 OnBnClickedAbort();
1163 void CRebaseDlg::OnBnClickedAbort()
1165 CString cmd,out;
1166 if(m_OrigUpstreamHash.IsEmpty())
1168 __super::OnCancel();
1171 if(m_RebaseStage == CHOOSE_BRANCH || m_RebaseStage== CHOOSE_COMMIT_PICK_MODE)
1173 return;
1176 if(CMessageBox::Show(NULL,_T("Are you sure abort rebase"),_T("TortoiseGit"),MB_YESNO) != IDYES)
1177 return;
1179 cmd.Format(_T("git.exe checkout -f %s"),this->m_UpstreamCtrl.GetString());
1180 if(g_Git.Run(cmd,&out,CP_UTF8))
1182 AddLogString(out);
1183 return ;
1186 cmd.Format(_T("git.exe reset --hard %s"),this->m_OrigUpstreamHash.Left(40));
1187 if(g_Git.Run(cmd,&out,CP_UTF8))
1189 AddLogString(out);
1190 return ;
1193 if(this->m_IsCherryPick) //there are not "branch" at cherry pick mode
1194 return;
1196 cmd.Format(_T("git checkout -f %s"),this->m_BranchCtrl.GetString());
1197 if(g_Git.Run(cmd,&out,CP_UTF8))
1199 AddLogString(out);
1200 return ;
1203 cmd.Format(_T("git.exe reset --hard %s"),this->m_OrigBranchHash.Left(40));
1204 if(g_Git.Run(cmd,&out,CP_UTF8))
1206 AddLogString(out);
1207 return ;
1209 __super::OnCancel();
1212 void CRebaseDlg::OnBnClickedButtonBrowse()
1214 CString origRef;
1215 m_UpstreamCtrl.GetLBText(m_UpstreamCtrl.GetCurSel(), origRef);
1216 CString resultRef = CBrowseRefsDlg::PickRef(false,origRef,gPickRef_NoTag);
1217 if(resultRef.IsEmpty())
1218 return;
1219 if(wcsncmp(resultRef,L"refs/",5)==0)
1220 resultRef = resultRef.Mid(5);
1221 if(wcsncmp(resultRef,L"heads/",6)==0)
1222 resultRef = resultRef.Mid(6);
1223 m_UpstreamCtrl.SetCurSel(m_UpstreamCtrl.FindStringExact(0,resultRef));