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>
104 lines
2.3 KiB
Go
104 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/steveyegge/beads/internal/storage"
|
|
)
|
|
|
|
type doltAutoCommitParams struct {
|
|
// Command is the top-level bd command name (e.g., "create", "update").
|
|
Command string
|
|
// IssueIDs are the primary issue IDs affected by the command (optional).
|
|
IssueIDs []string
|
|
// MessageOverride, if non-empty, is used verbatim.
|
|
MessageOverride string
|
|
}
|
|
|
|
// maybeAutoCommit creates a Dolt commit after a successful write command when enabled.
|
|
//
|
|
// Semantics:
|
|
// - Only applies when dolt auto-commit is enabled (on) AND the active store is versioned (Dolt).
|
|
// - Uses Dolt's "commit all" behavior under the hood (DOLT_COMMIT -Am).
|
|
// - Treats "nothing to commit" as a no-op.
|
|
func maybeAutoCommit(ctx context.Context, p doltAutoCommitParams) error {
|
|
mode, err := getDoltAutoCommitMode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if mode != doltAutoCommitOn {
|
|
return nil
|
|
}
|
|
|
|
st := getStore()
|
|
vs, ok := storage.AsVersioned(st)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
msg := p.MessageOverride
|
|
if strings.TrimSpace(msg) == "" {
|
|
msg = formatDoltAutoCommitMessage(p.Command, getActor(), p.IssueIDs)
|
|
}
|
|
|
|
if err := vs.Commit(ctx, msg); err != nil {
|
|
if isDoltNothingToCommit(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func isDoltNothingToCommit(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
s := strings.ToLower(err.Error())
|
|
// Dolt commonly reports "nothing to commit".
|
|
if strings.Contains(s, "nothing to commit") {
|
|
return true
|
|
}
|
|
// Some versions/paths may report "no changes".
|
|
if strings.Contains(s, "no changes") && strings.Contains(s, "commit") {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func formatDoltAutoCommitMessage(cmd string, actor string, issueIDs []string) string {
|
|
cmd = strings.TrimSpace(cmd)
|
|
if cmd == "" {
|
|
cmd = "write"
|
|
}
|
|
actor = strings.TrimSpace(actor)
|
|
if actor == "" {
|
|
actor = "unknown"
|
|
}
|
|
|
|
ids := make([]string, 0, len(issueIDs))
|
|
seen := make(map[string]bool, len(issueIDs))
|
|
for _, id := range issueIDs {
|
|
id = strings.TrimSpace(id)
|
|
if id == "" || seen[id] {
|
|
continue
|
|
}
|
|
seen[id] = true
|
|
ids = append(ids, id)
|
|
}
|
|
slices.Sort(ids)
|
|
|
|
const maxIDs = 5
|
|
if len(ids) > maxIDs {
|
|
ids = ids[:maxIDs]
|
|
}
|
|
|
|
if len(ids) == 0 {
|
|
return fmt.Sprintf("bd: %s (auto-commit) by %s", cmd, actor)
|
|
}
|
|
return fmt.Sprintf("bd: %s (auto-commit) by %s [%s]", cmd, actor, strings.Join(ids, ", "))
|
|
}
|