1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013, 2015-2019 - 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.
22 #include "GitBlameLogList.h"
24 #include "GitRevLoglist.h"
25 #include "TortoiseGitBlameDoc.h"
26 #include "TortoiseGitBlameView.h"
28 #include "CommonAppUtils.h"
30 IMPLEMENT_DYNAMIC(CGitBlameLogList
, CHintCtrl
<CListCtrl
>)
32 void CGitBlameLogList::hideUnimplementedCommands()
34 m_ContextMenuMask
|= GetContextMenuBit(ID_BLAMEPREVIOUS
) | GetContextMenuBit(ID_LOG
);
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
)
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
)
61 parentHash
.push_back(pRev
->m_ParentHash
[parentNo
]);
65 void RunTortoiseGitProcWithCurrentRev(const CString
& command
, const GitRev
* pRev
, const CString
&path
= g_Git
.m_CurrentDir
)
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*/, MAP_HASH_NAME
&)
75 POSITION pos
= GetFirstSelectedItemPosition();
76 int indexNext
= GetNextSelectedItem(pos
);
79 CTortoiseGitBlameView
*pView
= DYNAMIC_DOWNCAST(CTortoiseGitBlameView
,((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView());
81 GitRevLoglist
* pRev
= &this->m_logEntries
.GetGitRevAt(indexNext
);
85 case ID_BLAMEPREVIOUS
:
87 int index
= (cmd
>> 16) & 0xFFFF;
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
);
104 case ID_GNUDIFF1
: // fallthrough
105 case ID_COMPAREWITHPREVIOUS
:
107 int index
= (cmd
>> 16) & 0xFFFF;
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
);
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);
140 RunTortoiseGitProcWithCurrentRev(L
"export", pRev
);
142 case ID_CREATE_BRANCH
:
143 RunTortoiseGitProcWithCurrentRev(L
"branch", pRev
);
146 RunTortoiseGitProcWithCurrentRev(L
"tag", pRev
);
149 RunTortoiseGitProcWithCurrentRev(L
"switch", pRev
);
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
);
159 RunTortoiseGitProcWithCurrentRev(L
"repobrowser", pRev
, ((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName());
161 case ID_SHOWBRANCHES
:
162 RunTortoiseGitProcWithCurrentRev(L
"commitisonrefs", pRev
);
165 //CMessageBox::Show(nullptr, L"Have not implemented", L"TortoiseGit", MB_OK);
170 void CGitBlameLogList::GetPaths(const CGitHash
& hash
, std::vector
<CTGitPath
>& paths
)
172 CTortoiseGitBlameView
*pView
= DYNAMIC_DOWNCAST(CTortoiseGitBlameView
,((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView());
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
);
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
);
249 for (auto it
= parentNos
.cbegin(); it
!= parentNos
.cend(); ++it
, ++i
)
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
);