1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - TortoiseGit
4 // Copyright (C) 2005-2007 Marco Costalba
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 // GitLogList.cpp : implementation file
23 #include "TortoiseProc.h"
24 #include "GitLogList.h"
28 #include "GitProgressDlg.h"
29 #include "ProgressDlg.h"
30 #include "SysProgressDlg.h"
32 #include "MessageBox.h"
35 #include "StringUtils.h"
36 #include "UnicodeUtils.h"
38 #include "CommitDlg.h"
39 #include "RebaseDlg.h"
41 #include "../TGitCache/CacheInterface.h"
43 IMPLEMENT_DYNAMIC(CGitLogList
, CHintCtrl
<CListCtrl
>)
45 static void GetFirstEntryStartingWith(STRING_VECTOR
& heystack
, const CString
& needle
, CString
& result
)
47 auto it
= std::find_if(heystack
.cbegin(), heystack
.cend(), [&needle
](const CString
& entry
) { return wcsncmp(entry
, needle
, needle
.GetLength()) == 0; });
48 if (it
== heystack
.cend())
53 int CGitLogList::RevertSelectedCommits(int parent
)
55 CSysProgressDlg progress
;
59 if(!g_Git
.CheckCleanWorkTree())
60 CMessageBox::Show(nullptr, IDS_PROC_NOCLEAN
, IDS_APPNAME
, MB_OK
);
63 if (this->GetSelectedCount() > 1)
65 progress
.SetTitle(CString(MAKEINTRESOURCE(IDS_PROGS_TITLE_REVERTCOMMIT
)));
66 progress
.SetAnimation(IDR_MOVEANI
);
67 progress
.SetTime(true);
68 progress
.ShowModeless(this);
71 CBlockCacheForPath
cacheBlock(g_Git
.m_CurrentDir
);
73 POSITION pos
= GetFirstSelectedItemPosition();
77 int index
= GetNextSelectedItem(pos
);
78 GitRev
* r1
= m_arShownList
.SafeGetAt(index
);
80 if (progress
.IsVisible())
82 progress
.FormatNonPathLine(1, IDS_PROC_REVERTCOMMIT
, r1
->m_CommitHash
.ToString());
83 progress
.FormatNonPathLine(2, _T("%s"), (LPCTSTR
)r1
->GetSubject());
84 progress
.SetProgress(i
, this->GetSelectedCount());
88 if(r1
->m_CommitHash
.IsEmpty())
91 if (g_Git
.GitRevert(parent
, r1
->m_CommitHash
))
94 str
.LoadString(IDS_SVNACTION_FAILEDREVERT
);
95 str
= g_Git
.GetGitLastErr(str
, CGit::GIT_CMD_REVERT
);
96 if( GetSelectedCount() == 1)
97 CMessageBox::Show(GetSafeHwnd(), str
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
98 else if (CMessageBox::Show(GetSafeHwnd(), str
, L
"TortoiseGit", 2, IDI_ERROR
, CString(MAKEINTRESOURCE(IDS_SKIPBUTTON
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
))) == 2)
104 if (progress
.HasUserCancelled())
109 int CGitLogList::CherryPickFrom(CString from
, CString to
)
111 CLogDataVector
logs(&m_LogCache
);
113 range
.Format(_T("%s..%s"), (LPCTSTR
)from
, (LPCTSTR
)to
);
114 if (logs
.ParserFromLog(nullptr, 0, 0, &range
))
120 CSysProgressDlg progress
;
121 progress
.SetTitle(CString(MAKEINTRESOURCE(IDS_PROGS_TITLE_CHERRYPICK
)));
122 progress
.SetAnimation(IDR_MOVEANI
);
123 progress
.SetTime(true);
124 progress
.ShowModeless(this);
126 CBlockCacheForPath
cacheBlock(g_Git
.m_CurrentDir
);
128 for (int i
= (int)logs
.size() - 1; i
>= 0; i
--)
130 if (progress
.IsVisible())
132 progress
.FormatNonPathLine(1, IDS_PROC_PICK
, logs
.GetGitRevAt(i
).m_CommitHash
.ToString());
133 progress
.FormatNonPathLine(2, _T("%s"), (LPCTSTR
)logs
.GetGitRevAt(i
).GetSubject());
134 progress
.SetProgress64(logs
.size() - i
, logs
.size());
136 if (progress
.HasUserCancelled())
137 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_USERCANCELLED
))));
139 cmd
.Format(_T("git.exe cherry-pick %s"), (LPCTSTR
)logs
.GetGitRevAt(i
).m_CommitHash
.ToString());
140 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
141 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_CHERRYPICKFAILED
)) + _T(":\r\n\r\n") + out
));
147 int CGitLogList::DeleteRef(const CString
& ref
)
150 if (CGit::GetShortName(ref
, shortname
, _T("refs/remotes/")))
153 msg
.Format(IDS_PROC_DELETEREMOTEBRANCH
, (LPCTSTR
)ref
);
154 int result
= CMessageBox::Show(GetSafeHwnd(), msg
, L
"TortoiseGit", 3, IDI_QUESTION
, CString(MAKEINTRESOURCE(IDS_PROC_DELETEREMOTEBRANCH_LOCALREMOTE
)), CString(MAKEINTRESOURCE(IDS_PROC_DELETEREMOTEBRANCH_LOCAL
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
)));
157 CString remoteName
= shortname
.Left(shortname
.Find(L
'/'));
158 shortname
= shortname
.Mid(shortname
.Find(L
'/') + 1);
159 if (CAppUtils::IsSSHPutty())
160 CAppUtils::LaunchPAgent(nullptr, &remoteName
);
162 CSysProgressDlg sysProgressDlg
;
163 sysProgressDlg
.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME
)));
164 sysProgressDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS
)));
165 sysProgressDlg
.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT
)));
166 sysProgressDlg
.SetShowProgressBar(false);
167 sysProgressDlg
.ShowModal(this, true);
169 list
.push_back(_T("refs/heads/") + shortname
);
170 if (g_Git
.DeleteRemoteRefs(remoteName
, list
))
171 CMessageBox::Show(GetSafeHwnd(), g_Git
.GetGitLastErr(L
"Could not delete remote ref.", CGit::GIT_CMD_PUSH
), L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
172 sysProgressDlg
.Stop();
175 else if (result
== 2)
177 if (g_Git
.DeleteRef(ref
))
179 CMessageBox::Show(GetSafeHwnd(), g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
186 else if (CGit::GetShortName(ref
, shortname
, _T("refs/stash")))
189 std::vector
<GitRevLoglist
> stashList
;
190 size_t count
= !GitRevLoglist::GetRefLog(ref
, stashList
, err
) ? stashList
.size() : 0;
192 msg
.Format(IDS_PROC_DELETEALLSTASH
, count
);
193 int choose
= CMessageBox::Show(GetSafeHwnd(), msg
, L
"TortoiseGit", 3, IDI_QUESTION
, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON
)), CString(MAKEINTRESOURCE(IDS_DROPONESTASH
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
)));
197 sCmd
.Format(_T("git.exe stash clear"));
199 if (g_Git
.Run(sCmd
, &out
, CP_UTF8
))
200 CMessageBox::Show(GetSafeHwnd(), out
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
203 else if (choose
== 2)
206 sCmd
.Format(_T("git.exe stash drop refs/stash@{0}"));
208 if (g_Git
.Run(sCmd
, &out
, CP_UTF8
))
209 CMessageBox::Show(GetSafeHwnd(), out
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
216 msg
.Format(IDS_PROC_DELETEBRANCHTAG
, (LPCTSTR
)ref
);
217 if (CMessageBox::Show(GetSafeHwnd(), msg
, L
"TortoiseGit", 2, IDI_QUESTION
, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
))) == 1)
219 if (g_Git
.DeleteRef(ref
))
221 CMessageBox::Show(GetSafeHwnd(), g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
229 void CGitLogList::ContextMenuAction(int cmd
,int FirstSelect
, int LastSelect
, CMenu
*popmenu
)
231 POSITION pos
= GetFirstSelectedItemPosition();
232 int indexNext
= GetNextSelectedItem(pos
);
236 GitRevLoglist
* pSelLogEntry
= m_arShownList
.SafeGetAt(indexNext
);
238 bool bShiftPressed
= !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000);
240 theApp
.DoWaitCursor(1);
245 CTGitPathList pathlist
;
246 CTGitPathList selectedlist
;
247 pathlist
.AddPath(this->m_Path
);
248 bool bSelectFilesForCommit
= !!DWORD(CRegStdDWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE
));
250 CAppUtils::Commit(CString(),false,str
,
251 pathlist
,selectedlist
,bSelectFilesForCommit
);
253 this->GetParent()->PostMessage(WM_COMMAND
,ID_LOGDLG_REFRESH
,0);
258 if (CAppUtils::MergeAbort())
259 this->GetParent()->PostMessage(WM_COMMAND
,ID_LOGDLG_REFRESH
, 0);
262 case ID_GNUDIFF1
: // compare with WC, unified
264 GitRev
* r1
= m_arShownList
.SafeGetAt(FirstSelect
);
265 bool bMerge
= false, bCombine
= false;
267 if(!r1
->m_CommitHash
.IsEmpty())
271 if( (cmd
&0xFFFF) == 0xFFFF)
273 else if((cmd
&0xFFFF) == 0xFFFE)
275 else if ((cmd
& 0xFFFF) == 0xFFFD)
277 CString tempfile
= GetTempFile();
278 CString gitcmd
= _T("git.exe diff-tree --cc ") + r1
->m_CommitHash
.ToString();
280 if (g_Git
.RunLogFile(gitcmd
, tempfile
, &lastErr
))
282 MessageBox(lastErr
, _T("TortoiseGit"), MB_ICONERROR
);
288 CStdioFile
file(tempfile
, CFile::typeText
| CFile::modeRead
| CFile::shareDenyWrite
);
290 bool isHash
= file
.ReadString(strLine
) && r1
->m_CommitHash
.ToString() == strLine
;
291 bool more
= isHash
&& file
.ReadString(strLine
) && !strLine
.IsEmpty();
294 CMessageBox::Show(GetSafeHwnd(), IDS_NOCHANGEAFTERMERGE
, IDS_APPNAME
, MB_OK
);
298 catch (CFileException
* e
)
303 CAppUtils::StartUnifiedDiffViewer(tempfile
, _T("dd"));
308 if ((size_t)cmd
> r1
->m_ParentHash
.size())
311 str
.Format(IDS_PROC_NOPARENT
, cmd
);
312 MessageBox(str
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
318 hash2
= r1
->m_ParentHash
[cmd
-1].ToString();
321 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), hash2
, CTGitPath(), r1
->m_CommitHash
.ToString(), bShiftPressed
, false, false, bMerge
, bCombine
);
324 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), _T("HEAD"), CTGitPath(), GitRev::GetWorkingCopy(), bShiftPressed
, false, false, bMerge
, bCombine
);
328 case ID_GNUDIFF2
: // compare two revisions, unified
330 GitRev
* r1
= m_arShownList
.SafeGetAt(FirstSelect
);
331 GitRev
* r2
= m_arShownList
.SafeGetAt(LastSelect
);
332 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), r2
->m_CommitHash
.ToString(), CTGitPath(), r1
->m_CommitHash
.ToString(), bShiftPressed
);
336 case ID_COMPARETWO
: // compare two revisions
338 GitRev
* r1
= m_arShownList
.SafeGetAt(FirstSelect
);
339 GitRev
* r2
= m_arShownList
.SafeGetAt(LastSelect
);
340 if (m_Path
.IsDirectory() || !(m_ShowMask
& CGit::LOG_INFO_FOLLOW
))
341 CGitDiff::DiffCommit(m_Path
, r1
, r2
, bShiftPressed
);
344 CString path1
= m_Path
.GetGitPathString();
345 // start with 1 (0 = working copy changes)
346 for (int i
= m_bShowWC
? 1 : 0; i
< FirstSelect
; ++i
)
348 GitRevLoglist
* first
= m_arShownList
.SafeGetAt(i
);
349 CTGitPathList list
= first
->GetFiles(nullptr);
350 const CTGitPath
* file
= list
.LookForGitPath(path1
);
351 if (file
&& !file
->GetGitOldPathString().IsEmpty())
352 path1
= file
->GetGitOldPathString();
354 CString path2
= path1
;
355 for (int i
= FirstSelect
; i
< LastSelect
; ++i
)
357 GitRevLoglist
* first
= m_arShownList
.SafeGetAt(i
);
358 CTGitPathList list
= first
->GetFiles(nullptr);
359 const CTGitPath
* file
= list
.LookForGitPath(path2
);
360 if (file
&& !file
->GetGitOldPathString().IsEmpty())
361 path2
= file
->GetGitOldPathString();
363 CGitDiff::DiffCommit(CTGitPath(path1
), CTGitPath(path2
), r1
, r2
, bShiftPressed
);
369 case ID_COMPARE
: // compare revision with WC
371 GitRevLoglist
* r1
= &m_wcRev
;
372 GitRevLoglist
* r2
= pSelLogEntry
;
374 if (m_Path
.IsDirectory() || !(m_ShowMask
& CGit::LOG_INFO_FOLLOW
))
375 CGitDiff::DiffCommit(m_Path
, r1
, r2
, bShiftPressed
);
378 CString path1
= m_Path
.GetGitPathString();
379 // start with 1 (0 = working copy changes)
380 for (int i
= m_bShowWC
? 1 : 0; i
< FirstSelect
; ++i
)
382 GitRevLoglist
* first
= m_arShownList
.SafeGetAt(i
);
383 CTGitPathList list
= first
->GetFiles(nullptr);
384 const CTGitPath
* file
= list
.LookForGitPath(path1
);
385 if (file
&& !file
->GetGitOldPathString().IsEmpty())
386 path1
= file
->GetGitOldPathString();
388 CGitDiff::DiffCommit(m_Path
, CTGitPath(path1
), r1
, r2
, bShiftPressed
);
391 //user clicked on the menu item "compare with working copy"
394 // GitDiff diff(this, m_hWnd, true);
395 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
396 // diff.SetHEADPeg(m_LogRevision);
397 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
400 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
404 case ID_COMPAREWITHPREVIOUS
:
406 if (pSelLogEntry
->m_ParentHash
.empty())
408 if (pSelLogEntry
->GetParentFromHash(pSelLogEntry
->m_CommitHash
))
409 MessageBox(pSelLogEntry
->GetLastErr(), _T("TortoiseGit"), MB_ICONERROR
);
412 if (!pSelLogEntry
->m_ParentHash
.empty())
413 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
421 if (m_Path
.IsDirectory() || !(m_ShowMask
& CGit::LOG_INFO_FOLLOW
))
422 CGitDiff::DiffCommit(m_Path
, pSelLogEntry
->m_CommitHash
.ToString(), pSelLogEntry
->m_ParentHash
[cmd
- 1].ToString(), bShiftPressed
);
425 CString path1
= m_Path
.GetGitPathString();
426 // start with 1 (0 = working copy changes)
427 for (int i
= m_bShowWC
? 1 : 0; i
< indexNext
; ++i
)
429 GitRevLoglist
* first
= m_arShownList
.SafeGetAt(i
);
430 CTGitPathList list
= first
->GetFiles(nullptr);
431 const CTGitPath
* file
= list
.LookForGitPath(path1
);
432 if (file
&& !file
->GetGitOldPathString().IsEmpty())
433 path1
= file
->GetGitOldPathString();
435 CString path2
= path1
;
436 GitRevLoglist
* first
= m_arShownList
.SafeGetAt(indexNext
);
437 CTGitPathList list
= first
->GetFiles(nullptr);
438 const CTGitPath
* file
= list
.LookForGitPath(path2
);
439 if (file
&& !file
->GetGitOldPathString().IsEmpty())
440 path2
= file
->GetGitOldPathString();
442 CGitDiff::DiffCommit(CTGitPath(path1
), CTGitPath(path2
), pSelLogEntry
->m_CommitHash
.ToString(), pSelLogEntry
->m_ParentHash
[cmd
- 1].ToString(), bShiftPressed
);
447 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_NOPREVIOUSVERSION
, IDS_APPNAME
, MB_OK
);
451 // GitDiff diff(this, m_hWnd, true);
452 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
453 // diff.SetHEADPeg(m_LogRevision);
454 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
457 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
460 case ID_LOG_VIEWRANGE
:
461 case ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE
:
463 GitRev
* pLastEntry
= m_arShownList
.SafeGetAt(LastSelect
);
465 CString sep
= _T("..");
466 if ((cmd
& 0xFFFF) == ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE
)
470 cmdline
.Format(_T("/command:log /path:\"%s\" /range:\"%s%s%s\""),
471 (LPCTSTR
)g_Git
.CombinePath(m_Path
), (LPCTSTR
)pLastEntry
->m_CommitHash
.ToString(), (LPCTSTR
)sep
, (LPCTSTR
)pSelLogEntry
->m_CommitHash
.ToString());
472 CAppUtils::RunTortoiseGitProc(cmdline
);
475 case ID_COPYCLIPBOARD
:
477 CopySelectionToClipBoard();
480 case ID_COPYCLIPBOARDMESSAGES
:
482 if ((GetAsyncKeyState(VK_SHIFT
) & 0x8000) != 0)
483 CopySelectionToClipBoard(ID_COPY_SUBJECT
);
485 CopySelectionToClipBoard(ID_COPY_MESSAGE
);
490 CopySelectionToClipBoard(ID_COPY_HASH
);
495 CString str
=pSelLogEntry
->m_CommitHash
.ToString();
496 // try to get the tag
497 GetFirstEntryStartingWith(m_HashMap
[pSelLogEntry
->m_CommitHash
], _T("refs/tags/"), str
);
498 CAppUtils::Export(&str
, &m_Path
);
501 case ID_CREATE_BRANCH
:
504 const CString
* branch
= popmenu
? (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
& 0xFFFF) : nullptr;
505 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
508 else // try to guess remote branch in order to enable tracking
509 GetFirstEntryStartingWith(m_HashMap
[pSelLogEntry
->m_CommitHash
], _T("refs/remotes/"), str
);
511 CAppUtils::CreateBranchTag((cmd
&0xFFFF) == ID_CREATE_TAG
, &str
);
514 m_pFindDialog
->RefreshList();
516 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
521 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
522 const CString
* branch
= popmenu
? (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
& 0xFFFF) : nullptr;
525 else // try to guess remote branch in order to recommend good branch name and tracking
526 GetFirstEntryStartingWith(m_HashMap
[pSelLogEntry
->m_CommitHash
], _T("refs/remotes/"), str
);
528 CAppUtils::Switch(str
);
532 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
534 case ID_SWITCHBRANCH
:
537 const CString
* branch
= (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
);
540 CString name
= *branch
;
541 CGit::GetShortName(*branch
, name
, L
"refs/heads/");
542 CAppUtils::PerformSwitch(name
);
546 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
551 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
552 if (CAppUtils::GitReset(&str
))
561 SetSelectedRebaseAction(LOGACTIONS_REBASE_PICK
);
564 SetSelectedRebaseAction(LOGACTIONS_REBASE_EDIT
);
566 case ID_REBASE_SQUASH
:
567 SetSelectedRebaseAction(LOGACTIONS_REBASE_SQUASH
);
570 SetSelectedRebaseAction(LOGACTIONS_REBASE_SKIP
);
572 case ID_COMBINE_COMMIT
:
576 CGitHash hashFirst
,hashLast
;
578 int headindex
=GetHeadIndex();
579 if(headindex
>=0) //incase show all branch, head is not the first commits.
581 head
.Format(_T("HEAD~%d"),FirstSelect
-headindex
);
582 if (g_Git
.GetHash(hashFirst
, head
))
584 MessageBox(g_Git
.GetGitLastErr(_T("Could not get hash of first selected revision.")), _T("TortoiseGit"), MB_ICONERROR
);
588 head
.Format(_T("HEAD~%d"),LastSelect
-headindex
);
589 if (g_Git
.GetHash(hashLast
, head
))
591 MessageBox(g_Git
.GetGitLastErr(_T("Could not get hash of last selected revision.")), _T("TortoiseGit"), MB_ICONERROR
);
596 GitRev
* pFirstEntry
= m_arShownList
.SafeGetAt(FirstSelect
);
597 GitRev
* pLastEntry
= m_arShownList
.SafeGetAt(LastSelect
);
598 if(pFirstEntry
->m_CommitHash
!= hashFirst
|| pLastEntry
->m_CommitHash
!= hashLast
)
600 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_CANNOTCOMBINE
, IDS_APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
605 if (lastRevision
.GetParentFromHash(hashLast
))
607 MessageBox(lastRevision
.GetLastErr(), _T("TortoiseGit"), MB_ICONERROR
);
611 if (g_Git
.GetHash(headhash
, _T("HEAD")))
613 MessageBox(g_Git
.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR
);
617 if(!g_Git
.CheckCleanWorkTree())
619 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_NOCLEAN
, IDS_APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
624 //Use throw to abort this process (reset back to original HEAD)
627 sCmd
.Format(_T("git.exe reset --hard %s --"), (LPCTSTR
)pFirstEntry
->m_CommitHash
.ToString());
628 if(g_Git
.Run(sCmd
, &out
, CP_UTF8
))
630 MessageBox(out
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
631 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORSTEP1
)) + _T("\r\n\r\n") + out
));
633 sCmd
.Format(_T("git.exe reset --mixed %s --"), (LPCTSTR
)hashLast
.ToString());
634 if(g_Git
.Run(sCmd
, &out
, CP_UTF8
))
636 MessageBox(out
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
637 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORSTEP2
)) + _T("\r\n\r\n")+out
));
640 CTGitPathList PathList
;
641 /* don't why must add --stat to get action status*/
642 /* first -z will be omitted by gitdll*/
643 if(g_Git
.GetDiffPath(&PathList
,&pFirstEntry
->m_CommitHash
,&hashLast
,"-z --stat -r"))
645 MessageBox(_T("Get Diff file list error"), _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
646 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not get changed file list aborting...\r\n\r\n")+out
));
649 for (int i
= 0; i
< PathList
.GetCount(); ++i
)
651 if(PathList
[i
].m_Action
& CTGitPath::LOGACTIONS_ADDED
)
653 sCmd
.Format(_T("git.exe add -- \"%s\""), (LPCTSTR
)PathList
[i
].GetGitPathString());
654 if (g_Git
.Run(sCmd
, &out
, CP_UTF8
))
656 MessageBox(out
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
657 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not add new file aborting...\r\n\r\n")+out
));
660 if(PathList
[i
].m_Action
& CTGitPath::LOGACTIONS_DELETED
)
662 sCmd
.Format(_T("git.exe rm -- \"%s\""), (LPCTSTR
)PathList
[i
].GetGitPathString());
663 if (g_Git
.Run(sCmd
, &out
, CP_UTF8
))
665 MessageBox(out
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
666 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not rm file aborting...\r\n\r\n")+out
));
672 for (int i
= FirstSelect
; i
<= LastSelect
; ++i
)
674 GitRev
* pRev
= m_arShownList
.SafeGetAt(i
);
675 dlg
.m_sLogMessage
+=pRev
->GetSubject()+_T("\n")+pRev
->GetBody();
676 dlg
.m_sLogMessage
+=_T("\n");
678 dlg
.m_bWholeProject
=true;
679 dlg
.m_bSelectFilesForCommit
= true;
680 dlg
.m_bForceCommitAmend
=true;
682 gpl
.AddPath(CTGitPath(g_Git
.m_CurrentDir
));
683 dlg
.m_pathList
= gpl
;
684 if (lastRevision
.ParentsCount() != 1)
686 MessageBox(_T("The following commit dialog can only show changes of oldest commit if it has exactly one parent. This is not the case right now."), _T("TortoiseGit"), MB_OK
| MB_ICONINFORMATION
);
687 dlg
.m_bAmendDiffToLastCommit
= TRUE
;
690 dlg
.m_bAmendDiffToLastCommit
= FALSE
;
691 dlg
.m_bNoPostActions
=true;
692 dlg
.m_AmendStr
=dlg
.m_sLogMessage
;
694 if (dlg
.DoModal() == IDOK
)
696 if(pFirstEntry
->m_CommitHash
!=headhash
)
698 if(CherryPickFrom(pFirstEntry
->m_CommitHash
.ToString(),headhash
))
701 msg
.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));
702 throw std::exception(CUnicodeUtils::GetUTF8(msg
));
707 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_USERCANCELLED
))));
709 catch(std::exception
& e
)
711 CMessageBox::Show(GetSafeHwnd(), CUnicodeUtils::GetUnicode(CStringA(e
.what())), L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
712 sCmd
.Format(_T("git.exe reset --hard %s --"), (LPCTSTR
)headhash
.ToString());
714 if(g_Git
.Run(sCmd
, &out
, CP_UTF8
))
715 MessageBox(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORRESETHEAD
)) + _T("\r\n\r\n") + out
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
723 if (m_bThreadRunning
)
725 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_LOG_ONLYONCE
, IDS_APPNAME
, MB_ICONEXCLAMATION
);
729 dlg
.m_IsCherryPick
= TRUE
;
730 dlg
.m_Upstream
= this->m_CurrentBranch
;
731 POSITION pos2
= GetFirstSelectedItemPosition();
734 int indexNext2
= GetNextSelectedItem(pos2
);
735 dlg
.m_CommitList
.m_logEntries
.push_back(m_arShownList
.SafeGetAt(indexNext2
)->m_CommitHash
);
736 dlg
.m_CommitList
.m_LogCache
.m_HashMap
[m_arShownList
.SafeGetAt(indexNext2
)->m_CommitHash
] = *m_arShownList
.SafeGetAt(indexNext2
);
737 dlg
.m_CommitList
.m_logEntries
.GetGitRevAt(dlg
.m_CommitList
.m_logEntries
.size() - 1).GetRebaseAction() |= LOGACTIONS_REBASE_PICK
;
740 if(dlg
.DoModal() == IDOK
)
746 case ID_REBASE_TO_VERSION
:
748 if (m_bThreadRunning
)
750 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_LOG_ONLYONCE
, IDS_APPNAME
, MB_ICONEXCLAMATION
);
754 auto refList
= m_HashMap
[pSelLogEntry
->m_CommitHash
];
755 dlg
.m_Upstream
= refList
.empty() ? pSelLogEntry
->m_CommitHash
.ToString() : refList
.front();
756 for (const auto& ref
: refList
)
757 if (CGit::GetShortName(ref
, dlg
.m_Upstream
, L
"refs/heads/"))
760 if(dlg
.DoModal() == IDOK
)
769 if (CAppUtils::StashSave())
774 if (CAppUtils::StashPop())
779 CAppUtils::RunTortoiseGitProc(_T("/command:reflog /ref:refs/stash"));
782 case ID_REFLOG_STASH_APPLY
:
783 CAppUtils::StashApply(pSelLogEntry
->m_Ref
);
789 if (GetSelectedCount() > 1)
790 str
.Format(IDS_PROC_DELETENREFS
, GetSelectedCount());
792 str
.Format(IDS_PROC_DELETEREF
, (LPCTSTR
)pSelLogEntry
->m_Ref
);
794 if (CMessageBox::Show(GetSafeHwnd(), str
, L
"TortoiseGit", 1, IDI_QUESTION
, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
))) == 2)
797 std::vector
<CString
> refsToDelete
;
798 POSITION pos2
= GetFirstSelectedItemPosition();
801 CString ref
= m_arShownList
.SafeGetAt(GetNextSelectedItem(pos2
))->m_Ref
;
802 if (wcsncmp(ref
, L
"refs/", 5) == 0)
804 int refpos
= ref
.ReverseFind('{');
805 if (refpos
> 0 && ref
.Mid(refpos
- 1, 2) != _T("@{"))
806 ref
= ref
.Left(refpos
) + _T("@")+ ref
.Mid(refpos
);
807 refsToDelete
.push_back(ref
);
810 for (auto revIt
= refsToDelete
.crbegin(); revIt
!= refsToDelete
.crend(); ++revIt
)
812 CString ref
= *revIt
;
814 if (wcsncmp(ref
, L
"stash", 5) == 0)
815 sCmd
.Format(_T("git.exe stash drop %s"), (LPCTSTR
)ref
);
817 sCmd
.Format(_T("git.exe reflog delete %s"), (LPCTSTR
)ref
);
819 if (g_Git
.Run(sCmd
, &out
, CP_UTF8
))
820 MessageBox(out
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
822 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
828 CString sCmd
= _T("/command:log");
829 sCmd
+= _T(" /path:\"") + g_Git
.m_CurrentDir
+ _T("\" ");
830 GitRev
* r1
= m_arShownList
.SafeGetAt(FirstSelect
);
831 sCmd
+= _T(" /endrev:") + r1
->m_CommitHash
.ToString();
832 CAppUtils::RunTortoiseGitProc(sCmd
);
835 case ID_CREATE_PATCH
:
837 int select
=this->GetSelectedCount();
838 CString sCmd
= _T("/command:formatpatch");
839 sCmd
+= _T(" /path:\"") + g_Git
.m_CurrentDir
+ _T("\" ");
841 GitRev
* r1
= m_arShownList
.SafeGetAt(FirstSelect
);
842 GitRev
* r2
= nullptr;
845 sCmd
+= _T(" /startrev:") + r1
->m_CommitHash
.ToString();
849 r2
= m_arShownList
.SafeGetAt(LastSelect
);
850 if( this->m_IsOldFirst
)
852 sCmd
+= _T(" /startrev:") + r1
->m_CommitHash
.ToString() + _T("~1");
853 sCmd
+= _T(" /endrev:") + r2
->m_CommitHash
.ToString();
858 sCmd
+= _T(" /startrev:") + r2
->m_CommitHash
.ToString() + _T("~1");
859 sCmd
+= _T(" /endrev:") + r1
->m_CommitHash
.ToString();
864 CAppUtils::RunTortoiseGitProc(sCmd
);
869 GitRev
* first
= m_arShownList
.SafeGetAt(FirstSelect
);
870 GitRev
* last
= m_arShownList
.SafeGetAt(LastSelect
);
871 ASSERT(first
&& last
);
873 CString firstBad
= first
->m_CommitHash
.ToString();
874 if (!m_HashMap
[first
->m_CommitHash
].empty())
875 firstBad
= m_HashMap
[first
->m_CommitHash
].at(0);
876 CString lastGood
= last
->m_CommitHash
.ToString();
877 if (!m_HashMap
[last
->m_CommitHash
].empty())
878 lastGood
= m_HashMap
[last
->m_CommitHash
].at(0);
880 if (CAppUtils::BisectStart(lastGood
, firstBad
))
886 GitRev
* first
= m_arShownList
.SafeGetAt(FirstSelect
);
887 if (CAppUtils::BisectOperation(_T("good"), !first
->m_CommitHash
.IsEmpty() ? first
->m_CommitHash
.ToString() : _T("")))
893 GitRev
* first
= m_arShownList
.SafeGetAt(FirstSelect
);
894 if (CAppUtils::BisectOperation(_T("bad"), !first
->m_CommitHash
.IsEmpty() ? first
->m_CommitHash
.ToString() : _T("")))
900 if (CAppUtils::BisectOperation(_T("reset")))
907 sCmd
.Format(_T("/command:repobrowser /path:\"%s\" /rev:%s"), (LPCTSTR
)g_Git
.m_CurrentDir
, (LPCTSTR
)pSelLogEntry
->m_CommitHash
.ToString());
908 CAppUtils::RunTortoiseGitProc(sCmd
);
913 CString guessAssociatedBranch
= pSelLogEntry
->m_CommitHash
;
914 const CString
* branch
= popmenu
? (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
) : nullptr;
916 guessAssociatedBranch
= *branch
;
918 GetFirstEntryStartingWith(m_HashMap
[pSelLogEntry
->m_CommitHash
], _T("refs/heads/"), guessAssociatedBranch
);
920 if (CAppUtils::Push(guessAssociatedBranch
))
926 if (CAppUtils::Pull())
932 if (CAppUtils::Fetch())
939 sCmd
.Format(_T("/command:cleanup /path:\"%s\""), (LPCTSTR
)g_Git
.m_CurrentDir
);
940 CAppUtils::RunTortoiseGitProc(sCmd
);
943 case ID_SUBMODULE_UPDATE
:
946 sCmd
.Format(_T("/command:subupdate /bkpath:\"%s\""), (LPCTSTR
)g_Git
.m_CurrentDir
);
947 CAppUtils::RunTortoiseGitProc(sCmd
);
950 case ID_SHOWBRANCHES
:
953 sCmd
.Format(_T("git.exe branch -a --contains %s"), (LPCTSTR
)pSelLogEntry
->m_CommitHash
.ToString());
954 CProgressDlg progress
;
955 progress
.m_AutoClose
= AUTOCLOSE_NO
;
956 progress
.m_GitCmd
= sCmd
;
962 const CString
* branch
= popmenu
? (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
) : nullptr;
965 CMessageBox::Show(GetSafeHwnd(), IDS_ERROR_NOREF
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
969 if (branch
== (CString
*)MAKEINTRESOURCE(IDS_ALL
))
971 CString currentBranch
= L
"refs/heads/" + m_CurrentBranch
;
972 bool nothingDeleted
= true;
973 for (const auto& ref
: m_HashMap
[pSelLogEntry
->m_CommitHash
])
975 if (ref
== currentBranch
)
979 nothingDeleted
= false;
984 else if (!DeleteRef(*branch
))
986 this->ReloadHashMap();
988 m_pFindDialog
->RefreshList();
990 this->GetItemRect(FirstSelect
,&rect
,LVIR_BOUNDS
);
991 this->InvalidateRect(rect
);
997 m_nSearchIndex
= GetSelectionMark();
998 if (m_nSearchIndex
< 0)
1004 m_pFindDialog
= new CFindDlg();
1005 m_pFindDialog
->Create(this);
1011 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
1012 const CString
* branch
= popmenu
? (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
& 0xFFFF) : nullptr;
1015 else if (!m_HashMap
[pSelLogEntry
->m_CommitHash
].empty())
1016 str
= m_HashMap
[pSelLogEntry
->m_CommitHash
].at(0);
1017 // we need an URL to complete this command, so error out if we can't get an URL
1018 if(CAppUtils::Merge(&str
))
1027 if (GetSelectedCount() == 1)
1030 if ((size_t)parent
> pSelLogEntry
->m_ParentHash
.size())
1033 str
.Format(IDS_PROC_NOPARENT
, parent
);
1034 MessageBox(str
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
1039 if (!this->RevertSelectedCommits(parent
))
1041 if (CMessageBox::Show(m_hWnd
, IDS_REVREVERTED
, IDS_APPNAME
, 1, IDI_QUESTION
, IDS_OKBUTTON
, IDS_COMMITBUTTON
) == 2)
1043 CTGitPathList pathlist
;
1044 CTGitPathList selectedlist
;
1045 pathlist
.AddPath(this->m_Path
);
1046 bool bSelectFilesForCommit
= !!DWORD(CRegStdDWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE
));
1048 CAppUtils::Commit(CString(), false, str
, pathlist
, selectedlist
, bSelectFilesForCommit
);
1056 CAppUtils::EditNote(pSelLogEntry
);
1057 this->SetItemState(FirstSelect
, 0, LVIS_SELECTED
);
1058 this->SetItemState(FirstSelect
, LVIS_SELECTED
, LVIS_SELECTED
);
1062 //CMessageBox::Show(nullptr, _T("Have not implemented"), _T("TortoiseGit"), MB_OK);
1066 case ID_BLAMECOMPARE
:
1068 //user clicked on the menu item "compare with working copy"
1069 //now first get the revision which is selected
1072 GitDiff
diff(this, this->m_hWnd
, true);
1073 diff
.SetHEADPeg(m_LogRevision
);
1074 diff
.ShowCompare(m_path
, GitRev::REV_BASE
, m_path
, revSelected
, GitRev(), false, true);
1077 CAppUtils::StartShowCompare(m_hWnd
, m_path
, GitRev::REV_BASE
, m_path
, revSelected
, GitRev(), m_LogRevision
, false, false, true);
1080 case ID_BLAMEWITHPREVIOUS
:
1082 //user clicked on the menu item "Compare and Blame with previous revision"
1085 GitDiff
diff(this, this->m_hWnd
, true);
1086 diff
.SetHEADPeg(m_LogRevision
);
1087 diff
.ShowCompare(CTGitPath(pathURL
), revPrevious
, CTGitPath(pathURL
), revSelected
, GitRev(), false, true);
1090 CAppUtils::StartShowCompare(m_hWnd
, CTGitPath(pathURL
), revPrevious
, CTGitPath(pathURL
), revSelected
, GitRev(), m_LogRevision
, false, false, true);
1098 CProgressDlg progDlg
;
1099 progDlg
.SetTitle(IDS_APPNAME
);
1100 progDlg
.SetAnimation(IDR_DOWNLOAD
);
1102 sInfoLine
.Format(IDS_PROGRESSGETFILEREVISION
, m_path
.GetWinPath(), (LPCTSTR
)revSelected
.ToString());
1103 progDlg
.SetLine(1, sInfoLine
, true);
1104 SetAndClearProgressInfo(&progDlg
);
1105 progDlg
.ShowModeless(m_hWnd
);
1106 CTGitPath tempfile
= CTempFiles::Instance().GetTempFilePath(false, m_path
, revSelected
);
1107 bool bSuccess
= true;
1108 if (!Cat(m_path
, GitRev(GitRev::REV_HEAD
), revSelected
, tempfile
))
1111 // try again, but with the selected revision as the peg revision
1112 if (!Cat(m_path
, revSelected
, revSelected
, tempfile
))
1115 SetAndClearProgressInfo(nullptr);
1116 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
1125 SetAndClearProgressInfo(nullptr);
1126 SetFileAttributes(tempfile
.GetWinPath(), FILE_ATTRIBUTE_READONLY
);
1128 CAppUtils::ShellOpen(tempfile
.GetWinPath(), GetSafeHwnd());
1130 CAppUtils::ShowOpenWithDialog(tempfile
.GetWinPathString(), GetSafeHwnd());
1137 dlg
.EndRev
= revSelected
;
1138 if (dlg
.DoModal() == IDOK
)
1143 tempfile
= blame
.BlameToTempFile(m_path
, dlg
.StartRev
, dlg
.EndRev
, dlg
.EndRev
, logfile
, _T(""), dlg
.m_bIncludeMerge
, TRUE
, TRUE
);
1144 if (!tempfile
.IsEmpty())
1146 if (dlg
.m_bTextView
)
1148 //open the default text editor for the result file
1149 CAppUtils::StartTextViewer(tempfile
);
1153 CString sParams
= _T("/path:\"") + m_path
.GetGitPathString() + _T("\" ");
1154 if(!CAppUtils::LaunchTortoiseBlame(tempfile
, logfile
, CPathUtils::GetFileNameFromPath(m_path
.GetFileOrDirectoryName()),sParams
))
1162 CMessageBox::Show(this->m_hWnd
, blame
.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
1170 sCmd
.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
1171 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseGitProc.exe")),
1172 (LPCTSTR
)pathURL
, (LONG
)revSelected
);
1173 CAppUtils::LaunchApplication(sCmd
, nullptr, false);
1178 CString url
= m_ProjectProperties
.sWebViewerRev
;
1179 url
= GetAbsoluteUrlFromRelativeUrl(url
);
1180 url
.Replace(_T("%REVISION%"), revSelected
.ToString());
1182 ShellExecute(this->m_hWnd
, _T("open"), url
, nullptr, nullptr, SW_SHOWDEFAULT
);
1185 case ID_VIEWPATHREV
:
1187 CString relurl
= pathURL
;
1188 CString sRoot
= GetRepositoryRoot(CTGitPath(relurl
));
1189 relurl
= relurl
.Mid(sRoot
.GetLength());
1190 CString url
= m_ProjectProperties
.sWebViewerPathRev
;
1191 url
= GetAbsoluteUrlFromRelativeUrl(url
);
1192 url
.Replace(_T("%REVISION%"), revSelected
.ToString());
1193 url
.Replace(_T("%PATH%"), relurl
);
1195 ShellExecute(this->m_hWnd
, _T("open"), url
, nullptr, nullptr, SW_SHOWDEFAULT
);
1202 theApp
.DoWaitCursor(-1);
1205 void CGitLogList::SetSelectedRebaseAction(int action
)
1207 POSITION pos
= GetFirstSelectedItemPosition();
1212 index
= GetNextSelectedItem(pos
);
1213 if (m_arShownList
.SafeGetAt(index
)->GetRebaseAction() & (LOGACTIONS_REBASE_CURRENT
| LOGACTIONS_REBASE_DONE
) || (index
== GetItemCount() - 1 && action
== LOGACTIONS_REBASE_SQUASH
))
1215 if (!m_bIsCherryPick
&& m_arShownList
.SafeGetAt(index
)->ParentsCount() > 1 && action
== LOGACTIONS_REBASE_SQUASH
)
1217 m_arShownList
.SafeGetAt(index
)->GetRebaseAction() = action
;
1219 this->GetItemRect(index
,&rect
,LVIR_BOUNDS
);
1220 this->InvalidateRect(rect
);
1223 GetParent()->PostMessage(CGitLogListBase::m_RebaseActionMessage
);
1226 void CGitLogList::SetUnselectedRebaseAction(int action
)
1228 POSITION pos
= GetFirstSelectedItemPosition();
1229 int index
= pos
? GetNextSelectedItem(pos
) : -1;
1230 for (int i
= 0; i
< GetItemCount(); i
++)
1234 index
= pos
? GetNextSelectedItem(pos
) : -1;
1238 if (m_arShownList
.SafeGetAt(i
)->GetRebaseAction() & (LOGACTIONS_REBASE_CURRENT
| LOGACTIONS_REBASE_DONE
) || (i
== GetItemCount() - 1 && action
== LOGACTIONS_REBASE_SQUASH
) || (!m_bIsCherryPick
&& action
== LOGACTIONS_REBASE_SQUASH
&& m_arShownList
.SafeGetAt(i
)->ParentsCount() != 1))
1240 m_arShownList
.SafeGetAt(i
)->GetRebaseAction() = action
;
1242 this->GetItemRect(i
, &rect
, LVIR_BOUNDS
);
1243 this->InvalidateRect(rect
);
1246 GetParent()->PostMessage(CGitLogListBase::m_RebaseActionMessage
);
1249 void CGitLogList::ShiftSelectedRebaseAction()
1251 POSITION pos
= GetFirstSelectedItemPosition();
1255 index
= GetNextSelectedItem(pos
);
1256 int* action
= &(m_arShownList
.SafeGetAt(index
))->GetRebaseAction();
1259 case LOGACTIONS_REBASE_PICK
:
1260 *action
= LOGACTIONS_REBASE_SKIP
;
1262 case LOGACTIONS_REBASE_SKIP
:
1263 *action
= LOGACTIONS_REBASE_EDIT
;
1265 case LOGACTIONS_REBASE_EDIT
:
1266 *action
= LOGACTIONS_REBASE_SQUASH
;
1267 if (index
== GetItemCount() - 1 && (m_bIsCherryPick
|| m_arShownList
.SafeGetAt(index
)->m_ParentHash
.size() == 1))
1268 *action
= LOGACTIONS_REBASE_PICK
;
1270 case LOGACTIONS_REBASE_SQUASH
:
1271 *action
= LOGACTIONS_REBASE_PICK
;
1275 this->GetItemRect(index
, &rect
, LVIR_BOUNDS
);
1276 this->InvalidateRect(rect
);