Deduplicate code
[TortoiseGit.git] / src / TortoiseProc / LogDataVector.cpp
blob612e4d4b26f2c16afdd8ac79db71ab55eb02dae2
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;
35 CGitHashMap a;
37 void CLogDataVector::ClearAll()
40 clear();
41 m_HashMap.clear();
42 m_Lns.clear();
44 m_FirstFreeLane=0;
45 m_Lns.clear();
47 m_RawlogData.clear();
48 m_RawLogStart.clear();
51 //CLogDataVector Class
52 int CLogDataVector::ParserFromLog(CTGitPath *path, int count, int infomask, CString *range)
54 // only enable --follow on files
55 if ((path == NULL || path->IsDirectory()) && (infomask & CGit::LOG_INFO_FOLLOW))
56 infomask = infomask ^ CGit::LOG_INFO_FOLLOW;
58 CString gitrange = _T("HEAD");
59 if (range != nullptr)
60 gitrange = *range;
61 CString cmd = g_Git.GetLogCmd(gitrange, path, count, infomask, true);
63 if (!g_Git.CanParseRev(gitrange))
64 return 0;
66 try
68 [] { git_init(); } ();
70 catch (const char* msg)
72 MessageBox(NULL, _T("Could not initialize libgit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
73 return -1;
76 GIT_LOG handle;
77 try
79 CAutoLocker lock(g_Git.m_critGitDllSec);
80 if (git_open_log(&handle,CUnicodeUtils::GetMulti(cmd, CP_UTF8).GetBuffer()))
82 return -1;
85 catch (char* msg)
87 MessageBox(NULL, _T("Could not open log.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
88 return -1;
91 try
93 CAutoLocker lock(g_Git.m_critGitDllSec);
94 [&]{ git_get_log_firstcommit(handle); }();
96 catch (char* msg)
98 MessageBox(NULL, _T("Could not get first commit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
99 return -1;
102 int ret = 0;
103 while (ret == 0)
105 GIT_COMMIT commit;
109 CAutoLocker lock(g_Git.m_critGitDllSec);
110 [&]{ ret = git_get_log_nextcommit(handle, &commit, infomask & CGit::LOG_INFO_FOLLOW); }();
112 catch (char* msg)
114 MessageBox(NULL, _T("Could not get next commit.\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
115 break;
118 if (ret)
119 break;
121 if (commit.m_ignore == 1)
123 git_free_commit(&commit);
124 continue;
127 CGitHash hash = (char*)commit.m_hash ;
129 GitRev *pRev = this->m_pLogCache->GetCacheData(hash);
131 char *pNote = nullptr;
133 CAutoLocker lock(g_Git.m_critGitDllSec);
134 git_get_notes(commit.m_hash, &pNote);
136 if (pNote)
138 pRev->m_Notes.Empty();
139 g_Git.StringAppend(&pRev->m_Notes,(BYTE*)pNote);
142 ASSERT(pRev->m_CommitHash == hash);
143 pRev->ParserFromCommit(&commit);
144 pRev->ParserParentFromCommit(&commit);
145 git_free_commit(&commit);
146 // Must call free commit before SafeFetchFullInfo, commit parent is rewrite by log.
147 // file list will wrong if parent rewrite.
149 if (!pRev->m_IsFull && (infomask & CGit::LOG_INFO_FULL_DIFF))
153 pRev->SafeFetchFullInfo(&g_Git);
155 catch (char * g_last_error)
157 MessageBox(NULL, _T("Could not fetch full info of a commit.\nlibgit reports:\n") + CString(g_last_error), _T("TortoiseGit"), MB_ICONERROR);
158 return -1;
162 this->push_back(pRev->m_CommitHash);
164 m_HashMap[pRev->m_CommitHash] = (int)size() - 1;
169 CAutoLocker lock(g_Git.m_critGitDllSec);
170 git_close_log(handle);
173 return 0;
176 int AddTolist(unsigned char * /*osha1*/, unsigned char *nsha1, const char * /*name*/, unsigned long /*time*/, int /*sz*/, const char *msg, void *data)
178 CLogDataVector *vector = (CLogDataVector*)data;
179 GitRev rev;
180 rev.m_CommitHash=(char*)nsha1;
182 CString one;
183 g_Git.StringAppend(&one, (BYTE *)msg);
185 int message=one.Find(_T(":"),0);
186 if(message>0)
188 rev.m_RefAction=one.Left(message);
189 rev.GetSubject()=one.Mid(message+1);
192 vector->m_pLogCache->m_HashMap[rev.m_CommitHash]=rev;
193 vector->insert(vector->begin(),rev.m_CommitHash);
195 return 0;
198 int CLogDataVector::ParserFromRefLog(CString ref)
200 if(g_Git.m_IsUseGitDLL)
202 git_for_each_reflog_ent(CUnicodeUtils::GetUTF8(ref),AddTolist,this);
203 for (size_t i = 0; i < size(); ++i)
205 m_pLogCache->m_HashMap[at(i)].m_Ref.Format(_T("%s{%d}"), ref,i);
209 else
212 CString cmd, out;
213 GitRev rev;
214 cmd.Format(_T("git.exe reflog show %s"),ref);
215 if (g_Git.Run(cmd, &out, NULL, CP_UTF8))
216 return -1;
218 int pos=0;
219 while(pos>=0)
221 CString one=out.Tokenize(_T("\n"),pos);
222 int ref=one.Find(_T(' '),0);
223 if(ref<0)
224 continue;
226 rev.Clear();
228 if (g_Git.GetHash(rev.m_CommitHash, one.Left(ref)))
230 MessageBox(NULL, g_Git.GetGitLastErr(_T("Could not get hash of ") + one.Left(ref) + _T(".")), _T("TortoiseGit"), MB_ICONERROR);
231 return -1;
233 int action=one.Find(_T(' '),ref+1);
234 if(action>0)
236 rev.m_Ref=one.Mid(ref+1,action-ref-2);
237 int message = one.Find(_T(":"), action);
238 if(message>0)
240 rev.m_RefAction=one.Mid(action+1,message-action-1);
241 rev.GetSubject()=one.Right(one.GetLength()-message-1);
245 this->m_pLogCache->m_HashMap[rev.m_CommitHash]=rev;
247 this->push_back(rev.m_CommitHash);
251 return 0;
254 void CLogDataVector::append(CGitHash& sha, bool storeInVector)
256 if (storeInVector)
257 this->push_back(sha);
259 GitRev* r = &m_pLogCache->m_HashMap[sha];
260 updateLanes(*r, this->m_Lns, sha);
263 void CLogDataVector::setLane(CGitHash& sha)
265 Lanes* l = &(this->m_Lns);
266 int i = m_FirstFreeLane;
268 // QVector<QByteArray> ba;
269 // const ShaString& ss = toPersistentSha(sha, ba);
270 // const ShaVect& shaVec(fh->revOrder);
272 for (int cnt = (int)size(); i < cnt; ++i) {
274 GitRev* r = & this->GetGitRevAt(i);
275 CGitHash curSha=r->m_CommitHash;
277 if (r->m_Lanes.empty())
278 updateLanes(*r, *l, curSha);
280 if (curSha == sha)
281 break;
283 m_FirstFreeLane = ++i;
285 #if 0
286 Lanes* l = &(this->m_Lanes);
287 int i = m_FirstFreeLane;
289 QVector<QByteArray> ba;
290 const ShaString& ss = toPersistentSha(sha, ba);
291 const ShaVect& shaVec(fh->revOrder);
293 for (uint cnt = shaVec.count(); i < cnt; ++i) {
295 const ShaString& curSha = shaVec[i];
296 Rev* r = m_HashMap[curSha]const_cast<Rev*>(revLookup(curSha, fh));
297 if (r->lanes.count() == 0)
298 updateLanes(*r, *l, curSha);
300 if (curSha == ss)
301 break;
303 fh->firstFreeLane = ++i;
304 #endif
308 void CLogDataVector::updateLanes(GitRev& c, Lanes& lns, CGitHash &sha)
310 // we could get third argument from c.sha(), but we are in fast path here
311 // and c.sha() involves a deep copy, so we accept a little redundancy
313 if (lns.isEmpty())
314 lns.init(sha);
316 bool isDiscontinuity;
317 bool isFork = lns.isFork(sha, isDiscontinuity);
318 bool isMerge = (c.ParentsCount() > 1);
319 bool isInitial = (c.ParentsCount() == 0);
321 if (isDiscontinuity)
322 lns.changeActiveLane(sha); // uses previous isBoundary state
324 lns.setBoundary(c.IsBoundary() == TRUE); // update must be here
325 TRACE(_T("%s %d"),c.m_CommitHash.ToString(),c.IsBoundary());
327 if (isFork)
328 lns.setFork(sha);
329 if (isMerge)
330 lns.setMerge(c.m_ParentHash);
331 //if (c.isApplied)
332 // lns.setApplied();
333 if (isInitial)
334 lns.setInitial();
336 lns.getLanes(c.m_Lanes); // here lanes are snapshotted
338 CGitHash nextSha;
339 if( !isInitial)
340 nextSha = c.m_ParentHash[0];
342 lns.nextParent(nextSha);
344 //if (c.isApplied)
345 // lns.afterApplied();
346 if (isMerge)
347 lns.afterMerge();
348 if (isFork)
349 lns.afterFork();
350 if (lns.isBranch())
351 lns.afterBranch();