Fixed issue #2469: Support reviewing merge commits (git show merge-commit)
[TortoiseGit.git] / src / TortoiseProc / GitLogListAction.cpp
blob7f5049a9f684ffca459d930f823f5fe13fa0ef88
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2015 - 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
22 #include "stdafx.h"
23 #include "TortoiseProc.h"
24 #include "GitLogList.h"
25 #include "GitRev.h"
26 #include "IconMenu.h"
27 #include "cursor.h"
28 #include "GitProgressDlg.h"
29 #include "ProgressDlg.h"
30 #include "SysProgressDlg.h"
31 #include "LogDlg.h"
32 #include "MessageBox.h"
33 #include "registry.h"
34 #include "AppUtils.h"
35 #include "StringUtils.h"
36 #include "UnicodeUtils.h"
37 #include "TempFile.h"
38 #include "FileDiffDlg.h"
39 #include "CommitDlg.h"
40 #include "RebaseDlg.h"
41 #include "GitDiff.h"
42 #include "../TGitCache/CacheInterface.h"
44 IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl)
46 int CGitLogList::RevertSelectedCommits(int parent)
48 CSysProgressDlg progress;
49 int ret = -1;
51 #if 0
52 if(!g_Git.CheckCleanWorkTree())
54 CMessageBox::Show(NULL, IDS_PROC_NOCLEAN, IDS_APPNAME, MB_OK);
57 #endif
59 if (this->GetSelectedCount() > 1)
61 progress.SetTitle(CString(MAKEINTRESOURCE(IDS_PROGS_TITLE_REVERTCOMMIT)));
62 progress.SetAnimation(IDR_MOVEANI);
63 progress.SetTime(true);
64 progress.ShowModeless(this);
67 CBlockCacheForPath cacheBlock(g_Git.m_CurrentDir);
69 POSITION pos = GetFirstSelectedItemPosition();
70 int i=0;
71 while(pos)
73 int index = GetNextSelectedItem(pos);
74 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(index));
76 if (progress.IsVisible())
78 progress.FormatNonPathLine(1, IDS_PROC_REVERTCOMMIT, r1->m_CommitHash.ToString());
79 progress.FormatNonPathLine(2, _T("%s"), r1->GetSubject());
80 progress.SetProgress(i, this->GetSelectedCount());
82 ++i;
84 if(r1->m_CommitHash.IsEmpty())
85 continue;
87 if (g_Git.GitRevert(parent, r1->m_CommitHash))
89 CString str;
90 str.LoadString(IDS_SVNACTION_FAILEDREVERT);
91 str = g_Git.GetGitLastErr(str, CGit::GIT_CMD_REVERT);
92 if( GetSelectedCount() == 1)
93 CMessageBox::Show(NULL, str, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
94 else
96 if(CMessageBox::Show(NULL, str, _T("TortoiseGit"),2 , IDI_ERROR, CString(MAKEINTRESOURCE(IDS_SKIPBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
98 return ret;
102 else
104 ret =0;
107 if (progress.HasUserCancelled())
108 break;
110 return ret;
112 int CGitLogList::CherryPickFrom(CString from, CString to)
114 CLogDataVector logs(&m_LogCache);
115 CString range;
116 range.Format(_T("%s..%s"), from, to);
117 if (logs.ParserFromLog(nullptr, -1, 0, &range))
118 return -1;
120 if (logs.empty())
121 return 0;
123 CSysProgressDlg progress;
124 progress.SetTitle(CString(MAKEINTRESOURCE(IDS_PROGS_TITLE_CHERRYPICK)));
125 progress.SetAnimation(IDR_MOVEANI);
126 progress.SetTime(true);
127 progress.ShowModeless(this);
129 CBlockCacheForPath cacheBlock(g_Git.m_CurrentDir);
131 for (int i = (int)logs.size() - 1; i >= 0; i--)
133 if (progress.IsVisible())
135 progress.FormatNonPathLine(1, IDS_PROC_PICK, logs.GetGitRevAt(i).m_CommitHash.ToString());
136 progress.FormatNonPathLine(2, _T("%s"), logs.GetGitRevAt(i).GetSubject());
137 progress.SetProgress64(logs.size() - i, logs.size());
139 if (progress.HasUserCancelled())
141 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_SVN_USERCANCELLED))));
143 CString cmd,out;
144 cmd.Format(_T("git.exe cherry-pick %s"),logs.GetGitRevAt(i).m_CommitHash.ToString());
145 out.Empty();
146 if(g_Git.Run(cmd,&out,CP_UTF8))
148 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_CHERRYPICKFAILED)) + _T(":\r\n\r\n") + out));
152 return 0;
155 void CGitLogList::ContextMenuAction(int cmd,int FirstSelect, int LastSelect, CMenu *popmenu)
157 POSITION pos = GetFirstSelectedItemPosition();
158 int indexNext = GetNextSelectedItem(pos);
159 if (indexNext < 0)
160 return;
162 GitRevLoglist* pSelLogEntry = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(indexNext));
164 theApp.DoWaitCursor(1);
165 switch (cmd&0xFFFF)
167 case ID_COMMIT:
169 CTGitPathList pathlist;
170 CTGitPathList selectedlist;
171 pathlist.AddPath(this->m_Path);
172 bool bSelectFilesForCommit = !!DWORD(CRegStdDWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE));
173 CString str;
174 CAppUtils::Commit(CString(),false,str,
175 pathlist,selectedlist,bSelectFilesForCommit);
176 //this->Refresh();
177 this->GetParent()->PostMessage(WM_COMMAND,ID_LOGDLG_REFRESH,0);
179 break;
180 case ID_MERGE_ABORT:
182 if (CAppUtils::MergeAbort())
183 this->GetParent()->PostMessage(WM_COMMAND,ID_LOGDLG_REFRESH, 0);
185 break;
186 case ID_GNUDIFF1: // compare with WC, unified
188 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
189 bool bMerge = false, bCombine = false;
190 CString hash2;
191 if(!r1->m_CommitHash.IsEmpty())
193 CString merge;
194 cmd >>= 16;
195 if( (cmd&0xFFFF) == 0xFFFF)
196 bMerge = true;
197 else if((cmd&0xFFFF) == 0xFFFE)
198 bCombine = true;
199 else if ((cmd & 0xFFFF) == 0xFFFD)
201 CString tempfile = GetTempFile();
202 CString cmd = _T("git.exe diff-tree --cc ") + r1->m_CommitHash.ToString();
203 CString lastErr;
204 if (g_Git.RunLogFile(cmd, tempfile, &lastErr))
206 MessageBox(lastErr, _T("TortoiseGit"), MB_ICONERROR);
207 break;
210 CAppUtils::StartUnifiedDiffViewer(tempfile, _T("dd"));
211 break;
213 else
215 if(cmd > r1->m_ParentHash.size())
217 CString str;
218 str.Format(IDS_PROC_NOPARENT, cmd);
219 MessageBox(str, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
220 return;
222 else
224 if(cmd>0)
225 hash2 = r1->m_ParentHash[cmd-1].ToString();
228 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), hash2, CTGitPath(), r1->m_CommitHash.ToString(), false, false, false, bMerge, bCombine);
230 else
231 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), _T("HEAD"), CTGitPath(), GitRev::GetWorkingCopy(), false, false, false, bMerge, bCombine);
233 break;
235 case ID_GNUDIFF2: // compare two revisions, unified
237 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
238 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
239 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), r2->m_CommitHash.ToString(), CTGitPath(), r1->m_CommitHash.ToString());
241 break;
243 case ID_COMPARETWO: // compare two revisions
245 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
246 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
247 if (m_Path.IsDirectory() || !(m_ShowMask & CGit::LOG_INFO_FOLLOW))
248 CGitDiff::DiffCommit(this->m_Path, r1,r2);
249 else
251 CString path1 = m_Path.GetGitPathString();
252 // start with 1 (0 = working copy changes)
253 for (int i = 1; i < FirstSelect; ++i)
255 GitRevLoglist* first = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(i));
256 CTGitPathList list = first->GetFiles(NULL);
257 CTGitPath * file = list.LookForGitPath(path1);
258 if (file && !file->GetGitOldPathString().IsEmpty())
259 path1 = file->GetGitOldPathString();
261 CString path2 = path1;
262 for (int i = FirstSelect; i < LastSelect; ++i)
264 GitRevLoglist* first = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(i));
265 CTGitPathList list = first->GetFiles(NULL);
266 CTGitPath * file = list.LookForGitPath(path2);
267 if (file && !file->GetGitOldPathString().IsEmpty())
268 path2 = file->GetGitOldPathString();
270 CGitDiff::DiffCommit(CTGitPath(path1), CTGitPath(path2), r1, r2);
274 break;
276 case ID_COMPARE: // compare revision with WC
278 GitRevLoglist* r1 = &m_wcRev;
279 GitRevLoglist* r2 = pSelLogEntry;
281 if (m_Path.IsDirectory() || !(m_ShowMask & CGit::LOG_INFO_FOLLOW))
282 CGitDiff::DiffCommit(this->m_Path, r1,r2);
283 else
285 CString path1 = m_Path.GetGitPathString();
286 // start with 1 (0 = working copy changes)
287 for (int i = 1; i < FirstSelect; ++i)
289 GitRevLoglist* first = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(i));
290 CTGitPathList list = first->GetFiles(NULL);
291 CTGitPath * file = list.LookForGitPath(path1);
292 if (file && !file->GetGitOldPathString().IsEmpty())
293 path1 = file->GetGitOldPathString();
295 CGitDiff::DiffCommit(m_Path, CTGitPath(path1), r1, r2);
298 //user clicked on the menu item "compare with working copy"
299 //if (PromptShown())
301 // GitDiff diff(this, m_hWnd, true);
302 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
303 // diff.SetHEADPeg(m_LogRevision);
304 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
306 //else
307 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
309 break;
311 case ID_COMPAREWITHPREVIOUS:
313 CFileDiffDlg dlg;
315 if (pSelLogEntry->m_ParentHash.empty())
317 if (pSelLogEntry->GetParentFromHash(pSelLogEntry->m_CommitHash))
318 MessageBox(pSelLogEntry->GetLastErr(), _T("TortoiseGit"), MB_ICONERROR);
321 if (!pSelLogEntry->m_ParentHash.empty())
322 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
324 cmd>>=16;
325 cmd&=0xFFFF;
327 if(cmd == 0)
328 cmd=1;
330 if (m_Path.IsDirectory() || !(m_ShowMask & CGit::LOG_INFO_FOLLOW))
331 CGitDiff::DiffCommit(m_Path, pSelLogEntry->m_CommitHash.ToString(), pSelLogEntry->m_ParentHash[cmd - 1].ToString());
332 else
334 CString path1 = m_Path.GetGitPathString();
335 // start with 1 (0 = working copy changes)
336 for (int i = 1; i < indexNext; ++i)
338 GitRevLoglist* first = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(i));
339 CTGitPathList list = first->GetFiles(NULL);
340 CTGitPath * file = list.LookForGitPath(path1);
341 if (file && !file->GetGitOldPathString().IsEmpty())
342 path1 = file->GetGitOldPathString();
344 CString path2 = path1;
345 GitRevLoglist* first = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(indexNext));
346 CTGitPathList list = first->GetFiles(NULL);
347 CTGitPath * file = list.LookForGitPath(path2);
348 if (file && !file->GetGitOldPathString().IsEmpty())
349 path2 = file->GetGitOldPathString();
351 CGitDiff::DiffCommit(CTGitPath(path1), CTGitPath(path2), pSelLogEntry->m_CommitHash.ToString(), pSelLogEntry->m_ParentHash[cmd - 1].ToString());
354 else
356 CMessageBox::Show(NULL, IDS_PROC_NOPREVIOUSVERSION, IDS_APPNAME, MB_OK);
358 //if (PromptShown())
360 // GitDiff diff(this, m_hWnd, true);
361 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
362 // diff.SetHEADPeg(m_LogRevision);
363 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
365 //else
366 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
368 break;
369 case ID_LOG_VIEWRANGE:
370 case ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE:
372 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.SafeGetAt(LastSelect));
374 CString sep = _T("..");
375 if ((cmd & 0xFFFF) == ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE)
376 sep = _T("...");
378 CString cmdline;
379 cmdline.Format(_T("/command:log /path:\"%s\" /range:\"%s%s%s\""),
380 g_Git.CombinePath(m_Path), pLastEntry->m_CommitHash.ToString(), sep, pSelLogEntry->m_CommitHash.ToString());
381 CAppUtils::RunTortoiseGitProc(cmdline);
383 break;
384 case ID_COPYCLIPBOARD:
386 CopySelectionToClipBoard();
388 break;
389 case ID_COPYCLIPBOARDMESSAGES:
391 if ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0)
392 CopySelectionToClipBoard(ID_COPY_SUBJECT);
393 else
394 CopySelectionToClipBoard(ID_COPY_MESSAGE);
396 break;
397 case ID_COPYHASH:
399 CopySelectionToClipBoard(ID_COPY_HASH);
401 break;
402 case ID_EXPORT:
404 CString str=pSelLogEntry->m_CommitHash.ToString();
405 // try to get the tag
406 for (size_t i = 0; i < m_HashMap[pSelLogEntry->m_CommitHash].size(); ++i)
408 if (m_HashMap[pSelLogEntry->m_CommitHash][i].Find(_T("refs/tags/")) == 0)
410 str = m_HashMap[pSelLogEntry->m_CommitHash][i];
411 break;
414 CAppUtils::Export(&str, &m_Path);
416 break;
417 case ID_CREATE_BRANCH:
418 case ID_CREATE_TAG:
420 CString str = pSelLogEntry->m_CommitHash.ToString();
421 // try to guess remote branch in order to enable tracking
422 for (size_t i = 0; i < m_HashMap[pSelLogEntry->m_CommitHash].size(); ++i)
424 if (m_HashMap[pSelLogEntry->m_CommitHash][i].Find(_T("refs/remotes/")) == 0)
426 str = m_HashMap[pSelLogEntry->m_CommitHash][i];
427 break;
430 CAppUtils::CreateBranchTag((cmd&0xFFFF) == ID_CREATE_TAG, &str);
431 ReloadHashMap();
432 if (m_pFindDialog)
433 m_pFindDialog->RefreshList();
434 Invalidate();
435 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
437 break;
438 case ID_SWITCHTOREV:
440 CString str = pSelLogEntry->m_CommitHash.ToString();
441 // try to guess remote branch in order to recommend good branch name and tracking
442 for (size_t i = 0; i < m_HashMap[pSelLogEntry->m_CommitHash].size(); ++i)
444 if (m_HashMap[pSelLogEntry->m_CommitHash][i].Find(_T("refs/remotes/")) == 0)
446 str = m_HashMap[pSelLogEntry->m_CommitHash][i];
447 break;
450 CAppUtils::Switch(str);
452 ReloadHashMap();
453 Invalidate();
454 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
455 break;
456 case ID_SWITCHBRANCH:
457 if(popmenu)
459 CString *branch = (CString*)((CIconMenu*)popmenu)->GetMenuItemData(cmd);
460 if(branch)
462 CString name;
463 if(branch->Find(_T("refs/heads/")) ==0 )
464 name = branch->Mid(11);
465 else
466 name = *branch;
468 CAppUtils::PerformSwitch(name);
470 ReloadHashMap();
471 Invalidate();
472 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
474 break;
475 case ID_RESET:
477 CString str = pSelLogEntry->m_CommitHash.ToString();
478 if (CAppUtils::GitReset(&str))
480 ResetWcRev(true);
481 ReloadHashMap();
482 Invalidate();
485 break;
486 case ID_REBASE_PICK:
487 SetSelectedRebaseAction(LOGACTIONS_REBASE_PICK);
488 break;
489 case ID_REBASE_EDIT:
490 SetSelectedRebaseAction(LOGACTIONS_REBASE_EDIT);
491 break;
492 case ID_REBASE_SQUASH:
493 SetSelectedRebaseAction(LOGACTIONS_REBASE_SQUASH);
494 break;
495 case ID_REBASE_SKIP:
496 SetSelectedRebaseAction(LOGACTIONS_REBASE_SKIP);
497 break;
498 case ID_COMBINE_COMMIT:
500 CString head;
501 CGitHash headhash;
502 CGitHash hashFirst,hashLast;
504 int headindex=GetHeadIndex();
505 if(headindex>=0) //incase show all branch, head is not the first commits.
507 head.Format(_T("HEAD~%d"),FirstSelect-headindex);
508 if (g_Git.GetHash(hashFirst, head))
510 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of first selected revision.")), _T("TortoiseGit"), MB_ICONERROR);
511 break;
514 head.Format(_T("HEAD~%d"),LastSelect-headindex);
515 if (g_Git.GetHash(hashLast, head))
517 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of last selected revision.")), _T("TortoiseGit"), MB_ICONERROR);
518 break;
522 GitRev* pFirstEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
523 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
524 if(pFirstEntry->m_CommitHash != hashFirst || pLastEntry->m_CommitHash != hashLast)
526 CMessageBox::Show(NULL, IDS_PROC_CANNOTCOMBINE, IDS_APPNAME, MB_OK);
527 break;
530 GitRev lastRevision;
531 if (lastRevision.GetParentFromHash(hashLast))
533 MessageBox(lastRevision.GetLastErr(), _T("TortoiseGit"), MB_ICONERROR);
534 break;
537 if (g_Git.GetHash(headhash, _T("HEAD")))
539 MessageBox(g_Git.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR);
540 break;
543 if(!g_Git.CheckCleanWorkTree())
545 CMessageBox::Show(NULL, IDS_PROC_NOCLEAN, IDS_APPNAME, MB_OK);
546 break;
548 CString sCmd, out;
550 //Use throw to abort this process (reset back to original HEAD)
553 sCmd.Format(_T("git.exe reset --hard %s --"), pFirstEntry->m_CommitHash.ToString());
554 if(g_Git.Run(sCmd, &out, CP_UTF8))
556 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
557 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORSTEP1)) + _T("\r\n\r\n") + out));
559 sCmd.Format(_T("git.exe reset --mixed %s --"), hashLast.ToString());
560 if(g_Git.Run(sCmd, &out, CP_UTF8))
562 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
563 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORSTEP2)) + _T("\r\n\r\n")+out));
566 CTGitPathList PathList;
567 /* don't why must add --stat to get action status*/
568 /* first -z will be omitted by gitdll*/
569 if(g_Git.GetDiffPath(&PathList,&pFirstEntry->m_CommitHash,&hashLast,"-z --stat -r"))
571 CMessageBox::Show(NULL,_T("Get Diff file list error"),_T("TortoiseGit"),MB_OK);
572 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not get changed file list aborting...\r\n\r\n")+out));
575 for (int i = 0; i < PathList.GetCount(); ++i)
577 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_ADDED)
579 sCmd.Format(_T("git.exe add -- \"%s\""), PathList[i].GetGitPathString());
580 if (g_Git.Run(sCmd, &out, CP_UTF8))
582 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
583 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not add new file aborting...\r\n\r\n")+out));
587 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_DELETED)
589 sCmd.Format(_T("git.exe rm -- \"%s\""), PathList[i].GetGitPathString());
590 if (g_Git.Run(sCmd, &out, CP_UTF8))
592 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
593 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not rm file aborting...\r\n\r\n")+out));
598 CCommitDlg dlg;
599 for (int i = FirstSelect; i <= LastSelect; ++i)
601 GitRev* pRev = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
602 dlg.m_sLogMessage+=pRev->GetSubject()+_T("\n")+pRev->GetBody();
603 dlg.m_sLogMessage+=_T("\n");
605 dlg.m_bWholeProject=true;
606 dlg.m_bSelectFilesForCommit = true;
607 dlg.m_bForceCommitAmend=true;
608 CTGitPathList gpl;
609 gpl.AddPath(CTGitPath(g_Git.m_CurrentDir));
610 dlg.m_pathList = gpl;
611 if (lastRevision.ParentsCount() != 1)
613 CMessageBox::Show(NULL, _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);
614 dlg.m_bAmendDiffToLastCommit = TRUE;
616 else
617 dlg.m_bAmendDiffToLastCommit = FALSE;
618 dlg.m_bNoPostActions=true;
619 dlg.m_AmendStr=dlg.m_sLogMessage;
621 if (dlg.DoModal() == IDOK)
623 if(pFirstEntry->m_CommitHash!=headhash)
625 if(CherryPickFrom(pFirstEntry->m_CommitHash.ToString(),headhash))
627 CString msg;
628 msg.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));
629 throw std::exception(CUnicodeUtils::GetUTF8(msg));
633 else
634 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_SVN_USERCANCELLED))));
636 catch(std::exception& e)
638 CMessageBox::Show(NULL, CUnicodeUtils::GetUnicode(CStringA(e.what())), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
639 sCmd.Format(_T("git.exe reset --hard %s --"), headhash.ToString());
640 out.Empty();
641 if(g_Git.Run(sCmd, &out, CP_UTF8))
643 CMessageBox::Show(NULL, CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORRESETHEAD)) + _T("\r\n\r\n") + out, _T("TortoiseGit"), MB_OK);
646 Refresh();
648 break;
650 case ID_CHERRY_PICK:
652 if (m_bThreadRunning)
654 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_LOG_ONLYONCE, IDS_APPNAME, MB_ICONEXCLAMATION);
655 break;
657 CRebaseDlg dlg;
658 dlg.m_IsCherryPick = TRUE;
659 dlg.m_Upstream = this->m_CurrentBranch;
660 POSITION pos2 = GetFirstSelectedItemPosition();
661 while(pos2)
663 int indexNext2 = GetNextSelectedItem(pos2);
664 dlg.m_CommitList.m_logEntries.push_back(((GitRevLoglist*)m_arShownList[indexNext2])->m_CommitHash);
665 dlg.m_CommitList.m_LogCache.m_HashMap[((GitRevLoglist*)m_arShownList[indexNext2])->m_CommitHash] = *(GitRevLoglist*)m_arShownList[indexNext2];
666 dlg.m_CommitList.m_logEntries.GetGitRevAt(dlg.m_CommitList.m_logEntries.size() - 1).GetRebaseAction() |= LOGACTIONS_REBASE_PICK;
669 if(dlg.DoModal() == IDOK)
671 Refresh();
674 break;
675 case ID_REBASE_TO_VERSION:
677 if (m_bThreadRunning)
679 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_LOG_ONLYONCE, IDS_APPNAME, MB_ICONEXCLAMATION);
680 break;
682 CRebaseDlg dlg;
683 auto refList = m_HashMap[pSelLogEntry->m_CommitHash];
684 dlg.m_Upstream = refList.empty() ? pSelLogEntry->m_CommitHash.ToString() : refList.front();
685 for (auto ref : refList)
687 if (ref.Left(11) == _T("refs/heads/"))
689 // 11=len("refs/heads/")
690 dlg.m_Upstream = ref.Mid(11);
691 break;
695 if(dlg.DoModal() == IDOK)
697 Refresh();
701 break;
703 case ID_STASH_SAVE:
704 if (CAppUtils::StashSave())
705 Refresh();
706 break;
708 case ID_STASH_POP:
709 if (CAppUtils::StashPop())
710 Refresh();
711 break;
713 case ID_STASH_LIST:
714 CAppUtils::RunTortoiseGitProc(_T("/command:reflog /ref:refs/stash"));
715 break;
717 case ID_REFLOG_STASH_APPLY:
718 CAppUtils::StashApply(pSelLogEntry->m_Ref);
719 break;
721 case ID_REFLOG_DEL:
723 CString str;
724 if (GetSelectedCount() > 1)
725 str.Format(IDS_PROC_DELETENREFS, GetSelectedCount());
726 else
727 str.Format(IDS_PROC_DELETEREF, pSelLogEntry->m_Ref);
729 if (CMessageBox::Show(NULL, str, _T("TortoiseGit"), 1, IDI_QUESTION, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
730 return;
732 std::vector<CString> refsToDelete;
733 POSITION pos2 = GetFirstSelectedItemPosition();
734 while (pos2)
736 CString ref = ((GitRevLoglist*)m_arShownList[GetNextSelectedItem(pos2)])->m_Ref;
737 if (ref.Find(_T("refs/")) == 0)
738 ref = ref.Mid(5);
739 int refpos = ref.ReverseFind('{');
740 if (refpos > 0 && ref.Mid(refpos - 1, 2) != _T("@{"))
741 ref = ref.Left(refpos) + _T("@")+ ref.Mid(refpos);
742 refsToDelete.push_back(ref);
745 for (auto revIt = refsToDelete.rbegin(); revIt != refsToDelete.rend(); ++revIt)
747 CString ref = *revIt;
748 CString sCmd, out;
749 if (ref.Find(_T("stash")) == 0)
750 sCmd.Format(_T("git.exe stash drop %s"), ref);
751 else
752 sCmd.Format(_T("git.exe reflog delete %s"), ref);
754 if (g_Git.Run(sCmd, &out, CP_UTF8))
755 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
757 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
760 break;
761 case ID_LOG:
763 CString sCmd = _T("/command:log");
764 sCmd += _T(" /path:\"") + g_Git.m_CurrentDir + _T("\" ");
765 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
766 sCmd += _T(" /endrev:") + r1->m_CommitHash.ToString();
767 CAppUtils::RunTortoiseGitProc(sCmd);
769 break;
770 case ID_CREATE_PATCH:
772 int select=this->GetSelectedCount();
773 CString sCmd = _T("/command:formatpatch");
774 sCmd += _T(" /path:\"") + g_Git.m_CurrentDir + _T("\" ");
776 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
777 GitRev * r2 = NULL;
778 if(select == 1)
780 sCmd += _T(" /startrev:") + r1->m_CommitHash.ToString();
782 else
784 r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
785 if( this->m_IsOldFirst )
787 sCmd += _T(" /startrev:") + r1->m_CommitHash.ToString() + _T("~1");
788 sCmd += _T(" /endrev:") + r2->m_CommitHash.ToString();
791 else
793 sCmd += _T(" /startrev:") + r2->m_CommitHash.ToString() + _T("~1");
794 sCmd += _T(" /endrev:") + r1->m_CommitHash.ToString();
799 CAppUtils::RunTortoiseGitProc(sCmd);
801 break;
802 case ID_BISECTSTART:
804 GitRev * first = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
805 GitRev * last = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
806 ASSERT(first != NULL && last != NULL);
808 CString firstBad = first->m_CommitHash.ToString();
809 if (!m_HashMap[first->m_CommitHash].empty())
810 firstBad = m_HashMap[first->m_CommitHash].at(0);
811 CString lastGood = last->m_CommitHash.ToString();
812 if (!m_HashMap[last->m_CommitHash].empty())
813 lastGood = m_HashMap[last->m_CommitHash].at(0);
815 if (CAppUtils::BisectStart(lastGood, firstBad))
816 Refresh();
818 break;
819 case ID_REPOBROWSE:
821 CString sCmd;
822 sCmd.Format(_T("/command:repobrowser /path:\"%s\" /rev:%s"), g_Git.m_CurrentDir, pSelLogEntry->m_CommitHash.ToString());
823 CAppUtils::RunTortoiseGitProc(sCmd);
825 break;
826 case ID_PUSH:
828 CString guessAssociatedBranch = pSelLogEntry->m_CommitHash;
829 if (!m_HashMap[pSelLogEntry->m_CommitHash].empty() && m_HashMap[pSelLogEntry->m_CommitHash].at(0).Find(_T("refs/heads/")) == 0)
830 guessAssociatedBranch = m_HashMap[pSelLogEntry->m_CommitHash].at(0);
831 if (CAppUtils::Push(guessAssociatedBranch))
832 Refresh();
834 break;
835 case ID_PULL:
837 if (CAppUtils::Pull())
838 Refresh();
840 break;
841 case ID_FETCH:
843 if (CAppUtils::Fetch())
844 Refresh();
846 break;
847 case ID_CLEANUP:
849 CString sCmd;
850 sCmd.Format(_T("/command:cleanup /path:\"%s\""), g_Git.m_CurrentDir);
851 CAppUtils::RunTortoiseGitProc(sCmd);
853 break;
854 case ID_SUBMODULE_UPDATE:
856 CString sCmd;
857 sCmd.Format(_T("/command:subupdate /bkpath:\"%s\""), g_Git.m_CurrentDir);
858 CAppUtils::RunTortoiseGitProc(sCmd);
860 break;
861 case ID_SHOWBRANCHES:
863 CString sCmd;
864 sCmd.Format(_T("git.exe branch -a --contains %s"), pSelLogEntry->m_CommitHash.ToString());
865 CProgressDlg progress;
866 progress.m_AutoClose = AUTOCLOSE_NO;
867 progress.m_GitCmd = sCmd;
868 progress.DoModal();
870 break;
871 case ID_DELETE:
873 CString *branch = (CString*)((CIconMenu*)popmenu)->GetMenuItemData(cmd);
874 if (!branch)
876 CMessageBox::Show(NULL,IDS_ERROR_NOREF,IDS_APPNAME,MB_OK|MB_ICONERROR);
877 return;
879 CString shortname;
880 if (CGit::GetShortName(*branch, shortname, _T("refs/remotes/")))
882 CString msg;
883 msg.Format(IDS_PROC_DELETEREMOTEBRANCH, *branch);
884 int result = CMessageBox::Show(NULL, msg, _T("TortoiseGit"), 3, IDI_QUESTION, CString(MAKEINTRESOURCE(IDS_PROC_DELETEREMOTEBRANCH_LOCALREMOTE)), CString(MAKEINTRESOURCE(IDS_PROC_DELETEREMOTEBRANCH_LOCAL)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON)));
885 if (result == 1)
887 CString remoteName = shortname.Left(shortname.Find('/'));
888 shortname = shortname.Mid(shortname.Find('/') + 1);
889 if(CAppUtils::IsSSHPutty())
890 CAppUtils::LaunchPAgent(NULL, &remoteName);
892 CSysProgressDlg sysProgressDlg;
893 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
894 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS)));
895 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
896 sysProgressDlg.SetShowProgressBar(false);
897 sysProgressDlg.ShowModal(this, true);
898 STRING_VECTOR list;
899 list.push_back(_T("refs/heads/") + shortname);
900 if (g_Git.DeleteRemoteRefs(remoteName, list))
901 CMessageBox::Show(NULL, g_Git.GetGitLastErr(_T("Could not delete remote ref."), CGit::GIT_CMD_PUSH), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
902 sysProgressDlg.Stop();
904 else if (result == 2)
906 if (g_Git.DeleteRef(*branch))
908 CMessageBox::Show(nullptr, g_Git.GetGitLastErr(L"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
909 return;
912 else
913 return;
915 else if (CGit::GetShortName(*branch, shortname, _T("refs/stash")))
917 if (CMessageBox::Show(NULL, IDS_PROC_DELETEALLSTASH, IDS_APPNAME, 2, IDI_QUESTION, IDS_DELETEBUTTON, IDS_ABORTBUTTON) == 1)
919 CString sCmd;
920 sCmd.Format(_T("git.exe stash clear"));
921 CString out;
922 if (g_Git.Run(sCmd, &out, CP_UTF8))
923 CMessageBox::Show(NULL, out, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
925 else
926 return;
928 else
930 CString msg;
931 msg.Format(IDS_PROC_DELETEBRANCHTAG, *branch);
932 if (CMessageBox::Show(NULL, msg, _T("TortoiseGit"), 2, IDI_QUESTION, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 1)
934 if (g_Git.DeleteRef(*branch))
936 CMessageBox::Show(nullptr, g_Git.GetGitLastErr(L"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
937 return;
941 this->ReloadHashMap();
942 if (m_pFindDialog)
943 m_pFindDialog->RefreshList();
944 CRect rect;
945 this->GetItemRect(FirstSelect,&rect,LVIR_BOUNDS);
946 this->InvalidateRect(rect);
948 break;
950 case ID_FINDENTRY:
952 m_nSearchIndex = GetSelectionMark();
953 if (m_nSearchIndex < 0)
954 m_nSearchIndex = 0;
955 if (m_pFindDialog)
957 break;
959 else
961 m_pFindDialog = new CFindDlg();
962 m_pFindDialog->Create(this);
965 break;
966 case ID_MERGEREV:
968 CString str = pSelLogEntry->m_CommitHash.ToString();
969 if (!m_HashMap[pSelLogEntry->m_CommitHash].empty())
970 str = m_HashMap[pSelLogEntry->m_CommitHash].at(0);
971 // we need an URL to complete this command, so error out if we can't get an URL
972 if(CAppUtils::Merge(&str))
974 this->Refresh();
977 break;
978 case ID_REVERTREV:
980 int parent = 0;
981 if (GetSelectedCount() == 1)
983 parent = cmd >> 16;
984 if (parent > pSelLogEntry->m_ParentHash.size())
986 CString str;
987 str.Format(IDS_PROC_NOPARENT, parent);
988 MessageBox(str, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
989 return;
993 if (!this->RevertSelectedCommits(parent))
995 if (CMessageBox::Show(m_hWnd, IDS_REVREVERTED, IDS_APPNAME, 1, IDI_QUESTION, IDS_OKBUTTON, IDS_COMMITBUTTON) == 2)
997 CTGitPathList pathlist;
998 CTGitPathList selectedlist;
999 pathlist.AddPath(this->m_Path);
1000 bool bSelectFilesForCommit = !!DWORD(CRegStdDWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE));
1001 CString str;
1002 CAppUtils::Commit(CString(), false, str, pathlist, selectedlist, bSelectFilesForCommit);
1004 this->Refresh();
1007 break;
1008 case ID_EDITNOTE:
1010 CAppUtils::EditNote(pSelLogEntry);
1011 this->SetItemState(FirstSelect, 0, LVIS_SELECTED);
1012 this->SetItemState(FirstSelect, LVIS_SELECTED, LVIS_SELECTED);
1014 break;
1015 default:
1016 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
1017 break;
1018 #if 0
1020 case ID_BLAMECOMPARE:
1022 //user clicked on the menu item "compare with working copy"
1023 //now first get the revision which is selected
1024 if (PromptShown())
1026 GitDiff diff(this, this->m_hWnd, true);
1027 diff.SetHEADPeg(m_LogRevision);
1028 diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
1030 else
1031 CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
1033 break;
1034 case ID_BLAMEWITHPREVIOUS:
1036 //user clicked on the menu item "Compare and Blame with previous revision"
1037 if (PromptShown())
1039 GitDiff diff(this, this->m_hWnd, true);
1040 diff.SetHEADPeg(m_LogRevision);
1041 diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
1043 else
1044 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
1046 break;
1048 case ID_OPENWITH:
1049 bOpenWith = true;
1050 case ID_OPEN:
1052 CProgressDlg progDlg;
1053 progDlg.SetTitle(IDS_APPNAME);
1054 progDlg.SetAnimation(IDR_DOWNLOAD);
1055 CString sInfoLine;
1056 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
1057 progDlg.SetLine(1, sInfoLine, true);
1058 SetAndClearProgressInfo(&progDlg);
1059 progDlg.ShowModeless(m_hWnd);
1060 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
1061 bool bSuccess = true;
1062 if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
1064 bSuccess = false;
1065 // try again, but with the selected revision as the peg revision
1066 if (!Cat(m_path, revSelected, revSelected, tempfile))
1068 progDlg.Stop();
1069 SetAndClearProgressInfo((HWND)NULL);
1070 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1071 EnableOKButton();
1072 break;
1074 bSuccess = true;
1076 if (bSuccess)
1078 progDlg.Stop();
1079 SetAndClearProgressInfo((HWND)NULL);
1080 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1081 if (!bOpenWith)
1082 CAppUtils::ShellOpen(tempfile.GetWinPath(), GetSafeHwnd());
1083 else
1084 CAppUtils::ShowOpenWithDialog(tempfile.GetWinPathString(), GetSafeHwnd());
1087 break;
1088 case ID_BLAME:
1090 CBlameDlg dlg;
1091 dlg.EndRev = revSelected;
1092 if (dlg.DoModal() == IDOK)
1094 CBlame blame;
1095 CString tempfile;
1096 CString logfile;
1097 tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
1098 if (!tempfile.IsEmpty())
1100 if (dlg.m_bTextView)
1102 //open the default text editor for the result file
1103 CAppUtils::StartTextViewer(tempfile);
1105 else
1107 CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
1108 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
1110 break;
1114 else
1116 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1120 break;
1121 case ID_EXPORT:
1123 CString sCmd;
1124 sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
1125 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseGitProc.exe")),
1126 (LPCTSTR)pathURL, (LONG)revSelected);
1127 CAppUtils::LaunchApplication(sCmd, NULL, false);
1129 break;
1130 case ID_VIEWREV:
1132 CString url = m_ProjectProperties.sWebViewerRev;
1133 url = GetAbsoluteUrlFromRelativeUrl(url);
1134 url.Replace(_T("%REVISION%"), revSelected.ToString());
1135 if (!url.IsEmpty())
1136 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
1138 break;
1139 case ID_VIEWPATHREV:
1141 CString relurl = pathURL;
1142 CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
1143 relurl = relurl.Mid(sRoot.GetLength());
1144 CString url = m_ProjectProperties.sWebViewerPathRev;
1145 url = GetAbsoluteUrlFromRelativeUrl(url);
1146 url.Replace(_T("%REVISION%"), revSelected.ToString());
1147 url.Replace(_T("%PATH%"), relurl);
1148 if (!url.IsEmpty())
1149 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
1151 break;
1152 #endif
1154 } // switch (cmd)
1156 theApp.DoWaitCursor(-1);
1159 void CGitLogList::SetSelectedRebaseAction(int action)
1161 POSITION pos = GetFirstSelectedItemPosition();
1162 if (!pos) return;
1163 int index;
1164 while(pos)
1166 index = GetNextSelectedItem(pos);
1167 if (((GitRevLoglist*)m_arShownList[index])->GetRebaseAction() & (LOGACTIONS_REBASE_CURRENT | LOGACTIONS_REBASE_DONE) || (index == GetItemCount() - 1 && action == LOGACTIONS_REBASE_SQUASH))
1168 continue;
1169 ((GitRevLoglist*)m_arShownList[index])->GetRebaseAction() = action;
1170 CRect rect;
1171 this->GetItemRect(index,&rect,LVIR_BOUNDS);
1172 this->InvalidateRect(rect);
1175 GetParent()->PostMessage(CGitLogListBase::m_RebaseActionMessage);
1178 void CGitLogList::SetUnselectedRebaseAction(int action)
1180 POSITION pos = GetFirstSelectedItemPosition();
1181 int index = pos ? GetNextSelectedItem(pos) : -1;
1182 for (int i = 0; i < GetItemCount(); i++)
1184 if (i == index)
1186 index = pos ? GetNextSelectedItem(pos) : -1;
1187 continue;
1190 if (((GitRevLoglist*)m_arShownList[i])->GetRebaseAction() & (LOGACTIONS_REBASE_CURRENT | LOGACTIONS_REBASE_DONE) || (i == GetItemCount() - 1 && action == LOGACTIONS_REBASE_SQUASH))
1191 continue;
1192 ((GitRevLoglist*)m_arShownList[i])->GetRebaseAction() = action;
1193 CRect rect;
1194 this->GetItemRect(i, &rect, LVIR_BOUNDS);
1195 this->InvalidateRect(rect);
1198 GetParent()->PostMessage(CGitLogListBase::m_RebaseActionMessage);
1201 void CGitLogList::ShiftSelectedRebaseAction()
1203 POSITION pos = GetFirstSelectedItemPosition();
1204 int index;
1205 while(pos)
1207 index = GetNextSelectedItem(pos);
1208 int *action = &((GitRevLoglist*)m_arShownList[index])->GetRebaseAction();
1209 switch (*action)
1211 case LOGACTIONS_REBASE_PICK:
1212 *action = LOGACTIONS_REBASE_SKIP;
1213 break;
1214 case LOGACTIONS_REBASE_SKIP:
1215 *action= LOGACTIONS_REBASE_EDIT;
1216 break;
1217 case LOGACTIONS_REBASE_EDIT:
1218 *action = LOGACTIONS_REBASE_SQUASH;
1219 if (index == GetItemCount() - 1)
1220 *action = LOGACTIONS_REBASE_PICK;
1221 break;
1222 case LOGACTIONS_REBASE_SQUASH:
1223 *action= LOGACTIONS_REBASE_PICK;
1224 break;
1226 CRect rect;
1227 this->GetItemRect(index, &rect, LVIR_BOUNDS);
1228 this->InvalidateRect(rect);