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"
37 #include "GITProgressDlg.h"
38 #include "ProgressDlg.h"
40 #include "MessageBox.h"
43 #include "PathUtils.h"
44 #include "StringUtils.h"
45 #include "UnicodeUtils.h"
48 #include "GitStatus.h"
49 #include "FileDiffDlg.h"
53 void CLogDataVector::ClearAll()
64 m_RawLogStart
.clear();
67 //CLogDataVector Class
68 int CLogDataVector::ParserFromLog(CTGitPath
*path
, int count
, int infomask
, CString
*range
)
70 // only enable --follow on files
71 if ((path
== NULL
|| path
->IsDirectory()) && (infomask
& CGit::LOG_INFO_FOLLOW
))
72 infomask
= infomask
^ CGit::LOG_INFO_FOLLOW
;
74 CString gitrange
= _T("HEAD");
77 CString cmd
= g_Git
.GetLogCmd(gitrange
, path
, count
, infomask
, true);
79 if (!g_Git
.CanParseRev(gitrange
))
84 [] { git_init(); } ();
86 catch (const char* msg
)
88 MessageBox(NULL
, _T("Could not initialize libgit.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);
93 g_Git
.m_critGitDllSec
.Lock();
96 if (git_open_log(&handle
,CUnicodeUtils::GetMulti(cmd
, CP_UTF8
).GetBuffer()))
103 g_Git
.m_critGitDllSec
.Unlock();
104 MessageBox(NULL
, _T("Could not open log.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);
107 g_Git
.m_critGitDllSec
.Unlock();
109 g_Git
.m_critGitDllSec
.Lock();
112 [&]{ git_get_log_firstcommit(handle
); }();
116 g_Git
.m_critGitDllSec
.Unlock();
117 MessageBox(NULL
, _T("Could not get first commit.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);
120 g_Git
.m_critGitDllSec
.Unlock();
126 g_Git
.m_critGitDllSec
.Lock();
129 [&]{ ret
= git_get_log_nextcommit(handle
, &commit
, infomask
& CGit::LOG_INFO_FOLLOW
); }();
133 g_Git
.m_critGitDllSec
.Unlock();
134 MessageBox(NULL
, _T("Could not get next commit.\nlibgit reports:\n") + CString(msg
), _T("TortoiseGit"), MB_ICONERROR
);
137 g_Git
.m_critGitDllSec
.Unlock();
142 if (commit
.m_ignore
== 1)
144 git_free_commit(&commit
);
148 CGitHash hash
= (char*)commit
.m_hash
;
150 GitRev
*pRev
= this->m_pLogCache
->GetCacheData(hash
);
153 g_Git
.m_critGitDllSec
.Lock();
154 git_get_notes(commit
.m_hash
,¬e
);
155 g_Git
.m_critGitDllSec
.Unlock();
158 pRev
->m_Notes
.Empty();
159 g_Git
.StringAppend(&pRev
->m_Notes
,(BYTE
*)note
);
162 if((pRev
== NULL
|| !pRev
->m_IsFull
) && infomask
& CGit::LOG_INFO_FULL_DIFF
)
164 pRev
->ParserFromCommit(&commit
);
165 pRev
->ParserParentFromCommit(&commit
);
166 git_free_commit(&commit
);
167 //Must call free commit before SafeFetchFullInfo, commit parent is rewrite by log.
168 //file list will wrong if parent rewrite.
171 pRev
->SafeFetchFullInfo(&g_Git
);
173 catch (char * g_last_error
)
175 MessageBox(NULL
, _T("Could not fetch full info of a commit.\nlibgit reports:\n") + CString(g_last_error
), _T("TortoiseGit"), MB_ICONERROR
);
181 ASSERT(pRev
->m_CommitHash
== hash
);
182 pRev
->ParserFromCommit(&commit
);
183 pRev
->ParserParentFromCommit(&commit
);
184 git_free_commit(&commit
);
187 this->push_back(pRev
->m_CommitHash
);
189 m_HashMap
[pRev
->m_CommitHash
] = (int)size() - 1;
193 g_Git
.m_critGitDllSec
.Lock();
194 git_close_log(handle
);
195 g_Git
.m_critGitDllSec
.Unlock();
200 int AddTolist(unsigned char * /*osha1*/, unsigned char *nsha1
, const char * /*name*/, unsigned long /*time*/, int /*sz*/, const char *msg
, void *data
)
202 CLogDataVector
*vector
= (CLogDataVector
*)data
;
204 rev
.m_CommitHash
=(char*)nsha1
;
207 g_Git
.StringAppend(&one
, (BYTE
*)msg
);
209 int message
=one
.Find(_T(":"),0);
212 rev
.m_RefAction
=one
.Left(message
);
213 rev
.GetSubject()=one
.Mid(message
+1);
216 vector
->m_pLogCache
->m_HashMap
[rev
.m_CommitHash
]=rev
;
217 vector
->insert(vector
->begin(),rev
.m_CommitHash
);
222 int CLogDataVector::ParserFromRefLog(CString ref
)
224 if(g_Git
.m_IsUseGitDLL
)
226 git_for_each_reflog_ent(CUnicodeUtils::GetUTF8(ref
),AddTolist
,this);
227 for (size_t i
= 0; i
< size(); ++i
)
229 m_pLogCache
->m_HashMap
[at(i
)].m_Ref
.Format(_T("%s{%d}"), ref
,i
);
238 cmd
.Format(_T("git.exe reflog show %s"),ref
);
239 if (g_Git
.Run(cmd
, &out
, NULL
, CP_UTF8
))
245 CString one
=out
.Tokenize(_T("\n"),pos
);
246 int ref
=one
.Find(_T(' '),0);
252 if (g_Git
.GetHash(rev
.m_CommitHash
, one
.Left(ref
)))
254 MessageBox(NULL
, g_Git
.GetGitLastErr(_T("Could not get hash of ") + one
.Left(ref
) + _T(".")), _T("TortoiseGit"), MB_ICONERROR
);
257 int action
=one
.Find(_T(' '),ref
+1);
261 rev
.m_Ref
=one
.Mid(ref
+1,action
-ref
-2);
262 message
=one
.Find(_T(":"),action
);
265 rev
.m_RefAction
=one
.Mid(action
+1,message
-action
-1);
266 rev
.GetSubject()=one
.Right(one
.GetLength()-message
-1);
270 this->m_pLogCache
->m_HashMap
[rev
.m_CommitHash
]=rev
;
272 this->push_back(rev
.m_CommitHash
);
279 void CLogDataVector::setLane(CGitHash
& sha
)
281 Lanes
* l
= &(this->m_Lns
);
282 int i
= m_FirstFreeLane
;
284 // QVector<QByteArray> ba;
285 // const ShaString& ss = toPersistentSha(sha, ba);
286 // const ShaVect& shaVec(fh->revOrder);
288 for (int cnt
= (int)size(); i
< cnt
; ++i
) {
290 GitRev
* r
= & this->GetGitRevAt(i
);
291 CGitHash curSha
=r
->m_CommitHash
;
293 if (r
->m_Lanes
.empty())
294 updateLanes(*r
, *l
, curSha
);
299 m_FirstFreeLane
= ++i
;
302 Lanes
* l
= &(this->m_Lanes
);
303 int i
= m_FirstFreeLane
;
305 QVector
<QByteArray
> ba
;
306 const ShaString
& ss
= toPersistentSha(sha
, ba
);
307 const ShaVect
& shaVec(fh
->revOrder
);
309 for (uint cnt
= shaVec
.count(); i
< cnt
; ++i
) {
311 const ShaString
& curSha
= shaVec
[i
];
312 Rev
* r
= m_HashMap
[curSha
]const_cast<Rev
*>(revLookup(curSha
, fh
));
313 if (r
->lanes
.count() == 0)
314 updateLanes(*r
, *l
, curSha
);
319 fh
->firstFreeLane
= ++i
;
324 void CLogDataVector::updateLanes(GitRev
& c
, Lanes
& lns
, CGitHash
&sha
)
326 // we could get third argument from c.sha(), but we are in fast path here
327 // and c.sha() involves a deep copy, so we accept a little redundancy
332 bool isDiscontinuity
;
333 bool isFork
= lns
.isFork(sha
, isDiscontinuity
);
334 bool isMerge
= (c
.ParentsCount() > 1);
335 bool isInitial
= (c
.ParentsCount() == 0);
338 lns
.changeActiveLane(sha
); // uses previous isBoundary state
340 lns
.setBoundary(c
.IsBoundary() == TRUE
); // update must be here
341 TRACE(_T("%s %d"),c
.m_CommitHash
.ToString(),c
.IsBoundary());
346 lns
.setMerge(c
.m_ParentHash
);
352 lns
.getLanes(c
.m_Lanes
); // here lanes are snapshotted
356 nextSha
= c
.m_ParentHash
[0];
358 lns
.nextParent(nextSha
);
361 // lns.afterApplied();