Improve error handling in GitRev
[TortoiseGit.git] / src / TortoiseProc / RebaseDlg.cpp
blobc7f7381e80d86a46b3a6864ee60f34d538abff83
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 GitRevLoglist& 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;
641 if (rev.GetCommit(text))
643 MessageBox(rev.GetLastErr(), _T("TortoiseGit"), MB_ICONERROR);
644 pBranch->DisableTooltip();
645 return;
648 tooltip.Format(_T("%s: %s\n%s: %s <%s>\n%s: %s\n%s:\n%s\n%s"),
649 CString(MAKEINTRESOURCE(IDS_LOG_REVISION)),
650 rev.m_CommitHash.ToString(),
651 CString(MAKEINTRESOURCE(IDS_LOG_AUTHOR)),
652 rev.GetAuthorName(),
653 rev.GetAuthorEmail(),
654 CString(MAKEINTRESOURCE(IDS_LOG_DATE)),
655 CLoglistUtils::FormatDateAndTime(rev.GetAuthorDate(), DATE_LONGDATE),
656 CString(MAKEINTRESOURCE(IDS_LOG_MESSAGE)),
657 rev.GetSubject(),
658 rev.GetBody());
660 pBranch->DisableTooltip();
661 this->m_tooltips.AddTool(pBranch->GetComboBoxCtrl(),tooltip);
665 BOOL CRebaseDlg::PreTranslateMessage(MSG*pMsg)
667 if (pMsg->message == WM_KEYDOWN)
669 switch (pMsg->wParam)
671 case ' ':
672 if (LogListHasFocus(pMsg->hwnd)
673 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_PICK)
674 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SQUASH)
675 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_EDIT)
676 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SKIP))
678 m_CommitList.ShiftSelectedRebaseAction();
679 return TRUE;
681 break;
682 case 'P':
683 if (LogListHasFocus(pMsg->hwnd) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_PICK))
685 m_CommitList.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_PICK);
686 return TRUE;
688 break;
689 case 'S':
690 if (LogListHasFocus(pMsg->hwnd) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SKIP))
692 m_CommitList.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_SKIP);
693 return TRUE;
695 break;
696 case 'Q':
697 if (LogListHasFocus(pMsg->hwnd) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SQUASH))
699 m_CommitList.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_SQUASH);
700 return TRUE;
702 break;
703 case 'E':
704 if (LogListHasFocus(pMsg->hwnd) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_EDIT))
706 m_CommitList.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_EDIT);
707 return TRUE;
709 break;
710 case 'A':
711 if(LogListHasFocus(pMsg->hwnd) && GetAsyncKeyState(VK_CONTROL) & 0x8000)
713 // select all entries
714 for (int i = 0; i < m_CommitList.GetItemCount(); ++i)
716 m_CommitList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
718 return TRUE;
720 break;
721 case VK_F5:
723 Refresh();
724 return TRUE;
726 break;
727 case VK_RETURN:
729 if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
731 if (GetDlgItem(IDC_REBASE_CONTINUE)->IsWindowEnabled())
732 GetDlgItem(IDC_REBASE_CONTINUE)->SetFocus();
733 else if (GetDlgItem(IDC_REBASE_ABORT)->IsWindowEnabled())
734 GetDlgItem(IDC_REBASE_ABORT)->SetFocus();
735 else
736 GetDlgItem(IDHELP)->SetFocus();
737 return TRUE;
740 break;
741 /* Avoid TAB control destroy but dialog exist*/
742 case VK_ESCAPE:
743 case VK_CANCEL:
745 TCHAR buff[128] = { 0 };
746 ::GetClassName(pMsg->hwnd,buff,128);
749 /* Use MSFTEDIT_CLASS http://msdn.microsoft.com/en-us/library/bb531344.aspx */
750 if (_tcsnicmp(buff, MSFTEDIT_CLASS, 128) == 0 || //Unicode and MFC 2012 and later
751 _tcsnicmp(buff, RICHEDIT_CLASS, 128) == 0 || //ANSI or MFC 2010
752 _tcsnicmp(buff,_T("Scintilla"),128)==0 ||
753 _tcsnicmp(buff,_T("SysListView32"),128)==0||
754 ::GetParent(pMsg->hwnd) == this->m_ctrlTabCtrl.m_hWnd)
756 this->PostMessage(WM_KEYDOWN,VK_ESCAPE,0);
757 return TRUE;
762 else if (pMsg->message == WM_NEXTDLGCTL)
764 HWND hwnd = GetFocus()->GetSafeHwnd();
765 if (hwnd == m_LogMessageCtrl.GetSafeHwnd() || hwnd == m_wndOutputRebase.GetSafeHwnd())
767 if (GetDlgItem(IDC_REBASE_CONTINUE)->IsWindowEnabled())
768 GetDlgItem(IDC_REBASE_CONTINUE)->SetFocus();
769 else if (GetDlgItem(IDC_REBASE_ABORT)->IsWindowEnabled())
770 GetDlgItem(IDC_REBASE_ABORT)->SetFocus();
771 else
772 GetDlgItem(IDHELP)->SetFocus();
773 return TRUE;
776 m_tooltips.RelayEvent(pMsg);
777 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
780 bool CRebaseDlg::LogListHasFocus(HWND hwnd)
782 TCHAR buff[128] = { 0 };
783 ::GetClassName(hwnd, buff, 128);
785 if(_tcsnicmp(buff, _T("SysListView32"), 128) == 0)
786 return true;
787 return false;
790 bool CRebaseDlg::LogListHasMenuItem(int i)
792 return (m_CommitList.m_ContextMenuMask & m_CommitList.GetContextMenuBit(i)) != 0;
795 int CRebaseDlg::CheckRebaseCondition()
797 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
799 if( !g_Git.CheckCleanWorkTree() )
801 if (CMessageBox::Show(NULL, IDS_ERROR_NOCLEAN_STASH, IDS_APPNAME, 1, IDI_QUESTION, IDS_STASHBUTTON, IDS_ABORTBUTTON) == 1)
803 CString cmd,out;
804 cmd=_T("git.exe stash");
805 this->AddLogString(cmd);
806 if (g_Git.Run(cmd, &out, CP_UTF8))
808 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
809 return -1;
811 m_bStashed = true;
813 else
814 return -1;
816 //Todo Check $REBASE_ROOT
817 //Todo Check $DOTEST
819 if (!CAppUtils::CheckUserData())
820 return -1;
822 //Todo call pre_rebase_hook
823 return 0;
826 void CRebaseDlg::CheckRestoreStash()
828 if (m_bStashed && CMessageBox::Show(nullptr, IDS_DCOMMIT_STASH_POP, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION) == IDYES)
829 CAppUtils::StashPop();
830 m_bStashed = false;
833 int CRebaseDlg::StartRebase()
835 CString cmd,out;
836 m_FileListCtrl.m_bIsRevertTheirMy = !m_IsCherryPick;
838 m_OrigHEADBranch = g_Git.GetCurrentBranch(true);
840 m_OrigHEADHash.Empty();
841 if (g_Git.GetHash(m_OrigHEADHash, _T("HEAD")))
843 AddLogString(CString(MAKEINTRESOURCE(IDS_PROC_NOHEAD)));
844 return -1;
846 //Todo
847 //git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null ||
848 // echo "detached HEAD" > "$DOTEST"/head-name
850 cmd.Format(_T("git.exe update-ref ORIG_HEAD ") + m_OrigHEADHash.ToString());
851 if(g_Git.Run(cmd,&out,CP_UTF8))
853 AddLogString(_T("update ORIG_HEAD Fail"));
854 return -1;
857 m_OrigUpstreamHash.Empty();
858 if (g_Git.GetHash(m_OrigUpstreamHash, (m_IsCherryPick || m_Onto.IsEmpty()) ? m_UpstreamCtrl.GetString() : m_Onto))
860 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);
861 return -1;
864 if( !this->m_IsCherryPick )
866 cmd.Format(_T("git.exe checkout -f %s --"), m_OrigUpstreamHash.ToString());
867 this->AddLogString(cmd);
868 while (true)
870 out.Empty();
871 if (g_Git.Run(cmd, &out, CP_UTF8))
873 this->AddLogString(out);
874 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
875 return -1;
877 else
878 break;
882 CString log;
883 if( !this->m_IsCherryPick )
885 if (g_Git.GetHash(m_OrigBranchHash, m_BranchCtrl.GetString()))
887 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of \"") + m_BranchCtrl.GetString() + _T("\".")), _T("TortoiseGit"), MB_ICONERROR);
888 return -1;
890 log.Format(_T("%s\r\n"), CString(MAKEINTRESOURCE(IDS_PROC_REBASE_STARTREBASE)));
892 else
893 log.Format(_T("%s\r\n"), CString(MAKEINTRESOURCE(IDS_PROC_REBASE_STARTCHERRYPICK)));
895 this->AddLogString(log);
896 return 0;
898 int CRebaseDlg::VerifyNoConflict()
900 int hasConflicts = g_Git.HasWorkingTreeConflicts();
901 if (hasConflicts < 0)
903 AddLogString(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
904 return -1;
906 if (hasConflicts)
908 CMessageBox::Show(NULL, IDS_PROGRS_CONFLICTSOCCURED, IDS_APPNAME, MB_OK);
909 return -1;
911 return 0;
915 static bool IsLocalBranch(CString ref)
917 STRING_VECTOR list;
918 g_Git.GetBranchList(list, nullptr, CGit::BRANCH_LOCAL);
919 return std::find(list.begin(), list.end(), ref) != list.end();
922 int CRebaseDlg::FinishRebase()
924 if (m_bFinishedRebase)
925 return 0;
927 m_bFinishedRebase = true;
928 if(this->m_IsCherryPick) //cherry pick mode no "branch", working at upstream branch
930 m_sStatusText.LoadString(IDS_DONE);
931 m_CtrlStatusText.SetWindowText(m_sStatusText);
932 m_bStatusWarning = false;
933 m_CtrlStatusText.Invalidate();
934 return 0;
937 CGitHash head;
938 if (g_Git.GetHash(head, _T("HEAD")))
940 MessageBox(g_Git.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR);
941 return -1;
943 CString out,cmd;
945 if (IsLocalBranch(m_BranchCtrl.GetString()))
947 cmd.Format(_T("git.exe checkout -f -B %s %s --"), m_BranchCtrl.GetString(), head.ToString());
948 AddLogString(cmd);
949 while (true)
951 out.Empty();
952 if (g_Git.Run(cmd, &out, CP_UTF8))
954 AddLogString(out);
955 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
956 return -1;
958 else
959 break;
961 AddLogString(out);
964 cmd.Format(_T("git.exe reset --hard %s --"), head.ToString());
965 AddLogString(cmd);
966 while (true)
968 out.Empty();
969 if (g_Git.Run(cmd, &out, CP_UTF8))
971 AddLogString(out);
972 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
973 return -1;
975 else
976 break;
978 AddLogString(out);
980 while (m_ctrlTabCtrl.GetTabsNum() > 1)
981 m_ctrlTabCtrl.RemoveTab(0);
982 m_CtrlStatusText.SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_REBASEFINISHED)));
983 m_sStatusText = CString(MAKEINTRESOURCE(IDS_PROC_REBASEFINISHED));
984 m_bStatusWarning = false;
985 m_CtrlStatusText.Invalidate();
987 return 0;
989 void CRebaseDlg::OnBnClickedContinue()
991 if( m_RebaseStage == REBASE_DONE)
993 OnOK();
994 CheckRestoreStash();
995 return;
998 if (m_RebaseStage == CHOOSE_BRANCH || m_RebaseStage == CHOOSE_COMMIT_PICK_MODE)
1000 if (CheckRebaseCondition())
1001 return;
1004 if( this->m_IsFastForward )
1006 CString cmd,out;
1007 if (g_Git.GetHash(m_OrigBranchHash, m_BranchCtrl.GetString()))
1009 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of \"") + m_BranchCtrl.GetString() + _T("\".")), _T("TortoiseGit"), MB_ICONERROR);
1010 return;
1012 if (g_Git.GetHash(m_OrigUpstreamHash, m_UpstreamCtrl.GetString()))
1014 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of \"") + m_UpstreamCtrl.GetString() + _T("\".")), _T("TortoiseGit"), MB_ICONERROR);
1015 return;
1018 if(!g_Git.IsFastForward(this->m_BranchCtrl.GetString(),this->m_UpstreamCtrl.GetString()))
1020 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1021 AddLogString(_T("No fast forward possible.\r\nMaybe repository changed"));
1022 return;
1025 if (IsLocalBranch(m_BranchCtrl.GetString()))
1027 cmd.Format(_T("git.exe checkout --no-track -f -B %s %s --"), m_BranchCtrl.GetString(), m_UpstreamCtrl.GetString());
1028 AddLogString(cmd);
1029 while (true)
1031 out.Empty();
1032 if (g_Git.Run(cmd, &out, CP_UTF8))
1034 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1035 AddLogString(out);
1036 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
1037 return;
1039 else
1040 break;
1042 AddLogString(out);
1043 out.Empty();
1045 cmd.Format(_T("git.exe reset --hard %s --"), g_Git.FixBranchName(this->m_UpstreamCtrl.GetString()));
1046 CString log;
1047 log.Format(IDS_PROC_REBASE_FFTO, m_UpstreamCtrl.GetString());
1048 this->AddLogString(log);
1050 AddLogString(cmd);
1051 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1052 while (true)
1054 out.Empty();
1055 if (g_Git.Run(cmd, &out, CP_UTF8))
1057 AddLogString(CString(MAKEINTRESOURCE(IDS_FAIL)));
1058 AddLogString(out);
1059 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
1060 return;
1062 else
1063 break;
1065 AddLogString(out);
1066 AddLogString(CString(MAKEINTRESOURCE(IDS_DONE)));
1067 m_RebaseStage = REBASE_DONE;
1068 UpdateCurrentStatus();
1069 return;
1072 if( m_RebaseStage == CHOOSE_BRANCH|| m_RebaseStage == CHOOSE_COMMIT_PICK_MODE )
1074 if(CheckRebaseCondition())
1075 return ;
1076 m_RebaseStage = REBASE_START;
1077 m_FileListCtrl.Clear();
1078 m_FileListCtrl.SetHasCheckboxes(false);
1079 m_FileListCtrl.m_CurrentVersion = L"";
1080 m_ctrlTabCtrl.SetTabLabel(REBASE_TAB_CONFLICT, CString(MAKEINTRESOURCE(IDS_PROC_CONFLICTFILES)));
1081 m_ctrlTabCtrl.AddTab(&m_wndOutputRebase, CString(MAKEINTRESOURCE(IDS_LOG)), 2);
1084 if( m_RebaseStage == REBASE_FINISH )
1086 if(FinishRebase())
1087 return ;
1089 OnOK();
1092 if( m_RebaseStage == REBASE_SQUASH_CONFLICT)
1094 if(VerifyNoConflict())
1095 return;
1096 GitRevLoglist* curRev = (GitRevLoglist*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1097 if(this->CheckNextCommitIsSquash())
1098 {//next commit is not squash;
1099 m_RebaseStage = REBASE_SQUASH_EDIT;
1100 this->OnRebaseUpdateUI(0,0);
1101 this->UpdateCurrentStatus();
1102 return ;
1105 m_RebaseStage=REBASE_CONTINUE;
1106 curRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1107 this->UpdateCurrentStatus();
1111 if( m_RebaseStage == REBASE_CONFLICT )
1113 if(VerifyNoConflict())
1114 return;
1116 GitRevLoglist* curRev = (GitRevLoglist*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1117 // ***************************************************
1118 // ATTENTION: Similar code in CommitDlg.cpp!!!
1119 // ***************************************************
1120 CMassiveGitTask mgtReAddAfterCommit(_T("add --ignore-errors -f"));
1121 CMassiveGitTask mgtAdd(_T("add -f"));
1122 CMassiveGitTask mgtUpdateIndexForceRemove(_T("update-index --force-remove"));
1123 CMassiveGitTask mgtUpdateIndex(_T("update-index"));
1124 CMassiveGitTask mgtRm(_T("rm --ignore-unmatch"));
1125 CMassiveGitTask mgtRmFCache(_T("rm -f --cache"));
1126 CMassiveGitTask mgtReset(_T("reset"), TRUE, true);
1127 for (int i = 0; i < m_FileListCtrl.GetItemCount(); i++)
1129 CTGitPath *entry = (CTGitPath *)m_FileListCtrl.GetItemData(i);
1130 if (entry->m_Checked)
1132 if (entry->m_Action & CTGitPath::LOGACTIONS_UNVER)
1133 mgtAdd.AddFile(entry->GetGitPathString());
1134 else if (entry->m_Action & CTGitPath::LOGACTIONS_DELETED)
1135 mgtUpdateIndexForceRemove.AddFile(entry->GetGitPathString());
1136 else
1137 mgtUpdateIndex.AddFile(entry->GetGitPathString());
1139 if (entry->m_Action & CTGitPath::LOGACTIONS_REPLACED)
1140 mgtRm.AddFile(entry->GetGitOldPathString());
1142 else
1144 if (entry->m_Action & CTGitPath::LOGACTIONS_ADDED || entry->m_Action & CTGitPath::LOGACTIONS_REPLACED)
1146 mgtRmFCache.AddFile(entry->GetGitPathString());
1147 mgtReAddAfterCommit.AddFile(*entry);
1149 if (entry->m_Action & CTGitPath::LOGACTIONS_REPLACED && !entry->GetGitOldPathString().IsEmpty())
1150 mgtReset.AddFile(entry->GetGitOldPathString());
1152 else if(!(entry->m_Action & CTGitPath::LOGACTIONS_UNVER))
1153 mgtReset.AddFile(entry->GetGitPathString());
1157 BOOL cancel = FALSE;
1158 bool successful = true;
1159 successful = successful && mgtAdd.Execute(cancel);
1160 successful = successful && mgtUpdateIndexForceRemove.Execute(cancel);
1161 successful = successful && mgtUpdateIndex.Execute(cancel);
1162 successful = successful && mgtRm.Execute(cancel);
1163 successful = successful && mgtRmFCache.Execute(cancel);
1164 successful = successful && mgtReset.Execute(cancel);
1166 if (!successful)
1168 AddLogString(_T("An error occurred while updating the index."));
1169 return;
1172 CString out =_T("");
1173 CString cmd;
1174 cmd.Format(_T("git.exe commit -C %s"), curRev->m_CommitHash.ToString());
1176 AddLogString(cmd);
1178 if(g_Git.Run(cmd,&out,CP_UTF8))
1180 AddLogString(out);
1181 if(!g_Git.CheckCleanWorkTree())
1183 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1184 return;
1188 AddLogString(out);
1190 // update commit message if needed
1191 CString str = m_LogMessageCtrl.GetText().Trim();
1192 if (str != (curRev->GetSubject() + _T("\n") + curRev->GetBody()).Trim())
1194 if (str.Trim().IsEmpty())
1196 CMessageBox::Show(NULL, IDS_PROC_COMMITMESSAGE_EMPTY,IDS_APPNAME, MB_OK | MB_ICONERROR);
1197 return;
1199 CString tempfile = ::GetTempFile();
1200 if (CAppUtils::SaveCommitUnicodeFile(tempfile, str))
1202 CMessageBox::Show(nullptr, _T("Could not save commit message"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1203 return;
1206 out.Empty();
1207 cmd.Format(_T("git.exe commit --amend -F \"%s\""), tempfile);
1208 AddLogString(cmd);
1210 if (g_Git.Run(cmd, &out, CP_UTF8))
1212 AddLogString(out);
1213 if (!g_Git.CheckCleanWorkTree())
1215 CMessageBox::Show(NULL, out, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1216 return;
1220 AddLogString(out);
1223 if (((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\ReaddUnselectedAddedFilesAfterCommit"), TRUE)) == TRUE)
1225 BOOL cancel = FALSE;
1226 mgtReAddAfterCommit.Execute(cancel);
1229 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1230 if (curRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_EDIT)
1232 m_RebaseStage=REBASE_EDIT;
1233 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_MESSAGE);
1234 this->UpdateCurrentStatus();
1235 return;
1237 else
1239 m_RebaseStage=REBASE_CONTINUE;
1240 curRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1241 this->UpdateCurrentStatus();
1243 if (CheckNextCommitIsSquash() == 0) // remember commit msg after edit if next commit if squash
1244 ResetParentForSquash(str);
1245 else
1246 m_SquashMessage.Empty();
1250 if ((m_RebaseStage == REBASE_EDIT || m_RebaseStage == REBASE_CONTINUE || m_bSplitCommit) && CheckNextCommitIsSquash() && (m_bSplitCommit || !g_Git.CheckCleanWorkTree(true)))
1252 if (!m_bSplitCommit && CMessageBox::Show(nullptr, IDS_PROC_REBASE_CONTINUE_NOTCLEAN, IDS_APPNAME, 1, IDI_ERROR, IDS_MSGBOX_OK, IDS_ABORTBUTTON) == 2)
1253 return;
1254 BOOL isFirst = TRUE;
1257 CCommitDlg dlg;
1258 if (isFirst)
1259 dlg.m_sLogMessage = m_LogMessageCtrl.GetText();
1260 dlg.m_bWholeProject = true;
1261 dlg.m_bSelectFilesForCommit = true;
1262 dlg.m_bCommitAmend = isFirst && (m_RebaseStage != REBASE_SQUASH_EDIT); // do not amend on squash_edit stage, we need a normal commit there
1263 CTGitPathList gpl;
1264 gpl.AddPath(CTGitPath(g_Git.m_CurrentDir));
1265 dlg.m_pathList = gpl;
1266 dlg.m_bAmendDiffToLastCommit = !m_bSplitCommit;
1267 dlg.m_bNoPostActions = true;
1268 if (dlg.m_bCommitAmend)
1269 dlg.m_AmendStr = dlg.m_sLogMessage;
1270 dlg.m_bWarnDetachedHead = false;
1272 if (dlg.DoModal() != IDOK)
1273 return;
1275 isFirst = !m_bSplitCommit; // only select amend on second+ runs if not in split commit mode
1277 m_SquashMessage.Empty();
1278 } while (!g_Git.CheckCleanWorkTree() || (m_bSplitCommit && MessageBox(_T("Add another commit?"), _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES));
1280 m_bSplitCommit = FALSE;
1281 UpdateData(FALSE);
1283 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1284 m_RebaseStage = REBASE_CONTINUE;
1285 GitRevLoglist* curRev = (GitRevLoglist*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1286 curRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1287 this->UpdateCurrentStatus();
1290 if( m_RebaseStage == REBASE_EDIT || m_RebaseStage == REBASE_SQUASH_EDIT )
1292 CString str;
1293 GitRevLoglist* curRev = (GitRevLoglist*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1295 str=this->m_LogMessageCtrl.GetText();
1296 if(str.Trim().IsEmpty())
1298 CMessageBox::Show(NULL, IDS_PROC_COMMITMESSAGE_EMPTY,IDS_APPNAME, MB_OK | MB_ICONERROR);
1299 return;
1302 CString tempfile=::GetTempFile();
1303 if (CAppUtils::SaveCommitUnicodeFile(tempfile, str))
1305 CMessageBox::Show(nullptr, _T("Could not save commit message"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1306 return;
1309 CString out,cmd;
1311 if( m_RebaseStage == REBASE_SQUASH_EDIT )
1312 cmd.Format(_T("git.exe commit %s-F \"%s\""), m_SquashFirstMetaData, tempfile);
1313 else
1315 CString options;
1316 int isEmpty = IsCommitEmpty(curRev->m_CommitHash);
1317 if (isEmpty == 1)
1318 options = _T("--allow-empty ");
1319 else if (isEmpty < 0)
1320 return;
1321 cmd.Format(_T("git.exe commit --amend %s-F \"%s\""), options, tempfile);
1324 if(g_Git.Run(cmd,&out,CP_UTF8))
1326 if(!g_Git.CheckCleanWorkTree())
1328 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1329 return;
1333 ::DeleteFile(tempfile);
1334 AddLogString(out);
1335 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
1337 ResetParentForSquash(str);
1339 else
1340 m_SquashMessage.Empty();
1341 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1342 m_RebaseStage=REBASE_CONTINUE;
1343 curRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1344 this->UpdateCurrentStatus();
1348 InterlockedExchange(&m_bThreadRunning, TRUE);
1349 SetControlEnable();
1351 if (AfxBeginThread(RebaseThreadEntry, this)==NULL)
1353 InterlockedExchange(&m_bThreadRunning, FALSE);
1354 CMessageBox::Show(NULL, _T("Create Rebase Thread Fail"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1355 SetControlEnable();
1358 void CRebaseDlg::ResetParentForSquash(const CString& commitMessage)
1360 m_SquashMessage = commitMessage;
1361 // reset parent so that we can do "git cherry-pick --no-commit" w/o introducing an unwanted commit
1362 CString cmd = _T("git.exe reset --soft HEAD~1");
1363 m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_LOG);
1364 while (true)
1366 CString out;
1367 if (g_Git.Run(cmd, &out, CP_UTF8))
1369 AddLogString(cmd);
1370 AddLogString(CString(MAKEINTRESOURCE(IDS_FAIL)));
1371 AddLogString(out);
1372 if (CMessageBox::Show(m_hWnd, out + _T("\nRetry?"), _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
1373 return;
1375 else
1376 break;
1379 int CRebaseDlg::CheckNextCommitIsSquash()
1381 int index;
1382 if(m_CommitList.m_IsOldFirst)
1383 index=m_CurrentRebaseIndex+1;
1384 else
1385 index=m_CurrentRebaseIndex-1;
1387 GitRevLoglist* curRev;
1390 if(index<0)
1391 return -1;
1392 if(index>= m_CommitList.GetItemCount())
1393 return -1;
1395 curRev = (GitRevLoglist*)m_CommitList.m_arShownList[index];
1397 if (curRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_SQUASH)
1398 return 0;
1399 if (curRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_SKIP)
1401 if(m_CommitList.m_IsOldFirst)
1402 ++index;
1403 else
1404 --index;
1406 else
1407 return -1;
1409 } while(curRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_SKIP);
1411 return -1;
1414 int CRebaseDlg::GoNext()
1416 if(m_CommitList.m_IsOldFirst)
1417 ++m_CurrentRebaseIndex;
1418 else
1419 --m_CurrentRebaseIndex;
1420 return 0;
1423 int CRebaseDlg::StateAction()
1425 switch(this->m_RebaseStage)
1427 case CHOOSE_BRANCH:
1428 case CHOOSE_COMMIT_PICK_MODE:
1429 if(StartRebase())
1430 return -1;
1431 m_RebaseStage = REBASE_START;
1432 GoNext();
1433 break;
1436 return 0;
1438 void CRebaseDlg::SetContinueButtonText()
1440 CString Text;
1441 switch(this->m_RebaseStage)
1443 case CHOOSE_BRANCH:
1444 case CHOOSE_COMMIT_PICK_MODE:
1445 if(this->m_IsFastForward)
1446 Text.LoadString(IDS_PROC_STARTREBASEFFBUTTON);
1447 else
1448 Text.LoadString(IDS_PROC_STARTREBASEBUTTON);
1449 break;
1451 case REBASE_START:
1452 case REBASE_ERROR:
1453 case REBASE_CONTINUE:
1454 case REBASE_SQUASH_CONFLICT:
1455 Text.LoadString(IDS_CONTINUEBUTTON);
1456 break;
1458 case REBASE_CONFLICT:
1459 Text.LoadString(IDS_COMMITBUTTON);
1460 break;
1461 case REBASE_EDIT:
1462 Text.LoadString(IDS_AMENDBUTTON);
1463 break;
1465 case REBASE_SQUASH_EDIT:
1466 Text.LoadString(IDS_COMMITBUTTON);
1467 break;
1469 case REBASE_ABORT:
1470 case REBASE_FINISH:
1471 Text.LoadString(IDS_FINISHBUTTON);
1472 break;
1474 case REBASE_DONE:
1475 Text.LoadString(IDS_DONE);
1476 break;
1478 this->GetDlgItem(IDC_REBASE_CONTINUE)->SetWindowText(Text);
1481 void CRebaseDlg::SetControlEnable()
1483 switch(this->m_RebaseStage)
1485 case CHOOSE_BRANCH:
1486 case CHOOSE_COMMIT_PICK_MODE:
1488 this->GetDlgItem(IDC_SPLITALLOPTIONS)->EnableWindow(TRUE);
1489 this->GetDlgItem(IDC_BUTTON_UP2)->EnableWindow(TRUE);
1490 this->GetDlgItem(IDC_BUTTON_DOWN2)->EnableWindow(TRUE);
1492 if(!m_IsCherryPick)
1494 this->GetDlgItem(IDC_REBASE_COMBOXEX_BRANCH)->EnableWindow(TRUE);
1495 this->GetDlgItem(IDC_REBASE_COMBOXEX_UPSTREAM)->EnableWindow(TRUE);
1496 this->GetDlgItem(IDC_BUTTON_REVERSE)->EnableWindow(TRUE);
1497 this->GetDlgItem(IDC_REBASE_CHECK_FORCE)->EnableWindow(TRUE);
1499 this->m_CommitList.m_ContextMenuMask |= m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_PICK)|
1500 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SQUASH)|
1501 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_EDIT)|
1502 m_CommitList.GetContextMenuBit(CGitLogListBase::ID_REBASE_SKIP);
1503 break;
1505 case REBASE_START:
1506 case REBASE_CONTINUE:
1507 case REBASE_ABORT:
1508 case REBASE_ERROR:
1509 case REBASE_FINISH:
1510 case REBASE_CONFLICT:
1511 case REBASE_EDIT:
1512 case REBASE_SQUASH_CONFLICT:
1513 case REBASE_DONE:
1514 this->GetDlgItem(IDC_SPLITALLOPTIONS)->EnableWindow(FALSE);
1515 this->GetDlgItem(IDC_REBASE_COMBOXEX_BRANCH)->EnableWindow(FALSE);
1516 this->GetDlgItem(IDC_REBASE_COMBOXEX_UPSTREAM)->EnableWindow(FALSE);
1517 this->GetDlgItem(IDC_BUTTON_REVERSE)->EnableWindow(FALSE);
1518 this->GetDlgItem(IDC_REBASE_CHECK_FORCE)->EnableWindow(FALSE);
1519 this->GetDlgItem(IDC_BUTTON_UP2)->EnableWindow(FALSE);
1520 this->GetDlgItem(IDC_BUTTON_DOWN2)->EnableWindow(FALSE);
1522 if( m_RebaseStage == REBASE_DONE && (this->m_PostButtonTexts.GetCount() != 0) )
1524 this->GetDlgItem(IDC_STATUS_STATIC)->ShowWindow(SW_HIDE);
1525 this->GetDlgItem(IDC_REBASE_POST_BUTTON)->ShowWindow(SW_SHOWNORMAL);
1526 this->m_PostButton.RemoveAll();
1527 this->m_PostButton.AddEntries(m_PostButtonTexts);
1528 //this->GetDlgItem(IDC_REBASE_POST_BUTTON)->SetWindowText(this->m_PostButtonText);
1530 break;
1533 GetDlgItem(IDC_REBASE_SPLIT_COMMIT)->ShowWindow((m_RebaseStage == REBASE_EDIT || m_RebaseStage == REBASE_SQUASH_EDIT) ? SW_SHOW : SW_HIDE);
1535 if(m_bThreadRunning)
1537 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(FALSE);
1540 else if (m_RebaseStage != REBASE_ERROR)
1542 this->GetDlgItem(IDC_REBASE_CONTINUE)->EnableWindow(TRUE);
1546 void CRebaseDlg::UpdateProgress()
1548 int index;
1549 CRect rect;
1551 if(m_CommitList.m_IsOldFirst)
1552 index = m_CurrentRebaseIndex+1;
1553 else
1554 index = m_CommitList.GetItemCount()-m_CurrentRebaseIndex;
1556 int finishedCommits = index - 1; // introduced an variable which shows the number handled revisions for the progress bars
1557 if (m_RebaseStage == REBASE_FINISH || finishedCommits == -1)
1558 finishedCommits = index;
1560 m_ProgressBar.SetRange32(0, m_CommitList.GetItemCount());
1561 m_ProgressBar.SetPos(finishedCommits);
1562 if (m_pTaskbarList)
1564 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1565 m_pTaskbarList->SetProgressValue(m_hWnd, finishedCommits, m_CommitList.GetItemCount());
1568 if(m_CurrentRebaseIndex>=0 && m_CurrentRebaseIndex< m_CommitList.GetItemCount())
1570 CString text;
1571 text.Format(IDS_PROC_REBASING_PROGRESS, index, m_CommitList.GetItemCount());
1572 m_sStatusText = text;
1573 m_CtrlStatusText.SetWindowText(text);
1574 m_bStatusWarning = false;
1575 m_CtrlStatusText.Invalidate();
1578 GitRevLoglist* prevRev = nullptr, *curRev = nullptr;
1580 if( m_CurrentRebaseIndex >= 0 && m_CurrentRebaseIndex< m_CommitList.m_arShownList.GetSize())
1582 curRev = (GitRevLoglist*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1585 for (int i = 0; i < m_CommitList.m_arShownList.GetSize(); ++i)
1587 prevRev = (GitRevLoglist*)m_CommitList.m_arShownList[i];
1588 if (prevRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_CURRENT)
1590 prevRev->GetRebaseAction() &= ~CGitLogListBase::LOGACTIONS_REBASE_CURRENT;
1591 m_CommitList.GetItemRect(i,&rect,LVIR_BOUNDS);
1592 m_CommitList.InvalidateRect(rect);
1596 if(curRev)
1598 curRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_CURRENT;
1599 m_CommitList.GetItemRect(m_CurrentRebaseIndex,&rect,LVIR_BOUNDS);
1600 m_CommitList.InvalidateRect(rect);
1602 m_CommitList.EnsureVisible(m_CurrentRebaseIndex,FALSE);
1605 void CRebaseDlg::UpdateCurrentStatus()
1607 SetContinueButtonText();
1608 SetControlEnable();
1609 UpdateProgress();
1610 if (m_RebaseStage == REBASE_DONE)
1611 GetDlgItem(IDC_REBASE_CONTINUE)->SetFocus();
1614 void CRebaseDlg::AddLogString(CString str)
1616 this->m_wndOutputRebase.SendMessage(SCI_SETREADONLY, FALSE);
1617 CStringA sTextA = m_wndOutputRebase.StringForControl(str);//CUnicodeUtils::GetUTF8(str);
1618 this->m_wndOutputRebase.SendMessage(SCI_DOCUMENTEND);
1619 this->m_wndOutputRebase.SendMessage(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)sTextA);
1620 this->m_wndOutputRebase.SendMessage(SCI_REPLACESEL, 0, (LPARAM)(LPCSTR)"\n");
1621 this->m_wndOutputRebase.SendMessage(SCI_SETREADONLY, TRUE);
1624 int CRebaseDlg::GetCurrentCommitID()
1626 if(m_CommitList.m_IsOldFirst)
1628 return this->m_CurrentRebaseIndex+1;
1631 else
1633 return m_CommitList.GetItemCount()-m_CurrentRebaseIndex;
1637 int CRebaseDlg::IsCommitEmpty(const CGitHash& hash)
1639 CString cmd, tree, ptree;
1640 cmd.Format(_T("git.exe rev-parse -q --verify %s^{tree}"), hash.ToString());
1641 if (g_Git.Run(cmd, &tree, CP_UTF8))
1643 AddLogString(cmd);
1644 AddLogString(tree);
1645 return -1;
1647 cmd.Format(_T("git.exe rev-parse -q --verify %s^^{tree}"), hash.ToString());
1648 if (g_Git.Run(cmd, &ptree, CP_UTF8))
1649 ptree = _T("4b825dc642cb6eb9a060e54bf8d69288fbee4904"); // empty tree
1650 return tree == ptree;
1653 int CRebaseDlg::DoRebase()
1655 CString cmd,out;
1656 if(m_CurrentRebaseIndex <0)
1657 return 0;
1658 if(m_CurrentRebaseIndex >= m_CommitList.GetItemCount() )
1659 return 0;
1661 GitRevLoglist* pRev = (GitRevLoglist*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1662 int mode = pRev->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_MODE_MASK;
1663 CString nocommit;
1665 if (mode == CGitLogListBase::LOGACTIONS_REBASE_SKIP)
1667 pRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1668 return 0;
1671 bool nextCommitIsSquash = (CheckNextCommitIsSquash() == 0);
1672 if (nextCommitIsSquash || mode != CGitLogListBase::LOGACTIONS_REBASE_PICK)
1673 { // next commit is squash or not pick
1674 if (!this->m_SquashMessage.IsEmpty())
1675 this->m_SquashMessage += _T("\n\n");
1676 this->m_SquashMessage += pRev->GetSubject();
1677 this->m_SquashMessage += _T("\n");
1678 this->m_SquashMessage += pRev->GetBody().TrimRight();
1679 if (m_bAddCherryPickedFrom)
1681 if (!pRev->GetBody().IsEmpty())
1682 m_SquashMessage += _T("\n");
1683 m_SquashMessage += _T("(cherry picked from commit ");
1684 m_SquashMessage += pRev->m_CommitHash.ToString();
1685 m_SquashMessage += _T(")");
1688 else
1690 this->m_SquashMessage.Empty();
1691 m_SquashFirstMetaData.Empty();
1694 if ((nextCommitIsSquash && mode != CGitLogListBase::LOGACTIONS_REBASE_EDIT) || mode == CGitLogListBase::LOGACTIONS_REBASE_SQUASH)
1695 { // next or this commit is squash (don't do this on edit->squash sequence)
1696 nocommit=_T(" --no-commit ");
1699 if (nextCommitIsSquash && mode != CGitLogListBase::LOGACTIONS_REBASE_SQUASH)
1700 m_SquashFirstMetaData.Format(_T("--date=%s --author=\"%s <%s>\" "), pRev->GetAuthorDate().Format(_T("%Y-%m-%dT%H:%M:%S")), pRev->GetAuthorName(), pRev->GetAuthorEmail());
1702 CString log;
1703 log.Format(_T("%s %d: %s"), CGitLogListBase::GetRebaseActionName(mode), GetCurrentCommitID(), pRev->m_CommitHash.ToString());
1704 AddLogString(log);
1705 AddLogString(pRev->GetSubject());
1706 if (pRev->GetSubject().IsEmpty())
1708 CMessageBox::Show(m_hWnd, IDS_PROC_REBASE_EMPTYCOMMITMSG, IDS_APPNAME, MB_OK | MB_ICONEXCLAMATION);
1709 mode = CGitLogListBase::LOGACTIONS_REBASE_EDIT;
1712 CString cherryPickedFrom;
1713 if (m_bAddCherryPickedFrom)
1714 cherryPickedFrom = _T("-x ");
1715 else if (!m_IsCherryPick && nocommit.IsEmpty())
1716 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."
1718 int isEmpty = IsCommitEmpty(pRev->m_CommitHash);
1719 if (isEmpty == 1)
1720 cherryPickedFrom += _T("--allow-empty ");
1721 else if (isEmpty < 0)
1722 return -1;
1724 while (true)
1726 cmd.Format(_T("git.exe cherry-pick %s%s %s"), cherryPickedFrom, nocommit, pRev->m_CommitHash.ToString());
1728 if(g_Git.Run(cmd,&out,CP_UTF8))
1730 AddLogString(out);
1731 int hasConflicts = g_Git.HasWorkingTreeConflicts();
1732 if (hasConflicts < 0)
1734 AddLogString(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
1735 return -1;
1737 if (!hasConflicts)
1739 if (mode == CGitLogListBase::LOGACTIONS_REBASE_PICK)
1741 if (m_pTaskbarList)
1742 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
1743 int choose = -1;
1744 if (!m_bAutoSkipFailedCommit)
1746 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);
1747 if (choose == 2)
1749 m_bAutoSkipFailedCommit = FALSE;
1750 continue; // retry cherry pick
1753 if (m_bAutoSkipFailedCommit || choose == 1)
1755 bool resetOK = false;
1756 while (!resetOK)
1758 out.Empty();
1759 if (g_Git.Run(_T("git.exe reset --hard"), &out, CP_UTF8))
1761 AddLogString(out);
1762 if (CMessageBox::Show(m_hWnd, _T("Retry?\nUnrecoverable error on cleanup:\n") + out, _T("TortoiseGit"), MB_YESNO | MB_ICONERROR) != IDYES)
1763 break;
1765 else
1766 resetOK = true;
1769 if (resetOK)
1771 pRev->GetRebaseAction() = CGitLogListBase::LOGACTIONS_REBASE_SKIP;
1772 m_CommitList.Invalidate();
1773 return 0;
1777 m_RebaseStage = REBASE_ERROR;
1778 AddLogString(_T("An unrecoverable error occurred."));
1779 return -1;
1781 if (mode == CGitLogListBase::LOGACTIONS_REBASE_EDIT)
1783 this->m_RebaseStage = REBASE_EDIT ;
1784 return -1; // Edit return -1 to stop rebase.
1786 // Squash Case
1787 if(CheckNextCommitIsSquash())
1788 { // no squash
1789 // let user edit last commmit message
1790 this->m_RebaseStage = REBASE_SQUASH_EDIT;
1791 return -1;
1795 if (m_pTaskbarList)
1796 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
1797 if (mode == CGitLogListBase::LOGACTIONS_REBASE_SQUASH)
1798 m_RebaseStage = REBASE_SQUASH_CONFLICT;
1799 else
1800 m_RebaseStage = REBASE_CONFLICT;
1801 return -1;
1804 else
1806 AddLogString(out);
1807 if (mode == CGitLogListBase::LOGACTIONS_REBASE_PICK)
1809 pRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1810 return 0;
1812 if (mode == CGitLogListBase::LOGACTIONS_REBASE_EDIT)
1814 this->m_RebaseStage = REBASE_EDIT ;
1815 return -1; // Edit return -1 to stop rebase.
1818 // Squash Case
1819 if(CheckNextCommitIsSquash())
1820 { // no squash
1821 // let user edit last commmit message
1822 this->m_RebaseStage = REBASE_SQUASH_EDIT;
1823 return -1;
1825 else if (mode == CGitLogListBase::LOGACTIONS_REBASE_SQUASH)
1826 pRev->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE;
1829 return 0;
1833 BOOL CRebaseDlg::IsEnd()
1835 if(m_CommitList.m_IsOldFirst)
1836 return m_CurrentRebaseIndex>= this->m_CommitList.GetItemCount();
1837 else
1838 return m_CurrentRebaseIndex<0;
1841 int CRebaseDlg::RebaseThread()
1843 CBlockCacheForPath cacheBlock(g_Git.m_CurrentDir);
1845 int ret=0;
1846 while(1)
1848 if( m_RebaseStage == REBASE_START )
1850 if( this->StartRebase() )
1852 ret = -1;
1853 break;
1855 m_RebaseStage = REBASE_CONTINUE;
1858 else if( m_RebaseStage == REBASE_CONTINUE )
1860 this->GoNext();
1861 SendMessage(MSG_REBASE_UPDATE_UI);
1862 if(IsEnd())
1864 ret = 0;
1865 m_RebaseStage = REBASE_FINISH;
1868 else
1870 ret = DoRebase();
1872 if( ret )
1874 break;
1879 else if( m_RebaseStage == REBASE_FINISH )
1881 SendMessage(MSG_REBASE_UPDATE_UI);
1882 m_RebaseStage = REBASE_DONE;
1883 break;
1886 else
1888 break;
1890 this->PostMessage(MSG_REBASE_UPDATE_UI);
1893 InterlockedExchange(&m_bThreadRunning, FALSE);
1894 this->PostMessage(MSG_REBASE_UPDATE_UI);
1895 return ret;
1898 void CRebaseDlg::ListConflictFile()
1900 this->m_FileListCtrl.Clear();
1901 m_FileListCtrl.SetHasCheckboxes(true);
1902 CTGitPathList list;
1903 CTGitPath path;
1904 list.AddPath(path);
1906 m_FileListCtrl.m_bIsRevertTheirMy = !m_IsCherryPick;
1908 this->m_FileListCtrl.GetStatus(&list,true);
1909 this->m_FileListCtrl.Show(CTGitPath::LOGACTIONS_UNMERGED|CTGitPath::LOGACTIONS_MODIFIED|CTGitPath::LOGACTIONS_ADDED|CTGitPath::LOGACTIONS_DELETED,
1910 CTGitPath::LOGACTIONS_UNMERGED);
1912 m_FileListCtrl.Check(GITSLC_SHOWFILES);
1913 bool hasSubmoduleChange = false;
1914 for (int i = 0; i < m_FileListCtrl.GetItemCount(); i++)
1916 CTGitPath *entry = (CTGitPath *)m_FileListCtrl.GetItemData(i);
1917 if (entry->IsDirectory())
1919 hasSubmoduleChange = true;
1920 break;
1924 if (hasSubmoduleChange)
1926 m_CtrlStatusText.SetWindowText(m_sStatusText + _T(", ") + CString(MAKEINTRESOURCE(IDS_CARE_SUBMODULE_CHANGES)));
1927 m_bStatusWarning = true;
1928 m_CtrlStatusText.Invalidate();
1930 else
1932 m_CtrlStatusText.SetWindowText(m_sStatusText);
1933 m_bStatusWarning = false;
1934 m_CtrlStatusText.Invalidate();
1938 LRESULT CRebaseDlg::OnRebaseUpdateUI(WPARAM,LPARAM)
1940 if (m_RebaseStage == REBASE_FINISH)
1942 FinishRebase();
1943 return 0;
1945 UpdateCurrentStatus();
1946 if (m_RebaseStage == REBASE_DONE && m_pTaskbarList)
1947 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS); // do not show progress on taskbar any more to show we finished
1948 if(m_CurrentRebaseIndex <0)
1949 return 0;
1950 if(m_CurrentRebaseIndex >= m_CommitList.GetItemCount() )
1951 return 0;
1952 GitRev *curRev=(GitRev*)m_CommitList.m_arShownList[m_CurrentRebaseIndex];
1954 switch(m_RebaseStage)
1956 case REBASE_CONFLICT:
1957 case REBASE_SQUASH_CONFLICT:
1959 ListConflictFile();
1960 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_CONFLICT);
1961 if (m_pTaskbarList)
1962 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
1963 this->m_LogMessageCtrl.Call(SCI_SETREADONLY, FALSE);
1964 CString logMessage;
1965 if (m_IsCherryPick)
1967 CString dotGitPath;
1968 GitAdminDir::GetAdminDirPath(g_Git.m_CurrentDir, dotGitPath);
1969 // vanilla git also re-uses MERGE_MSG on conflict (listing all conflicted files)
1970 // and it's also needed for cherry-pick in order to get cherry-picked-from included on conflicts
1971 CGit::LoadTextFile(dotGitPath + _T("MERGE_MSG"), logMessage);
1973 if (logMessage.IsEmpty())
1974 logMessage = curRev->GetSubject() + _T("\n") + curRev->GetBody();
1975 this->m_LogMessageCtrl.SetText(logMessage);
1976 break;
1978 case REBASE_EDIT:
1979 this->m_ctrlTabCtrl.SetActiveTab(REBASE_TAB_MESSAGE);
1980 if (m_pTaskbarList)
1981 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_PAUSED);
1982 this->m_LogMessageCtrl.Call(SCI_SETREADONLY, FALSE);
1983 if (m_bAddCherryPickedFrom)
1985 // Since the new commit is done and the HEAD points to it,
1986 // just using the new body modified by git self.
1987 GitRev headRevision;
1988 if (headRevision.GetCommit(_T("HEAD")))
1989 MessageBox(headRevision.GetLastErr(), _T("TortoiseGit"), MB_ICONERROR);
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 GitRevLoglist* pLogEntry = reinterpret_cast<GitRevLoglist*>(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 GitRevLoglist* pRev = (GitRevLoglist*)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();