Fixed issue #4133: libgit2 returned: failed to parse revision specifier (ref ending...
[TortoiseGit.git] / src / Git / Git.h
blob066e6effdecc5e24a8cd220f43324eec298aadf1
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2023 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #pragma once
21 #include "TGitPath.h"
22 #include "gittype.h"
23 #include "GitAdminDir.h"
24 #include "gitdll.h"
25 #include <functional>
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) | (1 << CGit::GIT_CMD_GETCONFLICTINFO)
35 struct git_repository;
37 using CAutoLocker = CComCritSecLock<CComCriticalSection>;
39 constexpr static inline int ConvertVersionToInt(unsigned __int8 major, unsigned __int8 minor, unsigned __int8 patchlevel, unsigned __int8 build = 0)
41 return (major << 24) + (minor << 16) + (patchlevel << 8) + build;
44 class CFilterData
46 public:
48 enum
50 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.
51 SHOW_LAST_SEL_DATE,
52 SHOW_LAST_N_COMMITS,
53 SHOW_LAST_N_YEARS,
54 SHOW_LAST_N_MONTHS,
55 SHOW_LAST_N_WEEKS,
58 CFilterData()
60 m_From=m_To=-1;
61 m_NumberOfLogsScale = SHOW_NO_LIMIT;
62 m_NumberOfLogs = 1;
65 DWORD m_NumberOfLogsScale;
66 DWORD m_NumberOfLogs;
67 __time64_t m_From;
68 __time64_t m_To;
71 class CGitCall
73 public:
74 CGitCall(){}
75 CGitCall(CString cmd):m_Cmd(cmd){}
76 virtual ~CGitCall() {}
78 CString GetCmd()const{return m_Cmd;}
79 void SetCmd(CString cmd){m_Cmd=cmd;}
81 //This function is called when command output data is available.
82 //When this function returns 'true' the git command should be aborted.
83 //This behavior is not implemented yet.
84 virtual bool OnOutputData(const char* data, size_t size) = 0;
85 virtual bool OnOutputErrData(const char* data, size_t size) = 0;
86 virtual void OnEnd(){}
88 private:
89 CString m_Cmd;
92 template <typename GitReceiverFunc>
93 class CGitCallCb : public CGitCall
95 public:
96 CGitCallCb(CString cmd, const GitReceiverFunc recv, BYTE_VECTOR* pvectorErr = nullptr)
97 : CGitCall(cmd)
98 , m_recv(recv)
99 , m_pvectorErr(pvectorErr)
101 static_assert(std::is_convertible_v<GitReceiverFunc, std::function<void(const CStringA&)>>, "Wrong signature for GitReceiverFunc!");
104 bool OnOutputData(const char* data, size_t size) override
106 ASSERT(data);
107 // Add data
108 if (size == 0 || size >= INT_MAX)
109 return false;
110 const int oldEndPos = m_buffer.GetLength();
111 int newLength;
112 if (IntAdd(oldEndPos, static_cast<int>(size), &newLength) != S_OK)
113 return false;
114 memcpy(CStrBufA(m_buffer, newLength, 0) + oldEndPos, data, size);
116 // Break into lines and feed to m_recv
117 int eolPos;
118 CStringA line;
119 while ((eolPos = m_buffer.Find('\n')) >= 0)
121 memcpy(CStrBufA(line, eolPos, 0), static_cast<const char*>(m_buffer), eolPos);
122 auto oldLen = m_buffer.GetLength();
123 memmove(m_buffer.GetBuffer(oldLen), static_cast<const char*>(m_buffer) + eolPos + 1, m_buffer.GetLength() - eolPos - 1);
124 m_buffer.ReleaseBuffer(oldLen - eolPos - 1);
125 m_recv(line);
127 return false;
130 bool OnOutputErrData(const char* data, size_t size) override
132 ASSERT(data);
133 if (!m_pvectorErr || size == 0 || size >= INT_MAX)
134 return false;
135 const size_t oldsize = m_pvectorErr->size();
136 size_t newLength;
137 if (SizeTAdd(oldsize, size, &newLength) != S_OK)
138 return false;
139 m_pvectorErr->resize(newLength);
140 memcpy(&*(m_pvectorErr->begin() + oldsize), data, size);
141 return false;
144 void OnEnd() override
146 if (!m_buffer.IsEmpty())
147 m_recv(m_buffer);
148 m_buffer.Empty(); // Just for sure
151 private:
152 GitReceiverFunc m_recv;
153 CStringA m_buffer;
154 BYTE_VECTOR* m_pvectorErr;
157 class CEnvironment : protected std::vector<wchar_t>
159 public:
160 CEnvironment() : baseptr(nullptr) {}
161 CEnvironment(const CEnvironment& env) : std::vector<wchar_t>(env)
163 baseptr = data();
165 CEnvironment& operator =(const CEnvironment& env)
167 __super::operator=(env);
168 if (empty())
169 baseptr = nullptr;
170 else
171 baseptr = data();
172 return *this;
174 void CopyProcessEnvironment();
175 CString GetEnv(const wchar_t* name) const;
176 void SetEnv(const wchar_t* name, const wchar_t* value);
177 void AddToPath(CString value);
178 void clear();
179 bool empty() const;
180 operator LPWSTR();
181 operator const LPWSTR*() const;
182 LPWSTR baseptr;
183 CEnvironment(CEnvironment&& env) = delete;
184 CEnvironment& operator =(CEnvironment&& env) = delete;
186 class CGit
188 private:
189 CString gitLastErr;
190 protected:
191 GIT_DIFF m_GitDiff = nullptr;
192 GIT_DIFF m_GitSimpleListDiff = nullptr;
193 #ifdef GOOGLETEST_INCLUDE_GTEST_GTEST_H_
194 public:
195 #endif
196 bool m_IsGitDllInited = false;
197 public:
198 CComAutoCriticalSection m_critGitDllSec;
199 bool m_IsUseGitDLL;
200 bool m_IsUseLibGit2;
201 DWORD m_IsUseLibGit2_mask;
203 CEnvironment m_Environment;
205 static BOOL GitPathFileExists(const CString &path)
207 if (path[0] == L'\\' && path[1] == L'\\')
208 //it is netshare \\server\sharefoldername
209 // \\server\.git will create smb error log.
211 const int length = path.GetLength();
213 if(length<2)
214 return false;
216 int start = path.Find(L'\\', 2);
217 if(start<0)
218 return false;
220 start = path.Find(L'\\', start + 1);
221 if(start<0)
222 return false;
224 return PathFileExists(path);
227 else
228 return PathFileExists(path);
231 inline void ForceReInitDll()
233 #ifdef TGITCACHE
234 ATLASSERT("we should never get here");
235 #endif
236 m_IsGitDllInited = false;
237 CheckAndInitDll();
239 void CheckAndInitDll()
241 #ifdef TGITCACHE
242 ATLASSERT("we should never get here");
243 #endif
244 if(!m_IsGitDllInited)
246 git_init(m_Environment);
247 m_IsGitDllInited=true;
251 GIT_DIFF GetGitDiff()
253 #ifdef TGITCACHE
254 ATLASSERT("we should never get here");
255 #endif
256 if(m_GitDiff)
257 return m_GitDiff;
258 else
260 // cf. GitRevLoglist::SafeFetchFullInfo
261 CStringA params;
262 params.Format("-C%d%% -M%d%% -r", ms_iSimilarityIndexThreshold, ms_iSimilarityIndexThreshold);
263 git_open_diff(&m_GitDiff, params);
264 return m_GitDiff;
268 GIT_DIFF GetGitSimpleListDiff()
270 #ifdef TGITCACHE
271 ATLASSERT("we should never get here");
272 #endif
273 if(m_GitSimpleListDiff)
274 return m_GitSimpleListDiff;
275 else
277 git_open_diff(&m_GitSimpleListDiff, "-r");
278 return m_GitSimpleListDiff;
282 BOOL CheckMsysGitDir(BOOL bFallback = TRUE);
283 BOOL FindAndSetGitExePath(BOOL bFallback);
284 bool m_bInitialized = false;
286 enum LIBGIT2_CMD
288 GIT_CMD_CLONE,
289 GIT_CMD_FETCH,
290 GIT_CMD_COMMIT_UPDATE_INDEX,
291 GIT_CMD_DIFF,
292 GIT_CMD_RESET,
293 GIT_CMD_REVERT,
294 GIT_CMD_MERGE_BASE,
295 GIT_CMD_DELETETAGBRANCH,
296 GIT_CMD_GETONEFILE,
297 GIT_CMD_ADD,
298 GIT_CMD_PUSH,
299 GIT_CMD_CHECK_CLEAN_WT,
300 GIT_CMD_CHECKCONFLICTS,
301 GIT_CMD_GET_COMMIT,
302 GIT_CMD_LOGLISTDIFF,
303 GIT_CMD_BRANCH_CONTAINS,
304 GIT_CMD_GETCONFLICTINFO,
305 LAST_VALUE,
307 static_assert(LIBGIT2_CMD::LAST_VALUE < sizeof(DWORD) * 8, "too many flags for storing them in a DWORD bitfield");
308 bool UsingLibGit2(LIBGIT2_CMD cmd) const;
310 * callback type should be git_cred_acquire_cb
312 static void SetGit2CredentialCallback(void* callback);
313 static void SetGit2CertificateCheckCertificate(void* callback);
315 CString GetHomeDirectory() const;
316 CString GetGitLocalConfig() const;
317 CString GetGitGlobalConfig() const;
318 CString GetGitGlobalXDGConfigPath() const;
319 CString GetGitGlobalXDGConfig() const;
320 CString GetGitSystemConfig() const;
321 CAutoRepository GetGitRepository() const;
322 static CStringA GetGitPathStringA(const CString &path);
323 static CString ms_LastMsysGitDir; // the last msysgitdir added to the path, blank if none
324 static CString ms_MsysGitRootDir;
325 static int ms_LastMsysGitVersion;
326 static bool ms_bCygwinGit;
327 static bool ms_bMsys2Git;
328 static int ms_iSimilarityIndexThreshold;
329 static int m_LogEncode;
330 CString GetNotesRef() const;
331 static bool IsBranchNameValid(const CString& branchname);
332 bool IsLocalBranch(const CString& shortName);
333 bool IsBranchTagNameUnique(const CString& name);
335 * Checks if a branch or tag with the given name exists
336 *isBranch is true -> branch, tag otherwise
338 bool BranchTagExists(const CString& name, bool isBranch = true);
339 unsigned int Hash2int(const CGitHash &hash);
341 PROCESS_INFORMATION m_CurrentGitPi{};
343 CGit();
344 ~CGit();
346 int Run(CString cmd, CString* output, int code);
347 int Run(CString cmd, CString* output, CString* outputErr, int code);
348 int Run(CString cmd, BYTE_VECTOR* byte_array, BYTE_VECTOR* byte_arrayErr = nullptr);
349 int Run(CGitCall& pcall);
350 template<typename GitReceiverFunc>
351 int Run(CString cmd, GitReceiverFunc recv, CString* outputErr = nullptr)
353 if (outputErr)
355 BYTE_VECTOR vectorErr;
356 CGitCallCb call(cmd, recv, &vectorErr);
357 const int ret = Run(call);
358 vectorErr.push_back(0);
359 StringAppend(*outputErr, vectorErr.data());
360 return ret;
363 CGitCallCb call(cmd, recv);
364 return Run(call);
367 private:
368 CComAutoCriticalSection m_critSecThreadMap;
369 std::map<DWORD, HANDLE> m_AsyncReadStdErrThreadMap;
370 static DWORD WINAPI AsyncReadStdErrThread(LPVOID lpParam);
371 struct ASYNCREADSTDERRTHREADARGS
373 HANDLE fileHandle;
374 CGitCall* pcall;
376 CString GetUnifiedDiffCmd(const CTGitPath& path, const CString& rev1, const CString& rev2, bool bMerge, bool bCombine, int diffContext, bool bNoPrefix = false);
378 public:
379 #ifdef _MFC_VER
380 void KillRelatedThreads(CWinThread* thread);
381 #endif
382 int RunAsync(CString cmd, PROCESS_INFORMATION& pi, HANDLE* hRead, HANDLE* hErrReadOut, const CString* StdioFile = nullptr);
383 int RunLogFile(CString cmd, const CString &filename, CString *stdErr);
385 bool IsFastForward(const CString& from, const CString& to, CGitHash* commonAncestor = nullptr);
386 CString GetConfigValue(const CString& name, const CString& def = CString(), bool wantBool = false);
387 bool GetConfigValueBool(const CString& name, const bool def = false);
388 int GetConfigValueInt32(const CString& name, const int def = 0);
390 int SetConfigValue(const CString& key, const CString& value, CONFIG_TYPE type = CONFIG_LOCAL);
391 int UnsetConfigValue(const CString& key, CONFIG_TYPE type = CONFIG_LOCAL);
393 CString GetUserName();
394 CString GetUserEmail();
395 CString GetCommitterName();
396 CString GetCommitterEmail();
397 CString GetCurrentBranch(bool fallback = false);
398 void GetRemoteTrackedBranch(const CString& localBranch, CString& remote, CString& branch);
399 void GetRemoteTrackedBranchForHEAD(CString& remote, CString& branch);
400 void GetRemotePushBranch(const CString& localBranch, CString& pushRemote, CString& pushBranch);
401 // read current branch name from HEAD file, returns 0 on success, -1 on failure, 1 detached (branch name "HEAD" returned)
402 static int GetCurrentBranchFromFile(const CString &sProjectRoot, CString &sBranchOut, bool fallback = false);
404 Use this method only when the HEAD is exist.
406 BOOL CheckCleanWorkTree(bool stagedOk = false);
407 BOOL IsResultingCommitBecomeEmpty(bool amend = false);
408 int DeleteRef(const CString& reference);
410 Use this method only if m_IsUseLibGit2 is used for fallbacks.
411 If you directly use libgit2 methods, use GetLibGit2LastErr instead.
413 CString GetGitLastErr(const CString& msg);
414 CString GetGitLastErr(const CString& msg, LIBGIT2_CMD cmd);
415 static CString GetLibGit2LastErr();
416 static CString GetLibGit2LastErr(const CString& msg);
417 bool SetCurrentDir(CString path, bool submodule = false)
419 bool b = GitAdminDir::HasAdminDir(path, submodule ? false : !!PathIsDirectory(path), &m_CurrentDir);
420 if (!b && GitAdminDir::IsBareRepo(path))
422 m_CurrentDir = path;
423 b = true;
425 if (m_CurrentDir.GetLength() == 2 && m_CurrentDir[1] == L':') //C: D:
426 m_CurrentDir += L'\\';
428 return b;
430 CString m_CurrentDir;
432 enum
434 LOG_ORDER_CHRONOLOGIALREVERSED,
435 LOG_ORDER_TOPOORDER,
436 LOG_ORDER_DATEORDER,
439 typedef enum
441 BRANCH_LOCAL = 0x1,
442 BRANCH_REMOTE = 0x2,
443 BRANCH_FETCH_HEAD = 0x4,
444 BRANCH_LOCAL_F = BRANCH_LOCAL | BRANCH_FETCH_HEAD,
445 BRANCH_ALL = BRANCH_LOCAL | BRANCH_REMOTE,
446 BRANCH_ALL_F = BRANCH_ALL | BRANCH_FETCH_HEAD,
447 }BRANCH_TYPE;
449 typedef enum
451 LOG_INFO_STAT=0x1,
452 LOG_INFO_FILESTATE=0x2,
453 LOG_INFO_BOUNDARY=0x10,
454 LOG_INFO_ALL_BRANCH=0x20,
455 LOG_INFO_ONLY_HASH=0x40,
456 LOG_INFO_DETECT_RENAME=0x80,
457 LOG_INFO_DETECT_COPYRENAME=0x100,
458 LOG_INFO_FIRST_PARENT = 0x200,
459 LOG_INFO_NO_MERGE = 0x400,
460 LOG_INFO_FOLLOW = 0x800,
461 LOG_INFO_SHOW_MERGEDFILE=0x1000,
462 LOG_INFO_FULL_DIFF = 0x2000,
463 LOG_INFO_SIMPILFY_BY_DECORATION = 0x4000,
464 LOG_INFO_LOCAL_BRANCHES = 0x8000,
465 LOG_INFO_BASIC_REFS = 0x10000,
466 LOG_INFO_SPARSE = 0x20000,
467 LOG_INFO_ALWAYS_APPLY_RANGE = 0x40000,
468 LOG_INFO_FULL_HISTORY = 0x80000,
469 }LOG_INFO_MASK;
471 typedef enum
473 LOCAL_BRANCH,
474 REMOTE_BRANCH,
475 ANNOTATED_TAG,
476 TAG,
477 STASH,
478 BISECT_GOOD,
479 BISECT_BAD,
480 BISECT_SKIP,
481 NOTES,
482 UNKNOWN,
484 }REF_TYPE;
486 int GetRemoteList(STRING_VECTOR &list);
487 int GetBranchList(STRING_VECTOR& list, int* current, BRANCH_TYPE type = BRANCH_LOCAL, bool skipCurrent = false);
488 int GetTagList(STRING_VECTOR &list);
489 int GetRefsCommitIsOn(STRING_VECTOR& list, const CGitHash& hash, bool includeTags, bool includeBranches, BRANCH_TYPE type = BRANCH_LOCAL);
490 int GetRemoteRefs(const CString& remote, REF_VECTOR& list, bool includeTags, bool includeBranches);
491 int DeleteRemoteRefs(const CString& remote, const STRING_VECTOR& list);
492 int GetBranchDescriptions(MAP_STRING_STRING& map);
493 int GuessRefForHash(CString& ref, const CGitHash& hash);
494 int GetMapHashToFriendName(MAP_HASH_NAME &map);
495 static int GetMapHashToFriendName(git_repository* repo, MAP_HASH_NAME &map);
497 CString DerefFetchHead();
499 // FixBranchName():
500 // When branchName == FETCH_HEAD, dereference it.
501 // A selected branch name got from GetBranchList(), with flag BRANCH_FETCH_HEAD enabled,
502 // should go through this function before it is used.
503 CString FixBranchName_Mod(CString& branchName);
504 CString FixBranchName(const CString& branchName);
506 CString GetLogCmd(CString range, const CTGitPath* path, int InfoMask, CFilterData* filter, int logOrderBy);
508 int GetHash(CGitHash &hash, const CString& friendname);
509 static int GetHash(git_repository * repo, CGitHash &hash, const CString& friendname, bool skipFastCheck = false);
511 static void StringAppend(CString& str, const char* p, int code = CP_UTF8, int length = -1);
513 BOOL CanParseRev(CString ref);
515 Checks if HEAD points to an unborn branch
516 This method assumes, that we already know that we are in a working tree.
518 BOOL IsInitRepos();
519 /** Returns 0 if no conflict, if a conflict was found and -1 in case of a failure */
520 int HasWorkingTreeConflicts();
521 /** Returns 0 if no conflict, if a conflict was found and -1 in case of a failure */
522 int HasWorkingTreeConflicts(git_repository* repo);
523 void GetBisectTerms(CString* good, CString* bad);
524 int GetRefList(STRING_VECTOR &list);
526 class SubmoduleInfo
528 public:
529 CGitHash superProjectHash;
530 CGitHash mergeconflictMineHash;
531 CGitHash mergeconflictTheirsHash;
532 CString mineLabel;
533 CString theirsLabel;
535 bool AnyMatches(const CGitHash& hash) const
537 return !superProjectHash.IsEmpty() && superProjectHash == hash || !mergeconflictMineHash.IsEmpty() && mergeconflictMineHash == hash || !mergeconflictTheirsHash.IsEmpty() && mergeconflictTheirsHash == hash;
539 void Empty()
541 superProjectHash.Empty();
542 mergeconflictMineHash.Empty();
543 mergeconflictTheirsHash.Empty();
546 int GetSubmodulePointer(SubmoduleInfo& mergeInfo) const;
548 int ApplyPatchToIndex(const CString& patchPath, CString* out);
549 int ApplyPatchToIndexReverse(const CString& patchPath, CString* out);
551 int RefreshGitIndex();
552 int GetOneFile(const CString &Refname, const CTGitPath &path, const CString &outputfile);
554 //Example: master -> refs/heads/master
555 CString GetFullRefName(const CString& shortRefName);
556 //Removes 'refs/heads/' or just 'refs'. Example: refs/heads/master -> master
557 static CString StripRefName(CString refName);
559 int GetCommitDiffList(const CString &rev1, const CString &rev2, CTGitPathList &outpathlist, bool ignoreSpaceAtEol = false, bool ignoreSpaceChange = false, bool ignoreAllSpace = false, bool ignoreBlankLines = false);
560 int GetInitAddList(CTGitPathList &outpathlist, bool getStagingStatus = false);
561 int GetWorkingTreeChanges(CTGitPathList& result, bool amend = false, const CTGitPathList* filterlist = nullptr, bool includedStaged = false, bool getStagingStatus = false);
563 static int ParseConflictHashesFromLsFile(const BYTE_VECTOR& out, CGitHash& baseHash, bool& baseIsFile, CGitHash& mineHash, bool& mineIsFile, CGitHash& remoteHash, bool& remoteIsFile);
565 constexpr static __int64 filetime_to_time_t(__int64 winTime) noexcept
567 winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
568 winTime /= 10000000; /* Nano to seconds resolution */
569 return static_cast<time_t>(winTime);
572 static int GetFileModifyTime(LPCWSTR filename, __int64* time, bool* isDir = nullptr, __int64* size = nullptr, bool* isSymlink = nullptr)
574 WIN32_FILE_ATTRIBUTE_DATA fdata;
575 if (GetFileAttributesEx(filename, GetFileExInfoStandard, &fdata))
577 if (time)
578 *time = static_cast<__int64>(fdata.ftLastWriteTime.dwHighDateTime) << 32 | fdata.ftLastWriteTime.dwLowDateTime;
580 if (size)
581 *size = static_cast<__int64>(fdata.nFileSizeHigh) << 32 | fdata.nFileSizeLow;
583 if(isDir)
584 *isDir = !!( fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
586 if (isSymlink)
587 *isSymlink = (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && !CPathUtils::ReadLink(filename);
589 return 0;
591 return -1;
594 int GetShortHASHLength() const;
596 static BOOL GetShortName(const CString& ref, CString& shortname, const CString& prefix)
598 //TRACE(L"%s %s\r\n", ref, prefix);
599 if (CStringUtils::StartsWith(ref, prefix))
601 shortname = ref.Right(ref.GetLength() - prefix.GetLength());
602 if (CStringUtils::EndsWith(shortname, L"^{}"))
603 shortname.Truncate(shortname.GetLength() - static_cast<int>(wcslen(L"^{}")));
604 return TRUE;
606 return FALSE;
609 static CString GetShortName(const CString& ref, REF_TYPE *type);
611 static bool LoadTextFile(const CString &filename, CString &msg);
613 int GetGitNotes(const CGitHash& hash, CString& notes);
614 int SetGitNotes(const CGitHash& hash, const CString& notes);
616 int GetUnifiedDiff(const CTGitPath& path, const CString& rev1, const CString& rev2, CString patchfile, bool bMerge, bool bCombine, int diffContext, bool bNoPrefix = false);
617 int GetUnifiedDiff(const CTGitPath& path, const CString& rev1, const CString& rev2, CStringA& buffer, bool bMerge, bool bCombine, int diffContext);
619 int GitRevert(int parent, const CGitHash &hash);
621 int GetGitVersion(CString* versiondebug, CString* errStr);
623 CString CombinePath(const CString &path) const
625 if (path.IsEmpty())
626 return m_CurrentDir;
627 if (m_CurrentDir.IsEmpty())
628 return path;
629 return m_CurrentDir + (CStringUtils::EndsWith(m_CurrentDir, L'\\') ? L"" : L"\\") + path;
632 CString CombinePath(const CTGitPath &path) const
634 return CombinePath(path.GetWinPath());
637 CString CombinePath(const CTGitPath *path) const
639 ATLASSERT(path);
640 return CombinePath(path->GetWinPath());
643 extern void GetTempPath(CString &path);
644 extern CString GetTempFile();
645 extern DWORD GetTortoiseGitTempPath(DWORD nBufferLength, LPWSTR lpBuffer);
647 extern CGit g_Git;