Catch the CMemoryExceptions when loading files in TortoiseMerge
[TortoiseGit.git] / src / Git / Git.cpp
blob48b2c14c3b4880cddcc4ee9e58f504c653bf9b37
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2012 - 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"
29 #include <fstream>
31 int CGit::m_LogEncode=CP_UTF8;
32 typedef CComCritSecLock<CComCriticalSection> CAutoLocker;
34 static LPTSTR nextpath(LPCTSTR src, LPTSTR dst, UINT maxlen)
36 LPCTSTR orgsrc;
38 while (*src == _T(';'))
39 src++;
41 orgsrc = src;
43 if (!--maxlen)
44 goto nullterm;
46 while (*src && *src != _T(';'))
48 if (*src != _T('"'))
50 *dst++ = *src++;
51 if (!--maxlen)
53 orgsrc = src;
54 goto nullterm;
57 else
59 src++;
60 while (*src && *src != _T('"'))
62 *dst++ = *src++;
63 if (!--maxlen)
65 orgsrc = src;
66 goto nullterm;
70 if (*src)
71 src++;
75 while (*src == _T(';'))
76 src++;
78 nullterm:
80 *dst = 0;
82 return (orgsrc != src) ? (LPTSTR)src : NULL;
85 static inline BOOL FileExists(LPCTSTR lpszFileName)
87 struct _stat st;
88 return _tstat(lpszFileName, &st) == 0;
91 static BOOL FindGitPath()
93 size_t size;
94 _tgetenv_s(&size, NULL, 0, _T("PATH"));
96 if (!size)
98 return FALSE;
101 TCHAR *env = (TCHAR*)alloca(size * sizeof(TCHAR));
102 _tgetenv_s(&size, env, size, _T("PATH"));
104 TCHAR buf[_MAX_PATH];
106 // search in all paths defined in PATH
107 while ((env = nextpath(env, buf, _MAX_PATH-1)) && *buf)
109 TCHAR *pfin = buf + _tcslen(buf)-1;
111 // ensure trailing slash
112 if (*pfin != _T('/') && *pfin != _T('\\'))
113 _tcscpy(++pfin, _T("\\"));
115 const int len = _tcslen(buf);
117 if ((len + 7) < _MAX_PATH)
118 _tcscpy(pfin+1, _T("git.exe"));
119 else
120 break;
122 if ( FileExists(buf) )
124 // dir found
125 pfin[1] = 0;
126 CGit::ms_LastMsysGitDir = buf;
127 return TRUE;
131 return FALSE;
135 #define MAX_DIRBUFFER 1000
136 #define CALL_OUTPUT_READ_CHUNK_SIZE 1024
138 CString CGit::ms_LastMsysGitDir;
139 CGit g_Git;
142 CGit::CGit(void)
144 GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));
145 m_CurrentDir.ReleaseBuffer();
146 m_IsGitDllInited = false;
147 m_GitDiff=0;
148 m_GitSimpleListDiff=0;
149 m_IsUseGitDLL = !!CRegDWORD(_T("Software\\TortoiseGit\\UsingGitDLL"),1);
150 this->m_bInitialized =false;
151 CheckMsysGitDir();
152 m_critGitDllSec.Init();
155 CGit::~CGit(void)
157 if(this->m_GitDiff)
159 git_close_diff(m_GitDiff);
160 m_GitDiff=0;
162 if(this->m_GitSimpleListDiff)
164 git_close_diff(m_GitSimpleListDiff);
165 m_GitSimpleListDiff=0;
169 bool CGit::IsBranchNameValid(CString branchname)
171 if (branchname.IsEmpty())
172 return false;
174 for(int i=0; i < branchname.GetLength(); i++)
176 TCHAR c = branchname.GetAt(i);
177 if (c <= ' ' || c == '~' || c == '^' || c == ':' || c == '\\' || c == '?' || c == '[')
178 return false;
181 if (branchname.Find(L".") == 0 || branchname.Find(L"/.") >= 0 || branchname.Find(L"..") >= 0 || branchname.Find(L"@{") >= 0 || branchname.ReverseFind('*') >= 0)
182 return false;
184 CString reverseBranchname = branchname.MakeReverse();
185 if (branchname.Find(L'.') == 0 || branchname.Find(L'/') == 0 || reverseBranchname.Find(L"kcol.") >= 0)
186 return false;
188 return true;
191 static char g_Buffer[4096];
193 int CGit::RunAsync(CString cmd, PROCESS_INFORMATION *piOut, HANDLE *hReadOut, HANDLE *hErrReadOut, CString *StdioFile)
195 SECURITY_ATTRIBUTES sa;
196 HANDLE hRead, hWrite, hReadErr = NULL, hWriteErr = NULL;
197 HANDLE hStdioFile = NULL;
199 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
200 sa.lpSecurityDescriptor=NULL;
201 sa.bInheritHandle=TRUE;
202 if(!CreatePipe(&hRead,&hWrite,&sa,0))
204 return TGIT_GIT_ERROR_OPEN_PIP;
206 if (hErrReadOut && !CreatePipe(&hReadErr, &hWriteErr, &sa, 0))
207 return TGIT_GIT_ERROR_OPEN_PIP;
209 if(StdioFile)
211 hStdioFile=CreateFile(*StdioFile,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
212 &sa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
215 STARTUPINFO si;
216 PROCESS_INFORMATION pi;
217 si.cb=sizeof(STARTUPINFO);
218 GetStartupInfo(&si);
220 if (hErrReadOut)
221 si.hStdError = hWriteErr;
222 else
223 si.hStdError = hWrite;
224 if(StdioFile)
225 si.hStdOutput=hStdioFile;
226 else
227 si.hStdOutput=hWrite;
229 si.wShowWindow=SW_HIDE;
230 si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
232 LPTSTR pEnv = m_Environment.size()? &m_Environment[0]: NULL;
233 DWORD dwFlags = pEnv ? CREATE_UNICODE_ENVIRONMENT : 0;
235 //DETACHED_PROCESS make ssh recognize that it has no console to launch askpass to input password.
236 dwFlags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
238 memset(&this->m_CurrentGitPi,0,sizeof(PROCESS_INFORMATION));
239 memset(&pi, 0, sizeof(PROCESS_INFORMATION));
241 if(cmd.Find(_T("git")) == 0)
243 int firstSpace = cmd.Find(_T(" "));
244 if (firstSpace > 0)
245 cmd = _T('"')+CGit::ms_LastMsysGitDir+_T("\\")+ cmd.Left(firstSpace) + _T('"')+ cmd.Mid(firstSpace);
246 else
247 cmd=_T('"')+CGit::ms_LastMsysGitDir+_T("\\")+cmd+_T('"');
250 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,dwFlags,pEnv,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
252 LPVOID lpMsgBuf;
253 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
254 NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
255 (LPTSTR)&lpMsgBuf,
256 0,NULL);
257 return TGIT_GIT_ERROR_CREATE_PROCESS;
260 m_CurrentGitPi = pi;
262 CloseHandle(hWrite);
263 if (hErrReadOut)
264 CloseHandle(hWriteErr);
265 if(piOut)
266 *piOut=pi;
267 if(hReadOut)
268 *hReadOut=hRead;
269 if(hErrReadOut)
270 *hErrReadOut = hReadErr;
271 return 0;
274 //Must use sperate function to convert ANSI str to union code string
275 //Becuase A2W use stack as internal convert buffer.
276 void CGit::StringAppend(CString *str,BYTE *p,int code,int length)
278 //USES_CONVERSION;
279 //str->Append(A2W_CP((LPCSTR)p,code));
280 if(str == NULL)
281 return ;
283 WCHAR * buf;
285 int len ;
286 if(length<0)
287 len= strlen((const char*)p);
288 else
289 len=length;
290 //if (len==0)
291 // return ;
292 //buf = new WCHAR[len*4 + 1];
293 buf = str->GetBuffer(len*4+1+str->GetLength())+str->GetLength();
294 SecureZeroMemory(buf, (len*4 + 1)*sizeof(WCHAR));
295 MultiByteToWideChar(code, 0, (LPCSTR)p, len, buf, len*4);
296 str->ReleaseBuffer();
297 //str->Append(buf);
298 //delete buf;
300 BOOL CGit::IsInitRepos()
302 CString cmdout;
303 cmdout.Empty();
304 if(g_Git.Run(_T("git.exe rev-parse --revs-only HEAD"),&cmdout,CP_UTF8))
306 // CMessageBox::Show(NULL,cmdout,_T("TortoiseGit"),MB_OK);
307 return TRUE;
309 if(cmdout.IsEmpty())
310 return TRUE;
312 return FALSE;
315 DWORD WINAPI CGit::AsyncReadStdErrThread(LPVOID lpParam)
317 PASYNCREADSTDERRTHREADARGS pDataArray;
318 pDataArray = (PASYNCREADSTDERRTHREADARGS)lpParam;
320 DWORD readnumber;
321 BYTE data[CALL_OUTPUT_READ_CHUNK_SIZE];
322 while (ReadFile(pDataArray->fileHandle, data, CALL_OUTPUT_READ_CHUNK_SIZE, &readnumber, NULL))
324 if (pDataArray->pcall->OnOutputErrData(data,readnumber))
325 break;
328 return 0;
331 int CGit::Run(CGitCall* pcall)
333 PROCESS_INFORMATION pi;
334 HANDLE hRead, hReadErr;
335 if(RunAsync(pcall->GetCmd(),&pi,&hRead, &hReadErr))
336 return TGIT_GIT_ERROR_CREATE_PROCESS;
338 HANDLE thread;
339 ASYNCREADSTDERRTHREADARGS threadArguments;
340 threadArguments.fileHandle = hReadErr;
341 threadArguments.pcall = pcall;
342 thread = CreateThread(NULL, 0, AsyncReadStdErrThread, &threadArguments, 0, NULL);
344 DWORD readnumber;
345 BYTE data[CALL_OUTPUT_READ_CHUNK_SIZE];
346 bool bAborted=false;
347 while(ReadFile(hRead,data,CALL_OUTPUT_READ_CHUNK_SIZE,&readnumber,NULL))
349 // TODO: when OnOutputData() returns 'true', abort git-command. Send CTRL-C signal?
350 if(!bAborted)//For now, flush output when command aborted.
351 if(pcall->OnOutputData(data,readnumber))
352 bAborted=true;
354 if(!bAborted)
355 pcall->OnEnd();
357 if (thread)
358 WaitForSingleObject(thread, INFINITE);
360 CloseHandle(pi.hThread);
362 WaitForSingleObject(pi.hProcess, INFINITE);
363 DWORD exitcode =0;
365 if(!GetExitCodeProcess(pi.hProcess,&exitcode))
367 return TGIT_GIT_ERROR_GET_EXIT_CODE;
370 CloseHandle(pi.hProcess);
372 CloseHandle(hRead);
373 CloseHandle(hReadErr);
374 return exitcode;
376 class CGitCall_ByteVector : public CGitCall
378 public:
379 CGitCall_ByteVector(CString cmd,BYTE_VECTOR* pvector, BYTE_VECTOR* pvectorErr = NULL):CGitCall(cmd),m_pvector(pvector),m_pvectorErr(pvectorErr){}
380 virtual bool OnOutputData(const BYTE* data, size_t size)
382 size_t oldsize=m_pvector->size();
383 m_pvector->resize(m_pvector->size()+size);
384 memcpy(&*(m_pvector->begin()+oldsize),data,size);
385 return false;
387 virtual bool OnOutputErrData(const BYTE* data, size_t size)
389 if (!m_pvectorErr)
390 return false;
391 size_t oldsize = m_pvectorErr->size();
392 m_pvectorErr->resize(m_pvectorErr->size() + size);
393 memcpy(&*(m_pvectorErr->begin() + oldsize), data, size);
394 return false;
396 BYTE_VECTOR* m_pvector;
397 BYTE_VECTOR* m_pvectorErr;
400 int CGit::Run(CString cmd,BYTE_VECTOR *vector, BYTE_VECTOR *vectorErr)
402 CGitCall_ByteVector call(cmd, vector, vectorErr);
403 return Run(&call);
405 int CGit::Run(CString cmd, CString* output, int code)
407 CString err;
408 int ret;
409 ret = Run(cmd, output, &err, code);
411 if (output && !err.IsEmpty())
413 if (!output->IsEmpty())
414 *output += _T("\n");
415 *output += err;
418 return ret;
420 int CGit::Run(CString cmd, CString* output, CString* outputErr, int code)
422 BYTE_VECTOR vector, vectorErr;
423 int ret;
424 if (outputErr)
425 ret = Run(cmd, &vector, &vectorErr);
426 else
427 ret = Run(cmd, &vector);
429 vector.push_back(0);
430 StringAppend(output, &(vector[0]), code);
432 if (outputErr)
434 vectorErr.push_back(0);
435 StringAppend(outputErr, &(vectorErr[0]), code);
438 return ret;
441 CString CGit::GetUserName(void)
443 CEnvironment env;
444 env.CopyProcessEnvironment();
445 CString envname = env.GetEnv(_T("GIT_AUTHOR_NAME"));
446 if (!envname.IsEmpty())
447 return envname;
448 return GetConfigValue(L"user.name", this->GetGitEncode(L"i18n.commitencoding"));
450 CString CGit::GetUserEmail(void)
452 CEnvironment env;
453 env.CopyProcessEnvironment();
454 CString envmail = env.GetEnv(_T("GIT_AUTHOR_EMAIL"));
455 if (!envmail.IsEmpty())
456 return envmail;
458 return GetConfigValue(L"user.email");
461 CString CGit::GetConfigValue(CString name,int encoding, CString *GitPath, BOOL RemoveCR)
463 CString configValue;
464 int start = 0;
465 if(this->m_IsUseGitDLL)
467 CString *git_path=NULL;
469 CAutoLocker lock(g_Git.m_critGitDllSec);
473 CTGitPath path;
475 CheckAndInitDll();
476 git_path = GitPath;
478 }catch(...)
481 CStringA key, value;
482 key = CUnicodeUtils::GetMulti(name, encoding);
483 CStringA p;
484 if(git_path)
485 p = CUnicodeUtils::GetMulti(*GitPath, CP_UTF8);
487 if(git_get_config(key.GetBuffer(), value.GetBufferSetLength(4096), 4096, p.GetBuffer()))
488 return CString();
489 else
491 g_Git.StringAppend(&configValue,(BYTE*)value.GetBuffer(),encoding);
492 if(RemoveCR)
493 return configValue.Tokenize(_T("\n"),start);
494 return configValue;
498 else
500 CString cmd;
501 cmd.Format(L"git.exe config %s", name);
502 Run(cmd, &configValue, NULL, encoding);
503 if(RemoveCR)
504 return configValue.Tokenize(_T("\n"),start);
505 return configValue;
509 int CGit::SetConfigValue(CString key, CString value, CONFIG_TYPE type, int encoding, CString *GitPath)
511 if(this->m_IsUseGitDLL)
513 CAutoLocker lock(g_Git.m_critGitDllSec);
517 CheckAndInitDll();
519 }catch(...)
522 CStringA keya, valuea;
523 keya = CUnicodeUtils::GetMulti(key, CP_UTF8);
524 valuea = CUnicodeUtils::GetMulti(value, encoding);
525 CStringA p;
526 if(GitPath)
527 p = CUnicodeUtils::GetMulti(*GitPath, CP_UTF8);
529 return get_set_config(keya.GetBuffer(), valuea.GetBuffer(), type, p.GetBuffer());
532 else
534 CString cmd;
535 CString option;
536 switch(type)
538 case CONFIG_GLOBAL:
539 option = _T("--global");
540 break;
541 case CONFIG_SYSTEM:
542 option = _T("--system");
543 break;
544 default:
545 break;
547 cmd.Format(_T("git.exe config %s %s \"%s\""), option, key, value);
548 CString out;
549 if (Run(cmd, &out, NULL, encoding))
551 return -1;
554 return 0;
557 int CGit::UnsetConfigValue(CString key, CONFIG_TYPE type, int encoding, CString *GitPath)
559 if(this->m_IsUseGitDLL)
561 CAutoLocker lock(g_Git.m_critGitDllSec);
565 CheckAndInitDll();
566 }catch(...)
569 CStringA keya;
570 keya = CUnicodeUtils::GetMulti(key, CP_UTF8);
571 CStringA p;
572 if(GitPath)
573 p=CUnicodeUtils::GetMulti(*GitPath,CP_ACP);
575 return get_set_config(keya.GetBuffer(), NULL, type, p.GetBuffer());
577 else
579 CString cmd;
580 CString option;
581 switch(type)
583 case CONFIG_GLOBAL:
584 option = _T("--global");
585 break;
586 case CONFIG_SYSTEM:
587 option = _T("--system");
588 break;
589 default:
590 break;
592 cmd.Format(_T("git.exe config %s --unset %s"), option, key);
593 CString out;
594 if (Run(cmd, &out, NULL, encoding))
596 return -1;
599 return 0;
602 CString CGit::GetCurrentBranch(void)
604 CString output;
605 //Run(_T("git.exe branch"),&branch);
607 if(this->GetCurrentBranchFromFile(this->m_CurrentDir,output))
609 return _T("(no branch)");
611 else
612 return output;
616 CString CGit::GetSymbolicRef(const wchar_t* symbolicRefName, bool bStripRefsHeads)
618 CString refName;
619 if(this->m_IsUseGitDLL)
621 unsigned char sha1[20];
622 int flag;
624 CAutoLocker lock(g_Git.m_critGitDllSec);
625 const char *refs_heads_master = git_resolve_ref(CUnicodeUtils::GetUTF8(CString(symbolicRefName)), sha1, 0, &flag);
626 if(refs_heads_master && (flag&REF_ISSYMREF))
628 g_Git.StringAppend(&refName,(BYTE*)refs_heads_master);
629 if(bStripRefsHeads)
630 refName = StripRefName(refName);
634 else
636 CString cmd;
637 cmd.Format(L"git symbolic-ref %s", symbolicRefName);
638 if (Run(cmd, &refName, NULL, CP_UTF8) != 0)
639 return CString();//Error
640 int iStart = 0;
641 refName = refName.Tokenize(L"\n", iStart);
642 if(bStripRefsHeads)
643 refName = StripRefName(refName);
645 return refName;
648 CString CGit::GetFullRefName(CString shortRefName)
650 CString refName;
651 CString cmd;
652 cmd.Format(L"git rev-parse --symbolic-full-name %s", shortRefName);
653 if (Run(cmd, &refName, NULL, CP_UTF8) != 0)
654 return CString();//Error
655 int iStart = 0;
656 return refName.Tokenize(L"\n", iStart);
659 CString CGit::StripRefName(CString refName)
661 if(wcsncmp(refName, L"refs/heads/", 11) == 0)
662 refName = refName.Mid(11);
663 else if(wcsncmp(refName, L"refs/", 5) == 0)
664 refName = refName.Mid(5);
665 int start =0;
666 return refName.Tokenize(_T("\n"),start);
669 int CGit::GetCurrentBranchFromFile(const CString &sProjectRoot, CString &sBranchOut)
671 // read current branch name like git-gui does, by parsing the .git/HEAD file directly
673 if ( sProjectRoot.IsEmpty() )
674 return -1;
676 CString sDotGitPath;
677 if (!g_GitAdminDir.GetAdminDirPath(sProjectRoot, sDotGitPath))
678 return -1;
680 CString sHeadFile = sDotGitPath + _T("HEAD");
682 FILE *pFile;
683 _tfopen_s(&pFile, sHeadFile.GetString(), _T("r"));
685 if (!pFile)
687 return -1;
690 char s[256] = {0};
691 fgets(s, sizeof(s), pFile);
693 fclose(pFile);
695 const char *pfx = "ref: refs/heads/";
696 const int len = 16;//strlen(pfx)
698 if ( !strncmp(s, pfx, len) )
700 //# We're on a branch. It might not exist. But
701 //# HEAD looks good enough to be a branch.
702 CStringA utf8Branch(s + len);
703 sBranchOut = CUnicodeUtils::GetUnicode(utf8Branch);
704 sBranchOut.TrimRight(_T(" \r\n\t"));
706 if ( sBranchOut.IsEmpty() )
707 return -1;
709 else
711 //# Assume this is a detached head.
712 sBranchOut = "HEAD";
714 return 1;
717 return 0;
720 int CGit::BuildOutputFormat(CString &format,bool IsFull)
722 CString log;
723 log.Format(_T("#<%c>%%x00"),LOG_REV_ITEM_BEGIN);
724 format += log;
725 if(IsFull)
727 log.Format(_T("#<%c>%%an%%x00"),LOG_REV_AUTHOR_NAME);
728 format += log;
729 log.Format(_T("#<%c>%%ae%%x00"),LOG_REV_AUTHOR_EMAIL);
730 format += log;
731 log.Format(_T("#<%c>%%ai%%x00"),LOG_REV_AUTHOR_DATE);
732 format += log;
733 log.Format(_T("#<%c>%%cn%%x00"),LOG_REV_COMMIT_NAME);
734 format += log;
735 log.Format(_T("#<%c>%%ce%%x00"),LOG_REV_COMMIT_EMAIL);
736 format += log;
737 log.Format(_T("#<%c>%%ci%%x00"),LOG_REV_COMMIT_DATE);
738 format += log;
739 log.Format(_T("#<%c>%%b%%x00"),LOG_REV_COMMIT_BODY);
740 format += log;
743 log.Format(_T("#<%c>%%m%%H%%x00"),LOG_REV_COMMIT_HASH);
744 format += log;
745 log.Format(_T("#<%c>%%P%%x00"),LOG_REV_COMMIT_PARENT);
746 format += log;
747 log.Format(_T("#<%c>%%s%%x00"),LOG_REV_COMMIT_SUBJECT);
748 format += log;
750 if(IsFull)
752 log.Format(_T("#<%c>%%x00"),LOG_REV_COMMIT_FILE);
753 format += log;
755 return 0;
758 int CGit::GetLog(BYTE_VECTOR& logOut, const CString &hash, CTGitPath *path ,int count,int mask,CString *from,CString *to)
760 CGitCall_ByteVector gitCall(CString(), &logOut, NULL);
761 return GetLog(&gitCall,hash,path,count,mask,from,to);
764 CString CGit::GetLogCmd( const CString &hash, CTGitPath *path, int count, int mask,CString *from,CString *to,bool paramonly,
765 CFilterData *Filter)
767 CString cmd;
768 CString num;
769 CString since;
771 CString file;
773 if(path)
774 file.Format(_T(" -- \"%s\""),path->GetGitPathString());
776 if(count>0)
777 num.Format(_T("-n%d"),count);
779 CString param;
781 if(mask& LOG_INFO_STAT )
782 param += _T(" --numstat ");
783 if(mask& LOG_INFO_FILESTATE)
784 param += _T(" --raw ");
786 if(mask& LOG_INFO_FULLHISTORY)
787 param += _T(" --full-history ");
789 if(mask& LOG_INFO_BOUNDARY)
790 param += _T(" --left-right --boundary ");
792 if(mask& CGit::LOG_INFO_ALL_BRANCH)
793 param += _T(" --all ");
795 if(mask& CGit::LOG_INFO_DETECT_COPYRENAME)
796 param += _T(" -C ");
798 if(mask& CGit::LOG_INFO_DETECT_RENAME )
799 param += _T(" -M ");
801 if(mask& CGit::LOG_INFO_FIRST_PARENT )
802 param += _T(" --first-parent ");
804 if(mask& CGit::LOG_INFO_NO_MERGE )
805 param += _T(" --no-merges ");
807 if(mask& CGit::LOG_INFO_FOLLOW)
808 param += _T(" --follow ");
810 if(mask& CGit::LOG_INFO_SHOW_MERGEDFILE)
811 param += _T(" -c ");
813 if(mask& CGit::LOG_INFO_FULL_DIFF)
814 param += _T(" --full-diff ");
816 if(from != NULL && to != NULL)
818 CString range;
819 range.Format(_T(" %s..%s "),FixBranchName(*from),FixBranchName(*to));
820 param += range;
822 else if(to != NULL && hash.IsEmpty())
824 CString range;
825 range.Format(_T(" %s "), FixBranchName(*to));
826 param += range;
828 param+=hash;
830 CString st1,st2;
832 if( Filter && (Filter->m_From != -1))
834 st1.Format(_T(" --max-age=%I64u "), Filter->m_From);
835 param += st1;
838 if( Filter && (Filter->m_To != -1))
840 st2.Format(_T(" --min-age=%I64u "), Filter->m_To);
841 param += st2;
844 bool isgrep = false;
845 if( Filter && (!Filter->m_Author.IsEmpty()))
847 st1.Format(_T(" --author=\"%s\"" ),Filter->m_Author);
848 param += st1;
849 isgrep = true;
852 if( Filter && (!Filter->m_Committer.IsEmpty()))
854 st1.Format(_T(" --committer=\"%s\"" ),Filter->m_Author);
855 param += st1;
856 isgrep = true;
859 if( Filter && (!Filter->m_MessageFilter.IsEmpty()))
861 st1.Format(_T(" --grep=\"%s\"" ),Filter->m_MessageFilter);
862 param += st1;
863 isgrep = true;
866 if(Filter && isgrep)
868 if(!Filter->m_IsRegex)
869 param += _T(" --fixed-strings ");
871 param += _T(" --regexp-ignore-case --extended-regexp ");
874 if (CRegDWORD(_T("Software\\TortoiseGit\\LogTopoOrder"), TRUE))
875 param += _T(" --topo-order");
877 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
878 cmd.Format(_T("--ignore-this-parameter %s -z %s --parents "), num, param);
879 else
881 CString log;
882 BuildOutputFormat(log,!(mask&CGit::LOG_INFO_ONLY_HASH));
883 cmd.Format(_T("git.exe log %s -z %s --parents --pretty=format:\"%s\""),
884 num,param,log);
887 cmd += file;
889 return cmd;
891 //int CGit::GetLog(CGitCall* pgitCall, const CString &hash, CTGitPath *path ,int count,int mask)
892 int CGit::GetLog(CGitCall* pgitCall, const CString &hash, CTGitPath *path, int count, int mask,CString *from,CString *to)
894 pgitCall->SetCmd( GetLogCmd(hash,path,count,mask,from,to) );
896 return Run(pgitCall);
897 // return Run(cmd,&logOut);
900 #define BUFSIZE 512
901 void GetTempPath(CString &path)
903 TCHAR lpPathBuffer[BUFSIZE];
904 DWORD dwRetVal;
905 DWORD dwBufSize=BUFSIZE;
906 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
907 lpPathBuffer); // buffer for path
908 if (dwRetVal > dwBufSize || (dwRetVal == 0))
910 path=_T("");
912 path.Format(_T("%s"),lpPathBuffer);
914 CString GetTempFile()
916 TCHAR lpPathBuffer[BUFSIZE];
917 DWORD dwRetVal;
918 DWORD dwBufSize=BUFSIZE;
919 TCHAR szTempName[BUFSIZE];
920 UINT uRetVal;
922 dwRetVal = GetTempPath(dwBufSize, // length of the buffer
923 lpPathBuffer); // buffer for path
924 if (dwRetVal > dwBufSize || (dwRetVal == 0))
926 return _T("");
928 // Create a temporary file.
929 uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files
930 TEXT("Patch"), // temp file name prefix
931 0, // create unique name
932 szTempName); // buffer for name
935 if (uRetVal == 0)
937 return _T("");
940 return CString(szTempName);
944 int CGit::RunLogFile(CString cmd,const CString &filename)
946 STARTUPINFO si;
947 PROCESS_INFORMATION pi;
948 si.cb=sizeof(STARTUPINFO);
949 GetStartupInfo(&si);
951 SECURITY_ATTRIBUTES psa={sizeof(psa),NULL,TRUE};;
952 psa.bInheritHandle=TRUE;
954 HANDLE houtfile=CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
955 &psa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
957 si.wShowWindow = SW_HIDE;
958 si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
959 si.hStdOutput = houtfile;
961 LPTSTR pEnv = m_Environment.size()? &m_Environment[0]: NULL;
962 DWORD dwFlags = pEnv ? CREATE_UNICODE_ENVIRONMENT : 0;
964 if(cmd.Find(_T("git")) == 0)
965 cmd=CGit::ms_LastMsysGitDir+_T("\\")+cmd;
967 if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,dwFlags,pEnv,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
969 LPVOID lpMsgBuf;
970 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
971 NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
972 (LPTSTR)&lpMsgBuf,
973 0,NULL);
974 return TGIT_GIT_ERROR_CREATE_PROCESS;
977 WaitForSingleObject(pi.hProcess,INFINITE);
979 CloseHandle(pi.hThread);
980 CloseHandle(pi.hProcess);
981 CloseHandle(houtfile);
982 return TGIT_GIT_SUCCESS;
983 // return 0;
986 CGitHash CGit::GetHash(TCHAR* friendname)
988 if(this->m_IsUseGitDLL)
990 CAutoLocker lock(g_Git.m_critGitDllSec);
992 this->CheckAndInitDll();
994 CGitHash hash;
995 CStringA ref;
996 ref = CUnicodeUtils::GetMulti(FixBranchName(friendname), CP_UTF8);
999 git_get_sha1(ref, hash.m_hash);
1001 }catch(...)
1004 return hash;
1007 else
1009 CString cmd;
1010 CString out;
1011 cmd.Format(_T("git.exe rev-parse %s" ),FixBranchName(friendname));
1012 Run(cmd, &out, NULL, CP_UTF8);
1013 // int pos=out.ReverseFind(_T('\n'));
1014 out.FindOneOf(_T("\r\n"));
1015 return CGitHash(out);
1019 int CGit::GetInitAddList(CTGitPathList &outputlist)
1021 CString cmd;
1022 BYTE_VECTOR cmdout;
1024 cmd=_T("git.exe ls-files -s -t -z");
1025 outputlist.Clear();
1026 if (g_Git.Run(cmd, &cmdout))
1027 return -1;
1029 outputlist.ParserFromLsFile(cmdout);
1030 for(int i=0;i<outputlist.GetCount();i++)
1031 ((unsigned int)outputlist[i].m_Action) = CTGitPath::LOGACTIONS_ADDED;
1033 return 0;
1035 int CGit::GetCommitDiffList(const CString &rev1,const CString &rev2,CTGitPathList &outputlist)
1037 CString cmd;
1039 if(rev1 == GIT_REV_ZERO || rev2 == GIT_REV_ZERO)
1041 //rev1=+_T("");
1042 if(rev1 == GIT_REV_ZERO)
1043 cmd.Format(_T("git.exe diff -r --raw -C -M --numstat -z %s"),rev2);
1044 else
1045 cmd.Format(_T("git.exe diff -r -R --raw -C -M --numstat -z %s"),rev1);
1047 else
1049 cmd.Format(_T("git.exe diff-tree -r --raw -C -M --numstat -z %s %s"),rev2,rev1);
1052 BYTE_VECTOR out;
1053 if (g_Git.Run(cmd, &out))
1054 return -1;
1056 outputlist.ParserFromLog(out);
1058 return 0;
1061 int addto_list_each_ref_fn(const char *refname, const unsigned char * /*sha1*/, int /*flags*/, void *cb_data)
1063 STRING_VECTOR *list = (STRING_VECTOR*)cb_data;
1064 CString str;
1065 g_Git.StringAppend(&str, (BYTE*)refname, CP_UTF8);
1066 list->push_back(str);
1067 return 0;
1070 int CGit::GetTagList(STRING_VECTOR &list)
1072 int ret;
1074 if(this->m_IsUseGitDLL)
1076 CAutoLocker lock(g_Git.m_critGitDllSec);
1077 return git_for_each_ref_in("refs/tags/",addto_list_each_ref_fn, &list);
1080 else
1082 CString cmd, output;
1083 cmd=_T("git.exe tag -l");
1084 int i=0;
1085 ret = g_Git.Run(cmd, &output, NULL, CP_UTF8);
1086 if(!ret)
1088 int pos=0;
1089 CString one;
1090 while( pos>=0 )
1092 i++;
1093 one=output.Tokenize(_T("\n"),pos);
1094 list.push_back(one);
1098 return ret;
1101 CString CGit::FixBranchName_Mod(CString& branchName)
1103 if(branchName == "FETCH_HEAD")
1104 branchName = DerefFetchHead();
1105 return branchName;
1108 CString CGit::FixBranchName(const CString& branchName)
1110 CString tempBranchName = branchName;
1111 FixBranchName_Mod(tempBranchName);
1112 return tempBranchName;
1116 CString CGit::DerefFetchHead()
1118 using namespace std;
1119 CString dotGitPath;
1120 g_GitAdminDir.GetAdminDirPath(m_CurrentDir, dotGitPath);
1121 ifstream fetchHeadFile((dotGitPath + L"FETCH_HEAD").GetString(), ios::in | ios::binary);
1122 int forMergeLineCount = 0;
1123 string line;
1124 string hashToReturn;
1125 while(getline(fetchHeadFile, line))
1127 //Tokenize this line
1128 string::size_type prevPos = 0;
1129 string::size_type pos = line.find('\t');
1130 if(pos == string::npos) continue; //invalid line
1132 string hash = line.substr(0, pos);
1133 ++pos; prevPos = pos; pos = line.find('\t', pos); if(pos == string::npos) continue;
1135 bool forMerge = pos == prevPos;
1136 ++pos; prevPos = pos; pos = line.size(); if(pos == string::npos) continue;
1138 string remoteBranch = line.substr(prevPos, pos - prevPos);
1140 //Process this line
1141 if(forMerge)
1143 hashToReturn = hash;
1144 ++forMergeLineCount;
1145 if(forMergeLineCount > 1)
1146 return L""; //More then 1 branch to merge found. Octopus merge needed. Cannot pick single ref from FETCH_HEAD
1150 return CUnicodeUtils::GetUnicode(hashToReturn.c_str());
1153 int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)
1155 int ret;
1156 CString cmd, output;
1157 cmd = _T("git.exe branch --no-color");
1159 if((type&BRANCH_ALL) == BRANCH_ALL)
1160 cmd += _T(" -a");
1161 else if(type&BRANCH_REMOTE)
1162 cmd += _T(" -r");
1164 int i=0;
1165 ret = g_Git.Run(cmd, &output, NULL, CP_UTF8);
1166 if(!ret)
1168 int pos=0;
1169 CString one;
1170 while( pos>=0 )
1172 one=output.Tokenize(_T("\n"),pos);
1173 one.Trim(L" \r\n\t");
1174 if(one.Find(L" -> ") >= 0 || one.IsEmpty())
1175 continue; // skip something like: refs/origin/HEAD -> refs/origin/master
1176 if(one[0] == _T('*'))
1178 if(current)
1179 *current=i;
1180 one = one.Mid(2);
1182 if (one != _T("(no branch)"))
1183 list.push_back(one);
1184 i++;
1188 if(type & BRANCH_FETCH_HEAD && !DerefFetchHead().IsEmpty())
1189 list.push_back(L"FETCH_HEAD");
1191 return ret;
1194 int CGit::GetRemoteList(STRING_VECTOR &list)
1196 int ret;
1197 CString cmd, output;
1198 cmd=_T("git.exe remote");
1199 ret = g_Git.Run(cmd, &output, NULL, CP_UTF8);
1200 if(!ret)
1202 int pos=0;
1203 CString one;
1204 while( pos>=0 )
1206 one=output.Tokenize(_T("\n"),pos);
1207 list.push_back(one);
1210 return ret;
1213 int CGit::GetRefList(STRING_VECTOR &list)
1215 int ret;
1216 if(this->m_IsUseGitDLL)
1218 CAutoLocker lock(g_Git.m_critGitDllSec);
1219 return git_for_each_ref_in("",addto_list_each_ref_fn, &list);
1222 else
1224 CString cmd, output;
1225 cmd=_T("git.exe show-ref -d");
1226 ret = g_Git.Run(cmd, &output, NULL, CP_UTF8);
1227 if(!ret)
1229 int pos=0;
1230 CString one;
1231 while( pos>=0 )
1233 one=output.Tokenize(_T("\n"),pos);
1234 int start=one.Find(_T(" "),0);
1235 if(start>0)
1237 CString name;
1238 name=one.Right(one.GetLength()-start-1);
1239 list.push_back(name);
1244 return ret;
1247 int addto_map_each_ref_fn(const char *refname, const unsigned char *sha1, int /*flags*/, void *cb_data)
1249 MAP_HASH_NAME *map = (MAP_HASH_NAME*)cb_data;
1250 CString str;
1251 g_Git.StringAppend(&str, (BYTE*)refname, CP_UTF8);
1252 CGitHash hash((char*)sha1);
1254 (*map)[hash].push_back(str);
1256 if(strncmp(refname, "refs/tags", 9) == 0)
1258 GIT_HASH refhash;
1259 if(!git_deref_tag(sha1, refhash))
1261 (*map)[(char*)refhash].push_back(str+_T("^{}"));
1264 return 0;
1267 int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)
1269 int ret;
1270 if(this->m_IsUseGitDLL)
1272 CAutoLocker lock(g_Git.m_critGitDllSec);
1273 return git_for_each_ref_in("",addto_map_each_ref_fn, &map);
1276 else
1278 CString cmd, output;
1279 cmd=_T("git.exe show-ref -d");
1280 ret = g_Git.Run(cmd, &output, NULL, CP_UTF8);
1281 if(!ret)
1283 int pos=0;
1284 CString one;
1285 while( pos>=0 )
1287 one=output.Tokenize(_T("\n"),pos);
1288 int start=one.Find(_T(" "),0);
1289 if(start>0)
1291 CString name;
1292 name=one.Right(one.GetLength()-start-1);
1294 CString hash;
1295 hash=one.Left(start);
1297 map[CGitHash(hash)].push_back(name);
1302 return ret;
1305 BOOL CGit::CheckMsysGitDir()
1307 if (m_bInitialized)
1309 return TRUE;
1312 this->m_Environment.clear();
1313 m_Environment.CopyProcessEnvironment();
1315 TCHAR *oldpath;
1316 size_t homesize,size;
1318 // set HOME if not set already
1319 _tgetenv_s(&homesize, NULL, 0, _T("HOME"));
1320 if (!homesize)
1322 char charBuf[MAX_PATH];
1323 TCHAR buf[MAX_PATH];
1324 strcpy_s(charBuf, MAX_PATH, get_windows_home_directory());
1325 _tcscpy_s(buf, MAX_PATH, CA2CT(charBuf));
1326 m_Environment.SetEnv(_T("HOME"), buf);
1328 CString str;
1330 //setup ssh client
1331 CString sshclient=CRegString(_T("Software\\TortoiseGit\\SSH"));
1333 if(!sshclient.IsEmpty())
1335 m_Environment.SetEnv(_T("GIT_SSH"), sshclient.GetBuffer());
1336 m_Environment.SetEnv(_T("SVN_SSH"), sshclient.GetBuffer());
1338 else
1340 TCHAR sPlink[MAX_PATH];
1341 GetModuleFileName(NULL, sPlink, _countof(sPlink));
1342 LPTSTR ptr = _tcsrchr(sPlink, _T('\\'));
1343 if (ptr) {
1344 _tcscpy(ptr + 1, _T("TortoisePlink.exe"));
1345 m_Environment.SetEnv(_T("GIT_SSH"), sPlink);
1346 m_Environment.SetEnv(_T("SVN_SSH"), sPlink);
1351 TCHAR sAskPass[MAX_PATH];
1352 GetModuleFileName(NULL, sAskPass, _countof(sAskPass));
1353 LPTSTR ptr = _tcsrchr(sAskPass, _T('\\'));
1354 if (ptr)
1356 _tcscpy(ptr + 1, _T("SshAskPass.exe"));
1357 m_Environment.SetEnv(_T("DISPLAY"),_T(":9999"));
1358 m_Environment.SetEnv(_T("SSH_ASKPASS"),sAskPass);
1359 m_Environment.SetEnv(_T("GIT_ASKPASS"),sAskPass);
1363 // add git/bin path to PATH
1365 CRegString msysdir=CRegString(REG_MSYSGIT_PATH,_T(""),FALSE);
1366 str=msysdir;
1367 if(str.IsEmpty())
1369 CRegString msysinstalldir=CRegString(REG_MSYSGIT_INSTALL,_T(""),FALSE,HKEY_LOCAL_MACHINE);
1370 str=msysinstalldir;
1371 if ( !str.IsEmpty() )
1373 str += (str[str.GetLength()-1] != '\\') ? "\\bin" : "bin";
1374 msysdir=str;
1375 CGit::ms_LastMsysGitDir = str;
1376 msysdir.write();
1378 else
1380 // search PATH if git/bin directory is already present
1381 if ( FindGitPath() )
1383 m_bInitialized = TRUE;
1384 msysdir = CGit::ms_LastMsysGitDir;
1385 msysdir.write();
1386 return TRUE;
1389 return FALSE;
1392 else
1394 CGit::ms_LastMsysGitDir = str;
1397 // check for git.exe existance (maybe it was deinstalled in the meantime)
1398 if (!FileExists(CGit::ms_LastMsysGitDir + _T("\\git.exe")))
1399 return FALSE;
1401 //set path
1402 _tdupenv_s(&oldpath,&size,_T("PATH"));
1404 CString path;
1405 path.Format(_T("%s;%s"),oldpath,str + _T(";")+ (CString)CRegString(REG_MSYSGIT_EXTRA_PATH,_T(""),FALSE));
1407 m_Environment.SetEnv(_T("PATH"),path.GetBuffer());
1409 CString str1 = m_Environment.GetEnv(_T("PATH"));
1411 CString sOldPath = oldpath;
1412 free(oldpath);
1414 m_bInitialized = TRUE;
1415 return true;
1418 BOOL CGit::CheckCleanWorkTree()
1420 CString out;
1421 CString cmd;
1422 cmd=_T("git.exe rev-parse --verify HEAD");
1424 if(g_Git.Run(cmd,&out,CP_UTF8))
1425 return FALSE;
1427 cmd=_T("git.exe update-index --ignore-submodules --refresh");
1428 if(g_Git.Run(cmd,&out,CP_UTF8))
1429 return FALSE;
1431 cmd=_T("git.exe diff-files --quiet --ignore-submodules");
1432 if(g_Git.Run(cmd,&out,CP_UTF8))
1433 return FALSE;
1435 cmd=_T("git diff-index --cached --quiet HEAD --ignore-submodules");
1436 if(g_Git.Run(cmd,&out,CP_UTF8))
1437 return FALSE;
1439 return TRUE;
1441 int CGit::Revert(CString commit, CTGitPathList &list, bool)
1443 int ret;
1444 for(int i=0;i<list.GetCount();i++)
1446 ret = Revert(commit, (CTGitPath&)list[i]);
1447 if(ret)
1448 return ret;
1450 return 0;
1452 int CGit::Revert(CString commit, CTGitPath &path)
1454 CString cmd, out;
1456 if(path.m_Action & CTGitPath::LOGACTIONS_REPLACED && !path.GetGitOldPathString().IsEmpty())
1458 cmd.Format(_T("git.exe mv -- \"%s\" \"%s\""),path.GetGitPathString(),path.GetGitOldPathString());
1459 if (g_Git.Run(cmd, &out, CP_UTF8))
1461 ::MessageBox(NULL, out, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
1462 return -1;
1465 cmd.Format(_T("git.exe checkout %s -f -- \"%s\""), commit, path.GetGitOldPathString());
1466 if (g_Git.Run(cmd, &out, CP_UTF8))
1468 ::MessageBox(NULL, out, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
1469 return -1;
1473 else if(path.m_Action & CTGitPath::LOGACTIONS_ADDED)
1474 { //To init git repository, there are not HEAD, so we can use git reset command
1475 cmd.Format(_T("git.exe rm -f --cached -- \"%s\""),path.GetGitPathString());
1477 if (g_Git.Run(cmd, &out, CP_UTF8))
1479 ::MessageBox(NULL, out, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
1480 return -1;
1483 else
1485 cmd.Format(_T("git.exe checkout %s -f -- \"%s\""), commit, path.GetGitPathString());
1486 if (g_Git.Run(cmd, &out, CP_UTF8))
1488 ::MessageBox(NULL, out, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
1489 return -1;
1493 if (path.m_Action & CTGitPath::LOGACTIONS_DELETED)
1495 cmd.Format(_T("git.exe add -f -- \"%s\""), path.GetGitPathString());
1496 if (g_Git.Run(cmd, &out, CP_UTF8))
1498 ::MessageBox(NULL, out, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
1499 return -1;
1503 return 0;
1506 int CGit::ListConflictFile(CTGitPathList &list,CTGitPath *path)
1508 BYTE_VECTOR vector;
1510 CString cmd;
1511 if(path)
1512 cmd.Format(_T("git.exe ls-files -u -t -z -- \"%s\""),path->GetGitPathString());
1513 else
1514 cmd=_T("git.exe ls-files -u -t -z");
1516 if (g_Git.Run(cmd, &vector))
1518 return -1;
1521 list.ParserFromLsFile(vector);
1523 return 0;
1526 bool CGit::IsFastForward(const CString &from, const CString &to)
1528 CString base;
1529 CGitHash basehash,hash;
1530 CString cmd, err;
1531 cmd.Format(_T("git.exe merge-base %s %s"), FixBranchName(to), FixBranchName(from));
1533 if (g_Git.Run(cmd, &base, &err, CP_UTF8))
1535 //CMessageBox::Show(NULL, base + _T("\n") + err, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
1536 return false;
1538 basehash = base;
1540 hash=g_Git.GetHash(from);
1542 return hash == basehash;
1545 unsigned int CGit::Hash2int(CGitHash &hash)
1547 int ret=0;
1548 for(int i=0;i<4;i++)
1550 ret = ret << 8;
1551 ret |= hash.m_hash[i];
1553 return ret;
1556 int CGit::RefreshGitIndex()
1558 if(g_Git.m_IsUseGitDLL)
1562 CAutoLocker lock(g_Git.m_critGitDllSec);
1563 return git_run_cmd("update-index","update-index -q --refresh");
1565 }catch(...)
1567 return -1;
1571 else
1573 CString cmd,output;
1574 cmd=_T("git.exe update-index --refresh");
1575 return Run(cmd, &output, CP_UTF8);
1579 int CGit::GetOneFile(CString Refname, CTGitPath &path, const CString &outputfile)
1581 if(g_Git.m_IsUseGitDLL)
1585 CAutoLocker lock(g_Git.m_critGitDllSec);
1586 g_Git.CheckAndInitDll();
1587 CStringA ref, patha, outa;
1588 ref = CUnicodeUtils::GetMulti(Refname, CP_UTF8);
1589 patha = CUnicodeUtils::GetMulti(path.GetGitPathString(), CP_UTF8);
1590 outa = CUnicodeUtils::GetMulti(outputfile, CP_UTF8);
1591 ::DeleteFile(outputfile);
1592 return git_checkout_file((const char*)ref.GetBuffer(),(const char*)patha.GetBuffer(),(const char*)outa.GetBuffer());
1594 }catch(...)
1596 return -1;
1599 else
1601 CString cmd;
1602 cmd.Format(_T("git.exe cat-file -p %s:\"%s\""), Refname, path.GetGitPathString());
1603 return g_Git.RunLogFile(cmd,outputfile);
1606 void CEnvironment::CopyProcessEnvironment()
1608 TCHAR *p = GetEnvironmentStrings();
1609 while(*p !=0 || *(p+1) !=0)
1610 this->push_back(*p++);
1612 push_back(_T('\0'));
1613 push_back(_T('\0'));
1616 CString CEnvironment::GetEnv(TCHAR *name)
1618 CString str;
1619 for(int i=0;i<size();i++)
1621 str = &(*this)[i];
1622 int start =0;
1623 CString sname = str.Tokenize(_T("="),start);
1624 if(sname.CompareNoCase(name) == 0)
1626 return &(*this)[i+start];
1628 i+=str.GetLength();
1630 return _T("");
1633 void CEnvironment::SetEnv(TCHAR *name, TCHAR* value)
1635 unsigned int i;
1636 for( i=0;i<size();i++)
1638 CString str = &(*this)[i];
1639 int start =0;
1640 CString sname = str.Tokenize(_T("="),start);
1641 if(sname.CompareNoCase(name) == 0)
1643 break;
1645 i+=str.GetLength();
1648 if(i == size())
1650 i -= 1; // roll back terminate \0\0
1651 this->push_back(_T('\0'));
1654 CEnvironment::iterator it;
1655 it=this->begin();
1656 it += i;
1658 while(*it && i<size())
1660 this->erase(it);
1661 it=this->begin();
1662 it += i;
1665 while(*name)
1667 this->insert(it,*name++);
1668 i++;
1669 it= begin()+i;
1672 this->insert(it, _T('='));
1673 i++;
1674 it= begin()+i;
1676 while(*value)
1678 this->insert(it,*value++);
1679 i++;
1680 it= begin()+i;
1685 int CGit::GetGitEncode(TCHAR* configkey)
1687 CString str=GetConfigValue(configkey);
1689 if(str.IsEmpty())
1690 return CP_UTF8;
1692 return CUnicodeUtils::GetCPCode(str);
1696 int CGit::GetDiffPath(CTGitPathList *PathList, CGitHash *hash1, CGitHash *hash2, char *arg)
1698 GIT_FILE file=0;
1699 int ret=0;
1700 GIT_DIFF diff=0;
1702 CAutoLocker lock(g_Git.m_critGitDllSec);
1704 if(arg == NULL)
1705 diff = GetGitDiff();
1706 else
1707 git_open_diff(&diff, arg);
1709 if(diff ==NULL)
1710 return -1;
1712 bool isStat = 0;
1713 if(arg == NULL)
1714 isStat = true;
1715 else
1716 isStat = !!strstr(arg, "stat");
1718 int count=0;
1720 if(hash2 == NULL)
1721 ret = git_root_diff(diff, hash1->m_hash, &file, &count,isStat);
1722 else
1723 ret = git_diff(diff,hash2->m_hash,hash1->m_hash,&file,&count,isStat);
1725 if(ret)
1726 return -1;
1728 CTGitPath path;
1729 CString strnewname;
1730 CString stroldname;
1732 for(int j=0;j<count;j++)
1734 path.Reset();
1735 char *newname;
1736 char *oldname;
1738 strnewname.Empty();
1739 stroldname.Empty();
1741 int mode=0,IsBin=0,inc=0,dec=0;
1742 git_get_diff_file(diff,file,j,&newname,&oldname,
1743 &mode,&IsBin,&inc,&dec);
1745 StringAppend(&strnewname, (BYTE*)newname, CP_UTF8);
1746 StringAppend(&stroldname, (BYTE*)oldname, CP_UTF8);
1748 path.SetFromGit(strnewname,&stroldname);
1749 path.ParserAction((BYTE)mode);
1751 if(IsBin)
1753 path.m_StatAdd=_T("-");
1754 path.m_StatDel=_T("-");
1756 else
1758 path.m_StatAdd.Format(_T("%d"),inc);
1759 path.m_StatDel.Format(_T("%d"),dec);
1761 PathList->AddPath(path);
1763 git_diff_flush(diff);
1765 if(arg)
1766 git_close_diff(diff);
1768 return 0;
1771 int CGit::GetShortHASHLength()
1773 return 7;