Make use of unordered_set and unordered_map for CGitHash
[TortoiseGit.git] / src / TortoiseProc / LogDataVector.cpp
blobf3d58705037d3b717371daee8e1d4239c8a068f3
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2013, 2015-2016 - 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
29 #include "stdafx.h"
30 #include "TortoiseProc.h"
31 #include "Git.h"
32 #include "GitHash.h"
33 #include "TGitPath.h"
34 #include "GitLogListBase.h"
35 #include "UnicodeUtils.h"
37 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
39 void CLogDataVector::ClearAll()
41 clear();
42 m_HashMap.clear();
43 m_Lns.clear();
45 m_FirstFreeLane=0;
46 m_Lns.clear();
47 if (m_pLogCache)
48 m_pLogCache->ClearAllLanes();
51 //CLogDataVector Class
52 int CLogDataVector::ParserFromLog(CTGitPath* path, DWORD count, DWORD infomask, CString* range)
54 ATLASSERT(m_pLogCache);
55 // only enable --follow on files
56 if ((!path || path->IsDirectory()) && (infomask & CGit::LOG_INFO_FOLLOW))
57 infomask = infomask ^ CGit::LOG_INFO_FOLLOW;
59 CString gitrange = L"HEAD";
60 if (range != nullptr)
61 gitrange = *range;
62 CFilterData filter;
63 if (count == 0)
64 filter.m_NumberOfLogsScale = CFilterData::SHOW_NO_LIMIT;
65 else
67 filter.m_NumberOfLogs = count;
68 filter.m_NumberOfLogsScale = CFilterData::SHOW_LAST_N_COMMITS;
70 CString cmd = g_Git.GetLogCmd(gitrange, path, infomask, &filter, m_logOrderBy);
72 if (!g_Git.CanParseRev(gitrange))
73 return -1;
75 try
77 [] { git_init(); } ();
79 catch (const char* msg)
81 MessageBox(nullptr, L"Could not initialize libgit.\nlibgit reports:\n" + CString(msg), L"TortoiseGit", MB_ICONERROR);
82 return -1;
85 GIT_LOG handle;
86 try
88 CAutoLocker lock(g_Git.m_critGitDllSec);
89 if (git_open_log(&handle,CUnicodeUtils::GetMulti(cmd, CP_UTF8).GetBuffer()))
90 return -1;
92 catch (char* msg)
94 MessageBox(nullptr, L"Could not open log.\nlibgit reports:\n" + CString(msg), L"TortoiseGit", MB_ICONERROR);
95 return -1;
98 try
100 CAutoLocker lock(g_Git.m_critGitDllSec);
101 [&]{ git_get_log_firstcommit(handle); }();
103 catch (char* msg)
105 MessageBox(nullptr, L"Could not get first commit.\nlibgit reports:\n" + CString(msg), L"TortoiseGit", MB_ICONERROR);
106 return -1;
109 int ret = 0;
110 while (ret == 0)
112 GIT_COMMIT commit;
116 CAutoLocker lock(g_Git.m_critGitDllSec);
117 [&]{ ret = git_get_log_nextcommit(handle, &commit, infomask & CGit::LOG_INFO_FOLLOW); }();
119 catch (char* msg)
121 MessageBox(nullptr, L"Could not get next commit.\nlibgit reports:\n" + CString(msg), L"TortoiseGit", MB_ICONERROR);
122 break;
125 if (ret)
127 if (ret != -2) // other than end of revision walking
128 MessageBox(nullptr, (L"Could not get next commit.\nlibgit returns:" + std::to_wstring(ret)).c_str(), L"TortoiseGit", MB_ICONERROR);
129 break;
132 if (commit.m_ignore == 1)
134 git_free_commit(&commit);
135 continue;
138 CGitHash hash(commit.m_hash);
140 GitRevLoglist* pRev = this->m_pLogCache->GetCacheData(hash);
142 char *pNote = nullptr;
144 CAutoLocker lock(g_Git.m_critGitDllSec);
145 git_get_notes(commit.m_hash, &pNote);
147 if (pNote)
149 pRev->m_Notes = CUnicodeUtils::GetUnicode(pNote);
150 free(pNote);
151 pNote = nullptr;
154 pRev->ParserFromCommit(&commit);
155 pRev->ParserParentFromCommit(&commit);
156 git_free_commit(&commit);
157 // Must call free commit before SafeFetchFullInfo, commit parent is rewrite by log.
158 // file list will wrong if parent rewrite.
160 if (!pRev->m_IsFull && (infomask & CGit::LOG_INFO_FULL_DIFF))
162 if (pRev->SafeFetchFullInfo(&g_Git))
164 MessageBox(nullptr, pRev->GetLastErr(), L"TortoiseGit", MB_ICONERROR);
165 return -1;
169 this->push_back(pRev->m_CommitHash);
171 m_HashMap[pRev->m_CommitHash] = (int)size() - 1;
175 CAutoLocker lock(g_Git.m_critGitDllSec);
176 git_close_log(handle);
179 return 0;
182 struct SortByParentDate
184 bool operator()(GitRevLoglist* pLhs, GitRevLoglist* pRhs)
186 if (pLhs->m_CommitHash == pRhs->m_CommitHash)
187 return false;
188 for (const auto& hash : pLhs->m_ParentHash)
190 if (hash == pRhs->m_CommitHash)
191 return true;
193 for (const auto& hash : pRhs->m_ParentHash)
195 if (hash == pLhs->m_CommitHash)
196 return false;
198 return pLhs->GetCommitterDate()>pRhs->GetCommitterDate();
202 int CLogDataVector::Fill(std::unordered_set<CGitHash>& hashes)
204 ATLASSERT(m_pLogCache);
207 [] { git_init(); } ();
209 catch (const char* msg)
211 MessageBox(nullptr, L"Could not initialize libgit.\nlibgit reports:\n" + CString(msg), L"TortoiseGit", MB_ICONERROR);
212 return -1;
215 std::set<GitRevLoglist*, SortByParentDate> revs;
217 for (const auto& hash : hashes)
219 GIT_COMMIT commit;
222 CAutoLocker lock(g_Git.m_critGitDllSec);
223 if (git_get_commit_from_hash(&commit, hash.m_hash))
224 return -1;
226 catch (char * msg)
228 MessageBox(nullptr, L"Could not get commit \"" + hash.ToString() + L"\".\nlibgit reports:\n" + CString(msg), L"TortoiseGit", MB_ICONERROR);
229 return -1;
232 GitRevLoglist* pRev = this->m_pLogCache->GetCacheData(hash);
234 // right now this code is only used by TortoiseGitBlame,
235 // as such git notes are not needed to be loaded
237 pRev->ParserFromCommit(&commit);
238 pRev->ParserParentFromCommit(&commit);
239 git_free_commit(&commit);
241 revs.insert(pRev);
244 for (const auto& pRev : revs)
246 this->push_back(pRev->m_CommitHash);
247 m_HashMap[pRev->m_CommitHash] = (int)size() - 1;
250 return 0;
253 void CLogDataVector::append(CGitHash& sha, bool storeInVector)
255 if (storeInVector)
256 this->push_back(sha);
258 GitRevLoglist* r = &m_pLogCache->m_HashMap[sha];
259 updateLanes(*r, this->m_Lns, sha);
262 void CLogDataVector::setLane(CGitHash& sha)
264 Lanes* l = &(this->m_Lns);
265 int i = m_FirstFreeLane;
267 // QVector<QByteArray> ba;
268 // const ShaString& ss = toPersistentSha(sha, ba);
269 // const ShaVect& shaVec(fh->revOrder);
271 for (int cnt = (int)size(); i < cnt; ++i) {
272 GitRevLoglist* r = &this->GetGitRevAt(i);
273 CGitHash curSha=r->m_CommitHash;
275 if (r->m_Lanes.empty())
276 updateLanes(*r, *l, curSha);
278 if (curSha == sha)
279 break;
281 m_FirstFreeLane = ++i;
283 #if 0
284 Lanes* l = &(this->m_Lanes);
285 int i = m_FirstFreeLane;
287 QVector<QByteArray> ba;
288 const ShaString& ss = toPersistentSha(sha, ba);
289 const ShaVect& shaVec(fh->revOrder);
291 for (uint cnt = shaVec.count(); i < cnt; ++i) {
292 const ShaString& curSha = shaVec[i];
293 Rev* r = m_HashMap[curSha]const_cast<Rev*>(revLookup(curSha, fh));
294 if (r->lanes.count() == 0)
295 updateLanes(*r, *l, curSha);
297 if (curSha == ss)
298 break;
300 fh->firstFreeLane = ++i;
301 #endif
304 void CLogDataVector::updateLanes(GitRevLoglist& c, Lanes& lns, CGitHash& sha)
306 // we could get third argument from c.sha(), but we are in fast path here
307 // and c.sha() involves a deep copy, so we accept a little redundancy
309 if (lns.isEmpty())
310 lns.init(sha);
312 bool isDiscontinuity;
313 bool isFork = lns.isFork(sha, isDiscontinuity);
314 bool isMerge = (c.ParentsCount() > 1);
315 bool isInitial = (c.ParentsCount() == 0);
317 if (isDiscontinuity)
318 lns.changeActiveLane(sha); // uses previous isBoundary state
320 lns.setBoundary(c.IsBoundary() == TRUE); // update must be here
321 TRACE(L"%s %d", (LPCTSTR)c.m_CommitHash.ToString(), c.IsBoundary());
323 if (isFork)
324 lns.setFork(sha);
325 if (isMerge)
326 lns.setMerge(c.m_ParentHash);
327 //if (c.isApplied)
328 // lns.setApplied();
329 if (isInitial)
330 lns.setInitial();
332 lns.getLanes(c.m_Lanes); // here lanes are snapshotted
334 CGitHash nextSha;
335 if( !isInitial)
336 nextSha = c.m_ParentHash[0];
338 lns.nextParent(nextSha);
340 //if (c.isApplied)
341 // lns.afterApplied();
342 if (isMerge)
343 lns.afterMerge();
344 if (isFork)
345 lns.afterFork();
346 if (lns.isBranch())
347 lns.afterBranch();