OSDN Git Service

Fix Show Log boundary show more than 1 log item when using --boundary & -n1
[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_CP(p,CP_UTF8));\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         if(mask& LOG_INFO_FULLHISTORY)\r
222                 param += _T(" --full-history ");\r
223 \r
224         if(mask& LOG_INFO_BOUNDARY)\r
225                 param += _T("--left-right --boundary ");\r
226 \r
227         param+=hash;\r
228 \r
229         cmd.Format(_T("git.exe log %s -C --topo-order --parents %s --pretty=format:\""),\r
230                                 num,param);\r
231 \r
232         BuildOutputFormat(log);\r
233         cmd += log;\r
234         cmd += CString(_T("\"  "))+hash+file;\r
235 \r
236         return Run(cmd,&logOut);\r
237 }\r
238 \r
239 \r
240 int CGit::GetShortLog(CString &logOut,CTGitPath * path, int count)\r
241 {\r
242         CString cmd;\r
243         CString log;\r
244         int n;\r
245         if(count<0)\r
246                 n=100;\r
247         else\r
248                 n=count;\r
249         cmd.Format(_T("git.exe log --left-right --boundary --topo-order -n%d --pretty=format:\""),n);\r
250         BuildOutputFormat(log,false);\r
251         cmd += log+_T("\"");\r
252         if (path)\r
253                 cmd+= _T("  -- \"")+path->GetGitPathString()+_T("\"");\r
254         //cmd += CString(_T("\" HEAD~40..HEAD"));\r
255         return Run(cmd,&logOut);\r
256 }\r
257 \r
258 #define BUFSIZE 512\r
259 void GetTempPath(CString &path)\r
260 {\r
261         TCHAR lpPathBuffer[BUFSIZE];\r
262         DWORD dwRetVal;\r
263         DWORD dwBufSize=BUFSIZE;\r
264         dwRetVal = GetTempPath(dwBufSize,     // length of the buffer\r
265                            lpPathBuffer); // buffer for path \r
266     if (dwRetVal > dwBufSize || (dwRetVal == 0))\r
267     {\r
268         path=_T("");\r
269     }\r
270         path.Format(_T("%s"),lpPathBuffer);\r
271 }\r
272 CString GetTempFile()\r
273 {\r
274         TCHAR lpPathBuffer[BUFSIZE];\r
275         DWORD dwRetVal;\r
276     DWORD dwBufSize=BUFSIZE;\r
277         TCHAR szTempName[BUFSIZE];  \r
278         UINT uRetVal;\r
279 \r
280         dwRetVal = GetTempPath(dwBufSize,     // length of the buffer\r
281                            lpPathBuffer); // buffer for path \r
282     if (dwRetVal > dwBufSize || (dwRetVal == 0))\r
283     {\r
284         return _T("");\r
285     }\r
286          // Create a temporary file. \r
287     uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files\r
288                               TEXT("Patch"),  // temp file name prefix \r
289                               0,            // create unique name \r
290                               szTempName);  // buffer for name \r
291 \r
292 \r
293     if (uRetVal == 0)\r
294     {\r
295         return _T("");\r
296     }\r
297 \r
298         return CString(szTempName);\r
299 \r
300 }\r
301 \r
302 int CGit::RunLogFile(CString cmd,CString &filename)\r
303 {\r
304         HANDLE hRead, hWrite;\r
305 \r
306         STARTUPINFO si;\r
307         PROCESS_INFORMATION pi;\r
308         si.cb=sizeof(STARTUPINFO);\r
309         GetStartupInfo(&si);\r
310 \r
311         SECURITY_ATTRIBUTES   psa={sizeof(psa),NULL,TRUE};;   \r
312         psa.bInheritHandle=TRUE;   \r
313     \r
314         HANDLE   houtfile=CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ   |   FILE_SHARE_WRITE,   \r
315                         &psa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);   \r
316 \r
317 \r
318         si.wShowWindow=SW_HIDE;\r
319         si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;\r
320         si.hStdOutput   =   houtfile; \r
321         \r
322         if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,NULL,NULL,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))\r
323         {\r
324                 LPVOID lpMsgBuf;\r
325                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,\r
326                         NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
327                         (LPTSTR)&lpMsgBuf,\r
328                         0,NULL);\r
329                 return GIT_ERROR_CREATE_PROCESS;\r
330         }\r
331         \r
332         WaitForSingleObject(pi.hProcess,INFINITE);   \r
333         \r
334         CloseHandle(pi.hThread);\r
335         CloseHandle(pi.hProcess);\r
336         CloseHandle(houtfile);\r
337         return GIT_SUCCESS;\r
338         return 0;\r
339 }\r
340 \r
341 git_revnum_t CGit::GetHash(CString &friendname)\r
342 {\r
343         CString cmd;\r
344         CString out;\r
345         cmd.Format(_T("git.exe rev-parse %s" ),friendname);\r
346         Run(cmd,&out);\r
347         int pos=out.ReverseFind(_T('\n'));\r
348         if(pos>0)\r
349                 return out.Left(pos);\r
350         return out;\r
351 }\r
352 \r
353 int CGit::GetTagList(STRING_VECTOR &list)\r
354 {\r
355         int ret;\r
356         CString cmd,output;\r
357         cmd=_T("git.exe tag -l");\r
358         int i=0;\r
359         ret=g_Git.Run(cmd,&output);\r
360         if(!ret)\r
361         {               \r
362                 int pos=0;\r
363                 CString one;\r
364                 while( pos>=0 )\r
365                 {\r
366                         i++;\r
367                         one=output.Tokenize(_T("\n"),pos);\r
368                         list.push_back(one);\r
369                 }\r
370         }\r
371         return ret;\r
372 }\r
373 \r
374 int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)\r
375 {\r
376         int ret;\r
377         CString cmd,output;\r
378         cmd=_T("git.exe branch");\r
379 \r
380         if(type==(BRANCH_LOCAL|BRANCH_REMOTE))\r
381                 cmd+=_T(" -a");\r
382         else if(type==BRANCH_REMOTE)\r
383                 cmd+=_T(" -r");\r
384 \r
385         int i=0;\r
386         ret=g_Git.Run(cmd,&output);\r
387         if(!ret)\r
388         {               \r
389                 int pos=0;\r
390                 CString one;\r
391                 while( pos>=0 )\r
392                 {\r
393                         i++;\r
394                         one=output.Tokenize(_T("\n"),pos);\r
395                         list.push_back(one.Right(one.GetLength()-2));\r
396                         if(one[0] == _T('*'))\r
397                                 if(current)\r
398                                         *current=i;\r
399                 }\r
400         }\r
401         return ret;\r
402 }\r
403 \r
404 int CGit::GetRemoteList(STRING_VECTOR &list)\r
405 {\r
406         int ret;\r
407         CString cmd,output;\r
408         cmd=_T("git.exe config  --get-regexp remote.*.url");\r
409         ret=g_Git.Run(cmd,&output);\r
410         if(!ret)\r
411         {\r
412                 int pos=0;\r
413                 CString one;\r
414                 while( pos>=0 )\r
415                 {\r
416                         one=output.Tokenize(_T("\n"),pos);\r
417                         int start=one.Find(_T("."),0);\r
418                         if(start>0)\r
419                         {\r
420                                 CString url;\r
421                                 url=one.Right(one.GetLength()-start-1);\r
422                                 one=url;\r
423                                 one=one.Left(one.Find(_T("."),0));\r
424                                 list.push_back(one);\r
425                         }\r
426                 }\r
427         }\r
428         return ret;\r
429 }\r
430 \r
431 int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)\r
432 {\r
433         int ret;\r
434         CString cmd,output;\r
435         cmd=_T("git show-ref -d");\r
436         ret=g_Git.Run(cmd,&output);\r
437         if(!ret)\r
438         {\r
439                 int pos=0;\r
440                 CString one;\r
441                 while( pos>=0 )\r
442                 {\r
443                         one=output.Tokenize(_T("\n"),pos);\r
444                         int start=one.Find(_T(" "),0);\r
445                         if(start>0)\r
446                         {\r
447                                 CString name;\r
448                                 name=one.Right(one.GetLength()-start-1);\r
449 \r
450                                 CString hash;\r
451                                 hash=one.Left(start);\r
452 \r
453                                 map[hash].push_back(name);\r
454                         }\r
455                 }\r
456         }\r
457         return ret;\r
458 }\r
459 \r
460 BOOL CGit::CheckMsysGitDir()\r
461 {\r
462         CRegString msysdir=CRegString(_T("Software\\TortoiseGit\\MSysGit"),_T(""),FALSE,HKEY_LOCAL_MACHINE);\r
463         CString str=msysdir;\r
464         if(str.IsEmpty())\r
465         {\r
466                 CRegString msysinstalldir=CRegString(_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1\\InstallLocation"),_T(""),FALSE,HKEY_LOCAL_MACHINE);\r
467                 str=msysinstalldir;\r
468                 str+="\\bin";\r
469                 msysdir=str;\r
470                 msysdir.write();\r
471 \r
472         }\r
473         //CGit::m_MsysGitPath=str;\r
474 \r
475         TCHAR *oldpath,*home;\r
476         size_t size;\r
477 \r
478         _tdupenv_s(&home,&size,_T("HOME")); \r
479         \r
480         if(home == NULL)\r
481         {               \r
482                 _tdupenv_s(&home,&size,_T("USERPROFILE")); \r
483                 _tputenv_s(_T("HOME"),home);\r
484                 free(home);\r
485         }\r
486         //set path\r
487         _tdupenv_s(&oldpath,&size,_T("PATH")); \r
488 \r
489         CString path;\r
490         path.Format(_T("%s;"),str);\r
491         path+=oldpath;\r
492 \r
493         _tputenv_s(_T("PATH"),path);\r
494 \r
495         free(oldpath);\r
496 \r
497         CString cmd,out;\r
498         cmd=_T("git.exe --version");\r
499         if(g_Git.Run(cmd,&out))\r
500         {\r
501                 return false;\r
502         }\r
503         else\r
504                 return true;\r
505 \r
506 }