1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - 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
24 #include "TortoiseProc.h"
25 #include "RebaseDlg.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"
37 #include "StringUtils.h"
41 IMPLEMENT_DYNAMIC(CRebaseDlg
, CResizableStandAloneDialog
)
43 CRebaseDlg::CRebaseDlg(CWnd
* pParent
/*=nullptr*/)
44 : CResizableStandAloneDialog(CRebaseDlg::IDD
, pParent
)
45 , m_bAddCherryPickedFrom(FALSE
)
46 , m_bStatusWarning(false)
47 , m_bAutoSkipFailedCommit(FALSE
)
48 , m_bFinishedRebase(false)
50 , m_bSplitCommit(FALSE
)
51 , m_bPreserveMerges(FALSE
)
52 , m_bRebaseAutoStart(false)
53 , m_bRebaseAutoEnd(false)
55 m_RebaseStage
=CHOOSE_BRANCH
;
56 m_CurrentRebaseIndex
=-1;
57 m_bThreadRunning
=FALSE
;
58 this->m_IsCherryPick
= FALSE
;
60 m_IsFastForward
=FALSE
;
63 CRebaseDlg::~CRebaseDlg()
67 void CRebaseDlg::DoDataExchange(CDataExchange
* pDX
)
69 CDialog::DoDataExchange(pDX
);
70 DDX_Control(pDX
, IDC_REBASE_PROGRESS
, m_ProgressBar
);
71 DDX_Control(pDX
, IDC_STATUS_STATIC
, m_CtrlStatusText
);
72 DDX_Control(pDX
, IDC_REBASE_SPLIT
, m_wndSplitter
);
73 DDX_Control(pDX
,IDC_COMMIT_LIST
,m_CommitList
);
74 DDX_Control(pDX
,IDC_REBASE_COMBOXEX_BRANCH
, this->m_BranchCtrl
);
75 DDX_Control(pDX
,IDC_REBASE_COMBOXEX_UPSTREAM
, this->m_UpstreamCtrl
);
76 DDX_Check(pDX
, IDC_REBASE_CHECK_FORCE
,m_bForce
);
77 DDX_Check(pDX
, IDC_REBASE_CHECK_PRESERVEMERGES
, m_bPreserveMerges
);
78 DDX_Check(pDX
, IDC_CHECK_CHERRYPICKED_FROM
, m_bAddCherryPickedFrom
);
79 DDX_Control(pDX
,IDC_REBASE_POST_BUTTON
,m_PostButton
);
80 DDX_Control(pDX
, IDC_SPLITALLOPTIONS
, m_SplitAllOptions
);
81 DDX_Check(pDX
, IDC_REBASE_SPLIT_COMMIT
, m_bSplitCommit
);
85 BEGIN_MESSAGE_MAP(CRebaseDlg
, CResizableStandAloneDialog
)
86 ON_BN_CLICKED(IDC_REBASE_SPLIT
, &CRebaseDlg::OnBnClickedRebaseSplit
)
87 ON_BN_CLICKED(IDC_REBASE_CONTINUE
,OnBnClickedContinue
)
88 ON_BN_CLICKED(IDC_REBASE_ABORT
, OnBnClickedAbort
)
90 ON_CBN_SELCHANGE(IDC_REBASE_COMBOXEX_BRANCH
, &CRebaseDlg::OnCbnSelchangeBranch
)
91 ON_CBN_SELCHANGE(IDC_REBASE_COMBOXEX_UPSTREAM
, &CRebaseDlg::OnCbnSelchangeUpstream
)
92 ON_MESSAGE(MSG_REBASE_UPDATE_UI
, OnRebaseUpdateUI
)
93 ON_REGISTERED_MESSAGE(CGitStatusListCtrl::GITSLNM_NEEDSREFRESH
, OnGitStatusListCtrlNeedsRefresh
)
94 ON_BN_CLICKED(IDC_BUTTON_REVERSE
, OnBnClickedButtonReverse
)
95 ON_BN_CLICKED(IDC_BUTTON_BROWSE
, &CRebaseDlg::OnBnClickedButtonBrowse
)
96 ON_BN_CLICKED(IDC_REBASE_CHECK_FORCE
, &CRebaseDlg::OnBnClickedRebaseCheckForce
)
97 ON_BN_CLICKED(IDC_REBASE_CHECK_PRESERVEMERGES
, &CRebaseDlg::OnBnClickedRebaseCheckForce
)
98 ON_BN_CLICKED(IDC_CHECK_CHERRYPICKED_FROM
, &CRebaseDlg::OnBnClickedCheckCherryPickedFrom
)
99 ON_BN_CLICKED(IDC_REBASE_POST_BUTTON
, &CRebaseDlg::OnBnClickedRebasePostButton
)
100 ON_BN_CLICKED(IDC_BUTTON_UP2
, &CRebaseDlg::OnBnClickedButtonUp2
)
101 ON_BN_CLICKED(IDC_BUTTON_DOWN2
, &CRebaseDlg::OnBnClickedButtonDown2
)
102 ON_REGISTERED_MESSAGE(WM_TASKBARBTNCREATED
, OnTaskbarBtnCreated
)
103 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_COMMIT_LIST
, OnLvnItemchangedLoglist
)
104 ON_REGISTERED_MESSAGE(CGitLogListBase::m_RebaseActionMessage
, OnRebaseActionMessage
)
106 ON_BN_CLICKED(IDC_SPLITALLOPTIONS
, &CRebaseDlg::OnBnClickedSplitAllOptions
)
107 ON_BN_CLICKED(IDC_REBASE_SPLIT_COMMIT
, &CRebaseDlg::OnBnClickedRebaseSplitCommit
)
108 ON_BN_CLICKED(IDC_BUTTON_ONTO
, &CRebaseDlg::OnBnClickedButtonOnto
)
109 ON_BN_CLICKED(IDHELP
, OnHelp
)
112 void CRebaseDlg::CleanUpRebaseActiveFolder()
117 if (GitAdminDir::GetAdminDirPath(g_Git
.m_CurrentDir
, adminDir
))
118 RemoveDirectory(adminDir
+ L
"tgitrebase.active");
121 void CRebaseDlg::AddRebaseAnchor()
123 AddAnchor(IDC_REBASE_TAB
,TOP_LEFT
,BOTTOM_RIGHT
);
124 AddAnchor(IDC_COMMIT_LIST
,TOP_LEFT
, TOP_RIGHT
);
125 AddAnchor(IDC_REBASE_SPLIT
,TOP_LEFT
, TOP_RIGHT
);
126 AddAnchor(IDC_STATUS_STATIC
, BOTTOM_LEFT
,BOTTOM_RIGHT
);
127 AddAnchor(IDC_REBASE_CONTINUE
,BOTTOM_RIGHT
);
128 AddAnchor(IDC_REBASE_ABORT
, BOTTOM_RIGHT
);
129 AddAnchor(IDC_REBASE_PROGRESS
,BOTTOM_LEFT
, BOTTOM_RIGHT
);
130 AddAnchor(IDC_SPLITALLOPTIONS
, TOP_LEFT
);
131 AddAnchor(IDC_BUTTON_UP2
,TOP_LEFT
);
132 AddAnchor(IDC_BUTTON_DOWN2
,TOP_LEFT
);
133 AddAnchor(IDC_REBASE_COMBOXEX_UPSTREAM
,TOP_LEFT
);
134 AddAnchor(IDC_REBASE_COMBOXEX_BRANCH
,TOP_LEFT
);
135 AddAnchor(IDC_REBASE_STATIC_UPSTREAM
,TOP_LEFT
);
136 AddAnchor(IDC_REBASE_STATIC_BRANCH
,TOP_LEFT
);
137 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
138 AddAnchor(IDC_REBASE_CHECK_FORCE
,TOP_RIGHT
);
139 AddAnchor(IDC_REBASE_CHECK_PRESERVEMERGES
, TOP_LEFT
);
140 AddAnchor(IDC_CHECK_CHERRYPICKED_FROM
, TOP_RIGHT
);
141 AddAnchor(IDC_REBASE_SPLIT_COMMIT
, BOTTOM_RIGHT
);
142 AddAnchor(IDC_REBASE_POST_BUTTON
,BOTTOM_LEFT
);
144 this->AddOthersToAnchor();
147 BOOL
CRebaseDlg::OnInitDialog()
149 CResizableStandAloneDialog::OnInitDialog();
150 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
152 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
153 // do this, Explorer would be unable to send that message to our window if we
154 // were running elevated. It's OK to make the call all the time, since if we're
155 // not elevated, this is a no-op.
156 CHANGEFILTERSTRUCT cfs
= { sizeof(CHANGEFILTERSTRUCT
) };
157 typedef BOOL STDAPICALLTYPE
ChangeWindowMessageFilterExDFN(HWND hWnd
, UINT message
, DWORD action
, PCHANGEFILTERSTRUCT pChangeFilterStruct
);
158 CAutoLibrary hUser
= AtlLoadSystemLibraryUsingFullPath(_T("user32.dll"));
161 ChangeWindowMessageFilterExDFN
*pfnChangeWindowMessageFilterEx
= (ChangeWindowMessageFilterExDFN
*)GetProcAddress(hUser
, "ChangeWindowMessageFilterEx");
162 if (pfnChangeWindowMessageFilterEx
)
164 pfnChangeWindowMessageFilterEx(m_hWnd
, WM_TASKBARBTNCREATED
, MSGFLT_ALLOW
, &cfs
);
167 m_pTaskbarList
.Release();
168 if (FAILED(m_pTaskbarList
.CoCreateInstance(CLSID_TaskbarList
)))
169 m_pTaskbarList
= nullptr;
172 //IDC_REBASE_DUMY_TAB
174 GetClientRect(m_DlgOrigRect
);
175 m_CommitList
.GetClientRect(m_CommitListOrigRect
);
177 CWnd
*pwnd
=this->GetDlgItem(IDC_REBASE_DUMY_TAB
);
178 pwnd
->GetWindowRect(&rectDummy
);
179 this->ScreenToClient(rectDummy
);
181 if (!m_ctrlTabCtrl
.Create(CMFCTabCtrl::STYLE_FLAT
, rectDummy
, this, IDC_REBASE_TAB
))
183 TRACE0("Failed to create output tab window\n");
184 return FALSE
; // fail to create
186 m_ctrlTabCtrl
.SetResizeMode(CMFCTabCtrl::RESIZE_NO
);
187 // Create output panes:
188 //const DWORD dwStyle = LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL;
189 DWORD dwStyle
=LVS_REPORT
| LVS_SHOWSELALWAYS
| LVS_ALIGNLEFT
| WS_BORDER
| WS_TABSTOP
| WS_CHILD
| WS_VISIBLE
;
191 if (! this->m_FileListCtrl
.Create(dwStyle
,rectDummy
,&this->m_ctrlTabCtrl
,0) )
193 TRACE0("Failed to create output windows\n");
194 return FALSE
; // fail to create
196 m_FileListCtrl
.m_hwndLogicalParent
= this;
198 if( ! this->m_LogMessageCtrl
.Create(_T("Scintilla"),_T("source"),0,rectDummy
,&m_ctrlTabCtrl
,0,0) )
200 TRACE0("Failed to create log message control");
203 m_ProjectProperties
.ReadProps();
204 m_LogMessageCtrl
.Init(m_ProjectProperties
);
205 m_LogMessageCtrl
.SetFont((CString
)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")), (DWORD
)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8));
206 m_LogMessageCtrl
.Call(SCI_SETREADONLY
, TRUE
);
208 dwStyle
= LBS_NOINTEGRALHEIGHT
| WS_CHILD
| WS_VISIBLE
| WS_HSCROLL
| WS_VSCROLL
;
210 if (!m_wndOutputRebase
.Create(_T("Scintilla"),_T("source"),0,rectDummy
, &m_ctrlTabCtrl
, 0,0) )
212 TRACE0("Failed to create output windows\n");
213 return -1; // fail to create
215 m_wndOutputRebase
.Init(-1);
216 m_wndOutputRebase
.SetFont((CString
)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")), (DWORD
)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8));
217 m_wndOutputRebase
.Call(SCI_SETREADONLY
, TRUE
);
219 m_tooltips
.AddTool(IDC_REBASE_CHECK_FORCE
,IDS_REBASE_FORCE_TT
);
220 m_tooltips
.AddTool(IDC_REBASE_ABORT
, IDS_REBASE_ABORT_TT
);
221 m_tooltips
.AddTool(IDC_REBASE_CHECK_PRESERVEMERGES
, IDS_REBASE_PRESERVEMERGES_TT
);
225 temp
.LoadString(IDS_PROC_REBASE_SELECTALL_PICK
);
226 m_SplitAllOptions
.AddEntry(temp
);
227 temp
.LoadString(IDS_PROC_REBASE_SELECTALL_SQUASH
);
228 m_SplitAllOptions
.AddEntry(temp
);
229 temp
.LoadString(IDS_PROC_REBASE_SELECTALL_EDIT
);
230 m_SplitAllOptions
.AddEntry(temp
);
231 temp
.LoadString(IDS_PROC_REBASE_UNSELECTED_SKIP
);
232 m_SplitAllOptions
.AddEntry(temp
);
233 temp
.LoadString(IDS_PROC_REBASE_UNSELECTED_SQUASH
);
234 m_SplitAllOptions
.AddEntry(temp
);
235 temp
.LoadString(IDS_PROC_REBASE_UNSELECTED_EDIT
);
236 m_SplitAllOptions
.AddEntry(temp
);
239 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
);
241 m_ctrlTabCtrl
.AddTab(&m_FileListCtrl
, CString(MAKEINTRESOURCE(IDS_PROC_REVISIONFILES
)));
242 m_ctrlTabCtrl
.AddTab(&m_LogMessageCtrl
, CString(MAKEINTRESOURCE(IDS_PROC_COMMITMESSAGE
)), 1);
245 AdjustControlSize(IDC_CHECK_CHERRYPICKED_FROM
);
246 AdjustControlSize(IDC_REBASE_SPLIT_COMMIT
);
248 CString sWindowTitle
;
249 GetWindowText(sWindowTitle
);
250 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, sWindowTitle
);
252 EnableSaveRestore(_T("RebaseDlg"));
254 DWORD yPos
= CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RebaseDlgSizer"));
255 RECT rcDlg
, rcLogMsg
, rcFileList
;
256 GetClientRect(&rcDlg
);
257 m_CommitList
.GetWindowRect(&rcLogMsg
);
258 ScreenToClient(&rcLogMsg
);
259 this->m_ctrlTabCtrl
.GetWindowRect(&rcFileList
);
260 ScreenToClient(&rcFileList
);
264 m_wndSplitter
.GetWindowRect(&rectSplitter
);
265 ScreenToClient(&rectSplitter
);
266 int delta
= yPos
- rectSplitter
.top
;
267 if ((rcLogMsg
.bottom
+ delta
> rcLogMsg
.top
)&&(rcLogMsg
.bottom
+ delta
< rcFileList
.bottom
- 30))
269 m_wndSplitter
.SetWindowPos(nullptr, 0, yPos
, 0, 0, SWP_NOSIZE
);
274 if (this->m_RebaseStage
== CHOOSE_BRANCH
&& !m_IsCherryPick
)
275 this->LoadBranchInfo();
278 this->m_BranchCtrl
.EnableWindow(FALSE
);
279 this->m_UpstreamCtrl
.EnableWindow(FALSE
);
280 GetDlgItem(IDC_BUTTON_REVERSE
)->EnableWindow(FALSE
);
283 m_CommitList
.m_ColumnRegKey
= _T("Rebase");
284 m_CommitList
.m_IsIDReplaceAction
= TRUE
;
285 // m_CommitList.m_IsOldFirst = TRUE;
286 m_CommitList
.m_IsRebaseReplaceGraph
= TRUE
;
287 m_CommitList
.m_bNoHightlightHead
= TRUE
;
288 m_CommitList
.m_bIsCherryPick
= !!m_IsCherryPick
;
290 m_CommitList
.InsertGitColumn();
292 this->SetControlEnable();
296 this->m_BranchCtrl
.SetCurSel(-1);
297 this->m_BranchCtrl
.EnableWindow(FALSE
);
298 GetDlgItem(IDC_REBASE_CHECK_FORCE
)->ShowWindow(SW_HIDE
);
299 GetDlgItem(IDC_REBASE_CHECK_PRESERVEMERGES
)->ShowWindow(SW_HIDE
);
300 GetDlgItem(IDC_BUTTON_BROWSE
)->EnableWindow(FALSE
);
301 GetDlgItem(IDC_BUTTON_REVERSE
)->EnableWindow(FALSE
);
302 GetDlgItem(IDC_BUTTON_ONTO
)->EnableWindow(FALSE
);
303 this->m_UpstreamCtrl
.AddString(_T("HEAD"));
304 this->m_UpstreamCtrl
.EnableWindow(FALSE
);
305 CAppUtils::SetWindowTitle(m_hWnd
, g_Git
.m_CurrentDir
, CString(MAKEINTRESOURCE(IDS_PROGS_TITLE_CHERRYPICK
)));
306 this->m_CommitList
.StartFilter();
310 ((CButton
*)GetDlgItem(IDC_BUTTON_ONTO
))->SetCheck(m_Onto
.IsEmpty() ? BST_UNCHECKED
: BST_CHECKED
);
311 GetDlgItem(IDC_CHECK_CHERRYPICKED_FROM
)->ShowWindow(SW_HIDE
);
312 ((CButton
*)GetDlgItem(IDC_BUTTON_REVERSE
))->SetIcon((HICON
)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SWITCHLEFTRIGHT
), IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
));
313 SetContinueButtonText();
314 m_CommitList
.DeleteAllItems();
318 m_CommitList
.m_ContextMenuMask
&= ~(m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_CHERRY_PICK
)|
319 m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_SWITCHTOREV
)|
320 m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_RESET
)|
321 m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_REVERTREV
)|
322 m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_MERGEREV
) |
323 m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_REBASE_TO_VERSION
)|
324 m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_REVERTTOREV
)|
325 m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_COMBINE_COMMIT
));
327 if(m_CommitList
.m_IsOldFirst
)
328 this->m_CurrentRebaseIndex
= -1;
330 this->m_CurrentRebaseIndex
= (int)m_CommitList
.m_logEntries
.size();
332 if (GetDlgItem(IDC_REBASE_CONTINUE
)->IsWindowEnabled() && m_bRebaseAutoStart
)
333 this->PostMessage(WM_COMMAND
, MAKELONG(IDC_REBASE_CONTINUE
, BN_CLICKED
), (LPARAM
)GetDlgItem(IDC_REBASE_CONTINUE
)->GetSafeHwnd());
337 // CRebaseDlg message handlers
339 HBRUSH
CRebaseDlg::OnCtlColor(CDC
* pDC
, CWnd
* pWnd
, UINT nCtlColor
)
341 if (pWnd
->GetDlgCtrlID() == IDC_STATUS_STATIC
&& nCtlColor
== CTLCOLOR_STATIC
&& m_bStatusWarning
)
343 pDC
->SetBkColor(RGB(255, 0, 0));
344 pDC
->SetTextColor(RGB(255, 255, 255));
345 return CreateSolidBrush(RGB(255, 0, 0));
348 return CResizableStandAloneDialog::OnCtlColor(pDC
, pWnd
, nCtlColor
);
351 void CRebaseDlg::SetAllRebaseAction(int action
)
353 for (size_t i
= 0; i
< this->m_CommitList
.m_logEntries
.size(); ++i
)
355 if (action
== CGitLogListBase::LOGACTIONS_REBASE_SQUASH
&& (i
== this->m_CommitList
.m_logEntries
.size() - 1 || (!m_IsCherryPick
&& m_CommitList
.m_logEntries
.GetGitRevAt(i
).ParentsCount() != 1)))
357 m_CommitList
.m_logEntries
.GetGitRevAt(i
).GetRebaseAction() = action
;
359 m_CommitList
.Invalidate();
362 void CRebaseDlg::OnBnClickedRebaseSplit()
367 LRESULT
CRebaseDlg::DefWindowProc(UINT message
, WPARAM wParam
, LPARAM lParam
)
371 if (wParam
== IDC_REBASE_SPLIT
)
373 SPC_NMHDR
* pHdr
= (SPC_NMHDR
*) lParam
;
379 return __super::DefWindowProc(message
, wParam
, lParam
);
382 void CRebaseDlg::DoSize(int delta
)
384 this->RemoveAllAnchors();
386 CSplitterControl::ChangeHeight(GetDlgItem(IDC_COMMIT_LIST
), delta
, CW_TOPALIGN
);
387 //CSplitterControl::ChangeHeight(GetDlgItem(), delta, CW_TOPALIGN);
388 CSplitterControl::ChangeHeight(GetDlgItem(IDC_REBASE_TAB
), -delta
, CW_BOTTOMALIGN
);
389 //CSplitterControl::ChangeHeight(GetDlgItem(), -delta, CW_BOTTOMALIGN);
390 CSplitterControl::ChangePos(GetDlgItem(IDC_SPLITALLOPTIONS
), 0, delta
);
391 CSplitterControl::ChangePos(GetDlgItem(IDC_BUTTON_UP2
),0,delta
);
392 CSplitterControl::ChangePos(GetDlgItem(IDC_BUTTON_DOWN2
),0,delta
);
393 CSplitterControl::ChangePos(GetDlgItem(IDC_REBASE_CHECK_FORCE
),0,delta
);
394 CSplitterControl::ChangePos(GetDlgItem(IDC_REBASE_CHECK_PRESERVEMERGES
), 0, delta
);
395 CSplitterControl::ChangePos(GetDlgItem(IDC_CHECK_CHERRYPICKED_FROM
), 0, delta
);
397 this->AddRebaseAnchor();
398 // adjust the minimum size of the dialog to prevent the resizing from
399 // moving the list control too far down.
401 m_CommitList
.GetClientRect(rcLogMsg
);
402 SetMinTrackSize(CSize(m_DlgOrigRect
.Width(), m_DlgOrigRect
.Height()-m_CommitListOrigRect
.Height()+rcLogMsg
.Height()));
405 // m_CommitList.Invalidate();
407 // GetDlgItem(IDC_LOGMESSAGE)->Invalidate();
409 this->m_ctrlTabCtrl
.Invalidate();
410 this->m_CommitList
.Invalidate();
411 this->m_FileListCtrl
.Invalidate();
412 this->m_LogMessageCtrl
.Invalidate();
415 void CRebaseDlg::SetSplitterRange()
417 if ((m_CommitList
)&&(m_ctrlTabCtrl
))
420 m_CommitList
.GetWindowRect(rcTop
);
421 ScreenToClient(rcTop
);
423 m_ctrlTabCtrl
.GetWindowRect(rcMiddle
);
424 ScreenToClient(rcMiddle
);
425 if (rcMiddle
.Height() && rcMiddle
.Width())
426 m_wndSplitter
.SetRange(rcTop
.top
+60, rcMiddle
.bottom
-80);
430 void CRebaseDlg::OnSize(UINT nType
,int cx
, int cy
)
432 // first, let the resizing take place
433 __super::OnSize(nType
, cx
, cy
);
439 void CRebaseDlg::SaveSplitterPos()
443 CRegDWORD regPos
= CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\RebaseDlgSizer"));
445 m_wndSplitter
.GetWindowRect(&rectSplitter
);
446 ScreenToClient(&rectSplitter
);
447 regPos
= rectSplitter
.top
;
451 void CRebaseDlg::LoadBranchInfo()
453 m_BranchCtrl
.SetMaxHistoryItems(0x7FFFFFFF);
454 m_UpstreamCtrl
.SetMaxHistoryItems(0x7FFFFFFF);
459 g_Git
.GetBranchList(list
,¤t
,CGit::BRANCH_ALL
);
460 m_BranchCtrl
.SetList(list
);
462 m_BranchCtrl
.SetCurSel(current
);
464 m_BranchCtrl
.AddString(g_Git
.GetCurrentBranch(true));
466 g_Git
.GetBranchList(list
, nullptr, CGit::BRANCH_ALL_F
);
467 g_Git
.GetTagList(list
);
468 m_UpstreamCtrl
.SetList(list
);
470 AddBranchToolTips(&m_BranchCtrl
);
472 if(!m_Upstream
.IsEmpty())
473 m_UpstreamCtrl
.AddString(m_Upstream
);
476 //Select pull-remote from current branch
477 CString pullRemote
, pullBranch
;
478 g_Git
.GetRemoteTrackedBranchForHEAD(pullRemote
, pullBranch
);
480 CString defaultUpstream
;
481 defaultUpstream
.Format(L
"remotes/%s/%s", (LPCTSTR
)pullRemote
, (LPCTSTR
)pullBranch
);
482 int found
= m_UpstreamCtrl
.FindStringExact(0, defaultUpstream
);
484 m_UpstreamCtrl
.SetCurSel(found
);
486 m_UpstreamCtrl
.SetCurSel(-1);
488 AddBranchToolTips(&m_UpstreamCtrl
);
491 void CRebaseDlg::OnCbnSelchangeBranch()
496 void CRebaseDlg::OnCbnSelchangeUpstream()
501 void CRebaseDlg::FetchLogList()
503 CGitHash base
,hash
,upstream
;
504 m_IsFastForward
=FALSE
;
506 if (m_BranchCtrl
.GetString().IsEmpty())
508 m_CommitList
.ShowText(CString(MAKEINTRESOURCE(IDS_SELECTBRANCH
)));
509 this->GetDlgItem(IDC_REBASE_CONTINUE
)->EnableWindow(false);
513 if (g_Git
.GetHash(hash
, m_BranchCtrl
.GetString()))
515 m_CommitList
.ShowText(g_Git
.GetGitLastErr(_T("Could not get hash of \"") + m_BranchCtrl
.GetString() + _T("\".")));
516 this->GetDlgItem(IDC_REBASE_CONTINUE
)->EnableWindow(false);
520 if (m_UpstreamCtrl
.GetString().IsEmpty())
522 m_CommitList
.ShowText(CString(MAKEINTRESOURCE(IDS_SELECTUPSTREAM
)));
523 this->GetDlgItem(IDC_REBASE_CONTINUE
)->EnableWindow(false);
527 if (g_Git
.GetHash(upstream
, m_UpstreamCtrl
.GetString()))
529 m_CommitList
.ShowText(g_Git
.GetGitLastErr(_T("Could not get hash of \"") + m_UpstreamCtrl
.GetString() + _T("\".")));
530 this->GetDlgItem(IDC_REBASE_CONTINUE
)->EnableWindow(false);
534 if (hash
== upstream
)
536 m_CommitList
.Clear();
538 text
.Format(IDS_REBASE_EQUAL_FMT
, (LPCTSTR
)m_BranchCtrl
.GetString(), (LPCTSTR
)this->m_UpstreamCtrl
.GetString());
540 m_CommitList
.ShowText(text
);
541 this->GetDlgItem(IDC_REBASE_CONTINUE
)->EnableWindow(false);
542 if (m_bRebaseAutoStart
)
543 PostMessage(WM_COMMAND
, MAKELONG(IDC_REBASE_ABORT
, BN_CLICKED
), (LPARAM
)GetDlgItem(IDC_REBASE_ABORT
)->GetSafeHwnd());
547 if (g_Git
.IsFastForward(m_BranchCtrl
.GetString(), m_UpstreamCtrl
.GetString(), &base
) && m_Onto
.IsEmpty())
549 this->m_IsFastForward
=TRUE
;
551 m_CommitList
.Clear();
553 text
.Format(IDS_REBASE_FASTFORWARD_FMT
, (LPCTSTR
)m_BranchCtrl
.GetString(), (LPCTSTR
)this->m_UpstreamCtrl
.GetString(),
554 (LPCTSTR
)m_BranchCtrl
.GetString(), (LPCTSTR
)this->m_UpstreamCtrl
.GetString());
556 m_CommitList
.ShowText(text
);
557 this->GetDlgItem(IDC_REBASE_CONTINUE
)->EnableWindow(true);
558 SetContinueButtonText();
563 if (!m_bForce
&& m_Onto
.IsEmpty())
565 if (base
== upstream
)
567 m_CommitList
.Clear();
569 text
.Format(IDS_REBASE_UPTODATE_FMT
, (LPCTSTR
)m_BranchCtrl
.GetString());
570 m_CommitList
.ShowText(text
);
571 this->GetDlgItem(IDC_REBASE_CONTINUE
)->EnableWindow(FALSE
);
572 SetContinueButtonText();
573 if (m_bRebaseAutoStart
)
574 PostMessage(WM_COMMAND
, MAKELONG(IDC_REBASE_ABORT
, BN_CLICKED
), (LPARAM
)GetDlgItem(IDC_REBASE_ABORT
)->GetSafeHwnd());
579 m_CommitList
.Clear();
580 CString refFrom
= g_Git
.FixBranchName(m_UpstreamCtrl
.GetString());
581 CString refTo
= g_Git
.FixBranchName(m_BranchCtrl
.GetString());
583 range
.Format(_T("%s..%s"), (LPCTSTR
)refFrom
, (LPCTSTR
)refTo
);
584 this->m_CommitList
.FillGitLog(nullptr, &range
, (m_bPreserveMerges
? 0 : CGit::LOG_INFO_NO_MERGE
) | CGit::LOG_ORDER_TOPOORDER
);
586 if( m_CommitList
.GetItemCount() == 0 )
587 m_CommitList
.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_NOTHINGTOREBASE
)));
589 m_rewrittenCommitsMap
.clear();
590 if (m_bPreserveMerges
)
593 if (g_Git
.GetHash(head
, _T("HEAD")))
595 AddLogString(CString(MAKEINTRESOURCE(IDS_PROC_NOHEAD
)));
598 CGitHash upstreamHash
;
599 if (g_Git
.GetHash(upstreamHash
, m_Onto
.IsEmpty() ? m_UpstreamCtrl
.GetString() : m_Onto
))
601 MessageBox(g_Git
.GetGitLastErr(_T("Could not get hash of \"") + (m_Onto
.IsEmpty() ? m_UpstreamCtrl
.GetString() : m_Onto
) + _T("\".")), _T("TortoiseGit"), MB_ICONERROR
);
605 mergecmd
.Format(L
"git merge-base --all %s %s", (LPCTSTR
)head
.ToString(), (LPCTSTR
)upstreamHash
.ToString());
606 g_Git
.Run(mergecmd
, [&](const CStringA
& line
)
609 hash
.ConvertFromStrA(line
);
612 m_rewrittenCommitsMap
[hash
] = upstreamHash
;
615 std::vector
<size_t> toDrop
;
616 for (size_t i
= m_CommitList
.m_arShownList
.size(); i
-- > 0;)
618 bool preserve
= false;
619 GitRevLoglist
* pRev
= m_CommitList
.m_arShownList
.SafeGetAt(i
);
620 for (const auto& parent
: pRev
->m_ParentHash
)
622 const auto rewrittenParent
= m_rewrittenCommitsMap
.find(parent
);
623 if (rewrittenParent
!= m_rewrittenCommitsMap
.cend())
630 m_rewrittenCommitsMap
[pRev
->m_CommitHash
] = CGitHash();
635 // Drop already included commits
636 std::vector
<CGitHash
> nonCherryPicked
;
638 cherryCmd
.Format(L
"git rev-list \"%s...%s\" --left-right --cherry-pick", (LPCTSTR
)refFrom
, (LPCTSTR
)refTo
);
639 g_Git
.Run(cherryCmd
, [&](const CStringA
& line
)
641 if (line
.GetLength() < 2)
645 CString hash
= CUnicodeUtils::GetUnicode(line
.Mid(1));
647 nonCherryPicked
.emplace_back(hash
);
649 for (size_t i
= m_CommitList
.m_arShownList
.size(); i
-- > 0;)
651 GitRevLoglist
* pRev
= m_CommitList
.m_arShownList
.SafeGetAt(i
);
652 pRev
->GetRebaseAction() = CGitLogListBase::LOGACTIONS_REBASE_PICK
;
653 if (m_rewrittenCommitsMap
.find(pRev
->m_CommitHash
) != m_rewrittenCommitsMap
.cend() && std::find(nonCherryPicked
.cbegin(), nonCherryPicked
.cend(), pRev
->m_CommitHash
) == nonCherryPicked
.cend())
655 m_droppedCommitsMap
[pRev
->m_CommitHash
].clear();
656 m_droppedCommitsMap
[pRev
->m_CommitHash
].push_back(pRev
->m_ParentHash
[0]);
658 m_rewrittenCommitsMap
.erase(pRev
->m_CommitHash
);
661 std::sort(toDrop
.begin(), toDrop
.end());
662 toDrop
.erase(unique(toDrop
.begin(), toDrop
.end()), toDrop
.end());
663 for (auto it
= toDrop
.crbegin(); it
!= toDrop
.crend(); ++it
)
665 m_CommitList
.m_arShownList
.SafeRemoveAt(*it
);
666 m_CommitList
.m_logEntries
.erase(m_CommitList
.m_logEntries
.begin() + *it
);
668 m_CommitList
.SetItemCountEx((int)m_CommitList
.m_logEntries
.size());
672 if(m_CommitList
.m_logEntries
[m_CommitList
.m_logEntries
.size()-1].m_ParentHash
.size() >=0 )
674 if(upstream
== m_CommitList
.m_logEntries
[m_CommitList
.m_logEntries
.size()-1].m_ParentHash
[0])
676 m_CommitList
.Clear();
677 m_CommitList
.ShowText(_T("Nothing Rebase"));
683 AddBranchToolTips(&this->m_BranchCtrl
);
684 AddBranchToolTips(&this->m_UpstreamCtrl
);
686 bool bHasSKip
= false;
687 if (!m_bPreserveMerges
)
689 // Default all actions to 'pick'
690 std::map
<CGitHash
, size_t> revIxMap
;
691 for (size_t i
= 0; i
< m_CommitList
.m_logEntries
.size(); ++i
)
693 GitRevLoglist
& rev
= m_CommitList
.m_logEntries
.GetGitRevAt(i
);
694 rev
.GetRebaseAction() = CGitLogListBase::LOGACTIONS_REBASE_PICK
;
695 revIxMap
[rev
.m_CommitHash
] = i
;
698 // Default to skip when already in upstream
699 if (!m_Onto
.IsEmpty())
700 refFrom
= g_Git
.FixBranchName(m_Onto
);
702 cherryCmd
.Format(L
"git.exe cherry \"%s\" \"%s\"", (LPCTSTR
)refFrom
, (LPCTSTR
)refTo
);
703 g_Git
.Run(cherryCmd
, [&](const CStringA
& line
)
705 if (line
.GetLength() < 2)
708 return; // Don't skip (only skip commits starting with a '-')
709 CString hash
= CUnicodeUtils::GetUnicode(line
.Mid(1));
711 auto itIx
= revIxMap
.find(CGitHash(hash
));
712 if (itIx
== revIxMap
.end())
713 return; // Not found?? Should not occur...
716 m_CommitList
.m_logEntries
.GetGitRevAt(itIx
->second
).GetRebaseAction() = CGitLogListBase::LOGACTIONS_REBASE_SKIP
;
720 m_CommitList
.Invalidate();
723 m_CtrlStatusText
.SetWindowText(CString(MAKEINTRESOURCE(IDS_REBASE_AUTOSKIPPED
)));
724 m_bStatusWarning
= true;
728 m_CtrlStatusText
.SetWindowText(m_sStatusText
);
729 m_bStatusWarning
= false;
731 m_CtrlStatusText
.Invalidate();
733 if(m_CommitList
.m_IsOldFirst
)
734 this->m_CurrentRebaseIndex
= -1;
736 this->m_CurrentRebaseIndex
= (int)m_CommitList
.m_logEntries
.size();
738 this->GetDlgItem(IDC_REBASE_CONTINUE
)->EnableWindow(m_bPreserveMerges
|| m_CommitList
.GetItemCount());
739 SetContinueButtonText();
742 void CRebaseDlg::AddBranchToolTips(CHistoryCombo
*pBranch
)
746 CString text
=pBranch
->GetString();
751 pBranch
->DisableTooltip();
756 if (rev
.GetCommit(text
))
758 MessageBox(rev
.GetLastErr(), _T("TortoiseGit"), MB_ICONERROR
);
759 pBranch
->DisableTooltip();
763 tooltip
.Format(_T("%s: %s\n%s: %s <%s>\n%s: %s\n%s:\n%s\n%s"),
764 (LPCTSTR
)CString(MAKEINTRESOURCE(IDS_LOG_REVISION
)),
765 (LPCTSTR
)rev
.m_CommitHash
.ToString(),
766 (LPCTSTR
)CString(MAKEINTRESOURCE(IDS_LOG_AUTHOR
)),
767 (LPCTSTR
)rev
.GetAuthorName(),
768 (LPCTSTR
)rev
.GetAuthorEmail(),
769 (LPCTSTR
)CString(MAKEINTRESOURCE(IDS_LOG_DATE
)),
770 (LPCTSTR
)CLoglistUtils::FormatDateAndTime(rev
.GetAuthorDate(), DATE_LONGDATE
),
771 (LPCTSTR
)CString(MAKEINTRESOURCE(IDS_LOG_MESSAGE
)),
772 (LPCTSTR
)rev
.GetSubject(),
773 (LPCTSTR
)rev
.GetBody());
775 pBranch
->DisableTooltip();
776 this->m_tooltips
.AddTool(pBranch
->GetComboBoxCtrl(),tooltip
);
780 BOOL
CRebaseDlg::PreTranslateMessage(MSG
*pMsg
)
782 if (pMsg
->message
== WM_KEYDOWN
)
784 switch (pMsg
->wParam
)
787 if (LogListHasFocus(pMsg
->hwnd
)
788 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_PICK
)
789 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SQUASH
)
790 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_EDIT
)
791 && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SKIP
))
793 m_CommitList
.ShiftSelectedRebaseAction();
798 if (LogListHasFocus(pMsg
->hwnd
) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_PICK
))
800 m_CommitList
.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_PICK
);
805 if (LogListHasFocus(pMsg
->hwnd
) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SKIP
))
807 m_CommitList
.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_SKIP
);
812 if (LogListHasFocus(pMsg
->hwnd
) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_SQUASH
))
814 m_CommitList
.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_SQUASH
);
819 if (LogListHasFocus(pMsg
->hwnd
) && LogListHasMenuItem(CGitLogListBase::ID_REBASE_EDIT
))
821 m_CommitList
.SetSelectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_EDIT
);
826 if(LogListHasFocus(pMsg
->hwnd
) && GetAsyncKeyState(VK_CONTROL
) & 0x8000)
828 // select all entries
829 for (int i
= 0; i
< m_CommitList
.GetItemCount(); ++i
)
830 m_CommitList
.SetItemState(i
, LVIS_SELECTED
, LVIS_SELECTED
);
842 if (GetAsyncKeyState(VK_CONTROL
) & 0x8000)
844 if (GetDlgItem(IDC_REBASE_CONTINUE
)->IsWindowEnabled())
845 GetDlgItem(IDC_REBASE_CONTINUE
)->SetFocus();
846 else if (GetDlgItem(IDC_REBASE_ABORT
)->IsWindowEnabled())
847 GetDlgItem(IDC_REBASE_ABORT
)->SetFocus();
849 GetDlgItem(IDHELP
)->SetFocus();
854 /* Avoid TAB control destroy but dialog exist*/
858 TCHAR buff
[128] = { 0 };
859 ::GetClassName(pMsg
->hwnd
,buff
,128);
862 /* Use MSFTEDIT_CLASS http://msdn.microsoft.com/en-us/library/bb531344.aspx */
863 if (_tcsnicmp(buff
, MSFTEDIT_CLASS
, 128) == 0 || //Unicode and MFC 2012 and later
864 _tcsnicmp(buff
, RICHEDIT_CLASS
, 128) == 0 || //ANSI or MFC 2010
865 _tcsnicmp(buff
,_T("Scintilla"),128)==0 ||
866 _tcsnicmp(buff
,_T("SysListView32"),128)==0||
867 ::GetParent(pMsg
->hwnd
) == this->m_ctrlTabCtrl
.m_hWnd
)
869 this->PostMessage(WM_KEYDOWN
,VK_ESCAPE
,0);
875 else if (pMsg
->message
== WM_NEXTDLGCTL
)
877 HWND hwnd
= GetFocus()->GetSafeHwnd();
878 if (hwnd
== m_LogMessageCtrl
.GetSafeHwnd() || hwnd
== m_wndOutputRebase
.GetSafeHwnd())
880 if (GetDlgItem(IDC_REBASE_CONTINUE
)->IsWindowEnabled())
881 GetDlgItem(IDC_REBASE_CONTINUE
)->SetFocus();
882 else if (GetDlgItem(IDC_REBASE_ABORT
)->IsWindowEnabled())
883 GetDlgItem(IDC_REBASE_ABORT
)->SetFocus();
885 GetDlgItem(IDHELP
)->SetFocus();
889 return CResizableStandAloneDialog::PreTranslateMessage(pMsg
);
892 bool CRebaseDlg::LogListHasFocus(HWND hwnd
)
894 TCHAR buff
[128] = { 0 };
895 ::GetClassName(hwnd
, buff
, 128);
897 if(_tcsnicmp(buff
, _T("SysListView32"), 128) == 0)
902 bool CRebaseDlg::LogListHasMenuItem(int i
)
904 return (m_CommitList
.m_ContextMenuMask
& m_CommitList
.GetContextMenuBit(i
)) != 0;
907 int CRebaseDlg::CheckRebaseCondition()
909 this->m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_LOG
);
911 if( !g_Git
.CheckCleanWorkTree() )
913 if ((!m_IsCherryPick
&& g_Git
.GetConfigValueBool(L
"rebase.autostash")) || CMessageBox::Show(GetSafeHwnd(), IDS_ERROR_NOCLEAN_STASH
, IDS_APPNAME
, 1, IDI_QUESTION
, IDS_STASHBUTTON
, IDS_ABORTBUTTON
) == 1)
916 cmd
=_T("git.exe stash");
917 this->AddLogString(cmd
);
918 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
920 MessageBox(out
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
928 //Todo Check $REBASE_ROOT
931 if (!CAppUtils::CheckUserData())
934 //Todo call pre_rebase_hook
938 void CRebaseDlg::CheckRestoreStash()
940 bool autoStash
= !m_IsCherryPick
&& g_Git
.GetConfigValueBool(L
"rebase.autostash");
941 if (m_bStashed
&& (autoStash
|| CMessageBox::Show(GetSafeHwnd(), IDS_DCOMMIT_STASH_POP
, IDS_APPNAME
, MB_YESNO
| MB_ICONQUESTION
) == IDYES
))
942 CAppUtils::StashPop(autoStash
? 0 : 1);
946 int CRebaseDlg::StartRebase()
949 m_OrigHEADBranch
= g_Git
.GetCurrentBranch(true);
951 m_OrigHEADHash
.Empty();
952 if (g_Git
.GetHash(m_OrigHEADHash
, _T("HEAD")))
954 AddLogString(CString(MAKEINTRESOURCE(IDS_PROC_NOHEAD
)));
958 //git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null ||
959 // echo "detached HEAD" > "$DOTEST"/head-name
961 cmd
.Format(_T("git.exe update-ref ORIG_HEAD ") + m_OrigHEADHash
.ToString());
962 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
964 AddLogString(_T("update ORIG_HEAD Fail"));
968 m_OrigUpstreamHash
.Empty();
969 if (g_Git
.GetHash(m_OrigUpstreamHash
, (m_IsCherryPick
|| m_Onto
.IsEmpty()) ? m_UpstreamCtrl
.GetString() : m_Onto
))
971 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
);
975 if( !this->m_IsCherryPick
)
977 cmd
.Format(_T("git.exe checkout -f %s --"), (LPCTSTR
)m_OrigUpstreamHash
.ToString());
978 this->AddLogString(cmd
);
979 if (RunGitCmdRetryOrAbort(cmd
))
984 if( !this->m_IsCherryPick
)
986 if (g_Git
.GetHash(m_OrigBranchHash
, m_BranchCtrl
.GetString()))
988 MessageBox(g_Git
.GetGitLastErr(_T("Could not get hash of \"") + m_BranchCtrl
.GetString() + _T("\".")), _T("TortoiseGit"), MB_ICONERROR
);
991 log
.Format(_T("%s\r\n"), (LPCTSTR
)CString(MAKEINTRESOURCE(IDS_PROC_REBASE_STARTREBASE
)));
994 log
.Format(_T("%s\r\n"), (LPCTSTR
)CString(MAKEINTRESOURCE(IDS_PROC_REBASE_STARTCHERRYPICK
)));
996 this->AddLogString(log
);
999 int CRebaseDlg::VerifyNoConflict()
1001 int hasConflicts
= g_Git
.HasWorkingTreeConflicts();
1002 if (hasConflicts
< 0)
1004 AddLogString(g_Git
.GetGitLastErr(L
"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS
));
1009 CMessageBox::Show(GetSafeHwnd(), IDS_PROGRS_CONFLICTSOCCURED
, IDS_APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
1012 CleanUpRebaseActiveFolder();
1016 static bool IsLocalBranch(CString ref
)
1019 g_Git
.GetBranchList(list
, nullptr, CGit::BRANCH_LOCAL
);
1020 return std::find(list
.cbegin(), list
.cend(), ref
) != list
.cend();
1023 int CRebaseDlg::FinishRebase()
1025 if (m_bFinishedRebase
)
1028 m_bFinishedRebase
= true;
1029 if(this->m_IsCherryPick
) //cherry pick mode no "branch", working at upstream branch
1031 m_sStatusText
.LoadString(IDS_DONE
);
1032 m_CtrlStatusText
.SetWindowText(m_sStatusText
);
1033 m_bStatusWarning
= false;
1034 m_CtrlStatusText
.Invalidate();
1041 if (g_Git
.GetHash(head
, _T("HEAD")))
1043 MessageBox(g_Git
.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR
);
1048 if (IsLocalBranch(m_BranchCtrl
.GetString()))
1050 cmd
.Format(_T("git.exe checkout -f -B %s %s --"), (LPCTSTR
)m_BranchCtrl
.GetString(), (LPCTSTR
)head
.ToString());
1052 if (RunGitCmdRetryOrAbort(cmd
))
1057 cmd
.Format(_T("git.exe reset --hard %s --"), (LPCTSTR
)head
.ToString());
1059 if (RunGitCmdRetryOrAbort(cmd
))
1063 while (m_ctrlTabCtrl
.GetTabsNum() > 1)
1064 m_ctrlTabCtrl
.RemoveTab(0);
1065 m_CtrlStatusText
.SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_REBASEFINISHED
)));
1066 m_sStatusText
.LoadString(IDS_PROC_REBASEFINISHED
);
1067 m_bStatusWarning
= false;
1068 m_CtrlStatusText
.Invalidate();
1070 m_bRebaseAutoEnd
= m_bRebaseAutoStart
;
1075 void CRebaseDlg::RewriteNotes()
1078 for (const auto& entry
: m_rewrittenCommitsMap
)
1080 if (entry
.second
.IsEmpty())
1082 rewrites
+= entry
.first
.ToString();
1084 rewrites
+= entry
.second
.ToString();
1087 if (rewrites
.IsEmpty())
1089 CString tmpfile
= GetTempFile();
1090 tmpfile
.Replace(L
"\\", L
"/");
1091 if (!CStringUtils::WriteStringToTextFile((LPCTSTR
)tmpfile
, (LPCTSTR
)rewrites
))
1093 SCOPE_EXIT
{ ::DeleteFile(tmpfile
); };
1094 CString pipefile
= GetTempFile();
1095 pipefile
.Replace(L
"\\", L
"/");
1097 pipecmd
.Format(L
"git notes copy --for-rewrite=rebase < %s", (LPCTSTR
)tmpfile
);
1098 if (!CStringUtils::WriteStringToTextFile((LPCTSTR
)pipefile
, (LPCTSTR
)pipecmd
))
1100 SCOPE_EXIT
{ ::DeleteFile(pipefile
); };
1102 g_Git
.Run(L
"bash.exe " + pipefile
, &out
, CP_UTF8
);
1105 void CRebaseDlg::OnBnClickedContinue()
1107 if( m_RebaseStage
== REBASE_DONE
)
1110 CleanUpRebaseActiveFolder();
1111 CheckRestoreStash();
1115 if (m_RebaseStage
== CHOOSE_BRANCH
|| m_RebaseStage
== CHOOSE_COMMIT_PICK_MODE
)
1117 if (CheckRebaseCondition())
1121 if( this->m_IsFastForward
)
1124 if (g_Git
.GetHash(m_OrigBranchHash
, m_BranchCtrl
.GetString()))
1126 MessageBox(g_Git
.GetGitLastErr(_T("Could not get hash of \"") + m_BranchCtrl
.GetString() + _T("\".")), _T("TortoiseGit"), MB_ICONERROR
);
1129 if (g_Git
.GetHash(m_OrigUpstreamHash
, m_UpstreamCtrl
.GetString()))
1131 MessageBox(g_Git
.GetGitLastErr(_T("Could not get hash of \"") + m_UpstreamCtrl
.GetString() + _T("\".")), _T("TortoiseGit"), MB_ICONERROR
);
1135 if(!g_Git
.IsFastForward(this->m_BranchCtrl
.GetString(),this->m_UpstreamCtrl
.GetString()))
1137 this->m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_LOG
);
1138 AddLogString(_T("No fast forward possible.\r\nMaybe repository changed"));
1142 if (IsLocalBranch(m_BranchCtrl
.GetString()))
1144 cmd
.Format(_T("git.exe checkout --no-track -f -B %s %s --"), (LPCTSTR
)m_BranchCtrl
.GetString(), (LPCTSTR
)m_UpstreamCtrl
.GetString());
1146 if (RunGitCmdRetryOrAbort(cmd
))
1151 cmd
.Format(_T("git.exe reset --hard %s --"), (LPCTSTR
)g_Git
.FixBranchName(this->m_UpstreamCtrl
.GetString()));
1153 log
.Format(IDS_PROC_REBASE_FFTO
, m_UpstreamCtrl
.GetString());
1154 this->AddLogString(log
);
1157 this->m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_LOG
);
1158 if (RunGitCmdRetryOrAbort(cmd
))
1161 AddLogString(CString(MAKEINTRESOURCE(IDS_DONE
)));
1162 m_RebaseStage
= REBASE_DONE
;
1163 UpdateCurrentStatus();
1165 if (m_bRebaseAutoStart
)
1166 this->PostMessage(WM_COMMAND
, MAKELONG(IDC_REBASE_CONTINUE
, BN_CLICKED
), (LPARAM
)GetDlgItem(IDC_REBASE_CONTINUE
)->GetSafeHwnd());
1171 if( m_RebaseStage
== CHOOSE_BRANCH
|| m_RebaseStage
== CHOOSE_COMMIT_PICK_MODE
)
1173 if(CheckRebaseCondition())
1175 m_RebaseStage
= REBASE_START
;
1176 m_FileListCtrl
.Clear();
1177 m_FileListCtrl
.SetHasCheckboxes(false);
1178 m_FileListCtrl
.m_CurrentVersion
= L
"";
1179 m_ctrlTabCtrl
.SetTabLabel(REBASE_TAB_CONFLICT
, CString(MAKEINTRESOURCE(IDS_PROC_CONFLICTFILES
)));
1180 m_ctrlTabCtrl
.AddTab(&m_wndOutputRebase
, CString(MAKEINTRESOURCE(IDS_LOG
)), 2);
1183 if( m_RebaseStage
== REBASE_FINISH
)
1191 if( m_RebaseStage
== REBASE_SQUASH_CONFLICT
)
1193 if(VerifyNoConflict())
1195 GitRevLoglist
* curRev
= m_CommitList
.m_arShownList
.SafeGetAt(m_CurrentRebaseIndex
);
1196 if(this->CheckNextCommitIsSquash())
1197 {//next commit is not squash;
1198 m_RebaseStage
= REBASE_SQUASH_EDIT
;
1199 this->OnRebaseUpdateUI(0,0);
1200 this->UpdateCurrentStatus();
1203 m_RebaseStage
=REBASE_CONTINUE
;
1204 curRev
->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE
;
1205 m_forRewrite
.push_back(curRev
->m_CommitHash
);
1206 this->UpdateCurrentStatus();
1209 if( m_RebaseStage
== REBASE_CONFLICT
)
1211 if(VerifyNoConflict())
1214 GitRevLoglist
* curRev
= m_CommitList
.m_arShownList
.SafeGetAt(m_CurrentRebaseIndex
);
1215 // ***************************************************
1216 // ATTENTION: Similar code in CommitDlg.cpp!!!
1217 // ***************************************************
1218 CMassiveGitTask
mgtReAddAfterCommit(_T("add --ignore-errors -f"));
1219 CMassiveGitTask
mgtReDelAfterCommit(_T("rm --cached --ignore-unmatch"));
1220 CMassiveGitTask
mgtAdd(_T("add -f"));
1221 CMassiveGitTask
mgtUpdateIndexForceRemove(_T("update-index --force-remove"));
1222 CMassiveGitTask
mgtUpdateIndex(_T("update-index"));
1223 CMassiveGitTask
mgtRm(_T("rm --ignore-unmatch"));
1224 CMassiveGitTask
mgtRmFCache(_T("rm -f --cache"));
1225 CMassiveGitTask
mgtReset(_T("reset"), TRUE
, true);
1226 for (int i
= 0; i
< m_FileListCtrl
.GetItemCount(); i
++)
1228 CTGitPath
*entry
= (CTGitPath
*)m_FileListCtrl
.GetItemData(i
);
1229 if (entry
->m_Checked
)
1231 if (entry
->m_Action
& CTGitPath::LOGACTIONS_UNVER
)
1232 mgtAdd
.AddFile(entry
->GetGitPathString());
1233 else if (entry
->m_Action
& CTGitPath::LOGACTIONS_DELETED
)
1234 mgtUpdateIndexForceRemove
.AddFile(entry
->GetGitPathString());
1236 mgtUpdateIndex
.AddFile(entry
->GetGitPathString());
1238 if ((entry
->m_Action
& CTGitPath::LOGACTIONS_REPLACED
) && !entry
->GetGitOldPathString().IsEmpty())
1239 mgtRm
.AddFile(entry
->GetGitOldPathString());
1243 if (entry
->m_Action
& CTGitPath::LOGACTIONS_ADDED
|| entry
->m_Action
& CTGitPath::LOGACTIONS_REPLACED
)
1245 mgtRmFCache
.AddFile(entry
->GetGitPathString());
1246 mgtReAddAfterCommit
.AddFile(*entry
);
1248 if (entry
->m_Action
& CTGitPath::LOGACTIONS_REPLACED
&& !entry
->GetGitOldPathString().IsEmpty())
1250 mgtReset
.AddFile(entry
->GetGitOldPathString());
1251 mgtReDelAfterCommit
.AddFile(entry
->GetGitOldPathString());
1254 else if(!(entry
->m_Action
& CTGitPath::LOGACTIONS_UNVER
))
1256 mgtReset
.AddFile(entry
->GetGitPathString());
1257 if (entry
->m_Action
& CTGitPath::LOGACTIONS_DELETED
&& !(entry
->m_Action
& CTGitPath::LOGACTIONS_MISSING
))
1258 mgtReDelAfterCommit
.AddFile(entry
->GetGitPathString());
1263 BOOL cancel
= FALSE
;
1264 bool successful
= true;
1265 successful
= successful
&& mgtAdd
.Execute(cancel
);
1266 successful
= successful
&& mgtUpdateIndexForceRemove
.Execute(cancel
);
1267 successful
= successful
&& mgtUpdateIndex
.Execute(cancel
);
1268 successful
= successful
&& mgtRm
.Execute(cancel
);
1269 successful
= successful
&& mgtRmFCache
.Execute(cancel
);
1270 successful
= successful
&& mgtReset
.Execute(cancel
);
1274 AddLogString(_T("An error occurred while updating the index."));
1278 CString out
=_T("");
1280 cmd
.Format(_T("git.exe commit --allow-empty-message -C %s"), (LPCTSTR
)curRev
->m_CommitHash
.ToString());
1284 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
1287 if(!g_Git
.CheckCleanWorkTree())
1289 CMessageBox::Show(GetSafeHwnd(), out
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1296 // update commit message if needed
1297 CString str
= m_LogMessageCtrl
.GetText().Trim();
1298 if (str
!= (curRev
->GetSubject() + _T("\n") + curRev
->GetBody()).Trim())
1300 if (str
.Trim().IsEmpty())
1302 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_COMMITMESSAGE_EMPTY
,IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1305 CString tempfile
= ::GetTempFile();
1306 if (CAppUtils::SaveCommitUnicodeFile(tempfile
, str
))
1308 CMessageBox::Show(GetSafeHwnd(), _T("Could not save commit message"), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1313 cmd
.Format(_T("git.exe commit --amend -F \"%s\""), (LPCTSTR
)tempfile
);
1316 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
1319 if (!g_Git
.CheckCleanWorkTree())
1321 CMessageBox::Show(GetSafeHwnd(), out
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1329 if (((DWORD
)CRegStdDWORD(_T("Software\\TortoiseGit\\ReaddUnselectedAddedFilesAfterCommit"), TRUE
)) == TRUE
)
1331 BOOL cancel2
= FALSE
;
1332 mgtReAddAfterCommit
.Execute(cancel2
);
1333 mgtReDelAfterCommit
.Execute(cancel2
);
1336 this->m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_LOG
);
1337 if (curRev
->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_EDIT
)
1339 m_RebaseStage
=REBASE_EDIT
;
1340 this->m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_MESSAGE
);
1341 this->UpdateCurrentStatus();
1346 m_RebaseStage
=REBASE_CONTINUE
;
1347 curRev
->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE
;
1348 this->UpdateCurrentStatus();
1350 if (CheckNextCommitIsSquash() == 0) // remember commit msg after edit if next commit if squash
1351 ResetParentForSquash(str
);
1354 m_SquashMessage
.Empty();
1356 if (g_Git
.GetHash(head
, _T("HEAD")))
1358 MessageBox(g_Git
.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR
);
1361 m_rewrittenCommitsMap
[curRev
->m_CommitHash
] = head
;
1366 if ((m_RebaseStage
== REBASE_EDIT
|| m_RebaseStage
== REBASE_CONTINUE
|| m_bSplitCommit
|| m_RebaseStage
== REBASE_SQUASH_EDIT
) && CheckNextCommitIsSquash() && (m_bSplitCommit
|| !g_Git
.CheckCleanWorkTree(true)))
1368 if (!m_bSplitCommit
&& CMessageBox::Show(GetSafeHwnd(), IDS_PROC_REBASE_CONTINUE_NOTCLEAN
, IDS_APPNAME
, 1, IDI_ERROR
, IDS_MSGBOX_OK
, IDS_ABORTBUTTON
) == 2)
1370 BOOL isFirst
= TRUE
;
1375 dlg
.m_sLogMessage
= m_LogMessageCtrl
.GetText();
1376 dlg
.m_bWholeProject
= true;
1377 dlg
.m_bSelectFilesForCommit
= true;
1378 dlg
.m_bCommitAmend
= isFirst
&& (m_RebaseStage
!= REBASE_SQUASH_EDIT
); // do not amend on squash_edit stage, we need a normal commit there
1379 if (isFirst
&& m_RebaseStage
== REBASE_SQUASH_EDIT
)
1381 dlg
.SetTime(m_SquashFirstMetaData
.time
);
1382 dlg
.SetAuthor(m_SquashFirstMetaData
.GetAuthor());
1385 gpl
.AddPath(CTGitPath());
1386 dlg
.m_pathList
= gpl
;
1387 dlg
.m_bAmendDiffToLastCommit
= !m_bSplitCommit
;
1388 dlg
.m_bNoPostActions
= true;
1389 if (dlg
.m_bCommitAmend
)
1390 dlg
.m_AmendStr
= dlg
.m_sLogMessage
;
1391 dlg
.m_bWarnDetachedHead
= false;
1393 if (dlg
.DoModal() != IDOK
)
1396 isFirst
= !m_bSplitCommit
; // only select amend on second+ runs if not in split commit mode
1398 m_SquashMessage
.Empty();
1399 } while (!g_Git
.CheckCleanWorkTree() || (m_bSplitCommit
&& CMessageBox::Show(GetSafeHwnd(), IDS_REBASE_ADDANOTHERCOMMIT
, IDS_APPNAME
, MB_YESNO
| MB_ICONQUESTION
) == IDYES
));
1401 m_bSplitCommit
= FALSE
;
1404 this->m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_LOG
);
1405 m_RebaseStage
= REBASE_CONTINUE
;
1406 GitRevLoglist
* curRev
= m_CommitList
.m_arShownList
.SafeGetAt(m_CurrentRebaseIndex
);
1408 if (g_Git
.GetHash(head
, _T("HEAD")))
1410 MessageBox(g_Git
.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR
);
1413 m_rewrittenCommitsMap
[curRev
->m_CommitHash
] = head
;
1414 for (const auto& hash
: m_forRewrite
)
1415 m_rewrittenCommitsMap
[hash
] = head
;
1416 m_forRewrite
.clear();
1417 curRev
->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE
;
1418 this->UpdateCurrentStatus();
1421 if( m_RebaseStage
== REBASE_EDIT
|| m_RebaseStage
== REBASE_SQUASH_EDIT
)
1424 GitRevLoglist
* curRev
= m_CommitList
.m_arShownList
.SafeGetAt(m_CurrentRebaseIndex
);
1426 str
=this->m_LogMessageCtrl
.GetText();
1427 if(str
.Trim().IsEmpty())
1429 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_COMMITMESSAGE_EMPTY
,IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1433 CString tempfile
=::GetTempFile();
1434 if (CAppUtils::SaveCommitUnicodeFile(tempfile
, str
))
1436 CMessageBox::Show(GetSafeHwnd(), _T("Could not save commit message"), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1442 if( m_RebaseStage
== REBASE_SQUASH_EDIT
)
1443 cmd
.Format(_T("git.exe commit %s-F \"%s\""), (LPCTSTR
)m_SquashFirstMetaData
.GetAsParam(), (LPCTSTR
)tempfile
);
1447 int isEmpty
= IsCommitEmpty(curRev
->m_CommitHash
);
1449 options
= _T("--allow-empty ");
1450 else if (isEmpty
< 0)
1452 cmd
.Format(_T("git.exe commit --amend %s-F \"%s\""), (LPCTSTR
)options
, (LPCTSTR
)tempfile
);
1455 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
1457 if(!g_Git
.CheckCleanWorkTree())
1459 CMessageBox::Show(GetSafeHwnd(), out
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1464 ::DeleteFile(tempfile
);
1466 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
1468 ResetParentForSquash(str
);
1471 m_SquashMessage
.Empty();
1472 this->m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_LOG
);
1473 m_RebaseStage
=REBASE_CONTINUE
;
1475 if (g_Git
.GetHash(head
, _T("HEAD")))
1477 MessageBox(g_Git
.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR
);
1480 m_rewrittenCommitsMap
[curRev
->m_CommitHash
] = head
; // we had a reset to parent, so this is not the correct hash
1481 for (const auto& hash
: m_forRewrite
)
1482 m_rewrittenCommitsMap
[hash
] = head
;
1483 m_forRewrite
.clear();
1484 curRev
->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE
;
1485 this->UpdateCurrentStatus();
1489 InterlockedExchange(&m_bThreadRunning
, TRUE
);
1492 if (!AfxBeginThread(RebaseThreadEntry
, this))
1494 InterlockedExchange(&m_bThreadRunning
, FALSE
);
1495 CMessageBox::Show(GetSafeHwnd(), _T("Create Rebase Thread Fail"), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1500 void CRebaseDlg::ResetParentForSquash(const CString
& commitMessage
)
1502 m_SquashMessage
= commitMessage
;
1503 // reset parent so that we can do "git cherry-pick --no-commit" w/o introducing an unwanted commit
1504 CString cmd
= _T("git.exe reset --soft HEAD~1");
1505 m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_LOG
);
1506 if (RunGitCmdRetryOrAbort(cmd
))
1510 int CRebaseDlg::CheckNextCommitIsSquash()
1513 if(m_CommitList
.m_IsOldFirst
)
1514 index
=m_CurrentRebaseIndex
+1;
1516 index
=m_CurrentRebaseIndex
-1;
1518 GitRevLoglist
* curRev
;
1523 if(index
>= m_CommitList
.GetItemCount())
1526 curRev
= m_CommitList
.m_arShownList
.SafeGetAt(index
);
1528 if (curRev
->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_SQUASH
)
1530 if (curRev
->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_SKIP
)
1532 if(m_CommitList
.m_IsOldFirst
)
1540 } while(curRev
->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_SKIP
);
1545 int CRebaseDlg::GoNext()
1547 if(m_CommitList
.m_IsOldFirst
)
1548 ++m_CurrentRebaseIndex
;
1550 --m_CurrentRebaseIndex
;
1554 int CRebaseDlg::StateAction()
1556 switch(this->m_RebaseStage
)
1559 case CHOOSE_COMMIT_PICK_MODE
:
1562 m_RebaseStage
= REBASE_START
;
1569 void CRebaseDlg::SetContinueButtonText()
1572 switch(this->m_RebaseStage
)
1575 case CHOOSE_COMMIT_PICK_MODE
:
1576 if(this->m_IsFastForward
)
1577 Text
.LoadString(IDS_PROC_STARTREBASEFFBUTTON
);
1579 Text
.LoadString(IDS_PROC_STARTREBASEBUTTON
);
1584 case REBASE_CONTINUE
:
1585 case REBASE_SQUASH_CONFLICT
:
1586 Text
.LoadString(IDS_CONTINUEBUTTON
);
1589 case REBASE_CONFLICT
:
1590 Text
.LoadString(IDS_COMMITBUTTON
);
1593 Text
.LoadString(IDS_AMENDBUTTON
);
1596 case REBASE_SQUASH_EDIT
:
1597 Text
.LoadString(IDS_COMMITBUTTON
);
1602 Text
.LoadString(IDS_FINISHBUTTON
);
1606 Text
.LoadString(IDS_DONE
);
1609 this->GetDlgItem(IDC_REBASE_CONTINUE
)->SetWindowText(Text
);
1612 void CRebaseDlg::SetControlEnable()
1614 switch(this->m_RebaseStage
)
1617 case CHOOSE_COMMIT_PICK_MODE
:
1619 this->GetDlgItem(IDC_SPLITALLOPTIONS
)->EnableWindow(TRUE
);
1620 this->GetDlgItem(IDC_BUTTON_UP2
)->EnableWindow(TRUE
);
1621 this->GetDlgItem(IDC_BUTTON_DOWN2
)->EnableWindow(TRUE
);
1625 this->GetDlgItem(IDC_REBASE_COMBOXEX_BRANCH
)->EnableWindow(TRUE
);
1626 this->GetDlgItem(IDC_REBASE_COMBOXEX_UPSTREAM
)->EnableWindow(TRUE
);
1627 this->GetDlgItem(IDC_BUTTON_REVERSE
)->EnableWindow(TRUE
);
1628 this->GetDlgItem(IDC_REBASE_CHECK_FORCE
)->EnableWindow(TRUE
);
1629 this->GetDlgItem(IDC_REBASE_CHECK_PRESERVEMERGES
)->EnableWindow(TRUE
);
1631 this->m_CommitList
.m_ContextMenuMask
|= m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_REBASE_PICK
)|
1632 m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_REBASE_SQUASH
)|
1633 m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_REBASE_EDIT
)|
1634 m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_REBASE_SKIP
)|
1635 m_CommitList
.GetContextMenuBit(CGitLogListBase::ID_LOG
);
1639 case REBASE_CONTINUE
:
1643 case REBASE_CONFLICT
:
1645 case REBASE_SQUASH_CONFLICT
:
1647 this->GetDlgItem(IDC_SPLITALLOPTIONS
)->EnableWindow(FALSE
);
1648 this->GetDlgItem(IDC_REBASE_COMBOXEX_BRANCH
)->EnableWindow(FALSE
);
1649 this->GetDlgItem(IDC_REBASE_COMBOXEX_UPSTREAM
)->EnableWindow(FALSE
);
1650 this->GetDlgItem(IDC_BUTTON_REVERSE
)->EnableWindow(FALSE
);
1651 this->GetDlgItem(IDC_REBASE_CHECK_FORCE
)->EnableWindow(FALSE
);
1652 this->GetDlgItem(IDC_REBASE_CHECK_PRESERVEMERGES
)->EnableWindow(FALSE
);
1653 this->GetDlgItem(IDC_BUTTON_UP2
)->EnableWindow(FALSE
);
1654 this->GetDlgItem(IDC_BUTTON_DOWN2
)->EnableWindow(FALSE
);
1656 if( m_RebaseStage
== REBASE_DONE
&& (this->m_PostButtonTexts
.GetCount() != 0) )
1658 this->GetDlgItem(IDC_STATUS_STATIC
)->ShowWindow(SW_HIDE
);
1659 this->GetDlgItem(IDC_REBASE_POST_BUTTON
)->ShowWindow(SW_SHOWNORMAL
);
1660 this->m_PostButton
.RemoveAll();
1661 this->m_PostButton
.AddEntries(m_PostButtonTexts
);
1662 //this->GetDlgItem(IDC_REBASE_POST_BUTTON)->SetWindowText(this->m_PostButtonText);
1667 GetDlgItem(IDC_REBASE_SPLIT_COMMIT
)->ShowWindow((m_RebaseStage
== REBASE_EDIT
|| m_RebaseStage
== REBASE_SQUASH_EDIT
) ? SW_SHOW
: SW_HIDE
);
1669 if(m_bThreadRunning
)
1671 this->GetDlgItem(IDC_REBASE_CONTINUE
)->EnableWindow(FALSE
);
1674 else if (m_RebaseStage
!= REBASE_ERROR
)
1676 this->GetDlgItem(IDC_REBASE_CONTINUE
)->EnableWindow(TRUE
);
1680 void CRebaseDlg::UpdateProgress()
1685 if(m_CommitList
.m_IsOldFirst
)
1686 index
= m_CurrentRebaseIndex
+1;
1688 index
= m_CommitList
.GetItemCount()-m_CurrentRebaseIndex
;
1690 int finishedCommits
= index
- 1; // introduced an variable which shows the number handled revisions for the progress bars
1691 if (m_RebaseStage
== REBASE_FINISH
|| finishedCommits
== -1)
1692 finishedCommits
= index
;
1694 m_ProgressBar
.SetRange32(0, m_CommitList
.GetItemCount());
1695 m_ProgressBar
.SetPos(finishedCommits
);
1698 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NORMAL
);
1699 m_pTaskbarList
->SetProgressValue(m_hWnd
, finishedCommits
, m_CommitList
.GetItemCount());
1702 if(m_CurrentRebaseIndex
>=0 && m_CurrentRebaseIndex
< m_CommitList
.GetItemCount())
1705 text
.Format(IDS_PROC_REBASING_PROGRESS
, index
, m_CommitList
.GetItemCount());
1706 m_sStatusText
= text
;
1707 m_CtrlStatusText
.SetWindowText(text
);
1708 m_bStatusWarning
= false;
1709 m_CtrlStatusText
.Invalidate();
1712 GitRevLoglist
* prevRev
= nullptr, *curRev
= nullptr;
1714 if (m_CurrentRebaseIndex
>= 0 && m_CurrentRebaseIndex
< (int)m_CommitList
.m_arShownList
.size())
1715 curRev
= m_CommitList
.m_arShownList
.SafeGetAt(m_CurrentRebaseIndex
);
1717 for (int i
= 0; i
< (int)m_CommitList
.m_arShownList
.size(); ++i
)
1719 prevRev
= m_CommitList
.m_arShownList
.SafeGetAt(i
);
1720 if (prevRev
->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_CURRENT
)
1722 prevRev
->GetRebaseAction() &= ~CGitLogListBase::LOGACTIONS_REBASE_CURRENT
;
1723 m_CommitList
.GetItemRect(i
,&rect
,LVIR_BOUNDS
);
1724 m_CommitList
.InvalidateRect(rect
);
1730 curRev
->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_CURRENT
;
1731 m_CommitList
.GetItemRect(m_CurrentRebaseIndex
,&rect
,LVIR_BOUNDS
);
1732 m_CommitList
.InvalidateRect(rect
);
1734 m_CommitList
.EnsureVisible(m_CurrentRebaseIndex
,FALSE
);
1737 void CRebaseDlg::UpdateCurrentStatus()
1739 SetContinueButtonText();
1742 if (m_RebaseStage
== REBASE_DONE
)
1743 GetDlgItem(IDC_REBASE_CONTINUE
)->SetFocus();
1746 void CRebaseDlg::AddLogString(CString str
)
1748 this->m_wndOutputRebase
.SendMessage(SCI_SETREADONLY
, FALSE
);
1749 CStringA sTextA
= m_wndOutputRebase
.StringForControl(str
);//CUnicodeUtils::GetUTF8(str);
1750 this->m_wndOutputRebase
.SendMessage(SCI_DOCUMENTEND
);
1751 this->m_wndOutputRebase
.SendMessage(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)sTextA
);
1752 this->m_wndOutputRebase
.SendMessage(SCI_REPLACESEL
, 0, (LPARAM
)(LPCSTR
)"\n");
1753 this->m_wndOutputRebase
.SendMessage(SCI_SETREADONLY
, TRUE
);
1756 int CRebaseDlg::GetCurrentCommitID()
1758 if(m_CommitList
.m_IsOldFirst
)
1759 return this->m_CurrentRebaseIndex
+1;
1761 return m_CommitList
.GetItemCount()-m_CurrentRebaseIndex
;
1764 int CRebaseDlg::IsCommitEmpty(const CGitHash
& hash
)
1766 CString cmd
, tree
, ptree
;
1767 cmd
.Format(_T("git.exe rev-parse -q --verify %s^{tree}"), (LPCTSTR
)hash
.ToString());
1768 if (g_Git
.Run(cmd
, &tree
, CP_UTF8
))
1774 cmd
.Format(_T("git.exe rev-parse -q --verify %s^^{tree}"), (LPCTSTR
)hash
.ToString());
1775 if (g_Git
.Run(cmd
, &ptree
, CP_UTF8
))
1776 ptree
= _T("4b825dc642cb6eb9a060e54bf8d69288fbee4904"); // empty tree
1777 return tree
== ptree
;
1780 int CRebaseDlg::DoRebase()
1783 if(m_CurrentRebaseIndex
<0)
1785 if(m_CurrentRebaseIndex
>= m_CommitList
.GetItemCount() )
1788 GitRevLoglist
* pRev
= m_CommitList
.m_arShownList
.SafeGetAt(m_CurrentRebaseIndex
);
1789 int mode
= pRev
->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_MODE_MASK
;
1792 if (mode
== CGitLogListBase::LOGACTIONS_REBASE_SKIP
)
1794 pRev
->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE
;
1798 bool nextCommitIsSquash
= (CheckNextCommitIsSquash() == 0);
1799 if (nextCommitIsSquash
|| mode
!= CGitLogListBase::LOGACTIONS_REBASE_PICK
)
1800 { // next commit is squash or not pick
1801 if (!this->m_SquashMessage
.IsEmpty())
1802 this->m_SquashMessage
+= _T("\n\n");
1803 this->m_SquashMessage
+= pRev
->GetSubject();
1804 this->m_SquashMessage
+= _T("\n");
1805 this->m_SquashMessage
+= pRev
->GetBody().TrimRight();
1806 if (m_bAddCherryPickedFrom
)
1808 if (!pRev
->GetBody().IsEmpty())
1809 m_SquashMessage
+= _T("\n");
1810 m_SquashMessage
+= _T("(cherry picked from commit ");
1811 m_SquashMessage
+= pRev
->m_CommitHash
.ToString();
1812 m_SquashMessage
+= _T(")");
1817 this->m_SquashMessage
.Empty();
1818 m_SquashFirstMetaData
.Empty();
1821 if ((nextCommitIsSquash
&& mode
!= CGitLogListBase::LOGACTIONS_REBASE_EDIT
) || mode
== CGitLogListBase::LOGACTIONS_REBASE_SQUASH
)
1822 { // next or this commit is squash (don't do this on edit->squash sequence)
1823 nocommit
=_T(" --no-commit ");
1826 if (nextCommitIsSquash
&& mode
!= CGitLogListBase::LOGACTIONS_REBASE_SQUASH
)
1827 m_SquashFirstMetaData
= SquashFirstMetaData(pRev
);
1830 log
.Format(_T("%s %d: %s"), (LPCTSTR
)CGitLogListBase::GetRebaseActionName(mode
), GetCurrentCommitID(), (LPCTSTR
)pRev
->m_CommitHash
.ToString());
1832 AddLogString(pRev
->GetSubject());
1833 if (pRev
->GetSubject().IsEmpty())
1835 CMessageBox::Show(m_hWnd
, IDS_PROC_REBASE_EMPTYCOMMITMSG
, IDS_APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
1836 mode
= CGitLogListBase::LOGACTIONS_REBASE_EDIT
;
1839 CString cherryPickedFrom
;
1840 if (m_bAddCherryPickedFrom
)
1841 cherryPickedFrom
= _T("-x ");
1842 else if (!m_IsCherryPick
&& nocommit
.IsEmpty())
1843 cherryPickedFrom
= _T("--ff "); // for issue #1833: "If the current HEAD is the same as the parent of the cherry-picked commit, then a fast forward to this commit will be performed."
1845 int isEmpty
= IsCommitEmpty(pRev
->m_CommitHash
);
1847 cherryPickedFrom
+= _T("--allow-empty ");
1848 else if (isEmpty
< 0)
1851 if (m_IsCherryPick
&& pRev
->m_ParentHash
.size() > 1)
1854 msg
.Format(IDS_CHERRYPICK_MERGECOMMIT
, (LPCTSTR
)pRev
->m_CommitHash
.ToString(), (LPCTSTR
)pRev
->GetSubject());
1856 parent1
.Format(IDS_PARENT
, 1);
1858 parent2
.Format(IDS_PARENT
, 2);
1860 cancel
.LoadString(IDS_MSGBOX_CANCEL
);
1861 auto ret
= CMessageBox::Show(m_hWnd
, msg
, _T("TortoiseGit"), 3, IDI_QUESTION
, parent1
, parent2
, cancel
);
1865 cherryPickedFrom
.AppendFormat(L
"-m %d ", ret
);
1870 cmd
.Format(_T("git.exe cherry-pick %s%s %s"), (LPCTSTR
)cherryPickedFrom
, (LPCTSTR
)nocommit
, (LPCTSTR
)pRev
->m_CommitHash
.ToString());
1871 if (m_bPreserveMerges
)
1873 bool parentRewritten
= false;
1874 CGitHash currentHeadHash
;
1875 if (g_Git
.GetHash(currentHeadHash
, _T("HEAD")))
1877 m_RebaseStage
= REBASE_ERROR
;
1878 MessageBox(g_Git
.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR
);
1881 if (!m_currentCommits
.empty())
1883 for (const auto& commit
: m_currentCommits
)
1884 m_rewrittenCommitsMap
[commit
] = currentHeadHash
;
1885 m_currentCommits
.clear();
1887 m_currentCommits
.push_back(pRev
->m_CommitHash
);
1888 GIT_REV_LIST possibleParents
= pRev
->m_ParentHash
;
1889 GIT_REV_LIST newParents
;
1890 for (auto it
= possibleParents
.cbegin(); it
!= possibleParents
.cend(); it
= possibleParents
.begin())
1892 CGitHash parent
= *it
;
1893 possibleParents
.erase(it
);
1895 const auto rewrittenParent
= m_rewrittenCommitsMap
.find(parent
);
1896 if (rewrittenParent
== m_rewrittenCommitsMap
.cend())
1898 auto droppedCommitParents
= m_droppedCommitsMap
.find(parent
);
1899 if (droppedCommitParents
!= m_droppedCommitsMap
.cend())
1901 parentRewritten
= true;
1902 for (auto droppedIt
= droppedCommitParents
->second
.crbegin(); droppedIt
!= droppedCommitParents
->second
.crend(); ++droppedIt
)
1903 possibleParents
.insert(possibleParents
.begin(), *droppedIt
);
1907 newParents
.push_back(parent
);
1911 if (rewrittenParent
->second
.IsEmpty() && parent
== pRev
->m_ParentHash
[0] && pRev
->ParentsCount() > 1)
1913 m_RebaseStage
= REBASE_ERROR
;
1914 AddLogString(_T(""));
1915 AddLogString(_T("Unrecoverable error: Merge commit parent missing."));
1919 CGitHash newParent
= rewrittenParent
->second
;
1920 if (newParent
.IsEmpty()) // use current HEAD as fallback
1921 newParent
= currentHeadHash
;
1923 if (newParent
!= parent
)
1924 parentRewritten
= true;
1926 if (std::find(newParents
.begin(), newParents
.end(), newParent
) == newParents
.end())
1927 newParents
.push_back(newParent
);
1929 if (pRev
->ParentsCount() > 1)
1931 if (mode
== CGitLogListBase::LOGACTIONS_REBASE_SQUASH
)
1933 m_RebaseStage
= REBASE_ERROR
;
1934 AddLogString(_T("Cannot squash merge commit on rebase."));
1937 if (!parentRewritten
&& nocommit
.IsEmpty())
1938 cmd
.Format(_T("git.exe reset --hard %s"), (LPCTSTR
)pRev
->m_CommitHash
.ToString());
1941 CString parentString
;
1942 for (const auto& parent
: newParents
)
1943 parentString
+= L
" " + parent
.ToString();
1944 cmd
.Format(_T("git.exe checkout %s"), (LPCTSTR
)newParents
[0].ToString());
1945 if (RunGitCmdRetryOrAbort(cmd
))
1947 m_RebaseStage
= REBASE_ERROR
;
1950 cmd
.Format(_T("git.exe merge --no-ff%s %s"), (LPCTSTR
)nocommit
, (LPCTSTR
)parentString
);
1951 if (nocommit
.IsEmpty())
1953 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
1957 int hasConflicts
= g_Git
.HasWorkingTreeConflicts();
1958 if (hasConflicts
> 0)
1960 m_RebaseStage
= REBASE_CONFLICT
;
1963 else if (hasConflicts
< 0)
1964 AddLogString(g_Git
.GetGitLastErr(L
"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS
));
1965 AddLogString(_T("An unrecoverable error occurred."));
1966 m_RebaseStage
= REBASE_ERROR
;
1969 CGitHash newHeadHash
;
1970 if (g_Git
.GetHash(newHeadHash
, _T("HEAD")))
1972 m_RebaseStage
= REBASE_ERROR
;
1973 MessageBox(g_Git
.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR
);
1976 // do nothing if already up2date
1977 if (currentHeadHash
!= newHeadHash
)
1978 cmd
.Format(_T("git.exe commit --amend -C %s"), (LPCTSTR
)pRev
->m_CommitHash
.ToString());
1984 if (mode
!= CGitLogListBase::LOGACTIONS_REBASE_SQUASH
)
1986 cmd
.Format(_T("git.exe checkout %s"), (LPCTSTR
)newParents
[0].ToString());
1987 if (RunGitCmdRetryOrAbort(cmd
))
1989 m_RebaseStage
= REBASE_ERROR
;
1993 cmd
.Format(_T("git.exe cherry-pick %s%s %s"), (LPCTSTR
)cherryPickedFrom
, (LPCTSTR
)nocommit
, (LPCTSTR
)pRev
->m_CommitHash
.ToString());
1997 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
2000 int hasConflicts
= g_Git
.HasWorkingTreeConflicts();
2001 if (hasConflicts
< 0)
2003 AddLogString(g_Git
.GetGitLastErr(L
"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS
));
2008 if (mode
== CGitLogListBase::LOGACTIONS_REBASE_PICK
)
2011 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_ERROR
);
2013 if (!m_bAutoSkipFailedCommit
)
2015 choose
= CMessageBox::ShowCheck(GetSafeHwnd(), IDS_CHERRYPICKFAILEDSKIP
, IDS_APPNAME
, 1, IDI_QUESTION
, IDS_SKIPBUTTON
, IDS_MSGBOX_RETRY
, IDS_MSGBOX_CANCEL
, nullptr, IDS_DO_SAME_FOR_REST
, &m_bAutoSkipFailedCommit
);
2018 m_bAutoSkipFailedCommit
= FALSE
;
2019 continue; // retry cherry pick
2022 if (m_bAutoSkipFailedCommit
|| choose
== 1)
2024 if (!RunGitCmdRetryOrAbort(_T("git.exe reset --hard")))
2026 pRev
->GetRebaseAction() = CGitLogListBase::LOGACTIONS_REBASE_SKIP
;
2027 m_CommitList
.Invalidate();
2032 m_RebaseStage
= REBASE_ERROR
;
2033 AddLogString(_T("An unrecoverable error occurred."));
2036 if (mode
== CGitLogListBase::LOGACTIONS_REBASE_EDIT
)
2038 this->m_RebaseStage
= REBASE_EDIT
;
2039 return -1; // Edit return -1 to stop rebase.
2042 if(CheckNextCommitIsSquash())
2044 // let user edit last commmit message
2045 this->m_RebaseStage
= REBASE_SQUASH_EDIT
;
2051 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_ERROR
);
2052 if (mode
== CGitLogListBase::LOGACTIONS_REBASE_SQUASH
)
2053 m_RebaseStage
= REBASE_SQUASH_CONFLICT
;
2055 m_RebaseStage
= REBASE_CONFLICT
;
2062 if (mode
== CGitLogListBase::LOGACTIONS_REBASE_PICK
)
2064 if (nocommit
.IsEmpty())
2067 if (g_Git
.GetHash(head
, _T("HEAD")))
2069 MessageBox(g_Git
.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR
);
2070 m_RebaseStage
= REBASE_ERROR
;
2073 m_rewrittenCommitsMap
[pRev
->m_CommitHash
] = head
;
2076 m_forRewrite
.push_back(pRev
->m_CommitHash
);
2077 pRev
->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE
;
2080 if (mode
== CGitLogListBase::LOGACTIONS_REBASE_EDIT
)
2082 this->m_RebaseStage
= REBASE_EDIT
;
2083 return -1; // Edit return -1 to stop rebase.
2087 if(CheckNextCommitIsSquash())
2089 // let user edit last commmit message
2090 this->m_RebaseStage
= REBASE_SQUASH_EDIT
;
2093 else if (mode
== CGitLogListBase::LOGACTIONS_REBASE_SQUASH
)
2095 pRev
->GetRebaseAction() |= CGitLogListBase::LOGACTIONS_REBASE_DONE
;
2096 m_forRewrite
.push_back(pRev
->m_CommitHash
);
2104 BOOL
CRebaseDlg::IsEnd()
2106 if(m_CommitList
.m_IsOldFirst
)
2107 return m_CurrentRebaseIndex
>= this->m_CommitList
.GetItemCount();
2109 return m_CurrentRebaseIndex
<0;
2112 int CRebaseDlg::RebaseThread()
2114 CBlockCacheForPath
cacheBlock(g_Git
.m_CurrentDir
);
2119 if( m_RebaseStage
== REBASE_START
)
2121 if( this->StartRebase() )
2126 m_RebaseStage
= REBASE_CONTINUE
;
2128 else if( m_RebaseStage
== REBASE_CONTINUE
)
2131 SendMessage(MSG_REBASE_UPDATE_UI
);
2135 m_RebaseStage
= REBASE_FINISH
;
2144 else if( m_RebaseStage
== REBASE_FINISH
)
2146 SendMessage(MSG_REBASE_UPDATE_UI
);
2147 m_RebaseStage
= REBASE_DONE
;
2152 this->PostMessage(MSG_REBASE_UPDATE_UI
);
2155 InterlockedExchange(&m_bThreadRunning
, FALSE
);
2156 this->PostMessage(MSG_REBASE_UPDATE_UI
);
2160 void CRebaseDlg::ListConflictFile()
2162 this->m_FileListCtrl
.Clear();
2163 m_FileListCtrl
.SetHasCheckboxes(true);
2168 if (!m_IsCherryPick
)
2171 if (GitAdminDir::GetAdminDirPath(g_Git
.m_CurrentDir
, adminDir
))
2172 CreateDirectory(adminDir
+ L
"tgitrebase.active", nullptr);
2175 this->m_FileListCtrl
.GetStatus(&list
,true);
2176 this->m_FileListCtrl
.Show(CTGitPath::LOGACTIONS_UNMERGED
|CTGitPath::LOGACTIONS_MODIFIED
|CTGitPath::LOGACTIONS_ADDED
|CTGitPath::LOGACTIONS_DELETED
,
2177 CTGitPath::LOGACTIONS_UNMERGED
);
2179 m_FileListCtrl
.Check(GITSLC_SHOWFILES
);
2180 bool hasSubmoduleChange
= false;
2181 for (int i
= 0; i
< m_FileListCtrl
.GetItemCount(); i
++)
2183 CTGitPath
*entry
= (CTGitPath
*)m_FileListCtrl
.GetItemData(i
);
2184 if (entry
->IsDirectory())
2186 hasSubmoduleChange
= true;
2191 if (hasSubmoduleChange
)
2193 m_CtrlStatusText
.SetWindowText(m_sStatusText
+ _T(", ") + CString(MAKEINTRESOURCE(IDS_CARE_SUBMODULE_CHANGES
)));
2194 m_bStatusWarning
= true;
2195 m_CtrlStatusText
.Invalidate();
2199 m_CtrlStatusText
.SetWindowText(m_sStatusText
);
2200 m_bStatusWarning
= false;
2201 m_CtrlStatusText
.Invalidate();
2205 LRESULT
CRebaseDlg::OnRebaseUpdateUI(WPARAM
,LPARAM
)
2207 if (m_RebaseStage
== REBASE_FINISH
)
2212 UpdateCurrentStatus();
2214 if (m_RebaseStage
== REBASE_DONE
&& m_bRebaseAutoEnd
)
2216 m_bRebaseAutoEnd
= false;
2217 this->PostMessage(WM_COMMAND
, MAKELONG(IDC_REBASE_CONTINUE
, BN_CLICKED
), (LPARAM
)GetDlgItem(IDC_REBASE_CONTINUE
)->GetSafeHwnd());
2220 if (m_RebaseStage
== REBASE_DONE
&& m_pTaskbarList
)
2221 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NOPROGRESS
); // do not show progress on taskbar any more to show we finished
2222 if(m_CurrentRebaseIndex
<0)
2224 if(m_CurrentRebaseIndex
>= m_CommitList
.GetItemCount() )
2226 GitRev
* curRev
= m_CommitList
.m_arShownList
.SafeGetAt(m_CurrentRebaseIndex
);
2228 switch(m_RebaseStage
)
2230 case REBASE_CONFLICT
:
2231 case REBASE_SQUASH_CONFLICT
:
2234 this->m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_CONFLICT
);
2236 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_ERROR
);
2237 this->m_LogMessageCtrl
.Call(SCI_SETREADONLY
, FALSE
);
2242 GitAdminDir::GetAdminDirPath(g_Git
.m_CurrentDir
, dotGitPath
);
2243 // vanilla git also re-uses MERGE_MSG on conflict (listing all conflicted files)
2244 // and it's also needed for cherry-pick in order to get cherry-picked-from included on conflicts
2245 CGit::LoadTextFile(dotGitPath
+ _T("MERGE_MSG"), logMessage
);
2247 if (logMessage
.IsEmpty())
2248 logMessage
= curRev
->GetSubject() + _T("\n") + curRev
->GetBody();
2249 this->m_LogMessageCtrl
.SetText(logMessage
);
2253 this->m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_MESSAGE
);
2255 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_PAUSED
);
2256 this->m_LogMessageCtrl
.Call(SCI_SETREADONLY
, FALSE
);
2257 if (m_bAddCherryPickedFrom
)
2259 // Since the new commit is done and the HEAD points to it,
2260 // just using the new body modified by git self.
2261 GitRev headRevision
;
2262 if (headRevision
.GetCommit(_T("HEAD")))
2263 MessageBox(headRevision
.GetLastErr(), _T("TortoiseGit"), MB_ICONERROR
);
2265 m_LogMessageCtrl
.SetText(headRevision
.GetSubject() + _T("\n") + headRevision
.GetBody());
2268 m_LogMessageCtrl
.SetText(curRev
->GetSubject() + _T("\n") + curRev
->GetBody());
2270 case REBASE_SQUASH_EDIT
:
2271 this->m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_MESSAGE
);
2272 this->m_LogMessageCtrl
.Call(SCI_SETREADONLY
, FALSE
);
2273 this->m_LogMessageCtrl
.SetText(this->m_SquashMessage
);
2275 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_PAUSED
);
2278 this->m_ctrlTabCtrl
.SetActiveTab(REBASE_TAB_LOG
);
2283 void CRebaseDlg::OnCancel()
2288 void CRebaseDlg::OnBnClickedAbort()
2291 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NOPROGRESS
);
2295 if(m_OrigUpstreamHash
.IsEmpty())
2297 __super::OnCancel();
2300 if(m_RebaseStage
== CHOOSE_BRANCH
|| m_RebaseStage
== CHOOSE_COMMIT_PICK_MODE
)
2305 if (CMessageBox::Show(GetSafeHwnd(), IDS_PROC_REBASE_ABORT
, IDS_APPNAME
, MB_YESNO
| MB_ICONQUESTION
) != IDYES
)
2308 if(this->m_IsFastForward
)
2311 cmd
.Format(_T("git.exe reset --hard %s --"), (LPCTSTR
)this->m_OrigBranchHash
.ToString());
2312 RunGitCmdRetryOrAbort(cmd
);
2313 __super::OnCancel();
2317 if (m_IsCherryPick
) // there are not "branch" at cherry pick mode
2320 cmd
.Format(_T("git.exe reset --hard %s --"), (LPCTSTR
)m_OrigUpstreamHash
.ToString());
2321 RunGitCmdRetryOrAbort(cmd
);
2322 __super::OnCancel();
2326 if (m_OrigHEADBranch
== m_BranchCtrl
.GetString())
2329 if (IsLocalBranch(m_OrigHEADBranch
))
2330 cmd
.Format(_T("git.exe checkout -f -B %s %s --"), (LPCTSTR
)m_BranchCtrl
.GetString(), (LPCTSTR
)m_OrigBranchHash
.ToString());
2332 cmd
.Format(_T("git.exe checkout -f %s --"), (LPCTSTR
)m_OrigBranchHash
.ToString());
2333 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
2336 ::MessageBox(m_hWnd
, _T("Unrecoverable error on cleanup:\n") + out
, _T("TortoiseGit"), MB_ICONERROR
);
2337 __super::OnCancel();
2341 cmd
.Format(_T("git.exe reset --hard %s --"), (LPCTSTR
)m_OrigBranchHash
.ToString());
2342 RunGitCmdRetryOrAbort(cmd
);
2347 if (m_OrigHEADBranch
!= g_Git
.GetCurrentBranch(true))
2349 if (IsLocalBranch(m_OrigHEADBranch
))
2350 cmd
.Format(_T("git.exe checkout -f -B %s %s --"), (LPCTSTR
)m_OrigHEADBranch
, (LPCTSTR
)m_OrigHEADHash
.ToString());
2352 cmd
.Format(_T("git.exe checkout -f %s --"), (LPCTSTR
)m_OrigHEADHash
.ToString());
2353 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
2356 ::MessageBox(m_hWnd
, _T("Unrecoverable error on cleanup:\n") + out
, _T("TortoiseGit"), MB_ICONERROR
);
2357 // continue to restore moved branch
2361 cmd
.Format(_T("git.exe reset --hard %s --"), (LPCTSTR
)m_OrigHEADHash
.ToString());
2362 RunGitCmdRetryOrAbort(cmd
);
2364 // restore moved branch
2365 if (IsLocalBranch(m_BranchCtrl
.GetString()))
2367 cmd
.Format(_T("git.exe branch -f %s %s --"), (LPCTSTR
)m_BranchCtrl
.GetString(), (LPCTSTR
)m_OrigBranchHash
.ToString());
2368 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
2371 ::MessageBox(m_hWnd
, _T("Unrecoverable error on cleanup:\n") + out
, _T("TortoiseGit"), MB_ICONERROR
);
2372 __super::OnCancel();
2377 __super::OnCancel();
2379 CleanUpRebaseActiveFolder();
2380 CheckRestoreStash();
2383 void CRebaseDlg::OnBnClickedButtonReverse()
2385 CString temp
= m_BranchCtrl
.GetString();
2386 m_BranchCtrl
.AddString(m_UpstreamCtrl
.GetString());
2387 m_UpstreamCtrl
.AddString(temp
);
2388 OnCbnSelchangeUpstream();
2391 void CRebaseDlg::OnBnClickedButtonBrowse()
2393 if(CBrowseRefsDlg::PickRefForCombo(&m_UpstreamCtrl
))
2394 OnCbnSelchangeUpstream();
2397 void CRebaseDlg::OnBnClickedRebaseCheckForce()
2400 this->FetchLogList();
2403 void CRebaseDlg::OnBnClickedRebasePostButton()
2405 this->m_Upstream
=this->m_UpstreamCtrl
.GetString();
2406 this->m_Branch
=this->m_BranchCtrl
.GetString();
2408 this->EndDialog((int)(IDC_REBASE_POST_BUTTON
+this->m_PostButton
.GetCurrentEntry()));
2411 LRESULT
CRebaseDlg::OnGitStatusListCtrlNeedsRefresh(WPARAM
, LPARAM
)
2417 void CRebaseDlg::Refresh()
2419 if (m_RebaseStage
== REBASE_CONFLICT
|| m_RebaseStage
== REBASE_SQUASH_CONFLICT
)
2425 if(this->m_IsCherryPick
)
2428 if(this->m_RebaseStage
== CHOOSE_BRANCH
)
2431 this->FetchLogList();
2435 void CRebaseDlg::OnBnClickedButtonUp2()
2438 pos
= m_CommitList
.GetFirstSelectedItemPosition();
2440 bool moveToTop
= !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000);
2441 // do nothing if the first selected item is the first item in the list
2442 if (!moveToTop
&& m_CommitList
.GetNextSelectedItem(pos
) == 0)
2445 pos
= m_CommitList
.GetFirstSelectedItemPosition();
2448 bool changed
= false;
2451 int index
=m_CommitList
.GetNextSelectedItem(pos
);
2452 count
= moveToTop
? count
: (index
- 1);
2453 while (index
> count
)
2455 CGitHash old
= m_CommitList
.m_logEntries
[index
- 1];
2456 m_CommitList
.m_logEntries
[index
- 1] = m_CommitList
.m_logEntries
[index
];
2457 m_CommitList
.m_logEntries
[index
] = old
;
2458 m_CommitList
.RecalculateShownList(&m_CommitList
.m_arShownList
);
2459 m_CommitList
.SetItemState(index
- 1, LVIS_SELECTED
, LVIS_SELECTED
);
2460 m_CommitList
.SetItemState(index
, 0, LVIS_SELECTED
);
2468 pos
= m_CommitList
.GetFirstSelectedItemPosition();
2469 m_CommitList
.EnsureVisible(m_CommitList
.GetNextSelectedItem(pos
), false);
2470 m_CommitList
.Invalidate();
2471 m_CommitList
.SetFocus();
2475 void CRebaseDlg::OnBnClickedButtonDown2()
2477 if (m_CommitList
.GetSelectedCount() == 0)
2480 bool moveToBottom
= !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000);
2482 pos
= m_CommitList
.GetFirstSelectedItemPosition();
2483 bool changed
= false;
2484 // use an array to store all selected item indexes; the user won't select too much items
2485 auto indexes
= std::make_unique
<int[]>(m_CommitList
.GetSelectedCount());
2488 indexes
.get()[i
++] = m_CommitList
.GetNextSelectedItem(pos
);
2489 // don't move any item if the last selected item is the last item in the m_CommitList
2490 // (that would change the order of the selected items)
2491 if (!moveToBottom
&& indexes
.get()[m_CommitList
.GetSelectedCount() - 1] >= m_CommitList
.GetItemCount() - 1)
2493 int count
= m_CommitList
.GetItemCount() - 1;
2494 // iterate over the indexes backwards in order to correctly move multiselected items
2495 for (i
= m_CommitList
.GetSelectedCount() - 1; i
>= 0; i
--)
2497 int index
= indexes
.get()[i
];
2498 count
= moveToBottom
? count
: (index
+ 1);
2499 while (index
< count
)
2501 CGitHash old
= m_CommitList
.m_logEntries
[index
+ 1];
2502 m_CommitList
.m_logEntries
[index
+ 1] = m_CommitList
.m_logEntries
[index
];
2503 m_CommitList
.m_logEntries
[index
] = old
;
2504 m_CommitList
.RecalculateShownList(&m_CommitList
.m_arShownList
);
2505 m_CommitList
.SetItemState(index
, 0, LVIS_SELECTED
);
2506 m_CommitList
.SetItemState(index
+ 1, LVIS_SELECTED
, LVIS_SELECTED
);
2512 m_CommitList
.EnsureVisible(indexes
.get()[m_CommitList
.GetSelectedCount() - 1] + 1, false);
2515 m_CommitList
.Invalidate();
2516 m_CommitList
.SetFocus();
2520 LRESULT
CRebaseDlg::OnTaskbarBtnCreated(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
2522 m_pTaskbarList
.Release();
2523 m_pTaskbarList
.CoCreateInstance(CLSID_TaskbarList
);
2524 SetUUIDOverlayIcon(m_hWnd
);
2528 void CRebaseDlg::OnLvnItemchangedLoglist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
2530 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
2532 if(m_CommitList
.m_bNoDispUpdates
)
2534 if (pNMLV
->iItem
>= 0)
2536 this->m_CommitList
.m_nSearchIndex
= pNMLV
->iItem
;
2537 if (pNMLV
->iSubItem
!= 0)
2539 if (pNMLV
->iItem
== (int)m_CommitList
.m_arShownList
.size())
2541 // remove the selected state
2542 if (pNMLV
->uChanged
& LVIF_STATE
)
2544 m_CommitList
.SetItemState(pNMLV
->iItem
, 0, LVIS_SELECTED
);
2545 FillLogMessageCtrl();
2549 if (pNMLV
->uChanged
& LVIF_STATE
)
2550 FillLogMessageCtrl();
2553 FillLogMessageCtrl();
2556 void CRebaseDlg::FillLogMessageCtrl()
2558 int selCount
= m_CommitList
.GetSelectedCount();
2559 if (selCount
== 1 && (m_RebaseStage
== CHOOSE_BRANCH
|| m_RebaseStage
== CHOOSE_COMMIT_PICK_MODE
))
2561 POSITION pos
= m_CommitList
.GetFirstSelectedItemPosition();
2562 int selIndex
= m_CommitList
.GetNextSelectedItem(pos
);
2563 GitRevLoglist
* pLogEntry
= m_CommitList
.m_arShownList
.SafeGetAt(selIndex
);
2564 m_FileListCtrl
.UpdateWithGitPathList(pLogEntry
->GetFiles(&m_CommitList
));
2565 m_FileListCtrl
.m_CurrentVersion
= pLogEntry
->m_CommitHash
;
2566 m_FileListCtrl
.Show(GITSLC_SHOWVERSIONED
);
2567 m_LogMessageCtrl
.Call(SCI_SETREADONLY
, FALSE
);
2568 m_LogMessageCtrl
.SetText(pLogEntry
->GetSubject() + _T("\n") + pLogEntry
->GetBody());
2569 m_LogMessageCtrl
.Call(SCI_SETREADONLY
, TRUE
);
2572 void CRebaseDlg::OnBnClickedCheckCherryPickedFrom()
2577 LRESULT
CRebaseDlg::OnRebaseActionMessage(WPARAM
, LPARAM
)
2579 if (m_RebaseStage
== REBASE_ERROR
|| m_RebaseStage
== REBASE_CONFLICT
)
2581 GitRevLoglist
* pRev
= m_CommitList
.m_arShownList
.SafeGetAt(m_CurrentRebaseIndex
);
2582 int mode
= pRev
->GetRebaseAction() & CGitLogListBase::LOGACTIONS_REBASE_MODE_MASK
;
2583 if (mode
== CGitLogListBase::LOGACTIONS_REBASE_SKIP
)
2585 if (!RunGitCmdRetryOrAbort(_T("git.exe reset --hard")))
2587 m_FileListCtrl
.Clear();
2588 m_RebaseStage
= REBASE_CONTINUE
;
2589 UpdateCurrentStatus();
2597 void CRebaseDlg::OnBnClickedSplitAllOptions()
2599 switch (m_SplitAllOptions
.GetCurrentEntry())
2602 SetAllRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_PICK
);
2605 SetAllRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_SQUASH
);
2608 SetAllRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_EDIT
);
2611 m_CommitList
.SetUnselectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_SKIP
);
2614 m_CommitList
.SetUnselectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_SQUASH
);
2617 m_CommitList
.SetUnselectedRebaseAction(CGitLogListBase::LOGACTIONS_REBASE_EDIT
);
2624 void CRebaseDlg::OnBnClickedRebaseSplitCommit()
2629 static bool GetCompareHash(const CString
& ref
, const CGitHash
& hash
)
2632 if (g_Git
.GetHash(refHash
, ref
))
2633 MessageBox(nullptr, g_Git
.GetGitLastErr(_T("Could not get hash of \"") + ref
+ _T("\".")), _T("TortoiseGit"), MB_ICONERROR
);
2634 return refHash
.IsEmpty() || (hash
== refHash
);
2637 void CRebaseDlg::OnBnClickedButtonOnto()
2639 m_Onto
= CBrowseRefsDlg::PickRef(false, m_Onto
);
2640 if (!m_Onto
.IsEmpty())
2642 // make sure that the user did not select upstream, selected branch or HEAD
2644 if (g_Git
.GetHash(hash
, m_Onto
))
2646 MessageBox(g_Git
.GetGitLastErr(_T("Could not get hash of \"") + m_BranchCtrl
.GetString() + _T("\".")), _T("TortoiseGit"), MB_ICONERROR
);
2648 ((CButton
*)GetDlgItem(IDC_BUTTON_ONTO
))->SetCheck(m_Onto
.IsEmpty() ? BST_UNCHECKED
: BST_CHECKED
);
2651 if (GetCompareHash(_T("HEAD"), hash
) || GetCompareHash(m_UpstreamCtrl
.GetString(), hash
) || GetCompareHash(m_BranchCtrl
.GetString(), hash
))
2654 if (m_Onto
.IsEmpty())
2655 m_tooltips
.DelTool(IDC_BUTTON_ONTO
);
2657 m_tooltips
.AddTool(IDC_BUTTON_ONTO
, m_Onto
);
2658 ((CButton
*)GetDlgItem(IDC_BUTTON_ONTO
))->SetCheck(m_Onto
.IsEmpty() ? BST_UNCHECKED
: BST_CHECKED
);
2662 void CRebaseDlg::OnHelp()
2664 HtmlHelp(0x20000 + (m_IsCherryPick
? IDD_REBASECHERRYPICK
: IDD_REBASE
));
2667 int CRebaseDlg::RunGitCmdRetryOrAbort(const CString
& cmd
)
2672 if (g_Git
.Run(cmd
, &out
, CP_UTF8
))
2675 AddLogString(CString(MAKEINTRESOURCE(IDS_FAIL
)));
2678 msg
.Format(L
"\"%s\" failed.\n%s", (LPCTSTR
)cmd
, (LPCTSTR
)out
);
2679 if (CMessageBox::Show(GetSafeHwnd(), msg
, _T("TortoiseGit"), 1, IDI_ERROR
, CString(MAKEINTRESOURCE(IDS_MSGBOX_RETRY
)), CString(MAKEINTRESOURCE(IDS_MSGBOX_ABORT
))) != 1)