34#include <wx/filename.h>
36#include <wx/textfile.h>
84 const char* workdir = git_repository_workdir(
m_repo );
87 return wxString( workdir );
96 wxCHECK(
m_repo, wxEmptyString );
97 git_reference* head =
nullptr;
99 int retval = git_repository_head( &head,
m_repo );
101 if( retval && retval != GIT_EUNBORNBRANCH && retval != GIT_ENOTFOUND )
102 return wxEmptyString;
105 git_reference* branch;
107 if( git_reference_resolve( &branch, head ) )
109 wxLogTrace(
traceGit,
"Failed to resolve branch" );
110 return wxEmptyString;
114 const char* branchName =
"";
116 if( git_branch_name( &branchName, branch ) )
118 wxLogTrace(
traceGit,
"Failed to get branch name" );
119 return wxEmptyString;
122 return wxString( branchName );
150 std::vector<wxString> branchNames;
151 std::map<git_time_t, wxString> branchNamesMap;
154 git_branch_iterator* branchIterator =
nullptr;
156 if( git_branch_iterator_new( &branchIterator,
m_repo, GIT_BRANCH_LOCAL ) )
158 wxLogTrace(
traceGit,
"Failed to get branch iterator" );
163 git_reference* branchReference =
nullptr;
164 git_branch_t branchType;
166 while( git_branch_next( &branchReference, &branchType, branchIterator ) != GIT_ITEROVER )
168 const char* branchName =
"";
171 if( git_branch_name( &branchName, branchReference ) )
173 wxLogTrace(
traceGit,
"Failed to get branch name in iter loop" );
177 const git_oid* commitId = git_reference_target( branchReference );
179 git_commit* commit =
nullptr;
181 if( git_commit_lookup( &commit,
m_repo, commitId ) )
183 wxLogTrace(
traceGit,
"Failed to get commit in iter loop" );
188 git_time_t commitTime = git_commit_time( commit );
190 if( git_branch_is_head( branchReference ) )
191 firstName = branchName;
193 branchNamesMap.emplace( commitTime, branchName );
197 if( !firstName.IsEmpty() )
198 branchNames.push_back( firstName );
201 for(
auto rit = branchNamesMap.rbegin(); rit != branchNamesMap.rend(); ++rit )
202 branchNames.push_back( rit->second );
211 std::vector<wxString> projDirs;
217 if( git_reference_name_to_id( &oid,
m_repo,
"HEAD" ) != GIT_OK )
223 if( git_commit_lookup( &commit,
m_repo, &oid ) != GIT_OK )
231 if( git_commit_tree( &tree, commit ) != GIT_OK )
241 tree, GIT_TREEWALK_PRE,
242 [](
const char* root,
const git_tree_entry* entry,
void* payload )
244 std::vector<wxString>* prjs =
static_cast<std::vector<wxString>*
>( payload );
245 wxFileName root_fn( git_tree_entry_name( entry ) );
247 root_fn.SetPath( root );
249 if( git_tree_entry_type( entry ) == GIT_OBJECT_BLOB
250 && ( ( root_fn.GetExt() ==
"kicad_pro" ) || ( root_fn.GetExt() ==
"pro" ) ) )
252 prjs->push_back( root_fn.GetFullPath() );
259 std::sort( projDirs.begin(), projDirs.end(),
260 [](
const wxString& a,
const wxString& b )
262 int a_freq = a.Freq( wxFileName::GetPathSeparator() );
263 int b_freq = b.Freq( wxFileName::GetPathSeparator() );
265 if( a_freq == b_freq )
268 return a_freq < b_freq;
278 auto get_modified_files = [&](
const git_oid* from_oid,
const git_oid* to_oid ) -> std::set<wxString>
280 std::set<wxString> modified_set;
281 git_revwalk* walker =
nullptr;
286 if( git_revwalk_new( &walker,
m_repo ) != GIT_OK )
294 if( git_revwalk_push( walker, from_oid ) != GIT_OK )
300 if( to_oid && git_revwalk_hide( walker, to_oid ) != GIT_OK )
309 while( git_revwalk_next( &oid, walker ) == GIT_OK )
315 if( git_commit_lookup( &commit,
m_repo, &oid ) != GIT_OK )
322 git_tree *tree, *parent_tree =
nullptr;
324 if( git_commit_tree( &tree, commit ) != GIT_OK )
333 if( !git_commit_parentcount( commit ) )
336 tree, GIT_TREEWALK_PRE,
337 [](
const char* root,
const git_tree_entry* entry,
void* payload )
339 std::set<wxString>* modified_set_internal =
static_cast<std::set<wxString>*
>( payload );
340 wxString filePath = wxString::Format(
"%s%s", root, git_tree_entry_name( entry ) );
341 modified_set_internal->insert( std::move( filePath ) );
350 if( git_commit_parent( &parent, commit, 0 ) != GIT_OK )
359 if( git_commit_tree( &parent_tree, parent ) != GIT_OK )
369 git_diff_options diff_opts;
370 git_diff_init_options( &diff_opts, GIT_DIFF_OPTIONS_VERSION );
372 if( !
m_repo || !parent_tree || !tree )
375 if( git_diff_tree_to_tree( &diff,
m_repo, parent_tree, tree, &diff_opts ) == GIT_OK )
377 size_t num_deltas = git_diff_num_deltas( diff );
379 for(
size_t i = 0; i < num_deltas; ++i )
381 const git_diff_delta*
delta = git_diff_get_delta( diff, i );
382 modified_set.insert(
delta->new_file.path );
385 git_diff_free( diff );
398 std::pair<std::set<wxString>,std::set<wxString>> modified_files;
401 return modified_files;
403 git_reference* head =
nullptr;
404 git_reference* remote_head =
nullptr;
406 if( git_repository_head( &head,
m_repo ) != GIT_OK )
408 wxLogTrace(
traceGit,
"Failed to get modified HEAD" );
409 return modified_files;
414 if( git_branch_upstream( &remote_head, head ) != GIT_OK )
416 wxLogTrace(
traceGit,
"Failed to get modified remote HEAD" );
421 if( remote_head !=
nullptr && head !=
nullptr )
423 const git_oid* head_oid = git_reference_target( head );
424 const git_oid* remote_oid = git_reference_target( remote_head );
426 modified_files.first = get_modified_files( head_oid, remote_oid );
427 modified_files.second = get_modified_files( remote_oid, head_oid );
430 return modified_files;
439 git_reference* head =
nullptr;
440 git_reference* remote_head =
nullptr;
442 if( git_repository_head( &head,
m_repo ) != GIT_OK )
450 if( git_branch_upstream( &remote_head, head ) != GIT_OK )
458 const git_oid* head_oid = git_reference_target( head );
459 const git_oid* remote_oid = git_reference_target( remote_head );
460 git_revwalk* walker =
nullptr;
462 if( git_revwalk_new( &walker,
m_repo ) != GIT_OK )
470 if( !head_oid || git_revwalk_push( walker, head_oid ) != GIT_OK )
476 if( remote_oid && git_revwalk_hide( walker, remote_oid ) != GIT_OK )
485 if( git_revwalk_next( &oid, walker ) != GIT_OK )
498 git_remote* remote =
nullptr;
500 if( git_remote_lookup( &remote,
m_repo,
"origin" ) != GIT_OK )
502 wxLogTrace(
traceGit,
"Failed to get remote for haspushpull" );
509 const char* fetch_url = git_remote_url( remote );
510 const char* push_url = git_remote_pushurl( remote );
515 wxLogTrace(
traceGit,
"No push URL set, using fetch URL" );
516 push_url = fetch_url;
519 return fetch_url && push_url;
525 wxCHECK(
m_repo, wxEmptyString );
528 git_reference* head =
nullptr;
529 git_reference* upstream =
nullptr;
531 if( git_repository_head( &head,
m_repo ) != GIT_OK )
539 if( git_branch_upstream( &upstream, head ) != GIT_OK )
542 git_strarray remotes = {
nullptr, 0 };
544 if( git_remote_list( &remotes,
m_repo ) == GIT_OK )
546 if( remotes.count == 1 )
547 retval = remotes.strings[0];
549 git_strarray_dispose( &remotes );
558 git_remote* remote =
nullptr;
560 if( git_remote_lookup( &remote,
m_repo,
"origin" ) == GIT_OK )
562 retval = git_remote_name( remote );
563 git_remote_free( remote );
567 wxLogTrace(
traceGit,
"Failed to get remote name from default remote: %s",
576 git_buf remote_name = GIT_BUF_INIT_CONST(
nullptr, 0 );
578 if( git_branch_remote_name( &remote_name,
m_repo, git_reference_name( upstream ) ) == GIT_OK )
580 retval = remote_name.ptr;
581 git_buf_dispose( &remote_name );
586 "Failed to get remote name from upstream branch: %s",
608 return wxEmptyString;
610 const char *
path = git_repository_path(
m_repo );
611 wxString retval =
path;
620 wxFileName keyFile( wxGetHomeDir(), wxEmptyString );
621 keyFile.AppendDir(
".ssh" );
622 keyFile.SetFullName(
"id_rsa" );
624 if( keyFile.FileExists() )
627 keyFile.SetFullName(
"id_dsa" );
629 if( keyFile.FileExists() )
632 keyFile.SetFullName(
"id_ecdsa" );
634 if( keyFile.FileExists() )
637 keyFile.SetFullName(
"id_ed25519" );
639 if( keyFile.FileExists() )
643 wxFileName sshConfig( wxGetHomeDir(), wxEmptyString );
644 sshConfig.AppendDir(
".ssh" );
645 sshConfig.SetFullName(
"config" );
647 if( sshConfig.FileExists() )
649 wxTextFile configFile( sshConfig.GetFullPath() );
654 for( wxString line = configFile.GetFirstLine(); !configFile.Eof(); line = configFile.GetNextLine() )
656 line.Trim(
false ).Trim(
true );
658 if( line.StartsWith(
"Host " ) )
663 if( line.StartsWith(
"Host" ) && line.Contains(
m_hostname ) )
666 if( match && line.StartsWith(
"IdentityFile" ) )
668 wxString keyPath = line.AfterFirst(
' ' ).Trim(
false ).Trim(
true );
671 if( keyPath.StartsWith(
"~" ) )
672 keyPath.Replace(
"~", wxGetHomeDir(),
false );
675 if( wxFileName::FileExists( keyPath ) )
693 git_remote* remote =
nullptr;
699 if( git_remote_lookup( &remote,
m_repo, remote_name.ToStdString().c_str() ) == GIT_OK )
701 const char* url = git_remote_url( remote );
706 git_remote_free( remote );
718 if( remote.IsEmpty() )
721 if( remote.StartsWith(
"https://" ) || remote.StartsWith(
"http://" ) )
725 else if( remote.StartsWith(
"ssh://" ) || remote.StartsWith(
"git@" ) || remote.StartsWith(
"git+ssh://" )
726 || remote.EndsWith(
".git" ) )
747 size_t atPos = uri.find(
'@' );
749 if( atPos != wxString::npos )
751 size_t protoEnd = uri.find(
"//" );
753 if( protoEnd != wxString::npos )
755 wxString credentials = uri.Mid( protoEnd + 2, atPos - protoEnd - 2 );
756 size_t colonPos = credentials.find(
':' );
758 if( colonPos != wxString::npos )
761 m_password = credentials.Mid( colonPos + 1, credentials.Length() - colonPos - 1 );
777 size_t colonPos =
m_remote.find(
':' );
779 if( colonPos != wxString::npos )
785 size_t hostStart =
m_remote.find(
"://" ) + 2;
786 size_t hostEnd =
m_remote.find(
'/', hostStart );
789 if( hostEnd != wxString::npos )
790 host =
m_remote.Mid( hostStart, hostEnd - hostStart );
794 atPos = host.find(
'@' );
796 if( atPos != wxString::npos )
815 if( sshKey.IsEmpty() )
817 wxLogTrace(
traceGit,
"Finished testing all possible ssh keys" );
819 return GIT_PASSTHROUGH;
822 wxString sshPubKey = sshKey +
".pub";
825 wxLogTrace(
traceGit,
"Testing %s\n", sshKey );
827 if( git_credential_ssh_key_new( aOut, aUsername.mbc_str(), sshPubKey.mbc_str(), sshKey.mbc_str(),
828 password.mbc_str() ) != GIT_OK )
830 wxLogTrace(
traceGit,
"Failed to create SSH key credential for %s: %s",
832 return GIT_PASSTHROUGH;
843 git_credential_userpass_plaintext_new( aOut, aUsername.mbc_str(), password.mbc_str() );
852 if( git_credential_ssh_key_from_agent( aOut, aUsername.mbc_str() ) != GIT_OK )
854 wxLogTrace(
traceGit,
"Failed to create SSH agent credential for %s: %s",
856 return GIT_PASSTHROUGH;
865 const git_oid* aOID,
unsigned int aIsMerge,
void* aPayload )
868 git_oid_cpy( (git_oid*) aPayload, aOID );
874extern "C" void clone_progress_cb(
const char* aStr,
size_t aLen,
size_t aTotal,
void* aPayload )
878 wxString progressMessage( aStr );
883extern "C" int progress_cb(
const char* str,
int len,
void* aPayload )
889 wxLogTrace(
traceGit,
"Progress CB cancelled" );
893 wxString progressMessage( str, len );
904 wxString progressMessage = wxString::Format(
_(
"Received %u of %u objects" ),
905 aStats->received_objects,
906 aStats->total_objects );
909 wxLogTrace(
traceGit,
"Transfer progress cancelled" );
913 parent->
UpdateProgress( aStats->received_objects, aStats->total_objects, progressMessage );
919extern "C" int update_cb(
const char* aRefname,
const git_oid* aFirst,
const git_oid* aSecond,
922 constexpr int cstring_len = 8;
923 char a_str[cstring_len + 1];
924 char b_str[cstring_len + 1];
929 git_oid_tostr( b_str, cstring_len, aSecond );
931#if ( LIBGIT2_VER_MAJOR >= 1 ) || ( LIBGIT2_VER_MINOR >= 99 )
932 if( !git_oid_is_zero( aFirst ) )
934 if( !git_oid_iszero( aFirst ) )
937 git_oid_tostr( a_str, cstring_len, aFirst );
938 status = wxString::Format(
_(
"* [updated] %s..%s %s" ), a_str, b_str, aRefname );
942 status = wxString::Format(
_(
"* [new] %s %s" ), b_str, aRefname );
954 long long progress = 100;
959 progress = ( aCurrent * 100ll ) / aTotal;
962 wxString progressMessage = wxString::Format(
_(
"Writing objects: %lld%% (%u/%u), %zu bytes" ),
963 progress, aCurrent, aTotal, aBytes );
973 wxString status( aStatus );
975 if( !status.IsEmpty() )
977 wxString statusMessage = wxString::Format(
_(
"* [rejected] %s (%s)" ), aRefname, aStatus );
982 wxString statusMessage = wxString::Format(
_(
"[updated] %s" ), aRefname );
990extern "C" int credentials_cb( git_cred** aOut,
const char* aUrl,
const char* aUsername,
991 unsigned int aAllowedTypes,
void* aPayload )
996 wxLogTrace(
traceGit,
"Credentials callback for %s, testing %d", aUrl, aAllowedTypes );
1000 wxLogTrace(
traceGit,
"Local repository, no credentials needed" );
1001 return GIT_PASSTHROUGH;
1004 if( aAllowedTypes & GIT_CREDENTIAL_USERNAME
1005 && !( parent->
TestedTypes() & GIT_CREDENTIAL_USERNAME ) )
1007 wxString username = parent->
GetUsername().Trim().Trim(
false );
1008 wxLogTrace(
traceGit,
"Username credential for %s at %s with allowed type %d",
1009 username, aUrl, aAllowedTypes );
1011 if( git_credential_username_new( aOut, username.ToStdString().c_str() ) != GIT_OK )
1013 wxLogTrace(
traceGit,
"Failed to create username credential for %s: %s",
1018 wxLogTrace(
traceGit,
"Created username credential for %s", username );
1024 && ( aAllowedTypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT )
1025 && !( parent->
TestedTypes() & GIT_CREDENTIAL_USERPASS_PLAINTEXT ) )
1028 wxLogTrace(
traceGit,
"Plaintext authentication for %s at %s with allowed type %d",
1033 && ( aAllowedTypes & GIT_CREDENTIAL_SSH_KEY )
1034 && !( parent->
TestedTypes() & GIT_CREDENTIAL_SSH_KEY ) )
1044 return GIT_PASSTHROUGH;
1047 git_error_set_str( GIT_ERROR_NET,
_(
"Unable to authenticate" ).mbc_str() );
virtual void UpdateProgress(int aCurrent, int aTotal, const wxString &aMessage)
std::mutex m_gitActionMutex
std::vector< wxString > GetBranchNames() const
void updateConnectionType()
GIT_CONN_TYPE GetConnType() const
static wxString GetLastGitError()
wxString GetCurrentBranchName() const
wxString GetGitRootDirectory() const
void SetSSHKey(const wxString &aSSHKey)
static const unsigned KIGIT_CREDENTIAL_SSH_AGENT
int HandlePlaintextAuthentication(git_cred **aOut, const wxString &aUsername)
std::vector< wxString > GetProjectDirs()
Return a vector of project files in the repository.
git_repository * GetRepo() const
wxString GetProjectDir() const
Get the project directory path.
wxString GetNextPublicKey()
KIGIT_COMMON(git_repository *aRepo)
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 ...
std::vector< wxString > m_publicKeys
bool HasPushAndPullRemote() const
void UpdateCurrentBranchInfo()
bool HasLocalCommits() const
int HandleSSHAgentAuthentication(git_cred **aOut, const wxString &aUsername)
wxString GetRemotename() const
int HandleSSHKeyAuthentication(git_cred **aOut, const wxString &aUsername)
wxString GetUsername() const
Get the username.
unsigned & TestedTypes()
Return the connection types that have been tested for authentication.
KIGIT_COMMON * GetCommon() const
Get the common object.
KIGIT_COMMON::GIT_CONN_TYPE GetConnType() const
Get the connection type.
const wxChar *const traceGit
Flag to enable Git debugging output.
int fetchhead_foreach_cb(const char *, const char *, const git_oid *aOID, unsigned int aIsMerge, void *aPayload)
int progress_cb(const char *str, int len, void *aPayload)
int push_update_reference_cb(const char *aRefname, const char *aStatus, void *aPayload)
int update_cb(const char *aRefname, const git_oid *aFirst, const git_oid *aSecond, void *aPayload)
int transfer_progress_cb(const git_transfer_progress *aStats, void *aPayload)
int credentials_cb(git_cred **aOut, const char *aUrl, const char *aUsername, unsigned int aAllowedTypes, void *aPayload)
void clone_progress_cb(const char *aStr, size_t aLen, size_t aTotal, void *aPayload)
int push_transfer_progress_cb(unsigned int aCurrent, unsigned int aTotal, size_t aBytes, void *aPayload)
std::unique_ptr< git_tree, decltype([](git_tree *aTree) { git_tree_free(aTree); })> GitTreePtr
A unique pointer for git_tree objects with automatic cleanup.
std::unique_ptr< git_revwalk, decltype([](git_revwalk *aWalker) { git_revwalk_free(aWalker); })> GitRevWalkPtr
A unique pointer for git_revwalk 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_reference, decltype([](git_reference *aRef) { git_reference_free(aRef); })> GitReferencePtr
A unique pointer for git_reference objects with automatic cleanup.
std::unique_ptr< git_branch_iterator, decltype([](git_branch_iterator *aIter) { git_branch_iterator_free(aIter); })> GitBranchIteratorPtr
A unique pointer for git_branch_iterator objects with automatic cleanup.
std::unique_ptr< git_remote, decltype([](git_remote *aRemote) { git_remote_free(aRemote); })> GitRemotePtr
A unique pointer for git_remote objects with automatic cleanup.
wxLogTrace helper definitions.