OSDN Git Service

Basic File diff dialog is okay to show prediff command
[tortoisegit/TortoiseGitJp.git] / src / Git / GitRev.cpp
1 #include "StdAfx.h"\r
2 #include "ATLComTime.h"\r
3 #include "GitRev.h"\r
4 #include "Git.h"\r
5 #include "GitDLL.h"\r
6 #include "UnicodeUtils.h"\r
7 \r
8 class CException; //Just in case afx.h is not included (cannot be included in every project which uses this file)\r
9 \r
10 // provide an ASSERT macro for when compiled without MFC\r
11 #if !defined ASSERT\r
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().\r
13         #define ASSERT(x) _ASSERTE(x)\r
14 #endif\r
15 \r
16 \r
17 GitRev::GitRev(void)\r
18 {\r
19         m_Action=0;\r
20         m_IsFull = 0;\r
21         m_IsUpdateing = 0;\r
22         // fetch local machine timezone info\r
23         if ( GetTimeZoneInformation( &m_TimeZone ) == TIME_ZONE_ID_INVALID )\r
24         {\r
25                 ASSERT(false);\r
26         }\r
27 }\r
28 \r
29 GitRev::~GitRev(void)\r
30 {\r
31 }\r
32 \r
33 #if 0\r
34 GitRev::GitRev(GitRev & rev)\r
35 {\r
36 }\r
37 GitRev& GitRev::operator=(GitRev &rev)\r
38 {\r
39         return *this;\r
40 }\r
41 #endif\r
42 void GitRev::Clear()\r
43 {\r
44         this->m_Action=0;\r
45         this->m_Files.Clear();\r
46         this->m_Action=0;\r
47         this->m_ParentHash.clear();\r
48         m_CommitterName.Empty();\r
49         m_CommitterEmail.Empty();\r
50         m_Body.Empty();\r
51         m_Subject.Empty();\r
52         m_CommitHash.Empty();\r
53         m_Ref.Empty();\r
54         m_RefAction.Empty();\r
55         m_Mark=0;\r
56 \r
57 }\r
58 int GitRev::CopyFrom(GitRev &rev,bool OmitParentAndMark)\r
59 {\r
60         m_AuthorName    =rev.m_AuthorName       ;\r
61         m_AuthorEmail   =rev.m_AuthorEmail      ;\r
62         m_AuthorDate    =rev.m_AuthorDate       ;\r
63         m_CommitterName =rev.m_CommitterName    ;\r
64         m_CommitterEmail=rev.m_CommitterEmail;\r
65         m_CommitterDate =rev.m_CommitterDate    ;\r
66         m_Subject               =rev.m_Subject          ;\r
67         m_Body                  =rev.m_Body                     ;\r
68         m_CommitHash    =rev.m_CommitHash       ;\r
69         m_Files                 =rev.m_Files                    ;       \r
70         m_Action                =rev.m_Action           ;\r
71         m_IsFull                =rev.m_IsFull;\r
72 \r
73         if(!OmitParentAndMark)\r
74         {\r
75                 m_ParentHash    =rev.m_ParentHash       ;\r
76                 m_Mark                  =rev.m_Mark;\r
77         }\r
78         return 0;\r
79 }\r
80 int GitRev::ParserFromLog(BYTE_VECTOR &log,int start)\r
81 {\r
82         int pos=start;\r
83         CString one;\r
84         CString key;\r
85         CString text;\r
86         BYTE_VECTOR filelist;\r
87         BYTE mode=0;\r
88         CTGitPath  path;\r
89         this->m_Files.Clear();\r
90     m_Action=0;\r
91         int begintime=0;\r
92         int filebegin=-1;\r
93 \r
94         while( pos < log.size() && pos>=0)\r
95         {\r
96                 \r
97                 //one=log.Tokenize(_T("\n"),pos);\r
98                 if(log[pos]==_T('#') && log[pos+1] == _T('<') && log[pos+3] == _T('>'))\r
99                 {\r
100                         //text = one.Right(one.GetLength()-4);\r
101                         text.Empty();\r
102                         g_Git.StringAppend(&text,&log[pos+4],CGit::m_LogEncode);\r
103                         mode = log[pos+2];\r
104                         \r
105                         switch(mode)\r
106                         {\r
107                         case LOG_REV_ITEM_BEGIN:\r
108                                 begintime++;\r
109                                 if(begintime>1)\r
110                                         break;\r
111                                 else\r
112                                         this->Clear();\r
113                                 break;\r
114                         case LOG_REV_AUTHOR_NAME:\r
115                                 this->m_AuthorName = text;\r
116                                 break;\r
117                         case LOG_REV_AUTHOR_EMAIL:\r
118                                 this->m_AuthorEmail = text;\r
119                                 break;\r
120                         case LOG_REV_AUTHOR_DATE:\r
121                                 this->m_AuthorDate =ConverFromString(text);\r
122                                 break;\r
123                         case LOG_REV_COMMIT_NAME:\r
124                                 this->m_CommitterName = text;\r
125                                 break;\r
126                         case LOG_REV_COMMIT_EMAIL:\r
127                                 this->m_CommitterEmail = text;\r
128                                 break;\r
129                         case LOG_REV_COMMIT_DATE:\r
130                                 this->m_CommitterDate =ConverFromString(text);\r
131                                 break;\r
132                         case LOG_REV_COMMIT_SUBJECT:\r
133                                 this->m_Subject = text;\r
134                                 break;\r
135                         case LOG_REV_COMMIT_BODY:\r
136                                 this->m_Body = text +_T("\n");\r
137                                 break;\r
138                         case LOG_REV_COMMIT_HASH:\r
139                                 this->m_CommitHash = text.Right(40);\r
140                                 if(text.GetLength()>40)\r
141                                 {\r
142                                         this->m_Mark=text[0];\r
143                                 }\r
144                                 break;\r
145                         case LOG_REV_COMMIT_PARENT:\r
146                                 while(text.GetLength()>0)\r
147                                 {\r
148                                         this->m_ParentHash.insert(this->m_ParentHash.end(),text.Left(40));\r
149                                         if(text.GetLength()>40)\r
150                                                 text=text.Right(text.GetLength()-41);\r
151                                         else\r
152                                                 break;\r
153                                 }\r
154                                 if(m_ParentHash.size()>1)\r
155                                 {\r
156                                         int a=1;\r
157                                 }\r
158                                 break;\r
159                         case LOG_REV_COMMIT_FILE:\r
160                                 break;\r
161                         }\r
162                 }else\r
163                 {\r
164                         switch(mode)\r
165                         {\r
166 //                      case LOG_REV_COMMIT_BODY:\r
167 //                              this->m_Body += one+_T("\n");\r
168 //                              break;\r
169                         case LOG_REV_COMMIT_FILE:\r
170                                 //filelist += one +_T("\n");\r
171                                 //filelist.append(log,pos,log.find(0,pos));\r
172                                 if(filebegin<0)\r
173                                         filebegin=pos;\r
174                                 break;\r
175                         }\r
176                 }\r
177                 \r
178                 if(begintime>1)\r
179                 {\r
180                         break;\r
181                 }\r
182 \r
183                 //find next string start \r
184                 pos=log.findNextString(pos);\r
185         }\r
186         \r
187         if(filebegin>=0)\r
188         {\r
189                 \r
190                 filelist.append(log,filebegin,pos);     \r
191                 this->m_Files.ParserFromLog(filelist);\r
192                 this->m_Action=this->m_Files.GetAction();\r
193         }\r
194         return pos;\r
195 }\r
196 \r
197 CTime GitRev::ConverFromString(CString input)\r
198 {\r
199         // pick up date from string\r
200         try\r
201         {\r
202                 COleDateTime tm(_wtoi(input.Mid(0,4)),\r
203                                  _wtoi(input.Mid(5,2)),\r
204                                  _wtoi(input.Mid(8,2)),\r
205                                  _wtoi(input.Mid(11,2)),\r
206                                  _wtoi(input.Mid(14,2)),\r
207                                  _wtoi(input.Mid(17,2)));\r
208                 if( tm.GetStatus() != COleDateTime::valid )\r
209                         return CTime();//Error parsing time-string\r
210 \r
211                 // pick up utc offset\r
212                 CString sign = input.Mid(20,1);         // + or -\r
213                 int hoursOffset =  _wtoi(input.Mid(21,2));\r
214                 int minsOffset = _wtoi(input.Mid(23,2));\r
215                 // convert to a fraction of a day\r
216                 double offset = (hoursOffset*60 + minsOffset) / 1440.0;         // 1440 mins = 1 day\r
217                 if ( sign == "-" )\r
218                 {\r
219                         offset = -offset;\r
220                 }\r
221                 // we have to subtract this from the time given to get UTC\r
222                 tm -= offset;\r
223                 // get utc time as a SYSTEMTIME\r
224                 SYSTEMTIME sysTime;\r
225                 tm.GetAsSystemTime( sysTime );\r
226                 // and convert to users local time\r
227                 SYSTEMTIME local;\r
228                 if ( SystemTimeToTzSpecificLocalTime( &m_TimeZone, &sysTime, &local ) )\r
229                 {\r
230                         sysTime = local;\r
231                 }\r
232                 else\r
233                 {\r
234                         ASSERT(false);  // this should not happen but leave time in utc if it does\r
235                 }\r
236                 // convert to CTime and return\r
237                 return CTime( sysTime, -1 );;\r
238         }\r
239         catch(CException* e)\r
240         {\r
241                 //Probably the date was something like 1970-01-01 00:00:00. _mktime64() doesnt like this.\r
242                 //Dont let the application crash on this exception\r
243 \r
244 #ifdef _AFX //CException classes are only defined when afx.h is included.\r
245                         //When afx.h is not included, the exception is leaked.\r
246                         //This will probably never happen because when CException is not defined, it cannot be thrown.\r
247                 e->Delete();\r
248 #endif //ifdef _AFX\r
249         }\r
250         return CTime(); //Return an invalid time\r
251 }\r
252 \r
253 int GitRev::SafeFetchFullInfo(CGit *git)\r
254 {\r
255         if(InterlockedExchange(&m_IsUpdateing,TRUE) == FALSE)\r
256         {\r
257 #if 0\r
258                 //GitRev rev;\r
259                 BYTE_VECTOR onelog;\r
260                 TCHAR oldmark=this->m_Mark;\r
261                 CString commithash = m_CommitHash;\r
262                 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);\r
263                 CString oldhash=m_CommitHash;\r
264                 GIT_REV_LIST oldlist=this->m_ParentHash;\r
265                 ParserFromLog(onelog);\r
266                 \r
267                 //ASSERT(oldhash==m_CommitHash);\r
268                 if(oldmark!=0)\r
269                         this->m_Mark=oldmark;  //parser full log will cause old mark overwrited. \r
270                                                                //So we need keep old bound mark.\r
271                 this->m_ParentHash=oldlist;\r
272                 InterlockedExchange(&m_IsUpdateing,FALSE);\r
273                 InterlockedExchange(&m_IsFull,TRUE);\r
274                 return 0;\r
275 #endif\r
276                 this->m_Files.Clear();\r
277                 git->CheckAndInitDll();\r
278                 GIT_COMMIT commit;\r
279                 GIT_COMMIT_LIST list;\r
280                 GIT_HASH   parent;\r
281                 if(git_get_commit_from_hash(&commit, this->m_CommitHash.m_hash))\r
282                         return -1;\r
283 \r
284                 int i=0;\r
285                 bool isRoot = this->m_ParentHash.size()==0;\r
286                 git_get_commit_first_parent(&commit,&list);\r
287                 while(git_get_commit_next_parent(&list,parent) == 0 || isRoot)\r
288                 {\r
289                         GIT_FILE file;\r
290                         int count;\r
291                         if(isRoot)\r
292                                 git_root_diff(git->GetGitDiff(), this->m_CommitHash.m_hash, &file, &count);\r
293                         else\r
294                                 git_diff(git->GetGitDiff(),parent,commit.m_hash,&file,&count);\r
295                         \r
296                         isRoot = false;\r
297 \r
298                         CTGitPath path;\r
299                         CString strnewname;\r
300                         CString stroldname;\r
301                         \r
302                         for(int j=0;j<count;j++)\r
303                         {\r
304                                 path.Reset();\r
305                                 char *newname;\r
306                                 char *oldname;\r
307                                 \r
308                                 strnewname.Empty();\r
309                                 stroldname.Empty();\r
310 \r
311                                 int mode,IsBin,inc,dec;\r
312                                 git_get_diff_file(git->GetGitDiff(),file,j,&newname,&oldname,\r
313                                                 &mode,&IsBin,&inc,&dec);\r
314                                 \r
315                                 git->StringAppend(&strnewname,(BYTE*)newname,CP_ACP);\r
316                                 git->StringAppend(&stroldname,(BYTE*)oldname,CP_ACP);\r
317 \r
318                                 path.m_ParentNo = i;\r
319                                 path.SetFromGit(strnewname,&stroldname);\r
320                                 path.ParserAction((BYTE)mode);\r
321 \r
322                                 this->m_Action|=path.m_Action;\r
323 \r
324                                 if(IsBin)\r
325                                 {\r
326                                         path.m_StatAdd=_T("-");\r
327                                         path.m_StatDel=_T("-");\r
328                                 }else\r
329                                 {\r
330                                         path.m_StatAdd.Format(_T("%d"),inc);\r
331                                         path.m_StatDel.Format(_T("%d"),dec);\r
332                                 }\r
333                                 m_Files.AddPath(path);\r
334                         }\r
335                         git_diff_flush(git->GetGitDiff());\r
336                         i++;\r
337                 }\r
338 \r
339                 InterlockedExchange(&m_IsUpdateing,FALSE);\r
340                 InterlockedExchange(&m_IsFull,TRUE);\r
341 \r
342         }\r
343         return -1;\r
344 }\r
345 \r
346 int GitRev::ParserParentFromCommit(GIT_COMMIT *commit)\r
347 {\r
348         this->m_ParentHash.clear();\r
349         GIT_COMMIT_LIST list;\r
350         GIT_HASH   parent;\r
351         \r
352         git_get_commit_first_parent(commit,&list);\r
353         while(git_get_commit_next_parent(&list,parent)==0)\r
354         {\r
355                 m_ParentHash.push_back(CGitHash((char *)parent));\r
356         }\r
357         return 0;\r
358 }\r
359 \r
360 int GitRev::ParserFromCommit(GIT_COMMIT *commit)\r
361 {\r
362         int encode =CP_UTF8;\r
363         \r
364         if(commit->m_Encode != 0 && commit->m_EncodeSize != 0)\r
365         {\r
366                 CString str;\r
367                 g_Git.StringAppend(&str, (BYTE*)commit->m_Encode, CP_UTF8, commit->m_EncodeSize);\r
368                 encode = CUnicodeUtils::GetCPCode(str);\r
369         }\r
370 \r
371         this->m_AuthorDate = commit->m_Author.Date;\r
372         \r
373         this->m_AuthorEmail.Empty();\r
374         g_Git.StringAppend(&m_AuthorEmail,(BYTE*)commit->m_Author.Email,CP_UTF8,commit->m_Author.EmailSize);\r
375 \r
376         this->m_AuthorName.Empty();\r
377         g_Git.StringAppend(&m_AuthorName,(BYTE*)commit->m_Author.Name,CP_UTF8,commit->m_Author.NameSize);\r
378         \r
379         this->m_Body.Empty();\r
380         g_Git.StringAppend(&m_Body,(BYTE*)commit->m_Body,encode,commit->m_BodySize);\r
381 \r
382         this->m_CommitterDate = commit->m_Committer.Date;\r
383         \r
384         this->m_CommitterEmail.Empty();\r
385         g_Git.StringAppend(&m_CommitterEmail, (BYTE*)commit->m_Committer.Email,CP_UTF8, commit->m_Committer.EmailSize);\r
386 \r
387         this->m_CommitterName.Empty();\r
388         g_Git.StringAppend(&m_CommitterName, (BYTE*)commit->m_Committer.Name,CP_UTF8, commit->m_Committer.NameSize);\r
389 \r
390         this->m_Subject.Empty();\r
391         g_Git.StringAppend(&m_Subject, (BYTE*)commit->m_Subject,encode,commit->m_SubjectSize);\r
392         \r
393         return 0;\r
394 }\r
395 #ifndef TRACE\r
396 #define TRACE(x) 1?0:(x)\r
397 #endif\r
398 void GitRev::DbgPrint()\r
399 {\r
400         TRACE(_T("Commit %s\r\n"), this->m_CommitHash.ToString());\r
401         for(int i=0;i<this->m_ParentHash.size();i++)\r
402         {\r
403                 TRACE(_T("Parent %i %s"),i, m_ParentHash[i].ToString());\r
404         }\r
405         TRACE(_T("\n"));\r
406 }\r
407 \r
408 int GitRev::GetCommitFromHash(CGitHash &hash)\r
409 {\r
410         g_Git.CheckAndInitDll();\r
411 \r
412         GIT_COMMIT commit;\r
413         if(git_get_commit_from_hash( &commit, hash.m_hash))\r
414                 return -1;\r
415 \r
416         this->ParserFromCommit(&commit);\r
417         git_free_commit(&commit);\r
418 \r
419         this->m_CommitHash=hash;\r
420 \r
421         return 0;\r
422         \r
423 }\r
424 \r
425 int GitRev::GetCommit(CString &refname)\r
426 {\r
427         g_Git.CheckAndInitDll();\r
428         CStringA rev;\r
429         rev= CUnicodeUtils::GetUTF8(refname);\r
430         GIT_HASH sha;\r
431 \r
432         if(git_get_sha1(rev.GetBuffer(),sha))\r
433                 return -1;\r
434 \r
435         GetCommitFromHash(CGitHash((char*)sha));\r
436 }