Fix Sync dialog crash when using msysgit 1.7.2.3
[TortoiseGit.git] / src / Git / GitRev.cpp
blob8c62aff86f1ec4ef76c342d98b95b3cfaaf99a2f
1 #include "StdAfx.h"
2 #include "ATLComTime.h"
3 #include "GitRev.h"
4 #include "Git.h"
5 #include "GitDLL.h"
6 #include "UnicodeUtils.h"
8 class CException; //Just in case afx.h is not included (cannot be included in every project which uses this file)
10 // provide an ASSERT macro for when compiled without MFC
11 #if !defined ASSERT
12 // Don't use _asm here, it isn't supported by x64 version of compiler. In fact, MFC's ASSERT() is the same with _ASSERTE().
13 #define ASSERT(x) _ASSERTE(x)
14 #endif
17 GitRev::GitRev(void)
19 m_Action=0;
20 m_IsFull = 0;
21 m_IsUpdateing = 0;
22 // fetch local machine timezone info
23 if ( GetTimeZoneInformation( &m_TimeZone ) == TIME_ZONE_ID_INVALID )
25 ASSERT(false);
29 GitRev::~GitRev(void)
33 #if 0
34 GitRev::GitRev(GitRev & rev)
37 GitRev& GitRev::operator=(GitRev &rev)
39 return *this;
41 #endif
42 void GitRev::Clear()
44 this->m_Action=0;
45 this->m_Files.Clear();
46 this->m_Action=0;
47 this->m_ParentHash.clear();
48 m_CommitterName.Empty();
49 m_CommitterEmail.Empty();
50 m_Body.Empty();
51 m_Subject.Empty();
52 m_CommitHash.Empty();
53 m_Ref.Empty();
54 m_RefAction.Empty();
55 m_Mark=0;
58 int GitRev::CopyFrom(GitRev &rev,bool OmitParentAndMark)
60 m_AuthorName =rev.m_AuthorName ;
61 m_AuthorEmail =rev.m_AuthorEmail ;
62 m_AuthorDate =rev.m_AuthorDate ;
63 m_CommitterName =rev.m_CommitterName ;
64 m_CommitterEmail=rev.m_CommitterEmail;
65 m_CommitterDate =rev.m_CommitterDate ;
66 m_Subject =rev.m_Subject ;
67 m_Body =rev.m_Body ;
68 m_CommitHash =rev.m_CommitHash ;
69 m_Files =rev.m_Files ;
70 m_Action =rev.m_Action ;
71 m_IsFull =rev.m_IsFull;
73 if(!OmitParentAndMark)
75 m_ParentHash =rev.m_ParentHash ;
76 m_Mark =rev.m_Mark;
78 return 0;
80 int GitRev::ParserFromLog(BYTE_VECTOR &log,int start)
82 int pos=start;
83 CString one;
84 CString key;
85 CString text;
86 BYTE_VECTOR filelist;
87 BYTE mode=0;
88 CTGitPath path;
89 this->m_Files.Clear();
90 m_Action=0;
91 int begintime=0;
92 int filebegin=-1;
94 while( pos < log.size() && pos>=0)
97 //one=log.Tokenize(_T("\n"),pos);
98 if(log[pos]==_T('#') && log[pos+1] == _T('<') && log[pos+3] == _T('>'))
100 //text = one.Right(one.GetLength()-4);
101 text.Empty();
102 if(log.size()>4)
103 g_Git.StringAppend(&text,&log[pos+4],CGit::m_LogEncode);
105 mode = log[pos+2];
107 switch(mode)
109 case LOG_REV_ITEM_BEGIN:
110 begintime++;
111 if(begintime>1)
112 break;
113 else
114 this->Clear();
115 break;
116 case LOG_REV_AUTHOR_NAME:
117 this->m_AuthorName = text;
118 break;
119 case LOG_REV_AUTHOR_EMAIL:
120 this->m_AuthorEmail = text;
121 break;
122 case LOG_REV_AUTHOR_DATE:
123 this->m_AuthorDate =ConverFromString(text);
124 break;
125 case LOG_REV_COMMIT_NAME:
126 this->m_CommitterName = text;
127 break;
128 case LOG_REV_COMMIT_EMAIL:
129 this->m_CommitterEmail = text;
130 break;
131 case LOG_REV_COMMIT_DATE:
132 this->m_CommitterDate =ConverFromString(text);
133 break;
134 case LOG_REV_COMMIT_SUBJECT:
135 this->m_Subject = text;
136 break;
137 case LOG_REV_COMMIT_BODY:
138 this->m_Body = text +_T("\n");
139 break;
140 case LOG_REV_COMMIT_HASH:
141 this->m_CommitHash = text.Right(40);
142 if(text.GetLength()>40)
144 this->m_Mark=text[0];
146 break;
147 case LOG_REV_COMMIT_PARENT:
148 while(text.GetLength()>0)
150 this->m_ParentHash.insert(this->m_ParentHash.end(),text.Left(40));
151 if(text.GetLength()>40)
152 text=text.Right(text.GetLength()-41);
153 else
154 break;
156 if(m_ParentHash.size()>1)
158 int a=1;
160 break;
161 case LOG_REV_COMMIT_FILE:
162 break;
164 }else
166 switch(mode)
168 // case LOG_REV_COMMIT_BODY:
169 // this->m_Body += one+_T("\n");
170 // break;
171 case LOG_REV_COMMIT_FILE:
172 //filelist += one +_T("\n");
173 //filelist.append(log,pos,log.find(0,pos));
174 if(filebegin<0)
175 filebegin=pos;
176 break;
180 if(begintime>1)
182 break;
185 //find next string start
186 pos=log.findNextString(pos);
189 if(filebegin>=0)
192 filelist.append(log,filebegin,pos);
193 this->m_Files.ParserFromLog(filelist);
194 this->m_Action=this->m_Files.GetAction();
196 return pos;
199 CTime GitRev::ConverFromString(CString input)
201 // pick up date from string
204 COleDateTime tm(_wtoi(input.Mid(0,4)),
205 _wtoi(input.Mid(5,2)),
206 _wtoi(input.Mid(8,2)),
207 _wtoi(input.Mid(11,2)),
208 _wtoi(input.Mid(14,2)),
209 _wtoi(input.Mid(17,2)));
210 if( tm.GetStatus() != COleDateTime::valid )
211 return CTime();//Error parsing time-string
213 // pick up utc offset
214 CString sign = input.Mid(20,1); // + or -
215 int hoursOffset = _wtoi(input.Mid(21,2));
216 int minsOffset = _wtoi(input.Mid(23,2));
217 // convert to a fraction of a day
218 double offset = (hoursOffset*60 + minsOffset) / 1440.0; // 1440 mins = 1 day
219 if ( sign == "-" )
221 offset = -offset;
223 // we have to subtract this from the time given to get UTC
224 tm -= offset;
225 // get utc time as a SYSTEMTIME
226 SYSTEMTIME sysTime;
227 tm.GetAsSystemTime( sysTime );
228 // and convert to users local time
229 SYSTEMTIME local;
230 if ( SystemTimeToTzSpecificLocalTime( &m_TimeZone, &sysTime, &local ) )
232 sysTime = local;
234 else
236 ASSERT(false); // this should not happen but leave time in utc if it does
238 // convert to CTime and return
239 return CTime( sysTime, -1 );;
241 catch(CException* e)
243 //Probably the date was something like 1970-01-01 00:00:00. _mktime64() doesnt like this.
244 //Dont let the application crash on this exception
246 #ifdef _AFX //CException classes are only defined when afx.h is included.
247 //When afx.h is not included, the exception is leaked.
248 //This will probably never happen because when CException is not defined, it cannot be thrown.
249 e->Delete();
250 #endif //ifdef _AFX
252 return CTime(); //Return an invalid time
255 int GitRev::SafeFetchFullInfo(CGit *git)
257 if(InterlockedExchange(&m_IsUpdateing,TRUE) == FALSE)
259 #if 0
260 //GitRev rev;
261 BYTE_VECTOR onelog;
262 TCHAR oldmark=this->m_Mark;
263 CString commithash = m_CommitHash;
264 git->GetLog(onelog,commithash,NULL,1,CGit::LOG_INFO_FULL_DIFF|CGit::LOG_INFO_STAT|CGit::LOG_INFO_FILESTATE|CGit::LOG_INFO_DETECT_COPYRENAME|CGit::LOG_INFO_SHOW_MERGEDFILE);
265 CString oldhash=m_CommitHash;
266 GIT_REV_LIST oldlist=this->m_ParentHash;
267 ParserFromLog(onelog);
269 //ASSERT(oldhash==m_CommitHash);
270 if(oldmark!=0)
271 this->m_Mark=oldmark; //parser full log will cause old mark overwrited.
272 //So we need keep old bound mark.
273 this->m_ParentHash=oldlist;
274 InterlockedExchange(&m_IsUpdateing,FALSE);
275 InterlockedExchange(&m_IsFull,TRUE);
276 return 0;
277 #endif
278 this->m_Files.Clear();
279 git->CheckAndInitDll();
280 GIT_COMMIT commit;
281 GIT_COMMIT_LIST list;
282 GIT_HASH parent;
283 memset(&commit,0,sizeof(GIT_COMMIT));
285 if(git_get_commit_from_hash(&commit, this->m_CommitHash.m_hash))
286 return -1;
288 int i=0;
289 bool isRoot = this->m_ParentHash.size()==0;
290 git_get_commit_first_parent(&commit,&list);
291 while(git_get_commit_next_parent(&list,parent) == 0 || isRoot)
293 GIT_FILE file;
294 int count;
295 if(isRoot)
296 git_root_diff(git->GetGitDiff(), this->m_CommitHash.m_hash, &file, &count);
297 else
298 git_diff(git->GetGitDiff(),parent,commit.m_hash,&file,&count);
300 isRoot = false;
302 CTGitPath path;
303 CString strnewname;
304 CString stroldname;
306 for(int j=0;j<count;j++)
308 path.Reset();
309 char *newname;
310 char *oldname;
312 strnewname.Empty();
313 stroldname.Empty();
315 int mode,IsBin,inc,dec;
316 git_get_diff_file(git->GetGitDiff(),file,j,&newname,&oldname,
317 &mode,&IsBin,&inc,&dec);
319 git->StringAppend(&strnewname,(BYTE*)newname,CP_ACP);
320 git->StringAppend(&stroldname,(BYTE*)oldname,CP_ACP);
322 path.m_ParentNo = i;
323 path.SetFromGit(strnewname,&stroldname);
324 path.ParserAction((BYTE)mode);
326 this->m_Action|=path.m_Action;
328 if(IsBin)
330 path.m_StatAdd=_T("-");
331 path.m_StatDel=_T("-");
332 }else
334 path.m_StatAdd.Format(_T("%d"),inc);
335 path.m_StatDel.Format(_T("%d"),dec);
337 m_Files.AddPath(path);
339 git_diff_flush(git->GetGitDiff());
340 i++;
344 InterlockedExchange(&m_IsUpdateing,FALSE);
345 InterlockedExchange(&m_IsFull,TRUE);
346 git_free_commit(&commit);
349 return 0;
352 int GitRev::ParserParentFromCommit(GIT_COMMIT *commit)
354 this->m_ParentHash.clear();
355 GIT_COMMIT_LIST list;
356 GIT_HASH parent;
358 git_get_commit_first_parent(commit,&list);
359 while(git_get_commit_next_parent(&list,parent)==0)
361 m_ParentHash.push_back(CGitHash((char *)parent));
363 return 0;
366 int GitRev::ParserFromCommit(GIT_COMMIT *commit)
368 int encode =CP_UTF8;
370 if(commit->m_Encode != 0 && commit->m_EncodeSize != 0)
372 CString str;
373 g_Git.StringAppend(&str, (BYTE*)commit->m_Encode, CP_UTF8, commit->m_EncodeSize);
374 encode = CUnicodeUtils::GetCPCode(str);
377 this->m_AuthorDate = commit->m_Author.Date;
379 this->m_AuthorEmail.Empty();
380 g_Git.StringAppend(&m_AuthorEmail,(BYTE*)commit->m_Author.Email,CP_UTF8,commit->m_Author.EmailSize);
382 this->m_AuthorName.Empty();
383 g_Git.StringAppend(&m_AuthorName,(BYTE*)commit->m_Author.Name,CP_UTF8,commit->m_Author.NameSize);
385 this->m_Body.Empty();
386 g_Git.StringAppend(&m_Body,(BYTE*)commit->m_Body,encode,commit->m_BodySize);
388 this->m_CommitterDate = commit->m_Committer.Date;
390 this->m_CommitterEmail.Empty();
391 g_Git.StringAppend(&m_CommitterEmail, (BYTE*)commit->m_Committer.Email,CP_UTF8, commit->m_Committer.EmailSize);
393 this->m_CommitterName.Empty();
394 g_Git.StringAppend(&m_CommitterName, (BYTE*)commit->m_Committer.Name,CP_UTF8, commit->m_Committer.NameSize);
396 this->m_Subject.Empty();
397 g_Git.StringAppend(&m_Subject, (BYTE*)commit->m_Subject,encode,commit->m_SubjectSize);
399 return 0;
401 #ifndef TRACE
402 #define TRACE(x) 1?0:(x)
403 #endif
404 void GitRev::DbgPrint()
406 TRACE(_T("Commit %s\r\n"), this->m_CommitHash.ToString());
407 for(int i=0;i<this->m_ParentHash.size();i++)
409 TRACE(_T("Parent %i %s"),i, m_ParentHash[i].ToString());
411 TRACE(_T("\n"));
414 int GitRev::GetCommitFromHash(CGitHash &hash)
416 g_Git.CheckAndInitDll();
418 GIT_COMMIT commit;
419 if(git_get_commit_from_hash( &commit, hash.m_hash))
420 return -1;
422 this->ParserFromCommit(&commit);
423 git_free_commit(&commit);
425 this->m_CommitHash=hash;
427 return 0;
431 int GitRev::GetCommit(CString &refname)
433 g_Git.CheckAndInitDll();
435 if(refname.GetLength() >= 8)
436 if(refname.Find(_T("00000000")) == 0)
438 this->m_CommitHash.Empty();
439 this->m_Subject=_T("Working Copy");
440 return 0;
442 CStringA rev;
443 rev= CUnicodeUtils::GetUTF8(refname);
444 GIT_HASH sha;
446 if(git_get_sha1(rev.GetBuffer(),sha))
447 return -1;
449 GetCommitFromHash(CGitHash((char*)sha));
450 return 0;