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"
40 #include "CommitIsOnRefsDlg.h"
42 #include "../TGitCache/CacheInterface.h"
44 IMPLEMENT_DYNAMIC(CGitLogList
, CHintCtrl
<CListCtrl
>)
46 static void GetFirstEntryStartingWith(STRING_VECTOR
& heystack
, const CString
& needle
, CString
& result
)
48 auto it
= std::find_if(heystack
.cbegin(), heystack
.cend(), [&needle
](const CString
& entry
) { return CStringUtils::StartsWith(entry
, needle
); });
49 if (it
== heystack
.cend())
54 int CGitLogList::RevertSelectedCommits(int parent
)
56 CSysProgressDlg progress
;
60 if(!g_Git
.CheckCleanWorkTree())
61 CMessageBox::Show(nullptr, IDS_PROC_NOCLEAN
, IDS_APPNAME
, MB_OK
);
64 if (this->GetSelectedCount() > 1)
66 progress
.SetTitle(CString(MAKEINTRESOURCE(IDS_PROGS_TITLE_REVERTCOMMIT
)));
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, L
"%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(GetSafeOwner()->GetSafeHwnd(), str
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
98 else if (CMessageBox::Show(GetSafeOwner()->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(L
"%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
.SetTime(true);
123 progress
.ShowModeless(this);
125 CBlockCacheForPath
cacheBlock(g_Git
.m_CurrentDir
);
127 for (int i
= (int)logs
.size() - 1; i
>= 0; i
--)
129 if (progress
.IsVisible())
131 progress
.FormatNonPathLine(1, IDS_PROC_PICK
, logs
.GetGitRevAt(i
).m_CommitHash
.ToString());
132 progress
.FormatNonPathLine(2, L
"%s", (LPCTSTR
)logs
.GetGitRevAt(i
).GetSubject());
133 progress
.SetProgress64(logs
.size() - i
, logs
.size());
135 if (progress
.HasUserCancelled())
136 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_USERCANCELLED
))));
138 cmd
.Format(L
"git.exe cherry-pick %s", (LPCTSTR
)logs
.GetGitRevAt(i
).m_CommitHash
.ToString());
139 if(g_Git
.Run(cmd
,&out
,CP_UTF8
))
140 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_CHERRYPICKFAILED
)) + L
":\r\n\r\n" + out
));
146 int CGitLogList::DeleteRef(const CString
& ref
)
149 if (CGit::GetShortName(ref
, shortname
, L
"refs/remotes/"))
152 msg
.Format(IDS_PROC_DELETEREMOTEBRANCH
, (LPCTSTR
)ref
);
153 int result
= CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), msg
, L
"TortoiseGit", 3, IDI_QUESTION
, CString(MAKEINTRESOURCE(IDS_PROC_DELETEREMOTEBRANCH_LOCALREMOTE
)), CString(MAKEINTRESOURCE(IDS_PROC_DELETEREMOTEBRANCH_LOCAL
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
)));
156 CString remoteName
= shortname
.Left(shortname
.Find(L
'/'));
157 shortname
= shortname
.Mid(shortname
.Find(L
'/') + 1);
158 if (CAppUtils::IsSSHPutty())
159 CAppUtils::LaunchPAgent(nullptr, &remoteName
);
161 CSysProgressDlg sysProgressDlg
;
162 sysProgressDlg
.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME
)));
163 sysProgressDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS
)));
164 sysProgressDlg
.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT
)));
165 sysProgressDlg
.SetShowProgressBar(false);
166 sysProgressDlg
.ShowModal(this, true);
168 list
.push_back(L
"refs/heads/" + shortname
);
169 if (g_Git
.DeleteRemoteRefs(remoteName
, list
))
170 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), g_Git
.GetGitLastErr(L
"Could not delete remote ref.", CGit::GIT_CMD_PUSH
), L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
171 sysProgressDlg
.Stop();
174 else if (result
== 2)
176 if (g_Git
.DeleteRef(ref
))
178 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
185 else if (CGit::GetShortName(ref
, shortname
, L
"refs/stash"))
188 std::vector
<GitRevLoglist
> stashList
;
189 size_t count
= !GitRevLoglist::GetRefLog(ref
, stashList
, err
) ? stashList
.size() : 0;
191 msg
.Format(IDS_PROC_DELETEALLSTASH
, count
);
192 int choose
= CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), msg
, L
"TortoiseGit", 3, IDI_QUESTION
, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON
)), CString(MAKEINTRESOURCE(IDS_DROPONESTASH
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
)));
196 if (g_Git
.Run(L
"git.exe stash clear", &out
, CP_UTF8
))
197 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), out
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
200 else if (choose
== 2)
203 if (g_Git
.Run(L
"git.exe stash drop refs/stash@{0}", &out
, CP_UTF8
))
204 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), out
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
211 msg
.Format(IDS_PROC_DELETEBRANCHTAG
, (LPCTSTR
)ref
);
212 if (CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), msg
, L
"TortoiseGit", 2, IDI_QUESTION
, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
))) == 1)
214 if (g_Git
.DeleteRef(ref
))
216 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), g_Git
.GetGitLastErr(L
"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH
), L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
224 void CGitLogList::ContextMenuAction(int cmd
,int FirstSelect
, int LastSelect
, CMenu
*popmenu
)
226 POSITION pos
= GetFirstSelectedItemPosition();
227 int indexNext
= GetNextSelectedItem(pos
);
231 GitRevLoglist
* pSelLogEntry
= m_arShownList
.SafeGetAt(indexNext
);
233 bool bShiftPressed
= !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000);
235 theApp
.DoWaitCursor(1);
240 CTGitPathList pathlist
;
241 CTGitPathList selectedlist
;
242 pathlist
.AddPath(this->m_Path
);
243 bool bSelectFilesForCommit
= !!DWORD(CRegStdDWORD(L
"Software\\TortoiseGit\\SelectFilesForCommit", TRUE
));
245 CAppUtils::Commit(CString(),false,str
,
246 pathlist
,selectedlist
,bSelectFilesForCommit
);
248 this->GetParent()->PostMessage(WM_COMMAND
,ID_LOGDLG_REFRESH
,0);
253 if (CAppUtils::MergeAbort())
254 this->GetParent()->PostMessage(WM_COMMAND
,ID_LOGDLG_REFRESH
, 0);
257 case ID_GNUDIFF1
: // compare with WC, unified
259 GitRev
* r1
= m_arShownList
.SafeGetAt(FirstSelect
);
260 bool bMerge
= false, bCombine
= false;
262 if(!r1
->m_CommitHash
.IsEmpty())
266 if( (cmd
&0xFFFF) == 0xFFFF)
268 else if((cmd
&0xFFFF) == 0xFFFE)
270 else if ((cmd
& 0xFFFF) == 0xFFFD)
272 CString tempfile
= GetTempFile();
273 CString gitcmd
= L
"git.exe diff-tree --cc " + r1
->m_CommitHash
.ToString();
275 if (g_Git
.RunLogFile(gitcmd
, tempfile
, &lastErr
))
277 MessageBox(lastErr
, L
"TortoiseGit", MB_ICONERROR
);
283 CStdioFile
file(tempfile
, CFile::typeText
| CFile::modeRead
| CFile::shareDenyWrite
);
285 bool isHash
= file
.ReadString(strLine
) && r1
->m_CommitHash
.ToString() == strLine
;
286 bool more
= isHash
&& file
.ReadString(strLine
) && !strLine
.IsEmpty();
289 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), IDS_NOCHANGEAFTERMERGE
, IDS_APPNAME
, MB_OK
);
293 catch (CFileException
* e
)
298 CAppUtils::StartUnifiedDiffViewer(tempfile
, r1
->m_CommitHash
.ToString());
305 if ((size_t)cmd
> r1
->m_ParentHash
.size())
308 str
.Format(IDS_PROC_NOPARENT
, cmd
);
309 MessageBox(str
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
315 hash2
= r1
->m_ParentHash
[cmd
-1].ToString();
318 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), hash2
, CTGitPath(), r1
->m_CommitHash
.ToString(), bShiftPressed
, false, false, bMerge
, bCombine
);
321 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), L
"HEAD", CTGitPath(), GitRev::GetWorkingCopy(), bShiftPressed
, false, false, bMerge
, bCombine
);
325 case ID_GNUDIFF2
: // compare two revisions, unified
327 GitRev
* r1
= m_arShownList
.SafeGetAt(FirstSelect
);
328 GitRev
* r2
= m_arShownList
.SafeGetAt(LastSelect
);
329 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), r2
->m_CommitHash
.ToString(), CTGitPath(), r1
->m_CommitHash
.ToString(), bShiftPressed
);
333 case ID_COMPARETWO
: // compare two revisions
335 GitRev
* r1
= m_arShownList
.SafeGetAt(FirstSelect
);
336 GitRev
* r2
= m_arShownList
.SafeGetAt(LastSelect
);
337 if (m_Path
.IsDirectory() || !(m_ShowMask
& CGit::LOG_INFO_FOLLOW
))
338 CGitDiff::DiffCommit(m_Path
, r1
, r2
, bShiftPressed
);
341 CString path1
= m_Path
.GetGitPathString();
342 // start with 1 (0 = working copy changes)
343 for (int i
= m_bShowWC
? 1 : 0; i
< FirstSelect
; ++i
)
345 GitRevLoglist
* first
= m_arShownList
.SafeGetAt(i
);
346 CTGitPathList list
= first
->GetFiles(nullptr);
347 const CTGitPath
* file
= list
.LookForGitPath(path1
);
348 if (file
&& !file
->GetGitOldPathString().IsEmpty())
349 path1
= file
->GetGitOldPathString();
351 CString path2
= path1
;
352 for (int i
= FirstSelect
; i
< LastSelect
; ++i
)
354 GitRevLoglist
* first
= m_arShownList
.SafeGetAt(i
);
355 CTGitPathList list
= first
->GetFiles(nullptr);
356 const CTGitPath
* file
= list
.LookForGitPath(path2
);
357 if (file
&& !file
->GetGitOldPathString().IsEmpty())
358 path2
= file
->GetGitOldPathString();
360 CGitDiff::DiffCommit(CTGitPath(path1
), CTGitPath(path2
), r1
, r2
, bShiftPressed
);
366 case ID_COMPARE
: // compare revision with WC
368 GitRevLoglist
* r1
= &m_wcRev
;
369 GitRevLoglist
* r2
= pSelLogEntry
;
371 if (m_Path
.IsDirectory() || !(m_ShowMask
& CGit::LOG_INFO_FOLLOW
))
372 CGitDiff::DiffCommit(m_Path
, r1
, r2
, bShiftPressed
);
375 CString path1
= m_Path
.GetGitPathString();
376 // start with 1 (0 = working copy changes)
377 for (int i
= m_bShowWC
? 1 : 0; i
< FirstSelect
; ++i
)
379 GitRevLoglist
* first
= m_arShownList
.SafeGetAt(i
);
380 CTGitPathList list
= first
->GetFiles(nullptr);
381 const CTGitPath
* file
= list
.LookForGitPath(path1
);
382 if (file
&& !file
->GetGitOldPathString().IsEmpty())
383 path1
= file
->GetGitOldPathString();
385 CGitDiff::DiffCommit(m_Path
, CTGitPath(path1
), r1
, r2
, bShiftPressed
);
388 //user clicked on the menu item "compare with working copy"
391 // GitDiff diff(this, m_hWnd, true);
392 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
393 // diff.SetHEADPeg(m_LogRevision);
394 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
397 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
401 case ID_COMPAREWITHPREVIOUS
:
403 if (pSelLogEntry
->m_ParentHash
.empty())
405 if (pSelLogEntry
->GetParentFromHash(pSelLogEntry
->m_CommitHash
))
406 MessageBox(pSelLogEntry
->GetLastErr(), L
"TortoiseGit", MB_ICONERROR
);
409 if (!pSelLogEntry
->m_ParentHash
.empty())
410 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
418 if (m_Path
.IsDirectory() || !(m_ShowMask
& CGit::LOG_INFO_FOLLOW
))
419 CGitDiff::DiffCommit(m_Path
, pSelLogEntry
->m_CommitHash
.ToString(), pSelLogEntry
->m_ParentHash
[cmd
- 1].ToString(), bShiftPressed
);
422 CString path1
= m_Path
.GetGitPathString();
423 // start with 1 (0 = working copy changes)
424 for (int i
= m_bShowWC
? 1 : 0; i
< indexNext
; ++i
)
426 GitRevLoglist
* first
= m_arShownList
.SafeGetAt(i
);
427 CTGitPathList list
= first
->GetFiles(nullptr);
428 const CTGitPath
* file
= list
.LookForGitPath(path1
);
429 if (file
&& !file
->GetGitOldPathString().IsEmpty())
430 path1
= file
->GetGitOldPathString();
432 CString path2
= path1
;
433 GitRevLoglist
* first
= m_arShownList
.SafeGetAt(indexNext
);
434 CTGitPathList list
= first
->GetFiles(nullptr);
435 const CTGitPath
* file
= list
.LookForGitPath(path2
);
436 if (file
&& !file
->GetGitOldPathString().IsEmpty())
437 path2
= file
->GetGitOldPathString();
439 CGitDiff::DiffCommit(CTGitPath(path1
), CTGitPath(path2
), pSelLogEntry
->m_CommitHash
.ToString(), pSelLogEntry
->m_ParentHash
[cmd
- 1].ToString(), bShiftPressed
);
444 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), IDS_PROC_NOPREVIOUSVERSION
, IDS_APPNAME
, MB_OK
);
448 // GitDiff diff(this, m_hWnd, true);
449 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
450 // diff.SetHEADPeg(m_LogRevision);
451 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
454 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
457 case ID_COMPARETWOCOMMITCHANGES
:
459 auto pFirstEntry
= m_arShownList
.SafeGetAt(FirstSelect
);
460 auto pLastEntry
= m_arShownList
.SafeGetAt(LastSelect
);
461 CString patch1
= GetTempFile();
462 CString patch2
= GetTempFile();
464 if (g_Git
.RunLogFile(L
"git.exe format-patch --stdout " + pFirstEntry
->m_CommitHash
.ToString() + L
"~1.." + pFirstEntry
->m_CommitHash
.ToString() + L
"", patch1
, &err
))
466 MessageBox(L
"Could not generate patch for commit " + pFirstEntry
->m_CommitHash
.ToString() + L
".\n" + err
, L
"TortoiseGit", MB_ICONERROR
);
469 if (g_Git
.RunLogFile(L
"git.exe format-patch --stdout " + pLastEntry
->m_CommitHash
.ToString() + L
"~1.." + pLastEntry
->m_CommitHash
.ToString() + L
"", patch2
, &err
))
471 MessageBox(L
"Could not generate patch for commit " + pLastEntry
->m_CommitHash
.ToString() + L
".\n" + err
, L
"TortoiseGit", MB_ICONERROR
);
474 CAppUtils::DiffFlags flags
;
475 CAppUtils::StartExtDiff(patch1
, patch2
, pFirstEntry
->m_CommitHash
.ToString(), pLastEntry
->m_CommitHash
.ToString(), pFirstEntry
->m_CommitHash
.ToString() + L
".patch", pLastEntry
->m_CommitHash
.ToString() + L
".patch", pFirstEntry
->m_CommitHash
.ToString(), pLastEntry
->m_CommitHash
.ToString(), flags
);
478 case ID_LOG_VIEWRANGE
:
479 case ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE
:
481 GitRev
* pLastEntry
= m_arShownList
.SafeGetAt(LastSelect
);
484 if ((cmd
& 0xFFFF) == ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE
)
488 cmdline
.Format(L
"/command:log /path:\"%s\" /range:\"%s%s%s\"",
489 (LPCTSTR
)g_Git
.CombinePath(m_Path
), (LPCTSTR
)pLastEntry
->m_CommitHash
.ToString(), (LPCTSTR
)sep
, (LPCTSTR
)pSelLogEntry
->m_CommitHash
.ToString());
490 CAppUtils::RunTortoiseGitProc(cmdline
);
493 case ID_COPYCLIPBOARD
:
495 CopySelectionToClipBoard();
498 case ID_COPYCLIPBOARDMESSAGES
:
500 if ((GetAsyncKeyState(VK_SHIFT
) & 0x8000) != 0)
501 CopySelectionToClipBoard(ID_COPY_SUBJECT
);
503 CopySelectionToClipBoard(ID_COPY_MESSAGE
);
508 CopySelectionToClipBoard(ID_COPY_HASH
);
513 CString str
=pSelLogEntry
->m_CommitHash
.ToString();
514 // try to get the tag
515 GetFirstEntryStartingWith(m_HashMap
[pSelLogEntry
->m_CommitHash
], L
"refs/tags/", str
);
516 CAppUtils::Export(&str
, &m_Path
);
519 case ID_CREATE_BRANCH
:
522 const CString
* branch
= popmenu
? (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
& 0xFFFF) : nullptr;
523 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
526 else // try to guess remote branch in order to enable tracking
527 GetFirstEntryStartingWith(m_HashMap
[pSelLogEntry
->m_CommitHash
], L
"refs/remotes/", str
);
529 CAppUtils::CreateBranchTag((cmd
&0xFFFF) == ID_CREATE_TAG
, &str
);
532 m_pFindDialog
->RefreshList();
534 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
539 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
540 const CString
* branch
= popmenu
? (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
& 0xFFFF) : nullptr;
543 else // try to guess remote branch in order to recommend good branch name and tracking
544 GetFirstEntryStartingWith(m_HashMap
[pSelLogEntry
->m_CommitHash
], L
"refs/remotes/", str
);
546 CAppUtils::Switch(str
);
550 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
552 case ID_SWITCHBRANCH
:
555 const CString
* branch
= (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
);
558 CString name
= *branch
;
559 CGit::GetShortName(*branch
, name
, L
"refs/heads/");
560 CAppUtils::PerformSwitch(name
);
564 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
569 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
570 if (CAppUtils::GitReset(&str
))
579 SetSelectedRebaseAction(LOGACTIONS_REBASE_PICK
);
582 SetSelectedRebaseAction(LOGACTIONS_REBASE_EDIT
);
584 case ID_REBASE_SQUASH
:
585 SetSelectedRebaseAction(LOGACTIONS_REBASE_SQUASH
);
588 SetSelectedRebaseAction(LOGACTIONS_REBASE_SKIP
);
590 case ID_COMBINE_COMMIT
:
594 CGitHash hashFirst
,hashLast
;
596 int headindex
=GetHeadIndex();
597 if(headindex
>=0) //incase show all branch, head is not the first commits.
599 head
.Format(L
"HEAD~%d", FirstSelect
- headindex
);
600 if (g_Git
.GetHash(hashFirst
, head
))
602 MessageBox(g_Git
.GetGitLastErr(L
"Could not get hash of first selected revision."), L
"TortoiseGit", MB_ICONERROR
);
606 head
.Format(L
"HEAD~%d", LastSelect
- headindex
);
607 if (g_Git
.GetHash(hashLast
, head
))
609 MessageBox(g_Git
.GetGitLastErr(L
"Could not get hash of last selected revision."), L
"TortoiseGit", MB_ICONERROR
);
614 GitRev
* pFirstEntry
= m_arShownList
.SafeGetAt(FirstSelect
);
615 GitRev
* pLastEntry
= m_arShownList
.SafeGetAt(LastSelect
);
616 if(pFirstEntry
->m_CommitHash
!= hashFirst
|| pLastEntry
->m_CommitHash
!= hashLast
)
618 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), IDS_PROC_CANNOTCOMBINE
, IDS_APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
623 if (lastRevision
.GetParentFromHash(hashLast
))
625 MessageBox(lastRevision
.GetLastErr(), L
"TortoiseGit", MB_ICONERROR
);
629 if (g_Git
.GetHash(headhash
, L
"HEAD"))
631 MessageBox(g_Git
.GetGitLastErr(L
"Could not get HEAD hash."), L
"TortoiseGit", MB_ICONERROR
);
635 if(!g_Git
.CheckCleanWorkTree())
637 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), IDS_PROC_NOCLEAN
, IDS_APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
642 //Use throw to abort this process (reset back to original HEAD)
645 sCmd
.Format(L
"git.exe reset --hard %s --", (LPCTSTR
)pFirstEntry
->m_CommitHash
.ToString());
646 if(g_Git
.Run(sCmd
, &out
, CP_UTF8
))
648 MessageBox(out
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
649 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORSTEP1
)) + L
"\r\n\r\n" + out
));
651 sCmd
.Format(L
"git.exe reset --soft %s --", (LPCTSTR
)hashLast
.ToString());
652 if(g_Git
.Run(sCmd
, &out
, CP_UTF8
))
654 MessageBox(out
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
655 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORSTEP2
)) + L
"\r\n\r\n"+out
));
659 for (int i
= FirstSelect
; i
<= LastSelect
; ++i
)
661 GitRev
* pRev
= m_arShownList
.SafeGetAt(i
);
662 dlg
.m_sLogMessage
+= pRev
->GetSubject() + L
'\n' + pRev
->GetBody();
663 dlg
.m_sLogMessage
+= L
'\n';
665 dlg
.m_bWholeProject
=true;
666 dlg
.m_bSelectFilesForCommit
= true;
667 dlg
.m_bForceCommitAmend
=true;
668 int squashDate
= (int)CRegDWORD(L
"Software\\TortoiseGit\\SquashDate", 0);
670 dlg
.SetTime(m_arShownList
.SafeGetAt(FirstSelect
)->GetAuthorDate());
671 else if (squashDate
== 2)
672 dlg
.SetTime(CTime::GetCurrentTime());
674 dlg
.SetTime(m_arShownList
.SafeGetAt(LastSelect
)->GetAuthorDate());
676 gpl
.AddPath(CTGitPath(g_Git
.m_CurrentDir
));
677 dlg
.m_pathList
= gpl
;
678 if (lastRevision
.ParentsCount() != 1)
680 MessageBox(L
"The following commit dialog can only show changes of oldest commit if it has exactly one parent. This is not the case right now.", L
"TortoiseGit", MB_OK
| MB_ICONINFORMATION
);
681 dlg
.m_bAmendDiffToLastCommit
= TRUE
;
684 dlg
.m_bAmendDiffToLastCommit
= FALSE
;
685 dlg
.m_bNoPostActions
=true;
686 dlg
.m_AmendStr
=dlg
.m_sLogMessage
;
688 if (dlg
.DoModal() == IDOK
)
690 if(pFirstEntry
->m_CommitHash
!=headhash
)
692 if(CherryPickFrom(pFirstEntry
->m_CommitHash
.ToString(),headhash
))
695 msg
.Format(L
"Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n");
696 throw std::exception(CUnicodeUtils::GetUTF8(msg
));
701 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_USERCANCELLED
))));
703 catch(std::exception
& e
)
705 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), CUnicodeUtils::GetUnicode(CStringA(e
.what())), L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
706 sCmd
.Format(L
"git.exe reset --hard %s --", (LPCTSTR
)headhash
.ToString());
708 if(g_Git
.Run(sCmd
, &out
, CP_UTF8
))
709 MessageBox(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORRESETHEAD
)) + L
"\r\n\r\n" + out
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
717 if (m_bThreadRunning
)
719 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), IDS_PROC_LOG_ONLYONCE
, IDS_APPNAME
, MB_ICONEXCLAMATION
);
723 dlg
.m_IsCherryPick
= TRUE
;
724 dlg
.m_Upstream
= this->m_CurrentBranch
;
725 POSITION pos2
= GetFirstSelectedItemPosition();
728 int indexNext2
= GetNextSelectedItem(pos2
);
729 dlg
.m_CommitList
.m_logEntries
.push_back(m_arShownList
.SafeGetAt(indexNext2
)->m_CommitHash
);
730 dlg
.m_CommitList
.m_LogCache
.m_HashMap
[m_arShownList
.SafeGetAt(indexNext2
)->m_CommitHash
] = *m_arShownList
.SafeGetAt(indexNext2
);
731 dlg
.m_CommitList
.m_logEntries
.GetGitRevAt(dlg
.m_CommitList
.m_logEntries
.size() - 1).GetRebaseAction() |= LOGACTIONS_REBASE_PICK
;
734 if(dlg
.DoModal() == IDOK
)
740 case ID_REBASE_TO_VERSION
:
742 if (m_bThreadRunning
)
744 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), IDS_PROC_LOG_ONLYONCE
, IDS_APPNAME
, MB_ICONEXCLAMATION
);
748 auto refList
= m_HashMap
[pSelLogEntry
->m_CommitHash
];
749 dlg
.m_Upstream
= refList
.empty() ? pSelLogEntry
->m_CommitHash
.ToString() : refList
.front();
750 for (const auto& ref
: refList
)
751 if (CGit::GetShortName(ref
, dlg
.m_Upstream
, L
"refs/heads/"))
754 if(dlg
.DoModal() == IDOK
)
763 if (CAppUtils::StashSave())
768 if (CAppUtils::StashPop())
773 CAppUtils::RunTortoiseGitProc(L
"/command:reflog /ref:refs/stash");
776 case ID_REFLOG_STASH_APPLY
:
777 CAppUtils::StashApply(pSelLogEntry
->m_Ref
);
783 if (GetSelectedCount() > 1)
784 str
.Format(IDS_PROC_DELETENREFS
, GetSelectedCount());
786 str
.Format(IDS_PROC_DELETEREF
, (LPCTSTR
)pSelLogEntry
->m_Ref
);
788 if (CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), str
, L
"TortoiseGit", 1, IDI_QUESTION
, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON
)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
))) == 2)
791 std::vector
<CString
> refsToDelete
;
792 POSITION pos2
= GetFirstSelectedItemPosition();
795 CString ref
= m_arShownList
.SafeGetAt(GetNextSelectedItem(pos2
))->m_Ref
;
796 if (CStringUtils::StartsWith(ref
, L
"refs/"))
798 int refpos
= ref
.ReverseFind(L
'{');
799 if (refpos
> 0 && ref
.Mid(refpos
- 1, 2) != L
"@{")
800 ref
= ref
.Left(refpos
) + L
'@'+ ref
.Mid(refpos
);
801 refsToDelete
.push_back(ref
);
804 for (auto revIt
= refsToDelete
.crbegin(); revIt
!= refsToDelete
.crend(); ++revIt
)
806 CString ref
= *revIt
;
808 if (CStringUtils::StartsWith(ref
, L
"stash"))
809 sCmd
.Format(L
"git.exe stash drop %s", (LPCTSTR
)ref
);
811 sCmd
.Format(L
"git.exe reflog delete %s", (LPCTSTR
)ref
);
813 if (g_Git
.Run(sCmd
, &out
, CP_UTF8
))
814 MessageBox(out
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
816 ::PostMessage(this->GetParent()->m_hWnd
,MSG_REFLOG_CHANGED
,0,0);
822 CString sCmd
= L
"/command:log";
823 sCmd
+= L
" /path:\"" + g_Git
.m_CurrentDir
+ L
"\" ";
824 GitRev
* r1
= m_arShownList
.SafeGetAt(FirstSelect
);
825 sCmd
+= L
" /endrev:" + r1
->m_CommitHash
.ToString();
826 CAppUtils::RunTortoiseGitProc(sCmd
);
829 case ID_CREATE_PATCH
:
831 int select
=this->GetSelectedCount();
832 CString sCmd
= L
"/command:formatpatch";
833 sCmd
+= L
" /path:\"" + g_Git
.m_CurrentDir
+ L
"\" ";
835 GitRev
* r1
= m_arShownList
.SafeGetAt(FirstSelect
);
836 GitRev
* r2
= nullptr;
839 sCmd
+= L
" /startrev:" + r1
->m_CommitHash
.ToString();
843 r2
= m_arShownList
.SafeGetAt(LastSelect
);
844 if( this->m_IsOldFirst
)
846 sCmd
+= L
" /startrev:" + r1
->m_CommitHash
.ToString() + L
"~1";
847 sCmd
+= L
" /endrev:" + r2
->m_CommitHash
.ToString();
852 sCmd
+= L
" /startrev:" + r2
->m_CommitHash
.ToString() + L
"~1";
853 sCmd
+= L
" /endrev:" + r1
->m_CommitHash
.ToString();
858 CAppUtils::RunTortoiseGitProc(sCmd
);
863 GitRev
* first
= m_arShownList
.SafeGetAt(FirstSelect
);
864 GitRev
* last
= m_arShownList
.SafeGetAt(LastSelect
);
865 ASSERT(first
&& last
);
867 CString firstBad
= first
->m_CommitHash
.ToString();
868 if (!m_HashMap
[first
->m_CommitHash
].empty())
869 firstBad
= m_HashMap
[first
->m_CommitHash
].at(0);
870 CString lastGood
= last
->m_CommitHash
.ToString();
871 if (!m_HashMap
[last
->m_CommitHash
].empty())
872 lastGood
= m_HashMap
[last
->m_CommitHash
].at(0);
874 if (CAppUtils::BisectStart(lastGood
, firstBad
))
880 GitRev
* first
= m_arShownList
.SafeGetAt(FirstSelect
);
881 if (CAppUtils::BisectOperation(L
"good", !first
->m_CommitHash
.IsEmpty() ? first
->m_CommitHash
.ToString() : L
""))
887 GitRev
* first
= m_arShownList
.SafeGetAt(FirstSelect
);
888 if (CAppUtils::BisectOperation(L
"bad", !first
->m_CommitHash
.IsEmpty() ? first
->m_CommitHash
.ToString() : L
""))
895 POSITION pos2
= GetFirstSelectedItemPosition();
898 int indexNext2
= GetNextSelectedItem(pos2
);
899 auto rev
= m_arShownList
.SafeGetAt(indexNext2
);
900 if (!rev
->m_CommitHash
.IsEmpty())
901 refs
.AppendFormat(L
" %s", (LPCTSTR
)rev
->m_CommitHash
.ToString());
903 if (CAppUtils::BisectOperation(L
"skip", refs
))
909 if (CAppUtils::BisectOperation(L
"reset"))
916 sCmd
.Format(L
"/command:repobrowser /path:\"%s\" /rev:%s", (LPCTSTR
)g_Git
.m_CurrentDir
, (LPCTSTR
)pSelLogEntry
->m_CommitHash
.ToString());
917 CAppUtils::RunTortoiseGitProc(sCmd
);
922 CString guessAssociatedBranch
= pSelLogEntry
->m_CommitHash
;
923 const CString
* branch
= popmenu
? (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
) : nullptr;
925 guessAssociatedBranch
= *branch
;
927 GetFirstEntryStartingWith(m_HashMap
[pSelLogEntry
->m_CommitHash
], L
"refs/heads/", guessAssociatedBranch
);
929 if (CAppUtils::Push(guessAssociatedBranch
))
935 if (CAppUtils::Pull())
941 if (CAppUtils::Fetch())
947 if (CAppUtils::SVNDCommit())
954 sCmd
.Format(L
"/command:cleanup /path:\"%s\"", (LPCTSTR
)g_Git
.m_CurrentDir
);
955 CAppUtils::RunTortoiseGitProc(sCmd
);
958 case ID_SUBMODULE_UPDATE
:
961 sCmd
.Format(L
"/command:subupdate /bkpath:\"%s\"", (LPCTSTR
)g_Git
.m_CurrentDir
);
962 CAppUtils::RunTortoiseGitProc(sCmd
);
965 case ID_SHOWBRANCHES
:
967 CCommitIsOnRefsDlg
* dlg
= new CCommitIsOnRefsDlg(this);
968 dlg
->m_Rev
= (LPCTSTR
)pSelLogEntry
->m_CommitHash
.ToString();
970 // pointer won't leak as it is destroyed within PostNcDestroy()
975 const CString
* branch
= popmenu
? (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
) : nullptr;
978 CMessageBox::Show(GetSafeOwner()->GetSafeHwnd(), IDS_ERROR_NOREF
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
982 if (branch
== (CString
*)MAKEINTRESOURCE(IDS_ALL
))
984 CString currentBranch
= L
"refs/heads/" + m_CurrentBranch
;
985 bool nothingDeleted
= true;
986 for (const auto& ref
: m_HashMap
[pSelLogEntry
->m_CommitHash
])
988 if (ref
== currentBranch
)
992 nothingDeleted
= false;
997 else if (!DeleteRef(*branch
))
999 this->ReloadHashMap();
1001 m_pFindDialog
->RefreshList();
1003 this->GetItemRect(FirstSelect
,&rect
,LVIR_BOUNDS
);
1004 this->InvalidateRect(rect
);
1010 m_nSearchIndex
= GetSelectionMark();
1011 if (m_nSearchIndex
< 0)
1017 m_pFindDialog
= new CFindDlg();
1018 m_pFindDialog
->Create(this);
1024 CString str
= pSelLogEntry
->m_CommitHash
.ToString();
1025 const CString
* branch
= popmenu
? (const CString
*)((CIconMenu
*)popmenu
)->GetMenuItemData(cmd
& 0xFFFF) : nullptr;
1028 else if (!m_HashMap
[pSelLogEntry
->m_CommitHash
].empty())
1029 str
= m_HashMap
[pSelLogEntry
->m_CommitHash
].at(0);
1030 // we need an URL to complete this command, so error out if we can't get an URL
1031 if(CAppUtils::Merge(&str
))
1040 if (GetSelectedCount() == 1)
1043 if ((size_t)parent
> pSelLogEntry
->m_ParentHash
.size())
1046 str
.Format(IDS_PROC_NOPARENT
, parent
);
1047 MessageBox(str
, L
"TortoiseGit", MB_OK
| MB_ICONERROR
);
1052 if (!this->RevertSelectedCommits(parent
))
1054 if (CMessageBox::Show(m_hWnd
, IDS_REVREVERTED
, IDS_APPNAME
, 1, IDI_QUESTION
, IDS_OKBUTTON
, IDS_COMMITBUTTON
) == 2)
1056 CTGitPathList pathlist
;
1057 CTGitPathList selectedlist
;
1058 pathlist
.AddPath(this->m_Path
);
1059 bool bSelectFilesForCommit
= !!DWORD(CRegStdDWORD(L
"Software\\TortoiseGit\\SelectFilesForCommit", TRUE
));
1061 CAppUtils::Commit(CString(), false, str
, pathlist
, selectedlist
, bSelectFilesForCommit
);
1069 CAppUtils::EditNote(pSelLogEntry
, &m_ProjectProperties
);
1070 this->SetItemState(FirstSelect
, 0, LVIS_SELECTED
);
1071 this->SetItemState(FirstSelect
, LVIS_SELECTED
, LVIS_SELECTED
);
1075 //CMessageBox::Show(nullptr, L"Have not implemented", L"TortoiseGit", MB_OK);
1079 case ID_BLAMECOMPARE
:
1081 //user clicked on the menu item "compare with working copy"
1082 //now first get the revision which is selected
1085 GitDiff
diff(this, this->m_hWnd
, true);
1086 diff
.SetHEADPeg(m_LogRevision
);
1087 diff
.ShowCompare(m_path
, GitRev::REV_BASE
, m_path
, revSelected
, GitRev(), false, true);
1090 CAppUtils::StartShowCompare(m_hWnd
, m_path
, GitRev::REV_BASE
, m_path
, revSelected
, GitRev(), m_LogRevision
, false, false, true);
1093 case ID_BLAMEWITHPREVIOUS
:
1095 //user clicked on the menu item "Compare and Blame with previous revision"
1098 GitDiff
diff(this, this->m_hWnd
, true);
1099 diff
.SetHEADPeg(m_LogRevision
);
1100 diff
.ShowCompare(CTGitPath(pathURL
), revPrevious
, CTGitPath(pathURL
), revSelected
, GitRev(), false, true);
1103 CAppUtils::StartShowCompare(m_hWnd
, CTGitPath(pathURL
), revPrevious
, CTGitPath(pathURL
), revSelected
, GitRev(), m_LogRevision
, false, false, true);
1111 CProgressDlg progDlg
;
1112 progDlg
.SetTitle(IDS_APPNAME
);
1113 progDlg
.SetAnimation(IDR_DOWNLOAD
);
1115 sInfoLine
.Format(IDS_PROGRESSGETFILEREVISION
, m_path
.GetWinPath(), (LPCTSTR
)revSelected
.ToString());
1116 progDlg
.SetLine(1, sInfoLine
, true);
1117 SetAndClearProgressInfo(&progDlg
);
1118 progDlg
.ShowModeless(m_hWnd
);
1119 CTGitPath tempfile
= CTempFiles::Instance().GetTempFilePath(false, m_path
, revSelected
);
1120 bool bSuccess
= true;
1121 if (!Cat(m_path
, GitRev(GitRev::REV_HEAD
), revSelected
, tempfile
))
1124 // try again, but with the selected revision as the peg revision
1125 if (!Cat(m_path
, revSelected
, revSelected
, tempfile
))
1128 SetAndClearProgressInfo(nullptr);
1129 CMessageBox::Show(GetSafeHwnd(), GetLastErrorMessage(), L
"TortoiseGit", MB_ICONERROR
);
1138 SetAndClearProgressInfo(nullptr);
1139 SetFileAttributes(tempfile
.GetWinPath(), FILE_ATTRIBUTE_READONLY
);
1141 CAppUtils::ShellOpen(tempfile
.GetWinPath(), GetSafeHwnd());
1143 CAppUtils::ShowOpenWithDialog(tempfile
.GetWinPathString(), GetSafeHwnd());
1150 dlg
.EndRev
= revSelected
;
1151 if (dlg
.DoModal() == IDOK
)
1156 tempfile
= blame
.BlameToTempFile(m_path
, dlg
.StartRev
, dlg
.EndRev
, dlg
.EndRev
, logfile
, L
"", dlg
.m_bIncludeMerge
, TRUE
, TRUE
);
1157 if (!tempfile
.IsEmpty())
1159 if (dlg
.m_bTextView
)
1161 //open the default text editor for the result file
1162 CAppUtils::StartTextViewer(tempfile
);
1166 CString sParams
= L
"/path:\"" + m_path
.GetGitPathString() + L
"\" ";
1167 if(!CAppUtils::LaunchTortoiseBlame(tempfile
, logfile
, CPathUtils::GetFileNameFromPath(m_path
.GetFileOrDirectoryName()),sParams
))
1175 CMessageBox::Show(GetSafeHwnd(), blame
.GetLastErrorMessage(), L
"TortoiseGit", MB_ICONERROR
);
1183 sCmd
.Format(L
"%s /command:export /path:\"%s\" /revision:%ld",
1184 (LPCTSTR
)(CPathUtils::GetAppDirectory() + L
"TortoiseGitProc.exe"),
1185 (LPCTSTR
)pathURL
, (LONG
)revSelected
);
1186 CAppUtils::LaunchApplication(sCmd
, nullptr, false);
1191 CString url
= m_ProjectProperties
.sWebViewerRev
;
1192 url
= GetAbsoluteUrlFromRelativeUrl(url
);
1193 url
.Replace(L
"%REVISION%", revSelected
.ToString());
1195 ShellExecute(GetSafeHwnd(), L
"open", url
, nullptr, nullptr, SW_SHOWDEFAULT
);
1198 case ID_VIEWPATHREV
:
1200 CString relurl
= pathURL
;
1201 CString sRoot
= GetRepositoryRoot(CTGitPath(relurl
));
1202 relurl
= relurl
.Mid(sRoot
.GetLength());
1203 CString url
= m_ProjectProperties
.sWebViewerPathRev
;
1204 url
= GetAbsoluteUrlFromRelativeUrl(url
);
1205 url
.Replace(L
"%REVISION%", revSelected
.ToString());
1206 url
.Replace(L
"%PATH%", relurl
);
1208 ShellExecute(GetSafeHwnd(), L
"open", url
, nullptr, nullptr, SW_SHOWDEFAULT
);
1215 theApp
.DoWaitCursor(-1);
1218 void CGitLogList::SetSelectedRebaseAction(int action
)
1220 POSITION pos
= GetFirstSelectedItemPosition();
1225 index
= GetNextSelectedItem(pos
);
1226 if (m_arShownList
.SafeGetAt(index
)->GetRebaseAction() & (LOGACTIONS_REBASE_CURRENT
| LOGACTIONS_REBASE_DONE
) || (index
== GetItemCount() - 1 && action
== LOGACTIONS_REBASE_SQUASH
))
1228 if (!m_bIsCherryPick
&& m_arShownList
.SafeGetAt(index
)->ParentsCount() > 1 && action
== LOGACTIONS_REBASE_SQUASH
)
1230 m_arShownList
.SafeGetAt(index
)->GetRebaseAction() = action
;
1232 this->GetItemRect(index
,&rect
,LVIR_BOUNDS
);
1233 this->InvalidateRect(rect
);
1236 GetParent()->PostMessage(CGitLogListBase::m_RebaseActionMessage
);
1239 void CGitLogList::SetUnselectedRebaseAction(int action
)
1241 POSITION pos
= GetFirstSelectedItemPosition();
1242 int index
= pos
? GetNextSelectedItem(pos
) : -1;
1243 for (int i
= 0; i
< GetItemCount(); i
++)
1247 index
= pos
? GetNextSelectedItem(pos
) : -1;
1251 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))
1253 m_arShownList
.SafeGetAt(i
)->GetRebaseAction() = action
;
1255 this->GetItemRect(i
, &rect
, LVIR_BOUNDS
);
1256 this->InvalidateRect(rect
);
1259 GetParent()->PostMessage(CGitLogListBase::m_RebaseActionMessage
);
1262 void CGitLogList::ShiftSelectedRebaseAction()
1264 POSITION pos
= GetFirstSelectedItemPosition();
1268 index
= GetNextSelectedItem(pos
);
1269 int* action
= &(m_arShownList
.SafeGetAt(index
))->GetRebaseAction();
1272 case LOGACTIONS_REBASE_PICK
:
1273 *action
= LOGACTIONS_REBASE_SKIP
;
1275 case LOGACTIONS_REBASE_SKIP
:
1276 *action
= LOGACTIONS_REBASE_EDIT
;
1278 case LOGACTIONS_REBASE_EDIT
:
1279 *action
= LOGACTIONS_REBASE_SQUASH
;
1280 if (index
== GetItemCount() - 1 && (m_bIsCherryPick
|| m_arShownList
.SafeGetAt(index
)->m_ParentHash
.size() == 1))
1281 *action
= LOGACTIONS_REBASE_PICK
;
1283 case LOGACTIONS_REBASE_SQUASH
:
1284 *action
= LOGACTIONS_REBASE_PICK
;
1288 this->GetItemRect(index
, &rect
, LVIR_BOUNDS
);
1289 this->InvalidateRect(rect
);