OSDN Git Service

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