cleaups
[TortoiseGit.git] / src / Git / Git.cpp
blob52b0bbb30e4edd58123571f0151bb414c8b40471
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2011 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "StdAfx.h"
21 #include "Git.h"
22 #include "atlconv.h"
23 #include "GitRev.h"
24 #include "registry.h"
25 #include "GitConfig.h"
26 #include <map>
27 #include "UnicodeUtils.h"
28 #include "gitdll.h"
30 int CGit::m_LogEncode=CP_UTF8;
31 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
33 static LPTSTR nextpath(LPCTSTR src, LPTSTR dst, UINT maxlen)
35 LPCTSTR orgsrc;
37 while (*src == _T(';'))
38 src++;
40 orgsrc = src;
42 if (!--maxlen)
43 goto nullterm;
45 while (*src && *src != _T(';'))
47 if (*src != _T('"'))
49 *dst++ = *src++;
50 if (!--maxlen)
52 orgsrc = src;
53 goto nullterm;
56 else
58 src++;
59 while (*src && *src != _T('"'))
61 *dst++ = *src++;
62 if (!--maxlen)
64 orgsrc = src;
65 goto nullterm;
69 if (*src)
70 src++;
74 while (*src == _T(';'))
75 src++;
77 nullterm:
79 *dst = 0;
81 return (orgsrc != src) ? (LPTSTR)src : NULL;
84 static inline BOOL FileExists(LPCTSTR lpszFileName)
86 struct _stat st;
87 return _tstat(lpszFileName, &st) == 0;
90 static BOOL FindGitPath()
92 size_t size;
93 _tgetenv_s(&size, NULL, 0, _T("PATH"));
95 if (!size)
97 return FALSE;
100 TCHAR *env = (TCHAR*)alloca(size * sizeof(TCHAR));
101 _tgetenv_s(&size, env, size, _T("PATH"));
103 TCHAR buf[_MAX_PATH];
105 // search in all paths defined in PATH
106 while ((env = nextpath(env, buf, _MAX_PATH-1)) && *buf)
108 TCHAR *pfin = buf + _tcslen(buf)-1;
110 // ensure trailing slash
111 if (*pfin != _T('/') && *pfin != _T('\\'))
112 _tcscpy(++pfin, _T("\\"));
114 const int len = _tcslen(buf);
116 if ((len + 7) < _MAX_PATH)
117 _tcscpy(pfin+1, _T("git.exe"));
118 else
119 break;
121 if ( FileExists(buf) )
123 // dir found
124 pfin[1] = 0;
125 CGit::ms_LastMsysGitDir = buf;
126 return TRUE;
130 return FALSE;
134 #define MAX_DIRBUFFER 1000
135 #define CALL_OUTPUT_READ_CHUNK_SIZE 1024
137 CString CGit::ms_LastMsysGitDir;
138 CGit g_Git;
141 CGit::CGit(void)
143 GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));
144 m_CurrentDir.ReleaseBuffer();
145 m_IsGitDllInited = false;
146 m_GitDiff=0;
147 m_GitSimpleListDiff=0;
148 m_IsUseGitDLL = !!CRegDWORD(_T("Software\\TortoiseGit\\UsingGitDLL"),1);
149 this->m_bInitialized =false;
150 CheckMsysGitDir();
151 m_critGitDllSec.Init();
154 CGit::~CGit(void)
156 if(this->m_GitDiff)
158 git_close_diff(m_GitDiff);
159 m_GitDiff=0;
161 if(this->m_GitSimpleListDiff)
163 git_close_diff(m_GitSimpleListDiff);
164 m_GitSimpleListDiff=0;
168 static char g_Buffer[4096];
170 int CGit::RunAsync(CString cmd,PROCESS_INFORMATION *piOut,HANDLE *hReadOut,CString *StdioFile)
172 SECURITY_ATTRIBUTES sa;
173 HANDLE hRead, hWrite;
174 HANDLE hStdioFile = NULL;
176 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
177 sa.lpSecurityDescriptor=NULL;
178 sa.bInheritHandle=TRUE;
179 if(!CreatePipe(&hRead,&hWrite,&sa,0))
181 return GIT_ERROR_OPEN_PIP;
184 if(StdioFile)
186 hStdioFile=CreateFile(*StdioFile,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
187 &sa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
190 STARTUPINFO si;
191 PROCESS_INFORMATION pi;
192 si.cb=sizeof(STARTUPINFO);
193 GetStartupInfo(&si);
195 si.hStdError=hWrite;
196 if(StdioFile)
197 si.hStdOutput=hStdioFile;
198 else
199 si.hStdOutput=hWrite;
201 si.wShowWindow=SW_HIDE;
202 si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
204 LPTSTR pEnv = m_Environment.size()? &m_Environment[0]: NULL;
205 DWORD dwFlags = pEnv ? CREATE_UNICODE_ENVIRONMENT : 0;
207 //DETACHED_PROCESS make ssh recognize that it has no console to launch askpass to input password.
208 dwFlags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
210 memset(&this->m_CurrentGitPi,0,sizeof(PROCESS_INFORMATION));
211 memset(&pi, 0, sizeof(PROCESS_INFORMATION));
213 if(cmd.Find(_T("git")) == 0)
215 int firstSpace = cmd.Find(_T(" "));
216 if (firstSpace > 0)
217 cmd = _T('"')+CGit::ms_LastMsysGitDir+_T("\\")+ cmd.Left(firstSpace) + _T('"')+ cmd.Mid(firstSpace);
218 else
219 cmd=_T('"')+CGit::ms_LastMsysGitDir+_T("\\")+cmd+_T('"');
222 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,dwFlags,pEnv,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
224 LPVOID lpMsgBuf;
225 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
226 NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
227 (LPTSTR)&lpMsgBuf,
228 0,NULL);
229 return GIT_ERROR_CREATE_PROCESS;
232 m_CurrentGitPi = pi;
234 CloseHandle(hWrite);
235 if(piOut)
236 *piOut=pi;
237 if(hReadOut)
238 *hReadOut=hRead;
240 return 0;
243 //Must use sperate function to convert ANSI str to union code string
244 //Becuase A2W use stack as internal convert buffer.
245 void CGit::StringAppend(CString *str,BYTE *p,int code,int length)
247 //USES_CONVERSION;
248 //str->Append(A2W_CP((LPCSTR)p,code));
249 if(str == NULL)
250 return ;
252 WCHAR * buf;
254 int len ;
255 if(length<0)
256 len= strlen((const char*)p);
257 else
258 len=length;
259 //if (len==0)
260 // return ;
261 //buf = new WCHAR[len*4 + 1];
262 buf = str->GetBuffer(len*4+1+str->GetLength())+str->GetLength();
263 SecureZeroMemory(buf, (len*4 + 1)*sizeof(WCHAR));
264 MultiByteToWideChar(code, 0, (LPCSTR)p, len, buf, len*4);
265 str->ReleaseBuffer();
266 //str->Append(buf);
267 //delete buf;
269 BOOL CGit::IsInitRepos()
271 CString cmdout;
272 cmdout.Empty();
273 if(g_Git.Run(_T("git.exe rev-parse --revs-only HEAD"),&cmdout,CP_UTF8))
275 // CMessageBox::Show(NULL,cmdout,_T("TortoiseGit"),MB_OK);
276 return TRUE;
278 if(cmdout.IsEmpty())
279 return TRUE;
281 return FALSE;
283 int CGit::Run(CGitCall* pcall)
285 PROCESS_INFORMATION pi;
286 HANDLE hRead;
287 if(RunAsync(pcall->GetCmd(),&pi,&hRead))
288 return GIT_ERROR_CREATE_PROCESS;
290 DWORD readnumber;
291 BYTE data[CALL_OUTPUT_READ_CHUNK_SIZE];
292 bool bAborted=false;
293 while(ReadFile(hRead,data,CALL_OUTPUT_READ_CHUNK_SIZE,&readnumber,NULL))
295 // TODO: when OnOutputData() returns 'true', abort git-command. Send CTRL-C signal?
296 if(!bAborted)//For now, flush output when command aborted.
297 if(pcall->OnOutputData(data,readnumber))
298 bAborted=true;
300 if(!bAborted)
301 pcall->OnEnd();
303 CloseHandle(pi.hThread);
305 WaitForSingleObject(pi.hProcess, INFINITE);
306 DWORD exitcode =0;
308 if(!GetExitCodeProcess(pi.hProcess,&exitcode))
310 return GIT_ERROR_GET_EXIT_CODE;
313 CloseHandle(pi.hProcess);
315 CloseHandle(hRead);
316 return exitcode;
318 class CGitCall_ByteVector : public CGitCall
320 public:
321 CGitCall_ByteVector(CString cmd,BYTE_VECTOR* pvector):CGitCall(cmd),m_pvector(pvector){}
322 virtual bool OnOutputData(const BYTE* data, size_t size)
324 size_t oldsize=m_pvector->size();
325 m_pvector->resize(m_pvector->size()+size);
326 memcpy(&*(m_pvector->begin()+oldsize),data,size);
327 return false;
329 BYTE_VECTOR* m_pvector;
332 int CGit::Run(CString cmd,BYTE_VECTOR *vector)
334 CGitCall_ByteVector call(cmd,vector);
335 return Run(&call);
337 int CGit::Run(CString cmd, CString* output,int code)
339 BYTE_VECTOR vector;
340 int ret;
341 ret=Run(cmd,&vector);
343 vector.push_back(0);
345 StringAppend(output,&(vector[0]),code);
346 return ret;
349 CString CGit::GetUserName(void)
351 return GetConfigValue(L"user.name", this->GetGitEncode(L"i18n.commitencoding"));
353 CString CGit::GetUserEmail(void)
355 return GetConfigValue(L"user.email");
358 CString CGit::GetConfigValue(CString name,int encoding, CString *GitPath, BOOL RemoveCR)
360 CString configValue;
361 int start = 0;
362 if(this->m_IsUseGitDLL)
364 CString *git_path=NULL;
368 CTGitPath path;
370 CheckAndInitDll();
371 git_path = GitPath;
373 }catch(...)
376 CStringA key, value;
377 key = CUnicodeUtils::GetMulti(name, encoding);
378 CStringA p;
379 if(git_path)
380 p=CUnicodeUtils::GetMulti(*GitPath,CP_ACP);
382 if(git_get_config(key.GetBuffer(), value.GetBufferSetLength(4096), 4096, p.GetBuffer()))
383 return CString();
384 else
386 g_Git.StringAppend(&configValue,(BYTE*)value.GetBuffer(),encoding);
387 if(RemoveCR)
388 return configValue.Tokenize(_T("\n"),start);
389 return configValue;
392 }else
394 CString cmd;
395 cmd.Format(L"git.exe config %s", name);
396 Run(cmd,&configValue,encoding);
397 if(RemoveCR)
398 return configValue.Tokenize(_T("\n"),start);
399 return configValue;
403 int CGit::SetConfigValue(CString key, CString value, CONFIG_TYPE type, int encoding, CString *GitPath)
405 if(this->m_IsUseGitDLL)
409 CheckAndInitDll();
411 }catch(...)
414 CStringA keya, valuea;
415 keya = CUnicodeUtils::GetMulti(key, CP_UTF8);
416 valuea = CUnicodeUtils::GetMulti(value, encoding);
417 CStringA p;
418 if(GitPath)
419 p=CUnicodeUtils::GetMulti(*GitPath,CP_ACP);
421 return get_set_config(keya.GetBuffer(), valuea.GetBuffer(), type, p.GetBuffer());
423 }else
425 CString cmd;
426 CString option;
427 switch(type)
429 case CONFIG_GLOBAL:
430 option = _T("--global");
431 break;
432 case CONFIG_SYSTEM:
433 option = _T("--system");
434 break;
435 default:
436 break;
438 cmd.Format(_T("git.exe config %s %s \"%s\""), option, key, value);
439 CString out;
440 if(Run(cmd,&out,encoding))
442 return -1;
445 return 0;
449 CString CGit::GetCurrentBranch(void)
451 CString output;
452 //Run(_T("git.exe branch"),&branch);
454 if(this->GetCurrentBranchFromFile(this->m_CurrentDir,output))
456 return _T("(no branch)");
457 }else
458 return output;
462 CString CGit::GetSymbolicRef(const wchar_t* symbolicRefName, bool bStripRefsHeads)
464 CString refName;
465 if(this->m_IsUseGitDLL)
467 unsigned char sha1[20];
468 int flag;
470 const char *refs_heads_master = git_resolve_ref(CUnicodeUtils::GetUTF8(CString(symbolicRefName)), sha1, 0, &flag);
471 if(refs_heads_master && (flag&REF_ISSYMREF))
473 g_Git.StringAppend(&refName,(BYTE*)refs_heads_master);
474 if(bStripRefsHeads)
475 refName = StripRefName(refName);
478 }else
480 CString cmd;
481 cmd.Format(L"git symbolic-ref %s", symbolicRefName);
482 if(Run(cmd, &refName, CP_UTF8) != 0)
483 return CString();//Error
484 int iStart = 0;
485 refName = refName.Tokenize(L"\n", iStart);
486 if(bStripRefsHeads)
487 refName = StripRefName(refName);
489 return refName;
492 CString CGit::GetFullRefName(CString shortRefName)
494 CString refName;
495 CString cmd;
496 cmd.Format(L"git rev-parse --symbolic-full-name %s", shortRefName);
497 if(Run(cmd, &refName, CP_UTF8) != 0)
498 return CString();//Error
499 int iStart = 0;
500 return refName.Tokenize(L"\n", iStart);
503 CString CGit::StripRefName(CString refName)
505 if(wcsncmp(refName, L"refs/heads/", 11) == 0)
506 refName = refName.Mid(11);
507 else if(wcsncmp(refName, L"refs/", 5) == 0)
508 refName = refName.Mid(5);
509 int start =0;
510 return refName.Tokenize(_T("\n"),start);
513 int CGit::GetCurrentBranchFromFile(const CString &sProjectRoot, CString &sBranchOut)
515 // read current branch name like git-gui does, by parsing the .git/HEAD file directly
517 if ( sProjectRoot.IsEmpty() )
518 return -1;
520 CString sHeadFile = sProjectRoot + _T("\\") + g_GitAdminDir.GetAdminDirName() + _T("\\HEAD");
522 FILE *pFile;
523 _tfopen_s(&pFile, sHeadFile.GetString(), _T("r"));
525 if (!pFile)
527 return -1;
530 char s[256] = {0};
531 fgets(s, sizeof(s), pFile);
533 fclose(pFile);
535 const char *pfx = "ref: refs/heads/";
536 const int len = 16;//strlen(pfx)
538 if ( !strncmp(s, pfx, len) )
540 //# We're on a branch. It might not exist. But
541 //# HEAD looks good enough to be a branch.
542 sBranchOut = s + len;
543 sBranchOut.TrimRight(_T(" \r\n\t"));
545 if ( sBranchOut.IsEmpty() )
546 return -1;
548 else
550 //# Assume this is a detached head.
551 sBranchOut = "HEAD";
553 return 1;
556 return 0;
559 int CGit::BuildOutputFormat(CString &format,bool IsFull)
561 CString log;
562 log.Format(_T("#<%c>%%x00"),LOG_REV_ITEM_BEGIN);
563 format += log;
564 if(IsFull)
566 log.Format(_T("#<%c>%%an%%x00"),LOG_REV_AUTHOR_NAME);
567 format += log;
568 log.Format(_T("#<%c>%%ae%%x00"),LOG_REV_AUTHOR_EMAIL);
569 format += log;
570 log.Format(_T("#<%c>%%ai%%x00"),LOG_REV_AUTHOR_DATE);
571 format += log;
572 log.Format(_T("#<%c>%%cn%%x00"),LOG_REV_COMMIT_NAME);
573 format += log;
574 log.Format(_T("#<%c>%%ce%%x00"),LOG_REV_COMMIT_EMAIL);
575 format += log;
576 log.Format(_T("#<%c>%%ci%%x00"),LOG_REV_COMMIT_DATE);
577 format += log;
578 log.Format(_T("#<%c>%%b%%x00"),LOG_REV_COMMIT_BODY);
579 format += log;
582 log.Format(_T("#<%c>%%m%%H%%x00"),LOG_REV_COMMIT_HASH);
583 format += log;
584 log.Format(_T("#<%c>%%P%%x00"),LOG_REV_COMMIT_PARENT);
585 format += log;
586 log.Format(_T("#<%c>%%s%%x00"),LOG_REV_COMMIT_SUBJECT);
587 format += log;
589 if(IsFull)
591 log.Format(_T("#<%c>%%x00"),LOG_REV_COMMIT_FILE);
592 format += log;
594 return 0;
597 int CGit::GetLog(BYTE_VECTOR& logOut, const CString &hash, CTGitPath *path ,int count,int mask,CString *from,CString *to)
599 CGitCall_ByteVector gitCall(CString(),&logOut);
600 return GetLog(&gitCall,hash,path,count,mask,from,to);
603 CString CGit::GetLogCmd( const CString &hash, CTGitPath *path, int count, int mask,CString *from,CString *to,bool paramonly,
604 CFilterData *Filter)
606 CString cmd;
607 CString num;
608 CString since;
610 CString file;
612 if(path)
613 file.Format(_T(" -- \"%s\""),path->GetGitPathString());
615 if(count>0)
616 num.Format(_T("-n%d"),count);
618 CString param;
620 if(mask& LOG_INFO_STAT )
621 param += _T(" --numstat ");
622 if(mask& LOG_INFO_FILESTATE)
623 param += _T(" --raw ");
625 if(mask& LOG_INFO_FULLHISTORY)
626 param += _T(" --full-history ");
628 if(mask& LOG_INFO_BOUNDARY)
629 param += _T(" --left-right --boundary ");
631 if(mask& CGit::LOG_INFO_ALL_BRANCH)
632 param += _T(" --all ");
634 if(mask& CGit::LOG_INFO_DETECT_COPYRENAME)
635 param += _T(" -C ");
637 if(mask& CGit::LOG_INFO_DETECT_RENAME )
638 param += _T(" -M ");
640 if(mask& CGit::LOG_INFO_FIRST_PARENT )
641 param += _T(" --first-parent ");
643 if(mask& CGit::LOG_INFO_NO_MERGE )
644 param += _T(" --no-merges ");
646 if(mask& CGit::LOG_INFO_FOLLOW)
647 param += _T(" --follow ");
649 if(mask& CGit::LOG_INFO_SHOW_MERGEDFILE)
650 param += _T(" -c ");
652 if(mask& CGit::LOG_INFO_FULL_DIFF)
653 param += _T(" --full-diff ");
655 if(from != NULL && to != NULL)
657 CString range;
658 range.Format(_T(" %s..%s "),*from,*to);
659 param += range;
661 param+=hash;
663 CString st1,st2;
665 if( Filter && (Filter->m_From != -1))
667 st1.Format(_T(" --max-age=%I64u "), Filter->m_From);
668 param += st1;
671 if( Filter && (Filter->m_To != -1))
673 st2.Format(_T(" --min-age=%I64u "), Filter->m_To);
674 param += st2;
677 bool isgrep = false;
678 if( Filter && (!Filter->m_Author.IsEmpty()))
680 st1.Format(_T(" --author=\"%s\"" ),Filter->m_Author);
681 param += st1;
682 isgrep = true;
685 if( Filter && (!Filter->m_Committer.IsEmpty()))
687 st1.Format(_T(" --committer=\"%s\"" ),Filter->m_Author);
688 param += st1;
689 isgrep = true;
692 if( Filter && (!Filter->m_MessageFilter.IsEmpty()))
694 st1.Format(_T(" --grep=\"%s\"" ),Filter->m_MessageFilter);
695 param += st1;
696 isgrep = true;
699 if(Filter && isgrep)
701 if(!Filter->m_IsRegex)
702 param+=_T(" --fixed-strings ");
704 param += _T(" --regexp-ignore-case --extended-regexp ");
707 if(paramonly) //tgit.dll.Git.cpp:setup_revisions() only looks at args[1] and greater. To account for this, pass a dummy parameter in the 0th place
708 cmd.Format(_T("--ignore-this-parameter %s -z %s --parents "), num, param);
709 else
711 CString log;
712 BuildOutputFormat(log,!(mask&CGit::LOG_INFO_ONLY_HASH));
713 cmd.Format(_T("git.exe log %s -z %s --parents --pretty=format:\"%s\""),
714 num,param,log);
717 cmd += file;
719 return cmd;
721 //int CGit::GetLog(CGitCall* pgitCall, const CString &hash, CTGitPath *path ,int count,int mask)
722 int CGit::GetLog(CGitCall* pgitCall, const CString &hash, CTGitPath *path, int count, int mask,CString *from,CString *to)
724 pgitCall->SetCmd( GetLogCmd(hash,path,count,mask,from,to) );
726 return Run(pgitCall);
727 // return Run(cmd,&logOut);
730 #define BUFSIZE 512
731 void GetTempPath(CString &path)
733 TCHAR lpPathBuffer[BUFSIZE];
734 DWORD dwRetVal;
735 DWORD dwBufSize=BUFSIZE;
736 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
737 lpPathBuffer); // buffer for path
738 if (dwRetVal > dwBufSize || (dwRetVal == 0))
740 path=_T("");
742 path.Format(_T("%s"),lpPathBuffer);
744 CString GetTempFile()
746 TCHAR lpPathBuffer[BUFSIZE];
747 DWORD dwRetVal;
748 DWORD dwBufSize=BUFSIZE;
749 TCHAR szTempName[BUFSIZE];
750 UINT uRetVal;
752 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
753 lpPathBuffer); // buffer for path
754 if (dwRetVal > dwBufSize || (dwRetVal == 0))
756 return _T("");
758 // Create a temporary file.
759 uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files
760 TEXT("Patch"), // temp file name prefix
761 0, // create unique name
762 szTempName); // buffer for name
765 if (uRetVal == 0)
767 return _T("");
770 return CString(szTempName);
774 int CGit::RunLogFile(CString cmd,const CString &filename)
776 STARTUPINFO si;
777 PROCESS_INFORMATION pi;
778 si.cb=sizeof(STARTUPINFO);
779 GetStartupInfo(&si);
781 SECURITY_ATTRIBUTES psa={sizeof(psa),NULL,TRUE};;
782 psa.bInheritHandle=TRUE;
784 HANDLE houtfile=CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
785 &psa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
787 si.wShowWindow = SW_HIDE;
788 si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
789 si.hStdOutput = houtfile;
791 LPTSTR pEnv = m_Environment.size()? &m_Environment[0]: NULL;
792 DWORD dwFlags = pEnv ? CREATE_UNICODE_ENVIRONMENT : 0;
794 if(cmd.Find(_T("git")) == 0)
795 cmd=CGit::ms_LastMsysGitDir+_T("\\")+cmd;
797 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,dwFlags,pEnv,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
799 LPVOID lpMsgBuf;
800 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
801 NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
802 (LPTSTR)&lpMsgBuf,
803 0,NULL);
804 return GIT_ERROR_CREATE_PROCESS;
807 WaitForSingleObject(pi.hProcess,INFINITE);
809 CloseHandle(pi.hThread);
810 CloseHandle(pi.hProcess);
811 CloseHandle(houtfile);
812 return GIT_SUCCESS;
813 // return 0;
816 CGitHash CGit::GetHash(TCHAR* friendname)
818 if(this->m_IsUseGitDLL)
820 this->CheckAndInitDll();
822 CGitHash hash;
823 CStringA ref;
824 ref = CUnicodeUtils::GetMulti(friendname,CP_ACP);
827 git_get_sha1(ref, hash.m_hash);
829 }catch(...)
832 return hash;
834 }else
836 CString cmd;
837 CString out;
838 cmd.Format(_T("git.exe rev-parse %s" ),friendname);
839 Run(cmd,&out,CP_UTF8);
840 // int pos=out.ReverseFind(_T('\n'));
841 out.FindOneOf(_T("\r\n"));
842 return CGitHash(out);
846 int CGit::GetInitAddList(CTGitPathList &outputlist)
848 CString cmd;
849 BYTE_VECTOR cmdout;
851 cmd=_T("git.exe ls-files -s -t -z");
852 outputlist.Clear();
853 if(g_Git.Run(cmd,&cmdout))
854 return -1;
856 outputlist.ParserFromLsFile(cmdout);
857 for(int i=0;i<outputlist.GetCount();i++)
858 ((int)outputlist[i].m_Action) = CTGitPath::LOGACTIONS_ADDED;
860 return 0;
862 int CGit::GetCommitDiffList(const CString &rev1,const CString &rev2,CTGitPathList &outputlist)
864 CString cmd;
866 if(rev1 == GIT_REV_ZERO || rev2 == GIT_REV_ZERO)
868 //rev1=+_T("");
869 if(rev1 == GIT_REV_ZERO)
870 cmd.Format(_T("git.exe diff -r --raw -C -M --numstat -z %s"),rev2);
871 else
872 cmd.Format(_T("git.exe diff -r -R --raw -C -M --numstat -z %s"),rev1);
873 }else
875 cmd.Format(_T("git.exe diff-tree -r --raw -C -M --numstat -z %s %s"),rev2,rev1);
878 BYTE_VECTOR out;
879 if(g_Git.Run(cmd,&out))
880 return -1;
882 outputlist.ParserFromLog(out);
884 return 0;
887 int addto_list_each_ref_fn(const char *refname, const unsigned char * /*sha1*/, int /*flags*/, void *cb_data)
889 STRING_VECTOR *list = (STRING_VECTOR*)cb_data;
890 CString str;
891 g_Git.StringAppend(&str,(BYTE*)refname,CP_ACP);
892 list->push_back(str);
893 return 0;
896 int CGit::GetTagList(STRING_VECTOR &list)
898 int ret;
900 if(this->m_IsUseGitDLL)
902 return git_for_each_ref_in("refs/tags/",addto_list_each_ref_fn, &list);
904 }else
906 CString cmd,output;
907 cmd=_T("git.exe tag -l");
908 int i=0;
909 ret=g_Git.Run(cmd,&output,CP_UTF8);
910 if(!ret)
912 int pos=0;
913 CString one;
914 while( pos>=0 )
916 i++;
917 one=output.Tokenize(_T("\n"),pos);
918 list.push_back(one);
922 return ret;
925 int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)
927 int ret;
928 CString cmd,output;
929 cmd=_T("git.exe branch --no-color");
931 if(type==(BRANCH_LOCAL|BRANCH_REMOTE))
932 cmd+=_T(" -a");
933 else if(type==BRANCH_REMOTE)
934 cmd+=_T(" -r");
936 int i=0;
937 ret=g_Git.Run(cmd,&output,CP_UTF8);
938 if(!ret)
940 int pos=0;
941 CString one;
942 while( pos>=0 )
944 one=output.Tokenize(_T("\n"),pos);
945 one.Trim(L" \r\n\t");
946 if(one.Find(L" -> ") >= 0 || one.IsEmpty())
947 continue; // skip something like: refs/origin/HEAD -> refs/origin/master
948 if(one[0] == _T('*'))
950 if(current)
951 *current=i;
952 one = one.Mid(2);
954 list.push_back(one);
955 i++;
958 return ret;
961 int CGit::GetRemoteList(STRING_VECTOR &list)
963 int ret;
964 CString cmd,output;
965 cmd=_T("git.exe remote");
966 ret=g_Git.Run(cmd,&output,CP_UTF8);
967 if(!ret)
969 int pos=0;
970 CString one;
971 while( pos>=0 )
973 one=output.Tokenize(_T("\n"),pos);
974 list.push_back(one);
977 return ret;
980 int CGit::GetRefList(STRING_VECTOR &list)
982 int ret;
983 if(this->m_IsUseGitDLL)
985 return git_for_each_ref_in("",addto_list_each_ref_fn, &list);
987 }else
989 CString cmd,output;
990 cmd=_T("git.exe show-ref -d");
991 ret=g_Git.Run(cmd,&output,CP_UTF8);
992 if(!ret)
994 int pos=0;
995 CString one;
996 while( pos>=0 )
998 one=output.Tokenize(_T("\n"),pos);
999 int start=one.Find(_T(" "),0);
1000 if(start>0)
1002 CString name;
1003 name=one.Right(one.GetLength()-start-1);
1004 list.push_back(name);
1009 return ret;
1012 int addto_map_each_ref_fn(const char *refname, const unsigned char *sha1, int /*flags*/, void *cb_data)
1014 MAP_HASH_NAME *map = (MAP_HASH_NAME*)cb_data;
1015 CString str;
1016 g_Git.StringAppend(&str,(BYTE*)refname,CP_ACP);
1017 CGitHash hash((char*)sha1);
1019 (*map)[hash].push_back(str);
1021 if(strncmp(refname, "refs/tags", 9) == 0)
1023 GIT_HASH refhash;
1024 if(!git_deref_tag(sha1, refhash))
1026 (*map)[(char*)refhash].push_back(str+_T("^{}"));
1029 return 0;
1032 int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)
1034 int ret;
1035 if(this->m_IsUseGitDLL)
1037 return git_for_each_ref_in("",addto_map_each_ref_fn, &map);
1039 }else
1041 CString cmd,output;
1042 cmd=_T("git.exe show-ref -d");
1043 ret=g_Git.Run(cmd,&output,CP_UTF8);
1044 if(!ret)
1046 int pos=0;
1047 CString one;
1048 while( pos>=0 )
1050 one=output.Tokenize(_T("\n"),pos);
1051 int start=one.Find(_T(" "),0);
1052 if(start>0)
1054 CString name;
1055 name=one.Right(one.GetLength()-start-1);
1057 CString hash;
1058 hash=one.Left(start);
1060 map[CGitHash(hash)].push_back(name);
1065 return ret;
1068 BOOL CGit::CheckMsysGitDir()
1070 if (m_bInitialized)
1072 return TRUE;
1075 this->m_Environment.clear();
1076 m_Environment.CopyProcessEnvironment();
1078 TCHAR *oldpath,*home;
1079 size_t homesize,size,httpsize;
1081 // set HOME if not set already
1082 _tgetenv_s(&homesize, NULL, 0, _T("HOME"));
1083 if (!homesize)
1085 if ( (!_tdupenv_s(&home,&size,_T("USERPROFILE"))) && (home != NULL) )
1087 m_Environment.SetEnv(_T("HOME"),home);
1088 free(home);
1090 else
1092 ATLTRACE("CGit::CheckMsysGitDir Unable to SetEnv HOME\n");
1095 CString str;
1097 //set http_proxy
1098 _tgetenv_s(&httpsize, NULL, 0, _T("http_proxy"));
1099 if (!httpsize)
1101 CString regServeraddress_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-host"), _T(""));
1102 CString regServerport_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-port"), _T(""));
1103 CString regUsername_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-username"), _T(""));
1104 CString regPassword_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-password"), _T(""));
1105 CString regTimeout_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-timeout"), _T(""));
1106 CString regExceptions_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-exceptions"), _T(""));
1108 CString http_proxy;
1109 if(!regServeraddress_copy.IsEmpty())
1111 if(regServeraddress_copy.Left(4) != _T("http"))
1112 http_proxy=_T("http://");
1114 if(!regUsername_copy.IsEmpty())
1116 http_proxy += regUsername_copy;
1117 http_proxy += _T(":")+regPassword_copy;
1118 http_proxy += _T("@");
1120 http_proxy+=regServeraddress_copy;
1121 if(!regServerport_copy.IsEmpty())
1123 http_proxy +=_T(":")+regServerport_copy;
1125 m_Environment.SetEnv(_T("http_proxy"),(LPTSTR)http_proxy.GetBuffer());
1128 //setup ssh client
1129 CString sshclient=CRegString(_T("Software\\TortoiseGit\\SSH"));
1131 if(!sshclient.IsEmpty())
1133 m_Environment.SetEnv(_T("GIT_SSH"),sshclient.GetBuffer());
1135 //Setup SVN_SSH
1136 CString ssh=sshclient;
1137 ssh.Replace(_T("/"),_T("\\"));
1138 ssh.Replace(_T("\\"),_T("\\\\"));
1139 ssh=CString(_T("\""))+ssh+_T('\"');
1140 m_Environment.SetEnv(_T("SVN_SSH"),ssh.GetBuffer());
1142 }else
1144 TCHAR sPlink[MAX_PATH];
1145 GetModuleFileName(NULL, sPlink, _countof(sPlink));
1146 LPTSTR ptr = _tcsrchr(sPlink, _T('\\'));
1147 if (ptr) {
1148 _tcscpy(ptr + 1, _T("TortoisePlink.exe"));
1149 m_Environment.SetEnv(_T("GIT_SSH"), sPlink);
1151 //Setup SVN_SSH
1152 CString ssh=sPlink;
1153 ssh.Replace(_T("/"),_T("\\"));
1154 ssh.Replace(_T("\\"),_T("\\\\"));
1155 ssh=CString(_T("\""))+ssh+_T('\"');
1156 m_Environment.SetEnv(_T("SVN_SSH"),ssh.GetBuffer());
1161 TCHAR sAskPass[MAX_PATH];
1162 GetModuleFileName(NULL, sAskPass, _countof(sAskPass));
1163 LPTSTR ptr = _tcsrchr(sAskPass, _T('\\'));
1164 if (ptr)
1166 _tcscpy(ptr + 1, _T("SshAskPass.exe"));
1167 m_Environment.SetEnv(_T("DISPLAY"),_T(":9999"));
1168 m_Environment.SetEnv(_T("SSH_ASKPASS"),sAskPass);
1169 m_Environment.SetEnv(_T("GIT_ASKPASS"),sAskPass);
1173 // add git/bin path to PATH
1175 CRegString msysdir=CRegString(REG_MSYSGIT_PATH,_T(""),FALSE);
1176 str=msysdir;
1177 if(str.IsEmpty())
1179 CRegString msysinstalldir=CRegString(REG_MSYSGIT_INSTALL,_T(""),FALSE,HKEY_LOCAL_MACHINE);
1180 str=msysinstalldir;
1181 if ( !str.IsEmpty() )
1183 str += (str[str.GetLength()-1] != '\\') ? "\\bin" : "bin";
1184 msysdir=str;
1185 CGit::ms_LastMsysGitDir = str;
1186 msysdir.write();
1188 else
1190 // search PATH if git/bin directory is already present
1191 if ( FindGitPath() )
1193 m_bInitialized = TRUE;
1194 return TRUE;
1197 return FALSE;
1199 }else
1201 CGit::ms_LastMsysGitDir = str;
1204 // check for git.exe existance (maybe it was deinstalled in the meantime)
1205 if (!FileExists(CGit::ms_LastMsysGitDir + _T("\\git.exe")))
1206 return FALSE;
1208 //set path
1209 _tdupenv_s(&oldpath,&size,_T("PATH"));
1211 CString path;
1212 path.Format(_T("%s;%s"),oldpath,str + _T(";")+ (CString)CRegString(REG_MSYSGIT_EXTRA_PATH,_T(""),FALSE));
1214 m_Environment.SetEnv(_T("PATH"),path.GetBuffer());
1216 CString str1 = m_Environment.GetEnv(_T("PATH"));
1218 CString sOldPath = oldpath;
1219 free(oldpath);
1221 m_bInitialized = TRUE;
1222 return true;
1225 class CGitCall_EnumFiles : public CGitCall
1227 public:
1228 CGitCall_EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)
1229 : m_pszProjectPath(pszProjectPath),
1230 m_pszSubPath(pszSubPath),
1231 m_nFlags(nFlags),
1232 m_pEnumCb(pEnumCb),
1233 m_pUserData(pUserData)
1237 typedef std::map<CStringA,char> TStrCharMap;
1239 const TCHAR * m_pszProjectPath;
1240 const TCHAR * m_pszSubPath;
1241 unsigned int m_nFlags;
1242 WGENUMFILECB * m_pEnumCb;
1243 void * m_pUserData;
1245 BYTE_VECTOR m_DataCollector;
1247 virtual bool OnOutputData(const BYTE* data, size_t size)
1249 m_DataCollector.append(data,size);
1250 while(true)
1252 // lines from igit.exe are 0 terminated
1253 int found=m_DataCollector.findData((const BYTE*)"",1);
1254 if(found<0)
1255 return false;
1256 OnSingleLine( (LPCSTR)&*m_DataCollector.begin() );
1257 m_DataCollector.erase(m_DataCollector.begin(), m_DataCollector.begin()+found+1);
1259 return false;//Should never reach this
1261 virtual void OnEnd()
1265 BYTE HexChar(char ch)
1267 if (ch >= '0' && ch <= '9')
1268 return (UINT)(ch - '0');
1269 else if (ch >= 'A' && ch <= 'F')
1270 return (UINT)(ch - 'A') + 10;
1271 else if (ch >= 'a' && ch <= 'f')
1272 return (UINT)(ch - 'a') + 10;
1273 else
1274 return 0;
1277 bool OnSingleLine(LPCSTR line)
1279 //Parse single line
1281 wgFile_s fileStatus;
1283 // file/dir type
1285 fileStatus.nFlags = 0;
1286 if (*line == 'D')
1287 fileStatus.nFlags |= WGFF_Directory;
1288 else if (*line != 'F')
1289 // parse error
1290 return false;
1291 line += 2;
1293 // status
1295 fileStatus.nStatus = WGFS_Unknown;
1296 switch (*line)
1298 case 'N': fileStatus.nStatus = WGFS_Normal; break;
1299 case 'M': fileStatus.nStatus = WGFS_Modified; break;
1300 case 'S': fileStatus.nStatus = WGFS_Staged; break;
1301 case 'A': fileStatus.nStatus = WGFS_Added; break;
1302 case 'C': fileStatus.nStatus = WGFS_Conflicted; break;
1303 case 'D': fileStatus.nStatus = WGFS_Deleted; break;
1304 case 'I': fileStatus.nStatus = WGFS_Ignored; break;
1305 case 'U': fileStatus.nStatus = WGFS_Unversioned; break;
1306 case 'E': fileStatus.nStatus = WGFS_Empty; break;
1307 case '?': fileStatus.nStatus = WGFS_Unknown; break;
1308 default:
1309 // parse error
1310 return false;
1312 line += 2;
1314 // file sha1
1316 BYTE sha1[20];
1317 fileStatus.sha1 = NULL;
1318 if ( !(fileStatus.nFlags & WGFF_Directory) )
1320 for (int i=0; i<20; i++)
1322 sha1[i] = (HexChar(line[0])<<4)&0xF0;
1323 sha1[i] |= HexChar(line[1])&0xF;
1325 line += 2;
1328 line++;
1331 // filename
1332 int len = strlen(line);
1333 if (len && len < 2048)
1335 WCHAR *buf = (WCHAR*)alloca((len*4+2)*sizeof(WCHAR));
1336 *buf = 0;
1337 MultiByteToWideChar(CP_ACP, 0, line, len+1, buf, len*4+1);
1338 fileStatus.sFileName = buf;
1340 if (*buf && (*m_pEnumCb)(&fileStatus,m_pUserData))
1341 return false;
1344 return true;
1348 BOOL CGit::EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)
1350 if(!pszProjectPath || *pszProjectPath=='\0')
1351 return FALSE;
1353 CGitCall_EnumFiles W_GitCall(pszProjectPath,pszSubPath,nFlags,pEnumCb,pUserData);
1354 CString cmd;
1356 /* char W_szToDir[MAX_PATH];
1357 strncpy(W_szToDir,pszProjectPath,sizeof(W_szToDir)-1);
1358 if(W_szToDir[strlen(W_szToDir)-1]!='\\')
1359 strncat(W_szToDir,"\\",sizeof(W_szToDir)-1);
1361 SetCurrentDirectoryA(W_szToDir);
1362 GetCurrentDirectoryA(sizeof(W_szToDir)-1,W_szToDir);
1364 SetCurrentDir(pszProjectPath);
1366 CString sMode;
1367 if (nFlags)
1369 if (nFlags & WGEFF_NoRecurse) sMode += _T("r");
1370 if (nFlags & WGEFF_FullPath) sMode += _T("f");
1371 if (nFlags & WGEFF_DirStatusDelta) sMode += _T("d");
1372 if (nFlags & WGEFF_DirStatusAll) sMode += _T("D");
1373 if (nFlags & WGEFF_EmptyAsNormal) sMode += _T("e");
1374 if (nFlags & WGEFF_SingleFile) sMode += _T("s");
1376 else
1378 sMode = _T("-");
1381 // NOTE: there seems to be some issue with msys based app receiving backslash on commandline, at least
1382 // if followed by " like for example 'igit "C:\"', the commandline igit receives is 'igit.exe C:" status' with
1383 // the 'C:" status' part as a single arg, Maybe it uses unix style processing. In order to avoid this just
1384 // use forward slashes for supplied project and sub paths
1386 CString sProjectPath = pszProjectPath;
1387 sProjectPath.Replace(_T('\\'), _T('/'));
1389 if (pszSubPath)
1391 CString sSubPath = pszSubPath;
1392 sSubPath.Replace(_T('\\'), _T('/'));
1394 cmd.Format(_T("tgit.exe statusex \"%s\" status %s \"%s\""), sProjectPath, sMode, sSubPath);
1396 else
1398 cmd.Format(_T("tgit.exe statusex \"%s\" status %s"), sProjectPath, sMode);
1401 //OutputDebugStringA("---");OutputDebugStringW(cmd);OutputDebugStringA("\r\n");
1403 W_GitCall.SetCmd(cmd);
1404 // NOTE: should igit get added as a part of msysgit then use below line instead of the above one
1405 //W_GitCall.SetCmd(CGit::ms_LastMsysGitDir + cmd);
1407 if ( Run(&W_GitCall) )
1408 return FALSE;
1410 return TRUE;
1413 BOOL CGit::CheckCleanWorkTree()
1415 CString out;
1416 CString cmd;
1417 cmd=_T("git.exe rev-parse --verify HEAD");
1419 if(g_Git.Run(cmd,&out,CP_UTF8))
1420 return FALSE;
1422 cmd=_T("git.exe update-index --ignore-submodules --refresh");
1423 if(g_Git.Run(cmd,&out,CP_UTF8))
1424 return FALSE;
1426 cmd=_T("git.exe diff-files --quiet --ignore-submodules");
1427 if(g_Git.Run(cmd,&out,CP_UTF8))
1428 return FALSE;
1430 cmd=_T("git diff-index --cached --quiet HEAD --ignore-submodules");
1431 if(g_Git.Run(cmd,&out,CP_UTF8))
1432 return FALSE;
1434 return TRUE;
1436 int CGit::Revert(CTGitPathList &list,bool keep)
1438 int ret;
1439 for(int i=0;i<list.GetCount();i++)
1441 ret = Revert((CTGitPath&)list[i],keep);
1442 if(ret)
1443 return ret;
1445 return 0;
1447 int CGit::Revert(CTGitPath &path,bool /*keep*/)
1449 CString cmd, out;
1451 if(path.m_Action & CTGitPath::LOGACTIONS_REPLACED )
1453 cmd.Format(_T("git.exe mv -- \"%s\" \"%s\""),path.GetGitPathString(),path.GetGitOldPathString());
1454 if(g_Git.Run(cmd,&out,CP_ACP))
1455 return -1;
1457 cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitOldPathString());
1458 if(g_Git.Run(cmd,&out,CP_ACP))
1459 return -1;
1461 }else if(path.m_Action & CTGitPath::LOGACTIONS_ADDED)
1462 { //To init git repository, there are not HEAD, so we can use git reset command
1463 cmd.Format(_T("git.exe rm --cached -- \"%s\""),path.GetGitPathString());
1465 if(g_Git.Run(cmd,&out,CP_ACP))
1466 return -1;
1468 else
1470 cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitPathString());
1471 if(g_Git.Run(cmd,&out,CP_ACP))
1472 return -1;
1474 return 0;
1477 int CGit::ListConflictFile(CTGitPathList &list,CTGitPath *path)
1479 BYTE_VECTOR vector;
1481 CString cmd;
1482 if(path)
1483 cmd.Format(_T("git.exe ls-files -u -t -z -- \"%s\""),path->GetGitPathString());
1484 else
1485 cmd=_T("git.exe ls-files -u -t -z");
1487 if(g_Git.Run(cmd,&vector))
1489 return -1;
1492 list.ParserFromLsFile(vector);
1494 return 0;
1497 bool CGit::IsFastForward(const CString &from, const CString &to)
1499 CString base;
1500 CGitHash basehash,hash;
1501 CString cmd;
1502 cmd.Format(_T("git.exe merge-base %s %s"), to,from);
1504 if(g_Git.Run(cmd,&base,CP_ACP))
1506 //CMessageBox::Show(NULL,base,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1507 return false;
1509 basehash = base;
1511 hash=g_Git.GetHash(from);
1513 return hash == basehash;
1516 unsigned int CGit::Hash2int(CGitHash &hash)
1518 int ret=0;
1519 for(int i=0;i<4;i++)
1521 ret = ret << 8;
1522 ret |= hash.m_hash[i];
1524 return ret;
1527 int CGit::RefreshGitIndex()
1529 if(g_Git.m_IsUseGitDLL)
1533 return git_run_cmd("update-index","update-index -q --refresh");
1535 }catch(...)
1537 return -1;
1540 }else
1542 CString cmd,output;
1543 cmd=_T("git.exe update-index --refresh");
1544 return Run(cmd,&output,CP_ACP);
1548 int CGit::GetOneFile(CString Refname, CTGitPath &path, const CString &outputfile)
1550 if(g_Git.m_IsUseGitDLL)
1554 g_Git.CheckAndInitDll();
1555 CStringA ref, patha, outa;
1556 ref = CUnicodeUtils::GetMulti(Refname,CP_ACP);
1557 patha = CUnicodeUtils::GetMulti(path.GetGitPathString(), CP_ACP);
1558 outa = CUnicodeUtils::GetMulti(outputfile,CP_ACP);
1559 ::DeleteFile(outputfile);
1560 return git_checkout_file((const char*)ref.GetBuffer(),(const char*)patha.GetBuffer(),(const char*)outa.GetBuffer());
1562 }catch(...)
1564 return -1;
1566 }else
1568 CString cmd;
1569 cmd.Format(_T("git.exe cat-file -p %s:\"%s\""), Refname, path.GetGitPathString());
1570 return g_Git.RunLogFile(cmd,outputfile);
1573 void CEnvironment::CopyProcessEnvironment()
1575 TCHAR *p = GetEnvironmentStrings();
1576 while(*p !=0 || *(p+1) !=0)
1577 this->push_back(*p++);
1579 push_back(_T('\0'));
1580 push_back(_T('\0'));
1583 CString CEnvironment::GetEnv(TCHAR *name)
1585 CString str;
1586 for(int i=0;i<size();i++)
1588 str = &(*this)[i];
1589 int start =0;
1590 CString sname = str.Tokenize(_T("="),start);
1591 if(sname.CompareNoCase(name) == 0)
1593 return &(*this)[i+start+1];
1595 i+=str.GetLength();
1597 return _T("");
1600 void CEnvironment::SetEnv(TCHAR *name, TCHAR* value)
1602 unsigned int i;
1603 for( i=0;i<size();i++)
1605 CString str = &(*this)[i];
1606 int start =0;
1607 CString sname = str.Tokenize(_T("="),start);
1608 if(sname.CompareNoCase(name) == 0)
1610 break;
1612 i+=str.GetLength();
1615 if(i == size())
1617 i -= 1; // roll back terminate \0\0
1618 this->push_back(_T('\0'));
1621 CEnvironment::iterator it;
1622 it=this->begin();
1623 it += i;
1625 while(*it && i<size())
1627 this->erase(it);
1628 it=this->begin();
1629 it += i;
1632 while(*name)
1634 this->insert(it,*name++);
1635 i++;
1636 it= begin()+i;
1639 this->insert(it, _T('='));
1640 i++;
1641 it= begin()+i;
1643 while(*value)
1645 this->insert(it,*value++);
1646 i++;
1647 it= begin()+i;
1652 int CGit::GetGitEncode(TCHAR* configkey)
1654 CString str=GetConfigValue(configkey);
1656 if(str.IsEmpty())
1657 return CP_UTF8;
1659 return CUnicodeUtils::GetCPCode(str);
1663 int CGit::GetDiffPath(CTGitPathList *PathList, CGitHash *hash1, CGitHash *hash2, char *arg)
1665 GIT_FILE file=0;
1666 int ret=0;
1667 GIT_DIFF diff=0;
1669 CAutoLocker lock(g_Git.m_critGitDllSec);
1671 if(arg == NULL)
1672 diff = GetGitDiff();
1673 else
1674 git_open_diff(&diff, arg);
1676 if(diff ==NULL)
1677 return -1;
1679 bool isStat = 0;
1680 if(arg == NULL)
1681 isStat = true;
1682 else
1683 isStat = !!strstr(arg, "stat");
1685 int count=0;
1687 if(hash2 == NULL)
1688 ret = git_root_diff(diff, hash1->m_hash, &file, &count,isStat);
1689 else
1690 ret = git_diff(diff,hash2->m_hash,hash1->m_hash,&file,&count,isStat);
1692 if(ret)
1693 return -1;
1695 CTGitPath path;
1696 CString strnewname;
1697 CString stroldname;
1699 for(int j=0;j<count;j++)
1701 path.Reset();
1702 char *newname;
1703 char *oldname;
1705 strnewname.Empty();
1706 stroldname.Empty();
1708 int mode=0,IsBin=0,inc=0,dec=0;
1709 git_get_diff_file(diff,file,j,&newname,&oldname,
1710 &mode,&IsBin,&inc,&dec);
1712 StringAppend(&strnewname,(BYTE*)newname,CP_ACP);
1713 StringAppend(&stroldname,(BYTE*)oldname,CP_ACP);
1715 path.SetFromGit(strnewname,&stroldname);
1716 path.ParserAction((BYTE)mode);
1718 if(IsBin)
1720 path.m_StatAdd=_T("-");
1721 path.m_StatDel=_T("-");
1722 }else
1724 path.m_StatAdd.Format(_T("%d"),inc);
1725 path.m_StatDel.Format(_T("%d"),dec);
1727 PathList->AddPath(path);
1729 git_diff_flush(diff);
1731 if(arg)
1732 git_close_diff(diff);
1734 return 0;