OSDN Git Service

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