@@ -18,7 +18,6 @@ import (
1818 "errors"
1919 "fmt"
2020 "hash/fnv"
21- "log"
2221 "net"
2322 "os"
2423 "os/exec"
@@ -1017,7 +1016,9 @@ func initSchemaOnDB(ctx context.Context, db *sql.DB) error {
10171016 _ , err = db .ExecContext (ctx , "ALTER TABLE dependencies DROP FOREIGN KEY fk_dep_depends_on" )
10181017 if err == nil {
10191018 // DDL change succeeded - commit it so it persists (required for Dolt server mode)
1020- _ , _ = db .ExecContext (ctx , "CALL DOLT_COMMIT('-Am', 'migration: remove fk_dep_depends_on for external references')" ) // Best effort: migration commit is advisory; schema change already applied
1019+ // GH#2455: Stage only the affected table, not all dirty tables.
1020+ _ , _ = db .ExecContext (ctx , "CALL DOLT_ADD('dependencies')" )
1021+ _ , _ = db .ExecContext (ctx , "CALL DOLT_COMMIT('-m', 'migration: remove fk_dep_depends_on for external references')" ) // Best effort: migration commit is advisory; schema change already applied
10211022 } else if ! strings .Contains (strings .ToLower (err .Error ()), "can't drop" ) &&
10221023 ! strings .Contains (strings .ToLower (err .Error ()), "doesn't exist" ) &&
10231024 ! strings .Contains (strings .ToLower (err .Error ()), "check that it exists" ) &&
@@ -1205,51 +1206,92 @@ func (s *DoltStore) commitAuthorString() string {
12051206
12061207// Commit creates a Dolt commit with the given message.
12071208//
1208- // Uses DOLT_COMMIT('-Am') to stage all dirty tables and commit. This is the
1209- // general-purpose commit used by maybeAutoCommit and callers that want to
1210- // commit all pending working set changes. Defense-in-depth for GH#2455:
1211- // snapshots issue_prefix before committing and restores it if corrupted
1212- // by stale working set changes from concurrent operations.
1209+ // GH#2455: Stages all dirty tables EXCEPT config, then commits with '-m'.
1210+ // The old '-Am' approach staged ALL dirty tables including config, which
1211+ // swept up stale issue_prefix changes from concurrent operations. By
1212+ // excluding config from automatic staging, we prevent the corruption.
1213+ //
1214+ // Callers that intentionally modify config (e.g., CommitPending after
1215+ // 'bd config set') must call CommitWithConfig instead.
12131216func (s * DoltStore ) Commit (ctx context.Context , message string ) (retErr error ) {
12141217 ctx , span := doltTracer .Start (ctx , "dolt.commit" ,
12151218 trace .WithSpanKind (trace .SpanKindClient ),
12161219 trace .WithAttributes (s .doltSpanAttrs ()... ),
12171220 )
12181221 defer func () { endSpan (span , retErr ) }()
12191222
1220- // Pin a single connection so snapshot, commit, and restore all run
1221- // on the same Dolt session. (GH#2455)
1223+ // Pin a single connection so all operations run on the same Dolt session.
12221224 conn , err := s .db .Conn (ctx )
12231225 if err != nil {
12241226 return fmt .Errorf ("failed to acquire connection: %w" , err )
12251227 }
12261228 defer conn .Close ()
12271229
1228- // Snapshot issue_prefix from HEAD before commit to detect corruption.
1229- var prefixBefore string
1230- _ = conn .QueryRowContext (ctx , "SELECT value FROM config WHERE `key` = 'issue_prefix'" ).Scan (& prefixBefore )
1230+ // GH#2455: Stage all dirty tables EXCEPT config. Query dolt_status for
1231+ // dirty tables and stage each one individually, skipping config to avoid
1232+ // sweeping up stale issue_prefix changes from concurrent operations.
1233+ rows , err := conn .QueryContext (ctx , "SELECT table_name FROM dolt_status" )
1234+ if err != nil {
1235+ // If dolt_status fails, fall back to nothing (rare edge case).
1236+ return fmt .Errorf ("failed to query dolt_status: %w" , err )
1237+ }
1238+ var tables []string
1239+ for rows .Next () {
1240+ var table string
1241+ if err := rows .Scan (& table ); err != nil {
1242+ _ = rows .Close ()
1243+ return fmt .Errorf ("failed to scan dolt_status: %w" , err )
1244+ }
1245+ if table != "config" {
1246+ tables = append (tables , table )
1247+ }
1248+ }
1249+ _ = rows .Close ()
1250+ if err := rows .Err (); err != nil {
1251+ return fmt .Errorf ("failed to iterate dolt_status: %w" , err )
1252+ }
1253+
1254+ if len (tables ) == 0 {
1255+ return nil // Nothing to commit (all changes were config-only or dolt_ignore'd)
1256+ }
1257+
1258+ for _ , table := range tables {
1259+ if _ , err := conn .ExecContext (ctx , "CALL DOLT_ADD(?)" , table ); err != nil {
1260+ // Best effort: some tables may be dolt_ignore'd (e.g., wisps).
1261+ // DOLT_ADD fails for ignored tables; skip silently.
1262+ continue
1263+ }
1264+ }
12311265
12321266 // NOTE: In SQL procedure mode, Dolt defaults author to the authenticated SQL user
12331267 // (e.g. root@localhost). Always pass an explicit author for deterministic history.
1234- if _ , err := conn .ExecContext (ctx , "CALL DOLT_COMMIT('-Am ', ?, '--author', ?)" , message , s .commitAuthorString ()); err != nil {
1268+ if _ , err := conn .ExecContext (ctx , "CALL DOLT_COMMIT('-m ', ?, '--author', ?)" , message , s .commitAuthorString ()); err != nil {
12351269 if isDoltNothingToCommit (err ) {
12361270 return nil
12371271 }
12381272 return fmt .Errorf ("failed to commit: %w" , err )
12391273 }
12401274
1241- // Verify issue_prefix wasn't corrupted by sweeping up stale working set changes.
1242- if prefixBefore != "" {
1243- var prefixAfter string
1244- _ = conn .QueryRowContext (ctx , "SELECT value FROM config WHERE `key` = 'issue_prefix'" ).Scan (& prefixAfter )
1245- if prefixAfter != prefixBefore {
1246- // Restore the correct prefix and create a fix-up commit.
1247- log .Printf ("WARNING: issue_prefix corrupted by DOLT_COMMIT (%q -> %q), restoring (GH#2455)" , prefixBefore , prefixAfter )
1248- _ , _ = conn .ExecContext (ctx , "UPDATE config SET value = ? WHERE `key` = 'issue_prefix'" , prefixBefore )
1249- _ , _ = conn .ExecContext (ctx , "CALL DOLT_COMMIT('-Am', 'fix: restore issue_prefix corrupted by stale working set (GH#2455)', '--author', ?)" , s .commitAuthorString ())
1250- }
1275+ return nil
1276+ }
1277+
1278+ // CommitWithConfig creates a Dolt commit that includes the config table.
1279+ // Use this instead of Commit when the caller intentionally modified config
1280+ // (e.g., CommitPending after 'bd config set', 'bd init', or 'bd rename-prefix').
1281+ // GH#2455: Commit() excludes config to prevent sweeping up stale changes.
1282+ func (s * DoltStore ) CommitWithConfig (ctx context.Context , message string ) error {
1283+ conn , err := s .db .Conn (ctx )
1284+ if err != nil {
1285+ return fmt .Errorf ("failed to acquire connection: %w" , err )
12511286 }
1287+ defer conn .Close ()
12521288
1289+ if _ , err := conn .ExecContext (ctx , "CALL DOLT_COMMIT('-Am', ?, '--author', ?)" , message , s .commitAuthorString ()); err != nil {
1290+ if isDoltNothingToCommit (err ) {
1291+ return nil
1292+ }
1293+ return fmt .Errorf ("failed to commit: %w" , err )
1294+ }
12531295 return nil
12541296}
12551297
@@ -1301,7 +1343,10 @@ func (s *DoltStore) CommitPending(ctx context.Context, actor string) (bool, erro
13011343 }
13021344
13031345 msg := s .buildBatchCommitMessage (ctx , actor )
1304- if err := s .Commit (ctx , msg ); err != nil {
1346+ // GH#2455: CommitPending is an explicit user action (bd dolt commit) that
1347+ // should include ALL pending changes, including config. Use CommitWithConfig
1348+ // instead of Commit to ensure intentional config changes are committed.
1349+ if err := s .CommitWithConfig (ctx , msg ); err != nil {
13051350 // Dolt may report "nothing to commit" even when Status() showed changes
13061351 // (e.g., system tables or schema-only diffs). Treat as no-op.
13071352 errLower := strings .ToLower (err .Error ())
@@ -1711,8 +1756,11 @@ func (s *DoltStore) tryAutoResolveMetadataConflicts(ctx context.Context, tx *sql
17111756 return false , fmt .Errorf ("failed to resolve metadata conflicts: %w" , err )
17121757 }
17131758
1714- // Commit the merge with resolved conflicts.
1715- if _ , err := tx .ExecContext (ctx , "CALL DOLT_COMMIT('-Am', 'auto-resolve metadata merge conflicts (GH#2466)')" ); err != nil {
1759+ // GH#2455: Stage only metadata (the table we resolved), not all dirty tables.
1760+ if _ , err := tx .ExecContext (ctx , "CALL DOLT_ADD('metadata')" ); err != nil {
1761+ return false , fmt .Errorf ("failed to stage metadata: %w" , err )
1762+ }
1763+ if _ , err := tx .ExecContext (ctx , "CALL DOLT_COMMIT('-m', 'auto-resolve metadata merge conflicts (GH#2466)')" ); err != nil {
17161764 return false , fmt .Errorf ("failed to commit resolved conflicts: %w" , err )
17171765 }
17181766
0 commit comments