OSDN Git Service

Fix Issue 236: CGit::GetRemoteList uses bad regular expression
[tortoisegit/TortoiseGitJp.git] / src / Git / Git.cpp
index 5d62fb2..f100171 100644 (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
-\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
-BOOL CGit::IsVista()\r
-{\r
-\r
-       if( CRegStdWORD(_T("Software\\TortoiseGit\\CacheType") ) == 0)\r
-       {\r
-               return TRUE;\r
-       }\r
-\r
-       OSVERSIONINFO osvi;\r
-    BOOL bIsWindowsXPorLater;\r
-\r
-    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));\r
-    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);\r
-\r
-    GetVersionEx(&osvi);\r
-       \r
-       if(osvi.dwMajorVersion >= 6)\r
-               return TRUE;\r
-       else\r
-               return FALSE;\r
-}\r
-\r
-CGit::CGit(void)\r
-{\r
-       GetCurrentDirectory(MAX_DIRBUFFER,m_CurrentDir.GetBuffer(MAX_DIRBUFFER));\r
-       m_CurrentDir.ReleaseBuffer();\r
-\r
-       CheckMsysGitDir();\r
-}\r
-\r
-CGit::~CGit(void)\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
-       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
-       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
-       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
-       CString UserName;\r
-       Run(_T("git.exe config user.name"),&UserName,CP_UTF8);\r
-       return UserName;\r
-}\r
-CString CGit::GetUserEmail(void)\r
-{\r
-       CString UserName;\r
-       Run(_T("git.exe config user.email"),&UserName,CP_UTF8);\r
-       return UserName;\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"),&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
-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>%%s%%x00"),LOG_REV_COMMIT_SUBJECT);\r
-               format += log;\r
-               log.Format(_T("#<%c>%%b%%x00"),LOG_REV_COMMIT_BODY);\r
-               format += log;\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
-\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)\r
-{\r
-       CGitCall_ByteVector gitCall(CString(),&logOut);\r
-       return GetLog(&gitCall,hash,path,count,mask);\r
-}\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)\r
-{\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
-       param+=hash;\r
-\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
-       cmd += log;\r
-       cmd += CString(_T("\"  "))+hash+file;\r
-\r
-       pgitCall->SetCmd(cmd);\r
-\r
-       return Run(pgitCall);\r
-//     return Run(cmd,&logOut);\r
-}\r
-\r
-#if 0\r
-int CGit::GetShortLog(CString &logOut,CTGitPath * path, int count)\r
-{\r
-       CString cmd;\r
-       CString log;\r
-       int n;\r
-       if(count<0)\r
-               n=100;\r
-       else\r
-               n=count;\r
-       cmd.Format(_T("git.exe log --left-right --boundary --topo-order -n%d --pretty=format:\""),n);\r
-       BuildOutputFormat(log,false);\r
-       cmd += log+_T("\"");\r
-       if (path)\r
-               cmd+= _T("  -- \"")+path->GetGitPathString()+_T("\"");\r
-       //cmd += CString(_T("\" HEAD~40..HEAD"));\r
-       return Run(cmd,&logOut);\r
-}\r
-#endif\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(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
-       if(pos>0)\r
-               return out.Left(pos);\r
-       return out;\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");\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
-                       list.push_back(one.Right(one.GetLength()-2));\r
-                       if(one[0] == _T('*'))\r
-                               if(current)\r
-                                       *current=i;\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::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 size;\r
-\r
-       // set HOME if not set already\r
-       _tgetenv_s(&size, NULL, 0, _T("HOME"));\r
-       if (!size)\r
-       {\r
-               _tdupenv_s(&home,&size,_T("USERPROFILE")); \r
-               _tputenv_s(_T("HOME"),home);\r
-               free(home);\r
-       }\r
-\r
-       //setup ssh client\r
-       CRegString sshclient=CRegString(_T("Software\\TortoiseGit\\SSH"));\r
-       CString ssh=sshclient;\r
-\r
-       if(!ssh.IsEmpty())\r
-       {\r
-               _tputenv_s(_T("GIT_SSH"),ssh);\r
-       }else\r
-       {\r
-               _tputenv_s(_T("GIT_SSH"),_T(""));\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,HKEY_LOCAL_MACHINE);\r
-       CString 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
-       //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
-               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
-#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 char *pszProjectPath, const char *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 char *    m_pszProjectPath;\r
-       const char *    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
-       UINT 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
-               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
-               }\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]) << 8) | HexChar(line[1]);\r
-                               line += 2;\r
-                       }\r
-\r
-                       line++;\r
-               }\r
-\r
-               // filename\r
-               fileStatus.sFileName = line;\r
-\r
-               if ( (*m_pEnumCb)(&fileStatus,m_pUserData) )\r
-                       return false;\r
-\r
-               return true;\r
-       }\r
-};\r
-\r
-BOOL CGit::EnumFiles(const char *pszProjectPath, const char *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(CUnicodeUtils::GetUnicode(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
-       if (pszSubPath)\r
-               cmd.Format(_T("igit.exe %s status %s %s"), CUnicodeUtils::GetUnicode(pszProjectPath), sMode, CUnicodeUtils::GetUnicode(pszSubPath));\r
-       else\r
-               cmd.Format(_T("igit.exe %s status %s"), CUnicodeUtils::GetUnicode(pszProjectPath), sMode);\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
+#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