feat(VCS): auto-commit hooked up to all changeful commands

This commit is contained in:
Lewis Wynne 2025-12-18 20:57:54 +00:00
parent 63e2cc55a0
commit 9506a2b657
6 changed files with 112 additions and 14 deletions

View file

@ -43,18 +43,22 @@ var delDbCmd = &cobra.Command{
func delDb(cmd *cobra.Command, args []string) error { func delDb(cmd *cobra.Command, args []string) error {
store := &Store{} store := &Store{}
var notFound errNotFound dbName, err := store.parseDB(args[0], false)
path, err := store.FindStore(args[0])
if errors.As(err, &notFound) {
return fmt.Errorf("cannot delete-db '%s': %v", args[0], err)
}
if err != nil { if err != nil {
return fmt.Errorf("cannot delete-db '%s': %v", args[0], err) return fmt.Errorf("cannot delete-db '%s': %v", args[0], err)
} }
var notFound errNotFound
path, err := store.FindStore(dbName)
if errors.As(err, &notFound) {
return fmt.Errorf("cannot delete-db '%s': %v", dbName, err)
}
if err != nil {
return fmt.Errorf("cannot delete-db '%s': %v", dbName, err)
}
interactive, err := cmd.Flags().GetBool("interactive") interactive, err := cmd.Flags().GetBool("interactive")
if err != nil { if err != nil {
return fmt.Errorf("cannot delete-db '%s': %v", args[0], err) return fmt.Errorf("cannot delete-db '%s': %v", dbName, err)
} }
if interactive || config.Store.AlwaysPromptDelete { if interactive || config.Store.AlwaysPromptDelete {
@ -63,13 +67,17 @@ func delDb(cmd *cobra.Command, args []string) error {
var confirm string var confirm string
if _, err := fmt.Scanln(&confirm); err != nil { if _, err := fmt.Scanln(&confirm); err != nil {
return fmt.Errorf("cannot delete-db '%s': %v", args[0], err) return fmt.Errorf("cannot delete-db '%s': %v", dbName, err)
} }
if strings.ToLower(confirm) != "y" { if strings.ToLower(confirm) != "y" {
return nil return nil
} }
} }
return executeDeletion(path) if err := executeDeletion(path); err != nil {
return err
}
msg := fmt.Sprintf("rm-db @%s", dbName)
return autoCommit(store, []string{dbName}, msg)
} }
func executeDeletion(path string) error { func executeDeletion(path string) error {

View file

@ -71,6 +71,7 @@ func del(cmd *cobra.Command, args []string) error {
return fmt.Errorf("cannot remove: No such key") return fmt.Errorf("cannot remove: No such key")
} }
var processed []resolvedTarget
for _, target := range targets { for _, target := range targets {
if interactive || config.Key.AlwaysPromptDelete { if interactive || config.Key.AlwaysPromptDelete {
var confirm string var confirm string
@ -101,11 +102,27 @@ func del(cmd *cobra.Command, args []string) error {
if err := store.Transaction(trans); err != nil { if err := store.Transaction(trans); err != nil {
return err return err
} }
processed = append(processed, target)
} }
if len(processed) == 0 {
return nil return nil
} }
var dbs []string
var labels []string
for _, t := range processed {
spec, err := store.parseKey(t.full, true)
if err != nil {
return err
}
dbs = append(dbs, spec.DB)
labels = append(labels, t.display)
}
msg := fmt.Sprintf("rm %s", strings.Join(labels, ", "))
return autoCommit(store, dbs, msg)
}
func init() { func init() {
delCmd.Flags().BoolP("interactive", "i", false, "Prompt yes/no for each deletion") delCmd.Flags().BoolP("interactive", "i", false, "Prompt yes/no for each deletion")
delCmd.Flags().StringSliceP("glob", "g", nil, "Delete keys matching glob pattern (repeatable)") delCmd.Flags().StringSliceP("glob", "g", nil, "Delete keys matching glob pattern (repeatable)")

View file

@ -142,17 +142,23 @@ func mv(cmd *cobra.Command, args []string) error {
} }
if copy { if copy {
return nil msg := fmt.Sprintf("cp %s -> %s", fromSpec.Display(), toSpec.Display())
return autoCommit(store, []string{fromSpec.DB, toSpec.DB}, msg)
} }
return store.Transaction(TransactionArgs{ if err := store.Transaction(TransactionArgs{
key: fromRef, key: fromRef,
readonly: false, readonly: false,
sync: false, sync: false,
transact: func(tx *badger.Txn, k []byte) error { transact: func(tx *badger.Txn, k []byte) error {
return tx.Delete(k) return tx.Delete(k)
}, },
}) }); err != nil {
return err
}
msg := fmt.Sprintf("mv %s -> %s", fromSpec.Display(), toSpec.Display())
return autoCommit(store, []string{fromSpec.DB, toSpec.DB}, msg)
} }
var ( var (

View file

@ -165,7 +165,8 @@ func restore(cmd *cobra.Command, args []string) error {
} }
fmt.Fprintf(cmd.ErrOrStderr(), "Restored %d entries into @%s\n", restored, dbName) fmt.Fprintf(cmd.ErrOrStderr(), "Restored %d entries into @%s\n", restored, dbName)
return nil msg := fmt.Sprintf("restore @%s (%d entries)", dbName, restored)
return autoCommit(store, []string{dbName}, msg)
} }
func restoreInput(cmd *cobra.Command) (io.Reader, io.Closer, error) { func restoreInput(cmd *cobra.Command) (io.Reader, io.Closer, error) {

View file

@ -26,6 +26,7 @@ import (
"fmt" "fmt"
"io" "io"
"strings" "strings"
"unicode/utf8"
"github.com/dgraph-io/badger/v4" "github.com/dgraph-io/badger/v4"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -118,7 +119,13 @@ func set(cmd *cobra.Command, args []string) error {
}, },
} }
return store.Transaction(trans) if err := store.Transaction(trans); err != nil {
return err
}
valSummary := summarizeValue(value)
msg := fmt.Sprintf("set %s: %s", spec.Display(), valSummary)
return autoCommit(store, []string{spec.DB}, msg)
} }
func init() { func init() {
@ -127,3 +134,14 @@ func init() {
setCmd.Flags().DurationP("ttl", "t", 0, "Expire the key after the provided duration (e.g. 24h, 30m)") setCmd.Flags().DurationP("ttl", "t", 0, "Expire the key after the provided duration (e.g. 24h, 30m)")
setCmd.Flags().BoolP("interactive", "i", false, "Prompt before overwriting an existing key") setCmd.Flags().BoolP("interactive", "i", false, "Prompt before overwriting an existing key")
} }
func summarizeValue(v []byte) string {
if !utf8.Valid(v) {
return "(binary)"
}
s := string(v)
if len(s) > 80 {
return s[:80] + "..."
}
return s
}

View file

@ -3,6 +3,7 @@ package cmd
import ( import (
"bufio" "bufio"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -478,3 +479,50 @@ func restoreSnapshot(store *Store, path string, dbName string) error {
} }
return nil return nil
} }
func autoCommit(store *Store, dbs []string, message string) error {
if !config.Git.AutoCommit {
return nil
}
repoDir, err := ensureVCSInitialized()
if err != nil {
return err
}
unique := make(map[string]struct{})
for _, db := range dbs {
if db == "" {
db = config.Store.DefaultStoreName
}
unique[db] = struct{}{}
}
for db := range unique {
if err := snapshotOrRemoveDB(store, repoDir, db); err != nil {
return err
}
}
if err := runGit(repoDir, "add", "snapshots"); err != nil {
return err
}
return runGit(repoDir, "commit", "--allow-empty", "-m", message)
}
func snapshotOrRemoveDB(store *Store, repoDir, db string) error {
_, err := store.FindStore(db)
var nf errNotFound
if errors.As(err, &nf) {
snapPath := filepath.Join(repoDir, "snapshots", fmt.Sprintf("%s.ndjson", db))
if rmErr := os.Remove(snapPath); rmErr != nil && !os.IsNotExist(rmErr) {
return rmErr
}
return nil
}
if err != nil {
return err
}
return snapshotDB(store, repoDir, db)
}