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.
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(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
)
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", 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
);
78 CTortoiseGitBlameView
*pView
= DYNAMIC_DOWNCAST(CTortoiseGitBlameView
,((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView());
80 GitRev
*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
);
149 RunTortoiseGitProcWithCurrentRev(_T("log"), pRev
, ((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName());
152 RunTortoiseGitProcWithCurrentRev(_T("repobrowser"), pRev
, ((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView()->GetDocument()->GetPathName());
155 //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
160 void CGitBlameLogList::GetPaths(const CGitHash
& hash
, std::vector
<CTGitPath
>& paths
)
162 CTortoiseGitBlameView
*pView
= DYNAMIC_DOWNCAST(CTortoiseGitBlameView
,((CMainFrame
*)::AfxGetApp()->GetMainWnd())->GetActiveView());
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
));
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
);
249 for (auto it
= parentNos
.cbegin(); it
!= parentNos
.cend(); ++it
, ++i
)
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
);