5 #include "registry.h"
\r
6 #include "GitConfig.h"
\r
9 static LPTSTR nextpath(LPCTSTR src, LPTSTR dst, UINT maxlen)
\r
13 while (*src == _T(';'))
\r
21 while (*src && *src != _T(';'))
\r
23 if (*src != _T('"'))
\r
35 while (*src && *src != _T('"'))
\r
50 while (*src == _T(';'))
\r
57 return (orgsrc != src) ? (LPTSTR)src : NULL;
\r
60 static inline BOOL FileExists(LPCTSTR lpszFileName)
\r
63 return _tstat(lpszFileName, &st) == 0;
\r
66 static BOOL FindGitPath()
\r
69 _tgetenv_s(&size, NULL, 0, _T("PATH"));
\r
76 TCHAR *env = (TCHAR*)alloca(size * sizeof(TCHAR));
\r
77 _tgetenv_s(&size, env, size, _T("PATH"));
\r
79 TCHAR buf[_MAX_PATH];
\r
81 // search in all paths defined in PATH
\r
82 while ((env = nextpath(env, buf, _MAX_PATH-1)) && *buf)
\r
84 TCHAR *pfin = buf + _tcslen(buf)-1;
\r
86 // ensure trailing slash
\r
87 if (*pfin != _T('/') && *pfin != _T('\\'))
\r
88 _tcscpy(++pfin, _T("\\"));
\r
90 const int len = _tcslen(buf);
\r
92 if ((len + 7) < _MAX_PATH)
\r
93 _tcscpy(pfin+1, _T("git.exe"));
\r
97 if ( FileExists(buf) )
\r
108 #define MAX_DIRBUFFER 1000
\r
109 #define CALL_OUTPUT_READ_CHUNK_SIZE 1024
\r
111 CString CGit::ms_LastMsysGitDir;
\r
113 BOOL g_IsWingitDllload = TRUE;
\r
115 LPBYTE wgGetRevisionID_safe(const char *pszProjectPath, const char *pszName)
\r
117 if(g_IsWingitDllload)
\r
118 return wgGetRevisionID(pszProjectPath,pszName);
\r
123 BOOL wgEnumFiles_safe(const char *pszProjectPath, const char *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)
\r
125 if(g_IsWingitDllload)
\r
126 return wgEnumFiles(pszProjectPath,pszSubPath,nFlags,pEnumCb,pUserData);
\r
131 BOOL CGit::IsVista()
\r
134 if( CRegStdWORD(_T("Software\\TortoiseGit\\CacheType") ) == 0)
\r
136 g_IsWingitDllload=FALSE;
\r
140 OSVERSIONINFO osvi;
\r
141 BOOL bIsWindowsXPorLater;
\r
143 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
\r
144 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
\r
146 GetVersionEx(&osvi);
\r
148 if(osvi.dwMajorVersion >= 6)
\r
154 static void InitWinGitDll()
\r
159 if( CGit::IsVista () )
\r
161 g_IsWingitDllload=FALSE;
\r
171 g_IsWingitDllload=FALSE;
\r
178 GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));
\r
179 m_CurrentDir.ReleaseBuffer();
\r
180 // make sure git/bin is in PATH before wingit.dll gets (delay) loaded by wgInit()
\r
181 if ( !CheckMsysGitDir() )
\r
192 static char g_Buffer[4096];
\r
194 int CGit::RunAsync(CString cmd,PROCESS_INFORMATION *piOut,HANDLE *hReadOut,CString *StdioFile)
\r
196 SECURITY_ATTRIBUTES sa;
\r
197 HANDLE hRead, hWrite;
\r
198 HANDLE hStdioFile = NULL;
\r
200 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
201 sa.lpSecurityDescriptor=NULL;
\r
202 sa.bInheritHandle=TRUE;
\r
203 if(!CreatePipe(&hRead,&hWrite,&sa,0))
\r
205 return GIT_ERROR_OPEN_PIP;
\r
210 hStdioFile=CreateFile(*StdioFile,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
\r
211 &sa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
\r
215 PROCESS_INFORMATION pi;
\r
216 si.cb=sizeof(STARTUPINFO);
\r
217 GetStartupInfo(&si);
\r
219 si.hStdError=hWrite;
\r
221 si.hStdOutput=hStdioFile;
\r
223 si.hStdOutput=hWrite;
\r
225 si.wShowWindow=SW_HIDE;
\r
226 si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
\r
228 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,NULL,NULL,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
\r
231 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
\r
232 NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
\r
235 return GIT_ERROR_CREATE_PROCESS;
\r
238 CloseHandle(hWrite);
\r
247 //Must use sperate function to convert ANSI str to union code string
\r
248 //Becuase A2W use stack as internal convert buffer.
\r
249 void CGit::StringAppend(CString *str,BYTE *p,int code,int length)
\r
252 //str->Append(A2W_CP((LPCSTR)p,code));
\r
257 len= strlen((const char*)p);
\r
262 //buf = new WCHAR[len*4 + 1];
\r
263 buf = str->GetBuffer(len*4+1+str->GetLength())+str->GetLength();
\r
264 SecureZeroMemory(buf, (len*4 + 1)*sizeof(WCHAR));
\r
265 MultiByteToWideChar(code, 0, (LPCSTR)p, len, buf, len*4);
\r
266 str->ReleaseBuffer();
\r
267 //str->Append(buf);
\r
270 BOOL CGit::IsInitRepos()
\r
274 if(g_Git.Run(_T("git.exe rev-parse --revs-only HEAD"),&cmdout,CP_UTF8))
\r
276 // CMessageBox::Show(NULL,cmdout,_T("TortoiseGit"),MB_OK);
\r
279 if(cmdout.IsEmpty())
\r
284 int CGit::Run(CGitCall* pcall)
\r
286 PROCESS_INFORMATION pi;
\r
288 if(RunAsync(pcall->GetCmd(),&pi,&hRead))
\r
289 return GIT_ERROR_CREATE_PROCESS;
\r
292 BYTE data[CALL_OUTPUT_READ_CHUNK_SIZE];
\r
293 while(ReadFile(hRead,data,CALL_OUTPUT_READ_CHUNK_SIZE,&readnumber,NULL))
\r
295 pcall->OnOutputData(data,readnumber);
\r
299 CloseHandle(pi.hThread);
\r
301 WaitForSingleObject(pi.hProcess, INFINITE);
\r
304 if(!GetExitCodeProcess(pi.hProcess,&exitcode))
\r
306 return GIT_ERROR_GET_EXIT_CODE;
\r
309 CloseHandle(pi.hProcess);
\r
311 CloseHandle(hRead);
\r
314 class CGitCall_ByteVector : public CGitCall
\r
317 CGitCall_ByteVector(CString cmd,BYTE_VECTOR* pvector):CGitCall(cmd),m_pvector(pvector){}
\r
318 virtual bool OnOutputData(const BYTE* data, size_t size)
\r
320 size_t oldsize=m_pvector->size();
\r
321 m_pvector->resize(m_pvector->size()+size);
\r
322 memcpy(&*(m_pvector->begin()+oldsize),data,size);
\r
325 BYTE_VECTOR* m_pvector;
\r
328 int CGit::Run(CString cmd,BYTE_VECTOR *vector)
\r
330 CGitCall_ByteVector call(cmd,vector);
\r
333 int CGit::Run(CString cmd, CString* output,int code)
\r
335 BYTE_VECTOR vector;
\r
337 ret=Run(cmd,&vector);
\r
339 vector.push_back(0);
\r
341 StringAppend(output,&(vector[0]),code);
\r
345 CString CGit::GetUserName(void)
\r
348 Run(_T("git.exe config user.name"),&UserName,CP_UTF8);
\r
351 CString CGit::GetUserEmail(void)
\r
354 Run(_T("git.exe config user.email"),&UserName,CP_UTF8);
\r
358 CString CGit::GetCurrentBranch(void)
\r
361 //Run(_T("git.exe branch"),&branch);
\r
363 int ret=g_Git.Run(_T("git.exe branch"),&output,CP_UTF8);
\r
371 one=output.Tokenize(_T("\n"),pos);
\r
372 //list.push_back(one.Right(one.GetLength()-2));
\r
373 if(one[0] == _T('*'))
\r
374 return one.Right(one.GetLength()-2);
\r
377 return CString("");
\r
380 int CGit::BuildOutputFormat(CString &format,bool IsFull)
\r
383 log.Format(_T("#<%c>%%x00"),LOG_REV_ITEM_BEGIN);
\r
387 log.Format(_T("#<%c>%%an%%x00"),LOG_REV_AUTHOR_NAME);
\r
389 log.Format(_T("#<%c>%%ae%%x00"),LOG_REV_AUTHOR_EMAIL);
\r
391 log.Format(_T("#<%c>%%ai%%x00"),LOG_REV_AUTHOR_DATE);
\r
393 log.Format(_T("#<%c>%%cn%%x00"),LOG_REV_COMMIT_NAME);
\r
395 log.Format(_T("#<%c>%%ce%%x00"),LOG_REV_COMMIT_EMAIL);
\r
397 log.Format(_T("#<%c>%%ci%%x00"),LOG_REV_COMMIT_DATE);
\r
399 log.Format(_T("#<%c>%%s%%x00"),LOG_REV_COMMIT_SUBJECT);
\r
401 log.Format(_T("#<%c>%%b%%x00"),LOG_REV_COMMIT_BODY);
\r
404 log.Format(_T("#<%c>%%m%%H%%x00"),LOG_REV_COMMIT_HASH);
\r
406 log.Format(_T("#<%c>%%P%%x00"),LOG_REV_COMMIT_PARENT);
\r
411 log.Format(_T("#<%c>%%x00"),LOG_REV_COMMIT_FILE);
\r
417 int CGit::GetLog(BYTE_VECTOR& logOut, CString &hash, CTGitPath *path ,int count,int mask)
\r
419 CGitCall_ByteVector gitCall(CString(),&logOut);
\r
420 return GetLog(&gitCall,hash,path,count,mask);
\r
423 //int CGit::GetLog(CGitCall* pgitCall, CString &hash, CTGitPath *path ,int count,int mask)
\r
424 int CGit::GetLog(CGitCall* pgitCall, CString &hash, CTGitPath *path, int count, int mask)
\r
435 file.Format(_T(" -- \"%s\""),path->GetGitPathString());
\r
438 num.Format(_T("-n%d"),count);
\r
442 if(mask& LOG_INFO_STAT )
\r
443 param += _T(" --numstat ");
\r
444 if(mask& LOG_INFO_FILESTATE)
\r
445 param += _T(" --raw ");
\r
447 if(mask& LOG_INFO_FULLHISTORY)
\r
448 param += _T(" --full-history ");
\r
450 if(mask& LOG_INFO_BOUNDARY)
\r
451 param += _T(" --left-right --boundary ");
\r
453 if(mask& CGit::LOG_INFO_ALL_BRANCH)
\r
454 param += _T(" --all ");
\r
456 if(mask& CGit::LOG_INFO_DETECT_COPYRENAME)
\r
457 param += _T(" -C ");
\r
459 if(mask& CGit::LOG_INFO_DETECT_RENAME )
\r
460 param += _T(" -M ");
\r
462 if(mask& CGit::LOG_INFO_FIRST_PARENT )
\r
463 param += _T(" --first-parent ");
\r
465 if(mask& CGit::LOG_INFO_NO_MERGE )
\r
466 param += _T(" --no-merges ");
\r
468 if(mask& CGit::LOG_INFO_FOLLOW)
\r
469 param += _T(" --follow ");
\r
473 cmd.Format(_T("git.exe log %s -z --topo-order %s --parents --pretty=format:\""),
\r
476 BuildOutputFormat(log,!(mask&CGit::LOG_INFO_ONLY_HASH));
\r
479 cmd += CString(_T("\" "))+hash+file;
\r
481 pgitCall->SetCmd(cmd);
\r
483 return Run(pgitCall);
\r
484 // return Run(cmd,&logOut);
\r
488 int CGit::GetShortLog(CString &logOut,CTGitPath * path, int count)
\r
497 cmd.Format(_T("git.exe log --left-right --boundary --topo-order -n%d --pretty=format:\""),n);
\r
498 BuildOutputFormat(log,false);
\r
499 cmd += log+_T("\"");
\r
501 cmd+= _T(" -- \"")+path->GetGitPathString()+_T("\"");
\r
502 //cmd += CString(_T("\" HEAD~40..HEAD"));
\r
503 return Run(cmd,&logOut);
\r
507 #define BUFSIZE 512
\r
508 void GetTempPath(CString &path)
\r
510 TCHAR lpPathBuffer[BUFSIZE];
\r
512 DWORD dwBufSize=BUFSIZE;
\r
513 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
\r
514 lpPathBuffer); // buffer for path
\r
515 if (dwRetVal > dwBufSize || (dwRetVal == 0))
\r
519 path.Format(_T("%s"),lpPathBuffer);
\r
521 CString GetTempFile()
\r
523 TCHAR lpPathBuffer[BUFSIZE];
\r
525 DWORD dwBufSize=BUFSIZE;
\r
526 TCHAR szTempName[BUFSIZE];
\r
529 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
\r
530 lpPathBuffer); // buffer for path
\r
531 if (dwRetVal > dwBufSize || (dwRetVal == 0))
\r
535 // Create a temporary file.
\r
536 uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files
\r
537 TEXT("Patch"), // temp file name prefix
\r
538 0, // create unique name
\r
539 szTempName); // buffer for name
\r
547 return CString(szTempName);
\r
551 int CGit::RunLogFile(CString cmd,CString &filename)
\r
554 PROCESS_INFORMATION pi;
\r
555 si.cb=sizeof(STARTUPINFO);
\r
556 GetStartupInfo(&si);
\r
558 SECURITY_ATTRIBUTES psa={sizeof(psa),NULL,TRUE};;
\r
559 psa.bInheritHandle=TRUE;
\r
561 HANDLE houtfile=CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
\r
562 &psa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
\r
565 si.wShowWindow=SW_HIDE;
\r
566 si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
\r
567 si.hStdOutput = houtfile;
\r
569 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,NULL,NULL,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
\r
572 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
\r
573 NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
\r
576 return GIT_ERROR_CREATE_PROCESS;
\r
579 WaitForSingleObject(pi.hProcess,INFINITE);
\r
581 CloseHandle(pi.hThread);
\r
582 CloseHandle(pi.hProcess);
\r
583 CloseHandle(houtfile);
\r
584 return GIT_SUCCESS;
\r
588 git_revnum_t CGit::GetHash(CString &friendname)
\r
590 // NOTE: could replace this with wgGetRevisionID call
\r
594 cmd.Format(_T("git.exe rev-parse %s" ),friendname);
\r
595 Run(cmd,&out,CP_UTF8);
\r
596 int pos=out.ReverseFind(_T('\n'));
\r
598 return out.Left(pos);
\r
602 int CGit::GetTagList(STRING_VECTOR &list)
\r
605 CString cmd,output;
\r
606 cmd=_T("git.exe tag -l");
\r
608 ret=g_Git.Run(cmd,&output,CP_UTF8);
\r
616 one=output.Tokenize(_T("\n"),pos);
\r
617 list.push_back(one);
\r
623 int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)
\r
626 CString cmd,output;
\r
627 cmd=_T("git.exe branch");
\r
629 if(type==(BRANCH_LOCAL|BRANCH_REMOTE))
\r
631 else if(type==BRANCH_REMOTE)
\r
635 ret=g_Git.Run(cmd,&output,CP_UTF8);
\r
642 one=output.Tokenize(_T("\n"),pos);
\r
643 list.push_back(one.Right(one.GetLength()-2));
\r
644 if(one[0] == _T('*'))
\r
653 int CGit::GetRemoteList(STRING_VECTOR &list)
\r
656 CString cmd,output;
\r
657 cmd=_T("git.exe config --get-regexp remote.*.url");
\r
658 ret=g_Git.Run(cmd,&output,CP_UTF8);
\r
665 one=output.Tokenize(_T("\n"),pos);
\r
666 int start=one.Find(_T("."),0);
\r
670 url=one.Right(one.GetLength()-start-1);
\r
672 one=one.Left(one.Find(_T("."),0));
\r
673 list.push_back(one);
\r
680 int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)
\r
683 CString cmd,output;
\r
684 cmd=_T("git show-ref -d");
\r
685 ret=g_Git.Run(cmd,&output,CP_UTF8);
\r
692 one=output.Tokenize(_T("\n"),pos);
\r
693 int start=one.Find(_T(" "),0);
\r
697 name=one.Right(one.GetLength()-start-1);
\r
700 hash=one.Left(start);
\r
702 map[hash].push_back(name);
\r
709 BOOL CGit::CheckMsysGitDir()
\r
711 static BOOL bInitialized = FALSE;
\r
718 TCHAR *oldpath,*home;
\r
721 // set HOME if not set already
\r
722 _tgetenv_s(&size, NULL, 0, _T("HOME"));
\r
725 _tdupenv_s(&home,&size,_T("USERPROFILE"));
\r
726 _tputenv_s(_T("HOME"),home);
\r
731 CRegString sshclient=CRegString(_T("Software\\TortoiseGit\\SSH"));
\r
732 CString ssh=sshclient;
\r
736 _tputenv_s(_T("GIT_SSH"),ssh);
\r
739 _tputenv_s(_T("GIT_SSH"),_T(""));
\r
742 // search PATH if git/bin directory is alredy present
\r
743 if ( FindGitPath() )
\r
745 bInitialized = TRUE;
\r
749 // add git/bin path to PATH
\r
751 CRegString msysdir=CRegString(REG_MSYSGIT_PATH,_T(""),FALSE,HKEY_LOCAL_MACHINE);
\r
752 CString str=msysdir;
\r
755 CRegString msysinstalldir=CRegString(REG_MSYSGIT_INSTALL,_T(""),FALSE,HKEY_LOCAL_MACHINE);
\r
756 str=msysinstalldir;
\r
757 if ( !str.IsEmpty() )
\r
759 str += (str[str.GetLength()-1] != '\\') ? "\\bin" : "bin";
\r
768 //CGit::m_MsysGitPath=str;
\r
772 _tdupenv_s(&oldpath,&size,_T("PATH"));
\r
775 path.Format(_T("%s;%s"),oldpath,str);
\r
777 _tputenv_s(_T("PATH"),path);
\r
782 if( !FindGitPath() )
\r
788 bInitialized = TRUE;
\r