CGitDiff::Diff: Diffing with non SHA-1 revisions < 40 chars and slashes failed
[TortoiseGit.git] / src / Git / GitRev.cpp
blobd1aaf2e12bad5c4c3a316b057c4008ffc21090e9
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2012 - 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 #endif //ifdef _AFX
161 return CTime(); //Return an invalid time
164 int GitRev::SafeGetSimpleList(CGit *git)
166 if(InterlockedExchange(&m_IsUpdateing,TRUE) == FALSE)
168 m_SimpleFileList.clear();
169 git->CheckAndInitDll();
170 GIT_COMMIT commit;
171 GIT_COMMIT_LIST list;
172 GIT_HASH parent;
173 memset(&commit,0,sizeof(GIT_COMMIT));
175 CAutoLocker lock(g_Git.m_critGitDllSec);
179 if(git_get_commit_from_hash(&commit, this->m_CommitHash.m_hash))
180 return -1;
182 catch (char * msg)
184 return -1;
187 int i=0;
188 bool isRoot = this->m_ParentHash.size()==0;
189 git_get_commit_first_parent(&commit,&list);
190 while(git_get_commit_next_parent(&list,parent) == 0 || isRoot)
192 GIT_FILE file=0;
193 int count=0;
196 if(isRoot)
197 git_root_diff(git->GetGitSimpleListDiff(), commit.m_hash, &file, &count, 0);
198 else
199 git_diff(git->GetGitSimpleListDiff(), parent, commit.m_hash, &file, &count, 0);
201 catch (char * msg)
203 return -1;
206 isRoot = false;
208 CTGitPath path;
209 CString strnewname;
210 CString stroldname;
212 for(int j=0;j<count;j++)
214 path.Reset();
215 char *newname;
216 char *oldname;
218 strnewname.Empty();
219 stroldname.Empty();
221 int mode,IsBin,inc,dec;
224 git_get_diff_file(git->GetGitSimpleListDiff(), file, j, &newname, &oldname, &mode, &IsBin, &inc, &dec);
226 catch (char * msg)
228 return -1;
231 git->StringAppend(&strnewname, (BYTE*)newname, CP_UTF8);
233 m_SimpleFileList.push_back(strnewname);
236 git_diff_flush(git->GetGitSimpleListDiff());
237 i++;
240 InterlockedExchange(&m_IsUpdateing,FALSE);
241 InterlockedExchange(&m_IsSimpleListReady, TRUE);
242 git_free_commit(&commit);
245 return 0;
247 int GitRev::SafeFetchFullInfo(CGit *git)
249 if(InterlockedExchange(&m_IsUpdateing,TRUE) == FALSE)
251 this->m_Files.Clear();
252 git->CheckAndInitDll();
253 GIT_COMMIT commit;
254 GIT_COMMIT_LIST list;
255 GIT_HASH parent;
256 memset(&commit,0,sizeof(GIT_COMMIT));
258 CAutoLocker lock(g_Git.m_critGitDllSec);
262 if (git_get_commit_from_hash(&commit, this->m_CommitHash.m_hash))
263 return -1;
265 catch (char * msg)
267 return -1;
270 int i=0;
272 git_get_commit_first_parent(&commit,&list);
273 bool isRoot = (list==NULL);
275 while(git_get_commit_next_parent(&list,parent) == 0 || isRoot)
277 GIT_FILE file=0;
278 int count=0;
282 if (isRoot)
283 git_root_diff(git->GetGitDiff(), this->m_CommitHash.m_hash, &file, &count, 1);
284 else
285 git_diff(git->GetGitDiff(), parent, commit.m_hash, &file, &count, 1);
287 catch (char * msg)
289 git_free_commit(&commit);
290 return -1;
292 isRoot = false;
294 CTGitPath path;
295 CString strnewname;
296 CString stroldname;
298 for(int j=0;j<count;j++)
300 path.Reset();
301 char *newname;
302 char *oldname;
304 strnewname.Empty();
305 stroldname.Empty();
307 int mode,IsBin,inc,dec;
308 git_get_diff_file(git->GetGitDiff(),file,j,&newname,&oldname,
309 &mode,&IsBin,&inc,&dec);
311 git->StringAppend(&strnewname, (BYTE*)newname, CP_UTF8);
312 git->StringAppend(&stroldname, (BYTE*)oldname, CP_UTF8);
314 path.SetFromGit(strnewname,&stroldname);
315 path.ParserAction((BYTE)mode);
316 path.m_ParentNo = i;
318 this->m_Action|=path.m_Action;
320 if(IsBin)
322 path.m_StatAdd=_T("-");
323 path.m_StatDel=_T("-");
325 else
327 path.m_StatAdd.Format(_T("%d"),inc);
328 path.m_StatDel.Format(_T("%d"),dec);
330 m_Files.AddPath(path);
332 git_diff_flush(git->GetGitDiff());
333 i++;
337 InterlockedExchange(&m_IsUpdateing,FALSE);
338 InterlockedExchange(&m_IsFull,TRUE);
339 git_free_commit(&commit);
342 return 0;
345 int GitRev::ParserParentFromCommit(GIT_COMMIT *commit)
347 this->m_ParentHash.clear();
348 GIT_COMMIT_LIST list;
349 GIT_HASH parent;
351 git_get_commit_first_parent(commit,&list);
352 while(git_get_commit_next_parent(&list,parent)==0)
354 m_ParentHash.push_back(CGitHash((char *)parent));
356 return 0;
359 int GitRev::ParserFromCommit(GIT_COMMIT *commit)
361 int encode =CP_UTF8;
363 if(commit->m_Encode != 0 && commit->m_EncodeSize != 0)
365 CString str;
366 g_Git.StringAppend(&str, (BYTE*)commit->m_Encode, CP_UTF8, commit->m_EncodeSize);
367 encode = CUnicodeUtils::GetCPCode(str);
370 this->m_AuthorDate = commit->m_Author.Date;
372 this->m_AuthorEmail.Empty();
373 g_Git.StringAppend(&m_AuthorEmail,(BYTE*)commit->m_Author.Email,encode,commit->m_Author.EmailSize);
375 this->m_AuthorName.Empty();
376 g_Git.StringAppend(&m_AuthorName,(BYTE*)commit->m_Author.Name,encode,commit->m_Author.NameSize);
378 this->m_Body.Empty();
379 g_Git.StringAppend(&m_Body,(BYTE*)commit->m_Body,encode,commit->m_BodySize);
381 this->m_CommitterDate = commit->m_Committer.Date;
383 this->m_CommitterEmail.Empty();
384 g_Git.StringAppend(&m_CommitterEmail, (BYTE*)commit->m_Committer.Email,encode, commit->m_Committer.EmailSize);
386 this->m_CommitterName.Empty();
387 g_Git.StringAppend(&m_CommitterName, (BYTE*)commit->m_Committer.Name,encode, commit->m_Committer.NameSize);
389 this->m_Subject.Empty();
390 g_Git.StringAppend(&m_Subject, (BYTE*)commit->m_Subject,encode,commit->m_SubjectSize);
392 return 0;
394 #ifndef TRACE
395 #define TRACE(x) 1?0:(x)
396 #endif
397 void GitRev::DbgPrint()
399 TRACE(_T("Commit %s\r\n"), this->m_CommitHash.ToString());
400 for(unsigned int i=0;i<this->m_ParentHash.size();i++)
402 TRACE(_T("Parent %i %s"),i, m_ParentHash[i].ToString());
404 TRACE(_T("\n"));
407 int GitRev::GetParentFromHash(CGitHash &hash)
409 CAutoLocker lock(g_Git.m_critGitDllSec);
411 g_Git.CheckAndInitDll();
413 GIT_COMMIT commit;
414 if(git_get_commit_from_hash( &commit, hash.m_hash))
415 return -1;
417 this->ParserParentFromCommit(&commit);
418 git_free_commit(&commit);
420 this->m_CommitHash=hash;
422 return 0;
424 int GitRev::GetCommitFromHash(CGitHash &hash)
426 CAutoLocker lock(g_Git.m_critGitDllSec);
428 g_Git.CheckAndInitDll();
430 return GetCommitFromHash_withoutLock(hash);
433 int GitRev::GetCommitFromHash_withoutLock(CGitHash &hash)
435 GIT_COMMIT commit;
436 if(git_get_commit_from_hash( &commit, hash.m_hash))
437 return -1;
439 this->ParserFromCommit(&commit);
440 git_free_commit(&commit);
442 this->m_CommitHash=hash;
444 return 0;
448 int GitRev::GetCommit(CString refname)
450 CAutoLocker lock(g_Git.m_critGitDllSec);
452 g_Git.CheckAndInitDll();
454 if(refname.GetLength() >= 8)
455 if(refname.Find(_T("00000000")) == 0)
457 this->m_CommitHash.Empty();
458 this->m_Subject=_T("Working Copy");
459 return 0;
461 CStringA rev;
462 rev= CUnicodeUtils::GetUTF8(g_Git.FixBranchName(refname));
463 GIT_HASH sha;
465 if(git_get_sha1(rev.GetBuffer(),sha))
466 return -1;
468 CGitHash hash((char*)sha);
469 GetCommitFromHash_withoutLock(hash);
470 return 0;
473 int GitRev::AddMergeFiles()
475 std::map<CString, int> map;
476 std::map<CString, int>::iterator it;
478 for(int i=0;i<m_Files.GetCount();i++)
480 if(map.find(m_Files[i].GetGitPathString()) == map.end())
482 map[m_Files[i].GetGitPathString()]=0;
484 else
486 map[m_Files[i].GetGitPathString()]++;
490 for(it=map.begin();it!=map.end();it++)
492 if(it->second)
494 CTGitPath path;
495 path.SetFromGit(it->first);
496 path.m_ParentNo = MERGE_MASK;
497 path.m_StatAdd=_T("-");
498 path.m_StatDel=_T("-");
499 path.m_Action = CTGitPath::LOGACTIONS_MERGED;
500 m_Files.AddPath(path);
503 return 0;