Fixed issue #1620: Apply Patch serial can't open file dialog on WinXP
[TortoiseGit.git] / src / Git / GitRev.cpp
blob3dd43c98b3edcffc33495f122735965c8050f0b0
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "ATLComTime.h"
22 #include "GitRev.h"
23 #include "Git.h"
24 #include "GitDLL.h"
25 #include "UnicodeUtils.h"
27 class CException; //Just in case afx.h is not included (cannot be included in every project which uses this file)
29 // provide an ASSERT macro for when compiled without MFC
30 #if !defined ASSERT
31 // Don't use _asm here, it isn't supported by x64 version of compiler. In fact, MFC's ASSERT() is the same with _ASSERTE().
32 #define ASSERT(x) _ASSERTE(x)
33 #endif
35 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
37 GitRev::GitRev(void)
39 m_Action=0;
40 m_IsFull = 0;
41 m_IsUpdateing = 0;
42 m_IsCommitParsed = 0;
43 m_IsDiffFiles = 0;
44 m_CallDiffAsync = NULL;
45 m_IsSimpleListReady =0;
47 memset(&this->m_GitCommit,0,sizeof(GIT_COMMIT));
49 // fetch local machine timezone info
50 if ( GetTimeZoneInformation( &m_TimeZone ) == TIME_ZONE_ID_INVALID )
52 ASSERT(false);
56 GitRev::~GitRev(void)
60 #if 0
61 GitRev::GitRev(GitRev & rev)
64 GitRev& GitRev::operator=(GitRev &rev)
66 return *this;
68 #endif
69 void GitRev::Clear()
71 this->m_Action=0;
72 this->m_Files.Clear();
73 this->m_Action=0;
74 this->m_ParentHash.clear();
75 m_CommitterName.Empty();
76 m_CommitterEmail.Empty();
77 m_Body.Empty();
78 m_Subject.Empty();
79 m_CommitHash.Empty();
80 m_Ref.Empty();
81 m_RefAction.Empty();
82 m_Mark=0;
85 int GitRev::CopyFrom(GitRev &rev,bool OmitParentAndMark)
87 m_AuthorName =rev.m_AuthorName;
88 m_AuthorEmail =rev.m_AuthorEmail;
89 m_AuthorDate =rev.m_AuthorDate;
90 m_CommitterName =rev.m_CommitterName;
91 m_CommitterEmail=rev.m_CommitterEmail;
92 m_CommitterDate =rev.m_CommitterDate;
93 m_Subject =rev.m_Subject;
94 m_Body =rev.m_Body;
95 m_CommitHash =rev.m_CommitHash;
96 m_Files =rev.m_Files;
97 m_Action =rev.m_Action;
98 m_IsFull =rev.m_IsFull;
100 if(!OmitParentAndMark)
102 m_ParentHash =rev.m_ParentHash;
103 m_Mark =rev.m_Mark;
105 return 0;
108 CTime GitRev::ConverFromString(CString input)
110 // pick up date from string
113 COleDateTime tm(_wtoi(input.Mid(0,4)),
114 _wtoi(input.Mid(5,2)),
115 _wtoi(input.Mid(8,2)),
116 _wtoi(input.Mid(11,2)),
117 _wtoi(input.Mid(14,2)),
118 _wtoi(input.Mid(17,2)));
119 if( tm.GetStatus() != COleDateTime::valid )
120 return CTime();//Error parsing time-string
122 // pick up utc offset
123 CString sign = input.Mid(20,1); // + or -
124 int hoursOffset = _wtoi(input.Mid(21,2));
125 int minsOffset = _wtoi(input.Mid(23,2));
126 // convert to a fraction of a day
127 double offset = (hoursOffset*60 + minsOffset) / 1440.0; // 1440 mins = 1 day
128 if ( sign == "-" )
130 offset = -offset;
132 // we have to subtract this from the time given to get UTC
133 tm -= offset;
134 // get utc time as a SYSTEMTIME
135 SYSTEMTIME sysTime;
136 tm.GetAsSystemTime( sysTime );
137 // and convert to users local time
138 SYSTEMTIME local;
139 if ( SystemTimeToTzSpecificLocalTime( &m_TimeZone, &sysTime, &local ) )
141 sysTime = local;
143 else
145 ASSERT(false); // this should not happen but leave time in utc if it does
147 // convert to CTime and return
148 return CTime( sysTime, -1 );
150 catch(CException* e)
152 //Probably the date was something like 1970-01-01 00:00:00. _mktime64() doesnt like this.
153 //Dont let the application crash on this exception
155 #ifdef _AFX //CException classes are only defined when afx.h is included.
156 //When afx.h is not included, the exception is leaked.
157 //This will probably never happen because when CException is not defined, it cannot be thrown.
158 e->Delete();
159 #else
160 UNREFERENCED_PARAMETER(e);
161 #endif //ifdef _AFX
163 return CTime(); //Return an invalid time
166 int GitRev::SafeGetSimpleList(CGit *git)
168 if(InterlockedExchange(&m_IsUpdateing,TRUE) == FALSE)
170 m_SimpleFileList.clear();
171 git->CheckAndInitDll();
172 GIT_COMMIT commit;
173 GIT_COMMIT_LIST list;
174 GIT_HASH parent;
175 memset(&commit,0,sizeof(GIT_COMMIT));
177 CAutoLocker lock(g_Git.m_critGitDllSec);
181 if(git_get_commit_from_hash(&commit, this->m_CommitHash.m_hash))
182 return -1;
184 catch (char *)
186 return -1;
189 int i=0;
190 bool isRoot = this->m_ParentHash.empty();
191 git_get_commit_first_parent(&commit,&list);
192 while(git_get_commit_next_parent(&list,parent) == 0 || isRoot)
194 GIT_FILE file=0;
195 int count=0;
198 if(isRoot)
199 git_root_diff(git->GetGitSimpleListDiff(), commit.m_hash, &file, &count, 0);
200 else
201 git_diff(git->GetGitSimpleListDiff(), parent, commit.m_hash, &file, &count, 0);
203 catch (char *)
205 return -1;
208 isRoot = false;
210 CTGitPath path;
211 CString strnewname;
212 CString stroldname;
214 for(int j=0;j<count;j++)
216 path.Reset();
217 char *newname;
218 char *oldname;
220 strnewname.Empty();
221 stroldname.Empty();
223 int mode,IsBin,inc,dec;
226 git_get_diff_file(git->GetGitSimpleListDiff(), file, j, &newname, &oldname, &mode, &IsBin, &inc, &dec);
228 catch (char *)
230 return -1;
233 git->StringAppend(&strnewname, (BYTE*)newname, CP_UTF8);
235 m_SimpleFileList.push_back(strnewname);
238 git_diff_flush(git->GetGitSimpleListDiff());
239 i++;
242 InterlockedExchange(&m_IsUpdateing,FALSE);
243 InterlockedExchange(&m_IsSimpleListReady, TRUE);
244 git_free_commit(&commit);
247 return 0;
249 int GitRev::SafeFetchFullInfo(CGit *git)
251 if(InterlockedExchange(&m_IsUpdateing,TRUE) == FALSE)
253 this->m_Files.Clear();
254 git->CheckAndInitDll();
255 GIT_COMMIT commit;
256 GIT_COMMIT_LIST list;
257 GIT_HASH parent;
258 memset(&commit,0,sizeof(GIT_COMMIT));
260 CAutoLocker lock(g_Git.m_critGitDllSec);
264 if (git_get_commit_from_hash(&commit, this->m_CommitHash.m_hash))
265 return -1;
267 catch (char *)
269 return -1;
272 int i=0;
274 git_get_commit_first_parent(&commit,&list);
275 bool isRoot = (list==NULL);
277 while(git_get_commit_next_parent(&list,parent) == 0 || isRoot)
279 GIT_FILE file=0;
280 int count=0;
284 if (isRoot)
285 git_root_diff(git->GetGitDiff(), this->m_CommitHash.m_hash, &file, &count, 1);
286 else
287 git_diff(git->GetGitDiff(), parent, commit.m_hash, &file, &count, 1);
289 catch (char *)
291 git_free_commit(&commit);
292 return -1;
294 isRoot = false;
296 CTGitPath path;
297 CString strnewname;
298 CString stroldname;
300 for(int j=0;j<count;j++)
302 path.Reset();
303 char *newname;
304 char *oldname;
306 strnewname.Empty();
307 stroldname.Empty();
309 int mode,IsBin,inc,dec;
310 git_get_diff_file(git->GetGitDiff(),file,j,&newname,&oldname,
311 &mode,&IsBin,&inc,&dec);
313 git->StringAppend(&strnewname, (BYTE*)newname, CP_UTF8);
314 git->StringAppend(&stroldname, (BYTE*)oldname, CP_UTF8);
316 path.SetFromGit(strnewname,&stroldname);
317 path.ParserAction((BYTE)mode);
318 path.m_ParentNo = i;
320 this->m_Action|=path.m_Action;
322 if(IsBin)
324 path.m_StatAdd=_T("-");
325 path.m_StatDel=_T("-");
327 else
329 path.m_StatAdd.Format(_T("%d"),inc);
330 path.m_StatDel.Format(_T("%d"),dec);
332 m_Files.AddPath(path);
334 git_diff_flush(git->GetGitDiff());
335 i++;
339 InterlockedExchange(&m_IsUpdateing,FALSE);
340 InterlockedExchange(&m_IsFull,TRUE);
341 git_free_commit(&commit);
344 return 0;
347 int GitRev::ParserParentFromCommit(GIT_COMMIT *commit)
349 this->m_ParentHash.clear();
350 GIT_COMMIT_LIST list;
351 GIT_HASH parent;
353 git_get_commit_first_parent(commit,&list);
354 while(git_get_commit_next_parent(&list,parent)==0)
356 m_ParentHash.push_back(CGitHash((char *)parent));
358 return 0;
361 int GitRev::ParserFromCommit(GIT_COMMIT *commit)
363 int encode =CP_UTF8;
365 if(commit->m_Encode != 0 && commit->m_EncodeSize != 0)
367 CString str;
368 g_Git.StringAppend(&str, (BYTE*)commit->m_Encode, CP_UTF8, commit->m_EncodeSize);
369 encode = CUnicodeUtils::GetCPCode(str);
372 this->m_CommitHash = (char*)commit->m_hash;
374 this->m_AuthorDate = commit->m_Author.Date;
376 this->m_AuthorEmail.Empty();
377 g_Git.StringAppend(&m_AuthorEmail,(BYTE*)commit->m_Author.Email,encode,commit->m_Author.EmailSize);
379 this->m_AuthorName.Empty();
380 g_Git.StringAppend(&m_AuthorName,(BYTE*)commit->m_Author.Name,encode,commit->m_Author.NameSize);
382 this->m_Body.Empty();
383 g_Git.StringAppend(&m_Body,(BYTE*)commit->m_Body,encode,commit->m_BodySize);
385 this->m_CommitterDate = commit->m_Committer.Date;
387 this->m_CommitterEmail.Empty();
388 g_Git.StringAppend(&m_CommitterEmail, (BYTE*)commit->m_Committer.Email,encode, commit->m_Committer.EmailSize);
390 this->m_CommitterName.Empty();
391 g_Git.StringAppend(&m_CommitterName, (BYTE*)commit->m_Committer.Name,encode, commit->m_Committer.NameSize);
393 this->m_Subject.Empty();
394 g_Git.StringAppend(&m_Subject, (BYTE*)commit->m_Subject,encode,commit->m_SubjectSize);
396 return 0;
398 void GitRev::DbgPrint()
400 ATLTRACE(_T("Commit %s\r\n"), this->m_CommitHash.ToString());
401 for(unsigned int i=0;i<this->m_ParentHash.size();i++)
403 ATLTRACE(_T("Parent %i %s"), i, m_ParentHash[i].ToString());
405 ATLTRACE(_T("\n"));
408 int GitRev::GetParentFromHash(CGitHash &hash)
410 CAutoLocker lock(g_Git.m_critGitDllSec);
412 g_Git.CheckAndInitDll();
414 GIT_COMMIT commit;
415 if(git_get_commit_from_hash( &commit, hash.m_hash))
416 return -1;
418 this->ParserParentFromCommit(&commit);
419 git_free_commit(&commit);
421 this->m_CommitHash=hash;
423 return 0;
425 int GitRev::GetCommitFromHash(CGitHash &hash)
427 CAutoLocker lock(g_Git.m_critGitDllSec);
429 g_Git.CheckAndInitDll();
431 return GetCommitFromHash_withoutLock(hash);
434 int GitRev::GetCommitFromHash_withoutLock(CGitHash &hash)
436 GIT_COMMIT commit;
437 if(git_get_commit_from_hash( &commit, hash.m_hash))
438 return -1;
440 this->ParserFromCommit(&commit);
441 git_free_commit(&commit);
443 return 0;
447 int GitRev::GetCommit(CString refname)
449 CAutoLocker lock(g_Git.m_critGitDllSec);
451 g_Git.CheckAndInitDll();
453 if(refname.GetLength() >= 8)
454 if(refname.Find(_T("00000000")) == 0)
456 this->m_CommitHash.Empty();
457 this->m_Subject=_T("Working Copy");
458 return 0;
460 CStringA rev;
461 rev= CUnicodeUtils::GetUTF8(g_Git.FixBranchName(refname));
462 GIT_HASH sha;
464 if(git_get_sha1(rev.GetBuffer(),sha))
465 return -1;
467 CGitHash hash((char*)sha);
468 GetCommitFromHash_withoutLock(hash);
469 return 0;
472 int GitRev::AddMergeFiles()
474 std::map<CString, int> map;
475 std::map<CString, int>::iterator it;
477 for(int i=0;i<m_Files.GetCount();i++)
479 if(map.find(m_Files[i].GetGitPathString()) == map.end())
481 map[m_Files[i].GetGitPathString()]=0;
483 else
485 map[m_Files[i].GetGitPathString()]++;
489 for(it=map.begin();it!=map.end();it++)
491 if(it->second)
493 CTGitPath path;
494 path.SetFromGit(it->first);
495 path.m_ParentNo = MERGE_MASK;
496 path.m_StatAdd=_T("-");
497 path.m_StatDel=_T("-");
498 path.m_Action = CTGitPath::LOGACTIONS_MERGED;
499 m_Files.AddPath(path);
502 return 0;