OSDN Git Service

Git Blame Can Show Log
[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,char *p)\r
76 {\r
77        USES_CONVERSION;\r
78        str->Append(A2W(p));\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))\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, CString* output)\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         while(ReadFile(hRead,g_Buffer,1023,&readnumber,NULL))\r
104         {\r
105                 g_Buffer[readnumber]=0;\r
106                 StringAppend(output,g_Buffer);\r
107         }\r
108 \r
109         \r
110         CloseHandle(pi.hThread);\r
111 \r
112         WaitForSingleObject(pi.hProcess, INFINITE);\r
113         DWORD exitcode =0;\r
114 \r
115         if(!GetExitCodeProcess(pi.hProcess,&exitcode))\r
116         {\r
117                 return GIT_ERROR_GET_EXIT_CODE;\r
118         }\r
119 \r
120         CloseHandle(pi.hProcess);\r
121 \r
122         CloseHandle(hRead);\r
123         return exitcode;\r
124 }\r
125 \r
126 CString CGit::GetUserName(void)\r
127 {\r
128         CString UserName;\r
129         Run(_T("git.exe config user.name"),&UserName);\r
130         return UserName;\r
131 }\r
132 CString CGit::GetUserEmail(void)\r
133 {\r
134         CString UserName;\r
135         Run(_T("git.exe config user.email"),&UserName);\r
136         return UserName;\r
137 }\r
138 \r
139 CString CGit::GetCurrentBranch(void)\r
140 {\r
141         CString output;\r
142         //Run(_T("git.exe branch"),&branch);\r
143 \r
144         int ret=g_Git.Run(_T("git.exe branch"),&output);\r
145         if(!ret)\r
146         {               \r
147                 int pos=0;\r
148                 CString one;\r
149                 while( pos>=0 )\r
150                 {\r
151                         //i++;\r
152                         one=output.Tokenize(_T("\n"),pos);\r
153                         //list.push_back(one.Right(one.GetLength()-2));\r
154                         if(one[0] == _T('*'))\r
155                                 return one.Right(one.GetLength()-2);\r
156                 }\r
157         }\r
158         return CString("");\r
159 }\r
160 \r
161 int CGit::BuildOutputFormat(CString &format,bool IsFull)\r
162 {\r
163         CString log;\r
164         log.Format(_T("#<%c>%%n"),LOG_REV_ITEM_BEGIN);\r
165         format += log;\r
166         if(IsFull)\r
167         {\r
168                 log.Format(_T("#<%c>%%an%%n"),LOG_REV_AUTHOR_NAME);\r
169                 format += log;\r
170                 log.Format(_T("#<%c>%%ae%%n"),LOG_REV_AUTHOR_EMAIL);\r
171                 format += log;\r
172                 log.Format(_T("#<%c>%%ai%%n"),LOG_REV_AUTHOR_DATE);\r
173                 format += log;\r
174                 log.Format(_T("#<%c>%%cn%%n"),LOG_REV_COMMIT_NAME);\r
175                 format += log;\r
176                 log.Format(_T("#<%c>%%ce%%n"),LOG_REV_COMMIT_EMAIL);\r
177                 format += log;\r
178                 log.Format(_T("#<%c>%%ci%%n"),LOG_REV_COMMIT_DATE);\r
179                 format += log;\r
180                 log.Format(_T("#<%c>%%s%%n"),LOG_REV_COMMIT_SUBJECT);\r
181                 format += log;\r
182                 log.Format(_T("#<%c>%%b%%n"),LOG_REV_COMMIT_BODY);\r
183                 format += log;\r
184         }\r
185         log.Format(_T("#<%c>%%m%%H%%n"),LOG_REV_COMMIT_HASH);\r
186         format += log;\r
187         log.Format(_T("#<%c>%%P%%n"),LOG_REV_COMMIT_PARENT);\r
188         format += log;\r
189 \r
190         if(IsFull)\r
191         {\r
192                 log.Format(_T("#<%c>%%n"),LOG_REV_COMMIT_FILE);\r
193                 format += log;\r
194         }\r
195         return 0;\r
196 }\r
197 \r
198 int CGit::GetLog(CString& logOut, CString &hash,  CTGitPath *path ,int count,int mask)\r
199 {\r
200 \r
201         CString cmd;\r
202         CString log;\r
203         CString num;\r
204         CString since;\r
205 \r
206         CString file;\r
207 \r
208         if(path)\r
209                 file.Format(_T(" -- \"%s\""),path->GetGitPathString());\r
210         \r
211         if(count>0)\r
212                 num.Format(_T("-n%d"),count);\r
213 \r
214         CString param;\r
215 \r
216         if(mask& LOG_INFO_STAT )\r
217                 param += _T(" -numstat ");\r
218         if(mask& LOG_INFO_FILESTATE)\r
219                 param += _T("--raw");\r
220 \r
221         cmd.Format(_T("git.exe log %s -C %s --pretty=format:\""),\r
222                                 num,param);\r
223 \r
224         BuildOutputFormat(log);\r
225         cmd += log;\r
226         cmd += CString(_T("\"  "))+hash+file;\r
227 \r
228         return Run(cmd,&logOut);\r
229 }\r
230 \r
231 \r
232 int CGit::GetShortLog(CString &logOut,CTGitPath * path, int count)\r
233 {\r
234         CString cmd;\r
235         CString log;\r
236         int n;\r
237         if(count<0)\r
238                 n=100;\r
239         else\r
240                 n=count;\r
241         cmd.Format(_T("git.exe log --left-right --boundary --topo-order -n%d --pretty=format:\""),n);\r
242         BuildOutputFormat(log,false);\r
243         cmd += log+_T("\"");\r
244         if (path)\r
245                 cmd+= _T("  -- \"")+path->GetGitPathString()+_T("\"");\r
246         //cmd += CString(_T("\" HEAD~40..HEAD"));\r
247         return Run(cmd,&logOut);\r
248 }\r
249 \r
250 #define BUFSIZE 512\r
251 void GetTempPath(CString &path)\r
252 {\r
253         TCHAR lpPathBuffer[BUFSIZE];\r
254         DWORD dwRetVal;\r
255         DWORD dwBufSize=BUFSIZE;\r
256         dwRetVal = GetTempPath(dwBufSize,     // length of the buffer\r
257                            lpPathBuffer); // buffer for path \r
258     if (dwRetVal > dwBufSize || (dwRetVal == 0))\r
259     {\r
260         path=_T("");\r
261     }\r
262         path.Format(_T("%s"),lpPathBuffer);\r
263 }\r
264 CString GetTempFile()\r
265 {\r
266         TCHAR lpPathBuffer[BUFSIZE];\r
267         DWORD dwRetVal;\r
268     DWORD dwBufSize=BUFSIZE;\r
269         TCHAR szTempName[BUFSIZE];  \r
270         UINT uRetVal;\r
271 \r
272         dwRetVal = GetTempPath(dwBufSize,     // length of the buffer\r
273                            lpPathBuffer); // buffer for path \r
274     if (dwRetVal > dwBufSize || (dwRetVal == 0))\r
275     {\r
276         return _T("");\r
277     }\r
278          // Create a temporary file. \r
279     uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files\r
280                               TEXT("Patch"),  // temp file name prefix \r
281                               0,            // create unique name \r
282                               szTempName);  // buffer for name \r
283 \r
284 \r
285     if (uRetVal == 0)\r
286     {\r
287         return _T("");\r
288     }\r
289 \r
290         return CString(szTempName);\r
291 \r
292 }\r
293 \r
294 int CGit::RunLogFile(CString cmd,CString &filename)\r
295 {\r
296         HANDLE hRead, hWrite;\r
297 \r
298         STARTUPINFO si;\r
299         PROCESS_INFORMATION pi;\r
300         si.cb=sizeof(STARTUPINFO);\r
301         GetStartupInfo(&si);\r
302 \r
303         SECURITY_ATTRIBUTES   psa={sizeof(psa),NULL,TRUE};;   \r
304         psa.bInheritHandle=TRUE;   \r
305     \r
306         HANDLE   houtfile=CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ   |   FILE_SHARE_WRITE,   \r
307                         &psa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);   \r
308 \r
309 \r
310         si.wShowWindow=SW_HIDE;\r
311         si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;\r
312         si.hStdOutput   =   houtfile; \r
313         \r
314         if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,NULL,NULL,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))\r
315         {\r
316                 LPVOID lpMsgBuf;\r
317                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,\r
318                         NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
319                         (LPTSTR)&lpMsgBuf,\r
320                         0,NULL);\r
321                 return GIT_ERROR_CREATE_PROCESS;\r
322         }\r
323         \r
324         WaitForSingleObject(pi.hProcess,INFINITE);   \r
325         \r
326         CloseHandle(pi.hThread);\r
327         CloseHandle(pi.hProcess);\r
328         CloseHandle(houtfile);\r
329         return GIT_SUCCESS;\r
330         return 0;\r
331 }\r
332 \r
333 git_revnum_t CGit::GetHash(CString &friendname)\r
334 {\r
335         CString cmd;\r
336         CString out;\r
337         cmd.Format(_T("git.exe rev-parse %s" ),friendname);\r
338         Run(cmd,&out);\r
339         int pos=out.ReverseFind(_T('\n'));\r
340         if(pos>0)\r
341                 return out.Left(pos);\r
342         return out;\r
343 }\r
344 \r
345 int CGit::GetTagList(STRING_VECTOR &list)\r
346 {\r
347         int ret;\r
348         CString cmd,output;\r
349         cmd=_T("git.exe tag -l");\r
350         int i=0;\r
351         ret=g_Git.Run(cmd,&output);\r
352         if(!ret)\r
353         {               \r
354                 int pos=0;\r
355                 CString one;\r
356                 while( pos>=0 )\r
357                 {\r
358                         i++;\r
359                         one=output.Tokenize(_T("\n"),pos);\r
360                         list.push_back(one);\r
361                 }\r
362         }\r
363         return ret;\r
364 }\r
365 \r
366 int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)\r
367 {\r
368         int ret;\r
369         CString cmd,output;\r
370         cmd=_T("git.exe branch");\r
371 \r
372         if(type==(BRANCH_LOCAL|BRANCH_REMOTE))\r
373                 cmd+=_T(" -a");\r
374         else if(type==BRANCH_REMOTE)\r
375                 cmd+=_T(" -r");\r
376 \r
377         int i=0;\r
378         ret=g_Git.Run(cmd,&output);\r
379         if(!ret)\r
380         {               \r
381                 int pos=0;\r
382                 CString one;\r
383                 while( pos>=0 )\r
384                 {\r
385                         i++;\r
386                         one=output.Tokenize(_T("\n"),pos);\r
387                         list.push_back(one.Right(one.GetLength()-2));\r
388                         if(one[0] == _T('*'))\r
389                                 if(current)\r
390                                         *current=i;\r
391                 }\r
392         }\r
393         return ret;\r
394 }\r
395 \r
396 int CGit::GetRemoteList(STRING_VECTOR &list)\r
397 {\r
398         int ret;\r
399         CString cmd,output;\r
400         cmd=_T("git.exe config  --get-regexp remote.*.url");\r
401         ret=g_Git.Run(cmd,&output);\r
402         if(!ret)\r
403         {\r
404                 int pos=0;\r
405                 CString one;\r
406                 while( pos>=0 )\r
407                 {\r
408                         one=output.Tokenize(_T("\n"),pos);\r
409                         int start=one.Find(_T("."),0);\r
410                         if(start>0)\r
411                         {\r
412                                 CString url;\r
413                                 url=one.Right(one.GetLength()-start-1);\r
414                                 one=url;\r
415                                 one=one.Left(one.Find(_T("."),0));\r
416                                 list.push_back(one);\r
417                         }\r
418                 }\r
419         }\r
420         return ret;\r
421 }\r
422 \r
423 int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)\r
424 {\r
425         int ret;\r
426         CString cmd,output;\r
427         cmd=_T("git show-ref -d");\r
428         ret=g_Git.Run(cmd,&output);\r
429         if(!ret)\r
430         {\r
431                 int pos=0;\r
432                 CString one;\r
433                 while( pos>=0 )\r
434                 {\r
435                         one=output.Tokenize(_T("\n"),pos);\r
436                         int start=one.Find(_T(" "),0);\r
437                         if(start>0)\r
438                         {\r
439                                 CString name;\r
440                                 name=one.Right(one.GetLength()-start-1);\r
441 \r
442                                 CString hash;\r
443                                 hash=one.Left(start);\r
444 \r
445                                 map[hash].push_back(name);\r
446                         }\r
447                 }\r
448         }\r
449         return ret;\r
450 }\r
451 \r
452 BOOL CGit::CheckMsysGitDir()\r
453 {\r
454         CRegString msysdir=CRegString(_T("Software\\TortoiseGit\\MSysGit"),_T(""),FALSE,HKEY_LOCAL_MACHINE);\r
455         CString str=msysdir;\r
456         if(str.IsEmpty())\r
457         {\r
458                 CRegString msysinstalldir=CRegString(_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1\\InstallLocation"),_T(""),FALSE,HKEY_LOCAL_MACHINE);\r
459                 str=msysinstalldir;\r
460                 str+="\\bin";\r
461                 msysdir=str;\r
462                 msysdir.write();\r
463 \r
464         }\r
465         //CGit::m_MsysGitPath=str;\r
466 \r
467         TCHAR *oldpath,*home;\r
468         size_t size;\r
469 \r
470         _tdupenv_s(&home,&size,_T("HOME")); \r
471         \r
472         if(home == NULL)\r
473         {               \r
474                 _tdupenv_s(&home,&size,_T("USERPROFILE")); \r
475                 _tputenv_s(_T("HOME"),home);\r
476                 free(home);\r
477         }\r
478         //set path\r
479         _tdupenv_s(&oldpath,&size,_T("PATH")); \r
480 \r
481         CString path;\r
482         path.Format(_T("%s;"),str);\r
483         path+=oldpath;\r
484 \r
485         _tputenv_s(_T("PATH"),path);\r
486 \r
487         free(oldpath);\r
488 \r
489         CString cmd,out;\r
490         cmd=_T("git.exe --version");\r
491         if(g_Git.Run(cmd,&out))\r
492         {\r
493                 return false;\r
494         }\r
495         else\r
496                 return true;\r
497 \r
498 }