34#include <wx/filename.h> 
   36#include <wx/textfile.h> 
   74    wxCHECK( 
m_repo, wxEmptyString );
 
   75    git_reference* head = 
nullptr;
 
   77    int retval = git_repository_head( &head, 
m_repo );
 
   79    if( retval && retval != GIT_EUNBORNBRANCH && retval != GIT_ENOTFOUND )
 
   83    git_reference*         branch;
 
   85    if( git_reference_resolve( &branch, head ) )
 
   87        wxLogTrace( 
traceGit, 
"Failed to resolve branch" );
 
   92    const char*            branchName = 
"";
 
   94    if( git_branch_name( &branchName, branch ) )
 
   96        wxLogTrace( 
traceGit, 
"Failed to get branch name" );
 
  100    return wxString( branchName );
 
 
  128    std::vector<wxString> branchNames;
 
  129    std::map<git_time_t, wxString> branchNamesMap;
 
  132    git_branch_iterator* branchIterator = 
nullptr;
 
  134    if( git_branch_iterator_new( &branchIterator, 
m_repo, GIT_BRANCH_LOCAL ) )
 
  136        wxLogTrace( 
traceGit, 
"Failed to get branch iterator" );
 
  141    git_reference*              branchReference = 
nullptr;
 
  142    git_branch_t                branchType;
 
  144    while( git_branch_next( &branchReference, &branchType, branchIterator ) != GIT_ITEROVER )
 
  146        const char* branchName = 
"";
 
  149        if( git_branch_name( &branchName, branchReference ) )
 
  151            wxLogTrace( 
traceGit, 
"Failed to get branch name in iter loop" );
 
  155        const git_oid* commitId = git_reference_target( branchReference );
 
  157        git_commit* commit = 
nullptr;
 
  159        if( git_commit_lookup( &commit, 
m_repo, commitId ) )
 
  161            wxLogTrace( 
traceGit, 
"Failed to get commit in iter loop" );
 
  166        git_time_t          commitTime = git_commit_time( commit );
 
  168        if( git_branch_is_head( branchReference ) )
 
  169            firstName = branchName;
 
  171            branchNamesMap.emplace( commitTime, branchName );
 
  175    if( !firstName.IsEmpty() )
 
  176        branchNames.push_back( firstName );
 
  179    for( 
auto rit = branchNamesMap.rbegin(); rit != branchNamesMap.rend(); ++rit )
 
  180        branchNames.push_back( rit->second );
 
 
  189    std::vector<wxString> projDirs;
 
  195    if( git_reference_name_to_id( &oid, 
m_repo, 
"HEAD" ) != GIT_OK )
 
  201    if( git_commit_lookup( &commit, 
m_repo, &oid ) != GIT_OK )
 
  209    if( git_commit_tree( &tree, commit ) != GIT_OK )
 
  219            tree, GIT_TREEWALK_PRE,
 
  220            []( 
const char* root, 
const git_tree_entry* entry, 
void* payload )
 
  222                std::vector<wxString>* prjs = 
static_cast<std::vector<wxString>*
>( payload );
 
  223                wxFileName             root_fn( git_tree_entry_name( entry ) );
 
  225                root_fn.SetPath( root );
 
  227                if( git_tree_entry_type( entry ) == GIT_OBJECT_BLOB
 
  228                    && ( ( root_fn.GetExt() == 
"kicad_pro" ) || ( root_fn.GetExt() == 
"pro" ) ) )
 
  230                    prjs->push_back( root_fn.GetFullPath() );
 
  237    std::sort( projDirs.begin(), projDirs.end(),
 
  238               []( 
const wxString& a, 
const wxString& b )
 
  240                    int a_freq = a.Freq( wxFileName::GetPathSeparator() );
 
  241                    int b_freq = b.Freq( wxFileName::GetPathSeparator() );
 
  243                    if( a_freq == b_freq )
 
  246                        return a_freq < b_freq;
 
 
  256    auto get_modified_files = [&]( 
const git_oid* from_oid, 
const git_oid* to_oid ) -> std::set<wxString>
 
  258        std::set<wxString> modified_set;
 
  259        git_revwalk* walker = 
nullptr;
 
  261        if( !
m_repo || !from_oid )
 
  264        if( git_revwalk_new( &walker, 
m_repo ) != GIT_OK )
 
  272        if( git_revwalk_push( walker, from_oid ) != GIT_OK )
 
  278        if( to_oid && git_revwalk_hide( walker, to_oid ) != GIT_OK )
 
  287        while( git_revwalk_next( &oid, walker ) == GIT_OK )
 
  289            if( git_commit_lookup( &commit, 
m_repo, &oid ) != GIT_OK )
 
  296            git_tree *tree, *parent_tree = 
nullptr;
 
  298            if( git_commit_tree( &tree, commit ) != GIT_OK )
 
  307            if( !git_commit_parentcount( commit ) )
 
  310                    tree, GIT_TREEWALK_PRE,
 
  311                    []( 
const char* root, 
const git_tree_entry* entry, 
void* payload )
 
  313                        std::set<wxString>* modified_set_internal = 
static_cast<std::set<wxString>*
>( payload );
 
  314                        wxString filePath = wxString::Format( 
"%s%s", root, git_tree_entry_name( entry ) );
 
  315                        modified_set_internal->insert( std::move( filePath ) );
 
  324            if( git_commit_parent( &parent, commit, 0 ) != GIT_OK )
 
  333            if( git_commit_tree( &parent_tree, parent ) != GIT_OK )
 
  343            git_diff_options diff_opts;
 
  344            git_diff_init_options( &diff_opts, GIT_DIFF_OPTIONS_VERSION );
 
  346            if( !
m_repo || !parent_tree || !tree )
 
  349            if( git_diff_tree_to_tree( &diff, 
m_repo, parent_tree, tree, &diff_opts ) == GIT_OK )
 
  351                size_t num_deltas = git_diff_num_deltas( diff );
 
  353                for( 
size_t i = 0; i < num_deltas; ++i )
 
  355                    const git_diff_delta* 
delta = git_diff_get_delta( diff, i );
 
  356                    modified_set.insert( 
delta->new_file.path );
 
  359                git_diff_free( diff );
 
  372    std::pair<std::set<wxString>,std::set<wxString>> modified_files;
 
  375        return modified_files;
 
  377    git_reference* head = 
nullptr;
 
  378    git_reference* remote_head = 
nullptr;
 
  380    if( git_repository_head( &head, 
m_repo ) != GIT_OK )
 
  382        wxLogTrace( 
traceGit, 
"Failed to get modified HEAD" );
 
  383        return modified_files;
 
  388    if( git_branch_upstream( &remote_head, head ) != GIT_OK )
 
  390        wxLogTrace( 
traceGit, 
"Failed to get modified remote HEAD" );
 
  395    if( remote_head != 
nullptr && head != 
nullptr )
 
  397        const git_oid*         head_oid = git_reference_target( head );
 
  398        const git_oid*         remote_oid = git_reference_target( remote_head );
 
  400        modified_files.first = get_modified_files( head_oid, remote_oid );
 
  401        modified_files.second = get_modified_files( remote_oid, head_oid );
 
  404    return modified_files;
 
 
  413    git_reference* head = 
nullptr;
 
  414    git_reference* remote_head = 
nullptr;
 
  416    if( git_repository_head( &head, 
m_repo ) != GIT_OK )
 
  424    if( git_branch_upstream( &remote_head, head ) != GIT_OK )
 
  432    const git_oid*         head_oid = git_reference_target( head );
 
  433    const git_oid*         remote_oid = git_reference_target( remote_head );
 
  434    git_revwalk*           walker = 
nullptr;
 
  436    if( git_revwalk_new( &walker, 
m_repo ) != GIT_OK )
 
  444    if( !head_oid || git_revwalk_push( walker, head_oid ) != GIT_OK )
 
  450    if( remote_oid && git_revwalk_hide( walker, remote_oid ) != GIT_OK )
 
  459    if( git_revwalk_next( &oid, walker ) != GIT_OK )
 
 
  472    git_remote* remote = 
nullptr;
 
  474    if( git_remote_lookup( &remote, 
m_repo, 
"origin" ) != GIT_OK )
 
  476        wxLogTrace( 
traceGit, 
"Failed to get remote for haspushpull" );
 
  483    const char* fetch_url = git_remote_url( remote );
 
  484    const char* push_url = git_remote_pushurl( remote );
 
  489        wxLogTrace( 
traceGit, 
"No push URL set, using fetch URL" );
 
  490        push_url = fetch_url;
 
  493    return fetch_url && push_url;
 
 
  499    wxCHECK( 
m_repo, wxEmptyString );
 
  502    git_reference* head = 
nullptr;
 
  503    git_reference* upstream = 
nullptr;
 
  505    if( git_repository_head( &head, 
m_repo ) != GIT_OK )
 
  513    if( git_branch_upstream( &upstream, head ) != GIT_OK )
 
  516        git_strarray remotes = { 
nullptr, 0 };
 
  518        if( git_remote_list( &remotes, 
m_repo ) == GIT_OK )
 
  520            if( remotes.count == 1 )
 
  521                retval = remotes.strings[0];
 
  523            git_strarray_dispose( &remotes );
 
  532            git_remote* remote = 
nullptr;
 
  534            if( git_remote_lookup( &remote, 
m_repo, 
"origin" ) == GIT_OK )
 
  536                retval = git_remote_name( remote );
 
  537                git_remote_free( remote );
 
  541                wxLogTrace( 
traceGit, 
"Failed to get remote name from default remote: %s",
 
  550    git_buf     remote_name = GIT_BUF_INIT_CONST( 
nullptr, 0 );
 
  552    if( git_branch_remote_name( &remote_name, 
m_repo, git_reference_name( upstream ) ) == GIT_OK )
 
  554        retval = remote_name.ptr;
 
  555        git_buf_dispose( &remote_name );
 
  560                    "Failed to get remote name from upstream branch: %s",
 
 
  581        return wxEmptyString;
 
  583    const char *
path = git_repository_path( 
m_repo );
 
  584    wxString    retval = 
path;
 
 
  593    wxFileName keyFile( wxGetHomeDir(), wxEmptyString );
 
  594    keyFile.AppendDir( 
".ssh" );
 
  595    keyFile.SetFullName( 
"id_rsa" );
 
  597    if( keyFile.FileExists() )
 
  600    keyFile.SetFullName( 
"id_dsa" );
 
  602    if( keyFile.FileExists() )
 
  605    keyFile.SetFullName( 
"id_ecdsa" );
 
  607    if( keyFile.FileExists() )
 
  610    keyFile.SetFullName( 
"id_ed25519" );
 
  612    if( keyFile.FileExists() )
 
  616    wxFileName sshConfig( wxGetHomeDir(), wxEmptyString );
 
  617    sshConfig.AppendDir( 
".ssh" );
 
  618    sshConfig.SetFullName( 
"config" );
 
  620    if( sshConfig.FileExists() )
 
  622        wxTextFile configFile( sshConfig.GetFullPath() );
 
  627        for( wxString line = configFile.GetFirstLine(); !configFile.Eof(); line = configFile.GetNextLine() )
 
  629            line.Trim( 
false ).Trim( 
true );
 
  631            if( line.StartsWith( 
"Host " ) )
 
  636            if( line.StartsWith( 
"Host" ) && line.Contains( 
m_hostname ) )
 
  639            if( match && line.StartsWith( 
"IdentityFile" ) )
 
  641                wxString keyPath = line.AfterFirst( 
' ' ).Trim( 
false ).Trim( 
true );
 
  644                if( keyPath.StartsWith( 
"~" ) )
 
  645                    keyPath.Replace( 
"~", wxGetHomeDir(), 
false );
 
  648                if( wxFileName::FileExists( keyPath ) )
 
 
  666    git_remote* remote = 
nullptr;
 
  672    if( git_remote_lookup( &remote, 
m_repo, remote_name.ToStdString().c_str() ) == GIT_OK )
 
  674        const char* url = git_remote_url( remote );
 
  679        git_remote_free( remote );
 
 
  690    if( remote.IsEmpty() )
 
  693    if( remote.StartsWith( 
"https://" ) || remote.StartsWith( 
"http://" ) )
 
  697    else if( remote.StartsWith( 
"ssh://" ) || remote.StartsWith( 
"git@" ) || remote.StartsWith( 
"git+ssh://" )
 
  698             || remote.EndsWith( 
".git" ) )
 
 
  718        size_t atPos = uri.find( 
'@' );
 
  720        if( atPos != wxString::npos )
 
  722            size_t protoEnd = uri.find( 
"//" );
 
  724            if( protoEnd != wxString::npos )
 
  726                wxString credentials = uri.Mid( protoEnd + 2, atPos - protoEnd - 2 );
 
  727                size_t colonPos = credentials.find( 
':' );
 
  729                if( colonPos != wxString::npos )
 
  732                    m_password = credentials.Mid( colonPos + 1, credentials.Length() - colonPos - 1 );
 
  748            size_t colonPos = 
m_remote.find( 
':' );
 
  749            if( colonPos != wxString::npos )
 
  755            size_t hostStart = 
m_remote.find( 
"://" ) + 2;
 
  756            size_t hostEnd = 
m_remote.find( 
'/', hostStart );
 
  759            if( hostEnd != wxString::npos )
 
  760                host = 
m_remote.Mid( hostStart, hostEnd - hostStart );
 
  764            atPos = host.find( 
'@' );
 
  766            if( atPos != wxString::npos )
 
 
  785    if( sshKey.IsEmpty() )
 
  787        wxLogTrace( 
traceGit, 
"Finished testing all possible ssh keys" );
 
  789        return GIT_PASSTHROUGH;
 
  792    wxString sshPubKey = sshKey + 
".pub";
 
  795    wxLogTrace( 
traceGit, 
"Testing %s\n", sshKey );
 
  797    if( git_credential_ssh_key_new( aOut, aUsername.mbc_str(), sshPubKey.mbc_str(), sshKey.mbc_str(),
 
  798                                password.mbc_str() ) != GIT_OK )
 
  800        wxLogTrace( 
traceGit, 
"Failed to create SSH key credential for %s: %s",
 
  802        return GIT_PASSTHROUGH;
 
 
  813    git_credential_userpass_plaintext_new( aOut, aUsername.mbc_str(), password.mbc_str() );
 
 
  821    if( git_credential_ssh_key_from_agent( aOut, aUsername.mbc_str() ) != GIT_OK )
 
  823        wxLogTrace( 
traceGit, 
"Failed to create SSH agent credential for %s: %s",
 
  825        return GIT_PASSTHROUGH;
 
 
  834                                     const git_oid* aOID, 
unsigned int aIsMerge, 
void* aPayload )
 
  837        git_oid_cpy( (git_oid*) aPayload, aOID );
 
 
  843extern "C" void clone_progress_cb( 
const char* aStr, 
size_t aLen, 
size_t aTotal, 
void* aPayload )
 
  847    wxString progressMessage( aStr );
 
 
  852extern "C" int progress_cb( 
const char* str, 
int len, 
void* aPayload )
 
  858        wxLogTrace( 
traceGit, 
"Progress CB cancelled" );
 
  862    wxString progressMessage( str, len );
 
 
  873    wxString progressMessage = wxString::Format( 
_( 
"Received %u of %u objects" ),
 
  874                                                 aStats->received_objects,
 
  875                                                 aStats->total_objects );
 
  878        wxLogTrace( 
traceGit, 
"Transfer progress cancelled" );
 
  882    parent->
UpdateProgress( aStats->received_objects, aStats->total_objects, progressMessage );
 
 
  888extern "C" int update_cb( 
const char* aRefname, 
const git_oid* aFirst, 
const git_oid* aSecond,
 
  891    constexpr int cstring_len = 8;
 
  892    char          a_str[cstring_len + 1];
 
  893    char          b_str[cstring_len + 1];
 
  898    git_oid_tostr( b_str, cstring_len, aSecond );
 
  900#if ( LIBGIT2_VER_MAJOR >= 1 ) || ( LIBGIT2_VER_MINOR >= 99 ) 
  901    if( !git_oid_is_zero( aFirst ) )
 
  903    if( !git_oid_iszero( aFirst ) )
 
  906        git_oid_tostr( a_str, cstring_len, aFirst );
 
  907        status = wxString::Format( 
_( 
"* [updated] %s..%s %s" ), a_str, b_str, aRefname );
 
  911        status = wxString::Format( 
_( 
"* [new] %s %s" ), b_str, aRefname );
 
 
  923    long long         progress = 100;
 
  928        progress = ( aCurrent * 100ll ) / aTotal;
 
  931    wxString progressMessage = wxString::Format( 
_( 
"Writing objects: %lld%% (%u/%u), %zu bytes" ),
 
  932                                                 progress, aCurrent, aTotal, aBytes );
 
 
  942    wxString          status( aStatus );
 
  944    if( !status.IsEmpty() )
 
  946        wxString statusMessage = wxString::Format( 
_( 
"* [rejected] %s (%s)" ), aRefname, aStatus );
 
  951        wxString statusMessage = wxString::Format( 
_( 
"[updated] %s" ), aRefname );
 
 
  959extern "C" int credentials_cb( git_cred** aOut, 
const char* aUrl, 
const char* aUsername,
 
  960                               unsigned int aAllowedTypes, 
void* aPayload )
 
  965    wxLogTrace( 
traceGit, 
"Credentials callback for %s, testing %d", aUrl, aAllowedTypes );
 
  969        wxLogTrace( 
traceGit, 
"Local repository, no credentials needed" );
 
  970        return GIT_PASSTHROUGH;
 
  973    if( aAllowedTypes & GIT_CREDENTIAL_USERNAME
 
  974        && !( parent->
TestedTypes() & GIT_CREDENTIAL_USERNAME ) )
 
  976        wxString username = parent->
GetUsername().Trim().Trim( 
false );
 
  977        wxLogTrace( 
traceGit, 
"Username credential for %s at %s with allowed type %d",
 
  978                    username, aUrl, aAllowedTypes );
 
  979        if( git_credential_username_new( aOut, username.ToStdString().c_str() ) != GIT_OK )
 
  981            wxLogTrace( 
traceGit, 
"Failed to create username credential for %s: %s",
 
  986            wxLogTrace( 
traceGit, 
"Created username credential for %s", username );
 
  992                && ( aAllowedTypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT )
 
  993                && !( parent->
TestedTypes() & GIT_CREDENTIAL_USERPASS_PLAINTEXT ) )
 
  996        wxLogTrace( 
traceGit, 
"Plaintext authentication for %s at %s with allowed type %d",
 
 1001                && ( aAllowedTypes & GIT_CREDENTIAL_SSH_KEY )
 
 1002                && !( parent->
TestedTypes() & GIT_CREDENTIAL_SSH_KEY ) )
 
 1012            return GIT_PASSTHROUGH;
 
 1015        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 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.