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.
21 #include "StringUtils.h"
22 #include "BrowseFolder.h"
23 #include "PathUtils.h"
25 #include "MessageBox.h"
28 #define GIT_FOR_WINDOWS_URL L"https://git-for-windows.github.io/"
30 class CConfigureGitExe
34 : m_regCygwinHack(L
"Software\\TortoiseGit\\CygwinHack", FALSE
)
35 , m_regMsys2Hack(L
"Software\\TortoiseGit\\Msys2Hack", FALSE
)
38 static bool CheckGitVersion(HWND hwnd
)
40 if (CAppUtils::GetMsysgitVersion() >= 0x01090500)
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
);
47 CMessageBox::RemoveRegistryKey(L
"OldMsysgitVersionWarning"); // only store answer if it is "Ignore"
49 ShellExecute(hwnd
, L
"open", GIT_FOR_WINDOWS_URL
, nullptr, nullptr, SW_SHOW
);
55 CRegDWORD m_regCygwinHack
;
56 CRegDWORD m_regMsys2Hack
;
57 CRegString m_regMsysGitPath
;
58 CRegString m_regMsysGitExtranPath
;
60 static void PerformCommonGitPathCleanup(CString
& path
)
64 if (path
.Find(L
'%') >= 0)
66 int neededSize
= ExpandEnvironmentStrings(path
, nullptr, 0);
67 CString
origPath(path
);
68 ExpandEnvironmentStrings(origPath
, path
.GetBufferSetLength(neededSize
), 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
)
113 CBrowseFolder browseFolder
;
114 browseFolder
.m_style
= BIF_EDITBOX
| BIF_NEWDIALOGSTYLE
| BIF_RETURNFSANCESTORS
| BIF_RETURNONLYFSDIRS
;
118 dir
= CPathUtils::GetProgramsDirectory();
119 if (browseFolder
.Show(hwnd
, dir
) != CBrowseFolder::OK
)
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
);
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
);
144 CString oldpath
= m_regMsysGitPath
;
145 CString oldextranpath
= m_regMsysGitExtranPath
;
147 StoreSetting(hwnd
, gitpath
, m_regMsysGitPath
);
148 StoreSetting(hwnd
, pathaddition
, m_regMsysGitExtranPath
);
150 StoreSetting(hwnd
, oldpath
, m_regMsysGitPath
);
151 StoreSetting(hwnd
, oldextranpath
, m_regMsysGitExtranPath
);
154 g_Git
.m_bInitialized
= false;
156 if (g_Git
.CheckMsysGitDir(FALSE
))
159 int ret
= g_Git
.Run(L
"git.exe --version", &out
, CP_UTF8
);
160 SetWindowText(GetDlgItem(hwnd
, versionLabelId
), out
);
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
);
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
);
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
);
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
);
187 *needWorkarounds
= true;
188 if (!wasAlreadyWarned
)
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
);
196 *needWorkarounds
= true;
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
);
207 CGit::ms_LastMsysGitVersion
= 0;
208 if (!CheckGitVersion(hwnd
))
213 template<class T
, class Reg
>
214 void StoreSetting(HWND hwnd
, const T
& value
, Reg
& registryKey
)
217 if (registryKey
.GetLastError() != ERROR_SUCCESS
)
218 CMessageBox::Show(hwnd
, registryKey
.getErrorString(), L
"TortoiseGit", MB_ICONERROR
);