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
, CHintCtrl
<CListCtrl
>)
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");
121 if (!!(GetAsyncKeyState(VK_SHIFT
) & 0x8000))
122 procCmd
+= L
" /alternative";
124 CCommonAppUtils::RunTortoiseGitProc(procCmd
);
128 case ID_COPYCLIPBOARD
:
130 CopySelectionToClipBoard();
135 CopySelectionToClipBoard(ID_COPY_HASH
);
139 RunTortoiseGitProcWithCurrentRev(_T("export"), pRev
);
141 case ID_CREATE_BRANCH
:
142 RunTortoiseGitProcWithCurrentRev(_T("branch"), pRev
);
145 RunTortoiseGitProcWithCurrentRev(_T("tag"), pRev
);
148 RunTortoiseGitProcWithCurrentRev(_T("switch"), pRev
);
153 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());
154 CCommonAppUtils::RunTortoiseGitProc(procCmd
);
158 RunTortoiseGitProcWithCurrentRev(_T("repobrowser"), pRev
, ((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName());
161 //CMessageBox::Show(nullptr, _T("Have not implemented"), _T("TortoiseGit"), MB_OK);
166 void CGitBlameLogList::GetPaths(const CGitHash
& hash
, std::vector
<CTGitPath
>& paths
)
168 CTortoiseGitBlameView
*pView
= DYNAMIC_DOWNCAST(CTortoiseGitBlameView
,((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView());
172 std::set
<CString
> filenames
;
173 int numberOfLines
= pView
->m_data
.GetNumberOfLines();
174 for (int i
= 0; i
< numberOfLines
; ++i
)
176 if (pView
->m_data
.GetHash(i
) == hash
)
177 filenames
.insert(pView
->m_data
.GetFilename(i
));
179 for (auto it
= filenames
.cbegin(); it
!= filenames
.cend(); ++it
)
180 paths
.emplace_back(*it
);
184 // in case the hash does not exist in the blame output but it exists in the log follow only the file
185 paths
.push_back(pView
->GetDocument()->m_GitPath
);
190 void CGitBlameLogList::GetParentNumbers(GitRevLoglist
* pRev
, const std::vector
<CTGitPath
>& paths
, std::set
<int>& parentNos
)
192 if (pRev
->m_ParentHash
.empty())
194 if (pRev
->GetParentFromHash(pRev
->m_CommitHash
))
195 MessageBox(pRev
->GetLastErr(), _T("TortoiseGit"), MB_ICONERROR
);
198 GIT_REV_LIST allParentHash
;
199 CGitLogListBase::GetParentHashes(pRev
, allParentHash
);
203 const CTGitPathList
& files
= pRev
->GetFiles(nullptr);
204 for (int j
=0, j_size
= files
.GetCount(); j
< j_size
; ++j
)
206 const CTGitPath
&file
= files
[j
];
207 for (auto it
=paths
.cbegin(); it
!= paths
.cend(); ++it
)
209 const CTGitPath
& path
= *it
;
210 if (file
.IsEquivalentTo(path
))
212 if (!(file
.m_ParentNo
& MERGE_MASK
))
214 int action
= file
.m_Action
;
215 // ignore (action & CTGitPath::LOGACTIONS_ADDED), as then there is nothing to blame/diff
216 // ignore (action & CTGitPath::LOGACTIONS_DELETED), should never happen as the file must exist
217 if (action
& (CTGitPath::LOGACTIONS_MODIFIED
| CTGitPath::LOGACTIONS_REPLACED
))
219 int parentNo
= file
.m_ParentNo
& PARENT_MASK
;
220 if (parentNo
>= 0 && (size_t)parentNo
< pRev
->m_ParentHash
.size())
221 parentNos
.insert(parentNo
);
228 catch (const char* msg
)
230 MessageBox(_T("Could not get files of parents.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);
234 void CGitBlameLogList::GetParentHash(GitRevLoglist
* pRev
, int index
, CGitHash
& parentHash
, std::vector
<CString
>& parentFilenames
)
236 std::vector
<CTGitPath
> paths
;
237 GetPaths(pRev
->m_CommitHash
, paths
);
239 std::set
<int> parentNos
;
240 GetParentNumbers(pRev
, paths
, parentNos
);
245 for (auto it
= parentNos
.cbegin(); it
!= parentNos
.cend(); ++it
, ++i
)
254 parentHash
= pRev
->m_ParentHash
[parentNo
];
258 const CTGitPathList
& files
= pRev
->GetFiles(nullptr);
259 for (int j
= 0, j_size
= files
.GetCount(); j
< j_size
; ++j
)
261 const CTGitPath
&file
= files
[j
];
262 for (auto it
= paths
.cbegin(); it
!= paths
.cend(); ++it
)
264 const CTGitPath
& path
= *it
;
265 if (file
.IsEquivalentTo(path
))
267 if (!(file
.m_ParentNo
& MERGE_MASK
))
269 int action
= file
.m_Action
;
270 // ignore (action & CTGitPath::LOGACTIONS_ADDED), as then there is nothing to blame/diff
271 // ignore (action & CTGitPath::LOGACTIONS_DELETED), should never happen as the file must exist
272 if (action
& (CTGitPath::LOGACTIONS_MODIFIED
| CTGitPath::LOGACTIONS_REPLACED
))
274 if (parentNo
== (file
.m_ParentNo
& PARENT_MASK
) && (size_t)parentNo
< pRev
->m_ParentHash
.size())
275 parentFilenames
.push_back( (action
& CTGitPath::LOGACTIONS_REPLACED
) ? file
.GetGitOldPathString() : file
.GetGitPathString());
282 catch (const char* msg
)
284 MessageBox(_T("Could not get files of parents.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);