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
30 #include "TortoiseProc.h"
31 #include "GitLogListBase.h"
32 #include "UnicodeUtils.h"
34 typedef CComCritSecLock
<CComCriticalSection
> CAutoLocker
;
36 void CLogDataVector::ClearAll()
47 m_pLogCache
->ClearAllLanes();
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");
64 CString cmd
= g_Git
.GetLogCmd(gitrange
, path
, count
, infomask
, true);
66 if (!g_Git
.CanParseRev(gitrange
))
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
);
82 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
83 if (git_open_log(&handle
,CUnicodeUtils::GetMulti(cmd
, CP_UTF8
).GetBuffer()))
90 MessageBox(NULL
, _T("Could not open log.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);
96 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
97 [&]{ git_get_log_firstcommit(handle
); }();
101 MessageBox(NULL
, _T("Could not get first commit.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);
112 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
113 [&]{ ret
= git_get_log_nextcommit(handle
, &commit
, infomask
& CGit::LOG_INFO_FOLLOW
); }();
117 MessageBox(NULL
, _T("Could not get next commit.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);
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
);
128 if (commit
.m_ignore
== 1)
130 git_free_commit(&commit
);
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
);
145 pRev
->m_Notes
.Empty();
146 g_Git
.StringAppend(&pRev
->m_Notes
,(BYTE
*)pNote
);
151 ASSERT(pRev
->m_CommitHash
== hash
);
152 pRev
->ParserFromCommit(&commit
);
153 pRev
->ParserParentFromCommit(&commit
);
154 git_free_commit(&commit
);
155 // Must call free commit before SafeFetchFullInfo, commit parent is rewrite by log.
156 // file list will wrong if parent rewrite.
158 if (!pRev
->m_IsFull
&& (infomask
& CGit::LOG_INFO_FULL_DIFF
))
162 pRev
->SafeFetchFullInfo(&g_Git
);
164 catch (char * g_last_error
)
166 MessageBox(NULL
, _T("Could not fetch full info of a commit.\nlibgit reports:\n") + CString(g_last_error
), _T("TortoiseGit"), MB_ICONERROR
);
171 this->push_back(pRev
->m_CommitHash
);
173 m_HashMap
[pRev
->m_CommitHash
] = (int)size() - 1;
178 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
179 git_close_log(handle
);
185 struct SortByParentDate
187 bool operator()(GitRev
*pLhs
, GitRev
*pRhs
)
189 if (pLhs
->m_CommitHash
== pRhs
->m_CommitHash
)
191 for (auto it
= pLhs
->m_ParentHash
.begin(); it
!= pLhs
->m_ParentHash
.end(); ++it
)
193 if (*it
== pRhs
->m_CommitHash
)
196 for (auto it
= pRhs
->m_ParentHash
.begin(); it
!= pRhs
->m_ParentHash
.end(); ++it
)
198 if (*it
== pLhs
->m_CommitHash
)
201 return pLhs
->GetCommitterDate()>pRhs
->GetCommitterDate();
205 int CLogDataVector::Fill(std::set
<CGitHash
>& hashes
)
209 [] { git_init(); } ();
211 catch (const char* msg
)
213 MessageBox(nullptr, _T("Could not initialize libgit.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);
217 std::set
<GitRev
*, SortByParentDate
> revs
;
219 for (auto it
= hashes
.begin(); it
!= hashes
.end(); ++it
)
222 GitRev
*pRev
= this->m_pLogCache
->GetCacheData(hash
);
223 ASSERT(pRev
->m_CommitHash
== hash
);
228 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
229 if (git_get_commit_from_hash(&commit
, hash
.m_hash
))
236 MessageBox(nullptr, _T("Could not get commit \"") + hash
.ToString() + _T("\".\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);
240 // right now this code is only used by TortoiseGitBlame,
241 // as such git notes are not needed to be loaded
243 pRev
->ParserFromCommit(&commit
);
244 pRev
->ParserParentFromCommit(&commit
);
245 git_free_commit(&commit
);
250 for (auto it
= revs
.begin(); it
!= revs
.end(); ++it
)
253 this->push_back(pRev
->m_CommitHash
);
254 m_HashMap
[pRev
->m_CommitHash
] = (int)size() - 1;
260 int AddTolist(unsigned char * /*osha1*/, unsigned char *nsha1
, const char * /*name*/, unsigned long /*time*/, int /*sz*/, const char *msg
, void *data
)
262 CLogDataVector
*vector
= (CLogDataVector
*)data
;
264 rev
.m_CommitHash
= (char*)nsha1
;
267 g_Git
.StringAppend(&one
, (BYTE
*)msg
);
269 int message
= one
.Find(_T(":"), 0);
272 rev
.m_RefAction
= one
.Left(message
);
273 rev
.GetSubject() = one
.Mid(message
+ 1);
276 vector
->m_pLogCache
->m_HashMap
[rev
.m_CommitHash
] = rev
;
277 vector
->insert(vector
->begin(),rev
.m_CommitHash
);
282 void CLogDataVector::append(CGitHash
& sha
, bool storeInVector
)
285 this->push_back(sha
);
287 GitRev
* r
= &m_pLogCache
->m_HashMap
[sha
];
288 updateLanes(*r
, this->m_Lns
, sha
);
291 void CLogDataVector::setLane(CGitHash
& sha
)
293 Lanes
* l
= &(this->m_Lns
);
294 int i
= m_FirstFreeLane
;
296 // QVector<QByteArray> ba;
297 // const ShaString& ss = toPersistentSha(sha, ba);
298 // const ShaVect& shaVec(fh->revOrder);
300 for (int cnt
= (int)size(); i
< cnt
; ++i
) {
302 GitRev
* r
= & this->GetGitRevAt(i
);
303 CGitHash curSha
=r
->m_CommitHash
;
305 if (r
->m_Lanes
.empty())
306 updateLanes(*r
, *l
, curSha
);
311 m_FirstFreeLane
= ++i
;
314 Lanes
* l
= &(this->m_Lanes
);
315 int i
= m_FirstFreeLane
;
317 QVector
<QByteArray
> ba
;
318 const ShaString
& ss
= toPersistentSha(sha
, ba
);
319 const ShaVect
& shaVec(fh
->revOrder
);
321 for (uint cnt
= shaVec
.count(); i
< cnt
; ++i
) {
323 const ShaString
& curSha
= shaVec
[i
];
324 Rev
* r
= m_HashMap
[curSha
]const_cast<Rev
*>(revLookup(curSha
, fh
));
325 if (r
->lanes
.count() == 0)
326 updateLanes(*r
, *l
, curSha
);
331 fh
->firstFreeLane
= ++i
;
336 void CLogDataVector::updateLanes(GitRev
& c
, Lanes
& lns
, CGitHash
&sha
)
338 // we could get third argument from c.sha(), but we are in fast path here
339 // and c.sha() involves a deep copy, so we accept a little redundancy
344 bool isDiscontinuity
;
345 bool isFork
= lns
.isFork(sha
, isDiscontinuity
);
346 bool isMerge
= (c
.ParentsCount() > 1);
347 bool isInitial
= (c
.ParentsCount() == 0);
350 lns
.changeActiveLane(sha
); // uses previous isBoundary state
352 lns
.setBoundary(c
.IsBoundary() == TRUE
); // update must be here
353 TRACE(_T("%s %d"),c
.m_CommitHash
.ToString(),c
.IsBoundary());
358 lns
.setMerge(c
.m_ParentHash
);
364 lns
.getLanes(c
.m_Lanes
); // here lanes are snapshotted
368 nextSha
= c
.m_ParentHash
[0];
370 lns
.nextParent(nextSha
);
373 // lns.afterApplied();