OSDN Git Service

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