Fixed issue #3668: "Revert to revision" fails for added files
[TortoiseGit.git] / src / TortoiseProc / LogDataVector.cpp
blob073a4264fd8cf2d533b886b5ca4f77c43326407e
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
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 void CLogDataVector::ClearAll()
39 clear();
40 m_HashMap.clear();
41 m_Lns.clear();
43 m_FirstFreeLane=0;
44 m_Lns.clear();
45 if (m_pLogCache)
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";
59 if (range != nullptr)
60 gitrange = *range;
61 CFilterData filter;
62 if (count == 0)
63 filter.m_NumberOfLogsScale = CFilterData::SHOW_NO_LIMIT;
64 else
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))
72 return -1;
74 try
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);
82 return -1;
85 GIT_LOG handle;
86 try
88 CAutoLocker lock(g_Git.m_critGitDllSec);
89 if (git_open_log(&handle, CUnicodeUtils::GetUTF8(cmd)))
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 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;
115 int ret = 0;
116 while (ret == 0)
118 GIT_COMMIT commit;
122 CAutoLocker lock(g_Git.m_critGitDllSec);
123 [&]{ ret = git_get_log_nextcommit(handle, &commit, infomask & CGit::LOG_INFO_FOLLOW); }();
125 catch (char* msg)
127 MessageBox(nullptr, L"Could not get next commit.\nlibgit reports:\n" + CString(msg), L"TortoiseGit", MB_ICONERROR);
128 break;
131 if (ret)
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);
135 break;
138 if (commit.m_ignore == 1)
140 git_free_commit(&commit);
141 continue;
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);
153 if (pNote)
155 pRev->m_Notes = CUnicodeUtils::GetUnicode(pNote);
156 free(pNote);
157 pNote = nullptr;
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);
173 return 0;
176 struct SortByParentDate
178 bool operator()(GitRevLoglist* pLhs, GitRevLoglist* pRhs) const
180 if (pLhs->m_CommitHash == pRhs->m_CommitHash)
181 return false;
182 if (std::any_of(pLhs->m_ParentHash.cbegin(), pLhs->m_ParentHash.cend(), [&pRhs](auto& hash) { return hash == pRhs->m_CommitHash; }))
183 return true;
184 if (std::any_of(pRhs->m_ParentHash.cbegin(), pRhs->m_ParentHash.cend(), [&pLhs](auto& hash) { return hash == pLhs->m_CommitHash; }))
185 return false;
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);
200 return -1;
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)
212 GIT_COMMIT commit;
215 CAutoLocker lock(g_Git.m_critGitDllSec);
216 if (git_get_commit_from_hash(&commit, hash.ToRaw()))
217 return -1;
219 catch (char * msg)
221 MessageBox(nullptr, L"Could not get commit \"" + hash.ToString() + L"\".\nlibgit reports:\n" + CString(msg), L"TortoiseGit", MB_ICONERROR);
222 return -1;
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);
233 revs.insert(pRev);
236 for (const auto& pRev : revs)
238 this->push_back(pRev->m_CommitHash);
239 m_HashMap[pRev->m_CommitHash] = size() - 1;
242 return 0;
245 void CLogDataVector::append(CGitHash& sha, bool storeInVector)
247 if (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);
270 if (curSha == sha)
271 break;
273 m_FirstFreeLane = ++i;
275 #if 0
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);
289 if (curSha == ss)
290 break;
292 fh->firstFreeLane = ++i;
293 #endif
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
301 if (lns.isEmpty())
302 lns.init(sha);
304 bool isDiscontinuity;
305 bool isFork = lns.isFork(sha, isDiscontinuity);
306 bool isMerge = (c.ParentsCount() > 1);
307 bool isInitial = (c.ParentsCount() == 0);
309 if (isDiscontinuity)
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());
315 if (isFork)
316 lns.setFork(sha);
317 if (isMerge)
318 lns.setMerge(c.m_ParentHash);
319 //if (c.isApplied)
320 // lns.setApplied();
321 if (isInitial)
322 lns.setInitial();
324 lns.getLanes(c.m_Lanes); // here lanes are snapshotted
326 CGitHash nextSha;
327 if( !isInitial)
328 nextSha = c.m_ParentHash[0];
330 lns.nextParent(nextSha);
332 //if (c.isApplied)
333 // lns.afterApplied();
334 if (isMerge)
335 lns.afterMerge();
336 if (isFork)
338 lns.afterFork();
339 if (isInitial)
340 lns.setInitial();
342 if (lns.isBranch())
343 lns.afterBranch();