Do not use GitAdminDir objects
[TortoiseGit.git] / src / TortoiseProc / RebaseDlg.cpp
blobe35b5e73be00adc10301bdf224c5619b80973ddf
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2015 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 // RebaseDlg.cpp : implementation file
23 #include "stdafx.h"
24 #include "TortoiseProc.h"
25 #include "RebaseDlg.h"
26 #include "AppUtils.h"
27 #include "LoglistUtils.h"
28 #include "MessageBox.h"
29 #include "UnicodeUtils.h"
30 #include "BrowseRefsDlg.h"
31 #include "ProgressDlg.h"
32 #include "SmartHandle.h"
33 #include "../TGitCache/CacheInterface.h"
34 #include "Settings\Settings.h"
35 #include "MassiveGitTask.h"
36 #include "CommitDlg.h"
38 // CRebaseDlg dialog
40 IMPLEMENT_DYNAMIC(CRebaseDlg, CResizableStandAloneDialog)
42 CRebaseDlg::CRebaseDlg(CWnd* pParent /*=NULL*/)
43 : CResizableStandAloneDialog(CRebaseDlg::IDD, pParent)
44 , m_bAddCherryPickedFrom(FALSE)
45 , m_bStatusWarning(false)
46 , m_bAutoSkipFailedCommit(FALSE)
47 , m_bFinishedRebase(false)
48 , m_bStashed(false)
49 , m_bSplitCommit(FALSE)
51 m_RebaseStage=CHOOSE_BRANCH;
52 m_CurrentRebaseIndex=-1;
53 m_bThreadRunning =FALSE;
54 this->m_IsCherryPick = FALSE;
55 m_bForce=FALSE;
56 m_IsFastForward=FALSE;
59 CRebaseDlg::~CRebaseDlg()
63 void CRebaseDlg::DoDataExchange(CDataExchange* pDX)
65 CDialog::DoDataExchange(pDX);
66 DDX_Control(pDX, IDC_REBASE_PROGRESS, m_ProgressBar);
67 DDX_Control(pDX, IDC_STATUS_STATIC, m_CtrlStatusText);
68 DDX_Control(pDX, IDC_REBASE_SPLIT, m_wndSplitter);
69 DDX_Control(pDX,IDC_COMMIT_LIST,m_CommitList);
70 DDX_Control(pDX,IDC_REBASE_COMBOXEX_BRANCH, this->m_BranchCtrl);
71 DDX_Control(pDX,IDC_REBASE_COMBOXEX_UPSTREAM, this->m_UpstreamCtrl);
72 DDX_Check(pDX, IDC_REBASE_CHECK_FORCE,m_bForce);
73 DDX_Check(pDX, IDC_CHECK_CHERRYPICKED_FROM, m_bAddCherryPickedFrom);
74 DDX_Control(pDX,IDC_REBASE_POST_BUTTON,m_PostButton);
75 DDX_Control(pDX, IDC_SPLITALLOPTIONS, m_SplitAllOptions);
76 DDX_Check(pDX, IDC_REBASE_SPLIT_COMMIT, m_bSplitCommit);
80 BEGIN_MESSAGE_MAP(CRebaseDlg, CResizableStandAloneDialog)
81 ON_BN_CLICKED(IDC_REBASE_SPLIT, &CRebaseDlg::OnBnClickedRebaseSplit)
82 ON_BN_CLICKED(IDC_REBASE_CONTINUE,OnBnClickedContinue)
83 ON_BN_CLICKED(IDC_REBASE_ABORT, OnBnClickedAbort)
84 ON_WM_SIZE()
85 ON_CBN_SELCHANGE(IDC_REBASE_COMBOXEX_BRANCH, &CRebaseDlg::OnCbnSelchangeBranch)
86 ON_CBN_SELCHANGE(IDC_REBASE_COMBOXEX_UPSTREAM, &CRebaseDlg::OnCbnSelchangeUpstream)
87 ON_MESSAGE(MSG_REBASE_UPDATE_UI, OnRebaseUpdateUI)
88 ON_REGISTERED_MESSAGE(CGitStatusListCtrl::GITSLNM_NEEDSREFRESH, OnGitStatusListCtrlNeedsRefresh)
89 ON_BN_CLICKED(IDC_BUTTON_REVERSE, OnBnClickedButtonReverse)
90 ON_BN_CLICKED(IDC_BUTTON_BROWSE, &CRebaseDlg::OnBnClickedButtonBrowse)
91 ON_BN_CLICKED(IDC_REBASE_CHECK_FORCE, &CRebaseDlg::OnBnClickedRebaseCheckForce)
92 ON_BN_CLICKED(IDC_CHECK_CHERRYPICKED_FROM, &CRebaseDlg::OnBnClickedCheckCherryPickedFrom)
93 ON_BN_CLICKED(IDC_REBASE_POST_BUTTON, &CRebaseDlg::OnBnClickedRebasePostButton)
94 ON_BN_CLICKED(IDC_BUTTON_UP2, &CRebaseDlg::OnBnClickedButtonUp2)
95 ON_BN_CLICKED(IDC_BUTTON_DOWN2, &CRebaseDlg::OnBnClickedButtonDown2)
96 ON_REGISTERED_MESSAGE(WM_TASKBARBTNCREATED, OnTaskbarBtnCreated)
97 ON_NOTIFY(LVN_ITEMCHANGED, IDC_COMMIT_LIST, OnLvnItemchangedLoglist)
98 ON_REGISTERED_MESSAGE(CGitLogListBase::m_RebaseActionMessage, OnRebaseActionMessage)
99 ON_WM_CTLCOLOR()
100 ON_BN_CLICKED(IDC_SPLITALLOPTIONS, &CRebaseDlg::OnBnClickedSplitAllOptions)
101 ON_BN_CLICKED(IDC_REBASE_SPLIT_COMMIT, &CRebaseDlg::OnBnClickedRebaseSplitCommit)
102 ON_BN_CLICKED(IDC_BUTTON_ONTO, &CRebaseDlg::OnBnClickedButtonOnto)
103 END_MESSAGE_MAP()
105 void CRebaseDlg::AddRebaseAnchor()
107 AddAnchor(IDC_REBASE_TAB,TOP_LEFT,BOTTOM_RIGHT);
108 AddAnchor(IDC_COMMIT_LIST,TOP_LEFT, TOP_RIGHT);
109 AddAnchor(IDC_REBASE_SPLIT,TOP_LEFT, TOP_RIGHT);
110 AddAnchor(IDC_STATUS_STATIC, BOTTOM_LEFT,BOTTOM_RIGHT);
111 AddAnchor(IDC_REBASE_CONTINUE,BOTTOM_RIGHT);
112 AddAnchor(IDC_REBASE_ABORT, BOTTOM_RIGHT);
113 AddAnchor(IDC_REBASE_PROGRESS,BOTTOM_LEFT, BOTTOM_RIGHT);
114 AddAnchor(IDC_SPLITALLOPTIONS, TOP_LEFT);
115 AddAnchor(IDC_BUTTON_UP2,TOP_LEFT);
116 AddAnchor(IDC_BUTTON_DOWN2,TOP_LEFT);
117 AddAnchor(IDC_REBASE_COMBOXEX_UPSTREAM,TOP_LEFT);
118 AddAnchor(IDC_REBASE_COMBOXEX_BRANCH,TOP_LEFT);
119 AddAnchor(IDC_REBASE_STATIC_UPSTREAM,TOP_LEFT);
120 AddAnchor(IDC_REBASE_STATIC_BRANCH,TOP_LEFT);
121 AddAnchor(IDHELP, BOTTOM_RIGHT);
122 AddAnchor(IDC_REBASE_CHECK_FORCE,TOP_RIGHT);
123 AddAnchor(IDC_CHECK_CHERRYPICKED_FROM, TOP_RIGHT);
124 AddAnchor(IDC_REBASE_SPLIT_COMMIT, BOTTOM_RIGHT);
125 AddAnchor(IDC_REBASE_POST_BUTTON,BOTTOM_LEFT);
127 this->AddOthersToAnchor();
130 BOOL CRebaseDlg::OnInitDialog()
132 CResizableStandAloneDialog::OnInitDialog();
133 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
135 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
136 // do this, Explorer would be unable to send that message to our window if we
137 // were running elevated. It's OK to make the call all the time, since if we're
138 // not elevated, this is a no-op.
139 CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) };
140 typedef BOOL STDAPICALLTYPE ChangeWindowMessageFilterExDFN(HWND hWnd, UINT message, DWORD action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
141 CAutoLibrary hUser = AtlLoadSystemLibraryUsingFullPath(_T("user32.dll"));
142 if (hUser)
144 ChangeWindowMessageFilterExDFN *pfnChangeWindowMessageFilterEx = (ChangeWindowMessageFilterExDFN*)GetProcAddress(hUser, "ChangeWindowMessageFilterEx");
145 if (pfnChangeWindowMessageFilterEx)
147 pfnChangeWindowMessageFilterEx(m_hWnd, WM_TASKBARBTNCREATED, MSGFLT_ALLOW, &cfs);
150 m_pTaskbarList.Release();
151 if (FAILED(m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList)))
152 m_pTaskbarList = nullptr;
154 CRect rectDummy;
155 //IDC_REBASE_DUMY_TAB
157 GetClientRect(m_DlgOrigRect);
158 m_CommitList.GetClientRect(m_CommitListOrigRect);
160 CWnd *pwnd=this->GetDlgItem(IDC_REBASE_DUMY_TAB);
161 pwnd->GetWindowRect(&rectDummy);
162 this->ScreenToClient(rectDummy);
164 if (!m_ctrlTabCtrl.Create(CMFCTabCtrl::STYLE_FLAT, rectDummy, this, IDC_REBASE_TAB))
166 TRACE0("Failed to create output tab window\n");
167 return FALSE; // fail to create
169 m_ctrlTabCtrl.SetResizeMode(CMFCTabCtrl::RESIZE_NO);
170 // Create output panes:
171 //const DWORD dwStyle = LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL;
172 DWORD dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
174 if (! this->m_FileListCtrl.Create(dwStyle,rectDummy,&this->m_ctrlTabCtrl,0) )
176 TRACE0("Failed to create output windows\n");
177 return FALSE; // fail to create
179 m_FileListCtrl.m_hwndLogicalParent = this;
181 if( ! this->m_LogMessageCtrl.Create(_T("Scintilla"),_T("source"),0,rectDummy,&m_ctrlTabCtrl,0,0) )
183 TRACE0("Failed to create log message control");
184 return FALSE;
186 m_ProjectProperties.ReadProps();
187 m_LogMessageCtrl.Init(m_ProjectProperties);
188 m_LogMessageCtrl.SetFont((CString)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")), (DWORD)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8));
189 m_LogMessageCtrl.Call(SCI_SETREADONLY, TRUE);
191 dwStyle = LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL;
193 if (!m_wndOutputRebase.Create(_T("Scintilla"),_T("source"),0,rectDummy, &m_ctrlTabCtrl, 0,0) )
195 TRACE0("Failed to create output windows\n");
196 return -1; // fail to create
198 m_wndOutputRebase.Init(0, FALSE);
199 m_wndOutputRebase.Call(SCI_SETREADONLY, TRUE);
201 m_tooltips.Create(this);
203 m_tooltips.AddTool(IDC_REBASE_CHECK_FORCE,IDS_REBASE_FORCE_TT);
204 m_tooltips.AddTool(IDC_REBASE_ABORT, IDS_REBASE_ABORT_TT);
207 CString temp;
208 temp.LoadString(IDS_PROC_REBASE_SELECTALL_PICK);
209 m_SplitAllOptions.AddEntry(temp);
210 temp.LoadString(IDS_PROC_REBASE_SELECTALL_SQUASH);
211 m_SplitAllOptions.AddEntry(temp);
212 temp.LoadString(IDS_PROC_REBASE_SELECTALL_EDIT);
213 m_SplitAllOptions.AddEntry(temp);
216 m_FileListCtrl.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL , _T("RebaseDlg"),(GITSLC_POPALL ^ (GITSLC_POPCOMMIT|GITSLC_POPRESTORE)), false, true, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD| GITSLC_COLDEL);
218 m_ctrlTabCtrl.AddTab(&m_FileListCtrl, CString(MAKEINTRESOURCE(IDS_PROC_REVISIONFILES)));
219 m_ctrlTabCtrl.AddTab(&m_LogMessageCtrl, CString(MAKEINTRESOURCE(IDS_PROC_COMMITMESSAGE)), 1);
220 AddRebaseAnchor();
222 AdjustControlSize(IDC_CHECK_CHERRYPICKED_FROM);
223 AdjustControlSize(IDC_REBASE_SPLIT_COMMIT);
225 CString sWindowTitle;
226 GetWindowText(sWindowTitle);
227 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
229 EnableSaveRestore(_T("RebaseDlg"));
231 DWORD yPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RebaseDlgSizer"));
232 RECT rcDlg, rcLogMsg, rcFileList;
233 GetClientRect(&rcDlg);
234 m_CommitList.GetWindowRect(&rcLogMsg);
235 ScreenToClient(&rcLogMsg);
236 this->m_ctrlTabCtrl.GetWindowRect(&rcFileList);
237 ScreenToClient(&rcFileList);
238 if (yPos)
240 RECT rectSplitter;
241 m_wndSplitter.GetWindowRect(&rectSplitter);
242 ScreenToClient(&rectSplitter);
243 int delta = yPos - rectSplitter.top;
244 if ((rcLogMsg.bottom + delta > rcLogMsg.top)&&(rcLogMsg.bottom + delta < rcFileList.bottom - 30))
246 m_wndSplitter.SetWindowPos(NULL, 0, yPos, 0, 0, SWP_NOSIZE);
247 DoSize(delta);
251 if( this->m_RebaseStage == CHOOSE_BRANCH)
253 this->LoadBranchInfo();
256 else
258 this->m_BranchCtrl.EnableWindow(FALSE);
259 this->m_UpstreamCtrl.EnableWindow(FALSE);
260 GetDlgItem(IDC_BUTTON_REVERSE)->EnableWindow(FALSE);
263 m_CommitList.m_ColumnRegKey = _T("Rebase");
264 m_CommitList.m_IsIDReplaceAction = TRUE;
265 // m_CommitList.m_IsOldFirst = TRUE;
266 m_CommitList.m_IsRebaseReplaceGraph = TRUE;
267 m_CommitList.m_bNoHightlightHead = TRUE;
269 m_CommitList.InsertGitColumn();
271 this->SetControlEnable();
273 if(m_IsCherryPick)
275 this->m_BranchCtrl.SetCurSel(-1);
276 this->m_BranchCtrl.EnableWindow(FALSE);
277 GetDlgItem(IDC_REBASE_CHECK_FORCE)->ShowWindow(SW_HIDE);
278 GetDlgItem(IDC_BUTTON_BROWSE)->EnableWindow(FALSE);
279 GetDlgItem(IDC_BUTTON_REVERSE)->EnableWindow(FALSE);
280 GetDlgItem(IDC_BUTTON_ONTO)->EnableWindow(FALSE);
281 this->m_UpstreamCtrl.AddString(_T("HEAD"));
282 this->m_UpstreamCtrl.EnableWindow(FALSE);
283 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, CString(MAKEINTRESOURCE(IDS_PROGS_TITLE_CHERRYPICK)));
284 this->m_CommitList.StartFilter();
286 else
288 ((CButton*)GetDlgItem(IDC_BUTTON_ONTO))->SetCheck(m_Onto.IsEmpty() ? BST_UNCHECKED : BST_CHECKED);
289 GetDlgItem(IDC_CHECK_CHERRYPICKED_FROM)->ShowWindow(SW_HIDE);
290 ((CButton *)GetDlgItem(IDC_BUTTON_REVERSE))->SetIcon((HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SWITCHLEFTRIGHT), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR));
291 SetContinueButtonText();
292 m_CommitList.DeleteAllItems();
293 FetchLogList();
296 m_CommitList.m_ContextMenuMask &= ~(m_CommitList.GetContextMenuBit(CGitLogListBase::ID_CHERRY_PICK)|
297 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_SWITCHTOREV)|
298 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_RESET)|
299 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REVERTREV)|
300 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_MERGEREV) |
301 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_TO_VERSION)|
302 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REVERTTOREV)|
303 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_COMBINE_COMMIT));
305 if(m_CommitList.m_IsOldFirst)
306 this->m_CurrentRebaseIndex = -1;
307 else
308 this->m_CurrentRebaseIndex = (int)m_CommitList.m_logEntries.size();
310 return TRUE;
312 // CRebaseDlg message handlers
314 HBRUSH CRebaseDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
316 if (pWnd->GetDlgCtrlID() == IDC_STATUS_STATIC && nCtlColor == CTLCOLOR_STATIC && m_bStatusWarning)
318 pDC->SetBkColor(RGB(255, 0, 0));
319 pDC->SetTextColor(RGB(255, 255, 255));
320 return CreateSolidBrush(RGB(255, 0, 0));
323 return CResizableStandAloneDialog::OnCtlColor(pDC, pWnd, nCtlColor);
326 void CRebaseDlg::SetAllRebaseAction(int action)
328 for (size_t i = 0; i < this->m_CommitList.m_logEntries.size(); ++i)
330 m_CommitList.m_logEntries.GetGitRevAt(i).GetRebaseAction() = action;
332 m_CommitList.Invalidate();
335 void CRebaseDlg::OnBnClickedRebaseSplit()
337 this->UpdateData();
340 LRESULT CRebaseDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
342 switch (message) {
343 case WM_NOTIFY:
344 if (wParam == IDC_REBASE_SPLIT)
346 SPC_NMHDR* pHdr = (SPC_NMHDR*) lParam;
347 DoSize(pHdr->delta);
349 break;
352 return __super::DefWindowProc(message, wParam, lParam);
355 void CRebaseDlg::DoSize(int delta)
357 this->RemoveAllAnchors();
359 CSplitterControl::ChangeHeight(GetDlgItem(IDC_COMMIT_LIST), delta, CW_TOPALIGN);
360 //CSplitterControl::ChangeHeight(GetDlgItem(), delta, CW_TOPALIGN);
361 CSplitterControl::ChangeHeight(GetDlgItem(IDC_REBASE_TAB), -delta, CW_BOTTOMALIGN);
362 //CSplitterControl::ChangeHeight(GetDlgItem(), -delta, CW_BOTTOMALIGN);
363 CSplitterControl::ChangePos(GetDlgItem(IDC_SPLITALLOPTIONS), 0, delta);
364 CSplitterControl::ChangePos(GetDlgItem(IDC_BUTTON_UP2),0,delta);
365 CSplitterControl::ChangePos(GetDlgItem(IDC_BUTTON_DOWN2),0,delta);
366 CSplitterControl::ChangePos(GetDlgItem(IDC_REBASE_CHECK_FORCE),0,delta);
367 CSplitterControl::ChangePos(GetDlgItem(IDC_CHECK_CHERRYPICKED_FROM), 0, delta);
369 this->AddRebaseAnchor();
370 // adjust the minimum size of the dialog to prevent the resizing from
371 // moving the list control too far down.
372 CRect rcLogMsg;
373 m_CommitList.GetClientRect(rcLogMsg);
374 SetMinTrackSize(CSize(m_DlgOrigRect.Width(), m_DlgOrigRect.Height()-m_CommitListOrigRect.Height()+rcLogMsg.Height()));
376 SetSplitterRange();
377 // m_CommitList.Invalidate();
379 // GetDlgItem(IDC_LOGMESSAGE)->Invalidate();
381 this->m_ctrlTabCtrl.Invalidate();
382 this->m_CommitList.Invalidate();
383 this->m_FileListCtrl.Invalidate();
384 this->m_LogMessageCtrl.Invalidate();
388 void CRebaseDlg::SetSplitterRange()
390 if ((m_CommitList)&&(m_ctrlTabCtrl))
392 CRect rcTop;
393 m_CommitList.GetWindowRect(rcTop);
394 ScreenToClient(rcTop);
395 CRect rcMiddle;
396 m_ctrlTabCtrl.GetWindowRect(rcMiddle);
397 ScreenToClient(rcMiddle);
398 if (rcMiddle.Height() && rcMiddle.Width())
399 m_wndSplitter.SetRange(rcTop.top+60, rcMiddle.bottom-80);
403 void CRebaseDlg::OnSize(UINT nType,int cx, int cy)
405 // first, let the resizing take place
406 __super::OnSize(nType, cx, cy);
408 //set range
409 SetSplitterRange();
412 void CRebaseDlg::SaveSplitterPos()
414 if (!IsIconic())
416 CRegDWORD regPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RebaseDlgSizer"));
417 RECT rectSplitter;
418 m_wndSplitter.GetWindowRect(&rectSplitter);
419 ScreenToClient(&rectSplitter);
420 regPos = rectSplitter.top;
424 void CRebaseDlg::LoadBranchInfo()
426 m_BranchCtrl.SetMaxHistoryItems(0x7FFFFFFF);
427 m_UpstreamCtrl.SetMaxHistoryItems(0x7FFFFFFF);
429 STRING_VECTOR list;
430 list.clear();
431 int current = -1;
432 g_Git.GetBranchList(list,&current,CGit::BRANCH_ALL);
433 m_BranchCtrl.SetList(list);
434 if (current >= 0)
435 m_BranchCtrl.SetCurSel(current);
436 else
437 m_BranchCtrl.AddString(g_Git.GetCurrentBranch(true));
438 list.clear();
439 g_Git.GetBranchList(list, NULL, CGit::BRANCH_ALL_F);
440 g_Git.GetTagList(list);
441 m_UpstreamCtrl.SetList(list);
443 AddBranchToolTips(&m_BranchCtrl);
445 if(!m_Upstream.IsEmpty())
447 m_UpstreamCtrl.AddString(m_Upstream);
449 else
451 //Select pull-remote from current branch
452 CString pullRemote, pullBranch;
453 g_Git.GetRemoteTrackedBranchForHEAD(pullRemote, pullBranch);
455 CString defaultUpstream;
456 defaultUpstream.Format(L"remotes/%s/%s", pullRemote, pullBranch);
457 int found = m_UpstreamCtrl.FindStringExact(0, defaultUpstream);
458 if(found >= 0)
459 m_UpstreamCtrl.SetCurSel(found);
460 else
461 m_UpstreamCtrl.SetCurSel(-1);
463 AddBranchToolTips(&m_UpstreamCtrl);
466 void CRebaseDlg::OnCbnSelchangeBranch()
468 FetchLogList();
471 void CRebaseDlg::OnCbnSelchangeUpstream()
473 FetchLogList();
476 void CRebaseDlg::FetchLogList()
478 CGitHash base,hash,upstream;
479 m_IsFastForward=FALSE;
481 if (m_BranchCtrl.GetString().IsEmpty())
483 m_CommitList.ShowText(CString(MAKEINTRESOURCE(IDS_SELECTBRANCH)));
484 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(false);
485 return;
488 if (g_Git.GetHash(hash, m_BranchCtrl.GetString()))
490 m_CommitList.ShowText(g_Git.GetGitLastErr(_T("Could not get hash of \"") + m_BranchCtrl.GetString() + _T("\".")));
491 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(false);
492 return;
495 if (m_UpstreamCtrl.GetString().IsEmpty())
497 m_CommitList.ShowText(CString(MAKEINTRESOURCE(IDS_SELECTUPSTREAM)));
498 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(false);
499 return;
502 if (g_Git.GetHash(upstream, m_UpstreamCtrl.GetString()))
504 m_CommitList.ShowText(g_Git.GetGitLastErr(_T("Could not get hash of \"") + m_UpstreamCtrl.GetString() + _T("\".")));
505 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(false);
506 return;
509 if (hash == upstream)
511 m_CommitList.Clear();
512 CString text,fmt;
513 fmt.LoadString(IDS_REBASE_EQUAL_FMT);
514 text.Format(fmt,m_BranchCtrl.GetString(),this->m_UpstreamCtrl.GetString());
516 m_CommitList.ShowText(text);
517 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(false);
518 return;
521 if (g_Git.IsFastForward(m_BranchCtrl.GetString(), m_UpstreamCtrl.GetString(), &base) && m_Onto.IsEmpty())
523 //fast forword
524 this->m_IsFastForward=TRUE;
526 m_CommitList.Clear();
527 CString text,fmt;
528 fmt.LoadString(IDS_REBASE_FASTFORWARD_FMT);
529 text.Format(fmt,m_BranchCtrl.GetString(),this->m_UpstreamCtrl.GetString(),
530 m_BranchCtrl.GetString(),this->m_UpstreamCtrl.GetString());
532 m_CommitList.ShowText(text);
533 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(true);
534 SetContinueButtonText();
536 return ;
539 if (!m_bForce && m_Onto.IsEmpty())
541 if (base == upstream)
543 m_CommitList.Clear();
544 CString text,fmt;
545 fmt.LoadString(IDS_REBASE_UPTODATE_FMT);
546 text.Format(fmt,m_BranchCtrl.GetString());
547 m_CommitList.ShowText(text);
548 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(m_CommitList.GetItemCount());
549 SetContinueButtonText();
550 return;
554 m_CommitList.Clear();
555 CString refFrom = g_Git.FixBranchName(m_UpstreamCtrl.GetString());
556 CString refTo = g_Git.FixBranchName(m_BranchCtrl.GetString());
557 CString range;
558 range.Format(_T("%s..%s"), refFrom, refTo);
559 this->m_CommitList.FillGitLog(nullptr, &range, 0);
561 if( m_CommitList.GetItemCount() == 0 )
562 m_CommitList.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_NOTHINGTOREBASE)));
564 #if 0
565 if(m_CommitList.m_logEntries[m_CommitList.m_logEntries.size()-1].m_ParentHash.size() >=0 )
567 if(upstream == m_CommitList.m_logEntries[m_CommitList.m_logEntries.size()-1].m_ParentHash[0])
569 m_CommitList.Clear();
570 m_CommitList.ShowText(_T("Nothing Rebase"));
573 #endif
575 m_tooltips.Pop();
576 AddBranchToolTips(&this->m_BranchCtrl);
577 AddBranchToolTips(&this->m_UpstreamCtrl);
579 // Default all actions to 'pick'
580 std::map<CGitHash, size_t> revIxMap;
581 for (size_t i = 0; i < m_CommitList.m_logEntries.size(); ++i)
583 GitRev& rev = m_CommitList.m_logEntries.GetGitRevAt(i);
584 rev.GetRebaseAction() = CGitLogListBase::LOGACTIONS_REBASE_PICK;
585 revIxMap[rev.m_CommitHash] = i;
588 // Default to skip when already in upstream
589 if (!m_Onto.IsEmpty())
590 refFrom = g_Git.FixBranchName(m_Onto);
591 CString cherryCmd;
592 cherryCmd.Format(L"git.exe cherry \"%s\" \"%s\"", refFrom, refTo);
593 bool bHasSKip = false;
594 g_Git.Run(cherryCmd, [&](const CStringA& line)
596 if (line.GetLength() < 2)
597 return;
598 if (line[0] != '-')
599 return; // Don't skip (only skip commits starting with a '-')
600 CString hash = CUnicodeUtils::GetUnicode(line.Mid(1));
601 hash.Trim();
602 auto itIx = revIxMap.find(CGitHash(hash));
603 if (itIx == revIxMap.end())
604 return; // Not found?? Should not occur...
606 // Found. Skip it.
607 m_CommitList.m_logEntries.GetGitRevAt(itIx->second).GetRebaseAction() = CGitLogListBase::LOGACTIONS_REBASE_SKIP;
608 bHasSKip = true;
611 m_CommitList.Invalidate();
612 if (bHasSKip)
614 m_CtrlStatusText.SetWindowText(CString(MAKEINTRESOURCE(IDS_REBASE_AUTOSKIPPED)));
615 m_bStatusWarning = true;
617 else
619 m_CtrlStatusText.SetWindowText(m_sStatusText);
620 m_bStatusWarning = false;
622 m_CtrlStatusText.Invalidate();
624 if(m_CommitList.m_IsOldFirst)
625 this->m_CurrentRebaseIndex = -1;
626 else
627 this->m_CurrentRebaseIndex = (int)m_CommitList.m_logEntries.size();
629 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(m_CommitList.GetItemCount());
630 SetContinueButtonText();
633 void CRebaseDlg::AddBranchToolTips(CHistoryCombo *pBranch)
635 if(pBranch)
637 CString text=pBranch->GetString();
638 CString tooltip;
640 GitRev rev;
643 rev.GetCommit(text);
645 catch (const char *msg)
647 CMessageBox::Show(m_hWnd, _T("Could not get commit ") + text + _T("\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
650 tooltip.Format(_T("%s: %s\n%s: %s <%s>\n%s: %s\n%s:\n%s\n%s"),
651 CString(MAKEINTRESOURCE(IDS_LOG_REVISION)),
652 rev.m_CommitHash.ToString(),
653 CString(MAKEINTRESOURCE(IDS_LOG_AUTHOR)),
654 rev.GetAuthorName(),
655 rev.GetAuthorEmail(),
656 CString(MAKEINTRESOURCE(IDS_LOG_DATE)),
657 CLoglistUtils::FormatDateAndTime(rev.GetAuthorDate(), DATE_LONGDATE),
658 CString(MAKEINTRESOURCE(IDS_LOG_MESSAGE)),
659 rev.GetSubject(),
660 rev.GetBody());
662 pBranch->DisableTooltip();
663 this->m_tooltips.AddTool(pBranch->GetComboBoxCtrl(),tooltip);
667 BOOL CRebaseDlg::PreTranslateMessage(MSG*pMsg)
669 if (pMsg->message == WM_KEYDOWN)
671 switch (pMsg->wParam)
673 case ' ':
674 if (LogListHasFocus(pMsg->hwnd)
675 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_PICK)
676 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SQUASH)
677 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_EDIT)
678 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SKIP))
680 m_CommitList.ShiftSelectedRebaseAction();
681 return TRUE;
683 break;
684 case 'P':
685 if (LogListHasFocus(pMsg->hwnd) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_PICK))
687 m_CommitList.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_PICK);
688 return TRUE;
690 break;
691 case 'S':
692 if (LogListHasFocus(pMsg->hwnd) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SKIP))
694 m_CommitList.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_SKIP);
695 return TRUE;
697 break;
698 case 'Q':
699 if (LogListHasFocus(pMsg->hwnd) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SQUASH))
701 m_CommitList.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_SQUASH);
702 return TRUE;
704 break;
705 case 'E':
706 if (LogListHasFocus(pMsg->hwnd) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_EDIT))
708 m_CommitList.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_EDIT);
709 return TRUE;
711 break;
712 case 'A':
713 if(LogListHasFocus(pMsg->hwnd) && GetAsyncKeyState(VK_CONTROL) & 0x8000)
715 // select all entries
716 for (int i = 0; i < m_CommitList.GetItemCount(); ++i)
718 m_CommitList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
720 return TRUE;
722 break;
723 case VK_F5:
725 Refresh();
726 return TRUE;
728 break;
729 case VK_RETURN:
731 if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
733 if (GetDlgItem(IDC_REBASE_CONTINUE)->IsWindowEnabled())
734 GetDlgItem(IDC_REBASE_CONTINUE)->SetFocus();
735 else if (GetDlgItem(IDC_REBASE_ABORT)->IsWindowEnabled())
736 GetDlgItem(IDC_REBASE_ABORT)->SetFocus();
737 else
738 GetDlgItem(IDHELP)->SetFocus();
739 return TRUE;
742 break;
743 /* Avoid TAB control destroy but dialog exist*/
744 case VK_ESCAPE:
745 case VK_CANCEL:
747 TCHAR buff[128] = { 0 };
748 ::GetClassName(pMsg->hwnd,buff,128);
751 /* Use MSFTEDIT_CLASS http://msdn.microsoft.com/en-us/library/bb531344.aspx */
752 if (_tcsnicmp(buff, MSFTEDIT_CLASS, 128) == 0 || //Unicode and MFC 2012 and later
753 _tcsnicmp(buff, RICHEDIT_CLASS, 128) == 0 || //ANSI or MFC 2010
754 _tcsnicmp(buff,_T("Scintilla"),128)==0 ||
755 _tcsnicmp(buff,_T("SysListView32"),128)==0||
756 ::GetParent(pMsg->hwnd) == this->m_ctrlTabCtrl.m_hWnd)
758 this->PostMessage(WM_KEYDOWN,VK_ESCAPE,0);
759 return TRUE;
764 else if (pMsg->message == WM_NEXTDLGCTL)
766 HWND hwnd = GetFocus()->GetSafeHwnd();
767 if (hwnd == m_LogMessageCtrl.GetSafeHwnd() || hwnd == m_wndOutputRebase.GetSafeHwnd())
769 if (GetDlgItem(IDC_REBASE_CONTINUE)->IsWindowEnabled())
770 GetDlgItem(IDC_REBASE_CONTINUE)->SetFocus();
771 else if (GetDlgItem(IDC_REBASE_ABORT)->IsWindowEnabled())
772 GetDlgItem(IDC_REBASE_ABORT)->SetFocus();
773 else
774 GetDlgItem(IDHELP)->SetFocus();
775 return TRUE;
778 m_tooltips.RelayEvent(pMsg);
779 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
782 bool CRebaseDlg::LogListHasFocus(HWND hwnd)
784 TCHAR buff[128] = { 0 };
785 ::GetClassName(hwnd, buff, 128);
787 if(_tcsnicmp(buff, _T("SysListView32"), 128) == 0)
788 return true;
789 return false;
792 bool CRebaseDlg::LogListHasMenuItem(int i)
794 return (m_CommitList.m_ContextMenuMask & m_CommitList.GetContextMenuBit(i)) != 0;
797 int CRebaseDlg::CheckRebaseCondition()
799 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
801 if( !g_Git.CheckCleanWorkTree() )
803 if (CMessageBox::Show(NULL, IDS_ERROR_NOCLEAN_STASH, IDS_APPNAME, 1, IDI_QUESTION, IDS_STASHBUTTON, IDS_ABORTBUTTON) == 1)
805 CString cmd,out;
806 cmd=_T("git.exe stash");
807 this->AddLogString(cmd);
808 if (g_Git.Run(cmd, &out, CP_UTF8))
810 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
811 return -1;
813 m_bStashed = true;
815 else
816 return -1;
818 //Todo Check $REBASE_ROOT
819 //Todo Check $DOTEST
821 if (!CAppUtils::CheckUserData())
822 return -1;
824 //Todo call pre_rebase_hook
825 return 0;
828 void CRebaseDlg::CheckRestoreStash()
830 if (m_bStashed && CMessageBox::Show(nullptr, IDS_DCOMMIT_STASH_POP, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION) == IDYES)
831 CAppUtils::StashPop();
832 m_bStashed = false;
835 int CRebaseDlg::StartRebase()
837 CString cmd,out;
838 m_FileListCtrl.m_bIsRevertTheirMy = !m_IsCherryPick;
840 m_OrigHEADBranch = g_Git.GetCurrentBranch(true);
842 m_OrigHEADHash.Empty();
843 if (g_Git.GetHash(m_OrigHEADHash, _T("HEAD")))
845 AddLogString(CString(MAKEINTRESOURCE(IDS_PROC_NOHEAD)));
846 return -1;
848 //Todo
849 //git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null ||
850 // echo "detached HEAD" > "$DOTEST"/head-name
852 cmd.Format(_T("git.exe update-ref ORIG_HEAD ") + m_OrigHEADHash.ToString());
853 if(g_Git.Run(cmd,&out,CP_UTF8))
855 AddLogString(_T("update ORIG_HEAD Fail"));
856 return -1;
859 m_OrigUpstreamHash.Empty();
860 if (g_Git.GetHash(m_OrigUpstreamHash, (m_IsCherryPick || m_Onto.IsEmpty()) ? m_UpstreamCtrl.GetString() : m_Onto))
862 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of \"") + (m_IsCherryPick || m_Onto.IsEmpty()) ? m_UpstreamCtrl.GetString() : m_Onto + _T("\".")), _T("TortoiseGit"), MB_ICONERROR);
863 return -1;
866 if( !this->m_IsCherryPick )
868 cmd.Format(_T("git.exe checkout -f %s --"), m_OrigUpstreamHash.ToString());
869 this->AddLogString(cmd);
870 while (true)
872 out.Empty();
873 if (g_Git.Run(cmd, &out, CP_UTF8))
875 this->AddLogString(out);
876 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
877 return -1;
879 else
880 break;
884 CString log;
885 if( !this->m_IsCherryPick )
887 if (g_Git.GetHash(m_OrigBranchHash, m_BranchCtrl.GetString()))
889 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of \"") + m_BranchCtrl.GetString() + _T("\".")), _T("TortoiseGit"), MB_ICONERROR);
890 return -1;
892 log.Format(_T("%s\r\n"), CString(MAKEINTRESOURCE(IDS_PROC_REBASE_STARTREBASE)));
894 else
895 log.Format(_T("%s\r\n"), CString(MAKEINTRESOURCE(IDS_PROC_REBASE_STARTCHERRYPICK)));
897 this->AddLogString(log);
898 return 0;
900 int CRebaseDlg::VerifyNoConflict()
902 CTGitPathList list;
903 if(g_Git.ListConflictFile(list))
905 AddLogString(_T("Get conflict files fail"));
906 return -1;
908 if (!list.IsEmpty())
910 CMessageBox::Show(NULL, IDS_PROGRS_CONFLICTSOCCURED, IDS_APPNAME, MB_OK);
911 return -1;
913 return 0;
917 static bool IsLocalBranch(CString ref)
919 STRING_VECTOR list;
920 g_Git.GetBranchList(list, nullptr, CGit::BRANCH_LOCAL);
921 return std::find(list.begin(), list.end(), ref) != list.end();
924 int CRebaseDlg::FinishRebase()
926 if (m_bFinishedRebase)
927 return 0;
929 m_bFinishedRebase = true;
930 if(this->m_IsCherryPick) //cherry pick mode no "branch", working at upstream branch
932 m_sStatusText.LoadString(IDS_DONE);
933 m_CtrlStatusText.SetWindowText(m_sStatusText);
934 m_bStatusWarning = false;
935 m_CtrlStatusText.Invalidate();
936 return 0;
939 CGitHash head;
940 if (g_Git.GetHash(head, _T("HEAD")))
942 MessageBox(g_Git.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR);
943 return -1;
945 CString out,cmd;
947 if (IsLocalBranch(m_BranchCtrl.GetString()))
949 cmd.Format(_T("git.exe checkout -f -B %s %s --"), m_BranchCtrl.GetString(), head.ToString());
950 AddLogString(cmd);
951 while (true)
953 out.Empty();
954 if (g_Git.Run(cmd, &out, CP_UTF8))
956 AddLogString(out);
957 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
958 return -1;
960 else
961 break;
963 AddLogString(out);
966 cmd.Format(_T("git.exe reset --hard %s --"), head.ToString());
967 AddLogString(cmd);
968 while (true)
970 out.Empty();
971 if (g_Git.Run(cmd, &out, CP_UTF8))
973 AddLogString(out);
974 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
975 return -1;
977 else
978 break;
980 AddLogString(out);
982 while (m_ctrlTabCtrl.GetTabsNum() > 1)
983 m_ctrlTabCtrl.RemoveTab(0);
984 m_CtrlStatusText.SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_REBASEFINISHED)));
985 m_sStatusText = CString(MAKEINTRESOURCE(IDS_PROC_REBASEFINISHED));
986 m_bStatusWarning = false;
987 m_CtrlStatusText.Invalidate();
989 return 0;
991 void CRebaseDlg::OnBnClickedContinue()
993 if( m_RebaseStage == REBASE_DONE)
995 OnOK();
996 CheckRestoreStash();
997 return;
1000 if (m_RebaseStage == CHOOSE_BRANCH || m_RebaseStage == CHOOSE_COMMIT_PICK_MODE)
1002 if (CheckRebaseCondition())
1003 return;
1006 if( this->m_IsFastForward )
1008 CString cmd,out;
1009 if (g_Git.GetHash(m_OrigBranchHash, m_BranchCtrl.GetString()))
1011 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of \"") + m_BranchCtrl.GetString() + _T("\".")), _T("TortoiseGit"), MB_ICONERROR);
1012 return;
1014 if (g_Git.GetHash(m_OrigUpstreamHash, m_UpstreamCtrl.GetString()))
1016 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of \"") + m_UpstreamCtrl.GetString() + _T("\".")), _T("TortoiseGit"), MB_ICONERROR);
1017 return;
1020 if(!g_Git.IsFastForward(this->m_BranchCtrl.GetString(),this->m_UpstreamCtrl.GetString()))
1022 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1023 AddLogString(_T("No fast forward possible.\r\nMaybe repository changed"));
1024 return;
1027 if (IsLocalBranch(m_BranchCtrl.GetString()))
1029 cmd.Format(_T("git.exe checkout --no-track -f -B %s %s --"), m_BranchCtrl.GetString(), m_UpstreamCtrl.GetString());
1030 AddLogString(cmd);
1031 while (true)
1033 out.Empty();
1034 if (g_Git.Run(cmd, &out, CP_UTF8))
1036 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1037 AddLogString(out);
1038 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
1039 return;
1041 else
1042 break;
1044 AddLogString(out);
1045 out.Empty();
1047 cmd.Format(_T("git.exe reset --hard %s --"), g_Git.FixBranchName(this->m_UpstreamCtrl.GetString()));
1048 CString log;
1049 log.Format(IDS_PROC_REBASE_FFTO, m_UpstreamCtrl.GetString());
1050 this->AddLogString(log);
1052 AddLogString(cmd);
1053 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1054 while (true)
1056 out.Empty();
1057 if (g_Git.Run(cmd, &out, CP_UTF8))
1059 AddLogString(CString(MAKEINTRESOURCE(IDS_FAIL)));
1060 AddLogString(out);
1061 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
1062 return;
1064 else
1065 break;
1067 AddLogString(out);
1068 AddLogString(CString(MAKEINTRESOURCE(IDS_DONE)));
1069 m_RebaseStage = REBASE_DONE;
1070 UpdateCurrentStatus();
1071 return;
1074 if( m_RebaseStage == CHOOSE_BRANCH|| m_RebaseStage == CHOOSE_COMMIT_PICK_MODE )
1076 if(CheckRebaseCondition())
1077 return ;
1078 m_RebaseStage = REBASE_START;
1079 m_FileListCtrl.Clear();
1080 m_FileListCtrl.SetHasCheckboxes(false);
1081 m_FileListCtrl.m_CurrentVersion = L"";
1082 m_ctrlTabCtrl.SetTabLabel(REBASE_TAB_CONFLICT, CString(MAKEINTRESOURCE(IDS_PROC_CONFLICTFILES)));
1083 m_ctrlTabCtrl.AddTab(&m_wndOutputRebase, CString(MAKEINTRESOURCE(IDS_LOG)), 2);
1086 if( m_RebaseStage == REBASE_FINISH )
1088 if(FinishRebase())
1089 return ;
1091 OnOK();
1094 if( m_RebaseStage == REBASE_SQUASH_CONFLICT)
1096 if(VerifyNoConflict())
1097 return;
1098 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1099 if(this->CheckNextCommitIsSquash())
1100 {//next commit is not squash;
1101 m_RebaseStage = REBASE_SQUASH_EDIT;
1102 this->OnRebaseUpdateUI(0,0);
1103 this->UpdateCurrentStatus();
1104 return ;
1107 m_RebaseStage=REBASE_CONTINUE;
1108 curRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1109 this->UpdateCurrentStatus();
1113 if( m_RebaseStage == REBASE_CONFLICT )
1115 if(VerifyNoConflict())
1116 return;
1118 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1119 // ***************************************************
1120 // ATTENTION: Similar code in CommitDlg.cpp!!!
1121 // ***************************************************
1122 CMassiveGitTask mgtReAddAfterCommit(_T("add --ignore-errors -f"));
1123 CMassiveGitTask mgtAdd(_T("add -f"));
1124 CMassiveGitTask mgtUpdateIndexForceRemove(_T("update-index --force-remove"));
1125 CMassiveGitTask mgtUpdateIndex(_T("update-index"));
1126 CMassiveGitTask mgtRm(_T("rm --ignore-unmatch"));
1127 CMassiveGitTask mgtRmFCache(_T("rm -f --cache"));
1128 CMassiveGitTask mgtReset(_T("reset"), TRUE, true);
1129 for (int i = 0; i < m_FileListCtrl.GetItemCount(); i++)
1131 CTGitPath *entry = (CTGitPath *)m_FileListCtrl.GetItemData(i);
1132 if (entry->m_Checked)
1134 if (entry->m_Action & CTGitPath::LOGACTIONS_UNVER)
1135 mgtAdd.AddFile(entry->GetGitPathString());
1136 else if (entry->m_Action & CTGitPath::LOGACTIONS_DELETED)
1137 mgtUpdateIndexForceRemove.AddFile(entry->GetGitPathString());
1138 else
1139 mgtUpdateIndex.AddFile(entry->GetGitPathString());
1141 if (entry->m_Action & CTGitPath::LOGACTIONS_REPLACED)
1142 mgtRm.AddFile(entry->GetGitOldPathString());
1144 else
1146 if (entry->m_Action & CTGitPath::LOGACTIONS_ADDED || entry->m_Action & CTGitPath::LOGACTIONS_REPLACED)
1148 mgtRmFCache.AddFile(entry->GetGitPathString());
1149 mgtReAddAfterCommit.AddFile(*entry);
1151 if (entry->m_Action & CTGitPath::LOGACTIONS_REPLACED && !entry->GetGitOldPathString().IsEmpty())
1152 mgtReset.AddFile(entry->GetGitOldPathString());
1154 else if(!(entry->m_Action & CTGitPath::LOGACTIONS_UNVER))
1155 mgtReset.AddFile(entry->GetGitPathString());
1159 BOOL cancel = FALSE;
1160 bool successful = true;
1161 successful = successful && mgtAdd.Execute(cancel);
1162 successful = successful && mgtUpdateIndexForceRemove.Execute(cancel);
1163 successful = successful && mgtUpdateIndex.Execute(cancel);
1164 successful = successful && mgtRm.Execute(cancel);
1165 successful = successful && mgtRmFCache.Execute(cancel);
1166 successful = successful && mgtReset.Execute(cancel);
1168 if (!successful)
1170 AddLogString(_T("An error occurred while updating the index."));
1171 return;
1174 CString out =_T("");
1175 CString cmd;
1176 cmd.Format(_T("git.exe commit -C %s"), curRev->m_CommitHash.ToString());
1178 AddLogString(cmd);
1180 if(g_Git.Run(cmd,&out,CP_UTF8))
1182 AddLogString(out);
1183 if(!g_Git.CheckCleanWorkTree())
1185 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1186 return;
1190 AddLogString(out);
1192 // update commit message if needed
1193 CString str = m_LogMessageCtrl.GetText().Trim();
1194 if (str != (curRev->GetSubject() + _T("\n") + curRev->GetBody()).Trim())
1196 if (str.Trim().IsEmpty())
1198 CMessageBox::Show(NULL, IDS_PROC_COMMITMESSAGE_EMPTY,IDS_APPNAME, MB_OK | MB_ICONERROR);
1199 return;
1201 CString tempfile = ::GetTempFile();
1202 if (CAppUtils::SaveCommitUnicodeFile(tempfile, str))
1204 CMessageBox::Show(nullptr, _T("Could not save commit message"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1205 return;
1208 out.Empty();
1209 cmd.Format(_T("git.exe commit --amend -F \"%s\""), tempfile);
1210 AddLogString(cmd);
1212 if (g_Git.Run(cmd, &out, CP_UTF8))
1214 AddLogString(out);
1215 if (!g_Git.CheckCleanWorkTree())
1217 CMessageBox::Show(NULL, out, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1218 return;
1222 AddLogString(out);
1225 if (((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\ReaddUnselectedAddedFilesAfterCommit"), TRUE)) == TRUE)
1227 BOOL cancel = FALSE;
1228 mgtReAddAfterCommit.Execute(cancel);
1231 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1232 if (curRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_EDIT)
1234 m_RebaseStage=REBASE_EDIT;
1235 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_MESSAGE);
1236 this->UpdateCurrentStatus();
1237 return;
1239 else
1241 m_RebaseStage=REBASE_CONTINUE;
1242 curRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1243 this->UpdateCurrentStatus();
1245 if (CheckNextCommitIsSquash() == 0) // remember commit msg after edit if next commit if squash
1246 ResetParentForSquash(str);
1247 else
1248 m_SquashMessage.Empty();
1252 if ((m_RebaseStage == REBASE_EDIT || m_RebaseStage == REBASE_CONTINUE || m_bSplitCommit) && CheckNextCommitIsSquash() && (m_bSplitCommit || !g_Git.CheckCleanWorkTree(true)))
1254 if (!m_bSplitCommit && CMessageBox::Show(nullptr, IDS_PROC_REBASE_CONTINUE_NOTCLEAN, IDS_APPNAME, 1, IDI_ERROR, IDS_MSGBOX_OK, IDS_ABORTBUTTON) == 2)
1255 return;
1256 BOOL isFirst = TRUE;
1259 CCommitDlg dlg;
1260 if (isFirst)
1261 dlg.m_sLogMessage = m_LogMessageCtrl.GetText();
1262 dlg.m_bWholeProject = true;
1263 dlg.m_bSelectFilesForCommit = true;
1264 dlg.m_bCommitAmend = isFirst && (m_RebaseStage != REBASE_SQUASH_EDIT); // do not amend on squash_edit stage, we need a normal commit there
1265 CTGitPathList gpl;
1266 gpl.AddPath(CTGitPath(g_Git.m_CurrentDir));
1267 dlg.m_pathList = gpl;
1268 dlg.m_bAmendDiffToLastCommit = !m_bSplitCommit;
1269 dlg.m_bNoPostActions = true;
1270 if (dlg.m_bCommitAmend)
1271 dlg.m_AmendStr = dlg.m_sLogMessage;
1272 dlg.m_bWarnDetachedHead = false;
1274 if (dlg.DoModal() != IDOK)
1275 return;
1277 isFirst = !m_bSplitCommit; // only select amend on second+ runs if not in split commit mode
1279 m_SquashMessage.Empty();
1280 } while (!g_Git.CheckCleanWorkTree() || (m_bSplitCommit && MessageBox(_T("Add another commit?"), _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES));
1282 m_bSplitCommit = FALSE;
1283 UpdateData(FALSE);
1285 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1286 m_RebaseStage = REBASE_CONTINUE;
1287 GitRev* curRev = (GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1288 curRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1289 this->UpdateCurrentStatus();
1292 if( m_RebaseStage == REBASE_EDIT || m_RebaseStage == REBASE_SQUASH_EDIT )
1294 CString str;
1295 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1297 str=this->m_LogMessageCtrl.GetText();
1298 if(str.Trim().IsEmpty())
1300 CMessageBox::Show(NULL, IDS_PROC_COMMITMESSAGE_EMPTY,IDS_APPNAME, MB_OK | MB_ICONERROR);
1301 return;
1304 CString tempfile=::GetTempFile();
1305 if (CAppUtils::SaveCommitUnicodeFile(tempfile, str))
1307 CMessageBox::Show(nullptr, _T("Could not save commit message"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1308 return;
1311 CString out,cmd;
1313 if( m_RebaseStage == REBASE_SQUASH_EDIT )
1314 cmd.Format(_T("git.exe commit %s-F \"%s\""), m_SquashFirstMetaData, tempfile);
1315 else
1317 CString options;
1318 int isEmpty = IsCommitEmpty(curRev->m_CommitHash);
1319 if (isEmpty == 1)
1320 options = _T("--allow-empty ");
1321 else if (isEmpty < 0)
1322 return;
1323 cmd.Format(_T("git.exe commit --amend %s-F \"%s\""), options, tempfile);
1326 if(g_Git.Run(cmd,&out,CP_UTF8))
1328 if(!g_Git.CheckCleanWorkTree())
1330 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1331 return;
1335 ::DeleteFile(tempfile);
1336 AddLogString(out);
1337 if (CheckNextCommitIsSquash() == 0 && m_RebaseStage != REBASE_SQUASH_EDIT) // remember commit msg after edit if next commit if squash; but don't do this if ...->squash(reset here)->pick->squash
1339 ResetParentForSquash(str);
1341 else
1342 m_SquashMessage.Empty();
1343 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1344 m_RebaseStage=REBASE_CONTINUE;
1345 curRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1346 this->UpdateCurrentStatus();
1350 InterlockedExchange(&m_bThreadRunning, TRUE);
1351 SetControlEnable();
1353 if (AfxBeginThread(RebaseThreadEntry, this)==NULL)
1355 InterlockedExchange(&m_bThreadRunning, FALSE);
1356 CMessageBox::Show(NULL, _T("Create Rebase Thread Fail"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1357 SetControlEnable();
1360 void CRebaseDlg::ResetParentForSquash(const CString& commitMessage)
1362 m_SquashMessage = commitMessage;
1363 // reset parent so that we can do "git cherry-pick --no-commit" w/o introducing an unwanted commit
1364 CString cmd = _T("git.exe reset --soft HEAD~1");
1365 m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1366 while (true)
1368 CString out;
1369 if (g_Git.Run(cmd, &out, CP_UTF8))
1371 AddLogString(cmd);
1372 AddLogString(CString(MAKEINTRESOURCE(IDS_FAIL)));
1373 AddLogString(out);
1374 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
1375 return;
1377 else
1378 break;
1381 int CRebaseDlg::CheckNextCommitIsSquash()
1383 int index;
1384 if(m_CommitList.m_IsOldFirst)
1385 index=m_CurrentRebaseIndex+1;
1386 else
1387 index=m_CurrentRebaseIndex-1;
1389 GitRev *curRev;
1392 if(index<0)
1393 return -1;
1394 if(index>= m_CommitList.GetItemCount())
1395 return -1;
1397 curRev=(GitRev*)m_CommitList.m_arShownList[index];
1399 if (curRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_SQUASH)
1400 return 0;
1401 if (curRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_SKIP)
1403 if(m_CommitList.m_IsOldFirst)
1404 ++index;
1405 else
1406 --index;
1408 else
1409 return -1;
1411 } while(curRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_SKIP);
1413 return -1;
1416 int CRebaseDlg::GoNext()
1418 if(m_CommitList.m_IsOldFirst)
1419 ++m_CurrentRebaseIndex;
1420 else
1421 --m_CurrentRebaseIndex;
1422 return 0;
1425 int CRebaseDlg::StateAction()
1427 switch(this->m_RebaseStage)
1429 case CHOOSE_BRANCH:
1430 case CHOOSE_COMMIT_PICK_MODE:
1431 if(StartRebase())
1432 return -1;
1433 m_RebaseStage = REBASE_START;
1434 GoNext();
1435 break;
1438 return 0;
1440 void CRebaseDlg::SetContinueButtonText()
1442 CString Text;
1443 switch(this->m_RebaseStage)
1445 case CHOOSE_BRANCH:
1446 case CHOOSE_COMMIT_PICK_MODE:
1447 if(this->m_IsFastForward)
1448 Text.LoadString(IDS_PROC_STARTREBASEFFBUTTON);
1449 else
1450 Text.LoadString(IDS_PROC_STARTREBASEBUTTON);
1451 break;
1453 case REBASE_START:
1454 case REBASE_ERROR:
1455 case REBASE_CONTINUE:
1456 case REBASE_SQUASH_CONFLICT:
1457 Text.LoadString(IDS_CONTINUEBUTTON);
1458 break;
1460 case REBASE_CONFLICT:
1461 Text.LoadString(IDS_COMMITBUTTON);
1462 break;
1463 case REBASE_EDIT:
1464 Text.LoadString(IDS_AMENDBUTTON);
1465 break;
1467 case REBASE_SQUASH_EDIT:
1468 Text.LoadString(IDS_COMMITBUTTON);
1469 break;
1471 case REBASE_ABORT:
1472 case REBASE_FINISH:
1473 Text.LoadString(IDS_FINISHBUTTON);
1474 break;
1476 case REBASE_DONE:
1477 Text.LoadString(IDS_DONE);
1478 break;
1480 this->GetDlgItem(IDC_REBASE_CONTINUE)->SetWindowText(Text);
1483 void CRebaseDlg::SetControlEnable()
1485 switch(this->m_RebaseStage)
1487 case CHOOSE_BRANCH:
1488 case CHOOSE_COMMIT_PICK_MODE:
1490 this->GetDlgItem(IDC_SPLITALLOPTIONS)->EnableWindow(TRUE);
1491 this->GetDlgItem(IDC_BUTTON_UP2)->EnableWindow(TRUE);
1492 this->GetDlgItem(IDC_BUTTON_DOWN2)->EnableWindow(TRUE);
1494 if(!m_IsCherryPick)
1496 this->GetDlgItem(IDC_REBASE_COMBOXEX_BRANCH)->EnableWindow(TRUE);
1497 this->GetDlgItem(IDC_REBASE_COMBOXEX_UPSTREAM)->EnableWindow(TRUE);
1498 this->GetDlgItem(IDC_BUTTON_REVERSE)->EnableWindow(TRUE);
1499 this->GetDlgItem(IDC_REBASE_CHECK_FORCE)->EnableWindow(TRUE);
1501 this->m_CommitList.m_ContextMenuMask |= m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_PICK)|
1502 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SQUASH)|
1503 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_EDIT)|
1504 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SKIP);
1505 break;
1507 case REBASE_START:
1508 case REBASE_CONTINUE:
1509 case REBASE_ABORT:
1510 case REBASE_ERROR:
1511 case REBASE_FINISH:
1512 case REBASE_CONFLICT:
1513 case REBASE_EDIT:
1514 case REBASE_SQUASH_CONFLICT:
1515 case REBASE_DONE:
1516 this->GetDlgItem(IDC_SPLITALLOPTIONS)->EnableWindow(FALSE);
1517 this->GetDlgItem(IDC_REBASE_COMBOXEX_BRANCH)->EnableWindow(FALSE);
1518 this->GetDlgItem(IDC_REBASE_COMBOXEX_UPSTREAM)->EnableWindow(FALSE);
1519 this->GetDlgItem(IDC_BUTTON_REVERSE)->EnableWindow(FALSE);
1520 this->GetDlgItem(IDC_REBASE_CHECK_FORCE)->EnableWindow(FALSE);
1521 this->GetDlgItem(IDC_BUTTON_UP2)->EnableWindow(FALSE);
1522 this->GetDlgItem(IDC_BUTTON_DOWN2)->EnableWindow(FALSE);
1524 if( m_RebaseStage == REBASE_DONE && (this->m_PostButtonTexts.GetCount() != 0) )
1526 this->GetDlgItem(IDC_STATUS_STATIC)->ShowWindow(SW_HIDE);
1527 this->GetDlgItem(IDC_REBASE_POST_BUTTON)->ShowWindow(SW_SHOWNORMAL);
1528 this->m_PostButton.RemoveAll();
1529 this->m_PostButton.AddEntries(m_PostButtonTexts);
1530 //this->GetDlgItem(IDC_REBASE_POST_BUTTON)->SetWindowText(this->m_PostButtonText);
1532 break;
1535 GetDlgItem(IDC_REBASE_SPLIT_COMMIT)->ShowWindow((m_RebaseStage == REBASE_EDIT || m_RebaseStage == REBASE_SQUASH_EDIT) ? SW_SHOW : SW_HIDE);
1537 if(m_bThreadRunning)
1539 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(FALSE);
1542 else if (m_RebaseStage != REBASE_ERROR)
1544 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(TRUE);
1548 void CRebaseDlg::UpdateProgress()
1550 int index;
1551 CRect rect;
1553 if(m_CommitList.m_IsOldFirst)
1554 index = m_CurrentRebaseIndex+1;
1555 else
1556 index = m_CommitList.GetItemCount()-m_CurrentRebaseIndex;
1558 int finishedCommits = index - 1; // introduced an variable which shows the number handled revisions for the progress bars
1559 if (m_RebaseStage == REBASE_FINISH || finishedCommits == -1)
1560 finishedCommits = index;
1562 m_ProgressBar.SetRange32(0, m_CommitList.GetItemCount());
1563 m_ProgressBar.SetPos(finishedCommits);
1564 if (m_pTaskbarList)
1566 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1567 m_pTaskbarList->SetProgressValue(m_hWnd, finishedCommits, m_CommitList.GetItemCount());
1570 if(m_CurrentRebaseIndex>=0 && m_CurrentRebaseIndex< m_CommitList.GetItemCount())
1572 CString text;
1573 text.Format(IDS_PROC_REBASING_PROGRESS, index, m_CommitList.GetItemCount());
1574 m_sStatusText = text;
1575 m_CtrlStatusText.SetWindowText(text);
1576 m_bStatusWarning = false;
1577 m_CtrlStatusText.Invalidate();
1580 GitRev *prevRev=NULL, *curRev=NULL;
1582 if( m_CurrentRebaseIndex >= 0 && m_CurrentRebaseIndex< m_CommitList.m_arShownList.GetSize())
1584 curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1587 for (int i = 0; i < m_CommitList.m_arShownList.GetSize(); ++i)
1589 prevRev=(GitRev*)m_CommitList.m_arShownList[i];
1590 if (prevRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_CURRENT)
1592 prevRev->GetRebaseAction() &= ~CGitLogListBase::LOGACTIONS_REBASE_CURRENT;
1593 m_CommitList.GetItemRect(i,&rect,LVIR_BOUNDS);
1594 m_CommitList.InvalidateRect(rect);
1598 if(curRev)
1600 curRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_CURRENT;
1601 m_CommitList.GetItemRect(m_CurrentRebaseIndex,&rect,LVIR_BOUNDS);
1602 m_CommitList.InvalidateRect(rect);
1604 m_CommitList.EnsureVisible(m_CurrentRebaseIndex,FALSE);
1607 void CRebaseDlg::UpdateCurrentStatus()
1609 SetContinueButtonText();
1610 SetControlEnable();
1611 UpdateProgress();
1612 if (m_RebaseStage == REBASE_DONE)
1613 GetDlgItem(IDC_REBASE_CONTINUE)->SetFocus();
1616 void CRebaseDlg::AddLogString(CString str)
1618 this->m_wndOutputRebase.SendMessage(SCI_SETREADONLY, FALSE);
1619 CStringA sTextA = m_wndOutputRebase.StringForControl(str);//CUnicodeUtils::GetUTF8(str);
1620 this->m_wndOutputRebase.SendMessage(SCI_DOCUMENTEND);
1621 this->m_wndOutputRebase.SendMessage(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)sTextA);
1622 this->m_wndOutputRebase.SendMessage(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)"\n");
1623 this->m_wndOutputRebase.SendMessage(SCI_SETREADONLY, TRUE);
1626 int CRebaseDlg::GetCurrentCommitID()
1628 if(m_CommitList.m_IsOldFirst)
1630 return this->m_CurrentRebaseIndex+1;
1633 else
1635 return m_CommitList.GetItemCount()-m_CurrentRebaseIndex;
1639 int CRebaseDlg::IsCommitEmpty(const CGitHash& hash)
1641 CString cmd, tree, ptree;
1642 cmd.Format(_T("git.exe rev-parse -q --verify %s^{tree}"), hash.ToString());
1643 if (g_Git.Run(cmd, &tree, CP_UTF8))
1645 AddLogString(cmd);
1646 AddLogString(tree);
1647 return -1;
1649 cmd.Format(_T("git.exe rev-parse -q --verify %s^^{tree}"), hash.ToString());
1650 if (g_Git.Run(cmd, &ptree, CP_UTF8))
1651 ptree = _T("4b825dc642cb6eb9a060e54bf8d69288fbee4904"); // empty tree
1652 return tree == ptree;
1655 int CRebaseDlg::DoRebase()
1657 CString cmd,out;
1658 if(m_CurrentRebaseIndex <0)
1659 return 0;
1660 if(m_CurrentRebaseIndex >= m_CommitList.GetItemCount() )
1661 return 0;
1663 GitRev *pRev = (GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1664 int mode = pRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_MODE_MASK;
1665 CString nocommit;
1667 if (mode == CGitLogListBase::LOGACTIONS_REBASE_SKIP)
1669 pRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1670 return 0;
1673 bool nextCommitIsSquash = (CheckNextCommitIsSquash() == 0);
1674 if (nextCommitIsSquash || mode != CGitLogListBase::LOGACTIONS_REBASE_PICK)
1675 { // next commit is squash or not pick
1676 if (!this->m_SquashMessage.IsEmpty())
1677 this->m_SquashMessage += _T("\n\n");
1678 this->m_SquashMessage += pRev->GetSubject();
1679 this->m_SquashMessage += _T("\n");
1680 this->m_SquashMessage += pRev->GetBody().TrimRight();
1681 if (m_bAddCherryPickedFrom)
1683 if (!pRev->GetBody().IsEmpty())
1684 m_SquashMessage += _T("\n");
1685 m_SquashMessage += _T("(cherry picked from commit ");
1686 m_SquashMessage += pRev->m_CommitHash.ToString();
1687 m_SquashMessage += _T(")");
1690 else
1692 this->m_SquashMessage.Empty();
1693 m_SquashFirstMetaData.Empty();
1696 if ((nextCommitIsSquash && mode != CGitLogListBase::LOGACTIONS_REBASE_EDIT) || mode == CGitLogListBase::LOGACTIONS_REBASE_SQUASH)
1697 { // next or this commit is squash (don't do this on edit->squash sequence)
1698 nocommit=_T(" --no-commit ");
1701 if (nextCommitIsSquash && mode != CGitLogListBase::LOGACTIONS_REBASE_SQUASH)
1702 m_SquashFirstMetaData.Format(_T("--date=%s --author=\"%s <%s>\" "), pRev->GetAuthorDate().Format(_T("%Y-%m-%dT%H:%M:%S")), pRev->GetAuthorName(), pRev->GetAuthorEmail());
1704 CString log;
1705 log.Format(_T("%s %d: %s"), CGitLogListBase::GetRebaseActionName(mode), GetCurrentCommitID(), pRev->m_CommitHash.ToString());
1706 AddLogString(log);
1707 AddLogString(pRev->GetSubject());
1708 if (pRev->GetSubject().IsEmpty())
1710 CMessageBox::Show(m_hWnd, IDS_PROC_REBASE_EMPTYCOMMITMSG, IDS_APPNAME, MB_OK | MB_ICONEXCLAMATION);
1711 mode = CGitLogListBase::LOGACTIONS_REBASE_EDIT;
1714 CString cherryPickedFrom;
1715 if (m_bAddCherryPickedFrom)
1716 cherryPickedFrom = _T("-x ");
1717 else if (!m_IsCherryPick && nocommit.IsEmpty())
1718 cherryPickedFrom = _T("--ff "); // for issue #1833: "If the current HEAD is the same as the parent of the cherry-pick’ed commit, then a fast forward to this commit will be performed."
1720 int isEmpty = IsCommitEmpty(pRev->m_CommitHash);
1721 if (isEmpty == 1)
1722 cherryPickedFrom += _T("--allow-empty ");
1723 else if (isEmpty < 0)
1724 return -1;
1726 while (true)
1728 cmd.Format(_T("git.exe cherry-pick %s%s %s"), cherryPickedFrom, nocommit, pRev->m_CommitHash.ToString());
1730 if(g_Git.Run(cmd,&out,CP_UTF8))
1732 AddLogString(out);
1733 CTGitPathList list;
1734 if(g_Git.ListConflictFile(list))
1736 AddLogString(_T("Get conflict files fail"));
1737 return -1;
1739 if (list.IsEmpty())
1741 if (mode == CGitLogListBase::LOGACTIONS_REBASE_PICK)
1743 if (m_pTaskbarList)
1744 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
1745 int choose = -1;
1746 if (!m_bAutoSkipFailedCommit)
1748 choose = CMessageBox::ShowCheck(m_hWnd, IDS_CHERRYPICKFAILEDSKIP, IDS_APPNAME, 1, IDI_QUESTION, IDS_SKIPBUTTON, IDS_MSGBOX_RETRY, IDS_MSGBOX_CANCEL, NULL, IDS_DO_SAME_FOR_REST, &m_bAutoSkipFailedCommit);
1749 if (choose == 2)
1751 m_bAutoSkipFailedCommit = FALSE;
1752 continue; // retry cherry pick
1755 if (m_bAutoSkipFailedCommit || choose == 1)
1757 bool resetOK = false;
1758 while (!resetOK)
1760 out.Empty();
1761 if (g_Git.Run(_T("git.exe reset --hard"), &out, CP_UTF8))
1763 AddLogString(out);
1764 if (CMessageBox::Show(m_hWnd, _T("Retry?\nUnrecoverable error on cleanup:\n") + out, _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
1765 break;
1767 else
1768 resetOK = true;
1771 if (resetOK)
1773 pRev->GetRebaseAction() = CGitLogListBase::LOGACTIONS_REBASE_SKIP;
1774 m_CommitList.Invalidate();
1775 return 0;
1779 m_RebaseStage = REBASE_ERROR;
1780 AddLogString(_T("An unrecoverable error occurred."));
1781 return -1;
1783 if (mode == CGitLogListBase::LOGACTIONS_REBASE_EDIT)
1785 this->m_RebaseStage = REBASE_EDIT ;
1786 return -1; // Edit return -1 to stop rebase.
1788 // Squash Case
1789 if(CheckNextCommitIsSquash())
1790 { // no squash
1791 // let user edit last commmit message
1792 this->m_RebaseStage = REBASE_SQUASH_EDIT;
1793 return -1;
1797 if (m_pTaskbarList)
1798 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
1799 if (mode == CGitLogListBase::LOGACTIONS_REBASE_SQUASH)
1800 m_RebaseStage = REBASE_SQUASH_CONFLICT;
1801 else
1802 m_RebaseStage = REBASE_CONFLICT;
1803 return -1;
1806 else
1808 AddLogString(out);
1809 if (mode == CGitLogListBase::LOGACTIONS_REBASE_PICK)
1811 pRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1812 return 0;
1814 if (mode == CGitLogListBase::LOGACTIONS_REBASE_EDIT)
1816 this->m_RebaseStage = REBASE_EDIT ;
1817 return -1; // Edit return -1 to stop rebase.
1820 // Squash Case
1821 if(CheckNextCommitIsSquash())
1822 { // no squash
1823 // let user edit last commmit message
1824 this->m_RebaseStage = REBASE_SQUASH_EDIT;
1825 return -1;
1827 else if (mode == CGitLogListBase::LOGACTIONS_REBASE_SQUASH)
1828 pRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1831 return 0;
1835 BOOL CRebaseDlg::IsEnd()
1837 if(m_CommitList.m_IsOldFirst)
1838 return m_CurrentRebaseIndex>= this->m_CommitList.GetItemCount();
1839 else
1840 return m_CurrentRebaseIndex<0;
1843 int CRebaseDlg::RebaseThread()
1845 CBlockCacheForPath cacheBlock(g_Git.m_CurrentDir);
1847 int ret=0;
1848 while(1)
1850 if( m_RebaseStage == REBASE_START )
1852 if( this->StartRebase() )
1854 ret = -1;
1855 break;
1857 m_RebaseStage = REBASE_CONTINUE;
1860 else if( m_RebaseStage == REBASE_CONTINUE )
1862 this->GoNext();
1863 SendMessage(MSG_REBASE_UPDATE_UI);
1864 if(IsEnd())
1866 ret = 0;
1867 m_RebaseStage = REBASE_FINISH;
1870 else
1872 ret = DoRebase();
1874 if( ret )
1876 break;
1881 else if( m_RebaseStage == REBASE_FINISH )
1883 SendMessage(MSG_REBASE_UPDATE_UI);
1884 m_RebaseStage = REBASE_DONE;
1885 break;
1888 else
1890 break;
1892 this->PostMessage(MSG_REBASE_UPDATE_UI);
1895 InterlockedExchange(&m_bThreadRunning, FALSE);
1896 this->PostMessage(MSG_REBASE_UPDATE_UI);
1897 return ret;
1900 void CRebaseDlg::ListConflictFile()
1902 this->m_FileListCtrl.Clear();
1903 m_FileListCtrl.SetHasCheckboxes(true);
1904 CTGitPathList list;
1905 CTGitPath path;
1906 list.AddPath(path);
1908 m_FileListCtrl.m_bIsRevertTheirMy = !m_IsCherryPick;
1910 this->m_FileListCtrl.GetStatus(&list,true);
1911 this->m_FileListCtrl.Show(CTGitPath::LOGACTIONS_UNMERGED|CTGitPath::LOGACTIONS_MODIFIED|CTGitPath::LOGACTIONS_ADDED|CTGitPath::LOGACTIONS_DELETED,
1912 CTGitPath::LOGACTIONS_UNMERGED);
1914 m_FileListCtrl.Check(GITSLC_SHOWFILES);
1915 bool hasSubmoduleChange = false;
1916 for (int i = 0; i < m_FileListCtrl.GetItemCount(); i++)
1918 CTGitPath *entry = (CTGitPath *)m_FileListCtrl.GetItemData(i);
1919 if (entry->IsDirectory())
1921 hasSubmoduleChange = true;
1922 break;
1926 if (hasSubmoduleChange)
1928 m_CtrlStatusText.SetWindowText(m_sStatusText + _T(", ") + CString(MAKEINTRESOURCE(IDS_CARE_SUBMODULE_CHANGES)));
1929 m_bStatusWarning = true;
1930 m_CtrlStatusText.Invalidate();
1932 else
1934 m_CtrlStatusText.SetWindowText(m_sStatusText);
1935 m_bStatusWarning = false;
1936 m_CtrlStatusText.Invalidate();
1940 LRESULT CRebaseDlg::OnRebaseUpdateUI(WPARAM,LPARAM)
1942 if (m_RebaseStage == REBASE_FINISH)
1944 FinishRebase();
1945 return 0;
1947 UpdateCurrentStatus();
1948 if (m_RebaseStage == REBASE_DONE && m_pTaskbarList)
1949 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS); // do not show progress on taskbar any more to show we finished
1950 if(m_CurrentRebaseIndex <0)
1951 return 0;
1952 if(m_CurrentRebaseIndex >= m_CommitList.GetItemCount() )
1953 return 0;
1954 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1956 switch(m_RebaseStage)
1958 case REBASE_CONFLICT:
1959 case REBASE_SQUASH_CONFLICT:
1961 ListConflictFile();
1962 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_CONFLICT);
1963 if (m_pTaskbarList)
1964 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
1965 this->m_LogMessageCtrl.Call(SCI_SETREADONLY, FALSE);
1966 CString logMessage;
1967 if (m_IsCherryPick)
1969 CString dotGitPath;
1970 GitAdminDir::GetAdminDirPath(g_Git.m_CurrentDir, dotGitPath);
1971 // vanilla git also re-uses MERGE_MSG on conflict (listing all conflicted files)
1972 // and it's also needed for cherry-pick in order to get cherry-picked-from included on conflicts
1973 CGit::LoadTextFile(dotGitPath + _T("MERGE_MSG"), logMessage);
1975 if (logMessage.IsEmpty())
1976 logMessage = curRev->GetSubject() + _T("\n") + curRev->GetBody();
1977 this->m_LogMessageCtrl.SetText(logMessage);
1978 break;
1980 case REBASE_EDIT:
1981 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_MESSAGE);
1982 if (m_pTaskbarList)
1983 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_PAUSED);
1984 this->m_LogMessageCtrl.Call(SCI_SETREADONLY, FALSE);
1985 if (m_bAddCherryPickedFrom)
1987 // Since the new commit is done and the HEAD points to it,
1988 // just using the new body modified by git self.
1989 GitRev headRevision;
1990 headRevision.GetCommit(_T("HEAD"));
1991 m_LogMessageCtrl.SetText(headRevision.GetSubject() + _T("\n") + headRevision.GetBody());
1993 else
1994 m_LogMessageCtrl.SetText(curRev->GetSubject() + _T("\n") + curRev->GetBody());
1995 break;
1996 case REBASE_SQUASH_EDIT:
1997 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_MESSAGE);
1998 this->m_LogMessageCtrl.Call(SCI_SETREADONLY, FALSE);
1999 this->m_LogMessageCtrl.SetText(this->m_SquashMessage);
2000 if (m_pTaskbarList)
2001 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_PAUSED);
2002 break;
2003 default:
2004 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
2006 return 0;
2008 void CRebaseDlg::OnCancel()
2010 OnBnClickedAbort();
2012 void CRebaseDlg::OnBnClickedAbort()
2014 if (m_pTaskbarList)
2015 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
2017 m_tooltips.Pop();
2019 CString cmd,out;
2020 if(m_OrigUpstreamHash.IsEmpty())
2022 __super::OnCancel();
2025 if(m_RebaseStage == CHOOSE_BRANCH || m_RebaseStage== CHOOSE_COMMIT_PICK_MODE)
2027 goto end;
2030 if(CMessageBox::Show(NULL, IDS_PROC_REBASE_ABORT, IDS_APPNAME, MB_YESNO) != IDYES)
2031 goto end;
2033 if(this->m_IsFastForward)
2035 cmd.Format(_T("git.exe reset --hard %s --"),this->m_OrigBranchHash.ToString());
2036 while (true)
2038 out.Empty();
2039 if (g_Git.Run(cmd, &out, CP_UTF8))
2041 AddLogString(out);
2042 if (CMessageBox::Show(m_hWnd, _T("Retry?\nUnrecoverable error on cleanup:\n") + out, _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
2043 break;
2045 else
2046 break;
2048 __super::OnCancel();
2049 goto end;
2052 if (m_IsCherryPick) // there are not "branch" at cherry pick mode
2054 cmd.Format(_T("git.exe reset --hard %s --"), m_OrigUpstreamHash.ToString());
2055 while (true)
2057 out.Empty();
2058 if (g_Git.Run(cmd, &out, CP_UTF8))
2060 AddLogString(out);
2061 if (CMessageBox::Show(m_hWnd, _T("Retry?\nUnrecoverable error on cleanup:\n") + out, _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
2062 break;
2064 else
2065 break;
2068 __super::OnCancel();
2069 goto end;
2072 if (m_OrigHEADBranch == m_BranchCtrl.GetString())
2074 if (IsLocalBranch(m_OrigHEADBranch))
2075 cmd.Format(_T("git.exe checkout -f -B %s %s --"), m_BranchCtrl.GetString(), m_OrigBranchHash.ToString());
2076 else
2077 cmd.Format(_T("git.exe checkout -f %s --"), m_OrigBranchHash.ToString());
2078 if (g_Git.Run(cmd, &out, CP_UTF8))
2080 AddLogString(out);
2081 ::MessageBox(m_hWnd, _T("Unrecoverable error on cleanup:\n") + out, _T("TortoiseGit"), MB_ICONERROR);
2082 __super::OnCancel();
2083 goto end;
2086 cmd.Format(_T("git.exe reset --hard %s --"), m_OrigBranchHash.ToString());
2087 while (true)
2089 out.Empty();
2090 if (g_Git.Run(cmd, &out, CP_UTF8))
2092 AddLogString(out);
2093 if (CMessageBox::Show(m_hWnd, _T("Retry?\nUnrecoverable error on cleanup:\n") + out, _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
2094 break;
2096 else
2097 break;
2100 else
2102 if (m_OrigHEADBranch != g_Git.GetCurrentBranch(true))
2104 if (IsLocalBranch(m_OrigHEADBranch))
2105 cmd.Format(_T("git.exe checkout -f -B %s %s --"), m_OrigHEADBranch, m_OrigHEADHash.ToString());
2106 else
2107 cmd.Format(_T("git.exe checkout -f %s --"), m_OrigHEADHash.ToString());
2108 if (g_Git.Run(cmd, &out, CP_UTF8))
2110 AddLogString(out);
2111 ::MessageBox(m_hWnd, _T("Unrecoverable error on cleanup:\n") + out, _T("TortoiseGit"), MB_ICONERROR);
2112 // continue to restore moved branch
2116 cmd.Format(_T("git.exe reset --hard %s --"), m_OrigHEADHash.ToString());
2117 while (true)
2119 out.Empty();
2120 if (g_Git.Run(cmd, &out, CP_UTF8))
2122 AddLogString(out);
2123 if (CMessageBox::Show(m_hWnd, _T("Retry?\nUnrecoverable error on cleanup:\n") + out, _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
2124 break;
2126 else
2127 break;
2130 // restore moved branch
2131 if (IsLocalBranch(m_BranchCtrl.GetString()))
2133 cmd.Format(_T("git.exe branch -f %s %s --"), m_BranchCtrl.GetString(), m_OrigBranchHash.ToString());
2134 if (g_Git.Run(cmd, &out, CP_UTF8))
2136 AddLogString(out);
2137 ::MessageBox(m_hWnd, _T("Unrecoverable error on cleanup:\n") + out, _T("TortoiseGit"), MB_ICONERROR);
2138 __super::OnCancel();
2139 goto end;
2143 __super::OnCancel();
2144 end:
2145 CheckRestoreStash();
2148 void CRebaseDlg::OnBnClickedButtonReverse()
2150 CString temp = m_BranchCtrl.GetString();
2151 m_BranchCtrl.AddString(m_UpstreamCtrl.GetString());
2152 m_UpstreamCtrl.AddString(temp);
2153 OnCbnSelchangeUpstream();
2156 void CRebaseDlg::OnBnClickedButtonBrowse()
2158 if(CBrowseRefsDlg::PickRefForCombo(&m_UpstreamCtrl))
2159 OnCbnSelchangeUpstream();
2162 void CRebaseDlg::OnBnClickedRebaseCheckForce()
2164 this->UpdateData();
2165 this->FetchLogList();
2168 void CRebaseDlg::OnBnClickedRebasePostButton()
2170 this->m_Upstream=this->m_UpstreamCtrl.GetString();
2171 this->m_Branch=this->m_BranchCtrl.GetString();
2173 this->EndDialog((int)(IDC_REBASE_POST_BUTTON+this->m_PostButton.GetCurrentEntry()));
2176 LRESULT CRebaseDlg::OnGitStatusListCtrlNeedsRefresh(WPARAM, LPARAM)
2178 Refresh();
2179 return 0;
2182 void CRebaseDlg::Refresh()
2184 if (m_RebaseStage == REBASE_CONFLICT || m_RebaseStage == REBASE_SQUASH_CONFLICT)
2186 ListConflictFile();
2187 return;
2190 if(this->m_IsCherryPick)
2191 return ;
2193 if(this->m_RebaseStage == CHOOSE_BRANCH )
2195 this->UpdateData();
2196 this->FetchLogList();
2200 void CRebaseDlg::OnBnClickedButtonUp2()
2202 POSITION pos;
2203 pos = m_CommitList.GetFirstSelectedItemPosition();
2205 // do nothing if the first selected item is the first item in the list
2206 int idx = m_CommitList.GetNextSelectedItem(pos);
2207 if (idx == 0)
2208 return;
2210 bool moveToTop = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);
2211 int move = moveToTop ? idx : 1;
2212 pos = m_CommitList.GetFirstSelectedItemPosition();
2214 bool changed = false;
2215 while(pos)
2217 int index=m_CommitList.GetNextSelectedItem(pos);
2218 if(index>=1)
2220 CGitHash old = m_CommitList.m_logEntries[index - move];
2221 m_CommitList.m_logEntries[index - move] = m_CommitList.m_logEntries[index];
2222 m_CommitList.m_logEntries[index] = old;
2223 m_CommitList.RecalculateShownList(&m_CommitList.m_arShownList);
2224 m_CommitList.SetItemState(index - move, LVIS_SELECTED, LVIS_SELECTED);
2225 m_CommitList.SetItemState(index, 0, LVIS_SELECTED);
2226 changed = true;
2229 if (changed)
2231 pos = m_CommitList.GetFirstSelectedItemPosition();
2232 m_CommitList.EnsureVisible(m_CommitList.GetNextSelectedItem(pos), false);
2233 m_CommitList.Invalidate();
2234 m_CommitList.SetFocus();
2238 void CRebaseDlg::OnBnClickedButtonDown2()
2240 if (m_CommitList.GetSelectedCount() == 0)
2241 return;
2243 bool moveToBottom = !!(GetAsyncKeyState(VK_SHIFT) & 0x8000);
2244 POSITION pos;
2245 pos = m_CommitList.GetFirstSelectedItemPosition();
2246 bool changed = false;
2247 // use an array to store all selected item indexes; the user won't select too much items
2248 int* indexes = NULL;
2249 indexes = new int[m_CommitList.GetSelectedCount()];
2250 int i = 0;
2251 while(pos)
2253 indexes[i++] = m_CommitList.GetNextSelectedItem(pos);
2255 int distanceToBottom = m_CommitList.GetItemCount() - 1 - indexes[m_CommitList.GetSelectedCount() - 1];
2256 int move = moveToBottom ? distanceToBottom : 1;
2257 // don't move any item if the last selected item is the last item in the m_CommitList
2258 // (that would change the order of the selected items)
2259 if (distanceToBottom > 0)
2261 // iterate over the indexes backwards in order to correctly move multiselected items
2262 for (i = m_CommitList.GetSelectedCount() - 1; i >= 0; i--)
2264 int index = indexes[i];
2265 CGitHash old = m_CommitList.m_logEntries[index + move];
2266 m_CommitList.m_logEntries[index + move] = m_CommitList.m_logEntries[index];
2267 m_CommitList.m_logEntries[index] = old;
2268 m_CommitList.RecalculateShownList(&m_CommitList.m_arShownList);
2269 m_CommitList.SetItemState(index, 0, LVIS_SELECTED);
2270 m_CommitList.SetItemState(index + move, LVIS_SELECTED, LVIS_SELECTED);
2271 changed = true;
2274 m_CommitList.EnsureVisible(indexes[m_CommitList.GetSelectedCount() - 1] + move, false);
2275 delete [] indexes;
2276 indexes = NULL;
2277 if (changed)
2279 m_CommitList.Invalidate();
2280 m_CommitList.SetFocus();
2284 LRESULT CRebaseDlg::OnTaskbarBtnCreated(WPARAM /*wParam*/, LPARAM /*lParam*/)
2286 m_pTaskbarList.Release();
2287 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
2288 SetUUIDOverlayIcon(m_hWnd);
2289 return 0;
2292 void CRebaseDlg::OnLvnItemchangedLoglist(NMHDR *pNMHDR, LRESULT *pResult)
2294 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
2295 *pResult = 0;
2296 if(m_CommitList.m_bNoDispUpdates)
2297 return;
2298 if (pNMLV->iItem >= 0)
2300 this->m_CommitList.m_nSearchIndex = pNMLV->iItem;
2301 if (pNMLV->iSubItem != 0)
2302 return;
2303 if ((pNMLV->iItem == m_CommitList.m_arShownList.GetCount()))
2305 // remove the selected state
2306 if (pNMLV->uChanged & LVIF_STATE)
2308 m_CommitList.SetItemState(pNMLV->iItem, 0, LVIS_SELECTED);
2309 FillLogMessageCtrl();
2311 return;
2313 if (pNMLV->uChanged & LVIF_STATE)
2315 FillLogMessageCtrl();
2318 else
2320 FillLogMessageCtrl();
2324 void CRebaseDlg::FillLogMessageCtrl()
2326 int selCount = m_CommitList.GetSelectedCount();
2327 if (selCount == 1 && (m_RebaseStage == CHOOSE_BRANCH || m_RebaseStage == CHOOSE_COMMIT_PICK_MODE))
2329 POSITION pos = m_CommitList.GetFirstSelectedItemPosition();
2330 int selIndex = m_CommitList.GetNextSelectedItem(pos);
2331 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_CommitList.m_arShownList.SafeGetAt(selIndex));
2332 m_FileListCtrl.UpdateWithGitPathList(pLogEntry->GetFiles(&m_CommitList));
2333 m_FileListCtrl.m_CurrentVersion = pLogEntry->m_CommitHash;
2334 m_FileListCtrl.Show(GITSLC_SHOWVERSIONED);
2335 m_LogMessageCtrl.Call(SCI_SETREADONLY, FALSE);
2336 m_LogMessageCtrl.SetText(pLogEntry->GetSubject() + _T("\n") + pLogEntry->GetBody());
2337 m_LogMessageCtrl.Call(SCI_SETREADONLY, TRUE);
2340 void CRebaseDlg::OnBnClickedCheckCherryPickedFrom()
2342 UpdateData();
2345 LRESULT CRebaseDlg::OnRebaseActionMessage(WPARAM, LPARAM)
2347 if (m_RebaseStage == REBASE_ERROR || m_RebaseStage == REBASE_CONFLICT)
2349 GitRev *pRev = (GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
2350 int mode = pRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_MODE_MASK;
2351 if (mode == CGitLogListBase::LOGACTIONS_REBASE_SKIP)
2353 CString out;
2354 bool resetOK = false;
2355 while (!resetOK)
2357 out.Empty();
2358 if (g_Git.Run(_T("git.exe reset --hard"), &out, CP_UTF8))
2360 AddLogString(out);
2361 if (CMessageBox::Show(m_hWnd, _T("Retry?\nUnrecoverable error on cleanup:\n") + out, _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
2362 break;
2364 else
2365 resetOK = true;
2368 if (resetOK)
2370 m_FileListCtrl.Clear();
2371 m_RebaseStage = REBASE_CONTINUE;
2372 UpdateCurrentStatus();
2376 return 0;
2380 void CRebaseDlg::OnBnClickedSplitAllOptions()
2382 switch (m_SplitAllOptions.GetCurrentEntry())
2384 case 0:
2385 SetAllRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_PICK);
2386 break;
2387 case 1:
2388 SetAllRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_SQUASH);
2389 break;
2390 case 2:
2391 SetAllRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_EDIT);
2392 break;
2393 default:
2394 ATLASSERT(false);
2398 void CRebaseDlg::OnBnClickedRebaseSplitCommit()
2400 UpdateData();
2403 static bool GetCompareHash(const CString& ref, const CGitHash& hash)
2405 CGitHash refHash;
2406 if (g_Git.GetHash(refHash, ref))
2407 MessageBox(nullptr, g_Git.GetGitLastErr(_T("Could not get hash of \"") + ref + _T("\".")), _T("TortoiseGit"), MB_ICONERROR);
2408 return refHash.IsEmpty() || (hash == refHash);
2411 void CRebaseDlg::OnBnClickedButtonOnto()
2413 m_Onto = CBrowseRefsDlg::PickRef(false, m_Onto);
2414 if (!m_Onto.IsEmpty())
2416 // make sure that the user did not select upstream, selected branch or HEAD
2417 CGitHash hash;
2418 if (g_Git.GetHash(hash, m_Onto))
2420 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of \"") + m_BranchCtrl.GetString() + _T("\".")), _T("TortoiseGit"), MB_ICONERROR);
2421 m_Onto.Empty();
2422 ((CButton*)GetDlgItem(IDC_BUTTON_ONTO))->SetCheck(m_Onto.IsEmpty() ? BST_UNCHECKED : BST_CHECKED);
2423 return;
2425 if (GetCompareHash(_T("HEAD"), hash) || GetCompareHash(m_UpstreamCtrl.GetString(), hash) || GetCompareHash(m_BranchCtrl.GetString(), hash))
2426 m_Onto.Empty();
2428 if (m_Onto.IsEmpty())
2429 m_tooltips.DelTool(IDC_BUTTON_ONTO);
2430 else
2431 m_tooltips.AddTool(IDC_BUTTON_ONTO, m_Onto);
2432 ((CButton*)GetDlgItem(IDC_BUTTON_ONTO))->SetCheck(m_Onto.IsEmpty() ? BST_UNCHECKED : BST_CHECKED);
2433 FetchLogList();