Updated to add local changes ignored files when updating status list
[TortoiseGit.git] / src / TortoiseGitBlame / LogListBlameAction.cpp
blob34f68a67f3a6ba940976c8e495b33f164e0d57f9
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013, 2015-2018 - 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 "GitRevLoglist.h"
25 #include "TortoiseGitBlameDoc.h"
26 #include "TortoiseGitBlameView.h"
27 #include "MainFrm.h"
28 #include "CommonAppUtils.h"
30 IMPLEMENT_DYNAMIC(CGitBlameLogList, CHintCtrl<CListCtrl>)
32 void CGitBlameLogList::hideUnimplementedCommands()
34 m_ContextMenuMask |= GetContextMenuBit(ID_BLAMEPREVIOUS) | GetContextMenuBit(ID_LOG);
35 hideFromContextMenu(
36 GetContextMenuBit(ID_COMPAREWITHPREVIOUS) |
37 GetContextMenuBit(ID_GNUDIFF1) |
38 GetContextMenuBit(ID_BLAMEPREVIOUS) |
39 GetContextMenuBit(ID_COPYCLIPBOARD) |
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_SHOWBRANCHES) |
46 GetContextMenuBit(ID_REPOBROWSE)
47 , true);
50 void CGitBlameLogList::GetParentHashes(GitRevLoglist* pRev, GIT_REV_LIST& parentHash)
52 std::vector<CTGitPath> paths;
53 GetPaths(pRev->m_CommitHash, paths);
55 std::set<int> parentNos;
56 GetParentNumbers(pRev, paths, parentNos);
58 for (auto it = parentNos.cbegin(); it != parentNos.cend(); ++it)
60 int parentNo = *it;
61 parentHash.push_back(pRev->m_ParentHash[parentNo]);
65 void RunTortoiseGitProcWithCurrentRev(const CString& command, const GitRev* pRev, const CString &path = g_Git.m_CurrentDir)
67 ASSERT(pRev);
68 CString procCmd;
69 procCmd.Format(L"/command:%s /path:\"%s\" /rev:%s", (LPCTSTR)command, (LPCTSTR)path, (LPCTSTR)pRev->m_CommitHash.ToString());
70 CCommonAppUtils::RunTortoiseGitProc(procCmd);
73 void CGitBlameLogList::ContextMenuAction(int cmd, int /*FirstSelect*/, int /*LastSelect*/, CMenu * /*menu*/)
75 POSITION pos = GetFirstSelectedItemPosition();
76 int indexNext = GetNextSelectedItem(pos);
77 if (indexNext < 0)
78 return;
79 CTortoiseGitBlameView *pView = DYNAMIC_DOWNCAST(CTortoiseGitBlameView,((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView());
81 GitRevLoglist* pRev = &this->m_logEntries.GetGitRevAt(indexNext);
83 switch (cmd & 0xFFFF)
85 case ID_BLAMEPREVIOUS:
87 int index = (cmd >> 16) & 0xFFFF;
88 if (index > 0)
89 index -= 1;
91 CGitHash parentHash;
92 std::vector<CString> parentFilenames;
93 GetParentHash(pRev, index, parentHash, parentFilenames);
94 for (size_t i = 0; i < parentFilenames.size(); ++i)
96 CString procCmd = L"/path:\"" + pView->ResolveCommitFile(parentFilenames[i]) + L"\" ";
97 procCmd += L" /command:blame";
98 procCmd += L" /endrev:" + parentHash.ToString();
100 CCommonAppUtils::RunTortoiseGitProc(procCmd);
103 break;
104 case ID_GNUDIFF1: // fallthrough
105 case ID_COMPAREWITHPREVIOUS:
107 int index = (cmd >> 16) & 0xFFFF;
108 if (index > 0)
109 index -= 1;
111 CGitHash parentHash;
112 std::vector<CString> parentFilenames;
113 GetParentHash(pRev, index, parentHash, parentFilenames);
114 for (size_t i = 0; i < parentFilenames.size(); ++i)
116 CString procCmd = L"/path:\"" + pView->ResolveCommitFile(parentFilenames[i]) + L"\" ";
117 procCmd += L" /command:diff";
118 procCmd += L" /startrev:" + parentHash.ToString();
119 procCmd += L" /endrev:" + pRev->m_CommitHash.ToString();
120 if ((cmd & 0xFFFF) == ID_GNUDIFF1)
121 procCmd += L" /unified";
122 if (!!(GetAsyncKeyState(VK_SHIFT) & 0x8000))
123 procCmd += L" /alternative";
125 CCommonAppUtils::RunTortoiseGitProc(procCmd);
128 break;
129 case ID_COPYCLIPBOARDFULL:
130 case ID_COPYCLIPBOARDFULLNOPATHS:
131 case ID_COPYCLIPBOARDHASH:
132 case ID_COPYCLIPBOARDAUTHORSFULL:
133 case ID_COPYCLIPBOARDAUTHORSNAME:
134 case ID_COPYCLIPBOARDAUTHORSEMAIL:
135 case ID_COPYCLIPBOARDSUBJECTS:
136 case ID_COPYCLIPBOARDMESSAGES:
137 CopySelectionToClipBoard(cmd & 0xFFFF);
138 break;
139 case ID_EXPORT:
140 RunTortoiseGitProcWithCurrentRev(L"export", pRev);
141 break;
142 case ID_CREATE_BRANCH:
143 RunTortoiseGitProcWithCurrentRev(L"branch", pRev);
144 break;
145 case ID_CREATE_TAG:
146 RunTortoiseGitProcWithCurrentRev(L"tag", pRev);
147 break;
148 case ID_SWITCHTOREV:
149 RunTortoiseGitProcWithCurrentRev(L"switch", pRev);
150 break;
151 case ID_LOG:
153 CString procCmd;
154 procCmd.Format(L"/command:log /path:\"%s\" /endrev:%s /rev:%s", (LPCTSTR)((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName(), (LPCTSTR)pRev->m_CommitHash.ToString(), (LPCTSTR)pRev->m_CommitHash.ToString());
155 CCommonAppUtils::RunTortoiseGitProc(procCmd);
157 break;
158 case ID_REPOBROWSE:
159 RunTortoiseGitProcWithCurrentRev(L"repobrowser", pRev, ((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName());
160 break;
161 case ID_SHOWBRANCHES:
162 RunTortoiseGitProcWithCurrentRev(L"commitisonrefs", pRev);
163 break;
164 default:
165 //CMessageBox::Show(nullptr, L"Have not implemented", L"TortoiseGit", MB_OK);
166 break;
167 } // switch (cmd)
170 void CGitBlameLogList::GetPaths(const CGitHash& hash, std::vector<CTGitPath>& paths)
172 CTortoiseGitBlameView *pView = DYNAMIC_DOWNCAST(CTortoiseGitBlameView,((CMainFrame*)::AfxGetApp()->GetMainWnd())->GetActiveView());
173 if (pView)
176 std::set<CString> filenames;
177 auto numberOfLines = pView->m_data.GetNumberOfLines();
178 for (size_t i = 0; i < numberOfLines; ++i)
180 if (pView->m_data.GetHash(i) == hash)
181 filenames.insert(pView->m_data.GetFilename(i));
183 for (auto it = filenames.cbegin(); it != filenames.cend(); ++it)
184 paths.emplace_back(*it);
186 if (paths.empty())
188 // in case the hash does not exist in the blame output but it exists in the log follow only the file
189 paths.push_back(pView->GetDocument()->m_GitPath);
194 void CGitBlameLogList::GetParentNumbers(GitRevLoglist* pRev, const std::vector<CTGitPath>& paths, std::set<int>& parentNos)
196 if (pRev->m_ParentHash.empty())
198 if (pRev->GetParentFromHash(pRev->m_CommitHash))
199 MessageBox(pRev->GetLastErr(), L"TortoiseGit", MB_ICONERROR);
202 GIT_REV_LIST allParentHash;
203 CGitLogListBase::GetParentHashes(pRev, allParentHash);
207 const CTGitPathList& files = pRev->GetFiles(nullptr);
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(L"Could not get files of parents.\nlibgit reports:\n" + CString(msg), L"TortoiseGit", MB_ICONERROR);
238 void CGitBlameLogList::GetParentHash(GitRevLoglist* 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(nullptr);
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) && (size_t)parentNo < pRev->m_ParentHash.size())
279 parentFilenames.push_back( (action & CTGitPath::LOGACTIONS_REPLACED) ? file.GetGitOldPathString() : file.GetGitPathString());
286 catch (const char* msg)
288 MessageBox(L"Could not get files of parents.\nlibgit reports:\n" + CString(msg), L"TortoiseGit", MB_ICONERROR);