Refactored: Replace CGitLogListBase::m_From/m_To with the member variable of CFilterData
[TortoiseGit.git] / src / TortoiseProc / GitLogListAction.cpp
blob2d3a56c9f1388d649465fe9cec605853d3ce9eca
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"), (LPCTSTR)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"), (LPCTSTR)from, (LPCTSTR)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"), (LPCTSTR)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_USERCANCELLED))));
143 CString cmd,out;
144 cmd.Format(_T("git.exe cherry-pick %s"), (LPCTSTR)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;
212 CStdioFile file(tempfile, CFile::typeText | CFile::modeRead | CFile::shareDenyWrite);
213 CString strLine;
214 bool isHash = file.ReadString(strLine) && r1->m_CommitHash.ToString() == strLine;
215 bool more = isHash && file.ReadString(strLine) && !strLine.IsEmpty();
216 if (!more)
218 CMessageBox::Show(nullptr, IDS_NOCHANGEAFTERMERGE, IDS_APPNAME, MB_OK);
219 break;
222 catch (CFileException* e)
224 e->Delete();
227 CAppUtils::StartUnifiedDiffViewer(tempfile, _T("dd"));
228 break;
230 else
232 if(cmd > r1->m_ParentHash.size())
234 CString str;
235 str.Format(IDS_PROC_NOPARENT, cmd);
236 MessageBox(str, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
237 return;
239 else
241 if(cmd>0)
242 hash2 = r1->m_ParentHash[cmd-1].ToString();
245 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), hash2, CTGitPath(), r1->m_CommitHash.ToString(), false, false, false, bMerge, bCombine);
247 else
248 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), _T("HEAD"), CTGitPath(), GitRev::GetWorkingCopy(), false, false, false, bMerge, bCombine);
250 break;
252 case ID_GNUDIFF2: // compare two revisions, unified
254 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
255 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
256 CAppUtils::StartShowUnifiedDiff(nullptr, CTGitPath(), r2->m_CommitHash.ToString(), CTGitPath(), r1->m_CommitHash.ToString());
258 break;
260 case ID_COMPARETWO: // compare two revisions
262 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
263 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
264 if (m_Path.IsDirectory() || !(m_ShowMask & CGit::LOG_INFO_FOLLOW))
265 CGitDiff::DiffCommit(this->m_Path, r1,r2);
266 else
268 CString path1 = m_Path.GetGitPathString();
269 // start with 1 (0 = working copy changes)
270 for (int i = 1; i < FirstSelect; ++i)
272 GitRevLoglist* first = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(i));
273 CTGitPathList list = first->GetFiles(NULL);
274 CTGitPath * file = list.LookForGitPath(path1);
275 if (file && !file->GetGitOldPathString().IsEmpty())
276 path1 = file->GetGitOldPathString();
278 CString path2 = path1;
279 for (int i = FirstSelect; i < LastSelect; ++i)
281 GitRevLoglist* first = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(i));
282 CTGitPathList list = first->GetFiles(NULL);
283 CTGitPath * file = list.LookForGitPath(path2);
284 if (file && !file->GetGitOldPathString().IsEmpty())
285 path2 = file->GetGitOldPathString();
287 CGitDiff::DiffCommit(CTGitPath(path1), CTGitPath(path2), r1, r2);
291 break;
293 case ID_COMPARE: // compare revision with WC
295 GitRevLoglist* r1 = &m_wcRev;
296 GitRevLoglist* r2 = pSelLogEntry;
298 if (m_Path.IsDirectory() || !(m_ShowMask & CGit::LOG_INFO_FOLLOW))
299 CGitDiff::DiffCommit(this->m_Path, r1,r2);
300 else
302 CString path1 = m_Path.GetGitPathString();
303 // start with 1 (0 = working copy changes)
304 for (int i = 1; i < FirstSelect; ++i)
306 GitRevLoglist* first = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(i));
307 CTGitPathList list = first->GetFiles(NULL);
308 CTGitPath * file = list.LookForGitPath(path1);
309 if (file && !file->GetGitOldPathString().IsEmpty())
310 path1 = file->GetGitOldPathString();
312 CGitDiff::DiffCommit(m_Path, CTGitPath(path1), r1, r2);
315 //user clicked on the menu item "compare with working copy"
316 //if (PromptShown())
318 // GitDiff diff(this, m_hWnd, true);
319 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
320 // diff.SetHEADPeg(m_LogRevision);
321 // diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
323 //else
324 // CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
326 break;
328 case ID_COMPAREWITHPREVIOUS:
330 CFileDiffDlg dlg;
332 if (pSelLogEntry->m_ParentHash.empty())
334 if (pSelLogEntry->GetParentFromHash(pSelLogEntry->m_CommitHash))
335 MessageBox(pSelLogEntry->GetLastErr(), _T("TortoiseGit"), MB_ICONERROR);
338 if (!pSelLogEntry->m_ParentHash.empty())
339 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
341 cmd>>=16;
342 cmd&=0xFFFF;
344 if(cmd == 0)
345 cmd=1;
347 if (m_Path.IsDirectory() || !(m_ShowMask & CGit::LOG_INFO_FOLLOW))
348 CGitDiff::DiffCommit(m_Path, pSelLogEntry->m_CommitHash.ToString(), pSelLogEntry->m_ParentHash[cmd - 1].ToString());
349 else
351 CString path1 = m_Path.GetGitPathString();
352 // start with 1 (0 = working copy changes)
353 for (int i = 1; i < indexNext; ++i)
355 GitRevLoglist* first = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(i));
356 CTGitPathList list = first->GetFiles(NULL);
357 CTGitPath * file = list.LookForGitPath(path1);
358 if (file && !file->GetGitOldPathString().IsEmpty())
359 path1 = file->GetGitOldPathString();
361 CString path2 = path1;
362 GitRevLoglist* first = reinterpret_cast<GitRevLoglist*>(m_arShownList.GetAt(indexNext));
363 CTGitPathList list = first->GetFiles(NULL);
364 CTGitPath * file = list.LookForGitPath(path2);
365 if (file && !file->GetGitOldPathString().IsEmpty())
366 path2 = file->GetGitOldPathString();
368 CGitDiff::DiffCommit(CTGitPath(path1), CTGitPath(path2), pSelLogEntry->m_CommitHash.ToString(), pSelLogEntry->m_ParentHash[cmd - 1].ToString());
371 else
373 CMessageBox::Show(NULL, IDS_PROC_NOPREVIOUSVERSION, IDS_APPNAME, MB_OK);
375 //if (PromptShown())
377 // GitDiff diff(this, m_hWnd, true);
378 // diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
379 // diff.SetHEADPeg(m_LogRevision);
380 // diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
382 //else
383 // CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
385 break;
386 case ID_LOG_VIEWRANGE:
387 case ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE:
389 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.SafeGetAt(LastSelect));
391 CString sep = _T("..");
392 if ((cmd & 0xFFFF) == ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE)
393 sep = _T("...");
395 CString cmdline;
396 cmdline.Format(_T("/command:log /path:\"%s\" /range:\"%s%s%s\""),
397 (LPCTSTR)g_Git.CombinePath(m_Path), (LPCTSTR)pLastEntry->m_CommitHash.ToString(), (LPCTSTR)sep, (LPCTSTR)pSelLogEntry->m_CommitHash.ToString());
398 CAppUtils::RunTortoiseGitProc(cmdline);
400 break;
401 case ID_COPYCLIPBOARD:
403 CopySelectionToClipBoard();
405 break;
406 case ID_COPYCLIPBOARDMESSAGES:
408 if ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0)
409 CopySelectionToClipBoard(ID_COPY_SUBJECT);
410 else
411 CopySelectionToClipBoard(ID_COPY_MESSAGE);
413 break;
414 case ID_COPYHASH:
416 CopySelectionToClipBoard(ID_COPY_HASH);
418 break;
419 case ID_EXPORT:
421 CString str=pSelLogEntry->m_CommitHash.ToString();
422 // try to get the tag
423 for (size_t i = 0; i < m_HashMap[pSelLogEntry->m_CommitHash].size(); ++i)
425 if (m_HashMap[pSelLogEntry->m_CommitHash][i].Find(_T("refs/tags/")) == 0)
427 str = m_HashMap[pSelLogEntry->m_CommitHash][i];
428 break;
431 CAppUtils::Export(&str, &m_Path);
433 break;
434 case ID_CREATE_BRANCH:
435 case ID_CREATE_TAG:
437 CString str = pSelLogEntry->m_CommitHash.ToString();
438 // try to guess remote branch in order to enable tracking
439 for (size_t i = 0; i < m_HashMap[pSelLogEntry->m_CommitHash].size(); ++i)
441 if (m_HashMap[pSelLogEntry->m_CommitHash][i].Find(_T("refs/remotes/")) == 0)
443 str = m_HashMap[pSelLogEntry->m_CommitHash][i];
444 break;
447 CAppUtils::CreateBranchTag((cmd&0xFFFF) == ID_CREATE_TAG, &str);
448 ReloadHashMap();
449 if (m_pFindDialog)
450 m_pFindDialog->RefreshList();
451 Invalidate();
452 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
454 break;
455 case ID_SWITCHTOREV:
457 CString str = pSelLogEntry->m_CommitHash.ToString();
458 // try to guess remote branch in order to recommend good branch name and tracking
459 for (size_t i = 0; i < m_HashMap[pSelLogEntry->m_CommitHash].size(); ++i)
461 if (m_HashMap[pSelLogEntry->m_CommitHash][i].Find(_T("refs/remotes/")) == 0)
463 str = m_HashMap[pSelLogEntry->m_CommitHash][i];
464 break;
467 CAppUtils::Switch(str);
469 ReloadHashMap();
470 Invalidate();
471 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
472 break;
473 case ID_SWITCHBRANCH:
474 if(popmenu)
476 CString *branch = (CString*)((CIconMenu*)popmenu)->GetMenuItemData(cmd);
477 if(branch)
479 CString name;
480 if(branch->Find(_T("refs/heads/")) ==0 )
481 name = branch->Mid(11);
482 else
483 name = *branch;
485 CAppUtils::PerformSwitch(name);
487 ReloadHashMap();
488 Invalidate();
489 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
491 break;
492 case ID_RESET:
494 CString str = pSelLogEntry->m_CommitHash.ToString();
495 if (CAppUtils::GitReset(&str))
497 ResetWcRev(true);
498 ReloadHashMap();
499 Invalidate();
502 break;
503 case ID_REBASE_PICK:
504 SetSelectedRebaseAction(LOGACTIONS_REBASE_PICK);
505 break;
506 case ID_REBASE_EDIT:
507 SetSelectedRebaseAction(LOGACTIONS_REBASE_EDIT);
508 break;
509 case ID_REBASE_SQUASH:
510 SetSelectedRebaseAction(LOGACTIONS_REBASE_SQUASH);
511 break;
512 case ID_REBASE_SKIP:
513 SetSelectedRebaseAction(LOGACTIONS_REBASE_SKIP);
514 break;
515 case ID_COMBINE_COMMIT:
517 CString head;
518 CGitHash headhash;
519 CGitHash hashFirst,hashLast;
521 int headindex=GetHeadIndex();
522 if(headindex>=0) //incase show all branch, head is not the first commits.
524 head.Format(_T("HEAD~%d"),FirstSelect-headindex);
525 if (g_Git.GetHash(hashFirst, head))
527 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of first selected revision.")), _T("TortoiseGit"), MB_ICONERROR);
528 break;
531 head.Format(_T("HEAD~%d"),LastSelect-headindex);
532 if (g_Git.GetHash(hashLast, head))
534 MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of last selected revision.")), _T("TortoiseGit"), MB_ICONERROR);
535 break;
539 GitRev* pFirstEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
540 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
541 if(pFirstEntry->m_CommitHash != hashFirst || pLastEntry->m_CommitHash != hashLast)
543 CMessageBox::Show(NULL, IDS_PROC_CANNOTCOMBINE, IDS_APPNAME, MB_OK);
544 break;
547 GitRev lastRevision;
548 if (lastRevision.GetParentFromHash(hashLast))
550 MessageBox(lastRevision.GetLastErr(), _T("TortoiseGit"), MB_ICONERROR);
551 break;
554 if (g_Git.GetHash(headhash, _T("HEAD")))
556 MessageBox(g_Git.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR);
557 break;
560 if(!g_Git.CheckCleanWorkTree())
562 CMessageBox::Show(NULL, IDS_PROC_NOCLEAN, IDS_APPNAME, MB_OK);
563 break;
565 CString sCmd, out;
567 //Use throw to abort this process (reset back to original HEAD)
570 sCmd.Format(_T("git.exe reset --hard %s --"), (LPCTSTR)pFirstEntry->m_CommitHash.ToString());
571 if(g_Git.Run(sCmd, &out, CP_UTF8))
573 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
574 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORSTEP1)) + _T("\r\n\r\n") + out));
576 sCmd.Format(_T("git.exe reset --mixed %s --"), (LPCTSTR)hashLast.ToString());
577 if(g_Git.Run(sCmd, &out, CP_UTF8))
579 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
580 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORSTEP2)) + _T("\r\n\r\n")+out));
583 CTGitPathList PathList;
584 /* don't why must add --stat to get action status*/
585 /* first -z will be omitted by gitdll*/
586 if(g_Git.GetDiffPath(&PathList,&pFirstEntry->m_CommitHash,&hashLast,"-z --stat -r"))
588 CMessageBox::Show(NULL,_T("Get Diff file list error"),_T("TortoiseGit"),MB_OK);
589 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not get changed file list aborting...\r\n\r\n")+out));
592 for (int i = 0; i < PathList.GetCount(); ++i)
594 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_ADDED)
596 sCmd.Format(_T("git.exe add -- \"%s\""), (LPCTSTR)PathList[i].GetGitPathString());
597 if (g_Git.Run(sCmd, &out, CP_UTF8))
599 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
600 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not add new file aborting...\r\n\r\n")+out));
604 if(PathList[i].m_Action & CTGitPath::LOGACTIONS_DELETED)
606 sCmd.Format(_T("git.exe rm -- \"%s\""), (LPCTSTR)PathList[i].GetGitPathString());
607 if (g_Git.Run(sCmd, &out, CP_UTF8))
609 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
610 throw std::exception(CUnicodeUtils::GetUTF8(_T("Could not rm file aborting...\r\n\r\n")+out));
615 CCommitDlg dlg;
616 for (int i = FirstSelect; i <= LastSelect; ++i)
618 GitRev* pRev = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
619 dlg.m_sLogMessage+=pRev->GetSubject()+_T("\n")+pRev->GetBody();
620 dlg.m_sLogMessage+=_T("\n");
622 dlg.m_bWholeProject=true;
623 dlg.m_bSelectFilesForCommit = true;
624 dlg.m_bForceCommitAmend=true;
625 CTGitPathList gpl;
626 gpl.AddPath(CTGitPath(g_Git.m_CurrentDir));
627 dlg.m_pathList = gpl;
628 if (lastRevision.ParentsCount() != 1)
630 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);
631 dlg.m_bAmendDiffToLastCommit = TRUE;
633 else
634 dlg.m_bAmendDiffToLastCommit = FALSE;
635 dlg.m_bNoPostActions=true;
636 dlg.m_AmendStr=dlg.m_sLogMessage;
638 if (dlg.DoModal() == IDOK)
640 if(pFirstEntry->m_CommitHash!=headhash)
642 if(CherryPickFrom(pFirstEntry->m_CommitHash.ToString(),headhash))
644 CString msg;
645 msg.Format(_T("Error while cherry pick commits on top of combined commits. Aborting.\r\n\r\n"));
646 throw std::exception(CUnicodeUtils::GetUTF8(msg));
650 else
651 throw std::exception(CUnicodeUtils::GetUTF8(CString(MAKEINTRESOURCE(IDS_USERCANCELLED))));
653 catch(std::exception& e)
655 CMessageBox::Show(NULL, CUnicodeUtils::GetUnicode(CStringA(e.what())), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
656 sCmd.Format(_T("git.exe reset --hard %s --"), (LPCTSTR)headhash.ToString());
657 out.Empty();
658 if(g_Git.Run(sCmd, &out, CP_UTF8))
660 CMessageBox::Show(NULL, CString(MAKEINTRESOURCE(IDS_PROC_COMBINE_ERRORRESETHEAD)) + _T("\r\n\r\n") + out, _T("TortoiseGit"), MB_OK);
663 Refresh();
665 break;
667 case ID_CHERRY_PICK:
669 if (m_bThreadRunning)
671 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_LOG_ONLYONCE, IDS_APPNAME, MB_ICONEXCLAMATION);
672 break;
674 CRebaseDlg dlg;
675 dlg.m_IsCherryPick = TRUE;
676 dlg.m_Upstream = this->m_CurrentBranch;
677 POSITION pos2 = GetFirstSelectedItemPosition();
678 while(pos2)
680 int indexNext2 = GetNextSelectedItem(pos2);
681 dlg.m_CommitList.m_logEntries.push_back(((GitRevLoglist*)m_arShownList[indexNext2])->m_CommitHash);
682 dlg.m_CommitList.m_LogCache.m_HashMap[((GitRevLoglist*)m_arShownList[indexNext2])->m_CommitHash] = *(GitRevLoglist*)m_arShownList[indexNext2];
683 dlg.m_CommitList.m_logEntries.GetGitRevAt(dlg.m_CommitList.m_logEntries.size() - 1).GetRebaseAction() |= LOGACTIONS_REBASE_PICK;
686 if(dlg.DoModal() == IDOK)
688 Refresh();
691 break;
692 case ID_REBASE_TO_VERSION:
694 if (m_bThreadRunning)
696 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_LOG_ONLYONCE, IDS_APPNAME, MB_ICONEXCLAMATION);
697 break;
699 CRebaseDlg dlg;
700 auto refList = m_HashMap[pSelLogEntry->m_CommitHash];
701 dlg.m_Upstream = refList.empty() ? pSelLogEntry->m_CommitHash.ToString() : refList.front();
702 for (auto ref : refList)
704 if (ref.Left(11) == _T("refs/heads/"))
706 // 11=len("refs/heads/")
707 dlg.m_Upstream = ref.Mid(11);
708 break;
712 if(dlg.DoModal() == IDOK)
714 Refresh();
718 break;
720 case ID_STASH_SAVE:
721 if (CAppUtils::StashSave())
722 Refresh();
723 break;
725 case ID_STASH_POP:
726 if (CAppUtils::StashPop())
727 Refresh();
728 break;
730 case ID_STASH_LIST:
731 CAppUtils::RunTortoiseGitProc(_T("/command:reflog /ref:refs/stash"));
732 break;
734 case ID_REFLOG_STASH_APPLY:
735 CAppUtils::StashApply(pSelLogEntry->m_Ref);
736 break;
738 case ID_REFLOG_DEL:
740 CString str;
741 if (GetSelectedCount() > 1)
742 str.Format(IDS_PROC_DELETENREFS, GetSelectedCount());
743 else
744 str.Format(IDS_PROC_DELETEREF, (LPCTSTR)pSelLogEntry->m_Ref);
746 if (CMessageBox::Show(NULL, str, _T("TortoiseGit"), 1, IDI_QUESTION, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
747 return;
749 std::vector<CString> refsToDelete;
750 POSITION pos2 = GetFirstSelectedItemPosition();
751 while (pos2)
753 CString ref = ((GitRevLoglist*)m_arShownList[GetNextSelectedItem(pos2)])->m_Ref;
754 if (ref.Find(_T("refs/")) == 0)
755 ref = ref.Mid(5);
756 int refpos = ref.ReverseFind('{');
757 if (refpos > 0 && ref.Mid(refpos - 1, 2) != _T("@{"))
758 ref = ref.Left(refpos) + _T("@")+ ref.Mid(refpos);
759 refsToDelete.push_back(ref);
762 for (auto revIt = refsToDelete.rbegin(); revIt != refsToDelete.rend(); ++revIt)
764 CString ref = *revIt;
765 CString sCmd, out;
766 if (ref.Find(_T("stash")) == 0)
767 sCmd.Format(_T("git.exe stash drop %s"), (LPCTSTR)ref);
768 else
769 sCmd.Format(_T("git.exe reflog delete %s"), (LPCTSTR)ref);
771 if (g_Git.Run(sCmd, &out, CP_UTF8))
772 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
774 ::PostMessage(this->GetParent()->m_hWnd,MSG_REFLOG_CHANGED,0,0);
777 break;
778 case ID_LOG:
780 CString sCmd = _T("/command:log");
781 sCmd += _T(" /path:\"") + g_Git.m_CurrentDir + _T("\" ");
782 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
783 sCmd += _T(" /endrev:") + r1->m_CommitHash.ToString();
784 CAppUtils::RunTortoiseGitProc(sCmd);
786 break;
787 case ID_CREATE_PATCH:
789 int select=this->GetSelectedCount();
790 CString sCmd = _T("/command:formatpatch");
791 sCmd += _T(" /path:\"") + g_Git.m_CurrentDir + _T("\" ");
793 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
794 GitRev * r2 = NULL;
795 if(select == 1)
797 sCmd += _T(" /startrev:") + r1->m_CommitHash.ToString();
799 else
801 r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
802 if( this->m_IsOldFirst )
804 sCmd += _T(" /startrev:") + r1->m_CommitHash.ToString() + _T("~1");
805 sCmd += _T(" /endrev:") + r2->m_CommitHash.ToString();
808 else
810 sCmd += _T(" /startrev:") + r2->m_CommitHash.ToString() + _T("~1");
811 sCmd += _T(" /endrev:") + r1->m_CommitHash.ToString();
816 CAppUtils::RunTortoiseGitProc(sCmd);
818 break;
819 case ID_BISECTSTART:
821 GitRev * first = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
822 GitRev * last = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
823 ASSERT(first != NULL && last != NULL);
825 CString firstBad = first->m_CommitHash.ToString();
826 if (!m_HashMap[first->m_CommitHash].empty())
827 firstBad = m_HashMap[first->m_CommitHash].at(0);
828 CString lastGood = last->m_CommitHash.ToString();
829 if (!m_HashMap[last->m_CommitHash].empty())
830 lastGood = m_HashMap[last->m_CommitHash].at(0);
832 if (CAppUtils::BisectStart(lastGood, firstBad))
833 Refresh();
835 break;
836 case ID_REPOBROWSE:
838 CString sCmd;
839 sCmd.Format(_T("/command:repobrowser /path:\"%s\" /rev:%s"), (LPCTSTR)g_Git.m_CurrentDir, (LPCTSTR)pSelLogEntry->m_CommitHash.ToString());
840 CAppUtils::RunTortoiseGitProc(sCmd);
842 break;
843 case ID_PUSH:
845 CString guessAssociatedBranch = pSelLogEntry->m_CommitHash;
846 if (!m_HashMap[pSelLogEntry->m_CommitHash].empty() && m_HashMap[pSelLogEntry->m_CommitHash].at(0).Find(_T("refs/heads/")) == 0)
847 guessAssociatedBranch = m_HashMap[pSelLogEntry->m_CommitHash].at(0);
848 if (CAppUtils::Push(guessAssociatedBranch))
849 Refresh();
851 break;
852 case ID_PULL:
854 if (CAppUtils::Pull())
855 Refresh();
857 break;
858 case ID_FETCH:
860 if (CAppUtils::Fetch())
861 Refresh();
863 break;
864 case ID_CLEANUP:
866 CString sCmd;
867 sCmd.Format(_T("/command:cleanup /path:\"%s\""), (LPCTSTR)g_Git.m_CurrentDir);
868 CAppUtils::RunTortoiseGitProc(sCmd);
870 break;
871 case ID_SUBMODULE_UPDATE:
873 CString sCmd;
874 sCmd.Format(_T("/command:subupdate /bkpath:\"%s\""), (LPCTSTR)g_Git.m_CurrentDir);
875 CAppUtils::RunTortoiseGitProc(sCmd);
877 break;
878 case ID_SHOWBRANCHES:
880 CString sCmd;
881 sCmd.Format(_T("git.exe branch -a --contains %s"), (LPCTSTR)pSelLogEntry->m_CommitHash.ToString());
882 CProgressDlg progress;
883 progress.m_AutoClose = AUTOCLOSE_NO;
884 progress.m_GitCmd = sCmd;
885 progress.DoModal();
887 break;
888 case ID_DELETE:
890 CString *branch = (CString*)((CIconMenu*)popmenu)->GetMenuItemData(cmd);
891 if (!branch)
893 CMessageBox::Show(NULL,IDS_ERROR_NOREF,IDS_APPNAME,MB_OK|MB_ICONERROR);
894 return;
896 CString shortname;
897 if (CGit::GetShortName(*branch, shortname, _T("refs/remotes/")))
899 CString msg;
900 msg.Format(IDS_PROC_DELETEREMOTEBRANCH, (LPCTSTR)*branch);
901 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)));
902 if (result == 1)
904 CString remoteName = shortname.Left(shortname.Find('/'));
905 shortname = shortname.Mid(shortname.Find('/') + 1);
906 if(CAppUtils::IsSSHPutty())
907 CAppUtils::LaunchPAgent(NULL, &remoteName);
909 CSysProgressDlg sysProgressDlg;
910 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
911 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_DELETING_REMOTE_REFS)));
912 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
913 sysProgressDlg.SetShowProgressBar(false);
914 sysProgressDlg.ShowModal(this, true);
915 STRING_VECTOR list;
916 list.push_back(_T("refs/heads/") + shortname);
917 if (g_Git.DeleteRemoteRefs(remoteName, list))
918 CMessageBox::Show(NULL, g_Git.GetGitLastErr(_T("Could not delete remote ref."), CGit::GIT_CMD_PUSH), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
919 sysProgressDlg.Stop();
921 else if (result == 2)
923 if (g_Git.DeleteRef(*branch))
925 CMessageBox::Show(nullptr, g_Git.GetGitLastErr(L"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
926 return;
929 else
930 return;
932 else if (CGit::GetShortName(*branch, shortname, _T("refs/stash")))
934 if (CMessageBox::Show(NULL, IDS_PROC_DELETEALLSTASH, IDS_APPNAME, 2, IDI_QUESTION, IDS_DELETEBUTTON, IDS_ABORTBUTTON) == 1)
936 CString sCmd;
937 sCmd.Format(_T("git.exe stash clear"));
938 CString out;
939 if (g_Git.Run(sCmd, &out, CP_UTF8))
940 CMessageBox::Show(NULL, out, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
942 else
943 return;
945 else
947 CString msg;
948 msg.Format(IDS_PROC_DELETEBRANCHTAG, (LPCTSTR)*branch);
949 if (CMessageBox::Show(NULL, msg, _T("TortoiseGit"), 2, IDI_QUESTION, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 1)
951 if (g_Git.DeleteRef(*branch))
953 CMessageBox::Show(nullptr, g_Git.GetGitLastErr(L"Could not delete reference.", CGit::GIT_CMD_DELETETAGBRANCH), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
954 return;
958 this->ReloadHashMap();
959 if (m_pFindDialog)
960 m_pFindDialog->RefreshList();
961 CRect rect;
962 this->GetItemRect(FirstSelect,&rect,LVIR_BOUNDS);
963 this->InvalidateRect(rect);
965 break;
967 case ID_FINDENTRY:
969 m_nSearchIndex = GetSelectionMark();
970 if (m_nSearchIndex < 0)
971 m_nSearchIndex = 0;
972 if (m_pFindDialog)
974 break;
976 else
978 m_pFindDialog = new CFindDlg();
979 m_pFindDialog->Create(this);
982 break;
983 case ID_MERGEREV:
985 CString str = pSelLogEntry->m_CommitHash.ToString();
986 if (!m_HashMap[pSelLogEntry->m_CommitHash].empty())
987 str = m_HashMap[pSelLogEntry->m_CommitHash].at(0);
988 // we need an URL to complete this command, so error out if we can't get an URL
989 if(CAppUtils::Merge(&str))
991 this->Refresh();
994 break;
995 case ID_REVERTREV:
997 int parent = 0;
998 if (GetSelectedCount() == 1)
1000 parent = cmd >> 16;
1001 if (parent > pSelLogEntry->m_ParentHash.size())
1003 CString str;
1004 str.Format(IDS_PROC_NOPARENT, parent);
1005 MessageBox(str, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1006 return;
1010 if (!this->RevertSelectedCommits(parent))
1012 if (CMessageBox::Show(m_hWnd, IDS_REVREVERTED, IDS_APPNAME, 1, IDI_QUESTION, IDS_OKBUTTON, IDS_COMMITBUTTON) == 2)
1014 CTGitPathList pathlist;
1015 CTGitPathList selectedlist;
1016 pathlist.AddPath(this->m_Path);
1017 bool bSelectFilesForCommit = !!DWORD(CRegStdDWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE));
1018 CString str;
1019 CAppUtils::Commit(CString(), false, str, pathlist, selectedlist, bSelectFilesForCommit);
1021 this->Refresh();
1024 break;
1025 case ID_EDITNOTE:
1027 CAppUtils::EditNote(pSelLogEntry);
1028 this->SetItemState(FirstSelect, 0, LVIS_SELECTED);
1029 this->SetItemState(FirstSelect, LVIS_SELECTED, LVIS_SELECTED);
1031 break;
1032 default:
1033 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
1034 break;
1035 #if 0
1037 case ID_BLAMECOMPARE:
1039 //user clicked on the menu item "compare with working copy"
1040 //now first get the revision which is selected
1041 if (PromptShown())
1043 GitDiff diff(this, this->m_hWnd, true);
1044 diff.SetHEADPeg(m_LogRevision);
1045 diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
1047 else
1048 CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
1050 break;
1051 case ID_BLAMEWITHPREVIOUS:
1053 //user clicked on the menu item "Compare and Blame with previous revision"
1054 if (PromptShown())
1056 GitDiff diff(this, this->m_hWnd, true);
1057 diff.SetHEADPeg(m_LogRevision);
1058 diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
1060 else
1061 CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
1063 break;
1065 case ID_OPENWITH:
1066 bOpenWith = true;
1067 case ID_OPEN:
1069 CProgressDlg progDlg;
1070 progDlg.SetTitle(IDS_APPNAME);
1071 progDlg.SetAnimation(IDR_DOWNLOAD);
1072 CString sInfoLine;
1073 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
1074 progDlg.SetLine(1, sInfoLine, true);
1075 SetAndClearProgressInfo(&progDlg);
1076 progDlg.ShowModeless(m_hWnd);
1077 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
1078 bool bSuccess = true;
1079 if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
1081 bSuccess = false;
1082 // try again, but with the selected revision as the peg revision
1083 if (!Cat(m_path, revSelected, revSelected, tempfile))
1085 progDlg.Stop();
1086 SetAndClearProgressInfo((HWND)NULL);
1087 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1088 EnableOKButton();
1089 break;
1091 bSuccess = true;
1093 if (bSuccess)
1095 progDlg.Stop();
1096 SetAndClearProgressInfo((HWND)NULL);
1097 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1098 if (!bOpenWith)
1099 CAppUtils::ShellOpen(tempfile.GetWinPath(), GetSafeHwnd());
1100 else
1101 CAppUtils::ShowOpenWithDialog(tempfile.GetWinPathString(), GetSafeHwnd());
1104 break;
1105 case ID_BLAME:
1107 CBlameDlg dlg;
1108 dlg.EndRev = revSelected;
1109 if (dlg.DoModal() == IDOK)
1111 CBlame blame;
1112 CString tempfile;
1113 CString logfile;
1114 tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
1115 if (!tempfile.IsEmpty())
1117 if (dlg.m_bTextView)
1119 //open the default text editor for the result file
1120 CAppUtils::StartTextViewer(tempfile);
1122 else
1124 CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
1125 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
1127 break;
1131 else
1133 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1137 break;
1138 case ID_EXPORT:
1140 CString sCmd;
1141 sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
1142 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseGitProc.exe")),
1143 (LPCTSTR)pathURL, (LONG)revSelected);
1144 CAppUtils::LaunchApplication(sCmd, NULL, false);
1146 break;
1147 case ID_VIEWREV:
1149 CString url = m_ProjectProperties.sWebViewerRev;
1150 url = GetAbsoluteUrlFromRelativeUrl(url);
1151 url.Replace(_T("%REVISION%"), revSelected.ToString());
1152 if (!url.IsEmpty())
1153 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
1155 break;
1156 case ID_VIEWPATHREV:
1158 CString relurl = pathURL;
1159 CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
1160 relurl = relurl.Mid(sRoot.GetLength());
1161 CString url = m_ProjectProperties.sWebViewerPathRev;
1162 url = GetAbsoluteUrlFromRelativeUrl(url);
1163 url.Replace(_T("%REVISION%"), revSelected.ToString());
1164 url.Replace(_T("%PATH%"), relurl);
1165 if (!url.IsEmpty())
1166 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
1168 break;
1169 #endif
1171 } // switch (cmd)
1173 theApp.DoWaitCursor(-1);
1176 void CGitLogList::SetSelectedRebaseAction(int action)
1178 POSITION pos = GetFirstSelectedItemPosition();
1179 if (!pos) return;
1180 int index;
1181 while(pos)
1183 index = GetNextSelectedItem(pos);
1184 if (((GitRevLoglist*)m_arShownList[index])->GetRebaseAction() & (LOGACTIONS_REBASE_CURRENT | LOGACTIONS_REBASE_DONE) || (index == GetItemCount() - 1 && action == LOGACTIONS_REBASE_SQUASH))
1185 continue;
1186 ((GitRevLoglist*)m_arShownList[index])->GetRebaseAction() = action;
1187 CRect rect;
1188 this->GetItemRect(index,&rect,LVIR_BOUNDS);
1189 this->InvalidateRect(rect);
1192 GetParent()->PostMessage(CGitLogListBase::m_RebaseActionMessage);
1195 void CGitLogList::SetUnselectedRebaseAction(int action)
1197 POSITION pos = GetFirstSelectedItemPosition();
1198 int index = pos ? GetNextSelectedItem(pos) : -1;
1199 for (int i = 0; i < GetItemCount(); i++)
1201 if (i == index)
1203 index = pos ? GetNextSelectedItem(pos) : -1;
1204 continue;
1207 if (((GitRevLoglist*)m_arShownList[i])->GetRebaseAction() & (LOGACTIONS_REBASE_CURRENT | LOGACTIONS_REBASE_DONE) || (i == GetItemCount() - 1 && action == LOGACTIONS_REBASE_SQUASH))
1208 continue;
1209 ((GitRevLoglist*)m_arShownList[i])->GetRebaseAction() = action;
1210 CRect rect;
1211 this->GetItemRect(i, &rect, LVIR_BOUNDS);
1212 this->InvalidateRect(rect);
1215 GetParent()->PostMessage(CGitLogListBase::m_RebaseActionMessage);
1218 void CGitLogList::ShiftSelectedRebaseAction()
1220 POSITION pos = GetFirstSelectedItemPosition();
1221 int index;
1222 while(pos)
1224 index = GetNextSelectedItem(pos);
1225 int *action = &((GitRevLoglist*)m_arShownList[index])->GetRebaseAction();
1226 switch (*action)
1228 case LOGACTIONS_REBASE_PICK:
1229 *action = LOGACTIONS_REBASE_SKIP;
1230 break;
1231 case LOGACTIONS_REBASE_SKIP:
1232 *action= LOGACTIONS_REBASE_EDIT;
1233 break;
1234 case LOGACTIONS_REBASE_EDIT:
1235 *action = LOGACTIONS_REBASE_SQUASH;
1236 if (index == GetItemCount() - 1)
1237 *action = LOGACTIONS_REBASE_PICK;
1238 break;
1239 case LOGACTIONS_REBASE_SQUASH:
1240 *action= LOGACTIONS_REBASE_PICK;
1241 break;
1243 CRect rect;
1244 this->GetItemRect(index, &rect, LVIR_BOUNDS);
1245 this->InvalidateRect(rect);