Use CUnicodeUtils::GetUnicode instead of CGit::StringAppend if possible
[TortoiseGit.git] / src / TortoiseProc / LogDataVector.cpp
blob0ca4eb3d5eb5a20618ca56ee8bca0196a1ee2e94
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2013 - 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 "GitLogListBase.h"
32 #include "UnicodeUtils.h"
34 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
36 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)
47 m_pLogCache->ClearAllLanes();
50 m_RawlogData.clear();
51 m_RawLogStart.clear();
54 //CLogDataVector Class
55 int CLogDataVector::ParserFromLog(CTGitPath *path, int count, int infomask, CString *range)
57 // only enable --follow on files
58 if ((path == NULL || path->IsDirectory()) && (infomask & CGit::LOG_INFO_FOLLOW))
59 infomask = infomask ^ CGit::LOG_INFO_FOLLOW;
61 CString gitrange = _T("HEAD");
62 if (range != nullptr)
63 gitrange = *range;
64 CString cmd = g_Git.GetLogCmd(gitrange, path, count, infomask, true);
66 if (!g_Git.CanParseRev(gitrange))
67 return 0;
69 try
71 [] { git_init(); } ();
73 catch (const char* msg)
75 MessageBox(NULL, _T("Could not initialize libgit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
76 return -1;
79 GIT_LOG handle;
80 try
82 CAutoLocker lock(g_Git.m_critGitDllSec);
83 if (git_open_log(&handle,CUnicodeUtils::GetMulti(cmd, CP_UTF8).GetBuffer()))
85 return -1;
88 catch (char* msg)
90 MessageBox(NULL, _T("Could not open log.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
91 return -1;
94 try
96 CAutoLocker lock(g_Git.m_critGitDllSec);
97 [&]{ git_get_log_firstcommit(handle); }();
99 catch (char* msg)
101 MessageBox(NULL, _T("Could not get first commit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
102 return -1;
105 int ret = 0;
106 while (ret == 0)
108 GIT_COMMIT commit;
112 CAutoLocker lock(g_Git.m_critGitDllSec);
113 [&]{ ret = git_get_log_nextcommit(handle, &commit, infomask & CGit::LOG_INFO_FOLLOW); }();
115 catch (char* msg)
117 MessageBox(NULL, _T("Could not get next commit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
118 break;
121 if (ret)
123 if (ret != -2) // other than end of revision walking
124 MessageBox(nullptr, (_T("Could not get next commit.\nlibgit returns:") + std::to_wstring(ret)).c_str(), _T("TortoiseGit"), MB_ICONERROR);
125 break;
128 if (commit.m_ignore == 1)
130 git_free_commit(&commit);
131 continue;
134 CGitHash hash = (char*)commit.m_hash ;
136 GitRev *pRev = this->m_pLogCache->GetCacheData(hash);
138 char *pNote = nullptr;
140 CAutoLocker lock(g_Git.m_critGitDllSec);
141 git_get_notes(commit.m_hash, &pNote);
143 if (pNote)
145 pRev->m_Notes = CUnicodeUtils::GetUnicode(pNote);
146 free(pNote);
147 pNote = nullptr;
150 ASSERT(pRev->m_CommitHash == hash);
151 pRev->ParserFromCommit(&commit);
152 pRev->ParserParentFromCommit(&commit);
153 git_free_commit(&commit);
154 // Must call free commit before SafeFetchFullInfo, commit parent is rewrite by log.
155 // file list will wrong if parent rewrite.
157 if (!pRev->m_IsFull && (infomask & CGit::LOG_INFO_FULL_DIFF))
161 pRev->SafeFetchFullInfo(&g_Git);
163 catch (char * g_last_error)
165 MessageBox(NULL, _T("Could not fetch full info of a commit.\nlibgit reports:\n") + CString(g_last_error), _T("TortoiseGit"), MB_ICONERROR);
166 return -1;
170 this->push_back(pRev->m_CommitHash);
172 m_HashMap[pRev->m_CommitHash] = (int)size() - 1;
177 CAutoLocker lock(g_Git.m_critGitDllSec);
178 git_close_log(handle);
181 return 0;
184 struct SortByParentDate
186 bool operator()(GitRev *pLhs, GitRev *pRhs)
188 if (pLhs->m_CommitHash == pRhs->m_CommitHash)
189 return false;
190 for (auto it = pLhs->m_ParentHash.begin(); it != pLhs->m_ParentHash.end(); ++it)
192 if (*it == pRhs->m_CommitHash)
193 return true;
195 for (auto it = pRhs->m_ParentHash.begin(); it != pRhs->m_ParentHash.end(); ++it)
197 if (*it == pLhs->m_CommitHash)
198 return false;
200 return pLhs->GetCommitterDate()>pRhs->GetCommitterDate();
204 int CLogDataVector::Fill(std::set<CGitHash>& hashes)
208 [] { git_init(); } ();
210 catch (const char* msg)
212 MessageBox(nullptr, _T("Could not initialize libgit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
213 return -1;
216 std::set<GitRev*, SortByParentDate> revs;
218 for (auto it = hashes.begin(); it != hashes.end(); ++it)
220 CGitHash hash = *it;
221 GitRev *pRev = this->m_pLogCache->GetCacheData(hash);
222 ASSERT(pRev->m_CommitHash == hash);
224 GIT_COMMIT commit;
227 CAutoLocker lock(g_Git.m_critGitDllSec);
228 if (git_get_commit_from_hash(&commit, hash.m_hash))
230 return -1;
233 catch (char * msg)
235 MessageBox(nullptr, _T("Could not get commit \"") + hash.ToString() + _T("\".\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
236 return -1;
239 // right now this code is only used by TortoiseGitBlame,
240 // as such git notes are not needed to be loaded
242 pRev->ParserFromCommit(&commit);
243 pRev->ParserParentFromCommit(&commit);
244 git_free_commit(&commit);
246 revs.insert(pRev);
249 for (auto it = revs.begin(); it != revs.end(); ++it)
251 GitRev *pRev = *it;
252 this->push_back(pRev->m_CommitHash);
253 m_HashMap[pRev->m_CommitHash] = (int)size() - 1;
256 return 0;
259 int AddTolist(unsigned char * /*osha1*/, unsigned char *nsha1, const char * /*name*/, unsigned long /*time*/, int /*sz*/, const char *msg, void *data)
261 CLogDataVector *vector = (CLogDataVector*)data;
262 GitRev rev;
263 rev.m_CommitHash = (char*)nsha1;
265 CString one = CUnicodeUtils::GetUnicode(msg);
267 int message = one.Find(_T(":"), 0);
268 if (message > 0)
270 rev.m_RefAction = one.Left(message);
271 rev.GetSubject() = one.Mid(message + 1);
274 vector->m_pLogCache->m_HashMap[rev.m_CommitHash] = rev;
275 vector->insert(vector->begin(),rev.m_CommitHash);
277 return 0;
280 void CLogDataVector::append(CGitHash& sha, bool storeInVector)
282 if (storeInVector)
283 this->push_back(sha);
285 GitRev* r = &m_pLogCache->m_HashMap[sha];
286 updateLanes(*r, this->m_Lns, sha);
289 void CLogDataVector::setLane(CGitHash& sha)
291 Lanes* l = &(this->m_Lns);
292 int i = m_FirstFreeLane;
294 // QVector<QByteArray> ba;
295 // const ShaString& ss = toPersistentSha(sha, ba);
296 // const ShaVect& shaVec(fh->revOrder);
298 for (int cnt = (int)size(); i < cnt; ++i) {
300 GitRev* r = & this->GetGitRevAt(i);
301 CGitHash curSha=r->m_CommitHash;
303 if (r->m_Lanes.empty())
304 updateLanes(*r, *l, curSha);
306 if (curSha == sha)
307 break;
309 m_FirstFreeLane = ++i;
311 #if 0
312 Lanes* l = &(this->m_Lanes);
313 int i = m_FirstFreeLane;
315 QVector<QByteArray> ba;
316 const ShaString& ss = toPersistentSha(sha, ba);
317 const ShaVect& shaVec(fh->revOrder);
319 for (uint cnt = shaVec.count(); i < cnt; ++i) {
321 const ShaString& curSha = shaVec[i];
322 Rev* r = m_HashMap[curSha]const_cast<Rev*>(revLookup(curSha, fh));
323 if (r->lanes.count() == 0)
324 updateLanes(*r, *l, curSha);
326 if (curSha == ss)
327 break;
329 fh->firstFreeLane = ++i;
330 #endif
334 void CLogDataVector::updateLanes(GitRev& c, Lanes& lns, CGitHash &sha)
336 // we could get third argument from c.sha(), but we are in fast path here
337 // and c.sha() involves a deep copy, so we accept a little redundancy
339 if (lns.isEmpty())
340 lns.init(sha);
342 bool isDiscontinuity;
343 bool isFork = lns.isFork(sha, isDiscontinuity);
344 bool isMerge = (c.ParentsCount() > 1);
345 bool isInitial = (c.ParentsCount() == 0);
347 if (isDiscontinuity)
348 lns.changeActiveLane(sha); // uses previous isBoundary state
350 lns.setBoundary(c.IsBoundary() == TRUE); // update must be here
351 TRACE(_T("%s %d"),c.m_CommitHash.ToString(),c.IsBoundary());
353 if (isFork)
354 lns.setFork(sha);
355 if (isMerge)
356 lns.setMerge(c.m_ParentHash);
357 //if (c.isApplied)
358 // lns.setApplied();
359 if (isInitial)
360 lns.setInitial();
362 lns.getLanes(c.m_Lanes); // here lanes are snapshotted
364 CGitHash nextSha;
365 if( !isInitial)
366 nextSha = c.m_ParentHash[0];
368 lns.nextParent(nextSha);
370 //if (c.isApplied)
371 // lns.afterApplied();
372 if (isMerge)
373 lns.afterMerge();
374 if (isFork)
375 lns.afterFork();
376 if (lns.isBranch())
377 lns.afterBranch();