1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2016-2019 - 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_CurrentDir
= g_Git
.m_CurrentDir
;
31 g_Git
.m_IsUseGitDLL
= true;
32 g_Git
.m_IsUseLibGit2
= false;
33 g_Git
.m_IsUseLibGit2_mask
= 0;
34 // libgit relies on CWD being set to working tree
35 SetCurrentDirectory(g_Git
.m_CurrentDir
);
38 EXPECT_EQ(0, g_Git
.Run(L
"git.exe init", &output
, CP_UTF8
));
39 EXPECT_STRNE(L
"", output
);
40 CString testFile
= tempdir
.GetTempDir() + L
"\\.git\\config";
41 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(testFile
, L
"[push]\ndefault=something-that-is-invalid\n"));
43 EXPECT_THROW(g_Git
.CheckAndInitDll(), const char*);
49 g_Git
.m_CurrentDir
= tempdir
.GetTempDir();
50 // libgit relies on CWD being set to working tree
51 SetCurrentDirectory(g_Git
.m_CurrentDir
);
53 GIT_MAILMAP mailmap
= (void*)0x12345678;
54 git_read_mailmap(&mailmap
);
55 EXPECT_EQ(nullptr, mailmap
);
57 CString mailmapFile
= tempdir
.GetTempDir() + L
"\\.mailmap";
58 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(mailmapFile
, L
""));
60 mailmap
= (void*)0x12345678;
61 git_read_mailmap(&mailmap
);
62 EXPECT_EQ(nullptr, mailmap
);
64 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(mailmapFile
, L
"Sven Strickroth <sven@tortoisegit.org>"));
65 git_read_mailmap(&mailmap
);
66 EXPECT_NE(nullptr, mailmap
);
67 const char* email1
= nullptr;
68 const char* author1
= nullptr;
69 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "email@cs-ware.de", nullptr, [](void*) { return "Sven S."; }));
70 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, "sven@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
71 EXPECT_EQ(nullptr, email1
);
72 EXPECT_STREQ("Sven Strickroth", author1
);
76 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, "Sven@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
77 EXPECT_EQ(nullptr, email1
);
78 EXPECT_STREQ("Sven Strickroth", author1
);
80 git_free_mailmap(mailmap
);
82 for (auto& entry
: { L
"", L
"1", L
"2", L
"A", L
"4", L
"5", L
"b", L
"7" })
83 content
.AppendFormat(L
"Sven%s Strickroth <sven%s@tortoisegit.org> <email%s@cs-ware.de>\n", entry
, entry
, entry
);
84 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(mailmapFile
, content
));
85 git_read_mailmap(&mailmap
);
86 EXPECT_NE(nullptr, mailmap
);
89 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "sven@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
90 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "aaa@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
91 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "zzz@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
92 for (auto& entry
: { "", "1", "2", "A", "4", "5", "b", "7" })
94 CStringA maillookup
, mail
, name
;
95 maillookup
.Format("email%s@cs-ware.de", entry
);
96 mail
.Format("sven%s@tortoisegit.org", entry
);
97 name
.Format("Sven%s Strickroth", entry
);
100 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, maillookup
, nullptr, [](void*) { return "Sven S."; }));
101 EXPECT_STREQ(mail
, email1
);
102 EXPECT_STREQ(name
, author1
);
107 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, "email@cs-ware.de", nullptr, [](void*) { return "Sven Strickroth"; }));
108 EXPECT_STREQ("sven@tortoisegit.org", email1
);
109 EXPECT_STREQ("Sven Strickroth", author1
);
111 git_free_mailmap(mailmap
);
112 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(mailmapFile
, L
"<sven@tortoisegit.org> <email@cs-ware.de>\nSven S. <sven@tortoisegit.org> Sven Strickroth <email@cs-ware.de>"));
113 git_read_mailmap(&mailmap
);
114 EXPECT_NE(nullptr, mailmap
);
117 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "sven@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
118 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "aaa@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
119 EXPECT_EQ(-1, git_lookup_mailmap(mailmap
, &email1
, &author1
, "zzz@tortoisegit.org", nullptr, [](void*) { return "Sven S."; }));
120 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, "email@cs-ware.de", nullptr, [](void*) { return "Sven S."; }));
121 EXPECT_STREQ("sven@tortoisegit.org", email1
);
122 EXPECT_STREQ(nullptr, author1
);
125 EXPECT_EQ(0, git_lookup_mailmap(mailmap
, &email1
, &author1
, "email@cs-ware.de", nullptr, [](void*) { return "Sven Strickroth"; }));
126 EXPECT_STREQ("sven@tortoisegit.org", email1
);
127 EXPECT_STREQ("Sven S.", author1
);
132 CAutoTempDir tempdir
;
133 CString subdir
= tempdir
.GetTempDir() + L
"\\abc";
135 EXPECT_FALSE(PathFileExists(subdir
));
136 EXPECT_EQ(0, git_mkdir(CUnicodeUtils::GetUTF8(subdir
)));
137 EXPECT_TRUE(PathFileExists(subdir
));
138 EXPECT_TRUE(PathIsDirectory(subdir
));
139 EXPECT_EQ(-1, git_mkdir(CUnicodeUtils::GetUTF8(subdir
)));
142 TEST(libgit
, RefreshIndex
)
144 CAutoTempDir tempdir
;
145 g_Git
.m_CurrentDir
= tempdir
.GetTempDir();
146 g_Git
.m_bInitialized
= false;
147 g_Git
.m_IsGitDllInited
= false;
148 g_Git
.m_IsUseGitDLL
= true;
149 g_Git
.m_IsUseLibGit2
= false;
150 g_Git
.m_IsUseLibGit2_mask
= 0;
152 // libgit relies on CWD being set to working tree
153 SetCurrentDirectory(g_Git
.m_CurrentDir
);
155 git_repository_init_options options
= GIT_REPOSITORY_INIT_OPTIONS_INIT
;
156 options
.flags
= GIT_REPOSITORY_INIT_MKPATH
| GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE
;
157 CAutoRepository repo
;
158 ASSERT_EQ(0, git_repository_init_ext(repo
.GetPointer(), CUnicodeUtils::GetUTF8(tempdir
.GetTempDir()), &options
));
159 CAutoConfig
config(repo
);
160 ASSERT_TRUE(config
.IsValid());
161 CStringA path
= CUnicodeUtils::GetUTF8(g_Git
.m_CurrentDir
);
162 path
.Replace('\\', '/');
163 EXPECT_EQ(0, git_config_set_string(config
, "filter.openssl.clean", path
+ "/clean_filter_openssl"));
164 EXPECT_EQ(0, git_config_set_string(config
, "filter.openssl.smudge", path
+ "/smudge_filter_openssl"));
165 EXPECT_EQ(0, git_config_set_bool(config
, "filter.openssl.required", 1));
166 CString cleanFilterFilename
= g_Git
.m_CurrentDir
+ L
"\\clean_filter_openssl";
167 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 -S FEEDDEADBEEFFEED -k PASS_FIXED\nfi\n"));
168 CString smudgeFilterFilename
= g_Git
.m_CurrentDir
+ L
"\\smudge_filter_openssl";
169 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 -k PASS_FIXED\nfi\n"));
170 EXPECT_EQ(0, git_config_set_string(config
, "filter.test.clean", path
+ "/clean_filter_openssl"));
171 EXPECT_EQ(0, git_config_set_string(config
, "filter.test.smudge", path
+ "/smudge_filter_openssl"));
172 EXPECT_EQ(0, git_config_set_string(config
, "filter.test.process", path
+ "/clean_filter_openssl"));
173 EXPECT_EQ(0, git_config_set_bool(config
, "filter.test.required", 1));
175 // need to make sure sh.exe is on PATH
176 g_Git
.CheckMsysGitDir();
178 _wgetenv_s(&size
, nullptr, 0, L
"PATH");
180 auto oldEnv
= std::make_unique
<wchar_t[]>(size
);
182 _wgetenv_s(&size
, oldEnv
.get(), size
, L
"PATH");
183 _wputenv_s(L
"PATH", g_Git
.m_Environment
.GetEnv(L
"PATH"));
184 SCOPE_EXIT
{ _wputenv_s(L
"PATH", oldEnv
.get()); };
185 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\somefile.txt", L
"some content"));
187 g_Git
.RefreshGitIndex();
190 EXPECT_EQ(0, g_Git
.Run(L
"git.exe add somefile.txt", &output
, CP_UTF8
));
191 EXPECT_STREQ(L
"", output
);
193 g_Git
.RefreshGitIndex();
195 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\somefile.txt", L
"some other 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 // now check with external command filters defined
206 CString attributesFile
= g_Git
.m_CurrentDir
+ L
"\\.gitattributes";
207 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(attributesFile
, L
"*.enc filter=openssl\n"));
209 CString encryptedFileOne
= g_Git
.m_CurrentDir
+ L
"\\1.enc";
210 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(encryptedFileOne
, L
"This should be encrypted...\nAnd decrypted on the fly\n"));
213 EXPECT_EQ(0, g_Git
.Run(L
"git.exe add 1.enc", &output
, CP_UTF8
));
214 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"
215 EXPECT_STREQ(L
"", output
);
217 WIN32_FILE_ATTRIBUTE_DATA fdata
;
218 GetFileAttributesEx(g_Git
.m_CurrentDir
+ L
"\\.git\\index", GetFileExInfoStandard
, &fdata
);
220 g_Git
.RefreshGitIndex();
222 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", L
"some other content"));
224 g_Git
.RefreshGitIndex();
226 // need racy timestamp
227 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", L
"somE other content"));
229 CAutoGeneralHandle handle
= ::CreateFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", FILE_WRITE_ATTRIBUTES
, 0, nullptr, 0, 0, nullptr);
230 SetFileTime(handle
, &fdata
.ftCreationTime
, &fdata
.ftLastAccessTime
, &fdata
.ftLastWriteTime
);
233 g_Git
.RefreshGitIndex();
235 // now check with external command filters with multi-filter (process) defined
236 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(attributesFile
, L
"*.enc filter=test\n"));
238 g_Git
.RefreshGitIndex();
240 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", L
"somE other conTentsome other conTentsome other conTen"));
242 g_Git
.RefreshGitIndex();
244 // need racy timestamp
245 GetFileAttributesEx(g_Git
.m_CurrentDir
+ L
"\\.git\\index", GetFileExInfoStandard
, &fdata
);
246 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", L
"some other conTentsome other conTentsome other conTen"));
248 CAutoGeneralHandle handle
= ::CreateFile(g_Git
.m_CurrentDir
+ L
"\\1.enc", FILE_WRITE_ATTRIBUTES
, 0, nullptr, 0, 0, nullptr);
249 SetFileTime(handle
, &fdata
.ftCreationTime
, &fdata
.ftLastAccessTime
, &fdata
.ftLastWriteTime
);
252 g_Git
.RefreshGitIndex();
255 TEST(libgit
, IncludeIf
)
257 CAutoTempDir tempdir
;
258 g_Git
.m_bInitialized
= false;
259 g_Git
.m_IsGitDllInited
= false;
260 g_Git
.m_IsUseGitDLL
= true;
261 g_Git
.m_IsUseLibGit2
= false;
262 g_Git
.m_IsUseLibGit2_mask
= 0;
265 CString repoDir
= tempdir
.GetTempDir() + L
"\\RepoWithAInPath";
266 g_Git
.m_CurrentDir
= repoDir
;
267 EXPECT_TRUE(CreateDirectory(repoDir
, nullptr));
268 // libgit relies on CWD being set to working tree
269 EXPECT_TRUE(SetCurrentDirectory(repoDir
));
271 git_repository_init_options options
= GIT_REPOSITORY_INIT_OPTIONS_INIT
;
272 options
.flags
= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE
;
273 CAutoRepository repo
;
274 ASSERT_EQ(0, git_repository_init_ext(repo
.GetPointer(), CUnicodeUtils::GetUTF8(repoDir
), &options
));
276 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"));
277 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(repoDir
+ L
"\\.git\\configA", L
"[somethinga]\n thevalue = jop\n"));
278 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(repoDir
+ L
"\\.git\\configB", L
"[somethingb]\n thevalue = jup\n"));
280 EXPECT_STREQ(L
"jap", g_Git
.GetConfigValue(L
"something.thevalue"));
281 EXPECT_STREQ(L
"jop",g_Git
.GetConfigValue(L
"somethinga.thevalue"));
282 EXPECT_STREQ(L
"", g_Git
.GetConfigValue(L
"somethingb.thevalue"));
285 g_Git
.m_bInitialized
= false;
286 g_Git
.m_IsGitDllInited
= false;
287 g_Git
.m_IsUseGitDLL
= true;
288 repoDir
= tempdir
.GetTempDir() + L
"\\RepoWithBInPath";
289 g_Git
.m_CurrentDir
= repoDir
;
290 EXPECT_TRUE(CreateDirectory(repoDir
, nullptr));
291 // libgit relies on CWD being set to working tree
292 EXPECT_TRUE(SetCurrentDirectory(repoDir
));
293 EXPECT_TRUE(CStringUtils::WriteStringToTextFile(repoDir
+ L
"\\.git", L
"gitdir: ../RepoWithAInPath/.git\n"));
295 EXPECT_STREQ(L
"jap", g_Git
.GetConfigValue(L
"something.thevalue"));
296 EXPECT_STREQ(L
"jop", g_Git
.GetConfigValue(L
"somethinga.thevalue"));
297 EXPECT_STREQ(L
"", g_Git
.GetConfigValue(L
"somethingb.thevalue"));
300 TEST(libgit
, StoreUninitializedRepositoryConfig
)
302 CAutoTempDir tempdir
;
303 g_Git
.m_CurrentDir
= tempdir
.GetTempDir();
304 g_Git
.m_IsGitDllInited
= false;
305 g_Git
.m_CurrentDir
= g_Git
.m_CurrentDir
;
306 g_Git
.m_IsUseGitDLL
= true;
307 g_Git
.m_IsUseLibGit2
= false;
308 g_Git
.m_IsUseLibGit2_mask
= 0;
309 // libgit relies on CWD being set to working tree
310 SetCurrentDirectory(g_Git
.m_CurrentDir
);
312 // clear any leftovers in caches
313 EXPECT_THROW(g_Git
.CheckAndInitDll(), const char*);
315 EXPECT_THROW(get_set_config("something", "else", CONFIG_LOCAL
), const char*);