/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright The KiCad Developers, see AUTHORS.TXT for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/gpl-3.0.html * or you may search the http://www.gnu.org website for the version 3 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "kicad_git_common.h" #include "kicad_git_memory.h" #include "git_repo_mixin.h" #include #include #include #include #include #include #include #include #include #include #include KIGIT_COMMON::KIGIT_COMMON( git_repository* aRepo ) : m_repo( aRepo ), m_connType( GIT_CONN_TYPE::GIT_CONN_LOCAL ), m_testedTypes( 0 ), m_nextPublicKey( 0 ) {} KIGIT_COMMON::KIGIT_COMMON( const KIGIT_COMMON& aOther ) : // Initialize base class and member variables m_repo( aOther.m_repo ), m_connType( aOther.m_connType ), m_remote( aOther.m_remote ), m_hostname( aOther.m_hostname ), m_username( aOther.m_username ), m_password( aOther.m_password ), m_testedTypes( aOther.m_testedTypes ), // The mutex is default-initialized, not copied m_gitActionMutex(), m_publicKeys( aOther.m_publicKeys ), m_nextPublicKey( aOther.m_nextPublicKey ) { } KIGIT_COMMON::~KIGIT_COMMON() {} git_repository* KIGIT_COMMON::GetRepo() const { return m_repo; } wxString KIGIT_COMMON::GetCurrentBranchName() const { wxCHECK( m_repo, wxEmptyString ); git_reference* head = nullptr; int retval = git_repository_head( &head, m_repo ); if( retval && retval != GIT_EUNBORNBRANCH && retval != GIT_ENOTFOUND ) return wxEmptyString; KIGIT::GitReferencePtr headPtr( head ); git_reference* branch; if( git_reference_resolve( &branch, head ) ) { wxLogTrace( traceGit, "Failed to resolve branch" ); return wxEmptyString; } KIGIT::GitReferencePtr branchPtr( branch ); const char* branchName = ""; if( git_branch_name( &branchName, branch ) ) { wxLogTrace( traceGit, "Failed to get branch name" ); return wxEmptyString; } return wxString( branchName ); } std::vector KIGIT_COMMON::GetBranchNames() const { if( !m_repo ) return {}; std::vector branchNames; std::map branchNamesMap; wxString firstName; git_branch_iterator* branchIterator = nullptr; if( git_branch_iterator_new( &branchIterator, m_repo, GIT_BRANCH_LOCAL ) ) { wxLogTrace( traceGit, "Failed to get branch iterator" ); return branchNames; } KIGIT::GitBranchIteratorPtr branchIteratorPtr( branchIterator ); git_reference* branchReference = nullptr; git_branch_t branchType; while( git_branch_next( &branchReference, &branchType, branchIterator ) != GIT_ITEROVER ) { const char* branchName = ""; KIGIT::GitReferencePtr branchReferencePtr( branchReference ); if( git_branch_name( &branchName, branchReference ) ) { wxLogTrace( traceGit, "Failed to get branch name in iter loop" ); continue; } const git_oid* commitId = git_reference_target( branchReference ); git_commit* commit = nullptr; if( git_commit_lookup( &commit, m_repo, commitId ) ) { wxLogTrace( traceGit, "Failed to get commit in iter loop" ); continue; } KIGIT::GitCommitPtr commitPtr( commit ); git_time_t commitTime = git_commit_time( commit ); if( git_branch_is_head( branchReference ) ) firstName = branchName; else branchNamesMap.emplace( commitTime, branchName ); } // Add the current branch to the top of the list if( !firstName.IsEmpty() ) branchNames.push_back( firstName ); // Add the remaining branches in order from newest to oldest for( auto rit = branchNamesMap.rbegin(); rit != branchNamesMap.rend(); ++rit ) branchNames.push_back( rit->second ); return branchNames; } std::vector KIGIT_COMMON::GetProjectDirs() { wxCHECK( m_repo, {} ); std::vector projDirs; git_oid oid; git_commit* commit; git_tree *tree; if( git_reference_name_to_id( &oid, m_repo, "HEAD" ) != GIT_OK ) { wxLogTrace( traceGit, "An error occurred: %s", KIGIT_COMMON::GetLastGitError() ); return projDirs; } if( git_commit_lookup( &commit, m_repo, &oid ) != GIT_OK ) { wxLogTrace( traceGit, "An error occurred: %s", KIGIT_COMMON::GetLastGitError() ); return projDirs; } KIGIT::GitCommitPtr commitPtr( commit ); if( git_commit_tree( &tree, commit ) != GIT_OK ) { wxLogTrace( traceGit, "An error occurred: %s", KIGIT_COMMON::GetLastGitError() ); return projDirs; } KIGIT::GitTreePtr treePtr( tree ); // Define callback git_tree_walk( tree, GIT_TREEWALK_PRE, []( const char* root, const git_tree_entry* entry, void* payload ) { std::vector* prjs = static_cast*>( payload ); wxFileName root_fn( git_tree_entry_name( entry ) ); root_fn.SetPath( root ); if( git_tree_entry_type( entry ) == GIT_OBJECT_BLOB && ( ( root_fn.GetExt() == "kicad_pro" ) || ( root_fn.GetExt() == "pro" ) ) ) { prjs->push_back( root_fn.GetFullPath() ); } return 0; // continue walking }, &projDirs ); std::sort( projDirs.begin(), projDirs.end(), []( const wxString& a, const wxString& b ) { int a_freq = a.Freq( wxFileName::GetPathSeparator() ); int b_freq = b.Freq( wxFileName::GetPathSeparator() ); if( a_freq == b_freq ) return a < b; else return a_freq < b_freq; } ); return projDirs; } std::pair,std::set> KIGIT_COMMON::GetDifferentFiles() const { auto get_modified_files = [&]( const git_oid* from_oid, const git_oid* to_oid ) -> std::set { std::set modified_set; git_revwalk* walker = nullptr; if( !m_repo || !from_oid ) return modified_set; if( git_revwalk_new( &walker, m_repo ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to create revwalker: %s", KIGIT_COMMON::GetLastGitError() ); return modified_set; } KIGIT::GitRevWalkPtr walkerPtr( walker ); if( git_revwalk_push( walker, from_oid ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to set from commit: %s", KIGIT_COMMON::GetLastGitError() ); return modified_set; } if( to_oid && git_revwalk_hide( walker, to_oid ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to set end commit (maybe new repo): %s", KIGIT_COMMON::GetLastGitError() ); } git_oid oid; git_commit* commit; // iterate over all local commits not in remote while( git_revwalk_next( &oid, walker ) == GIT_OK ) { if( git_commit_lookup( &commit, m_repo, &oid ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to lookup commit: %s", KIGIT_COMMON::GetLastGitError() ); continue; } KIGIT::GitCommitPtr commitPtr( commit ); git_tree *tree, *parent_tree = nullptr; if( git_commit_tree( &tree, commit ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to get commit tree: %s", KIGIT_COMMON::GetLastGitError() ); continue; } KIGIT::GitTreePtr treePtr( tree ); // get parent commit tree to diff against if( !git_commit_parentcount( commit ) ) { git_tree_walk( tree, GIT_TREEWALK_PRE, []( const char* root, const git_tree_entry* entry, void* payload ) { std::set* modified_set_internal = static_cast*>( payload ); wxString filePath = wxString::Format( "%s%s", root, git_tree_entry_name( entry ) ); modified_set_internal->insert( filePath ); return 0; // continue walking }, &modified_set ); continue; } git_commit* parent; if( git_commit_parent( &parent, commit, 0 ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to get parent commit: %s", KIGIT_COMMON::GetLastGitError() ); continue; } KIGIT::GitCommitPtr parentPtr( parent ); if( git_commit_tree( &parent_tree, parent ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to get parent commit tree: %s", KIGIT_COMMON::GetLastGitError() ); continue; } KIGIT::GitTreePtr parentTreePtr( parent_tree ); git_diff* diff; git_diff_options diff_opts; git_diff_init_options( &diff_opts, GIT_DIFF_OPTIONS_VERSION ); if( !m_repo || !parent_tree || !tree ) continue; if( git_diff_tree_to_tree( &diff, m_repo, parent_tree, tree, &diff_opts ) == GIT_OK ) { size_t num_deltas = git_diff_num_deltas( diff ); for( size_t i = 0; i < num_deltas; ++i ) { const git_diff_delta* delta = git_diff_get_delta( diff, i ); modified_set.insert( delta->new_file.path ); } git_diff_free( diff ); } else { wxLogTrace( traceGit, "Failed to diff trees: %s", KIGIT_COMMON::GetLastGitError() ); } } wxLogTrace( traceGit, "Finished walking commits with end: %s", KIGIT_COMMON::GetLastGitError() ); return modified_set; }; std::pair,std::set> modified_files; if( !m_repo ) return modified_files; git_reference* head = nullptr; git_reference* remote_head = nullptr; if( git_repository_head( &head, m_repo ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to get modified HEAD" ); return modified_files; } KIGIT::GitReferencePtr headPtr( head ); if( git_branch_upstream( &remote_head, head ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to get modified remote HEAD" ); } KIGIT::GitReferencePtr remoteHeadPtr( remote_head ); if( remote_head != nullptr && head != nullptr ) { const git_oid* head_oid = git_reference_target( head ); const git_oid* remote_oid = git_reference_target( remote_head ); modified_files.first = get_modified_files( head_oid, remote_oid ); modified_files.second = get_modified_files( remote_oid, head_oid ); } return modified_files; } bool KIGIT_COMMON::HasLocalCommits() const { if( !m_repo ) return false; git_reference* head = nullptr; git_reference* remote_head = nullptr; if( git_repository_head( &head, m_repo ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to get HEAD: %s", KIGIT_COMMON::GetLastGitError() ); return false; } KIGIT::GitReferencePtr headPtr( head ); if( git_branch_upstream( &remote_head, head ) != GIT_OK ) { // No remote branch, so we have local commits (new repo?) wxLogTrace( traceGit, "Failed to get remote HEAD: %s", KIGIT_COMMON::GetLastGitError() ); return true; } KIGIT::GitReferencePtr remoteHeadPtr( remote_head ); const git_oid* head_oid = git_reference_target( head ); const git_oid* remote_oid = git_reference_target( remote_head ); git_revwalk* walker = nullptr; if( git_revwalk_new( &walker, m_repo ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to create revwalker: %s", KIGIT_COMMON::GetLastGitError() ); return false; } KIGIT::GitRevWalkPtr walkerPtr( walker ); if( !head_oid || git_revwalk_push( walker, head_oid ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to push commits: %s", KIGIT_COMMON::GetLastGitError() ); return false; } if( remote_oid && git_revwalk_hide( walker, remote_oid ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to push/hide commits: %s", KIGIT_COMMON::GetLastGitError() ); return false; } git_oid oid; // If we can't walk to the next commit, then we are at or behind the remote if( git_revwalk_next( &oid, walker ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to walk to next commit: %s", KIGIT_COMMON::GetLastGitError() ); return false; } return true; } bool KIGIT_COMMON::HasPushAndPullRemote() const { wxCHECK( m_repo, false ); git_remote* remote = nullptr; if( git_remote_lookup( &remote, m_repo, "origin" ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to get remote for haspushpull" ); return false; } KIGIT::GitRemotePtr remotePtr( remote ); // Get the URLs associated with the remote const char* fetch_url = git_remote_url( remote ); const char* push_url = git_remote_pushurl( remote ); // If no push URL is set, libgit2 defaults to using the fetch URL for pushing if( !push_url ) { wxLogTrace( traceGit, "No push URL set, using fetch URL" ); push_url = fetch_url; } return fetch_url && push_url; } wxString KIGIT_COMMON::GetRemotename() const { wxCHECK( m_repo, wxEmptyString ); wxString retval; git_reference* head = nullptr; git_reference* upstream = nullptr; if( git_repository_head( &head, m_repo ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to get remote name: %s", KIGIT_COMMON::GetLastGitError() ); return retval; } KIGIT::GitReferencePtr headPtr( head ); if( git_branch_upstream( &upstream, head ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to get upstream branch: %s", KIGIT_COMMON::GetLastGitError() ); git_strarray remotes = { nullptr, 0 }; if( git_remote_list( &remotes, m_repo ) == GIT_OK ) { if( remotes.count == 1 ) retval = remotes.strings[0]; git_strarray_dispose( &remotes ); } else { wxLogTrace( traceGit, "Failed to list remotes: %s", KIGIT_COMMON::GetLastGitError() ); // If we can't get the remote name from the upstream branch or the list of remotes, // just return the default remote name git_remote* remote = nullptr; if( git_remote_lookup( &remote, m_repo, "origin" ) == GIT_OK ) { retval = git_remote_name( remote ); git_remote_free( remote ); } else { wxLogTrace( traceGit, "Failed to get remote name from default remote: %s", KIGIT_COMMON::GetLastGitError() ); } } return retval; } KIGIT::GitReferencePtr upstreamPtr( upstream ); git_buf remote_name = GIT_BUF_INIT_CONST( nullptr, 0 ); if( git_branch_remote_name( &remote_name, m_repo, git_reference_name( upstream ) ) == GIT_OK ) { retval = remote_name.ptr; git_buf_dispose( &remote_name ); } else { wxLogTrace( traceGit, "Failed to get remote name from upstream branch: %s", KIGIT_COMMON::GetLastGitError() ); } return retval; } void KIGIT_COMMON::SetSSHKey( const wxString& aKey ) { auto it = std::find( m_publicKeys.begin(), m_publicKeys.end(), aKey ); if( it != m_publicKeys.end() ) m_publicKeys.erase( it ); m_publicKeys.insert( m_publicKeys.begin(), aKey ); } wxString KIGIT_COMMON::GetGitRootDirectory() const { if( !m_repo ) return wxEmptyString; const char *path = git_repository_path( m_repo ); wxString retval = path; return retval; } void KIGIT_COMMON::updatePublicKeys() { m_publicKeys.clear(); wxFileName keyFile( wxGetHomeDir(), wxEmptyString ); keyFile.AppendDir( ".ssh" ); keyFile.SetFullName( "id_rsa" ); if( keyFile.FileExists() ) m_publicKeys.push_back( keyFile.GetFullPath() ); keyFile.SetFullName( "id_dsa" ); if( keyFile.FileExists() ) m_publicKeys.push_back( keyFile.GetFullPath() ); keyFile.SetFullName( "id_ecdsa" ); if( keyFile.FileExists() ) m_publicKeys.push_back( keyFile.GetFullPath() ); keyFile.SetFullName( "id_ed25519" ); if( keyFile.FileExists() ) m_publicKeys.push_back( keyFile.GetFullPath() ); // Parse SSH config file for hostname information wxFileName sshConfig( wxGetHomeDir(), wxEmptyString ); sshConfig.AppendDir( ".ssh" ); sshConfig.SetFullName( "config" ); if( sshConfig.FileExists() ) { wxTextFile configFile( sshConfig.GetFullPath() ); configFile.Open(); bool match = false; for( wxString line = configFile.GetFirstLine(); !configFile.Eof(); line = configFile.GetNextLine() ) { line.Trim( false ).Trim( true ); if( line.StartsWith( "Host " ) ) match = false; // The difference here is that we are matching either "Hostname" or "Host" to get the // match. This is because in the absence of a "Hostname" line, the "Host" line is used if( line.StartsWith( "Host" ) && line.Contains( m_hostname ) ) match = true; if( match && line.StartsWith( "IdentityFile" ) ) { wxString keyPath = line.AfterFirst( ' ' ).Trim( false ).Trim( true ); // Expand ~ to home directory if present if( keyPath.StartsWith( "~" ) ) keyPath.Replace( "~", wxGetHomeDir(), false ); // Add the public key to the beginning of the list if( wxFileName::FileExists( keyPath ) ) SetSSHKey( keyPath ); } } configFile.Close(); } } void KIGIT_COMMON::UpdateCurrentBranchInfo() { wxCHECK( m_repo, /* void */ ); // We want to get the current branch's upstream url as well as the stored password // if one exists given the url and username. wxString remote_name = GetRemotename(); git_remote* remote = nullptr; if( git_remote_lookup( &remote, m_repo, remote_name.ToStdString().c_str() ) == GIT_OK ) { const char* url = git_remote_url( remote ); if( url ) m_remote = url; git_remote_free( remote ); } // Find the stored password if it exists KIPLATFORM::SECRETS::GetSecret( m_remote, m_username, m_password ); updateConnectionType(); updatePublicKeys(); } KIGIT_COMMON::GIT_CONN_TYPE KIGIT_COMMON::GetConnType() const { wxString remote = m_remote; if( remote.IsEmpty() ) remote = GetRemotename(); if( remote.StartsWith( "https://" ) || remote.StartsWith( "http://" ) ) { return GIT_CONN_TYPE::GIT_CONN_HTTPS; } else if( remote.StartsWith( "ssh://" ) || remote.StartsWith( "git@" ) || remote.StartsWith( "git+ssh://" ) || remote.EndsWith( ".git" ) ) { return GIT_CONN_TYPE::GIT_CONN_SSH; } return GIT_CONN_TYPE::GIT_CONN_LOCAL; } void KIGIT_COMMON::updateConnectionType() { if( m_remote.StartsWith( "https://" ) || m_remote.StartsWith( "http://" ) ) m_connType = GIT_CONN_TYPE::GIT_CONN_HTTPS; else if( m_remote.StartsWith( "ssh://" ) || m_remote.StartsWith( "git@" ) || m_remote.StartsWith( "git+ssh://" ) ) m_connType = GIT_CONN_TYPE::GIT_CONN_SSH; else m_connType = GIT_CONN_TYPE::GIT_CONN_LOCAL; if( m_connType != GIT_CONN_TYPE::GIT_CONN_LOCAL ) { wxString uri = m_remote; size_t atPos = uri.find( '@' ); if( atPos != wxString::npos ) { size_t protoEnd = uri.find( "//" ); if( protoEnd != wxString::npos ) { wxString credentials = uri.Mid( protoEnd + 2, atPos - protoEnd - 2 ); size_t colonPos = credentials.find( ':' ); if( colonPos != wxString::npos ) { m_username = credentials.Left( colonPos ); m_password = credentials.Mid( colonPos + 1, credentials.Length() - colonPos - 1 ); } else { m_username = credentials; } } else { m_username = uri.Left( atPos ); } } if( m_remote.StartsWith( "git@" ) ) { // SSH format: git@hostname:path size_t colonPos = m_remote.find( ':' ); if( colonPos != wxString::npos ) m_hostname = m_remote.Mid( 4, colonPos - 4 ); } else { // other URL format: proto://[user@]hostname/path size_t hostStart = m_remote.find( "://" ) + 2; size_t hostEnd = m_remote.find( '/', hostStart ); wxString host; if( hostEnd != wxString::npos ) host = m_remote.Mid( hostStart, hostEnd - hostStart ); else host = m_remote.Mid( hostStart ); atPos = host.find( '@' ); if( atPos != wxString::npos ) m_hostname = host.Mid( atPos + 1 ); else m_hostname = host; } } } int KIGIT_COMMON::HandleSSHKeyAuthentication( git_cred** aOut, const wxString& aUsername ) { if( !( m_testedTypes & KIGIT_CREDENTIAL_SSH_AGENT ) ) return HandleSSHAgentAuthentication( aOut, aUsername ); // SSH key authentication with password wxString sshKey = GetNextPublicKey(); if( sshKey.IsEmpty() ) { wxLogTrace( traceGit, "Finished testing all possible ssh keys" ); m_testedTypes |= GIT_CREDENTIAL_SSH_KEY; return GIT_PASSTHROUGH; } wxString sshPubKey = sshKey + ".pub"; wxString password = GetPassword(); wxLogTrace( traceGit, "Testing %s\n", sshKey ); if( git_credential_ssh_key_new( aOut, aUsername.mbc_str(), sshPubKey.mbc_str(), sshKey.mbc_str(), password.mbc_str() ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to create SSH key credential for %s: %s", aUsername, KIGIT_COMMON::GetLastGitError() ); return GIT_PASSTHROUGH; } return GIT_OK; } int KIGIT_COMMON::HandlePlaintextAuthentication( git_cred** aOut, const wxString& aUsername ) { wxString password = GetPassword(); git_credential_userpass_plaintext_new( aOut, aUsername.mbc_str(), password.mbc_str() ); m_testedTypes |= GIT_CREDENTIAL_USERPASS_PLAINTEXT; return GIT_OK; } int KIGIT_COMMON::HandleSSHAgentAuthentication( git_cred** aOut, const wxString& aUsername ) { if( git_credential_ssh_key_from_agent( aOut, aUsername.mbc_str() ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to create SSH agent credential for %s: %s", aUsername, KIGIT_COMMON::GetLastGitError() ); return GIT_PASSTHROUGH; } m_testedTypes |= KIGIT_CREDENTIAL_SSH_AGENT; return GIT_OK; } extern "C" int fetchhead_foreach_cb( const char*, const char*, const git_oid* aOID, unsigned int aIsMerge, void* aPayload ) { if( aIsMerge ) git_oid_cpy( (git_oid*) aPayload, aOID ); return 0; } extern "C" void clone_progress_cb( const char* aStr, size_t aLen, size_t aTotal, void* aPayload ) { KIGIT_REPO_MIXIN* parent = reinterpret_cast( aPayload ); wxString progressMessage( aStr ); parent->UpdateProgress( aLen, aTotal, progressMessage ); } extern "C" int progress_cb( const char* str, int len, void* aPayload ) { KIGIT_REPO_MIXIN* parent = reinterpret_cast( aPayload ); wxString progressMessage( str, len ); parent->UpdateProgress( 0, 0, progressMessage ); return 0; } extern "C" int transfer_progress_cb( const git_transfer_progress* aStats, void* aPayload ) { KIGIT_REPO_MIXIN* parent = reinterpret_cast( aPayload ); wxString progressMessage = wxString::Format( _( "Received %u of %u objects" ), aStats->received_objects, aStats->total_objects ); parent->UpdateProgress( aStats->received_objects, aStats->total_objects, progressMessage ); return 0; } extern "C" int update_cb( const char* aRefname, const git_oid* aFirst, const git_oid* aSecond, void* aPayload ) { constexpr int cstring_len = 8; char a_str[cstring_len + 1]; char b_str[cstring_len + 1]; KIGIT_REPO_MIXIN* parent = reinterpret_cast( aPayload ); wxString status; git_oid_tostr( b_str, cstring_len, aSecond ); #if ( LIBGIT2_VER_MAJOR >= 1 ) || ( LIBGIT2_VER_MINOR >= 99 ) if( !git_oid_is_zero( aFirst ) ) #else if( !git_oid_iszero( aFirst ) ) #endif { git_oid_tostr( a_str, cstring_len, aFirst ); status = wxString::Format( _( "* [updated] %s..%s %s" ), a_str, b_str, aRefname ); } else { status = wxString::Format( _( "* [new] %s %s" ), b_str, aRefname ); } parent->UpdateProgress( 0, 0, status ); return 0; } extern "C" int push_transfer_progress_cb( unsigned int aCurrent, unsigned int aTotal, size_t aBytes, void* aPayload ) { long long progress = 100; KIGIT_REPO_MIXIN* parent = reinterpret_cast( aPayload ); if( aTotal != 0 ) { progress = ( aCurrent * 100ll ) / aTotal; } wxString progressMessage = wxString::Format( _( "Writing objects: %lld%% (%u/%u), %zu bytes" ), progress, aCurrent, aTotal, aBytes ); parent->UpdateProgress( aCurrent, aTotal, progressMessage ); return 0; } extern "C" int push_update_reference_cb( const char* aRefname, const char* aStatus, void* aPayload ) { KIGIT_REPO_MIXIN* parent = reinterpret_cast( aPayload ); wxString status( aStatus ); if( !status.IsEmpty() ) { wxString statusMessage = wxString::Format( _( "* [rejected] %s (%s)" ), aRefname, aStatus ); parent->UpdateProgress( 0, 0, statusMessage ); } else { wxString statusMessage = wxString::Format( _( "[updated] %s" ), aRefname ); parent->UpdateProgress( 0, 0, statusMessage ); } return 0; } extern "C" int credentials_cb( git_cred** aOut, const char* aUrl, const char* aUsername, unsigned int aAllowedTypes, void* aPayload ) { KIGIT_REPO_MIXIN* parent = reinterpret_cast( aPayload ); KIGIT_COMMON* common = parent->GetCommon(); wxLogTrace( traceGit, "Credentials callback for %s, testing %d", aUrl, aAllowedTypes ); if( parent->GetConnType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_LOCAL ) { wxLogTrace( traceGit, "Local repository, no credentials needed" ); return GIT_PASSTHROUGH; } if( aAllowedTypes & GIT_CREDENTIAL_USERNAME && !( parent->TestedTypes() & GIT_CREDENTIAL_USERNAME ) ) { wxString username = parent->GetUsername().Trim().Trim( false ); wxLogTrace( traceGit, "Username credential for %s at %s with allowed type %d", username, aUrl, aAllowedTypes ); if( git_credential_username_new( aOut, username.ToStdString().c_str() ) != GIT_OK ) { wxLogTrace( traceGit, "Failed to create username credential for %s: %s", username, KIGIT_COMMON::GetLastGitError() ); } else { wxLogTrace( traceGit, "Created username credential for %s", username ); } parent->TestedTypes() |= GIT_CREDENTIAL_USERNAME; } else if( parent->GetConnType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_HTTPS && ( aAllowedTypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT ) && !( parent->TestedTypes() & GIT_CREDENTIAL_USERPASS_PLAINTEXT ) ) { // Plaintext authentication wxLogTrace( traceGit, "Plaintext authentication for %s at %s with allowed type %d", parent->GetUsername(), aUrl, aAllowedTypes ); return common->HandlePlaintextAuthentication( aOut, parent->GetUsername() ); } else if( parent->GetConnType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_SSH && ( aAllowedTypes & GIT_CREDENTIAL_SSH_KEY ) && !( parent->TestedTypes() & GIT_CREDENTIAL_SSH_KEY ) ) { // SSH key authentication return common->HandleSSHKeyAuthentication( aOut, parent->GetUsername() ); } else { // If we didn't find anything to try, then we don't have a callback set that the // server likes if( !parent->TestedTypes() ) return GIT_PASSTHROUGH; git_error_clear(); git_error_set_str( GIT_ERROR_NET, _( "Unable to authenticate" ).mbc_str() ); // Otherwise, we did try something but we failed, so return an authentication error return GIT_EAUTH; } return GIT_OK; };