diff --git a/common/project/project_local_settings.cpp b/common/project/project_local_settings.cpp index 8dbfb837c5..ffc81d6348 100644 --- a/common/project/project_local_settings.cpp +++ b/common/project/project_local_settings.cpp @@ -45,6 +45,7 @@ PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( PROJECT* aProject, const wxStrin m_ShapeOpacity( 1.0 ), m_ImageOpacity( 0.6 ), m_PcbSelectionFilter(), + m_GitIntegrationDisabled( false ), m_project( aProject ), m_wasMigrated( false ) { @@ -207,6 +208,8 @@ PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( PROJECT* aProject, const wxStrin m_params.emplace_back( new PARAM( "git.ssh_key", &m_GitSSHKey, "" ) ); + m_params.emplace_back( new PARAM( "git.integration_disabled", &m_GitIntegrationDisabled, false ) ); + m_params.emplace_back( new PARAM( "net_inspector_panel.filter_text", &m_NetInspectorPanel.filter_text, "" ) ); m_params.emplace_back( new PARAM( "net_inspector_panel.filter_by_net_name", diff --git a/include/project/project_local_settings.h b/include/project/project_local_settings.h index ec50f43a77..a614b97d4a 100644 --- a/include/project/project_local_settings.h +++ b/include/project/project_local_settings.h @@ -153,6 +153,9 @@ public: wxString m_GitRepoType; wxString m_GitSSHKey; + /// If true, KiCad will not use Git integration for this project even if a .git directory exists + bool m_GitIntegrationDisabled; + private: /// A link to the owning project PROJECT* m_project; diff --git a/kicad/project_tree_pane.cpp b/kicad/project_tree_pane.cpp index 99a81d1c9b..a312372593 100644 --- a/kicad/project_tree_pane.cpp +++ b/kicad/project_tree_pane.cpp @@ -663,8 +663,9 @@ void PROJECT_TREE_PANE::ReCreateTreePrj() bool prjOpened = fn.FileExists(); - // Bind the git repository to the project tree (if it exists) - if( Pgm().GetCommonSettings()->m_Git.enableGit ) + // Bind the git repository to the project tree (if it exists and not disabled for this project) + if( Pgm().GetCommonSettings()->m_Git.enableGit + && !Prj().GetLocalSettings().m_GitIntegrationDisabled ) { m_TreeProject->SetGitRepo( get_git_repository_for_file( fn.GetPath().c_str() ) ); @@ -801,7 +802,9 @@ void PROJECT_TREE_PANE::onRight( wxTreeEvent& Event ) bool vcs_has_repo = m_TreeProject->GetGitRepo() != nullptr; bool vcs_can_commit = hasChangedFiles(); bool vcs_can_init = !vcs_has_repo; - bool vcs_can_remove = vcs_has_repo && git_name.StartsWith( prj_name ); // This means the .git is a subdirectory of the project + bool gitIntegrationDisabled = Prj().GetLocalSettings().m_GitIntegrationDisabled; + // Allow toggling if: repo exists in project, OR integration is disabled (to re-enable it) + bool vcs_can_remove = ( vcs_has_repo && git_name.StartsWith( prj_name ) ) || gitIntegrationDisabled; bool vcs_can_fetch = vcs_has_repo && git->HasPushAndPullRemote(); bool vcs_can_push = vcs_can_fetch && git->HasLocalCommits(); bool vcs_can_pull = vcs_can_fetch; @@ -1050,8 +1053,17 @@ void PROJECT_TREE_PANE::onRight( wxTreeEvent& Event ) vcs_submenu->AppendSeparator(); - vcs_menuitem = vcs_submenu->Append( ID_GIT_REMOVE_VCS, _( "Remove Version Control" ), - _( "Delete all version control files from the project directory." ) ); + if( gitIntegrationDisabled ) + { + vcs_menuitem = vcs_submenu->Append( ID_GIT_REMOVE_VCS, _( "Enable Git Integration" ), + _( "Re-enable Git integration for this project" ) ); + } + else + { + vcs_menuitem = vcs_submenu->Append( ID_GIT_REMOVE_VCS, _( "Disable Git Integration" ), + _( "Disable Git integration for this project" ) ); + } + vcs_menuitem->Enable( vcs_can_remove ); popup_menu.AppendSeparator(); @@ -1931,51 +1943,61 @@ void PROJECT_TREE_PANE::onGitSwitchBranch( wxCommandEvent& aEvent ) void PROJECT_TREE_PANE::onGitRemoveVCS( wxCommandEvent& aEvent ) { - git_repository* repo = m_TreeProject->GetGitRepo(); + PROJECT_LOCAL_SETTINGS& localSettings = Prj().GetLocalSettings(); - if( !repo - || !IsOK( wxGetTopLevelParent( this ), - _( "Are you sure you want to remove Git tracking from this project?" ) ) ) + // Toggle the Git integration disabled preference + localSettings.m_GitIntegrationDisabled = !localSettings.m_GitIntegrationDisabled; + + wxLogTrace( traceGit, wxS( "onGitRemoveVCS: Git integration %s" ), + localSettings.m_GitIntegrationDisabled ? wxS( "disabled" ) : wxS( "enabled" ) ); + + if( localSettings.m_GitIntegrationDisabled ) { - return; - } + // Disabling Git integration - clear the repo reference and item states + m_TreeProject->SetGitRepo( nullptr ); + m_gitIconsInitialized = false; - // Remove the VCS (git) from the project directory - git_repository_free( repo ); - m_TreeProject->SetGitRepo( nullptr ); + // Clear all item states to remove git status icons + std::stack items; + items.push( m_TreeProject->GetRootItem() ); - // Remove the .git directory - wxFileName fn( m_Parent->GetProjectFileName() ); - fn.AppendDir( ".git" ); - - wxString errors; - - if( !RmDirRecursive( fn.GetPath(), &errors ) ) - { - DisplayErrorMessage( m_parent, _( "Failed to remove Git directory" ), errors ); - } - - // Clear all item states - std::stack items; - items.push( m_TreeProject->GetRootItem() ); - - while( !items.empty() ) - { - wxTreeItemId current = items.top(); - items.pop(); - - // Process the current item - m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE ); - - wxTreeItemIdValue cookie; - wxTreeItemId child = m_TreeProject->GetFirstChild( current, cookie ); - - while( child.IsOk() ) + while( !items.empty() ) { - items.push( child ); - child = m_TreeProject->GetNextChild( current, cookie ); + wxTreeItemId current = items.top(); + items.pop(); + + m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE ); + + wxTreeItemIdValue cookie; + wxTreeItemId child = m_TreeProject->GetFirstChild( current, cookie ); + + while( child.IsOk() ) + { + items.push( child ); + child = m_TreeProject->GetNextChild( current, cookie ); + } } } + else + { + // Re-enabling Git integration - try to find and connect to the repository + git_repository* repo = nullptr; + wxString dir = Prj().GetProjectPath(); + + if( git_repository_open( &repo, dir.mb_str() ) == 0 ) + m_TreeProject->SetGitRepo( repo ); + else + m_TreeProject->SetGitRepo( nullptr ); + + if( m_TreeProject->GetGitRepo() ) + { + m_TreeProject->GitCommon()->SetUsername( localSettings.m_GitRepoUsername ); + m_TreeProject->GitCommon()->SetSSHKey( localSettings.m_GitSSHKey ); + } + } + + // Save the preference to the project local settings file + localSettings.SaveToFile( Prj().GetProjectPath() ); } diff --git a/qa/tests/pcbnew/drc/test_drc_creepage_issue21482.cpp b/qa/tests/pcbnew/drc/test_drc_creepage_issue21482.cpp index fe3bf30ad3..75da25a6cb 100644 --- a/qa/tests/pcbnew/drc/test_drc_creepage_issue21482.cpp +++ b/qa/tests/pcbnew/drc/test_drc_creepage_issue21482.cpp @@ -140,10 +140,10 @@ BOOST_FIXTURE_TEST_CASE( CreepagePerformanceIssue21482, DRC_CREEPAGE_PERF_TEST_F // Clear the violation handler bds.m_DRCEngine->ClearViolationHandler(); - // Performance check: should complete in less than 15 seconds - // The original was more than 2 minutes. - BOOST_CHECK_MESSAGE( elapsedSeconds < 15.0, - wxString::Format( "Creepage DRC too slow: %.2f seconds (target: <15s)", + // Performance check: should complete in a reasonable time. + // The original was more than 2 minutes. Allow extra headroom for debug builds. + BOOST_CHECK_MESSAGE( elapsedSeconds < 60.0, + wxString::Format( "Creepage DRC too slow: %.2f seconds (target: <60s)", elapsedSeconds ) ); // Performance tier feedback