Make sure buffer is large enough for the nul terminator
[TortoiseGit.git] / src / TortoiseProc / LogDataVector.cpp
blob84b58fe5544c814cb4ec3cf97d21b5e8b045bb92
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2013, 2015 - 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()
42 clear();
43 m_HashMap.clear();
44 m_Lns.clear();
46 m_FirstFreeLane=0;
47 m_Lns.clear();
48 if (m_pLogCache)
50 m_pLogCache->ClearAllLanes();
53 m_RawlogData.clear();
54 m_RawLogStart.clear();
57 //CLogDataVector Class
58 int CLogDataVector::ParserFromLog(CTGitPath *path, int count, int infomask, CString *range)
60 // only enable --follow on files
61 if ((path == NULL || path->IsDirectory()) && (infomask & CGit::LOG_INFO_FOLLOW))
62 infomask = infomask ^ CGit::LOG_INFO_FOLLOW;
64 CString gitrange = _T("HEAD");
65 if (range != nullptr)
66 gitrange = *range;
67 CString cmd = g_Git.GetLogCmd(gitrange, path, count, infomask, true);
69 if (!g_Git.CanParseRev(gitrange))
70 return 0;
72 try
74 [] { git_init(); } ();
76 catch (const char* msg)
78 MessageBox(NULL, _T("Could not initialize libgit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
79 return -1;
82 GIT_LOG handle;
83 try
85 CAutoLocker lock(g_Git.m_critGitDllSec);
86 if (git_open_log(&handle,CUnicodeUtils::GetMulti(cmd, CP_UTF8).GetBuffer()))
88 return -1;
91 catch (char* msg)
93 MessageBox(NULL, _T("Could not open log.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
94 return -1;
97 try
99 CAutoLocker lock(g_Git.m_critGitDllSec);
100 [&]{ git_get_log_firstcommit(handle); }();
102 catch (char* msg)
104 MessageBox(NULL, _T("Could not get first commit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
105 return -1;
108 int ret = 0;
109 while (ret == 0)
111 GIT_COMMIT commit;
115 CAutoLocker lock(g_Git.m_critGitDllSec);
116 [&]{ ret = git_get_log_nextcommit(handle, &commit, infomask & CGit::LOG_INFO_FOLLOW); }();
118 catch (char* msg)
120 MessageBox(NULL, _T("Could not get next commit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
121 break;
124 if (ret)
126 if (ret != -2) // other than end of revision walking
127 MessageBox(nullptr, (_T("Could not get next commit.\nlibgit returns:") + std::to_wstring(ret)).c_str(), _T("TortoiseGit"), MB_ICONERROR);
128 break;
131 if (commit.m_ignore == 1)
133 git_free_commit(&commit);
134 continue;
137 CGitHash hash = (char*)commit.m_hash ;
139 GitRevLoglist* pRev = this->m_pLogCache->GetCacheData(hash);
141 char *pNote = nullptr;
143 CAutoLocker lock(g_Git.m_critGitDllSec);
144 git_get_notes(commit.m_hash, &pNote);
146 if (pNote)
148 pRev->m_Notes = CUnicodeUtils::GetUnicode(pNote);
149 free(pNote);
150 pNote = nullptr;
153 ASSERT(pRev->m_CommitHash == hash);
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(), _T("TortoiseGit"), MB_ICONERROR);
165 return -1;
169 this->push_back(pRev->m_CommitHash);
171 m_HashMap[pRev->m_CommitHash] = (int)size() - 1;
176 CAutoLocker lock(g_Git.m_critGitDllSec);
177 git_close_log(handle);
180 return 0;
183 struct SortByParentDate
185 bool operator()(GitRevLoglist* pLhs, GitRevLoglist* pRhs)
187 if (pLhs->m_CommitHash == pRhs->m_CommitHash)
188 return false;
189 for (auto it = pLhs->m_ParentHash.begin(); it != pLhs->m_ParentHash.end(); ++it)
191 if (*it == pRhs->m_CommitHash)
192 return true;
194 for (auto it = pRhs->m_ParentHash.begin(); it != pRhs->m_ParentHash.end(); ++it)
196 if (*it == pLhs->m_CommitHash)
197 return false;
199 return pLhs->GetCommitterDate()>pRhs->GetCommitterDate();
203 int CLogDataVector::Fill(std::set<CGitHash>& hashes)
207 [] { git_init(); } ();
209 catch (const char* msg)
211 MessageBox(nullptr, _T("Could not initialize libgit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
212 return -1;
215 std::set<GitRevLoglist*, SortByParentDate> revs;
217 for (auto it = hashes.begin(); it != hashes.end(); ++it)
219 CGitHash hash = *it;
220 GitRevLoglist* pRev = this->m_pLogCache->GetCacheData(hash);
221 ASSERT(pRev->m_CommitHash == hash);
223 GIT_COMMIT commit;
226 CAutoLocker lock(g_Git.m_critGitDllSec);
227 if (git_get_commit_from_hash(&commit, hash.m_hash))
229 return -1;
232 catch (char * msg)
234 MessageBox(nullptr, _T("Could not get commit \"") + hash.ToString() + _T("\".\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
235 return -1;
238 // right now this code is only used by TortoiseGitBlame,
239 // as such git notes are not needed to be loaded
241 pRev->ParserFromCommit(&commit);
242 pRev->ParserParentFromCommit(&commit);
243 git_free_commit(&commit);
245 revs.insert(pRev);
248 for (auto it = revs.begin(); it != revs.end(); ++it)
250 GitRev *pRev = *it;
251 this->push_back(pRev->m_CommitHash);
252 m_HashMap[pRev->m_CommitHash] = (int)size() - 1;
255 return 0;
258 int AddTolist(unsigned char * /*osha1*/, unsigned char *nsha1, const char * /*name*/, unsigned long /*time*/, int /*sz*/, const char *msg, void *data)
260 CLogDataVector *vector = (CLogDataVector*)data;
261 GitRevLoglist rev;
262 rev.m_CommitHash = (char*)nsha1;
264 CString one = CUnicodeUtils::GetUnicode(msg);
266 int message = one.Find(_T(":"), 0);
267 if (message > 0)
269 rev.m_RefAction = one.Left(message);
270 rev.GetSubject() = one.Mid(message + 1);
273 vector->m_pLogCache->m_HashMap[rev.m_CommitHash] = rev;
274 vector->insert(vector->begin(),rev.m_CommitHash);
276 return 0;
279 void CLogDataVector::append(CGitHash& sha, bool storeInVector)
281 if (storeInVector)
282 this->push_back(sha);
284 GitRevLoglist* r = &m_pLogCache->m_HashMap[sha];
285 updateLanes(*r, this->m_Lns, sha);
288 void CLogDataVector::setLane(CGitHash& sha)
290 Lanes* l = &(this->m_Lns);
291 int i = m_FirstFreeLane;
293 // QVector<QByteArray> ba;
294 // const ShaString& ss = toPersistentSha(sha, ba);
295 // const ShaVect& shaVec(fh->revOrder);
297 for (int cnt = (int)size(); i < cnt; ++i) {
299 GitRevLoglist* r = &this->GetGitRevAt(i);
300 CGitHash curSha=r->m_CommitHash;
302 if (r->m_Lanes.empty())
303 updateLanes(*r, *l, curSha);
305 if (curSha == sha)
306 break;
308 m_FirstFreeLane = ++i;
310 #if 0
311 Lanes* l = &(this->m_Lanes);
312 int i = m_FirstFreeLane;
314 QVector<QByteArray> ba;
315 const ShaString& ss = toPersistentSha(sha, ba);
316 const ShaVect& shaVec(fh->revOrder);
318 for (uint cnt = shaVec.count(); i < cnt; ++i) {
320 const ShaString& curSha = shaVec[i];
321 Rev* r = m_HashMap[curSha]const_cast<Rev*>(revLookup(curSha, fh));
322 if (r->lanes.count() == 0)
323 updateLanes(*r, *l, curSha);
325 if (curSha == ss)
326 break;
328 fh->firstFreeLane = ++i;
329 #endif
333 void CLogDataVector::updateLanes(GitRevLoglist& c, Lanes& lns, CGitHash& sha)
335 // we could get third argument from c.sha(), but we are in fast path here
336 // and c.sha() involves a deep copy, so we accept a little redundancy
338 if (lns.isEmpty())
339 lns.init(sha);
341 bool isDiscontinuity;
342 bool isFork = lns.isFork(sha, isDiscontinuity);
343 bool isMerge = (c.ParentsCount() > 1);
344 bool isInitial = (c.ParentsCount() == 0);
346 if (isDiscontinuity)
347 lns.changeActiveLane(sha); // uses previous isBoundary state
349 lns.setBoundary(c.IsBoundary() == TRUE); // update must be here
350 TRACE(_T("%s %d"),c.m_CommitHash.ToString(),c.IsBoundary());
352 if (isFork)
353 lns.setFork(sha);
354 if (isMerge)
355 lns.setMerge(c.m_ParentHash);
356 //if (c.isApplied)
357 // lns.setApplied();
358 if (isInitial)
359 lns.setInitial();
361 lns.getLanes(c.m_Lanes); // here lanes are snapshotted
363 CGitHash nextSha;
364 if( !isInitial)
365 nextSha = c.m_ParentHash[0];
367 lns.nextParent(nextSha);
369 //if (c.isApplied)
370 // lns.afterApplied();
371 if (isMerge)
372 lns.afterMerge();
373 if (isFork)
374 lns.afterFork();
375 if (lns.isBranch())
376 lns.afterBranch();