RebaseDlg: Correctly remember commits for rewriting on Squash after (Edit|Squash...
[TortoiseGit.git] / src / TortoiseProc / ConfigureGitExe.h
blob5979ba28703cff7045de7928df353e67cecb20d2
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2019, 2021-2022, 2024 - 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 "StringUtils.h"
22 #include "Git.h"
23 #include "MessageBox.h"
24 #include "AppUtils.h"
25 #include "../TGitCache/CacheInterface.h"
27 #define GIT_FOR_WINDOWS_URL L"https://gitforwindows.org/"
29 class CConfigureGitExe
31 public:
32 CConfigureGitExe()
33 : m_regCygwinHack(L"Software\\TortoiseGit\\CygwinHack", FALSE)
34 , m_regMsys2Hack(L"Software\\TortoiseGit\\Msys2Hack", FALSE)
35 {};
37 static bool CheckGitVersion(HWND hwnd)
39 if (CAppUtils::IsGitVersionNewerOrEqual(hwnd, 2, 24))
40 return true;
42 CString tmp;
43 tmp.Format(IDS_PROC_OLDMSYSGIT, L"2.24+");
44 int ret = CMessageBox::ShowCheck(hwnd, tmp, L"TortoiseGit", 1, IDI_EXCLAMATION, CString(MAKEINTRESOURCE(IDS_PROC_GOTOMSYSGITWEBSITE)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON)), CString(MAKEINTRESOURCE(IDS_IGNOREBUTTON)), L"OldMsysgitVersionWarning", CString(MAKEINTRESOURCE(IDS_PROC_NOTSHOWAGAINIGNORE)));
45 if (ret == 3)
46 return true;
48 CMessageBox::RemoveRegistryKey(L"OldMsysgitVersionWarning"); // only store answer if it is "Ignore"
49 if (ret == 1)
50 ShellExecute(hwnd, L"open", GIT_FOR_WINDOWS_URL, nullptr, nullptr, SW_SHOW);
52 return false;
55 protected:
56 CRegDWORD m_regCygwinHack;
57 CRegDWORD m_regMsys2Hack;
58 CRegString m_regMsysGitPath;
59 CRegString m_regMsysGitExtranPath;
61 static void PerformCommonGitPathCleanup(CString& path)
63 path.Trim(L"\"'");
65 if (path.Find(L'%') >= 0)
67 int neededSize = ExpandEnvironmentStrings(path, nullptr, 0);
68 CString origPath(path);
69 ExpandEnvironmentStrings(origPath, CStrBuf(path, neededSize, CStrBuf::SET_LENGTH), neededSize);
72 path.Replace(L'/', L'\\');
73 path.Replace(L"\\\\", L"\\");
75 if (CStringUtils::EndsWith(path, L"git.exe"))
76 path.Truncate(path.GetLength() - 7);
78 path.TrimRight(L'\\');
80 // prefer git.exe in cmd-directory for Git for Windows based on msys2
81 if (path.GetLength() > 12 && (CStringUtils::EndsWith(path, L"\\mingw32\\bin") || CStringUtils::EndsWith(path, L"\\mingw64\\bin")) && PathFileExists(path.Left(path.GetLength() - 12) + L"\\cmd\\git.exe"))
82 path = path.Left(path.GetLength() - 12) + L"\\cmd";
84 // prefer git.exe in bin-directory, see https://github.com/msysgit/msysgit/issues/103
85 if (path.GetLength() > 5 && CStringUtils::EndsWith(path, L"\\cmd") && PathFileExists(path.Left(path.GetLength() - 4) + L"\\bin\\git.exe"))
86 path = path.Left(path.GetLength() - 4) + L"\\bin";
89 static bool GuessExtraPath(CString gitpath, CString& pathaddition)
91 if (gitpath.IsEmpty())
92 return false;
94 PerformCommonGitPathCleanup(gitpath);
96 if (!CStringUtils::EndsWith(gitpath, L"bin"))
97 return false;
99 gitpath.Truncate(gitpath.GetLength() - 3);
100 gitpath += L"mingw\\bin";
101 if (!::PathFileExists(gitpath))
102 return false;
104 if (pathaddition.Find(gitpath + L';') >= 0 || CStringUtils::EndsWith(pathaddition, gitpath))
105 return false;
107 pathaddition = gitpath + pathaddition;
108 return true;
111 static bool SelectFolder(HWND hwnd, CString& gitpath, CString& pathaddition)
113 CString dir;
114 dir = gitpath;
115 if (!dir.IsEmpty())
116 dir += L"\\git.exe";
117 if (!CCommonAppUtils::FileOpenSave(dir, nullptr, 0, IDS_GITEXEFILEFILTER, true, hwnd))
118 return false;
120 gitpath = dir;
121 PerformCommonGitPathCleanup(gitpath);
122 if (!PathFileExists(gitpath + L"\\git.exe"))
123 CMessageBox::Show(hwnd, IDS_PROC_NOMSYSGIT, IDS_APPNAME, MB_ICONEXCLAMATION);
124 GuessExtraPath(gitpath, pathaddition);
125 return true;
128 bool CheckGitExe(HWND hwnd, CString& gitpath, CString& pathaddition, int versionLabelId, std::function<void(UINT)> callHelp, bool* needWorkarounds = nullptr)
130 SetWindowText(GetDlgItem(hwnd, versionLabelId), L"");
132 PerformCommonGitPathCleanup(gitpath);
134 GuessExtraPath(gitpath, pathaddition);
136 if (gitpath.IsEmpty() || !PathFileExists(gitpath + L"\\git.exe"))
138 CMessageBox::Show(hwnd, IDS_PROC_NOMSYSGIT, IDS_APPNAME, MB_ICONERROR);
139 return false;
142 CString oldpath = m_regMsysGitPath;
143 CString oldextranpath = m_regMsysGitExtranPath;
145 StoreSetting(hwnd, gitpath, m_regMsysGitPath);
146 StoreSetting(hwnd, pathaddition, m_regMsysGitExtranPath);
147 bool undoOnError = true;
148 SCOPE_EXIT
150 if (!undoOnError)
151 return;
152 StoreSetting(hwnd, oldpath, m_regMsysGitPath);
153 StoreSetting(hwnd, oldextranpath, m_regMsysGitExtranPath);
156 CString checkhelpHint;
157 checkhelpHint.LoadString(IDS_SEEMANUALGITEXEPATH);
159 g_Git.m_bInitialized = false;
161 if (g_Git.CheckMsysGitDir(FALSE))
163 CString out;
164 int ret = g_Git.Run(L"git.exe --version", &out, CP_UTF8);
165 SetWindowText(GetDlgItem(hwnd, versionLabelId), out);
166 if (out.IsEmpty())
168 if (ret == 0xC0000135)
170 if (CMessageBox::Show(hwnd, IDS_ERR_GITDLLMISSING, IDS_APPNAME, 1, IDI_ERROR, IDS_MSGBOX_OK, IDS_MSGBOX_HELP) == 2)
171 callHelp(IDD_SETTINGSMAIN);
172 return false;
174 CString tmp;
175 tmp.Format(IDS_ERR_GITCALLFAILED, ret);
176 tmp.AppendChar(L'\n');
177 tmp.AppendChar(L'\n');
178 tmp.Append(checkhelpHint);
179 if (CMessageBox::Show(hwnd, tmp, L"TortoiseGit", 1, IDI_ERROR, CString(MAKEINTRESOURCE(IDS_MSGBOX_OK)), CString(MAKEINTRESOURCE(IDS_MSGBOX_HELP))) == 2)
180 callHelp(IDD_SETTINGSMAIN);
181 return false;
183 else if (!CStringUtils::StartsWith(out.Trim(), L"git version "))
185 CString tmp;
186 tmp.Format(IDS_ERR_GITNOVALIDOUTPUT, static_cast<LPCWSTR>(out.Trim()));
187 tmp.AppendChar(L'\n');
188 tmp.AppendChar(L'\n');
189 tmp.Append(checkhelpHint);
190 if (CMessageBox::Show(hwnd, tmp, L"TortoiseGit", 1, IDI_ERROR, CString(MAKEINTRESOURCE(IDS_MSGBOX_OK)), CString(MAKEINTRESOURCE(IDS_MSGBOX_HELP))) == 2)
191 callHelp(IDD_SETTINGSMAIN);
192 return false;
194 else if (!(CGit::ms_bCygwinGit || CGit::ms_bMsys2Git) && out.Find(L"msysgit") == -1 && out.Find(L"windows") == -1)
196 bool wasAlreadyWarned = !needWorkarounds || *needWorkarounds;
197 if (CMessageBox::Show(hwnd, IDS_ERR_GITNEEDHACKS, IDS_APPNAME, 1, IDI_INFORMATION, IDS_MSGBOX_OK, IDS_MSGBOX_HELP) == 2)
198 callHelp(IDD_SETTINGSMAIN);
199 if (needWorkarounds)
200 *needWorkarounds = true;
201 if (!wasAlreadyWarned)
202 return false;
204 else if ((CGit::ms_bCygwinGit || CGit::ms_bMsys2Git) && out.Find(L"msysgit") > 0 && out.Find(L"windows") > 0)
206 if (CMessageBox::Show(hwnd, IDS_ERR_GITUNNEEDEDHACKS, IDS_APPNAME, 1, IDI_INFORMATION, IDS_MSGBOX_OK, IDS_MSGBOX_HELP) == 2)
207 callHelp(IDD_SETTINGSMAIN);
208 if (needWorkarounds)
209 *needWorkarounds = true;
210 return false;
213 else
215 CString tmp;
216 tmp.LoadString(IDS_PROC_NOMSYSGIT);
217 tmp.AppendChar(L'\n');
218 tmp.AppendChar(L'\n');
219 tmp.Append(checkhelpHint);
220 if (CMessageBox::Show(hwnd, tmp, L"TortoiseGit", 1, IDI_ERROR, CString(MAKEINTRESOURCE(IDS_MSGBOX_OK)), CString(MAKEINTRESOURCE(IDS_MSGBOX_HELP))) == 2)
221 callHelp(IDD_SETTINGSMAIN);
222 return false;
225 // force recalculation of Git version
226 CGit::ms_LastMsysGitVersion = 0;
227 CRegDWORD(L"Software\\TortoiseGit\\git_file_time").removeValue();
228 if (!CheckGitVersion(hwnd))
229 return false;
231 // tell the cache to refresh everything and restart
232 SendCacheCommand(TGITCACHECOMMAND_REFRESHALL);
233 SendCacheCommand(TGITCACHECOMMAND_END);
235 undoOnError = false;
236 return true;
239 template<class T, class Reg>
240 void StoreSetting(HWND hwnd, const T& value, Reg& registryKey)
242 registryKey = value;
243 if (registryKey.GetLastError() != ERROR_SUCCESS)
244 CMessageBox::Show(hwnd, registryKey.getErrorString(), L"TortoiseGit", MB_ICONERROR);