From 9506a2b657d56903d94d5db13947a554c096aec0 Mon Sep 17 00:00:00 2001 From: lew Date: Thu, 18 Dec 2025 20:57:54 +0000 Subject: [PATCH] feat(VCS): auto-commit hooked up to all changeful commands --- cmd/del-db.go | 24 ++++++++++++++++-------- cmd/del.go | 19 ++++++++++++++++++- cmd/mv.go | 12 +++++++++--- cmd/restore.go | 3 ++- cmd/set.go | 20 +++++++++++++++++++- cmd/vcs.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 14 deletions(-) diff --git a/cmd/del-db.go b/cmd/del-db.go index 2d6805c..87fd27d 100644 --- a/cmd/del-db.go +++ b/cmd/del-db.go @@ -43,18 +43,22 @@ var delDbCmd = &cobra.Command{ func delDb(cmd *cobra.Command, args []string) error { store := &Store{} - var notFound errNotFound - path, err := store.FindStore(args[0]) - if errors.As(err, ¬Found) { - return fmt.Errorf("cannot delete-db '%s': %v", args[0], err) - } + dbName, err := store.parseDB(args[0], false) if err != nil { return fmt.Errorf("cannot delete-db '%s': %v", args[0], err) } + var notFound errNotFound + path, err := store.FindStore(dbName) + if errors.As(err, ¬Found) { + 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") 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 { @@ -63,13 +67,17 @@ func delDb(cmd *cobra.Command, args []string) error { var confirm string 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" { 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 { diff --git a/cmd/del.go b/cmd/del.go index ba133b4..5a59da1 100644 --- a/cmd/del.go +++ b/cmd/del.go @@ -71,6 +71,7 @@ func del(cmd *cobra.Command, args []string) error { return fmt.Errorf("cannot remove: No such key") } + var processed []resolvedTarget for _, target := range targets { if interactive || config.Key.AlwaysPromptDelete { var confirm string @@ -101,9 +102,25 @@ func del(cmd *cobra.Command, args []string) error { if err := store.Transaction(trans); err != nil { return err } + processed = append(processed, target) } - return nil + if len(processed) == 0 { + 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() { diff --git a/cmd/mv.go b/cmd/mv.go index 82d9682..39c02b5 100644 --- a/cmd/mv.go +++ b/cmd/mv.go @@ -142,17 +142,23 @@ func mv(cmd *cobra.Command, args []string) error { } 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, readonly: false, sync: false, transact: func(tx *badger.Txn, k []byte) error { 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 ( diff --git a/cmd/restore.go b/cmd/restore.go index e07d9b7..933b0ec 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -165,7 +165,8 @@ func restore(cmd *cobra.Command, args []string) error { } 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) { diff --git a/cmd/set.go b/cmd/set.go index 9419022..72ef5c4 100644 --- a/cmd/set.go +++ b/cmd/set.go @@ -26,6 +26,7 @@ import ( "fmt" "io" "strings" + "unicode/utf8" "github.com/dgraph-io/badger/v4" "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() { @@ -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().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 +} diff --git a/cmd/vcs.go b/cmd/vcs.go index 43b5971..b287c34 100644 --- a/cmd/vcs.go +++ b/cmd/vcs.go @@ -3,6 +3,7 @@ package cmd import ( "bufio" "encoding/json" + "errors" "fmt" "io" "os" @@ -478,3 +479,50 @@ func restoreSnapshot(store *Store, path string, dbName string) error { } 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) +}