1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2015 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "GitStatus.h"
24 #include "GitAdminDir.h"
28 #define REG_MSYSGIT_PATH _T("Software\\TortoiseGit\\MSysGit")
29 #define REG_SYSTEM_GITCONFIGPATH _T("Software\\TortoiseGit\\SystemConfig")
30 #define REG_MSYSGIT_EXTRA_PATH _T("Software\\TortoiseGit\\MSysGitExtra")
32 #define DEFAULT_USE_LIBGIT2_MASK (1 << CGit::GIT_CMD_MERGE_BASE) | (1 << CGit::GIT_CMD_DELETETAGBRANCH) | (1 << CGit::GIT_CMD_GETONEFILE) | (1 << CGit::GIT_CMD_ADD) | (1 << CGit::GIT_CMD_CHECKCONFLICTS) | (1 << CGit::GIT_CMD_GET_COMMIT)
34 struct git_repository
;
48 CString m_MessageFilter
;
56 CGitCall(CString cmd
):m_Cmd(cmd
){}
58 CString
GetCmd()const{return m_Cmd
;}
59 void SetCmd(CString cmd
){m_Cmd
=cmd
;}
61 //This function is called when command output data is available.
62 //When this function returns 'true' the git command should be aborted.
63 //This behavior is not implemented yet.
64 virtual bool OnOutputData(const BYTE
* data
, size_t size
)=0;
65 virtual bool OnOutputErrData(const BYTE
* data
, size_t size
)=0;
66 virtual void OnEnd(){}
72 typedef std::function
<void (const CStringA
&)> GitReceiverFunc
;
75 class CEnvironment
: protected std::vector
<TCHAR
>
78 void CopyProcessEnvironment();
79 CString
GetEnv(const TCHAR
*name
);
80 void SetEnv(const TCHAR
* name
, const TCHAR
* value
);
81 void AddToPath(CString value
);
92 GIT_DIFF m_GitSimpleListDiff
;
93 #ifdef GTEST_INCLUDE_GTEST_GTEST_H_
96 bool m_IsGitDllInited
;
98 CComCriticalSection m_critGitDllSec
;
101 DWORD m_IsUseLibGit2_mask
;
103 CEnvironment m_Environment
;
105 static BOOL
GitPathFileExists(const CString
&path
)
107 if(path
[0] == _T('\\') && path
[1] == _T('\\'))
108 //it is netshare \\server\sharefoldername
109 // \\server\.git will create smb error log.
111 int length
= path
.GetLength();
116 int start
= path
.Find(_T('\\'),2);
120 start
= path
.Find(_T('\\'),start
+1);
124 return PathFileExists(path
);
128 return PathFileExists(path
);
130 void CheckAndInitDll()
132 if(!m_IsGitDllInited
)
135 m_IsGitDllInited
=true;
139 GIT_DIFF
GetGitDiff()
145 git_open_diff(&m_GitDiff
,"-C -M -r");
150 GIT_DIFF
GetGitSimpleListDiff()
152 if(m_GitSimpleListDiff
)
153 return m_GitSimpleListDiff
;
156 git_open_diff(&m_GitSimpleListDiff
,"-r -r");
157 return m_GitSimpleListDiff
;
161 BOOL
CheckMsysGitDir(BOOL bFallback
= TRUE
);
162 BOOL
FindAndSetGitExePath(BOOL bFallback
);
169 GIT_CMD_COMMIT_UPDATE_INDEX
,
174 GIT_CMD_DELETETAGBRANCH
,
178 GIT_CMD_CHECK_CLEAN_WT
,
179 GIT_CMD_CHECKCONFLICTS
,
183 bool UsingLibGit2(LIBGIT2_CMD cmd
) const;
185 * callback type should be git_cred_acquire_cb
187 static void SetGit2CredentialCallback(void* callback
);
188 static void SetGit2CertificateCheckCertificate(void* callback
);
190 CString
GetHomeDirectory() const;
191 CString
GetGitLocalConfig() const;
192 CString
GetGitGlobalConfig() const;
193 CString
GetGitGlobalXDGConfigPath() const;
194 CString
GetGitGlobalXDGConfig() const;
195 CString
GetGitSystemConfig() const;
196 git_repository
* GetGitRepository() const;
197 static CStringA
GetGitPathStringA(const CString
&path
);
198 static CString ms_LastMsysGitDir
; // the last msysgitdir added to the path, blank if none
199 static CString ms_MsysGitRootDir
;
200 static int ms_LastMsysGitVersion
;
201 static bool ms_bCygwinGit
;
202 static bool ms_bMsys2Git
;
203 static int m_LogEncode
;
204 static bool IsBranchNameValid(const CString
& branchname
);
205 bool IsBranchTagNameUnique(const CString
& name
);
207 * Checks if a branch or tag with the given name exists
208 *isBranch is true -> branch, tag otherwise
210 bool BranchTagExists(const CString
& name
, bool isBranch
= true);
211 unsigned int Hash2int(const CGitHash
&hash
);
213 PROCESS_INFORMATION m_CurrentGitPi
;
218 int Run(CString cmd
, CString
* output
, int code
);
219 int Run(CString cmd
, CString
* output
, CString
* outputErr
, int code
);
220 int Run(CString cmd
, BYTE_VECTOR
*byte_array
, BYTE_VECTOR
*byte_arrayErr
= NULL
);
221 int Run(CGitCall
* pcall
);
222 int Run(CString cmd
, const GitReceiverFunc
& recv
);
225 static DWORD WINAPI
AsyncReadStdErrThread(LPVOID lpParam
);
226 typedef struct AsyncReadStdErrThreadArguments
230 } ASYNCREADSTDERRTHREADARGS
, *PASYNCREADSTDERRTHREADARGS
;
231 CString
GetUnifiedDiffCmd(const CTGitPath
& path
, const git_revnum_t
& rev1
, const git_revnum_t
& rev2
, bool bMerge
, bool bCombine
, int diffContext
);
234 int RunAsync(CString cmd
, PROCESS_INFORMATION
*pi
, HANDLE
* hRead
, HANDLE
*hErrReadOut
, CString
*StdioFile
= NULL
);
235 int RunLogFile(CString cmd
, const CString
&filename
, CString
*stdErr
);
237 int GetDiffPath(CTGitPathList
*PathList
, CGitHash
*hash1
, CGitHash
*hash2
, char *arg
=NULL
);
239 int GetGitEncode(TCHAR
* configkey
);
241 bool IsFastForward(const CString
&from
, const CString
&to
, CGitHash
* commonAncestor
= NULL
);
242 CString
GetConfigValue(const CString
& name
);
243 bool GetConfigValueBool(const CString
& name
);
244 int GetConfigValueInt32(const CString
& name
, int def
= 0);
246 int SetConfigValue(const CString
& key
, const CString
& value
, CONFIG_TYPE type
= CONFIG_LOCAL
);
247 int UnsetConfigValue(const CString
& key
, CONFIG_TYPE type
= CONFIG_LOCAL
);
249 CString
GetUserName(void);
250 CString
GetUserEmail(void);
251 CString
GetCurrentBranch(bool fallback
= false);
252 void GetRemoteTrackedBranch(const CString
& localBranch
, CString
& remote
, CString
& branch
);
253 void GetRemoteTrackedBranchForHEAD(CString
& remote
, CString
& branch
);
254 // read current branch name from HEAD file, returns 0 on success, -1 on failure, 1 detached (branch name "HEAD" returned)
255 static int GetCurrentBranchFromFile(const CString
&sProjectRoot
, CString
&sBranchOut
, bool fallback
= false);
257 Use this method only when the HEAD is exist.
259 BOOL
CheckCleanWorkTree(bool stagedOk
= false);
260 int Revert(const CString
& commit
, const CTGitPathList
&list
, CString
& err
);
261 int Revert(const CString
& commit
, const CTGitPath
&path
, CString
& err
);
262 int DeleteRef(const CString
& reference
);
264 Use this method only if m_IsUseLibGit2 is used for fallbacks.
265 If you directly use libgit2 methods, use GetLibGit2LastErr instead.
267 CString
GetGitLastErr(const CString
& msg
);
268 CString
GetGitLastErr(const CString
& msg
, LIBGIT2_CMD cmd
);
269 static CString
GetLibGit2LastErr();
270 static CString
GetLibGit2LastErr(const CString
& msg
);
271 bool SetCurrentDir(CString path
, bool submodule
= false)
273 bool b
= GitAdminDir::HasAdminDir(path
, submodule
? false : !!PathIsDirectory(path
), &m_CurrentDir
);
274 if (!b
&& GitAdminDir::IsBareRepo(path
))
279 if(m_CurrentDir
.GetLength() == 2 && m_CurrentDir
[1] == _T(':')) //C: D:
281 m_CurrentDir
+= _T('\\');
285 CString m_CurrentDir
;
289 LOG_ORDER_CHRONOLOGIALREVERSED
,
298 BRANCH_FETCH_HEAD
= 0x4,
299 BRANCH_LOCAL_F
= BRANCH_LOCAL
| BRANCH_FETCH_HEAD
,
300 BRANCH_ALL
= BRANCH_LOCAL
| BRANCH_REMOTE
,
301 BRANCH_ALL_F
= BRANCH_ALL
| BRANCH_FETCH_HEAD
,
307 LOG_INFO_FILESTATE
=0x2,
309 LOG_INFO_FULLHISTORY
=0x8,
310 LOG_INFO_BOUNDARY
=0x10,
311 LOG_INFO_ALL_BRANCH
=0x20,
312 LOG_INFO_ONLY_HASH
=0x40,
313 LOG_INFO_DETECT_RENAME
=0x80,
314 LOG_INFO_DETECT_COPYRENAME
=0x100,
315 LOG_INFO_FIRST_PARENT
= 0x200,
316 LOG_INFO_NO_MERGE
= 0x400,
317 LOG_INFO_FOLLOW
= 0x800,
318 LOG_INFO_SHOW_MERGEDFILE
=0x1000,
319 LOG_INFO_FULL_DIFF
= 0x2000,
320 LOG_INFO_SIMPILFY_BY_DECORATION
= 0x4000,
321 LOG_INFO_LOCAL_BRANCHES
= 0x8000,
337 int GetRemoteList(STRING_VECTOR
&list
);
338 int GetBranchList(STRING_VECTOR
&list
, int *Current
,BRANCH_TYPE type
=BRANCH_LOCAL
);
339 int GetTagList(STRING_VECTOR
&list
);
340 int GetRemoteTags(const CString
& remote
, STRING_VECTOR
& list
);
341 int DeleteRemoteRefs(const CString
& remote
, const STRING_VECTOR
& list
);
342 int GetBranchDescriptions(MAP_STRING_STRING
& map
);
343 int GetMapHashToFriendName(MAP_HASH_NAME
&map
);
344 static int GetMapHashToFriendName(git_repository
* repo
, MAP_HASH_NAME
&map
);
346 CString
DerefFetchHead();
349 // When branchName == FETCH_HEAD, derefrence it.
350 // A selected branch name got from GetBranchList(), with flag BRANCH_FETCH_HEAD enabled,
351 // should go through this function before it is used.
352 CString
FixBranchName_Mod(CString
& branchName
);
353 CString
FixBranchName(const CString
& branchName
);
355 CString
GetLogCmd(const CString
&range
, const CTGitPath
*path
= NULL
, int count
=-1, int InfoMask
= LOG_INFO_FULL_DIFF
|LOG_INFO_STAT
|LOG_INFO_FILESTATE
|LOG_INFO_BOUNDARY
|LOG_INFO_DETECT_COPYRENAME
|LOG_INFO_SHOW_MERGEDFILE
, bool paramonly
=false, CFilterData
* filter
=NULL
);
357 int GetHash(CGitHash
&hash
, const CString
& friendname
);
358 static int GetHash(git_repository
* repo
, CGitHash
&hash
, const CString
& friendname
, bool skipFastCheck
= false);
360 int BuildOutputFormat(CString
&format
,bool IsFull
=TRUE
);
361 static void StringAppend(CString
*str
, const BYTE
*p
, int code
= CP_UTF8
, int length
= -1);
363 BOOL
CanParseRev(CString ref
);
365 Checks if HEAD points to an unborn branch
366 This method assumes, that we already know that we are in a working tree.
369 /** Returns 0 if no conflict, if a conflict was found and -1 in case of a failure */
370 int HasWorkingTreeConflicts();
371 /** Returns 0 if no conflict, if a conflict was found and -1 in case of a failure */
372 int HasWorkingTreeConflicts(git_repository
* repo
);
373 int GetRefList(STRING_VECTOR
&list
);
375 int RefreshGitIndex();
376 int GetOneFile(const CString
&Refname
, const CTGitPath
&path
, const CString
&outputfile
);
378 //Example: master -> refs/heads/master
379 CString
GetFullRefName(const CString
& shortRefName
);
380 //Removes 'refs/heads/' or just 'refs'. Example: refs/heads/master -> master
381 static CString
StripRefName(CString refName
);
383 int GetCommitDiffList(const CString
&rev1
, const CString
&rev2
, CTGitPathList
&outpathlist
, bool ignoreSpaceAtEol
= false, bool ignoreSpaceChange
= false, bool ignoreAllSpace
= false, bool ignoreBlankLines
= false);
384 int GetInitAddList(CTGitPathList
&outpathlist
);
385 int GetWorkingTreeChanges(CTGitPathList
& result
, bool amend
= false, CTGitPathList
* filterlist
= nullptr);
387 __int64
filetime_to_time_t(const FILETIME
*ft
)
389 long long winTime
= ((long long)ft
->dwHighDateTime
<< 32) + ft
->dwLowDateTime
;
390 winTime
-= 116444736000000000LL; /* Windows to Unix Epoch conversion */
391 winTime
/= 10000000; /* Nano to seconds resolution */
392 return (time_t)winTime
;
395 int GetFileModifyTime(LPCTSTR filename
, __int64
* time
, bool* isDir
= nullptr, __int64
* size
= nullptr)
397 WIN32_FILE_ATTRIBUTE_DATA fdata
;
398 if (GetFileAttributesEx(filename
, GetFileExInfoStandard
, &fdata
))
401 *time
= filetime_to_time_t(&fdata
.ftLastWriteTime
);
404 *size
= ((__int64
)fdata
.nFileSizeHigh
<< 32) + fdata
.nFileSizeLow
;
407 *isDir
= !!( fdata
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
414 int GetShortHASHLength() const;
416 static BOOL
GetShortName(const CString
&ref
, CString
&shortname
, CString prefix
)
418 //TRACE(_T("%s %s\r\n"),ref,prefix);
419 if (ref
.Left(prefix
.GetLength()) == prefix
)
421 shortname
= ref
.Right(ref
.GetLength() - prefix
.GetLength());
422 if (shortname
.Right(3) == _T("^{}"))
423 shortname
=shortname
.Left(shortname
.GetLength() - 3);
429 static CString
GetShortName(const CString
& ref
, REF_TYPE
*type
);
431 static bool LoadTextFile(const CString
&filename
, CString
&msg
);
433 int GetUnifiedDiff(const CTGitPath
& path
, const git_revnum_t
& rev1
, const git_revnum_t
& rev2
, CString patchfile
, bool bMerge
, bool bCombine
, int diffContext
);
434 int GetUnifiedDiff(const CTGitPath
& path
, const git_revnum_t
& rev1
, const git_revnum_t
& rev2
, CStringA
* buffer
, bool bMerge
, bool bCombine
, int diffContext
);
436 int GitRevert(int parent
, const CGitHash
&hash
);
438 CString
CombinePath(const CString
&path
) const
442 if (m_CurrentDir
.IsEmpty())
444 return m_CurrentDir
+ (m_CurrentDir
.Right(1) == _T("\\") ? _T("") : _T("\\")) + path
;
447 CString
CombinePath(const CTGitPath
&path
) const
449 return CombinePath(path
.GetWinPath());
452 CString
CombinePath(const CTGitPath
*path
) const
455 return CombinePath(path
->GetWinPath());
458 extern void GetTempPath(CString
&path
);
459 extern CString
GetTempFile();
460 extern DWORD
GetTortoiseGitTempPath(DWORD nBufferLength
, LPTSTR lpBuffer
);