OSDN Git Service

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