Fixed issue #2219: "add cherry picked from" is not applied when squashing/editing...
[TortoiseGit.git] / src / TortoiseGitBlame / LogListBlameAction.cpp
blob5095a2bc11179f99a2518ac5d24b6be074a6d97f
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013 - TortoiseGit
4 // Copyright (C) 2011-2013 Sven Strickroth <email@cs-ware.de>
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.
21 #include "stdafx.h"
22 #include "GitBlameLogList.h"
23 #include "GitRev.h"
24 #include "TortoiseGitBlameDoc.h"
25 #include "TortoiseGitBlameView.h"
26 #include "MainFrm.h"
27 #include "CommonAppUtils.h"
29 IMPLEMENT_DYNAMIC(CGitBlameLogList, CHintListCtrl)
31 void CGitBlameLogList::hideUnimplementedCommands()
33 m_ContextMenuMask |= GetContextMenuBit(ID_BLAMEPREVIOUS) | GetContextMenuBit(ID_LOG);
34 hideFromContextMenu(
35 GetContextMenuBit(ID_COMPAREWITHPREVIOUS) |
36 GetContextMenuBit(ID_GNUDIFF1) |
37 GetContextMenuBit(ID_BLAMEPREVIOUS) |
38 GetContextMenuBit(ID_COPYCLIPBOARD) |
39 GetContextMenuBit(ID_COPYHASH) |
40 GetContextMenuBit(ID_EXPORT) |
41 GetContextMenuBit(ID_CREATE_BRANCH) |
42 GetContextMenuBit(ID_CREATE_TAG) |
43 GetContextMenuBit(ID_SWITCHTOREV) |
44 GetContextMenuBit(ID_LOG) |
45 GetContextMenuBit(ID_REPOBROWSE)
46 , true);
49 void CGitBlameLogList::GetParentHashes(GitRev *pRev, GIT_REV_LIST &parentHash)
51 std::vector<CTGitPath> paths;
52 GetPaths(pRev->m_CommitHash, paths);
54 std::set<int> parentNos;
55 GetParentNumbers(pRev, paths, parentNos);
57 for (auto it = parentNos.cbegin(); it != parentNos.cend(); ++it)
59 int parentNo = *it;
60 parentHash.push_back(pRev->m_ParentHash[parentNo]);
64 void RunTortoiseGitProcWithCurrentRev(const CString& command, const GitRev* pRev, const CString &path = g_Git.m_CurrentDir)
66 ASSERT(pRev);
67 CString procCmd;
68 procCmd.Format(L"/command:%s /path:\"%s\" /rev:%s", command, path, pRev->m_CommitHash.ToString());
69 CCommonAppUtils::RunTortoiseGitProc(procCmd);
72 void CGitBlameLogList::ContextMenuAction(int cmd, int /*FirstSelect*/, int /*LastSelect*/, CMenu * /*menu*/)
74 POSITION pos = GetFirstSelectedItemPosition();
75 int indexNext = GetNextSelectedItem(pos);
76 if (indexNext < 0)
77 return;
78 CTortoiseGitBlameView *pView = DYNAMIC_DOWNCAST(CTortoiseGitBlameView,((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView());
80 GitRev *pRev = &this->m_logEntries.GetGitRevAt(indexNext);
82 switch (cmd & 0xFFFF)
84 case ID_BLAMEPREVIOUS:
86 int index = (cmd >> 16) & 0xFFFF;
87 if (index > 0)
88 index -= 1;
90 CGitHash parentHash;
91 std::vector<CString> parentFilenames;
92 GetParentHash(pRev, index, parentHash, parentFilenames);
93 for (size_t i = 0; i < parentFilenames.size(); ++i)
95 CString procCmd = _T("/path:\"") + pView->ResolveCommitFile(parentFilenames[i]) + _T("\" ");
96 procCmd += _T(" /command:blame");
97 procCmd += _T(" /endrev:") + parentHash.ToString();
99 CCommonAppUtils::RunTortoiseGitProc(procCmd);
102 break;
103 case ID_GNUDIFF1: // fallthrough
104 case ID_COMPAREWITHPREVIOUS:
106 int index = (cmd >> 16) & 0xFFFF;
107 if (index > 0)
108 index -= 1;
110 CGitHash parentHash;
111 std::vector<CString> parentFilenames;
112 GetParentHash(pRev, index, parentHash, parentFilenames);
113 for (size_t i = 0; i < parentFilenames.size(); ++i)
115 CString procCmd = _T("/path:\"") + pView->ResolveCommitFile(parentFilenames[i]) + _T("\" ");
116 procCmd += _T(" /command:diff");
117 procCmd += _T(" /startrev:") + pRev->m_CommitHash.ToString();
118 procCmd += _T(" /endrev:") + parentHash.ToString();
119 if ((cmd & 0xFFFF) == ID_GNUDIFF1)
120 procCmd += _T(" /unified");
122 CCommonAppUtils::RunTortoiseGitProc(procCmd);
125 break;
126 case ID_COPYCLIPBOARD:
128 CopySelectionToClipBoard();
130 break;
131 case ID_COPYHASH:
133 CopySelectionToClipBoard(ID_COPY_HASH);
135 break;
136 case ID_EXPORT:
137 RunTortoiseGitProcWithCurrentRev(_T("export"), pRev);
138 break;
139 case ID_CREATE_BRANCH:
140 RunTortoiseGitProcWithCurrentRev(_T("branch"), pRev);
141 break;
142 case ID_CREATE_TAG:
143 RunTortoiseGitProcWithCurrentRev(_T("tag"), pRev);
144 break;
145 case ID_SWITCHTOREV:
146 RunTortoiseGitProcWithCurrentRev(_T("switch"), pRev);
147 break;
148 case ID_LOG:
149 RunTortoiseGitProcWithCurrentRev(_T("log"), pRev, ((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName());
150 break;
151 case ID_REPOBROWSE:
152 RunTortoiseGitProcWithCurrentRev(_T("repobrowser"), pRev, ((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName());
153 break;
154 default:
155 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
156 break;
157 } // switch (cmd)
160 void CGitBlameLogList::GetPaths(const CGitHash& hash, std::vector<CTGitPath>& paths)
162 CTortoiseGitBlameView *pView = DYNAMIC_DOWNCAST(CTortoiseGitBlameView,((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView());
163 if (pView)
166 std::set<CString> filenames;
167 int numberOfLines = pView->m_data.GetNumberOfLines();
168 for (int i = 0; i < numberOfLines; ++i)
170 if (pView->m_data.GetHash(i) == hash)
172 filenames.insert(pView->m_data.GetFilename(i));
175 for (auto it = filenames.cbegin(); it != filenames.cend(); ++it)
177 paths.push_back(CTGitPath(*it));
180 if (paths.empty())
182 // in case the hash does not exist in the blame output but it exists in the log follow only the file
183 paths.push_back(pView->GetDocument()->m_GitPath);
188 void CGitBlameLogList::GetParentNumbers(GitRev *pRev, const std::vector<CTGitPath>& paths, std::set<int> &parentNos)
190 if (pRev->m_ParentHash.empty())
194 pRev->GetParentFromHash(pRev->m_CommitHash);
196 catch (const char* msg)
198 MessageBox(_T("Could not get parent.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
202 GIT_REV_LIST allParentHash;
203 CGitLogListBase::GetParentHashes(pRev, allParentHash);
207 const CTGitPathList& files = pRev->GetFiles(NULL);
208 for (int j=0, j_size = files.GetCount(); j < j_size; ++j)
210 const CTGitPath &file = files[j];
211 for (auto it=paths.cbegin(); it != paths.cend(); ++it)
213 const CTGitPath& path = *it;
214 if (file.IsEquivalentTo(path))
216 if (!(file.m_ParentNo & MERGE_MASK))
218 int action = file.m_Action;
219 // ignore (action & CTGitPath::LOGACTIONS_ADDED), as then there is nothing to blame/diff
220 // ignore (action & CTGitPath::LOGACTIONS_DELETED), should never happen as the file must exist
221 if (action & (CTGitPath::LOGACTIONS_MODIFIED | CTGitPath::LOGACTIONS_REPLACED))
223 int parentNo = file.m_ParentNo & PARENT_MASK;
224 if (parentNo >= 0 && (size_t)parentNo < pRev->m_ParentHash.size())
225 parentNos.insert(parentNo);
232 catch (const char* msg)
234 MessageBox(_T("Could not get files of parents.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
238 void CGitBlameLogList::GetParentHash(GitRev *pRev, int index, CGitHash &parentHash, std::vector<CString>& parentFilenames)
240 std::vector<CTGitPath> paths;
241 GetPaths(pRev->m_CommitHash, paths);
243 std::set<int> parentNos;
244 GetParentNumbers(pRev, paths, parentNos);
246 int parentNo = 0;
248 int i = 0;
249 for (auto it = parentNos.cbegin(); it != parentNos.cend(); ++it, ++i)
251 if (i == index)
253 parentNo = *it;
254 break;
258 parentHash = pRev->m_ParentHash[parentNo];
262 const CTGitPathList& files = pRev->GetFiles(NULL);
263 for (int j = 0, j_size = files.GetCount(); j < j_size; ++j)
265 const CTGitPath &file = files[j];
266 for (auto it = paths.cbegin(); it != paths.cend(); ++it)
268 const CTGitPath& path = *it;
269 if (file.IsEquivalentTo(path))
271 if (!(file.m_ParentNo & MERGE_MASK))
273 int action = file.m_Action;
274 // ignore (action & CTGitPath::LOGACTIONS_ADDED), as then there is nothing to blame/diff
275 // ignore (action & CTGitPath::LOGACTIONS_DELETED), should never happen as the file must exist
276 if (action & (CTGitPath::LOGACTIONS_MODIFIED | CTGitPath::LOGACTIONS_REPLACED))
278 if (parentNo == (file.m_ParentNo & PARENT_MASK))
280 if (parentNo >= 0 && (size_t)parentNo < pRev->m_ParentHash.size())
281 parentFilenames.push_back( (action & CTGitPath::LOGACTIONS_REPLACED) ? file.GetGitOldPathString() : file.GetGitPathString());
289 catch (const char* msg)
291 MessageBox(_T("Could not get files of parents.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);