5 #include "registry.h"
\r
6 #include "GitConfig.h"
\r
8 #include "UnicodeUtils.h"
\r
10 int CGit::m_LogEncode=CP_UTF8;
\r
12 static LPTSTR nextpath(LPCTSTR src, LPTSTR dst, UINT maxlen)
\r
16 while (*src == _T(';'))
\r
24 while (*src && *src != _T(';'))
\r
26 if (*src != _T('"'))
\r
38 while (*src && *src != _T('"'))
\r
53 while (*src == _T(';'))
\r
60 return (orgsrc != src) ? (LPTSTR)src : NULL;
\r
63 static inline BOOL FileExists(LPCTSTR lpszFileName)
\r
66 return _tstat(lpszFileName, &st) == 0;
\r
69 static BOOL FindGitPath()
\r
72 _tgetenv_s(&size, NULL, 0, _T("PATH"));
\r
79 TCHAR *env = (TCHAR*)alloca(size * sizeof(TCHAR));
\r
80 _tgetenv_s(&size, env, size, _T("PATH"));
\r
82 TCHAR buf[_MAX_PATH];
\r
84 // search in all paths defined in PATH
\r
85 while ((env = nextpath(env, buf, _MAX_PATH-1)) && *buf)
\r
87 TCHAR *pfin = buf + _tcslen(buf)-1;
\r
89 // ensure trailing slash
\r
90 if (*pfin != _T('/') && *pfin != _T('\\'))
\r
91 _tcscpy(++pfin, _T("\\"));
\r
93 const int len = _tcslen(buf);
\r
95 if ((len + 7) < _MAX_PATH)
\r
96 _tcscpy(pfin+1, _T("git.exe"));
\r
100 if ( FileExists(buf) )
\r
104 CGit::ms_LastMsysGitDir = buf;
\r
113 #define MAX_DIRBUFFER 1000
\r
114 #define CALL_OUTPUT_READ_CHUNK_SIZE 1024
\r
116 CString CGit::ms_LastMsysGitDir;
\r
119 // contains system environment that should be used by child processes (RunAsync)
\r
120 // initialized by CheckMsysGitDir
\r
121 static LPTSTR l_processEnv = NULL;
\r
127 GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));
\r
128 m_CurrentDir.ReleaseBuffer();
\r
137 static char g_Buffer[4096];
\r
139 int CGit::RunAsync(CString cmd,PROCESS_INFORMATION *piOut,HANDLE *hReadOut,CString *StdioFile)
\r
141 SECURITY_ATTRIBUTES sa;
\r
142 HANDLE hRead, hWrite;
\r
143 HANDLE hStdioFile = NULL;
\r
145 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
146 sa.lpSecurityDescriptor=NULL;
\r
147 sa.bInheritHandle=TRUE;
\r
148 if(!CreatePipe(&hRead,&hWrite,&sa,0))
\r
150 return GIT_ERROR_OPEN_PIP;
\r
155 hStdioFile=CreateFile(*StdioFile,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
\r
156 &sa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
\r
160 PROCESS_INFORMATION pi;
\r
161 si.cb=sizeof(STARTUPINFO);
\r
162 GetStartupInfo(&si);
\r
164 si.hStdError=hWrite;
\r
166 si.hStdOutput=hStdioFile;
\r
168 si.hStdOutput=hWrite;
\r
170 si.wShowWindow=SW_HIDE;
\r
171 si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
\r
173 LPTSTR pEnv = l_processEnv;
\r
174 DWORD dwFlags = pEnv ? CREATE_UNICODE_ENVIRONMENT : 0;
\r
176 //DETACHED_PROCESS make ssh recognize that it has no console to launch askpass to input password.
\r
177 dwFlags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
\r
179 memset(&this->m_CurrentGitPi,0,sizeof(PROCESS_INFORMATION));
\r
181 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,dwFlags,pEnv,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
\r
184 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
\r
185 NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
\r
188 return GIT_ERROR_CREATE_PROCESS;
\r
191 m_CurrentGitPi = pi;
\r
193 CloseHandle(hWrite);
\r
202 //Must use sperate function to convert ANSI str to union code string
\r
203 //Becuase A2W use stack as internal convert buffer.
\r
204 void CGit::StringAppend(CString *str,BYTE *p,int code,int length)
\r
207 //str->Append(A2W_CP((LPCSTR)p,code));
\r
215 len= strlen((const char*)p);
\r
220 //buf = new WCHAR[len*4 + 1];
\r
221 buf = str->GetBuffer(len*4+1+str->GetLength())+str->GetLength();
\r
222 SecureZeroMemory(buf, (len*4 + 1)*sizeof(WCHAR));
\r
223 MultiByteToWideChar(code, 0, (LPCSTR)p, len, buf, len*4);
\r
224 str->ReleaseBuffer();
\r
225 //str->Append(buf);
\r
228 BOOL CGit::IsInitRepos()
\r
232 if(g_Git.Run(_T("git.exe rev-parse --revs-only HEAD"),&cmdout,CP_UTF8))
\r
234 // CMessageBox::Show(NULL,cmdout,_T("TortoiseGit"),MB_OK);
\r
237 if(cmdout.IsEmpty())
\r
242 int CGit::Run(CGitCall* pcall)
\r
244 PROCESS_INFORMATION pi;
\r
246 if(RunAsync(pcall->GetCmd(),&pi,&hRead))
\r
247 return GIT_ERROR_CREATE_PROCESS;
\r
250 BYTE data[CALL_OUTPUT_READ_CHUNK_SIZE];
\r
251 bool bAborted=false;
\r
252 while(ReadFile(hRead,data,CALL_OUTPUT_READ_CHUNK_SIZE,&readnumber,NULL))
\r
254 //Todo: when OnOutputData() returns 'true', abort git-command. Send CTRL-C signal?
\r
255 if(!bAborted)//For now, flush output when command aborted.
\r
256 if(pcall->OnOutputData(data,readnumber))
\r
263 CloseHandle(pi.hThread);
\r
265 WaitForSingleObject(pi.hProcess, INFINITE);
\r
268 if(!GetExitCodeProcess(pi.hProcess,&exitcode))
\r
270 return GIT_ERROR_GET_EXIT_CODE;
\r
273 CloseHandle(pi.hProcess);
\r
275 CloseHandle(hRead);
\r
278 class CGitCall_ByteVector : public CGitCall
\r
281 CGitCall_ByteVector(CString cmd,BYTE_VECTOR* pvector):CGitCall(cmd),m_pvector(pvector){}
\r
282 virtual bool OnOutputData(const BYTE* data, size_t size)
\r
284 size_t oldsize=m_pvector->size();
\r
285 m_pvector->resize(m_pvector->size()+size);
\r
286 memcpy(&*(m_pvector->begin()+oldsize),data,size);
\r
289 BYTE_VECTOR* m_pvector;
\r
292 int CGit::Run(CString cmd,BYTE_VECTOR *vector)
\r
294 CGitCall_ByteVector call(cmd,vector);
\r
297 int CGit::Run(CString cmd, CString* output,int code)
\r
299 BYTE_VECTOR vector;
\r
301 ret=Run(cmd,&vector);
\r
303 vector.push_back(0);
\r
305 StringAppend(output,&(vector[0]),code);
\r
309 CString CGit::GetUserName(void)
\r
311 return GetConfigValue(L"user.name");
\r
313 CString CGit::GetUserEmail(void)
\r
315 return GetConfigValue(L"user.email");
\r
318 CString CGit::GetConfigValue(CString name)
\r
320 CString configValue;
\r
322 cmd.Format(L"git.exe config %s", name);
\r
323 Run(cmd,&configValue,CP_UTF8);
\r
325 return configValue.Tokenize(_T("\n"),start);
\r
329 CString CGit::GetCurrentBranch(void)
\r
332 //Run(_T("git.exe branch"),&branch);
\r
334 int ret=g_Git.Run(_T("git.exe branch --no-color"),&output,CP_UTF8);
\r
342 one=output.Tokenize(_T("\n"),pos);
\r
343 //list.push_back(one.Right(one.GetLength()-2));
\r
344 if(one[0] == _T('*'))
\r
345 return one.Right(one.GetLength()-2);
\r
348 return CString("");
\r
351 CString CGit::GetSymbolicRef(const wchar_t* symbolicRefName, bool bStripRefsHeads)
\r
355 cmd.Format(L"git symbolic-ref %s", symbolicRefName);
\r
356 if(Run(cmd, &refName, CP_UTF8) != 0)
\r
357 return CString();//Error
\r
359 refName = refName.Tokenize(L"\n", iStart);
\r
360 if(bStripRefsHeads)
\r
361 refName = StripRefName(refName);
\r
365 CString CGit::GetFullRefName(CString shortRefName)
\r
369 cmd.Format(L"git rev-parse --symbolic-full-name %s", shortRefName);
\r
370 if(Run(cmd, &refName, CP_UTF8) != 0)
\r
371 return CString();//Error
\r
373 return refName.Tokenize(L"\n", iStart);
\r
376 CString CGit::StripRefName(CString refName)
\r
378 if(wcsncmp(refName, L"refs/heads/", 11) == 0)
\r
379 refName = refName.Mid(11);
\r
380 else if(wcsncmp(refName, L"refs/", 5) == 0)
\r
381 refName = refName.Mid(5);
\r
385 int CGit::GetCurrentBranchFromFile(const CString &sProjectRoot, CString &sBranchOut)
\r
387 // read current branch name like git-gui does, by parsing the .git/HEAD file directly
\r
389 if ( sProjectRoot.IsEmpty() )
\r
392 CString sHeadFile = sProjectRoot + _T("\\") + g_GitAdminDir.GetAdminDirName() + _T("\\HEAD");
\r
395 _tfopen_s(&pFile, sHeadFile.GetString(), _T("r"));
\r
403 fgets(s, sizeof(s), pFile);
\r
407 const char *pfx = "ref: refs/heads/";
\r
408 const int len = 16;//strlen(pfx)
\r
410 if ( !strncmp(s, pfx, len) )
\r
412 //# We're on a branch. It might not exist. But
\r
413 //# HEAD looks good enough to be a branch.
\r
414 sBranchOut = s + len;
\r
415 sBranchOut.TrimRight(_T(" \r\n\t"));
\r
417 if ( sBranchOut.IsEmpty() )
\r
422 //# Assume this is a detached head.
\r
423 sBranchOut = "HEAD";
\r
431 int CGit::BuildOutputFormat(CString &format,bool IsFull)
\r
434 log.Format(_T("#<%c>%%x00"),LOG_REV_ITEM_BEGIN);
\r
438 log.Format(_T("#<%c>%%an%%x00"),LOG_REV_AUTHOR_NAME);
\r
440 log.Format(_T("#<%c>%%ae%%x00"),LOG_REV_AUTHOR_EMAIL);
\r
442 log.Format(_T("#<%c>%%ai%%x00"),LOG_REV_AUTHOR_DATE);
\r
444 log.Format(_T("#<%c>%%cn%%x00"),LOG_REV_COMMIT_NAME);
\r
446 log.Format(_T("#<%c>%%ce%%x00"),LOG_REV_COMMIT_EMAIL);
\r
448 log.Format(_T("#<%c>%%ci%%x00"),LOG_REV_COMMIT_DATE);
\r
450 log.Format(_T("#<%c>%%b%%x00"),LOG_REV_COMMIT_BODY);
\r
454 log.Format(_T("#<%c>%%m%%H%%x00"),LOG_REV_COMMIT_HASH);
\r
456 log.Format(_T("#<%c>%%P%%x00"),LOG_REV_COMMIT_PARENT);
\r
458 log.Format(_T("#<%c>%%s%%x00"),LOG_REV_COMMIT_SUBJECT);
\r
463 log.Format(_T("#<%c>%%x00"),LOG_REV_COMMIT_FILE);
\r
469 int CGit::GetLog(BYTE_VECTOR& logOut, CString &hash, CTGitPath *path ,int count,int mask,CString *from,CString *to)
\r
471 CGitCall_ByteVector gitCall(CString(),&logOut);
\r
472 return GetLog(&gitCall,hash,path,count,mask,from,to);
\r
475 //int CGit::GetLog(CGitCall* pgitCall, CString &hash, CTGitPath *path ,int count,int mask)
\r
476 int CGit::GetLog(CGitCall* pgitCall, CString &hash, CTGitPath *path, int count, int mask,CString *from,CString *to)
\r
487 file.Format(_T(" -- \"%s\""),path->GetGitPathString());
\r
490 num.Format(_T("-n%d"),count);
\r
494 if(mask& LOG_INFO_STAT )
\r
495 param += _T(" --numstat ");
\r
496 if(mask& LOG_INFO_FILESTATE)
\r
497 param += _T(" --raw ");
\r
499 if(mask& LOG_INFO_FULLHISTORY)
\r
500 param += _T(" --full-history ");
\r
502 if(mask& LOG_INFO_BOUNDARY)
\r
503 param += _T(" --left-right --boundary ");
\r
505 if(mask& CGit::LOG_INFO_ALL_BRANCH)
\r
506 param += _T(" --all ");
\r
508 if(mask& CGit::LOG_INFO_DETECT_COPYRENAME)
\r
509 param += _T(" -C ");
\r
511 if(mask& CGit::LOG_INFO_DETECT_RENAME )
\r
512 param += _T(" -M ");
\r
514 if(mask& CGit::LOG_INFO_FIRST_PARENT )
\r
515 param += _T(" --first-parent ");
\r
517 if(mask& CGit::LOG_INFO_NO_MERGE )
\r
518 param += _T(" --no-merges ");
\r
520 if(mask& CGit::LOG_INFO_FOLLOW)
\r
521 param += _T(" --follow ");
\r
523 if(mask& CGit::LOG_INFO_SHOW_MERGEDFILE)
\r
524 param += _T(" -c ");
\r
526 if(mask& CGit::LOG_INFO_FULL_DIFF)
\r
527 param += _T(" --full-diff ");
\r
529 if(from != NULL && to != NULL)
\r
532 range.Format(_T(" %s..%s "),*from,*to);
\r
537 cmd.Format(_T("git.exe log %s -z --topo-order %s --parents --pretty=format:\""),
\r
540 BuildOutputFormat(log,!(mask&CGit::LOG_INFO_ONLY_HASH));
\r
543 cmd += CString(_T("\" "))+hash+file;
\r
545 pgitCall->SetCmd(cmd);
\r
547 return Run(pgitCall);
\r
548 // return Run(cmd,&logOut);
\r
552 int CGit::GetShortLog(CString &logOut,CTGitPath * path, int count)
\r
561 cmd.Format(_T("git.exe log --left-right --boundary --topo-order -n%d --pretty=format:\""),n);
\r
562 BuildOutputFormat(log,false);
\r
563 cmd += log+_T("\"");
\r
565 cmd+= _T(" -- \"")+path->GetGitPathString()+_T("\"");
\r
566 //cmd += CString(_T("\" HEAD~40..HEAD"));
\r
567 return Run(cmd,&logOut);
\r
571 #define BUFSIZE 512
\r
572 void GetTempPath(CString &path)
\r
574 TCHAR lpPathBuffer[BUFSIZE];
\r
576 DWORD dwBufSize=BUFSIZE;
\r
577 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
\r
578 lpPathBuffer); // buffer for path
\r
579 if (dwRetVal > dwBufSize || (dwRetVal == 0))
\r
583 path.Format(_T("%s"),lpPathBuffer);
\r
585 CString GetTempFile()
\r
587 TCHAR lpPathBuffer[BUFSIZE];
\r
589 DWORD dwBufSize=BUFSIZE;
\r
590 TCHAR szTempName[BUFSIZE];
\r
593 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
\r
594 lpPathBuffer); // buffer for path
\r
595 if (dwRetVal > dwBufSize || (dwRetVal == 0))
\r
599 // Create a temporary file.
\r
600 uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files
\r
601 TEXT("Patch"), // temp file name prefix
\r
602 0, // create unique name
\r
603 szTempName); // buffer for name
\r
611 return CString(szTempName);
\r
615 int CGit::RunLogFile(CString cmd,CString &filename)
\r
618 PROCESS_INFORMATION pi;
\r
619 si.cb=sizeof(STARTUPINFO);
\r
620 GetStartupInfo(&si);
\r
622 SECURITY_ATTRIBUTES psa={sizeof(psa),NULL,TRUE};;
\r
623 psa.bInheritHandle=TRUE;
\r
625 HANDLE houtfile=CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
\r
626 &psa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
\r
629 si.wShowWindow=SW_HIDE;
\r
630 si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
\r
631 si.hStdOutput = houtfile;
\r
633 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,NULL,NULL,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
\r
636 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
\r
637 NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
\r
640 return GIT_ERROR_CREATE_PROCESS;
\r
643 WaitForSingleObject(pi.hProcess,INFINITE);
\r
645 CloseHandle(pi.hThread);
\r
646 CloseHandle(pi.hProcess);
\r
647 CloseHandle(houtfile);
\r
648 return GIT_SUCCESS;
\r
652 git_revnum_t CGit::GetHash(const CString &friendname)
\r
656 cmd.Format(_T("git.exe rev-parse %s" ),friendname);
\r
657 Run(cmd,&out,CP_UTF8);
\r
658 // int pos=out.ReverseFind(_T('\n'));
\r
659 int pos=out.FindOneOf(_T("\r\n"));
\r
661 return out.Left(pos);
\r
665 int CGit::GetCommitDiffList(CString &rev1,CString &rev2,CTGitPathList &outputlist)
\r
669 if(rev1 == GIT_REV_ZERO || rev2 == GIT_REV_ZERO)
\r
672 if(rev1 == GIT_REV_ZERO)
\r
673 cmd.Format(_T("git.exe diff -r --raw -C -M --numstat -z %s"),rev2);
\r
675 cmd.Format(_T("git.exe diff -r -R --raw -C -M --numstat -z %s"),rev1);
\r
678 cmd.Format(_T("git.exe diff-tree -r --raw -C -M --numstat -z %s %s"),rev2,rev1);
\r
682 if(g_Git.Run(cmd,&out))
\r
685 outputlist.ParserFromLog(out);
\r
689 int CGit::GetTagList(STRING_VECTOR &list)
\r
692 CString cmd,output;
\r
693 cmd=_T("git.exe tag -l");
\r
695 ret=g_Git.Run(cmd,&output,CP_UTF8);
\r
703 one=output.Tokenize(_T("\n"),pos);
\r
704 list.push_back(one);
\r
710 int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)
\r
713 CString cmd,output;
\r
714 cmd=_T("git.exe branch --no-color");
\r
716 if(type==(BRANCH_LOCAL|BRANCH_REMOTE))
\r
718 else if(type==BRANCH_REMOTE)
\r
722 ret=g_Git.Run(cmd,&output,CP_UTF8);
\r
729 one=output.Tokenize(_T("\n"),pos);
\r
730 one.Trim(L" \r\n\t");
\r
731 if(one.Find(L" -> ") >= 0 || one.IsEmpty())
\r
732 continue; // skip something like: refs/origin/HEAD -> refs/origin/master
\r
733 if(one[0] == _T('*'))
\r
739 list.push_back(one);
\r
746 int CGit::GetRemoteList(STRING_VECTOR &list)
\r
749 CString cmd,output;
\r
750 cmd=_T("git.exe config --get-regexp \"^^remote[.].*[.]url\"");
\r
751 ret=g_Git.Run(cmd,&output,CP_UTF8);
\r
758 one=output.Tokenize(_T("\n"),pos);
\r
759 int start=one.Find(_T("."),0);
\r
763 url=one.Right(one.GetLength()-start-1);
\r
765 one=one.Left(one.Find(_T("."),0));
\r
766 list.push_back(one);
\r
773 int CGit::GetRefList(STRING_VECTOR &list)
\r
776 CString cmd,output;
\r
777 cmd=_T("git show-ref -d");
\r
778 ret=g_Git.Run(cmd,&output,CP_UTF8);
\r
785 one=output.Tokenize(_T("\n"),pos);
\r
786 int start=one.Find(_T(" "),0);
\r
790 name=one.Right(one.GetLength()-start-1);
\r
791 list.push_back(name);
\r
797 int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)
\r
800 CString cmd,output;
\r
801 cmd=_T("git show-ref -d");
\r
802 ret=g_Git.Run(cmd,&output,CP_UTF8);
\r
809 one=output.Tokenize(_T("\n"),pos);
\r
810 int start=one.Find(_T(" "),0);
\r
814 name=one.Right(one.GetLength()-start-1);
\r
817 hash=one.Left(start);
\r
819 map[hash].push_back(name);
\r
826 BOOL CGit::CheckMsysGitDir()
\r
828 static BOOL bInitialized = FALSE;
\r
835 TCHAR *oldpath,*home;
\r
836 size_t homesize,size,httpsize;
\r
838 // set HOME if not set already
\r
839 _tgetenv_s(&homesize, NULL, 0, _T("HOME"));
\r
842 _tdupenv_s(&home,&size,_T("USERPROFILE"));
\r
843 _tputenv_s(_T("HOME"),home);
\r
848 #ifndef _TORTOISESHELL
\r
850 _tgetenv_s(&httpsize, NULL, 0, _T("http_proxy"));
\r
853 CString regServeraddress_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-host"), _T(""));
\r
854 CString regServerport_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-port"), _T(""));
\r
855 CString regUsername_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-username"), _T(""));
\r
856 CString regPassword_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-password"), _T(""));
\r
857 CString regTimeout_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-timeout"), _T(""));
\r
858 CString regExceptions_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-exceptions"), _T(""));
\r
860 CString http_proxy;
\r
861 if(!regServeraddress_copy.IsEmpty())
\r
863 if(regServeraddress_copy.Left(4) != _T("http"))
\r
864 http_proxy=_T("http://");
\r
866 if(!regUsername_copy.IsEmpty())
\r
868 http_proxy += regUsername_copy;
\r
869 http_proxy += _T(":")+regPassword_copy;
\r
870 http_proxy += _T("@");
\r
872 http_proxy+=regServeraddress_copy;
\r
873 if(!regServerport_copy.IsEmpty())
\r
875 http_proxy +=_T(":")+regServerport_copy;
\r
877 _tputenv_s(_T("http_proxy"),http_proxy);
\r
881 CString sshclient=CRegString(_T("Software\\TortoiseGit\\SSH"));
\r
883 if(!sshclient.IsEmpty())
\r
885 _tputenv_s(_T("GIT_SSH"),sshclient);
\r
888 CString ssh=sshclient;
\r
889 ssh.Replace(_T("/"),_T("\\"));
\r
890 ssh.Replace(_T("\\"),_T("\\\\"));
\r
891 ssh=CString(_T("\""))+ssh+_T('\"');
\r
892 _tputenv_s(_T("SVN_SSH"),ssh);
\r
896 TCHAR sPlink[MAX_PATH];
\r
897 GetModuleFileName(NULL, sPlink, _countof(sPlink));
\r
898 LPTSTR ptr = _tcsrchr(sPlink, _T('\\'));
\r
900 _tcscpy(ptr + 1, _T("TortoisePlink.exe"));
\r
901 _tputenv_s(_T("GIT_SSH"), sPlink);
\r
904 CString ssh=sPlink;
\r
905 ssh.Replace(_T("/"),_T("\\"));
\r
906 ssh.Replace(_T("\\"),_T("\\\\"));
\r
907 ssh=CString(_T("\""))+ssh+_T('\"');
\r
908 _tputenv_s(_T("SVN_SSH"),ssh);
\r
913 TCHAR sAskPass[MAX_PATH];
\r
914 GetModuleFileName(NULL, sAskPass, _countof(sAskPass));
\r
915 LPTSTR ptr = _tcsrchr(sAskPass, _T('\\'));
\r
918 _tcscpy(ptr + 1, _T("SshAskPass.exe"));
\r
919 _tputenv_s(_T("DISPLAY"),_T(":9999"));
\r
920 _tputenv_s(_T("SSH_ASKPASS"),sAskPass);
\r
923 // search PATH if git/bin directory is alredy present
\r
924 if ( FindGitPath() )
\r
926 bInitialized = TRUE;
\r
930 // add git/bin path to PATH
\r
932 CRegString msysdir=CRegString(REG_MSYSGIT_PATH,_T(""),FALSE);
\r
936 CRegString msysinstalldir=CRegString(REG_MSYSGIT_INSTALL,_T(""),FALSE,HKEY_LOCAL_MACHINE);
\r
937 str=msysinstalldir;
\r
938 if ( !str.IsEmpty() )
\r
940 str += (str[str.GetLength()-1] != '\\') ? "\\bin" : "bin";
\r
950 //CGit::m_MsysGitPath=str;
\r
954 _tdupenv_s(&oldpath,&size,_T("PATH"));
\r
957 path.Format(_T("%s;%s"),oldpath,str);
\r
959 _tputenv_s(_T("PATH"),path);
\r
961 CString sOldPath = oldpath;
\r
965 if( !FindGitPath() )
\r
969 _tputenv_s(_T("HOME"),_T(""));
\r
975 #ifdef _TORTOISESHELL
\r
976 l_processEnv = GetEnvironmentStrings();
\r
977 // updated environment is now duplicated for use in CreateProcess, restore original PATH for current process
\r
978 _tputenv_s(_T("PATH"),sOldPath);
\r
981 _tputenv_s(_T("HOME"),_T(""));
\r
985 bInitialized = TRUE;
\r
991 class CGitCall_EnumFiles : public CGitCall
\r
994 CGitCall_EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)
\r
995 : m_pszProjectPath(pszProjectPath),
\r
996 m_pszSubPath(pszSubPath),
\r
998 m_pEnumCb(pEnumCb),
\r
999 m_pUserData(pUserData)
\r
1003 typedef std::map<CStringA,char> TStrCharMap;
\r
1005 const TCHAR * m_pszProjectPath;
\r
1006 const TCHAR * m_pszSubPath;
\r
1007 unsigned int m_nFlags;
\r
1008 WGENUMFILECB * m_pEnumCb;
\r
1009 void * m_pUserData;
\r
1011 BYTE_VECTOR m_DataCollector;
\r
1013 virtual bool OnOutputData(const BYTE* data, size_t size)
\r
1015 m_DataCollector.append(data,size);
\r
1018 // lines from igit.exe are 0 terminated
\r
1019 int found=m_DataCollector.findData((const BYTE*)"",1);
\r
1022 OnSingleLine( (LPCSTR)&*m_DataCollector.begin() );
\r
1023 m_DataCollector.erase(m_DataCollector.begin(), m_DataCollector.begin()+found+1);
\r
1025 return false;//Should never reach this
\r
1027 virtual void OnEnd()
\r
1031 BYTE HexChar(char ch)
\r
1033 if (ch >= '0' && ch <= '9')
\r
1034 return (UINT)(ch - '0');
\r
1035 else if (ch >= 'A' && ch <= 'F')
\r
1036 return (UINT)(ch - 'A') + 10;
\r
1037 else if (ch >= 'a' && ch <= 'f')
\r
1038 return (UINT)(ch - 'a') + 10;
\r
1043 bool OnSingleLine(LPCSTR line)
\r
1045 //Parse single line
\r
1047 wgFile_s fileStatus;
\r
1051 fileStatus.nFlags = 0;
\r
1053 fileStatus.nFlags |= WGFF_Directory;
\r
1054 else if (*line != 'F')
\r
1061 fileStatus.nStatus = WGFS_Unknown;
\r
1064 case 'N': fileStatus.nStatus = WGFS_Normal; break;
\r
1065 case 'M': fileStatus.nStatus = WGFS_Modified; break;
\r
1066 case 'S': fileStatus.nStatus = WGFS_Staged; break;
\r
1067 case 'A': fileStatus.nStatus = WGFS_Added; break;
\r
1068 case 'C': fileStatus.nStatus = WGFS_Conflicted; break;
\r
1069 case 'D': fileStatus.nStatus = WGFS_Deleted; break;
\r
1070 case 'I': fileStatus.nStatus = WGFS_Ignored; break;
\r
1071 case 'U': fileStatus.nStatus = WGFS_Unversioned; break;
\r
1072 case 'E': fileStatus.nStatus = WGFS_Empty; break;
\r
1073 case '?': fileStatus.nStatus = WGFS_Unknown; break;
\r
1083 fileStatus.sha1 = NULL;
\r
1084 if ( !(fileStatus.nFlags & WGFF_Directory) )
\r
1086 for (int i=0; i<20; i++)
\r
1088 sha1[i] = (HexChar(line[0])<<4)&0xF0;
\r
1089 sha1[i] |= HexChar(line[1])&0xF;
\r
1098 int len = strlen(line);
\r
1099 if (len && len < 2048)
\r
1101 WCHAR *buf = (WCHAR*)alloca((len*4+2)*sizeof(WCHAR));
\r
1103 MultiByteToWideChar(CP_ACP, 0, line, len+1, buf, len*4+1);
\r
1104 fileStatus.sFileName = buf;
\r
1106 if (*buf && (*m_pEnumCb)(&fileStatus,m_pUserData))
\r
1114 BOOL CGit::EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)
\r
1116 if(!pszProjectPath || *pszProjectPath=='\0')
\r
1119 CGitCall_EnumFiles W_GitCall(pszProjectPath,pszSubPath,nFlags,pEnumCb,pUserData);
\r
1122 /* char W_szToDir[MAX_PATH];
\r
1123 strncpy(W_szToDir,pszProjectPath,sizeof(W_szToDir)-1);
\r
1124 if(W_szToDir[strlen(W_szToDir)-1]!='\\')
\r
1125 strncat(W_szToDir,"\\",sizeof(W_szToDir)-1);
\r
1127 SetCurrentDirectoryA(W_szToDir);
\r
1128 GetCurrentDirectoryA(sizeof(W_szToDir)-1,W_szToDir);
\r
1130 SetCurrentDir(pszProjectPath);
\r
1135 if (nFlags & WGEFF_NoRecurse) sMode += _T("r");
\r
1136 if (nFlags & WGEFF_FullPath) sMode += _T("f");
\r
1137 if (nFlags & WGEFF_DirStatusDelta) sMode += _T("d");
\r
1138 if (nFlags & WGEFF_DirStatusAll) sMode += _T("D");
\r
1139 if (nFlags & WGEFF_EmptyAsNormal) sMode += _T("e");
\r
1140 if (nFlags & WGEFF_SingleFile) sMode += _T("s");
\r
1147 // NOTE: there seems to be some issue with msys based app receiving backslash on commandline, at least
\r
1148 // if followed by " like for example 'igit "C:\"', the commandline igit receives is 'igit.exe C:" status' with
\r
1149 // the 'C:" status' part as a single arg, Maybe it uses unix style processing. In order to avoid this just
\r
1150 // use forward slashes for supplied project and sub paths
\r
1152 CString sProjectPath = pszProjectPath;
\r
1153 sProjectPath.Replace(_T('\\'), _T('/'));
\r
1157 CString sSubPath = pszSubPath;
\r
1158 sSubPath.Replace(_T('\\'), _T('/'));
\r
1160 cmd.Format(_T("igit.exe \"%s\" status %s \"%s\""), sProjectPath, sMode, sSubPath);
\r
1164 cmd.Format(_T("igit.exe \"%s\" status %s"), sProjectPath, sMode);
\r
1167 //OutputDebugStringA("---");OutputDebugStringW(cmd);OutputDebugStringA("\r\n");
\r
1169 W_GitCall.SetCmd(cmd);
\r
1170 // NOTE: should igit get added as a part of msysgit then use below line instead of the above one
\r
1171 //W_GitCall.SetCmd(CGit::ms_LastMsysGitDir + cmd);
\r
1173 if ( Run(&W_GitCall) )
\r
1179 BOOL CGit::CheckCleanWorkTree()
\r
1183 cmd=_T("git.exe rev-parse --verify HEAD");
\r
1185 if(g_Git.Run(cmd,&out,CP_UTF8))
\r
1188 cmd=_T("git.exe update-index --ignore-submodules --refresh");
\r
1189 if(g_Git.Run(cmd,&out,CP_UTF8))
\r
1192 cmd=_T("git.exe diff-files --quiet --ignore-submodules");
\r
1193 if(g_Git.Run(cmd,&out,CP_UTF8))
\r
1196 cmd=_T("git diff-index --cached --quiet HEAD --ignore-submodules");
\r
1197 if(g_Git.Run(cmd,&out,CP_UTF8))
\r
1202 int CGit::Revert(CTGitPathList &list,bool keep)
\r
1205 for(int i=0;i<list.GetCount();i++)
\r
1207 ret = Revert((CTGitPath&)list[i],keep);
\r
1213 int CGit::Revert(CTGitPath &path,bool keep)
\r
1216 if(path.m_Action & CTGitPath::LOGACTIONS_ADDED)
\r
1217 { //To init git repository, there are not HEAD, so we can use git reset command
\r
1218 cmd.Format(_T("git.exe rm --cached -- \"%s\""),path.GetGitPathString());
\r
1220 if(g_Git.Run(cmd,&out,CP_ACP))
\r
1223 else if(path.m_Action & CTGitPath::LOGACTIONS_REPLACED )
\r
1225 cmd.Format(_T("git.exe mv -- \"%s\" \"%s\""),path.GetGitPathString(),path.GetGitOldPathString());
\r
1226 if(g_Git.Run(cmd,&out,CP_ACP))
\r
1229 cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitOldPathString());
\r
1230 if(g_Git.Run(cmd,&out,CP_ACP))
\r
1235 cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitPathString());
\r
1236 if(g_Git.Run(cmd,&out,CP_ACP))
\r
1242 int CGit::ListConflictFile(CTGitPathList &list,CTGitPath *path)
\r
1244 BYTE_VECTOR vector;
\r
1248 cmd.Format(_T("git.exe ls-files -u -t -z -- \"%s\""),path->GetGitPathString());
\r
1250 cmd=_T("git.exe ls-files -u -t -z");
\r
1252 if(g_Git.Run(cmd,&vector))
\r
1257 list.ParserFromLsFile(vector);
\r
1262 bool CGit::IsFastForward(CString &from, CString &to)
\r
1264 CString base,hash;
\r
1266 cmd.Format(_T("git.exe merge-base %s %s"), to,from);
\r
1268 if(g_Git.Run(cmd,&base,CP_ACP))
\r
1270 //CMessageBox::Show(NULL,base,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
\r
1273 base=base.Left(40);
\r
1275 hash=g_Git.GetHash(from);
\r
1277 hash=hash.Left(40);
\r
1279 return hash == base;
\r
1282 unsigned int CGit::Hash2int(CString &hash)
\r
1285 for(int i=0;i<8;i++)
\r
1288 if(hash[i]>=_T('a'))
\r
1289 ret |= (hash[i]-_T('a')+10)&0xFF;
\r
1290 else if(hash[i]>=_T('A'))
\r
1291 ret |= (hash[i]-_T('A')+10)&0xFF;
\r
1293 ret |= (hash[i]-_T('0'))&0xFF;
\r
1299 int CGit::RefreshGitIndex()
\r
1301 CString cmd,output;
\r
1302 cmd=_T("git.exe update-index --refresh");
\r
1303 return Run(cmd,&output,CP_ACP);
\r