-#include "StdAfx.h"
-#include "Git.h"
-#include "atlconv.h"
-#include "GitRev.h"
-#include "registry.h"
-#include "GitConfig.h"
-#include <map>
-#include "UnicodeUtils.h"
-
-int CGit::m_LogEncode=CP_UTF8;
-
-static LPTSTR nextpath(LPCTSTR src, LPTSTR dst, UINT maxlen)
-{
- LPCTSTR orgsrc;
-
- while (*src == _T(';'))
- src++;
-
- orgsrc = src;
-
- if (!--maxlen)
- goto nullterm;
-
- while (*src && *src != _T(';'))
- {
- if (*src != _T('"'))
- {
- *dst++ = *src++;
- if (!--maxlen)
- {
- orgsrc = src;
- goto nullterm;
- }
- }
- else
- {
- src++;
- while (*src && *src != _T('"'))
- {
- *dst++ = *src++;
- if (!--maxlen)
- {
- orgsrc = src;
- goto nullterm;
- }
- }
-
- if (*src)
- src++;
- }
- }
-
- while (*src == _T(';'))
- src++;
-
-nullterm:
-
- *dst = 0;
-
- return (orgsrc != src) ? (LPTSTR)src : NULL;
-}
-
-static inline BOOL FileExists(LPCTSTR lpszFileName)
-{
- struct _stat st;
- return _tstat(lpszFileName, &st) == 0;
-}
-
-static BOOL FindGitPath()
-{
- size_t size;
- _tgetenv_s(&size, NULL, 0, _T("PATH"));
-
- if (!size)
- {
- return FALSE;
- }
-
- TCHAR *env = (TCHAR*)alloca(size * sizeof(TCHAR));
- _tgetenv_s(&size, env, size, _T("PATH"));
-
- TCHAR buf[_MAX_PATH];
-
- // search in all paths defined in PATH
- while ((env = nextpath(env, buf, _MAX_PATH-1)) && *buf)
- {
- TCHAR *pfin = buf + _tcslen(buf)-1;
-
- // ensure trailing slash
- if (*pfin != _T('/') && *pfin != _T('\\'))
- _tcscpy(++pfin, _T("\\"));
-
- const int len = _tcslen(buf);
-
- if ((len + 7) < _MAX_PATH)
- _tcscpy(pfin+1, _T("git.exe"));
- else
- break;
-
- if ( FileExists(buf) )
- {
- // dir found
- pfin[1] = 0;
- CGit::ms_LastMsysGitDir = buf;
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-
-#define MAX_DIRBUFFER 1000
-#define CALL_OUTPUT_READ_CHUNK_SIZE 1024
-
-CString CGit::ms_LastMsysGitDir;
-CGit g_Git;
-
-// contains system environment that should be used by child processes (RunAsync)
-// initialized by CheckMsysGitDir
-static LPTSTR l_processEnv = NULL;
-
-
-
-CGit::CGit(void)
-{
- GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));
- m_CurrentDir.ReleaseBuffer();
-
- CheckMsysGitDir();
-}
-
-CGit::~CGit(void)
-{
-}
-
-static char g_Buffer[4096];
-
-int CGit::RunAsync(CString cmd,PROCESS_INFORMATION *piOut,HANDLE *hReadOut,CString *StdioFile)
-{
- SECURITY_ATTRIBUTES sa;
- HANDLE hRead, hWrite;
- HANDLE hStdioFile = NULL;
-
- sa.nLength = sizeof(SECURITY_ATTRIBUTES);
- sa.lpSecurityDescriptor=NULL;
- sa.bInheritHandle=TRUE;
- if(!CreatePipe(&hRead,&hWrite,&sa,0))
- {
- return GIT_ERROR_OPEN_PIP;
- }
-
- if(StdioFile)
- {
- hStdioFile=CreateFile(*StdioFile,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
- &sa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
- }
-
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- si.cb=sizeof(STARTUPINFO);
- GetStartupInfo(&si);
-
- si.hStdError=hWrite;
- if(StdioFile)
- si.hStdOutput=hStdioFile;
- else
- si.hStdOutput=hWrite;
-
- si.wShowWindow=SW_HIDE;
- si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
-
- LPTSTR pEnv = l_processEnv;
- DWORD dwFlags = pEnv ? CREATE_UNICODE_ENVIRONMENT : 0;
-
- //DETACHED_PROCESS make ssh recognize that it has no console to launch askpass to input password.
- dwFlags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
-
- memset(&this->m_CurrentGitPi,0,sizeof(PROCESS_INFORMATION));
-
- if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,dwFlags,pEnv,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
- {
- LPVOID lpMsgBuf;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
- NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&lpMsgBuf,
- 0,NULL);
- return GIT_ERROR_CREATE_PROCESS;
- }
-
- m_CurrentGitPi = pi;
-
- CloseHandle(hWrite);
- if(piOut)
- *piOut=pi;
- if(hReadOut)
- *hReadOut=hRead;
-
- return 0;
-
-}
-//Must use sperate function to convert ANSI str to union code string
-//Becuase A2W use stack as internal convert buffer.
-void CGit::StringAppend(CString *str,BYTE *p,int code,int length)
-{
- //USES_CONVERSION;
- //str->Append(A2W_CP((LPCSTR)p,code));
- if(str == NULL)
- return ;
-
- WCHAR * buf;
-
- int len ;
- if(length<0)
- len= strlen((const char*)p);
- else
- len=length;
- //if (len==0)
- // return ;
- //buf = new WCHAR[len*4 + 1];
- buf = str->GetBuffer(len*4+1+str->GetLength())+str->GetLength();
- SecureZeroMemory(buf, (len*4 + 1)*sizeof(WCHAR));
- MultiByteToWideChar(code, 0, (LPCSTR)p, len, buf, len*4);
- str->ReleaseBuffer();
- //str->Append(buf);
- //delete buf;
-}
-BOOL CGit::IsInitRepos()
-{
- CString cmdout;
- cmdout.Empty();
- if(g_Git.Run(_T("git.exe rev-parse --revs-only HEAD"),&cmdout,CP_UTF8))
- {
- // CMessageBox::Show(NULL,cmdout,_T("TortoiseGit"),MB_OK);
- return TRUE;
- }
- if(cmdout.IsEmpty())
- return TRUE;
-
- return FALSE;
-}
-int CGit::Run(CGitCall* pcall)
-{
- PROCESS_INFORMATION pi;
- HANDLE hRead;
- if(RunAsync(pcall->GetCmd(),&pi,&hRead))
- return GIT_ERROR_CREATE_PROCESS;
-
- DWORD readnumber;
- BYTE data[CALL_OUTPUT_READ_CHUNK_SIZE];
- bool bAborted=false;
- while(ReadFile(hRead,data,CALL_OUTPUT_READ_CHUNK_SIZE,&readnumber,NULL))
- {
- //Todo: when OnOutputData() returns 'true', abort git-command. Send CTRL-C signal?
- if(!bAborted)//For now, flush output when command aborted.
- if(pcall->OnOutputData(data,readnumber))
- bAborted=true;
- }
- if(!bAborted)
- pcall->OnEnd();
-
-
- CloseHandle(pi.hThread);
-
- WaitForSingleObject(pi.hProcess, INFINITE);
- DWORD exitcode =0;
-
- if(!GetExitCodeProcess(pi.hProcess,&exitcode))
- {
- return GIT_ERROR_GET_EXIT_CODE;
- }
-
- CloseHandle(pi.hProcess);
-
- CloseHandle(hRead);
- return exitcode;
-}
-class CGitCall_ByteVector : public CGitCall
-{
-public:
- CGitCall_ByteVector(CString cmd,BYTE_VECTOR* pvector):CGitCall(cmd),m_pvector(pvector){}
- virtual bool OnOutputData(const BYTE* data, size_t size)
- {
- size_t oldsize=m_pvector->size();
- m_pvector->resize(m_pvector->size()+size);
- memcpy(&*(m_pvector->begin()+oldsize),data,size);
- return false;
- }
- BYTE_VECTOR* m_pvector;
-
-};
-int CGit::Run(CString cmd,BYTE_VECTOR *vector)
-{
- CGitCall_ByteVector call(cmd,vector);
- return Run(&call);
-}
-int CGit::Run(CString cmd, CString* output,int code)
-{
- BYTE_VECTOR vector;
- int ret;
- ret=Run(cmd,&vector);
-
- vector.push_back(0);
-
- StringAppend(output,&(vector[0]),code);
- return ret;
-}
-
-CString CGit::GetUserName(void)
-{
- return GetConfigValue(L"user.name");
-}
-CString CGit::GetUserEmail(void)
-{
- return GetConfigValue(L"user.email");
-}
-
-CString CGit::GetConfigValue(CString name)
-{
- CString configValue;
- CString cmd;
- cmd.Format(L"git.exe config %s", name);
- Run(cmd,&configValue,CP_UTF8);
- int start = 0;
- return configValue.Tokenize(_T("\n"),start);
-}
-
-
-CString CGit::GetCurrentBranch(void)
-{
- CString output;
- //Run(_T("git.exe branch"),&branch);
-
- int ret=g_Git.Run(_T("git.exe branch --no-color"),&output,CP_UTF8);
- if(!ret)
- {
- int pos=0;
- CString one;
- while( pos>=0 )
- {
- //i++;
- one=output.Tokenize(_T("\n"),pos);
- //list.push_back(one.Right(one.GetLength()-2));
- if(one[0] == _T('*'))
- return one.Right(one.GetLength()-2);
- }
- }
- return CString("");
-}
-
-CString CGit::GetSymbolicRef(const wchar_t* symbolicRefName, bool bStripRefsHeads)
-{
- CString refName;
- CString cmd;
- cmd.Format(L"git symbolic-ref %s", symbolicRefName);
- if(Run(cmd, &refName, CP_UTF8) != 0)
- return CString();//Error
- int iStart = 0;
- refName = refName.Tokenize(L"\n", iStart);
- if(bStripRefsHeads)
- refName = StripRefName(refName);
- return refName;
-}
-
-CString CGit::GetFullRefName(CString shortRefName)
-{
- CString refName;
- CString cmd;
- cmd.Format(L"git rev-parse --symbolic-full-name %s", shortRefName);
- if(Run(cmd, &refName, CP_UTF8) != 0)
- return CString();//Error
- int iStart = 0;
- return refName.Tokenize(L"\n", iStart);
-}
-
-CString CGit::StripRefName(CString refName)
-{
- if(wcsncmp(refName, L"refs/heads/", 11) == 0)
- refName = refName.Mid(11);
- else if(wcsncmp(refName, L"refs/", 5) == 0)
- refName = refName.Mid(5);
- return refName;
-}
-
-int CGit::GetCurrentBranchFromFile(const CString &sProjectRoot, CString &sBranchOut)
-{
- // read current branch name like git-gui does, by parsing the .git/HEAD file directly
-
- if ( sProjectRoot.IsEmpty() )
- return -1;
-
- CString sHeadFile = sProjectRoot + _T("\\") + g_GitAdminDir.GetAdminDirName() + _T("\\HEAD");
-
- FILE *pFile;
- _tfopen_s(&pFile, sHeadFile.GetString(), _T("r"));
-
- if (!pFile)
- {
- return -1;
- }
-
- char s[256] = {0};
- fgets(s, sizeof(s), pFile);
-
- fclose(pFile);
-
- const char *pfx = "ref: refs/heads/";
- const int len = 16;//strlen(pfx)
-
- if ( !strncmp(s, pfx, len) )
- {
- //# We're on a branch. It might not exist. But
- //# HEAD looks good enough to be a branch.
- sBranchOut = s + len;
- sBranchOut.TrimRight(_T(" \r\n\t"));
-
- if ( sBranchOut.IsEmpty() )
- return -1;
- }
- else
- {
- //# Assume this is a detached head.
- sBranchOut = "HEAD";
-
- return 1;
- }
-
- return 0;
-}
-
-int CGit::BuildOutputFormat(CString &format,bool IsFull)
-{
- CString log;
- log.Format(_T("#<%c>%%x00"),LOG_REV_ITEM_BEGIN);
- format += log;
- if(IsFull)
- {
- log.Format(_T("#<%c>%%an%%x00"),LOG_REV_AUTHOR_NAME);
- format += log;
- log.Format(_T("#<%c>%%ae%%x00"),LOG_REV_AUTHOR_EMAIL);
- format += log;
- log.Format(_T("#<%c>%%ai%%x00"),LOG_REV_AUTHOR_DATE);
- format += log;
- log.Format(_T("#<%c>%%cn%%x00"),LOG_REV_COMMIT_NAME);
- format += log;
- log.Format(_T("#<%c>%%ce%%x00"),LOG_REV_COMMIT_EMAIL);
- format += log;
- log.Format(_T("#<%c>%%ci%%x00"),LOG_REV_COMMIT_DATE);
- format += log;
- log.Format(_T("#<%c>%%b%%x00"),LOG_REV_COMMIT_BODY);
- format += log;
- }
-
- log.Format(_T("#<%c>%%m%%H%%x00"),LOG_REV_COMMIT_HASH);
- format += log;
- log.Format(_T("#<%c>%%P%%x00"),LOG_REV_COMMIT_PARENT);
- format += log;
- log.Format(_T("#<%c>%%s%%x00"),LOG_REV_COMMIT_SUBJECT);
- format += log;
-
- if(IsFull)
- {
- log.Format(_T("#<%c>%%x00"),LOG_REV_COMMIT_FILE);
- format += log;
- }
- return 0;
-}
-
-int CGit::GetLog(BYTE_VECTOR& logOut, CString &hash, CTGitPath *path ,int count,int mask,CString *from,CString *to)
-{
- CGitCall_ByteVector gitCall(CString(),&logOut);
- return GetLog(&gitCall,hash,path,count,mask,from,to);
-}
-
-//int CGit::GetLog(CGitCall* pgitCall, CString &hash, CTGitPath *path ,int count,int mask)
-int CGit::GetLog(CGitCall* pgitCall, CString &hash, CTGitPath *path, int count, int mask,CString *from,CString *to)
-{
-
- CString cmd;
- CString log;
- CString num;
- CString since;
-
- CString file;
-
- if(path)
- file.Format(_T(" -- \"%s\""),path->GetGitPathString());
-
- if(count>0)
- num.Format(_T("-n%d"),count);
-
- CString param;
-
- if(mask& LOG_INFO_STAT )
- param += _T(" --numstat ");
- if(mask& LOG_INFO_FILESTATE)
- param += _T(" --raw ");
-
- if(mask& LOG_INFO_FULLHISTORY)
- param += _T(" --full-history ");
-
- if(mask& LOG_INFO_BOUNDARY)
- param += _T(" --left-right --boundary ");
-
- if(mask& CGit::LOG_INFO_ALL_BRANCH)
- param += _T(" --all ");
-
- if(mask& CGit::LOG_INFO_DETECT_COPYRENAME)
- param += _T(" -C ");
-
- if(mask& CGit::LOG_INFO_DETECT_RENAME )
- param += _T(" -M ");
-
- if(mask& CGit::LOG_INFO_FIRST_PARENT )
- param += _T(" --first-parent ");
-
- if(mask& CGit::LOG_INFO_NO_MERGE )
- param += _T(" --no-merges ");
-
- if(mask& CGit::LOG_INFO_FOLLOW)
- param += _T(" --follow ");
-
- if(mask& CGit::LOG_INFO_SHOW_MERGEDFILE)
- param += _T(" -c ");
-
- if(mask& CGit::LOG_INFO_FULL_DIFF)
- param += _T(" --full-diff ");
-
- if(from != NULL && to != NULL)
- {
- CString range;
- range.Format(_T(" %s..%s "),*from,*to);
- param += range;
- }
- param+=hash;
-
- cmd.Format(_T("git.exe log %s -z --topo-order %s --parents --pretty=format:\""),
- num,param);
-
- BuildOutputFormat(log,!(mask&CGit::LOG_INFO_ONLY_HASH));
-
- cmd += log;
- cmd += CString(_T("\" "))+hash+file;
-
- pgitCall->SetCmd(cmd);
-
- return Run(pgitCall);
-// return Run(cmd,&logOut);
-}
-
-#if 0
-int CGit::GetShortLog(CString &logOut,CTGitPath * path, int count)
-{
- CString cmd;
- CString log;
- int n;
- if(count<0)
- n=100;
- else
- n=count;
- cmd.Format(_T("git.exe log --left-right --boundary --topo-order -n%d --pretty=format:\""),n);
- BuildOutputFormat(log,false);
- cmd += log+_T("\"");
- if (path)
- cmd+= _T(" -- \"")+path->GetGitPathString()+_T("\"");
- //cmd += CString(_T("\" HEAD~40..HEAD"));
- return Run(cmd,&logOut);
-}
-#endif
-
-#define BUFSIZE 512
-void GetTempPath(CString &path)
-{
- TCHAR lpPathBuffer[BUFSIZE];
- DWORD dwRetVal;
- DWORD dwBufSize=BUFSIZE;
- dwRetVal = GetTempPath(dwBufSize, // length of the buffer
- lpPathBuffer); // buffer for path
- if (dwRetVal > dwBufSize || (dwRetVal == 0))
- {
- path=_T("");
- }
- path.Format(_T("%s"),lpPathBuffer);
-}
-CString GetTempFile()
-{
- TCHAR lpPathBuffer[BUFSIZE];
- DWORD dwRetVal;
- DWORD dwBufSize=BUFSIZE;
- TCHAR szTempName[BUFSIZE];
- UINT uRetVal;
-
- dwRetVal = GetTempPath(dwBufSize, // length of the buffer
- lpPathBuffer); // buffer for path
- if (dwRetVal > dwBufSize || (dwRetVal == 0))
- {
- return _T("");
- }
- // Create a temporary file.
- uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files
- TEXT("Patch"), // temp file name prefix
- 0, // create unique name
- szTempName); // buffer for name
-
-
- if (uRetVal == 0)
- {
- return _T("");
- }
-
- return CString(szTempName);
-
-}
-
-int CGit::RunLogFile(CString cmd,CString &filename)
-{
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- si.cb=sizeof(STARTUPINFO);
- GetStartupInfo(&si);
-
- SECURITY_ATTRIBUTES psa={sizeof(psa),NULL,TRUE};;
- psa.bInheritHandle=TRUE;
-
- HANDLE houtfile=CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,
- &psa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
-
-
- si.wShowWindow=SW_HIDE;
- si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
- si.hStdOutput = houtfile;
-
- if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,NULL,NULL,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))
- {
- LPVOID lpMsgBuf;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
- NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&lpMsgBuf,
- 0,NULL);
- return GIT_ERROR_CREATE_PROCESS;
- }
-
- WaitForSingleObject(pi.hProcess,INFINITE);
-
- CloseHandle(pi.hThread);
- CloseHandle(pi.hProcess);
- CloseHandle(houtfile);
- return GIT_SUCCESS;
-// return 0;
-}
-
-git_revnum_t CGit::GetHash(const CString &friendname)
-{
- CString cmd;
- CString out;
- cmd.Format(_T("git.exe rev-parse %s" ),friendname);
- Run(cmd,&out,CP_UTF8);
-// int pos=out.ReverseFind(_T('\n'));
- int pos=out.FindOneOf(_T("\r\n"));
- if(pos>0)
- return out.Left(pos);
- return out;
-}
-
-int CGit::GetCommitDiffList(CString &rev1,CString &rev2,CTGitPathList &outputlist)
-{
- CString cmd;
-
- if(rev1 == GIT_REV_ZERO || rev2 == GIT_REV_ZERO)
- {
- //rev1=+_T("");
- if(rev1 == GIT_REV_ZERO)
- cmd.Format(_T("git.exe diff -r --raw -C -M --numstat -z %s"),rev2);
- else
- cmd.Format(_T("git.exe diff -r -R --raw -C -M --numstat -z %s"),rev1);
- }else
- {
- cmd.Format(_T("git.exe diff-tree -r --raw -C -M --numstat -z %s %s"),rev2,rev1);
- }
-
- BYTE_VECTOR out;
- if(g_Git.Run(cmd,&out))
- return -1;
-
- outputlist.ParserFromLog(out);
-
-}
-
-int CGit::GetTagList(STRING_VECTOR &list)
-{
- int ret;
- CString cmd,output;
- cmd=_T("git.exe tag -l");
- int i=0;
- ret=g_Git.Run(cmd,&output,CP_UTF8);
- if(!ret)
- {
- int pos=0;
- CString one;
- while( pos>=0 )
- {
- i++;
- one=output.Tokenize(_T("\n"),pos);
- list.push_back(one);
- }
- }
- return ret;
-}
-
-int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)
-{
- int ret;
- CString cmd,output;
- cmd=_T("git.exe branch --no-color");
-
- if(type==(BRANCH_LOCAL|BRANCH_REMOTE))
- cmd+=_T(" -a");
- else if(type==BRANCH_REMOTE)
- cmd+=_T(" -r");
-
- int i=0;
- ret=g_Git.Run(cmd,&output,CP_UTF8);
- if(!ret)
- {
- int pos=0;
- CString one;
- while( pos>=0 )
- {
- one=output.Tokenize(_T("\n"),pos);
- one.Trim(L" \r\n\t");
- if(one.Find(L" -> ") >= 0 || one.IsEmpty())
- continue; // skip something like: refs/origin/HEAD -> refs/origin/master
- if(one[0] == _T('*'))
- {
- if(current)
- *current=i;
- one = one.Mid(2);
- }
- list.push_back(one);
- i++;
- }
- }
- return ret;
-}
-
-int CGit::GetRemoteList(STRING_VECTOR &list)
-{
- int ret;
- CString cmd,output;
- cmd=_T("git.exe config --get-regexp '^^remote[.].*[.]url'");
- ret=g_Git.Run(cmd,&output,CP_UTF8);
- if(!ret)
- {
- int pos=0;
- CString one;
- while( pos>=0 )
- {
- one=output.Tokenize(_T("\n"),pos);
- int start=one.Find(_T("."),0);
- if(start>0)
- {
- CString url;
- url=one.Right(one.GetLength()-start-1);
- one=url;
- one=one.Left(one.Find(_T("."),0));
- list.push_back(one);
- }
- }
- }
- return ret;
-}
-
-int CGit::GetRefList(STRING_VECTOR &list)
-{
- int ret;
- CString cmd,output;
- cmd=_T("git show-ref -d");
- ret=g_Git.Run(cmd,&output,CP_UTF8);
- if(!ret)
- {
- int pos=0;
- CString one;
- while( pos>=0 )
- {
- one=output.Tokenize(_T("\n"),pos);
- int start=one.Find(_T(" "),0);
- if(start>0)
- {
- CString name;
- name=one.Right(one.GetLength()-start-1);
- list.push_back(name);
- }
- }
- }
- return ret;
-}
-int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)
-{
- int ret;
- CString cmd,output;
- cmd=_T("git show-ref -d");
- ret=g_Git.Run(cmd,&output,CP_UTF8);
- if(!ret)
- {
- int pos=0;
- CString one;
- while( pos>=0 )
- {
- one=output.Tokenize(_T("\n"),pos);
- int start=one.Find(_T(" "),0);
- if(start>0)
- {
- CString name;
- name=one.Right(one.GetLength()-start-1);
-
- CString hash;
- hash=one.Left(start);
-
- map[hash].push_back(name);
- }
- }
- }
- return ret;
-}
-
-BOOL CGit::CheckMsysGitDir()
-{
- static BOOL bInitialized = FALSE;
-
- if (bInitialized)
- {
- return TRUE;
- }
-
- TCHAR *oldpath,*home;
- size_t homesize,size,httpsize;
-
- // set HOME if not set already
- _tgetenv_s(&homesize, NULL, 0, _T("HOME"));
- if (!homesize)
- {
- _tdupenv_s(&home,&size,_T("USERPROFILE"));
- _tputenv_s(_T("HOME"),home);
- free(home);
- }
- CString str;
-
-#ifndef _TORTOISESHELL
- //set http_proxy
- _tgetenv_s(&httpsize, NULL, 0, _T("http_proxy"));
- if (!httpsize)
- {
- CString regServeraddress_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-host"), _T(""));
- CString regServerport_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-port"), _T(""));
- CString regUsername_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-username"), _T(""));
- CString regPassword_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-password"), _T(""));
- CString regTimeout_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-timeout"), _T(""));
- CString regExceptions_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-exceptions"), _T(""));
-
- CString http_proxy;
- if(!regServeraddress_copy.IsEmpty())
- {
- if(regServeraddress_copy.Left(4) != _T("http"))
- http_proxy=_T("http://");
-
- if(!regUsername_copy.IsEmpty())
- {
- http_proxy += regUsername_copy;
- http_proxy += _T(":")+regPassword_copy;
- http_proxy += _T("@");
- }
- http_proxy+=regServeraddress_copy;
- if(!regServerport_copy.IsEmpty())
- {
- http_proxy +=_T(":")+regServerport_copy;
- }
- _tputenv_s(_T("http_proxy"),http_proxy);
- }
- }
- //setup ssh client
- CString sshclient=CRegString(_T("Software\\TortoiseGit\\SSH"));
-
- if(!sshclient.IsEmpty())
- {
- _tputenv_s(_T("GIT_SSH"),sshclient);
-
- //Setup SVN_SSH
- CString ssh=sshclient;
- ssh.Replace(_T("/"),_T("\\"));
- ssh.Replace(_T("\\"),_T("\\\\"));
- ssh=CString(_T("\""))+ssh+_T('\"');
- _tputenv_s(_T("SVN_SSH"),ssh);
-
- }else
- {
- TCHAR sPlink[MAX_PATH];
- GetModuleFileName(NULL, sPlink, _countof(sPlink));
- LPTSTR ptr = _tcsrchr(sPlink, _T('\\'));
- if (ptr) {
- _tcscpy(ptr + 1, _T("TortoisePlink.exe"));
- _tputenv_s(_T("GIT_SSH"), sPlink);
-
- //Setup SVN_SSH
- CString ssh=sPlink;
- ssh.Replace(_T("/"),_T("\\"));
- ssh.Replace(_T("\\"),_T("\\\\"));
- ssh=CString(_T("\""))+ssh+_T('\"');
- _tputenv_s(_T("SVN_SSH"),ssh);
- }
- }
-
- {
- TCHAR sAskPass[MAX_PATH];
- GetModuleFileName(NULL, sAskPass, _countof(sAskPass));
- LPTSTR ptr = _tcsrchr(sAskPass, _T('\\'));
- if (ptr)
- {
- _tcscpy(ptr + 1, _T("SshAskPass.exe"));
- _tputenv_s(_T("DISPLAY"),_T(":9999"));
- _tputenv_s(_T("SSH_ASKPASS"),sAskPass);
- }
- }
- // search PATH if git/bin directory is alredy present
- if ( FindGitPath() )
- {
- bInitialized = TRUE;
- return TRUE;
- }
-
- // add git/bin path to PATH
-
- CRegString msysdir=CRegString(REG_MSYSGIT_PATH,_T(""),FALSE);
- str=msysdir;
- if(str.IsEmpty())
- {
- CRegString msysinstalldir=CRegString(REG_MSYSGIT_INSTALL,_T(""),FALSE,HKEY_LOCAL_MACHINE);
- str=msysinstalldir;
- if ( !str.IsEmpty() )
- {
- str += (str[str.GetLength()-1] != '\\') ? "\\bin" : "bin";
- msysdir=str;
- msysdir.write();
- }
- else
- {
- return false;
- }
- }
-#endif
- //CGit::m_MsysGitPath=str;
-
- //set path
-
- _tdupenv_s(&oldpath,&size,_T("PATH"));
-
- CString path;
- path.Format(_T("%s;%s"),oldpath,str);
-
- _tputenv_s(_T("PATH"),path);
-
- CString sOldPath = oldpath;
- free(oldpath);
-
-
- if( !FindGitPath() )
- {
- if(!homesize)
- {
- _tputenv_s(_T("HOME"),_T(""));
- }
- return false;
- }
- else
- {
-#ifdef _TORTOISESHELL
- l_processEnv = GetEnvironmentStrings();
- // updated environment is now duplicated for use in CreateProcess, restore original PATH for current process
- _tputenv_s(_T("PATH"),sOldPath);
- if(!homesize)
- {
- _tputenv_s(_T("HOME"),_T(""));
- }
-#endif
-
- bInitialized = TRUE;
- return true;
- }
-}
-
-
-class CGitCall_EnumFiles : public CGitCall
-{
-public:
- CGitCall_EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)
- : m_pszProjectPath(pszProjectPath),
- m_pszSubPath(pszSubPath),
- m_nFlags(nFlags),
- m_pEnumCb(pEnumCb),
- m_pUserData(pUserData)
- {
- }
-
- typedef std::map<CStringA,char> TStrCharMap;
-
- const TCHAR * m_pszProjectPath;
- const TCHAR * m_pszSubPath;
- unsigned int m_nFlags;
- WGENUMFILECB * m_pEnumCb;
- void * m_pUserData;
-
- BYTE_VECTOR m_DataCollector;
-
- virtual bool OnOutputData(const BYTE* data, size_t size)
- {
- m_DataCollector.append(data,size);
- while(true)
- {
- // lines from igit.exe are 0 terminated
- int found=m_DataCollector.findData((const BYTE*)"",1);
- if(found<0)
- return false;
- OnSingleLine( (LPCSTR)&*m_DataCollector.begin() );
- m_DataCollector.erase(m_DataCollector.begin(), m_DataCollector.begin()+found+1);
- }
- return false;//Should never reach this
- }
- virtual void OnEnd()
- {
- }
-
- BYTE HexChar(char ch)
- {
- if (ch >= '0' && ch <= '9')
- return (UINT)(ch - '0');
- else if (ch >= 'A' && ch <= 'F')
- return (UINT)(ch - 'A') + 10;
- else if (ch >= 'a' && ch <= 'f')
- return (UINT)(ch - 'a') + 10;
- else
- return 0;
- }
-
- bool OnSingleLine(LPCSTR line)
- {
- //Parse single line
-
- wgFile_s fileStatus;
-
- // file/dir type
-
- fileStatus.nFlags = 0;
- if (*line == 'D')
- fileStatus.nFlags |= WGFF_Directory;
- else if (*line != 'F')
- // parse error
- return false;
- line += 2;
-
- // status
-
- fileStatus.nStatus = WGFS_Unknown;
- switch (*line)
- {
- case 'N': fileStatus.nStatus = WGFS_Normal; break;
- case 'M': fileStatus.nStatus = WGFS_Modified; break;
- case 'S': fileStatus.nStatus = WGFS_Staged; break;
- case 'A': fileStatus.nStatus = WGFS_Added; break;
- case 'C': fileStatus.nStatus = WGFS_Conflicted; break;
- case 'D': fileStatus.nStatus = WGFS_Deleted; break;
- case 'I': fileStatus.nStatus = WGFS_Ignored; break;
- case 'U': fileStatus.nStatus = WGFS_Unversioned; break;
- case 'E': fileStatus.nStatus = WGFS_Empty; break;
- case '?': fileStatus.nStatus = WGFS_Unknown; break;
- default:
- // parse error
- return false;
- }
- line += 2;
-
- // file sha1
-
- BYTE sha1[20];
- fileStatus.sha1 = NULL;
- if ( !(fileStatus.nFlags & WGFF_Directory) )
- {
- for (int i=0; i<20; i++)
- {
- sha1[i] = (HexChar(line[0])<<4)&0xF0;
- sha1[i] |= HexChar(line[1])&0xF;
-
- line += 2;
- }
-
- line++;
- }
-
- // filename
- int len = strlen(line);
- if (len && len < 2048)
- {
- WCHAR *buf = (WCHAR*)alloca((len*4+2)*sizeof(WCHAR));
- *buf = 0;
- MultiByteToWideChar(CP_ACP, 0, line, len+1, buf, len*4+1);
- fileStatus.sFileName = buf;
-
- if (*buf && (*m_pEnumCb)(&fileStatus,m_pUserData))
- return false;
- }
-
- return true;
- }
-};
-
-BOOL CGit::EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)
-{
- if(!pszProjectPath || *pszProjectPath=='\0')
- return FALSE;
-
- CGitCall_EnumFiles W_GitCall(pszProjectPath,pszSubPath,nFlags,pEnumCb,pUserData);
- CString cmd;
-
-/* char W_szToDir[MAX_PATH];
- strncpy(W_szToDir,pszProjectPath,sizeof(W_szToDir)-1);
- if(W_szToDir[strlen(W_szToDir)-1]!='\\')
- strncat(W_szToDir,"\\",sizeof(W_szToDir)-1);
-
- SetCurrentDirectoryA(W_szToDir);
- GetCurrentDirectoryA(sizeof(W_szToDir)-1,W_szToDir);
-*/
- SetCurrentDir(pszProjectPath);
-
- CString sMode;
- if (nFlags)
- {
- if (nFlags & WGEFF_NoRecurse) sMode += _T("r");
- if (nFlags & WGEFF_FullPath) sMode += _T("f");
- if (nFlags & WGEFF_DirStatusDelta) sMode += _T("d");
- if (nFlags & WGEFF_DirStatusAll) sMode += _T("D");
- if (nFlags & WGEFF_EmptyAsNormal) sMode += _T("e");
- if (nFlags & WGEFF_SingleFile) sMode += _T("s");
- }
- else
- {
- sMode = _T("-");
- }
-
- // NOTE: there seems to be some issue with msys based app receiving backslash on commandline, at least
- // if followed by " like for example 'igit "C:\"', the commandline igit receives is 'igit.exe C:" status' with
- // the 'C:" status' part as a single arg, Maybe it uses unix style processing. In order to avoid this just
- // use forward slashes for supplied project and sub paths
-
- CString sProjectPath = pszProjectPath;
- sProjectPath.Replace(_T('\\'), _T('/'));
-
- if (pszSubPath)
- {
- CString sSubPath = pszSubPath;
- sSubPath.Replace(_T('\\'), _T('/'));
-
- cmd.Format(_T("igit.exe \"%s\" status %s \"%s\""), sProjectPath, sMode, sSubPath);
- }
- else
- {
- cmd.Format(_T("igit.exe \"%s\" status %s"), sProjectPath, sMode);
- }
-
- //OutputDebugStringA("---");OutputDebugStringW(cmd);OutputDebugStringA("\r\n");
-
- W_GitCall.SetCmd(cmd);
- // NOTE: should igit get added as a part of msysgit then use below line instead of the above one
- //W_GitCall.SetCmd(CGit::ms_LastMsysGitDir + cmd);
-
- if ( Run(&W_GitCall) )
- return FALSE;
-
- return TRUE;
-}
-
-BOOL CGit::CheckCleanWorkTree()
-{
- CString out;
- CString cmd;
- cmd=_T("git.exe rev-parse --verify HEAD");
-
- if(g_Git.Run(cmd,&out,CP_UTF8))
- return FALSE;
-
- cmd=_T("git.exe update-index --ignore-submodules --refresh");
- if(g_Git.Run(cmd,&out,CP_UTF8))
- return FALSE;
-
- cmd=_T("git.exe diff-files --quiet --ignore-submodules");
- if(g_Git.Run(cmd,&out,CP_UTF8))
- return FALSE;
-
- cmd=_T("git diff-index --cached --quiet HEAD --ignore-submodules");
- if(g_Git.Run(cmd,&out,CP_UTF8))
- return FALSE;
-
- return TRUE;
-}
-int CGit::Revert(CTGitPathList &list,bool keep)
-{
- int ret;
- for(int i=0;i<list.GetCount();i++)
- {
- ret = Revert((CTGitPath&)list[i],keep);
- if(ret)
- return ret;
- }
- return 0;
-}
-int CGit::Revert(CTGitPath &path,bool keep)
-{
- CString cmd, out;
- if(path.m_Action & CTGitPath::LOGACTIONS_ADDED)
- { //To init git repository, there are not HEAD, so we can use git reset command
- cmd.Format(_T("git.exe rm --cached -- \"%s\""),path.GetGitPathString());
-
- if(g_Git.Run(cmd,&out,CP_ACP))
- return -1;
- }
- else if(path.m_Action & CTGitPath::LOGACTIONS_REPLACED )
- {
- cmd.Format(_T("git.exe mv -- \"%s\" \"%s\""),path.GetGitPathString(),path.GetGitOldPathString());
- if(g_Git.Run(cmd,&out,CP_ACP))
- return -1;
-
- cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitOldPathString());
- if(g_Git.Run(cmd,&out,CP_ACP))
- return -1;
- }
- else
- {
- cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitPathString());
- if(g_Git.Run(cmd,&out,CP_ACP))
- return -1;
- }
- return 0;
-}
-
-int CGit::ListConflictFile(CTGitPathList &list,CTGitPath *path)
-{
- BYTE_VECTOR vector;
-
- CString cmd;
- if(path)
- cmd.Format(_T("git.exe ls-files -u -t -z -- \"%s\""),path->GetGitPathString());
- else
- cmd=_T("git.exe ls-files -u -t -z");
-
- if(g_Git.Run(cmd,&vector))
- {
- return -1;
- }
-
- list.ParserFromLsFile(vector);
-
- return 0;
-}
-
-bool CGit::IsFastForward(CString &from, CString &to)
-{
- CString base,hash;
- CString cmd;
- cmd.Format(_T("git.exe merge-base %s %s"), to,from);
-
- if(g_Git.Run(cmd,&base,CP_ACP))
- {
- //CMessageBox::Show(NULL,base,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
- return false;
- }
- base=base.Left(40);
-
- hash=g_Git.GetHash(from);
-
- hash=hash.Left(40);
-
- return hash == base;
-}
-
-unsigned int CGit::Hash2int(CString &hash)
-{
- int ret=0;
- for(int i=0;i<8;i++)
- {
- ret =ret <<4;
- if(hash[i]>=_T('a'))
- ret |= (hash[i]-_T('a')+10)&0xFF;
- else if(hash[i]>=_T('A'))
- ret |= (hash[i]-_T('A')+10)&0xFF;
- else
- ret |= (hash[i]-_T('0'))&0xFF;
-
- }
- return ret;
-}
\ No newline at end of file
+#include "StdAfx.h"\r
+#include "Git.h"\r
+#include "atlconv.h"\r
+#include "GitRev.h"\r
+#include "registry.h"\r
+#include "GitConfig.h"\r
+#include <map>\r
+#include "UnicodeUtils.h"\r
+#include "gitdll.h"\r
+\r
+int CGit::m_LogEncode=CP_UTF8;\r
+\r
+\r
+static LPTSTR nextpath(LPCTSTR src, LPTSTR dst, UINT maxlen)\r
+{\r
+ LPCTSTR orgsrc;\r
+\r
+ while (*src == _T(';'))\r
+ src++;\r
+\r
+ orgsrc = src;\r
+\r
+ if (!--maxlen)\r
+ goto nullterm;\r
+\r
+ while (*src && *src != _T(';'))\r
+ {\r
+ if (*src != _T('"'))\r
+ {\r
+ *dst++ = *src++;\r
+ if (!--maxlen)\r
+ {\r
+ orgsrc = src;\r
+ goto nullterm;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ src++;\r
+ while (*src && *src != _T('"'))\r
+ {\r
+ *dst++ = *src++;\r
+ if (!--maxlen)\r
+ {\r
+ orgsrc = src;\r
+ goto nullterm;\r
+ }\r
+ }\r
+\r
+ if (*src)\r
+ src++;\r
+ }\r
+ }\r
+\r
+ while (*src == _T(';'))\r
+ src++;\r
+\r
+nullterm:\r
+\r
+ *dst = 0;\r
+\r
+ return (orgsrc != src) ? (LPTSTR)src : NULL;\r
+}\r
+\r
+static inline BOOL FileExists(LPCTSTR lpszFileName)\r
+{\r
+ struct _stat st;\r
+ return _tstat(lpszFileName, &st) == 0;\r
+}\r
+\r
+static BOOL FindGitPath()\r
+{\r
+ size_t size;\r
+ _tgetenv_s(&size, NULL, 0, _T("PATH"));\r
+\r
+ if (!size)\r
+ {\r
+ return FALSE;\r
+ }\r
+\r
+ TCHAR *env = (TCHAR*)alloca(size * sizeof(TCHAR));\r
+ _tgetenv_s(&size, env, size, _T("PATH"));\r
+\r
+ TCHAR buf[_MAX_PATH];\r
+\r
+ // search in all paths defined in PATH\r
+ while ((env = nextpath(env, buf, _MAX_PATH-1)) && *buf)\r
+ {\r
+ TCHAR *pfin = buf + _tcslen(buf)-1;\r
+\r
+ // ensure trailing slash\r
+ if (*pfin != _T('/') && *pfin != _T('\\'))\r
+ _tcscpy(++pfin, _T("\\"));\r
+\r
+ const int len = _tcslen(buf);\r
+\r
+ if ((len + 7) < _MAX_PATH)\r
+ _tcscpy(pfin+1, _T("git.exe"));\r
+ else\r
+ break;\r
+\r
+ if ( FileExists(buf) )\r
+ {\r
+ // dir found\r
+ pfin[1] = 0;\r
+ CGit::ms_LastMsysGitDir = buf;\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+#define MAX_DIRBUFFER 1000\r
+#define CALL_OUTPUT_READ_CHUNK_SIZE 1024\r
+\r
+CString CGit::ms_LastMsysGitDir;\r
+CGit g_Git;\r
+\r
+// contains system environment that should be used by child processes (RunAsync)\r
+// initialized by CheckMsysGitDir\r
+static LPTSTR l_processEnv = NULL;\r
+\r
+\r
+\r
+CGit::CGit(void)\r
+{\r
+ GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));\r
+ m_CurrentDir.ReleaseBuffer();\r
+ m_IsGitDllInited = false;\r
+ m_GitDiff=0;\r
+ CheckMsysGitDir();\r
+}\r
+\r
+CGit::~CGit(void)\r
+{\r
+ if(this->m_GitDiff)\r
+ {\r
+ git_close_diff(m_GitDiff);\r
+ m_GitDiff=0;\r
+ }\r
+}\r
+\r
+static char g_Buffer[4096];\r
+\r
+int CGit::RunAsync(CString cmd,PROCESS_INFORMATION *piOut,HANDLE *hReadOut,CString *StdioFile)\r
+{\r
+ SECURITY_ATTRIBUTES sa;\r
+ HANDLE hRead, hWrite;\r
+ HANDLE hStdioFile = NULL;\r
+\r
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);\r
+ sa.lpSecurityDescriptor=NULL;\r
+ sa.bInheritHandle=TRUE;\r
+ if(!CreatePipe(&hRead,&hWrite,&sa,0))\r
+ {\r
+ return GIT_ERROR_OPEN_PIP;\r
+ }\r
+ \r
+ if(StdioFile)\r
+ {\r
+ hStdioFile=CreateFile(*StdioFile,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE, \r
+ &sa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); \r
+ }\r
+\r
+ STARTUPINFO si;\r
+ PROCESS_INFORMATION pi;\r
+ si.cb=sizeof(STARTUPINFO);\r
+ GetStartupInfo(&si);\r
+\r
+ si.hStdError=hWrite;\r
+ if(StdioFile)\r
+ si.hStdOutput=hStdioFile;\r
+ else\r
+ si.hStdOutput=hWrite;\r
+\r
+ si.wShowWindow=SW_HIDE;\r
+ si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;\r
+\r
+ LPTSTR pEnv = l_processEnv;\r
+ DWORD dwFlags = pEnv ? CREATE_UNICODE_ENVIRONMENT : 0;\r
+ \r
+ //DETACHED_PROCESS make ssh recognize that it has no console to launch askpass to input password. \r
+ dwFlags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; \r
+\r
+ memset(&this->m_CurrentGitPi,0,sizeof(PROCESS_INFORMATION));\r
+\r
+ if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,dwFlags,pEnv,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))\r
+ {\r
+ LPVOID lpMsgBuf;\r
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,\r
+ NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+ (LPTSTR)&lpMsgBuf,\r
+ 0,NULL);\r
+ return GIT_ERROR_CREATE_PROCESS;\r
+ }\r
+ \r
+ m_CurrentGitPi = pi;\r
+ \r
+ CloseHandle(hWrite);\r
+ if(piOut)\r
+ *piOut=pi;\r
+ if(hReadOut)\r
+ *hReadOut=hRead;\r
+ \r
+ return 0;\r
+\r
+}\r
+//Must use sperate function to convert ANSI str to union code string\r
+//Becuase A2W use stack as internal convert buffer. \r
+void CGit::StringAppend(CString *str,BYTE *p,int code,int length)\r
+{\r
+ //USES_CONVERSION;\r
+ //str->Append(A2W_CP((LPCSTR)p,code));\r
+ if(str == NULL)\r
+ return ;\r
+\r
+ WCHAR * buf;\r
+\r
+ int len ;\r
+ if(length<0)\r
+ len= strlen((const char*)p);\r
+ else\r
+ len=length;\r
+ //if (len==0)\r
+ // return ;\r
+ //buf = new WCHAR[len*4 + 1];\r
+ buf = str->GetBuffer(len*4+1+str->GetLength())+str->GetLength();\r
+ SecureZeroMemory(buf, (len*4 + 1)*sizeof(WCHAR));\r
+ MultiByteToWideChar(code, 0, (LPCSTR)p, len, buf, len*4);\r
+ str->ReleaseBuffer();\r
+ //str->Append(buf);\r
+ //delete buf;\r
+} \r
+BOOL CGit::IsInitRepos()\r
+{\r
+ CString cmdout;\r
+ cmdout.Empty();\r
+ if(g_Git.Run(_T("git.exe rev-parse --revs-only HEAD"),&cmdout,CP_UTF8))\r
+ {\r
+ // CMessageBox::Show(NULL,cmdout,_T("TortoiseGit"),MB_OK);\r
+ return TRUE;\r
+ }\r
+ if(cmdout.IsEmpty())\r
+ return TRUE;\r
+\r
+ return FALSE;\r
+}\r
+int CGit::Run(CGitCall* pcall)\r
+{\r
+ PROCESS_INFORMATION pi;\r
+ HANDLE hRead;\r
+ if(RunAsync(pcall->GetCmd(),&pi,&hRead))\r
+ return GIT_ERROR_CREATE_PROCESS;\r
+\r
+ DWORD readnumber;\r
+ BYTE data[CALL_OUTPUT_READ_CHUNK_SIZE];\r
+ bool bAborted=false;\r
+ while(ReadFile(hRead,data,CALL_OUTPUT_READ_CHUNK_SIZE,&readnumber,NULL))\r
+ {\r
+ //Todo: when OnOutputData() returns 'true', abort git-command. Send CTRL-C signal?\r
+ if(!bAborted)//For now, flush output when command aborted.\r
+ if(pcall->OnOutputData(data,readnumber))\r
+ bAborted=true;\r
+ }\r
+ if(!bAborted)\r
+ pcall->OnEnd();\r
+\r
+ \r
+ CloseHandle(pi.hThread);\r
+\r
+ WaitForSingleObject(pi.hProcess, INFINITE);\r
+ DWORD exitcode =0;\r
+\r
+ if(!GetExitCodeProcess(pi.hProcess,&exitcode))\r
+ {\r
+ return GIT_ERROR_GET_EXIT_CODE;\r
+ }\r
+\r
+ CloseHandle(pi.hProcess);\r
+\r
+ CloseHandle(hRead);\r
+ return exitcode;\r
+}\r
+class CGitCall_ByteVector : public CGitCall\r
+{\r
+public:\r
+ CGitCall_ByteVector(CString cmd,BYTE_VECTOR* pvector):CGitCall(cmd),m_pvector(pvector){}\r
+ virtual bool OnOutputData(const BYTE* data, size_t size)\r
+ {\r
+ size_t oldsize=m_pvector->size();\r
+ m_pvector->resize(m_pvector->size()+size);\r
+ memcpy(&*(m_pvector->begin()+oldsize),data,size);\r
+ return false;\r
+ }\r
+ BYTE_VECTOR* m_pvector;\r
+\r
+};\r
+int CGit::Run(CString cmd,BYTE_VECTOR *vector)\r
+{\r
+ CGitCall_ByteVector call(cmd,vector);\r
+ return Run(&call);\r
+}\r
+int CGit::Run(CString cmd, CString* output,int code)\r
+{\r
+ BYTE_VECTOR vector;\r
+ int ret;\r
+ ret=Run(cmd,&vector);\r
+\r
+ vector.push_back(0);\r
+ \r
+ StringAppend(output,&(vector[0]),code);\r
+ return ret;\r
+}\r
+\r
+CString CGit::GetUserName(void)\r
+{\r
+ return GetConfigValue(L"user.name");\r
+}\r
+CString CGit::GetUserEmail(void)\r
+{\r
+ return GetConfigValue(L"user.email");\r
+}\r
+\r
+CString CGit::GetConfigValue(CString name)\r
+{\r
+ CString configValue;\r
+ CString cmd;\r
+ cmd.Format(L"git.exe config %s", name);\r
+ Run(cmd,&configValue,CP_UTF8);\r
+ int start = 0;\r
+ return configValue.Tokenize(_T("\n"),start);\r
+}\r
+\r
+\r
+CString CGit::GetCurrentBranch(void)\r
+{\r
+ CString output;\r
+ //Run(_T("git.exe branch"),&branch);\r
+\r
+ int ret=g_Git.Run(_T("git.exe branch --no-color"),&output,CP_UTF8);\r
+ if(!ret)\r
+ { \r
+ int pos=0;\r
+ CString one;\r
+ while( pos>=0 )\r
+ {\r
+ //i++;\r
+ one=output.Tokenize(_T("\n"),pos);\r
+ //list.push_back(one.Right(one.GetLength()-2));\r
+ if(one[0] == _T('*'))\r
+ return one.Right(one.GetLength()-2);\r
+ }\r
+ }\r
+ return CString("");\r
+}\r
+\r
+CString CGit::GetSymbolicRef(const wchar_t* symbolicRefName, bool bStripRefsHeads)\r
+{\r
+ CString refName;\r
+ CString cmd;\r
+ cmd.Format(L"git symbolic-ref %s", symbolicRefName);\r
+ if(Run(cmd, &refName, CP_UTF8) != 0)\r
+ return CString();//Error\r
+ int iStart = 0;\r
+ refName = refName.Tokenize(L"\n", iStart);\r
+ if(bStripRefsHeads)\r
+ refName = StripRefName(refName);\r
+ return refName;\r
+}\r
+\r
+CString CGit::GetFullRefName(CString shortRefName)\r
+{\r
+ CString refName;\r
+ CString cmd;\r
+ cmd.Format(L"git rev-parse --symbolic-full-name %s", shortRefName);\r
+ if(Run(cmd, &refName, CP_UTF8) != 0)\r
+ return CString();//Error\r
+ int iStart = 0;\r
+ return refName.Tokenize(L"\n", iStart);\r
+}\r
+\r
+CString CGit::StripRefName(CString refName)\r
+{\r
+ if(wcsncmp(refName, L"refs/heads/", 11) == 0)\r
+ refName = refName.Mid(11);\r
+ else if(wcsncmp(refName, L"refs/", 5) == 0)\r
+ refName = refName.Mid(5);\r
+ return refName;\r
+}\r
+\r
+int CGit::GetCurrentBranchFromFile(const CString &sProjectRoot, CString &sBranchOut)\r
+{\r
+ // read current branch name like git-gui does, by parsing the .git/HEAD file directly\r
+\r
+ if ( sProjectRoot.IsEmpty() )\r
+ return -1;\r
+\r
+ CString sHeadFile = sProjectRoot + _T("\\") + g_GitAdminDir.GetAdminDirName() + _T("\\HEAD");\r
+\r
+ FILE *pFile;\r
+ _tfopen_s(&pFile, sHeadFile.GetString(), _T("r"));\r
+\r
+ if (!pFile)\r
+ {\r
+ return -1;\r
+ }\r
+\r
+ char s[256] = {0};\r
+ fgets(s, sizeof(s), pFile);\r
+\r
+ fclose(pFile);\r
+\r
+ const char *pfx = "ref: refs/heads/";\r
+ const int len = 16;//strlen(pfx)\r
+\r
+ if ( !strncmp(s, pfx, len) )\r
+ {\r
+ //# We're on a branch. It might not exist. But\r
+ //# HEAD looks good enough to be a branch.\r
+ sBranchOut = s + len;\r
+ sBranchOut.TrimRight(_T(" \r\n\t"));\r
+\r
+ if ( sBranchOut.IsEmpty() )\r
+ return -1;\r
+ }\r
+ else\r
+ {\r
+ //# Assume this is a detached head.\r
+ sBranchOut = "HEAD";\r
+\r
+ return 1;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+int CGit::BuildOutputFormat(CString &format,bool IsFull)\r
+{\r
+ CString log;\r
+ log.Format(_T("#<%c>%%x00"),LOG_REV_ITEM_BEGIN);\r
+ format += log;\r
+ if(IsFull)\r
+ {\r
+ log.Format(_T("#<%c>%%an%%x00"),LOG_REV_AUTHOR_NAME);\r
+ format += log;\r
+ log.Format(_T("#<%c>%%ae%%x00"),LOG_REV_AUTHOR_EMAIL);\r
+ format += log;\r
+ log.Format(_T("#<%c>%%ai%%x00"),LOG_REV_AUTHOR_DATE);\r
+ format += log;\r
+ log.Format(_T("#<%c>%%cn%%x00"),LOG_REV_COMMIT_NAME);\r
+ format += log;\r
+ log.Format(_T("#<%c>%%ce%%x00"),LOG_REV_COMMIT_EMAIL);\r
+ format += log;\r
+ log.Format(_T("#<%c>%%ci%%x00"),LOG_REV_COMMIT_DATE);\r
+ format += log;\r
+ log.Format(_T("#<%c>%%b%%x00"),LOG_REV_COMMIT_BODY);\r
+ format += log;\r
+ }\r
+ \r
+ log.Format(_T("#<%c>%%m%%H%%x00"),LOG_REV_COMMIT_HASH);\r
+ format += log;\r
+ log.Format(_T("#<%c>%%P%%x00"),LOG_REV_COMMIT_PARENT);\r
+ format += log;\r
+ log.Format(_T("#<%c>%%s%%x00"),LOG_REV_COMMIT_SUBJECT);\r
+ format += log;\r
+\r
+ if(IsFull)\r
+ {\r
+ log.Format(_T("#<%c>%%x00"),LOG_REV_COMMIT_FILE);\r
+ format += log;\r
+ }\r
+ return 0;\r
+}\r
+\r
+int CGit::GetLog(BYTE_VECTOR& logOut, CString &hash, CTGitPath *path ,int count,int mask,CString *from,CString *to)\r
+{\r
+ CGitCall_ByteVector gitCall(CString(),&logOut);\r
+ return GetLog(&gitCall,hash,path,count,mask,from,to);\r
+}\r
+\r
+CString CGit::GetLogCmd( CString &hash, CTGitPath *path, int count, int mask,CString *from,CString *to,bool paramonly)\r
+{\r
+ CString cmd;\r
+ CString log;\r
+ CString num;\r
+ CString since;\r
+\r
+ CString file;\r
+\r
+ if(path)\r
+ file.Format(_T(" -- \"%s\""),path->GetGitPathString());\r
+ \r
+ if(count>0)\r
+ num.Format(_T("-n%d"),count);\r
+\r
+ CString param;\r
+\r
+ if(mask& LOG_INFO_STAT )\r
+ param += _T(" --numstat ");\r
+ if(mask& LOG_INFO_FILESTATE)\r
+ param += _T(" --raw ");\r
+\r
+ if(mask& LOG_INFO_FULLHISTORY)\r
+ param += _T(" --full-history ");\r
+\r
+ if(mask& LOG_INFO_BOUNDARY)\r
+ param += _T(" --left-right --boundary ");\r
+\r
+ if(mask& CGit::LOG_INFO_ALL_BRANCH)\r
+ param += _T(" --all ");\r
+\r
+ if(mask& CGit::LOG_INFO_DETECT_COPYRENAME)\r
+ param += _T(" -C ");\r
+ \r
+ if(mask& CGit::LOG_INFO_DETECT_RENAME )\r
+ param += _T(" -M ");\r
+\r
+ if(mask& CGit::LOG_INFO_FIRST_PARENT )\r
+ param += _T(" --first-parent ");\r
+ \r
+ if(mask& CGit::LOG_INFO_NO_MERGE )\r
+ param += _T(" --no-merges ");\r
+\r
+ if(mask& CGit::LOG_INFO_FOLLOW)\r
+ param += _T(" --follow ");\r
+\r
+ if(mask& CGit::LOG_INFO_SHOW_MERGEDFILE)\r
+ param += _T(" -c ");\r
+\r
+ if(mask& CGit::LOG_INFO_FULL_DIFF)\r
+ param += _T(" --full-diff ");\r
+\r
+ if(from != NULL && to != NULL)\r
+ {\r
+ CString range;\r
+ range.Format(_T(" %s..%s "),*from,*to);\r
+ param += range;\r
+ }\r
+ param+=hash;\r
+\r
+ if(paramonly)\r
+ cmd.Format(_T("%s -z --topo-order %s --parents "),\r
+ num,param);\r
+ else\r
+ cmd.Format(_T("git.exe log %s -z --topo-order %s --parents --pretty=format:\""),\r
+ num,param);\r
+\r
+ BuildOutputFormat(log,!(mask&CGit::LOG_INFO_ONLY_HASH));\r
+\r
+ if(paramonly)\r
+ {\r
+ cmd += hash+file;\r
+ }else\r
+ {\r
+ cmd += log;\r
+ cmd += CString(_T("\" "))+hash+file;\r
+ }\r
+\r
+ return cmd;\r
+}\r
+//int CGit::GetLog(CGitCall* pgitCall, CString &hash, CTGitPath *path ,int count,int mask)\r
+int CGit::GetLog(CGitCall* pgitCall, CString &hash, CTGitPath *path, int count, int mask,CString *from,CString *to)\r
+{\r
+ pgitCall->SetCmd( GetLogCmd(hash,path,count,mask,from,to) );\r
+\r
+ return Run(pgitCall);\r
+// return Run(cmd,&logOut);\r
+}\r
+\r
+#define BUFSIZE 512\r
+void GetTempPath(CString &path)\r
+{\r
+ TCHAR lpPathBuffer[BUFSIZE];\r
+ DWORD dwRetVal;\r
+ DWORD dwBufSize=BUFSIZE;\r
+ dwRetVal = GetTempPath(dwBufSize, // length of the buffer\r
+ lpPathBuffer); // buffer for path \r
+ if (dwRetVal > dwBufSize || (dwRetVal == 0))\r
+ {\r
+ path=_T("");\r
+ }\r
+ path.Format(_T("%s"),lpPathBuffer);\r
+}\r
+CString GetTempFile()\r
+{\r
+ TCHAR lpPathBuffer[BUFSIZE];\r
+ DWORD dwRetVal;\r
+ DWORD dwBufSize=BUFSIZE;\r
+ TCHAR szTempName[BUFSIZE]; \r
+ UINT uRetVal;\r
+\r
+ dwRetVal = GetTempPath(dwBufSize, // length of the buffer\r
+ lpPathBuffer); // buffer for path \r
+ if (dwRetVal > dwBufSize || (dwRetVal == 0))\r
+ {\r
+ return _T("");\r
+ }\r
+ // Create a temporary file. \r
+ uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files\r
+ TEXT("Patch"), // temp file name prefix \r
+ 0, // create unique name \r
+ szTempName); // buffer for name \r
+\r
+\r
+ if (uRetVal == 0)\r
+ {\r
+ return _T("");\r
+ }\r
+\r
+ return CString(szTempName);\r
+\r
+}\r
+\r
+int CGit::RunLogFile(CString cmd,CString &filename)\r
+{\r
+ STARTUPINFO si;\r
+ PROCESS_INFORMATION pi;\r
+ si.cb=sizeof(STARTUPINFO);\r
+ GetStartupInfo(&si);\r
+\r
+ SECURITY_ATTRIBUTES psa={sizeof(psa),NULL,TRUE};; \r
+ psa.bInheritHandle=TRUE; \r
+ \r
+ HANDLE houtfile=CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE, \r
+ &psa,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); \r
+\r
+\r
+ si.wShowWindow=SW_HIDE;\r
+ si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;\r
+ si.hStdOutput = houtfile; \r
+ \r
+ if(!CreateProcess(NULL,(LPWSTR)cmd.GetString(), NULL,NULL,TRUE,NULL,NULL,(LPWSTR)m_CurrentDir.GetString(),&si,&pi))\r
+ {\r
+ LPVOID lpMsgBuf;\r
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,\r
+ NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+ (LPTSTR)&lpMsgBuf,\r
+ 0,NULL);\r
+ return GIT_ERROR_CREATE_PROCESS;\r
+ }\r
+ \r
+ WaitForSingleObject(pi.hProcess,INFINITE); \r
+ \r
+ CloseHandle(pi.hThread);\r
+ CloseHandle(pi.hProcess);\r
+ CloseHandle(houtfile);\r
+ return GIT_SUCCESS;\r
+// return 0;\r
+}\r
+\r
+git_revnum_t CGit::GetHash(const CString &friendname)\r
+{\r
+ CString cmd;\r
+ CString out;\r
+ cmd.Format(_T("git.exe rev-parse %s" ),friendname);\r
+ Run(cmd,&out,CP_UTF8);\r
+// int pos=out.ReverseFind(_T('\n'));\r
+ int pos=out.FindOneOf(_T("\r\n"));\r
+ if(pos>0)\r
+ return out.Left(pos);\r
+ return out;\r
+}\r
+\r
+int CGit::GetCommitDiffList(CString &rev1,CString &rev2,CTGitPathList &outputlist)\r
+{\r
+ CString cmd;\r
+ \r
+ if(rev1 == GIT_REV_ZERO || rev2 == GIT_REV_ZERO)\r
+ {\r
+ //rev1=+_T("");\r
+ if(rev1 == GIT_REV_ZERO)\r
+ cmd.Format(_T("git.exe diff -r --raw -C -M --numstat -z %s"),rev2);\r
+ else\r
+ cmd.Format(_T("git.exe diff -r -R --raw -C -M --numstat -z %s"),rev1);\r
+ }else\r
+ {\r
+ cmd.Format(_T("git.exe diff-tree -r --raw -C -M --numstat -z %s %s"),rev2,rev1);\r
+ }\r
+\r
+ BYTE_VECTOR out;\r
+ if(g_Git.Run(cmd,&out))\r
+ return -1;\r
+\r
+ outputlist.ParserFromLog(out);\r
+\r
+}\r
+\r
+int CGit::GetTagList(STRING_VECTOR &list)\r
+{\r
+ int ret;\r
+ CString cmd,output;\r
+ cmd=_T("git.exe tag -l");\r
+ int i=0;\r
+ ret=g_Git.Run(cmd,&output,CP_UTF8);\r
+ if(!ret)\r
+ { \r
+ int pos=0;\r
+ CString one;\r
+ while( pos>=0 )\r
+ {\r
+ i++;\r
+ one=output.Tokenize(_T("\n"),pos);\r
+ list.push_back(one);\r
+ }\r
+ }\r
+ return ret;\r
+}\r
+\r
+int CGit::GetBranchList(STRING_VECTOR &list,int *current,BRANCH_TYPE type)\r
+{\r
+ int ret;\r
+ CString cmd,output;\r
+ cmd=_T("git.exe branch --no-color");\r
+\r
+ if(type==(BRANCH_LOCAL|BRANCH_REMOTE))\r
+ cmd+=_T(" -a");\r
+ else if(type==BRANCH_REMOTE)\r
+ cmd+=_T(" -r");\r
+\r
+ int i=0;\r
+ ret=g_Git.Run(cmd,&output,CP_UTF8);\r
+ if(!ret)\r
+ { \r
+ int pos=0;\r
+ CString one;\r
+ while( pos>=0 )\r
+ {\r
+ one=output.Tokenize(_T("\n"),pos);\r
+ one.Trim(L" \r\n\t");\r
+ if(one.Find(L" -> ") >= 0 || one.IsEmpty())\r
+ continue; // skip something like: refs/origin/HEAD -> refs/origin/master\r
+ if(one[0] == _T('*'))\r
+ {\r
+ if(current)\r
+ *current=i;\r
+ one = one.Mid(2);\r
+ }\r
+ list.push_back(one);\r
+ i++;\r
+ }\r
+ }\r
+ return ret;\r
+}\r
+\r
+int CGit::GetRemoteList(STRING_VECTOR &list)\r
+{\r
+ int ret;\r
+ CString cmd,output;\r
+ cmd=_T("git.exe config --get-regexp \"^^remote[.].*[.]url\"");\r
+ ret=g_Git.Run(cmd,&output,CP_UTF8);\r
+ if(!ret)\r
+ {\r
+ int pos=0;\r
+ CString one;\r
+ while( pos>=0 )\r
+ {\r
+ one=output.Tokenize(_T("\n"),pos);\r
+ int start=one.Find(_T("."),0);\r
+ if(start>0)\r
+ {\r
+ CString url;\r
+ url=one.Right(one.GetLength()-start-1);\r
+ one=url;\r
+ one=one.Left(one.Find(_T("."),0));\r
+ list.push_back(one);\r
+ }\r
+ }\r
+ }\r
+ return ret;\r
+}\r
+\r
+int CGit::GetRefList(STRING_VECTOR &list)\r
+{\r
+ int ret;\r
+ CString cmd,output;\r
+ cmd=_T("git show-ref -d");\r
+ ret=g_Git.Run(cmd,&output,CP_UTF8);\r
+ if(!ret)\r
+ {\r
+ int pos=0;\r
+ CString one;\r
+ while( pos>=0 )\r
+ {\r
+ one=output.Tokenize(_T("\n"),pos);\r
+ int start=one.Find(_T(" "),0);\r
+ if(start>0)\r
+ {\r
+ CString name;\r
+ name=one.Right(one.GetLength()-start-1);\r
+ list.push_back(name);\r
+ }\r
+ }\r
+ }\r
+ return ret;\r
+}\r
+int CGit::GetMapHashToFriendName(MAP_HASH_NAME &map)\r
+{\r
+ int ret;\r
+ CString cmd,output;\r
+ cmd=_T("git show-ref -d");\r
+ ret=g_Git.Run(cmd,&output,CP_UTF8);\r
+ if(!ret)\r
+ {\r
+ int pos=0;\r
+ CString one;\r
+ while( pos>=0 )\r
+ {\r
+ one=output.Tokenize(_T("\n"),pos);\r
+ int start=one.Find(_T(" "),0);\r
+ if(start>0)\r
+ {\r
+ CString name;\r
+ name=one.Right(one.GetLength()-start-1);\r
+\r
+ CString hash;\r
+ hash=one.Left(start);\r
+\r
+ map[hash].push_back(name);\r
+ }\r
+ }\r
+ }\r
+ return ret;\r
+}\r
+\r
+BOOL CGit::CheckMsysGitDir()\r
+{\r
+ static BOOL bInitialized = FALSE;\r
+\r
+ if (bInitialized)\r
+ {\r
+ return TRUE;\r
+ }\r
+\r
+ TCHAR *oldpath,*home;\r
+ size_t homesize,size,httpsize;\r
+\r
+ // set HOME if not set already\r
+ _tgetenv_s(&homesize, NULL, 0, _T("HOME"));\r
+ if (!homesize)\r
+ {\r
+ _tdupenv_s(&home,&size,_T("USERPROFILE")); \r
+ _tputenv_s(_T("HOME"),home);\r
+ free(home);\r
+ }\r
+ CString str;\r
+\r
+#ifndef _TORTOISESHELL\r
+ //set http_proxy\r
+ _tgetenv_s(&httpsize, NULL, 0, _T("http_proxy"));\r
+ if (!httpsize)\r
+ {\r
+ CString regServeraddress_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-host"), _T(""));\r
+ CString regServerport_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-port"), _T(""));\r
+ CString regUsername_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-username"), _T(""));\r
+ CString regPassword_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-password"), _T(""));\r
+ CString regTimeout_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-timeout"), _T(""));\r
+ CString regExceptions_copy = CRegString(_T("Software\\TortoiseGit\\Servers\\global\\http-proxy-exceptions"), _T(""));\r
+\r
+ CString http_proxy;\r
+ if(!regServeraddress_copy.IsEmpty())\r
+ {\r
+ if(regServeraddress_copy.Left(4) != _T("http"))\r
+ http_proxy=_T("http://");\r
+\r
+ if(!regUsername_copy.IsEmpty())\r
+ {\r
+ http_proxy += regUsername_copy;\r
+ http_proxy += _T(":")+regPassword_copy;\r
+ http_proxy += _T("@");\r
+ }\r
+ http_proxy+=regServeraddress_copy;\r
+ if(!regServerport_copy.IsEmpty())\r
+ {\r
+ http_proxy +=_T(":")+regServerport_copy;\r
+ }\r
+ _tputenv_s(_T("http_proxy"),http_proxy);\r
+ }\r
+ }\r
+ //setup ssh client\r
+ CString sshclient=CRegString(_T("Software\\TortoiseGit\\SSH"));\r
+\r
+ if(!sshclient.IsEmpty())\r
+ {\r
+ _tputenv_s(_T("GIT_SSH"),sshclient);\r
+ \r
+ //Setup SVN_SSH\r
+ CString ssh=sshclient;\r
+ ssh.Replace(_T("/"),_T("\\"));\r
+ ssh.Replace(_T("\\"),_T("\\\\"));\r
+ ssh=CString(_T("\""))+ssh+_T('\"');\r
+ _tputenv_s(_T("SVN_SSH"),ssh);\r
+\r
+ }else\r
+ {\r
+ TCHAR sPlink[MAX_PATH];\r
+ GetModuleFileName(NULL, sPlink, _countof(sPlink));\r
+ LPTSTR ptr = _tcsrchr(sPlink, _T('\\'));\r
+ if (ptr) {\r
+ _tcscpy(ptr + 1, _T("TortoisePlink.exe"));\r
+ _tputenv_s(_T("GIT_SSH"), sPlink);\r
+\r
+ //Setup SVN_SSH\r
+ CString ssh=sPlink;\r
+ ssh.Replace(_T("/"),_T("\\"));\r
+ ssh.Replace(_T("\\"),_T("\\\\"));\r
+ ssh=CString(_T("\""))+ssh+_T('\"');\r
+ _tputenv_s(_T("SVN_SSH"),ssh);\r
+ }\r
+ }\r
+\r
+ {\r
+ TCHAR sAskPass[MAX_PATH];\r
+ GetModuleFileName(NULL, sAskPass, _countof(sAskPass));\r
+ LPTSTR ptr = _tcsrchr(sAskPass, _T('\\'));\r
+ if (ptr) \r
+ {\r
+ _tcscpy(ptr + 1, _T("SshAskPass.exe"));\r
+ _tputenv_s(_T("DISPLAY"),_T(":9999"));\r
+ _tputenv_s(_T("SSH_ASKPASS"),sAskPass);\r
+ }\r
+ }\r
+ // search PATH if git/bin directory is alredy present\r
+ if ( FindGitPath() )\r
+ {\r
+ bInitialized = TRUE;\r
+ return TRUE;\r
+ }\r
+\r
+ // add git/bin path to PATH\r
+\r
+ CRegString msysdir=CRegString(REG_MSYSGIT_PATH,_T(""),FALSE);\r
+ str=msysdir;\r
+ if(str.IsEmpty())\r
+ {\r
+ CRegString msysinstalldir=CRegString(REG_MSYSGIT_INSTALL,_T(""),FALSE,HKEY_LOCAL_MACHINE);\r
+ str=msysinstalldir;\r
+ if ( !str.IsEmpty() )\r
+ {\r
+ str += (str[str.GetLength()-1] != '\\') ? "\\bin" : "bin";\r
+ msysdir=str;\r
+ msysdir.write();\r
+ }\r
+ else\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+#endif\r
+ //CGit::m_MsysGitPath=str;\r
+\r
+ //set path\r
+\r
+ _tdupenv_s(&oldpath,&size,_T("PATH")); \r
+\r
+ CString path;\r
+ path.Format(_T("%s;%s"),oldpath,str);\r
+\r
+ _tputenv_s(_T("PATH"),path);\r
+\r
+ CString sOldPath = oldpath;\r
+ free(oldpath);\r
+\r
+\r
+ if( !FindGitPath() )\r
+ {\r
+ if(!homesize)\r
+ {\r
+ _tputenv_s(_T("HOME"),_T(""));\r
+ }\r
+ return false;\r
+ }\r
+ else\r
+ {\r
+#ifdef _TORTOISESHELL\r
+ l_processEnv = GetEnvironmentStrings();\r
+ // updated environment is now duplicated for use in CreateProcess, restore original PATH for current process\r
+ _tputenv_s(_T("PATH"),sOldPath);\r
+ if(!homesize)\r
+ {\r
+ _tputenv_s(_T("HOME"),_T(""));\r
+ }\r
+#endif\r
+\r
+ bInitialized = TRUE;\r
+ return true;\r
+ }\r
+}\r
+\r
+\r
+class CGitCall_EnumFiles : public CGitCall\r
+{\r
+public:\r
+ CGitCall_EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)\r
+ : m_pszProjectPath(pszProjectPath),\r
+ m_pszSubPath(pszSubPath),\r
+ m_nFlags(nFlags),\r
+ m_pEnumCb(pEnumCb),\r
+ m_pUserData(pUserData)\r
+ {\r
+ }\r
+\r
+ typedef std::map<CStringA,char> TStrCharMap;\r
+\r
+ const TCHAR * m_pszProjectPath;\r
+ const TCHAR * m_pszSubPath;\r
+ unsigned int m_nFlags;\r
+ WGENUMFILECB * m_pEnumCb;\r
+ void * m_pUserData;\r
+\r
+ BYTE_VECTOR m_DataCollector;\r
+\r
+ virtual bool OnOutputData(const BYTE* data, size_t size)\r
+ {\r
+ m_DataCollector.append(data,size);\r
+ while(true)\r
+ {\r
+ // lines from igit.exe are 0 terminated\r
+ int found=m_DataCollector.findData((const BYTE*)"",1);\r
+ if(found<0)\r
+ return false;\r
+ OnSingleLine( (LPCSTR)&*m_DataCollector.begin() );\r
+ m_DataCollector.erase(m_DataCollector.begin(), m_DataCollector.begin()+found+1);\r
+ }\r
+ return false;//Should never reach this\r
+ }\r
+ virtual void OnEnd()\r
+ {\r
+ }\r
+\r
+ BYTE HexChar(char ch)\r
+ {\r
+ if (ch >= '0' && ch <= '9')\r
+ return (UINT)(ch - '0');\r
+ else if (ch >= 'A' && ch <= 'F')\r
+ return (UINT)(ch - 'A') + 10;\r
+ else if (ch >= 'a' && ch <= 'f')\r
+ return (UINT)(ch - 'a') + 10;\r
+ else\r
+ return 0;\r
+ }\r
+\r
+ bool OnSingleLine(LPCSTR line)\r
+ {\r
+ //Parse single line\r
+\r
+ wgFile_s fileStatus;\r
+\r
+ // file/dir type\r
+\r
+ fileStatus.nFlags = 0;\r
+ if (*line == 'D')\r
+ fileStatus.nFlags |= WGFF_Directory;\r
+ else if (*line != 'F')\r
+ // parse error\r
+ return false;\r
+ line += 2;\r
+\r
+ // status\r
+\r
+ fileStatus.nStatus = WGFS_Unknown;\r
+ switch (*line)\r
+ {\r
+ case 'N': fileStatus.nStatus = WGFS_Normal; break;\r
+ case 'M': fileStatus.nStatus = WGFS_Modified; break;\r
+ case 'S': fileStatus.nStatus = WGFS_Staged; break;\r
+ case 'A': fileStatus.nStatus = WGFS_Added; break;\r
+ case 'C': fileStatus.nStatus = WGFS_Conflicted; break;\r
+ case 'D': fileStatus.nStatus = WGFS_Deleted; break;\r
+ case 'I': fileStatus.nStatus = WGFS_Ignored; break;\r
+ case 'U': fileStatus.nStatus = WGFS_Unversioned; break;\r
+ case 'E': fileStatus.nStatus = WGFS_Empty; break;\r
+ case '?': fileStatus.nStatus = WGFS_Unknown; break;\r
+ default:\r
+ // parse error\r
+ return false;\r
+ }\r
+ line += 2;\r
+\r
+ // file sha1\r
+\r
+ BYTE sha1[20];\r
+ fileStatus.sha1 = NULL;\r
+ if ( !(fileStatus.nFlags & WGFF_Directory) )\r
+ {\r
+ for (int i=0; i<20; i++)\r
+ {\r
+ sha1[i] = (HexChar(line[0])<<4)&0xF0;\r
+ sha1[i] |= HexChar(line[1])&0xF;\r
+\r
+ line += 2;\r
+ }\r
+\r
+ line++;\r
+ }\r
+\r
+ // filename\r
+ int len = strlen(line);\r
+ if (len && len < 2048)\r
+ {\r
+ WCHAR *buf = (WCHAR*)alloca((len*4+2)*sizeof(WCHAR));\r
+ *buf = 0;\r
+ MultiByteToWideChar(CP_ACP, 0, line, len+1, buf, len*4+1);\r
+ fileStatus.sFileName = buf;\r
+\r
+ if (*buf && (*m_pEnumCb)(&fileStatus,m_pUserData))\r
+ return false;\r
+ }\r
+\r
+ return true;\r
+ }\r
+};\r
+\r
+BOOL CGit::EnumFiles(const TCHAR *pszProjectPath, const TCHAR *pszSubPath, unsigned int nFlags, WGENUMFILECB *pEnumCb, void *pUserData)\r
+{\r
+ if(!pszProjectPath || *pszProjectPath=='\0')\r
+ return FALSE;\r
+\r
+ CGitCall_EnumFiles W_GitCall(pszProjectPath,pszSubPath,nFlags,pEnumCb,pUserData);\r
+ CString cmd;\r
+\r
+/* char W_szToDir[MAX_PATH];\r
+ strncpy(W_szToDir,pszProjectPath,sizeof(W_szToDir)-1);\r
+ if(W_szToDir[strlen(W_szToDir)-1]!='\\')\r
+ strncat(W_szToDir,"\\",sizeof(W_szToDir)-1);\r
+\r
+ SetCurrentDirectoryA(W_szToDir);\r
+ GetCurrentDirectoryA(sizeof(W_szToDir)-1,W_szToDir);\r
+*/\r
+ SetCurrentDir(pszProjectPath);\r
+\r
+ CString sMode;\r
+ if (nFlags)\r
+ {\r
+ if (nFlags & WGEFF_NoRecurse) sMode += _T("r");\r
+ if (nFlags & WGEFF_FullPath) sMode += _T("f");\r
+ if (nFlags & WGEFF_DirStatusDelta) sMode += _T("d");\r
+ if (nFlags & WGEFF_DirStatusAll) sMode += _T("D");\r
+ if (nFlags & WGEFF_EmptyAsNormal) sMode += _T("e");\r
+ if (nFlags & WGEFF_SingleFile) sMode += _T("s");\r
+ }\r
+ else\r
+ {\r
+ sMode = _T("-");\r
+ }\r
+\r
+ // NOTE: there seems to be some issue with msys based app receiving backslash on commandline, at least\r
+ // if followed by " like for example 'igit "C:\"', the commandline igit receives is 'igit.exe C:" status' with\r
+ // the 'C:" status' part as a single arg, Maybe it uses unix style processing. In order to avoid this just\r
+ // use forward slashes for supplied project and sub paths\r
+\r
+ CString sProjectPath = pszProjectPath;\r
+ sProjectPath.Replace(_T('\\'), _T('/'));\r
+\r
+ if (pszSubPath)\r
+ {\r
+ CString sSubPath = pszSubPath;\r
+ sSubPath.Replace(_T('\\'), _T('/'));\r
+\r
+ cmd.Format(_T("tgit.exe statusex \"%s\" status %s \"%s\""), sProjectPath, sMode, sSubPath);\r
+ }\r
+ else\r
+ {\r
+ cmd.Format(_T("tgit.exe statusex \"%s\" status %s"), sProjectPath, sMode);\r
+ }\r
+\r
+ //OutputDebugStringA("---");OutputDebugStringW(cmd);OutputDebugStringA("\r\n");\r
+\r
+ W_GitCall.SetCmd(cmd);\r
+ // NOTE: should igit get added as a part of msysgit then use below line instead of the above one\r
+ //W_GitCall.SetCmd(CGit::ms_LastMsysGitDir + cmd);\r
+\r
+ if ( Run(&W_GitCall) )\r
+ return FALSE;\r
+\r
+ return TRUE;\r
+}\r
+\r
+BOOL CGit::CheckCleanWorkTree()\r
+{\r
+ CString out;\r
+ CString cmd;\r
+ cmd=_T("git.exe rev-parse --verify HEAD");\r
+\r
+ if(g_Git.Run(cmd,&out,CP_UTF8))\r
+ return FALSE;\r
+\r
+ cmd=_T("git.exe update-index --ignore-submodules --refresh");\r
+ if(g_Git.Run(cmd,&out,CP_UTF8))\r
+ return FALSE;\r
+\r
+ cmd=_T("git.exe diff-files --quiet --ignore-submodules");\r
+ if(g_Git.Run(cmd,&out,CP_UTF8))\r
+ return FALSE;\r
+\r
+ cmd=_T("git diff-index --cached --quiet HEAD --ignore-submodules");\r
+ if(g_Git.Run(cmd,&out,CP_UTF8))\r
+ return FALSE;\r
+\r
+ return TRUE;\r
+}\r
+int CGit::Revert(CTGitPathList &list,bool keep)\r
+{\r
+ int ret;\r
+ for(int i=0;i<list.GetCount();i++)\r
+ { \r
+ ret = Revert((CTGitPath&)list[i],keep);\r
+ if(ret)\r
+ return ret;\r
+ }\r
+ return 0;\r
+}\r
+int CGit::Revert(CTGitPath &path,bool keep)\r
+{\r
+ CString cmd, out;\r
+ if(path.m_Action & CTGitPath::LOGACTIONS_ADDED)\r
+ { //To init git repository, there are not HEAD, so we can use git reset command\r
+ cmd.Format(_T("git.exe rm --cached -- \"%s\""),path.GetGitPathString());\r
+\r
+ if(g_Git.Run(cmd,&out,CP_ACP))\r
+ return -1;\r
+ }\r
+ else if(path.m_Action & CTGitPath::LOGACTIONS_REPLACED )\r
+ {\r
+ cmd.Format(_T("git.exe mv -- \"%s\" \"%s\""),path.GetGitPathString(),path.GetGitOldPathString());\r
+ if(g_Git.Run(cmd,&out,CP_ACP))\r
+ return -1;\r
+ \r
+ cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitOldPathString());\r
+ if(g_Git.Run(cmd,&out,CP_ACP))\r
+ return -1;\r
+ }\r
+ else\r
+ {\r
+ cmd.Format(_T("git.exe checkout HEAD -f -- \"%s\""),path.GetGitPathString());\r
+ if(g_Git.Run(cmd,&out,CP_ACP))\r
+ return -1;\r
+ }\r
+ return 0;\r
+}\r
+\r
+int CGit::ListConflictFile(CTGitPathList &list,CTGitPath *path)\r
+{\r
+ BYTE_VECTOR vector;\r
+\r
+ CString cmd;\r
+ if(path)\r
+ cmd.Format(_T("git.exe ls-files -u -t -z -- \"%s\""),path->GetGitPathString());\r
+ else\r
+ cmd=_T("git.exe ls-files -u -t -z");\r
+\r
+ if(g_Git.Run(cmd,&vector))\r
+ {\r
+ return -1;\r
+ }\r
+\r
+ list.ParserFromLsFile(vector);\r
+\r
+ return 0;\r
+}\r
+\r
+bool CGit::IsFastForward(CString &from, CString &to)\r
+{\r
+ CString base,hash;\r
+ CString cmd;\r
+ cmd.Format(_T("git.exe merge-base %s %s"), to,from);\r
+\r
+ if(g_Git.Run(cmd,&base,CP_ACP))\r
+ {\r
+ //CMessageBox::Show(NULL,base,_T("TortoiseGit"),MB_OK|MB_ICONERROR);\r
+ return false;\r
+ }\r
+ base=base.Left(40);\r
+\r
+ hash=g_Git.GetHash(from);\r
+\r
+ hash=hash.Left(40);\r
+ \r
+ return hash == base;\r
+}\r
+\r
+unsigned int CGit::Hash2int(CString &hash)\r
+{\r
+ int ret=0;\r
+ for(int i=0;i<8;i++)\r
+ {\r
+ ret =ret <<4;\r
+ if(hash[i]>=_T('a'))\r
+ ret |= (hash[i]-_T('a')+10)&0xFF;\r
+ else if(hash[i]>=_T('A'))\r
+ ret |= (hash[i]-_T('A')+10)&0xFF;\r
+ else\r
+ ret |= (hash[i]-_T('0'))&0xFF; \r
+ \r
+ }\r
+ return ret;\r
+}\r
+\r
+int CGit::RefreshGitIndex()\r
+{\r
+ CString cmd,output;\r
+ cmd=_T("git.exe update-index --refresh");\r
+ return Run(cmd,&output,CP_ACP);\r
+}\r
+\r