63#include <wx/filename.h>
64#include <wx/textfile.h>
96 git_repository_open( &repo,
m_repoPath.ToUTF8().data() );
103 const wxString& aMessage )
112 const std::string& aContent,
const char* aMessage )
116 git_commit* parentCommit =
nullptr;
117 BOOST_REQUIRE_EQUAL( git_commit_lookup( &parentCommit, aRepo, &aParentOid ), GIT_OK );
120 BOOST_REQUIRE_EQUAL( git_blob_create_from_buffer( &blobOid, aRepo, aContent.data(), aContent.size() ), GIT_OK );
122 git_tree* parentTree =
nullptr;
123 BOOST_REQUIRE_EQUAL( git_commit_tree( &parentTree, parentCommit ), GIT_OK );
125 git_treebuilder* builder =
nullptr;
126 BOOST_REQUIRE_EQUAL( git_treebuilder_new( &builder, aRepo, parentTree ), GIT_OK );
128 git_treebuilder_insert(
nullptr, builder, aFile.ToUTF8().data(), &blobOid, GIT_FILEMODE_BLOB ),
132 BOOST_REQUIRE_EQUAL( git_treebuilder_write( &treeOid, builder ), GIT_OK );
133 git_treebuilder_free( builder );
134 git_tree_free( parentTree );
136 git_tree* tree =
nullptr;
137 BOOST_REQUIRE_EQUAL( git_tree_lookup( &tree, aRepo, &treeOid ), GIT_OK );
139 git_signature* sig =
nullptr;
142 const git_commit* parents[1] = { parentCommit };
144 git_commit_create( &
result, aRepo,
nullptr, sig, sig,
nullptr, aMessage, tree, 1, parents ), GIT_OK );
146 git_signature_free( sig );
147 git_tree_free( tree );
148 git_commit_free( parentCommit );
155 char hex[GIT_OID_HEXSZ + 1] = { 0 };
156 git_oid_tostr( hex,
sizeof( hex ), &aOid );
158 wxString fetchHead =
repoPath() + wxFileName::GetPathSeparator() + wxT(
".git" )
159 + wxFileName::GetPathSeparator() + wxT(
"FETCH_HEAD" );
160 std::ofstream f( fetchHead.ToStdString() );
161 f << hex <<
"\t\tbranch 'master' of test\n";
167 git_remote* remote =
nullptr;
168 int rc = git_remote_create( &remote, aRepo,
"origin",
m_remotePath.ToUTF8().data() );
171 git_remote_free( remote );
181 git_config* cfg =
nullptr;
183 if( git_repository_config( &cfg, aRepo ) != 0 )
186 wxString remoteKey = wxString::Format(
"branch.%s.remote", aBranch );
187 wxString mergeKey = wxString::Format(
"branch.%s.merge", aBranch );
189 git_config_delete_entry( cfg, remoteKey.ToUTF8().data() );
190 git_config_delete_entry( cfg, mergeKey.ToUTF8().data() );
192 git_config_free( cfg );
200 git_config* cfg =
nullptr;
202 if( git_repository_config( &cfg, aRepo ) != 0 )
205 wxString remoteKey = wxString::Format(
"branch.%s.remote", aBranch );
206 wxString mergeKey = wxString::Format(
"branch.%s.merge", aBranch );
207 wxString mergeVal = wxString::Format(
"refs/heads/%s", aBranch );
209 git_config_set_string( cfg, remoteKey.ToUTF8().data(),
"origin" );
210 git_config_set_string( cfg, mergeKey.ToUTF8().data(), mergeVal.ToUTF8().data() );
211 git_config_free( cfg );
219 git_config* cfg =
nullptr;
221 if( git_repository_config( &cfg, aRepo ) != 0 )
222 return wxEmptyString;
224 git_buf buf = {
nullptr, 0, 0 };
227 if( git_config_get_string_buf( &buf, cfg, aKey.ToUTF8().data() ) == 0 && buf.ptr )
228 result = wxString::FromUTF8( buf.ptr );
230 git_buf_dispose( &buf );
231 git_config_free( cfg );
239 const wxString
path =
tempBase() + wxS(
"/global-ignore" );
240 git_config* rawConfig =
nullptr;
241 BOOST_REQUIRE_EQUAL( git_repository_config( &rawConfig, aRepo ), GIT_OK );
243 BOOST_REQUIRE_EQUAL( git_config_set_string(
config.get(),
"core.excludesfile",
path.utf8_str() ), GIT_OK );
253 git_config* cfg =
nullptr;
254 BOOST_REQUIRE_EQUAL( git_repository_config( &cfg, repo.get() ), GIT_OK );
256 BOOST_REQUIRE_EQUAL( git_config_set_bool( cfg,
"pull.rebase", 0 ), GIT_OK );
258 createCommit( repo.get(), wxT(
"file.txt" ), wxT(
"initial\n" ), wxT(
"Initial commit" ) );
265 git_clone_options opts;
266 git_clone_init_options( &opts, GIT_CLONE_OPTIONS_VERSION );
273 git_repository_free( bare );
295 BOOST_TEST_REQUIRE( ready() );
298 BOOST_TEST_REQUIRE( repo );
305 BOOST_REQUIRE_EQUAL( git_repository_open( &bare, remotePath().ToUTF8().data() ), GIT_OK );
309 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &head, bare,
"HEAD" ), GIT_OK );
311 for(
const char*
name : {
"refs/heads/aaa",
"refs/heads/zzz" } )
313 git_reference* ref =
nullptr;
314 BOOST_REQUIRE_EQUAL( git_reference_create( &ref, bare,
name, &head, 1,
nullptr ), GIT_OK );
315 git_reference_free( ref );
322 std::vector<std::string> names;
329 auto cb = [](
const char* aName,
const char*,
const git_oid*,
unsigned int aIsMerge,
void* aPayload ) ->
int
331 MARKED* m =
static_cast<MARKED*
>( aPayload );
335 m->names.push_back( std::string( aName ? aName :
"" ) );
340 BOOST_REQUIRE_EQUAL( git_repository_fetchhead_foreach( aRepo, cb, &marked ), GIT_OK );
349 BOOST_REQUIRE( clearUpstreamConfig( repo, wxT(
"master" ) ) );
354 MARKED marked = collect( repo );
367 MARKED marked = collect( repo );
369 BOOST_REQUIRE_EQUAL( marked.names.size(), 1u );
380 BOOST_TEST_REQUIRE( ready() );
383 BOOST_TEST_REQUIRE( repo );
391 BOOST_REQUIRE_EQUAL( git_repository_open( &bare, remotePath().ToUTF8().data() ), GIT_OK );
395 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &base, bare,
"refs/heads/master" ), GIT_OK );
397 git_oid attrs = createDetachedCommit( bare, base, wxT(
".gitattributes" ),
"*.kicad_pcb filter=lfs -text\n",
398 "Track boards with LFS" );
401 createDetachedCommit( bare, attrs, wxT(
"board.kicad_pcb" ),
402 "version https://git-lfs.github.com/spec/v1\n"
403 "oid sha256:0000000000000000000000000000000000000000000000000000000000000000\n"
407 git_reference* ref =
nullptr;
408 BOOST_REQUIRE_EQUAL( git_reference_create( &ref, bare,
"refs/heads/master", &board, 1,
nullptr ), GIT_OK );
409 git_reference_free( ref );
418 "the pull should refuse a commit that brings Git LFS in" );
419 BOOST_CHECK_MESSAGE( handler.
GetErrorString().Upper().Contains( wxS(
"LFS" ) ),
420 "the refusal should name Git LFS, got: " + handler.
GetErrorString().ToStdString() );
423 BOOST_CHECK( !wxFileName::FileExists( repoPath() + wxFileName::GetPathSeparator() + wxT(
"board.kicad_pcb" ) ) );
431 BOOST_TEST_REQUIRE( ready() );
434 BOOST_TEST_REQUIRE( repo );
446 "a cancelled push should stop at its next progress tick" );
452 const wxString
project = wxT(
"/repo/proj/" );
469 wxT(
"/repo/proj" ) ) );
473 wxT(
"C:\\repo\\proj\\" ) ) );
475 wxT(
"C:\\repo\\proj\\" ) ) );
487 BOOST_TEST_REQUIRE( ready() );
490 BOOST_TEST_REQUIRE( repo );
493 git_oid c1 = createCommit( repo, wxT(
"file.txt" ), wxT(
"edited\n" ), wxT(
"Edit file" ) );
496 git_reference* trackingRef =
nullptr;
497 git_reference_create( &trackingRef, repo,
"refs/remotes/origin/master", &c1, 1,
nullptr );
500 git_reference_free( trackingRef );
503 setUpstreamConfig( repo, wxT(
"master" ) );
506 git_reference* headRef =
nullptr;
507 git_repository_head( &headRef, repo );
509 git_commit* headCommit =
nullptr;
510 git_reference_peel( (git_object**) &headCommit, headRef, GIT_OBJECT_COMMIT );
512 git_tree* tree =
nullptr;
513 git_commit_tree( &tree, headCommit );
515 git_signature* sig =
nullptr;
519 int rc = git_commit_amend( &amendedOid, headCommit,
"HEAD", sig, sig,
nullptr,
"Edit file (message-only amend)",
521 BOOST_TEST_REQUIRE( rc == 0 );
523 git_tree_free( tree );
524 git_commit_free( headCommit );
525 git_reference_free( headRef );
526 git_signature_free( sig );
534 BOOST_CHECK_MESSAGE( ahead.empty(),
"Message-only amend should not report any AHEAD files; got "
535 + std::to_string( ahead.size() ) );
536 BOOST_CHECK_MESSAGE( behind.empty(),
"Message-only amend should not report any BEHIND files; got "
537 + std::to_string( behind.size() ) );
539 git_repository_free( repo );
546 BOOST_TEST_REQUIRE( ready() );
549 BOOST_TEST_REQUIRE( repo );
551 git_oid c1 = createCommit( repo, wxT(
"file.txt" ), wxT(
"edited\n" ), wxT(
"Edit file" ) );
553 git_reference* trackingRef =
nullptr;
554 git_reference_create( &trackingRef, repo,
"refs/remotes/origin/master", &c1, 1,
nullptr );
557 git_reference_free( trackingRef );
560 setUpstreamConfig( repo, wxT(
"master" ) );
564 createCommit( repo, wxT(
"file.txt" ), wxT(
"edited again\n" ), wxT(
"Re-edit file" ) );
569 BOOST_CHECK_MESSAGE( ahead.count( wxT(
"file.txt" ) ) == 1,
570 "AHEAD should contain file.txt when content differs from upstream" );
572 git_repository_free( repo );
579 BOOST_TEST_REQUIRE( ready() );
582 BOOST_TEST_REQUIRE( repo );
585 clearUpstreamConfig( repo, wxT(
"master" ) );
595 "Pull without upstream config should succeed via fallback; got "
596 + std::to_string(
static_cast<int>(
result ) ) +
", err='"
600 BOOST_CHECK_EQUAL( readConfig( repo, wxT(
"branch.master.remote" ) ), wxString(
"origin" ) );
601 BOOST_CHECK_EQUAL( readConfig( repo, wxT(
"branch.master.merge" ) ), wxString(
"refs/heads/master" ) );
603 git_repository_free( repo );
613 BOOST_TEST_REQUIRE( ready() );
616 BOOST_TEST_REQUIRE( repo );
619 setUpstreamConfig( repo, wxT(
"master" ) );
621 git_object* initial =
nullptr;
622 BOOST_TEST_REQUIRE( git_revparse_single( &initial, repo,
"HEAD" ) == 0 );
625 git_oid remoteOid = createCommit( repo, wxT(
"remote.txt" ), wxT(
"remote\n" ), wxT(
"Remote commit" ) );
632 BOOST_TEST_REQUIRE( git_reset( repo, initial, GIT_RESET_HARD,
nullptr ) == 0 );
633 git_object_free( initial );
635 createCommit( repo, wxT(
"local.txt" ), wxT(
"local\n" ), wxT(
"Local commit" ) );
637 git_config* cfg =
nullptr;
638 BOOST_TEST_REQUIRE( git_repository_config( &cfg, repo ) == 0 );
639 git_config_set_bool( cfg,
"pull.rebase", 1 );
640 git_config_free( cfg );
646 "Rebase pull failed: " + handler.
GetErrorString().ToStdString() );
647 BOOST_CHECK_EQUAL( git_repository_state( repo ),
static_cast<int>( GIT_REPOSITORY_STATE_NONE ) );
649 git_commit* head =
nullptr;
650 BOOST_TEST_REQUIRE( git_revparse_single( (git_object**) &head, repo,
"HEAD" ) == 0 );
651 BOOST_CHECK( git_oid_equal( git_commit_parent_id( head, 0 ), &remoteOid ) );
652 git_commit_free( head );
654 git_repository_free( repo );
661 BOOST_TEST_REQUIRE( ready() );
664 BOOST_TEST_REQUIRE( repo );
668 createCommit( repo, wxT(
"file.txt" ), wxT(
"second\n" ), wxT(
"Second commit" ) );
671 clearUpstreamConfig( repo, wxT(
"master" ) );
679 BOOST_CHECK_EQUAL( readConfig( repo, wxT(
"branch.master.remote" ) ), wxString(
"origin" ) );
680 BOOST_CHECK_EQUAL( readConfig( repo, wxT(
"branch.master.merge" ) ), wxString(
"refs/heads/master" ) );
682 git_repository_free( repo );
690 BOOST_TEST_REQUIRE( ready() );
693 BOOST_TEST_REQUIRE( repo );
695 createCommit( repo, wxT(
"file.txt" ), wxT(
"second\n" ), wxT(
"Second commit" ) );
699 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headOid, repo,
"HEAD" ), GIT_OK );
700 BOOST_REQUIRE_EQUAL( git_repository_set_head_detached( repo, &headOid ), GIT_OK );
706 BOOST_CHECK_MESSAGE( handler.
GetErrorString().Contains( wxS(
"branch" ) ),
707 "the refusal should say the project is not on a branch, got: "
710 git_repository_free( repo );
717 BOOST_TEST_REQUIRE( ready() );
720 BOOST_TEST_REQUIRE( repo );
724 BOOST_REQUIRE_EQUAL( git_repository_open( &remote, remotePath().ToUTF8().data() ), GIT_OK );
727 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &remoteHead, remote,
"refs/heads/master" ), GIT_OK );
729 git_oid ahead = createDetachedCommit( remote, remoteHead, wxT(
"theirs.txt" ),
"theirs\n",
"Remote commit" );
731 git_reference* ref =
nullptr;
732 BOOST_REQUIRE_EQUAL( git_reference_create( &ref, remote,
"refs/heads/master", &ahead, 1,
nullptr ), GIT_OK );
733 git_reference_free( ref );
734 git_repository_free( remote );
737 createCommit( repo, wxT(
"file.txt" ), wxT(
"second\n" ), wxT(
"Local commit" ) );
747 git_repository_free( repo );
756 BOOST_TEST_REQUIRE( ready() );
759 BOOST_TEST_REQUIRE( repo );
761 createCommit( repo, wxT(
"file.txt" ), wxT(
"second\n" ), wxT(
"Local commit" ) );
764 wxString headsDir = remotePath() + wxFileName::GetPathSeparator() + wxT(
"refs" ) + wxFileName::GetPathSeparator()
774 struct WRITABLE_AGAIN
777 ~WRITABLE_AGAIN() { chmod( dir.fn_str(), 0755 ); }
778 } restore{ headsDir };
780 chmod( headsDir.fn_str(), 0555 );
792 BOOST_REQUIRE_EQUAL( errors.size(), 1u );
793 BOOST_CHECK_MESSAGE( !errors.front().EndsWith( wxS(
"No error" ) ),
794 "the refusal lost git's reason: " + errors.front().ToStdString() );
796 git_repository_free( repo );
804 BOOST_TEST_REQUIRE( ready() );
807 BOOST_TEST_REQUIRE( repo );
811 BOOST_REQUIRE_EQUAL( git_repository_open( &remote, remotePath().ToUTF8().data() ), GIT_OK );
814 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &remoteHead, remote,
"refs/heads/master" ), GIT_OK );
816 git_oid ahead = createDetachedCommit( remote, remoteHead, wxT(
"file.txt" ),
"remote\n",
"Remote change" );
818 git_reference* ref =
nullptr;
819 BOOST_REQUIRE_EQUAL( git_reference_create( &ref, remote,
"refs/heads/master", &ahead, 1,
nullptr ), GIT_OK );
820 git_reference_free( ref );
821 git_repository_free( remote );
824 git_oid localHead = createCommit( repo, wxT(
"file.txt" ), wxT(
"local\n" ), wxT(
"Local change" ) );
827 setUpstreamConfig( repo, wxT(
"master" ) );
830 git_config* cfg =
nullptr;
831 BOOST_REQUIRE_EQUAL( git_repository_config( &cfg, repo ), GIT_OK );
832 git_config_set_bool( cfg,
"pull.rebase", 1 );
833 git_config_free( cfg );
845 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headNow, repo,
"HEAD" ), GIT_OK );
846 BOOST_CHECK( git_oid_equal( &headNow, &localHead ) );
848 wxTextFile file( repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" ) );
852 git_repository_free( repo );
859 BOOST_TEST_REQUIRE( ready() );
862 BOOST_TEST_REQUIRE( repo );
868 BOOST_REQUIRE_EQUAL( git_repository_open( &remote, remotePath().ToUTF8().data() ), GIT_OK );
871 git_reference* masterRef =
nullptr;
872 BOOST_REQUIRE_EQUAL( git_reference_lookup( &masterRef, remote,
"refs/heads/master" ), GIT_OK );
874 git_reference* mainRef =
nullptr;
875 BOOST_REQUIRE_EQUAL( git_reference_rename( &mainRef, masterRef,
"refs/heads/main", 0,
nullptr ), GIT_OK );
876 git_reference_free( masterRef );
877 BOOST_REQUIRE_EQUAL( git_repository_set_head( remote,
"refs/heads/main" ), GIT_OK );
879 git_oid remoteHead = *git_reference_target( mainRef );
880 git_reference_free( mainRef );
882 ahead = createDetachedCommit( remote, remoteHead, wxT(
"theirs.txt" ),
"theirs\n",
"Remote commit" );
884 git_reference* ref =
nullptr;
885 BOOST_REQUIRE_EQUAL( git_reference_create( &ref, remote,
"refs/heads/main", &ahead, 1,
nullptr ), GIT_OK );
886 git_reference_free( ref );
887 git_repository_free( remote );
893 git_config* cfg =
nullptr;
894 BOOST_REQUIRE_EQUAL( git_repository_config( &cfg, repo ), GIT_OK );
895 git_config_set_string( cfg,
"branch.master.remote",
"origin" );
896 git_config_set_string( cfg,
"branch.master.merge",
"refs/heads/main" );
897 git_config_free( cfg );
908 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headNow, repo,
"HEAD" ), GIT_OK );
909 BOOST_CHECK( git_oid_equal( &headNow, &ahead ) );
911 git_repository_free( repo );
919 BOOST_TEST_REQUIRE( ready() );
922 BOOST_TEST_REQUIRE( repo );
925 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &before, repo,
"HEAD" ), GIT_OK );
927 wxString trackedPath = repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" );
928 wxString contentBefore;
930 wxFFile f( trackedPath, wxT(
"rb" ) );
931 f.ReadAll( &contentBefore );
937 BOOST_REQUIRE_EQUAL( git_repository_open( &remote, remotePath().ToUTF8().data() ), GIT_OK );
940 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &remoteHead, remote,
"refs/heads/master" ), GIT_OK );
942 ahead = createDetachedCommit( remote, remoteHead, wxT(
"file.txt" ),
"changed by remote\n",
"Remote edit" );
944 git_reference* ref =
nullptr;
945 BOOST_REQUIRE_EQUAL( git_reference_create( &ref, remote,
"refs/heads/master", &ahead, 1,
nullptr ), GIT_OK );
946 git_reference_free( ref );
947 git_repository_free( remote );
953 wxString bystanderPath = repoPath() + wxFileName::GetPathSeparator() + wxT(
"bystander.txt" );
955 wxFFile bystander( bystanderPath, wxT(
"wb" ) );
956 BOOST_TEST_REQUIRE( bystander.IsOpened() );
957 bystander.Write( wxT(
"written by something else\n" ) );
961 wxString refLock = repoPath() + wxFileName::GetPathSeparator() + wxT(
".git" ) + wxFileName::GetPathSeparator()
962 + wxT(
"refs" ) + wxFileName::GetPathSeparator() + wxT(
"heads" )
963 + wxFileName::GetPathSeparator() + wxT(
"master.lock" );
965 wxFFile lock( refLock, wxT(
"wb" ) );
966 BOOST_TEST_REQUIRE( lock.IsOpened() );
967 lock.Write( wxT(
"locked\n" ) );
977 wxRemoveFile( refLock );
978 git_repository_free( repo );
982 BOOST_REQUIRE_EQUAL( git_repository_open( &fresh, repoPath().ToUTF8().data() ), GIT_OK );
985 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headAfter, fresh,
"HEAD" ), GIT_OK );
986 BOOST_CHECK_MESSAGE( git_oid_equal( &headAfter, &before ),
"HEAD must not have moved" );
988 wxString contentAfter;
990 wxFFile f( trackedPath, wxT(
"rb" ) );
991 f.ReadAll( &contentAfter );
994 BOOST_CHECK_MESSAGE( contentAfter == contentBefore,
"the working file must be back at the commit HEAD points at" );
997 git_status_options opts;
998 git_status_init_options( &opts, GIT_STATUS_OPTIONS_VERSION );
999 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
1001 git_status_list* list =
nullptr;
1002 BOOST_REQUIRE_EQUAL( git_status_list_new( &list, fresh, &opts ), GIT_OK );
1003 size_t dirty = git_status_list_entrycount( list );
1004 git_status_list_free( list );
1006 BOOST_CHECK_MESSAGE( dirty == 0,
"the rollback must leave no phantom local changes" );
1009 BOOST_CHECK_MESSAGE( wxFileName::FileExists( bystanderPath ),
1010 "the rollback must not remove a file outside the paths the pull wrote" );
1012 git_repository_free( fresh );
1020 BOOST_TEST_REQUIRE( ready() );
1023 BOOST_TEST_REQUIRE( repo );
1028 BOOST_REQUIRE_EQUAL( git_repository_open( &remote, remotePath().ToUTF8().data() ), GIT_OK );
1031 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &remoteHead, remote,
"refs/heads/master" ), GIT_OK );
1033 git_oid ahead = createDetachedCommit( remote, remoteHead, wxT(
"notes" ),
"from the remote\n",
"Add notes" );
1035 git_reference* ref =
nullptr;
1036 BOOST_REQUIRE_EQUAL( git_reference_create( &ref, remote,
"refs/heads/master", &ahead, 1,
nullptr ), GIT_OK );
1037 git_reference_free( ref );
1038 git_repository_free( remote );
1045 wxString notesDir = repoPath() + wxFileName::GetPathSeparator() + wxT(
"notes" );
1046 BOOST_REQUIRE( wxFileName::Mkdir( notesDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
1048 wxString keptFile = notesDir + wxFileName::GetPathSeparator() + wxT(
"keep.txt" );
1050 wxFFile f( keptFile, wxT(
"wb" ) );
1051 BOOST_TEST_REQUIRE( f.IsOpened() );
1052 f.Write( wxT(
"the only copy\n" ) );
1061 BOOST_CHECK_MESSAGE(
static_cast<int>(
result ) < 0,
"the checkout cannot succeed over a directory" );
1063 git_repository_free( repo );
1065 BOOST_CHECK_MESSAGE( wxFileName::DirExists( notesDir ),
1066 "the rollback must not remove an untracked directory it did not write" );
1067 BOOST_REQUIRE_MESSAGE( wxFileName::FileExists( keptFile ),
"the file inside it must survive" );
1071 wxFFile f( keptFile, wxT(
"rb" ) );
1072 f.ReadAll( &after );
1081 BOOST_TEST_REQUIRE( ready() );
1083 wxString emptyDir = tempBase() + wxFileName::GetPathSeparator() + wxT(
"unborn_rollback" );
1084 wxFileName::Mkdir( emptyDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
1087 BOOST_REQUIRE_EQUAL( git_repository_init( &fresh, emptyDir.ToUTF8().data(), 0 ), GIT_OK );
1090 git_remote* remote =
nullptr;
1091 BOOST_REQUIRE_EQUAL( git_remote_create( &remote, fresh,
"origin", remotePath().ToUTF8().data() ), GIT_OK );
1092 git_remote_free( remote );
1096 wxString
collide = emptyDir + wxFileName::GetPathSeparator() + wxT(
"file.txt" );
1098 wxFFile f(
collide, wxT(
"wb" ) );
1099 f.Write( wxT(
"mine\n" ) );
1108 "a colliding untracked file must not be overwritten" );
1110 git_repository_free( fresh );
1113 BOOST_REQUIRE_EQUAL( git_repository_open( &reopened, emptyDir.ToUTF8().data() ), GIT_OK );
1115 BOOST_CHECK_MESSAGE( git_repository_head_unborn( reopened ) == 1,
1116 "the branch must still be unborn after a refused checkout" );
1120 wxFFile f(
collide, wxT(
"rb" ) );
1126 git_repository_free( reopened );
1128 if( wxFileName::DirExists( emptyDir ) )
1129 wxFileName::Rmdir( emptyDir, wxPATH_RMDIR_RECURSIVE );
1135 BOOST_TEST_REQUIRE( ready() );
1138 BOOST_TEST_REQUIRE( repo );
1145 std::unique_lock<std::mutex> held = common.
LockAction();
1158 git_repository_free( repo );
1165 BOOST_TEST_REQUIRE( ready() );
1168 BOOST_TEST_REQUIRE( repo );
1172 BOOST_REQUIRE_EQUAL( git_repository_open( &remote, remotePath().ToUTF8().data() ), GIT_OK );
1175 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &remoteHead, remote,
"refs/heads/master" ), GIT_OK );
1177 git_oid ahead = createDetachedCommit( remote, remoteHead, wxT(
"theirs.txt" ),
"theirs\n",
"Remote adds" );
1179 git_reference* ref =
nullptr;
1180 BOOST_REQUIRE_EQUAL( git_reference_create( &ref, remote,
"refs/heads/master", &ahead, 1,
nullptr ), GIT_OK );
1181 git_reference_free( ref );
1182 git_repository_free( remote );
1185 wxString collidingPath = repoPath() + wxFileName::GetPathSeparator() + wxT(
"theirs.txt" );
1187 std::ofstream f( collidingPath.ToStdString() );
1188 f <<
"mine, never committed\n";
1192 setUpstreamConfig( repo, wxT(
"master" ) );
1195 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headBefore, repo,
"HEAD" ), GIT_OK );
1203 BOOST_CHECK( handler.
GetErrorString().Contains( wxT(
"theirs.txt" ) ) );
1206 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headAfter, repo,
"HEAD" ), GIT_OK );
1207 BOOST_CHECK( git_oid_equal( &headBefore, &headAfter ) );
1209 wxTextFile file( collidingPath );
1213 git_repository_free( repo );
1220 BOOST_TEST_REQUIRE( ready() );
1223 BOOST_TEST_REQUIRE( repo );
1227 BOOST_REQUIRE_EQUAL( git_repository_open( &remote, remotePath().ToUTF8().data() ), GIT_OK );
1230 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &remoteHead, remote,
"refs/heads/master" ), GIT_OK );
1232 git_oid ahead = createDetachedCommit( remote, remoteHead, wxT(
"theirs.txt" ),
"theirs\n",
"Remote adds" );
1234 git_reference* ref =
nullptr;
1235 BOOST_REQUIRE_EQUAL( git_reference_create( &ref, remote,
"refs/heads/master", &ahead, 1,
nullptr ), GIT_OK );
1236 git_reference_free( ref );
1237 git_repository_free( remote );
1240 git_oid localHead = createCommit( repo, wxT(
"other.txt" ), wxT(
"local\n" ), wxT(
"Local change" ) );
1242 wxString collidingPath = repoPath() + wxFileName::GetPathSeparator() + wxT(
"theirs.txt" );
1244 std::ofstream f( collidingPath.ToStdString() );
1245 f <<
"mine, never committed\n";
1249 setUpstreamConfig( repo, wxT(
"master" ) );
1257 BOOST_CHECK( handler.
GetErrorString().Contains( wxT(
"theirs.txt" ) ) );
1261 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headAfter, repo,
"HEAD" ), GIT_OK );
1262 BOOST_CHECK( git_oid_equal( &localHead, &headAfter ) );
1264 git_repository_free( repo );
1271 BOOST_TEST_REQUIRE( ready() );
1273 wxString freshPath = tempBase() + wxFileName::GetPathSeparator() + wxT(
"fresh" );
1276 git_repository_init_options initOpts;
1277 git_repository_init_options_init( &initOpts, GIT_REPOSITORY_INIT_OPTIONS_VERSION );
1278 initOpts.flags = GIT_REPOSITORY_INIT_MKPATH;
1279 initOpts.initial_head =
"master";
1280 BOOST_REQUIRE_EQUAL( git_repository_init_ext( &repo, freshPath.ToUTF8().data(), &initOpts ), GIT_OK );
1281 BOOST_REQUIRE_EQUAL( git_repository_head_unborn( repo ), 1 );
1293 BOOST_CHECK( wxFileName::FileExists( freshPath + wxFileName::GetPathSeparator() + wxT(
"file.txt" ) ) );
1295 git_repository_free( repo );
1301 BOOST_TEST_REQUIRE( ready() );
1304 BOOST_TEST_REQUIRE( repo );
1307 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &base, repo,
"HEAD" ), GIT_OK );
1309 git_oid side = createDetachedCommit( repo, base, wxT(
"file.txt" ),
"side\n",
"Side change" );
1310 git_oid localHead = createCommit( repo, wxT(
"file.txt" ), wxT(
"local\n" ), wxT(
"Local change" ) );
1313 git_annotated_commit* sideCommit =
nullptr;
1314 BOOST_REQUIRE_EQUAL( git_annotated_commit_lookup( &sideCommit, repo, &side ), GIT_OK );
1316 const git_annotated_commit* heads[1] = { sideCommit };
1317 git_checkout_options checkoutOpts = GIT_CHECKOUT_OPTIONS_INIT;
1318 checkoutOpts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
1320 BOOST_REQUIRE_EQUAL( git_merge( repo, heads, 1,
nullptr, &checkoutOpts ), GIT_OK );
1321 git_annotated_commit_free( sideCommit );
1324 BOOST_REQUIRE_EQUAL( git_repository_state( repo ), GIT_REPOSITORY_STATE_MERGE );
1337 git_index*
index =
nullptr;
1338 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
1339 BOOST_CHECK( !git_index_has_conflicts(
index ) );
1340 git_index_free(
index );
1343 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headNow, repo,
"HEAD" ), GIT_OK );
1344 BOOST_CHECK( git_oid_equal( &headNow, &localHead ) );
1346 wxTextFile file( repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" ) );
1350 git_repository_free( repo );
1362 BOOST_TEST_REQUIRE( ready() );
1365 BOOST_TEST_REQUIRE( repo );
1368 createCommit( repo, wxT(
"bystander.txt" ), wxT(
"keep\n" ), wxT(
"Add bystander" ) );
1371 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &base, repo,
"HEAD" ), GIT_OK );
1373 git_oid sideEdit = createDetachedCommit( repo, base, wxT(
"file.txt" ),
"side\n",
"Side change" );
1376 git_oid side = createDetachedCommit( repo, sideEdit, wxT(
"fromside.txt" ),
"new\n",
"Side addition" );
1378 createCommit( repo, wxT(
"file.txt" ), wxT(
"local\n" ), wxT(
"Local change" ) );
1381 wxString bystanderPath = repoPath() + wxFileName::GetPathSeparator() + wxT(
"bystander.txt" );
1383 std::ofstream f( bystanderPath.ToStdString() );
1384 f <<
"my own work\n";
1388 git_annotated_commit* sideCommit =
nullptr;
1389 BOOST_REQUIRE_EQUAL( git_annotated_commit_lookup( &sideCommit, repo, &side ), GIT_OK );
1391 const git_annotated_commit* heads[1] = { sideCommit };
1392 git_checkout_options checkoutOpts = GIT_CHECKOUT_OPTIONS_INIT;
1393 checkoutOpts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
1395 BOOST_REQUIRE_EQUAL( git_merge( repo, heads, 1,
nullptr, &checkoutOpts ), GIT_OK );
1396 git_annotated_commit_free( sideCommit );
1399 BOOST_REQUIRE_EQUAL( git_repository_state( repo ), GIT_REPOSITORY_STATE_MERGE );
1409 git_index*
index =
nullptr;
1410 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
1412 BOOST_CHECK( !git_index_has_conflicts(
index ) );
1415 std::ifstream f( ( repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" ) ).
ToStdString() );
1416 std::string content( ( std::istreambuf_iterator<char>( f ) ), std::istreambuf_iterator<char>() );
1421 BOOST_CHECK( !wxFileName::FileExists( repoPath() + wxFileName::GetPathSeparator() + wxT(
"fromside.txt" ) ) );
1425 std::ifstream f( bystanderPath.ToStdString() );
1426 std::string content( ( std::istreambuf_iterator<char>( f ) ), std::istreambuf_iterator<char>() );
1430 git_repository_free( repo );
1436 BOOST_TEST_REQUIRE( ready() );
1439 BOOST_TEST_REQUIRE( repo );
1442 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &head, repo,
"HEAD" ), GIT_OK );
1445 wxString gitDir = repoPath() + wxFileName::GetPathSeparator() + wxT(
".git" ) + wxFileName::GetPathSeparator();
1448 std::ofstream log( ( gitDir + wxT(
"BISECT_LOG" ) ).
ToStdString() );
1449 log <<
"git bisect start\n";
1453 std::ofstream start( ( gitDir + wxT(
"BISECT_START" ) ).
ToStdString() );
1454 start <<
"master\n";
1457 BOOST_REQUIRE_EQUAL( git_repository_state( repo ), GIT_REPOSITORY_STATE_BISECT );
1462 BOOST_CHECK_MESSAGE( !common.
AbandonOperation( error ),
"a bisect must not be abandoned from here" );
1463 BOOST_CHECK( !error.IsEmpty() );
1464 BOOST_CHECK_MESSAGE( wxFileName::FileExists( gitDir + wxT(
"BISECT_LOG" ) ),
1465 "the bisect bookkeeping must survive" );
1467 wxRemoveFile( gitDir + wxT(
"BISECT_LOG" ) );
1468 wxRemoveFile( gitDir + wxT(
"BISECT_START" ) );
1469 git_repository_free( repo );
1475 BOOST_TEST_REQUIRE( ready() );
1478 BOOST_TEST_REQUIRE( repo );
1480 const wxString sep = wxFileName::GetPathSeparator();
1483 const wxString rebaseDir = repoPath() + sep + wxT(
".git" ) + sep + wxT(
"rebase-merge" );
1485 BOOST_REQUIRE( wxFileName::Mkdir( rebaseDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
1488 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &head, repo,
"HEAD" ), GIT_OK );
1490 const std::string headHash = git_oid_tostr_s( &head );
1492 auto writeRebaseFile = [&](
const wxString& aName,
const std::string& aText )
1494 std::ofstream out( ( rebaseDir + sep + aName ).
ToStdString() );
1495 out << aText <<
"\n";
1498 writeRebaseFile( wxT(
"interactive" ),
"" );
1499 writeRebaseFile( wxT(
"head-name" ),
"refs/heads/master" );
1500 writeRebaseFile( wxT(
"onto" ), headHash );
1501 writeRebaseFile( wxT(
"orig-head" ), headHash );
1503 BOOST_REQUIRE_EQUAL( git_repository_state( repo ), GIT_REPOSITORY_STATE_REBASE_INTERACTIVE );
1509 BOOST_CHECK_MESSAGE( error.Contains( wxT(
"another program" ) ), error );
1510 BOOST_CHECK_MESSAGE( !error.Lower().Contains( wxT(
"not supported" ) ), error );
1512 for(
const wxString&
name : { wxT(
"interactive" ), wxT(
"head-name" ), wxT(
"onto" ), wxT(
"orig-head" ) } )
1513 wxRemoveFile( rebaseDir + sep +
name );
1515 wxFileName::Rmdir( rebaseDir );
1516 git_repository_free( repo );
1522 BOOST_TEST_REQUIRE( ready() );
1525 BOOST_TEST_REQUIRE( repo );
1528 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &base, repo,
"HEAD" ), GIT_OK );
1530 git_oid side = createDetachedCommit( repo, base, wxT(
"file.txt" ),
"side\n",
"Side change" );
1531 git_oid localHead = createCommit( repo, wxT(
"file.txt" ), wxT(
"local\n" ), wxT(
"Local change" ) );
1534 git_annotated_commit* upstream =
nullptr;
1535 BOOST_REQUIRE_EQUAL( git_annotated_commit_lookup( &upstream, repo, &side ), GIT_OK );
1537 git_rebase* rebase =
nullptr;
1538 BOOST_REQUIRE_EQUAL( git_rebase_init( &rebase, repo,
nullptr, upstream,
nullptr,
nullptr ), GIT_OK );
1540 git_rebase_operation* op =
nullptr;
1541 BOOST_REQUIRE_EQUAL( git_rebase_next( &op, rebase ), GIT_OK );
1544 git_rebase_free( rebase );
1545 git_annotated_commit_free( upstream );
1548 BOOST_REQUIRE_EQUAL( git_repository_state( repo ), GIT_REPOSITORY_STATE_REBASE_MERGE );
1549 BOOST_REQUIRE_EQUAL( git_repository_head_detached( repo ), 1 );
1559 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headNow, repo,
"HEAD" ), GIT_OK );
1560 BOOST_CHECK( git_oid_equal( &headNow, &localHead ) );
1562 wxTextFile file( repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" ) );
1566 git_repository_free( repo );
1574 BOOST_TEST_REQUIRE( ready() );
1577 BOOST_TEST_REQUIRE( repo );
1581 BOOST_REQUIRE_EQUAL( git_repository_open( &remote, remotePath().ToUTF8().data() ), GIT_OK );
1584 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &remoteHead, remote,
"refs/heads/master" ), GIT_OK );
1586 git_oid ahead = createDetachedCommit( remote, remoteHead, wxT(
"file.txt" ),
"remote\n",
"Remote change" );
1588 git_reference* ref =
nullptr;
1589 BOOST_REQUIRE_EQUAL( git_reference_create( &ref, remote,
"refs/heads/master", &ahead, 1,
nullptr ), GIT_OK );
1590 git_reference_free( ref );
1591 git_repository_free( remote );
1594 wxString filePath = repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" );
1596 std::ofstream f( filePath.ToStdString() );
1597 f <<
"local edit\n";
1601 setUpstreamConfig( repo, wxT(
"master" ) );
1604 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headBefore, repo,
"HEAD" ), GIT_OK );
1614 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headAfter, repo,
"HEAD" ), GIT_OK );
1615 BOOST_CHECK( git_oid_equal( &headBefore, &headAfter ) );
1617 wxTextFile file( filePath );
1621 git_repository_free( repo );
1628 BOOST_TEST_REQUIRE( ready() );
1631 BOOST_TEST_REQUIRE( repo );
1633 createCommit( repo, wxT(
"keep.txt" ), wxT(
"keep\n" ), wxT(
"Add keep" ) );
1634 createCommit( repo, wxT(
"gone.txt" ), wxT(
"gone\n" ), wxT(
"Add gone" ) );
1636 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
1638 auto writeAll = [](
const wxString& aPath,
const std::string& aContent )
1640 std::ofstream f( aPath.ToStdString(), std::ios::binary );
1644 auto readAll = [](
const wxString& aPath )
1646 std::ifstream in( aPath.ToStdString(), std::ios::binary );
1647 return std::string( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
1650 writeAll( workDir + wxT(
"file.txt" ),
"accidental\n" );
1651 writeAll( workDir + wxT(
"keep.txt" ),
"still dirty\n" );
1652 writeAll( workDir + wxT(
"new.txt" ),
"never committed\n" );
1653 BOOST_TEST_REQUIRE( wxRemoveFile( workDir + wxT(
"gone.txt" ) ) );
1657 BOOST_CHECK( common.
IsPathInHead( wxT(
"file.txt" ) ) );
1658 BOOST_CHECK( !common.
IsPathInHead( wxT(
"new.txt" ) ) );
1661 BOOST_REQUIRE_MESSAGE( common.
DiscardChanges( { wxT(
"file.txt" ), wxT(
"gone.txt" ) }, error ), error );
1666 BOOST_CHECK_EQUAL( readAll( workDir + wxT(
"new.txt" ) ),
"never committed\n" );
1668 git_repository_free( repo );
1676 BOOST_TEST_REQUIRE( ready() );
1679 BOOST_TEST_REQUIRE( repo );
1681 createCommit( repo, wxT(
"board[1].txt" ), wxT(
"committed\n" ), wxT(
"Add bracketed" ) );
1682 createCommit( repo, wxT(
"board1.txt" ), wxT(
"other committed\n" ), wxT(
"Add plain" ) );
1684 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
1686 auto writeAll = [](
const wxString& aPath,
const std::string& aContent )
1688 std::ofstream f( aPath.ToStdString(), std::ios::binary );
1692 auto readAll = [](
const wxString& aPath )
1694 std::ifstream in( aPath.ToStdString(), std::ios::binary );
1695 return std::string( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
1698 writeAll( workDir + wxT(
"board[1].txt" ),
"edited\n" );
1699 writeAll( workDir + wxT(
"board1.txt" ),
"edited too\n" );
1704 BOOST_REQUIRE_MESSAGE( common.
DiscardChanges( { wxT(
"board[1].txt" ) }, error ), error );
1709 git_repository_free( repo );
1717 BOOST_TEST_REQUIRE( ready() );
1720 BOOST_TEST_REQUIRE( repo );
1722 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
1724 auto writeAll = [](
const wxString& aPath,
const std::string& aContent )
1726 wxFileName fn( aPath );
1727 fn.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
1729 std::ofstream f( aPath.ToStdString(), std::ios::binary );
1733 writeAll( workDir + wxT(
"a[1]/x.txt" ),
"one\n" );
1734 writeAll( workDir + wxT(
"a1/y.txt" ),
"two\n" );
1739 std::map<wxString, FileStatus> status = handler.
GetFileStatus( wxT(
"a[1]/" ),
true );
1741 bool bracketed =
false;
1744 for(
const auto& [
path, fileStatus] : status )
1746 bracketed |=
path.EndsWith( wxT(
"a[1]/x.txt" ) );
1747 plain |=
path.EndsWith( wxT(
"a1/y.txt" ) );
1750 BOOST_CHECK_MESSAGE( bracketed,
"the folder named in the pathspec was not listed" );
1751 BOOST_CHECK_MESSAGE( !plain,
"a folder the pathspec does not name was listed" );
1753 git_repository_free( repo );
1760 BOOST_TEST_REQUIRE( ready() );
1763 BOOST_TEST_REQUIRE( repo );
1768 git_reference* head =
nullptr;
1769 BOOST_REQUIRE_EQUAL( git_repository_head( &head, repo ), GIT_OK );
1771 wxString branch = wxString::FromUTF8( git_reference_shorthand( head ) );
1772 std::string trackingRef = ( wxT(
"refs/remotes/origin/" ) + branch ).utf8_string();
1773 std::string upstreamName = ( wxT(
"origin/" ) + branch ).utf8_string();
1774 git_oid base = *git_reference_target( head );
1776 git_reference* tracking =
nullptr;
1777 BOOST_REQUIRE_EQUAL( git_reference_create( &tracking, repo, trackingRef.c_str(), &base, 0,
nullptr ), GIT_OK );
1778 git_reference_free( tracking );
1779 BOOST_REQUIRE_EQUAL( git_branch_set_upstream( head, upstreamName.c_str() ), GIT_OK );
1785 git_oid remoteTip = createDetachedCommit( repo, base, wxT(
"file.txt" ),
"remote\n",
"Remote change" );
1786 BOOST_REQUIRE_EQUAL( git_reference_create( &tracking, repo, trackingRef.c_str(), &remoteTip, 1,
nullptr ), GIT_OK );
1787 git_reference_free( tracking );
1791 git_reference_free( head );
1792 git_repository_free( repo );
1800 BOOST_TEST_REQUIRE( ready() );
1803 BOOST_TEST_REQUIRE( repo );
1806 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &baseOid, repo,
"HEAD" ), GIT_OK );
1808 createCommit( repo, wxT(
"file.txt" ), wxT(
"mine\n" ), wxT(
"Local change" ) );
1810 git_oid remoteCommitOid = createDetachedCommit( repo, baseOid, wxT(
"file.txt" ),
"remote\n",
"Remote change" );
1812 git_annotated_commit* theirs =
nullptr;
1813 BOOST_REQUIRE_EQUAL( git_annotated_commit_lookup( &theirs, repo, &remoteCommitOid ), GIT_OK );
1815 const git_annotated_commit* heads[1] = { theirs };
1816 BOOST_REQUIRE_EQUAL( git_merge( repo, heads, 1,
nullptr,
nullptr ), GIT_OK );
1817 git_annotated_commit_free( theirs );
1819 git_index*
index =
nullptr;
1820 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
1822 git_index_free(
index );
1825 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headBefore, repo,
"HEAD" ), GIT_OK );
1827 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
1829 auto readAll = [](
const wxString& aPath )
1831 std::ifstream in( aPath.ToStdString(), std::ios::binary );
1832 return std::string( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
1835 std::string conflictedBytes = readAll( workDir + wxT(
"file.txt" ) );
1848 std::ofstream f( ( workDir + wxT(
"other.txt" ) ).
ToStdString(), std::ios::binary );
1856 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headAfter, repo,
"HEAD" ), GIT_OK );
1859 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
1860 BOOST_CHECK( git_index_has_conflicts(
index ) );
1861 git_index_free(
index );
1868 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
1869 BOOST_REQUIRE_EQUAL( git_index_add_bypath(
index,
"file.txt" ), GIT_OK );
1870 BOOST_REQUIRE_EQUAL( git_index_write(
index ), GIT_OK );
1871 git_index_free(
index );
1877 git_repository_free( repo );
1884 BOOST_TEST_REQUIRE( ready() );
1887 BOOST_TEST_REQUIRE( repo );
1889 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
1892 std::ofstream f( ( workDir + wxT(
"staged.txt" ) ).
ToStdString(), std::ios::binary );
1893 f <<
"staged outside\n";
1896 git_index*
index =
nullptr;
1897 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
1898 BOOST_REQUIRE_EQUAL( git_index_add_bypath(
index,
"staged.txt" ), GIT_OK );
1899 BOOST_REQUIRE_EQUAL( git_index_write(
index ), GIT_OK );
1900 git_index_free(
index );
1902 BOOST_TEST_REQUIRE( wxRemoveFile( workDir + wxT(
"staged.txt" ) ) );
1907 BOOST_REQUIRE_MESSAGE( common.
DiscardChanges( { wxT(
"staged.txt" ) }, error ), error );
1910 git_repository_free( repo );
1918 BOOST_TEST_REQUIRE( ready() );
1921 BOOST_TEST_REQUIRE( repo );
1937 tokenA->store(
true );
1940 std::unique_lock<std::mutex> heldByA = common.
LockAction();
1945 BOOST_CHECK_MESSAGE( tokenB != tokenA,
"each operation must own its own token" );
1948 PushResult resultB = std::async( std::launch::async,
1957 BOOST_CHECK_MESSAGE( tokenA->load(),
"a Busy operation must not clear another's cancellation" );
1958 BOOST_CHECK_MESSAGE( !tokenB->load(),
"a Busy operation must not inherit another's cancellation" );
1960 "a Busy operation must leave the active read cancelled" );
1964 "later reads must not inherit the finished operation's cancellation" );
1969 BOOST_CHECK_MESSAGE( pushC.
CancelToken() != tokenA,
"a later operation must not inherit the retired token" );
1972 git_repository_free( repo );
2008 BOOST_TEST_REQUIRE( ready() );
2010 wxString dest = tempBase() + wxFileName::GetPathSeparator() + wxT(
"clone_cancelled" );
2015 handler.
SetRemote( wxS(
"file://" ) + remotePath() );
2018 auto reporter = std::make_unique<CLONE_CANCEL_REPORTER>();
2025 BOOST_REQUIRE_MESSAGE( watcher->
Ticks() > 0,
"the clone must report progress at least once" );
2027 BOOST_CHECK_MESSAGE( !cloned,
"a clone cancelled from the progress dialog must not report success" );
2028 BOOST_CHECK_MESSAGE( !wxDirExists( dest ),
"a cancelled clone must not leave a directory that blocks a retry" );
2032 git_repository_free( common.
GetRepo() );
2041 BOOST_TEST_REQUIRE( ready() );
2044 BOOST_TEST_REQUIRE( repo );
2046 createCommit( repo, wxT(
"file.txt" ), wxT(
"second\n" ), wxT(
"Second commit" ) );
2053 token->store(
true );
2069 git_repository_free( repo );
2076 BOOST_TEST_REQUIRE( ready() );
2079 BOOST_TEST_REQUIRE( repo );
2081 createCommit( repo, wxT(
"file.txt" ), wxT(
"second\n" ), wxT(
"Second commit" ) );
2097 git_repository_free( repo );
2103 BOOST_TEST_REQUIRE( ready() );
2106 BOOST_TEST_REQUIRE( repo );
2108 git_reference* headRefBefore =
nullptr;
2109 git_repository_head( &headRefBefore, repo );
2110 git_oid oidBefore = *git_reference_target( headRefBefore );
2112 git_commit* commitBefore =
nullptr;
2113 git_reference_peel( (git_object**) &commitBefore, headRefBefore, GIT_OBJECT_COMMIT );
2114 git_oid treeBefore = *git_commit_tree_id( commitBefore );
2116 git_commit_free( commitBefore );
2117 git_reference_free( headRefBefore );
2123 git_reference* headRefAfter =
nullptr;
2124 git_repository_head( &headRefAfter, repo );
2125 git_oid oidAfter = *git_reference_target( headRefAfter );
2127 git_commit* commitAfter =
nullptr;
2128 git_reference_peel( (git_object**) &commitAfter, headRefAfter, GIT_OBJECT_COMMIT );
2129 git_oid treeAfter = *git_commit_tree_id( commitAfter );
2131 BOOST_CHECK( !git_oid_equal( &oidBefore, &oidAfter ) );
2132 BOOST_CHECK( git_oid_equal( &treeBefore, &treeAfter ) );
2134 wxString amendedMsg = wxString::FromUTF8( git_commit_message( commitAfter ) );
2137 git_commit_free( commitAfter );
2138 git_reference_free( headRefAfter );
2139 git_repository_free( repo );
2145 BOOST_TEST_REQUIRE( ready() );
2148 BOOST_TEST_REQUIRE( repo );
2150 git_reference* headRefBefore =
nullptr;
2151 git_repository_head( &headRefBefore, repo );
2153 git_commit* commitBefore =
nullptr;
2154 git_reference_peel( (git_object**) &commitBefore, headRefBefore, GIT_OBJECT_COMMIT );
2155 git_oid treeBefore = *git_commit_tree_id( commitBefore );
2156 unsigned int parentCountBefore = git_commit_parentcount( commitBefore );
2158 git_commit_free( commitBefore );
2159 git_reference_free( headRefBefore );
2162 wxString newFile = repoPath() + wxFileName::GetPathSeparator() + wxT(
"new.txt" );
2164 std::ofstream f( newFile.ToStdString() );
2165 f <<
"new content\n";
2173 git_reference* headRefAfter =
nullptr;
2174 git_repository_head( &headRefAfter, repo );
2176 git_commit* commitAfter =
nullptr;
2177 git_reference_peel( (git_object**) &commitAfter, headRefAfter, GIT_OBJECT_COMMIT );
2178 git_oid treeAfter = *git_commit_tree_id( commitAfter );
2179 unsigned int parentCountAfter = git_commit_parentcount( commitAfter );
2181 BOOST_CHECK( !git_oid_equal( &treeBefore, &treeAfter ) );
2185 git_tree* treeObj =
nullptr;
2186 git_commit_tree( &treeObj, commitAfter );
2188 const git_tree_entry* entry = git_tree_entry_byname( treeObj,
"new.txt" );
2189 BOOST_CHECK( entry !=
nullptr );
2191 git_tree_free( treeObj );
2192 git_commit_free( commitAfter );
2193 git_reference_free( headRefAfter );
2194 git_repository_free( repo );
2201 BOOST_TEST_REQUIRE( ready() );
2203 wxString unbornDir = repoPath() + wxT(
"_unborn" );
2204 wxFileName::Mkdir( unbornDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
2207 BOOST_TEST_REQUIRE( git_repository_init( &unbornRepo, unbornDir.ToUTF8().data(), 0 ) == 0 );
2215 git_repository_free( unbornRepo );
2217 if( wxFileName::DirExists( unbornDir ) )
2218 wxFileName::Rmdir( unbornDir, wxPATH_RMDIR_RECURSIVE );
2224 BOOST_TEST_REQUIRE( ready() );
2233 config.username = wxS(
"saved-user" );
2234 config.password = wxS(
"key-passphrase" );
2248 BOOST_TEST_REQUIRE( ready() );
2257 config.url = wxS(
"runner@host:path/repo" );
2258 config.username = wxS(
"saved-user" );
2271 BOOST_TEST_REQUIRE( ready() );
2280 config.username = wxS(
"saved-user" );
2292 config.password = wxS(
"typed@pass:word" );
2302 BOOST_TEST_REQUIRE( ready() );
2310 config.url = wxS(
"http://example.com/repo.git" );
2311 config.username = wxS(
"alice" );
2312 config.password = wxS(
"typed@pass:word" );
2325 BOOST_TEST_REQUIRE( ready() );
2327 wxString freshDir = repoPath() + wxT(
"_fresh" );
2328 wxFileName::Mkdir( freshDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
2336 wxFileName gitignoreFile( freshDir, wxT(
".gitignore" ) );
2337 BOOST_TEST_REQUIRE( gitignoreFile.FileExists() );
2342 useEmptyGlobalIgnore( repo.get() );
2344 const std::pair<const char*, bool> cases[] = {
2345 {
"board.kicad_pcb",
false },
2346 {
"lib.kicad_sym",
false },
2347 {
"board.kicad_pcb-bak",
true },
2348 {
"board.lck",
true },
2349 {
"board.LCK",
true },
2350 {
"~board.kicad_pcb",
true },
2351 {
"proj.kicad_prl",
true },
2352 {
"upper.KiCaD_PrL",
true },
2353 {
"proj-backups/board.zip",
true },
2354 {
"data-proj-backups/board.kicad_pcb",
true },
2355 {
"nested/proj-backups/old/board.zip",
true },
2356 {
"proj-backups-old/board.kicad_pcb",
false },
2357 {
".history/board.kicad_pcb",
true },
2358 {
".history/.git/objects/ab/cdef",
true },
2359 {
".history-notes/board.kicad_pcb",
false },
2360 {
"named/.history",
false },
2361 {
"named/proj-backups",
false },
2362 {
"_autosave-board.kicad_pcb",
true },
2363 {
"_autosave-design/board.kicad_pcb",
true },
2364 {
"fp-info-cache",
true },
2365 {
"fp-info-cache.kicad_pcb",
false },
2366 {
"cache.kicad_prl/board.kicad_pcb",
true },
2367 {
"board.kicad_pcb.mine",
true },
2368 {
"board.kicad_pcb.theirs",
true },
2369 {
"board.kicad_pcb.mine.1",
true },
2370 {
"board.kicad_pcb.theirs.17",
true },
2371 {
"board.kicad_pcb.mine.99",
true },
2372 {
"board.kicad_pcb.mine.100",
false },
2373 {
"board.kicad_pcb.mine.1notes",
false },
2374 {
"board.kicad_pcb.theirs.notes",
false },
2375 {
"board.mine.kicad_pcb",
false },
2376 {
"board.kicad_pcb.1",
false },
2379 for(
const auto& [
path, transient] : cases )
2385 BOOST_REQUIRE_EQUAL( git_ignore_path_is_ignored( &ignored, repo.get(),
path ), GIT_OK );
2393 if( wxFileName::DirExists( freshDir ) )
2394 wxFileName::Rmdir( freshDir, wxPATH_RMDIR_RECURSIVE );
2401 BOOST_TEST_REQUIRE( ready() );
2403 wxString freshDir = repoPath() + wxT(
"_existing" );
2404 wxFileName::Mkdir( freshDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
2406 const wxString curated = wxT(
"# my custom ignores\n.history/\nbuild/\n" );
2407 wxFileName gitignoreFile( freshDir, wxT(
".gitignore" ) );
2410 wxFFile f( gitignoreFile.GetFullPath(), wxT(
"wb" ) );
2422 wxFFile f( gitignoreFile.GetFullPath(), wxT(
"rb" ) );
2423 f.ReadAll( &after );
2428 if( wxFileName::DirExists( freshDir ) )
2429 wxFileName::Rmdir( freshDir, wxPATH_RMDIR_RECURSIVE );
2436 BOOST_TEST_REQUIRE( ready() );
2438 wxString freshDir = repoPath() + wxT(
"_emptyignore" );
2439 wxFileName::Mkdir( freshDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
2441 wxFileName gitignoreFile( freshDir, wxT(
".gitignore" ) );
2444 wxFFile f( gitignoreFile.GetFullPath(), wxT(
"wb" ) );
2453 BOOST_TEST_REQUIRE( gitignoreFile.FileExists() );
2456 if( wxFileName::DirExists( freshDir ) )
2457 wxFileName::Rmdir( freshDir, wxPATH_RMDIR_RECURSIVE );
2464 wxString
path = aDir + wxFileName::GetPathSeparator() + wxT(
"proj.kicad_pro" );
2466 if( !wxFileName::FileExists(
path ) )
2468 wxFFile f(
path, wxT(
"w" ) );
2469 f.Write( wxT(
"{\"meta\": {\"filename\": \"proj.kicad_pro\", \"version\": 3}}" ) );
2478 BOOST_TEST_REQUIRE( ready() );
2483 BOOST_TEST_REQUIRE( mgr.
LoadProject( projectPath ) );
2486 BOOST_TEST_REQUIRE(
project !=
nullptr );
2488 std::shared_ptr<KIGIT_COMMON> common =
project->GetGitCommon();
2489 BOOST_TEST_REQUIRE( common !=
nullptr );
2490 BOOST_CHECK( common->GetRepo() ==
nullptr );
2491 BOOST_CHECK( common ==
project->GetGitCommon() );
2493 BOOST_TEST_REQUIRE(
project->AttachGitRepository() );
2495 BOOST_TEST_REQUIRE( repo !=
nullptr );
2496 BOOST_CHECK( !common->GetProjectDir().IsEmpty() );
2498 BOOST_TEST_REQUIRE(
project->AttachGitRepository() );
2499 BOOST_CHECK( common->GetRepo() == repo );
2503 std::shared_ptr<KIGIT_COMMON> fresh =
project->GetGitCommon();
2504 BOOST_TEST_REQUIRE( fresh !=
nullptr );
2505 BOOST_CHECK( fresh != common );
2506 BOOST_CHECK( fresh->GetRepo() ==
nullptr );
2507 BOOST_CHECK( common->GetRepo() ==
nullptr );
2510 BOOST_TEST_REQUIRE(
project->AttachGitRepository() );
2517 BOOST_TEST_REQUIRE( ready() );
2522 BOOST_TEST_REQUIRE( mgr.
LoadProject( projectPath ) );
2525 BOOST_TEST_REQUIRE(
project !=
nullptr );
2526 BOOST_TEST_REQUIRE(
project->AttachGitRepository() );
2529 BOOST_TEST_REQUIRE( repo !=
nullptr );
2539 BOOST_CHECK(
project->GetGitCommon()->GetRepo() ==
nullptr );
2544 git_repository_free( repo );
2550 BOOST_TEST_REQUIRE( ready() );
2552 wxFileName tempBase( repoPath(), wxEmptyString );
2553 tempBase.RemoveLastDir();
2554 wxString outsideDir = tempBase.GetPath() + wxFileName::GetPathSeparator() + wxT(
"no_repo_here" );
2555 wxFileName::Mkdir( outsideDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
2560 BOOST_TEST_REQUIRE( mgr.
LoadProject( projectPath ) );
2563 BOOST_TEST_REQUIRE(
project !=
nullptr );
2565 BOOST_CHECK( !
project->AttachGitRepository() );
2566 BOOST_CHECK(
project->GetGitCommon()->GetRepo() ==
nullptr );
2572 BOOST_TEST_REQUIRE( ready() );
2577 BOOST_TEST_REQUIRE( mgr.
LoadProject( projectPath ) );
2580 BOOST_TEST_REQUIRE(
project !=
nullptr );
2582 project->GetLocalSettings().m_GitIntegrationDisabled =
true;
2584 BOOST_CHECK( !
project->AttachGitRepository() );
2585 BOOST_CHECK(
project->GetGitCommon()->GetRepo() ==
nullptr );
2587 project->GetLocalSettings().m_GitIntegrationDisabled =
false;
2589 BOOST_CHECK(
project->AttachGitRepository() );
2590 BOOST_CHECK(
project->GetGitCommon()->GetRepo() !=
nullptr );
2598 BOOST_TEST_REQUIRE( ready() );
2603 BOOST_TEST_REQUIRE( mgr.
LoadProject( projectPath ) );
2606 BOOST_TEST_REQUIRE(
project !=
nullptr );
2608 BOOST_TEST_REQUIRE(
project->AttachGitRepository() );
2609 std::shared_ptr<KIGIT_COMMON> common =
project->GetGitCommon();
2611 std::promise<void> lockHeld;
2612 std::promise<void> allowUnlock;
2617 std::unique_lock<std::mutex> lock = common->LockAction();
2618 lockHeld.set_value();
2619 allowUnlock.get_future().wait();
2622 lockHeld.get_future().wait();
2629 BOOST_CHECK( common->GetRepo() !=
nullptr );
2631 allowUnlock.set_value();
2634 BOOST_REQUIRE_EQUAL(
GetGitBackend()->OrphanRegistry().JoinAll( std::chrono::seconds( 5 ) ), 0u );
2635 BOOST_CHECK( common->GetRepo() ==
nullptr );
2645 BOOST_TEST_REQUIRE( ready() );
2648 BOOST_TEST_REQUIRE( repo );
2650 wxString sep = wxFileName::GetPathSeparator();
2651 BOOST_REQUIRE( wxFileName::Mkdir( repoPath() + sep + wxT(
"shared" ), 0700, wxPATH_MKDIR_FULL ) );
2654 std::ofstream f( ( repoPath() + sep + wxT(
"shared" ) + sep + wxT(
"lib.txt" ) ).
ToStdString() );
2658 BOOST_REQUIRE_EQUAL( symlink(
"shared", ( repoPath() + sep + wxT(
"link" ) ).ToUTF8().data() ), 0 );
2667 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headOid, repo,
"HEAD" ), GIT_OK );
2669 git_commit* head =
nullptr;
2670 BOOST_REQUIRE_EQUAL( git_commit_lookup( &head, repo, &headOid ), GIT_OK );
2672 git_tree* tree =
nullptr;
2673 BOOST_REQUIRE_EQUAL( git_commit_tree( &tree, head ), GIT_OK );
2675 git_tree_entry* entry =
nullptr;
2676 BOOST_CHECK_MESSAGE( git_tree_entry_bypath( &entry, tree,
"link" ) == GIT_OK,
2677 "the commit dropped the symlink instead of recording it" );
2680 git_tree_entry_free( entry );
2682 git_tree_free( tree );
2683 git_commit_free( head );
2684 git_repository_free( repo );
2693 BOOST_CHECK( PROJECT_GIT_UTILS::IsTransientGitFile( wxFileName(
"proj\\.history\\board.kicad_pcb", wxPATH_DOS ) ) );
2695 PROJECT_GIT_UTILS::IsTransientGitFile( wxFileName(
"proj\\data-proj-backups\\board.zip", wxPATH_DOS ) ) );
2696 BOOST_CHECK( PROJECT_GIT_UTILS::IsTransientGitFile( wxFileName(
"proj\\settings.KICAD_PRL", wxPATH_DOS ) ) );
2697 BOOST_CHECK( PROJECT_GIT_UTILS::IsTransientGitFile( wxFileName(
"proj\\board.kicad_pcb.theirs.99", wxPATH_DOS ) ) );
2698 BOOST_CHECK( !PROJECT_GIT_UTILS::IsTransientGitFile(
2699 wxFileName(
"proj\\.history-notes\\board.kicad_pcb", wxPATH_DOS ) ) );
2700 BOOST_CHECK( !PROJECT_GIT_UTILS::IsTransientGitFile( wxFileName(
"proj\\board.kicad_pcb.mine.100", wxPATH_DOS ) ) );
2709 useEmptyGlobalIgnore( repo.get() );
2712 const wxString prefix = wxS(
"workspace-backups/project/" );
2713 const wxString projectPath = repoPath() + wxS(
"/" ) + prefix;
2716 KI_TEST::WriteGitFile( repoPath(), prefix + wxS(
"deleted.kicad_pcb.mine" ),
"original recovery\n" );
2719 KI_TEST::WriteGitFile( repoPath(), prefix + wxS(
"tracked.kicad_prl" ),
"changed settings with more data\n" );
2720 BOOST_REQUIRE( wxRemoveFile( projectPath + wxS(
"deleted.kicad_pcb.mine" ) ) );
2723 git_index* rawIndex =
nullptr;
2724 BOOST_REQUIRE_EQUAL( git_repository_index( &rawIndex, repo.get() ), GIT_OK );
2726 BOOST_REQUIRE_EQUAL( git_index_add_bypath(
index.get(), ( prefix + wxS(
"staged.kicad_prl" ) ).utf8_str() ),
2728 BOOST_REQUIRE_EQUAL( git_index_write(
index.get() ), GIT_OK );
2730 const char* untracked[] = {
2731 "board.kicad_pcb",
"untracked.kicad_prl",
"board.kicad_pcb.theirs.17",
2732 "_autosave-board.kicad_pcb",
"project-backups/board.zip",
".history/revisions/board.kicad_pcb",
2735 for(
const char*
path : untracked )
2738 const std::set<wxString>
expected = {
2739 prefix + wxS(
"board.kicad_pcb" ),
2740 prefix + wxS(
"tracked.kicad_prl" ),
2741 prefix + wxS(
"deleted.kicad_pcb.mine" ),
2742 prefix + wxS(
"staged.kicad_prl" ),
2745 auto checkEntries = [&repo, &
expected](
const wxString& aProjectPath )
2748 std::unique_lock<std::mutex> lock = common.
LockAction();
2749 std::set<wxString> paths;
2752 paths.insert( entry.path );
2757 checkEntries( projectPath );
2760 const wxString alias = tempBase() + wxS(
"/project-alias" );
2761 BOOST_REQUIRE_EQUAL( symlink( projectPath.utf8_str(), alias.utf8_str() ), 0 );
2762 checkEntries( alias );
2771 BOOST_TEST_REQUIRE( ready() );
2774 BOOST_TEST_REQUIRE( repo );
2776 const wxString sep = wxFileName::GetPathSeparator();
2778 createCommit( repo, wxT(
"root.kicad_sch" ), wxT(
"root\n" ), wxT(
"add root" ) );
2780 BOOST_REQUIRE( wxFileName::Mkdir( repoPath() + sep + wxT(
"sub" ), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
2781 createCommit( repo, wxT(
"sub/child.kicad_sch" ), wxT(
"child\n" ), wxT(
"add child" ) );
2784 wxString dest = tempBase() + wxFileName::GetPathSeparator() + wxT(
"tree_out" );
2787 BOOST_CHECK( wxFileName::FileExists( dest + sep + wxT(
"root.kicad_sch" ) ) );
2788 BOOST_CHECK_MESSAGE( wxFileName::FileExists( dest + sep + wxT(
"sub" ) + sep + wxT(
"child.kicad_sch" ) ),
2789 "sub-sheet file in a subdirectory must be materialized alongside the root" );
2791 if( wxFileName::DirExists( dest ) )
2792 wxFileName::Rmdir( dest, wxPATH_RMDIR_RECURSIVE );
2794 git_repository_free( repo );
2802 BOOST_TEST_REQUIRE( ready() );
2805 BOOST_TEST_REQUIRE( repo );
2808 git_reference* headRef =
nullptr;
2809 BOOST_REQUIRE_EQUAL( git_repository_head( &headRef, repo ), GIT_OK );
2810 git_oid baseOid = *git_reference_target( headRef );
2811 git_reference_free( headRef );
2813 createCommit( repo, wxT(
"file.txt" ), wxT(
"mine\n" ), wxT(
"Local change" ) );
2816 git_oid remoteCommitOid = createDetachedCommit( repo, baseOid, wxT(
"file.txt" ),
"remote\n",
"Remote change" );
2819 writeFetchHead( remoteCommitOid );
2823 wxString stageDir = tempBase() + wxFileName::GetPathSeparator() + wxT(
"stages" );
2824 wxString headOid, theirsOid, conflictsError;
2826 std::vector<KIGIT_CONFLICT_SIDES> conflicts;
2828 BOOST_REQUIRE_MESSAGE( common.
GetMergeConflicts( stageDir, headOid, theirsOid, conflicts, conflictsError ),
2829 "GetMergeConflicts failed: " + conflictsError.ToStdString() );
2830 BOOST_REQUIRE_EQUAL( conflicts.size(), 1u );
2832 BOOST_CHECK( conflicts[0].hasAncestor && conflicts[0].hasOurs && conflicts[0].hasTheirs );
2836 char expect[GIT_OID_HEXSZ + 1] = { 0 };
2837 git_oid_tostr( expect,
sizeof( expect ), &remoteCommitOid );
2841 auto readAll = [](
const wxString& aPath )
2843 std::ifstream in( aPath.ToStdString(), std::ios::binary );
2844 return std::string( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
2856 static_cast<int>( common.
CreateMergedCommit( wxT(
"0000000000000000000000000000000000000000" ), theirsOid,
2857 { { conflicts[0].path, conflicts[0].oursFile } }, error ) ),
2862 writeFetchHead( baseOid );
2866 wxString trackedFile = repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" );
2868 std::ofstream f( trackedFile.ToStdString(), std::ios::binary );
2869 f <<
"edited during resolution\n";
2873 headOid, theirsOid, { { conflicts[0].path, conflicts[0].oursFile } }, error ) ),
2877 std::ofstream f( trackedFile.ToStdString(), std::ios::binary );
2884 wxString indexLock = repoPath() + wxFileName::GetPathSeparator() + wxT(
".git" ) + wxFileName::GetPathSeparator()
2885 + wxT(
"index.lock" );
2887 std::ofstream f( indexLock.ToStdString() );
2891 wxString lockedMerged = tempBase() + wxFileName::GetPathSeparator() + wxT(
"locked_merged.txt" );
2892 std::ofstream f( lockedMerged.ToStdString(), std::ios::binary );
2899 headOid, theirsOid, { { conflicts[0].path, lockedMerged } }, error ) ),
2903 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headNow, repo,
"HEAD" ), GIT_OK );
2905 char headNowStr[GIT_OID_HEXSZ + 1] = { 0 };
2906 git_oid_tostr( headNowStr,
sizeof( headNowStr ), &headNow );
2910 wxRemoveFile( indexLock );
2916 if( geteuid() != 0 )
2920 wxString refsDir = repoPath() + wxFileName::GetPathSeparator() + wxT(
".git" ) + wxFileName::GetPathSeparator()
2921 + wxT(
"refs" ) + wxFileName::GetPathSeparator() + wxT(
"heads" );
2926 struct WRITABLE_AGAIN
2929 ~WRITABLE_AGAIN() { chmod( dir.fn_str(), 0755 ); }
2930 } restore{ refsDir };
2932 chmod( refsDir.fn_str(), 0555 );
2934 wxString refMerged = tempBase() + wxFileName::GetPathSeparator() + wxT(
"ref_merged.txt" );
2936 std::ofstream f( refMerged.ToStdString() );
2940 BOOST_CHECK_EQUAL(
static_cast<int>( common.CreateMergedCommit( headOid, theirsOid,
2941 { { conflicts[0].path, refMerged } }, error ) ),
2946 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headNow, repo,
"HEAD" ), GIT_OK );
2948 char headNowStr[GIT_OID_HEXSZ + 1] = { 0 };
2949 git_oid_tostr( headNowStr,
sizeof( headNowStr ), &headNow );
2952 std::ifstream in( trackedFile.ToStdString(), std::ios::binary );
2953 std::string rolled( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
2961 std::ofstream f( trackedFile.ToStdString(), std::ios::binary );
2966 wxString mergedFile = tempBase() + wxFileName::GetPathSeparator() + wxT(
"merged.txt" );
2968 std::ofstream f( mergedFile.ToStdString(), std::ios::binary );
2972 BOOST_REQUIRE_MESSAGE( common.CreateMergedCommit( headOid, theirsOid, { { conflicts[0].path, mergedFile } }, error )
2974 "CreateMergedCommit failed: " + error.summary.ToStdString() );
2979 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &newHead, repo,
"HEAD" ), GIT_OK );
2981 git_commit* mergeCommit =
nullptr;
2982 BOOST_REQUIRE_EQUAL( git_commit_lookup( &mergeCommit, repo, &newHead ), GIT_OK );
2986 const git_oid* secondParent = git_commit_parent_id( mergeCommit, 1 );
2988 BOOST_CHECK( git_oid_equal( secondParent, &remoteCommitOid ) );
2989 git_commit_free( mergeCommit );
2991 BOOST_CHECK_EQUAL( readAll( repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" ) ),
"merged\n" );
2994 git_repository_free( repo );
3002 BOOST_TEST_REQUIRE( ready() );
3005 BOOST_TEST_REQUIRE( repo );
3007 git_reference* headRef =
nullptr;
3008 BOOST_REQUIRE_EQUAL( git_repository_head( &headRef, repo ), GIT_OK );
3009 git_oid baseOid = *git_reference_target( headRef );
3010 git_reference_free( headRef );
3012 createCommit( repo, wxT(
"file.txt" ), wxT(
"mine\n" ), wxT(
"Local change" ) );
3013 git_oid remoteCommitOid = createDetachedCommit( repo, baseOid, wxT(
"file.txt" ),
"remote\n",
"Remote change" );
3014 writeFetchHead( remoteCommitOid );
3018 wxString stageDir = tempBase() + wxFileName::GetPathSeparator() + wxT(
"stages" );
3019 wxString headOid, theirsOid, conflictsError;
3021 std::vector<KIGIT_CONFLICT_SIDES> conflicts;
3023 BOOST_REQUIRE_MESSAGE( common.
GetMergeConflicts( stageDir, headOid, theirsOid, conflicts, conflictsError ),
3024 "GetMergeConflicts failed: " + conflictsError.ToStdString() );
3025 BOOST_REQUIRE_EQUAL( conflicts.size(), 1u );
3029 wxString
copy = repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt.mine" );
3030 std::ofstream f(
copy.ToStdString() );
3034 BOOST_CHECK_MESSAGE(
3035 common.
CreateMergedCommit( headOid, theirsOid, { { conflicts[0].path, conflicts[0].theirsFile } }, error )
3037 "refused with an untracked file present: " + error.summary.ToStdString() );
3039 git_repository_free( repo );
3047 BOOST_TEST_REQUIRE( ready() );
3050 BOOST_TEST_REQUIRE( repo );
3054 for(
int i = 1; i <= 12; ++i )
3055 base << wxString::Format( wxT(
"line %d\n" ), i );
3057 git_oid baseOid = createCommit( repo, wxT(
"file.txt" ), base, wxT(
"Base" ) );
3059 wxString mine = base;
3060 mine.Replace( wxT(
"line 6\n" ), wxT(
"line 6 mine\n" ) );
3061 createCommit( repo, wxT(
"file.txt" ), mine, wxT(
"Local change" ) );
3064 wxString theirs = base;
3065 theirs.Replace( wxT(
"line 6\n" ), wxT(
"line 6 theirs\n" ) );
3067 git_oid theirsOidRaw;
3069 git_commit* baseCommit =
nullptr;
3070 BOOST_REQUIRE_EQUAL( git_commit_lookup( &baseCommit, repo, &baseOid ), GIT_OK );
3071 git_tree* baseTree =
nullptr;
3072 BOOST_REQUIRE_EQUAL( git_commit_tree( &baseTree, baseCommit ), GIT_OK );
3074 git_treebuilder* tb =
nullptr;
3075 BOOST_REQUIRE_EQUAL( git_treebuilder_new( &tb, repo, baseTree ), GIT_OK );
3076 BOOST_REQUIRE_EQUAL( git_treebuilder_remove( tb,
"file.txt" ), GIT_OK );
3078 std::string content = theirs.utf8_string();
3080 BOOST_REQUIRE_EQUAL( git_blob_create_from_buffer( &blob, repo, content.data(), content.size() ), GIT_OK );
3081 BOOST_REQUIRE_EQUAL( git_treebuilder_insert(
nullptr, tb,
"renamed.txt", &blob, GIT_FILEMODE_BLOB ), GIT_OK );
3084 BOOST_REQUIRE_EQUAL( git_treebuilder_write( &treeOid, tb ), GIT_OK );
3085 git_tree* tree =
nullptr;
3086 BOOST_REQUIRE_EQUAL( git_tree_lookup( &tree, repo, &treeOid ), GIT_OK );
3088 git_signature* sig =
nullptr;
3089 BOOST_REQUIRE_EQUAL( git_signature_now( &sig,
"QA",
"[email protected]" ), GIT_OK );
3090 const git_commit* parents[1] = { baseCommit };
3091 BOOST_REQUIRE_EQUAL( git_commit_create( &theirsOidRaw, repo,
nullptr, sig, sig,
nullptr,
"Rename and edit",
3094 git_signature_free( sig );
3095 git_tree_free( tree );
3096 git_treebuilder_free( tb );
3097 git_tree_free( baseTree );
3098 git_commit_free( baseCommit );
3101 writeFetchHead( theirsOidRaw );
3104 wxString stageDir = tempBase() + wxFileName::GetPathSeparator() + wxT(
"stages" );
3105 wxString headOid, theirsOid, conflictsError;
3107 std::vector<KIGIT_CONFLICT_SIDES> conflicts;
3109 BOOST_REQUIRE_MESSAGE( common.
GetMergeConflicts( stageDir, headOid, theirsOid, conflicts, conflictsError ),
3113 BOOST_REQUIRE_EQUAL( conflicts.size(), 2u );
3115 std::vector<std::pair<wxString, wxString>> resolved;
3118 resolved.emplace_back( c.path, c.hasTheirs ? c.theirsFile : wxString() );
3120 BOOST_REQUIRE_MESSAGE( common.
CreateMergedCommit( headOid, theirsOid, resolved, error )
3124 wxString workdir = wxString::FromUTF8( git_repository_workdir( repo ) );
3125 bool renamedExists = wxFileExists( workdir + wxT(
"renamed.txt" ) );
3126 bool oldExists = wxFileExists( workdir + wxT(
"file.txt" ) );
3128 BOOST_CHECK_MESSAGE( renamedExists,
"theirs' content must land on the renamed path" );
3129 BOOST_CHECK_MESSAGE( !oldExists,
"the old path must not survive a take-theirs of a rename" );
3131 git_repository_free( repo );
3138 BOOST_TEST_REQUIRE( ready() );
3141 BOOST_TEST_REQUIRE( repo );
3144 createCommit( repo, wxT(
"second.txt" ), wxT(
"two\n" ), wxT(
"Add second" ) );
3147 wxString filePath = repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" );
3148 std::ofstream f( filePath.ToStdString() );
3157 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headOid, repo,
"HEAD" ), GIT_OK );
3159 git_commit* head =
nullptr;
3160 BOOST_REQUIRE_EQUAL( git_commit_lookup( &head, repo, &headOid ), GIT_OK );
3162 git_tree* tree =
nullptr;
3163 BOOST_REQUIRE_EQUAL( git_commit_tree( &tree, head ), GIT_OK );
3165 BOOST_CHECK_MESSAGE( git_tree_entry_byname( tree,
"second.txt" ) !=
nullptr,
3166 "a file already in the amended commit must survive an amend that does not list it" );
3167 BOOST_CHECK( git_tree_entry_byname( tree,
"file.txt" ) !=
nullptr );
3169 git_tree_free( tree );
3170 git_commit_free( head );
3171 git_repository_free( repo );
3181 BOOST_TEST_REQUIRE( ready() );
3184 BOOST_TEST_REQUIRE( repo );
3186 git_oid localTip = createCommit( repo, wxT(
"file.txt" ), wxT(
"local\n" ), wxT(
"local" ) );
3188 git_commit* tipCommit =
nullptr;
3189 BOOST_REQUIRE_EQUAL( git_commit_lookup( &tipCommit, repo, &localTip ), GIT_OK );
3192 git_reference* localBranch =
nullptr;
3193 BOOST_REQUIRE_EQUAL( git_branch_create( &localBranch, repo,
"feature", tipCommit, 0 ), GIT_OK );
3194 git_reference_free( localBranch );
3197 git_oid remoteTip = createDetachedCommit( repo, localTip, wxT(
"file.txt" ),
"remote\n",
"remote" );
3198 git_reference* tracking =
nullptr;
3199 BOOST_REQUIRE_EQUAL( git_reference_create( &tracking, repo,
"refs/remotes/origin/feature", &remoteTip, 1,
nullptr ),
3201 git_reference_free( tracking );
3207 BOOST_CHECK_MESSAGE( handler.
GetErrorString().Contains( wxS(
"remote" ) ),
3208 "the refusal should say the branch is on the remote, got: "
3213 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headNow, repo,
"HEAD" ), GIT_OK );
3214 BOOST_CHECK( git_oid_equal( &headNow, &localTip ) );
3216 std::ifstream f( ( repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" ) ).
ToStdString() );
3217 std::string content( ( std::istreambuf_iterator<char>( f ) ), std::istreambuf_iterator<char>() );
3220 git_repository_free( repo );
3226 BOOST_TEST_REQUIRE( ready() );
3229 BOOST_TEST_REQUIRE( repo );
3232 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headOid, repo,
"HEAD" ), GIT_OK );
3234 git_commit* headCommit =
nullptr;
3235 BOOST_REQUIRE_EQUAL( git_commit_lookup( &headCommit, repo, &headOid ), GIT_OK );
3237 git_reference* oldBranch =
nullptr;
3238 BOOST_REQUIRE_EQUAL( git_branch_create( &oldBranch, repo,
"old", headCommit, 0 ), GIT_OK );
3239 git_reference_free( oldBranch );
3240 git_commit_free( headCommit );
3242 git_oid advancedOid = createCommit( repo, wxT(
"file.txt" ), wxT(
"master2\n" ), wxT(
"Advance master" ) );
3244 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
3247 std::ofstream f( ( workDir + wxT(
"file.txt" ) ).
ToStdString(), std::ios::binary );
3248 f <<
"local edit\n";
3257 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &afterOid, repo,
"HEAD" ), GIT_OK );
3260 std::ifstream in( ( workDir + wxT(
"file.txt" ) ).
ToStdString(), std::ios::binary );
3261 std::string content( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
3264 git_repository_free( repo );
3271 BOOST_TEST_REQUIRE( ready() );
3274 BOOST_TEST_REQUIRE( repo );
3277 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headOid, repo,
"HEAD" ), GIT_OK );
3279 git_commit* headCommit =
nullptr;
3280 BOOST_REQUIRE_EQUAL( git_commit_lookup( &headCommit, repo, &headOid ), GIT_OK );
3282 git_reference* plainBranch =
nullptr;
3283 BOOST_REQUIRE_EQUAL( git_branch_create( &plainBranch, repo,
"plain", headCommit, 0 ), GIT_OK );
3284 git_reference_free( plainBranch );
3285 git_commit_free( headCommit );
3287 createCommit( repo, wxT(
"extra.txt" ), wxT(
"committed\n" ), wxT(
"Add extra" ) );
3294 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
3297 std::ofstream f( ( workDir + wxT(
"extra.txt" ) ).
ToStdString(), std::ios::binary );
3303 std::ifstream in( ( workDir + wxT(
"extra.txt" ) ).
ToStdString(), std::ios::binary );
3304 std::string content( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
3307 git_repository_free( repo );
3315 BOOST_TEST_REQUIRE( ready() );
3318 BOOST_TEST_REQUIRE( repo );
3320 createCommit( repo, wxT(
"doomed.txt" ), wxT(
"doomed\n" ), wxT(
"Add doomed" ) );
3322 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
3323 BOOST_TEST_REQUIRE( wxRemoveFile( workDir + wxT(
"doomed.txt" ) ) );
3332 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headOid, repo,
"HEAD" ), GIT_OK );
3334 git_commit* head =
nullptr;
3335 BOOST_REQUIRE_EQUAL( git_commit_lookup( &head, repo, &headOid ), GIT_OK );
3337 git_tree* tree =
nullptr;
3338 BOOST_REQUIRE_EQUAL( git_commit_tree( &tree, head ), GIT_OK );
3340 BOOST_CHECK( git_tree_entry_byname( tree,
"doomed.txt" ) ==
nullptr );
3341 BOOST_CHECK( git_tree_entry_byname( tree,
"file.txt" ) !=
nullptr );
3343 git_tree_free( tree );
3344 git_commit_free( head );
3345 git_repository_free( repo );
3353 BOOST_TEST_REQUIRE( ready() );
3356 BOOST_TEST_REQUIRE( repo );
3358 createCommit( repo, wxT(
"board.txt" ), wxT(
"board v1\n" ), wxT(
"Add board" ) );
3359 createCommit( repo, wxT(
"doomed.txt" ), wxT(
"doomed\n" ), wxT(
"Add doomed" ) );
3361 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
3363 auto writeAll = [&](
const wxString& aName,
const std::string& aContent )
3365 std::ofstream f( ( workDir + aName ).
ToStdString(), std::ios::binary );
3369 auto readAll = [&](
const wxString& aName )
3371 std::ifstream in( ( workDir + aName ).
ToStdString(), std::ios::binary );
3372 return std::string( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
3376 auto stagedContent = [&](
const char* aPath )
3378 std::string
result =
"<none>";
3379 git_index*
index =
nullptr;
3380 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
3381 BOOST_REQUIRE_EQUAL( git_index_read(
index, 1 ), GIT_OK );
3383 if(
const git_index_entry* entry = git_index_get_bypath(
index, aPath, 0 ) )
3385 git_blob* blob =
nullptr;
3386 BOOST_REQUIRE_EQUAL( git_blob_lookup( &blob, repo, &entry->id ), GIT_OK );
3387 result.assign(
static_cast<const char*
>( git_blob_rawcontent( blob ) ), git_blob_rawsize( blob ) );
3388 git_blob_free( blob );
3391 git_index_free(
index );
3395 auto committedContent = [&](
const char* aPath )
3397 std::string
result =
"<none>";
3399 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headOid, repo,
"HEAD" ), GIT_OK );
3401 git_commit* head =
nullptr;
3402 BOOST_REQUIRE_EQUAL( git_commit_lookup( &head, repo, &headOid ), GIT_OK );
3404 git_tree* tree =
nullptr;
3405 BOOST_REQUIRE_EQUAL( git_commit_tree( &tree, head ), GIT_OK );
3407 git_tree_entry* entry =
nullptr;
3409 if( git_tree_entry_bypath( &entry, tree, aPath ) == GIT_OK )
3411 git_blob* blob =
nullptr;
3412 BOOST_REQUIRE_EQUAL( git_blob_lookup( &blob, repo, git_tree_entry_id( entry ) ), GIT_OK );
3413 result.assign(
static_cast<const char*
>( git_blob_rawcontent( blob ) ), git_blob_rawsize( blob ) );
3414 git_blob_free( blob );
3415 git_tree_entry_free( entry );
3418 git_tree_free( tree );
3419 git_commit_free( head );
3425 writeAll( wxT(
"notes.txt" ),
"staged A\n" );
3426 writeAll( wxT(
"board.txt" ),
"board staged\n" );
3428 git_index*
index =
nullptr;
3429 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
3430 BOOST_REQUIRE_EQUAL( git_index_add_bypath(
index,
"notes.txt" ), GIT_OK );
3431 BOOST_REQUIRE_EQUAL( git_index_add_bypath(
index,
"board.txt" ), GIT_OK );
3432 BOOST_REQUIRE_EQUAL( git_index_remove_bypath(
index,
"doomed.txt" ), GIT_OK );
3433 BOOST_REQUIRE_EQUAL( git_index_write(
index ), GIT_OK );
3434 git_index_free(
index );
3438 writeAll( wxT(
"notes.txt" ),
"working B\n" );
3439 writeAll( wxT(
"board.txt" ),
"board working\n" );
3442 writeAll( wxT(
"file.txt" ),
"selected\n" );
3466 BOOST_TEST_REQUIRE( wxRemoveFile( workDir + wxT(
"file.txt" ) ) );
3488 writeAll( wxT(
"late.txt" ),
"late\n" );
3489 BOOST_CHECK( handler.
PerformCommit( { wxT(
"late.txt" ) }, wxT(
"Bad author" ), wxEmptyString, wxEmptyString )
3496 git_repository_free( repo );
3504 BOOST_TEST_REQUIRE( ready() );
3507 BOOST_TEST_REQUIRE( repo );
3509 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
3511 const wxString umlaut = wxString::FromUTF8(
"Gr\xc3\xb6\xc3\x9f"
3513 const wxString cyrillic = wxString::FromUTF8(
"\xd0\xbf\xd0\xbb\xd0\xb0\xd1\x82\xd0\xb0.txt" );
3514 const std::string umlautUtf8 = umlaut.utf8_string();
3515 const std::string cyrillicUtf8 = cyrillic.utf8_string();
3517 auto writeAll = [&](
const wxString& aName,
const std::string& aContent )
3522 auto inHeadTree = [&](
const std::string& aPath )
3525 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headOid, repo,
"HEAD" ), GIT_OK );
3527 git_commit* head =
nullptr;
3528 BOOST_REQUIRE_EQUAL( git_commit_lookup( &head, repo, &headOid ), GIT_OK );
3530 git_tree* tree =
nullptr;
3531 BOOST_REQUIRE_EQUAL( git_commit_tree( &tree, head ), GIT_OK );
3533 git_tree_entry* entry =
nullptr;
3534 bool found = git_tree_entry_bypath( &entry, tree, aPath.c_str() ) == GIT_OK;
3537 git_tree_entry_free( entry );
3539 git_tree_free( tree );
3540 git_commit_free( head );
3544 writeAll( umlaut,
"one\n" );
3545 writeAll( cyrillic,
"one\n" );
3553 BOOST_CHECK( inHeadTree( umlautUtf8 ) );
3554 BOOST_CHECK( inHeadTree( cyrillicUtf8 ) );
3556 writeAll( umlaut,
"two\n" );
3559 BOOST_CHECK( inHeadTree( umlautUtf8 ) );
3561 writeAll( umlaut,
"three\n" );
3564 BOOST_CHECK( inHeadTree( umlautUtf8 ) );
3565 BOOST_CHECK( inHeadTree( cyrillicUtf8 ) );
3567 BOOST_TEST_REQUIRE( wxRemoveFile( workDir + umlaut ) );
3571 BOOST_CHECK( !inHeadTree( umlautUtf8 ) );
3572 BOOST_CHECK( inHeadTree( cyrillicUtf8 ) );
3574 git_repository_free( repo );
3582 BOOST_TEST_REQUIRE( ready() );
3585 BOOST_TEST_REQUIRE( repo );
3587 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
3590 std::ofstream f( ( workDir + wxT(
"notes.txt" ) ).
ToStdString(), std::ios::binary );
3591 f <<
"staged elsewhere\n";
3595 BOOST_REQUIRE_EQUAL( git_repository_open( &foreignRepo, repoPath().ToUTF8().data() ), GIT_OK );
3597 git_index* foreign =
nullptr;
3598 BOOST_REQUIRE_EQUAL( git_repository_index( &foreign, foreignRepo ), GIT_OK );
3599 BOOST_REQUIRE_EQUAL( git_index_add_bypath( foreign,
"notes.txt" ), GIT_OK );
3600 BOOST_REQUIRE_EQUAL( git_index_write( foreign ), GIT_OK );
3601 git_index_free( foreign );
3602 git_repository_free( foreignRepo );
3605 std::ofstream f( ( workDir + wxT(
"file.txt" ) ).
ToStdString(), std::ios::binary );
3610 wxString indexLock = repoPath() + wxFileName::GetPathSeparator() + wxT(
".git" ) + wxFileName::GetPathSeparator()
3611 + wxT(
"index.lock" );
3613 std::ofstream f( indexLock.ToStdString() );
3626 BOOST_TEST_REQUIRE( wxRemoveFile( indexLock ) );
3629 BOOST_CHECK_MESSAGE( git_reference_name_to_id( &committed, repo,
"HEAD" ) == GIT_OK,
3630 "the commit must exist even though the index could not be rewritten" );
3633 git_index* after =
nullptr;
3634 BOOST_REQUIRE_EQUAL( git_repository_index( &after, repo ), GIT_OK );
3635 BOOST_REQUIRE_EQUAL( git_index_read( after, 1 ), GIT_OK );
3637 const git_index_entry* notes = git_index_get_bypath( after,
"notes.txt", 0 );
3638 BOOST_REQUIRE_MESSAGE( notes,
"the locked-out write damaged another process's staging" );
3640 git_blob* blob =
nullptr;
3641 BOOST_REQUIRE_EQUAL( git_blob_lookup( &blob, repo, ¬es->id ), GIT_OK );
3642 std::string content(
static_cast<const char*
>( git_blob_rawcontent( blob ) ), git_blob_rawsize( blob ) );
3645 git_blob_free( blob );
3646 git_index_free( after );
3647 git_repository_free( repo );
3654 BOOST_TEST_REQUIRE( ready() );
3657 BOOST_TEST_REQUIRE( repo );
3659 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
3661 git_index* cached =
nullptr;
3662 BOOST_REQUIRE_EQUAL( git_repository_index( &cached, repo ), GIT_OK );
3666 std::ofstream f( ( workDir + wxT(
"notes.txt" ) ).
ToStdString(), std::ios::binary );
3667 f <<
"staged elsewhere\n";
3672 BOOST_REQUIRE_EQUAL( git_repository_open( &foreignRepo, repoPath().ToUTF8().data() ), GIT_OK );
3674 git_index* foreign =
nullptr;
3675 BOOST_REQUIRE_EQUAL( git_repository_index( &foreign, foreignRepo ), GIT_OK );
3676 BOOST_REQUIRE_EQUAL( git_index_add_bypath( foreign,
"notes.txt" ), GIT_OK );
3677 BOOST_REQUIRE_EQUAL( git_index_write( foreign ), GIT_OK );
3679 const git_index_entry* stagedBefore = git_index_get_bypath( foreign,
"notes.txt", 0 );
3681 git_oid stagedBlob = stagedBefore->id;
3683 git_index_free( foreign );
3684 git_repository_free( foreignRepo );
3687 std::ofstream f( ( workDir + wxT(
"file.txt" ) ).
ToStdString(), std::ios::binary );
3696 git_repository_free( repo );
3700 BOOST_REQUIRE_EQUAL( git_repository_open( &fresh, repoPath().ToUTF8().data() ), GIT_OK );
3702 git_index* after =
nullptr;
3703 BOOST_REQUIRE_EQUAL( git_repository_index( &after, fresh ), GIT_OK );
3705 const git_index_entry* notes = git_index_get_bypath( after,
"notes.txt", 0 );
3706 BOOST_REQUIRE_MESSAGE( notes,
"the externally staged file must still be staged after an amend" );
3707 BOOST_CHECK_MESSAGE( git_oid_equal( ¬es->id, &stagedBlob ),
3708 "the externally staged blob must be the exact one the other process wrote" );
3712 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headOid, fresh,
"HEAD" ), GIT_OK );
3714 git_commit* headCommit =
nullptr;
3715 BOOST_REQUIRE_EQUAL( git_commit_lookup( &headCommit, fresh, &headOid ), GIT_OK );
3717 git_tree* headTree =
nullptr;
3718 BOOST_REQUIRE_EQUAL( git_commit_tree( &headTree, headCommit ), GIT_OK );
3720 git_tree_entry* leaked =
nullptr;
3721 BOOST_CHECK_MESSAGE( git_tree_entry_bypath( &leaked, headTree,
"notes.txt" ) != GIT_OK,
3722 "an unselected staged file must not ride into the amended commit" );
3723 git_tree_entry_free( leaked );
3725 git_tree_free( headTree );
3726 git_commit_free( headCommit );
3727 git_index_free( after );
3728 git_repository_free( fresh );
3734 BOOST_TEST_REQUIRE( ready() );
3737 BOOST_TEST_REQUIRE( repo );
3739 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
3742 git_index* cached =
nullptr;
3743 BOOST_REQUIRE_EQUAL( git_repository_index( &cached, repo ), GIT_OK );
3745 BOOST_REQUIRE_EQUAL( git_index_entrycount( cached ), 1 );
3748 std::ofstream f( ( workDir + wxT(
"notes.txt" ) ).
ToStdString(), std::ios::binary );
3749 f <<
"staged elsewhere\n";
3754 BOOST_REQUIRE_EQUAL( git_repository_open( &foreignRepo, repoPath().ToUTF8().data() ), GIT_OK );
3756 git_index* foreign =
nullptr;
3757 BOOST_REQUIRE_EQUAL( git_repository_index( &foreign, foreignRepo ), GIT_OK );
3758 BOOST_REQUIRE_EQUAL( git_index_add_bypath( foreign,
"notes.txt" ), GIT_OK );
3759 BOOST_REQUIRE_EQUAL( git_index_write( foreign ), GIT_OK );
3760 git_index_free( foreign );
3761 git_repository_free( foreignRepo );
3764 std::ofstream f( ( workDir + wxT(
"file.txt" ) ).
ToStdString(), std::ios::binary );
3773 git_index* after =
nullptr;
3774 BOOST_REQUIRE_EQUAL( git_repository_index( &after, repo ), GIT_OK );
3775 BOOST_REQUIRE_EQUAL( git_index_read( after, 1 ), GIT_OK );
3777 const git_index_entry* notes = git_index_get_bypath( after,
"notes.txt", 0 );
3778 BOOST_REQUIRE_MESSAGE( notes,
"staging written by another process was lost" );
3780 git_blob* blob =
nullptr;
3781 BOOST_REQUIRE_EQUAL( git_blob_lookup( &blob, repo, ¬es->id ), GIT_OK );
3782 std::string content(
static_cast<const char*
>( git_blob_rawcontent( blob ) ), git_blob_rawsize( blob ) );
3785 git_blob_free( blob );
3786 git_index_free( after );
3787 git_repository_free( repo );
3795 BOOST_TEST_REQUIRE( ready() );
3798 BOOST_TEST_REQUIRE( repo );
3800 createCommit( repo, wxT(
"sneaky.txt" ), wxT(
"clean\n" ), wxT(
"Add sneaky" ) );
3802 wxString workDir = wxString::FromUTF8( git_repository_workdir( repo ) );
3807 std::ofstream f( ( workDir + wxT(
"sneaky.txt" ) ).
ToStdString(), std::ios::binary );
3808 f <<
"staged elsewhere\n";
3811 git_index*
index =
nullptr;
3812 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
3813 BOOST_REQUIRE_EQUAL( git_index_add_bypath(
index,
"sneaky.txt" ), GIT_OK );
3814 BOOST_REQUIRE_EQUAL( git_index_write(
index ), GIT_OK );
3815 git_index_free(
index );
3818 std::ofstream f( ( workDir + wxT(
"file.txt" ) ).
ToStdString(), std::ios::binary );
3829 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &headOid, repo,
"HEAD" ), GIT_OK );
3831 git_commit* head =
nullptr;
3832 BOOST_REQUIRE_EQUAL( git_commit_lookup( &head, repo, &headOid ), GIT_OK );
3834 git_tree* tree =
nullptr;
3835 BOOST_REQUIRE_EQUAL( git_commit_tree( &tree, head ), GIT_OK );
3837 const git_tree_entry* sneaky = git_tree_entry_byname( tree,
"sneaky.txt" );
3840 git_blob* blob =
nullptr;
3841 BOOST_REQUIRE_EQUAL( git_blob_lookup( &blob, repo, git_tree_entry_id( sneaky ) ), GIT_OK );
3843 std::string sneakyContent(
static_cast<const char*
>( git_blob_rawcontent( blob ) ), git_blob_rawsize( blob ) );
3847 git_blob_free( blob );
3848 git_tree_free( tree );
3849 git_commit_free( head );
3850 git_repository_free( repo );
3857 BOOST_TEST_REQUIRE( ready() );
3860 BOOST_TEST_REQUIRE( repo );
3862 git_oid secondOid = createCommit( repo, wxT(
"file.txt" ), wxT(
"second\n" ), wxT(
"Second version" ) );
3864 git_object* firstObj =
nullptr;
3865 BOOST_REQUIRE_EQUAL( git_revparse_single( &firstObj, repo,
"HEAD~1" ), GIT_OK );
3866 git_oid firstOid = *git_object_id( firstObj );
3867 git_object_free( firstObj );
3869 char firstHash[GIT_OID_HEXSZ + 1] = { 0 };
3870 git_oid_tostr( firstHash,
sizeof( firstHash ), &firstOid );
3876 wxString trackedFile = repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" );
3878 std::ofstream f( trackedFile.ToStdString() );
3879 f <<
"uncommitted\n";
3885 std::ofstream f( trackedFile.ToStdString() );
3891 "RestoreVersion failed: " + error.
summary.ToStdString() );
3894 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &newHead, repo,
"HEAD" ), GIT_OK );
3896 git_commit* restoreCommit =
nullptr;
3897 BOOST_REQUIRE_EQUAL( git_commit_lookup( &restoreCommit, repo, &newHead ), GIT_OK );
3899 BOOST_CHECK( git_oid_equal( git_commit_parent_id( restoreCommit, 0 ), &secondOid ) );
3900 git_commit_free( restoreCommit );
3902 std::ifstream in( trackedFile.ToStdString() );
3903 std::string content( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
3909 git_repository_free( repo );
3916 BOOST_TEST_REQUIRE( ready() );
3922 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &rootOid, repo.get(),
"HEAD" ), GIT_OK );
3923 createCommit( repo.get(),
"projects/board/f.txt",
"a1\n",
"A v1" );
3924 git_oid bothV1 = createCommit( repo.get(),
"projects/board2/g.txt",
"b1\n",
"B v1" );
3926 createCommit( repo.get(),
"projects/board/f.txt",
"a2\n",
"A v2" );
3927 createCommit( repo.get(),
"projects/board2/g.txt",
"b2\n",
"B v2" );
3929 char v1Hash[GIT_OID_HEXSZ + 1] = { 0 };
3930 git_oid_tostr( v1Hash,
sizeof( v1Hash ), &bothV1 );
3932 const wxString projectPath = repoPath() + wxS(
"/projects/board" );
3937 "RestoreVersion failed: " + error.
summary.ToStdString() );
3939 auto readAll = [](
const wxString& aPath )
3941 std::ifstream in( aPath.ToStdString(), std::ios::binary );
3942 return std::string( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
3946 BOOST_CHECK_EQUAL( readAll( repoPath() + wxS(
"/projects/board/f.txt" ) ),
"a1\n" );
3947 BOOST_CHECK_EQUAL( readAll( repoPath() + wxS(
"/projects/board2/g.txt" ) ),
"b2\n" );
3951 char rootHash[GIT_OID_HEXSZ + 1] = { 0 };
3952 git_oid_tostr( rootHash,
sizeof( rootHash ), &rootOid );
3955 BOOST_CHECK_EQUAL( readAll( repoPath() + wxS(
"/projects/board/f.txt" ) ),
"a1\n" );
3959 const wxString projectLink = TempPath() + wxS(
"/project-link" );
3960 BOOST_REQUIRE_EQUAL( symlink( projectPath.utf8_str(), projectLink.utf8_str() ), 0 );
3961 createCommit( repo.get(),
"projects/board/f.txt",
"a3\n",
"A v3" );
3964 BOOST_REQUIRE_MESSAGE( linkedProject.
RestoreVersion( wxString::FromUTF8( v1Hash ), error )
3966 "RestoreVersion failed: " + error.
summary.ToStdString() );
3967 BOOST_CHECK_EQUAL( readAll( repoPath() + wxS(
"/projects/board/f.txt" ) ),
"a1\n" );
3968 BOOST_CHECK_EQUAL( readAll( repoPath() + wxS(
"/projects/board2/g.txt" ) ),
"b2\n" );
3975 BOOST_TEST_REQUIRE( ready() );
3981 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &firstOid, repo.get(),
"HEAD" ), GIT_OK );
3982 git_oid currentOid = createCommit( repo.get(),
"file.txt",
"second\n",
"Second version" );
3984 char firstHash[GIT_OID_HEXSZ + 1] = { 0 };
3985 git_oid_tostr( firstHash,
sizeof( firstHash ), &firstOid );
3987 const wxString outsidePath = repoPath() + wxS(
"2" );
3989 const std::vector<wxString> projectPaths = { wxEmptyString, TempPath(), outsidePath };
3991 for(
const wxString& projectPath : projectPaths )
3997 BOOST_CHECK( !error.
details.IsEmpty() );
4000 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &afterOid, repo.get(),
"HEAD" ), GIT_OK );
4001 BOOST_CHECK( git_oid_equal( ¤tOid, &afterOid ) );
4003 unsigned int status = 0;
4004 BOOST_REQUIRE_EQUAL( git_status_file( &status, repo.get(),
"file.txt" ), GIT_OK );
4008 wxFile file( repoPath() + wxS(
"/file.txt" ) );
4019 BOOST_TEST_REQUIRE( ready() );
4022 BOOST_TEST_REQUIRE( repo );
4025 git_oid withNotesOid = createCommit( repo, wxT(
"notes.txt" ), wxT(
"keep\n" ), wxT(
"Add notes" ) );
4028 git_index*
index =
nullptr;
4029 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
4030 BOOST_REQUIRE_EQUAL( git_index_remove_bypath(
index,
"notes.txt" ), GIT_OK );
4031 git_index_write(
index );
4034 BOOST_REQUIRE_EQUAL( git_index_write_tree( &treeOid,
index ), GIT_OK );
4035 git_index_free(
index );
4037 git_tree* tree =
nullptr;
4038 BOOST_REQUIRE_EQUAL( git_tree_lookup( &tree, repo, &treeOid ), GIT_OK );
4040 git_commit* parent =
nullptr;
4041 BOOST_REQUIRE_EQUAL( git_commit_lookup( &parent, repo, &withNotesOid ), GIT_OK );
4043 git_signature* sig =
nullptr;
4046 const git_commit* parents[1] = { parent };
4048 BOOST_REQUIRE_EQUAL(
4049 git_commit_create( &deleteOid, repo,
"HEAD", sig, sig,
nullptr,
"Delete notes", tree, 1, parents ),
4052 git_signature_free( sig );
4053 git_commit_free( parent );
4054 git_tree_free( tree );
4055 wxRemoveFile( repoPath() + wxFileName::GetPathSeparator() + wxT(
"notes.txt" ) );
4058 char withNotesHash[GIT_OID_HEXSZ + 1] = { 0 };
4059 git_oid_tostr( withNotesHash,
sizeof( withNotesHash ), &withNotesOid );
4065 wxString notesFile = repoPath() + wxFileName::GetPathSeparator() + wxT(
"notes.txt" );
4067 std::ofstream f( notesFile.ToStdString(), std::ios::binary );
4072 wxString unrelatedFile = repoPath() + wxFileName::GetPathSeparator() + wxT(
"unrelated.txt" );
4074 std::ofstream f( unrelatedFile.ToStdString(), std::ios::binary );
4078 auto readAll = [](
const wxString& aPath )
4080 std::ifstream in( aPath.ToStdString(), std::ios::binary );
4081 return std::string( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
4088 wxRemoveFile( notesFile );
4090 BOOST_REQUIRE_MESSAGE( common.
RestoreVersion( wxString::FromUTF8( withNotesHash ), error )
4092 "RestoreVersion failed: " + error.
summary.ToStdString() );
4096 git_repository_free( repo );
4104 BOOST_TEST_REQUIRE( ready() );
4107 BOOST_TEST_REQUIRE( repo );
4109 git_reference* headRef =
nullptr;
4110 BOOST_REQUIRE_EQUAL( git_repository_head( &headRef, repo ), GIT_OK );
4111 git_oid baseOid = *git_reference_target( headRef );
4112 git_reference_free( headRef );
4116 git_index*
index =
nullptr;
4117 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
4118 BOOST_REQUIRE_EQUAL( git_index_remove_bypath(
index,
"file.txt" ), GIT_OK );
4119 git_index_write(
index );
4122 BOOST_REQUIRE_EQUAL( git_index_write_tree( &treeOid,
index ), GIT_OK );
4123 git_index_free(
index );
4125 git_tree* tree =
nullptr;
4126 BOOST_REQUIRE_EQUAL( git_tree_lookup( &tree, repo, &treeOid ), GIT_OK );
4128 git_commit* baseCommit =
nullptr;
4129 BOOST_REQUIRE_EQUAL( git_commit_lookup( &baseCommit, repo, &baseOid ), GIT_OK );
4131 git_signature* sig =
nullptr;
4134 const git_commit* parents[1] = { baseCommit };
4136 BOOST_REQUIRE_EQUAL(
4137 git_commit_create( &deleteOid, repo,
"HEAD", sig, sig,
nullptr,
"Delete file", tree, 1, parents ),
4140 git_signature_free( sig );
4141 git_commit_free( baseCommit );
4142 git_tree_free( tree );
4143 wxRemoveFile( repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" ) );
4147 git_oid remoteCommitOid = createDetachedCommit( repo, baseOid, wxT(
"file.txt" ),
"remote\n",
"Remote change" );
4149 writeFetchHead( remoteCommitOid );
4153 wxString stageDir = tempBase() + wxFileName::GetPathSeparator() + wxT(
"del_stages" );
4154 wxString headOid, theirsOid, conflictsError;
4156 std::vector<KIGIT_CONFLICT_SIDES> conflicts;
4158 BOOST_REQUIRE_MESSAGE( common.
GetMergeConflicts( stageDir, headOid, theirsOid, conflicts, conflictsError ),
4159 "GetMergeConflicts failed: " + conflictsError.ToStdString() );
4160 BOOST_REQUIRE_EQUAL( conflicts.size(), 1u );
4161 BOOST_CHECK( !conflicts[0].hasOurs );
4162 BOOST_CHECK( conflicts[0].hasTheirs );
4165 BOOST_REQUIRE_MESSAGE( common.
CreateMergedCommit( headOid, theirsOid, { { conflicts[0].path, wxString() } }, error )
4167 "CreateMergedCommit failed: " + error.summary.ToStdString() );
4170 BOOST_REQUIRE_EQUAL( git_reference_name_to_id( &newHead, repo,
"HEAD" ), GIT_OK );
4172 git_commit* mergeCommit =
nullptr;
4173 BOOST_REQUIRE_EQUAL( git_commit_lookup( &mergeCommit, repo, &newHead ), GIT_OK );
4176 git_tree* mergedTree =
nullptr;
4177 BOOST_REQUIRE_EQUAL( git_commit_tree( &mergedTree, mergeCommit ), GIT_OK );
4178 BOOST_CHECK_MESSAGE( git_tree_entry_byname( mergedTree,
"file.txt" ) ==
nullptr,
4179 "keeping the deletion must not commit the file" );
4180 git_tree_free( mergedTree );
4181 git_commit_free( mergeCommit );
4183 BOOST_CHECK( !wxFileName::FileExists( repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" ) ) );
4185 git_repository_free( repo );
4193 BOOST_TEST_REQUIRE( ready() );
4196 BOOST_TEST_REQUIRE( repo );
4199 wxString filePath = repoPath() + wxFileName::GetPathSeparator() + wxT(
"file.txt" );
4201 std::ofstream f( filePath.ToStdString() );
4202 f <<
"staged-work-in-progress";
4205 git_index*
index =
nullptr;
4206 BOOST_REQUIRE_EQUAL( git_repository_index( &
index, repo ), GIT_OK );
4207 git_index_add_bypath(
index,
"file.txt" );
4208 git_index_write(
index );
4210 const git_index_entry* before = git_index_get_bypath(
index,
"file.txt", 0 );
4212 git_oid stagedOid = before->id;
4215 wxString dest = tempBase() + wxFileName::GetPathSeparator() + wxT(
"tree_out" );
4221 git_index_read(
index, 1 );
4222 const git_index_entry* after = git_index_get_bypath(
index,
"file.txt", 0 );
4224 BOOST_CHECK_MESSAGE( git_oid_equal( &stagedOid, &after->id ),
4225 "WriteTreeToDir must not rewrite the repository index" );
4227 git_index_free(
index );
4229 if( wxFileName::DirExists( dest ) )
4230 wxFileName::Rmdir( dest, wxPATH_RMDIR_RECURSIVE );
4232 git_repository_free( repo );
void AdvancePhase() override
Use the next available virtual zone of the dialog progress bar.
void SetTitle(const wxString &) override
Change the title displayed on the window caption.
void AdvanceProgress() override
Increment the progress bar length (inside the current virtual zone).
void AddPhases(int) override
void SetCurrentProgress(double) override
Set the progress value to aProgress (0..1).
void Report(const wxString &) override
Display aMessage in the progress bar dialog.
void BeginPhase(int) override
Initialize the aPhase virtual zone of the dialog progress bar.
bool IsCancelled() const override
void AdvancePhase(const wxString &) override
Use the next available virtual zone of the dialog progress bar and updates the message.
void SetMaxProgress(int) override
Fix the value that gives the 100 percent progress bar length (inside the current virtual zone).
bool KeepRefreshing(bool=false) override
Update the UI (if any).
void SetNumPhases(int) override
Set the number of phases.
BranchResult SwitchToBranch(const wxString &aBranchName)
Switch to the specified branch.
void SetRemote(const wxString &aRemote)
void SetClonePath(const wxString &aPath)
CommitResult PerformCommit(const std::vector< wxString > &aFiles, const wxString &aMessage, const wxString &aAuthorName, const wxString &aAuthorEmail)
CommitResult PerformAmend(const std::vector< wxString > &aFiles, const wxString &aMessage, const wxString &aAuthorName, const wxString &aAuthorEmail)
InitResult InitializeRepository(const wxString &aPath)
Initialize a new git repository in the specified directory.
bool SetupRemote(const RemoteConfig &aConfig)
Set up a remote for the repository.
void SetProgressReporter(std::unique_ptr< PROGRESS_REPORTER > aProgressReporter)
bool PerformFetch(bool aSkipLock=false)
PushResult PerformPush(bool aForce=false)
std::map< wxString, FileStatus > GetFileStatus(const wxString &aPathspec=wxEmptyString, bool aChangedOnly=false)
Get detailed file status for all files in the specified path.
Utility class with helper functions for project level git operations.
static bool IsTransientGitFile(const wxFileName &aFile)
Match a project-relative path against the shared transient patterns.
static bool WriteTreeToDir(git_repository *aRepo, const wxString &aRef, const wxString &aDestDir)
Check out a whole tree into a directory, leaving the repository's working copy and index alone.
static bool IsWithinProjectPath(const wxString &aAbsPath, const wxString &aProjectPath)
Test whether an absolute file path lives inside the current project directory.
Publishes a token for as long as its operation runs.
The project's git session.
std::shared_ptr< std::atomic< bool > > CANCEL_TOKEN
One token per operation.
GIT_CONN_TYPE GetConnType() const
std::vector< KIGIT_COMMIT_INFO > GetRecentCommits(size_t aMax=50, const KIGIT_HISTORY_FILTER &aFilter={}) const
bool HasUncommittedTrackedChanges(bool *aReadOk=nullptr) const
wxString GetRemote() const
bool IsOperationInProgress() const
True while a merge, rebase, cherry-pick or revert is in progress, which refuses commits.
std::unique_lock< std::mutex > LockAction()
bool HasUpstreamCommits() const
True when the upstream branch has commits the current branch does not.
git_repository * GetRepo() const
bool AddRemote(const wxString &aName, const wxString &aUrl)
static CANCEL_TOKEN NewCancelToken()
bool GetMergeConflicts(const wxString &aStageDir, wxString &aHeadOid, wxString &aTheirsOid, std::vector< KIGIT_CONFLICT_SIDES > &aConflicts, wxString &aError)
Recompute the merge in memory and extract each conflicted file's three versions into aStageDir,...
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 IsShuttingDown() const
Background timers ask this before starting work of their own.
bool DiscardChanges(const std::vector< wxString > &aPaths, wxString &aError)
Put the given working directory relative paths back to their committed state.
wxString GetUsername() const
void SetShuttingDown(bool aCancel)
Stop the operation running now and keep later ones from starting.
bool IsPathInHead(const wxString &aPath)
True when the working directory relative path exists in the HEAD commit's tree.
MergeCommitResult CreateMergedCommit(const wxString &aExpectedHeadOid, const wxString &aTheirsOid, const std::vector< std::pair< wxString, wxString > > &aResolvedFiles, KIGIT_ERROR_MESSAGE &aError)
Redo the merge with each conflicted path replaced by its resolved file, then commit and check out.
bool AbandonOperation(wxString &aError)
Drop that operation and return to the last commit. The caller holds the action lock.
GitOperation OperationInProgress() const
Which operation that is, so the panel can name it.
RestoreResult RestoreVersion(const wxString &aCommitHash, KIGIT_ERROR_MESSAGE &aError)
Restore the project as it was at aCommitHash, recorded as a new commit on top of the current head.
void SetRepo(git_repository *aRepo)
const std::vector< wxString > & GetErrorStrings() const
wxString GetErrorString()
void CancelThisOperation()
void SetCancelToken(const KIGIT_COMMON::CANCEL_TOKEN &aToken)
Callbacks read this token rather than the session, so one operation's cancel cannot reach another.
const KIGIT_COMMON::CANCEL_TOKEN & CancelToken() const
bool IsThisOperationCancelled() const
Give each test a backend and a temporary directory.
const wxString & TempPath() const
Container for project specific data.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
PROJECT * GetProject(const wxString &aFullPath) const
Retrieve a loaded project by name.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
static std::string ToStdString(const wxString &aStr)
void SetGitBackend(GIT_BACKEND *aBackend)
GIT_BACKEND * GetGitBackend()
@ SuccessIndexStale
Committed, but the staged list was left as another program had it. Never retry on this.
@ Busy
Another git operation held the action lock, so nothing was pushed.
std::vector< FILE_CHECKLIST_ENTRY > BuildGitChangeEntries(KIGIT_COMMON &aCommon, const wxString &aProjectPath, bool aDarkTheme)
Build the file checklist entries for the project's changed files, transient files filtered out and ea...
int push_transfer_progress_cb(unsigned int aCurrent, unsigned int aTotal, size_t aBytes, void *aPayload)
@ AlreadyCurrent
The chosen version's content matches the current head.
@ Restored
A new commit carrying the old version's content was created.
@ Error
The restore failed. See the error string.
@ Dirty
Uncommitted changes exist, and restoring would overwrite them.
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_index, decltype([](git_index *aIndex) { git_index_free(aIndex); })> GitIndexPtr
A unique pointer for git_index objects with automatic cleanup.
KIGIT::GitRepositoryPtr InitGitRepository(const wxString &aPath, const char *aBranch="main", const char *aAuthorName="QA Test", const char *aAuthorEmail="
[email protected]")
Pin the branch and author so repository setup does not depend on the user's Git config.
void WriteGitFile(const wxString &aRepoPath, const wxString &aRelativePath, const std::string &aContents)
Write test data without staging it. Create parent directories when needed.
git_oid CommitAllGitFiles(git_repository *aRepo, const wxString &aMessage)
Stage all changes, including deletions, and commit them.
git_oid CommitGitFile(git_repository *aRepo, const wxString &aPath, const wxString &aMessage)
Stage one file and commit the index. Other staged changes are kept.
bool collide(T aObject, U aAnotherObject, int aLayer, int aMinDistance)
Used by SHAPE_INDEX to implement Query().
One file row in a FILE_CHECKLIST_CTRL.
Build a temp directory tree containing a local working repo with one commit and a bare "remote" repo ...
const wxString & repoPath() const
bool initRepoWithCommit()
wxString readConfig(git_repository *aRepo, const wxString &aKey)
Read a string value from the repo config, or "" if absent.
git_oid createCommit(git_repository *aRepo, const wxString &aFile, const wxString &aContent, const wxString &aMessage)
Create a new commit on the current branch from a single (path, content) pair.
git_repository * openRepo() const
Open the working repo. Caller frees with git_repository_free.
void useEmptyGlobalIgnore(git_repository *aRepo)
bool clearUpstreamConfig(git_repository *aRepo, const wxString &aBranch)
Drop branch.
bool addOrigin(git_repository *aRepo)
Add an origin remote pointing at the bare remote on disk.
const wxString & remotePath() const
bool setUpstreamConfig(git_repository *aRepo, const wxString &aBranch)
Set branch.<aBranch>.merge = refs/heads/<aBranch>, branch.<aBranch>.remote = origin.
void writeFetchHead(const git_oid &aOid)
Write .git/FETCH_HEAD naming aOid, as a fetch would leave it.
bool initBareRemoteFromRepo()
git_oid createDetachedCommit(git_repository *aRepo, const git_oid &aParentOid, const wxString &aFile, const std::string &aContent, const char *aMessage)
Create a commit on top of aParentOid setting aFile to aContent, without moving HEAD.
const wxString & tempBase() const
A summary the user can act on, with the raw text that explains it kept apart for the details.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_CHECK_EQUAL_COLLECTIONS(mixed.begin(), mixed.end(), expMixed.begin(), expMixed.end())
static const char * TEST_AUTHOR_NAME
static const char * TEST_AUTHOR_EMAIL
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
IbisParser parser & reporter
static const char * TEST_AUTHOR_NAME
static const char * TEST_AUTHOR_EMAIL
BOOST_AUTO_TEST_CASE(FetchHeadMarksAtMostOneBranchToMerge)
static wxString seedProjectFile(const wxString &aDir)
VECTOR3I expected(15, 30, 45)
BOOST_TEST_CONTEXT("Test Clearance")
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
Definition of file extensions used in Kicad.