From 676dd4ceec4dc3d3e0c921c93147561f03915683 Mon Sep 17 00:00:00 2001
From: Seth Hillbrand <seth@kipro-pcb.com>
Date: Sat, 15 Mar 2025 13:05:06 -0700
Subject: [PATCH] Fix inheritance for git_common

Avoids repo type pointer issues when casting from void
---
 common/dialogs/git/dialog_git_repository.cpp |   4 +-
 common/dialogs/git/panel_git_repos.cpp       |   6 +-
 common/git/git_clone_handler.cpp             |  16 +-
 common/git/git_clone_handler.h               |   9 +-
 common/git/git_pull_handler.cpp              |   4 +-
 common/git/git_pull_handler.h                |   4 +-
 common/git/git_push_handler.cpp              |   4 +-
 common/git/git_push_handler.h                |   2 +-
 common/git/git_repo_mixin.h                  |  15 +-
 common/git/kicad_git_common.cpp              | 165 ++++++++++++++-----
 common/git/kicad_git_common.h                |  22 ++-
 kicad/project_tree_pane.cpp                  |  64 +++----
 kicad/tools/kicad_manager_control.cpp        |   8 +-
 pcbnew/git/kigit_pcb_merge.cpp               |   7 +-
 14 files changed, 208 insertions(+), 122 deletions(-)

diff --git a/common/dialogs/git/dialog_git_repository.cpp b/common/dialogs/git/dialog_git_repository.cpp
index 8ae7f0206f..b55ef2193c 100644
--- a/common/dialogs/git/dialog_git_repository.cpp
+++ b/common/dialogs/git/dialog_git_repository.cpp
@@ -289,7 +289,7 @@ void DIALOG_GIT_REPOSITORY::OnTestClick( wxCommandEvent& event )
     common.SetPassword( m_txtPassword->GetValue() );
     common.SetUsername( m_txtUsername->GetValue() );
     common.SetSSHKey( m_fpSSHKey->GetFileName().GetFullPath() );
-    common.SetConnType( static_cast<KIGIT_COMMON::GIT_CONN_TYPE>( m_ConnType->GetSelection() ) );
+
 
     wxString txtURL = m_txtURL->GetValue();
     git_remote_create_with_fetchspec( &remote, m_repository, "origin", txtURL.mbc_str(),
@@ -299,7 +299,7 @@ void DIALOG_GIT_REPOSITORY::OnTestClick( wxCommandEvent& event )
     if( git_remote_connect( remote, GIT_DIRECTION_FETCH, &callbacks, nullptr, nullptr ) == GIT_OK )
         success = true;
     else
-        error = git_error_last()->message;
+        error = KIGIT_COMMON::GetLastGitError();
 
     git_remote_disconnect( remote );
 
diff --git a/common/dialogs/git/panel_git_repos.cpp b/common/dialogs/git/panel_git_repos.cpp
index 774e8852af..fbdd57c35f 100644
--- a/common/dialogs/git/panel_git_repos.cpp
+++ b/common/dialogs/git/panel_git_repos.cpp
@@ -69,7 +69,7 @@ static std::pair<wxString, wxString> getDefaultAuthorAndEmail()
 
     if( git_config_open_default( &config ) != 0 )
     {
-        wxLogTrace( traceGit, "Failed to open default Git config: %s", giterr_last()->message );
+        wxLogTrace( traceGit, "Failed to open default Git config: %s", KIGIT_COMMON::GetLastGitError() );
         return std::make_pair( name, email );
     }
 
@@ -77,7 +77,7 @@ static std::pair<wxString, wxString> getDefaultAuthorAndEmail()
 
     if( git_config_get_entry( &name_c, config, "user.name" ) != 0 )
     {
-        wxLogTrace( traceGit, "Failed to get user.name from Git config: %s", giterr_last()->message );
+        wxLogTrace( traceGit, "Failed to get user.name from Git config: %s", KIGIT_COMMON::GetLastGitError() );
         return std::make_pair( name, email );
     }
 
@@ -85,7 +85,7 @@ static std::pair<wxString, wxString> getDefaultAuthorAndEmail()
 
     if( git_config_get_entry( &email_c, config, "user.email" ) != 0 )
     {
-        wxLogTrace( traceGit, "Failed to get user.email from Git config: %s", giterr_last()->message );
+        wxLogTrace( traceGit, "Failed to get user.email from Git config: %s", KIGIT_COMMON::GetLastGitError() );
         return std::make_pair( name, email );
     }
 
diff --git a/common/git/git_clone_handler.cpp b/common/git/git_clone_handler.cpp
index 5a46a549dc..32819e4456 100644
--- a/common/git/git_clone_handler.cpp
+++ b/common/git/git_clone_handler.cpp
@@ -31,20 +31,17 @@
 #include <wx/filename.h>
 #include <wx/log.h>
 
-GIT_CLONE_HANDLER::GIT_CLONE_HANDLER() :  KIGIT_COMMON( nullptr )
+GIT_CLONE_HANDLER::GIT_CLONE_HANDLER( KIGIT_COMMON* aCommon ) :  KIGIT_REPO_MIXIN( aCommon )
 {}
 
 
 GIT_CLONE_HANDLER::~GIT_CLONE_HANDLER()
-{
-    if( m_repo )
-        git_repository_free( m_repo );
-}
+{}
 
 
 bool GIT_CLONE_HANDLER::PerformClone()
 {
-    std::unique_lock<std::mutex> lock( m_gitActionMutex, std::try_to_lock );
+    std::unique_lock<std::mutex> lock( GetCommon()->m_gitActionMutex, std::try_to_lock );
 
     if( !lock.owns_lock() )
     {
@@ -73,16 +70,19 @@ bool GIT_CLONE_HANDLER::PerformClone()
     cloneOptions.fetch_opts.callbacks.credentials = credentials_cb;
     cloneOptions.fetch_opts.callbacks.payload = this;
 
-    m_testedTypes = 0;
+    TestedTypes() = 0;
     ResetNextKey();
+    git_repository* newRepo = nullptr;
 
-    if( git_clone( &m_repo, m_URL.ToStdString().c_str(), m_clonePath.ToStdString().c_str(),
+    if( git_clone( &newRepo, m_URL.ToStdString().c_str(), m_clonePath.ToStdString().c_str(),
                    &cloneOptions ) != 0 )
     {
         AddErrorString( wxString::Format( _( "Could not clone repository '%s'" ), m_URL ) );
         return false;
     }
 
+    GetCommon()->SetRepo( newRepo );
+
     if( m_progressReporter )
         m_progressReporter->Hide();
 
diff --git a/common/git/git_clone_handler.h b/common/git/git_clone_handler.h
index 10092b91a9..9bf85fca3e 100644
--- a/common/git/git_clone_handler.h
+++ b/common/git/git_clone_handler.h
@@ -24,13 +24,14 @@
 #ifndef GIT_CLONE_HANDLER_H_
 #define GIT_CLONE_HANDLER_H_
 
-#include <git/kicad_git_common.h>
-#include <git/git_progress.h>
+#include "kicad_git_common.h"
+#include "git_repo_mixin.h"
+#include "git_progress.h"
 
-class GIT_CLONE_HANDLER : public KIGIT_COMMON, public GIT_PROGRESS
+class GIT_CLONE_HANDLER : public KIGIT_REPO_MIXIN
 {
 public:
-    GIT_CLONE_HANDLER();
+    GIT_CLONE_HANDLER( KIGIT_COMMON* aCommon );
     ~GIT_CLONE_HANDLER();
 
     bool PerformClone();
diff --git a/common/git/git_pull_handler.cpp b/common/git/git_pull_handler.cpp
index 9522b96243..adf95fc2a5 100644
--- a/common/git/git_pull_handler.cpp
+++ b/common/git/git_pull_handler.cpp
@@ -79,7 +79,7 @@ bool GIT_PULL_HANDLER::PerformFetch( bool aSkipLock )
     if( git_remote_connect( remote, GIT_DIRECTION_FETCH, &remoteCallbacks, nullptr, nullptr ) )
     {
         AddErrorString( wxString::Format( _( "Could not connect to remote '%s': %s" ), "origin",
-                                          git_error_last()->message ) );
+                                          KIGIT_COMMON::GetLastGitError() ) );
         return false;
     }
 
@@ -90,7 +90,7 @@ bool GIT_PULL_HANDLER::PerformFetch( bool aSkipLock )
     if( git_remote_fetch( remote, nullptr, &fetchOptions, nullptr ) )
     {
         AddErrorString( wxString::Format( _( "Could not fetch data from remote '%s': %s" ),
-                                          "origin", git_error_last()->message ) );
+                                          "origin", KIGIT_COMMON::GetLastGitError() ) );
         return false;
     }
 
diff --git a/common/git/git_pull_handler.h b/common/git/git_pull_handler.h
index ab7f0f29b5..91861f78aa 100644
--- a/common/git/git_pull_handler.h
+++ b/common/git/git_pull_handler.h
@@ -25,8 +25,6 @@
 #define _GIT_PULL_HANDLER_H_
 
 #include <git/git_repo_mixin.h>
-#include <git/git_progress.h>
-#include <git/kicad_git_errors.h>
 
 #include <vector>
 #include <string>
@@ -65,7 +63,7 @@ struct ConflictData
 };
 
 
-class GIT_PULL_HANDLER : public KIGIT_ERRORS, public GIT_PROGRESS, public KIGIT_REPO_MIXIN
+class GIT_PULL_HANDLER : public KIGIT_REPO_MIXIN
 {
 public:
     GIT_PULL_HANDLER( KIGIT_COMMON* aCommon );
diff --git a/common/git/git_push_handler.cpp b/common/git/git_push_handler.cpp
index 8687830370..6f521bbbf1 100644
--- a/common/git/git_push_handler.cpp
+++ b/common/git/git_push_handler.cpp
@@ -74,7 +74,7 @@ PushResult GIT_PUSH_HANDLER::PerformPush()
     if( git_remote_connect( remote, GIT_DIRECTION_PUSH, &remoteCallbacks, nullptr, nullptr ) )
     {
         AddErrorString( wxString::Format( _( "Could not connect to remote: %s" ),
-                                          git_error_last()->message ) );
+                                          KIGIT_COMMON::GetLastGitError() ) );
         return PushResult::Error;
     }
 
@@ -102,7 +102,7 @@ PushResult GIT_PUSH_HANDLER::PerformPush()
     if( git_remote_push( remote, &refspecs, &pushOptions ) )
     {
         AddErrorString( wxString::Format( _( "Could not push to remote: %s" ),
-                                          git_error_last()->message ) );
+                                          KIGIT_COMMON::GetLastGitError() ) );
         git_remote_disconnect( remote );
         return PushResult::Error;
     }
diff --git a/common/git/git_push_handler.h b/common/git/git_push_handler.h
index d0d8951b80..d540ba2845 100644
--- a/common/git/git_push_handler.h
+++ b/common/git/git_push_handler.h
@@ -37,7 +37,7 @@ enum class PushResult
     Error
 };
 
-class GIT_PUSH_HANDLER : public KIGIT_ERRORS, public GIT_PROGRESS, public KIGIT_REPO_MIXIN
+class GIT_PUSH_HANDLER : public KIGIT_REPO_MIXIN
 {
 public:
     GIT_PUSH_HANDLER( KIGIT_COMMON* aCommon );
diff --git a/common/git/git_repo_mixin.h b/common/git/git_repo_mixin.h
index 7a08dea3e3..f86d841ac6 100644
--- a/common/git/git_repo_mixin.h
+++ b/common/git/git_repo_mixin.h
@@ -15,12 +15,16 @@
 #define GIT_REPO_MIXIN_H
 
 #include "kicad_git_common.h"
+#include "kicad_git_errors.h"
+#include "git_progress.h"
 
-class KIGIT_REPO_MIXIN
+class KIGIT_REPO_MIXIN: public KIGIT_ERRORS, public GIT_PROGRESS
 {
 public:
     KIGIT_REPO_MIXIN( KIGIT_COMMON* aCommon ) : m_common( aCommon )
     {
+        // Ensure m_common is initialized
+        wxASSERT( aCommon != nullptr );
     }
 
     virtual ~KIGIT_REPO_MIXIN()
@@ -124,15 +128,6 @@ public:
         return m_common->GetPassword();
     }
 
-    /**
-     * @brief Set the connection type
-     * @param aConnType The connection type
-     */
-    void SetConnType( KIGIT_COMMON::GIT_CONN_TYPE aConnType )
-    {
-        m_common->SetConnType( aConnType );
-    }
-
     /**
      * @brief Set the username
      * @param aUsername The username
diff --git a/common/git/kicad_git_common.cpp b/common/git/kicad_git_common.cpp
index 2c354361cb..4fb4138997 100644
--- a/common/git/kicad_git_common.cpp
+++ b/common/git/kicad_git_common.cpp
@@ -23,6 +23,7 @@
 
 #include "kicad_git_common.h"
 #include "kicad_git_memory.h"
+#include "git_repo_mixin.h"
 
 #include <git/git_progress.h>
 #include <kiplatform/secrets.h>
@@ -36,12 +37,12 @@
 #include <vector>
 
 KIGIT_COMMON::KIGIT_COMMON( git_repository* aRepo ) :
-        m_repo( aRepo ), m_connType( GIT_CONN_TYPE::GIT_CONN_LOCAL ), m_testedTypes( 0 )
+        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
-        KIGIT_ERRORS(),
         m_repo( aOther.m_repo ),
         m_connType( aOther.m_connType ),
         m_remote( aOther.m_remote ),
@@ -171,13 +172,13 @@ std::vector<wxString> KIGIT_COMMON::GetProjectDirs()
 
     if( git_reference_name_to_id( &oid, m_repo, "HEAD" ) != GIT_OK )
     {
-        wxLogTrace( traceGit, "An error occurred: %s", git_error_last()->message );
+        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", git_error_last()->message );
+        wxLogTrace( traceGit, "An error occurred: %s", KIGIT_COMMON::GetLastGitError() );
         return projDirs;
     }
 
@@ -185,7 +186,7 @@ std::vector<wxString> KIGIT_COMMON::GetProjectDirs()
 
     if( git_commit_tree( &tree, commit ) != GIT_OK )
     {
-        wxLogTrace( traceGit, "An error occurred: %s", git_error_last()->message );
+        wxLogTrace( traceGit, "An error occurred: %s", KIGIT_COMMON::GetLastGitError() );
         return projDirs;
     }
 
@@ -230,29 +231,33 @@ std::vector<wxString> KIGIT_COMMON::GetProjectDirs()
 
 std::pair<std::set<wxString>,std::set<wxString>> KIGIT_COMMON::GetDifferentFiles() const
 {
-    auto get_modified_files = [&]( git_oid* from_oid, git_oid* to_oid ) -> std::set<wxString>
+    auto get_modified_files = [&]( const git_oid* from_oid, const git_oid* to_oid ) -> std::set<wxString>
     {
         std::set<wxString> modified_set;
         git_revwalk* walker = nullptr;
 
-        if( !m_repo )
+        if( !m_repo || !from_oid )
             return modified_set;
 
         if( git_revwalk_new( &walker, m_repo ) != GIT_OK )
         {
-            wxLogTrace( traceGit, "Failed to create revwalker" );
+            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 )
-            || ( git_revwalk_hide( walker, to_oid ) != GIT_OK ) )
+        if( git_revwalk_push( walker, from_oid ) != GIT_OK )
         {
-            wxLogTrace( traceGit, "Failed to push/hide commits" );
+            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;
 
@@ -261,7 +266,7 @@ std::pair<std::set<wxString>,std::set<wxString>> KIGIT_COMMON::GetDifferentFiles
         {
             if( git_commit_lookup( &commit, m_repo, &oid ) != GIT_OK )
             {
-                wxLogTrace( traceGit, "Failed to lookup commit" );
+                wxLogTrace( traceGit, "Failed to lookup commit: %s", KIGIT_COMMON::GetLastGitError() );
                 continue;
             }
 
@@ -270,7 +275,7 @@ std::pair<std::set<wxString>,std::set<wxString>> KIGIT_COMMON::GetDifferentFiles
 
             if( git_commit_tree( &tree, commit ) != GIT_OK )
             {
-                wxLogTrace( traceGit, "Failed to get commit tree" );
+                wxLogTrace( traceGit, "Failed to get commit tree: %s", KIGIT_COMMON::GetLastGitError() );
                 continue;
             }
 
@@ -279,7 +284,16 @@ std::pair<std::set<wxString>,std::set<wxString>> KIGIT_COMMON::GetDifferentFiles
             // get parent commit tree to diff against
             if( !git_commit_parentcount( commit ) )
             {
-                wxLogTrace( traceGit, "No parent commit" );
+                git_tree_walk(
+                    tree, GIT_TREEWALK_PRE,
+                    []( const char* root, const git_tree_entry* entry, void* payload )
+                    {
+                        std::set<wxString>* modified_set_internal = static_cast<std::set<wxString>*>( 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;
             }
 
@@ -287,7 +301,7 @@ std::pair<std::set<wxString>,std::set<wxString>> KIGIT_COMMON::GetDifferentFiles
 
             if( git_commit_parent( &parent, commit, 0 ) != GIT_OK )
             {
-                wxLogTrace( traceGit, "Failed to get parent commit" );
+                wxLogTrace( traceGit, "Failed to get parent commit: %s", KIGIT_COMMON::GetLastGitError() );
                 continue;
             }
 
@@ -296,7 +310,7 @@ std::pair<std::set<wxString>,std::set<wxString>> KIGIT_COMMON::GetDifferentFiles
 
             if( git_commit_tree( &parent_tree, parent ) != GIT_OK )
             {
-                wxLogTrace( traceGit, "Failed to get parent commit tree" );
+                wxLogTrace( traceGit, "Failed to get parent commit tree: %s", KIGIT_COMMON::GetLastGitError() );
                 continue;
             }
 
@@ -322,8 +336,14 @@ std::pair<std::set<wxString>,std::set<wxString>> KIGIT_COMMON::GetDifferentFiles
 
                 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;
     };
 
@@ -346,15 +366,14 @@ std::pair<std::set<wxString>,std::set<wxString>> KIGIT_COMMON::GetDifferentFiles
     if( git_branch_upstream( &remote_head, head ) != GIT_OK )
     {
         wxLogTrace( traceGit, "Failed to get modified remote HEAD" );
-        return modified_files;
     }
 
     KIGIT::GitReferencePtr remoteHeadPtr( remote_head );
-    git_oid                head_oid = *git_reference_target( head );
-    git_oid                remote_oid = *git_reference_target( remote_head );
+    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 );
+    modified_files.first = get_modified_files( head_oid, remote_oid );
+    modified_files.second = get_modified_files( remote_oid, head_oid );
 
     return modified_files;
 }
@@ -370,7 +389,7 @@ bool KIGIT_COMMON::HasLocalCommits() const
 
     if( git_repository_head( &head, m_repo ) != GIT_OK )
     {
-        wxLogTrace( traceGit, "Failed to get HEAD" );
+        wxLogTrace( traceGit, "Failed to get HEAD: %s", KIGIT_COMMON::GetLastGitError() );
         return false;
     }
 
@@ -378,27 +397,33 @@ bool KIGIT_COMMON::HasLocalCommits() const
 
     if( git_branch_upstream( &remote_head, head ) != GIT_OK )
     {
-        wxLogTrace( traceGit, "Failed to get remote HEAD" );
-        return false;
+        // 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 );
-    git_oid                head_oid = *git_reference_target( head );
-    git_oid                remote_oid = *git_reference_target( 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" );
+        wxLogTrace( traceGit, "Failed to create revwalker: %s", KIGIT_COMMON::GetLastGitError() );
         return false;
     }
 
     KIGIT::GitRevWalkPtr walkerPtr( walker );
 
-    if( ( git_revwalk_push( walker, &head_oid ) != GIT_OK )
-        || ( git_revwalk_hide( walker, &remote_oid ) != GIT_OK ) )
+    if( !head_oid || git_revwalk_push( walker, head_oid ) != GIT_OK )
     {
-        wxLogTrace( traceGit, "Failed to push/hide commits" );
+        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;
     }
 
@@ -407,7 +432,7 @@ bool KIGIT_COMMON::HasLocalCommits() const
     // 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" );
+        wxLogTrace( traceGit, "Failed to walk to next commit: %s", KIGIT_COMMON::GetLastGitError() );
         return false;
     }
 
@@ -453,23 +478,61 @@ wxString KIGIT_COMMON::GetRemotename() const
 
     if( git_repository_head( &head, m_repo ) != GIT_OK )
     {
-        wxLogTrace( traceGit, "Failed to get remote name" );
+        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 )
+    if( git_branch_upstream( &upstream, head ) != GIT_OK )
     {
-        git_buf     remote_name = GIT_BUF_INIT_CONST( nullptr, 0 );
+        wxLogTrace( traceGit, "Failed to get upstream branch: %s", KIGIT_COMMON::GetLastGitError() );
+        git_strarray remotes = { nullptr, 0 };
 
-        if( git_branch_remote_name( &remote_name, m_repo, git_reference_name( upstream ) ) == GIT_OK )
+        if( git_remote_list( &remotes, m_repo ) == GIT_OK )
         {
-            retval = remote_name.ptr;
-            git_buf_dispose( &remote_name );
+            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() );
+            }
         }
 
-        git_reference_free( upstream );
+        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;
@@ -593,6 +656,21 @@ void KIGIT_COMMON::UpdateCurrentBranchInfo()
     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://" ) )
+        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://" ) )
@@ -683,7 +761,7 @@ int KIGIT_COMMON::HandleSSHKeyAuthentication( git_cred** aOut, const wxString& a
                                 password.mbc_str() ) != GIT_OK )
     {
         wxLogTrace( traceGit, "Failed to create SSH key credential for %s: %s",
-                    aUsername, git_error_last()->message );
+                    aUsername, KIGIT_COMMON::GetLastGitError() );
         return GIT_ERROR;
     }
 
@@ -706,7 +784,7 @@ int KIGIT_COMMON::HandleSSHAgentAuthentication( git_cred** aOut, const wxString&
     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, git_error_last()->message );
+                    aUsername, KIGIT_COMMON::GetLastGitError() );
         return GIT_ERROR;
     }
 
@@ -832,7 +910,8 @@ extern "C" int push_update_reference_cb( const char* aRefname, const char* aStat
 extern "C" int credentials_cb( git_cred** aOut, const char* aUrl, const char* aUsername,
                                unsigned int aAllowedTypes, void* aPayload )
 {
-    KIGIT_COMMON* parent = static_cast<KIGIT_COMMON*>( aPayload );
+    KIGIT_REPO_MIXIN* parent = reinterpret_cast<KIGIT_REPO_MIXIN*>( aPayload );
+    KIGIT_COMMON* common = parent->GetCommon();
 
     if( parent->GetConnType() == KIGIT_COMMON::GIT_CONN_TYPE::GIT_CONN_LOCAL )
         return GIT_PASSTHROUGH;
@@ -849,14 +928,14 @@ extern "C" int credentials_cb( git_cred** aOut, const char* aUrl, const char* aU
                 && !( parent->TestedTypes() & GIT_CREDENTIAL_USERPASS_PLAINTEXT ) )
     {
         // Plaintext authentication
-        return parent->HandlePlaintextAuthentication( aOut, aUsername );
+        return common->HandlePlaintextAuthentication( aOut, aUsername );
     }
     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 parent->HandleSSHKeyAuthentication( aOut, aUsername );
+        return common->HandleSSHKeyAuthentication( aOut, aUsername );
     }
     else
     {
diff --git a/common/git/kicad_git_common.h b/common/git/kicad_git_common.h
index f53c838f50..3dc64b6793 100644
--- a/common/git/kicad_git_common.h
+++ b/common/git/kicad_git_common.h
@@ -32,7 +32,7 @@
 
 #include <wx/string.h>
 
-class KIGIT_COMMON : public KIGIT_ERRORS
+class KIGIT_COMMON
 {
 
 public:
@@ -90,19 +90,12 @@ public:
 
     wxString GetUsername() const { return m_username; }
     wxString GetPassword() const { return m_password; }
-    GIT_CONN_TYPE GetConnType() const { return m_connType; }
+    GIT_CONN_TYPE GetConnType() const;
 
     void SetUsername( const wxString& aUsername ) { m_username = aUsername; }
     void SetPassword( const wxString& aPassword ) { m_password = aPassword; }
     void SetSSHKey( const wxString& aSSHKey );
 
-    void SetConnType( GIT_CONN_TYPE aConnType ) { m_connType = aConnType; }
-    void SetConnType( unsigned aConnType )
-    {
-        if( aConnType < static_cast<unsigned>( GIT_CONN_TYPE::GIT_CONN_LAST ) )
-            m_connType = static_cast<GIT_CONN_TYPE>( aConnType );
-    }
-
     // Holds a temporary variable that can be used by the authentication callback
     // to remember which types of authentication have been tested so that we
     // don't loop forever.
@@ -137,6 +130,16 @@ public:
 
     int HandleSSHAgentAuthentication( git_cred** aOut, const wxString& aUsername );
 
+    static wxString GetLastGitError()
+    {
+        const git_error* error = git_error_last();
+
+        if( error == nullptr )
+            return wxString( "No error" );
+
+        return wxString( error->message );
+    }
+
 protected:
     git_repository* m_repo;
 
@@ -153,6 +156,7 @@ protected:
     // Make git handlers friends so they can access the mutex
     friend class GIT_PUSH_HANDLER;
     friend class GIT_PULL_HANDLER;
+    friend class GIT_CLONE_HANDLER;
 
 private:
     void updatePublicKeys();
diff --git a/kicad/project_tree_pane.cpp b/kicad/project_tree_pane.cpp
index bf929af302..41b286f4fd 100644
--- a/kicad/project_tree_pane.cpp
+++ b/kicad/project_tree_pane.cpp
@@ -392,7 +392,7 @@ static git_repository* get_git_repository_for_file( const char* filename )
     // Find the repository path for the given file
     if( git_repository_discover( &repo_path, filename, 0, NULL ) != GIT_OK )
     {
-        wxLogTrace( traceGit, "Can't repo discover %s: %s", filename, git_error_last()->message );
+        wxLogTrace( traceGit, "Can't repo discover %s: %s", filename, KIGIT_COMMON::GetLastGitError() );
         return nullptr;
     }
 
@@ -400,7 +400,7 @@ static git_repository* get_git_repository_for_file( const char* filename )
 
     if( git_repository_open( &repo, repo_path.ptr ) != GIT_OK )
     {
-        wxLogTrace( traceGit, "Can't open repo for %s: %s", repo_path.ptr, git_error_last()->message );
+        wxLogTrace( traceGit, "Can't open repo for %s: %s", repo_path.ptr, KIGIT_COMMON::GetLastGitError() );
         return nullptr;
     }
 
@@ -758,7 +758,7 @@ bool PROJECT_TREE_PANE::hasChangedFiles()
 
     if( git_status_list_new( &status_list, repo, &opts ) != GIT_OK )
     {
-        wxLogError( _( "Failed to get status list: %s" ), git_error_last()->message );
+        wxLogError( _( "Failed to get status list: %s" ), KIGIT_COMMON::GetLastGitError() );
         return false;
     }
 
@@ -1661,7 +1661,7 @@ void PROJECT_TREE_PANE::onGitInitializeProject( wxCommandEvent& aEvent )
         {
             m_gitLastError = git_error_last()->klass;
             DisplayErrorMessage( m_parent, _( "Failed to initialize Git project." ),
-                                    git_error_last()->message );
+                                    KIGIT_COMMON::GetLastGitError() );
         }
 
         return;
@@ -1673,8 +1673,6 @@ void PROJECT_TREE_PANE::onGitInitializeProject( wxCommandEvent& aEvent )
     }
 
     //Set up the git remote
-
-    m_TreeProject->GitCommon()->SetConnType( dlg.GetRepoType() );
     m_TreeProject->GitCommon()->SetPassword( dlg.GetPassword() );
     m_TreeProject->GitCommon()->SetUsername( dlg.GetUsername() );
     m_TreeProject->GitCommon()->SetSSHKey( dlg.GetRepoSSHPath() );
@@ -1721,7 +1719,7 @@ void PROJECT_TREE_PANE::onGitInitializeProject( wxCommandEvent& aEvent )
         {
             m_gitLastError = git_error_last()->klass;
             DisplayErrorMessage( m_parent, _( "Failed to set default remote." ),
-                                 git_error_last()->message );
+                                 KIGIT_COMMON::GetLastGitError() );
         }
 
         return;
@@ -1775,6 +1773,8 @@ void PROJECT_TREE_PANE::onGitPullProject( wxCommandEvent& aEvent )
 
         DisplayErrorMessage( m_parent, _( "Failed to pull project" ), errorMessage );
     }
+
+    m_gitStatusTimer.Start( 500, wxTIMER_ONE_SHOT );
 }
 
 
@@ -1797,6 +1797,8 @@ void PROJECT_TREE_PANE::onGitPushProject( wxCommandEvent& aEvent )
 
         DisplayErrorMessage( m_parent, _( "Failed to push project" ), errorMessage );
     }
+
+    m_gitStatusTimer.Start( 500, wxTIMER_ONE_SHOT );
 }
 
 
@@ -1806,7 +1808,7 @@ static int git_create_branch( git_repository* aRepo, wxString& aBranchName )
 
     if( int error = git_reference_name_to_id( &head_oid, aRepo, "HEAD" ) != GIT_OK )
     {
-        wxLogTrace( traceGit, "Failed to lookup HEAD reference: %s", giterr_last()->message );
+        wxLogTrace( traceGit, "Failed to lookup HEAD reference: %s", KIGIT_COMMON::GetLastGitError() );
         return error;
     }
 
@@ -1815,7 +1817,7 @@ static int git_create_branch( git_repository* aRepo, wxString& aBranchName )
 
     if( int error = git_commit_lookup( &commit, aRepo, &head_oid ) != GIT_OK )
     {
-        wxLogTrace( traceGit, "Failed to lookup commit: %s", giterr_last()->message );
+        wxLogTrace( traceGit, "Failed to lookup commit: %s", KIGIT_COMMON::GetLastGitError() );
         return error;
     }
 
@@ -1824,7 +1826,7 @@ static int git_create_branch( git_repository* aRepo, wxString& aBranchName )
 
     if( int error = git_branch_create( &branchRef, aRepo, aBranchName.mb_str(), commit, 0 ) != GIT_OK )
     {
-        wxLogTrace( traceGit, "Failed to create branch: %s", giterr_last()->message );
+        wxLogTrace( traceGit, "Failed to create branch: %s", KIGIT_COMMON::GetLastGitError() );
         return error;
     }
 
@@ -1873,7 +1875,7 @@ void PROJECT_TREE_PANE::onGitSwitchBranch( wxCommandEvent& aEvent )
         git_reference_dwim( &branchRef, repo, branchName.mb_str() ) != GIT_OK )
     {
         wxString errorMessage = wxString::Format( _( "Failed to lookup branch '%s': %s" ),
-                                                  branchName, giterr_last()->message );
+                                                  branchName, KIGIT_COMMON::GetLastGitError() );
         DisplayError( m_parent, errorMessage );
         return;
     }
@@ -2112,7 +2114,7 @@ void PROJECT_TREE_PANE::updateGitStatusIconMap()
     if( git_repository_index( &index, repo ) != GIT_OK )
     {
         m_gitLastError = giterr_last()->klass;
-        wxLogTrace( traceGit, wxS( "Failed to get git index: %s" ), giterr_last()->message );
+        wxLogTrace( traceGit, wxS( "Failed to get git index: %s" ), KIGIT_COMMON::GetLastGitError() );
         return;
     }
 
@@ -2121,7 +2123,7 @@ void PROJECT_TREE_PANE::updateGitStatusIconMap()
 
     if( git_status_list_new( &status_list, repo, &status_options ) != GIT_OK )
     {
-        wxLogTrace( traceGit, wxS( "Failed to get git status list: %s" ), giterr_last()->message );
+        wxLogTrace( traceGit, wxS( "Failed to get git status list: %s" ), KIGIT_COMMON::GetLastGitError() );
         return;
     }
 
@@ -2191,22 +2193,22 @@ void PROJECT_TREE_PANE::updateGitStatusIconMap()
         else if( localChanges.count( path ) )
         {
             auto [it, inserted] = m_gitStatusIcons.try_emplace( iter->second,
-                                        KIGIT_COMMON::GIT_STATUS::GIT_STATUS_MODIFIED );
+                                        KIGIT_COMMON::GIT_STATUS::GIT_STATUS_AHEAD );
 
-            if( inserted || it->second != KIGIT_COMMON::GIT_STATUS::GIT_STATUS_MODIFIED )
+            if( inserted || it->second != KIGIT_COMMON::GIT_STATUS::GIT_STATUS_AHEAD )
                 updated = true;
 
-            it->second = KIGIT_COMMON::GIT_STATUS::GIT_STATUS_MODIFIED;
+            it->second = KIGIT_COMMON::GIT_STATUS::GIT_STATUS_AHEAD;
         }
         else if( remoteChanges.count( path ) )
         {
             auto [it, inserted] = m_gitStatusIcons.try_emplace( iter->second,
-                                        KIGIT_COMMON::GIT_STATUS::GIT_STATUS_MODIFIED );
+                                        KIGIT_COMMON::GIT_STATUS::GIT_STATUS_BEHIND );
 
-            if( inserted || it->second != KIGIT_COMMON::GIT_STATUS::GIT_STATUS_MODIFIED )
+            if( inserted || it->second != KIGIT_COMMON::GIT_STATUS::GIT_STATUS_BEHIND )
                 updated = true;
 
-            it->second = KIGIT_COMMON::GIT_STATUS::GIT_STATUS_MODIFIED;
+            it->second = KIGIT_COMMON::GIT_STATUS::GIT_STATUS_BEHIND;
         }
         else
         {
@@ -2237,7 +2239,7 @@ void PROJECT_TREE_PANE::updateGitStatusIconMap()
     else
     {
         if( giterr_last()->klass != m_gitLastError )
-            wxLogTrace( "git", "Failed to lookup current branch: %s", giterr_last()->message );
+            wxLogTrace( "git", "Failed to lookup current branch: %s", KIGIT_COMMON::GetLastGitError() );
 
         m_gitLastError = giterr_last()->klass;
     }
@@ -2420,7 +2422,7 @@ void PROJECT_TREE_PANE::onGitCommit( wxCommandEvent& aEvent )
         if( git_repository_index( &index, repo ) != 0 )
         {
             wxLogTrace( traceGit, wxString::Format( _( "Failed to get repository index: %s" ),
-                                            giterr_last()->message ) );
+                                            KIGIT_COMMON::GetLastGitError() ) );
             return;
         }
 
@@ -2431,7 +2433,7 @@ void PROJECT_TREE_PANE::onGitCommit( wxCommandEvent& aEvent )
             if( git_index_add_bypath( index, file.mb_str() ) != 0 )
             {
                 wxMessageBox( wxString::Format( _( "Failed to add file to index: %s" ),
-                                                giterr_last()->message ) );
+                                                KIGIT_COMMON::GetLastGitError() ) );
                 return;
             }
         }
@@ -2439,21 +2441,21 @@ void PROJECT_TREE_PANE::onGitCommit( wxCommandEvent& aEvent )
         if( git_index_write( index ) != 0 )
         {
             wxLogTrace( traceGit, wxString::Format( _( "Failed to write index: %s" ),
-                                            giterr_last()->message ) );
+                                            KIGIT_COMMON::GetLastGitError() ) );
             return;
         }
 
         if( git_index_write_tree( &tree_id, index ) != 0)
         {
             wxLogTrace( traceGit, wxString::Format( _( "Failed to write tree: %s" ),
-                                            giterr_last()->message ) );
+                                            KIGIT_COMMON::GetLastGitError() ) );
             return;
         }
 
         if( git_tree_lookup( &tree, repo, &tree_id ) != 0 )
         {
             wxLogTrace( traceGit, wxString::Format( _( "Failed to lookup tree: %s" ),
-                                            giterr_last()->message ) );
+                                            KIGIT_COMMON::GetLastGitError() ) );
             return;
         }
 
@@ -2465,7 +2467,7 @@ void PROJECT_TREE_PANE::onGitCommit( wxCommandEvent& aEvent )
             if( git_repository_head( &headRef, repo ) != 0 )
             {
                 wxLogTrace( traceGit, wxString::Format( _( "Failed to get HEAD reference: %s" ),
-                                                giterr_last()->message ) );
+                                                KIGIT_COMMON::GetLastGitError() ) );
                 return;
             }
 
@@ -2474,7 +2476,7 @@ void PROJECT_TREE_PANE::onGitCommit( wxCommandEvent& aEvent )
             if( git_reference_peel( (git_object**) &parent, headRef, GIT_OBJECT_COMMIT ) != 0 )
             {
                 wxLogTrace( traceGit, wxString::Format( _( "Failed to get commit: %s" ),
-                                                giterr_last()->message ) );
+                                                KIGIT_COMMON::GetLastGitError() ) );
                 return;
             }
         }
@@ -2489,7 +2491,7 @@ void PROJECT_TREE_PANE::onGitCommit( wxCommandEvent& aEvent )
         if( git_signature_now( &author, author_name.mb_str(), author_email.mb_str() ) != 0 )
         {
             wxLogTrace( traceGit, wxString::Format( _( "Failed to create author signature: %s" ),
-                                            giterr_last()->message ) );
+                                            KIGIT_COMMON::GetLastGitError() ) );
             return;
         }
 
@@ -2526,7 +2528,7 @@ void PROJECT_TREE_PANE::onGitCommit( wxCommandEvent& aEvent )
                            1, parents ) != 0 )
         {
             wxMessageBox( wxString::Format( _( "Failed to create commit: %s" ),
-                                            giterr_last()->message ) );
+                                            KIGIT_COMMON::GetLastGitError() ) );
             return;
         }
     }
@@ -2551,7 +2553,7 @@ bool PROJECT_TREE_PANE::canFileBeAddedToVCS( const wxString& aFile )
 
     if( git_repository_index( &index, repo ) != 0 )
     {
-        wxLogTrace( traceGit, "Failed to get git index: %s", giterr_last()->message );
+        wxLogTrace( traceGit, "Failed to get git index: %s", KIGIT_COMMON::GetLastGitError() );
         return false;
     }
 
@@ -2593,6 +2595,8 @@ void PROJECT_TREE_PANE::onGitFetch( wxCommandEvent& aEvent )
 
     GIT_PULL_HANDLER handler( gitCommon );
     handler.PerformFetch();
+
+    m_gitStatusTimer.Start( 500, wxTIMER_ONE_SHOT );
 }
 
 
diff --git a/kicad/tools/kicad_manager_control.cpp b/kicad/tools/kicad_manager_control.cpp
index b45bee9fcc..dd78372575 100644
--- a/kicad/tools/kicad_manager_control.cpp
+++ b/kicad/tools/kicad_manager_control.cpp
@@ -49,6 +49,8 @@
 #include <design_block_lib_table.h>
 #include "dialog_pcm.h"
 #include <project/project_archiver.h>
+#include <project_tree_pane.h>
+#include <project_tree.h>
 #include <launch_ext.h>
 
 #include "widgets/filedlg_new_project.h"
@@ -163,11 +165,13 @@ int KICAD_MANAGER_CONTROL::NewFromRepository( const TOOL_EVENT& aEvent )
     if( !pro.IsOk() )
         return -1;
 
-    GIT_CLONE_HANDLER cloneHandler;
+    PROJECT_TREE_PANE *pane = static_cast<PROJECT_TREE_PANE*>( m_frame->GetToolCanvas() );
+
+
+    GIT_CLONE_HANDLER cloneHandler( pane->m_TreeProject->GitCommon() );
 
     cloneHandler.SetURL( dlg.GetRepoURL() );
     cloneHandler.SetClonePath( pro.GetPath() );
-    cloneHandler.SetConnType( dlg.GetRepoType() );
     cloneHandler.SetUsername( dlg.GetUsername() );
     cloneHandler.SetPassword( dlg.GetPassword() );
     cloneHandler.SetSSHKey( dlg.GetRepoSSHPath() );
diff --git a/pcbnew/git/kigit_pcb_merge.cpp b/pcbnew/git/kigit_pcb_merge.cpp
index 766be3b6ec..bffb7db102 100644
--- a/pcbnew/git/kigit_pcb_merge.cpp
+++ b/pcbnew/git/kigit_pcb_merge.cpp
@@ -23,6 +23,7 @@
 
 #include "kigit_pcb_merge.h"
 #include <git/kicad_git_blob_reader.h>
+#include <git/kicad_git_common.h>
 #include <git/kicad_git_memory.h>
 
 #include <board.h>
@@ -95,7 +96,7 @@ int KIGIT_PCB_MERGE::Merge()
 
     if( git_blob_lookup( &ancestor_blob, repo, &ancestor->id ) != 0 )
     {
-        wxLogTrace( traceGit, "Could not find ancestor blob: %s", git_error_last()->message );
+        wxLogTrace( traceGit, "Could not find ancestor blob: %s", KIGIT_COMMON::GetLastGitError() );
         return GIT_ENOTFOUND;
     }
 
@@ -103,7 +104,7 @@ int KIGIT_PCB_MERGE::Merge()
 
     if( git_blob_lookup( &ours_blob, repo, &ours->id ) != 0 )
     {
-        wxLogTrace( traceGit, "Could not find ours blob: %s", git_error_last()->message );
+        wxLogTrace( traceGit, "Could not find ours blob: %s", KIGIT_COMMON::GetLastGitError() );
         return GIT_ENOTFOUND;
     }
 
@@ -111,7 +112,7 @@ int KIGIT_PCB_MERGE::Merge()
 
     if( git_blob_lookup( &theirs_blob, repo, &theirs->id ) != 0 )
     {
-        wxLogTrace( traceGit, "Could not find theirs blob: %s", git_error_last()->message );
+        wxLogTrace( traceGit, "Could not find theirs blob: %s", KIGIT_COMMON::GetLastGitError() );
         return GIT_ENOTFOUND;
     }