1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2016-2020, 2022-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.
22 #include "StringUtils.h"
25 TEST(libgit
, BrokenConfig
)
28 g_Git
.m_CurrentDir
= tempdir
.GetTempDir();
29 g_Git
.m_IsGitDllInited
= false;
30 g_Git
.m_IsUseGitDLL
= true;
31 g_Git
.m_IsUseLibGit2
= false;
32 g_Git
.m_IsUseLibGit2_mask
= 0;
33 // libgit relies on CWD being set to working tree
34 SetCurrentDirectory(g_Git
.m_CurrentDir
);
37 EXPECT_EQ(0, g_Git
.Run(L
"git.exe init", &output
, CP_UTF8
));
38 EXPECT_STRNE(L
"", output
);
39 CString testFile
= tempdir
.GetTempDir() + L
"\\.git\\config";
40 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(testFile
, L
"[push]\ndefault=something-that-is-invalid\n"));
42 EXPECT_THROW(g_Git
.CheckAndInitDll(), const char*);
48 g_Git
.m_CurrentDir
= tempdir
.GetTempDir();
49 // libgit relies on CWD being set to working tree
50 SetCurrentDirectory(g_Git
.m_CurrentDir
);
53 EXPECT_EQ(0, g_Git
.Run(L
"git.exe init", &output
, CP_UTF8
));
54 EXPECT_STRNE(L
"", output
);
55 g_Git
.ForceReInitDll();
57 GIT_MAILMAP mailmap
= reinterpret_cast<void*>(0x12345678);
58 git_read_mailmap(&mailmap
);
59 EXPECT_EQ(nullptr, mailmap
);
61 CString mailmapFile
= tempdir
.GetTempDir() + L
"\\.mailmap";
62 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(mailmapFile
, L
""));
64 mailmap
= reinterpret_cast<void*>(0x12345678);
65 git_read_mailmap(&mailmap
);
66 EXPECT_EQ(nullptr, mailmap
);
68 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(mailmapFile
, L
"Sven Strickroth <sven@tortoisegit.org>"));
69 git_read_mailmap(&mailmap
);
70 EXPECT_NE(nullptr, mailmap
);
71 const char* email1
= nullptr;
72 const char* author1
= nullptr;
73 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "email@cs-ware.de", nullptr, [](void*) { return "Sven S."; }));
74 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, "sven@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
75 EXPECT_EQ(nullptr, email1
);
76 EXPECT_STREQ("Sven Strickroth", author1
);
80 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, "Sven@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
81 EXPECT_EQ(nullptr, email1
);
82 EXPECT_STREQ("Sven Strickroth", author1
);
84 git_free_mailmap(mailmap
);
86 for (auto& entry
: { L
"", L
"1", L
"2", L
"A", L
"4", L
"5", L
"b", L
"7" })
87 content
.AppendFormat(L
"Sven%s Strickroth <sven%s@tortoisegit.org> <email%s@cs-ware.de>\n", entry
, entry
, entry
);
88 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(mailmapFile
, content
));
89 git_read_mailmap(&mailmap
);
90 EXPECT_NE(nullptr, mailmap
);
93 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "sven@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
94 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "aaa@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
95 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "zzz@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
96 for (auto& entry
: { "", "1", "2", "A", "4", "5", "b", "7" })
98 CStringA maillookup
, mail
, name
;
99 maillookup
.Format("email%s@cs-ware.de", entry
);
100 mail
.Format("sven%s@tortoisegit.org", entry
);
101 name
.Format("Sven%s Strickroth", entry
);
104 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, maillookup
, nullptr, [](void*) { return "Sven S."; }));
105 EXPECT_STREQ(mail
, email1
);
106 EXPECT_STREQ(name
, author1
);
111 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, "email@cs-ware.de", nullptr, [](void*) { return "Sven Strickroth"; }));
112 EXPECT_STREQ("sven@tortoisegit.org", email1
);
113 EXPECT_STREQ("Sven Strickroth", author1
);
115 git_free_mailmap(mailmap
);
116 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(mailmapFile
, L
"<sven@tortoisegit.org> <email@cs-ware.de>\nSven S. <sven@tortoisegit.org> Sven Strickroth <email@cs-ware.de>\n<another@example.com> S. Strickroth <email@cs-ware.de>\n"));
117 git_read_mailmap(&mailmap
);
118 EXPECT_NE(nullptr, mailmap
);
121 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "sven@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
122 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "aaa@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
123 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "zzz@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
124 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, "email@cs-ware.de", nullptr, [](void*) { return "Sven S."; }));
125 EXPECT_STREQ("sven@tortoisegit.org", email1
);
126 EXPECT_STREQ(nullptr, author1
);
129 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, "email@cs-ware.de", nullptr, [](void*) { return "Sven Strickroth"; }));
130 EXPECT_STREQ("sven@tortoisegit.org", email1
);
131 EXPECT_STREQ("Sven S.", author1
);
134 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, "email@cs-ware.de", nullptr, [](void*) { return "S. Strickroth"; }));
135 EXPECT_STREQ("another@example.com", email1
);
136 EXPECT_STREQ(nullptr, author1
);
141 CAutoTempDir tempdir
;
142 CString subdir
= tempdir
.GetTempDir() + L
"\\abc";
144 EXPECT_FALSE(PathFileExists(subdir
));
145 EXPECT_EQ(0, git_mkdir(CUnicodeUtils::GetUTF8(subdir
)));
146 EXPECT_TRUE(PathFileExists(subdir
));
147 EXPECT_TRUE(PathIsDirectory(subdir
));
148 EXPECT_EQ(-1, git_mkdir(CUnicodeUtils::GetUTF8(subdir
)));
151 TEST(libgit
, RefreshIndex
)
153 CAutoTempDir tempdir
;
154 g_Git
.m_CurrentDir
= tempdir
.GetTempDir();
155 g_Git
.m_bInitialized
= false;
156 g_Git
.m_IsGitDllInited
= false;
157 g_Git
.m_IsUseGitDLL
= true;
158 g_Git
.m_IsUseLibGit2
= false;
159 g_Git
.m_IsUseLibGit2_mask
= 0;
161 // libgit relies on CWD being set to working tree
162 SetCurrentDirectory(g_Git
.m_CurrentDir
);
164 git_repository_init_options options
= GIT_REPOSITORY_INIT_OPTIONS_INIT
;
165 options
.flags
= GIT_REPOSITORY_INIT_MKPATH
| GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE
;
166 CAutoRepository repo
;
167 ASSERT_EQ(0, git_repository_init_ext(repo
.GetPointer(), CUnicodeUtils::GetUTF8(tempdir
.GetTempDir()), &options
));
168 CAutoConfig
config(repo
);
169 ASSERT_TRUE(config
.IsValid());
170 CStringA path
= CUnicodeUtils::GetUTF8(g_Git
.m_CurrentDir
);
171 path
.Replace('\\', '/');
172 EXPECT_EQ(0, git_config_set_string(config
, "core.autocrlf", "false")); // newer Git complains for adding 1.enc with a safecrlf warning, especially an issue on AppVeyor where AutoCrLf=input is set
173 EXPECT_EQ(0, git_config_set_string(config
, "filter.openssl.clean", path
+ "/clean_filter_openssl"));
174 EXPECT_EQ(0, git_config_set_string(config
, "filter.openssl.smudge", path
+ "/smudge_filter_openssl"));
175 EXPECT_EQ(0, git_config_set_bool(config
, "filter.openssl.required", 1));
176 CString cleanFilterFilename
= g_Git
.m_CurrentDir
+ L
"\\clean_filter_openssl";
177 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(cleanFilterFilename
, L
"#!/bin/bash\nopenssl version | grep -q \" 1\\\\.0\\\\.\"\nif [[ $? = 0 ]]; then\n\topenssl enc -base64 -aes-256-ecb -S FEEDDEADBEEF -k PASS_FIXED\nelse\n\topenssl enc -base64 -pbkdf2 -aes-256-ecb -nosalt -k PASS_FIXED\nfi\n"));
178 CString smudgeFilterFilename
= g_Git
.m_CurrentDir
+ L
"\\smudge_filter_openssl";
179 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(smudgeFilterFilename
, L
"#!/bin/bash\nopenssl version | grep -q \" 1\\\\.0\\\\.\"\nif [[ $? = 0 ]]; then\n\topenssl enc -d -base64 -aes-256-ecb -k PASS_FIXED\nelse\n\topenssl enc -d -base64 -pbkdf2 -aes-256-ecb -nosalt -k PASS_FIXED\nfi\n"));
180 EXPECT_EQ(0, git_config_set_string(config
, "filter.test.clean", path
+ "/clean_filter_openssl"));
181 EXPECT_EQ(0, git_config_set_string(config
, "filter.test.smudge", path
+ "/smudge_filter_openssl"));
182 EXPECT_EQ(0, git_config_set_string(config
, "filter.test.process", path
+ "/clean_filter_openssl"));
183 EXPECT_EQ(0, git_config_set_bool(config
, "filter.test.required", 1));
185 // need to make sure sh.exe is on PATH
186 g_Git
.CheckMsysGitDir();
188 _wgetenv_s(&size
, nullptr, 0, L
"PATH");
190 auto oldEnv
= std::make_unique
<wchar_t[]>(size
);
192 _wgetenv_s(&size
, oldEnv
.get(), size
, L
"PATH");
193 _wputenv_s(L
"PATH", g_Git
.m_Environment
.GetEnv(L
"PATH"));
194 SCOPE_EXIT
{ _wputenv_s(L
"PATH", oldEnv
.get()); };
195 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\somefile.txt", L
"some content"));
197 g_Git
.RefreshGitIndex();
200 EXPECT_EQ(0, g_Git
.Run(L
"git.exe add somefile.txt", &output
, CP_UTF8
));
201 EXPECT_STREQ(L
"", output
);
203 g_Git
.RefreshGitIndex();
205 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\somefile.txt", L
"some other content"));
207 g_Git
.RefreshGitIndex();
210 EXPECT_EQ(0, g_Git
.Run(L
"git.exe add somefile.txt", &output
, CP_UTF8
));
211 EXPECT_STREQ(L
"", output
);
213 g_Git
.RefreshGitIndex();
215 // now check with external command filters defined
216 CString attributesFile
= g_Git
.m_CurrentDir
+ L
"\\.gitattributes";
217 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(attributesFile
, L
"*.enc filter=openssl\n"));
219 CString encryptedFileOne
= g_Git
.m_CurrentDir
+ L
"\\1.enc";
220 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(encryptedFileOne
, L
"This should be encrypted...\nAnd decrypted on the fly\n"));
223 EXPECT_EQ(0, g_Git
.Run(L
"git.exe add 1.enc", &output
, CP_UTF8
));
224 if (!g_Git
.ms_bCygwinGit
) // on AppVeyor with the VS2017 image we get a warning: "WARNING: can't open config file: /usr/local/ssl/openssl.cnf"
225 EXPECT_STREQ(L
"", output
);
227 WIN32_FILE_ATTRIBUTE_DATA fdata
;
228 GetFileAttributesEx(g_Git
.m_CurrentDir
+ L
"\\.git\\index", GetFileExInfoStandard
, &fdata
);
230 g_Git
.RefreshGitIndex();
232 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", L
"some other content"));
234 g_Git
.RefreshGitIndex();
236 // need racy timestamp
237 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", L
"somE other content"));
239 CAutoGeneralHandle handle
= ::CreateFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", FILE_WRITE_ATTRIBUTES
, 0, nullptr, 0, 0, nullptr);
240 SetFileTime(handle
, &fdata
.ftCreationTime
, &fdata
.ftLastAccessTime
, &fdata
.ftLastWriteTime
);
243 g_Git
.RefreshGitIndex();
245 // now check with external command filters with multi-filter (process) defined
246 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(attributesFile
, L
"*.enc filter=test\n"));
248 g_Git
.RefreshGitIndex();
250 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", L
"somE other conTentsome other conTentsome other conTen"));
252 g_Git
.RefreshGitIndex();
254 // need racy timestamp
255 GetFileAttributesEx(g_Git
.m_CurrentDir
+ L
"\\.git\\index", GetFileExInfoStandard
, &fdata
);
256 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", L
"some other conTentsome other conTentsome other conTen"));
258 CAutoGeneralHandle handle
= ::CreateFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", FILE_WRITE_ATTRIBUTES
, 0, nullptr, 0, 0, nullptr);
259 SetFileTime(handle
, &fdata
.ftCreationTime
, &fdata
.ftLastAccessTime
, &fdata
.ftLastWriteTime
);
262 g_Git
.RefreshGitIndex();
265 TEST(libgit
, IncludeIf
)
267 CAutoTempDir tempdir
;
268 g_Git
.m_bInitialized
= false;
269 g_Git
.m_IsGitDllInited
= false;
270 g_Git
.m_IsUseGitDLL
= true;
271 g_Git
.m_IsUseLibGit2
= false;
272 g_Git
.m_IsUseLibGit2_mask
= 0;
275 CString repoDir
= tempdir
.GetTempDir() + L
"\\RepoWithAInPath";
276 g_Git
.m_CurrentDir
= repoDir
;
277 EXPECT_TRUE(CreateDirectory(repoDir
, nullptr));
278 // libgit relies on CWD being set to working tree
279 EXPECT_TRUE(SetCurrentDirectory(repoDir
));
281 git_repository_init_options options
= GIT_REPOSITORY_INIT_OPTIONS_INIT
;
282 options
.flags
= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE
;
283 CAutoRepository repo
;
284 ASSERT_EQ(0, git_repository_init_ext(repo
.GetPointer(), CUnicodeUtils::GetUTF8(repoDir
), &options
));
286 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(repoDir
+ L
"\\.git\\config", L
"[core]\n repositoryformatversion = 0\n filemode = false\n bare = false\n logallrefupdates = true\n symlinks = false\n ignorecase = true\n hideDotFiles = dotGitOnly\n[something]\n thevalue = jap\n[includeIf \"gitdir:RepoWithAInPath/**\"]\n path = configA\n[includeIf \"gitdir:RepoWithBInPath/**\"]\n path = configB\n"));
287 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(repoDir
+ L
"\\.git\\configA", L
"[somethinga]\n thevalue = jop\n"));
288 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(repoDir
+ L
"\\.git\\configB", L
"[somethingb]\n thevalue = jup\n"));
290 EXPECT_STREQ(L
"jap", g_Git
.GetConfigValue(L
"something.thevalue"));
291 EXPECT_STREQ(L
"jop",g_Git
.GetConfigValue(L
"somethinga.thevalue"));
292 EXPECT_STREQ(L
"", g_Git
.GetConfigValue(L
"somethingb.thevalue"));
295 g_Git
.m_bInitialized
= false;
296 g_Git
.m_IsGitDllInited
= false;
297 g_Git
.m_IsUseGitDLL
= true;
298 repoDir
= tempdir
.GetTempDir() + L
"\\RepoWithBInPath";
299 g_Git
.m_CurrentDir
= repoDir
;
300 EXPECT_TRUE(CreateDirectory(repoDir
, nullptr));
301 // libgit relies on CWD being set to working tree
302 EXPECT_TRUE(SetCurrentDirectory(repoDir
));
303 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(repoDir
+ L
"\\.git", L
"gitdir: ../RepoWithAInPath/.git\n"));
305 EXPECT_STREQ(L
"jap", g_Git
.GetConfigValue(L
"something.thevalue"));
306 EXPECT_STREQ(L
"jop", g_Git
.GetConfigValue(L
"somethinga.thevalue"));
307 EXPECT_STREQ(L
"", g_Git
.GetConfigValue(L
"somethingb.thevalue"));
310 TEST(libgit
, StoreUninitializedRepositoryConfig
)
312 CAutoTempDir tempdir
;
313 g_Git
.m_CurrentDir
= tempdir
.GetTempDir();
314 g_Git
.m_IsGitDllInited
= false;
315 g_Git
.m_CurrentDir
= g_Git
.m_CurrentDir
;
316 g_Git
.m_IsUseGitDLL
= true;
317 g_Git
.m_IsUseLibGit2
= false;
318 g_Git
.m_IsUseLibGit2_mask
= 0;
319 // libgit relies on CWD being set to working tree
320 SetCurrentDirectory(g_Git
.m_CurrentDir
);
322 // clear any leftovers in caches
323 EXPECT_THROW(g_Git
.CheckAndInitDll(), const char*);
325 EXPECT_THROW(git_set_config("something", "else", CONFIG_LOCAL
), const char*);