arche / internal/cli/cmd_bookmark.go

commit 154431fd
  1package cli
  2
  3import (
  4	"encoding/json"
  5	"fmt"
  6	"time"
  7
  8	"arche/internal/repo"
  9	"arche/internal/store"
 10
 11	"github.com/spf13/cobra"
 12)
 13
 14var bookmarkCmd = &cobra.Command{
 15	Use:   "bookmark",
 16	Short: "Bookmark subcommands",
 17}
 18
 19var bookmarkSetCmd = &cobra.Command{
 20	Use:   "set <name> [commit]",
 21	Short: "Create or update a bookmark pointing to HEAD or a given commit",
 22	Args:  cobra.RangeArgs(1, 2),
 23	RunE: func(cmd *cobra.Command, args []string) error {
 24		r := openRepo()
 25		defer r.Close()
 26
 27		var commitID [32]byte
 28		if len(args) == 2 {
 29			resolved, err := resolveRef(r, args[1])
 30			if err != nil {
 31				return fmt.Errorf("resolve %q: %w", args[1], err)
 32			}
 33			commitID = resolved
 34		} else {
 35			_, id, err := r.HeadCommit()
 36			if err != nil {
 37				return err
 38			}
 39			commitID = id
 40		}
 41
 42		before, _ := r.CaptureRefState()
 43
 44		var beforeState repo.RefState
 45		_ = json.Unmarshal([]byte(before), &beforeState)
 46		afterState := beforeState
 47		if afterState.Bookmarks == nil {
 48			afterState.Bookmarks = make(map[string]string)
 49		}
 50		afterState.Bookmarks[args[0]] = fmt.Sprintf("%x", commitID)
 51		afterBytes, _ := json.Marshal(afterState)
 52		after := string(afterBytes)
 53
 54		tx, err := r.Store.Begin()
 55		if err != nil {
 56			return err
 57		}
 58		if err := r.Store.SetBookmark(tx, store.Bookmark{Name: args[0], CommitID: commitID}); err != nil {
 59			r.Store.Rollback(tx)
 60			return err
 61		}
 62		op := store.Operation{
 63			Kind: "bookmark-set", Timestamp: time.Now().Unix(), Before: before, After: after,
 64			Metadata: fmt.Sprintf("bookmark %q → %x", args[0], commitID[:6]),
 65		}
 66		if _, err := r.Store.InsertOperation(tx, op); err != nil {
 67			r.Store.Rollback(tx)
 68			return err
 69		}
 70		if err := r.Store.Commit(tx); err != nil {
 71			return err
 72		}
 73
 74		fmt.Printf("Bookmark '%s' set to %x\n", args[0], commitID[:8])
 75		return nil
 76	},
 77}
 78
 79var bookmarkDeleteCmd = &cobra.Command{
 80	Use:     "delete <name>",
 81	Aliases: []string{"rm", "remove"},
 82	Short:   "Delete a bookmark",
 83	Args:    cobra.ExactArgs(1),
 84	RunE: func(cmd *cobra.Command, args []string) error {
 85		r := openRepo()
 86		defer r.Close()
 87
 88		before, _ := r.CaptureRefState()
 89
 90		var beforeState repo.RefState
 91		_ = json.Unmarshal([]byte(before), &beforeState)
 92		afterState := beforeState
 93		if afterState.Bookmarks == nil {
 94			afterState.Bookmarks = make(map[string]string)
 95		}
 96		delete(afterState.Bookmarks, args[0])
 97		afterBytes, _ := json.Marshal(afterState)
 98		after := string(afterBytes)
 99
100		tx, err := r.Store.Begin()
101		if err != nil {
102			return err
103		}
104		if err := r.Store.DeleteBookmark(tx, args[0]); err != nil {
105			r.Store.Rollback(tx)
106			return err
107		}
108		op := store.Operation{
109			Kind: "bookmark-delete", Timestamp: time.Now().Unix(), Before: before, After: after,
110			Metadata: fmt.Sprintf("deleted bookmark %q", args[0]),
111		}
112		if _, err := r.Store.InsertOperation(tx, op); err != nil {
113			r.Store.Rollback(tx)
114			return err
115		}
116		if err := r.Store.Commit(tx); err != nil {
117			return err
118		}
119		fmt.Printf("Bookmark '%s' deleted.\n", args[0])
120		return nil
121	},
122}
123
124var bookmarkListCmd = &cobra.Command{
125	Use:   "list",
126	Short: "List all bookmarks",
127	RunE: func(cmd *cobra.Command, args []string) error {
128		r := openRepo()
129		defer r.Close()
130
131		bms, err := r.Store.ListBookmarks()
132		if err != nil {
133			return err
134		}
135		if len(bms) == 0 {
136			fmt.Println("No bookmarks.")
137			return nil
138		}
139		_, headID, _ := r.HeadCommit()
140		for _, bm := range bms {
141			marker := "  "
142			if bm.CommitID == headID {
143				marker = "> "
144			}
145			remote := ""
146			if bm.Remote != "" {
147				remote = " (remote: " + bm.Remote + ")"
148			}
149			fmt.Printf("%s%-24s  %x%s\n", marker, bm.Name, bm.CommitID[:8], remote)
150		}
151		return nil
152	},
153}
154
155func init() {
156	bookmarkCmd.AddCommand(bookmarkSetCmd, bookmarkDeleteCmd, bookmarkListCmd)
157}