27#include <wx/filename.h>
29#include <wx/stdpaths.h>
42 GitInitGuard() { git_libgit2_init(); }
43 ~GitInitGuard() { git_libgit2_shutdown(); }
53 auto now = std::chrono::steady_clock::now().time_since_epoch();
54 long long ticks = std::chrono::duration_cast<std::chrono::nanoseconds>( now ).count();
56 wxString base = wxFileName::GetTempDir();
59 fn.AppendDir( wxString::Format(
"kicad-qa-git-%lld-%p", ticks,
this ) );
60 wxFileName::Mkdir( fn.GetPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
67 char* resolved = realpath( fn.GetPath().utf8_string().c_str(),
nullptr );
71 path = wxString::FromUTF8( resolved );
86 wxFileName::Rmdir(
path, wxPATH_RMDIR_RECURSIVE );
92git_repository* makeRepoWithCommit(
const wxString& aRepoPath,
const wxString& aFileName,
93 const std::string& aFileContents )
95 git_repository* repo =
nullptr;
96 git_repository_init_options init_opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
97 init_opts.flags = GIT_REPOSITORY_INIT_MKPATH;
98 init_opts.initial_head =
"main";
100 BOOST_REQUIRE_EQUAL( git_repository_init_ext( &repo, aRepoPath.utf8_string().c_str(),
105 wxFileName filePath( aRepoPath, aFileName );
107 std::ofstream f( filePath.GetFullPath().utf8_string() );
112 git_config* cfg =
nullptr;
113 BOOST_REQUIRE_EQUAL( git_repository_config( &cfg, repo ), GIT_OK );
115 git_config_set_string( cfg,
"user.name",
"QA Test" );
119 git_index*
index =
nullptr;
120 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
122 BOOST_REQUIRE_EQUAL( git_index_add_bypath(
index, aFileName.utf8_string().c_str() ), GIT_OK );
123 BOOST_REQUIRE_EQUAL( git_index_write(
index ), GIT_OK );
126 BOOST_REQUIRE_EQUAL( git_index_write_tree( &tree_oid,
index ), GIT_OK );
128 git_tree* tree =
nullptr;
129 BOOST_REQUIRE_EQUAL( git_tree_lookup( &tree, repo, &tree_oid ), GIT_OK );
132 git_signature* sig =
nullptr;
133 BOOST_REQUIRE_EQUAL( git_signature_now( &sig,
"QA Test",
"[email protected]" ), GIT_OK );
137 BOOST_REQUIRE_EQUAL( git_commit_create( &commit_oid, repo,
"HEAD", sig, sig,
nullptr,
138 "initial", tree, 0,
nullptr ),
159 git_repository* repo = makeRepoWithCommit( tmp.
path,
"file.txt",
"data\n" );
165 BOOST_CHECK( !root.IsEmpty() );
166 BOOST_CHECK_MESSAGE( !root.Contains( wxS(
"/.git" ) ) && !root.Contains( wxS(
"\\.git" ) ),
167 "GetGitRootDirectory should return the working directory, got: "
168 + root.ToStdString() );
172 rootFn.AssignDir( root );
173 rootFn.MakeAbsolute();
175 tmpFn.AssignDir( tmp.
path );
176 tmpFn.MakeAbsolute();
177 BOOST_CHECK_EQUAL( rootFn.GetFullPath().ToStdString(), tmpFn.GetFullPath().ToStdString() );
189 git_repository* repo = makeRepoWithCommit( tmp.
path,
"file.txt",
"data\n" );
197 git_remote* remote =
nullptr;
198 BOOST_REQUIRE_EQUAL( git_remote_create( &remote, repo,
"github",
212 git_repository* repo = makeRepoWithCommit( tmp.
path,
"file.txt",
"data\n" );
215 git_remote* remote =
nullptr;
216 BOOST_REQUIRE_EQUAL( git_remote_create( &remote, repo,
"origin",
234 git_repository* repo = makeRepoWithCommit( tmp.
path,
"file.txt",
"data\n" );
240 BOOST_CHECK_MESSAGE( local.empty(),
241 "Expected no AHEAD files when no upstream is configured, got "
242 + std::to_string( local.size() ) );
243 BOOST_CHECK_MESSAGE( remote.empty(),
244 "Expected no BEHIND files when no upstream is configured, got "
245 + std::to_string( remote.size() ) );
262 git_repository* repo = makeRepoWithCommit( tmp.
path,
"untouched.txt",
"stable\n" );
267 wxFileName extra( tmp.
path, wxS(
"second.txt" ) );
268 std::ofstream f( extra.GetFullPath().utf8_string() );
272 git_index*
index =
nullptr;
273 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
275 BOOST_REQUIRE_EQUAL( git_index_add_bypath(
index,
"second.txt" ), GIT_OK );
276 BOOST_REQUIRE_EQUAL( git_index_write(
index ), GIT_OK );
279 BOOST_REQUIRE_EQUAL( git_index_write_tree( &newTreeOid,
index ), GIT_OK );
281 git_tree* newTree =
nullptr;
282 BOOST_REQUIRE_EQUAL( git_tree_lookup( &newTree, repo, &newTreeOid ), GIT_OK );
285 git_reference* head =
nullptr;
286 BOOST_REQUIRE_EQUAL( git_repository_head( &head, repo ), GIT_OK );
289 git_commit* parentCommit =
nullptr;
291 git_commit_lookup( &parentCommit, repo, git_reference_target( head ) ), GIT_OK );
294 git_signature* sig =
nullptr;
295 BOOST_REQUIRE_EQUAL( git_signature_now( &sig,
"QA Test",
"[email protected]" ), GIT_OK );
298 const git_commit* parents[1] = { parentCommit };
299 git_oid secondCommitOid;
300 BOOST_REQUIRE_EQUAL( git_commit_create( &secondCommitOid, repo,
"HEAD", sig, sig,
nullptr,
301 "second", newTree, 1, parents ),
305 git_treebuilder* tb =
nullptr;
306 BOOST_REQUIRE_EQUAL( git_treebuilder_new( &tb, repo,
nullptr ), GIT_OK );
309 const std::string upstreamData =
"upstream\n";
310 BOOST_REQUIRE_EQUAL( git_blob_create_from_buffer( &blobOid, repo, upstreamData.data(),
311 upstreamData.size() ),
313 BOOST_REQUIRE_EQUAL( git_treebuilder_insert(
nullptr, tb,
"remote_only.txt", &blobOid,
317 git_oid remoteTreeOid;
318 BOOST_REQUIRE_EQUAL( git_treebuilder_write( &remoteTreeOid, tb ), GIT_OK );
319 git_treebuilder_free( tb );
321 git_tree* remoteTree =
nullptr;
322 BOOST_REQUIRE_EQUAL( git_tree_lookup( &remoteTree, repo, &remoteTreeOid ), GIT_OK );
325 git_oid remoteCommitOid;
326 BOOST_REQUIRE_EQUAL( git_commit_create( &remoteCommitOid, repo,
nullptr, sig, sig,
nullptr,
327 "upstream root", remoteTree, 0,
nullptr ),
330 git_reference* remoteRef =
nullptr;
331 BOOST_REQUIRE_EQUAL( git_reference_create( &remoteRef, repo,
"refs/remotes/origin/main",
332 &remoteCommitOid,
true,
nullptr ),
336 git_config* cfg =
nullptr;
337 BOOST_REQUIRE_EQUAL( git_repository_config( &cfg, repo ), GIT_OK );
339 BOOST_REQUIRE_EQUAL( git_config_set_string( cfg,
"branch.main.remote",
"origin" ),
341 BOOST_REQUIRE_EQUAL( git_config_set_string( cfg,
"branch.main.merge",
"refs/heads/main" ),
352 BOOST_CHECK_MESSAGE( local.find( wxS(
"untouched.txt" ) ) == local.end(),
353 "untouched.txt should not be reported as AHEAD when the local and "
354 "remote histories share no ancestor" );
355 BOOST_CHECK_MESSAGE( remote.find( wxS(
"untouched.txt" ) ) == remote.end(),
356 "untouched.txt should not be reported as BEHIND when the local "
357 "and remote histories share no ancestor" );
370 git_repository* repo = makeRepoWithCommit( tmp.
path,
"untouched.txt",
"stable\n" );
374 wxFileName extra( tmp.
path, wxS(
"touched.txt" ) );
375 std::ofstream f( extra.GetFullPath().utf8_string() );
379 git_index*
index =
nullptr;
380 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
382 BOOST_REQUIRE_EQUAL( git_index_add_bypath(
index,
"touched.txt" ), GIT_OK );
383 BOOST_REQUIRE_EQUAL( git_index_write(
index ), GIT_OK );
386 BOOST_REQUIRE_EQUAL( git_index_write_tree( &treeOid,
index ), GIT_OK );
388 git_tree* tree =
nullptr;
389 BOOST_REQUIRE_EQUAL( git_tree_lookup( &tree, repo, &treeOid ), GIT_OK );
392 git_reference* head =
nullptr;
393 BOOST_REQUIRE_EQUAL( git_repository_head( &head, repo ), GIT_OK );
396 git_commit* parent =
nullptr;
397 BOOST_REQUIRE_EQUAL( git_commit_lookup( &parent, repo, git_reference_target( head ) ),
401 git_signature* sig =
nullptr;
402 BOOST_REQUIRE_EQUAL( git_signature_now( &sig,
"QA Test",
"[email protected]" ), GIT_OK );
405 const git_commit* parents[1] = { parent };
407 BOOST_REQUIRE_EQUAL( git_commit_create( &base_oid, repo,
"HEAD", sig, sig,
nullptr,
408 "add touched", tree, 1, parents ),
413 git_remote* origin =
nullptr;
414 BOOST_REQUIRE_EQUAL( git_remote_create( &origin, repo,
"origin",
419 git_reference* upstream_ref =
nullptr;
420 BOOST_REQUIRE_EQUAL( git_reference_create( &upstream_ref, repo,
"refs/remotes/origin/main",
421 &base_oid,
true,
nullptr ),
425 git_config* cfg =
nullptr;
426 BOOST_REQUIRE_EQUAL( git_repository_config( &cfg, repo ), GIT_OK );
428 BOOST_REQUIRE_EQUAL( git_config_set_string( cfg,
"branch.main.remote",
"origin" ),
430 BOOST_REQUIRE_EQUAL( git_config_set_string( cfg,
"branch.main.merge",
"refs/heads/main" ),
435 wxFileName extra( tmp.
path, wxS(
"touched.txt" ) );
436 std::ofstream f( extra.GetFullPath().utf8_string() );
440 BOOST_REQUIRE_EQUAL( git_index_add_bypath(
index,
"touched.txt" ), GIT_OK );
441 BOOST_REQUIRE_EQUAL( git_index_write(
index ), GIT_OK );
444 BOOST_REQUIRE_EQUAL( git_index_write_tree( &newTreeOid,
index ), GIT_OK );
446 git_tree* newTree =
nullptr;
447 BOOST_REQUIRE_EQUAL( git_tree_lookup( &newTree, repo, &newTreeOid ), GIT_OK );
450 git_commit* baseCommit =
nullptr;
451 BOOST_REQUIRE_EQUAL( git_commit_lookup( &baseCommit, repo, &base_oid ), GIT_OK );
454 const git_commit* aheadParents[1] = { baseCommit };
456 BOOST_REQUIRE_EQUAL( git_commit_create( &aheadOid, repo,
"HEAD", sig, sig,
nullptr,
457 "modify touched", newTree, 1, aheadParents ),
463 BOOST_CHECK( remote.empty() );
464 BOOST_CHECK_MESSAGE( local.find( wxS(
"touched.txt" ) ) != local.end(),
465 "touched.txt should be reported as AHEAD" );
466 BOOST_CHECK_MESSAGE( local.find( wxS(
"untouched.txt" ) ) == local.end(),
467 "untouched.txt should NOT be reported as AHEAD - this was the "
468 "regression in issue 21576" );
wxString GetGitRootDirectory() const
std::pair< std::set< wxString >, std::set< wxString > > GetDifferentFiles() const
Return a pair of sets of files that differ locally from the remote repository The first set is files ...
bool HasPushAndPullRemote() const
std::unique_ptr< git_tree, decltype([](git_tree *aTree) { git_tree_free(aTree); })> GitTreePtr
A unique pointer for git_tree objects with automatic cleanup.
std::unique_ptr< git_repository, decltype([](git_repository *aRepo) { git_repository_free(aRepo); })> GitRepositoryPtr
A unique pointer for git_repository objects with automatic cleanup.
std::unique_ptr< git_commit, decltype([](git_commit *aCommit) { git_commit_free(aCommit); })> GitCommitPtr
A unique pointer for git_commit objects with automatic cleanup.
std::unique_ptr< git_config, decltype([](git_config *aConfig) { git_config_free(aConfig); })> GitConfigPtr
A unique pointer for git_config objects with automatic cleanup.
std::unique_ptr< git_reference, decltype([](git_reference *aRef) { git_reference_free(aRef); })> GitReferencePtr
A unique pointer for git_reference objects with automatic cleanup.
std::unique_ptr< git_signature, decltype([](git_signature *aSignature) { git_signature_free(aSignature); })> GitSignaturePtr
A unique pointer for git_signature objects with automatic cleanup.
std::unique_ptr< git_index, decltype([](git_index *aIndex) { git_index_free(aIndex); })> GitIndexPtr
A unique pointer for git_index objects with automatic cleanup.
std::unique_ptr< git_remote, decltype([](git_remote *aRemote) { git_remote_free(aRemote); })> GitRemotePtr
A unique pointer for git_remote objects with automatic cleanup.
Scoped temporary directory used by the tests below.
ScopedTempDir(const wxString &aTag)
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(GitRootDirectoryReturnsWorkdir)
BOOST_CHECK_EQUAL(result, "25.4")