1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2017 - 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 "GitAdminDir.h"
26 #include "StringUtils.h"
27 #include "PathUtils.h"
29 #define REG_MSYSGIT_PATH L"Software\\TortoiseGit\\MSysGit"
30 #define REG_SYSTEM_GITCONFIGPATH L"Software\\TortoiseGit\\SystemConfig"
31 #define REG_MSYSGIT_EXTRA_PATH L"Software\\TortoiseGit\\MSysGitExtra"
33 #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)
35 struct git_repository
;
43 SHOW_NO_LIMIT
, // NOTE: no limitation does not mean "without all limitations", it's just without the following limitations. That say, the log still could be limited by author, committer, etc.
55 m_NumberOfLogsScale
= SHOW_NO_LIMIT
;
59 DWORD m_NumberOfLogsScale
;
65 CString m_MessageFilter
;
73 CGitCall(CString cmd
):m_Cmd(cmd
){}
74 virtual ~CGitCall() {}
76 CString
GetCmd()const{return m_Cmd
;}
77 void SetCmd(CString cmd
){m_Cmd
=cmd
;}
79 //This function is called when command output data is available.
80 //When this function returns 'true' the git command should be aborted.
81 //This behavior is not implemented yet.
82 virtual bool OnOutputData(const BYTE
* data
, size_t size
)=0;
83 virtual bool OnOutputErrData(const BYTE
* data
, size_t size
)=0;
84 virtual void OnEnd(){}
90 typedef std::function
<void (const CStringA
&)> GitReceiverFunc
;
92 class CEnvironment
: protected std::vector
<TCHAR
>
95 CEnvironment() : baseptr(nullptr) {}
96 CEnvironment(const CEnvironment
& env
) : std::vector
<TCHAR
>(env
)
100 CEnvironment
& operator =(const CEnvironment
& env
)
102 __super::operator=(env
);
109 void CopyProcessEnvironment();
110 CString
GetEnv(const TCHAR
*name
);
111 void SetEnv(const TCHAR
* name
, const TCHAR
* value
);
112 void AddToPath(CString value
);
118 CEnvironment(CEnvironment
&& env
) = delete;
119 CEnvironment
& operator =(CEnvironment
&& env
) = delete;
127 GIT_DIFF m_GitSimpleListDiff
;
128 #ifdef GTEST_INCLUDE_GTEST_GTEST_H_
131 bool m_IsGitDllInited
;
133 CComCriticalSection m_critGitDllSec
;
136 DWORD m_IsUseLibGit2_mask
;
138 CEnvironment m_Environment
;
140 static BOOL
GitPathFileExists(const CString
&path
)
142 if (path
[0] == L
'\\' && path
[1] == L
'\\')
143 //it is netshare \\server\sharefoldername
144 // \\server\.git will create smb error log.
146 int length
= path
.GetLength();
151 int start
= path
.Find(L
'\\', 2);
155 start
= path
.Find(L
'\\', start
+ 1);
159 return PathFileExists(path
);
163 return PathFileExists(path
);
165 void CheckAndInitDll()
167 if(!m_IsGitDllInited
)
170 m_IsGitDllInited
=true;
174 GIT_DIFF
GetGitDiff()
180 git_open_diff(&m_GitDiff
,"-C -M -r");
185 GIT_DIFF
GetGitSimpleListDiff()
187 if(m_GitSimpleListDiff
)
188 return m_GitSimpleListDiff
;
191 git_open_diff(&m_GitSimpleListDiff
,"-r -r");
192 return m_GitSimpleListDiff
;
196 BOOL
CheckMsysGitDir(BOOL bFallback
= TRUE
);
197 BOOL
FindAndSetGitExePath(BOOL bFallback
);
204 GIT_CMD_COMMIT_UPDATE_INDEX
,
209 GIT_CMD_DELETETAGBRANCH
,
213 GIT_CMD_CHECK_CLEAN_WT
,
214 GIT_CMD_CHECKCONFLICTS
,
218 bool UsingLibGit2(LIBGIT2_CMD cmd
) const;
220 * callback type should be git_cred_acquire_cb
222 static void SetGit2CredentialCallback(void* callback
);
223 static void SetGit2CertificateCheckCertificate(void* callback
);
225 CString
GetHomeDirectory() const;
226 CString
GetGitLocalConfig() const;
227 CString
GetGitGlobalConfig() const;
228 CString
GetGitGlobalXDGConfigPath() const;
229 CString
GetGitGlobalXDGConfig() const;
230 CString
GetGitSystemConfig() const;
231 CString
GetGitProgramDataConfig() const;
232 CAutoRepository
GetGitRepository() const;
233 static CStringA
GetGitPathStringA(const CString
&path
);
234 static CString ms_LastMsysGitDir
; // the last msysgitdir added to the path, blank if none
235 static CString ms_MsysGitRootDir
;
236 static int ms_LastMsysGitVersion
;
237 static bool ms_bCygwinGit
;
238 static bool ms_bMsys2Git
;
239 static int m_LogEncode
;
240 static bool IsBranchNameValid(const CString
& branchname
);
241 bool IsLocalBranch(const CString
& shortName
);
242 bool IsBranchTagNameUnique(const CString
& name
);
244 * Checks if a branch or tag with the given name exists
245 *isBranch is true -> branch, tag otherwise
247 bool BranchTagExists(const CString
& name
, bool isBranch
= true);
248 unsigned int Hash2int(const CGitHash
&hash
);
250 PROCESS_INFORMATION m_CurrentGitPi
;
255 int Run(CString cmd
, CString
* output
, int code
);
256 int Run(CString cmd
, CString
* output
, CString
* outputErr
, int code
);
257 int Run(CString cmd
, BYTE_VECTOR
* byte_array
, BYTE_VECTOR
* byte_arrayErr
= nullptr);
258 int Run(CGitCall
* pcall
);
259 int Run(CString cmd
, const GitReceiverFunc
& recv
, CString
* outputErr
= nullptr);
262 CComCriticalSection m_critSecThreadMap
;
263 std::map
<DWORD
, HANDLE
> m_AsyncReadStdErrThreadMap
;
264 static DWORD WINAPI
AsyncReadStdErrThread(LPVOID lpParam
);
265 typedef struct AsyncReadStdErrThreadArguments
269 } ASYNCREADSTDERRTHREADARGS
, *PASYNCREADSTDERRTHREADARGS
;
270 CString
GetUnifiedDiffCmd(const CTGitPath
& path
, const CString
& rev1
, const CString
& rev2
, bool bMerge
, bool bCombine
, int diffContext
, bool bNoPrefix
= false);
274 void KillRelatedThreads(CWinThread
* thread
);
276 int RunAsync(CString cmd
, PROCESS_INFORMATION
* pi
, HANDLE
* hRead
, HANDLE
* hErrReadOut
, const CString
* StdioFile
= nullptr);
277 int RunLogFile(CString cmd
, const CString
&filename
, CString
*stdErr
);
279 int GetGitEncode(TCHAR
* configkey
);
281 bool IsFastForward(const CString
& from
, const CString
& to
, CGitHash
* commonAncestor
= nullptr);
282 CString
GetConfigValue(const CString
& name
, const CString
& def
= CString(), bool wantBool
= false);
283 bool GetConfigValueBool(const CString
& name
, const bool def
= false);
284 int GetConfigValueInt32(const CString
& name
, const int def
= 0);
286 int SetConfigValue(const CString
& key
, const CString
& value
, CONFIG_TYPE type
= CONFIG_LOCAL
);
287 int UnsetConfigValue(const CString
& key
, CONFIG_TYPE type
= CONFIG_LOCAL
);
289 CString
GetUserName(void);
290 CString
GetUserEmail(void);
291 CString
GetCurrentBranch(bool fallback
= false);
292 void GetRemoteTrackedBranch(const CString
& localBranch
, CString
& remote
, CString
& branch
);
293 void GetRemoteTrackedBranchForHEAD(CString
& remote
, CString
& branch
);
294 void GetRemotePushBranch(const CString
& localBranch
, CString
& pushRemote
, CString
& pushBranch
);
295 // read current branch name from HEAD file, returns 0 on success, -1 on failure, 1 detached (branch name "HEAD" returned)
296 static int GetCurrentBranchFromFile(const CString
&sProjectRoot
, CString
&sBranchOut
, bool fallback
= false);
298 Use this method only when the HEAD is exist.
300 BOOL
CheckCleanWorkTree(bool stagedOk
= false);
301 int Revert(const CString
& commit
, const CTGitPathList
&list
, CString
& err
);
302 int Revert(const CString
& commit
, const CTGitPath
&path
, CString
& err
);
303 int DeleteRef(const CString
& reference
);
305 Use this method only if m_IsUseLibGit2 is used for fallbacks.
306 If you directly use libgit2 methods, use GetLibGit2LastErr instead.
308 CString
GetGitLastErr(const CString
& msg
);
309 CString
GetGitLastErr(const CString
& msg
, LIBGIT2_CMD cmd
);
310 static CString
GetLibGit2LastErr();
311 static CString
GetLibGit2LastErr(const CString
& msg
);
312 bool SetCurrentDir(CString path
, bool submodule
= false)
314 bool b
= GitAdminDir::HasAdminDir(path
, submodule
? false : !!PathIsDirectory(path
), &m_CurrentDir
);
315 if (!b
&& GitAdminDir::IsBareRepo(path
))
320 if (m_CurrentDir
.GetLength() == 2 && m_CurrentDir
[1] == L
':') //C: D:
321 m_CurrentDir
+= L
'\\';
325 CString m_CurrentDir
;
329 LOG_ORDER_CHRONOLOGIALREVERSED
,
338 BRANCH_FETCH_HEAD
= 0x4,
339 BRANCH_LOCAL_F
= BRANCH_LOCAL
| BRANCH_FETCH_HEAD
,
340 BRANCH_ALL
= BRANCH_LOCAL
| BRANCH_REMOTE
,
341 BRANCH_ALL_F
= BRANCH_ALL
| BRANCH_FETCH_HEAD
,
347 LOG_INFO_FILESTATE
=0x2,
349 LOG_INFO_FULLHISTORY
=0x8,
350 LOG_INFO_BOUNDARY
=0x10,
351 LOG_INFO_ALL_BRANCH
=0x20,
352 LOG_INFO_ONLY_HASH
=0x40,
353 LOG_INFO_DETECT_RENAME
=0x80,
354 LOG_INFO_DETECT_COPYRENAME
=0x100,
355 LOG_INFO_FIRST_PARENT
= 0x200,
356 LOG_INFO_NO_MERGE
= 0x400,
357 LOG_INFO_FOLLOW
= 0x800,
358 LOG_INFO_SHOW_MERGEDFILE
=0x1000,
359 LOG_INFO_FULL_DIFF
= 0x2000,
360 LOG_INFO_SIMPILFY_BY_DECORATION
= 0x4000,
361 LOG_INFO_LOCAL_BRANCHES
= 0x8000,
362 LOG_INFO_BASIC_REFS
= 0x10000,
380 int GetRemoteList(STRING_VECTOR
&list
);
381 int GetBranchList(STRING_VECTOR
& list
, int* current
, BRANCH_TYPE type
= BRANCH_LOCAL
, bool skipCurrent
= false);
382 int GetTagList(STRING_VECTOR
&list
);
383 int GetRefsCommitIsOn(STRING_VECTOR
& list
, const CGitHash
& hash
, bool includeTags
, bool includeBranches
, BRANCH_TYPE type
= BRANCH_LOCAL
);
384 int GetRemoteTags(const CString
& remote
, REF_VECTOR
& list
);
385 int DeleteRemoteRefs(const CString
& remote
, const STRING_VECTOR
& list
);
386 int GetBranchDescriptions(MAP_STRING_STRING
& map
);
387 int GuessRefForHash(CString
& ref
, const CGitHash
& hash
);
388 int GetMapHashToFriendName(MAP_HASH_NAME
&map
);
389 static int GetMapHashToFriendName(git_repository
* repo
, MAP_HASH_NAME
&map
);
391 CString
DerefFetchHead();
394 // When branchName == FETCH_HEAD, dereference it.
395 // A selected branch name got from GetBranchList(), with flag BRANCH_FETCH_HEAD enabled,
396 // should go through this function before it is used.
397 CString
FixBranchName_Mod(CString
& branchName
);
398 CString
FixBranchName(const CString
& branchName
);
400 CString
GetLogCmd(CString range
, const CTGitPath
* path
, int InfoMask
, CFilterData
* filter
, int logOrderBy
);
402 int GetHash(CGitHash
&hash
, const CString
& friendname
);
403 static int GetHash(git_repository
* repo
, CGitHash
&hash
, const CString
& friendname
, bool skipFastCheck
= false);
405 int BuildOutputFormat(CString
&format
,bool IsFull
=TRUE
);
406 static void StringAppend(CString
*str
, const BYTE
*p
, int code
= CP_UTF8
, int length
= -1);
408 BOOL
CanParseRev(CString ref
);
410 Checks if HEAD points to an unborn branch
411 This method assumes, that we already know that we are in a working tree.
414 /** Returns 0 if no conflict, if a conflict was found and -1 in case of a failure */
415 int HasWorkingTreeConflicts();
416 /** Returns 0 if no conflict, if a conflict was found and -1 in case of a failure */
417 int HasWorkingTreeConflicts(git_repository
* repo
);
418 int IsRebaseRunning();
419 void GetBisectTerms(CString
* good
, CString
* bad
);
420 int GetRefList(STRING_VECTOR
&list
);
422 int RefreshGitIndex();
423 int GetOneFile(const CString
&Refname
, const CTGitPath
&path
, const CString
&outputfile
);
425 //Example: master -> refs/heads/master
426 CString
GetFullRefName(const CString
& shortRefName
);
427 //Removes 'refs/heads/' or just 'refs'. Example: refs/heads/master -> master
428 static CString
StripRefName(CString refName
);
430 int GetCommitDiffList(const CString
&rev1
, const CString
&rev2
, CTGitPathList
&outpathlist
, bool ignoreSpaceAtEol
= false, bool ignoreSpaceChange
= false, bool ignoreAllSpace
= false, bool ignoreBlankLines
= false);
431 int GetInitAddList(CTGitPathList
&outpathlist
);
432 int GetWorkingTreeChanges(CTGitPathList
& result
, bool amend
= false, const CTGitPathList
* filterlist
= nullptr);
434 static __int64
filetime_to_time_t(__int64 winTime
)
436 winTime
-= 116444736000000000LL; /* Windows to Unix Epoch conversion */
437 winTime
/= 10000000; /* Nano to seconds resolution */
438 return (time_t)winTime
;
441 static inline __int64
filetime_to_time_t(const FILETIME
*ft
)
443 return filetime_to_time_t(((__int64
)ft
->dwHighDateTime
<< 32) + ft
->dwLowDateTime
);
446 static int GetFileModifyTime(LPCTSTR filename
, __int64
* time
, bool* isDir
= nullptr, __int64
* size
= nullptr, bool* isSymlink
= nullptr)
448 WIN32_FILE_ATTRIBUTE_DATA fdata
;
449 if (GetFileAttributesEx(filename
, GetFileExInfoStandard
, &fdata
))
452 *time
= ((__int64
)fdata
.ftLastWriteTime
.dwHighDateTime
<< 32) + fdata
.ftLastWriteTime
.dwLowDateTime
;
455 *size
= ((__int64
)fdata
.nFileSizeHigh
<< 32) + fdata
.nFileSizeLow
;
458 *isDir
= !!( fdata
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
461 *isSymlink
= (fdata
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) && !CPathUtils::ReadLink(filename
);
468 int GetShortHASHLength() const;
470 static BOOL
GetShortName(const CString
& ref
, CString
& shortname
, const CString
& prefix
)
472 //TRACE(L"%s %s\r\n", ref, prefix);
473 if (CStringUtils::StartsWith(ref
, prefix
))
475 shortname
= ref
.Right(ref
.GetLength() - prefix
.GetLength());
476 if (CStringUtils::EndsWith(shortname
, L
"^{}"))
477 shortname
.Truncate(shortname
.GetLength() - 3);
483 static CString
GetShortName(const CString
& ref
, REF_TYPE
*type
);
485 static bool LoadTextFile(const CString
&filename
, CString
&msg
);
487 int GetGitNotes(const CGitHash
& hash
, CString
& notes
);
488 int SetGitNotes(const CGitHash
& hash
, const CString
& notes
);
490 int GetUnifiedDiff(const CTGitPath
& path
, const CString
& rev1
, const CString
& rev2
, CString patchfile
, bool bMerge
, bool bCombine
, int diffContext
, bool bNoPrefix
= false);
491 int GetUnifiedDiff(const CTGitPath
& path
, const CString
& rev1
, const CString
& rev2
, CStringA
* buffer
, bool bMerge
, bool bCombine
, int diffContext
);
493 int GitRevert(int parent
, const CGitHash
&hash
);
495 int GetGitVersion(CString
* versiondebug
, CString
* errStr
);
497 CString
CombinePath(const CString
&path
) const
501 if (m_CurrentDir
.IsEmpty())
503 return m_CurrentDir
+ (CStringUtils::EndsWith(m_CurrentDir
, L
'\\') ? L
"" : L
"\\") + path
;
506 CString
CombinePath(const CTGitPath
&path
) const
508 return CombinePath(path
.GetWinPath());
511 CString
CombinePath(const CTGitPath
*path
) const
514 return CombinePath(path
->GetWinPath());
517 extern void GetTempPath(CString
&path
);
518 extern CString
GetTempFile();
519 extern DWORD
GetTortoiseGitTempPath(DWORD nBufferLength
, LPTSTR lpBuffer
);