feat(dolt): auto-commit write commands and set explicit commit authors (#1270)

Adds Dolt auto-commit functionality for write commands and sets explicit commit authors.

Includes fix for race condition in commandDidWrite (converted to atomic.Bool).

Original PR: #1267 by @coffeegoddd
Co-authored-by: Dustin Brown <dustin@dolthub.com>
This commit is contained in:
Steve Yegge
2026-01-22 20:52:20 -08:00
committed by John Ogle
parent 9fd0ea2c67
commit 0ee020ed76
18 changed files with 596 additions and 45 deletions

View File

@@ -12,6 +12,7 @@ import (
"slices"
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
@@ -71,19 +72,41 @@ var (
upgradeAcknowledged = false // Set to true after showing upgrade notification once per session
)
var (
noAutoFlush bool
noAutoImport bool
sandboxMode bool
allowStale bool // Use --allow-stale: skip staleness check (emergency escape hatch)
noDb bool // Use --no-db mode: load from JSONL, write back after each command
noAutoFlush bool
noAutoImport bool
sandboxMode bool
allowStale bool // Use --allow-stale: skip staleness check (emergency escape hatch)
noDb bool // Use --no-db mode: load from JSONL, write back after each command
readonlyMode bool // Read-only mode: block write operations (for worker sandboxes)
storeIsReadOnly bool // Track if store was opened read-only (for staleness checks)
lockTimeout time.Duration // SQLite busy_timeout (default 30s, 0 = fail immediately)
profileEnabled bool
profileFile *os.File
traceFile *os.File
verboseFlag bool // Enable verbose/debug output
quietFlag bool // Suppress non-essential output
profileEnabled bool
profileFile *os.File
traceFile *os.File
verboseFlag bool // Enable verbose/debug output
quietFlag bool // Suppress non-essential output
// Dolt auto-commit policy (flag/config). Values: off | on
doltAutoCommit string
// commandDidWrite is set when a command performs a write that should trigger
// auto-flush. Used to decide whether to auto-commit Dolt after the command completes.
// Thread-safe via atomic.Bool to avoid data races in concurrent flush operations.
commandDidWrite atomic.Bool
// commandDidExplicitDoltCommit is set when a command already created a Dolt commit
// explicitly (e.g., bd sync in dolt-native mode, hook flows, bd vc commit).
// This prevents a redundant auto-commit attempt in PersistentPostRun.
commandDidExplicitDoltCommit bool
// commandDidWriteTipMetadata is set when a command records a tip as "shown" by writing
// metadata (tip_*_last_shown). This will be used to create a separate Dolt commit for
// tip writes, even when the main command is read-only.
commandDidWriteTipMetadata bool
// commandTipIDsShown tracks which tip IDs were shown in this command (deduped).
// This is used for tip-commit message formatting.
commandTipIDsShown map[string]struct{}
)
// readOnlyCommands lists commands that only read from the database.
@@ -189,6 +212,7 @@ func init() {
rootCmd.PersistentFlags().BoolVar(&allowStale, "allow-stale", false, "Allow operations on potentially stale data (skip staleness check)")
rootCmd.PersistentFlags().BoolVar(&noDb, "no-db", false, "Use no-db mode: load from JSONL, no SQLite")
rootCmd.PersistentFlags().BoolVar(&readonlyMode, "readonly", false, "Read-only mode: block write operations (for worker sandboxes)")
rootCmd.PersistentFlags().StringVar(&doltAutoCommit, "dolt-auto-commit", "", "Dolt backend: auto-commit after write commands (off|on). Default from config key dolt.auto-commit")
rootCmd.PersistentFlags().DurationVar(&lockTimeout, "lock-timeout", 30*time.Second, "SQLite busy timeout (0 = fail immediately if locked)")
rootCmd.PersistentFlags().BoolVar(&profileEnabled, "profile", false, "Generate CPU profile for performance analysis")
rootCmd.PersistentFlags().BoolVarP(&verboseFlag, "verbose", "v", false, "Enable verbose/debug output")
@@ -231,6 +255,12 @@ var rootCmd = &cobra.Command{
// Initialize CommandContext to hold runtime state (replaces scattered globals)
initCommandContext()
// Reset per-command write tracking (used by Dolt auto-commit).
commandDidWrite.Store(false)
commandDidExplicitDoltCommit = false
commandDidWriteTipMetadata = false
commandTipIDsShown = make(map[string]struct{})
// Set up signal-aware context for graceful cancellation
rootCtx, rootCancel = signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
@@ -321,6 +351,14 @@ var rootCmd = &cobra.Command{
WasSet bool
}{actor, true}
}
if !cmd.Flags().Changed("dolt-auto-commit") && strings.TrimSpace(doltAutoCommit) == "" {
doltAutoCommit = config.GetString("dolt.auto-commit")
} else if cmd.Flags().Changed("dolt-auto-commit") {
flagOverrides["dolt-auto-commit"] = struct {
Value interface{}
WasSet bool
}{doltAutoCommit, true}
}
// Check for and log configuration overrides (only in verbose mode)
if verboseFlag {
@@ -330,6 +368,12 @@ var rootCmd = &cobra.Command{
}
}
// Validate Dolt auto-commit mode early so all commands fail fast on invalid config.
if _, err := getDoltAutoCommitMode(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
// GH#1093: Check noDbCommands BEFORE expensive operations (ensureForkProtection,
// signalOrchestratorActivity) to avoid spawning git subprocesses for simple commands
// like "bd version" that don't need database access.
@@ -930,6 +974,45 @@ var rootCmd = &cobra.Command{
}
}
// Dolt auto-commit: after a successful write command (and after final flush),
// create a Dolt commit so changes don't remain only in the working set.
if commandDidWrite.Load() && !commandDidExplicitDoltCommit {
if err := maybeAutoCommit(rootCtx, doltAutoCommitParams{Command: cmd.Name()}); err != nil {
fmt.Fprintf(os.Stderr, "Error: dolt auto-commit failed: %v\n", err)
os.Exit(1)
}
}
// Tip metadata auto-commit: if a tip was shown, create a separate Dolt commit for the
// tip_*_last_shown metadata updates. This may happen even for otherwise read-only commands.
if commandDidWriteTipMetadata && len(commandTipIDsShown) > 0 {
// Only applies when dolt auto-commit is enabled and backend is versioned (Dolt).
if mode, err := getDoltAutoCommitMode(); err != nil {
fmt.Fprintf(os.Stderr, "Error: dolt tip auto-commit failed: %v\n", err)
os.Exit(1)
} else if mode == doltAutoCommitOn {
// Apply tip metadata writes now (deferred in recordTipShown for Dolt).
for tipID := range commandTipIDsShown {
key := fmt.Sprintf("tip_%s_last_shown", tipID)
value := time.Now().Format(time.RFC3339)
if err := store.SetMetadata(rootCtx, key, value); err != nil {
fmt.Fprintf(os.Stderr, "Error: dolt tip auto-commit failed: %v\n", err)
os.Exit(1)
}
}
ids := make([]string, 0, len(commandTipIDsShown))
for tipID := range commandTipIDsShown {
ids = append(ids, tipID)
}
msg := formatDoltAutoCommitMessage("tip", getActor(), ids)
if err := maybeAutoCommit(rootCtx, doltAutoCommitParams{Command: "tip", MessageOverride: msg}); err != nil {
fmt.Fprintf(os.Stderr, "Error: dolt tip auto-commit failed: %v\n", err)
os.Exit(1)
}
}
}
// Signal that store is closing (prevents background flush from accessing closed store)
storeMutex.Lock()
storeActive = false