1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2020 - TortoiseGit
4 // Copyright (C) 2007-2008 - TortoiseSVN
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 Description: start-up repository opening and reading
23 Author: Marco Costalba (C) 2005-2007
25 Copyright: See COPYING file that comes with this distribution
30 #include "TortoiseProc.h"
34 #include "GitLogListBase.h"
35 #include "UnicodeUtils.h"
37 void CLogDataVector::ClearAll()
46 m_pLogCache
->ClearAllLanes();
49 //CLogDataVector Class
50 int CLogDataVector::ParserFromLog(CTGitPath
* path
, DWORD count
, DWORD infomask
, CString
* range
)
52 ATLASSERT(m_pLogCache
);
53 ATLASSERT(!(infomask
& CGit::LOG_INFO_FULL_DIFF
));
54 // only enable --follow on files
55 if ((!path
|| path
->IsDirectory()) && (infomask
& CGit::LOG_INFO_FOLLOW
))
56 infomask
= infomask
^ CGit::LOG_INFO_FOLLOW
;
58 CString gitrange
= L
"HEAD";
63 filter
.m_NumberOfLogsScale
= CFilterData::SHOW_NO_LIMIT
;
66 filter
.m_NumberOfLogs
= count
;
67 filter
.m_NumberOfLogsScale
= CFilterData::SHOW_LAST_N_COMMITS
;
69 CString cmd
= g_Git
.GetLogCmd(gitrange
, path
, infomask
, &filter
, m_logOrderBy
);
71 if (!g_Git
.CanParseRev(gitrange
))
76 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
77 g_Git
.ForceReInitDll();
79 catch (const char* msg
)
81 MessageBox(nullptr, L
"Could not initialize libgit.\nlibgit reports:\n" + CString(msg
), L
"TortoiseGit", MB_ICONERROR
);
88 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
89 if (git_open_log(&handle
, CUnicodeUtils::GetUTF8(cmd
)))
94 MessageBox(nullptr, L
"Could not open log.\nlibgit reports:\n" + CString(msg
), L
"TortoiseGit", MB_ICONERROR
);
100 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
101 [&]{ git_get_log_firstcommit(handle
); }();
105 MessageBox(nullptr, L
"Could not get first commit.\nlibgit reports:\n" + CString(msg
), L
"TortoiseGit", MB_ICONERROR
);
109 if (CGitMailmap::ShouldLoadMailmap())
110 GitRevLoglist::s_Mailmap
= std::make_shared
<CGitMailmap
>();
111 else if (GitRevLoglist::s_Mailmap
)
112 GitRevLoglist::s_Mailmap
= nullptr;
113 auto mailmap
= GitRevLoglist::s_Mailmap
;
122 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
123 [&]{ ret
= git_get_log_nextcommit(handle
, &commit
, infomask
& CGit::LOG_INFO_FOLLOW
); }();
127 MessageBox(nullptr, L
"Could not get next commit.\nlibgit reports:\n" + CString(msg
), L
"TortoiseGit", MB_ICONERROR
);
133 if (ret
!= -2) // other than end of revision walking
134 MessageBox(nullptr, (L
"Could not get next commit.\nlibgit returns:" + std::to_wstring(ret
)).c_str(), L
"TortoiseGit", MB_ICONERROR
);
138 if (commit
.m_ignore
== 1)
140 git_free_commit(&commit
);
144 CGitHash hash
= CGitHash::FromRaw(commit
.m_hash
);
146 GitRevLoglist
* pRev
= this->m_pLogCache
->GetCacheData(hash
);
148 char *pNote
= nullptr;
150 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
151 git_get_notes(commit
.m_hash
, &pNote
);
155 pRev
->m_Notes
= CUnicodeUtils::GetUnicode(pNote
);
160 pRev
->Parse(&commit
, mailmap
.get());
161 git_free_commit(&commit
);
163 this->push_back(pRev
->m_CommitHash
);
165 m_HashMap
[pRev
->m_CommitHash
] = size() - 1;
169 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
170 git_close_log(handle
);
176 struct SortByParentDate
178 bool operator()(GitRevLoglist
* pLhs
, GitRevLoglist
* pRhs
) const
180 if (pLhs
->m_CommitHash
== pRhs
->m_CommitHash
)
182 if (std::any_of(pLhs
->m_ParentHash
.cbegin(), pLhs
->m_ParentHash
.cend(), [&pRhs
](auto& hash
) { return hash
== pRhs
->m_CommitHash
; }))
184 if (std::any_of(pRhs
->m_ParentHash
.cbegin(), pRhs
->m_ParentHash
.cend(), [&pLhs
](auto& hash
) { return hash
== pLhs
->m_CommitHash
; }))
186 return pLhs
->GetCommitterDate()>pRhs
->GetCommitterDate();
190 int CLogDataVector::Fill(const std::unordered_set
<CGitHash
>& hashes
)
192 ATLASSERT(m_pLogCache
);
195 g_Git
.ForceReInitDll();
197 catch (const char* msg
)
199 MessageBox(nullptr, L
"Could not initialize libgit.\nlibgit reports:\n" + CString(msg
), L
"TortoiseGit", MB_ICONERROR
);
203 if (CGitMailmap::ShouldLoadMailmap())
204 GitRevLoglist::s_Mailmap
= std::make_shared
<CGitMailmap
>();
205 else if (GitRevLoglist::s_Mailmap
)
206 GitRevLoglist::s_Mailmap
= nullptr;
207 auto mailmap
= GitRevLoglist::s_Mailmap
;
209 std::set
<GitRevLoglist
*, SortByParentDate
> revs
;
210 for (const auto& hash
: hashes
)
215 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
216 if (git_get_commit_from_hash(&commit
, hash
.ToRaw()))
221 MessageBox(nullptr, L
"Could not get commit \"" + hash
.ToString() + L
"\".\nlibgit reports:\n" + CString(msg
), L
"TortoiseGit", MB_ICONERROR
);
225 GitRevLoglist
* pRev
= this->m_pLogCache
->GetCacheData(hash
);
227 // right now this code is only used by TortoiseGitBlame,
228 // as such git notes are not needed to be loaded
230 pRev
->Parse(&commit
, mailmap
.get());
231 git_free_commit(&commit
);
236 for (const auto& pRev
: revs
)
238 this->push_back(pRev
->m_CommitHash
);
239 m_HashMap
[pRev
->m_CommitHash
] = size() - 1;
245 void CLogDataVector::append(CGitHash
& sha
, bool storeInVector
)
248 this->push_back(sha
);
250 GitRevLoglist
* r
= &m_pLogCache
->m_HashMap
[sha
];
251 updateLanes(*r
, this->m_Lns
, sha
);
254 void CLogDataVector::setLane(const CGitHash
& sha
)
256 Lanes
* l
= &(this->m_Lns
);
257 int i
= m_FirstFreeLane
;
259 // QVector<QByteArray> ba;
260 // const ShaString& ss = toPersistentSha(sha, ba);
261 // const ShaVect& shaVec(fh->revOrder);
263 for (int cnt
= static_cast<int>(size()); i
< cnt
; ++i
) {
264 GitRevLoglist
* r
= &this->GetGitRevAt(i
);
265 CGitHash curSha
=r
->m_CommitHash
;
267 if (r
->m_Lanes
.empty())
268 updateLanes(*r
, *l
, curSha
);
273 m_FirstFreeLane
= ++i
;
276 Lanes
* l
= &(this->m_Lanes
);
277 int i
= m_FirstFreeLane
;
279 QVector
<QByteArray
> ba
;
280 const ShaString
& ss
= toPersistentSha(sha
, ba
);
281 const ShaVect
& shaVec(fh
->revOrder
);
283 for (uint cnt
= shaVec
.count(); i
< cnt
; ++i
) {
284 const ShaString
& curSha
= shaVec
[i
];
285 Rev
* r
= m_HashMap
[curSha
]const_cast<Rev
*>(revLookup(curSha
, fh
));
286 if (r
->lanes
.count() == 0)
287 updateLanes(*r
, *l
, curSha
);
292 fh
->firstFreeLane
= ++i
;
296 void CLogDataVector::updateLanes(GitRevLoglist
& c
, Lanes
& lns
, const CGitHash
& sha
)
298 // we could get third argument from c.sha(), but we are in fast path here
299 // and c.sha() involves a deep copy, so we accept a little redundancy
304 bool isDiscontinuity
;
305 bool isFork
= lns
.isFork(sha
, isDiscontinuity
);
306 bool isMerge
= (c
.ParentsCount() > 1);
307 bool isInitial
= (c
.ParentsCount() == 0);
310 lns
.changeActiveLane(sha
); // uses previous isBoundary state
312 lns
.setBoundary(c
.IsBoundary() == TRUE
, isInitial
); // update must be here
313 //TRACE(L"%s %d", static_cast<LPCTSTR>(c.m_CommitHash.ToString()), c.IsBoundary());
318 lns
.setMerge(c
.m_ParentHash
);
324 lns
.getLanes(c
.m_Lanes
); // here lanes are snapshotted
328 nextSha
= c
.m_ParentHash
[0];
330 lns
.nextParent(nextSha
);
333 // lns.afterApplied();