1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013, 2015-2016 - 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 "TortoiseGitBlameDoc.h"
25 #include "TortoiseGitBlameView.h"
27 #include "CommonAppUtils.h"
29 IMPLEMENT_DYNAMIC(CGitBlameLogList
, CHintListCtrl
)
31 void CGitBlameLogList::hideUnimplementedCommands()
33 m_ContextMenuMask
|= GetContextMenuBit(ID_BLAMEPREVIOUS
) | GetContextMenuBit(ID_LOG
);
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
)
49 void CGitBlameLogList::GetParentHashes(GitRevLoglist
* 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
)
60 parentHash
.push_back(pRev
->m_ParentHash
[parentNo
]);
64 void RunTortoiseGitProcWithCurrentRev(const CString
& command
, const GitRev
* pRev
, const CString
&path
= g_Git
.m_CurrentDir
)
68 procCmd
.Format(L
"/command:%s /path:\"%s\" /rev:%s", (LPCTSTR
)command
, (LPCTSTR
)path
, (LPCTSTR
)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
);
78 CTortoiseGitBlameView
*pView
= DYNAMIC_DOWNCAST(CTortoiseGitBlameView
,((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView());
80 GitRevLoglist
* pRev
= &this->m_logEntries
.GetGitRevAt(indexNext
);
84 case ID_BLAMEPREVIOUS
:
86 int index
= (cmd
>> 16) & 0xFFFF;
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
);
103 case ID_GNUDIFF1
: // fallthrough
104 case ID_COMPAREWITHPREVIOUS
:
106 int index
= (cmd
>> 16) & 0xFFFF;
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
);
126 case ID_COPYCLIPBOARD
:
128 CopySelectionToClipBoard();
133 CopySelectionToClipBoard(ID_COPY_HASH
);
137 RunTortoiseGitProcWithCurrentRev(_T("export"), pRev
);
139 case ID_CREATE_BRANCH
:
140 RunTortoiseGitProcWithCurrentRev(_T("branch"), pRev
);
143 RunTortoiseGitProcWithCurrentRev(_T("tag"), pRev
);
146 RunTortoiseGitProcWithCurrentRev(_T("switch"), pRev
);
151 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());
152 CCommonAppUtils::RunTortoiseGitProc(procCmd
);
156 RunTortoiseGitProcWithCurrentRev(_T("repobrowser"), pRev
, ((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName());
159 //CMessageBox::Show(nullptr, _T("Have not implemented"), _T("TortoiseGit"), MB_OK);
164 void CGitBlameLogList::GetPaths(const CGitHash
& hash
, std::vector
<CTGitPath
>& paths
)
166 CTortoiseGitBlameView
*pView
= DYNAMIC_DOWNCAST(CTortoiseGitBlameView
,((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView());
170 std::set
<CString
> filenames
;
171 int numberOfLines
= pView
->m_data
.GetNumberOfLines();
172 for (int i
= 0; i
< numberOfLines
; ++i
)
174 if (pView
->m_data
.GetHash(i
) == hash
)
175 filenames
.insert(pView
->m_data
.GetFilename(i
));
177 for (auto it
= filenames
.cbegin(); it
!= filenames
.cend(); ++it
)
178 paths
.emplace_back(*it
);
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(GitRevLoglist
* pRev
, const std::vector
<CTGitPath
>& paths
, std::set
<int>& parentNos
)
190 if (pRev
->m_ParentHash
.empty())
192 if (pRev
->GetParentFromHash(pRev
->m_CommitHash
))
193 MessageBox(pRev
->GetLastErr(), _T("TortoiseGit"), MB_ICONERROR
);
196 GIT_REV_LIST allParentHash
;
197 CGitLogListBase::GetParentHashes(pRev
, allParentHash
);
201 const CTGitPathList
& files
= pRev
->GetFiles(nullptr);
202 for (int j
=0, j_size
= files
.GetCount(); j
< j_size
; ++j
)
204 const CTGitPath
&file
= files
[j
];
205 for (auto it
=paths
.cbegin(); it
!= paths
.cend(); ++it
)
207 const CTGitPath
& path
= *it
;
208 if (file
.IsEquivalentTo(path
))
210 if (!(file
.m_ParentNo
& MERGE_MASK
))
212 int action
= file
.m_Action
;
213 // ignore (action & CTGitPath::LOGACTIONS_ADDED), as then there is nothing to blame/diff
214 // ignore (action & CTGitPath::LOGACTIONS_DELETED), should never happen as the file must exist
215 if (action
& (CTGitPath::LOGACTIONS_MODIFIED
| CTGitPath::LOGACTIONS_REPLACED
))
217 int parentNo
= file
.m_ParentNo
& PARENT_MASK
;
218 if (parentNo
>= 0 && (size_t)parentNo
< pRev
->m_ParentHash
.size())
219 parentNos
.insert(parentNo
);
226 catch (const char* msg
)
228 MessageBox(_T("Could not get files of parents.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);
232 void CGitBlameLogList::GetParentHash(GitRevLoglist
* pRev
, int index
, CGitHash
& parentHash
, std::vector
<CString
>& parentFilenames
)
234 std::vector
<CTGitPath
> paths
;
235 GetPaths(pRev
->m_CommitHash
, paths
);
237 std::set
<int> parentNos
;
238 GetParentNumbers(pRev
, paths
, parentNos
);
243 for (auto it
= parentNos
.cbegin(); it
!= parentNos
.cend(); ++it
, ++i
)
252 parentHash
= pRev
->m_ParentHash
[parentNo
];
256 const CTGitPathList
& files
= pRev
->GetFiles(nullptr);
257 for (int j
= 0, j_size
= files
.GetCount(); j
< j_size
; ++j
)
259 const CTGitPath
&file
= files
[j
];
260 for (auto it
= paths
.cbegin(); it
!= paths
.cend(); ++it
)
262 const CTGitPath
& path
= *it
;
263 if (file
.IsEquivalentTo(path
))
265 if (!(file
.m_ParentNo
& MERGE_MASK
))
267 int action
= file
.m_Action
;
268 // ignore (action & CTGitPath::LOGACTIONS_ADDED), as then there is nothing to blame/diff
269 // ignore (action & CTGitPath::LOGACTIONS_DELETED), should never happen as the file must exist
270 if (action
& (CTGitPath::LOGACTIONS_MODIFIED
| CTGitPath::LOGACTIONS_REPLACED
))
272 if (parentNo
== (file
.m_ParentNo
& PARENT_MASK
) && (size_t)parentNo
< pRev
->m_ParentHash
.size())
273 parentFilenames
.push_back( (action
& CTGitPath::LOGACTIONS_REPLACED
) ? file
.GetGitOldPathString() : file
.GetGitPathString());
280 catch (const char* msg
)
282 MessageBox(_T("Could not get files of parents.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);