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.
21 #include "StringUtils.h"
23 #include "MessageBox.h"
25 #include "../TGitCache/CacheInterface.h"
27 #define GIT_FOR_WINDOWS_URL L"https://gitforwindows.org/"
29 class CConfigureGitExe
33 : m_regCygwinHack(L
"Software\\TortoiseGit\\CygwinHack", FALSE
)
34 , m_regMsys2Hack(L
"Software\\TortoiseGit\\Msys2Hack", FALSE
)
37 static bool CheckGitVersion(HWND hwnd
)
39 if (CAppUtils::IsGitVersionNewerOrEqual(hwnd
, 2, 24))
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
)));
48 CMessageBox::RemoveRegistryKey(L
"OldMsysgitVersionWarning"); // only store answer if it is "Ignore"
50 ShellExecute(hwnd
, L
"open", GIT_FOR_WINDOWS_URL
, nullptr, nullptr, SW_SHOW
);
56 CRegDWORD m_regCygwinHack
;
57 CRegDWORD m_regMsys2Hack
;
58 CRegString m_regMsysGitPath
;
59 CRegString m_regMsysGitExtranPath
;
61 static void PerformCommonGitPathCleanup(CString
& path
)
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())
94 PerformCommonGitPathCleanup(gitpath
);
96 if (!CStringUtils::EndsWith(gitpath
, L
"bin"))
99 gitpath
.Truncate(gitpath
.GetLength() - 3);
100 gitpath
+= L
"mingw\\bin";
101 if (!::PathFileExists(gitpath
))
104 if (pathaddition
.Find(gitpath
+ L
';') >= 0 || CStringUtils::EndsWith(pathaddition
, gitpath
))
107 pathaddition
= gitpath
+ pathaddition
;
111 static bool SelectFolder(HWND hwnd
, CString
& gitpath
, CString
& pathaddition
)
117 if (!CCommonAppUtils::FileOpenSave(dir
, nullptr, 0, IDS_GITEXEFILEFILTER
, true, hwnd
))
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
);
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
);
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;
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
))
164 int ret
= g_Git
.Run(L
"git.exe --version", &out
, CP_UTF8
);
165 SetWindowText(GetDlgItem(hwnd
, versionLabelId
), out
);
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
);
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
);
183 else if (!CStringUtils::StartsWith(out
.Trim(), L
"git version "))
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
);
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
);
200 *needWorkarounds
= true;
201 if (!wasAlreadyWarned
)
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
);
209 *needWorkarounds
= true;
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
);
225 // force recalculation of Git version
226 CGit::ms_LastMsysGitVersion
= 0;
227 CRegDWORD(L
"Software\\TortoiseGit\\git_file_time").removeValue();
228 if (!CheckGitVersion(hwnd
))
231 // tell the cache to refresh everything and restart
232 SendCacheCommand(TGITCACHECOMMAND_REFRESHALL
);
233 SendCacheCommand(TGITCACHECOMMAND_END
);
239 template<class T
, class Reg
>
240 void StoreSetting(HWND hwnd
, const T
& value
, Reg
& registryKey
)
243 if (registryKey
.GetLastError() != ERROR_SUCCESS
)
244 CMessageBox::Show(hwnd
, registryKey
.getErrorString(), L
"TortoiseGit", MB_ICONERROR
);