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.
21 #include "ATLComTime.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
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)
35 typedef CComCritSecLock
<CComCriticalSection
> CAutoLocker
;
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
)
61 GitRev::GitRev(GitRev
& rev
)
64 GitRev
& GitRev::operator=(GitRev
&rev
)
72 this->m_Files
.Clear();
74 this->m_ParentHash
.clear();
75 m_CommitterName
.Empty();
76 m_CommitterEmail
.Empty();
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
;
95 m_CommitHash
=rev
.m_CommitHash
;
97 m_Action
=rev
.m_Action
;
98 m_IsFull
=rev
.m_IsFull
;
100 if(!OmitParentAndMark
)
102 m_ParentHash
=rev
.m_ParentHash
;
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
132 // we have to subtract this from the time given to get UTC
134 // get utc time as a SYSTEMTIME
136 tm
.GetAsSystemTime( sysTime
);
137 // and convert to users local time
139 if ( SystemTimeToTzSpecificLocalTime( &m_TimeZone
, &sysTime
, &local
) )
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 );
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.
160 UNREFERENCED_PARAMETER(e
);
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();
173 GIT_COMMIT_LIST list
;
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
))
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
)
199 git_root_diff(git
->GetGitSimpleListDiff(), commit
.m_hash
, &file
, &count
, 0);
201 git_diff(git
->GetGitSimpleListDiff(), parent
, commit
.m_hash
, &file
, &count
, 0);
214 for(int j
=0;j
<count
;j
++)
223 int mode
,IsBin
,inc
,dec
;
226 git_get_diff_file(git
->GetGitSimpleListDiff(), file
, j
, &newname
, &oldname
, &mode
, &IsBin
, &inc
, &dec
);
233 git
->StringAppend(&strnewname
, (BYTE
*)newname
, CP_UTF8
);
235 m_SimpleFileList
.push_back(strnewname
);
238 git_diff_flush(git
->GetGitSimpleListDiff());
242 InterlockedExchange(&m_IsUpdateing
,FALSE
);
243 InterlockedExchange(&m_IsSimpleListReady
, TRUE
);
244 git_free_commit(&commit
);
249 int GitRev::SafeFetchFullInfo(CGit
*git
)
251 if(InterlockedExchange(&m_IsUpdateing
,TRUE
) == FALSE
)
253 this->m_Files
.Clear();
254 git
->CheckAndInitDll();
256 GIT_COMMIT_LIST list
;
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
))
274 git_get_commit_first_parent(&commit
,&list
);
275 bool isRoot
= (list
==NULL
);
277 while(git_get_commit_next_parent(&list
,parent
) == 0 || isRoot
)
285 git_root_diff(git
->GetGitDiff(), this->m_CommitHash
.m_hash
, &file
, &count
, 1);
287 git_diff(git
->GetGitDiff(), parent
, commit
.m_hash
, &file
, &count
, 1);
291 git_free_commit(&commit
);
300 for(int j
=0;j
<count
;j
++)
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
);
320 this->m_Action
|=path
.m_Action
;
324 path
.m_StatAdd
=_T("-");
325 path
.m_StatDel
=_T("-");
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());
339 InterlockedExchange(&m_IsUpdateing
,FALSE
);
340 InterlockedExchange(&m_IsFull
,TRUE
);
341 git_free_commit(&commit
);
347 int GitRev::ParserParentFromCommit(GIT_COMMIT
*commit
)
349 this->m_ParentHash
.clear();
350 GIT_COMMIT_LIST list
;
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
));
361 int GitRev::ParserFromCommit(GIT_COMMIT
*commit
)
365 if(commit
->m_Encode
!= 0 && commit
->m_EncodeSize
!= 0)
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
);
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());
408 int GitRev::GetParentFromHash(CGitHash
&hash
)
410 CAutoLocker
lock(g_Git
.m_critGitDllSec
);
412 g_Git
.CheckAndInitDll();
415 if(git_get_commit_from_hash( &commit
, hash
.m_hash
))
418 this->ParserParentFromCommit(&commit
);
419 git_free_commit(&commit
);
421 this->m_CommitHash
=hash
;
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
)
437 if(git_get_commit_from_hash( &commit
, hash
.m_hash
))
440 this->ParserFromCommit(&commit
);
441 git_free_commit(&commit
);
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");
461 rev
= CUnicodeUtils::GetUTF8(g_Git
.FixBranchName(refname
));
464 if(git_get_sha1(rev
.GetBuffer(),sha
))
467 CGitHash
hash((char*)sha
);
468 GetCommitFromHash_withoutLock(hash
);
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;
485 map
[m_Files
[i
].GetGitPathString()]++;
489 for(it
=map
.begin();it
!=map
.end();it
++)
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
);