Also check git.exe version when setting it up
[TortoiseGit.git] / src / TortoiseProc / ConfigureGitExe.h
blob04795e8dc6f2bdcd96482c46b1eb6d34b6b84cf1
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - 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 "BrowseFolder.h"
23 #include "PathUtils.h"
24 #include "Git.h"
25 #include "MessageBox.h"
26 #include "AppUtils.h"
28 #define GIT_FOR_WINDOWS_URL L"https://git-for-windows.github.io/"
30 class CConfigureGitExe
32 public:
33 CConfigureGitExe()
34 : m_regCygwinHack(L"Software\\TortoiseGit\\CygwinHack", FALSE)
35 , m_regMsys2Hack(L"Software\\TortoiseGit\\Msys2Hack", FALSE)
36 {};
38 static bool CheckGitVersion(HWND hwnd)
40 if (CAppUtils::GetMsysgitVersion() >= 0x01090500)
41 return true;
43 int ret = CMessageBox::ShowCheck(nullptr, IDS_PROC_OLDMSYSGIT, IDS_APPNAME, 1, IDI_EXCLAMATION, IDS_PROC_GOTOMSYSGITWEBSITE, IDS_ABORTBUTTON, IDS_IGNOREBUTTON, L"OldMsysgitVersionWarning", IDS_PROC_NOTSHOWAGAINIGNORE);
44 if (ret == 3)
45 return true;
47 CMessageBox::RemoveRegistryKey(L"OldMsysgitVersionWarning"); // only store answer if it is "Ignore"
48 if (ret == 1)
49 ShellExecute(hwnd, L"open", GIT_FOR_WINDOWS_URL, nullptr, nullptr, SW_SHOW);
51 return false;
54 protected:
55 CRegDWORD m_regCygwinHack;
56 CRegDWORD m_regMsys2Hack;
57 CRegString m_regMsysGitPath;
58 CRegString m_regMsysGitExtranPath;
60 static void PerformCommonGitPathCleanup(CString& path)
62 path.Trim(L"\"'");
64 if (path.Find(L'%') >= 0)
66 int neededSize = ExpandEnvironmentStrings(path, nullptr, 0);
67 CString origPath(path);
68 ExpandEnvironmentStrings(origPath, path.GetBufferSetLength(neededSize), neededSize);
69 path.ReleaseBuffer();
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 CBrowseFolder browseFolder;
114 browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
115 CString dir;
116 dir = gitpath;
117 if (dir.IsEmpty())
118 dir = CPathUtils::GetProgramsDirectory();
119 if (browseFolder.Show(hwnd, dir) != CBrowseFolder::OK)
120 return false;
122 gitpath = dir;
123 PerformCommonGitPathCleanup(gitpath);
124 if (!PathFileExists(gitpath + L"\\git.exe"))
125 MessageBox(hwnd, L"No git.exe found in the selected folder.", L"TortoiseGit", MB_ICONEXCLAMATION);
126 GuessExtraPath(gitpath, pathaddition);
127 return true;
130 bool CheckGitExe(HWND hwnd, CString& gitpath, CString& pathaddition, int versionLabelId, std::function<void(UINT)> callHelp, bool* needWorkarounds = nullptr)
132 SetWindowText(GetDlgItem(hwnd, versionLabelId), L"");
134 PerformCommonGitPathCleanup(gitpath);
136 GuessExtraPath(gitpath, pathaddition);
138 if (gitpath.IsEmpty() || !PathFileExists(gitpath + L"\\git.exe"))
140 MessageBox(hwnd, L"No git.exe found. TortoiseGit requires a git.exe for its operations.", L"TortoiseGit", MB_ICONEXCLAMATION);
141 return false;
144 CString oldpath = m_regMsysGitPath;
145 CString oldextranpath = m_regMsysGitExtranPath;
147 StoreSetting(hwnd, gitpath, m_regMsysGitPath);
148 StoreSetting(hwnd, pathaddition, m_regMsysGitExtranPath);
149 SCOPE_EXIT{
150 StoreSetting(hwnd, oldpath, m_regMsysGitPath);
151 StoreSetting(hwnd, oldextranpath, m_regMsysGitExtranPath);
154 g_Git.m_bInitialized = false;
156 if (g_Git.CheckMsysGitDir(FALSE))
158 CString out;
159 int ret = g_Git.Run(L"git.exe --version", &out, CP_UTF8);
160 SetWindowText(GetDlgItem(hwnd, versionLabelId), out);
161 if (out.IsEmpty())
163 if (ret == 0xC0000135)
165 if (CMessageBox::Show(hwnd, L"Could not start git.exe. A dynamic library (dll) is missing.\nYou might need to specify an extra PATH.\nCheck help file for \"Extra PATH\".", L"TortoiseGit", 1, IDI_ERROR, CString(MAKEINTRESOURCE(IDS_MSGBOX_OK)), CString(MAKEINTRESOURCE(IDS_MSGBOX_HELP))) == 2)
166 callHelp(IDD_SETTINGSMAIN);
167 return false;
169 CString tmp;
170 tmp.Format(L"Calling git.exe returned an error (%d). Please check the git.exe path.\nCheck help file for \"Git.exe Path\".", ret);
171 if (CMessageBox::Show(hwnd, tmp, L"TortoiseGit", 1, IDI_ERROR, CString(MAKEINTRESOURCE(IDS_MSGBOX_OK)), CString(MAKEINTRESOURCE(IDS_MSGBOX_HELP))) == 2)
172 callHelp(IDD_SETTINGSMAIN);
173 return false;
175 else if (!CStringUtils::StartsWith(out.Trim(), L"git version "))
177 if (CMessageBox::Show(hwnd, L"Could not get read version information from git.exe.\nGot: \"" + out.Trim() + L"\"\n\nCheck help file for \"Git.exe Path\".", L"TortoiseGit", 1, IDI_ERROR, CString(MAKEINTRESOURCE(IDS_MSGBOX_OK)), CString(MAKEINTRESOURCE(IDS_MSGBOX_HELP))) == 2)
178 callHelp(IDD_SETTINGSMAIN);
179 return false;
181 else if (!(CGit::ms_bCygwinGit || CGit::ms_bMsys2Git) && out.Find(L"msysgit") == -1 && out.Find(L"windows") == -1)
183 bool wasAlreadyWarned = !needWorkarounds || *needWorkarounds;
184 if (CMessageBox::Show(hwnd, L"Could not find \"msysgit\" or \"windows\" in versionstring of git.exe.\nIf you are using git of the cygwin or msys2 environment please read the help file for the keyword \"cygwin git\" or \"msys2 git\".", L"TortoiseGit", 1, IDI_INFORMATION, CString(MAKEINTRESOURCE(IDS_MSGBOX_OK)), CString(MAKEINTRESOURCE(IDS_MSGBOX_HELP))) == 2)
185 callHelp(IDD_SETTINGSMAIN);
186 if (needWorkarounds)
187 *needWorkarounds = true;
188 if (!wasAlreadyWarned)
189 return false;
191 else if ((CGit::ms_bCygwinGit || CGit::ms_bMsys2Git) && out.Find(L"msysgit") > 0 && out.Find(L"windows") > 0)
193 if (CMessageBox::Show(hwnd, L"Found \"msysgit\" or \"windows\" in versionstring of git.exe, however, you have git.exe quirks enabled. These hacks must be disabled for proper operation with Git for Windows!\nYou can find more information in the help file for the keyword \"cygwin git\" or \"msys2 git\".", L"TortoiseGit", 1, IDI_INFORMATION, CString(MAKEINTRESOURCE(IDS_MSGBOX_OK)), CString(MAKEINTRESOURCE(IDS_MSGBOX_HELP))) == 2)
194 callHelp(IDD_SETTINGSMAIN);
195 if (needWorkarounds)
196 *needWorkarounds = true;
197 return false;
200 else
202 if (CMessageBox::Show(hwnd, L"Invalid git.exe path.\nCheck help file for \"Git.exe Path\".", L"TortoiseGit", 1, IDI_ERROR, CString(MAKEINTRESOURCE(IDS_MSGBOX_OK)), CString(MAKEINTRESOURCE(IDS_MSGBOX_HELP))) == 2)
203 callHelp(IDD_SETTINGSMAIN);
204 return false;
207 CGit::ms_LastMsysGitVersion = 0;
208 if (!CheckGitVersion(hwnd))
209 return false;
210 return true;
213 template<class T, class Reg>
214 void StoreSetting(HWND hwnd, const T& value, Reg& registryKey)
216 registryKey = value;
217 if (registryKey.GetLastError() != ERROR_SUCCESS)
218 CMessageBox::Show(hwnd, registryKey.getErrorString(), L"TortoiseGit", MB_ICONERROR);