use gitdll gethash and clean up some warning
[TortoiseGit.git] / src / Git / Git.cpp
blob8b305209e09fff3e7a3b6cf7738c3de797f7fb56
1 #include "StdAfx.h"
2 #include "Git.h"
3 #include "atlconv.h"
4 #include "GitRev.h"
5 #include "registry.h"
6 #include "GitConfig.h"
7 #include <map>
8 #include "UnicodeUtils.h"
9 #include "gitdll.h"
11 int CGit::m_LogEncode=CP_UTF8;
14 static LPTSTR nextpath(LPCTSTR src, LPTSTR dst, UINT maxlen)
16 LPCTSTR orgsrc;
18 while (*src == _T(';'))
19 src++;
21 orgsrc = src;
23 if (!--maxlen)
24 goto nullterm;
26 while (*src && *src != _T(';'))
28 if (*src != _T('"'))
30 *dst++ = *src++;
31 if (!--maxlen)
33 orgsrc = src;
34 goto nullterm;
37 else
39 src++;
40 while (*src && *src != _T('"'))
42 *dst++ = *src++;
43 if (!--maxlen)
45 orgsrc = src;
46 goto nullterm;
50 if (*src)
51 src++;
55 while (*src == _T(';'))
56 src++;
58 nullterm:
60 *dst = 0;
62 return (orgsrc != src) ? (LPTSTR)src : NULL;
65 static inline BOOL FileExists(LPCTSTR lpszFileName)
67 struct _stat st;
68 return _tstat(lpszFileName, &st) == 0;
71 static BOOL FindGitPath()
73 size_t size;
74 _tgetenv_s(&size, NULL, 0, _T("PATH"));
76 if (!size)
78 return FALSE;
81 TCHAR *env = (TCHAR*)alloca(size * sizeof(TCHAR));
82 _tgetenv_s(&size, env, size, _T("PATH"));
84 TCHAR buf[_MAX_PATH];
86 // search in all paths defined in PATH
87 while ((env = nextpath(env, buf, _MAX_PATH-1)) && *buf)
89 TCHAR *pfin = buf + _tcslen(buf)-1;
91 // ensure trailing slash
92 if (*pfin != _T('/') && *pfin != _T('\\'))
93 _tcscpy(++pfin, _T("\\"));
95 const int len = _tcslen(buf);
97 if ((len + 7) < _MAX_PATH)
98 _tcscpy(pfin+1, _T("git.exe"));
99 else
100 break;
102 if ( FileExists(buf) )
104 // dir found
105 pfin[1] = 0;
106 CGit::ms_LastMsysGitDir = buf;
107 return TRUE;
111 return FALSE;
115 #define MAX_DIRBUFFER 1000
116 #define CALL_OUTPUT_READ_CHUNK_SIZE 1024
118 CString CGit::ms_LastMsysGitDir;
119 CGit g_Git;
122 CGit::CGit(void)
124 GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));
125 m_CurrentDir.ReleaseBuffer();
126 m_IsGitDllInited = false;
127 m_GitDiff=0;
128 m_IsUseGitDLL = !!CRegDWORD(_T("Software\\TortoiseGit\\UsingGitDLL"),1);
129 this->m_bInitialized =false;
130 CheckMsysGitDir();
131 m_critGitDllSec.Init();
134 CGit::~CGit(void)
136 if(this->m_GitDiff)
138 git_close_diff(m_GitDiff);
139 m_GitDiff=0;
143 static char g_Buffer[4096];
145 int CGit::RunAsync(CString cmd,PROCESS_INFORMATION *piOut,HANDLE *hReadOut,CString *StdioFile)
147 SECURITY_ATTRIBUTES sa;
148 HANDLE hRead, hWrite;
149 HANDLE hStdioFile = NULL;
151 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
152 sa.lpSecurityDescriptor=NULL;
153 sa.bInheritHandle=TRUE;
154 if(!CreatePipe(&hRead,&hWrite,&sa,0))
156 return GIT_ERROR_OPEN_PIP;
159 if(StdioFile)
161 hStdioFile=CreateFile(*StdioFile,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
162 &sa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
165 STARTUPINFO si;
166 PROCESS_INFORMATION pi;
167 si.cb=sizeof(STARTUPINFO);
168 GetStartupInfo(&si);
170 si.hStdError=hWrite;
171 if(StdioFile)
172 si.hStdOutput=hStdioFile;
173 else
174 si.hStdOutput=hWrite;
176 si.wShowWindow=SW_HIDE;
177 si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
179 LPTSTR pEnv = m_Environment.size()? &m_Environment[0]: NULL;
180 DWORD dwFlags = pEnv ? CREATE_UNICODE_ENVIRONMENT : 0;
182 //DETACHED_PROCESS make ssh recognize that it has no console to launch askpass to input password.
183 dwFlags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
185 memset(&this->m_CurrentGitPi,0,sizeof(PROCESS_INFORMATION));
186 memset(&pi, 0, sizeof(PROCESS_INFORMATION));
188 if(cmd.Find(_T("git")) == 0)
189 cmd=CGit::ms_LastMsysGitDir+_T("\\")+cmd;
191 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,dwFlags,pEnv,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
193 LPVOID lpMsgBuf;
194 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
195 NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
196 (LPTSTR)&lpMsgBuf,
197 0,NULL);
198 return GIT_ERROR_CREATE_PROCESS;
201 m_CurrentGitPi = pi;
203 CloseHandle(hWrite);
204 if(piOut)
205 *piOut=pi;
206 if(hReadOut)
207 *hReadOut=hRead;
209 return 0;
212 //Must use sperate function to convert ANSI str to union code string
213 //Becuase A2W use stack as internal convert buffer.
214 void CGit::StringAppend(CString *str,BYTE *p,int code,int length)
216 //USES_CONVERSION;
217 //str->Append(A2W_CP((LPCSTR)p,code));
218 if(str == NULL)
219 return ;
221 WCHAR * buf;
223 int len ;
224 if(length<0)
225 len= strlen((const char*)p);
226 else
227 len=length;
228 //if (len==0)
229 // return ;
230 //buf = new WCHAR[len*4 + 1];
231 buf = str->GetBuffer(len*4+1+str->GetLength())+str->GetLength();
232 SecureZeroMemory(buf, (len*4 + 1)*sizeof(WCHAR));
233 MultiByteToWideChar(code, 0, (LPCSTR)p, len, buf, len*4);
234 str->ReleaseBuffer();
235 //str->Append(buf);
236 //delete buf;
238 BOOL CGit::IsInitRepos()
240 CString cmdout;
241 cmdout.Empty();
242 if(g_Git.Run(_T("git.exe rev-parse --revs-only HEAD"),&cmdout,CP_UTF8))
244 // CMessageBox::Show(NULL,cmdout,_T("TortoiseGit"),MB_OK);
245 return TRUE;
247 if(cmdout.IsEmpty())
248 return TRUE;
250 return FALSE;
252 int CGit::Run(CGitCall* pcall)
254 PROCESS_INFORMATION pi;
255 HANDLE hRead;
256 if(RunAsync(pcall->GetCmd(),&pi,&hRead))
257 return GIT_ERROR_CREATE_PROCESS;
259 DWORD readnumber;
260 BYTE data[CALL_OUTPUT_READ_CHUNK_SIZE];
261 bool bAborted=false;
262 while(ReadFile(hRead,data,CALL_OUTPUT_READ_CHUNK_SIZE,&readnumber,NULL))
264 // TODO: when OnOutputData() returns 'true', abort git-command. Send CTRL-C signal?
265 if(!bAborted)//For now, flush output when command aborted.
266 if(pcall->OnOutputData(data,readnumber))
267 bAborted=true;
269 if(!bAborted)
270 pcall->OnEnd();
272 CloseHandle(pi.hThread);
274 WaitForSingleObject(pi.hProcess, INFINITE);
275 DWORD exitcode =0;
277 if(!GetExitCodeProcess(pi.hProcess,&exitcode))
279 return GIT_ERROR_GET_EXIT_CODE;
282 CloseHandle(pi.hProcess);
284 CloseHandle(hRead);
285 return exitcode;
287 class CGitCall_ByteVector : public CGitCall
289 public:
290 CGitCall_ByteVector(CString cmd,BYTE_VECTOR* pvector):CGitCall(cmd),m_pvector(pvector){}
291 virtual bool OnOutputData(const BYTE* data, size_t size)
293 size_t oldsize=m_pvector->size();
294 m_pvector->resize(m_pvector->size()+size);
295 memcpy(&*(m_pvector->begin()+oldsize),data,size);
296 return false;
298 BYTE_VECTOR* m_pvector;
301 int CGit::Run(CString cmd,BYTE_VECTOR *vector)
303 CGitCall_ByteVector call(cmd,vector);
304 return Run(&call);
306 int CGit::Run(CString cmd, CString* output,int code)
308 BYTE_VECTOR vector;
309 int ret;
310 ret=Run(cmd,&vector);
312 vector.push_back(0);
314 StringAppend(output,&(vector[0]),code);
315 return ret;
318 CString CGit::GetUserName(void)
320 return GetConfigValue(L"user.name", this->GetGitEncode(L"i18n.commitencoding"));
322 CString CGit::GetUserEmail(void)
324 return GetConfigValue(L"user.email");
327 CString CGit::GetConfigValue(CString name,int encoding, CString *GitPath, BOOL RemoveCR)
329 CString configValue;
330 int start = 0;
331 if(this->m_IsUseGitDLL)
333 CString *git_path=NULL;
337 CTGitPath path;
339 CheckAndInitDll();
340 git_path = GitPath;
342 }catch(...)
345 CStringA key, value;
346 key = CUnicodeUtils::GetMulti(name, encoding);
347 CStringA p;
348 if(git_path)
349 p=CUnicodeUtils::GetMulti(*GitPath,CP_ACP);
351 if(git_get_config(key.GetBuffer(), value.GetBufferSetLength(4096), 4096, p.GetBuffer()))
352 return CString();
353 else
355 g_Git.StringAppend(&configValue,(BYTE*)value.GetBuffer(),encoding);
356 if(RemoveCR)
357 return configValue.Tokenize(_T("\n"),start);
358 return configValue;
361 }else
363 CString cmd;
364 cmd.Format(L"git.exe config %s", name);
365 Run(cmd,&configValue,encoding);
366 if(RemoveCR)
367 return configValue.Tokenize(_T("\n"),start);
368 return configValue;
372 int CGit::SetConfigValue(CString key, CString value, CONFIG_TYPE type, int encoding, CString *GitPath)
374 if(this->m_IsUseGitDLL)
378 CheckAndInitDll();
380 }catch(...)
383 CStringA keya, valuea;
384 keya = CUnicodeUtils::GetMulti(key, CP_UTF8);
385 valuea = CUnicodeUtils::GetMulti(value, encoding);
386 CStringA p;
387 if(GitPath)
388 p=CUnicodeUtils::GetMulti(*GitPath,CP_ACP);
390 return get_set_config(keya.GetBuffer(), valuea.GetBuffer(), type, p.GetBuffer());
392 }else
394 CString cmd;
395 CString option;
396 switch(type)
398 case CONFIG_GLOBAL:
399 option = _T("--global");
400 break;
401 case CONFIG_SYSTEM:
402 option = _T("--system");
403 break;
404 default:
405 break;
407 cmd.Format(_T("git.exe config %s %s \"%s\""), option, key, value);
408 CString out;
409 if(Run(cmd,&out,encoding))
411 return -1;
414 return 0;
418 CString CGit::GetCurrentBranch(void)
420 CString output;
421 //Run(_T("git.exe branch"),&branch);
423 if(this->GetCurrentBranchFromFile(this->m_CurrentDir,output))
425 return _T("(no branch)");
426 }else
427 return output;
431 CString CGit::GetSymbolicRef(const wchar_t* symbolicRefName, bool bStripRefsHeads)
433 CString refName;
434 if(this->m_IsUseGitDLL)
436 unsigned char sha1[20];
437 int flag;
439 const char *refs_heads_master = git_resolve_ref(CUnicodeUtils::GetUTF8(CString(symbolicRefName)), sha1, 0, &flag);
440 if(refs_heads_master && (flag&REF_ISSYMREF))
442 g_Git.StringAppend(&refName,(BYTE*)refs_heads_master);
443 if(bStripRefsHeads)
444 refName = StripRefName(refName);
447 }else
449 CString cmd;
450 cmd.Format(L"git symbolic-ref %s", symbolicRefName);
451 if(Run(cmd, &refName, CP_UTF8) != 0)
452 return CString();//Error
453 int iStart = 0;
454 refName = refName.Tokenize(L"\n", iStart);
455 if(bStripRefsHeads)
456 refName = StripRefName(refName);
458 return refName;
461 CString CGit::GetFullRefName(CString shortRefName)
463 CString refName;
464 CString cmd;
465 cmd.Format(L"git rev-parse --symbolic-full-name %s", shortRefName);
466 if(Run(cmd, &refName, CP_UTF8) != 0)
467 return CString();//Error
468 int iStart = 0;
469 return refName.Tokenize(L"\n", iStart);
472 CString CGit::StripRefName(CString refName)
474 if(wcsncmp(refName, L"refs/heads/", 11) == 0)
475 refName = refName.Mid(11);
476 else if(wcsncmp(refName, L"refs/", 5) == 0)
477 refName = refName.Mid(5);
478 int start =0;
479 return refName.Tokenize(_T("\n"),start);
482 int CGit::GetCurrentBranchFromFile(const CString &sProjectRoot, CString &sBranchOut)
484 // read current branch name like git-gui does, by parsing the .git/HEAD file directly
486 if ( sProjectRoot.IsEmpty() )
487 return -1;
489 CString sHeadFile = sProjectRoot + _T("\\") + g_GitAdminDir.GetAdminDirName() + _T("\\HEAD");
491 FILE *pFile;
492 _tfopen_s(&pFile, sHeadFile.GetString(), _T("r"));
494 if (!pFile)
496 return -1;
499 char s[256] = {0};
500 fgets(s, sizeof(s), pFile);
502 fclose(pFile);
504 const char *pfx = "ref: refs/heads/";
505 const int len = 16;//strlen(pfx)
507 if ( !strncmp(s, pfx, len) )
509 //# We're on a branch. It might not exist. But
510 //# HEAD looks good enough to be a branch.
511 sBranchOut = s + len;
512 sBranchOut.TrimRight(_T(" \r\n\t"));
514 if ( sBranchOut.IsEmpty() )
515 return -1;
517 else
519 //# Assume this is a detached head.
520 sBranchOut = "HEAD";
522 return 1;
525 return 0;
528 int CGit::BuildOutputFormat(CString &format,bool IsFull)
530 CString log;
531 log.Format(_T("#<%c>%%x00"),LOG_REV_ITEM_BEGIN);
532 format += log;
533 if(IsFull)
535 log.Format(_T("#<%c>%%an%%x00"),LOG_REV_AUTHOR_NAME);
536 format += log;
537 log.Format(_T("#<%c>%%ae%%x00"),LOG_REV_AUTHOR_EMAIL);
538 format += log;
539 log.Format(_T("#<%c>%%ai%%x00"),LOG_REV_AUTHOR_DATE);
540 format += log;
541 log.Format(_T("#<%c>%%cn%%x00"),LOG_REV_COMMIT_NAME);
542 format += log;
543 log.Format(_T("#<%c>%%ce%%x00"),LOG_REV_COMMIT_EMAIL);
544 format += log;
545 log.Format(_T("#<%c>%%ci%%x00"),LOG_REV_COMMIT_DATE);
546 format += log;
547 log.Format(_T("#<%c>%%b%%x00"),LOG_REV_COMMIT_BODY);
548 format += log;
551 log.Format(_T("#<%c>%%m%%H%%x00"),LOG_REV_COMMIT_HASH);
552 format += log;
553 log.Format(_T("#<%c>%%P%%x00"),LOG_REV_COMMIT_PARENT);
554 format += log;
555 log.Format(_T("#<%c>%%s%%x00"),LOG_REV_COMMIT_SUBJECT);
556 format += log;
558 if(IsFull)
560 log.Format(_T("#<%c>%%x00"),LOG_REV_COMMIT_FILE);
561 format += log;
563 return 0;
566 int CGit::GetLog(BYTE_VECTOR& logOut, CString &hash, CTGitPath *path ,int count,int mask,CString *from,CString *to)
568 CGitCall_ByteVector gitCall(CString(),&logOut);
569 return GetLog(&gitCall,hash,path,count,mask,from,to);
572 CString CGit::GetLogCmd( CString &hash, CTGitPath *path, int count, int mask,CString *from,CString *to,bool paramonly,
573 CFilterData *Filter)
575 CString cmd;
576 CString log;
577 CString num;
578 CString since;
580 CString file;
582 if(path)
583 file.Format(_T(" -- \"%s\""),path->GetGitPathString());
585 if(count>0)
586 num.Format(_T("-n%d"),count);
588 CString param;
590 if(mask& LOG_INFO_STAT )
591 param += _T(" --numstat ");
592 if(mask& LOG_INFO_FILESTATE)
593 param += _T(" --raw ");
595 if(mask& LOG_INFO_FULLHISTORY)
596 param += _T(" --full-history ");
598 if(mask& LOG_INFO_BOUNDARY)
599 param += _T(" --left-right --boundary ");
601 if(mask& CGit::LOG_INFO_ALL_BRANCH)
602 param += _T(" --all ");
604 if(mask& CGit::LOG_INFO_DETECT_COPYRENAME)
605 param += _T(" -C ");
607 if(mask& CGit::LOG_INFO_DETECT_RENAME )
608 param += _T(" -M ");
610 if(mask& CGit::LOG_INFO_FIRST_PARENT )
611 param += _T(" --first-parent ");
613 if(mask& CGit::LOG_INFO_NO_MERGE )
614 param += _T(" --no-merges ");
616 if(mask& CGit::LOG_INFO_FOLLOW)
617 param += _T(" --follow ");
619 if(mask& CGit::LOG_INFO_SHOW_MERGEDFILE)
620 param += _T(" -c ");
622 if(mask& CGit::LOG_INFO_FULL_DIFF)
623 param += _T(" --full-diff ");
625 if(from != NULL && to != NULL)
627 CString range;
628 range.Format(_T(" %s..%s "),*from,*to);
629 param += range;
631 param+=hash;
633 CString st1,st2;
635 if( Filter && (Filter->m_From != -1))
637 st1.Format(_T(" --max-age=%I64u "), Filter->m_From);
638 param += st1;
641 if( Filter && (Filter->m_To != -1))
643 st2.Format(_T(" --min-age=%I64u "), Filter->m_To);
644 param += st2;
647 bool isgrep = false;
648 if( Filter && (!Filter->m_Author.IsEmpty()))
650 st1.Format(_T(" --author=%s" ),Filter->m_Author);
651 param += st1;
652 isgrep = true;
655 if( Filter && (!Filter->m_Committer.IsEmpty()))
657 st1.Format(_T(" --committer=%s" ),Filter->m_Author);
658 param += st1;
659 isgrep = true;
662 if( Filter && (!Filter->m_MessageFilter.IsEmpty()))
664 st1.Format(_T(" --grep=%s" ),Filter->m_MessageFilter);
665 param += st1;
666 isgrep = true;
669 if(Filter && isgrep)
671 if(!Filter->m_IsRegex)
672 param+=_T(" --fixed-strings ");
674 param += _T(" --regexp-ignore-case --extended-regexp ");
677 if(paramonly)
678 cmd.Format(_T("%s -z %s --parents "),
679 num,param);
680 else
681 cmd.Format(_T("git.exe log %s -z %s --parents --pretty=format:\""),
682 num,param);
684 BuildOutputFormat(log,!(mask&CGit::LOG_INFO_ONLY_HASH));
686 if(paramonly)
688 cmd += _T("-- ")+file;
689 }else
691 cmd += log;
692 cmd += CString(_T("\" -- "))+file;
695 return cmd;
697 //int CGit::GetLog(CGitCall* pgitCall, CString &hash, CTGitPath *path ,int count,int mask)
698 int CGit::GetLog(CGitCall* pgitCall, CString &hash, CTGitPath *path, int count, int mask,CString *from,CString *to)
700 pgitCall->SetCmd( GetLogCmd(hash,path,count,mask,from,to) );
702 return Run(pgitCall);
703 // return Run(cmd,&logOut);
706 #define BUFSIZE 512
707 void GetTempPath(CString &path)
709 TCHAR lpPathBuffer[BUFSIZE];
710 DWORD dwRetVal;
711 DWORD dwBufSize=BUFSIZE;
712 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
713 lpPathBuffer); // buffer for path
714 if (dwRetVal > dwBufSize || (dwRetVal == 0))
716 path=_T("");
718 path.Format(_T("%s"),lpPathBuffer);
720 CString GetTempFile()
722 TCHAR lpPathBuffer[BUFSIZE];
723 DWORD dwRetVal;
724 DWORD dwBufSize=BUFSIZE;
725 TCHAR szTempName[BUFSIZE];
726 UINT uRetVal;
728 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
729 lpPathBuffer); // buffer for path
730 if (dwRetVal > dwBufSize || (dwRetVal == 0))
732 return _T("");
734 // Create a temporary file.
735 uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files
736 TEXT("Patch"), // temp file name prefix
737 0, // create unique name
738 szTempName); // buffer for name
741 if (uRetVal == 0)
743 return _T("");
746 return CString(szTempName);
750 int CGit::RunLogFile(CString cmd,CString &filename)
752 STARTUPINFO si;
753 PROCESS_INFORMATION pi;
754 si.cb=sizeof(STARTUPINFO);
755 GetStartupInfo(&si);
757 SECURITY_ATTRIBUTES psa={sizeof(psa),NULL,TRUE};;
758 psa.bInheritHandle=TRUE;
760 HANDLE houtfile=CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
761 &psa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
763 si.wShowWindow = SW_HIDE;
764 si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
765 si.hStdOutput = houtfile;
767 LPTSTR pEnv = m_Environment.size()? &m_Environment[0]: NULL;
768 DWORD dwFlags = pEnv ? CREATE_UNICODE_ENVIRONMENT : 0;
770 if(cmd.Find(_T("git")) == 0)
771 cmd=CGit::ms_LastMsysGitDir+_T("\\")+cmd;
773 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,dwFlags,pEnv,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
775 LPVOID lpMsgBuf;
776 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
777 NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
778 (LPTSTR)&lpMsgBuf,
779 0,NULL);
780 return GIT_ERROR_CREATE_PROCESS;
783 WaitForSingleObject(pi.hProcess,INFINITE);
785 CloseHandle(pi.hThread);
786 CloseHandle(pi.hProcess);
787 CloseHandle(houtfile);
788 return GIT_SUCCESS;
789 // return 0;
792 CGitHash CGit::GetHash(TCHAR* friendname)
794 if(this->m_IsUseGitDLL)
796 this->CheckAndInitDll();
798 CGitHash hash;
799 CStringA ref;
800 ref = CUnicodeUtils::GetMulti(friendname,CP_ACP);
803 git_get_sha1(ref, hash.m_hash);
805 }catch(...)
808 return hash;
810 }else
812 CString cmd;
813 CString out;
814 cmd.Format(_T("git.exe rev-parse %s" ),friendname);
815 Run(cmd,&out,CP_UTF8);
816 // int pos=out.ReverseFind(_T('\n'));
817 int pos=out.FindOneOf(_T("\r\n"));
818 return CGitHash(out);
822 int CGit::GetInitAddList(CTGitPathList &outputlist)
824 CString cmd;
825 BYTE_VECTOR cmdout;
827 cmd=_T("git.exe ls-files -s -t -z");
828 outputlist.Clear();
829 if(g_Git.Run(cmd,&cmdout))
830 return -1;
832 outputlist.ParserFromLsFile(cmdout);
833 for(int i=0;i<outputlist.GetCount();i++)
834 ((int)outputlist[i].m_Action) = CTGitPath::LOGACTIONS_ADDED;
836 return 0;
838 int CGit::GetCommitDiffList(CString &rev1,CString &rev2,CTGitPathList &outputlist)
840 CString cmd;
842 if(rev1 == GIT_REV_ZERO || rev2 == GIT_REV_ZERO)
844 //rev1=+_T("");
845 if(rev1 == GIT_REV_ZERO)
846 cmd.Format(_T("git.exe diff -r --raw -C -M --numstat -z %s"),rev2);
847 else
848 cmd.Format(_T("git.exe diff -r -R --raw -C -M --numstat -z %s"),rev1);
849 }else
851 cmd.Format(_T("git.exe diff-tree -r --raw -C -M --numstat -z %s %s"),rev2,rev1);
854 BYTE_VECTOR out;
855 if(g_Git.Run(cmd,&out))
856 return -1;
858 outputlist.ParserFromLog(out);
860 return 0;
863 int addto_list_each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
865 STRING_VECTOR *list = (STRING_VECTOR*)cb_data;
866 CString str;
867 g_Git.StringAppend(&str,(BYTE*)refname,CP_ACP);
868 list->push_back(str);
869 return 0;
872 int CGit::GetTagList(STRING_VECTOR &list)
874 int ret;
876 if(this->m_IsUseGitDLL)
878 return git_for_each_ref_in("refs/tags/",addto_list_each_ref_fn, &list);
880 }else
882 CString cmd,output;
883 cmd=_T("git.exe tag -l");
884 int i=0;
885 ret=g_Git.Run(cmd,&output,CP_UTF8);
886 if(!ret)
888 int pos=0;
889 CString one;
890 while( pos>=0 )
892 i++;
893 one=output.Tokenize(_T("\n"),pos);
894 list.push_back(one);
898 return ret;
901 int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)
903 int ret;
904 CString cmd,output;
905 cmd=_T("git.exe branch --no-color");
907 if(type==(BRANCH_LOCAL|BRANCH_REMOTE))
908 cmd+=_T(" -a");
909 else if(type==BRANCH_REMOTE)
910 cmd+=_T(" -r");
912 int i=0;
913 ret=g_Git.Run(cmd,&output,CP_UTF8);
914 if(!ret)
916 int pos=0;
917 CString one;
918 while( pos>=0 )
920 one=output.Tokenize(_T("\n"),pos);
921 one.Trim(L" \r\n\t");
922 if(one.Find(L" -> ") >= 0 || one.IsEmpty())
923 continue; // skip something like: refs/origin/HEAD -> refs/origin/master
924 if(one[0] == _T('*'))
926 if(current)
927 *current=i;
928 one = one.Mid(2);
930 list.push_back(one);
931 i++;
934 return ret;
937 int CGit::GetRemoteList(STRING_VECTOR &list)
939 int ret;
940 CString cmd,output;
941 cmd=_T("git.exe remote");
942 ret=g_Git.Run(cmd,&output,CP_UTF8);
943 if(!ret)
945 int pos=0;
946 CString one;
947 while( pos>=0 )
949 one=output.Tokenize(_T("\n"),pos);
950 list.push_back(one);
953 return ret;
956 int CGit::GetRefList(STRING_VECTOR &list)
958 int ret;
959 if(this->m_IsUseGitDLL)
961 return git_for_each_ref_in("",addto_list_each_ref_fn, &list);
963 }else
965 CString cmd,output;
966 cmd=_T("git.exe show-ref -d");
967 ret=g_Git.Run(cmd,&output,CP_UTF8);
968 if(!ret)
970 int pos=0;
971 CString one;
972 while( pos>=0 )
974 one=output.Tokenize(_T("\n"),pos);
975 int start=one.Find(_T(" "),0);
976 if(start>0)
978 CString name;
979 name=one.Right(one.GetLength()-start-1);
980 list.push_back(name);
985 return ret;
988 int addto_map_each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
990 MAP_HASH_NAME *map = (MAP_HASH_NAME*)cb_data;
991 CString str;
992 g_Git.StringAppend(&str,(BYTE*)refname,CP_ACP);
993 CGitHash hash((char*)sha1);
995 (*map)[hash].push_back(str);
997 if(strncmp(refname, "refs/tags", 9) == 0)
999 GIT_HASH refhash;
1000 if(!git_deref_tag(sha1, refhash))
1002 (*map)[(char*)refhash].push_back(str+_T("^{}"));
1005 return 0;
1008 int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)
1010 int ret;
1011 if(this->m_IsUseGitDLL)
1013 return git_for_each_ref_in("",addto_map_each_ref_fn, &map);
1015 }else
1017 CString cmd,output;
1018 cmd=_T("git.exe show-ref -d");
1019 ret=g_Git.Run(cmd,&output,CP_UTF8);
1020 if(!ret)
1022 int pos=0;
1023 CString one;
1024 while( pos>=0 )
1026 one=output.Tokenize(_T("\n"),pos);
1027 int start=one.Find(_T(" "),0);
1028 if(start>0)
1030 CString name;
1031 name=one.Right(one.GetLength()-start-1);
1033 CString hash;
1034 hash=one.Left(start);
1036 map[CGitHash(hash)].push_back(name);
1041 return ret;
1044 BOOL CGit::CheckMsysGitDir()
1046 if (m_bInitialized)
1048 return TRUE;
1051 this->m_Environment.clear();
1052 m_Environment.CopyProcessEnvironment();
1054 TCHAR *oldpath,*home;
1055 size_t homesize,size,httpsize;
1057 // set HOME if not set already
1058 _tgetenv_s(&homesize, NULL, 0, _T("HOME"));
1059 if (!homesize)
1061 if ( (!_tdupenv_s(&home,&size,_T("USERPROFILE"))) && (home != NULL) )
1063 m_Environment.SetEnv(_T("HOME"),home);
1064 free(home);
1066 else
1068 ATLTRACE("CGit::CheckMsysGitDir Unable to SetEnv HOME\n");
1071 CString str;
1073 //set http_proxy
1074 _tgetenv_s(&httpsize, NULL, 0, _T("http_proxy"));
1075 if (!httpsize)
1077 CString regServeraddress_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-host"), _T(""));
1078 CString regServerport_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-port"), _T(""));
1079 CString regUsername_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-username"), _T(""));
1080 CString regPassword_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-password"), _T(""));
1081 CString regTimeout_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-timeout"), _T(""));
1082 CString regExceptions_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-exceptions"), _T(""));
1084 CString http_proxy;
1085 if(!regServeraddress_copy.IsEmpty())
1087 if(regServeraddress_copy.Left(4) != _T("http"))
1088 http_proxy=_T("http://");
1090 if(!regUsername_copy.IsEmpty())
1092 http_proxy += regUsername_copy;
1093 http_proxy += _T(":")+regPassword_copy;
1094 http_proxy += _T("@");
1096 http_proxy+=regServeraddress_copy;
1097 if(!regServerport_copy.IsEmpty())
1099 http_proxy +=_T(":")+regServerport_copy;
1101 m_Environment.SetEnv(_T("http_proxy"),(LPTSTR)http_proxy.GetBuffer());
1104 //setup ssh client
1105 CString sshclient=CRegString(_T("Software\\TortoiseGit\\SSH"));
1107 if(!sshclient.IsEmpty())
1109 m_Environment.SetEnv(_T("GIT_SSH"),sshclient.GetBuffer());
1111 //Setup SVN_SSH
1112 CString ssh=sshclient;
1113 ssh.Replace(_T("/"),_T("\\"));
1114 ssh.Replace(_T("\\"),_T("\\\\"));
1115 ssh=CString(_T("\""))+ssh+_T('\"');
1116 m_Environment.SetEnv(_T("SVN_SSH"),ssh.GetBuffer());
1118 }else
1120 TCHAR sPlink[MAX_PATH];
1121 GetModuleFileName(NULL, sPlink, _countof(sPlink));
1122 LPTSTR ptr = _tcsrchr(sPlink, _T('\\'));
1123 if (ptr) {
1124 _tcscpy(ptr + 1, _T("TortoisePlink.exe"));
1125 m_Environment.SetEnv(_T("GIT_SSH"), sPlink);
1127 //Setup SVN_SSH
1128 CString ssh=sPlink;
1129 ssh.Replace(_T("/"),_T("\\"));
1130 ssh.Replace(_T("\\"),_T("\\\\"));
1131 ssh=CString(_T("\""))+ssh+_T('\"');
1132 m_Environment.SetEnv(_T("SVN_SSH"),ssh.GetBuffer());
1137 TCHAR sAskPass[MAX_PATH];
1138 GetModuleFileName(NULL, sAskPass, _countof(sAskPass));
1139 LPTSTR ptr = _tcsrchr(sAskPass, _T('\\'));
1140 if (ptr)
1142 _tcscpy(ptr + 1, _T("SshAskPass.exe"));
1143 m_Environment.SetEnv(_T("DISPLAY"),_T(":9999"));
1144 m_Environment.SetEnv(_T("SSH_ASKPASS"),sAskPass);
1145 m_Environment.SetEnv(_T("GIT_ASKPASS"),sAskPass);
1149 // add git/bin path to PATH
1151 CRegString msysdir=CRegString(REG_MSYSGIT_PATH,_T(""),FALSE);
1152 str=msysdir;
1153 if(str.IsEmpty())
1155 CRegString msysinstalldir=CRegString(REG_MSYSGIT_INSTALL,_T(""),FALSE,HKEY_LOCAL_MACHINE);
1156 str=msysinstalldir;
1157 if ( !str.IsEmpty() )
1159 str += (str[str.GetLength()-1] != '\\') ? "\\bin" : "bin";
1160 msysdir=str;
1161 CGit::ms_LastMsysGitDir = str;
1162 msysdir.write();
1164 else
1166 // search PATH if git/bin directory is alredy present
1167 if ( FindGitPath() )
1169 m_bInitialized = TRUE;
1170 return TRUE;
1173 return false;
1175 }else
1177 CGit::ms_LastMsysGitDir = str;
1179 //set path
1181 _tdupenv_s(&oldpath,&size,_T("PATH"));
1183 CString path;
1184 path.Format(_T("%s;%s"),oldpath,str + _T(";")+ (CString)CRegString(REG_MSYSGIT_EXTRA_PATH,_T(""),FALSE));
1186 m_Environment.SetEnv(_T("PATH"),path.GetBuffer());
1188 CString str1 = m_Environment.GetEnv(_T("PATH"));
1190 CString sOldPath = oldpath;
1191 free(oldpath);
1193 m_bInitialized = TRUE;
1194 return true;
1198 class CGitCall_EnumFiles : public CGitCall
1200 public:
1201 CGitCall_EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)
1202 : m_pszProjectPath(pszProjectPath),
1203 m_pszSubPath(pszSubPath),
1204 m_nFlags(nFlags),
1205 m_pEnumCb(pEnumCb),
1206 m_pUserData(pUserData)
1210 typedef std::map<CStringA,char> TStrCharMap;
1212 const TCHAR * m_pszProjectPath;
1213 const TCHAR * m_pszSubPath;
1214 unsigned int m_nFlags;
1215 WGENUMFILECB * m_pEnumCb;
1216 void * m_pUserData;
1218 BYTE_VECTOR m_DataCollector;
1220 virtual bool OnOutputData(const BYTE* data, size_t size)
1222 m_DataCollector.append(data,size);
1223 while(true)
1225 // lines from igit.exe are 0 terminated
1226 int found=m_DataCollector.findData((const BYTE*)"",1);
1227 if(found<0)
1228 return false;
1229 OnSingleLine( (LPCSTR)&*m_DataCollector.begin() );
1230 m_DataCollector.erase(m_DataCollector.begin(), m_DataCollector.begin()+found+1);
1232 return false;//Should never reach this
1234 virtual void OnEnd()
1238 BYTE HexChar(char ch)
1240 if (ch >= '0' && ch <= '9')
1241 return (UINT)(ch - '0');
1242 else if (ch >= 'A' && ch <= 'F')
1243 return (UINT)(ch - 'A') + 10;
1244 else if (ch >= 'a' && ch <= 'f')
1245 return (UINT)(ch - 'a') + 10;
1246 else
1247 return 0;
1250 bool OnSingleLine(LPCSTR line)
1252 //Parse single line
1254 wgFile_s fileStatus;
1256 // file/dir type
1258 fileStatus.nFlags = 0;
1259 if (*line == 'D')
1260 fileStatus.nFlags |= WGFF_Directory;
1261 else if (*line != 'F')
1262 // parse error
1263 return false;
1264 line += 2;
1266 // status
1268 fileStatus.nStatus = WGFS_Unknown;
1269 switch (*line)
1271 case 'N': fileStatus.nStatus = WGFS_Normal; break;
1272 case 'M': fileStatus.nStatus = WGFS_Modified; break;
1273 case 'S': fileStatus.nStatus = WGFS_Staged; break;
1274 case 'A': fileStatus.nStatus = WGFS_Added; break;
1275 case 'C': fileStatus.nStatus = WGFS_Conflicted; break;
1276 case 'D': fileStatus.nStatus = WGFS_Deleted; break;
1277 case 'I': fileStatus.nStatus = WGFS_Ignored; break;
1278 case 'U': fileStatus.nStatus = WGFS_Unversioned; break;
1279 case 'E': fileStatus.nStatus = WGFS_Empty; break;
1280 case '?': fileStatus.nStatus = WGFS_Unknown; break;
1281 default:
1282 // parse error
1283 return false;
1285 line += 2;
1287 // file sha1
1289 BYTE sha1[20];
1290 fileStatus.sha1 = NULL;
1291 if ( !(fileStatus.nFlags & WGFF_Directory) )
1293 for (int i=0; i<20; i++)
1295 sha1[i] = (HexChar(line[0])<<4)&0xF0;
1296 sha1[i] |= HexChar(line[1])&0xF;
1298 line += 2;
1301 line++;
1304 // filename
1305 int len = strlen(line);
1306 if (len && len < 2048)
1308 WCHAR *buf = (WCHAR*)alloca((len*4+2)*sizeof(WCHAR));
1309 *buf = 0;
1310 MultiByteToWideChar(CP_ACP, 0, line, len+1, buf, len*4+1);
1311 fileStatus.sFileName = buf;
1313 if (*buf && (*m_pEnumCb)(&fileStatus,m_pUserData))
1314 return false;
1317 return true;
1321 BOOL CGit::EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)
1323 if(!pszProjectPath || *pszProjectPath=='\0')
1324 return FALSE;
1326 CGitCall_EnumFiles W_GitCall(pszProjectPath,pszSubPath,nFlags,pEnumCb,pUserData);
1327 CString cmd;
1329 /* char W_szToDir[MAX_PATH];
1330 strncpy(W_szToDir,pszProjectPath,sizeof(W_szToDir)-1);
1331 if(W_szToDir[strlen(W_szToDir)-1]!='\\')
1332 strncat(W_szToDir,"\\",sizeof(W_szToDir)-1);
1334 SetCurrentDirectoryA(W_szToDir);
1335 GetCurrentDirectoryA(sizeof(W_szToDir)-1,W_szToDir);
1337 SetCurrentDir(pszProjectPath);
1339 CString sMode;
1340 if (nFlags)
1342 if (nFlags & WGEFF_NoRecurse) sMode += _T("r");
1343 if (nFlags & WGEFF_FullPath) sMode += _T("f");
1344 if (nFlags & WGEFF_DirStatusDelta) sMode += _T("d");
1345 if (nFlags & WGEFF_DirStatusAll) sMode += _T("D");
1346 if (nFlags & WGEFF_EmptyAsNormal) sMode += _T("e");
1347 if (nFlags & WGEFF_SingleFile) sMode += _T("s");
1349 else
1351 sMode = _T("-");
1354 // NOTE: there seems to be some issue with msys based app receiving backslash on commandline, at least
1355 // if followed by " like for example 'igit "C:\"', the commandline igit receives is 'igit.exe C:" status' with
1356 // the 'C:" status' part as a single arg, Maybe it uses unix style processing. In order to avoid this just
1357 // use forward slashes for supplied project and sub paths
1359 CString sProjectPath = pszProjectPath;
1360 sProjectPath.Replace(_T('\\'), _T('/'));
1362 if (pszSubPath)
1364 CString sSubPath = pszSubPath;
1365 sSubPath.Replace(_T('\\'), _T('/'));
1367 cmd.Format(_T("tgit.exe statusex \"%s\" status %s \"%s\""), sProjectPath, sMode, sSubPath);
1369 else
1371 cmd.Format(_T("tgit.exe statusex \"%s\" status %s"), sProjectPath, sMode);
1374 //OutputDebugStringA("---");OutputDebugStringW(cmd);OutputDebugStringA("\r\n");
1376 W_GitCall.SetCmd(cmd);
1377 // NOTE: should igit get added as a part of msysgit then use below line instead of the above one
1378 //W_GitCall.SetCmd(CGit::ms_LastMsysGitDir + cmd);
1380 if ( Run(&W_GitCall) )
1381 return FALSE;
1383 return TRUE;
1386 BOOL CGit::CheckCleanWorkTree()
1388 CString out;
1389 CString cmd;
1390 cmd=_T("git.exe rev-parse --verify HEAD");
1392 if(g_Git.Run(cmd,&out,CP_UTF8))
1393 return FALSE;
1395 cmd=_T("git.exe update-index --ignore-submodules --refresh");
1396 if(g_Git.Run(cmd,&out,CP_UTF8))
1397 return FALSE;
1399 cmd=_T("git.exe diff-files --quiet --ignore-submodules");
1400 if(g_Git.Run(cmd,&out,CP_UTF8))
1401 return FALSE;
1403 cmd=_T("git diff-index --cached --quiet HEAD --ignore-submodules");
1404 if(g_Git.Run(cmd,&out,CP_UTF8))
1405 return FALSE;
1407 return TRUE;
1409 int CGit::Revert(CTGitPathList &list,bool keep)
1411 int ret;
1412 for(int i=0;i<list.GetCount();i++)
1414 ret = Revert((CTGitPath&)list[i],keep);
1415 if(ret)
1416 return ret;
1418 return 0;
1420 int CGit::Revert(CTGitPath &path,bool keep)
1422 CString cmd, out;
1424 if(path.m_Action & CTGitPath::LOGACTIONS_REPLACED )
1426 cmd.Format(_T("git.exe mv -- \"%s\" \"%s\""),path.GetGitPathString(),path.GetGitOldPathString());
1427 if(g_Git.Run(cmd,&out,CP_ACP))
1428 return -1;
1430 cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitOldPathString());
1431 if(g_Git.Run(cmd,&out,CP_ACP))
1432 return -1;
1434 }else if(path.m_Action & CTGitPath::LOGACTIONS_ADDED)
1435 { //To init git repository, there are not HEAD, so we can use git reset command
1436 cmd.Format(_T("git.exe rm --cached -- \"%s\""),path.GetGitPathString());
1438 if(g_Git.Run(cmd,&out,CP_ACP))
1439 return -1;
1441 else
1443 cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitPathString());
1444 if(g_Git.Run(cmd,&out,CP_ACP))
1445 return -1;
1447 return 0;
1450 int CGit::ListConflictFile(CTGitPathList &list,CTGitPath *path)
1452 BYTE_VECTOR vector;
1454 CString cmd;
1455 if(path)
1456 cmd.Format(_T("git.exe ls-files -u -t -z -- \"%s\""),path->GetGitPathString());
1457 else
1458 cmd=_T("git.exe ls-files -u -t -z");
1460 if(g_Git.Run(cmd,&vector))
1462 return -1;
1465 list.ParserFromLsFile(vector);
1467 return 0;
1470 bool CGit::IsFastForward(CString &from, CString &to)
1472 CString base;
1473 CGitHash basehash,hash;
1474 CString cmd;
1475 cmd.Format(_T("git.exe merge-base %s %s"), to,from);
1477 if(g_Git.Run(cmd,&base,CP_ACP))
1479 //CMessageBox::Show(NULL,base,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1480 return false;
1482 basehash = base.Left(40);
1484 hash=g_Git.GetHash(from.GetBuffer());
1486 return hash == basehash;
1489 unsigned int CGit::Hash2int(CGitHash &hash)
1491 int ret=0;
1492 for(int i=0;i<4;i++)
1494 ret = ret << 8;
1495 ret |= hash.m_hash[i];
1497 return ret;
1500 int CGit::RefreshGitIndex()
1502 if(g_Git.m_IsUseGitDLL)
1506 return git_run_cmd("update-index","update-index -q --refresh");
1508 }catch(...)
1510 return -1;
1513 }else
1515 CString cmd,output;
1516 cmd=_T("git.exe update-index --refresh");
1517 return Run(cmd,&output,CP_ACP);
1521 int CGit::GetOneFile(CString Refname, CTGitPath &path, CString &outputfile)
1523 if(g_Git.m_IsUseGitDLL)
1527 g_Git.CheckAndInitDll();
1528 CStringA ref, patha, outa;
1529 ref = CUnicodeUtils::GetMulti(Refname,CP_ACP);
1530 patha = CUnicodeUtils::GetMulti(path.GetGitPathString(), CP_ACP);
1531 outa = CUnicodeUtils::GetMulti(outputfile,CP_ACP);
1532 ::DeleteFile(outputfile);
1533 return git_checkout_file((const char*)ref.GetBuffer(),(const char*)patha.GetBuffer(),(const char*)outa.GetBuffer());
1535 }catch(...)
1537 return -1;
1539 }else
1541 CString cmd;
1542 cmd.Format(_T("git.exe cat-file -p %s:\"%s\""), Refname, path.GetGitPathString());
1543 return g_Git.RunLogFile(cmd,outputfile);
1546 void CEnvironment::CopyProcessEnvironment()
1548 TCHAR *p = GetEnvironmentStrings();
1549 while(*p !=0 || *(p+1) !=0)
1550 this->push_back(*p++);
1552 push_back(_T('\0'));
1553 push_back(_T('\0'));
1556 CString CEnvironment::GetEnv(TCHAR *name)
1558 CString str;
1559 for(int i=0;i<size();i++)
1561 str = &(*this)[i];
1562 int start =0;
1563 CString sname = str.Tokenize(_T("="),start);
1564 if(sname.CompareNoCase(name) == 0)
1566 return &(*this)[i+start+1];
1568 i+=str.GetLength();
1570 return _T("");
1573 void CEnvironment::SetEnv(TCHAR *name, TCHAR* value)
1575 int i;
1576 for( i=0;i<size();i++)
1578 CString str = &(*this)[i];
1579 int start =0;
1580 CString sname = str.Tokenize(_T("="),start);
1581 if(sname.CompareNoCase(name) == 0)
1583 break;
1585 i+=str.GetLength();
1588 if(i == size())
1590 i -= 1; // roll back terminate \0\0
1591 this->push_back(_T('\0'));
1594 CEnvironment::iterator it;
1595 it=this->begin();
1596 it += i;
1598 while(*it && i<size())
1600 this->erase(it);
1601 it=this->begin();
1602 it += i;
1605 while(*name)
1607 this->insert(it,*name++);
1608 i++;
1609 it= begin()+i;
1612 this->insert(it, _T('='));
1613 i++;
1614 it= begin()+i;
1616 while(*value)
1618 this->insert(it,*value++);
1619 i++;
1620 it= begin()+i;
1625 int CGit::GetGitEncode(TCHAR* configkey)
1627 CString str=GetConfigValue(configkey);
1629 if(str.IsEmpty())
1630 return CP_UTF8;
1632 return CUnicodeUtils::GetCPCode(str);