OSDN Git Service

Use -z parser at gitlog
[tortoisegit/TortoiseGitJp.git] / src / Git / Git.cpp
1 #include "StdAfx.h"\r
2 #include "Git.h"\r
3 #include "atlconv.h"\r
4 #include "GitRev.h"\r
5 #include "registry.h"\r
6 \r
7 #define MAX_DIRBUFFER 1000\r
8 CGit g_Git;\r
9 CGit::CGit(void)\r
10 {\r
11         GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));\r
12 }\r
13 \r
14 CGit::~CGit(void)\r
15 {\r
16 }\r
17 \r
18 static char g_Buffer[4096];\r
19 \r
20 int CGit::RunAsync(CString cmd,PROCESS_INFORMATION *piOut,HANDLE *hReadOut,CString *StdioFile)\r
21 {\r
22         SECURITY_ATTRIBUTES sa;\r
23         HANDLE hRead, hWrite;\r
24         HANDLE hStdioFile;\r
25 \r
26         sa.nLength = sizeof(SECURITY_ATTRIBUTES);\r
27         sa.lpSecurityDescriptor=NULL;\r
28         sa.bInheritHandle=TRUE;\r
29         if(!CreatePipe(&hRead,&hWrite,&sa,0))\r
30         {\r
31                 return GIT_ERROR_OPEN_PIP;\r
32         }\r
33         \r
34         if(StdioFile)\r
35         {\r
36                 hStdioFile=CreateFile(*StdioFile,GENERIC_WRITE,FILE_SHARE_READ   |   FILE_SHARE_WRITE,   \r
37                         &sa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);  \r
38         }\r
39 \r
40         STARTUPINFO si;\r
41         PROCESS_INFORMATION pi;\r
42         si.cb=sizeof(STARTUPINFO);\r
43         GetStartupInfo(&si);\r
44 \r
45         si.hStdError=hWrite;\r
46         if(StdioFile)\r
47                 si.hStdOutput=hStdioFile;\r
48         else\r
49                 si.hStdOutput=hWrite;\r
50 \r
51         si.wShowWindow=SW_HIDE;\r
52         si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;\r
53 \r
54         if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,NULL,NULL,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))\r
55         {\r
56                 LPVOID lpMsgBuf;\r
57                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,\r
58                         NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
59                         (LPTSTR)&lpMsgBuf,\r
60                         0,NULL);\r
61                 return GIT_ERROR_CREATE_PROCESS;\r
62         }\r
63         \r
64         CloseHandle(hWrite);\r
65         if(piOut)\r
66                 *piOut=pi;\r
67         if(hReadOut)\r
68                 *hReadOut=hRead;\r
69         \r
70         return 0;\r
71 \r
72 }\r
73 //Must use sperate function to convert ANSI str to union code string\r
74 //Becuase A2W use stack as internal convert buffer. \r
75 void CGit::StringAppend(CString *str,BYTE *p,int code)\r
76 {\r
77        USES_CONVERSION;\r
78        str->Append(A2W_CP((LPCSTR)p,code));\r
79 \r
80 }       \r
81 BOOL CGit::IsInitRepos()\r
82 {\r
83         CString cmdout;\r
84         cmdout.Empty();\r
85         if(g_Git.Run(_T("git.exe rev-parse --revs-only HEAD"),&cmdout,CP_UTF8))\r
86         {\r
87         //      CMessageBox::Show(NULL,cmdout,_T("TortoiseGit"),MB_OK);\r
88                 return TRUE;\r
89         }\r
90         if(cmdout.IsEmpty())\r
91                 return TRUE;\r
92 \r
93         return FALSE;\r
94 }\r
95 int CGit::Run(CString cmd,BYTE_VECTOR *vector)\r
96 {\r
97         PROCESS_INFORMATION pi;\r
98         HANDLE hRead;\r
99         if(RunAsync(cmd,&pi,&hRead))\r
100                 return GIT_ERROR_CREATE_PROCESS;\r
101 \r
102         DWORD readnumber;\r
103         BYTE data;\r
104         while(ReadFile(hRead,&data,1,&readnumber,NULL))\r
105         {\r
106                 //g_Buffer[readnumber]=0;\r
107                 vector->push_back(data);\r
108 //              StringAppend(output,g_Buffer,codes);\r
109         }\r
110 \r
111         \r
112         CloseHandle(pi.hThread);\r
113 \r
114         WaitForSingleObject(pi.hProcess, INFINITE);\r
115         DWORD exitcode =0;\r
116 \r
117         if(!GetExitCodeProcess(pi.hProcess,&exitcode))\r
118         {\r
119                 return GIT_ERROR_GET_EXIT_CODE;\r
120         }\r
121 \r
122         CloseHandle(pi.hProcess);\r
123 \r
124         CloseHandle(hRead);\r
125         return exitcode;\r
126 \r
127 }\r
128 int CGit::Run(CString cmd, CString* output,int code)\r
129 {\r
130         BYTE_VECTOR vector;\r
131         Run(cmd,&vector);\r
132         StringAppend(output,&(vector[0]),code);\r
133         return 0;\r
134 }\r
135 \r
136 CString CGit::GetUserName(void)\r
137 {\r
138         CString UserName;\r
139         Run(_T("git.exe config user.name"),&UserName,CP_UTF8);\r
140         return UserName;\r
141 }\r
142 CString CGit::GetUserEmail(void)\r
143 {\r
144         CString UserName;\r
145         Run(_T("git.exe config user.email"),&UserName,CP_UTF8);\r
146         return UserName;\r
147 }\r
148 \r
149 CString CGit::GetCurrentBranch(void)\r
150 {\r
151         CString output;\r
152         //Run(_T("git.exe branch"),&branch);\r
153 \r
154         int ret=g_Git.Run(_T("git.exe branch"),&output,CP_UTF8);\r
155         if(!ret)\r
156         {               \r
157                 int pos=0;\r
158                 CString one;\r
159                 while( pos>=0 )\r
160                 {\r
161                         //i++;\r
162                         one=output.Tokenize(_T("\n"),pos);\r
163                         //list.push_back(one.Right(one.GetLength()-2));\r
164                         if(one[0] == _T('*'))\r
165                                 return one.Right(one.GetLength()-2);\r
166                 }\r
167         }\r
168         return CString("");\r
169 }\r
170 \r
171 int CGit::BuildOutputFormat(CString &format,bool IsFull)\r
172 {\r
173         CString log;\r
174         log.Format(_T("#<%c>%%n"),LOG_REV_ITEM_BEGIN);\r
175         format += log;\r
176         if(IsFull)\r
177         {\r
178                 log.Format(_T("#<%c>%%an%%n"),LOG_REV_AUTHOR_NAME);\r
179                 format += log;\r
180                 log.Format(_T("#<%c>%%ae%%n"),LOG_REV_AUTHOR_EMAIL);\r
181                 format += log;\r
182                 log.Format(_T("#<%c>%%ai%%n"),LOG_REV_AUTHOR_DATE);\r
183                 format += log;\r
184                 log.Format(_T("#<%c>%%cn%%n"),LOG_REV_COMMIT_NAME);\r
185                 format += log;\r
186                 log.Format(_T("#<%c>%%ce%%n"),LOG_REV_COMMIT_EMAIL);\r
187                 format += log;\r
188                 log.Format(_T("#<%c>%%ci%%n"),LOG_REV_COMMIT_DATE);\r
189                 format += log;\r
190                 log.Format(_T("#<%c>%%s%%n"),LOG_REV_COMMIT_SUBJECT);\r
191                 format += log;\r
192                 log.Format(_T("#<%c>%%b%%n"),LOG_REV_COMMIT_BODY);\r
193                 format += log;\r
194         }\r
195         log.Format(_T("#<%c>%%m%%H%%n"),LOG_REV_COMMIT_HASH);\r
196         format += log;\r
197         log.Format(_T("#<%c>%%P%%n"),LOG_REV_COMMIT_PARENT);\r
198         format += log;\r
199 \r
200         if(IsFull)\r
201         {\r
202                 log.Format(_T("#<%c>%%n"),LOG_REV_COMMIT_FILE);\r
203                 format += log;\r
204         }\r
205         return 0;\r
206 }\r
207 \r
208 int CGit::GetLog(BYTE_VECTOR& logOut, CString &hash,  CTGitPath *path ,int count,int mask)\r
209 {\r
210 \r
211         CString cmd;\r
212         CString log;\r
213         CString num;\r
214         CString since;\r
215 \r
216         CString file;\r
217 \r
218         if(path)\r
219                 file.Format(_T(" -- \"%s\""),path->GetGitPathString());\r
220         \r
221         if(count>0)\r
222                 num.Format(_T("-n%d"),count);\r
223 \r
224         CString param;\r
225 \r
226         if(mask& LOG_INFO_STAT )\r
227                 param += _T(" --numstat ");\r
228         if(mask& LOG_INFO_FILESTATE)\r
229                 param += _T(" --raw ");\r
230 \r
231         if(mask& LOG_INFO_FULLHISTORY)\r
232                 param += _T(" --full-history ");\r
233 \r
234         if(mask& LOG_INFO_BOUNDARY)\r
235                 param += _T(" --left-right --boundary ");\r
236 \r
237         if(mask& CGit::LOG_INFO_ALL_BRANCH)\r
238                 param += _T(" --all ");\r
239 \r
240         if(mask& CGit::LOG_INFO_DETECT_COPYRENAME)\r
241                 param += _T(" -C ");\r
242         \r
243         if(mask& CGit::LOG_INFO_DETECT_RENAME )\r
244                 param += _T(" -M ");\r
245 \r
246         param+=hash;\r
247 \r
248         cmd.Format(_T("git.exe log %s -z --topo-order --parents %s --pretty=format:\""),\r
249                                 num,param);\r
250 \r
251         BuildOutputFormat(log,!(mask&CGit::LOG_INFO_ONLY_HASH));\r
252 \r
253         cmd += log;\r
254         cmd += CString(_T("\"  "))+hash+file;\r
255 \r
256         return Run(cmd,&logOut);\r
257 }\r
258 \r
259 #if 0\r
260 int CGit::GetShortLog(CString &logOut,CTGitPath * path, int count)\r
261 {\r
262         CString cmd;\r
263         CString log;\r
264         int n;\r
265         if(count<0)\r
266                 n=100;\r
267         else\r
268                 n=count;\r
269         cmd.Format(_T("git.exe log --left-right --boundary --topo-order -n%d --pretty=format:\""),n);\r
270         BuildOutputFormat(log,false);\r
271         cmd += log+_T("\"");\r
272         if (path)\r
273                 cmd+= _T("  -- \"")+path->GetGitPathString()+_T("\"");\r
274         //cmd += CString(_T("\" HEAD~40..HEAD"));\r
275         return Run(cmd,&logOut);\r
276 }\r
277 #endif\r
278 \r
279 #define BUFSIZE 512\r
280 void GetTempPath(CString &path)\r
281 {\r
282         TCHAR lpPathBuffer[BUFSIZE];\r
283         DWORD dwRetVal;\r
284         DWORD dwBufSize=BUFSIZE;\r
285         dwRetVal = GetTempPath(dwBufSize,     // length of the buffer\r
286                            lpPathBuffer); // buffer for path \r
287     if (dwRetVal > dwBufSize || (dwRetVal == 0))\r
288     {\r
289         path=_T("");\r
290     }\r
291         path.Format(_T("%s"),lpPathBuffer);\r
292 }\r
293 CString GetTempFile()\r
294 {\r
295         TCHAR lpPathBuffer[BUFSIZE];\r
296         DWORD dwRetVal;\r
297     DWORD dwBufSize=BUFSIZE;\r
298         TCHAR szTempName[BUFSIZE];  \r
299         UINT uRetVal;\r
300 \r
301         dwRetVal = GetTempPath(dwBufSize,     // length of the buffer\r
302                            lpPathBuffer); // buffer for path \r
303     if (dwRetVal > dwBufSize || (dwRetVal == 0))\r
304     {\r
305         return _T("");\r
306     }\r
307          // Create a temporary file. \r
308     uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files\r
309                               TEXT("Patch"),  // temp file name prefix \r
310                               0,            // create unique name \r
311                               szTempName);  // buffer for name \r
312 \r
313 \r
314     if (uRetVal == 0)\r
315     {\r
316         return _T("");\r
317     }\r
318 \r
319         return CString(szTempName);\r
320 \r
321 }\r
322 \r
323 int CGit::RunLogFile(CString cmd,CString &filename)\r
324 {\r
325         HANDLE hRead, hWrite;\r
326 \r
327         STARTUPINFO si;\r
328         PROCESS_INFORMATION pi;\r
329         si.cb=sizeof(STARTUPINFO);\r
330         GetStartupInfo(&si);\r
331 \r
332         SECURITY_ATTRIBUTES   psa={sizeof(psa),NULL,TRUE};;   \r
333         psa.bInheritHandle=TRUE;   \r
334     \r
335         HANDLE   houtfile=CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ   |   FILE_SHARE_WRITE,   \r
336                         &psa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);   \r
337 \r
338 \r
339         si.wShowWindow=SW_HIDE;\r
340         si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;\r
341         si.hStdOutput   =   houtfile; \r
342         \r
343         if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,NULL,NULL,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))\r
344         {\r
345                 LPVOID lpMsgBuf;\r
346                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,\r
347                         NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
348                         (LPTSTR)&lpMsgBuf,\r
349                         0,NULL);\r
350                 return GIT_ERROR_CREATE_PROCESS;\r
351         }\r
352         \r
353         WaitForSingleObject(pi.hProcess,INFINITE);   \r
354         \r
355         CloseHandle(pi.hThread);\r
356         CloseHandle(pi.hProcess);\r
357         CloseHandle(houtfile);\r
358         return GIT_SUCCESS;\r
359         return 0;\r
360 }\r
361 \r
362 git_revnum_t CGit::GetHash(CString &friendname)\r
363 {\r
364         CString cmd;\r
365         CString out;\r
366         cmd.Format(_T("git.exe rev-parse %s" ),friendname);\r
367         Run(cmd,&out,CP_UTF8);\r
368         int pos=out.ReverseFind(_T('\n'));\r
369         if(pos>0)\r
370                 return out.Left(pos);\r
371         return out;\r
372 }\r
373 \r
374 int CGit::GetTagList(STRING_VECTOR &list)\r
375 {\r
376         int ret;\r
377         CString cmd,output;\r
378         cmd=_T("git.exe tag -l");\r
379         int i=0;\r
380         ret=g_Git.Run(cmd,&output,CP_UTF8);\r
381         if(!ret)\r
382         {               \r
383                 int pos=0;\r
384                 CString one;\r
385                 while( pos>=0 )\r
386                 {\r
387                         i++;\r
388                         one=output.Tokenize(_T("\n"),pos);\r
389                         list.push_back(one);\r
390                 }\r
391         }\r
392         return ret;\r
393 }\r
394 \r
395 int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)\r
396 {\r
397         int ret;\r
398         CString cmd,output;\r
399         cmd=_T("git.exe branch");\r
400 \r
401         if(type==(BRANCH_LOCAL|BRANCH_REMOTE))\r
402                 cmd+=_T(" -a");\r
403         else if(type==BRANCH_REMOTE)\r
404                 cmd+=_T(" -r");\r
405 \r
406         int i=0;\r
407         ret=g_Git.Run(cmd,&output,CP_UTF8);\r
408         if(!ret)\r
409         {               \r
410                 int pos=0;\r
411                 CString one;\r
412                 while( pos>=0 )\r
413                 {\r
414                         i++;\r
415                         one=output.Tokenize(_T("\n"),pos);\r
416                         list.push_back(one.Right(one.GetLength()-2));\r
417                         if(one[0] == _T('*'))\r
418                                 if(current)\r
419                                         *current=i;\r
420                 }\r
421         }\r
422         return ret;\r
423 }\r
424 \r
425 int CGit::GetRemoteList(STRING_VECTOR &list)\r
426 {\r
427         int ret;\r
428         CString cmd,output;\r
429         cmd=_T("git.exe config  --get-regexp remote.*.url");\r
430         ret=g_Git.Run(cmd,&output,CP_UTF8);\r
431         if(!ret)\r
432         {\r
433                 int pos=0;\r
434                 CString one;\r
435                 while( pos>=0 )\r
436                 {\r
437                         one=output.Tokenize(_T("\n"),pos);\r
438                         int start=one.Find(_T("."),0);\r
439                         if(start>0)\r
440                         {\r
441                                 CString url;\r
442                                 url=one.Right(one.GetLength()-start-1);\r
443                                 one=url;\r
444                                 one=one.Left(one.Find(_T("."),0));\r
445                                 list.push_back(one);\r
446                         }\r
447                 }\r
448         }\r
449         return ret;\r
450 }\r
451 \r
452 int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)\r
453 {\r
454         int ret;\r
455         CString cmd,output;\r
456         cmd=_T("git show-ref -d");\r
457         ret=g_Git.Run(cmd,&output,CP_UTF8);\r
458         if(!ret)\r
459         {\r
460                 int pos=0;\r
461                 CString one;\r
462                 while( pos>=0 )\r
463                 {\r
464                         one=output.Tokenize(_T("\n"),pos);\r
465                         int start=one.Find(_T(" "),0);\r
466                         if(start>0)\r
467                         {\r
468                                 CString name;\r
469                                 name=one.Right(one.GetLength()-start-1);\r
470 \r
471                                 CString hash;\r
472                                 hash=one.Left(start);\r
473 \r
474                                 map[hash].push_back(name);\r
475                         }\r
476                 }\r
477         }\r
478         return ret;\r
479 }\r
480 \r
481 BOOL CGit::CheckMsysGitDir()\r
482 {\r
483         CRegString msysdir=CRegString(_T("Software\\TortoiseGit\\MSysGit"),_T(""),FALSE,HKEY_LOCAL_MACHINE);\r
484         CString str=msysdir;\r
485         if(str.IsEmpty())\r
486         {\r
487                 CRegString msysinstalldir=CRegString(_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1\\InstallLocation"),_T(""),FALSE,HKEY_LOCAL_MACHINE);\r
488                 str=msysinstalldir;\r
489                 str+="\\bin";\r
490                 msysdir=str;\r
491                 msysdir.write();\r
492 \r
493         }\r
494         //CGit::m_MsysGitPath=str;\r
495 \r
496         TCHAR *oldpath,*home;\r
497         size_t size;\r
498 \r
499         _tdupenv_s(&home,&size,_T("HOME")); \r
500         \r
501         if(home == NULL)\r
502         {               \r
503                 _tdupenv_s(&home,&size,_T("USERPROFILE")); \r
504                 _tputenv_s(_T("HOME"),home);\r
505                 free(home);\r
506         }\r
507         //set path\r
508         _tdupenv_s(&oldpath,&size,_T("PATH")); \r
509 \r
510         CString path;\r
511         path.Format(_T("%s;"),str);\r
512         path+=oldpath;\r
513 \r
514         _tputenv_s(_T("PATH"),path);\r
515 \r
516         free(oldpath);\r
517 \r
518         CString cmd,out;\r
519         cmd=_T("git.exe --version");\r
520         if(g_Git.Run(cmd,&out,CP_UTF8))\r
521         {\r
522                 return false;\r
523         }\r
524         else\r
525                 return true;\r
526 \r
527 }