Fix Show Log boundary show more than 1 log item when using --boundary & -n1
[TortoiseGit.git] / src / Git / Git.cpp
blob89e4fd29042204b10d555eb93a4a44199f647fea
1 #include "StdAfx.h"
2 #include "Git.h"
3 #include "atlconv.h"
4 #include "GitRev.h"
5 #include "registry.h"
7 #define MAX_DIRBUFFER 1000
8 CGit g_Git;
9 CGit::CGit(void)
11 GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));
14 CGit::~CGit(void)
18 static char g_Buffer[4096];
20 int CGit::RunAsync(CString cmd,PROCESS_INFORMATION *piOut,HANDLE *hReadOut,CString *StdioFile)
22 SECURITY_ATTRIBUTES sa;
23 HANDLE hRead, hWrite;
24 HANDLE hStdioFile;
26 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
27 sa.lpSecurityDescriptor=NULL;
28 sa.bInheritHandle=TRUE;
29 if(!CreatePipe(&hRead,&hWrite,&sa,0))
31 return GIT_ERROR_OPEN_PIP;
34 if(StdioFile)
36 hStdioFile=CreateFile(*StdioFile,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
37 &sa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
40 STARTUPINFO si;
41 PROCESS_INFORMATION pi;
42 si.cb=sizeof(STARTUPINFO);
43 GetStartupInfo(&si);
45 si.hStdError=hWrite;
46 if(StdioFile)
47 si.hStdOutput=hStdioFile;
48 else
49 si.hStdOutput=hWrite;
51 si.wShowWindow=SW_HIDE;
52 si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
54 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,NULL,NULL,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
56 LPVOID lpMsgBuf;
57 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
58 NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
59 (LPTSTR)&lpMsgBuf,
60 0,NULL);
61 return GIT_ERROR_CREATE_PROCESS;
64 CloseHandle(hWrite);
65 if(piOut)
66 *piOut=pi;
67 if(hReadOut)
68 *hReadOut=hRead;
70 return 0;
73 //Must use sperate function to convert ANSI str to union code string
74 //Becuase A2W use stack as internal convert buffer.
75 void CGit::StringAppend(CString *str,char *p)
77 USES_CONVERSION;
78 str->Append(A2W_CP(p,CP_UTF8));
81 BOOL CGit::IsInitRepos()
83 CString cmdout;
84 cmdout.Empty();
85 if(g_Git.Run(_T("git.exe rev-parse --revs-only HEAD"),&cmdout))
87 // CMessageBox::Show(NULL,cmdout,_T("TortoiseGit"),MB_OK);
88 return TRUE;
90 if(cmdout.IsEmpty())
91 return TRUE;
93 return FALSE;
95 int CGit::Run(CString cmd, CString* output)
97 PROCESS_INFORMATION pi;
98 HANDLE hRead;
99 if(RunAsync(cmd,&pi,&hRead))
100 return GIT_ERROR_CREATE_PROCESS;
102 DWORD readnumber;
103 while(ReadFile(hRead,g_Buffer,1023,&readnumber,NULL))
105 g_Buffer[readnumber]=0;
106 StringAppend(output,g_Buffer);
110 CloseHandle(pi.hThread);
112 WaitForSingleObject(pi.hProcess, INFINITE);
113 DWORD exitcode =0;
115 if(!GetExitCodeProcess(pi.hProcess,&exitcode))
117 return GIT_ERROR_GET_EXIT_CODE;
120 CloseHandle(pi.hProcess);
122 CloseHandle(hRead);
123 return exitcode;
126 CString CGit::GetUserName(void)
128 CString UserName;
129 Run(_T("git.exe config user.name"),&UserName);
130 return UserName;
132 CString CGit::GetUserEmail(void)
134 CString UserName;
135 Run(_T("git.exe config user.email"),&UserName);
136 return UserName;
139 CString CGit::GetCurrentBranch(void)
141 CString output;
142 //Run(_T("git.exe branch"),&branch);
144 int ret=g_Git.Run(_T("git.exe branch"),&output);
145 if(!ret)
147 int pos=0;
148 CString one;
149 while( pos>=0 )
151 //i++;
152 one=output.Tokenize(_T("\n"),pos);
153 //list.push_back(one.Right(one.GetLength()-2));
154 if(one[0] == _T('*'))
155 return one.Right(one.GetLength()-2);
158 return CString("");
161 int CGit::BuildOutputFormat(CString &format,bool IsFull)
163 CString log;
164 log.Format(_T("#<%c>%%n"),LOG_REV_ITEM_BEGIN);
165 format += log;
166 if(IsFull)
168 log.Format(_T("#<%c>%%an%%n"),LOG_REV_AUTHOR_NAME);
169 format += log;
170 log.Format(_T("#<%c>%%ae%%n"),LOG_REV_AUTHOR_EMAIL);
171 format += log;
172 log.Format(_T("#<%c>%%ai%%n"),LOG_REV_AUTHOR_DATE);
173 format += log;
174 log.Format(_T("#<%c>%%cn%%n"),LOG_REV_COMMIT_NAME);
175 format += log;
176 log.Format(_T("#<%c>%%ce%%n"),LOG_REV_COMMIT_EMAIL);
177 format += log;
178 log.Format(_T("#<%c>%%ci%%n"),LOG_REV_COMMIT_DATE);
179 format += log;
180 log.Format(_T("#<%c>%%s%%n"),LOG_REV_COMMIT_SUBJECT);
181 format += log;
182 log.Format(_T("#<%c>%%b%%n"),LOG_REV_COMMIT_BODY);
183 format += log;
185 log.Format(_T("#<%c>%%m%%H%%n"),LOG_REV_COMMIT_HASH);
186 format += log;
187 log.Format(_T("#<%c>%%P%%n"),LOG_REV_COMMIT_PARENT);
188 format += log;
190 if(IsFull)
192 log.Format(_T("#<%c>%%n"),LOG_REV_COMMIT_FILE);
193 format += log;
195 return 0;
198 int CGit::GetLog(CString& logOut, CString &hash, CTGitPath *path ,int count,int mask)
201 CString cmd;
202 CString log;
203 CString num;
204 CString since;
206 CString file;
208 if(path)
209 file.Format(_T(" -- \"%s\""),path->GetGitPathString());
211 if(count>0)
212 num.Format(_T("-n%d"),count);
214 CString param;
216 if(mask& LOG_INFO_STAT )
217 param += _T(" --numstat ");
218 if(mask& LOG_INFO_FILESTATE)
219 param += _T(" --raw ");
221 if(mask& LOG_INFO_FULLHISTORY)
222 param += _T(" --full-history ");
224 if(mask& LOG_INFO_BOUNDARY)
225 param += _T("--left-right --boundary ");
227 param+=hash;
229 cmd.Format(_T("git.exe log %s -C --topo-order --parents %s --pretty=format:\""),
230 num,param);
232 BuildOutputFormat(log);
233 cmd += log;
234 cmd += CString(_T("\" "))+hash+file;
236 return Run(cmd,&logOut);
240 int CGit::GetShortLog(CString &logOut,CTGitPath * path, int count)
242 CString cmd;
243 CString log;
244 int n;
245 if(count<0)
246 n=100;
247 else
248 n=count;
249 cmd.Format(_T("git.exe log --left-right --boundary --topo-order -n%d --pretty=format:\""),n);
250 BuildOutputFormat(log,false);
251 cmd += log+_T("\"");
252 if (path)
253 cmd+= _T(" -- \"")+path->GetGitPathString()+_T("\"");
254 //cmd += CString(_T("\" HEAD~40..HEAD"));
255 return Run(cmd,&logOut);
258 #define BUFSIZE 512
259 void GetTempPath(CString &path)
261 TCHAR lpPathBuffer[BUFSIZE];
262 DWORD dwRetVal;
263 DWORD dwBufSize=BUFSIZE;
264 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
265 lpPathBuffer); // buffer for path
266 if (dwRetVal > dwBufSize || (dwRetVal == 0))
268 path=_T("");
270 path.Format(_T("%s"),lpPathBuffer);
272 CString GetTempFile()
274 TCHAR lpPathBuffer[BUFSIZE];
275 DWORD dwRetVal;
276 DWORD dwBufSize=BUFSIZE;
277 TCHAR szTempName[BUFSIZE];
278 UINT uRetVal;
280 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
281 lpPathBuffer); // buffer for path
282 if (dwRetVal > dwBufSize || (dwRetVal == 0))
284 return _T("");
286 // Create a temporary file.
287 uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files
288 TEXT("Patch"), // temp file name prefix
289 0, // create unique name
290 szTempName); // buffer for name
293 if (uRetVal == 0)
295 return _T("");
298 return CString(szTempName);
302 int CGit::RunLogFile(CString cmd,CString &filename)
304 HANDLE hRead, hWrite;
306 STARTUPINFO si;
307 PROCESS_INFORMATION pi;
308 si.cb=sizeof(STARTUPINFO);
309 GetStartupInfo(&si);
311 SECURITY_ATTRIBUTES psa={sizeof(psa),NULL,TRUE};;
312 psa.bInheritHandle=TRUE;
314 HANDLE houtfile=CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
315 &psa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
318 si.wShowWindow=SW_HIDE;
319 si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
320 si.hStdOutput = houtfile;
322 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,NULL,NULL,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
324 LPVOID lpMsgBuf;
325 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
326 NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
327 (LPTSTR)&lpMsgBuf,
328 0,NULL);
329 return GIT_ERROR_CREATE_PROCESS;
332 WaitForSingleObject(pi.hProcess,INFINITE);
334 CloseHandle(pi.hThread);
335 CloseHandle(pi.hProcess);
336 CloseHandle(houtfile);
337 return GIT_SUCCESS;
338 return 0;
341 git_revnum_t CGit::GetHash(CString &friendname)
343 CString cmd;
344 CString out;
345 cmd.Format(_T("git.exe rev-parse %s" ),friendname);
346 Run(cmd,&out);
347 int pos=out.ReverseFind(_T('\n'));
348 if(pos>0)
349 return out.Left(pos);
350 return out;
353 int CGit::GetTagList(STRING_VECTOR &list)
355 int ret;
356 CString cmd,output;
357 cmd=_T("git.exe tag -l");
358 int i=0;
359 ret=g_Git.Run(cmd,&output);
360 if(!ret)
362 int pos=0;
363 CString one;
364 while( pos>=0 )
366 i++;
367 one=output.Tokenize(_T("\n"),pos);
368 list.push_back(one);
371 return ret;
374 int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)
376 int ret;
377 CString cmd,output;
378 cmd=_T("git.exe branch");
380 if(type==(BRANCH_LOCAL|BRANCH_REMOTE))
381 cmd+=_T(" -a");
382 else if(type==BRANCH_REMOTE)
383 cmd+=_T(" -r");
385 int i=0;
386 ret=g_Git.Run(cmd,&output);
387 if(!ret)
389 int pos=0;
390 CString one;
391 while( pos>=0 )
393 i++;
394 one=output.Tokenize(_T("\n"),pos);
395 list.push_back(one.Right(one.GetLength()-2));
396 if(one[0] == _T('*'))
397 if(current)
398 *current=i;
401 return ret;
404 int CGit::GetRemoteList(STRING_VECTOR &list)
406 int ret;
407 CString cmd,output;
408 cmd=_T("git.exe config --get-regexp remote.*.url");
409 ret=g_Git.Run(cmd,&output);
410 if(!ret)
412 int pos=0;
413 CString one;
414 while( pos>=0 )
416 one=output.Tokenize(_T("\n"),pos);
417 int start=one.Find(_T("."),0);
418 if(start>0)
420 CString url;
421 url=one.Right(one.GetLength()-start-1);
422 one=url;
423 one=one.Left(one.Find(_T("."),0));
424 list.push_back(one);
428 return ret;
431 int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)
433 int ret;
434 CString cmd,output;
435 cmd=_T("git show-ref -d");
436 ret=g_Git.Run(cmd,&output);
437 if(!ret)
439 int pos=0;
440 CString one;
441 while( pos>=0 )
443 one=output.Tokenize(_T("\n"),pos);
444 int start=one.Find(_T(" "),0);
445 if(start>0)
447 CString name;
448 name=one.Right(one.GetLength()-start-1);
450 CString hash;
451 hash=one.Left(start);
453 map[hash].push_back(name);
457 return ret;
460 BOOL CGit::CheckMsysGitDir()
462 CRegString msysdir=CRegString(_T("Software\\TortoiseGit\\MSysGit"),_T(""),FALSE,HKEY_LOCAL_MACHINE);
463 CString str=msysdir;
464 if(str.IsEmpty())
466 CRegString msysinstalldir=CRegString(_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1\\InstallLocation"),_T(""),FALSE,HKEY_LOCAL_MACHINE);
467 str=msysinstalldir;
468 str+="\\bin";
469 msysdir=str;
470 msysdir.write();
473 //CGit::m_MsysGitPath=str;
475 TCHAR *oldpath,*home;
476 size_t size;
478 _tdupenv_s(&home,&size,_T("HOME"));
480 if(home == NULL)
482 _tdupenv_s(&home,&size,_T("USERPROFILE"));
483 _tputenv_s(_T("HOME"),home);
484 free(home);
486 //set path
487 _tdupenv_s(&oldpath,&size,_T("PATH"));
489 CString path;
490 path.Format(_T("%s;"),str);
491 path+=oldpath;
493 _tputenv_s(_T("PATH"),path);
495 free(oldpath);
497 CString cmd,out;
498 cmd=_T("git.exe --version");
499 if(g_Git.Run(cmd,&out))
501 return false;
503 else
504 return true;