fixed compiler warnings caused by typos
[TortoiseGit.git] / src / Git / Git.cpp
blob00e18fd81158765448dd3b9356651c133c188766
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 struct 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 git_revnum_t CGit::GetHash(const CString &friendname)
794 CString cmd;
795 CString out;
796 cmd.Format(_T("git.exe rev-parse %s" ),friendname);
797 Run(cmd,&out,CP_UTF8);
798 // int pos=out.ReverseFind(_T('\n'));
799 int pos=out.FindOneOf(_T("\r\n"));
800 if(pos>0)
801 return out.Left(pos);
802 return out;
805 int CGit::GetInitAddList(CTGitPathList &outputlist)
807 CString cmd;
808 BYTE_VECTOR cmdout;
810 cmd=_T("git.exe ls-files -s -t -z");
811 outputlist.Clear();
812 if(g_Git.Run(cmd,&cmdout))
813 return -1;
815 outputlist.ParserFromLsFile(cmdout);
816 for(int i=0;i<outputlist.GetCount();i++)
817 ((int)outputlist[i].m_Action) = CTGitPath::LOGACTIONS_ADDED;
819 return 0;
821 int CGit::GetCommitDiffList(CString &rev1,CString &rev2,CTGitPathList &outputlist)
823 CString cmd;
825 if(rev1 == GIT_REV_ZERO || rev2 == GIT_REV_ZERO)
827 //rev1=+_T("");
828 if(rev1 == GIT_REV_ZERO)
829 cmd.Format(_T("git.exe diff -r --raw -C -M --numstat -z %s"),rev2);
830 else
831 cmd.Format(_T("git.exe diff -r -R --raw -C -M --numstat -z %s"),rev1);
832 }else
834 cmd.Format(_T("git.exe diff-tree -r --raw -C -M --numstat -z %s %s"),rev2,rev1);
837 BYTE_VECTOR out;
838 if(g_Git.Run(cmd,&out))
839 return -1;
841 outputlist.ParserFromLog(out);
843 return 0;
846 int addto_list_each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
848 STRING_VECTOR *list = (STRING_VECTOR*)cb_data;
849 CString str;
850 g_Git.StringAppend(&str,(BYTE*)refname,CP_ACP);
851 list->push_back(str);
852 return 0;
855 int CGit::GetTagList(STRING_VECTOR &list)
857 int ret;
859 if(this->m_IsUseGitDLL)
861 return git_for_each_ref_in("refs/tags/",addto_list_each_ref_fn, &list);
863 }else
865 CString cmd,output;
866 cmd=_T("git.exe tag -l");
867 int i=0;
868 ret=g_Git.Run(cmd,&output,CP_UTF8);
869 if(!ret)
871 int pos=0;
872 CString one;
873 while( pos>=0 )
875 i++;
876 one=output.Tokenize(_T("\n"),pos);
877 list.push_back(one);
881 return ret;
884 int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)
886 int ret;
887 CString cmd,output;
888 cmd=_T("git.exe branch --no-color");
890 if(type==(BRANCH_LOCAL|BRANCH_REMOTE))
891 cmd+=_T(" -a");
892 else if(type==BRANCH_REMOTE)
893 cmd+=_T(" -r");
895 int i=0;
896 ret=g_Git.Run(cmd,&output,CP_UTF8);
897 if(!ret)
899 int pos=0;
900 CString one;
901 while( pos>=0 )
903 one=output.Tokenize(_T("\n"),pos);
904 one.Trim(L" \r\n\t");
905 if(one.Find(L" -> ") >= 0 || one.IsEmpty())
906 continue; // skip something like: refs/origin/HEAD -> refs/origin/master
907 if(one[0] == _T('*'))
909 if(current)
910 *current=i;
911 one = one.Mid(2);
913 list.push_back(one);
914 i++;
917 return ret;
920 int CGit::GetRemoteList(STRING_VECTOR &list)
922 int ret;
923 CString cmd,output;
924 cmd=_T("git.exe remote");
925 ret=g_Git.Run(cmd,&output,CP_UTF8);
926 if(!ret)
928 int pos=0;
929 CString one;
930 while( pos>=0 )
932 one=output.Tokenize(_T("\n"),pos);
933 list.push_back(one);
936 return ret;
939 int CGit::GetRefList(STRING_VECTOR &list)
941 int ret;
942 if(this->m_IsUseGitDLL)
944 return git_for_each_ref_in("",addto_list_each_ref_fn, &list);
946 }else
948 CString cmd,output;
949 cmd=_T("git.exe show-ref -d");
950 ret=g_Git.Run(cmd,&output,CP_UTF8);
951 if(!ret)
953 int pos=0;
954 CString one;
955 while( pos>=0 )
957 one=output.Tokenize(_T("\n"),pos);
958 int start=one.Find(_T(" "),0);
959 if(start>0)
961 CString name;
962 name=one.Right(one.GetLength()-start-1);
963 list.push_back(name);
968 return ret;
971 int addto_map_each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
973 MAP_HASH_NAME *map = (MAP_HASH_NAME*)cb_data;
974 CString str;
975 g_Git.StringAppend(&str,(BYTE*)refname,CP_ACP);
976 CGitHash hash((char*)sha1);
978 (*map)[hash].push_back(str);
980 const char *hex = NULL;
981 if(strncmp(refname, "refs/tags", 9) == 0)
983 GIT_HASH refhash;
984 if(!git_deref_tag(sha1, refhash))
986 (*map)[(char*)refhash].push_back(str+_T("^{}"));
989 return 0;
992 int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)
994 int ret;
995 if(this->m_IsUseGitDLL)
997 return git_for_each_ref_in("",addto_map_each_ref_fn, &map);
999 }else
1001 CString cmd,output;
1002 cmd=_T("git.exe show-ref -d");
1003 ret=g_Git.Run(cmd,&output,CP_UTF8);
1004 if(!ret)
1006 int pos=0;
1007 CString one;
1008 while( pos>=0 )
1010 one=output.Tokenize(_T("\n"),pos);
1011 int start=one.Find(_T(" "),0);
1012 if(start>0)
1014 CString name;
1015 name=one.Right(one.GetLength()-start-1);
1017 CString hash;
1018 hash=one.Left(start);
1020 map[CGitHash(hash)].push_back(name);
1025 return ret;
1028 BOOL CGit::CheckMsysGitDir()
1030 if (m_bInitialized)
1032 return TRUE;
1035 this->m_Environment.clear();
1036 m_Environment.CopyProcessEnvironment();
1038 TCHAR *oldpath,*home;
1039 size_t homesize,size,httpsize;
1041 // set HOME if not set already
1042 _tgetenv_s(&homesize, NULL, 0, _T("HOME"));
1043 if (!homesize)
1045 if ( (!_tdupenv_s(&home,&size,_T("USERPROFILE"))) && (home != NULL) )
1047 m_Environment.SetEnv(_T("HOME"),home);
1048 free(home);
1050 else
1052 ATLTRACE("CGit::CheckMsysGitDir Unable to SetEnv HOME\n");
1055 CString str;
1057 //set http_proxy
1058 _tgetenv_s(&httpsize, NULL, 0, _T("http_proxy"));
1059 if (!httpsize)
1061 CString regServeraddress_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-host"), _T(""));
1062 CString regServerport_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-port"), _T(""));
1063 CString regUsername_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-username"), _T(""));
1064 CString regPassword_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-password"), _T(""));
1065 CString regTimeout_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-timeout"), _T(""));
1066 CString regExceptions_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-exceptions"), _T(""));
1068 CString http_proxy;
1069 if(!regServeraddress_copy.IsEmpty())
1071 if(regServeraddress_copy.Left(4) != _T("http"))
1072 http_proxy=_T("http://");
1074 if(!regUsername_copy.IsEmpty())
1076 http_proxy += regUsername_copy;
1077 http_proxy += _T(":")+regPassword_copy;
1078 http_proxy += _T("@");
1080 http_proxy+=regServeraddress_copy;
1081 if(!regServerport_copy.IsEmpty())
1083 http_proxy +=_T(":")+regServerport_copy;
1085 m_Environment.SetEnv(_T("http_proxy"),(LPTSTR)http_proxy.GetBuffer());
1088 //setup ssh client
1089 CString sshclient=CRegString(_T("Software\\TortoiseGit\\SSH"));
1091 if(!sshclient.IsEmpty())
1093 m_Environment.SetEnv(_T("GIT_SSH"),sshclient.GetBuffer());
1095 //Setup SVN_SSH
1096 CString ssh=sshclient;
1097 ssh.Replace(_T("/"),_T("\\"));
1098 ssh.Replace(_T("\\"),_T("\\\\"));
1099 ssh=CString(_T("\""))+ssh+_T('\"');
1100 m_Environment.SetEnv(_T("SVN_SSH"),ssh.GetBuffer());
1102 }else
1104 TCHAR sPlink[MAX_PATH];
1105 GetModuleFileName(NULL, sPlink, _countof(sPlink));
1106 LPTSTR ptr = _tcsrchr(sPlink, _T('\\'));
1107 if (ptr) {
1108 _tcscpy(ptr + 1, _T("TortoisePlink.exe"));
1109 m_Environment.SetEnv(_T("GIT_SSH"), sPlink);
1111 //Setup SVN_SSH
1112 CString ssh=sPlink;
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());
1121 TCHAR sAskPass[MAX_PATH];
1122 GetModuleFileName(NULL, sAskPass, _countof(sAskPass));
1123 LPTSTR ptr = _tcsrchr(sAskPass, _T('\\'));
1124 if (ptr)
1126 _tcscpy(ptr + 1, _T("SshAskPass.exe"));
1127 m_Environment.SetEnv(_T("DISPLAY"),_T(":9999"));
1128 m_Environment.SetEnv(_T("SSH_ASKPASS"),sAskPass);
1129 m_Environment.SetEnv(_T("GIT_ASKPASS"),sAskPass);
1133 // add git/bin path to PATH
1135 CRegString msysdir=CRegString(REG_MSYSGIT_PATH,_T(""),FALSE);
1136 str=msysdir;
1137 if(str.IsEmpty())
1139 CRegString msysinstalldir=CRegString(REG_MSYSGIT_INSTALL,_T(""),FALSE,HKEY_LOCAL_MACHINE);
1140 str=msysinstalldir;
1141 if ( !str.IsEmpty() )
1143 str += (str[str.GetLength()-1] != '\\') ? "\\bin" : "bin";
1144 msysdir=str;
1145 CGit::ms_LastMsysGitDir = str;
1146 msysdir.write();
1148 else
1150 // search PATH if git/bin directory is alredy present
1151 if ( FindGitPath() )
1153 m_bInitialized = TRUE;
1154 return TRUE;
1157 return false;
1159 }else
1161 CGit::ms_LastMsysGitDir = str;
1163 //set path
1165 _tdupenv_s(&oldpath,&size,_T("PATH"));
1167 CString path;
1168 path.Format(_T("%s;%s"),oldpath,str + _T(";")+ (CString)CRegString(REG_MSYSGIT_EXTRA_PATH,_T(""),FALSE));
1170 m_Environment.SetEnv(_T("PATH"),path.GetBuffer());
1172 CString str1 = m_Environment.GetEnv(_T("PATH"));
1174 CString sOldPath = oldpath;
1175 free(oldpath);
1177 m_bInitialized = TRUE;
1178 return true;
1182 class CGitCall_EnumFiles : public CGitCall
1184 public:
1185 CGitCall_EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)
1186 : m_pszProjectPath(pszProjectPath),
1187 m_pszSubPath(pszSubPath),
1188 m_nFlags(nFlags),
1189 m_pEnumCb(pEnumCb),
1190 m_pUserData(pUserData)
1194 typedef std::map<CStringA,char> TStrCharMap;
1196 const TCHAR * m_pszProjectPath;
1197 const TCHAR * m_pszSubPath;
1198 unsigned int m_nFlags;
1199 WGENUMFILECB * m_pEnumCb;
1200 void * m_pUserData;
1202 BYTE_VECTOR m_DataCollector;
1204 virtual bool OnOutputData(const BYTE* data, size_t size)
1206 m_DataCollector.append(data,size);
1207 while(true)
1209 // lines from igit.exe are 0 terminated
1210 int found=m_DataCollector.findData((const BYTE*)"",1);
1211 if(found<0)
1212 return false;
1213 OnSingleLine( (LPCSTR)&*m_DataCollector.begin() );
1214 m_DataCollector.erase(m_DataCollector.begin(), m_DataCollector.begin()+found+1);
1216 return false;//Should never reach this
1218 virtual void OnEnd()
1222 BYTE HexChar(char ch)
1224 if (ch >= '0' && ch <= '9')
1225 return (UINT)(ch - '0');
1226 else if (ch >= 'A' && ch <= 'F')
1227 return (UINT)(ch - 'A') + 10;
1228 else if (ch >= 'a' && ch <= 'f')
1229 return (UINT)(ch - 'a') + 10;
1230 else
1231 return 0;
1234 bool OnSingleLine(LPCSTR line)
1236 //Parse single line
1238 wgFile_s fileStatus;
1240 // file/dir type
1242 fileStatus.nFlags = 0;
1243 if (*line == 'D')
1244 fileStatus.nFlags |= WGFF_Directory;
1245 else if (*line != 'F')
1246 // parse error
1247 return false;
1248 line += 2;
1250 // status
1252 fileStatus.nStatus = WGFS_Unknown;
1253 switch (*line)
1255 case 'N': fileStatus.nStatus = WGFS_Normal; break;
1256 case 'M': fileStatus.nStatus = WGFS_Modified; break;
1257 case 'S': fileStatus.nStatus = WGFS_Staged; break;
1258 case 'A': fileStatus.nStatus = WGFS_Added; break;
1259 case 'C': fileStatus.nStatus = WGFS_Conflicted; break;
1260 case 'D': fileStatus.nStatus = WGFS_Deleted; break;
1261 case 'I': fileStatus.nStatus = WGFS_Ignored; break;
1262 case 'U': fileStatus.nStatus = WGFS_Unversioned; break;
1263 case 'E': fileStatus.nStatus = WGFS_Empty; break;
1264 case '?': fileStatus.nStatus = WGFS_Unknown; break;
1265 default:
1266 // parse error
1267 return false;
1269 line += 2;
1271 // file sha1
1273 BYTE sha1[20];
1274 fileStatus.sha1 = NULL;
1275 if ( !(fileStatus.nFlags & WGFF_Directory) )
1277 for (int i=0; i<20; i++)
1279 sha1[i] = (HexChar(line[0])<<4)&0xF0;
1280 sha1[i] |= HexChar(line[1])&0xF;
1282 line += 2;
1285 line++;
1288 // filename
1289 int len = strlen(line);
1290 if (len && len < 2048)
1292 WCHAR *buf = (WCHAR*)alloca((len*4+2)*sizeof(WCHAR));
1293 *buf = 0;
1294 MultiByteToWideChar(CP_ACP, 0, line, len+1, buf, len*4+1);
1295 fileStatus.sFileName = buf;
1297 if (*buf && (*m_pEnumCb)(&fileStatus,m_pUserData))
1298 return false;
1301 return true;
1305 BOOL CGit::EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)
1307 if(!pszProjectPath || *pszProjectPath=='\0')
1308 return FALSE;
1310 CGitCall_EnumFiles W_GitCall(pszProjectPath,pszSubPath,nFlags,pEnumCb,pUserData);
1311 CString cmd;
1313 /* char W_szToDir[MAX_PATH];
1314 strncpy(W_szToDir,pszProjectPath,sizeof(W_szToDir)-1);
1315 if(W_szToDir[strlen(W_szToDir)-1]!='\\')
1316 strncat(W_szToDir,"\\",sizeof(W_szToDir)-1);
1318 SetCurrentDirectoryA(W_szToDir);
1319 GetCurrentDirectoryA(sizeof(W_szToDir)-1,W_szToDir);
1321 SetCurrentDir(pszProjectPath);
1323 CString sMode;
1324 if (nFlags)
1326 if (nFlags & WGEFF_NoRecurse) sMode += _T("r");
1327 if (nFlags & WGEFF_FullPath) sMode += _T("f");
1328 if (nFlags & WGEFF_DirStatusDelta) sMode += _T("d");
1329 if (nFlags & WGEFF_DirStatusAll) sMode += _T("D");
1330 if (nFlags & WGEFF_EmptyAsNormal) sMode += _T("e");
1331 if (nFlags & WGEFF_SingleFile) sMode += _T("s");
1333 else
1335 sMode = _T("-");
1338 // NOTE: there seems to be some issue with msys based app receiving backslash on commandline, at least
1339 // if followed by " like for example 'igit "C:\"', the commandline igit receives is 'igit.exe C:" status' with
1340 // the 'C:" status' part as a single arg, Maybe it uses unix style processing. In order to avoid this just
1341 // use forward slashes for supplied project and sub paths
1343 CString sProjectPath = pszProjectPath;
1344 sProjectPath.Replace(_T('\\'), _T('/'));
1346 if (pszSubPath)
1348 CString sSubPath = pszSubPath;
1349 sSubPath.Replace(_T('\\'), _T('/'));
1351 cmd.Format(_T("tgit.exe statusex \"%s\" status %s \"%s\""), sProjectPath, sMode, sSubPath);
1353 else
1355 cmd.Format(_T("tgit.exe statusex \"%s\" status %s"), sProjectPath, sMode);
1358 //OutputDebugStringA("---");OutputDebugStringW(cmd);OutputDebugStringA("\r\n");
1360 W_GitCall.SetCmd(cmd);
1361 // NOTE: should igit get added as a part of msysgit then use below line instead of the above one
1362 //W_GitCall.SetCmd(CGit::ms_LastMsysGitDir + cmd);
1364 if ( Run(&W_GitCall) )
1365 return FALSE;
1367 return TRUE;
1370 BOOL CGit::CheckCleanWorkTree()
1372 CString out;
1373 CString cmd;
1374 cmd=_T("git.exe rev-parse --verify HEAD");
1376 if(g_Git.Run(cmd,&out,CP_UTF8))
1377 return FALSE;
1379 cmd=_T("git.exe update-index --ignore-submodules --refresh");
1380 if(g_Git.Run(cmd,&out,CP_UTF8))
1381 return FALSE;
1383 cmd=_T("git.exe diff-files --quiet --ignore-submodules");
1384 if(g_Git.Run(cmd,&out,CP_UTF8))
1385 return FALSE;
1387 cmd=_T("git diff-index --cached --quiet HEAD --ignore-submodules");
1388 if(g_Git.Run(cmd,&out,CP_UTF8))
1389 return FALSE;
1391 return TRUE;
1393 int CGit::Revert(CTGitPathList &list,bool keep)
1395 int ret;
1396 for(int i=0;i<list.GetCount();i++)
1398 ret = Revert((CTGitPath&)list[i],keep);
1399 if(ret)
1400 return ret;
1402 return 0;
1404 int CGit::Revert(CTGitPath &path,bool keep)
1406 CString cmd, out;
1408 if(path.m_Action & CTGitPath::LOGACTIONS_REPLACED )
1410 cmd.Format(_T("git.exe mv -- \"%s\" \"%s\""),path.GetGitPathString(),path.GetGitOldPathString());
1411 if(g_Git.Run(cmd,&out,CP_ACP))
1412 return -1;
1414 cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitOldPathString());
1415 if(g_Git.Run(cmd,&out,CP_ACP))
1416 return -1;
1418 }else if(path.m_Action & CTGitPath::LOGACTIONS_ADDED)
1419 { //To init git repository, there are not HEAD, so we can use git reset command
1420 cmd.Format(_T("git.exe rm --cached -- \"%s\""),path.GetGitPathString());
1422 if(g_Git.Run(cmd,&out,CP_ACP))
1423 return -1;
1425 else
1427 cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitPathString());
1428 if(g_Git.Run(cmd,&out,CP_ACP))
1429 return -1;
1431 return 0;
1434 int CGit::ListConflictFile(CTGitPathList &list,CTGitPath *path)
1436 BYTE_VECTOR vector;
1438 CString cmd;
1439 if(path)
1440 cmd.Format(_T("git.exe ls-files -u -t -z -- \"%s\""),path->GetGitPathString());
1441 else
1442 cmd=_T("git.exe ls-files -u -t -z");
1444 if(g_Git.Run(cmd,&vector))
1446 return -1;
1449 list.ParserFromLsFile(vector);
1451 return 0;
1454 bool CGit::IsFastForward(CString &from, CString &to)
1456 CString base,hash;
1457 CString cmd;
1458 cmd.Format(_T("git.exe merge-base %s %s"), to,from);
1460 if(g_Git.Run(cmd,&base,CP_ACP))
1462 //CMessageBox::Show(NULL,base,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1463 return false;
1465 base=base.Left(40);
1467 hash=g_Git.GetHash(from);
1469 hash=hash.Left(40);
1471 return hash == base;
1474 unsigned int CGit::Hash2int(CString &hash)
1476 int ret=0;
1477 for(int i=0;i<8;i++)
1479 ret =ret <<4;
1480 if(hash[i]>=_T('a'))
1481 ret |= (hash[i]-_T('a')+10)&0xFF;
1482 else if(hash[i]>=_T('A'))
1483 ret |= (hash[i]-_T('A')+10)&0xFF;
1484 else
1485 ret |= (hash[i]-_T('0'))&0xFF;
1488 return ret;
1491 int CGit::RefreshGitIndex()
1493 if(g_Git.m_IsUseGitDLL)
1497 return git_run_cmd("update-index","update-index -q --refresh");
1499 }catch(...)
1501 return -1;
1504 }else
1506 CString cmd,output;
1507 cmd=_T("git.exe update-index --refresh");
1508 return Run(cmd,&output,CP_ACP);
1512 int CGit::GetOneFile(CString Refname, CTGitPath &path, CString &outputfile)
1514 if(g_Git.m_IsUseGitDLL)
1518 g_Git.CheckAndInitDll();
1519 CStringA ref, patha, outa;
1520 ref = CUnicodeUtils::GetMulti(Refname,CP_ACP);
1521 patha = CUnicodeUtils::GetMulti(path.GetGitPathString(), CP_ACP);
1522 outa = CUnicodeUtils::GetMulti(outputfile,CP_ACP);
1523 ::DeleteFile(outputfile);
1524 return git_checkout_file((const char*)ref.GetBuffer(),(const char*)patha.GetBuffer(),(const char*)outa.GetBuffer());
1526 }catch(...)
1528 return -1;
1530 }else
1532 CString cmd;
1533 cmd.Format(_T("git.exe cat-file -p %s:\"%s\""), Refname, path.GetGitPathString());
1534 return g_Git.RunLogFile(cmd,outputfile);
1537 void CEnvironment::CopyProcessEnvironment()
1539 TCHAR *p = GetEnvironmentStrings();
1540 while(*p !=0 || *(p+1) !=0)
1541 this->push_back(*p++);
1543 push_back(_T('\0'));
1544 push_back(_T('\0'));
1547 CString CEnvironment::GetEnv(TCHAR *name)
1549 CString str;
1550 for(int i=0;i<size();i++)
1552 str = &(*this)[i];
1553 int start =0;
1554 CString sname = str.Tokenize(_T("="),start);
1555 if(sname.CompareNoCase(name) == 0)
1557 return &(*this)[i+start+1];
1559 i+=str.GetLength();
1561 return _T("");
1564 void CEnvironment::SetEnv(TCHAR *name, TCHAR* value)
1566 int i;
1567 for( i=0;i<size();i++)
1569 CString str = &(*this)[i];
1570 int start =0;
1571 CString sname = str.Tokenize(_T("="),start);
1572 if(sname.CompareNoCase(name) == 0)
1574 break;
1576 i+=str.GetLength();
1579 if(i == size())
1581 i -= 1; // roll back terminate \0\0
1582 this->push_back(_T('\0'));
1585 CEnvironment::iterator it;
1586 it=this->begin();
1587 it += i;
1589 while(*it && i<size())
1591 this->erase(it);
1592 it=this->begin();
1593 it += i;
1596 while(*name)
1598 this->insert(it,*name++);
1599 i++;
1600 it= begin()+i;
1603 this->insert(it, _T('='));
1604 i++;
1605 it= begin()+i;
1607 while(*value)
1609 this->insert(it,*value++);
1610 i++;
1611 it= begin()+i;
1616 int CGit::GetGitEncode(TCHAR* configkey)
1618 CString str=GetConfigValue(configkey);
1620 if(str.IsEmpty())
1621 return CP_UTF8;
1623 return CUnicodeUtils::GetCPCode(str);