From a8ecaf2cc938416cf59011e6735f5c1cbf267c0f Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Mon, 26 Jan 2026 19:51:53 -0800 Subject: [PATCH] Git: Show error when pull fails due to unstaged changes When git pull fails due to unstaged changes in the working directory, show an informative error message instead of silently failing. Fixes https://gitlab.com/kicad/code/kicad/-/issues/20363 (cherry picked from commit 0b116673abd511bf2e266e568c230d5ffb286533) --- common/git/git_pull_handler.cpp | 50 +++++++++++++++++++++++++++++++++ common/git/git_pull_handler.h | 3 ++ 2 files changed, 53 insertions(+) diff --git a/common/git/git_pull_handler.cpp b/common/git/git_pull_handler.cpp index bedc30b0dd..fd0fcf21e2 100644 --- a/common/git/git_pull_handler.cpp +++ b/common/git/git_pull_handler.cpp @@ -378,6 +378,14 @@ PullResult GIT_PULL_HANDLER::handleFastForward() PullResult GIT_PULL_HANDLER::handleMerge( const git_annotated_commit** aMergeHeads, size_t aMergeHeadsCount ) { + if( hasUnstagedChanges( GetRepo() ) ) + { + AddErrorString( + _( "Cannot merge: you have unstaged changes. " + "Please commit or stash them before pulling." ) ); + return PullResult::DirtyWorkdir; + } + git_merge_options merge_opts; git_merge_options_init( &merge_opts, GIT_MERGE_OPTIONS_VERSION ); @@ -481,6 +489,14 @@ PullResult GIT_PULL_HANDLER::handleMerge( const git_annotated_commit** aMergeHea PullResult GIT_PULL_HANDLER::handleRebase( const git_annotated_commit** aMergeHeads, size_t aMergeHeadsCount ) { + if( hasUnstagedChanges( GetRepo() ) ) + { + AddErrorString( + _( "Cannot rebase: you have unstaged changes. " + "Please commit or stash them before pulling." ) ); + return PullResult::DirtyWorkdir; + } + // Get the current branch reference git_reference* head_ref = nullptr; @@ -563,6 +579,40 @@ PullResult GIT_PULL_HANDLER::handleRebase( const git_annotated_commit** aMergeHe } +bool GIT_PULL_HANDLER::hasUnstagedChanges( git_repository* aRepo ) +{ + if( !aRepo ) + return false; + + git_status_options opts; + git_status_init_options( &opts, GIT_STATUS_OPTIONS_VERSION ); + + opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED; + + git_status_list* status_list = nullptr; + + if( git_status_list_new( &status_list, aRepo, &opts ) != GIT_OK ) + { + wxLogTrace( traceGit, "Failed to get status list: %s", KIGIT_COMMON::GetLastGitError() ); + return false; + } + + KIGIT::GitStatusListPtr status_list_ptr( status_list ); + size_t count = git_status_list_entrycount( status_list ); + + for( size_t ii = 0; ii < count; ++ii ) + { + const git_status_entry* entry = git_status_byindex( status_list, ii ); + + if( entry->status & ( GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_DELETED | GIT_STATUS_WT_TYPECHANGE ) ) + return true; + } + + return false; +} + + void GIT_PULL_HANDLER::UpdateProgress( int aCurrent, int aTotal, const wxString& aMessage ) { diff --git a/common/git/git_pull_handler.h b/common/git/git_pull_handler.h index c553567c75..09174e8d8b 100644 --- a/common/git/git_pull_handler.h +++ b/common/git/git_pull_handler.h @@ -43,6 +43,7 @@ struct CommitDetails // Enum for result codes, error codes are negative, success codes are positive enum class PullResult : int { + DirtyWorkdir = -3, MergeFailed = -2, Error = -1, Success = 0, @@ -86,6 +87,8 @@ private: PullResult handleFastForward(); PullResult handleMerge( const git_annotated_commit** aMergeHeads, size_t aMergeHeadsCount ); PullResult handleRebase( const git_annotated_commit** aMergeHeads, size_t aMergeHeadsCount ); + + static bool hasUnstagedChanges( git_repository* aRepo ); }; #endif // _GIT_PULL_HANDLER_H_